# 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.

# Web


## Prerequisites

1. [Register your platform](https://dashboard.stripe.com/connect).
2. [Verify and add business details in Dashboard](https://dashboard.stripe.com/account/onboarding).
3. [Complete your platform profile](https://dashboard.stripe.com/connect/settings/profile).
4. [Customize your brand settings](https://dashboard.stripe.com/settings/connect/stripe-dashboard/branding). Add a business name, icon, and brand color.

## Set up Stripe [Server-side]

Install Stripe’s official libraries so you can access the API from your application:

#### 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'
```

## 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 Internet store.
![Screenshot of Connect Onboarding form](https://b.stripecdn.com/docs-statics-srv/assets/Kavholm-Seamless-Standard.78b64d90c0bf87130c8b6ba1ef53df7f.png)

### Create a connected account and prefill information 

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 "<<YOUR_SECRET_KEY>>:"
```

#### With account type

```curl
curl https://api.stripe.com/v1/accounts \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d type=standard
```

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.

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 "<<YOUR_SECRET_KEY>>:" \
  -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).

### Create an account link 

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 "<<YOUR_SECRET_KEY>>:" \
  -d "account={{CONNECTEDACCOUNT_ID}}" \
  --data-urlencode "refresh_url=https://example.com/reauth" \
  --data-urlencode "return_url=https://example.com/return" \
  -d type=account_onboarding
```

### Redirect your user to the account link URL 

The response to your [Account Links](https://docs.stripe.com/api/account_links.md) 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, you won’t be able to read or write information for the account.

> 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.

### Handle the user returning to your platform 

Connect 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 use HTTP for your `return_url` and `refresh_url` while you’re in a testing environment (for example, to test with localhost), but live mode only accepts HTTPS. You must update testing URLs to HTTPS URLs before you go live.

#### 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
- 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.

### Handle users that haven’t completed onboarding 

If a user is redirected to your `return_url`, they 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). To see if they’ve completed the onboarding process, check the state of the `details_submitted` parameter on their account.

## 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. This guide assumes Bancontact, credit cards, EPS, iDEAL, Przelewy24, SEPA Direct Debit, and Sofort are enabled.

Before the payment form is displayed, 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. Lower priority payment methods are hidden in an overflow menu.

> The [embedded payment method settings component](https://docs.stripe.com/connect/supported-embedded-components/payment-method-settings.md) lets connected accounts configure the payment methods they offer at checkout, without needing to access the Stripe Dashboard.

## Accept a payment

Embed [Stripe Checkout](https://stripe.com/payments/checkout) as a payment form directly on your website or redirect users to a Stripe-hosted page to accept payments. Checkout supports multiple payment methods and automatically shows the most relevant ones to your customer. You can also use the Payment Element, a prebuilt UI component that is embedded as an iframe in your payment form, to accept multiple payment methods with a single frontend integration.

#### Stripe-hosted page

### Create a Checkout Session  (Client-side) (Server-side)

A Checkout Session controls what your customer sees in the embeddable payment form such as line items, the order amount and currency, and acceptable payment methods. When performing direct charges, Checkout uses the connected account’s branding settings. See the [Customize branding](https://docs.stripe.com/connect/direct-charges.md?platform=web&ui=stripe-hosted#branding) section for more information.

Unlike destination charges and separate charges and transfers, users (connected accounts) are responsible for handling disputes on direct charges—it’s not the responsibility of the platform.

On your server, make the following call to the Stripe API. After creating a Checkout Session, redirect your customer to the [URL](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-url) returned in the response.

```curl
curl https://api.stripe.com/v1/checkout/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -H "Stripe-Account: {{CONNECTEDACCOUNT_ID}}" \
  -d mode=payment \
  -d "line_items[0][price]={{PRICE_ID}}" \
  -d "line_items[0][quantity]=1" \
  -d "payment_intent_data[application_fee_amount]=123" \
  --data-urlencode "success_url=https://example.com/success"
```

- `line_items` - This argument represents items that your customer is purchasing and that will show up in the hosted user interface.
- `success_url` - This argument redirects a user after they complete a payment.
- `Stripe-Account` - This header indicates a [direct charge](https://docs.stripe.com/connect/direct-charges.md) for your connected account. With direct charges, the connected account is responsible for Stripe fees, refunds, and chargebacks. The connected account’s branding is used in Checkout, which allows their customers to feel like they’re interacting directly with the business instead of your platform.
- (Optional) `payment_intent_data[application_fee_amount]` - This argument specifies the amount your platform plans to take from the transaction. After the payment is processed on the connected account, the `application_fee_amount` is transferred to the platform and the Stripe fee is deducted from the connected account’s balance.
![Account creation flow](https://b.stripecdn.com/docs-statics-srv/assets/direct_charges.a2a8b68037ac95fe22140d6dde9740d3.svg)

### Handle post-payment events  (Server-side)

Stripe sends a [checkout.session.completed](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.completed) event when the payment completes. [Use a webhook to receive these events](https://docs.stripe.com/webhooks/quickstart.md) and run actions, like 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. Some payment methods also take 2-14 days for payment confirmation. Setting up your integration to listen for asynchronous events enables you to accept multiple [payment methods](https://stripe.com/payments/payment-methods-guide) with a single integration.

In addition to handling the `checkout.session.completed` event, we recommend handling two other events when collecting payments with Checkout:

| Event                                                                                                                                        | Description                                                                           | Next steps                                                                  |
| -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [checkout.session.completed](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.completed)                             | The customer has successfully authorized the payment by submitting the Checkout form. | Wait for the payment to succeed or fail.                                    |
| [checkout.session.async_payment_succeeded](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.async_payment_succeeded) | The customer’s payment succeeded.                                                     | Fulfill the purchased goods or services.                                    |
| [checkout.session.async_payment_failed](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.async_payment_failed)       | The payment was declined, or failed for some other reason.                            | Contact the customer through email and request that they place a new order. |

These events all include the [Checkout Session](https://docs.stripe.com/api/checkout/sessions.md) object. After the payment succeeds, the underlying *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) status changes from `processing` to `succeeded`.

#### Full embedded page

### Create a Checkout Session  (Client-side) (Server-side)

A Checkout Session controls what your customer sees in the Stripe-hosted payment page such as line items, the order amount and currency, and acceptable payment methods. When performing direct charges, Checkout uses the connected account’s branding settings. See the [Customize branding](https://docs.stripe.com/connect/direct-charges.md?platform=web&ui=stripe-hosted#branding) section for more information.

Unlike destination charges and separate charges and transfers, users (connected accounts) are responsible for handling disputes on direct charges—it’s not the responsibility of the platform. On your server, make the following call to the Stripe API. The Checkout Session creation response includes a `client_secret`, which you’ll use in the next step to mount Checkout.

```curl
curl https://api.stripe.com/v1/checkout/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -H "Stripe-Account: {{CONNECTEDACCOUNT_ID}}" \
  -d mode=payment \
  -d "line_items[0][price]={{PRICE_ID}}" \
  -d "line_items[0][quantity]=1" \
  -d "payment_intent_data[application_fee_amount]=123" \
  -d ui_mode=embedded_page \
  --data-urlencode "return_url=https://example.com/checkout/return?session_id={CHECKOUT_SESSION_ID}"
```

- `line_items`: This argument represents the items your customer is purchasing, which will appear in the hosted user interface.
- `return_url`: This argument redirects a user after they complete a payment attempt.
- `Stripe-Account`: This header indicates a [direct charge](https://docs.stripe.com/connect/direct-charges.md) for your connected account. With direct charges, the connected account is responsible for Stripe fees, refunds, and chargebacks. Checkout uses the connected account’s branding, which allows customers to feel like they’re interacting directly with the business instead of your platform.
- (Optional) `payment_intent_data[application_fee_amount]`: This argument specifies the amount your platform plans to take from the transaction. If you use the Stripe [Platform Pricing Tool](https://docs.stripe.com/connect/platform-pricing-tools.md) to manage application fee pricing from the [Dashboard](https://dashboard.stripe.com/test/settings/connect/platform_pricing/payments), don’t include this argument, because it overrides any pricing logic set by the tool. After processing the payment on the connected account, Stripe transfers the `application_fee_amount` to the platform and deducts the Stripe fee from the connected account’s balance.

Make sure the `return_url` corresponds to a page on your website that provides the status of the payment. When Stripe redirects the customer to the `return_url`, we replace the `{CHECKOUT_SESSION_ID}` string with the Checkout Session ID. Use this to retrieve the Checkout Session and inspect the status to decide what to show your customers. You can also append your own query parameters when providing the `return_url`, which persist through the redirect process.
![Account creation flow](https://b.stripecdn.com/docs-statics-srv/assets/direct_charges.a2a8b68037ac95fe22140d6dde9740d3.svg)

### Mount Checkout   (Client-side)

#### HTML + JS

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
<head>
  <script src="https://js.stripe.com/endive/stripe.js"></script>
</head>
<body>
  <div id="checkout">
    <!-- Checkout will insert the payment form here -->
  </div>
</body>
```

Initialize Stripe.js with your publishable API key.

Create an asynchronous `fetchClientSecret` function that makes a request to your server to create the Checkout Session and retrieve the client secret. Pass this function into `options` when you create the Checkout instance:

```javascript
// Initialize Stripe.js
const stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>', {
   stripeAccount: '{{CONNECTED_ACCOUNT_ID}}',
});

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

Install Connect.js and the React Connect.js libraries from the [npm public registry](https://www.npmjs.com/package/@stripe/react-connect-js).

```bash
npm install --save @stripe/connect-js @stripe/react-connect-js
```

To use the Embedded Checkout component, create an `EmbeddedCheckoutProvider`. Call `loadStripe` with your publishable API key and pass the returned `Promise` to the provider.

Create an asynchronous `fetchClientSecret` function that makes a request to your server to create the Checkout Session and retrieve the client secret. Pass this function into the `options` prop accepted by the 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 (
    <EmbeddedCheckoutProvider
      stripe={stripePromise}
      options={options}
    >
      <EmbeddedCheckout />
    </EmbeddedCheckoutProvider>
  )
}
```

Checkout is rendered 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.

### Customize appearance

Customize Checkout to match the design of your site by setting the background color, button color, border radius, and fonts in your account’s [branding settings](https://dashboard.stripe.com/settings/branding).

By default, Checkout renders with no external padding or margin. We recommend using a container element such as a div to apply your desired margin (for example, 16px on all sides).

### Handle post-payment events  (Server-side)

Stripe sends a [checkout.session.completed](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.completed) event when the payment completes. [Use a webhook to receive these events](https://docs.stripe.com/webhooks/quickstart.md) and run actions, like 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. Some payment methods also take 2-14 days for payment confirmation. Setting up your integration to listen for asynchronous events enables you to accept multiple [payment methods](https://stripe.com/payments/payment-methods-guide) with a single integration.

In addition to handling the `checkout.session.completed` event, we recommend handling two other events when collecting payments with Checkout:

| Event                                                                                                                                        | Description                                                                           | Next steps                                                                  |
| -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [checkout.session.completed](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.completed)                             | The customer has successfully authorized the payment by submitting the Checkout form. | Wait for the payment to succeed or fail.                                    |
| [checkout.session.async_payment_succeeded](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.async_payment_succeeded) | The customer’s payment succeeded.                                                     | Fulfill the purchased goods or services.                                    |
| [checkout.session.async_payment_failed](https://docs.stripe.com/api/events/types.md#event_types-checkout.session.async_payment_failed)       | The payment was declined, or failed for some other reason.                            | Contact the customer through email and request that they place a new order. |

These events all include the [Checkout Session](https://docs.stripe.com/api/checkout/sessions.md) object. After the payment succeeds, the underlying *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) status changes from `processing` to `succeeded`.

#### Custom flow

### Create a PaymentIntent  (Server-side)

Stripe uses a [PaymentIntent](https://docs.stripe.com/api/payment_intents.md) object to represent your intent to collect payment from a customer, tracking charge attempts and payment state changes throughout the process.

> If you want to render the Payment Element without first creating a PaymentIntent, see [Collect payment details before creating an Intent](https://docs.stripe.com/payments/accept-a-payment-deferred.md?type=payment).
![An overview diagram of the entire payment flow](https://b.stripecdn.com/docs-statics-srv/assets/accept-a-payment-payment-element.5cf6795a02f864923f9953611493d735.svg)

The [payment methods shown to customers](https://docs.stripe.com/connect/manage-payment-methods.md) during the checkout process are also included on the `PaymentIntent`. You can let Stripe automatically pull payment methods from your Dashboard settings or you can list them manually.

Unless your integration requires a code-based option for offering payment methods, don’t list payment methods manually. Stripe evaluates the currency, payment method restrictions, and other parameters to determine the list of supported payment methods.

Stripe prioritizes payment methods that can help increase conversion and are most relevant to the currency and the customer’s location. We hide lower priority payment methods in an overflow menu.

#### Manage payment methods from the Dashboard

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -H "Stripe-Account: {{CONNECTEDACCOUNT_ID}}" \
  -d amount=1099 \
  -d currency=eur \
  -d "automatic_payment_methods[enabled]=true" \
  -d application_fee_amount=123
```

When creating a PaymentIntent, you need to specify certain parameters:

- `amount` and `currency`: Create a PaymentIntent on your server with an amount and currency.

- (Optional) `automatic_payment_methods`: You don’t need to specify this parameter, because it defaults to true. Stripe automatically returns eligible payment methods based on factors such as the transaction’s amount, currency, and payment flow.

- (Optional) `payment_intent_data[application_fee_amount]`: This argument specifies the amount your platform plans to take from the transaction. If you use the Stripe [Platform Pricing Tool](https://docs.stripe.com/connect/platform-pricing-tools.md) to manage application fee pricing from the [Dashboard](https://dashboard.stripe.com/test/settings/connect/platform_pricing/payments), don’t include this argument, because it overrides any pricing logic set by the tool. After processing the payment on the connected account, Stripe transfers the `application_fee_amount` to the platform and deducts the Stripe fee from the connected account’s balance.

#### List payment methods manually

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -H "Stripe-Account: {{CONNECTEDACCOUNT_ID}}" \
  -d amount=1099 \
  -d currency=eur \
  -d "allowed_payment_method_types[]=bancontact" \
  -d "allowed_payment_method_types[]=card" \
  -d "allowed_payment_method_types[]=eps" \
  -d "allowed_payment_method_types[]=ideal" \
  -d "allowed_payment_method_types[]=p24" \
  -d "allowed_payment_method_types[]=sepa_debit" \
  -d "allowed_payment_method_types[]=sofort" \
  -d application_fee_amount=123
```

When creating a `PaymentIntent`, you need to specify certain parameters:

- `amount`: Create a `PaymentIntent` on your server with a specified amount. Always determine how much to charge on the server side, because it’s a trusted environment. This approach prevents malicious customers from choosing their own prices.
- `currency`: The currency you include in the `PaymentIntent` filters the payment methods shown to the customer, so choose it based on the payment methods you want to offer. For example, if you pass `eur` and have OXXO enabled in the Dashboard, OXXO won’t appear to the customer because it doesn’t support EUR payments. Some payment methods support multiple currencies and countries. This guide uses Bancontact, credit cards, EPS, iDEAL, Przelewy24, SEPA Direct Debit, and Sofort in the example code.
- `"allowed_payment_method_types[]"`: Manually list all the payment methods you want to support.
- (Optional) `payment_intent_data[application_fee_amount]`: This argument specifies the amount your platform plans to take from the transaction. If you use the Stripe [Platform Pricing Tool](https://docs.stripe.com/connect/platform-pricing-tools.md) to manage application fee pricing from the [Dashboard](https://dashboard.stripe.com/test/settings/connect/platform_pricing/payments), don’t include this argument, because it overrides any pricing logic set by the tool. After processing the payment on the connected account, Stripe transfers the `application_fee_amount` to the platform and deducts the Stripe fee from the connected account’s balance.

> Each payment method needs to support the currency passed in the `PaymentIntent` and the country where your business is based. See [Payment method integration options](https://docs.stripe.com/payments/payment-methods/integration-options.md) for more details.

### Retrieve the client secret

The PaymentIntent includes a *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)) that the client side uses to securely complete the payment process. You can use different approaches to pass the client secret to the client side.

#### Single-page application

Retrieve the client secret from an endpoint on your server, using the browser’s `fetch` function. This approach is best if your client side is a single-page application, particularly one built with a modern frontend framework like React. Create the server endpoint that serves the client secret:

#### Ruby

```ruby
get '/secret' do
  intent = # ... Create or retrieve the PaymentIntent
  {client_secret: intent.client_secret}.to_json
end
```

And then fetch the client secret with JavaScript on the client side:

```javascript
(async () => {
  const response = await fetch('/secret');
  const {client_secret: clientSecret} = await response.json();
  // Render the form using the clientSecret
})();
```

#### Server-side rendering

Pass the client secret to the client from your server. This approach works best if your application generates static content on the server before sending it to the browser.

Add the [client_secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) in your checkout form. In your server-side code, retrieve the client secret from the PaymentIntent:

#### Ruby

```erb
<form id="payment-form" data-secret="<%= @intent.client_secret %>">
  <div id="payment-element">
    <!-- placeholder for Elements -->
  </div>
  <button id="submit">Submit</button>
</form>
```

```ruby
get '/checkout' do
  @intent = # ... Fetch or create the PaymentIntent
  erb :checkout
end
```

### Collect payment details  (Client-side)

Collect payment details on the client with the Payment Element. The Payment Element is a prebuilt UI component that simplifies collecting payment details for a variety of payment methods.

The Payment Element contains an iframe that securely sends payment information to Stripe over an HTTPS connection. Avoid placing the Payment Element within another iframe because some payment methods require redirecting to another page for payment confirmation.

The checkout page address must start with `https://` rather than `http://` for your integration to work. You can test your integration without using HTTPS, but remember to [enable it](https://docs.stripe.com/security/guide.md#tls) when you’re ready to accept live payments.

#### Set up Stripe.js 

#### HTML + JS

The Payment Element is automatically available as a feature of Stripe.js. Include the Stripe.js script on your checkout page by adding it to the `head` of your HTML file. Always load Stripe.js directly from js.stripe.com to remain PCI compliant. Don’t include the script in a bundle or host a copy of it yourself.

```html
<head>
  <title>Checkout</title>
  <script src="https://js.stripe.com/endive/stripe.js"></script>
</head>
```

Create an instance of `Stripe` with the following JavaScript on your checkout page:

```javascript
// Initialize Stripe.js with the same connected account ID used when creating
// the PaymentIntent.
const stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>', {
  stripeAccount: '{{CONNECTED_ACCOUNT_ID}}'
});
```

#### Add Stripe Elements and the Payment Element to your payment page 

The Payment Element needs a place to live on your payment page. Create an empty DOM node (container) with a unique ID in your payment form.

```html
<form id="payment-form">
  <div id="payment-element">
    <!-- Elements will create form elements here -->
  </div>
  <button id="submit">Pay</button>
</form>
```

When the form has loaded, create an instance of the Payment Element and mount it to the container DOM node along with the [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) from the previous step. Pass this value as an option when creating the [Elements](https://docs.stripe.com/js/elements_object/create) instance.

Handle the client secret carefully, because it can complete the charge. Don’t log it, embed it in URLs, or expose it to anyone but the customer.

```javascript
const options = {
  clientSecret: '{{CLIENT_SECRET}}',
  // Fully customizable with the Appearance API
  appearance: {/*...*/},
};

// Set up Stripe.js and Elements to use in checkout form using the client secret
const elements = stripe.elements(options);

// Create and mount the Payment Element
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-element");
```

The Payment Element renders a dynamic form that allows your customer to pick a payment method. The form automatically collects all necessary payments details for the payment method selected by the customer. You can [customize the appearance of the Payment Element](https://docs.stripe.com/elements/appearance-api.md) to match the design of your site when you set up the `Elements` object.

#### React

Install [React Stripe.js](https://www.npmjs.com/package/@stripe/react-stripe-js) and the [Stripe.js loader](https://www.npmjs.com/package/@stripe/stripe-js) from the npm public registry:

```bash
npm install --save @stripe/react-stripe-js @stripe/stripe-js
```

### Add and configure the Elements provider to your payment page 

To use the Payment Element component, wrap your checkout page component in an [Elements provider](https://docs.stripe.com/sdks/stripejs-react.md?ui=elements#elements-provider). Call `loadStripe` with your publishable key, and pass the returned `Promise` along with the [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) from the previous step as `options` in the `Elements` provider.

```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import {Elements} from '@stripe/react-stripe-js';
import {loadStripe} from '@stripe/stripe-js';
import CheckoutForm from './CheckoutForm';

// Make sure to call `loadStripe` outside of a component's render to avoid
// recreating the `Stripe` object on every render.
const stripePromise = loadStripe("<<YOUR_PUBLISHABLE_KEY>>", {
  stripeAccount: '{{CONNECTED_ACCOUNT_ID}}'
});

function App() {
  const options = {
    // pass the client secret from the previous step
    clientSecret: '{{CLIENT_SECRET}}',
    // Fully customizable with the Appearance API
    appearance: {/*...*/},
  };

  return (
    <Elements stripe={stripePromise} options={options}>
      <CheckoutForm />
    </Elements>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
```

#### Add the PaymentElement component 

Use the `PaymentElement` component to build your form.

```jsx
import React from 'react';
import {PaymentElement} from '@stripe/react-stripe-js';

const CheckoutForm = () => {
  return (
    <form>
      <PaymentElement />
      <button>Submit</button>
    </form>
  );
};

export default CheckoutForm;
```

The Payment Element renders a dynamic form that allows your customer to pick a payment method type. The form automatically collects all necessary payments details for the payment method selected by the customer. You can [customize the appearance of the Payment Element](https://docs.stripe.com/elements/appearance-api.md) to match the design of your site when you configure the `Elements` provider.

### Submit the payment to Stripe  (Client-side)

Use [stripe.confirmPayment](https://docs.stripe.com/js/payment_intents/confirm_payment) to complete the payment using details from the Payment Element. Provide a [return_url](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-return_url) to this function to indicate where Stripe should redirect the user after they complete the payment. Your user may be first redirected to an intermediate site, like a bank authorization page, before being redirected to the `return_url`. Card payments immediately redirect to the `return_url` when a payment is successful.

If you don’t want to redirect for card payments after payment completion, you can set [redirect](https://docs.stripe.com/js/payment_intents/confirm_payment#confirm_payment_intent-options-redirect) to `if_required`. This only redirects customers that check out with redirect-based payment methods.

#### HTML + JS

```javascript
const form = document.getElementById('payment-form');

form.addEventListener('submit', async (event) => {
  event.preventDefault();

  const {error} = await stripe.confirmPayment({
    //`Elements` instance that was used to create the Payment Element
    elements,
    confirmParams: {
      return_url: 'https://example.com/order/123/complete',
    },
  });

  if (error) {
    // This point will only be reached if there is an immediate error when
    // confirming the payment. Show error to your customer (for example, payment
    // details incomplete)
    const messageContainer = document.querySelector('#error-message');
    messageContainer.textContent = error.message;
  } else {
    // Your customer will be redirected to your `return_url`. For some payment
    // methods like iDEAL, your customer will be redirected to an intermediate
    // site first to authorize the payment, then redirected to the `return_url`.
  }
});
```

#### React

To call [stripe.confirmPayment](https://docs.stripe.com/js/payment_intents/confirm_payment) from your payment form component, use the [useStripe](https://docs.stripe.com/sdks/stripejs-react.md?ui=elements#usestripe-hook) and [useElements](https://docs.stripe.com/sdks/stripejs-react.md?ui=elements#useelements-hook) hooks.

If you prefer traditional class components over hooks, you can instead use an [ElementsConsumer](https://docs.stripe.com/sdks/stripejs-react.md?ui=elements#elements-consumer).

```jsx
import React, {useState} from 'react';
import {useStripe, useElements, PaymentElement} from '@stripe/react-stripe-js';

const CheckoutForm = () => {
  const stripe = useStripe();
  const elements = useElements();

  const [errorMessage, setErrorMessage] = useState(null);

  const handleSubmit = async (event) => {
    // We don't want to let default form submission happen here,
    // which would refresh the page.
    event.preventDefault();

    if (!stripe || !elements) {
      // Stripe.js hasn't yet loaded.
      // Make sure to disable form submission until Stripe.js has loaded.
      return;
    }

    const {error} = await stripe.confirmPayment({
      //`Elements` instance that was used to create the Payment Element
      elements,
      confirmParams: {
        return_url: 'https://example.com/order/123/complete',
      },
    });


    if (error) {
      // This point will only be reached if there is an immediate error when
      // confirming the payment. Show error to your customer (for example, payment
      // details incomplete)
      setErrorMessage(error.message);
    } else {
      // Your customer will be redirected to your `return_url`. For some payment
      // methods like iDEAL, your customer will be redirected to an intermediate
      // site first to authorize the payment, then redirected to the `return_url`.
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <button disabled={!stripe}>Submit</button>
      {/* Show error message to your customers */}
      {errorMessage && <div>{errorMessage}</div>}
    </form>
  );
};

export default CheckoutForm;
```

Make sure the `return_url` corresponds to a page on your website that provides the status of the payment. When Stripe redirects the customer to the `return_url`, we provide the following URL query parameters:

| Parameter                      | Description                                                                                                                                   |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `payment_intent`               | The unique identifier for the `PaymentIntent`.                                                                                                |
| `payment_intent_client_secret` | The [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) of the `PaymentIntent` object. |

> If you have tooling that tracks the customer’s browser session, you might need to add the `stripe.com` domain to the referrer exclude list. Redirects cause some tools to create new sessions, which prevents you from tracking the complete session.

Use one of the query parameters to retrieve the PaymentIntent. Inspect the [status of the PaymentIntent](https://docs.stripe.com/payments/paymentintents/lifecycle.md) to decide what to show your customers. You can also append your own query parameters when providing the `return_url`, which persist through the redirect process.

#### HTML + JS

```javascript

// Initialize Stripe.js using your publishable key
const stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>');

// Retrieve the "payment_intent_client_secret" query parameter appended to
// your return_url by Stripe.js
const clientSecret = new URLSearchParams(window.location.search).get(
  'payment_intent_client_secret'
);

// Retrieve the PaymentIntent
stripe.retrievePaymentIntent(clientSecret).then(({paymentIntent}) => {
  const message = document.querySelector('#message')

  // Inspect the PaymentIntent `status` to indicate the status of the payment
  // to your customer.
  //
  // Some payment methods will [immediately succeed or fail][0] upon
  // confirmation, while others will first enter a `processing` state.
  //
  // [0]: https://stripe.com/docs/payments/payment-methods#payment-notification
  switch (paymentIntent.status) {
    case 'succeeded':
      message.innerText = 'Success! Payment received.';
      break;

    case 'processing':
      message.innerText = "Payment processing. We'll update you when payment is received.";
      break;

    case 'requires_payment_method':
      message.innerText = 'Payment failed. Please try another payment method.';
      // Redirect your user back to your payment page to attempt collecting
      // payment again
      break;

    default:
      message.innerText = 'Something went wrong.';
      break;
  }
});
```

#### React

```jsx
import React, {useState, useEffect} from 'react';
import {useStripe} from '@stripe/react-stripe-js';

const PaymentStatus = () => {
  const stripe = useStripe();
  const [message, setMessage] = useState(null);

  useEffect(() => {
    if (!stripe) {
      return;
    }

    // Retrieve the "payment_intent_client_secret" query parameter appended to
    // your return_url by Stripe.js
    const clientSecret = new URLSearchParams(window.location.search).get(
      'payment_intent_client_secret'
    );

    // Retrieve the PaymentIntent
    stripe
      .retrievePaymentIntent(clientSecret)
      .then(({paymentIntent}) => {
        // Inspect the PaymentIntent `status` to indicate the status of the payment
        // to your customer.
        //
        // Some payment methods will [immediately succeed or fail][0] upon
        // confirmation, while others will first enter a `processing` state.
        //
        // [0]: https://stripe.com/docs/payments/payment-methods#payment-notification
        switch (paymentIntent.status) {
          case 'succeeded':
            setMessage('Success! Payment received.');
            break;

          case 'processing':
            setMessage("Payment processing. We'll update you when payment is received.");
            break;

          case 'requires_payment_method':
            // Redirect your user back to your payment page to attempt collecting
            // payment again
            setMessage('Payment failed. Please try another payment method.');
            break;

          default:
            setMessage('Something went wrong.');
            break;
        }
      });
  }, [stripe]);


  return message;
};

export default PaymentStatus;
```

### Collect wallet information 

If you accept Apple Pay or Google Pay, call [elements.submit()](https://docs.stripe.com/js/elements/submit) at the beginning of your submission handler. This triggers form validation and shows the native payment sheet so your customer can authorize the payment.

#### HTML + JS

```javascript
form.addEventListener('submit', async (event) => {
  event.preventDefault();

  // Trigger form validation and wallet collection
  const {error: submitError} = await elements.submit();
  if (submitError) {
    handleError(submitError);
    return;
  }

  // Confirm the payment
});
```

#### React

```jsx
const handleSubmit = async (event) => {
  event.preventDefault();

  if (!stripe || !elements) {
    // Stripe.js hasn't yet loaded.
    // Make sure to disable form submission until Stripe.js has loaded.
    return;
  }

  // Trigger form validation and wallet collection
  const {error: submitError} = await elements.submit();
  if (submitError) {
    handleError(submitError);
    return;
  }

  // Confirm the payment
};
```

### Handle post-payment events  (Server-side)

Stripe sends a [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) event when the payment completes. Use the [Dashboard webhook tool](https://dashboard.stripe.com/webhooks) or follow the [webhook guide](https://docs.stripe.com/webhooks/quickstart.md) 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](https://stripe.com/payments/payment-methods-guide) with a single integration.

In addition to handling the `payment_intent.succeeded` event, we recommend handling these other events when collecting payments with the Payment Element:

| Event                                                                                                                           | Description                                                                                                                                                                                                                                                                         | Action                                                                                                                                                                                           |
| ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.succeeded)           | Sent when a customer successfully completes a payment.                                                                                                                                                                                                                              | Send the customer an order confirmation and *fulfill* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected) their order. |
| [payment_intent.processing](https://docs.stripe.com/api/events/types.md?lang=php#event_types-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_intent.succeeded` or `payment_intent.payment_failed` 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](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.payment_failed) | Sent when a customer attempts a payment, but the payment fails.                                                                                                                                                                                                                     | If a payment transitions from `processing` to `payment_failed`, offer the customer another attempt to pay.                                                                                       |

## Testing

Test your account creation flow by [creating accounts](https://docs.stripe.com/connect/testing.md#creating-accounts) and [using OAuth](https://docs.stripe.com/connect/testing.md#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](https://dashboard.stripe.com/settings/payment_methods). Test your checkout flow with your test keys and a test account. You can use our [test cards](https://docs.stripe.com/testing.md) 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](https://docs.stripe.com/connect/account-balances.md) and is paid out on a daily rolling basis. Connected accounts can manage their own payout schedules in the [Stripe Dashboard](https://dashboard.stripe.com/settings/payouts).

## See also

- [Manage connected accounts in the Dashboard](https://docs.stripe.com/connect/dashboard.md)
- [Issue refunds](https://docs.stripe.com/connect/direct-charges.md#issue-refunds)
- [Customize statement descriptors](https://docs.stripe.com/connect/statement-descriptors.md)
- [Work with multiple currencies](https://docs.stripe.com/connect/currencies.md)
