Creating Payments and Fees

With Connect, you can make charges and create subscriptions on behalf of connected users, optionally taking transaction fees in the process. If you need help after reading this, check out our answers to common questions or chat live with other developers in #stripe on freenode.

Connect allows you to use the full Stripe API on behalf of your connected users. This means, among other functionality, you can:

Creating payments

There are two ways to create a charge on behalf of a connected account:

  • Create the charge directly on the connected account.
  • Create the charge on the platform’s account, but set a destination parameter identifying the Stripe account that should receive the funds from the payment.

With the first approach, the connected account is responsible for the fees, refunds, and chargebacks. The payment itself will appear as a charge in the connected account.

The latter, new approach provides much more customizability but makes the platform account responsible for the fees, refunds, and chargebacks. The payment appears as a charge in the platform account, along with a transfer from the platform account to the connected account.

Charging directly

Processing a charge directly on a connected account requires authentication, best achieved by providing the platform account’s secret key and the Stripe-Account header:

curl https://api.stripe.com/v1/charges \
   -u {PLATFORM_SECRET_KEY}: \
   -H "Stripe-Account: {CONNECTED_STRIPE_ACCOUNT_ID}" \
   -d amount=1000 \
   -d currency=usd \
   -d source="{TOKEN}"
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

Stripe::Charge.create({
  :amount => 1000,
  :currency => "usd",
  :source => {TOKEN}
}, {:stripe_account => CONNECTED_STRIPE_ACCOUNT_ID})
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

stripe.Charge.create(
  amount=1000,
  currency="usd",
  source={TOKEN},
  stripe_account={CONNECTED_STRIPE_ACCOUNT_ID}
)
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

\Stripe\Charge::create(array(
  'amount' => 1000,
  'currency' => 'usd',
  'source' => {TOKEN}
), array('stripe_account' => CONNECTED_STRIPE_ACCOUNT_ID));
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

RequestOptions requestOptions = RequestOptions.builder().setStripeAccount(CONNECTED_STRIPE_ACCOUNT_ID).build();
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("amount", 1000);
chargeParams.put("currency", "usd");
chargeParams.put("source", {TOKEN});

Charge.create(chargeParams, requestOptions);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

stripe.charges.create({
  amount: 1000,
  currency: 'usd',
  source: {TOKEN}
}, {stripe_account: {CONNECTED_STRIPE_ACCOUNT_ID}});

When charging directly on a connected account, you can provide a token created using either the platform’s or the connected account’s publishable key. (When using direct charging of a saved customer, not a token, the Customer object must exist within the connected account, not within the platform account.)

Charges created directly on a connected account will only be available on that account—you won’t see it in your platform’s dashboard or reporting.

Charging through the platform

The second way to perform a charge on behalf of a connected account is to create it on your platform’s Stripe account and use the destination parameter–specifying the connected account ID–in the charge request.

You’ll want to charge through the platform if you’d like to keep all of your charge data on one account (e.g., for aggregated reporting) or if you’d like to have greater flexibility over fees and what customers see on their credit card statement. If you’re managing Stripe accounts, we recommend that you create charges directly on your platform.

curl https://api.stripe.com/v1/charges \
   -u {PLATFORM_SECRET_KEY}: \
   -d amount=1000 \
   -d currency=usd \
   -d source="{TOKEN}" \
   -d destination="{CONNECTED_STRIPE_ACCOUNT_ID}"
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

Stripe::Charge.create({
  :amount => 1000,
  :currency => "usd",
  :source => {TOKEN},
  :destination => {CONNECTED_STRIPE_ACCOUNT_ID}
})
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

stripe.Charge.create(
  amount=1000,
  currency="usd",
  source={TOKEN},
  destination={CONNECTED_STRIPE_ACCOUNT_ID}
)
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

\Stripe\Charge::create(array(
  'amount' => 1000,
  'currency' => 'usd',
  'source' => {TOKEN},
  'destination' => {CONNECTED_STRIPE_ACCOUNT_ID}
));
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("amount", 1000);
chargeParams.put("currency", "usd");
chargeParams.put("source", {TOKEN});
chargeParams.put("destination", {CONNECTED_STRIPE_ACCOUNT_ID});

Charge.create(chargeParams);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

stripe.charges.create({
  amount: 1000,
  currency: 'usd',
  source: {TOKEN},
  destination: {CONNECTED_STRIPE_ACCOUNT_ID}
});

When you use the destination parameter:

  • The entire amount of the charge will be immediately transferred to the destination account. The ID of the resulting transfer will be returned in the transfer field of the response.
  • The Stripe fee comes out of the charge object on the platform’s account (i.e., you pay the fee).
  • The amount specified by the application_fee parameter (if provided) will be deducted from the total amount and credited to the platform’s account.
  • The charge will be attributed to the destination account for tax reporting.
  • When using a token as the payment source, the token must be created using the platform’s publishable key. If charging a Customer object, the Customer must exist within the platform’s account.
  • The charge will be settled in the default currency of the destination account. The platform will also take its application_fee in this currency, accumulating balances in the currency of the destination account. If the platform does not have a bank account in this currency, the platform may manually convert these balances to another currency.

As you can tell from that list, this approach also creates more objects within Stripe than processing the charge on the connected account directly. The destination account receives a charge object. The platform account will have the charge object and a transfer (the transfer is tied to the destination account’s charge). The platform account will have an application fee object, too, if an application fee was set. Note, as well, that the Stripe fee is paid by the platform account in this case.

This may seem complicated, but such complexity is necessary to handle many edge cases of currency conversion and possible refunds. This structure allows each part of the flow to be refunded independently of others. For example, you can choose to refund the charge to a customer’s credit card with or without reversing the transfer to the connected account, and with or without reversing the application fee you took.

Collecting application fees

With Connect, your platform can take an application fee on charges, subscriptions, and even on transfers.

There are three things to note about application fees, whenever they are requested:

  • The application_fee parameter must be a positive integer (e.g., an amount in cents)
  • The application_fee will be capped at the total transaction amount minus any Stripe fees—no error will be thrown for application fees that are automatically capped
  • No Stripe fees are applied to the application fee

The application_fee is always specified in the same currency as the transaction. For direct charges, application fees are collected in the platform’s default currency. When charging through the platform with the destination parameter, the application fee is collected in the settlement currency of the connected account.

Fees on charges

To charge a fee on top of Stripe’s fees for a one-off charge, all you need to do is pass in the optional application_fee parameter (in cents) when creating the charge.

curl https://api.stripe.com/v1/charges \
   -u {PLATFORM_SECRET_KEY}: \
   -H "Stripe-Account: {CONNECTED_STRIPE_ACCOUNT_ID}" \
   -d amount=1000 \
   -d currency=usd \
   -d source="{TOKEN}" \
   -d description="payinguser@example.com" \
   -d application_fee=123
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

# Get the credit card details submitted by the form
token = params[:stripeToken]

# Create the charge with Stripe
charge = Stripe::Charge.create({
    :amount => 1000, # amount in cents
    :currency => "usd",
    :source => token,
    :description => "Example charge",
    :application_fee => 123 # amount in cents
  },
  {:stripe_account => CONNECTED_STRIPE_ACCOUNT_ID}
)
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

# Get the credit card details submitted by the form
token = request.POST['stripeToken']

# Create the charge with Stripe
charge = stripe.Charge.create(
  amount=1000, # amount in cents
  currency="usd",
  source=token,
  description="Example charge",
  application_fee=123, # amount in cents
  stripe_account=CONNECTED_STRIPE_ACCOUNT_ID
)
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

// Get the credit card details submitted by the form
$token = $_POST['stripeToken'];

// Create the charge with Stripe
$charge = \Stripe\Charge::create(
  array(
    "amount" => 1000, // amount in cents
    "currency" => "usd",
    "source" => $token,
    "description" => "Example charge",
    "application_fee" => 123 // amount in cents
  ),
  array("stripe_account" => CONNECTED_STRIPE_ACCOUNT_ID)
);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

// Get the credit card details submitted by the form
String token = request.getParameter("stripeToken");

// Create the charge with Stripe
Map<String, Object> chargeParams = new HashMap<String, Object>();
chargeParams.put("amount", 1000); // amount in cents
chargeParams.put("currency", "usd");
chargeParams.put("source", token);
chargeParams.put("description", "Example charge");
chargeParams.put("application_fee", 123); // amount in cents

RequestOptions requestOptions = RequestOptions.builder().setStripeAccount(CONNECTED_STRIPE_ACCOUNT_ID).build();

Charge charge = Charge.create(chargeParams, requestOptions);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

// Get the credit card details submitted by the form
var token = request.body.stripeToken;

// Create the charge with Stripe
stripe.charges.create(
  {
    amount: 1000, // amount in cents
    currency: "usd",
    source: token,
    description: "Example charge",
    application_fee: 123 // amount in cents
  },
  {stripe_account: CONNECTED_STRIPE_ACCOUNT_ID},
  function(err, charge) {
    // check for `err`
    // do something with `charge`
  }
);

The resulting charge’s balance transaction will include a detailed fee breakdown:

{
  "object": "balance_transaction",
  "type": "charge",
  "amount": 1000,
  "currency": "usd",
  "fee": 182,
  ...
  "fee_details": [
    {
      "type": "stripe_fee",
      "amount": 59,
      "application": null,
      "currency": "usd"
    },
    {
      "type": "application_fee",
      "amount": 123,
      "application": "ca_32D88BD1qLklliziD7gYQvctJIhWBSQ7",
      "currency": "usd"
    }
  ]
}

Flow of funds

When you specify an application fee on a charge–either by creating the charge or through recurring billing, the fee amount will be transferred to your (the platform owner’s) Stripe account. If you process a charge directly on a connected account, the charge amount, less the Stripe fees and application fees, will be deposited into the connected user’s account. For example, if a charge of $10 is made as in the example above, $1.23 will be transferred to your account, and $8.18 cents ($10 - $0.59 - $1.23) will be transferred to your connected user’s account.

If you process a charge on the platform account, and use the destination parameter, the charge amount less the application fee will be deposited into the connected user’s account ($8.77, assuming a $1.23 application fee on a $10 charge). The application fee less the Stripe fees (on the charge amount) will be deposited into your account ($0.64, which is $1.23 - $0.59).

Any application fees earned will become available to your Stripe account on the normal 2-day rolling basis, just like funds from regular Stripe charges. Application fees are viewable in the Collected Fees section of the dashboard.

Refunding Fees

When a charge is refunded, the end customer will always be refunded the full amount of the charge. Stripe will never automatically refund the application fee: you must explicitly refund the application fee, or your connected user—the account on which the charge was created—will eat the cost of the fee.

You can refund application fees incurred by your application in several ways:

Creating subscriptions

Through Connect, you can create subscriptions on connected user accounts. You can also take an application fee percentage on those subscription payments. Both the customer and the plan must be defined on the connected account in order to create the subscription.

Subscriptions do not currently support the destination parameter: they can only be created directly on the connected account. But as with creating charges directly on a connected account, you can create a customer and a subscription in a connected account using a token created with either the platform’s or the connected account’s publishable key.

Fees on subscriptions

When creating or updating a subscription, you can specify an application_fee_percent parameter as an integer between 1 and 100. Each billing period, Stripe takes that percentage off the final invoice amount—including any bundled invoice items, discounts, or account balance adjustments—as a fee for your application, on top of any Stripe fees charged.

For example, take this scenario:

  • The subscription cost per billing cycle is $100
  • There’s a $10 invoice item
  • A 50% coupon applies
  • The application_fee_percent is 10

In that case, the total application fee taken will be $5.50: ($100 + $10) * 50% * 10%.

Application fees on subscriptions must be a percentage, as the amount billed via subscriptions often varies. You cannot set a flat amount for the application fee on a subscription, but can on an invoice as explained below.

To create a subscription without an application fee, omit the application_fee_percent argument.

Prorations and Invoice Items

Invoices created outside of a subscription billing period will not have the application_fee_percent applied to them. This includes proration invoice items that are immediately invoiced. You’ll need to set an application_fee on the invoice directly for the application fee amount you’d like to charge (see below).

If you’re not planning to create extra invoices, prorations will be automatically bundled into the next billing cycle and will automatically inherit the application_fee_percent set on the updated subscription.

Invoices

If you’d like to charge a flat or dynamic fee, or otherwise any fee that can’t be automatically calculated with application_fee_percent, you can add an application_fee directly on the invoice. (Usually this is done programmatically upon receiving an invoice.created webhook notification, although you can separately create an invoice for pending invoice items and set an application fee on it.)

# Update the invoice with an application_fee to be applied when the
# invoice is paid
curl https://api.stripe.com/v1/invoices/{INVOICE_ID} \
   -u {PLATFORM_SECRET_KEY}: \
   -H "Stripe-Account: {CONNECTED_STRIPE_ACCOUNT_ID}" \
   -d application_fee=100
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

# Update the invoice with an application_fee to be applied when the
# invoice is paid
invoice.application_fee = 100 # amount in cents
invoice.save
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

# Update the invoice with an application_fee to be applied when the
# invoice is paid
invoice.application_fee = 100 # amount in cents
invoice.save()
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

// Update the invoice with an application_fee to be applied when the
// invoice is paid
$invoice->application_fee = 100; // amount in cents
$invoice->save();
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

// Update the invoice with an application_fee to be applied when the
// invoice is paid
Map<String, Object> invoiceParams = new HashMap<String, Object>();
invoiceParams.put("application_fee", 100); // amount in cents
invoice.update(invoiceParams, requestOptions);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

// Update the invoice with an application_fee to be applied when the
// invoice is paid
stripe.invoices.update(
  invoiceId,
  {application_fee: 100},
  {stripe_account: CONNECTED_STRIPE_ACCOUNT_ID},
  function(err, invoice) {}
);

The application_fee set directly on an invoice overrides any application fee amount calculated with application_fee_percent, and will be capped at the invoice’s final charge amount.

Permissions

You cannot update or cancel a subscription that was not created by your application. You also can’t add an application_fee to an invoice that was not created by your application or that contains invoice items belonging to another application.

Issuing refunds on charges

Charges created on the platform account can be refunded using the platform account's secret key:

curl https://api.stripe.com/v1/charges/{CHARGE_ID}/refunds \
   -u {PLATFORM_SECRET_KEY}: \
   -d amount=1000
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

ch = Stripe::Charge.retrieve(CHARGE_ID)
ch.refunds.create(:amount => 1000)
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

ch = stripe.Charge.retrieve(CHARGE_ID)
ch.refunds.create(amount=1000)
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

$ch = \Stripe\Charge::retrieve(CHARGE_ID);
$ch->refunds->create(array('amount' => 1000));
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

Charge ch = Charge.retrieve(CHARGE_ID);

Map<String, Object> refundParams = new HashMap<String, Object>();
refundParams.put("amount", 1000);

ch.refund(refundParams);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

stripe.charges.createRefund(
  CHARGE_ID,
  {amount: 1000},
  function(err, refund) {}
);

Charges created on a connected account can be refunded simply by authenticating the platform and identifying the connected account:

curl https://api.stripe.com/v1/charges/{CHARGE_ID}/refunds \
   -u {PLATFORM_SECRET_KEY}: \
   -H "Stripe-Account: {CONNECTED_STRIPE_ACCOUNT_ID}" \
   -d amount=1000
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

ch = Stripe::Charge.retrieve(CHARGE_ID, :stripe_account => CONNECTED_STRIPE_ACCOUNT_ID)
ch.refunds.create(:amount => 1000)
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

ch = stripe.Charge.retrieve(CHARGE_ID, stripe_account=CONNECTED_STRIPE_ACCOUNT_ID)
ch.refunds.create(amount=1000)
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

$ch = \Stripe\Charge::retrieve(CHARGE_ID, array('stripe_account' => CONNECTED_STRIPE_ACCOUNT_ID));
$ch->refunds->create(array('amount' => 1000));
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

RequestOptions requestOptions = RequestOptions.builder().setStripeAccount(CONNECTED_STRIPE_ACCOUNT_ID).build();
Charge ch = Charge.retrieve(CHARGE_ID, requestOptions);

Map<String, Object> refundParams = new HashMap<String, Object>();
refundParams.put("amount", 1000);

ch.refund(refundParams);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

stripe.charges.createRefund(
  CHARGE_ID,
  {amount: 1000},
  {stripe_account: CONNECTED_STRIPE_ACCOUNT_ID},
  function(err, refund) {}
);

When refunding a charge that has a destination value, by default the destination account will keep the funds that were transferred to it, leaving the platform account to cover the negative balance from the refund. To pull back the funds from the connected account to cover the refund, set the reverse_transfer parameter to true when creating the refund:

curl https://api.stripe.com/v1/charges/{CHARGE_ID}/refunds \
   -u {PLATFORM_SECRET_KEY}: \
   -d amount=1000 \
   -d reverse_transfer=true
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

ch = Stripe::Charge.retrieve(CHARGE_ID)
ch.refunds.create(:amount => 1000, :reverse_transfer => true)
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

ch = stripe.Charge.retrieve(CHARGE_ID)
ch.refunds.create(amount=1000, reverse_transfer=true)
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

$ch = \Stripe\Charge::retrieve(CHARGE_ID);
$ch->refunds->create(array('amount' => 1000, 'reverse_transfer' => true));
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

Charge ch = Charge.retrieve(CHARGE_ID);

Map<String, Object> refundParams = new HashMap<String, Object>();
refundParams.put("amount", 1000);
refundParams.put("reverse_transfer", true);

ch.refund(refundParams);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

stripe.charges.createRefund(
  CHARGE_ID,
  {amount: 1000, reverse_transfer: true},
  function(err, refund) {}
);

When you use the reverse_transfer parameter, a proportional amount of the transfer will be reversed to cover the refund. If the refund_application_fee parameter is also set to true:

  • If the refund results in the entire charge being refunded, all unrefunded application fees will be refunded
  • Otherwise, a proportional amount of the application fee will be refunded to the Connected account

Accounting for negative balances

Some actions, such as refunds and chargebacks, create negative transactions in a Stripe account. If at all possible, Stripe automatically offsets those against payments to ensure a positive Stripe balance.

Stripe first assigns negative transactions to the account on which the associated charge was made. For example, when charging on a connected account, a refund or chargeback will come from the connected account. When charging on the platform using the destination parameter, a refund or chargeback will come from the platform account.

Ultimate responsibility for negative balances depends upon the relationship between the platform and the connected account. A connected standalone account is always ultimately responsible. For managed accounts, the platform is ultimately responsible to cover negative balances.

For example, your platform uses managed accounts and you create a charge on a connected account. Should a chargeback occur, we’ll first attempt to take the funds out of the managed account balance, because the charge was created on the connected account. In most cases, the adjustment is made without problem. If, for some reason, we cannot adjust the connected account and its balance goes negative (and is negative for a while), we’ll eventually look to the platform account to make the balance whole again (because it’s a managed account and the platform has ultimate liability).

Handling balances in multiple currencies

When creating destination charges on behalf of a connected account, the platform will collect its application fees in the settlement currency of the connected account. If your platform has connected accounts in many different countries, you can expect to accumulate balances in a variety of currencies.

Because platforms may not have bank accounts for these other currencies, Stripe provides a way for platforms to pay out from balances in non-default currencies to the platform’s default bank account. These payouts are created as a manual transfer with currency set as the currency for the source balance, and destination set to default_for_currency:

curl https://api.stripe.com/v1/transfers \
   -u {PLATFORM_SECRET_KEY}: \
   -d amount=1000 \
   -d currency=kzt \
   -d destination=default_for_currency
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

Stripe::Transfer.create(
  :amount => 1000,
  :currency => 'kzt',
  :destination => 'default_for_currency'
)
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

stripe.Transfer.create(
  amount=1000,
  currency="kzt",
  destination="default_for_currency"
)
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

\Stripe\Transfer::create(array(
  'amount' => 1000,
  'currency' => "kzt",
  'destination' => "default_for_currency"
));
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

Map<String, Object> transferParams = new HashMap<String, Object>();
transferParams.put("amount", 1000);
transferParams.put("currency", "kzt");
transferParams.put("destination", "default_for_currency");

Transfer.create(transferParams);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

stripe.transfers.create(
  {
    amount: 1000,
    currency: 'kzt',
    destination: 'default_for_currency'
  }
);

As long as there are sufficient funds in the balance for the specified currency, Stripe will automatically convert the funds to the default bank account’s currency.

To create these currency conversions, the platform will have to be on manual transfers.

Further reading

Read up on the rest of this section to discover what other Connect functionality is available for your users.