The short answer to Stripe Connect direct charges vs destination charges is this: a direct charge is created on the seller's connected account, so by default the seller's balance receives the payment and absorbs refunds and disputes; a destination charge is created on your platform account and immediately transfers funds to one connected account, so by default your platform's balance carries Stripe fees, refunds and disputes. If one payment has to be split across several sellers, or you do not know the seller when you charge, neither fits cleanly, and the third model, separate charges and transfers, is usually the right one.

The rest of this guide explains each model in plain terms, shows where money settles and who carries which cost by default, and walks through the decision with worked examples: a service marketplace, a multi-vendor store and a vertical SaaS product. Every behavior described here is checked against Stripe's Connect charges documentation as of October 2026. Where the answer depends on your account configuration, country or capabilities, we say so, because it often does.

What are the three Stripe Connect charge types?

Stripe groups Connect payments into two categories. A direct charge is a payment made directly to a connected account. An indirect charge is a payment made to your platform, from which you then send the connected account its share. Indirect charges come in two forms, destination charges and separate charges and transfers, which differ only in how and when funds move from the platform to connected accounts.

Direct charges

Stripe direct charges are created on the connected account itself, usually by making the API call with the connected account's ID in the `Stripe-Account` header. The payment lands in the connected account's balance, not yours. You can add an `application_fee_amount`, which moves your cut to the platform balance when the payment is collected. Funds settle in the country of the connected account. Stripe's guidance is that this model suits businesses whose connected accounts transact directly with their own customers, often without those customers knowing the platform exists, such as a storefront builder or invoicing software.

Direct charges need the connected account to have the `card_payments` capability active. Stripe's documentation also notes that direct charges are not recommended for legacy v1 Express and Custom accounts; for those, it points to v2 accounts or to destination charges instead.

Destination charges

Stripe destination charges are created on your platform account with `transfer_data[destination]` set to one connected account. Stripe charges the customer on the platform and automatically transfers funds to that connected account. You decide whether all or part of the money moves, and whether to take an application fee. One charge, one customer, one seller. Stripe positions this model for marketplaces where customers transact with your platform for goods or services that connected accounts provide, such as a rental marketplace or a contractor-matching site.

Separate charges and transfers

Separate charges and transfers decouples the payment from the payout to sellers. You charge on the platform first, then create one or more Transfer objects to connected accounts, now or later, for amounts you decide. A `transfer_group` string ties the related objects together for your own reconciliation. Stripe describes this as the more complex integration and suggests using it only when the business genuinely needs it: one payment split across several parties, a charge created before the recipient is known, or transfers that must happen before or independently of the customer's payment.

Illustrative funds flow for each Connect charge type, with the default balance debited for refunds and disputes. Exact behavior depends on account configuration and country.

Stripe Connect direct charges vs destination charges: who carries what by default

The charge type decides which Stripe balance a payment lands in, and that balance is the one Stripe debits when money has to go back. Everything else follows from that. Here is the default behavior for each responsibility, as Stripe documents it.

Stripe processing fees

With direct charges, Stripe lets the platform choose whether Stripe debits its fees from the connected account or from the platform account. With accounts configured through controller properties, that choice is recorded on the account (`controller.fees.payer`); with legacy account types it was largely implied by the type. With destination charges and separate charges and transfers, Stripe fees are typically assessed on the platform under the platform's pricing. We will not quote numbers here, because fees depend on your agreement with Stripe, the country and the payment method; see Stripe's Connect pricing page and your own contract.

Refunds

For a direct charge, a refund is debited from the connected account's balance. If that balance cannot cover it, Stripe marks the refund `pending` and processes it once funds arrive. For a destination charge, the refund is debited from your platform balance, and by default the connected account keeps the money that was transferred to it. To pull that money back, you set `reverse_transfer: true` on the refund, which reverses the transfer in full or in proportion for a partial refund. Separately, `refund_application_fee: true` returns your application fee to the connected account; Stripe requires the transfer to be reversed if you refund the fee. For separate charges and transfers, refunding the charge has no effect on the transfers at all. Your code has to decide what each seller owes back and either reverse their transfers or reduce future ones.

Disputes and chargebacks

For direct charges, Stripe debits the disputed amount from the connected account's balance; the dispute fee may be billed to the platform or the connected account depending on configuration. For destination charges and separate charges and transfers, with or without `on_behalf_of`, Stripe debits the platform balance for the disputed amount and the fee. The platform can then try to recover funds by reversing the related transfer. Stripe's advice on timing is practical: if you accept the dispute, reverse straight away; if you challenge it, wait for the outcome and reverse only if you lose.

Negative balances

When a connected account's balance goes below zero, someone ultimately covers the shortfall. That is decided by account configuration rather than by the charge type alone. With controller properties, `controller.losses.payments` records whether the platform (`application`) or Stripe is responsible; in the Accounts v2 API the equivalent sits under the account's responsibilities. Stripe can also attempt to debit a connected account's external bank account when `debit_negative_balances` is enabled. Notably, Stripe recommends separate charges and transfers only for platforms that are responsible for their connected accounts' negative balances, which tells you how much risk that model concentrates on the platform.

Merchant of record and the statement descriptor

Stripe calls this the settlement merchant or business of record: whose details are used for the charge, including the name on the customer's card statement. With a direct charge, it is the connected account by default, which is why customers of a storefront builder see the store's name. With a destination charge or separate charges and transfers, the platform is the business of record unless you set `on_behalf_of`. This matters more than teams expect. A customer who does not recognize the name on their statement is more likely to dispute the charge, and on indirect charges that dispute lands on your balance.

Default responsibilities by charge type. Asterisk: the platform can recover funds by reversing transfers. Double asterisk: with on_behalf_of, the connected account's details are used.

How does the platform collect its fee?

Each model has its own mechanism, and they behave differently in reporting and refunds. Choosing the wrong one does not usually break payments; it breaks reconciliation, which is harder to notice and harder to fix later.

  • Application fee (direct or destination charges). You set `application_fee_amount` on the PaymentIntent. Stripe creates an Application Fee object linked to the charge, which gives you itemized fee reporting and lets the connected account see both the total and the fee in Stripe-hosted dashboards and components. The fee is capped at the charge amount and computed in the charge currency.
  • Transfer amount (destination charges). Instead of an application fee, you set `transfer_data[amount]` to the seller's share and keep the rest. There is no Application Fee object, and in Stripe-hosted dashboards the connected account only sees the amount transferred, not the full charge. Stripe itself suggests the application fee approach when you want simpler reporting.
  • Keeping the difference (separate charges and transfers). You charge the full amount, transfer each seller their share, and your fee is whatever remains. That is flexible, but your fee exists only as arithmetic in your own ledger, so your database must be the source of truth for who was owed what.

A useful rule: if a seller will ever ask “why did I receive this amount?”, prefer the mechanism that leaves an explicit, linked record in Stripe. That is usually the application fee. Below is a minimal destination charge with an application fee in Node.js. The amounts are illustrative and come from your own order and pricing logic, never from the client.

Destination charge with an application fee (Node.js)
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export async function createBookingPayment(order, seller) {
  return stripe.paymentIntents.create(
    {
      amount: order.totalCents, // computed on the server from the order
      currency: "usd",
      automatic_payment_methods: { enabled: true },
      application_fee_amount: order.platformFeeCents, // your pricing, not Stripe's
      transfer_data: { destination: seller.stripeAccountId },
      // on_behalf_of: seller.stripeAccountId, // only if the seller is business of record
      metadata: { order_id: order.id },
    },
    { idempotencyKey: "order_" + order.id + "_payment" }
  );
}

What does on_behalf_of change?

`on_behalf_of` is a parameter on indirect charges (destination charges and separate charges and transfers) that makes a connected account the settlement merchant for that payment. The money still flows through your platform and your platform still carries refunds and disputes by default. What changes is whose business the payment is made for. According to Stripe's documentation, when `on_behalf_of` is set, the charge settles in the connected account's country and settlement currency, the fee structure for that country is used, the connected account's statement descriptor appears on the customer's statement, and if the connected account is in a different country from the platform, its address and phone number are shown too.

Three practical consequences follow. First, `on_behalf_of` only works for connected accounts with a payments capability such as `card_payments`; accounts on the recipient service agreement cannot request it. Second, for platforms and sellers in different regions, Stripe may require `on_behalf_of` for destination charges, or may not support the flow at all; cross-region support for indirect charges depends on the regions involved, so check Stripe's cross-border documentation for your exact pairing. Third, payment method configuration changes: indirect charges without `on_behalf_of` use the platform's payment method configuration, while charges with it can use configurations set up for connected accounts.

When should you use it? When the seller is the business the customer believes they are paying, and the seller can carry a payments capability. A marketplace of independent clinics, where patients expect to see the clinic's name, is a typical example. A rideshare-style service where customers think they are buying from your brand usually does not need it.

Multi-seller baskets: when separate charges and transfers is the only fit

A destination charge has exactly one destination. The moment a customer can check out a basket with items from three sellers, you either create three charges (three authorizations, three card statement lines and three chances of partial failure) or you take one charge on the platform and split it with transfers. The second option is what separate charges and transfers exists for. Stripe also lists other cases where it fits: a delivery that pays both a store and a courier, a charge taken before the job is assigned to a provider, or a transfer that must be bigger than the customer's payment because you are funding a discount.

The price of that flexibility is responsibility. Three behaviors catch teams out:

  • Transfers draw on your available balance. By default, a transfer that exceeds the platform's available balance fails, and Stripe does not retry it. Setting `source_transaction` to the charge ID makes the transfer succeed immediately but delays the seller's funds until the charge's funds are available, and Stripe assigns a matching `transfer_group` automatically. You can create several transfers against one `source_transaction`, as long as they do not exceed the charge.
  • Asynchronous payment methods need care. For bank debits and other delayed methods, Stripe advises waiting for `charge.succeeded` before transferring. If a payment used as a `source_transaction` later fails, Stripe moves funds from your platform balance to cover the transfer, and it is up to you to reverse it.
  • Refunds and disputes do not touch transfers. Your code has to decide which seller's share to reverse for a partial refund of a three-seller basket. That decision belongs in your order model, not in an ad hoc support script.
Splitting one basket across sellers after fulfilment (Node.js)
// Called once per order line after that seller fulfils it.
export async function paySellerForLine(stripe, order, line, chargeId) {
  return stripe.transfers.create(
    {
      amount: line.sellerShareCents, // from your ledger, after your fee
      currency: "usd",
      destination: line.sellerStripeAccountId,
      source_transaction: chargeId, // funds follow the charge's availability
      transfer_group: "order_" + order.id,
      metadata: { order_id: order.id, line_id: line.id },
    },
    { idempotencyKey: "transfer_line_" + line.id }
  );
}

Both snippets pass an idempotency key derived from your own IDs, so a retried request cannot create a second payment or a second transfer. The webhook side of this, where `charge.dispute.created`, `charge.refunded` and transfer events arrive more than once and out of order, is covered in our guide to Stripe webhooks best practices.

Stripe Connect marketplace vs platform: three worked examples

Stripe uses “platform” for any business that uses Connect, but in practice the question people search for, marketplace vs platform, is about who the customer thinks they are buying from. A marketplace sells other people's goods or services under its own brand. A software platform sells tools to businesses who then sell under their own brand. That single distinction drives most of the charge-type decision.

A service marketplace

Picture a marketplace where homeowners book vetted electricians. The customer chooses the marketplace because of its brand, its vetting and its guarantee; the electrician is the provider of the service. Each booking involves one customer and one provider. This is the textbook case for destination charges: the marketplace is the business of record, the charge settles on the platform, the provider receives their share by transfer, and the marketplace takes an application fee. Because the marketplace already carries refunds and disputes by default, it also owns the customer support relationship, which matches the brand promise. The design work is in the refund policy: when a job is cancelled after the provider has turned up, you need explicit rules for whether `reverse_transfer` applies in full, in part or not at all. If providers are in several countries, the cross-region rules above may push you towards `on_behalf_of`.

A multi-vendor store

Now picture a store for independent makers where one basket routinely holds items from several sellers, each shipping separately. A destination charge cannot represent that payment, so the realistic options are separate charges and transfers or one charge per seller. Most teams choose separate charges and transfers: one payment for the customer, one line on their statement, and a transfer per seller once that seller ships, tied to the charge with `source_transaction`. The platform takes responsibility for refunds, disputes and the sellers' negative balances, so it needs a ledger that records each seller's share of each order, a policy for partial refunds by line item, and monitoring on its own available balance. If that sounds like a lot, it is; it is also the honest cost of a single-basket experience.

A vertical SaaS product

Finally, picture booking software for independent fitness studios. The studios' clients book and pay inside software they may not know by name; on their statement they expect to see the studio. Each payment involves one studio and one client. This is where direct charges fit: the charge is created on the studio's connected account, the studio is the business of record, refunds and disputes hit the studio's balance by default, and the software company takes an application fee. The software company still needs to decide whether Stripe fees are debited from the studios or from the platform, and it needs connected accounts configured to support direct charges with the `card_payments` capability. What it avoids is becoming the customer-facing merchant for thousands of businesses it does not run.

A decision table for choosing a charge type

If you only read one section, read this one. Work through the questions in order; the first answer that applies usually decides the model. Stripe also shows a recommendation for your business in your Connect platform profile in the Dashboard, which is worth reading alongside this.

  • Can one payment involve more than one seller, or is the seller unknown when you charge? Use separate charges and transfers, and accept that the platform carries refunds, disputes and reconciliation.
  • Do customers buy from the seller's brand, with your software in the background? Use direct charges, with an application fee for your cut.
  • Do customers buy from your brand, one seller per payment, and should your name be on the statement? Use destination charges, with an application fee or a transfer amount.
  • Same as above, but the seller must be the business of record, or the seller is in another region? Use destination charges with `on_behalf_of`, if the seller can hold a payments capability and the region pairing is supported.
  • Do you need payments to settle in the seller's country and currency? Direct charges, or indirect charges with `on_behalf_of`.
  • Is your platform unwilling or unable to carry sellers' negative balances? Avoid separate charges and transfers, and look closely at account configuration before choosing destination charges.
Illustrative decision path. Confirm country, account configuration and capability support in Stripe's documentation before committing.

Account configuration and charge type are one decision

Older articles talk about Standard, Express and Custom accounts. Stripe now treats those as legacy account types and recommends describing connected accounts by their properties instead. With the Accounts v1 API, those are controller properties: who pays Stripe fees (`controller.fees.payer`), who is responsible for losses such as negative balances (`controller.losses.payments`), who collects verification requirements (`controller.requirement_collection`), and which dashboard the account gets (`controller.stripe_dashboard.type`: full, Express or none). With the Accounts v2 API, an account gains functionality through configurations such as `merchant` (accept payments), `recipient` (receive transfers, which indirect charges need) and `customer`, while fee and loss responsibilities sit under the account's defaults. See Stripe's Accounts v2 overview for the current shape.

Why this matters for the charge type: the two choices constrain each other. Stripe recommends destination charges for connected accounts that do not have access to the full Stripe Dashboard. Direct charges require the connected account to accept payments itself. Separate charges and transfers is recommended only when the platform is responsible for negative balances. Some account settings, such as the dashboard type, cannot be changed after the account is created; you would have to create a new account. So decide the charge model and the account configuration together, write both down, and treat them as one architectural decision rather than a coding detail. The onboarding side of that decision, which requirements you collect and when, is covered in our Stripe Connect onboarding guide.

Can you migrate between charge types later?

Yes, but it is a migration, not a flag. Stripe explicitly allows a platform to use more than one charge type, which is what makes gradual moves possible: new orders can use the new model while old orders finish their lifecycle under the old one. What makes it work is planning for the parts that do not move automatically.

  • Historical payments stay where they are. A destination charge created last month is still on your platform balance. Its refunds and disputes follow the old rules, so your refund code must branch on the charge type of the original payment, not on today's default.
  • Saved payment methods live on an account. Moving from indirect to direct charges means future payments are created on connected accounts, while your customers' saved cards are on the platform. Stripe documents how to clone platform payment methods onto connected accounts for direct charges, currently for cards and US bank accounts; check whether they cover the payment methods you use before you promise customers they will not need to re-enter details.
  • Account configuration may need to change. Direct charges need connected accounts that can accept payments. If your sellers were onboarded as transfer-only recipients, they will need new capabilities and possibly new onboarding steps, which reopens requirements for every seller.
  • Fees and reporting change shape. Moving from keeping the difference to application fees, or from a transfer amount to an application fee, changes how your finance team reconciles. Run both reports side by side for a period.
  • Statement descriptors change. If the business of record changes, customers will see a different name on their statements. Tell them before they see it, or expect more disputes.

The safest pattern is to put the charge type behind a single function in your payment service, store the model used on every order, and migrate by cohort: a few sellers first, then more, with reconciliation reports checked at each step.

Common mistakes with Stripe Connect charge types

  • Choosing by code sample. Teams copy the first working example and discover the liability model months later, when the first dispute hits the platform balance. Choose by responsibility, then write code.
  • Assuming a refund claws back the seller's share. For destination charges, the connected account keeps the transfer unless you set `reverse_transfer`. For separate charges and transfers, refunds never touch transfers. Either way, an unreversed refund is money the platform paid on the seller's behalf.
  • Reversing a transfer for a dispute you then win. If you challenge a dispute, wait for the outcome. Reversing early and repaying later can fail on balance or cross-border restrictions.
  • Transferring before funds are available. Without `source_transaction`, transfers draw on the platform's available balance and fail when it is short, with no automatic retry.
  • Ignoring skipped transfers. For asynchronous payment methods, if a destination account loses its transfer capability before the funds settle, Stripe skips the transfer and the money stays on your platform. Stripe's guidance is to watch `charge.updated` and treat a null `transfer_data` as a skipped transfer.
  • Forgetting the customer's statement. If customers see a name they do not recognize, disputes rise, and on indirect charges you pay for them.
  • Treating country support as a given. Cross-region destination charges and transfers are supported only for certain region pairings. Check before you sign sellers in a new country.
  • Skipping idempotency. A retried charge or transfer request without an idempotency key can move money twice.

What to decide before writing any Connect code

Most Connect rewrites we see trace back to a decision nobody made explicitly. Before anyone writes a PaymentIntent, get answers to these questions in writing and agree them with whoever owns finance and support.

If you can answer every item, the code is the straightforward part. If two people give different answers to the first question, stop and resolve that first; it changes everything downstream.

From here, this article is about Cevrynt

How Cevrynt approaches Stripe Connect architecture

Cevrynt is a Stripe partner, a member of the Stripe Partner Ecosystem, and our Connect work starts with the questions above rather than with code. Arindam Pradhan, our founder, is a Stripe Certified Professional (Implementation Architect), and Anupam Pradhan is a Stripe Certified Professional (Payments Architect). Together with our senior engineers, we map your funds flow, write down the charge model and account configuration with an owner for every responsibility, then build the server-side charge, transfer, refund and webhook code that you own at handover.

Every Connect project is scoped and priced in writing after a technical review. You can read how we work on our Stripe Connect service page, see the wider Stripe services overview, or tell us about your marketplace or platform and we will reply with the questions we need answered first.