Enable businesses on your platform to accept payments directly
Facilitate direct payments to businesses on your SaaS platform from their own customers.
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.

Integrate Stripe’s prebuilt payment UI into the checkout of your iOS app with the PaymentSheet class. See our sample integration on GitHub.
Prerequisites 
- Register your platform.
- Add business details to activate your account.
- Complete your platform profile.
- Customize your brand settings. Add a business name, icon, and brand color.
Set up StripeServer-sideClient-side
First, you need a Stripe account. Register now.
Server-side 
This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:
Client-side 
The Stripe iOS SDK is open source, fully documented, and compatible with apps supporting iOS 13 or above.
Note
For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository.
Configure the SDK with your Stripe publishable key on app start. This enables your app to make requests to the Stripe API.
Create a connected account
When a user (seller or service provider) signs up on your platform, create a user Account (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 Internet store.

Step 2.1: Create a connected account and prefill information Server-side 
Use the /v1/accounts
API to create a connected account. You can create the connected account by using the default connected account parameters, or by specifying the account type.
If you’ve already collected information for your connected accounts, you can prefill that information on the Account
object. You can prefill any account information, including personal and business information, external account information, and so on.
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.
When testing your integration, prefill account information using test data.
Step 2.2: Create an account link Server-side 
You can create an account link by calling the Account Links API with the following parameters:
account
refresh_
url return_
url type
=account_
onboarding
Step 2.3: Redirect your user to the account link URL Client-side 
The response to your Account Links request includes a value for the key url
. Redirect to this link to send your user into the flow. Account Links are temporary and are single-use only because they grant access to the connected account user’s personal information. Authenticate the user in your application before redirecting them to this URL. 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.
Security tip
Don’t email, text, or otherwise send account link URLs outside of 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 Onboarding requires you to pass both a return_
and refresh_
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 universal link to enable iOS 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_
, check the state of the details_
parameter on their account by doing either of the following:
refresh_url 
Your user is redirected to the refresh_
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_
should trigger a method on your server to call Account Links 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_
might not have completed the onboarding process. Use the /v1/accounts
endpoint to retrieve the user’s account and check for charges_
. 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_
parameter on their account to see if they’ve completed the onboarding process.
Enable payment methods
View your payment methods settings 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 endpointServer-side
Note
To display the PaymentSheet before you create a PaymentIntent, see Collect payment details before creating an Intent.
This integration uses three Stripe API objects:
PaymentIntent: Stripe uses this to represent your intent to collect payment from a customer, tracking your charge attempts and payment state changes throughout the process.
(Optional) Customer: To set up a payment method for future payments, you must attach it to a Customer. Create a Customer object when your customer creates an account with your business. If your customer is making a payment as a guest, you can create a Customer object before payment and associate it with your own internal representation of the customer’s account later.
(Optional) Customer Ephemeral Key: Information on the Customer object is sensitive, and can’t be retrieved directly from an app. An Ephemeral Key grants the SDK temporary access to the Customer.
Note
If you never save cards to a Customer and don’t allow returning Customers to reuse saved cards, you can omit the Customer and Customer Ephemeral Key objects from your integration.
For security reasons, your app can’t create these objects. Instead, add an endpoint on your server that:
- Retrieves the Customer, or creates a new one.
- Creates an Ephemeral Key for the Customer.
- Creates a PaymentIntent with the amount, currency, and customer. You can also optionally include the
automatic_
parameter. Stripe enables its functionality by default in the latest version of the API.payment_ methods - Returns the Payment Intent’s client secret, the Ephemeral Key’s
secret
, the Customer’s id, and your publishable key 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, know 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 prioritized.
Integrate the payment sheetClient-side
To display the mobile Payment Element on your checkout screen, make sure you:
- Display the products the customer is purchasing along with the total amount
- Use the Address Element to collect any required shipping information from the customer
- Add a checkout button to display Stripe’s UI
If PaymentSheetResult
is .
, inform the user (for example, by displaying an order confirmation screen).
Setting allowsDelayedPaymentMethods
to true allows delayed notification payment methods like US bank accounts. For these payment methods, the final payment status isn’t known when the PaymentSheet
completes, and instead succeeds or fails later. If you support these types of payment methods, inform the customer their order is confirmed and only fulfill their order (for example, ship their product) when the payment is successful.
Set up a return URLClient-side
The customer might navigate away from your app to authenticate (for example, in Safari or their banking app). To allow them to automatically return to your app after authenticating, configure a custom URL scheme and set up your app delegate to forward the URL to the SDK. Stripe doesn’t support universal links.
Handle post-payment eventsServer-side
Stripe sends a payment_intent.succeeded event when the payment completes. Use the Dashboard webhook tool or follow the webhook guide to receive these events and run actions, such as 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, and malicious clients could manipulate the response. Setting up your integration to listen for asynchronous events is what enables you to accept different types of payment methods with a single integration.
In addition to handling the payment_
event, we recommend handling these other events when collecting payments with the Payment Element:
Event | Description | Action |
---|---|---|
payment_intent.succeeded | Sent when a customer successfully completes a payment. | Send the customer an order confirmation and fulfill their order. |
payment_intent.processing | Sent when a customer successfully initiates a payment, but the payment has yet to complete. This event is most commonly sent when the customer initiates a bank debit. It’s followed by either a payment_ or payment_ event in the future. | Send the customer an order confirmation that indicates their payment is pending. For digital goods, you might want to fulfill the order before waiting for payment to complete. |
payment_intent.payment_failed | Sent when a customer attempts a payment, but the payment fails. | If a payment transitions from processing to payment_ , offer the customer another attempt to pay. |
Test the integration
See Testing for additional information to test your integration.
Testing
Test your account creation flow by creating accounts and using OAuth. Test your Payment methods settings for your connected accounts by logging into one of your test accounts and navigating to the Payment methods settings. Test your checkout flow with your test keys and a test account. You can use our test cards to test your payments flow and simulate various payment outcomes.
Payouts
By default, any charge that you create for a connected account accumulates in the connected account’s Stripe balance and is paid out on a daily rolling basis. Connected accounts can manage their own payout schedules in the Stripe Dashboard.