srmklive/paypal
A Laravel package for the PayPal REST API that also works as a standalone PHP client. Covers orders, payments, subscriptions, billing plans, invoices, payouts, disputes, and more, with PHP 8.2–8.5 and Laravel 12/13 support.
Build vs. buy for payment connectivity. This package answers a recurring PM question — "do we write our own PayPal integration or adopt a client?" — with a clear middle path: a thin, MIT-licensed REST wrapper covering Orders v2, Subscriptions v2, Payouts, Invoices, Disputes, Vault/Payment Method Tokens, Reporting, and Partner Referrals. The build alternative is 4–8 engineering weeks plus permanent maintenance of OAuth token refresh, retries, and error normalisation; the buy alternative (Braintree/Stripe-first, or a paid PSP orchestration layer) means renegotiating rates and re-onboarding. Adopting this package is effectively "buy the plumbing, own the product logic."
A hard roadmap forcing function: the v1 Payments sunset (Jan 2027). The single biggest product decision this enables is scheduling a migration that is otherwise invisible until it breaks. If we run legacy create-payment → redirect → execute flows or classic Billing Agreements, this package's Orders v2 / Subscriptions v2 surface is the target state. That converts a vague "tech debt" line item into a dated compliance milestone with a defined end state — which is what makes it fundable in planning.
Checkout scope and conversion. Supports the decision of which checkout experiences to ship: standard redirect checkout, and PayPal Fastlane for one-click guest checkout (the README claims ~50% higher conversion on guest flows — treat as a vendor claim to A/B test, not a forecast). If our funnel is guest-heavy, this is a credible roadmap bet; if our users are logged-in account holders, it is not a priority.
Recurring revenue architecture. Subscriptions v2 plus Catalog Products/Billing Plans and the fluent BillingPlanBuilder/subscription helpers let us decide between per-seat plans, tiered plans, and trial-to-paid flows without a bespoke billing service. Payouts and Referenced Payouts extend this to marketplace/creator-payout models — a strategic option we can keep on the roadmap without new infrastructure.
Operational defaults as product-quality decisions. Configurable timeout, connect_timeout, and max_retries (exponential backoff, honours Retry-After on 429) turn "payment flakiness" from an incident category into a tuning parameter. Local webhook verification (verifyWebHookLocally()) removes an API roundtrip from the critical path — a latency and reliability decision, not just a code decision. PSR-18 injection means we can standardise on one HTTP client across the platform for observability and egress policy.
Error contract as a UX decision. Array-with-error-key by default versus opt-in PayPalApiException with getHttpStatus() and structured getPayPalError() determines whether we can render precise, localised decline messaging or generic "payment failed" screens. That is a measurable support-ticket and conversion lever.
Platform reach and lock-in posture. Standalone PHP usage means payments knowledge and integration work are reusable outside the Laravel app — relevant if we plan a service extraction, a second product surface, or an eventual framework migration. MIT licence means no per-transaction tax and no vendor can revoke our right to run it.
Adopt when:
Look elsewhere when:
To executives: This is a free, MIT-licensed PHP client that lets us integrate PayPal's current REST APIs — checkout, subscriptions, payouts, invoicing, disputes — without building or maintaining that plumbing ourselves. It is also our cheapest route off PayPal's v1 Payments API, which PayPal sunsets in January 2027; migrating via this package de-risks a dated compliance deadline for roughly the cost of one integration sprint rather than a rebuild. It is not a vendor relationship: it is a community project with no support contract and no PayPal endorsement, so we should treat it as owned code — pin the version, monitor upstream, and keep a fork path. Upside is speed and optionality (subscriptions and payouts become roadmap-available, not infrastructure projects); downside is a governance and bus-factor exposure we manage through code review and version pinning, not through a contract.
To engineering: Adopt it as a dependency, not as an abstraction boundary we trust blindly. Wrap it behind our own payment gateway interface so the PayPal client is swappable and testable. Turn on withExceptions() and map PayPalApiException into our domain errors so we get precise decline handling instead of generic failures. Configure timeouts and retries explicitly rather than inheriting defaults. Prefer verifyWebHookLocally() with cert caching to keep webhooks off the API rate-limit budget. Inject our standard PSR-18 client if we have one, but note the built-in retry middleware only applies to the default Guzzle client — if we inject, we own retries. Pin the version; treat upstream as a fork candidate, since a single-maintainer project on a revenue path needs an explicit continuity plan. Test coverage is strong and static analysis is at PHPStan level 7, which lowers review burden — but we still own the payment path.
To finance/ops: No licence fees, no per-transaction uplift, no revenue share. Costs are engineering time for integration and the one-time migration off v1 Payments, plus ongoing maintenance of a pinned dependency. Reconciliation quality depends on us persisting capture IDs (getCaptureIdFromOrder()) and honouring webhooks — design the ledger before launch, not after.
How can I help you explore Laravel packages today?