# Charge SaaS fees to your connected accounts Use Billing to charge SaaS fee subscriptions directly to your connected accounts. You can use Stripe Billing to charge your connected accounts a recurring subscription fee for using your platform. ## Create a product with a recurring price Create a product and price representing your subscription fee. You can use the API or the Dashboard. #### Dashboard In your Dashboard, go to the [Product catalog](https://dashboard.stripe.com/products?active=true) and click **+ Create product**. [Create a product](https://docs.stripe.com/products-prices/manage-prices.md#create-product) and assign a price to it. To configure the price as a subscription, select **Recurring** pricing and set the **Billing period**. After you add the product and price, record the price ID to use later when you create a subscription. The price ID looks like `price_xxxxxxxxxxxxxx`. In the Dashboard, you can copy the price ID to the clipboard from the product details page. In the Pricing section, click the overflow menu (⋯) next to the subscription price, then click **Copy price ID**. #### API [Create a Product](https://docs.stripe.com/api/products/create.md?api-version=preview) and define the default price as a recurring charge. Return the price information by including `default_price` in the `expand` parameter. ```curl curl https://api.stripe.com/v1/products \ -u "<>:" \ -d "name=Connected account subscription fee" \ -d "default_price_data[unit_amount]=1000" \ -d "default_price_data[currency]=usd" \ -d "default_price_data[recurring][interval]=month" \ -d "expand[]=default_price" ``` Record the price ID (`price_xxxxxxxxxxxxxx`) to use later when you create a subscription. > If you accept balance payments in multiple currencies, [create a separate price](https://docs.stripe.com/products-prices/manage-prices.md#create-price) in each currency for each product. For example, if you create a product with a USD price, and want to charge a connected account for it in EUR, add a price for that product in EUR. ## Create subscriptions to charge your connected accounts You can collect SaaS subscription fees directly from a connected account’s Stripe balance. The connected account must meet the following requirements: - It must have both the `merchant` and `customer` configurations. - Its `merchant` configuration’s `card_payments` capability must be active. - Its available balance must have sufficient funds to make a full payment. #### Use SetupIntents To implement a custom process for your connected account owners to purchase subscriptions, [use a SetupIntent](https://docs.stripe.com/payments/save-and-reuse.md?platform=web&ui=elements) to attach a `PaymentMethod` to their `Account` object. To attach [payment from the account’s Stripe balance](https://docs.stripe.com/payments/pay-with-balance.md) as the payment method when creating a SetupIntent, specify the following values: ```curl curl https://api.stripe.com/v1/setup_intents \ -u "<>:" \ -H "Stripe-Version: 2025-04-30.preview" \ -d "payment_method_types[]=stripe_balance" \ -d confirm=true \ -d customer_account=acct_xxxxxxxxxxxxxx \ -d usage=off_session \ -d "payment_method_data[type]=stripe_balance" ``` Successful creation of the SetupIntent returns data similar to the following: ```json { "id": "seti_123", "object": "setup_intent", "customer": "cus_xxxxxxxxxxxxxx", "customer_account": "acct_xxxxxxxxxxxxxx", "payment_method": "pm_xxxxxxxxxxxxxx", "status": "succeeded" } ``` You can then [create Subscriptions](https://docs.stripe.com/billing/subscriptions/build-subscriptions.md?payment-ui=elements&api-integration=checkout#create-session) using the account’s attached `PaymentMethod` by passing the `Account` ID as the [customer_account](https://docs.stripe.com/api/subscriptions/create.md?api-version=preview#create_subscription-customer). To create a subscription that collects payments from the account’s Stripe balance, set the payment method type explicitly, as in the following example. ```curl curl https://api.stripe.com/v1/subscriptions \ -u "<>:" \ -H "Stripe-Version: 2025-04-30.preview" \ -d customer_account=acct_xxxxxxxxxxxxxx \ -d default_payment_method=pm_xxxxxxxxxxxxxx \ -d "items[0][price]=price_xxxxxxxxxxxxxx" \ -d "items[0][quantity]=1" \ -d "payment_settings[payment_method_types][0]=stripe_balance" ``` #### Use Embedded Checkout With [Embedded Checkout](https://docs.stripe.com/billing/subscriptions/build-subscriptions.md?payment-ui=checkout&ui=embedded-form), you embed a payment form on your site that your connected account owners can access to purchase subscriptions. > Embedded Checkout doesn’t support [payments directly from Stripe balances](https://docs.stripe.com/payments/pay-with-balance.md). To collect subscription fees from Stripe balances, create a custom confirmation flow using SetupIntents. #### Create a Checkout Session Create a Checkout Session, and pass the following parameters: - Add a hash to the [line_items](https://docs.stripe.com/api/checkout/sessions/create.md?api-version=preview#create_checkout_session-line_items) array. Set the `quantity` to `1` and the `price` to the price ID of the product you created. - Set [mode](https://docs.stripe.com/api/checkout/sessions/create.md?api-version=preview#create_checkout_session-mode) to `subscription`. - Set [ui_mode](https://docs.stripe.com/api/checkout/sessions/create.md?api-version=preview#create_checkout_session-ui_mode) to `embedded_page`. - Set [return_url](https://docs.stripe.com/api/checkout/sessions/create.md?api-version=preview#create_checkout_session-return_url) to the address of the page to return the connected account owner to after they attempt payment. In the URL, set `session_id` to the `{CHECKOUT_SESSION_ID}` template variable, as shown in the following code example. Checkout replaces the variable with the Checkout Session ID so your return page can access it. - Set [customer_account](https://docs.stripe.com/api/checkout/sessions/create.md?api-version=preview#create_checkout_session-customer_account) to the ID of the `Account` that you want to charge. ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -H "Stripe-Version: 2025-04-30.preview" \ -d "line_items[0][price]=price_xxxxxxxxxxxxxx" \ -d "line_items[0][quantity]=1" \ -d mode=subscription \ -d ui_mode=embedded_page \ --data-urlencode "return_url=https://example.com/checkout/return?session_id={CHECKOUT_SESSION_ID}" \ -d customer_account=acct_xxxxxxxxxxxxxxxx ``` > You’re responsible for creating and hosting the page that Stripe returns the connected account owner to after payment. They return to that page regardless of whether the payment succeeds. The returned `Session` object includes a [client_secret](https://docs.stripe.com/api/checkout/sessions/object.md?api-version=preview#checkout_session_object-client_secret) value. Record it to use when you render the checkout form. ### Render the embedded checkout form Render the embedded checkout form using the `client_secret` from the previous step, as shown in the following code examples. #### HTML + JS 1. Load Stripe.js Use *Stripe.js* (Use Stripe.js’ APIs to tokenize customer information, collect sensitive card data, and accept payments with browser payment APIs) to remain *PCI compliant* (Any party involved in processing, transmitting, or storing credit card data must comply with the rules specified in the Payment Card Industry (PCI) Data Security Standards. PCI compliance is a shared responsibility and applies to both Stripe and your business) by ensuring that payment details are sent directly to Stripe without hitting your server. Always load Stripe.js from `js.stripe.com` to remain compliant. Don’t include the script in a bundle or host it yourself. 1. Define the payment form To securely collect the customer’s information, create an empty placeholder `div`. Stripe inserts an iframe into the `div`. Checkout is available as part of [Stripe.js](https://docs.stripe.com/js.md). Include the Stripe.js script on your page by adding it to the head of your HTML file. Next, create an empty DOM node (container) to use for mounting. ```html Accept a payment
``` 1. Initialize Stripe.js Initialize Stripe.js with your publishable API key. 1. Fetch a Checkout Session client secret Create an asynchronous `fetchClientSecret` function that makes a request to your server to [create a Checkout Session](https://docs.stripe.com/api/checkout/sessions/create.md?api-version=preview) and retrieve the client secret. 1. Initialize Checkout Initialize Checkout with your `fetchClientSecret` function and mount it to the placeholder `
` in your payment form. Checkout renders in an iframe that securely sends payment information to Stripe over an HTTPS connection. Avoid placing Checkout within another iframe because some payment methods require redirecting to another page for payment confirmation. ```javascript // Initialize Stripe.js const stripe = Stripe('<>'); initialize(); // Fetch Checkout Session and retrieve the client secret async function initialize() { const fetchClientSecret = async () => { const response = await fetch("/create-checkout-session", { method: "POST", }); const { clientSecret } = await response.json(); return clientSecret; }; // Initialize Checkout const checkout = await stripe.createEmbeddedCheckoutPage({ fetchClientSecret, }); // Mount Checkout checkout.mount('#checkout'); } ``` #### React 1. Add Stripe to your React app To stay *PCI compliant* (Any party involved in processing, transmitting, or storing credit card data must comply with the rules specified in the Payment Card Industry (PCI) Data Security Standards. PCI compliance is a shared responsibility and applies to both Stripe and your business) by ensuring that payment details go directly to Stripe and never reach your server, install [React Stripe.js](https://docs.stripe.com/sdks/stripejs-react.md). ```bash npm install --save @stripe/react-stripe-js @stripe/stripe-js ``` 1. Load Stripe.js To configure the Stripe library, call `loadStripe()` with your Stripe publishable API key. Create an `EmbeddedCheckoutProvider`. Pass the returned `Promise` to the provider. 1. Fetch a Checkout Session client secret Create an asynchronous `fetchClientSecret` function that makes a request to your server to [create a Checkout Session](https://docs.stripe.com/api/checkout/sessions/create.md?api-version=preview) and retrieve the client secret. 1. Initialize Checkout To allow the child components to access the Stripe service through the embedded Checkout consumer, pass the resulting promise from `loadStripe` and the `fetchClientSecret` function as an `option` to the embedded Checkout provider. ```jsx import * as React from 'react'; import {loadStripe} from '@stripe/stripe-js'; import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js'; // Make sure to call `loadStripe` outside of a component's render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe('pk_test_123', { }); const App = ({fetchClientSecret}) => { const options = {fetchClientSecret}; return ( ); } ``` ### Show a return page After your customer attempts payment, Stripe redirects them to a return page that you host on your site. When you created the Checkout Session, you specified the URL of the return page in the [return_url](https://docs.stripe.com/api/checkout/sessions/create.md?api-version=preview#create_checkout_session-return_url) parameter. You also configured it to include the Checkout Session ID. > During payment, some payment methods redirect the customer to an intermediate page, such as a bank authorization page. When they complete that page, Stripe redirects them to your return page. Handle the payment result on your return page. When it loads, use the Checkout Session ID from the URL to [retrieve the Checkout Session](https://docs.stripe.com/api/checkout/sessions/retrieve.md?api-version=preview) and examine its [status](https://docs.stripe.com/api/checkout/sessions/object.md?api-version=preview#checkout_session_object-status). The status value corresponds to the payment result as follows: - `complete`: The payment succeeded. Use the information from the Checkout Session to render a success page. - `open`: The payment failed or was canceled. Remount Checkout so that your customer can try again. ```js // Retrieve a Checkout Session // Use the session ID initialize(); async function initialize() { const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); const sessionId = urlParams.get('session_id'); const response = await fetch(`/session-status?session_id=${sessionId}`); const session = await response.json(); // Handle the session according to its status if (session.status == 'open') { // Remount embedded Checkout window.location.replace('http://localhost:4242/checkout.html') } else if (session.status == 'complete') { document.getElementById('success').classList.remove('hidden'); document.getElementById('customer-email').textContent = session.customer_email; // Show success page // Optionally use session.payment_status or session.customer_email // to customize the success page } } ``` ```javascript // Add an endpoint to fetch the Checkout Session status app.get('/session_status', async (req, res) => { const session = await stripe.checkout.sessions.retrieve(req.query.session_id); const customer = await stripe.customers.retrieve(session.customer); res.send({ status: session.status, payment_status: session.payment_status, customer_email: customer.email }); }); ``` ## Configure your integration to manage Stripe balance payment failures When you collect a payment from a connected account’s Stripe balance, the account’s available balance must have sufficient funds to make the full payment. Otherwise, the payment fails. If you plan to collect payments directly from your connected accounts’ Stripe balances, we recommend configuring your integration to manage balance-related payment failures. ### Avoid balance payment failures Because payments from a connected account’s Stripe balance rely on its available funds, you can avoid payment failures by taking steps to maximize your connected accounts’ balances. #### Tailor connected account payout schedules Coordinate your payout schedules with your subscription billing cycles. For example, if you charge subscription fees on the first day of each month, and schedule weekly payouts on Mondays, then months with more Mondays have more payouts. Those months have lower available balances than months with fewer payouts, making payment failures more likely. Another way to avoid payment failures due to payouts is to change to manual payouts before a subscription payment. At a set time before each subscription payment, if a connected account has sufficient available funds, switch it to [manual payouts](https://docs.stripe.com/connect/manage-payout-schedule.md) so the subscription payment gets paid before the automatic payout clears the account. After the subscription payment, resume automatic payouts. #### Set a minimum balance on connected accounts You can prevent automatic payouts from reducing a connected account’s available balance below a certain amount by defining a minimum balance for that account. You can programmatically configure minimum balances per connected account using the [Balance Settings API](https://docs.stripe.com/payouts/minimum-balances-for-automatic-payouts.md#minimum-balances-platforms). Alternatively, you can manually set the minimum balance for each connected account in the Dashboard: 1. Find the account in your Dashboard. 1. From the account’s overflow menu (⋯), select [View Dashboard as…](https://docs.stripe.com/connect/dashboard/managing-individual-accounts.md#view-the-dashboard-as-a-connected-account). 1. Click the gear icon and select **Settings**. 1. Under Account Settings, click **Business**. 1. Select the **External payout accounts and scheduling** tab. 1. Turn on **Keep a minimum amount in your payments balance** and enter an amount. ### Handle balance payment failures Set up [webhooks](https://docs.stripe.com/webhooks.md) and [event destinations](https://docs.stripe.com/billing/subscriptions/webhooks.md#events) to receive notifications about subscription payments. Identify payment failures by listening for the `invoice.payment_failed` event. When a payment fails: - The PaymentIntent status changes to `requires_action`. - The Subscription status remains `incomplete` for the current invoice. - The Subscription continues to generate invoices, which remain in `draft` status. #### Automatic retries [Enable retries](https://dashboard.stripe.com/revenue_recovery/retries) for failed balance payments due to insufficient available funds on recurring subscription invoices. Automatic retries schedules 2 payment attempts following an insufficient funds failure: - The first retry happens at least 24 hours after the failed payment. - If needed, the second retry happens on the next Sunday following the first retry. This retry schedule aims to give your connected account time to accumulate more funds in their Stripe balance and to avoid conflicts with scheduled payouts. You can enable retries for any amount for no additional fee. #### Manually retrying If a payment from a Stripe balance fails due to insufficient available funds, you can manually retry it by following these steps: 1. Set the connected account’s payout schedule interval to `manual`. 1. Listen for the next payment that comes into the connected account, then [check the account’s available balance](https://docs.stripe.com/api/balance/balance_retrieve.md). 1. If the available balance is equal to or greater than the subscription fee, set the unpaid invoice’s payment method to `stripe_balance` and retry it. Otherwise, continue listening for payments until the available balance is enough to pay the invoice. 1. If the payment succeeds, restore the connected account’s normal payout schedule. Instead of retrying a failed payment from a Stripe balance, you can try using a different payment method by [specifying it directly on the invoice](https://docs.stripe.com/billing/collection-method.md#set-collection-method-invoice). You can also implement a flow that allows connected accounts to [update their own subscription payment methods](https://docs.stripe.com/billing/subscriptions/payment-methods-setting.md#update-payment-method). ## Next steps When your connected accounts can accept payments and you’ve configured your monetization, [set up payouts](https://docs.stripe.com/connect/saas/tasks/payout.md) so your connected account can transfer their Stripe balance to their external account.