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

Laravel Cashier Stripe Connect Laravel Package

expdev07/laravel-cashier-stripe-connect

Laravel Cashier Stripe Connect extends Cashier with Stripe Connect support, letting platforms manage connected accounts, charge customers, and handle payouts and transfers—ideal for marketplaces, SaaS platforms, and multi-vendor apps built on Laravel.

View on GitHub
Deep Wiki
Context7

Getting Started

This package layers Stripe Connect semantics onto Cashier, so the fastest path is to already have Cashier working for a single-account setup, then add the Connect dimension.

Prerequisites

  • Laravel 9 (per the v1.2.0 release note; verify Laravel 10/11 support before relying on it).
  • PHP 8.0+.
  • An existing laravel/cashier install with a working billable model and webhook endpoint.
  • A Stripe platform account with Connect enabled, and test-mode connected accounts created in the dashboard.

Install and wire up

composer require expdev07/laravel-cashier-stripe-connect

Because the package is Cashier-adjacent, expect to publish a config file and possibly migrations:

php artisan vendor:publish --provider="Expdev07\CashierConnect\CashierConnectServiceProvider"

Confirm the exact provider namespace and published tags by reading the package's src/ directory and composer.json first — the README is thin and the package has had no release since 2022, so the source is the authoritative reference.

Where to look first, in order

  1. composer.json — the illuminate/* and laravel/cashier constraints decide whether you can even install it on your framework version.
  2. The service provider in src/ — which Cashier classes it extends or decorates (likely Billable, Subscription, and a payment/charge builder).
  3. The published config — fee defaults, currency, and any stripe/connect key wiring.
  4. The package's own tests (if any) — the closest thing to API documentation you will get.

First use case: a destination charge with a platform fee

The canonical marketplace flow is: a buyer pays your platform, funds are routed to the seller's connected account, and your platform keeps an application fee. In Cashier-Connect terms this is roughly:

// Verify exact method names against src/ before shipping.
$user->charge($amountInCents, $paymentMethod, [
    'destination' => $seller->stripe_account_id,
    'application_fee_amount' => (int) round($amountInCents * 0.10),
]);

Set this up in test mode first, confirm the transfer and fee appear on both the platform and connected-account sides of the Stripe dashboard, then build the UI around connected-account onboarding (account_links) before worrying about subscriptions.

Implementation Patterns

Connected-account onboarding is your first real feature, not charging. Stripe requires each seller to complete KYC via a hosted onboarding flow (AccountLink) before they can receive funds. Treat account creation and onboarding as a separate domain from billing:

  • Create a StripeAccount (Express or Standard) per seller, store the acct_... ID on your users/sellers table.
  • Generate an account_link on demand and redirect; never cache the URL (they expire in minutes).
  • Gate charging and payouts on charges_enabled and payouts_enabled from the account object, not on "onboarding completed" flags you invent.

Two charge architectures — pick one and standardize.

Pattern When to use Trade-off
Destination charges (platform is merchant of record) Simple marketplace; you set the price, want one dispute surface Disputes and refunds land on the platform, not the seller
Separate charges and transfers Sellers set their own prices; you need to decouple charge from payout Two API objects to reconcile, transfer timing is your problem

Wrap whichever you choose in a single application-level service so the rest of the codebase never calls Stripe directly:

class MarketplaceCharge
{
    public function __construct(private FeeCalculator $fees) {}

    public function charge(Billable $buyer, Billable $seller, int $amount, string $pm): Payment
    {
        $fee = $this->fees->for($seller, $amount);

        return $buyer->charge($amount, $pm, [
            'destination' => $seller->stripe_account_id,
            'application_fee_amount' => $fee,
        ]);
    }
}

Application fees belong behind a calculator, not inline * 0.10. Fees change (tiered rates, per-category, minimums, currency-specific rounding). A FeeCalculator object keeps the policy testable and lets you introduce per-seller overrides without touching charge sites.

Subscriptions need a decision up front. Cashier's subscription lifecycle is built around a single merchant. On a marketplace you are usually subscribing the buyer to the platform while routing revenue to sellers — or you are running subscriptions on behalf of a connected account (stripe_account header on subscription creation). Confirm which the package supports by reading its Subscription implementation; the two are not interchangeable, and invoices, proration, and webhooks differ.

Webhooks are where reliability lives. You need two streams:

  • Platform events (invoice.paid, charge.refunded, customer.subscription.updated) at your normal Cashier endpoint.
  • Connect events (account.updated, transfer.created, payout.paid, charge.dispute.created) — these must be routed with the connected-account context so you can update the right seller's state.
// routes/api.php — separate endpoints, separate secrets if configured.
Route::post('/stripe/webhook', StripeWebhookController::class);
Route::post('/stripe/connect/webhook', StripeConnectWebhookController::class);

Reconcile, don't assume. Persist every charge, fee, and transfer to your own tables with the Stripe IDs. Your database is what your finance team queries; Stripe is what you audit against. A payments table with stripe_charge_id, stripe_transfer_id, application_fee_amount, and connected_account_id will save you weeks at quarter-end.

Integration tip: build the "seller dashboard" view from webhook state, not API polling. Cache charges_enabled, balance, and payout status on the seller record, updated by account.updated and payout.* events. Polling the Stripe API per page load will rate-limit you at scale.

Gotchas and Tips

The package is frozen at 2022. Last release was April 2022 (Laravel 9). Confirm Laravel 10/11 support before assuming it installs, and expect the composer.json constraints — not a runtime error — to be your first blocker. Pin the version explicitly ("expdev07/laravel-cashier-stripe-connect": "1.2.0") so a future patch cannot silently change behavior.

The documentation is sparse. Method names in this assessment are illustrative. Before writing application code, grep src/ for the actual public API — particularly whether charges are exposed as $billable->charge(...), a dedicated Payment builder, or a trait method. Budget an hour for a source read; it will save a day of debugging.

Platform vs connected account keys. Almost every confusing 404/No such charge in Connect work is a request made with the platform key that should have used the Stripe-Account header (or vice versa). Cashier-Connect is supposed to handle this for you — verify it does by watching the Stripe-Account header in a test-mode request.

Application-fee arithmetic is in the smallest currency unit and currency-specific. Don't round with floats. Use integer minor units end-to-end and confirm your fee calculator handles zero-decimal currencies (JPY, KRW) and Stripe's rounding rules. A one-cent fee mismatch on refunds causes real reconciliation headaches.

Refunds and disputes need explicit handling. In destination-charge mode, refunding a charge by default does not claw back the application fee or the transfer. Set refund_application_fee and reverse_transfer deliberately — the defaults will surprise you.

Webhook signatures differ per endpoint. Connect webhook secrets are not the same as your platform endpoint secret. Read config/cashier.php and the package config carefully; a misconfigured secret fails silently if you swallow exceptions.

Idempotency is on you. Pass an Idempotency-Key (e.g., your order UUID) on every create call. Retries on timeouts are common, and duplicate charges are the failure mode that loses customers.

Test-mode friction. Connect testing needs multiple test accounts and, for Express onboarding, stripe listen --forward-connect-to. If your local environment already forwards platform events, add a second forwarder for Connect — otherwise account.updated never arrives and sellers stay stuck in "pending."

Escape hatch. For edge cases the package doesn't cover (treasury, multi-currency payouts, complex fee splits), you can always drop to Stripe\StripeClient for that specific operation and keep Cashier-Connect for the CRUD-heavy parts. Don't fight the abstraction; you are not on the maintainer's support contract.

Ownership. Assume a single-maintainer, community project with no SLA. Name an internal owner, keep a quarterly check on framework compatibility, and pre-identify whether the alternative is raw stripe/stripe-php plus a thin internal wrapper. That decision will eventually be forced; better to have it scoped than to discover it mid-upgrade.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin