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

Payment Stripe Bundle Laravel Package

kj/payment-stripe-bundle

KJ Payment Stripe Bundle integrates Stripe payments into your PHP application, wrapping payment intents, checkout flows, and webhook handling behind simple configuration so you can accept cards and manage transactions with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

First, confirm what you are actually installing. kj/payment-stripe-bundle is a Symfony bundle (Symfony\Component\HttpKernel\Bundle\Bundle), not a Laravel package. It ships a DI extension and a config tree, not a ServiceProvider or a Facade. There is no bundle kernel in Laravel to register it with, so day-to-day "use" in a Laravel app means reading and lifting its wrapper code, not requiring it.

  1. Inspect the constraints before touching composer.

    composer show kj/payment-stripe-bundle --all
    

    Check the require block. Expect a pinned, old stripe/stripe-php (~3/~4/~5) and possibly symfony/framework-bundle: ~2.8|~3.0. On PHP 8.1+ resolution will likely fail, or succeed by dragging an EOL Stripe SDK into your revenue path. Either outcome decides your approach.

  2. Read the wiring, not the README. The entire API surface is three files:

    • DependencyInjection/Configuration.php — the config tree, keys, and defaults
    • DependencyInjection/*Extension.php — how parameters (e.g. %kj_payment_stripe.api_key%) bind into services
    • Resources/config/services.yml — the service ID you would otherwise inject

    Whatever that service wraps — a configured Stripe client, a charge helper — is the thing you are re-implementing.

  3. First use case: set a secret key, create a Charge, read the failure. If the bundle (or extracted class) cannot cleanly do a single charge in isolation, it will not do refunds, webhooks, or SCA either.

Pragmatic path for a Laravel app: install the supported SDK and own the thin gateway yourself.

composer require stripe/stripe-php
// config/services.php
'stripe' => [
    'key'            => env('STRIPE_KEY'),
    'secret'         => env('STRIPE_SECRET'),
    'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
],
final class StripeGateway
{
    public function __construct(private readonly StripeClient $client) {}

    public function charge(int $amountCents, string $currency, string $token, array $meta = []): Charge
    {
        return $this->client->charges->create([
            'amount'   => $amountCents,
            'currency' => $currency,
            'source'   => $token,
            'metadata' => $meta,
        ], ['idempotency_key' => $meta['idempotency_key'] ?? Str::uuid()->toString()]);
    }
}

The bundle's only value-add over this is ~50 lines of config plumbing you would write anyway.

Implementation Patterns

Vendor, don't require. If you are porting behaviour out of an existing Symfony app, copy the service class into app/Services/ and treat it as vendored source. You inherit the naming and the call shapes without inheriting the abandoned dependency tree.

Keep payment logic behind an internal interface. The single durable architectural decision is reversibility:

interface PaymentGateway
{
    public function charge(int $amountCents, string $currency, string $token, array $meta = []): Charge;
    public function refund(string $chargeId, ?int $amountCents = null): Refund;
    public function handleWebhook(Request $request): WebhookEvent;
}

Call sites depend on PaymentGateway, never on a Stripe class or a bundle service ID. When you later swap implementations, call sites never learn the bundle went away.

Strangler pattern for existing code. If legacy Symfony code already calls the bundle's service, put the interface in front of existing call sites first, then swap the implementation underneath. This is the only safe sequencing.

Always pass an idempotency key on charge and refund calls. Derive it from your own domain (order ID, attempt counter), not a random per-request UUID, or retries still duplicate.

Webhook handling is a route, not a controller action you forget about. Verify signatures with the raw request body — Laravel's parsed payload will not match. Reconcile webhook events against your own persisted charge records rather than trusting the event as the source of truth.

Config plumbing when porting the bundle's wiring. Replace %kj_payment_stripe.api_key%-style parameters with config('services.stripe.secret'), and replace the bundle service ID binding with a container binding:

$this->app->singleton(StripeClient::class, fn () => new StripeClient(config('services.stripe.secret')));
$this->app->bind(PaymentGateway::class, StripeGateway::class);

Use the bundle as a reference for naming conventions only. Its charge/refund/callback method names are the one thing worth copying, because they probably match the domain language the rest of the KJ bundle family uses.

Gotchas and Tips

  • The Bundle suffix is the tell. No ServiceProvider, no publishable config, no facade. Any tutorial implying otherwise is describing a different package. If a colleague asks you to composer require it into Laravel, stop them at the require block.
  • Silent failure is the worst property here. The three highest-impact defects in this class of wrapper — missing SCA/PaymentIntents, missing webhook signature verification, missing idempotency keys — all fail quietly as "customers stopped paying" or "revenue doesn't reconcile," not as exceptions.
  • Check the pinned Stripe API version explicitly. Stripe sunsets API versions; an old wrapper can break at the transport or version-pinning layer with no code change on your side. Document the version you target as a decision, not a default.
  • Secrets in YAML is a smell the bundle encourages. If the DI config puts API keys in a committed parameters.yml, rotate anything that has been in version control.
  • Composer conflicts are the early warning, not a nuisance to work around. Force-installing against PHP 8 or Guzzle 7 constraints means you have adopted an unmaintained wrapper around a deprecated SDK. Do not fork it to fix this — you take on the maintenance without the maintainer.
  • Debugging checklist when something "works in test but not live": confirm the API version, confirm whether webhook signature verification exists at all, confirm retries carry idempotency keys, and confirm the key in use is the live secret and not a test key masked by config caching.
  • Extension point you actually want: your PaymentGateway interface, not the bundle's service definitions. Everything the bundle offers in the way of extensibility is Symfony container machinery that will not survive a framework migration.
  • Zero stars, zero dependents, zero maintainer. Bus factor is one and the one is absent. Budget for owning every line from day one, and prefer stripe/stripe-php or Cashier with your own wrapper over treating this as an upstream dependency.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums