Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Appstore Purchases Laravel Package

aporat/laravel-appstore-purchases

Laravel package for verifying Apple App Store in-app purchase receipts and subscriptions server-side. Validate transactions, inspect purchase data, and integrate iOS payments into your app with a simple, framework-friendly API.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package remains a Laravel-native integration (service provider, publishable config, publishable migrations, events, queued listeners), so it fits idiomatically inside an existing Laravel app with no new runtime. The material architectural change is scope: 5.0.0 adds a Subscriptions\ persistence and reconciliation layer (ManagesStoreSubscriptions, StoreSubscription, SubscriptionRecorder, notification listeners, VerifyStoreSubscriptions). The package shifts from "receipt validator you call" to "owner of subscription state you query." That is a defensible direction — it centralises the webhook/worker-authoritative model — but it is a real coupling increase: the package now defines a schema, a model contract on the account, and a source of truth for entitlements.

Integration feasibility. High for new adopters (publish migrations, add trait, wire routes). For existing installs, feasibility is high but the upgrade is genuinely breaking: key_path is removed in favour of private_key (path or inline PEM), and a config still carrying key_path fails with an explicit message rather than degrading. Fail-fast is the right choice; it makes the migration a config edit, not a silent incident.

Technical risk.

  • Breaking config removal (key_path → private_key, APPLE_APPSTORE_KEY_PATH no longer read) — every deployment's secret wiring must change in the same release.
  • Dependency floor aporat/store-receipt-validator ^11.1 and the removed 11.0.0 compatibility shim — anyone pinned below 11.1 is blocked.
  • phpseclib pinned to ^3.0.36 — necessary (v4 silently broke Google RTDN verification with a blanket 401), but it can now conflict with applications or sibling packages that require phpseclib 4. This is the highest-probability install-time failure.
  • New cache dependency — Google token and OIDC certificate caching assume a shared cache store with lock support to deliver their benefit; the array driver degrades to the old per-process behaviour. Graceful fallback exists, so this is a performance risk, not an availability risk.
  • State ownership ambiguity — one subscriptions row per store means an account with both Apple and Google subscriptions has multiple rows; entitlement aggregation logic must live somewhere, and that must be decided explicitly.

Key questions.

  1. Who is the system of record for entitlements — the package's subscriptions table, or the application's existing entitlement model? Is the package's table read-only projection or authoritative?
  2. Is the SubscriptionRecorder dedupe window documented and configurable, and does it survive client retries across the window boundary?
  3. What is the intended reconciliation story between VerifyStoreSubscriptions (worker) and webhooks when they disagree?
  4. Do all production workers share one cache store, and is a cache lock available (Redis/memcached) or will lock-less concurrent misses stampede?
  5. Does the package's account row lock create contention on hot accounts (high-frequency renewals/restores)?
  6. Are UnhandledNotification types (and future Apple types) expected to be handled by the app, or is a no-op acceptable?

Integration Approach

Stack fit. Laravel with a queue worker and a shared cache store (Redis recommended: cache locks plus cross-worker token sharing). Package requires PHP with phpseclib 3, psr/cache ^2.0 || ^3.0. The CI fix matters here: prior lanes installed composer.lock, so "Laravel 12" lanes actually ran Laravel 13 — the supported-version matrix was effectively untested before this release. The new per-lane fresh resolution and lowest-dependency lane mean 5.0.0's compatibility claims are the first ones backed by the matrix they advertise. Treat pre-5.0 compatibility claims with reduced confidence.

Migration path (ordered).

  1. Confirm no sibling dependency requires phpseclib 4; run composer why-not phpseclib/phpseclib ^3.0 before touching anything.
  2. Upgrade aporat/store-receipt-validator to ^11.1.
  3. Rename key_path → private_key in config/appstore.php; move the value out of APPLE_APPSTORE_KEY_PATH into APPLE_APPSTORE_PRIVATE_KEY (inline PEM from the secrets manager is now the supported pattern). Verify PEM line breaks survive your secret store — the package repairs spaces/\n, but confirm in staging.
  4. Add the google_cache block if the config is already published (absent block uses defaults, so this is optional); confirm store resolves to the shared cache, not array.
  5. Publish and run the subscription migrations; do not backfill from scratch — use VerifyStoreSubscriptions to seed state from the stores.
  6. Add ManagesStoreSubscriptions to the account model; wire notification listeners to the configured subscribers.
  7. Add an UnhandledNotification listener for observability before routing production traffic.
  8. Deploy recorder endpoint separately from webhooks — the recorder is inbound from clients, webhooks are inbound from Apple/Google, and they fail differently.

Compatibility and sequencing. Ship config + secrets change first in isolation (it fails loudly, so it can be validated without new features). Then migrations and the model trait. Then the recorder endpoint. Then webhook listeners and the verification worker. Do not couple the key rename to the new subscription layer in one change set — a rollback should be possible without reverting schema.

Operational Impact

Maintenance. The package now owns schema, so migrations become upgrade obligations — this is a durable, recurring cost, not a one-off. Backup/restore and staging refresh flows must include the new tables. Pin the version in composer.json and treat 11.x validator bumps as coupled releases.

Support. Net positive: the phpseclib fix eliminates a total RTDN verification outage (401 on every push), and unknown-but-valid Apple notification types now return 2xx and dispatch UnhandledNotification instead of 400, which stops Apple retry storms. Log lines now name the notification type as Apple sent it, which shortens triage. The residual support surface is the new subscription model itself — "client credential only extends" and "webhooks/worker are authoritative" are counter-intuitive to front-end teams and will generate questions.

Scaling. Two concrete wins: Google Play access tokens are cached per service account across processes (previously every PHP process minted its own token on first call), and OIDC signing certificates are cached for Google's max-age (previously fetched on every push). At push volume the certificate cache is the larger saving. Both need a shared store to matter; on array the release is a no-op.

Failure modes and mitigations.

  • Cache store down or misconfigured → falls back to direct token exchange and per-request certificate fetch. Slower, not broken. Monitor token exchange rate as the signal.
  • Cache lock absent (non-Redis) → concurrent misses stampede the token endpoint; acceptable at low worker counts, risky at scale.
  • phpseclib 4 present via transitive resolution → previously a silent 401 on all pushes; now blocked at install. Watch for the reverse: an app pinned to phpseclib 4 cannot adopt 5.0.0 at all.
  • key_path left in config → hard failure with an explicit message. Contained to deployment, caught in staging.
  • Apple key rotation → OIDC refetch is throttled to once per minute, so a rotation can be rejected for up to ~60s; retries should be expected, not alarmed on.
  • Account row-lock contention under high renewal concurrency → watch lock wait time on the account table.
  • UnhandledNotification with no listener → silent no-op. Assign an owner before go-live.

Ramp-up. Modest engineering effort (config + migrations + trait), but real product/support education: the lifecycle semantics (start / offer start / renewal reporting), the one-row-per-store model, and the authoritative-webhook contract need to be written down. Budget roughly a sprint for a team unfamiliar with store subscription mechanics, and gate the rollout behind staging validation of the key loading path and a synthetic RTDN push. Testability improved: injectable TokenVerifier on the Apple controller and optional PSR-6/httpHandler on GoogleOidcPushVerifier let you exercise the notification path without Apple's or Google's real chains — worth using as the acceptance test.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route