Destination charges or separate transfers in Stripe Connect
Destination charges fit most marketplaces and the platform pays the Stripe fee. Separate charges and transfers buy flexibility, and a ledger you now own.
Pick destination charges when your customer buys from your marketplace and one seller fulfils the order. Pick separate charges and transfers when a single payment has to fund several sellers, or when you cannot say who gets paid at the moment the card is charged. Either way the platform pays the Stripe fee. The rest of the decision is about who owns the customer.
A destination charge is a payment taken on your platform's account that Stripe transfers to the seller's connected account in the same operation.
Who the customer thinks they are paying
Stripe's three charge types are three answers to one question: whose business is this payment? With direct charges the connected account is the business of record, the money lands in their balance, and Stripe's own documentation describes the customers as "often unaware of your platform's existence". The buyer knows the shop. Your platform is plumbing underneath it.
Destination charges and separate charges and transfers both put the payment on your platform account and make the platform the business of record. Your name is on the statement unless you say otherwise. Your inbox gets the "where is my order" email. So the decision is less about payments than about who has to answer the phone. If a customer unhappy with a seller would reasonably come to you, be the merchant of record and accept the operational load that follows.
The mistake worth avoiding is a mismatch. The customer relationship obviously belongs to the platform, and the payment says otherwise. Then every refund is a negotiation with a seller who has already been paid, and the person doing the negotiating is a support agent with no position to argue from and no button to press.
The platform pays the Stripe fee, and that changes the arithmetic
Worth saying plainly, because it catches people out: on destination charges and on separate charges and transfers, Stripe debits the processing fee from the platform's balance, not the seller's. Only direct charges give you a choice about which account the fee comes out of.
So your commission is a gross number. Whatever you take absorbs card processing before it covers hosting and support. A take rate that modelled fine gets thinner once the fee lands on your side of the line, and the time to find that out is before you publish a rate card.
On a destination charge you can express your cut two ways, and they are not equivalent from the seller's point of view. application_fee_amount sends the whole charge to the connected account and then pulls your fee back, creating an application fee object as a record; the seller sees the transaction total and your fee as separate lines. transfer_data[amount] sends only the net figure you calculated, and the connected account sees just what arrived.
That is a product decision wearing an API parameter's clothes. If your pitch to sellers is a published percentage, use the application fee and let them audit you against it. If your pricing is a negotiated net rate, the transfer amount is simpler and starts fewer arguments.
What on_behalf_of changes, and what it does not
Setting on_behalf_of to a connected account makes that account the settlement merchant for the payment. Per Stripe's charge documentation, the charge settles in the connected account's country and settlement currency, uses the fee structure for that country, and puts the connected account's statement descriptor in front of the customer. Where the two accounts sit in different countries, the connected account's address and phone number appear on the statement as well. Across regions, you have no choice: it has to be set.
What it does not change is who pays for a dispute. For destination charges, with or without on_behalf_of, Stripe debits dispute amounts and fees from the platform account. That gets misread often, and expensively so if you have designed a risk process around the opposite. Dispute exposure follows the charge type. Recovering from the seller afterwards is a separate call with its own failure modes.
Set it when the seller is the named business the customer believes they bought from, or when a UK platform pays out sellers settling elsewhere. Leave it off when your marketplace brand is what the customer recognises. A name nobody knows on a card statement is one of the cheapest ways to earn a chargeback.
When separate charges and transfers earn the complexity
Stripe's own list of use cases makes a decent filter. Reach for separate charges and transfers when:
- a single transaction can involve several connected accounts
- you need to create the charge before you know which account gets paid
- you need to create the charge before you are in a position to transfer the funds at all
The last one catches teams out. A payment for something not yet delivered is money you are holding, not money you have routed. Destination charges move the seller's share the instant the charge is captured, into the connected account's pending balance. If your unit of value gets delivered days or weeks later, you have paid a seller for work they have not done, and your only route back is a reversal that can fail.
The mechanic that makes the pattern workable is source_transaction. Tie the transfer to the originating charge and, in Stripe's words, "the transfer request automatically succeeds. However, we don't execute the transfer until the funds from that charge are available in the platform account". You stop juggling your platform's available balance to make a payout land. A transfer must not exceed its source charge, several transfers can share one, and you cannot change the attribute after creation.
One caveat bites teams taking bank debits. Unlike destination charges, Stripe does not automatically reverse a transfer if an asynchronous payment later fails. With ACH or SEPA Debit, wait for the charge to succeed before transferring anything, or your platform balance covers the difference.
The two options side by side
| Destination charges | Separate charges and transfers | |
|---|---|---|
| Business of record | Platform, or the seller with on_behalf_of | Platform |
| Where money lands | Platform account, seller's share moved at capture | Platform account, until you move it |
| Who pays the Stripe fee | Platform | Platform |
| Sellers per payment | One | Several |
| Seller known at charge time | Required | Not required |
| Disputes debited from | Platform | Platform |
| Refund after the seller is paid | reverse_transfer on the refund | An explicit transfer reversal |
| What you build | Fee calculation | Fee calculation, a payout ledger, release logic, a scheduler, reconciliation |
| Fits when | One seller fulfils one order | Allocation is delayed or split |
Most of that table reads the same in both columns. One row does the work: whether you know who gets paid at the moment the card is charged. If you do, destination charges hand you the flow. If you do not, you are building a ledger.
Why we chose separate transfers on a coaching platform
On a coaching platform we built, money is earned per delivered session rather than at the point of sale. A client buys a block of sessions up front. The coach earns each session's share once that session has happened. If the client stops after three, the rest goes back, and the platform's own cut on the unused sessions goes back with it.
Destination charges cannot express that. They move the seller's share at capture, and everything afterwards is a reversal. So the whole payment lands on the platform account with no application fee set at charge time, and our ledger gets a pending transfer row for every payable unit: one per invoice for a programme, one per session for a class subscription. Each row carries a release date that starts empty and is filled in only once that unit has been delivered. Programme rows get theirs when the document is sent. Session rows get theirs a fixed delay after that specific session completes. A scheduler process wakes on a short cycle, walks each tenant with cursor pagination, and pushes out the rows that are due, with source_transaction set to the originating charge so funds draw from that charge rather than from the platform balance.
The part engineers enjoy is the splitting. Amounts divide evenly with the remainder landing on the last slice, applied independently to the payout and to the fee, and then three guardrails assert that the slice count, the amount total and the fee total reconcile before anything is persisted. Each one logs and throws. A rounding bug cannot quietly underpay a coach, because the code refuses to write a split that does not add up.
A smaller one in the same spirit: support staff can set a per-account fee override, and the value is clamped so it can only ever move the fee down. A decimal point in the wrong place charges the standard rate instead of a multiple of it. One line, and a whole category of support incident stops existing.
Would we do it again? For that product, yes. If the unit of value had been one item shipped by one seller, we would have used destination charges and written a fraction of the code. Separate transfers are not the better architecture. They are what you pay when delivery and payment happen on different days.
What separate transfers cost, itemised
Four things move onto your side of the line, permanently.
You own a ledger. Stripe knows what it transferred; only you know what each transfer was for. Every payout row has to name the thing it paid for, or month-end reconciliation turns into archaeology.
You own the release rule, which is business logic, will change, and belongs where a product person can read it.
You operate a scheduler, which means you own a process that can stop. We keep an invariant written down for exactly this: a "mark as done" path must write the in-progress status and let the completion consumer do the transition, because writing "completed" directly means credits and transfers silently never process. Nothing errors. Money just stops moving, and you find out from a seller.
And you own the disagreement. Your ledger and Stripe's will diverge eventually, so decide now which one wins. That answer is always worse made under pressure in month 13.
Individually these are small. Together they are a standing tax on the team, and the bill turns up after launch rather than during it, which is a different thing from work you can put in a sprint.
Common questions
Can I change charge type later?
Not cheaply. Historical charges keep their original shape, so for a long while you run both paths and reconcile two models of the same money. Budget for old payments, refunds against them, and reports that cover both. Treat the choice as close to permanent.
Who pays the Stripe fee in a marketplace?
Your platform, on destination charges and on separate charges and transfers. Stripe debits the fee from the platform balance in both, so your commission is a gross figure that absorbs processing before anything else. Direct charges are the only type where you choose which account carries the fee.
Does on_behalf_of move chargeback liability to the seller?
No. It makes the connected account the settlement merchant, which governs where the charge settles and whose descriptor the customer sees. For destination charges, with or without it, Stripe debits dispute amounts and fees from the platform account. Getting that money back from the seller is a separate transfer reversal.
Do I need separate transfers just to delay a payout?
Often not. Destination charges with a longer payout schedule on the connected account keep funds sitting in Stripe rather than reaching the seller's bank, which covers a delay measured in days. Separate transfers earn their keep when the decision about who gets paid, or whether anyone does, comes later.
Ready to build the thing?
Book a free 30-minute call. We'll dig into your idea, your stack and your timeline, and give you an honest read on what it will take to build and launch. You'll leave with a clearer plan whether or not you hire us.