Open source
Purchases for Laravel
composer require roundly-consulting/purchases-for-laravelOverview
A unified in-app purchase and payments toolkit for Laravel: one API for Apple App Store, Google Play and Stripe purchases and subscriptions. It verifies every store’s signed payload natively, decodes it into one provider-agnostic result, records it as Eloquent models and fires lifecycle events you can listen to — including refunds, chargebacks and grace periods. MIT-licensed and built only on Laravel’s HTTP client and Roundly’s own crypto, enums and money packages — no stripe/stripe-php, no google/apiclient, no third-party SDKs.
What you get
One API, three stores
Apple App Store, Google Play and Stripe behind a single Purchases facade — verify, decode and record in one call.
Native verification
ES256 JWS with pinned Apple certificates, Google’s OAuth2 JWT-bearer grant and Stripe HMAC webhooks — no SDKs.
Unified result contract
Every payload maps to one ProviderResult: type, status, price, dates, line items and the raw payload.
Models, scopes & events
Purchases, subscriptions and refunds as Eloquent models, with lifecycle events for grace periods and chargebacks.
Exact money
Prices as arbitrary-precision Money in minor units, converted with each store’s own rules.
Audit, queue & replay
Log every verified payload, persist on a queue, and replay stored notifications with one command.
Facade, DI or actions
One Purchases facade over an injectable PurchasesManager and public actions, plus a recording fake that still runs the real pipeline.
Documentation
Installation
Install via Composer, publish the publish-only migrations, and optionally publish the config and the webhook routes file.
Configuration
Every config key and env variable — models, providers, audit, queue, webhook routes and Apple, Google and Stripe credentials.
The Purchases facade
Every Purchases facade method — result() and handle() for requests, sync() and replay() for held results, for($owner) and provider lookups.
DI and actions
Inject PurchasesManager instead of calling the facade, or run the action behind handle(), sync() or replay() — the same code either way.
Unified result contract
Every provider maps to one ProviderResult — type, status, ids, price, dates, line items and the raw store payload.
Recording pipeline
How handle(), sync() and replay() turn a result into a Purchase, Subscription or PurchaseRefund — audited, idempotent, events only on change.
Events
Ten lifecycle events — purchases, subscriptions, grace periods, refunds and chargebacks — each carrying the model and the result.
Models & the owner trait
Six swappable Eloquent models, the HasPurchases owner trait, subscription scopes and helpers, and the database schema.
Money & prices
Prices as arbitrary-precision money-for-laravel Money, cast over two columns and converted exactly from each store’s format.
Refunds & chargebacks
Apple, Google and Stripe refunds and disputes decode into PurchaseRefund records linked to the purchase they reverse.
Apple App Store
Verify signed App Store server notifications, look up transactions via the App Store Server API, and map Apple’s states.
Google Play
Verify one-time products and subscriptionsv2 purchases, auto-acknowledge them, and decode real-time developer notifications.
Stripe
Verify Stripe webhook signatures natively, map payment, subscription, invoice and dispute events, and read REST objects.
Signature verification & trust
How each store’s signature is verified — crypto-for-laravel primitives, pinned Apple certificates and a validated clock-skew leeway.
Webhook routes
An opt-in POST /purchases/webhooks/{provider} endpoint that verifies, records, fires events and answers 204.
Queue, audit log & replay
Persist verified notifications on a queue, keep a replayable audit log of every payload, and re-run it with purchases:replay.
Custom providers
Add a payment provider of your own by implementing the Provider contract and returning a GenericResult.
Enums & exceptions
The Status and ResultType enums with select-option and validation helpers, and the package’s exception types.
Artisan commands
Install and configure providers, list their readiness, verify credentials against the live stores, and replay notifications.
Testing
Test purchase flows without real payments or network calls — Purchases::fake() records handle(), sync() and replay(); FakeResult and PayloadFactory.
Requirements
PHP 8.4+ with ext-bcmath, Laravel 12 or 13, and Roundly’s crypto, enums and money packages — no third-party SDKs.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.