Search by

asteriskpound / laravel-iap-verification

asteriskpound

Server-side in-app-purchase receipt verification and webhook handling for Apple App Store and Google Play — plain Laravel, no NativePHP dependency.

Package info

github.com/AsteriskPound/laravel-iap-verification

pkg:composer/asteriskpound/laravel-iap-verification

Statistics

Installs: 113

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.2 2026-09-26 23:37 UTC

This package is auto-updated.

Last update: 2026-09-26 23:38:15 UTC


README

Server-side in-app-purchase receipt verification and webhook handling for the Apple App Store and Google Play — plain Laravel, no NativePHP dependency. Usable by any Laravel app, mobile or not.

Pairs with asteriskpound/nativephp-mobile-payments if your purchases come from a NativePHP app, but doesn't require it — any source of a transaction ID / purchase token works.

Status: scaffolding. The Apple/Google client wrappers and webhook controllers are a first pass, not yet run against a real sandbox purchase or a live webhook delivery.

Install

composer require asteriskpound/laravel-iap-verification
php artisan vendor:publish --tag=iap-verification-config
php artisan migrate

Set in .env (see config/iap-verification.php for what each does):

APPLE_IAP_ISSUER_ID=
APPLE_IAP_KEY_ID=
APPLE_IAP_PRIVATE_KEY_PATH=
APPLE_IAP_BUNDLE_ID=
APPLE_IAP_ENVIRONMENT=production

GOOGLE_IAP_PACKAGE_NAME=
GOOGLE_IAP_SERVICE_ACCOUNT_JSON=

IAP_VERIFICATION_GOOGLE_PUBSUB_TOKEN=

Usage

use Asteriskpound\LaravelIapVerification\Facades\IapVerification;

$result = IapVerification::verify(platform: 'ios', transactionId: $transactionId);
// or: IapVerification::verify(platform: 'android', purchaseToken: $token, productId: $productId);

if ($result->isValid()) {
    // grant entitlement in YOUR OWN subscription/user model, then tell the
    // mobile app it's safe to call Payments::finish($transactionId)
}

On iOS, transactionId may be either the bare transaction ID or the StoreKit 2 signed transaction (JWS) the device hands you — the ID is read from the JWS payload and looked up against Apple.

Store $result->originalTransactionId against the user you grant to. It stays the same across every renewal of the subscription (Apple's originalTransactionId, Google's purchase token), so it's the key for keeping one store subscription bound to one account and for matching webhook events back to that account.

Webhooks

Two routes are registered automatically (disable via IAP_VERIFICATION_REGISTER_ROUTES=false and mount them yourself if you'd rather):

  • POST /iap-verification/webhooks/apple — register this URL in App Store Connect as your App Store Server Notifications V2 endpoint.
  • POST /iap-verification/webhooks/google — point a Google Cloud Pub/Sub push subscription at this URL, configured for Real-time Developer Notifications in Play Console. Secure it with a bearer token matching IAP_VERIFICATION_GOOGLE_PUBSUB_TOKEN.

Both dispatch Laravel events (SubscriptionRenewed, SubscriptionExpired, SubscriptionRefunded, SubscriptionRevoked) — listen for those in your own app to keep your entitlement model in sync. This package deliberately doesn't assume your schema; it only tells you what happened. Every event carries originalTransactionId for that matching. Google's notifications carry no expiry date, so on SubscriptionRenewed with a null expiresDate, re-verify the purchase token to get the new one.

License

MIT