Download the PHP package kkxdev/laravel-apple-iap without Composer

On this page you can find all versions of the php package kkxdev/laravel-apple-iap. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-apple-iap

Laravel Apple In-App Purchase

A Laravel package for Apple In-App Purchase — receipt validation, App Store Server API (StoreKit 2), JWS transaction verification, and App Store Server Notifications v2.

Core IAP functionality lives in the package. Subscription state management (active/expired/grace period tracking, database records) is left to your application via Laravel events.

Requirements

Dependency Version
PHP ^8.0
Laravel ^8.0 \| ^9.0 \| ^10.0 \| ^11.0 \| ^12.0

Installation

Install via Composer:

The service provider and AppleIap facade are auto-discovered via Laravel's package discovery.

Publish the config file:

This creates config/apple-iap.php in your application.

Configuration

Add the following variables to your .env file:

Webhook URL

Register your notification URL in App Store Connect → Your App → App Information → App Store Server Notifications:

The default path can be changed in config:

Circuit Breaker

The circuit breaker is enabled by default and wraps all HTTP calls to Apple's APIs. It opens after 5 consecutive network failures and probes recovery after 60 seconds:

Usage

Facade

All functionality is available through the AppleIap facade:


App Store Server Notifications v2 (Recommended)

Register a webhook route and process incoming notifications:

The apple-iap.verify-notification middleware verifies the cryptographic signature of every incoming notification before your controller runs. Requests with invalid signatures receive a 400 response automatically.

processServerNotification() fires:

  1. ServerNotificationReceived — always (catch-all)
  2. A specific typed event matching the notification type (e.g. SubscriptionRenewed)

Listening to Events

Register listeners in your EventServiceProvider:

Example Listener


All Available Events

Event Fired when
ServerNotificationReceived Every successfully verified notification (catch-all)
SubscriptionPurchased New auto-renewable subscription
SubscriptionRenewed Subscription successfully renewed
SubscriptionExpired Subscription expired (billing retry exhausted or product removed)
SubscriptionCancelled User disabled auto-renew (EXPIRED + VOLUNTARY)
SubscriptionRevoked Family-sharing access revoked
SubscriptionInBillingRetry Payment failed; Apple retrying
SubscriptionInGracePeriod Payment failed but grace period is active
GracePeriodExpired Grace period ended without successful payment
SubscriptionAutoRenewEnabled User re-enabled auto-renew
SubscriptionAutoRenewDisabled User disabled auto-renew (will expire at period end)
SubscriptionPlanChanged User downgraded or upgraded plan
SubscriptionOfferRedeemed Promotional or offer code redeemed
SubscriptionPriceIncrease Apple notified user of price increase
SubscriptionExpiredPriceIncrease Subscription expired because user declined price increase
ConsumablePurchased Consumable IAP purchased
NonConsumablePurchased Non-consumable IAP purchased
NonRenewingSubscriptionPurchased Non-renewing subscription purchased
RefundIssued Refund granted by Apple
RefundDeclined Refund request declined
RefundReversed Previously granted refund reversed
ConsumptionRequest Apple requesting consumption data
RenewalExtension Subscription renewal date was extended
OneTimeChargePurchased One-time charge (e.g. consumable via StoreKit 2)
ReceiptValidated Legacy receipt successfully validated
TransactionVerified JWS transaction decoded and verified

Every event exposes the raw decoded DTOs — your listener has everything it needs without making additional API calls.


App Store Server API (StoreKit 2)

Get Transaction History

Get Subscription Statuses

Look Up by Order ID

Get Refund History

Extend a Subscription Renewal Date

Send a Test Notification


Verifying JWS Transactions Directly

When your app sends a StoreKit 2 transaction to your server, verify it directly:

Decode renewal info:


Promotional Offer Signatures

Promotional offers let you give discounted (or free) subscription periods to existing or lapsed subscribers. Because generating the signature requires your private key, it must always be done on a secure server — never on the device.

How it works

  1. Your iOS app determines the user is eligible for a promotional offer.
  2. The app calls your backend to obtain a signed payload.
  3. The backend calls AppleIap::generatePromotionalOfferSignature() and returns the result.
  4. The iOS app passes the four values to StoreKit when initiating the purchase.
  5. Apple verifies the signature; on success the promotional price applies.

Basic usage

The toArray() response contains exactly the four fields StoreKit requires:

Note: A new nonce is generated automatically on every call. Each nonce is single-use — Apple rejects duplicate nonces. Signatures expire after 24 hours.

iOS integration (StoreKit 2)

iOS integration (StoreKit 1 / SKPaymentDiscount)

Example controller

Using a dedicated Subscription Key

By default the package reuses the App Store Server API key (APPLE_IAP_KEY_ID / APPLE_IAP_PRIVATE_KEY_PATH). If you want a separate key downloaded from App Store Connect → Users and Access → Keys → In-App Purchase:

applicationUsername rules

Scenario Value to pass
You use appAccountToken UUIDs Pass the user's UUID string (lowercase)
You don't use appAccountToken Pass an empty string ""
You pass null Do not do this — causes a double separator and signature mismatch

Legacy Receipt Validation

Apple has deprecated verifyReceipt. Use the App Store Server API for new integrations.

The package automatically retries against the sandbox endpoint when Apple returns status 21007 (sandbox receipt sent to production), so you do not need to handle this case yourself.


Dependency Injection

All components are bound in the container. You can inject them directly:


Artisan Command

Verify a receipt from the command line (useful for debugging customer issues):


Error Handling

All exceptions extend Kkxdev\AppleIap\Exceptions\AppleIapException:

Exception Thrown when
ReceiptValidationException Apple returns a non-zero receipt validation status
JwsVerificationException A JWS token fails certificate chain or signature verification
NotificationVerificationException A server notification payload fails verification
ApiException App Store Server API returns a 4xx response
NetworkException Connection failure or 5xx response from Apple
CircuitBreakerOpenException Circuit breaker is open due to repeated failures
InvalidEnvironmentException An invalid environment value is configured

Circuit Breaker

The circuit breaker prevents cascading failures when Apple's APIs are experiencing issues.

States:

What counts as a failure: NetworkException only (5xx responses, connection timeouts). ApiException with 4xx status does not trip the circuit — those are caller errors.

Two independent circuit breakers run in parallel:

To disable entirely:


Testing

The package binds all components through contracts, making them easy to swap in tests.

Faking HTTP calls

Faking events

Mocking the verifier


DTO Reference

JwsTransaction

Property Type Description
transactionId string Unique transaction identifier
originalTransactionId string Original transaction (stable across renewals)
bundleId string App bundle identifier
productId string Product identifier
subscriptionGroupIdentifier ?string Subscription group
purchaseDate int Purchase timestamp in milliseconds
originalPurchaseDate int Original purchase timestamp in milliseconds
expiresDate ?int Expiry timestamp in milliseconds (subscriptions only)
quantity int Quantity purchased
type string Product type (see ProductType constants)
appAccountToken ?string UUID you associated with the user at purchase time
inAppOwnershipType string PURCHASED or FAMILY_SHARED
environment ?string Production or Sandbox
price ?int Price in milliunits of the currency
currency ?string ISO 4217 currency code
revocationDate ?int Set if the transaction was revoked
revocationReason ?string Reason for revocation
isUpgraded bool Whether this was superseded by an upgrade

Helper methods:

Method Signature Description
matchesBundleId() matchesBundleId(): bool Returns true if the transaction's bundleId matches APPLE_IAP_BUNDLE_ID from your config. Use this to guard against cross-app transaction replay attacks.
isExpired() isExpired(): bool Returns true if expiresDate is in the past.
isRevoked() isRevoked(): bool Returns true if revocationDate is set.
isSandbox() isSandbox(): bool Returns true when environment is "Sandbox".
purchaseDateAsDateTime() purchaseDateAsDateTime(): \DateTimeImmutable Returns purchaseDate as a DateTimeImmutable.
expiresDateAsDateTime() expiresDateAsDateTime(): ?\DateTimeImmutable Returns expiresDate as a DateTimeImmutable, or null for non-subscriptions.

JwsRenewalInfo

Property Type Description
originalTransactionId string Links to the subscription
productId string Current product identifier
autoRenewProductId string Product to renew into
autoRenewStatus int 1 = will renew, 0 = won't
isInBillingRetryPeriod bool Payment failed; Apple retrying
gracePeriodExpiresDate ?int Grace period end in milliseconds
offerIdentifier ?string Applied offer code
expirationIntent ?int Why the subscription expired

Helper methods: willAutoRenew(), isInGracePeriod(), gracePeriodExpiresDateAsDateTime()

ProductType constants


Security

JWS verification uses the Apple Root CA G3 certificate embedded in the package. No certificate is fetched at runtime. The package walks the full x5c certificate chain in every JWS token and verifies:

  1. Each certificate in the chain is signed by the next.
  2. The chain root matches the embedded Apple Root CA G3.
  3. The JWS signature is valid using the leaf certificate's public key (ES256).

This means no Apple notification or transaction token can be forged, even if an attacker controls the network.


License

MIT


All versions of laravel-apple-iap with dependencies

PHP Build Version
Package Version
Requires php Version ^8.0|^8.1|^8.2|^8.3|^8.4
illuminate/support Version ^8.0|^9.0|^10.0|^11.0|^12.0
illuminate/http Version ^8.0|^9.0|^10.0|^11.0|^12.0
illuminate/cache Version ^8.0|^9.0|^10.0|^11.0|^12.0
illuminate/console Version ^8.0|^9.0|^10.0|^11.0|^12.0
illuminate/events Version ^8.0|^9.0|^10.0|^11.0|^12.0
illuminate/routing Version ^8.0|^9.0|^10.0|^11.0|^12.0
firebase/php-jwt Version ^v5.5.1
psr/log Version ^1.0|^2.0|^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package kkxdev/laravel-apple-iap contains the following files

Loading the files please wait ...