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

Pay Laravel Package

yansongda/pay

A popular PHP payment library for Laravel and other frameworks, integrating Alipay and WeChat Pay with a clean API. Supports common payment flows, refunds, queries, callbacks/notifications, and flexible configuration for multi-app and sandbox use.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture Fit

  • High Compatibility with Laravel Ecosystem: The package remains explicitly Laravel-targeted (dedicated laravel-pay extension) and adheres to PSR standards (PSR-2, PSR-4, PSR-7, PSR-11, PSR-14, PSR-18), fitting Laravel’s container, config, and event systems.
  • Modular & Plugin-Based Design: The StartPlugin → Business Plugins → ParserPlugin pipeline continues to allow provider additions without core changes. This release validates the claim: Apple Pay was added as a first-class provider (Pay::apple()) without breaking the existing Alipay/WeChat/UnionPay call paths.
  • Provider Coverage Now Spans Mobile-App Commerce: With Apple (App Store Server API + App Store Server Notifications V2) alongside Alipay/WeChat, the package covers both “server-initiated” payment models and Apple’s “client-initiated, server-verified” model. TPM implication: Apple’s semantics are materially different (no server-initiated refund, no cancel/close), so the abstraction is a facade over asymmetric capabilities rather than a uniform API.
  • Multi-Tenancy Support: Tenant abstraction unchanged; still bindable via middleware or container. Apple introduces a new dimension — configuration is keyed on bundle_id and environment, so tenant isolation must now account for per-tenant bundle IDs where applicable.
  • Event-Driven: PSR-14 events still map cleanly to Laravel events; Apple callbacks (JWS-verified) should dispatch the same domain events after verification to keep downstream workflows provider-agnostic.
  • Internal Refactor, Stable Surface: Apple implementation moved to src/Crypto/Apple/ (Cryptor, TokenVerifier, JwsVerifier) with AppleTrait retained as a facade. Good separation for review/testability; no public API change.

Integration Feasibility

  • Laravel-Specific Extensions: Unchanged — service provider bootstrapping, Pay facade, config/pay.php, tenant middleware.
  • Configuration Flexibility: Still env-driven. Apple adds required config blocks: App Store Connect issuer_id, bundle_id, api_key_id, api_private_key (ES256 JWT), plus optional apple_root_ca / apple_intermediate_ca overrides. These belong in .env/secret manager, not committed config.
  • Zero New Dependencies for Apple Crypto: PKCS#7 chain validation, ECDH, and AES-256-GCM are implemented natively, and Apple root/intermediate certs ship in-tree (AppleRootCA-G3.pem, AppleAAICAG3.pem). This is a strong supply-chain and auditability win — no new transitive risk — but it makes correctness of the in-house crypto a review item.
  • HTTP Client Agnosticism: Preserved. Note Apple merchantSession requires TLS mutual authentication passed through _http; any outbound egress proxy, service mesh sidecar, or HTTP client swap must support client certificates end-to-end.
  • Enum-First Configuration: PayMode (SANDBOX/NORMAL/SERVICE) plus WeChat AppType, PapayScene, ReceiverType, VirtualPayEnv and Alipay ProductCode, BizCode replace magic literals. Improves correctness and IDE support; historic int config values (0/1/2) still accepted for mode, and toJson() still emits 0/1/2, so wire payloads are byte-identical.

Technical Risk

  • BREAKING: getMode() Return Type Change (highest priority): ProviderConfigInterface::getMode() now returns the PayMode enum, not int. Any code doing $config->getMode() === 1 or Pay::MODE_SERVICE === $config->getMode() silently evaluates to false — a fail-open condition in callbacks/mode-gating logic that will not throw. Mitigation: grep-and-replace with ->isSandbox() / ->isService() / ->isNormal() before upgrading, and add a regression test asserting mode-gated branches.
  • BREAKING: Removal of Pay::MODE_NORMAL / MODE_SANDBOX / MODE_SERVICE: Hard fatal errors at boot/include time. Prior guidance in this assessment (using Pay::MODE_SERVICE for service-account callbacks, Pay::MODE_SANDBOX in test suites) is now invalid — replace with PayMode::* (use Yansongda\Pay\Enum\PayMode;). AbstractConfig::getModeEnum() is also removed.
  • Beta Stability: This is v3.8.0-beta.8 — eight betas into a release line indicates active churn. Treat as non-GA: pin an exact version, no floating constraints, and require a green integration suite before any bump.
  • PHP Version Split for Apple Token Decryption: RSA_v1 token decryption requires PHP ≥ 8.5 (OAEP-SHA256 parameter constraints); EC_v1 has no version requirement. If the platform runs PHP 8.1–8.3 and a merchant/payment token is RSA_v1, DECRYPT_APPLE_FAILED (9612) results. Confirm which token format your Apple Pay merchant configuration issues before committing to a timeline.
  • Asymmetric Refund Semantics (business risk, not a bug): Apple has no server-initiated refund — refund returns refund history only, and cancel/close throw. This invalidates any assumption of a uniform “refund API” across providers; the refund workflow must be redesigned as user-initiated via Apple with server-side reconciliation.
  • Certificate Lifecycle & Bundled Trust Anchors: Apple chain validity checks are enforced, and certs are bundled with override hooks. Risks: (a) stale bundled intermediates over time, (b) forgotten overrides that pin expired certs. Mitigation: monitor expiry, prefer explicit apple_root_ca/apple_intermediate_ca config managed by platform engineering, and track upstream cert rotation.
  • Callback Verification Depth: App Store Server Notifications V2 use JWS x5c verification plus a secondary decode of the embedded transaction JWS, with bundleId/environment ownership checks aligned to Apple’s official library. Strong, but two-stage verification plus idempotency on transactionId is mandatory — Apple retries notifications.
  • Webhook Idempotency (unchanged but now broader): Alipay/WeChat race conditions remain; Apple adds another retry source. A single idempotency key strategy across providers is now a hard requirement.
  • Swoole Compatibility (unchanged): Keep Swoole usage confined to queues/console. Apple merchantSession mTLS and coroutine concurrency is an untested combination — do not enable under Swoole without a spike.

Key Questions

  1. Apple Pay Scope: Is the requirement (a) web/Safari Apple Pay via merchantSession (mTLS, initiative_context mandatory), (b) in-app/App Store verification via payToken decrypt, or (c) both? (c) doubles the integration surface and the certificate/domain-validation work.
  2. Token Format: Will Apple Pay merchant tokens be EC_v1 or RSA_v1? If RSA_v1, is a PHP 8.5 upgrade on the roadmap — and what is the interim plan?
  3. Refund Policy Ownership: Since Apple refunds cannot be server-initiated, who owns the support workflow, what SLA applies, and how does query (_action: transaction/history/subscriptions) reconcile Apple-side state into our payments records?
  4. Provider Prioritization: With Apple added, is the MVP still Alipay/WeChat-first with Apple as phase 2, or does iOS revenue make Apple a launch blocker? Prioritize by revenue channel, not by implementation order.
  5. Upgrade Timing vs. GA: Do we adopt v3.8.0-beta.8 now (pinned) to unblock Apple, or hold at v3.7.x for Alipay/WeChat and schedule Apple for the GA release? If holding, no action is needed on the breaking changes yet — but the audit should still start.
  6. Tenant Strategy for Apple: Apple config is bundle_id/environment keyed. Do tenants share one app/bundle, or does each tenant carry its own App Store Connect credentials (issuer_id, api_key_id, api_private_key)? The latter has key-custody and rotation implications.
  7. Secret Custody: Where do App Store Connect .p8 keys and merchant certificates live — env, Vault, KMS? Who can read them, and what is the rotation runbook?
  8. Error Taxonomy: New codes (9234 URL missing, 9235 token invalid, 9413 config invalid, 9612 decrypt failed) need mapping to alerting/runbooks and to customer-facing messages.
  9. Testing: Confirm PayMode::SANDBOX is wired into CI, plus Apple sandbox environment and fixture JWS/cert chains for verifier unit tests.

Integration Approach

Stack Fit

  • Laravel Core:
    • Service Container: Bind Pay facade/service with tenant-aware config; no change required for Apple beyond a new config block.
    • Configuration: Publish/merge config/pay.php; add apple section with issuer_id, bundle_id, api_key_id, api_private_key, optional CA overrides. Migrate mode to 'mode' => PayMode::SANDBOX (int form still accepted).
    • Events: Dispatch PaymentProcessed / PaymentFailed uniformly for Apple and Alipay/WeChat so downstream fulfillment is provider-agnostic.
  • HTTP Layer:
    • Middleware: VerifyPaymentCallback middleware still applies; extend for Apple JWS verification. Apple notification endpoint must be publicly reachable over HTTPS and configured in App Store Connect.
    • Outbound TLS: merchantSession requires mTLS client certificates — validate that egress path (no certificate-terminating proxy) supports it before design sign-off.
    • Routing: Named routes (pay.apple.callback, pay.alipay.notify) rather than hardcoded URLs.
  • Database:
    • payments table: add provider, out_trade_no, provider_transaction_id (transactionId for Apple), environment (Sandbox/Production), tenant_id, status, metadata JSON. The existing schema shape is sufficient; Apple’s nested transaction JWS payload lands in metadata.
    • Unique constraint on (provider, provider_transaction_id) as the idempotency backbone.
  • Queue:
    • HandleAppleNotificationJob (verify + persist + dispatch) and ReconcileAppleSubscriptionsJob (scheduled query with _status whitelist) alongside existing ProcessPaymentJob / HandleWebhookJob.

Migration Path

  1. Phase 0: Breaking-Change Audit (2–4 days, do first regardless of Apple)
    • Grep for Pay::MODE_NORMAL|MODE_SANDBOX|MODE_SERVICE, getModeEnum(, and getMode() compared to ints.
    • Replace with PayMode::* and ->isSandbox()/isService()/isNormal().
    • Add unit tests asserting mode-gated branches (especially service-account callback paths) to catch silent false regressions.
  2. Phase 1: Upgrade & Pin (3–5 days)
    • Pin exact yansongda/pay + yansongda/laravel-pay to the chosen beta tag; no floating constraints.
    • Run full Alipay/WeChat regression suite; verify toJson() payloads remain byte-identical.
  3. Phase 2: Apple Prerequisites (parallel, 1 week, externally gated)
    • Apple Developer: Merchant ID, Apple Pay Payment Processing Certificate, App Store Connect API key (.p8), App Store Server Notifications V2 URL registration.
    • Confirm token format (EC_v1 vs RSA_v1) and PHP version decision.
  4. Phase 3: Apple Pay Verification Path (1–2 weeks)
    • payToken verification/decryption; merchantSession mTLS proxy; map new exception codes to domain errors.
    • Callback endpoint with JWS x5c + nested transaction decode, bundleId/environment ownership validation, idempotent persistence.
  5. Phase 4: Query & Refund-History Reconciliation (1 week)
    • Wire query (_action: transaction/history/subscriptions, _status whitelist) into scheduled reconciliation.
    • Redesign refund workflow: support-initiated request with Apple-side user action, reconciled via refund history query.
  6. Phase 5: Multi-Tenancy & Observability (1 week)
    • Tenant-aware Apple config and credentials if required; TenantMiddleware binding.
    • Telescope/structured logging, per-provider dashboards, alerting on 9612/9413/9235.

Compatibility

  • Laravel Versions: Laravel 9+ / PHP 8.0+ baseline unchanged for Alipay/WeChat. Apple RSA_v1 token decryption requires PHP ≥ 8.5; EC_v1 has no version constraint. Verify PHP runtime before scoping.
  • PHP Extensions: openssl, curl, dom (XML for Alipay/UnionPay). Apple path additionally exercises OpenSSL PKCS#7/ECDH/AES-256-GCM — confirm the OpenSSL build supports the required curves/ciphers.
  • Dependencies: No new packages introduced by Apple (in-house crypto). Existing Guzzle/PSR-14/Monolog integration unchanged.
  • Semantic Compatibility (the real constraint): cancel/close throw for Apple; refund is history-only. Any provider-agnostic interface in our code (e.g., PaymentProvider::refund($amount)) will need capability flags (supportsServerRefund(), supportsCancel()) rather than assuming parity.
  • Configuration Compatibility: Historic int mode still accepted; toArray()['mode'] is now an enum (watch any config serialization into logs or admin UI), while toJson()/json_encode() still emit 0/1/2.

Sequencing

  1. Prerequisites: PHP 8.0+ (8.5 if RSA_v1), Composer, DB, Apple Developer account + App Store Connect API key, publicly reachable HTTPS callback endpoint with mTLS-capable egress.
  2. Order of Implementation:
    • Step 1 (blocking): MODE_* / getMode() audit and remediation — independent of Apple, de-risks the upgrade.
    • Step 2: Pin and upgrade to v3.8.0-beta.8; regression-test existing Alipay/WeChat flows.
    • Step 3: Apple payToken verification + merchantSession proxy (or narrow to one of the two per Key Question 1).
    • Step 4: Apple callback (JWS) + idempotency + reconciliation jobs.
    • Step 5: Refund/cancel capability modeling and support workflow.
    • Step 6: Multi-tenancy, observability, cert-rotation automation, sandbox CI.

Operational Impact

Maintenance

  • Version Discipline: Beta line with 8 pre-releases → pin exact versions, subscribe to release notes, and require a passing integration suite (Alipay + WeChat + Apple sandbox) before any bump. Budget standing capacity for upgrade work each beta.
  • Breaking-Change Watchlist: The PayMode enum migration is the pattern to expect — enum-ification of remaining string/int literals is explicitly ongoing. Maintain an internal changelog of every Pay:: constant and $config->get*() call so future removals are greppable.
  • Certificate Rotation (now two systems): WeChat dynamic public certs via CertManager::reloadWechatPublicCerts() on the scheduler plus Apple root/intermediate CAs. Prefer explicit apple_root_ca / apple_intermediate_ca overrides over bundled defaults so rotation is a config change, not a package bump. Alert on expiry with lead time.
  • Secret Management: App Store Connect .p8 keys, merchant certificates, and Alipay/WeChat secrets in a secret manager. Rotate API keys on a schedule; document revocation blast radius (Apple JWT auth failure → all Apple server calls fail).
  • Configuration Caching: Continue config:cache; ensure enum-valued mode serializes cleanly in any admin/config UI that reads toArray().
  • Crypto Review Obligation: In-house PKCS#7/ECDH/AES-GCM and JWS verification is a maintenance asset and liability — schedule periodic review against Apple’s spec and upstream library behavior, and keep fixture-based tests for known-good/known-bad chains.

Support

  • Troubleshooting Surfaces: logs/payments.log + queue:failed, now with new Apple codes — 9234 (missing URL: config/route gap), 9235 (invalid token: client/SDK or merchant config), 9413 (invalid config: credentials/JWT), 9612 (decrypt failed: token format or PHP version). Publish a mapping table to first-response actions.
  • Refund Requests Are a Support Process, Not an API Call: Apple refunds are user-initiated. Tier-1 needs a script that (a) confirms eligibility in Apple’s history via refund query, (b) instructs the customer through Apple’s flow, (c) reconciles state via query. Set expectations that “refund now” is not a server action — this is the single largest operational change in this release.
  • Cancellation: cancel/close throw for Apple. Support scripts must branch on provider capability; subscriptions are managed via App Store, not our backend.
  • Callback Diagnosis: Two-stage JWS verification failures are opaque to non-engineers. Log the failing stage (chain validation vs. nested transaction decode vs. bundleId/environment mismatch) so escalations are actionable.
  • Documentation: New web/docs/v3/apple/ pages (payment/query/refund/callback/response/plugin) and updated enum usage docs exist — but they are Chinese-first. Budget for internal translation/summary notes so on-call engineers can self-serve.

Scaling

  • Throughput: Apple notification verification is CPU-bound but modest; certificate chain parsing is the hot spot — cache parsed/validated chains per process. No scaling concern for typical volumes.
  • Queue Topology: Separate queues per provider (or priority) so an Apple notification backlog or a retry storm cannot delay Alipay/WeChat fulfillment. Set idempotency at the DB layer (unique provider_transaction_id) so concurrency is safe regardless of worker count.
  • mTLS Egress: merchantSession calls carry client certificates per request. If horizontally scaling behind NAT/proxies, verify certificate presentation is preserved and connection reuse doesn’t leak tenant credentials across tenants.
  • Multi-Tenant Fan-Out: Per-tenant Apple credentials multiply secret-management and rotation work linearly. If tenant count is high, prefer a shared app/bundle model and treat per-tenant bundles as an exception.
  • Reconciliation Load: Subscription/history polling adds scheduled read traffic. Batch and stagger by tenant; respect Apple rate limits and back off on errors.

Failure Modes

  • Silent Mode-Gating Failure (highest severity): Post-upgrade, getMode() === 1 returns false — no exception, wrong branch. Could route service-account callbacks through normal-mode logic. Detect via assertion tests; monitor callback success-rate deltas immediately after deploy.
  • Hard Boot Failure: Unresolved Pay::MODE_* references fatal on class load. Caught by CI if the full app boots in tests.
  • Apple Decrypt Failure (9612): RSA_v1 token on PHP < 8.5, or malformed token. Manifests as payment failures with a clear code — but only if error mapping is wired; otherwise it looks like a generic gateway error.
  • Callback Rejection: bundleId/environment mismatch (common when sandbox and production endpoints are cross-configured) or expired intermediate cert. Apple retries then stops — silent revenue reconciliation drift. Alert on verification-failure rate and on notifications with no matching local transaction.
  • mTLS Breakage: Proxy/mesh stripping client certs causes merchantSession failures only at runtime, only on Apple web checkout. Requires an end-to-end smoke test in staging through the real egress path.
  • Duplicate Processing: Apple retries V2 notifications; without a unique provider_transaction_id, duplicate fulfillment. Enforce at DB constraint, not application logic.
  • Cert Rotation Miss: Expired override or stale bundled intermediate → total Apple callback rejection. Monitor expiry 30/14/7 days out.
  • Provider Asymmetry Surprise: A generic admin “Refund” button calling refund on Apple returns history and throws on cancel — user-visible errors. Gate UI actions on capability flags.

Ramp-Up

  • New Concepts to Absorb: App Store Server API (ES256 JWT), App Store Server Notifications V2 (JWS x5c), PKCS#7 chain validation, ECDH + AES-256-GCM token decryption, mTLS merchant validation. This is a meaningfully steeper learning curve than adding a form-post gateway.
  • Estimated Effort: Phase 0 audit 2–4 days; upgrade + regression 3–5 days; Apple verification/callback 1–2 weeks; query/refund reconstruction 1 week; multi-tenancy + observability 1 week. Call it 5–7 engineer-weeks for both Apple paths with a single experienced Laravel engineer, plus Apple Developer account lead time (certificate issuance is externally gated — start it on day one).
  • Skills Required: One engineer comfortable with PHP crypto (OpenSSL APIs) and JWT/JWS; one engineer for Laravel integration/queue/idempotency; platform support for mTLS egress; support-team training on the Apple refund workflow (non-negotiable, and the most underestimated item).
  • Enablement Artifacts: internal runbook (error-code → action), refund/cancel capability matrix per provider, cert-rotation calendar covering WeChat and Apple, and a one-page “what changed in v3.8” note for support and finance given the refund semantics shift.
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.
dakiquang/laravel-oauth-refresh-lock
woda/laravel-worktrees
dalefranks/ramasite
faghy/laracashbook
roundly-consulting/google-places-for-laravel
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
adachsoft/changelog-linter