Taking a cut with Stripe Connect
Tags: stripe, tutorial, webdev
Published: 2023-01-24 05:00:00

There are several ways to take a cut, or collect a commission on the payments flowing through your Stripe integration. In the previous articles in this series, you learned [how to decide between Standard, Express, and Custom accounts types](https://www.cjav.dev/articles/picking-the-right-charge-type-for-your-stripe-connect-platform), and about the different funds flows (also known as charge types) that you can implement with Stripe Connect. Here, you’ll learn approaches to collecting a portion of the proceeds as revenue for your platform.

**There are four primary ways to earn money with your platform:**

1. Collecting application fees with direct or destination charges
2. Withholding a small amount of the payment when transferring funds to the connected account with destination charges or separate charges and transfers
3. Use Account debits to collect a one-off payment from a connected account
4. Collecting a recurring payment from your users as customers of your platform


## Application fees

The first solution for taking a cut is to use application fees with **direct** or **destination** charges. Soon after the direct charges funds flow was released, Stripe added support for “fee splitting” with the [`application_fee_amount`](https://stripe.com/docs/api/payment_intents/create#create_payment_intent-application_fee_amount) parameter. The `application_fee_amount` parameter can be included in an API call to create payment-related objects like a PaymentIntent or Checkout Session. Application fees are first class objects in the API and represent funds going from the connected account to the platform.

You can use the API to query for [Application fees](https://stripe.com/docs/api/application_fees/list). Or you can view them from your platform dashboard –  you’ll see the fees collected under [Payments > All Payments > Collected fees](https://dashboard.stripe.com/test/connect/application_fees).


![Screenshot of the platform dashboard showing fees collected](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ntlk21wbe129y65gwyts.png)



Your users who have access to the Standard dashboard can see the breakdown of Stripe fees and application fees in their dashboard:


![Screenshot of the connected account's dashboard and the fees they see](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3tgxzckgq45j1hd5fber.png)




### Direct charges with application fees

Here’s an example of adding an application fee of $3.21 to a direct charge for $40 to the connected account with ID `acct_1LuN62ClUWl50Go4`:

```bash
curl https://api.stripe.com/v1/payment_intents \
  -u sk_test_abc123: \
  -d amount=4000 \
  -d currency="usd" \
  -d payment_method=pm_card_visa \
  -d confirm=true \
  -d application_fee_amount="321" \
  -H "Stripe-Account: acct_1LuN62ClUWl50Go4"
```

As soon as the direct charge is complete, application and stripe fees are collected from the amount and the connected account’s pending balance increases by the net amount:

![Diagram of the direct funds flow](https://cjav.dev/connect/03-direct-charge-2.png)


### Destination charges with application fees

Here’s an example of adding an application fee of $3.21 to a destination charge for $40 to the connected account with ID `acct_1KPRf02R4eodYxfv`:


```bash
curl https://api.stripe.com/v1/payment_intents \
  -u sk_test_abc123: \
  -d amount=4000 \
  -d currency="usd" \
  -d payment_method=pm_card_visa \
  -d confirm=true \
  -d application_fee_amount="321" \
  -d "transfer_data[destination]=acct_1KPRf02R4eodYxfv"
```

Recall that a destination charge combines two actions: collecting payment on the platform account, then creating a transfer that moves money to the connected account. Here’s what the flow looks like with application fees:


<img src="https://cjav.dev/connect/04-destination-charge-app-fee.png" alt="" />


From the platform account’s dashboard, you’ll find the collected fees in the same place as those collected with direct charges, here’s what it looks like in this scenario:

<img src="https://cjav.dev/connect/05-platform-collected-fees-destination.png" alt="" />

If your user has access to the Express dashboard, this is how they would see the fee when they log-in:

<img src="https://cjav.dev/connect/06-express-dashboard-fees.png" alt="" />


### Separate charge and transfer application fees

Application fees are **not** supported for the separate charge and transfer funds flow. Instead, you’ll need to use one of the other approaches that we’ll outline next.

**Reasons to consider using application fees to take a cut:**

* You use direct charges
* Your users need to understand the amount the platform account collects and see that as a separate fee
* You need to query the API for the list of fees you’ve collected


## Transfer a smaller amount

The second solution for taking a cut is to transfer an amount less than the total payment to the connected account. You can use this with destination charges or separate charges and transfers. This approach is less explicit about the fee being collected from the platform. No objects are explicitly created representing the amount you, the platform, are collecting. Furthermore, connected accounts will only see the amount transferred into their account without any breakdown of application fees.


### Destination charge with smaller transfer

Here's an example of collecting a destination charge for $40 and then transferring $35 to the connected account with ID `acct_1KPRf02R4eodYxfv`:


```bash
curl https://api.stripe.com/v1/payment_intents \
  -u sk_test_abc123: \
  -d amount=4000 \
  -d currency="usd" \
  -d payment_method=pm_card_visa \
  -d confirm=true \
  -d "transfer_data[amount]=3500" \
  -d "transfer_data[destination]=acct_1KPRf02R4eodYxfv"
```

Notice that instead of passing the `application_fee_amount`, we include a new `transfer_data[amount]` parameter with the amount we want transferred to the destination connected account.

From your platform dashboard, you’ll see links to the relevant transfer, the amount transferred, and the destination connected account where the transfer was sent:


<img src="https://cjav.dev/connect/07-platform-sct-payment.png" alt="" />


The connected account will see that they received a payment of $35 and they don’t see any fees. Original charge amount is not shared, and this may make reporting more difficult as it does not create an application fee object.



<img src="https://cjav.dev/connect/08-express-dashboard-sct.png" alt="" />



<img src="https://cjav.dev/connect/09-destination-charge-smaller-transfer.png" alt="" />



### Separate charge and transfer smaller amount

When using separate charges and transfers, you make the API call to create the payment related object, and the API call to create the transfer, so you can transfer less than the payment total. Since the most common use-case for SCT is to split payments across several connected accounts, for this example we’ll use a food delivery service example where we’ll collect a $40 payment, then send $10 to the driver’s connected account and $23 to the restaurant’s connected account.

First, we’ll collect a $40 payment from the end customer to our platform using a `transfer_group`:


```bash
curl https://api.stripe.com/v1/payment_intents \
  -u sk_test_abc123: \
  -d amount=4000 \
  -d currency=usd \
  -d payment_method=pm_card_visa \
  -d confirm=true \
  -d transfer_group=demo-03
```


The payment from the platform dashboard perspective appears as any payment collected outside of the context of Connect:

<img src="https://cjav.dev/connect/10-plat-sct-payment.png" alt="" />

Then, using the ID of that payment intent’s latest charge as the source transaction, we’ll create a transfer for $10 to the driver’s connected account (`acct_1KPRf02R4eodYxfv`).


```bash
curl https://api.stripe.com/v1/transfers \
  -u sk_test_abc123: \
  -d amount=1000 \
  -d currency=usd \
  -d destination=acct_1KPRf02R4eodYxfv \
  -d transfer_group=demo-04 \
  -d source_transaction=ch_3MRIjjCZ6qsJgndJ0s4F8WUP
```

<img src="https://cjav.dev/connect/11-express-sct-payment-driver.png" alt="" />


Next, we’ll send a second transfer of $23 to the restaurant’s connected account `acct_1KP8JM2ROgShXwm9`.


```bash
curl https://api.stripe.com/v1/transfers \
  -u sk_test_abc123: \
  -d amount=2300 \
  -d currency=usd \
  -d destination=acct_1KP8JM2ROgShXwm9 \
  -d transfer_group=demo-04 \
  -d source_transaction=ch_3MRIjjCZ6qsJgndJ0s4F8WUP
```

<img src="https://cjav.dev/connect/12-express-sct-payment-restaurant.png" alt="" />

Again from the platform dashboard, we can see the transfer:

Here’s a diagram of the food delivery use-case. Again, the end customer pays $40, then the driver receives $10 and the restaurant receives $23. After paying the Stripe fees, the platform keeps $5.54.

<img src="https://cjav.dev/connect/13-sct.png" alt="" />

To understand what was included in the transfer, you’ll need to look under [Balances > Transfers](https://dashboard.stripe.com/test/connect/transfers) then drill into the specific transfer which has links to the source transaction and the payment created on the destination connected account.  \


**Reasons to consider transferring a smaller amount:**

* You need to make transfers to several connected accounts
* You don’t want your users to see the application fee amounts or to know how much the original charge was for
* You do not need to query for the list of fees collected and will build your own reporting solution


## Collect a one-time payment using account debits

There are a handful of use-cases where you might need to move money one-time from the connected account to your platform. You might need to recover funds for a previous refund, correct an error on the connected account’s balance, or as a one-time fee for your products or services. With Connect, your [platform can debit](https://stripe.com/docs/connect/account-debits#transferring-from-a-connected-account) the Stripe balance of an Express or Custom account using one of two methods: charge _or_ transfer.


### Collect a one-time payment from a connected account with a Charge

Using the lower-level Charges API (not PaymentIntents), you can use the connected account as the source and create a charge that will collect payment from the connected account to your platform:


```bash
curl https://api.stripe.com/v1/charges \
  -u sk_test_1234: \
  -d "amount"=1500 \
  -d "currency"="usd" \
  -d "source"="acct_1LuN62ClUWl50Go4"
```


### Collect a one-time payment from a connected account with a Transfer

Recall that a Transfer moves money from one Stripe account to another. Most of the time, we use Transfers indirectly with destination charges, or directly with separate charge and transfer to move money from the platform to the connected account. In this scenario, we’ll create a Transfer to move money _from_ the connected account back to the platform.

This time, we need to use the Stripe-Account header set to the ID of the connected account. This means we’re creating the Transfer _on_ the connected account (`acct_1LuN62ClUWl50Go4`) and the destination is the platform (`acct_1EceeUCZ6qsJgndJ`).


```bash
curl https://api.stripe.com/v1/transfers \
  -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
  -H "Stripe-Account: acct_1LuN62ClUWl50Go4" \
  -d "amount"=1500 \
  -d "currency"="usd" \
  -d "destination"="acct_1EceeUCZ6qsJgndJ"
```


Note that account debits only work for Express and Custom account types and there are several other restrictions you’ll need to review in the [documentation](https://site-admin.stripe.com/docs/connect/account-debits#transferring-from-a-connected-account).

**Reasons to consider using account debits:**

* One-off payments from the connected account to the platform


## Collect recurring payments from your users as customers

While the title of the article references taking a cut or commission on payments you facilitate, you might want to monetize your platform without commissions.  Let's say you want to collect $10 USD each month from your users, owners of the connected accounts with Stripe Billing. This is not possible if you only have the connected account. You’ll need to collect payment details, create a Customer, and create a Subscription instead.

When integrating with Stripe's Billing APIs for recurring payments, it's important to keep in mind that creating a Subscription requires having a Customer object, and you cannot use the connected account for this. A Customer and an Account are completely separate objects in Stripe's API, and there is no way to turn one into the other or "share" information between them.

The most common approach for platforms using Billing is to create a separate Customer object and handle all the Billing logic like payment method updates, handling payments, and proration using Subscriptions. To mitigate the confusion between the Account and the Customer objects for your users, you can use metadata on both the Customer and Account to track the association.

If you’re interested in taking this approach, take a look at the [SaaS fundamentals](https://dev.to/cjav_dev/series/19251) series to get started.

Consider collecting payment from your users as customers if:

* You want to collect $N each month
* You do not want to collect a small part of each transaction


## Conclusion

Your approach to taking a cut from payments your platform facilitates will depend on a number of factors. Here’s a summary of my recommendations:

If you want your users to see the fees collected, use application fees with the `application_fee_amount` parameter for direct or destination charges.

If you want to obfuscate the charge or fee totals, use transfers with amounts less than the payment total.

If you want to charge a fixed monthly fee, or a usage based amount every month, create platform customers for each of your users and charge them with a Stripe Billing subscription.


## About the author

![CJ Avilla](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mef4k8k1jhnula9b1mxc.png)


[CJ Avilla](https://cjav.dev) ([@cjav_dev](https://twitter.com/cjav_dev)) is a Developer Advocate at Stripe, a Ruby on Rails developer, and a [YouTuber](https://www.youtube.com/CJAvilla). He loves learning and teaching new programming languages and web frameworks. When he’s not at his computer, he’s spending time with his family or on a bike ride 🚲.