paymentsuite/stripe-bundle
A Symfony bundle that integrates Stripe payments into your application, providing ready-made services and configuration for charges, subscriptions, and webhook handling so you can accept payments with less boilerplate.
Before anything else: this is a Symfony bundle, not a Laravel package. There is no service provider, no config/paymentsuite.php to publish, and no facade. If you're in a Laravel codebase, you'll be wiring PaymentSuite\StripeBundle\StripeMethod and its collaborators by hand or via a small custom service provider — the bundle's own auto-configuration only exists for Symfony's DI container.
Minimal path in a Symfony app (this is what the bundle is designed for):
composer require paymentsuite/stripe-bundle — pin deliberately. The last tagged release is from April 2014, so expect Composer to drag you onto an ancient stripe/stripe-php (1.x/2.x era). Run composer why stripe/stripe-php immediately and confirm you can live with the result.AppKernel::registerBundles() in Symfony 2/3, config/bundles.php in Symfony 4+ — you may need a compat shim because the bundle predates Flex).StripeMethod::processPayment() create a Charge.The first place to look in the source is StripeMethod — it is the whole contract surface. Everything else (manager, DI extension, Twig template) is plumbing around it. Look at how getPaymentName() and processPayment() are implemented, then trace into the manager to see exactly which Stripe API call is made and with what amount/currency. That trace tells you more about this bundle than the README does.
The bridge/method/wrapper triad. PaymentSuite's core is deliberately generic: a PaymentBridgeInterface implementation describes what is being paid, a PaymentMethodInterface implementation describes how, and a wrapper service coordinates the two and collects the result. Day to day you spend your time on the bridge (your domain object) and almost never touch the method.
$wrapper->processPayment($orderBridge); // delegates to StripeMethod -> manager -> Stripe Charge
Controller flow for a card charge. Two actions, not one. GET renders the form with the publishable key and a Stripe.js handler that swaps the card fields for a hidden stripeToken. POST receives the token, hydrates the bridge from the persisted order (never from the request — amount tampering is the classic bug here), and invokes the wrapper. On success, mark the order paid and redirect; on PaymentException, re-render the form with the message attached.
Error mapping as a first-class concern. Catch the PaymentSuite exception type, then inspect the underlying Stripe error code (card_declined, expired_card, incorrect_cvc, processing_error) and translate to user-facing copy. Persist the raw code and request id alongside the order for support tickets — you will need it when a customer insists the charge failed but Stripe says otherwise.
Persist before you charge. Write a pending payment row with the order id, amount, currency, and a generated idempotency-ish reference before calling Stripe. The old SDK integration in this bundle does not set an idempotency key, so if the request times out you have no built-in way to distinguish "never charged" from "charged but response lost." The pre-written row is your only reconciliation handle.
Don't wrap the Stripe call in a DB transaction. Holding a transaction open across an HTTP round trip to Stripe is how you get lock contention and half-committed orders. Reserve the order, commit, charge, then commit the result.
Testing. There are no test helpers shipped here. Inject a fake manager (or a Stripe test key with a mocked HTTP client via the SDK's setHttpClient) and assert on the wrapper's success/exception path. Keep one integration test against sk_test_… for the happy path only.
Integration with the rest of your app. Treat the bundle as an adapter boundary: expose your own PaymentGateway interface to your domain, and let the bundle live entirely behind it. That way the eventual migration to PaymentIntents is a swap of one implementation, not a codebase-wide refactor.
This is the headline issue: no SCA, no PaymentIntents. The bundle predates both. It uses the Charges API, which means cards issued in the EU/UK will be declined with authentication_required under PSD2, and any Stripe-side mandatory 3DS flow will fail the payment. There is no upgrade path inside this package. If you need to accept European cards, you are not using this bundle — you are using stripe/stripe-php directly with PaymentIntents or Laravel Cashier.
The repo is READ-ONLY. It's a subtree split of the PaymentSuite monorepo. Pull requests against this repository go nowhere; issues may be ignored. Any patch you make is a fork or a Composer patch, and you should record it as such.
Composer constraints are the real integration cost. An old stripe/stripe-php pin will collide with anything else in your app that wants a modern version. Check before you commit to the package; if you can't resolve it, stop — don't try to force-update the SDK under the bundle, because the manager's calls assume the old API shape.
Config quirks. The bundle reads keys from its DI alias, and there's no environment-aware safeguard. If sk_test_… becomes sk_live_… in the wrong .env, you find out from your customers. Add an explicit assertion at boot or in a health check that the prefix matches the environment.
Amounts are in the smallest currency unit. Stripe wants integer cents. Some PaymentSuite bridges expose decimal amounts and rely on the manager to convert; verify which side of the boundary does the multiplication. An off-by-100 here is silent and expensive.
Swallowed errors become 500s. If you don't catch the PaymentSuite exception, the Stripe error detail never reaches the user, and your logs just show a stack trace. Wrap the call and always log $e->getMessage() plus the Stripe error code.
No webhooks, no async events. This bundle is synchronous charge-only. Refunds, disputes, and delayed failures must be reconciled from the Stripe dashboard or a webhook endpoint you build yourself.
Debugging locally. If TLS verification fails against Stripe from a dev box behind a proxy, reach for \Stripe\Stripe::setVerifySslCerts(false) and the SDK's logger — and make absolutely certain neither setting is reachable in production config.
Extension points, in order of usefulness: redeclare the manager service in your app's DI config to inject your own HTTP client or logger; implement PaymentMethodInterface yourself if you need a variant behavior; override the bundle's Twig template through the standard Symfony template resolution. For Laravel, the only realistic extension point is constructing the objects yourself in a service provider — which is fine, because once you're doing that you should ask whether the bundle is buying you anything at all over a direct stripe/stripe-php call.
How can I help you explore Laravel packages today?