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

Bundle Stripe Laravel Package

serendipity_hq/bundle-stripe

A Symfony bundle for integrating Stripe payments, offering configuration and services to work with charges, customers, subscriptions, and webhooks via Stripe's PHP SDK. Helps wire Stripe into Symfony applications with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

This bundle is an archived Symfony wrapper around Stripe's PHP SDK from the pre-PaymentIntents era (Stripe API ~2017–2018). It is safe to read its source for historical patterns, but treat it as deprecated. Before committing, sanity-check whether a current stripe/stripe-php + a thin Symfony service is a better fit — for most greenfield apps it is.

Install and register

composer require serendipity_hq/bundle-stripe

For Symfony 4+/Flex era, register the bundle manually in config/bundles.php (it predates recipes so there is no auto-generated config):

SerendipityHQ\Bundle\StripeBundle\SerendipityHQStripeBundle::class => ['all' => true],

Configure

Create config/packages/serendipity_hq_stripe.yaml with your keys pulled from env vars (never commit secrets):

serendipity_hq_stripe:
    api:
        public_key: '%env(STRIPE_PUBLIC_KEY)%'
        secret_key: '%env(STRIPE_SECRET_KEY)%'

First place to look

Because there is no maintained documentation, run:

php bin/console debug:config serendipity_hq_stripe
php bin/console debug:container stripe

Confirm the service ID that resolves to the Stripe API client (typically serendipity_hq.stripe.api or similar — the exact ID varies by version, which is itself a sign of how undocumented this package is). Autowiring by type is often unreliable here because the class name changed across Stripe SDK major versions, so wire by service ID.

First use case

The package was designed for the Charge API. In a controller:

use SerendipityHQ\Bundle\StripeBundle\Service\StripeApi;
use Stripe\Charge;

class CheckoutController
{
    public function __construct(private StripeApi $stripe) {}

    public function charge(int $amountCents, string $token): Charge
    {
        return Charge::create([
            'amount'   => $amountCents,
            'currency' => 'eur',
            'source'   => $token,
            'description' => 'Order #1234',
        ]);
    }
}

Note that this calls the static Stripe\Charge::create — the bundle's job is mostly configuring Stripe::setApiKey() and exposing the client, so most of your code will still touch the underlying Stripe SDK directly.

Implementation Patterns

Centralize key handling behind the bundle service. Resist setting \Stripe\Stripe::setApiKey() in controllers. Let the bundle do it once at container boot, and inject the API service everywhere else. If you find yourself adding a second Stripe::setApiKey call anywhere, the bundle isn't wired correctly.

Separate environments by config file, not by code. Maintain serendipity_hq_stripe.yaml with env-var placeholders, and set STRIPE_SECRET_KEY per environment (.env.test, CI secrets, etc.). Never branch on APP_ENV inside payment code.

Wrap charges in a domain service. Create an App\Payment\StripeGateway that depends on the bundle's API service and returns your own DTOs (ChargeResult, WebhookEvent), not raw Stripe objects. This is the single most important pattern for making an eventual migration away from this archived bundle cheap — swap the gateway implementation, leave callers untouched.

final class StripeGateway
{
    public function __construct(private StripeApi $stripe, private LoggerInterface $logger) {}

    public function charge(int $cents, string $token, array $meta = []): ChargeResult
    {
        try {
            $charge = Charge::create([
                'amount' => $cents, 'currency' => 'eur',
                'source' => $token, 'metadata' => $meta,
            ]);
            return ChargeResult::success($charge->id);
        } catch (\Stripe\Exception\CardException $e) {
            $this->logger->warning('Stripe card error', ['code' => $e->getStripeCode()]);
            return ChargeResult::failure($e->getMessage());
        }
    }
}

Webhook controller. You'll typically build this yourself; the bundle's event dispatching (where present) is minimal and inconsistent across versions. Verify signatures with \Stripe\Webhook::constructEvent($payload, $sigHeader, $secret), dispatch to a Symfony Messenger bus or an internal event dispatcher, and return 200 quickly. Do the actual work asynchronously.

$event = Webhook::constructEvent($payload, $sigHeader, $endpointSecret);
$this->bus->dispatch(new StripeWebhookReceived($event->id, $event->type, $event->data->object->toArray()));
return new Response('', 200);

Testing with the Symfony kernel. Because everything runs through static Stripe\* calls, unit tests can't mock the transport easily. Two workable approaches: (a) put your StripeGateway behind an interface and mock that; (b) use Stripe\ApiRequestor::setHttpClient(new \Stripe\HttpClient\CurlClient(...)) with a fake in integration tests. Option (a) is cleaner and worth the extra class.

Gotchas and Tips

  • Archived status is the top gotcha. The repo has been dormant since the Charge API era. It will not keep up with Stripe SDK major versions, PaymentIntents/SCA, StripeClient (the new object-based client), or the modern exception hierarchy (\Stripe\Exception\ApiErrorException vs. the old \Stripe\Error\*). Pin stripe/stripe-php in composer.json yourself and read its changelog before every bump — the bundle does not constrain it for you.

  • SCA / PSD2 compliance breakage. Charges with source will be rejected for European cards under 3DS regulation. If you need real EU payments, the Charge API path in this bundle won't cut it — you need PaymentIntents, which means bypassing the bundle's helpers and calling PaymentIntent::create yourself, or migrating off the bundle.

  • Amounts are in the smallest currency unit. A €10.00 charge is 1000, not 10.0. This trips up every new contributor; put the conversion in one place (a Money value object or the gateway) and never accept floats from controllers.

  • Idempotency is your responsibility. The bundle predates first-class idempotency ergonomics. Pass ['idempotency_key' => $orderUuid] as the second argument to ::create for anything retried (charges, refunds, payouts). Without it, network retries double-charge customers.

  • Webhook signature secret is separate from the API secret key. It's set in the Stripe dashboard per endpoint, not in your API keys. Store it as STRIPE_WEBHOOK_SECRET. Use the raw request body ($request->getContent()), not $request->request — parsing JSON before verification invalidates the signature.

  • Service IDs are unstable across versions. If autowiring fails, drop to php bin/console debug:container --show-private stripe or debug:container SerendipityHQ\\Bundle\\StripeBundle and grep the class list. Wire by id in services.yaml rather than type-hinting the API class, which has changed names repeatedly.

  • Config key drift between bundle releases. The YAML tree (serendipity_hq_stripe.api.secret_key vs. older serendipity_hq_stripe.secret_key shapes) changed across minor versions during its life. Always validate against debug:config serendipity_hq_stripe after upgrading.

  • Extension point worth knowing. If the bundle exposes an event subscriber hook for webhooks, prefer dispatching your own Symfony event from the controller instead — it keeps the integration testable and lets you swap to stripe/stripe-php directly later without touching subscribers.

  • Migration plan. Because the bundle is essentially a thin key-configuration shim, migrating is small: replace its bootstrap with new \Stripe\StripeClient($secret) in a Symfony service, inject that, and rewrite any static Charge::create calls to $client->charges->create. Keep your StripeGateway boundary intact and the rest of your app won't notice.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo