Stripe Connect marketplace billing: four hard-to-reverse decisions
Charge type, fee ownership, payout policy and ledger strategy are cheap to choose and expensive to undo. What we picked on a coaching platform, and why.
Topic: engineering
Four decisions in a marketplace billing design are cheap to make and expensive to undo. They are which charge type you use, who carries the processing cost and where your platform fee comes out, when sellers get paid, and whether your database or Stripe's is the record of what happened. Nearly everything else can be changed on a Tuesday afternoon.
Marketplace billing is the set of rules deciding who the customer pays, who carries the processing cost, when the seller is paid, and which system holds the record of it.
Every Stripe page linked here was checked on 28 October 2026.
We made all four on a coaching platform where practitioners are paid per session.
Why these four resist reversal
Ordinary software decisions are reversible because the only thing at stake is code. These four produce artefacts you cannot rewrite: money that moved, accounts onboarded under a particular arrangement, statements customers have already read, and a year of records an accountant will want to reproduce.
Charge type is the worst of them. Connected accounts are onboarded against a set of capabilities, historical charges live in whichever account processed them, and a customer's bank statement carries whoever was the settlement merchant at the time. Changing it does not migrate any of that. You run both models side by side, with two code paths and two reporting shapes, until the old charges age out of anything anyone asks about.
Fee ownership is reversible in code and not in commerce. The number a seller nets is the number they budget against, and moving it is a conversation with every seller you have.
Payout policy is the one sellers notice fastest. Lengthening a hold is felt as a cash flow cut and generates support tickets within a day.
Ledger strategy is technically the easiest to change and practically the hardest, because you cannot backfill intent. If your rows never recorded which delivered thing a transfer paid for, no migration invents that later.
Make them deliberately and write down why, so you know which ones you have signed up to for the life of the product.
Decision one: who owns the customer relationship
Stripe offers three shapes, and its guide to charge types is the place to compare them properly. Direct charges put the payment on the connected account, which suits software where sellers transact with their own customers and your platform is mostly invisible. Destination charges put the charge on your platform and immediately move funds to the connected account. Separate charges and transfers also take the payment on your platform but decouple the transfers entirely. You need them when one payment splits across several sellers, or when you cannot yet say who should be paid.
The deciding question is who the customer thinks they bought from. If they would email your support address about a bad experience, the charge belongs on your platform. If they would email the seller, and the seller has their own brand and their own dashboard, direct charges are honest about that. Stripe's on_behalf_of parameter sits in between: on a destination charge it sets the settlement merchant, which changes whose statement descriptor and address the customer sees, and the settlement currency. The full comparison, including chargeback control, is in the piece on destination charges versus separate charges and transfers.
On the coaching platform we chose separate charges and transfers, and the reason was not flexibility. It was that money arrives weeks before anybody has earned it. A destination charge moves the seller's share at checkout, which is precisely what we did not want. Every mechanism further down this page exists to undo that timing.
Reversing that decision later would mean re-onboarding connected accounts against different capabilities and rewriting every refund path. A year of charges would sit in one account and the new ones in another. We would do it only if the business model changed.
Decision two: who pays Stripe's fee, and where yours comes out
Two separate questions get muddled here. The first is who carries Stripe's processing cost. With direct charges, you choose when you create the account: Stripe can take its fee from the connected account or bill your platform. With destination charges and with separate charges and transfers, Stripe debits the platform account for fees, refunds and chargebacks, which the destination charges documentation states in as many words. Choosing an indirect charge type means choosing to be the fee payer, whatever your commercial arrangement with sellers says.
The second question is where your own cut comes out. With destination and direct charges you can set an application fee at charge time and Stripe splits it for you. With separate charges and transfers there is no application fee at all: you decide the transfer amount, and your cut is simply the part you did not transfer.
We took the second route deliberately. The coaching platform takes no application fee at charge time. The platform's share is computed per payable unit and folded into each transfer, with every split checked against what came in before a row is written.
That arrangement earns its keep on refunds. Because the fee is expressed per unit, a partial refund can return the platform's own share of the refunded units. That is the policy the product wanted: the platform eats its cut when a client cancels. Doing that with an application fee taken at charge time is possible but noticeably more fiddly.
Support staff can override the fee for an individual account. The override is clamped in one direction only, downwards, so a mistyped value is pulled back to the standard rate before it can become a multiple of what somebody agreed to.
Decision three: when the seller gets the money
The default is that you do not decide much. Funds land in a connected account's balance and Stripe pays them out on a schedule, which you can configure per account: the interval, the days, and how long charges take to become available. Stripe's guide to managing the payout schedule covers the settings, including a settlement delay that can be extended for accounts where your platform owns dispute liability. You can also switch an account to manual payouts and trigger them yourself. That is the lightest version of holding funds back. Stripe requires the funds to go out within a period set by the business's country: 90 days for a UK business.
The alternative is to hold the money on the platform and release it against delivery, which is what we built. Stripe's own guidance is to hold funds only for a clear purpose, such as waiting for a service to be completed. Every payable unit gets a pending transfer row with a release date that starts empty. It is filled in on delivery: a one-off deliverable's transfer becomes due when the document reaches the client, and a session's transfer becomes due 24 hours after that session finishes. A background process picks up whatever has come due since it last looked.
Refunding money you still hold is a database update and a Stripe refund. Refunding money you already paid out is a negotiation. The seller has been paid, has possibly spent it, and now either gives it back or you absorb it. Stripe's mechanics for connected account balances going negative are the least pleasant corner of Connect. We wrote up what happens in refunds, chargebacks and negative balances in a marketplace, and the summary is that every option is worse than not being in that position.
Sellers wait longer for money than they would on a plain destination charge, and any hold you introduce is felt immediately. Lengthening one later is the change most likely to produce an angry email, so put the policy in the seller terms in the first week, before somebody gets burned.
Decision four: whose records are the record
Every marketplace starts by treating the Stripe dashboard as the source of truth, because for the first year it more or less is. Charges are visible, payouts are listed, and anything a founder needs is two clicks away. The month it stops working is the month somebody has to produce accounts.
The dashboard records what happened to money. It cannot tell you what the money was for, such as which delivered unit a transfer paid for or which sessions a client has left. It also cannot show what you owe sellers who have not been paid yet, or why a particular payout is smaller than the invoices it covers.
Those answers only exist if you wrote your own rows at the moment each movement was decided, which is the argument set out in reconciling Stripe payouts with your own ledger. Revenue recognition is one of the things buyers test, so the same rows answer a diligence question that turns up later. They sit alongside the other technical due diligence red flags worth clearing early.
The version of this decision we would defend hardest is the unglamorous one. It is one row per movement of money, append-only, written when the movement is decided and before it settles, with Stripe's own identifiers on every row. The coaching platform computes refunds from unconsumed transfer rows, and where anything else in the system says something different, the transfer rows are the ones we believe. They are the only rows in the system written because money moved.
The same reasoning led to a decision that is harder to defend. We did not use Stripe's billing product for subscriptions, because the unit of value is a session credit consumed one at a time rather than continuous access to something. So renewals are off-session payments against a saved card, on top of an in-house credit ledger of typed events. That cost us proration, a dunning ladder, smart retries and tax automation, all of which would have arrived free with the hosted product, and we knew it when we chose. A failed payment sets a status and appears on a practitioner's list of things needing attention, which a human then chases.
The four decisions in one table
| Decision | The realistic options | What we chose | What reversing it costs |
|---|---|---|---|
| Charge type | Direct, destination, or separate charges and transfers | Separate charges and transfers, because the platform holds money it has not yet earned | Re-onboarding connected accounts, rewriting refund paths, and two reporting shapes until old charges age out |
| Fee ownership | Your choice of payer with direct charges, or platform pays (destination and separate) | Platform pays Stripe; the platform's own share is folded into each transfer rather than taken at checkout | Cheap in code, expensive in commerce: the seller's net is the number they budget against |
| Payout policy | Stripe's automatic schedule, adjusted settlement delay, manual payouts, or release against delivery | Release against delivery, with a transfer row per payable unit and a scheduler pushing due rows | Sellers feel any lengthened hold within a day, so it belongs in the terms from week one |
| Ledger strategy | Stripe's records, or your own row per money movement | Own ledger, append-only, written when the movement is decided | You cannot backfill intent, so the gap is permanent for every month you did not write rows |
Read down the third column and the pattern is that we chose the option that keeps money and information on the platform for longer, and paid for it in machinery. That is the right trade when the platform holds funds it has not yet earned. It is the wrong trade for a marketplace where the seller is owed the money the moment the customer pays, and we would not build any of this for that.
The four decisions, in the order the post takes them. The dark station is the ledger, the decision the post would defend hardest.
The gap to look for first
Writing the failure paths down is how you find the holes, and one is worth looking for in any build shaped like this. If a refund call fails but the cancellation and the voiding of the pending transfers still go ahead, a client ends up cancelled without their money back, with nothing but a log line saying so. The window is small, the fix is making the whole thing a single retriable unit of work, and it stays invisible until you write out what happens when each external call fails.
What connects all four decisions is a preference for a fine-grained record: assertions that throw instead of logging, a fee expressed per unit, a row for every obligation written before any money moves. Nothing there is clever. It is most of the difference between a billing system you can explain to an accountant and one you have to reconstruct in front of them. We build apps and SaaS platforms where money moves between parties, and that is the part we would not compromise on.
Common questions
Should a marketplace use destination charges or separate charges and transfers?
Destination charges when each payment goes to one seller and you want Stripe to move the funds at checkout. Separate charges and transfers when a payment splits across several sellers, when you do not yet know who to pay, or when you are holding money until a service is delivered.
Who pays the Stripe fee in a marketplace?
It follows the charge type. With direct charges you pick, when the account is created, whether the connected account or your platform covers Stripe's fee. Destination charges and separate charges and transfers both leave the platform paying for fees, refunds and chargebacks, whatever your seller agreement says about who bears them commercially.
Can we change our Connect charge type later?
Technically yes, practically it is a migration. Connected accounts are onboarded against particular capabilities, old charges stay with the account that processed them, and refunds and disputes on old payments still follow the old model. Expect to run both paths side by side during the changeover.
Should we hold funds until the service is delivered?
Hold them if refunds before delivery are a normal event. Holding costs sellers cash flow and costs you a release mechanism to build. It buys you the ability to refund without asking anybody to return money they have already received and possibly spent.
Do we need our own ledger if Stripe already records everything?
Yes. Stripe records what happened to money. Your ledger records what it was for, which delivered thing each transfer covers, and what you owe sellers who have not been paid yet. Those columns cannot be reconstructed later from payment records alone.
Tell us what you're building.
Tell us about your product. On a 30-minute call we'll ask who it is for and what it has to do by when, then tell you what a first release would take.
More on AI product developmentWhat happens after you get in touch
We reply within one working day
By email, to arrange a time for the call.
A free 30-minute call
With a senior engineer, about what you are building or deciding.
The fee in writing first
If a first step is worth taking, you get its exact fee in writing before any work starts.