Create separate charges and transfers
Create charges on your platform account and transfer funds to multiple connected accounts.
Create separate charges and transfers to transfer funds from one payment to multiple connected accounts, or when a specific user isn’t known at the time of the payment. The charge on your platform account is decoupled from the transfers to your connected accounts. With this charge type:
- You create a charge on your platform’s account and also transfer funds to your connected accounts. The payment appears as a charge on your account and there are also transfers to connected accounts (amount determined by you), which are withdrawn from your account balance.
- You can transfer funds to multiple connected accounts.
- Your account balance is debited for the cost of the Stripe fees, refunds, and chargebacks.
This charge type helps marketplaces split payments between multiple parties. For example, a restaurant delivery platform that splits payments between the restaurant and the deliverer.
Note
Funds segregation is a private preview feature that keeps payment funds in a protected holding state before you transfer them to connected accounts. This prevents allocated funds from being used for unrelated platform operations. Contact your Stripe account manager to request access.
Stripe supports separate charges and transfers in the following regions:
Australia
Austria
Belgium
Brazil
Bulgaria
Canada
Croatia
Cyprus
Czech Republic
Denmark
Estonia
Finland
France
Germany
Greece
Hungary
Ireland
Italy
Japan
Latvia
Liechtenstein
Lithuania
Luxembourg
Malaysia
Malta
Mexico
Netherlands
New Zealand
Norway
Poland
Portugal
Romania
Singapore
Slovakia
Slovenia
Spain
Sweden
Switzerland
United Arab Emirates
United Kingdom
United States
Cross-border transfers
Stripe supports cross-border transfers on the payments balance between the United States, Canada, United Kingdom, EEA, and Switzerland. In other scenarios, your platform and any connected account must be in the same region. Attempting to transfer funds across unsupported borders or balances returns an error. See Cross-border payouts for supported funds flows between other regions.
You must only use transfers in combination with the permitted use cases for charges, tops-ups and fees. We recommend using separate charges and transfers only when you’re responsible for negative balances of your connected accounts.
Checkout
Custom flow
Mobile
Redirect to a Stripe-hosted payment page using Stripe Checkout. See how this integration compares to Stripe’s other integration types.
Integration effort
Low code
Integration type
Redirect to Stripe-hosted payment page
UI customization
Limited customization
First, register for a Stripe account.
Use our official libraries to access the Stripe API from your application:
Command Line
Select a language
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
# Available as a gem
sudo gem install stripe
Gemfile
Select a language
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
# If you use bundler, you can add this line to your Gemfile
gem 'stripe'
Create a Checkout Session Client-side Server-side
A Checkout Session controls what your customer sees in the payment form such as line items, the order amount and currency, and acceptable payment methods. Add a checkout button to your website that calls a server-side endpoint to create a Checkout Session.
checkout.html
<html>
<head>
<title>Checkout</title>
</head>
<body>
<form action="/create-checkout-session" method="POST">
<button type="submit">Checkout</button>
</form>
</body>
</html>
On your server, create a Checkout Session and redirect your customer to the URL returned in the response.
Command Line
Select a language
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
- line _ items - This attribute represents the items the customer is purchasing. The items are displayed in the Stripe-hosted checkout page.
- payment _ intent _ data[transfer _ group] - Use a unique string as the transfer _ group to identify objects that are associated with each other. When Stripe automatically creates a charge for a PaymentIntent with a transfer _ group value, it assigns the same value to the charge’s transfer _ group .
- success _ url - Stripe redirects the customer to the success URL after they complete a payment and replaces the {the related setting _ the related setting _ ID} string with the Checkout Session ID. Use this to retrieve the Checkout Session and inspect the status to decide what to show your customer. You can also append your own query parameters, which persist through the redirect process. See customize redirect behavior with a Stripe-hosted page for more information.
Handle post-payment events Server-side
Stripe sends a checkout.session.completed event when the payment completes. Use a webhook to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.
Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes. Some payment methods also take 2-14 days for payment confirmation. Setting up your integration to listen for asynchronous events enables you to accept multiple payment methods with a single integration.
Stripe recommends handling all of the following events when collecting payments with Checkout:
| Event | Description | Next steps |
|---|---|---|
| checkout.session.completed | The customer has successfully authorized the payment by submitting the Checkout form. | Wait for the payment to succeed or fail. |
| checkout.session.async_payment_succeeded | The customer’s payment succeeded. | Fulfill the purchased goods or services. |
| checkout.session.async_payment_failed | The payment was declined, or failed for some other reason. | Contact the customer through email and request that they place a new order. |
These events all include the Checkout Session object. After the payment succeeds, the underlying PaymentIntent status changes from processing to succeeded or a failure status.
Create a Transfer Server-side
On your server, send funds from your account to a connected account by creating a Transfer and specifying the transfer_group used.
Command Line
Select a language
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
Transfer and charge amounts don’t have to match. You can split a single charge between multiple transfers or include multiple charges in a single transfer. The following example creates an additional transfer associated with the same transfer_group.
Command Line
Select a language
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
Transfer options
You can assign any value to the transfer_group string, but it must represent a single business action. You can also make a transfer with neither an associated charge nor a transfer_group —for example, when you must pay a provider but there’s no associated customer payment.
Note
The transfer_group only identifies associated objects. It doesn’t affect any standard functionality. To prevent a transfer from executing before the funds from the associated charge are available, use the transfer’s source_transaction attribute.
By default, a transfer request fails when the amount exceeds the platform’s available account balance. Stripe doesn’t automatically retry failed transfer requests.
You can avoid failed transfer requests for transfers that are associated with charges. When you specify the associated charge as the transfer’s source_transaction, the transfer request automatically succeeds. However, we don’t execute the transfer until the funds from that charge are available in the platform account.
Note
If you use separate charges and transfers, take that into account when planning your payout schedule. Automatic payouts can interfere with transfers that don’t have a defined source_transaction.
Asynchronous payment methods
If you’re using asynchronous payment methods (such as ACH Debit or the related setting Debit), wait for a charge.succeeded event before creating a transfer. Unlike destination charges, Stripe doesn’t automatically reverse a transfer if the associated async payment fails. If you create a transfer and the payment subsequently fails, your platform’s balance is debited for the transfer amount. You must then manually reverse the transfer to recover the funds.
Test the integration
| Card number | Scenario | How to test |
|---|---|---|
| The card payment succeeds and doesn’t require authentication. | Fill out the credit card form using the credit card number with any expiration, CVC, and postal code. | |
| The card payment requires authentication. | Fill out the credit card form using the credit card number with any expiration, CVC, and postal code. | |
The card is declined with a decline code like insufficient_funds. | Fill out the credit card form using the credit card number with any expiration, CVC, and postal code. | |
| The UnionPay card has a variable length of 13-19 digits. | Fill out the credit card form using the credit card number with any expiration, CVC, and postal code. |
See Testing for additional information to test your integration.
Specify the settlement merchant
The settlement merchant is dependent on the capabilities set on an account and how a charge is created. The settlement merchant determines whose information is used to make the charge. This includes the statement descriptor (either the platform’s or the connected account’s) that’s displayed on the customer’s credit card or bank statement for that charge.
Specifying the settlement merchant allows you to be more explicit about who to create charges for. For example, some platforms prefer to be the settlement merchant because the end customer interacts directly with their platform (such as on-demand platforms). However, some platforms have connected accounts that interact directly with end customers instead (such as a storefront on an e-commerce platform). In these scenarios, it might make more sense for the connected account to be the settlement merchant.
You can set the on_behalf_of parameter to the ID of a connected account to make that account the settlement merchant for the payment. When using on_behalf_of:
- Charges settle in the connected account’s country and settlement currency .
- The fee structure for the connected account’s country is used.
- The connected account’s statement descriptor is displayed on the customer’s credit card statement.
- If the connected account is in a different country than the platform, the connected account’s address and phone number are displayed on the customer’s credit card statement.
- The number of days that a pending balance is held before being paid out depends on the delay_days setting on the connected account.
Accounts v2 API
You can’t use the Accounts v2 API to manage payout settings. Use the Accounts v1 API.
If on_behalf_of is omitted, the platform is the business of record for the payment.
Caution
The on_behalf_of parameter is supported only for connected accounts with a payments capability such as card_payments. Accounts under the recipient service agreement can’t request card_payments or other payments capabilities.
Command Line
Select a language
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
Collect fees
When using separate charges and transfers, the platform can collect fees on a charge by reducing the amount it transfers to the destination accounts. For example, consider a restaurant delivery service transaction that involves payments to the restaurant and to the driver:
- The customer pays a 100 USD charge.
- Stripe collects a 3.20 USD fee and adds the remaining 96.80 USD to the platform account’s pending balance.
- The platform transfers 70 USD to the restaurant’s connected account and 20 USD to the driver’s connected account.
- A platform fee of 6.80 USD remains in the platform account.
Application fees with funds segregation
Funds segregation is a private preview feature that allows you to debit application fees directly from allocated funds during transfer, providing clean accounting separation. Contact your Stripe account manager to request access.
To learn about processing payments in multiple currencies with Connect, see working with multiple currencies.
Transfer availability
The default behavior is to transfer funds from the platform account’s available balance. Attempting a transfer that exceeds the available balance fails with an error. To avoid this problem, when creating a transfer, tie it to an existing charge by specifying the charge ID as the source_transaction parameter. With a source_transaction, the transfer request returns success regardless of your available balance if the related charge hasn’t settled yet. However, the funds don’t become available in the destination account until the funds from the associated charge are available to transfer from the platform account.
Transfers with funds segregation
The private preview funds segregation feature requires the source_transaction parameter for transfers from allocated funds so they’re linked to their original payment.
If a transfer fails due to insufficient funds in your platform balance, adding funds doesn’t automatically retry the failed action—after adding funds, you must repeat any failed transfers or payouts.
If the source charge has a transfer_group value, Stripe assigns the same value to the transfer’s transfer_group. If it doesn’t, then Stripe generates a string in the format group_ plus the associated PaymentIntent ID, for example: group_pi_2NHDDD589O8KAxCG0179Du2s. It assigns that string as the transfer_group for both the charge and the transfer.
Note
You must specify the source_transaction when you create a transfer. You can’t update that attribute later.
Command Line
Select a language
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
You can get the charge ID from the PaymentIntent:
- Get the PaymentIntent’s latest_charge attribute . This attribute is the ID of the most recent charge associated with the PaymentIntent.
- Request a list of charges , specifying the payment _ intent in the request. This method returns full data for all charges associated with the PaymentIntent.
When using this parameter:
- The amount of the transfer must not exceed the amount of the source charge
- You can create multiple transfers with the same source _ transaction , as long as the sum of the transfers doesn’t exceed the source charge
- The transfer takes on the pending status of the associated charge: if the funds from the charge become available in N days, the payment that the destination Stripe account receives from the transfer also becomes available in N days
- Stripe automatically creates a transfer _ group for you
- The currency of the balance transaction associated with the charge must match the currency of the transfer
Asynchronous payment methods, like ACH, can fail after a subsequent transfer request is made. For these payments, avoid using source_transaction. Instead, wait until a charge.succeeded event is triggered before transferring the funds. If you have to use source_transaction with these payments, you must implement functionality to manage payment failures.
When a payment used as a source_transaction fails, funds from your platform’s account balance are transferred to the connected account to cover the payment. To recover these funds, reverse the transfer associated with the failed source_transaction.
Issue refunds
You can refund charges created on your platform using its secret key. However, refunding a charge has no impact on any associated transfers. It’s up to your platform to reconcile any amount owed back to it by reducing subsequent transfer amounts or by reversing transfers.
Refunds with funds segregation
The private preview funds segregation feature uses allocated funds for refunds before debiting your platform’s payments balance, providing clean accounting separation.
Command Line
Select a language
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
Reverse transfers
Connect supports the ability to reverse transfers made to connected accounts, either entirely or partially (by setting an amount value). Use transfer reversals only for refunds or disputes related to the charge, or to correct errors in the transfer.
Command Line
Select a language
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
Transfer reversals add the specified (or entire) amount back to the platform’s available balance, reducing the connected account’s available balance accordingly. It’s only possible to reverse a transfer if the connected account’s available balance is greater than the reversal amount or has connected reserves enabled.
If the transfer reversal requires a currency conversion, and the reversal amount would result in a zero balance after the conversion, it returns an error.
Disabling refunds for a connected account won’t block the ability to process transfer reversals.
