# Build a SaaS platform with Accounts v1 Create connected accounts using Accounts v1, then facilitate direct payments to them on your SaaS platform. > #### Accounts v2 API integrations > > This guide only applies to existing Connect platforms that use the Accounts v1 API. If you’re a new Connect user, or if you use the Accounts v2 API, see the [v2 SaaS platform guide](https://docs.stripe.com/connect/saas.md). This guide covers letting your users accept payments, moving a portion of your users’ earnings into your balance, and paying out the remainder to your users’ bank accounts. To illustrate these concepts, we’ll use an example platform that lets businesses build their own online stores. # React Native ![](https://b.stripecdn.com/docs-statics-srv/assets/ios-overview.9e0d68d009dc005f73a6f5df69e00458.png) This integration combines all of the steps required to pay, including collecting payment details and confirming the payment, into a single sheet that displays on top of your app. > #### Accounts v2 API support > > The Payment Sheet doesn’t support *customer-configured Accounts* (Account configurations represent role-based functionality that you can enable for accounts, such as merchant, customer, or recipient). It only supports `Customer` objects. ## Prerequisites 1. [Register your platform](https://dashboard.stripe.com/connect). 1. [Verify and add business details in Dashboard](https://dashboard.stripe.com/account/onboarding). 1. [Complete your platform profile](https://dashboard.stripe.com/connect/settings/profile). 1. [Customise your brand settings](https://dashboard.stripe.com/settings/connect/stripe-dashboard/branding). Add a business name, icon, and brand colour. ## Set up Stripe [Server-side] [Client-side] First, you need a Stripe account. [Register now](https://dashboard.stripe.com/register). ### Server-side This integration requires endpoints on your server that talk to the Stripe API. Use the official libraries for access to the Stripe API from your server: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### Client-side The [React Native SDK](https://github.com/stripe/stripe-react-native) is open source and fully documented. Internally, it uses the [native iOS](https://github.com/stripe/stripe-ios) and [Android](https://github.com/stripe/stripe-android) SDKs. To install Stripe’s React Native SDK, run one of the following commands in your project’s directory (depending on which package manager you use): #### yarn ```bash yarn add @stripe/stripe-react-native ``` #### npm ```bash npm install @stripe/stripe-react-native ``` Next, install some other necessary dependencies: - For iOS, go to the **ios** directory and run `pod install` to ensure that you also install the required native dependencies. - For Android, there are no more dependencies to install. > We recommend following the [official TypeScript guide](https://reactnative.dev/docs/typescript#adding-typescript-to-an-existing-project) to add TypeScript support. ### Stripe initialisation To initialise Stripe in your React Native app, either wrap your payment screen with the `StripeProvider` component, or use the `initStripe` initialisation method. Only the API [publishable key](https://docs.stripe.com/keys.md#obtain-api-keys) in `publishableKey` is required. The following example shows how to initialise Stripe using the `StripeProvider` component. ```jsx import { useState, useEffect } from 'react'; import { StripeProvider } from '@stripe/stripe-react-native'; function App() { const [publishableKey, setPublishableKey] = useState(''); const fetchPublishableKey = async () => { const key = await fetchKey(); // fetch key from your server here setPublishableKey(key); }; useEffect(() => { fetchPublishableKey(); }, []); return ( {/* Your app code here */} ); } ``` > Use your API [test keys](https://docs.stripe.com/keys.md#obtain-api-keys) while you test and develop, and your [live mode](https://docs.stripe.com/keys.md#test-live-modes) keys when you publish your app. ## Create a connected account When a user (seller or service provider) signs up on your platform, create a user [Account](https://docs.stripe.com/api/accounts.md) (referred to as a *connected account*) so you can accept payments and move funds to their bank account. Connected accounts represent your user in Stripe’s API and help facilitate the collection of onboarding requirements so Stripe can verify the user’s identity. In our store builder example, the connected account represents the business setting up their online store. ### Step 2.1: Create a connected account and prefill information (Server-side) Use the `/v1/accounts` API to [create](https://docs.stripe.com/api/accounts/create.md) a connected account. You can create the connected account by using the [default connected account parameters](https://docs.stripe.com/connect/migrate-to-controller-properties.md), or by specifying the account type. #### With default properties ```curl curl -X POST https://api.stripe.com/v1/accounts \ -u "<>:" ``` #### With account type ```curl curl https://api.stripe.com/v1/accounts \ -u "<>:" \ -d type=standard ``` If you’ve already collected information for your connected accounts, you can pre-fill that information on the `Account` object. You can pre-fill any account information, including personal and business information, external account information, and so on. After creating the `Account`, create a [Person](https://docs.stripe.com/api/persons/create.md) to represent the person responsible for opening the account, with `relationship.representative` set to true and any account information you want to prefill (for example, their first and last name). ```curl curl https://api.stripe.com/v1/accounts/{{ACCOUNT_ID}}/persons \ -u "<>:" \ -d first_name=Jenny \ -d last_name=Rosen \ -d "relationship[representative]=true" ``` Connect Onboarding doesn’t ask for the prefilled information. However, it does ask the account holder to confirm the prefilled information before accepting the [Connect service agreement](https://docs.stripe.com/connect/service-agreement-types.md). When testing your integration, prefill account information using [test data](https://docs.stripe.com/connect/testing.md). ### Step 2.2: Create an account link (Server-side) You can create an account link by calling the [Account Links](https://docs.stripe.com/api/account_links.md) API with the following parameters: - `account` - `refresh_url` - `return_url` - `type` = `account_onboarding` ```curl curl https://api.stripe.com/v1/account_links \ -u "<>:" \ -d "account={{CONNECTEDACCOUNT_ID}}" \ --data-urlencode "refresh_url=https://example.com/reauth" \ --data-urlencode "return_url=https://example.com/return" \ -d type=account_onboarding ``` ### Step 2.3: Redirect your user to the account link URL (Client-side) The response to your [Account Links](https://docs.stripe.com/api/account_links.md) request includes a value for the key `url`. Account Links are temporary and are single-use only because they grant access to the connected account user’s personal information. If you want to prefill information, you must do so before generating the account link. After you create the account link for a Standard account, you won’t be able to read or write information for the account. Send this URL to your app and open it in the browser for the user to complete the Connect Onboarding flow. > Don’t email, text, or otherwise send account link URLs outside your platform application. Instead, provide them to the authenticated account holder within your application. ### Step 2.4: Handle the user returning to your platform (Client-side) *Connect* (Connect is Stripe's solution for multi-party businesses, such as marketplace or software platforms, to route payments between sellers, customers, and other recipients) Onboarding requires you to pass both a `return_url` and `refresh_url` to handle all cases where the user is redirected to your platform. It’s important that you implement these correctly to provide the best experience for your user. You can set up a [deep link](https://reactnavigation.org/docs/deep-linking/) to enable the Stripe webpage to redirect to your app automatically. #### return_url Stripe issues a redirect to this URL when the user completes the Connect Onboarding flow. This doesn’t mean that all information has been collected or that there are no outstanding requirements on the account. This only means the flow was entered and exited properly. No state is passed through this URL. After a user is redirected to your `return_url`, check the state of the `details_submitted` parameter on their account by doing either of the following: - Listening to `account.updated` *webhooks* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) - Calling the [Accounts](https://docs.stripe.com/api/accounts.md) API and inspecting the returned object #### refresh_url Your user is redirected to the `refresh_url` in these cases: - The link is expired (a few minutes went by since the link was created) - The user already visited the link (the user refreshed the page or clicked back or forward in the browser) - Your platform is no longer able to access the account - The account has been rejected Your `refresh_url` should trigger a method on your server to call [Account Links](https://docs.stripe.com/api/account_links.md) again with the same parameters, and redirect the user to the Connect Onboarding flow to create a seamless experience. ### Step 2.5: Handle users that haven’t completed onboarding A user that is redirected to your `return_url` might not have completed the onboarding process. Use the `/v1/accounts` endpoint to retrieve the user’s account and check for `charges_enabled`. If the account isn’t fully onboarded, provide UI prompts to allow the user to continue onboarding later. The user can complete their account activation through a new account link (generated by your integration). You can check the state of the `details_submitted` parameter on their account to see if they’ve completed the onboarding process. ## Enable payment methods View your [payment methods settings](https://dashboard.stripe.com/settings/connect/payment_methods) and enable the payment methods you want to support. Card payments are enabled by default but you can enable and disable payment methods as needed. ## Add an endpoint [Server-side] > #### Note > > To display the PaymentSheet before you create a PaymentIntent, see [Collect payment details before creating an Intent](https://docs.stripe.com/payments/accept-a-payment-deferred.md?type=payment). This integration uses three Stripe API objects: 1. [PaymentIntent](https://docs.stripe.com/api/payment_intents.md): Stripe uses this to represent your intent to collect payment from a customer, tracking your charge attempts and payment state changes throughout the process. 1. (Optional) A [customer-configured Account](https://docs.stripe.com/api/v2/core/accounts/object.md#v2_account_object-applied_configurations) or a [Customer](https://docs.stripe.com/api/customers.md) object: To set up a payment method for future payments, you must attach it to a customer. Create an object to represent your customer when they create an account with your business. If your customer makes a payment as a guest, you can create an `Account` or `Customer` object before payment and associate it with your own internal representation of the customer’s account later. 1. (Optional) [CustomerSession](https://docs.stripe.com/api/customer_sessions.md): Information on the object that represents your customer is sensitive, and can’t be retrieved directly from an app. A `CustomerSession` grants the SDK temporary scoped access to the `Account` or `Customer` and provides additional configuration options. See a complete list of [configuration options](https://docs.stripe.com/api/customer_sessions/create.md#create_customer_session-components). > If you never save cards for customers and don’t allow returning customers to reuse saved cards, you can omit the `Account` or `Customer` object and the `CustomerSession` object from your integration. For security reasons, your app can’t create these objects. Instead, add an endpoint on your server that: 1. Retrieves the `Account` or `Customer`, or creates a new one. 1. Creates a [CustomerSession](https://docs.stripe.com/api/customer_sessions.md) for the `Account` or `Customer`. 1. Creates a `PaymentIntent` with the [amount](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-amount), [currency](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-currency), and either the [customer_account](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer_account) or the [customer](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer). 1. Returns the `PaymentIntent`’s *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)), the `CustomerSession`’s `client_secret`, the ID of the `Account` or `Customer`, and your [publishable key](https://dashboard.stripe.com/apikeys) to your app. The payment methods shown to customers during the checkout process are also included on the PaymentIntent. You can let Stripe pull payment methods from your Dashboard settings or you can list them manually. Regardless of the option you choose, note that the currency passed in the PaymentIntent filters the payment methods shown to the customer. For example, if you pass `eur` on the PaymentIntent and have OXXO enabled in the Dashboard, OXXO won’t be shown to the customer because OXXO doesn’t support `eur` payments. Unless your integration requires a code-based option for offering payment methods, Stripe recommends the automated option. This is because Stripe evaluates the currency, payment method restrictions, and other parameters to determine the list of supported payment methods. Payment methods that increase conversion and that are most relevant to the currency and customer’s location are prioritised. #### Manage payment methods from the Dashboard You can manage payment methods from the [Dashboard](https://dashboard.stripe.com/settings/payment_methods). Stripe handles the return of eligible payment methods based on factors such as the transaction’s amount, currency, and payment flow. The PaymentIntent is created using the payment methods you configured in the Dashboard. If you don’t want to use the Dashboard or if you want to specify payment methods manually, you can list them using the `payment_method_types` attribute. #### curl ```bash # Create a Customer (use an existing Customer ID if this is a returning customer) curl https://api.stripe.com/v1/customers \ -u <>: \ -X "POST" \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" # Create an CustomerSession for the Customer curl https://api.stripe.com/v1/customer_sessions \ -u <>: \ -X "POST" \ -d "customer"="{{CUSTOMER_ID}}" \ -d "components[mobile_payment_element][enabled]"=true \ -d "components[mobile_payment_element][features][payment_method_save]"=enabled \ -d "components[mobile_payment_element][features][payment_method_redisplay]"=enabled \ -d "components[mobile_payment_element][features][payment_method_remove]"=enabled # Create a PaymentIntent curl https://api.stripe.com/v1/payment_intents \ -u <>: \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" -X "POST" \ -d "customer"="{{CUSTOMER_ID}}" \ -d "amount"=1099 \ -d "currency"="eur" \ -d "automatic_payment_methods[enabled]"=true \ -d application_fee_amount="123" \ ``` #### Listing payment methods manually #### curl ```bash # Create a Customer (use an existing Customer ID if this is a returning customer) curl https://api.stripe.com/v1/customers \ -u <>: \ -X "POST" \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" # Create an CustomerSession for the Customer curl https://api.stripe.com/v1/customer_sessions \ -u <>: \ -X "POST" \ -d "customer"="{{CUSTOMER_ID}}" \ -d "components[mobile_payment_element][enabled]"=true \ -d "components[mobile_payment_element][features][payment_method_save]"=enabled \ -d "components[mobile_payment_element][features][payment_method_redisplay]"=enabled \ -d "components[mobile_payment_element][features][payment_method_remove]"=enabled # Create a PaymentIntent curl https://api.stripe.com/v1/payment_intents \ -u <>: \ -H "Stripe-Account: {{CONNECTED_ACCOUNT_ID}}" -X "POST" \ -d "customer"="{{CUSTOMER_ID}}" \ -d "amount"=1099 \ -d "currency"="eur" \ -d "payment_method_types[]"="bancontact" \ -d "payment_method_types[]"="card" \ -d "payment_method_types[]"="ideal" \ -d "payment_method_types[]"="klarna" \ -d "payment_method_types[]"="sepa_debit" \ -d application_fee_amount="123" \ ``` > Each payment method needs to support the currency passed in the PaymentIntent and your business needs to be based in one of the countries each payment method supports. See the [Payment method integration options](https://docs.stripe.com/payments/payment-methods/integration-options.md) page for more details about what’s supported. ## Integrate the payment sheet [Client-side] Before displaying the mobile Payment Element, your checkout page should: - Show the products being purchased and the total amount - Collect any required shipping information - Include a checkout button to present Stripe’s UI In the checkout of your app, make a network request to the backend endpoint you created in the previous step and call `initPaymentSheet` from the `useStripe` hook. #### Accounts v2 ```javascript export default function CheckoutScreen() { const { initPaymentSheet, presentPaymentSheet } = useStripe(); const [loading, setLoading] = useState(false); const fetchPaymentSheetParams = async () => { const response = await fetch(`${API_URL}/payment-sheet`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, }); const { paymentIntent, ephemeralKey, customer_account } = await response.json(); return { paymentIntent, ephemeralKey, customer_account, }; }; const initializePaymentSheet = async () => { const { paymentIntent, ephemeralKey, customer_account, } = await fetchPaymentSheetParams(); const { error } = await initPaymentSheet({ merchantDisplayName: "Example, Inc.", customerAccountId: customer_account, customerEphemeralKeySecret: ephemeralKey, paymentIntentClientSecret: paymentIntent, // Set `allowsDelayedPaymentMethods` to true if your business accepts payment // methods that complete payment after a delay, like SEPA Debit and Sofort. allowsDelayedPaymentMethods: true, defaultBillingDetails: { name: 'Jane Doe', } }); if (!error) { setLoading(true); } }; const openPaymentSheet = async () => { // see below }; useEffect(() => { initializePaymentSheet(); }, []); return (