fpt/stripe-bundle
A Symfony bundle for integrating Stripe payments into PHP applications, with configuration for API keys, services for charges and webhooks, and helpers for checkout, subscriptions, and payment flows. Simplifies adding Stripe support to Symfony projects.
First, calibrate expectations: this is not a Laravel package. fpt/stripe-bundle is a Symfony bundle (FabienPapet, MIT, ~14 stars, last release Oct 2024). There is no ServiceProvider, no config/stripe.php auto-merge, no facade, no publishable assets. A Laravel developer cannot composer require it and expect it to boot. Treat it as either (a) a reference implementation to read, or (b) something you swap for a thin Laravel wrapper around the same underlying SDK.
The bundle itself is a thin DI/config layer over stripe/stripe-php. That is the important fact: the hard part (Stripe's API) is identical in Laravel and Symfony. Only the wiring differs.
Day-one path for a Laravel developer:
composer require stripe/stripe-php in your Laravel app — the bundle adds convenience, not capability.src/ to crib three things: the config shape (keys, webhook secret, API version pin), how it registers a StripeClient-style service, and how it verifies webhook signatures.AppServiceProvider:$this->app->singleton(\Stripe\StripeClient::class, fn () => new \Stripe\StripeClient([
'api_key' => config('services.stripe.secret'),
'stripe_version' => '2024-06-20',
]));
STRIPE_KEY, STRIPE_SECRET, STRIPE_WEBHOOK_SECRET to .env and config/services.php.First use case: a Checkout Session or PaymentIntent created from a controller, and a webhook endpoint that verifies the signature. Those two flows cover ~80% of what teams actually ship, and both map cleanly from the bundle's Symfony controllers/subscribers to Laravel controllers/listeners.
Webhooks are the load-bearing wall. The bundle's main value is demonstrating signature verification and event dispatch as framework-native constructs. In Laravel the translation is:
Route::post('/stripe/webhook', StripeWebhookController::class)
->withoutMiddleware([VerifyCsrfToken::class]);
// controller
$event = \Stripe\Webhook::constructEvent(
$request->getContent(), // raw body, not $request->all()
$request->header('Stripe-Signature'),
config('services.stripe.webhook_secret')
);
Then switch on $event->type — or, better, map to Laravel events (PaymentSucceeded, SubscriptionDeleted) and register listeners, mirroring the bundle's Symfony event subscribers. This keeps Stripe's wire format out of your domain code.
Service-layer pattern. The bundle injects Stripe services into application services via Symfony DI. The Laravel analogue is constructor injection of the singleton plus an idempotency key on every mutating call:
$stripe->paymentIntents->create($params, [
'idempotency_key' => "order-{$order->id}-attempt-{$order->attempts}",
]);
Configuration parity. Symfony bundles use config/packages/stripe.yaml; Laravel uses config/services.php + .env. Whatever keys the bundle supports (public_key, secret_key, webhook_secret, endpoints, API version), reproduce them as env-driven config so test/live switching is a deploy concern, not a code concern.
Entity/model mapping, if you use its Doctrine side. If the bundle ships entities for customers/subscriptions, don't port them literally. In Laravel you want a stripe_customers table keyed by user_id + stripe_id, and a subscriptions table that mirrors only the fields you query on. Stripe remains the source of truth; your DB is a projection rebuilt from webhooks.
Test both sides. Use Stripe\StripeClient against test-mode keys, and stripe listen --forward-to localhost:8000/stripe/webhook locally. Mirror the bundle's own test approach (it almost certainly mocks the client or uses Stripe's test fixtures) so you can assert your listeners without hitting the network.
Integration tip: put the Stripe client behind a small port interface (PaymentGateway) implemented with the SDK. When the bundle's abstractions change — or when you eventually swap to Laravel Cashier — only the adapter changes.
fpt/stripe-bundle to a Laravel composer.json hoping autodiscovery does something. Symfony bundles require config/bundles.php registration and a kernel. php artisan will not help you. If you truly need it, run it in a Symfony sidecar service and talk over HTTP — but for most teams, that's overkill versus stripe/stripe-php.$request->all() or any middleware that re-encodes the payload will break signature verification. Read getContent() and keep JSON-parsing middleware off that route.VerifyCsrfToken, or the POST dies with a 419 before your code runs.api_version. Pin the same version in your SDK config — Stripe changes object shapes between versions, and un-pinned clients drift.event->id values (a webhook_events table with a unique index) and short-circuit on repeats.Stripe-Signature header and event id/type at the top of your handler. When verification fails, the exception message is generic; comparing the timestamp (Stripe-Signature t= value) against server clock drift catches most of them.How can I help you explore Laravel packages today?