scribe/stripe-bundle
A Symfony bundle that streamlines Stripe integration with preconfigured services, dependency injection and thin wrappers around the Stripe PHP SDK, so you can add payments, customers, subscriptions and webhooks to your app with minimal boilerplate.
First, the framework mismatch. scribe/stripe-bundle is a Symfony bundle, not a Laravel package. It ships a Bundle subclass plus a DI Extension and service definitions — none of which Laravel's container reads. composer require scribe/stripe-bundle in a Laravel app installs Symfony DI components and wires nothing. The realistic day-to-day posture for a Laravel developer is: read it for reference, install stripe/stripe-php, write the binding yourself.
Where to look first in the source. The DependencyInjection/Extension class and the service definition file under Resources/config/ (YAML or XML). That is effectively the entire feature set: one StripeClient service, a key parameter sourced from config, a public alias for injection, and a webhook helper. Read those two files and you have internalised everything the package does.
Minimal steps for the Laravel equivalent (roughly the same effort as installing the bundle, ~20–40 lines):
composer require stripe/stripe-phpstripe block to config/services.php:
'stripe' => [
'secret' => env('STRIPE_SECRET'),
'public' => env('STRIPE_KEY'),
'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
'api_version' => env('STRIPE_API_VERSION'),
],
StripeServiceProvider:
$this->app->singleton(\Stripe\StripeClient::class, fn ($app) =>
new \Stripe\StripeClient([
'api_key' => $app['config']['services.stripe.secret'],
'api_version' => $app['config']['services.stripe.api_version'],
])
);
StripeClient into a controller or job and make one call ($stripe->customers->retrieve($id)) to prove config wiring before building charges.First use case. A single read call (retrieve customer/subscription) from a controller confirms keys, API version, and container binding. Do that before touching write paths — the bundle adds no retries or idempotency, and neither does the SDK, so get the wire right first.
What the Symfony bundle gives you, translated to Laravel.
StripeClient via service alias. Laravel: public function __construct(private StripeClient $stripe) works once the singleton is bound.services_test.yaml. Laravel: call $this->app->instance(StripeClient::class, $fake) in a base TestCase::setUp() and use a Mockery fake.app()->bind('stripe.connect', ...)) or build a per-request client in a service:
$client = new \Stripe\StripeClient([
'api_key' => $connectedAccount->secret,
'stripe_account' => $connectedAccount->stripe_id,
]);
Don't try to make one global singleton serve multiple accounts; the stripe_account header changes per call.Webhook handling — the part the bundle advertises and the part you must own in Laravel:
$request->getContent(), not $request->all().\Stripe\Webhook::constructEvent($payload, $request->header('Stripe-Signature'), config('services.stripe.webhook_secret')).VerifyCsrfToken and from TrimStrings/ConvertEmptyStringsToNull middleware — those mutate the body and break signature matching.event->id in a unique column before processing. Retries are guaranteed; the bundle doesn't give you idempotency, and Stripe will resend on non-2xx.Job / queue pattern. Inject the singleton into jobs. Pass idempotency_key explicitly on every write:
$this->stripe->charges->create(
['amount' => 1000, 'currency' => 'usd', 'source' => $token],
['idempotency_key' => $order->uuid]
);
Testing pattern. Use Stripe test keys in phpunit.xml; mock the client in unit tests via container swap. The bundle's test-friendly override maps cleanly to Laravel's $this->app->instance().
NOASSERTION. No verified SPDX license. Do not copy source verbatim into a distributed artifact without legal sign-off. Writing the ~40 lines yourself removes the question.composer.json constraint on stripe/stripe-php. Thin wrappers often cap it below current, which blocks API-version and security upgrades. If it does, that alone disqualifies it regardless of everything else.Webhook::constructEvent throws UnexpectedValueException on any body mutation — the most common cause in Laravel is middleware (TrimStrings, ConvertEmptyStringsToNull) touching the payload. Scope webhook routes outside that group. Also: Stripe rejects events older than 5 minutes by default, so clock skew on your hosts causes intermittent 400s.api_version explicitly. Passing api_version on client construction prevents Stripe from silently upgrading your response schema when the account default changes. The bundle may or may not expose this — verify, and set it in your own provider either way.config('services.stripe.*') is env-backed and config:cache-safe; don't read env() outside config files.setLogger) — keep it off in production and audit any wrapper you adopt for what it writes.WebhookVerifier helper class, and a const map for event types. The Symfony service definitions themselves are not worth dragging across the framework boundary.stripe/stripe-php range. Until then, file it as "reference only, reason reviewed" so the next engineer doesn't rediscover it and reopen the decision.How can I help you explore Laravel packages today?