# Collect an account to build data-powered products

Collect your user's account and use data such as balances, ownership details, and transactions to build products.
Available in: US
Not sure about which Financial Connections integration to use? See our [overview of integration options](https://docs.stripe.com/financial-connections/use-cases.md).

Financial Connections lets your users securely share their financial data by linking their external financial accounts to your business. You can use Financial Connections to access user-permissioned financial data such as tokenized account and routing numbers, account balances, account owner information, and historical transactions.

Some common examples of how you can use Financial Connections to improve product experiences for your users include:

- Mitigate fraud when onboarding a customer or business by verifying the [ownership](https://docs.stripe.com/financial-connections/ownership.md) information of an account, such as the name and address of the bank account holder.
- Help your users track expenses, handle bills, manage their finances and take control of their financial well-being with [transactions](https://docs.stripe.com/financial-connections/transactions.md) data.
- Speed up underwriting and improve access to credit and other financial services with transactions and balances data.
- Enable your users to connect their accounts in fewer steps with Link, allowing them to save and reuse their bank account details across Stripe businesses.

## Set up Stripe [Server-side]

[Register](https://dashboard.stripe.com/financial-connections/application) for Financial Connections after we approve your account for live-mode access.

Use our official libraries for access to the Stripe 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 or retrieve an account holder [Server-side]

Create a [Customer object](https://docs.stripe.com/api/customers/object.md) when users create an account with your business. By providing an email address, Financial Connections can optimize the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow) by dynamically showing a streamlined user interface, for returning [Link](https://support.stripe.com/questions/link-for-financial-connections-support-for-businesses) users.

```curl
curl https://api.stripe.com/v1/customers \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d email={{CUSTOMER_EMAIL}} \
  -d name={{CUSTOMER_NAME}}
```

## Create a Financial Connections Session [Server-side]

Before you can retrieve data from a user’s bank account with Financial Connections, your user must authenticate their account with the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow).

Your user begins the authentication flow when they want to connect their account to your site or application. Insert a button or link on your site or in your application, which allows a user to link their account—for example, your button might say “Link your bank account”.

Create a Financial Connections Session by posting to `/v1/financial_connections/sessions`:

```curl
curl https://api.stripe.com/v1/financial_connections/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "account_holder[type]=customer" \
  -d "account_holder[customer]={{CUSTOMER_ID}}" \
  -d "permissions[]=balances" \
  -d "permissions[]=ownership" \
  -d "permissions[]=payment_method" \
  -d "permissions[]=transactions"
```

1. Set `account_holder[customer]` to the Customer `id`.
1. Set the data `permissions` parameter to include the data required to fulfill your use case.
1. (Optional) set the `prefetch` parameter for retrieving the data on account creation.

The [permissions](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-permissions) parameter controls which account data you can access. You must request at least one permission. When completing the authentication flow, your user can see the data you’ve requested access to, and provide their consent to share it.

Consider the data required to fulfill your use case and request permission to access only the data you need. Requesting permissions that go well beyond your application’s scope might erode your users’ trust in how you use their data. For example, if you’re building a personal financial management application or a lending product, you might request `transactions` data. If you’re mitigating fraud such as account takeovers, you might want to request `ownership` details.

After your user authenticates their account, you can expand data permissions only by creating a new Financial Connections Session and specifying a new value for the `permissions` parameter. Your user must complete the authentication flow again, where they’ll see the additional data you’ve requested permission to access, and provide consent to share their data.

The optional [prefetch parameter](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-prefetch) controls which data you retrieve immediately after the user connects their account. Use this option if you know you always want a certain type of data. It removes the need to make an extra API call to initiate a [data refresh](https://docs.stripe.com/api/financial_connections/accounts/refresh.md).

To preserve the option to [accept ACH Direct Debit payments](https://docs.stripe.com/financial-connections/other-data-powered-products.md#accept-ach-direct-debit), request the `payment_method` permission.

## Collect a Financial Connections account [Client-side]

Use the returned `client_secret` with Stripe.js to allow your user to connect their accounts. A `client_secret` allows client-side Stripe SDKs to make changes to the Financial Connections Session. Don’t store it, log it, embed it in URLs, or expose it to anyone other than your end user. Make sure that you have [TLS](https://docs.stripe.com/security/guide.md#tls) enabled on any page that includes the client secret.

Use [collectFinancialConnectionsAccounts](https://docs.stripe.com/js/financial_connections/collect_financial_connections_accounts) to collect an account.

```javascript
const stripe = new Stripe('<<YOUR_PUBLISHABLE_KEY>>')
const financialConnectionsSessionResult = await stripe.collectFinancialConnectionsAccounts({
  clientSecret: "{{SESSION_CLIENT_SECRET}}",
});
```

This method loads the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow), the client-side Stripe.js UI that helps your users link their financial accounts to you and Stripe.

The return value of `stripe.collectFinancialConnectionsAccounts` is a Promise. When the user completes the authentication flow, the Promise resolves with an object that contains the list of connected accounts:

```json
{
  "financialConnectionsSession": {
    "id": "fcsess_123",
    "accounts": [
      {
        "id": "fca_456",
        "object": "financial_connections.account",
        "category": "Checking",
        "display_name": "Premium Checking",
        "institution_name": "Test Bank",
        "last4": "4242"
      }
    ]
  }
}
```

If the user connects no accounts, or exits the authentication flow early, the response contains an empty `accounts` array.

Successful completion of the authentication flow also sends one `financial_connections.account.created` webhook per account connected.

## Retrieve data on a Financial Connections account [Server-side]

After your user has successfully completed the authentication flow, access or refresh the account data you’ve specified in the `permissions` parameter of the Financial Connections Session.

To protect the privacy of your user’s data, account data accessible to you is limited to the data you’ve specified in the `permissions` parameter.

Follow the guides for [balances](https://docs.stripe.com/financial-connections/balances.md), [ownership](https://docs.stripe.com/financial-connections/ownership.md) and [transactions](https://docs.stripe.com/financial-connections/transactions.md) to start retrieving account data.

## Optional: Accept an ACH Direct Debit payment from a Financial Connections account

You can optionally accept ACH Direct Debit payments on a previously collected Financial Connections account as long as the `supported_payment_method_types` attribute on the account includes `us_bank_account`.

Only US bank accounts are eligible to accept ACH Direct Debits such as a checking or savings account.

To accept an ACH Direct Debit payment on a previously collected account, you must have specified `payment_method` in the [data permissions](https://docs.stripe.com/financial-connections/fundamentals.md#data-permissions) parameter on the Financial Connections Session.

### Create a Payment Intent or Setup Intent

Use the *Payment Intents API* (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) to accept an ACH Direct payment for an account you want to charge now. Use the *Setup Intents API* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) to save details for future ACH Direct Debit payments. [Learn more about the difference between a](https://support.stripe.com/questions/payment-intents-api-vs-setup-intents-api) Payment Intent and Setup Intent.

This path shows you how to accept an ACH Direct Debit payment using the Payment Intents API.

Use the Financial Connections account ID and the customer ID to create a Payment Intent:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method_types[]=us_bank_account" \
  -d "payment_method_data[us_bank_account][financial_connections_account]={{FINANCIALCONNECTIONSACCOUNT_ID}}" \
  -d "payment_method_data[type]=us_bank_account" \
  -d "payment_method_data[billing_details][name]=J. Customer" \
  -d amount=100 \
  -d currency=usd
```

This will return a Payment Intent similar to:

```json

{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_details": {
    "tip": {
      "amount": null
    }
  },
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [

    ],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "confirmation_method": "automatic",
  "created": 1651010665,
  "currency": "usd",
  "customer": null,
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "requires_confirmation",
  "transfer_data": null,
  "transfer_group": null
}
```

### Collect mandate acknowledgement and submit the payment

Before you can initiate the payment, you must obtain authorization from your customer by displaying mandate terms for them to accept.

To be compliant with Nacha rules, you must obtain authorization from your customer before you can initiate payment by displaying mandate terms for them to accept. For more information on mandates, see [Mandates](https://docs.stripe.com/payments/ach-direct-debit.md#mandates).

### Confirm the Payment Intent

To confirm this Payment Intent, you need to provide either a [`mandate` parameter](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate) with an existing Mandate ID, or provide [mandate_data](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate_data) to create a new mandate.

For example, if you have a checkout page with a “Place Order” button, clicking that button could trigger a `POST` to an endpoint on your server. That endpoint could extract the request’s User Agent and the client’s IP address, and then make the following request to Stripe’s API:

```curl
curl https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}}/confirm \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "mandate_data[customer_acceptance][accepted_at]=1647448692" \
  -d "mandate_data[customer_acceptance][type]=online" \
  -d "mandate_data[customer_acceptance][online][ip_address]=71.183.194.54" \
  --data-urlencode "mandate_data[customer_acceptance][online][user_agent]=Mozilla/5.0 ..."
```

After successfully confirming the Payment Intent, it will look similar to:

```json
{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [
      {
        "id": "py_17F8CPDyDglZKgWE3uzOAUe9",
        "object": "charge",
        "amount": 100,
        "amount_captured": 100,
        "amount_refunded": 0,
        "application": null,
        "application_fee": null,
        "application_fee_amount": null,
        "balance_transaction": null,
        "billing_details": {
          "address": {
            "city": null,
            "country": null,
            "line1": null,
            "line2": null,
            "postal_code": null,
            "state": null
          },
          "email": null,
          "name": "J. Customer",
          "phone": null
        },
        "calculated_statement_descriptor": null,
        "captured": true,
        "created": 1647448692,
        "currency": "usd",
        "customer": "cus_LKe65xcPnrCiTZ",
        "description": null,
        "destination": null,
        "dispute": null,
        "disputed": false,
        "failure_code": null,
        "failure_message": null,
        "fraud_details": {
        },
        "invoice": null,
        "livemode": false,
        "metadata": {
        },
        "on_behalf_of": null,
        "order": null,
        "outcome": null,
        "paid": false,
        "payment_intent": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
        "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
        "payment_method_details": {
          "type": "us_bank_account",
          "us_bank_account": {
            "account_holder_type": "individual",
            "account_type": "checking",
            "bank_name": "STRIPE TEST BANK",
            "fingerprint": "QnXqpAeqjjh8pPFa",
            "last4": "6789",
            "routing_number": "110000000"
          }
        },
        "receipt_email": null,
        "receipt_number": null,
        "receipt_url": null,
        "refunded": false,
        "refunds": {
          "object": "list",
          "data": [

          ],
          "has_more": false,
          "total_count": 0,
          "url": "/v1/charges/py_17F8CPDyDglZKgWE3uzOAUe9/refunds"
        },
        "review": null,
        "shipping": null,
        "source": null,
        "source_transfer": null,
        "statement_descriptor": null,
        "statement_descriptor_suffix": null,
        "status": "pending",
        "transfer_data": null,
        "transfer_group": null
      }
    ],
    "has_more": false,
    "total_count": 1,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP_secret_m38BKZvm3vSdpGdra360hPEov",
  "confirmation_method": "automatic",
  "created": 1647447792,
  "currency": "usd",
  "customer": "cus_LKe65xcPnrCiTZ",
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "processing",
  "transfer_data": null,
  "transfer_group": null
}
```

ACH Direct Debit is a *delayed notification payment method* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods). This means that it can take up to four business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.

The PaymentIntent you create initially has a status of `processing`. After the payment succeeds, the PaymentIntent status is updated from `processing` to `succeeded`. Learn about the events that are sent when the [PaymentIntent status is updated](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#web-confirm-paymentintent-succeeded).
Available in: US
Not sure about which Financial Connections integration to use? See our [overview of integration options](https://docs.stripe.com/financial-connections/use-cases.md).

Financial Connections lets your users securely share their financial data by linking their external financial accounts to your business. You can use Financial Connections to access user-permissioned financial data such as tokenized account and routing numbers, account balances, account owner information, and historical transactions.

Some common examples of how you can use Financial Connections to improve product experiences for your users include:

- Mitigate fraud when onboarding a customer or business by verifying the [ownership](https://docs.stripe.com/financial-connections/ownership.md) information of an account, such as the name and address of the bank account holder.
- Help your users track expenses, handle bills, manage their finances and take control of their financial well-being with [transactions](https://docs.stripe.com/financial-connections/transactions.md) data.
- Speed up underwriting and improve access to credit and other financial services with transactions and balances data.
- Enable your users to connect their accounts in fewer steps with Link, allowing them to save and reuse their bank account details across Stripe businesses.

## Set up Stripe [Server-side] [Client-side]

[Register](https://dashboard.stripe.com/financial-connections/application) for Financial Connections after your account is approved for live-mode access.

### 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 [Stripe iOS SDK](https://github.com/stripe/stripe-ios) is open source, [fully documented](https://stripe.dev/stripe-ios/index.html), and compatible with apps supporting iOS 13 or above.

#### Swift Package Manager

To install the SDK, follow these steps:

1. In Xcode, select **File** > **Add Package Dependencies…** and enter `https://github.com/stripe/stripe-ios-spm` as the repository URL.
1. Select the latest version number from our [releases page](https://github.com/stripe/stripe-ios/releases).
1. Add the **StripeFinancialConnections** product to the [target of your app](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app).

#### CocoaPods

1. If you haven’t already, install the latest version of [CocoaPods](https://guides.cocoapods.org/using/getting-started.html).
1. If you don’t have an existing [Podfile](https://guides.cocoapods.org/syntax/podfile.html), run the following command to create one:
   ```bash
   pod init
   ```
1. Add this line to your `Podfile`:
   ```podfile
   pod 'StripeFinancialConnections'
   ```
1. Run the following command:
   ```bash
   pod install
   ```
1. Don’t forget to use the `.xcworkspace` file to open your project in Xcode, instead of the `.xcodeproj` file, from here on out.
1. In the future, to update to the latest version of the SDK, run:
   ```bash
   pod update StripeFinancialConnections
   ```

#### Carthage

1. If you haven’t already, install the latest version of [Carthage](https://github.com/Carthage/Carthage#installing-carthage).
1. Add this line to your `Cartfile`:
   ```cartfile
   github "stripe/stripe-ios"
   ```
1. Follow the [Carthage installation instructions](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos). Make sure to embed all of the required frameworks listed [here](https://github.com/stripe/stripe-ios/blob/master/StripeFinancialConnections/README.md#manual-linking).
1. In the future, to update to the latest version of the SDK, run the following command:
   ```bash
   carthage update stripe-ios --platform ios
   ```

#### Manual Framework

1. Head to our [GitHub releases page](https://github.com/stripe/stripe-ios/releases/latest) and download and unzip **Stripe.xcframework.zip**.
1. Drag **StripeFinancialConnections.xcframework** to the **Embedded Binaries** section of the **General** settings in your Xcode project. Make sure to select **Copy items if needed**.
1. Repeat step 2 for all required frameworks listed [here](https://github.com/stripe/stripe-ios/blob/master/StripeFinancialConnections/README.md#manual-linking).
1. In the future, to update to the latest version of our SDK, repeat steps 1–3.

> For details on the latest SDK release and past versions, see the [Releases](https://github.com/stripe/stripe-ios/releases) page on GitHub. To receive notifications when a new release is published, [watch releases](https://help.github.com/en/articles/watching-and-unwatching-releases-for-a-repository#watching-releases-for-a-repository) for the repository.

## Create or retrieve an account holder [Server-side]

Create a [Customer object](https://docs.stripe.com/api/customers/object.md) when users create an account with your business. By providing an email address, Financial Connections can optimize the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow) by dynamically showing a streamlined user interface, for returning [Link](https://support.stripe.com/questions/link-for-financial-connections-support-for-businesses) users.

```curl
curl https://api.stripe.com/v1/customers \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d email={{CUSTOMER_EMAIL}} \
  -d name={{CUSTOMER_NAME}}
```

## Create a Financial Connections Session [Server-side]

> You can find a running implementation of this endpoint [available on Glitch](https://glitch.com/edit/#!/remix/stripe-mobile-connections-example) for quick testing.

Before you can retrieve data from a user’s bank account with Financial Connections, your user must authenticate their account with the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow).

Your user begins the authentication flow when they want to connect their account to your site or application. Insert a button or link on your site or in your application, which allows a user to link their account—for example, your button might say “Link your bank account”.

Create a Financial Connections Session by posting to `/v1/financial_connections/sessions`:

```curl
curl https://api.stripe.com/v1/financial_connections/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "account_holder[type]=customer" \
  -d "account_holder[customer]={{CUSTOMER_ID}}" \
  -d "permissions[]=balances" \
  -d "permissions[]=ownership" \
  -d "permissions[]=payment_method" \
  -d "permissions[]=transactions"
```

1. Set `account_holder[customer]` to the Customer `id`.
1. Set the data `permissions` parameter to include the data required to fulfill your use case.
1. (Optional) set the `prefetch` parameter for retrieving the data on account creation.

The [permissions](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-permissions) parameter controls which account data you can access. You must request at least one permission. When completing the authentication flow, your user can see the data you’ve requested access to, and provide their consent to share it.

Consider the data required to fulfill your use case and request permission to access only the data you need. Requesting permissions that go well beyond your application’s scope might erode your users’ trust in how you use their data. For example, if you’re building a personal financial management application or a lending product, you might request `transactions` data. If you’re mitigating fraud such as account takeovers, you might want to request `ownership` details.

After your user authenticates their account, you can expand data permissions only by creating a new Financial Connections Session and specifying a new value for the `permissions` parameter. Your user must complete the authentication flow again, where they’ll see the additional data you’ve requested permission to access, and provide consent to share their data.

The optional [prefetch parameter](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-prefetch) controls which data you retrieve immediately after the user connects their account. Use this option if you know you always want a certain type of data. It removes the need to make an extra API call to initiate a [data refresh](https://docs.stripe.com/api/financial_connections/accounts/refresh.md).

To preserve the option to [accept ACH Direct Debit payments](https://docs.stripe.com/financial-connections/other-data-powered-products.md#accept-ach-direct-debit), request the `payment_method` permission.

## Set up a return URL [Client-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](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app) and set up your app delegate to forward the URL to the SDK. Stripe doesn’t support [universal links](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content).

> Stripe might append additional parameters to the provided return URL. Make sure that return URLs with extra parameters aren’t rejected by your code.

#### SceneDelegate

#### Swift

```swift
// This method handles opening custom URL schemes (for example, "your-app://stripe-redirect")
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else {
        return
    }
    let stripeHandled = StripeAPI.handleURLCallback(with: url)
    if (!stripeHandled) {
        // This was not a Stripe url – handle the URL normally as you would
    }
}

```

#### AppDelegate

#### Swift

```swift
// This method handles opening custom URL schemes (for example, "your-app://stripe-redirect")
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    let stripeHandled = StripeAPI.handleURLCallback(with: url)
    if (stripeHandled) {
        return true
    } else {
        // This was not a Stripe url – handle the URL normally as you would
    }
    return false
}
```

#### SwiftUI

#### Swift

```swift

@main
struct MyApp: App {
  var body: some Scene {
    WindowGroup {
      Text("Hello, world!").onOpenURL { incomingURL in
          let stripeHandled = StripeAPI.handleURLCallback(with: incomingURL)
          if (!stripeHandled) {
            // This was not a Stripe url – handle the URL normally as you would
          }
        }
    }
  }
}
```

## Integrate FinancialConnectionsSheet [Client-side]

Before displaying the Financial Connections flow, your page should include a **Connect Financial Account** button to present the Stripe’s UI.

In your app, retrieve the `FinancialConnectionsSession` *client secret* (A client secret is used with your publishable key to authenticate a request for a single object. Each client secret is unique to the object it's associated with) and publishable key from the endpoint you created in the previous step. Set your publishable key using `StripeAPI.shared` and initialize `FinancialConnectionsSheet`. Pass the `returnURL` you set up in the previous step.

#### Swift

```swift

import StripeFinancialConnections

class ViewController: UIViewController {
  @IBOutlet weak var connectFinancialAccountButton: UIButton!
  var financialConnectionsSheet: FinancialConnectionsSheet?
  let backendCheckoutUrl = URL(string: "Your backend endpoint")! // Your backend endpoint

  override func viewDidLoad() {
    super.viewDidLoad()

    connectFinancialAccountButton.addTarget(self, action: #selector(didTapConnectFinancialAccountButton), for: .touchUpInside)
    connectFinancialAccountButton.isEnabled = false

    // MARK: Fetch the FinancialConnectionsSession client secret and publishable key
    var request = URLRequest(url: backendCheckoutUrl)
    request.httpMethod = "POST"
    let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in
      guard let data = data,
            let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any],
            let clientSecret = json["client_secret"] as? String,
            let publishableKey = json["publishable_key"] as? String,
            let self = self else {
        // Handle error
        return
      }

      // MARK: Set your Stripe publishable key - this allows the SDK to make requests to Stripe for your account
      STPAPIClient.shared.publishableKey = publishableKey

      self.financialConnectionsSheet = FinancialConnectionsSheet(
        financialConnectionsSessionClientSecret: clientSecret,
        returnURL: "https://your-app-domain.com/stripe-redirect"
      )

      DispatchQueue.main.async {
        self.connectFinancialAccountButton.isEnabled = true
      }
    })
    task.resume()
  }

  @objc
  func didTapConnectFinancialAccountButton() {
      // implemented in the next steps
  }
}
```

When the customer taps the **Connect Financial Account** button, call `FinancialConnectionsSheet#present` to present the financial connections sheet. After the customer completes the financial connections flow, the sheet is dismissed and the completion block is called with a `FinancialConnectionsSheetResult`.

#### Swift

```swift

@objc
func didTapConnectFinancialAccountButton() {// MARK: Start the financial connections flow
  financialConnectionsSheet?.present(
    from: self,
    completion: { result in
        switch result {
        case .completed(session: let financialConnectionsSession):
            let accounts = financialConnectionsSession.accounts.data.filter { $0.last4 != nil }
            let accountInfos = accounts.map { "\($0.institutionName) ....\($0.last4!)" }
            print("Completed with \(accountInfos.joined(separator: "\n")) accounts")
        case .canceled:
            print("Canceled!")
        case .failed(let error):
            print("Failed!")
            print(error)
        }
  })

}
```

## Retrieve data on a Financial Connections account [Server-side]

After your user has successfully completed the authentication flow, access or refresh the account data you’ve specified in the `permissions` parameter of the Financial Connections Session.

To protect the privacy of your user’s data, account data accessible to you is limited to the data you’ve specified in the `permissions` parameter.

Follow the guides for [balances](https://docs.stripe.com/financial-connections/balances.md), [ownership](https://docs.stripe.com/financial-connections/ownership.md) and [transactions](https://docs.stripe.com/financial-connections/transactions.md) to start retrieving account data.

## Optional: Customize the sheet [Client-side]

All customization configurations use the `FinancialConnectionsSheet.Configuration` object.

### Dark mode

`FinancialConnectionsSheet` automatically adapts to the user’s system-wide appearance settings (light and dark mode). If your app doesn’t support dark mode, you can set `style` to `alwaysLight` or `alwaysDark` mode.

```swift
var configuration = FinancialConnectionsSheet.Configuration()
configuration.style = .alwaysLight
```

> When `FinancialConnectionsSheet` is launched through the [PaymentSheet SDK](https://docs.stripe.com/payments/accept-a-payment.md?payment-ui=mobile&platform=ios), the [style](https://docs.stripe.com/payments/accept-a-payment.md?payment-ui=mobile&platform=ios#dark-mode) specified in the `PaymentSheet.Configuration` is automatically inherited.

## Optional: Accept an ACH Direct Debit payment from a Financial Connections account

You can optionally accept ACH Direct Debit payments on a previously collected Financial Connections account as long as the `supported_payment_method_types` attribute on the account includes `us_bank_account`.

Only US bank accounts are eligible to accept ACH Direct Debits such as a checking or savings account.

To accept an ACH Direct Debit payment on a previously collected account, you must have specified `payment_method` in the [data permissions](https://docs.stripe.com/financial-connections/fundamentals.md#data-permissions) parameter on the Financial Connections Session.

### Create a Payment Intent or Setup Intent

Use the *Payment Intents API* (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) to accept an ACH Direct payment for an account you want to charge now. Use the *Setup Intents API* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) to save details for future ACH Direct Debit payments. [Learn more about the difference between a](https://support.stripe.com/questions/payment-intents-api-vs-setup-intents-api) Payment Intent and Setup Intent.

This path shows you how to accept an ACH Direct Debit payment using the Payment Intents API.

Use the Financial Connections account ID and the customer ID to create a Payment Intent:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method_types[]=us_bank_account" \
  -d "payment_method_data[us_bank_account][financial_connections_account]={{FINANCIALCONNECTIONSACCOUNT_ID}}" \
  -d "payment_method_data[type]=us_bank_account" \
  -d "payment_method_data[billing_details][name]=J. Customer" \
  -d amount=100 \
  -d currency=usd
```

This will return a Payment Intent similar to:

```json

{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_details": {
    "tip": {
      "amount": null
    }
  },
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [

    ],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "confirmation_method": "automatic",
  "created": 1651010665,
  "currency": "usd",
  "customer": null,
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "requires_confirmation",
  "transfer_data": null,
  "transfer_group": null
}
```

### Collect mandate acknowledgement and submit the payment

Before you can initiate the payment, you must obtain authorization from your customer by displaying mandate terms for them to accept.

To be compliant with Nacha rules, you must obtain authorization from your customer before you can initiate payment by displaying mandate terms for them to accept. For more information on mandates, see [Mandates](https://docs.stripe.com/payments/ach-direct-debit.md#mandates).

### Confirm the Payment Intent

To confirm this Payment Intent, you need to provide either a [`mandate` parameter](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate) with an existing Mandate ID, or provide [mandate_data](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate_data) to create a new mandate.

For example, if you have a checkout page with a “Place Order” button, clicking that button could trigger a `POST` to an endpoint on your server. That endpoint could extract the request’s User Agent and the client’s IP address, and then make the following request to Stripe’s API:

```curl
curl https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}}/confirm \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "mandate_data[customer_acceptance][accepted_at]=1647448692" \
  -d "mandate_data[customer_acceptance][type]=online" \
  -d "mandate_data[customer_acceptance][online][ip_address]=71.183.194.54" \
  --data-urlencode "mandate_data[customer_acceptance][online][user_agent]=Mozilla/5.0 ..."
```

After successfully confirming the Payment Intent, it will look similar to:

```json
{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [
      {
        "id": "py_17F8CPDyDglZKgWE3uzOAUe9",
        "object": "charge",
        "amount": 100,
        "amount_captured": 100,
        "amount_refunded": 0,
        "application": null,
        "application_fee": null,
        "application_fee_amount": null,
        "balance_transaction": null,
        "billing_details": {
          "address": {
            "city": null,
            "country": null,
            "line1": null,
            "line2": null,
            "postal_code": null,
            "state": null
          },
          "email": null,
          "name": "J. Customer",
          "phone": null
        },
        "calculated_statement_descriptor": null,
        "captured": true,
        "created": 1647448692,
        "currency": "usd",
        "customer": "cus_LKe65xcPnrCiTZ",
        "description": null,
        "destination": null,
        "dispute": null,
        "disputed": false,
        "failure_code": null,
        "failure_message": null,
        "fraud_details": {
        },
        "invoice": null,
        "livemode": false,
        "metadata": {
        },
        "on_behalf_of": null,
        "order": null,
        "outcome": null,
        "paid": false,
        "payment_intent": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
        "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
        "payment_method_details": {
          "type": "us_bank_account",
          "us_bank_account": {
            "account_holder_type": "individual",
            "account_type": "checking",
            "bank_name": "STRIPE TEST BANK",
            "fingerprint": "QnXqpAeqjjh8pPFa",
            "last4": "6789",
            "routing_number": "110000000"
          }
        },
        "receipt_email": null,
        "receipt_number": null,
        "receipt_url": null,
        "refunded": false,
        "refunds": {
          "object": "list",
          "data": [

          ],
          "has_more": false,
          "total_count": 0,
          "url": "/v1/charges/py_17F8CPDyDglZKgWE3uzOAUe9/refunds"
        },
        "review": null,
        "shipping": null,
        "source": null,
        "source_transfer": null,
        "statement_descriptor": null,
        "statement_descriptor_suffix": null,
        "status": "pending",
        "transfer_data": null,
        "transfer_group": null
      }
    ],
    "has_more": false,
    "total_count": 1,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP_secret_m38BKZvm3vSdpGdra360hPEov",
  "confirmation_method": "automatic",
  "created": 1647447792,
  "currency": "usd",
  "customer": "cus_LKe65xcPnrCiTZ",
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "processing",
  "transfer_data": null,
  "transfer_group": null
}
```

ACH Direct Debit is a *delayed notification payment method* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods). This means that it can take up to four business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.

The PaymentIntent you create initially has a status of `processing`. After the payment succeeds, the PaymentIntent status is updated from `processing` to `succeeded`. Learn about the events that are sent when the [PaymentIntent status is updated](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#web-confirm-paymentintent-succeeded).
Available in: US
Not sure about which Financial Connections integration to use? See our [overview of integration options](https://docs.stripe.com/financial-connections/use-cases.md).

Financial Connections lets your users securely share their financial data by linking their external financial accounts to your business. You can use Financial Connections to access user-permissioned financial data such as tokenized account and routing numbers, account balances, account owner information, and historical transactions.

Some common examples of how you can use Financial Connections to improve product experiences for your users include:

- Mitigate fraud when onboarding a customer or business by verifying the [ownership](https://docs.stripe.com/financial-connections/ownership.md) information of an account, such as the name and address of the bank account holder.
- Help your users track expenses, handle bills, manage their finances and take control of their financial well-being with [transactions](https://docs.stripe.com/financial-connections/transactions.md) data.
- Speed up underwriting and improve access to credit and other financial services with transactions and balances data.
- Enable your users to connect their accounts in fewer steps with Link, allowing them to save and reuse their bank account details across Stripe businesses.

## Set up Stripe [Server-side] [Client-side]

[Register](https://dashboard.stripe.com/financial-connections/application) for Financial Connections after your account is approved for live-mode access.

### Server-side 

This integration requires endpoints on your server that communicate with 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 [Stripe Android SDK](https://github.com/stripe/stripe-android) is open source and [fully documented](https://stripe.dev/stripe-android/).

To install the SDK, add `financial-connections` to the `dependencies` block of your [app/build.gradle](https://developer.android.com/studio/build/dependencies) file:

#### Kotlin

```kotlin
plugins {
    id("com.android.application")
}

android { ... }

dependencies {
  // ...

  // Financial Connections Android SDK
  implementation("com.stripe:financial-connections:23.5.0")
}
```

> For details on the latest SDK release and past versions, see the [Releases](https://github.com/stripe/stripe-android/releases) page on GitHub. To receive notifications when a new release is published, [watch releases for the repository](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository).

## Create or retrieve an account holder [Server-side]

Create a [Customer object](https://docs.stripe.com/api/customers/object.md) when users create an account with your business. By providing an email address, Financial Connections can optimize the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow) by dynamically showing a streamlined user interface, for returning [Link](https://support.stripe.com/questions/link-for-financial-connections-support-for-businesses) users.

```curl
curl https://api.stripe.com/v1/customers \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d email={{CUSTOMER_EMAIL}} \
  -d name={{CUSTOMER_NAME}}
```

## Create a Financial Connections Session [Server-side]

> You can find a running implementation of this endpoint [available on Glitch](https://glitch.com/edit/#!/remix/stripe-mobile-connections-example) for quick testing.

Before you can retrieve data from a user’s bank account with Financial Connections, your user must authenticate their account with the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow).

Your user begins the authentication flow when they want to connect their account to your site or application. Insert a button or link on your site or in your application, which allows a user to link their account—for example, your button might say “Link your bank account”.

Create a Financial Connections Session by posting to `/v1/financial_connections/sessions`:

```curl
curl https://api.stripe.com/v1/financial_connections/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "account_holder[type]=customer" \
  -d "account_holder[customer]={{CUSTOMER_ID}}" \
  -d "permissions[]=balances" \
  -d "permissions[]=ownership" \
  -d "permissions[]=payment_method" \
  -d "permissions[]=transactions"
```

1. Set `account_holder[customer]` to the Customer `id`.
1. Set the data `permissions` parameter to include the data required to fulfill your use case.
1. (Optional) set the `prefetch` parameter for retrieving the data on account creation.

The [permissions](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-permissions) parameter controls which account data you can access. You must request at least one permission. When completing the authentication flow, your user can see the data you’ve requested access to, and provide their consent to share it.

Consider the data required to fulfill your use case and request permission to access only the data you need. Requesting permissions that go well beyond your application’s scope might erode your users’ trust in how you use their data. For example, if you’re building a personal financial management application or a lending product, you might request `transactions` data. If you’re mitigating fraud such as account takeovers, you might want to request `ownership` details.

After your user authenticates their account, you can expand data permissions only by creating a new Financial Connections Session and specifying a new value for the `permissions` parameter. Your user must complete the authentication flow again, where they’ll see the additional data you’ve requested permission to access, and provide consent to share their data.

The optional [prefetch parameter](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-prefetch) controls which data you retrieve immediately after the user connects their account. Use this option if you know you always want a certain type of data. It removes the need to make an extra API call to initiate a [data refresh](https://docs.stripe.com/api/financial_connections/accounts/refresh.md).

To preserve the option to [accept ACH Direct Debit payments](https://docs.stripe.com/financial-connections/other-data-powered-products.md#accept-ach-direct-debit), request the `payment_method` permission.

## Integrate FinancialConnectionsSheet [Server-side] [Client-side]

Before displaying the Financial Connections flow, your page should include a **Connect Financial Account** button to present the Stripe’s UI.

[Initialize](https://stripe.dev/stripe-android/financial-connections/com.stripe.android.financialconnections/-financial-connections-sheet/-companion/index.html) a `FinancialConnectionsSheet` instance inside `onCreate` of your checkout Activity, passing a method to handle the result.

#### Kotlin

```kotlin
import com.stripe.android.financialconnections.FinancialConnectionsSheet

class MyHostActivity : AppCompatActivity() {
  lateinit var financialConnectionsSheet: FinancialConnectionsSheet

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    financialConnectionsSheet = FinancialConnectionsSheet.create(this, ::onFinancialConnectionsSheetResult)
  }

  fun onFinancialConnectionsSheetResult(result: FinancialConnectionsSheetResult) {
    // implemented in the next steps
  }
}
```

Next, fetch the `FinancialConnectionsSession` *client secret* (A client secret is used with your publishable key to authenticate a request for a single object. Each client secret is unique to the object it's associated with), and publishable key from the endpoint you created in the previous step. Set these fields using the [FinancialConnectionsSheet.Configuration](https://stripe.dev/stripe-android/financial-connections/com.stripe.android.financialconnections/-financial-connections-sheet/-configuration/index.html) and store the others for use when you present the [FinancialConnectionsSheet](https://stripe.dev/stripe-android/financial-connections/com.stripe.android.financialconnections/-financial-connections-sheet/index.html).

#### Kotlin

```kotlin
import com.stripe.android.financialconnections.FinancialConnectionsSheet// Add the following lines to build.gradle to use this example's networking library:
//   implementation 'com.github.kittinunf.fuel:fuel:2.3.1'
//   implementation 'com.github.kittinunf.fuel:fuel-json:2.3.1'
import com.github.kittinunf.fuel.httpPost
import com.github.kittinunf.fuel.json.responseJson
import com.github.kittinunf.result.Result

class MyHostActivity : AppCompatActivity() {
  lateinit var financialConnectionsSheet: FinancialConnectionsSheetlateinit var clientSecret: String
  lateinit var publishableKey: String

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    financialConnectionsSheet = FinancialConnectionsSheet.create(this, ::onFinancialConnectionsSheetResult)"Your backend endpoint/connections-sheet".httpPost().responseJson { _, _, result ->
      if (result is Result.Success) {
        val responseJson = result.get().obj()
        clientSecret = responseJson.getString("financialConnectionsSessionClientSecret")
        publishableKey = responseJson.getString("publishableKey")
      }
    }
  }

  fun onFinancialConnectionsSheetResult(result: FinancialConnectionsSheetResult) {
    // implemented in the next steps
  }
}
```

When the customer taps your **Connect Financial Account** button, call [FinancialConnectionsSheet#present](https://stripe.dev/stripe-android/financial-connections/com.stripe.android.financialconnections/-financial-connections-sheet/present.html) to present the Financial Connections Sheet. After the customer completes the connection, the sheet closes. The [FinancialConnectionsSheetResultCallback](https://stripe.dev/stripe-android/financial-connections/com.stripe.android.financialconnections/-financial-connections-sheet-result-callback/index.html) that you declared in the preceding step is called with [FinancialConnectionsSheetResult](https://stripe.dev/stripe-android/financial-connections/com.stripe.android.financialconnections/-financial-connections-sheet-result/index.html).

#### Kotlin

```kotlin
// ...
class MyHostActivity : AppCompatActivity() {
  lateinit var financialConnectionsSheet: FinancialConnectionsSheet
  lateinit var clientSecret: String
  lateinit var publishableKey: String
  // ...fun presentFinancialConnectionsSheet() {
    financialConnectionsSheet.present(
      configuration = FinancialConnectionsSheet.Configuration(
        financialConnectionsSessionClientSecret = clientSecret,
        publishableKey = publishableKey
      )
    )
  }

  fun onFinancialConnectionsSheetResult(result: FinancialConnectionsSheetResult) {when(result) {
      is FinancialConnectionsSheetResult.Canceled -> {
        print("Canceled")
      }
      is FinancialConnectionsSheetResult.Failed -> {
        print("Failed")
        print("${result.error}")
      }
      is FinancialConnectionsSheetResult.Completed -> {
        // Display for example, a list of accounts.
        val accountInfos = result.financialConnectionsSession.accounts.data
          .map { "${it.institutionName} ${it.last4}" }
        print("Completed with ${accountInfos.joinToString("\n")} accounts")
      }
    }
  }
}
```

## Retrieve data on a Financial Connections account [Server-side]

After your user has successfully completed the authentication flow, access or refresh the account data you’ve specified in the `permissions` parameter of the Financial Connections Session.

To protect the privacy of your user’s data, account data accessible to you is limited to the data you’ve specified in the `permissions` parameter.

Follow the guides for [balances](https://docs.stripe.com/financial-connections/balances.md), [ownership](https://docs.stripe.com/financial-connections/ownership.md) and [transactions](https://docs.stripe.com/financial-connections/transactions.md) to start retrieving account data.

## Optional: Customize the sheet [Client-side]

### Dark mode

By default, `FinancialConnectionsSheet` automatically adapts to the user’s system-wide appearance settings (light and dark mode). You can change this by setting light or dark mode on your app:

#### Kotlin

```kotlin
// force dark
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
// force light
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
```

## Optional: Accept an ACH Direct Debit payment from a Financial Connections account

You can optionally accept ACH Direct Debit payments on a previously collected Financial Connections account as long as the `supported_payment_method_types` attribute on the account includes `us_bank_account`.

Only US bank accounts are eligible to accept ACH Direct Debits such as a checking or savings account.

To accept an ACH Direct Debit payment on a previously collected account, you must have specified `payment_method` in the [data permissions](https://docs.stripe.com/financial-connections/fundamentals.md#data-permissions) parameter on the Financial Connections Session.

### Create a Payment Intent or Setup Intent

Use the *Payment Intents API* (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) to accept an ACH Direct payment for an account you want to charge now. Use the *Setup Intents API* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) to save details for future ACH Direct Debit payments. [Learn more about the difference between a](https://support.stripe.com/questions/payment-intents-api-vs-setup-intents-api) Payment Intent and Setup Intent.

This path shows you how to accept an ACH Direct Debit payment using the Payment Intents API.

Use the Financial Connections account ID and the customer ID to create a Payment Intent:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method_types[]=us_bank_account" \
  -d "payment_method_data[us_bank_account][financial_connections_account]={{FINANCIALCONNECTIONSACCOUNT_ID}}" \
  -d "payment_method_data[type]=us_bank_account" \
  -d "payment_method_data[billing_details][name]=J. Customer" \
  -d amount=100 \
  -d currency=usd
```

This will return a Payment Intent similar to:

```json

{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_details": {
    "tip": {
      "amount": null
    }
  },
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [

    ],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "confirmation_method": "automatic",
  "created": 1651010665,
  "currency": "usd",
  "customer": null,
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "requires_confirmation",
  "transfer_data": null,
  "transfer_group": null
}
```

### Collect mandate acknowledgement and submit the payment

Before you can initiate the payment, you must obtain authorization from your customer by displaying mandate terms for them to accept.

To be compliant with Nacha rules, you must obtain authorization from your customer before you can initiate payment by displaying mandate terms for them to accept. For more information on mandates, see [Mandates](https://docs.stripe.com/payments/ach-direct-debit.md#mandates).

### Confirm the Payment Intent

To confirm this Payment Intent, you need to provide either a [`mandate` parameter](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate) with an existing Mandate ID, or provide [mandate_data](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate_data) to create a new mandate.

For example, if you have a checkout page with a “Place Order” button, clicking that button could trigger a `POST` to an endpoint on your server. That endpoint could extract the request’s User Agent and the client’s IP address, and then make the following request to Stripe’s API:

```curl
curl https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}}/confirm \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "mandate_data[customer_acceptance][accepted_at]=1647448692" \
  -d "mandate_data[customer_acceptance][type]=online" \
  -d "mandate_data[customer_acceptance][online][ip_address]=71.183.194.54" \
  --data-urlencode "mandate_data[customer_acceptance][online][user_agent]=Mozilla/5.0 ..."
```

After successfully confirming the Payment Intent, it will look similar to:

```json
{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [
      {
        "id": "py_17F8CPDyDglZKgWE3uzOAUe9",
        "object": "charge",
        "amount": 100,
        "amount_captured": 100,
        "amount_refunded": 0,
        "application": null,
        "application_fee": null,
        "application_fee_amount": null,
        "balance_transaction": null,
        "billing_details": {
          "address": {
            "city": null,
            "country": null,
            "line1": null,
            "line2": null,
            "postal_code": null,
            "state": null
          },
          "email": null,
          "name": "J. Customer",
          "phone": null
        },
        "calculated_statement_descriptor": null,
        "captured": true,
        "created": 1647448692,
        "currency": "usd",
        "customer": "cus_LKe65xcPnrCiTZ",
        "description": null,
        "destination": null,
        "dispute": null,
        "disputed": false,
        "failure_code": null,
        "failure_message": null,
        "fraud_details": {
        },
        "invoice": null,
        "livemode": false,
        "metadata": {
        },
        "on_behalf_of": null,
        "order": null,
        "outcome": null,
        "paid": false,
        "payment_intent": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
        "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
        "payment_method_details": {
          "type": "us_bank_account",
          "us_bank_account": {
            "account_holder_type": "individual",
            "account_type": "checking",
            "bank_name": "STRIPE TEST BANK",
            "fingerprint": "QnXqpAeqjjh8pPFa",
            "last4": "6789",
            "routing_number": "110000000"
          }
        },
        "receipt_email": null,
        "receipt_number": null,
        "receipt_url": null,
        "refunded": false,
        "refunds": {
          "object": "list",
          "data": [

          ],
          "has_more": false,
          "total_count": 0,
          "url": "/v1/charges/py_17F8CPDyDglZKgWE3uzOAUe9/refunds"
        },
        "review": null,
        "shipping": null,
        "source": null,
        "source_transfer": null,
        "statement_descriptor": null,
        "statement_descriptor_suffix": null,
        "status": "pending",
        "transfer_data": null,
        "transfer_group": null
      }
    ],
    "has_more": false,
    "total_count": 1,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP_secret_m38BKZvm3vSdpGdra360hPEov",
  "confirmation_method": "automatic",
  "created": 1647447792,
  "currency": "usd",
  "customer": "cus_LKe65xcPnrCiTZ",
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "processing",
  "transfer_data": null,
  "transfer_group": null
}
```

ACH Direct Debit is a *delayed notification payment method* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods). This means that it can take up to four business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.

The PaymentIntent you create initially has a status of `processing`. After the payment succeeds, the PaymentIntent status is updated from `processing` to `succeeded`. Learn about the events that are sent when the [PaymentIntent status is updated](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#web-confirm-paymentintent-succeeded).
Available in: US
Not sure about which Financial Connections integration to use? See our [overview of integration options](https://docs.stripe.com/financial-connections/use-cases.md).

Financial Connections lets your users securely share their financial data by linking their external financial accounts to your business. You can use Financial Connections to access user-permissioned financial data such as tokenized account and routing numbers, account balances, account owner information, and historical transactions.

Some common examples of how you can use Financial Connections to improve product experiences for your users include:

- Mitigate fraud when onboarding a customer or business by verifying the [ownership](https://docs.stripe.com/financial-connections/ownership.md) information of an account, such as the name and address of the bank account holder.
- Help your users track expenses, handle bills, manage their finances and take control of their financial well-being with [transactions](https://docs.stripe.com/financial-connections/transactions.md) data.
- Speed up underwriting and improve access to credit and other financial services with transactions and balances data.
- Enable your users to connect their accounts in fewer steps with Link, allowing them to save and reuse their bank account details across Stripe businesses.

## Set up Stripe [Server-side] [Client-side]

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

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

To initialize Stripe in your React Native app, either wrap your payment screen with the `StripeProvider` component, or use the `initStripe` initialization 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 initialize 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 (
    <StripeProvider
      publishableKey={publishableKey}
      merchantIdentifier="merchant.identifier" // required for Apple Pay
      urlScheme="your-url-scheme" // required for 3D Secure and bank redirects
    >
      {/* Your app code here */}
    </StripeProvider>
  );
}
```

> 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 or retrieve an account holder [Server-side]

Create a [Customer object](https://docs.stripe.com/api/customers/object.md) when users create an account with your business. By providing an email address, Financial Connections can optimize the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow) by dynamically showing a streamlined user interface, for returning [Link](https://support.stripe.com/questions/link-for-financial-connections-support-for-businesses) users.

```curl
curl https://api.stripe.com/v1/customers \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d email={{CUSTOMER_EMAIL}} \
  -d name={{CUSTOMER_NAME}}
```

## Create a Financial Connections Session [Server-side]

> You can find a running implementation of this endpoint [available on Glitch](https://glitch.com/edit/#!/remix/stripe-mobile-connections-example) for quick testing.

Before you can retrieve data from a user’s bank account with Financial Connections, your user must authenticate their account with the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow).

Your user begins the authentication flow when they want to connect their account to your site or application. Insert a button or link on your site or in your application, which allows a user to link their account—for example, your button might say “Link your bank account”.

Create a Financial Connections Session by posting to `/v1/financial_connections/sessions`:

```curl
curl https://api.stripe.com/v1/financial_connections/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "account_holder[type]=customer" \
  -d "account_holder[customer]={{CUSTOMER_ID}}" \
  -d "permissions[]=balances" \
  -d "permissions[]=ownership" \
  -d "permissions[]=payment_method" \
  -d "permissions[]=transactions"
```

1. Set `account_holder[customer]` to the Customer `id`.
1. Set the data `permissions` parameter to include the data required to fulfill your use case.
1. (Optional) set the `prefetch` parameter for retrieving the data on account creation.

The [permissions](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-permissions) parameter controls which account data you can access. You must request at least one permission. When completing the authentication flow, your user can see the data you’ve requested access to, and provide their consent to share it.

Consider the data required to fulfill your use case and request permission to access only the data you need. Requesting permissions that go well beyond your application’s scope might erode your users’ trust in how you use their data. For example, if you’re building a personal financial management application or a lending product, you might request `transactions` data. If you’re mitigating fraud such as account takeovers, you might want to request `ownership` details.

After your user authenticates their account, you can expand data permissions only by creating a new Financial Connections Session and specifying a new value for the `permissions` parameter. Your user must complete the authentication flow again, where they’ll see the additional data you’ve requested permission to access, and provide consent to share their data.

The optional [prefetch parameter](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-prefetch) controls which data you retrieve immediately after the user connects their account. Use this option if you know you always want a certain type of data. It removes the need to make an extra API call to initiate a [data refresh](https://docs.stripe.com/api/financial_connections/accounts/refresh.md).

To preserve the option to [accept ACH Direct Debit payments](https://docs.stripe.com/financial-connections/other-data-powered-products.md#accept-ach-direct-debit), request the `payment_method` permission.

## Collect a Financial Connections account [Client-side]

Import the `collectFinancialConnectionsAccounts` function from Stripe’s React Native SDK.

```javascript
import {collectFinancialConnectionsAccounts} from '@stripe/stripe-react-native';
```

Use `collectFinancialConnectionsAccounts` to collect the bank account by passing in the `client_secret` from above, and then handle the result appropriately:

```javascript
// Assume you have a <Button /> to collect payout accounts, whose onPress is handleCollectPress
const handleCollectPress = async () => {
  // Fetch the clientSecret you created above and pass it to collectFinancialConnectionsAccounts
  const {session, error} = await collectFinancialConnectionsAccounts(
    clientSecret,
  );

  if (error) {
    Alert.alert(`Error code: ${error.code}`, error.message);
  } else {
    Alert.alert('Success');
    console.log(
      'Successfully obtained session: ',
      JSON.stringify(session, null, 2),
    );
  }
};
```

`collectFinancialConnectionsAccounts` returns a `Promise`. When your user completes the modal flow, the `Promise` resolves with one of the following:

- A `session`, representing the completed Financial Connections Session, if the user can successfully link their account. This `session` includes an `accounts` array that contains the list of connected accounts.
- An `error` if the Session fails or is canceled.

By default your connected accounts can only add account types like a checking or savings account in the authentication flow because Stripe can only facilitate payouts to an ACH-enabled account.

## Optional: Customize the bank account collector [Client-side]

### Dark mode

By default, the bank account collector automatically switches between light and dark mode compatible colors based on device settings. If your app doesn’t support dark or light mode, you can set `style` to `alwaysLight` or `alwaysDark`, respectively.

```javascript
const {session, error} = await collectFinancialConnectionsAccounts(
   clientSecret,
   {
     style: 'alwaysLight',
   },
);
```

To achieve an always light or always dark appearance in your Android app, make use of Android’s `AppCompatDelegate`. Our UI automatically respects this setting.

#### Kotlin

```kotlin
// force dark
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
// force light
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
```

## Set up a return URL (iOS only) [Client-side]

When a customer exits your app (for example to authenticate in Safari or their banking app), provide a way for them to automatically return to your app. Many payment method types *require* a return URL. If you don’t provide one, we can’t present payment methods that require a return URL to your users, even if you’ve enabled them.

To provide a return URL:

1. [Register](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app#Register-your-URL-scheme) a custom URL. Universal links aren’t supported.
1. [Configure](https://reactnative.dev/docs/linking) your custom URL.
1. Set up your root component to forward the URL to the Stripe SDK as shown below.

> If you’re using Expo, [set your scheme](https://docs.expo.io/guides/linking/#in-a-standalone-app) in the `app.json` file.

```jsx
import { useEffect, useCallback } from 'react';
import { Linking } from 'react-native';
import { useStripe } from '@stripe/stripe-react-native';

export default function MyApp() {
  const { handleURLCallback } = useStripe();

  const handleDeepLink = useCallback(
    async (url: string | null) => {
      if (url) {
        const stripeHandled = await handleURLCallback(url);
        if (stripeHandled) {
          // This was a Stripe URL - you can return or add extra handling here as you see fit
        } else {
          // This was NOT a Stripe URL – handle as you normally would
        }
      }
    },
    [handleURLCallback]
  );

  useEffect(() => {
    const getUrlAsync = async () => {
      const initialUrl = await Linking.getInitialURL();
      handleDeepLink(initialUrl);
    };

    getUrlAsync();

    const deepLinkListener = Linking.addEventListener(
      'url',
      (event: { url: string }) => {
        handleDeepLink(event.url);
      }
    );

    return () => deepLinkListener.remove();
  }, [handleDeepLink]);

  return (
    <View>
      <AwesomeAppComponent />
    </View>
  );
}
```

For more information on native URL schemes, refer to the [Android](https://developer.android.com/training/app-links/deep-linking) and [iOS](https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app) docs.

## Retrieve data on a Financial Connections account [Server-side]

After your user has successfully completed the authentication flow, access or refresh the account data you’ve specified in the `permissions` parameter of the Financial Connections Session.

To protect the privacy of your user’s data, account data accessible to you is limited to the data you’ve specified in the `permissions` parameter.

Follow the guides for [balances](https://docs.stripe.com/financial-connections/balances.md), [ownership](https://docs.stripe.com/financial-connections/ownership.md) and [transactions](https://docs.stripe.com/financial-connections/transactions.md) to start retrieving account data.

## Optional: Accept an ACH Direct Debit payment from a Financial Connections account

You can optionally accept ACH Direct Debit payments on a previously collected Financial Connections account as long as the `supported_payment_method_types` attribute on the account includes `us_bank_account`.

Only US bank accounts are eligible to accept ACH Direct Debits such as a checking or savings account.

To accept an ACH Direct Debit payment on a previously collected account, you must have specified `payment_method` in the [data permissions](https://docs.stripe.com/financial-connections/fundamentals.md#data-permissions) parameter on the Financial Connections Session.

### Create a Payment Intent or Setup Intent

Use the *Payment Intents API* (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) to accept an ACH Direct payment for an account you want to charge now. Use the *Setup Intents API* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) to save details for future ACH Direct Debit payments. [Learn more about the difference between a](https://support.stripe.com/questions/payment-intents-api-vs-setup-intents-api) Payment Intent and Setup Intent.

This path shows you how to accept an ACH Direct Debit payment using the Payment Intents API.

Use the Financial Connections account ID and the customer ID to create a Payment Intent:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method_types[]=us_bank_account" \
  -d "payment_method_data[us_bank_account][financial_connections_account]={{FINANCIALCONNECTIONSACCOUNT_ID}}" \
  -d "payment_method_data[type]=us_bank_account" \
  -d "payment_method_data[billing_details][name]=J. Customer" \
  -d amount=100 \
  -d currency=usd
```

This will return a Payment Intent similar to:

```json

{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_details": {
    "tip": {
      "amount": null
    }
  },
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [

    ],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "confirmation_method": "automatic",
  "created": 1651010665,
  "currency": "usd",
  "customer": null,
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "requires_confirmation",
  "transfer_data": null,
  "transfer_group": null
}
```

### Collect mandate acknowledgement and submit the payment

Before you can initiate the payment, you must obtain authorization from your customer by displaying mandate terms for them to accept.

To be compliant with Nacha rules, you must obtain authorization from your customer before you can initiate payment by displaying mandate terms for them to accept. For more information on mandates, see [Mandates](https://docs.stripe.com/payments/ach-direct-debit.md#mandates).

### Confirm the Payment Intent

To confirm this Payment Intent, you need to provide either a [`mandate` parameter](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate) with an existing Mandate ID, or provide [mandate_data](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate_data) to create a new mandate.

For example, if you have a checkout page with a “Place Order” button, clicking that button could trigger a `POST` to an endpoint on your server. That endpoint could extract the request’s User Agent and the client’s IP address, and then make the following request to Stripe’s API:

```curl
curl https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}}/confirm \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "mandate_data[customer_acceptance][accepted_at]=1647448692" \
  -d "mandate_data[customer_acceptance][type]=online" \
  -d "mandate_data[customer_acceptance][online][ip_address]=71.183.194.54" \
  --data-urlencode "mandate_data[customer_acceptance][online][user_agent]=Mozilla/5.0 ..."
```

After successfully confirming the Payment Intent, it will look similar to:

```json
{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [
      {
        "id": "py_17F8CPDyDglZKgWE3uzOAUe9",
        "object": "charge",
        "amount": 100,
        "amount_captured": 100,
        "amount_refunded": 0,
        "application": null,
        "application_fee": null,
        "application_fee_amount": null,
        "balance_transaction": null,
        "billing_details": {
          "address": {
            "city": null,
            "country": null,
            "line1": null,
            "line2": null,
            "postal_code": null,
            "state": null
          },
          "email": null,
          "name": "J. Customer",
          "phone": null
        },
        "calculated_statement_descriptor": null,
        "captured": true,
        "created": 1647448692,
        "currency": "usd",
        "customer": "cus_LKe65xcPnrCiTZ",
        "description": null,
        "destination": null,
        "dispute": null,
        "disputed": false,
        "failure_code": null,
        "failure_message": null,
        "fraud_details": {
        },
        "invoice": null,
        "livemode": false,
        "metadata": {
        },
        "on_behalf_of": null,
        "order": null,
        "outcome": null,
        "paid": false,
        "payment_intent": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
        "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
        "payment_method_details": {
          "type": "us_bank_account",
          "us_bank_account": {
            "account_holder_type": "individual",
            "account_type": "checking",
            "bank_name": "STRIPE TEST BANK",
            "fingerprint": "QnXqpAeqjjh8pPFa",
            "last4": "6789",
            "routing_number": "110000000"
          }
        },
        "receipt_email": null,
        "receipt_number": null,
        "receipt_url": null,
        "refunded": false,
        "refunds": {
          "object": "list",
          "data": [

          ],
          "has_more": false,
          "total_count": 0,
          "url": "/v1/charges/py_17F8CPDyDglZKgWE3uzOAUe9/refunds"
        },
        "review": null,
        "shipping": null,
        "source": null,
        "source_transfer": null,
        "statement_descriptor": null,
        "statement_descriptor_suffix": null,
        "status": "pending",
        "transfer_data": null,
        "transfer_group": null
      }
    ],
    "has_more": false,
    "total_count": 1,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP_secret_m38BKZvm3vSdpGdra360hPEov",
  "confirmation_method": "automatic",
  "created": 1647447792,
  "currency": "usd",
  "customer": "cus_LKe65xcPnrCiTZ",
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "processing",
  "transfer_data": null,
  "transfer_group": null
}
```

ACH Direct Debit is a *delayed notification payment method* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods). This means that it can take up to four business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.

The PaymentIntent you create initially has a status of `processing`. After the payment succeeds, the PaymentIntent status is updated from `processing` to `succeeded`. Learn about the events that are sent when the [PaymentIntent status is updated](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#web-confirm-paymentintent-succeeded).
Available in: US
> Starting January 1, 2024, all webview-based integrations must properly manage secure institution authentication and app redirects to avoid disruptions in your Financial Connections authorization flow.
> 
> While using the WebView API, make sure that you follow the provided instructions to correctly set up redirects and handle authentication within a secure browser instance. This is a required step because some financial institutions block requests from insecure sessions.
> 
> To simplify development, use our SDKs for [Android](https://docs.stripe.com/financial-connections/other-data-powered-products.md?platform=android), [iOS](https://docs.stripe.com/financial-connections/other-data-powered-products.md?platform=ios), and [React Native](https://docs.stripe.com/financial-connections/other-data-powered-products.md?platform=react-native) to handle authentication, secure redirects, and session management. This reduces the need for manual configuration and helps make sure the process of linking accounts is straightforward and secure for users.

> As an alternative to using the Webview API, you can redirect your users to a Stripe-hosted page to link their accounts. If you’re interested in using this preview feature, [email us](mailto:financial-connections-beta+hosted@stripe.com) for access.

Not sure about which Financial Connections integration to use? See our [overview of integration options](https://docs.stripe.com/financial-connections/use-cases.md).

Financial Connections lets your users securely share their financial data by linking their external financial accounts to your business. You can use Financial Connections to access user-permissioned financial data such as tokenized account and routing numbers, account balances, account owner information, and historical transactions.

Some common examples of how you can use Financial Connections to improve product experiences for your users include:

- Mitigate fraud when onboarding a customer or business by verifying the [ownership](https://docs.stripe.com/financial-connections/ownership.md) information of an account, such as the name and address of the bank account holder.
- Help your users track expenses, handle bills, manage their finances and take control of their financial well-being with [transactions](https://docs.stripe.com/financial-connections/transactions.md) data.
- Speed up underwriting and improve access to credit and other financial services with transactions and balances data.
- Enable your users to connect their accounts in fewer steps with Link, allowing them to save and reuse their bank account details across Stripe businesses.

## Overview [Client-side]

To embed Financial Connections within a WebView, begin with one of our example WebView applications:

- [Android Webview](https://github.com/stripe/stripe-android/blob/master/financial-connections-example/src/main/java/com/stripe/android/financialconnections/example/FinancialConnectionsWebviewExampleActivity.kt)
- [iOS WKWebview](https://github.com/stripe/stripe-ios/blob/master/Example/FinancialConnections%20Example/FinancialConnections%20Example/WebViewViewController.swift)

These sample applications are fully functional (on both simulators and physical devices) and contain all necessary code to set up Financial Connections and handle events delivered to your application through HTTP redirect urls.

## Set up Stripe [Server-side]

[Register](https://dashboard.stripe.com/financial-connections/application) for Financial Connections after we approve your account for live-mode access.

Use our official libraries for access to the Stripe 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 or retrieve an account holder [Server-side]

Create a [Customer object](https://docs.stripe.com/api/customers/object.md) when users create an account with your business. By providing an email address, Financial Connections can optimize the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow) by dynamically showing a streamlined user interface, for returning [Link](https://support.stripe.com/questions/link-for-financial-connections-support-for-businesses) users.

```curl
curl https://api.stripe.com/v1/customers \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d email={{CUSTOMER_EMAIL}} \
  -d name={{CUSTOMER_NAME}}
```

## Create a Financial Connections Session [Server-side]

Before you can retrieve data from a user’s bank account with Financial Connections, your user must authenticate their account with the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow).

Your user begins the authentication flow when they want to connect their account to your site or application. Insert a button or link on your site or in your application, which allows a user to link their account—for example, your button might say “Link your bank account”.

Create a Financial Connections Session by posting to `/v1/financial_connections/sessions`:

The [permissions](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-permissions) parameter controls which account data you can access. You must request at least one permission. When completing the authentication flow, your user can see the data you’ve requested access to, and provide their consent to share it.

Consider the data required to fulfill your use case and request permission to access only the data you need. Requesting permissions that go well beyond your application’s scope might erode your users’ trust in how you use their data. For example, if you’re building a personal financial management application or a lending product, you might request `transactions` data. If you’re mitigating fraud such as account takeovers, you might want to request `ownership` details.

After your user authenticates their account, you can expand data permissions only by creating a new Financial Connections Session and specifying a new value for the `permissions` parameter. Your user must complete the authentication flow again, where they’ll see the additional data you’ve requested permission to access, and provide consent to share their data.

The optional [prefetch parameter](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-prefetch) controls which data you retrieve immediately after the user connects their account. Use this option if you know you always want a certain type of data. It removes the need to make an extra API call to initiate a [data refresh](https://docs.stripe.com/api/financial_connections/accounts/refresh.md).

To preserve the option to [accept ACH Direct Debit payments](https://docs.stripe.com/financial-connections/other-data-powered-products.md#accept-ach-direct-debit), request the `payment_method` permission.

To collect accounts in a WebView, specify the `return_url` parameter, which you can use to redirect your customer back to your native app if they log into their bank with OAuth in the native browser.

| iOS                                                 | Android                                               |
| --------------------------------------------------- | ----------------------------------------------------- |
| The return_url needs to be an https universal link. | The return_url needs to be an https Android App Link. |

The following code example demonstrates how to create a WebView-optimized Financial Connections Session that collects accounts with `balances` and `payment_method` data:

```curl
curl https://api.stripe.com/v1/financial_connections/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "account_holder[type]=customer" \
  -d "account_holder[customer]={{CUSTOMER_ID}}" \
  -d "permissions[]=balances" \
  -d "permissions[]=payment_method" \
  --data-urlencode "return_url=https://stripe.com"
```

The request returns a response similar to the following:

```json
{
  "id": "fcsess_1MuOL4IG1CZuezXpoeuzQJYm",
  "object": "financial_connections.session",
  "client_secret": "fcsess_client_secret_UsESkKYzeiRcivgDJZfxZRFh",
  "accounts": {
    "object": "list",
    "data": [],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/financial_connections/accounts?session=fcsess_1MuOL4IG1CZuezXpoeuzQJYm"
  },
  "livemode": false,
  "return_url": "https://stripe.com"
}
```

## Collect a Financial Connections account [Client-side]

Use the returned `client_secret` with Stripe.js to allow your user to connect their accounts. A `client_secret` allows client-side Stripe SDKs to make changes to the Financial Connections Session. Don’t store it, log it, embed it in URLs, or expose it to anyone other than your end user. Make sure that you have [TLS](https://docs.stripe.com/security/guide.md#tls) enabled on any page that includes the client secret.

Use [collectFinancialConnectionsAccounts](https://docs.stripe.com/js/financial_connections/collect_financial_connections_accounts) to collect an account.

```javascript
const stripe = new Stripe('<<YOUR_PUBLISHABLE_KEY>>')
const financialConnectionsSessionResult = await stripe.collectFinancialConnectionsAccounts({ clientSecret: "fcsess_client_secret_UsESkKYzeiRcivgDJZfxZRFh" });
```

### Handle OAuth redirects on your mobile app

In addition to including Stripe.js on your webview-embedded page, your app might need to handle redirecting your customer to their native mobile browser for OAuth login.

> Beginning January 1, 2024, all webview-based integrations need to properly handle secure institution authentication and app redirects, or it will impact your Financial Connections authorization flow. Refer to the iOS or Android instructions above.

#### Android

On Android your app must register an [intent filter](https://developer.android.com/guide/components/intents-filters) for the previously set `return_url` in your manifest.

Read more about [handling links on Android](https://developer.android.com/training/app-links).

```xml
<activity
    android:name="...">

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <!-- Data structure to listen for "https://stripe.com" URL -->
        <data android:scheme="http" />
        <data android:scheme="https" />

        <data android:host="stripe.com" />
    </intent-filter>
</activity>
```

Register a [WebViewClient](https://developer.android.com/reference/android/webkit/WebViewClient) on your WebView. You can override `shouldOverrideUrlLoading` to open OAuth login pages on a Custom Tab / secure browser instance:

```kotlin
val webViewClient = object : WebViewClient() {
  override fun shouldOverrideUrlLoading(
      view: WebView,
      webResourceRequest: WebResourceRequest
  ): Boolean {
      val url = webResourceRequest.url.toString()
      CustomTabsIntent.Builder().build().launchUrl(view.context, Uri.parse(url))
      return true
  }
}
// ...

// register the web view client on your WebView
myWebView.webViewClient = webViewClient
```

#### iOS

On iOS, your app must be equipped to handle the WebView’s attempt to open an OAuth window. This process involves attempting to open the URL in a bank app or, if unsupported, in a secure browser instance.

When the `WKWebView` intercepts a new window request, first attempt to open the URL as a universal link. Universal links allow banking apps installed on the user’s device to directly handle their associated URLs to simplify the authentication process. This method enhances user security and convenience by leveraging the existing, trusted apps already on the device.

If opening the URL as a universal link isn’t successful—either because no supporting bank app is installed or the link isn’t recognized—the app needs to fall back to using `ASWebAuthenticationSession`. This session facilitates the authentication flow within a secure, in-app browser that ensures the safe handling of sensitive information. `ASWebAuthenticationSession` mimics the native browser, allowing users to authenticate without leaving the app.

```swift
class ViewController: UIViewController {
  private var webAuthenticationSession: ASWebAuthenticationSession?

  override func viewDidLoad() {
      super.viewDidLoad()

      // Initialize and configure your WKWebView
      let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())

      // Load your website URL
      webView.load(URLRequest(url: URL(string: "https://yourwebsite.com")!))

      // Add the web view to the view
      view.addSubview(webView)

      // Important: Assign a `uiDelegate` to handle redirects
      webView.uiDelegate = self
  }
}

extension ViewController: WKUIDelegate {
  func webView(
      _ webView: WKWebView,
      createWebViewWith configuration: WKWebViewConfiguration,
      for navigationAction: WKNavigationAction,
      windowFeatures: WKWindowFeatures
  ) -> WKWebView? {
      // Check if the link is attempting to open in a new window.
      // This is typically the case for a banking partner's authentication flow.
      let isAttemptingToOpenLinkInNewWindow = navigationAction.targetFrame?.isMainFrame != true

      if isAttemptingToOpenLinkInNewWindow, let url = navigationAction.request.url {
          // Attempt to open the URL as a universal link.
          // Universal links allow apps on the device to handle specific URLs directly.
          UIApplication.shared.open(
              url,
              options: [.universalLinksOnly: true],
              completionHandler: { [weak self] success in
                  guard let self else { return }
                  if success {
                      // App-to-app flow:
                      // The URL was successfully opened in a banking application that supports universal links.
                      print("Successfully opened the authentication URL in a bank app: \(url)")
                  } else {
                      // Fallback for when no compatible bank app is found:
                      // Create an `ASWebAuthenticationSession` to handle the authentication in a secure in-app browser
                      self.launchInSecureBrowser(url: url)
                  }
              })
      }
      return nil
  }

  private func launchInSecureBrowser(url: URL) {
    let redirectURL = URL(string: "https://yourwebsite.com")!
    let webAuthenticationSession = ASWebAuthenticationSession(
        url: url,
        callbackURLScheme: redirectURL.scheme,
        completionHandler: { redirectURL, error in
            if let error {
                if
                    (error as NSError).domain == ASWebAuthenticationSessionErrorDomain,
                    (error as NSError).code == ASWebAuthenticationSessionError.canceledLogin.rawValue
                {
                    print("User manually closed the browser by pressing 'Cancel' at the top-left corner.")
                } else {
                    print("Received an error from ASWebAuthenticationSession: \(error)")
                }
            } else {
                // IMPORTANT NOTE:
                // The browser will automatically close when
                // the `callbackURLScheme` is called.
                print("Received a redirect URL: \(redirectURL?.absoluteString ?? "null")")
            }
        }
    )
    // Store the session reference to prevent premature deallocation
    self.webAuthenticationSession = webAuthenticationSession

    // Set the presentation context provider to handle the browser's UI presentation
    webAuthenticationSession.presentationContextProvider = self

    // Use an ephemeral session to enhance privacy
    // This also disables the initial Apple alert about signing in to another app
    webAuthenticationSession.prefersEphemeralWebBrowserSession = true

    // Initiate the authentication session
    webAuthenticationSession.start()
  }
}
```

After your customer logs into their institution and authorizes access to their accounts, Stripe redirects to the `return_url` to return to your app. After returning to the app, your customer can resume and complete the bank account detail collection process.

The return value of `stripe.collectFinancialConnectionsAccounts` is a Promise. When the user completes the authentication flow, the Promise resolves with an object that contains the list of connected accounts:

```json
{
  financialConnectionsSession: {
    id: "fcsess_1MuOL4IG1CZuezXpoeuzQJYm",
    accounts: [
      {
        id: "fca_1Jbry3BAjqvGMUSxCDjFsrLU",
        object: "financial_connections.account",
        category: "Checking",
        display_name: "Premium Checking",
        institution_name: "Test Bank",
        last4: "4242"
      }
    ],
    return_url: "https://stripe.com"
  }
}
```

If the user connects no accounts, or exits the authentication flow early, the `accounts` array will be empty.

## Retrieve data on a Financial Connections account [Server-side]

After your user has successfully completed the authentication flow, access or refresh the account data you’ve specified in the `permissions` parameter of the Financial Connections Session.

To protect the privacy of your user’s data, account data accessible to you is limited to the data you’ve specified in the `permissions` parameter.

Follow the guides for [balances](https://docs.stripe.com/financial-connections/balances.md), [ownership](https://docs.stripe.com/financial-connections/ownership.md) and [transactions](https://docs.stripe.com/financial-connections/transactions.md) to start retrieving account data.

## Optional: Accept an ACH Direct Debit payment from a Financial Connections account

You can optionally accept ACH Direct Debit payments on a previously collected Financial Connections account as long as the `supported_payment_method_types` attribute on the account includes `us_bank_account`.

Only US bank accounts are eligible to accept ACH Direct Debits such as a checking or savings account.

To accept an ACH Direct Debit payment on a previously collected account, you must have specified `payment_method` in the [data permissions](https://docs.stripe.com/financial-connections/fundamentals.md#data-permissions) parameter on the Financial Connections Session.

### Create a Payment Intent or Setup Intent

Use the *Payment Intents API* (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) to accept an ACH Direct payment for an account you want to charge now. Use the *Setup Intents API* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) to save details for future ACH Direct Debit payments. [Learn more about the difference between a](https://support.stripe.com/questions/payment-intents-api-vs-setup-intents-api) Payment Intent and Setup Intent.

This path shows you how to accept an ACH Direct Debit payment using the Payment Intents API.

Use the Financial Connections account ID and the customer ID to create a Payment Intent:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method_types[]=us_bank_account" \
  -d "payment_method_data[us_bank_account][financial_connections_account]={{FINANCIALCONNECTIONSACCOUNT_ID}}" \
  -d "payment_method_data[type]=us_bank_account" \
  -d "payment_method_data[billing_details][name]=J. Customer" \
  -d amount=100 \
  -d currency=usd
```

This will return a Payment Intent similar to:

```json

{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_details": {
    "tip": {
      "amount": null
    }
  },
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [

    ],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "confirmation_method": "automatic",
  "created": 1651010665,
  "currency": "usd",
  "customer": null,
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "requires_confirmation",
  "transfer_data": null,
  "transfer_group": null
}
```

### Collect mandate acknowledgement and submit the payment

Before you can initiate the payment, you must obtain authorization from your customer by displaying mandate terms for them to accept.

To be compliant with Nacha rules, you must obtain authorization from your customer before you can initiate payment by displaying mandate terms for them to accept. For more information on mandates, see [Mandates](https://docs.stripe.com/payments/ach-direct-debit.md#mandates).

### Confirm the Payment Intent

To confirm this Payment Intent, you need to provide either a [`mandate` parameter](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate) with an existing Mandate ID, or provide [mandate_data](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate_data) to create a new mandate.

For example, if you have a checkout page with a “Place Order” button, clicking that button could trigger a `POST` to an endpoint on your server. That endpoint could extract the request’s User Agent and the client’s IP address, and then make the following request to Stripe’s API:

```curl
curl https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}}/confirm \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "mandate_data[customer_acceptance][accepted_at]=1647448692" \
  -d "mandate_data[customer_acceptance][type]=online" \
  -d "mandate_data[customer_acceptance][online][ip_address]=71.183.194.54" \
  --data-urlencode "mandate_data[customer_acceptance][online][user_agent]=Mozilla/5.0 ..."
```

After successfully confirming the Payment Intent, it will look similar to:

```json
{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [
      {
        "id": "py_17F8CPDyDglZKgWE3uzOAUe9",
        "object": "charge",
        "amount": 100,
        "amount_captured": 100,
        "amount_refunded": 0,
        "application": null,
        "application_fee": null,
        "application_fee_amount": null,
        "balance_transaction": null,
        "billing_details": {
          "address": {
            "city": null,
            "country": null,
            "line1": null,
            "line2": null,
            "postal_code": null,
            "state": null
          },
          "email": null,
          "name": "J. Customer",
          "phone": null
        },
        "calculated_statement_descriptor": null,
        "captured": true,
        "created": 1647448692,
        "currency": "usd",
        "customer": "cus_LKe65xcPnrCiTZ",
        "description": null,
        "destination": null,
        "dispute": null,
        "disputed": false,
        "failure_code": null,
        "failure_message": null,
        "fraud_details": {
        },
        "invoice": null,
        "livemode": false,
        "metadata": {
        },
        "on_behalf_of": null,
        "order": null,
        "outcome": null,
        "paid": false,
        "payment_intent": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
        "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
        "payment_method_details": {
          "type": "us_bank_account",
          "us_bank_account": {
            "account_holder_type": "individual",
            "account_type": "checking",
            "bank_name": "STRIPE TEST BANK",
            "fingerprint": "QnXqpAeqjjh8pPFa",
            "last4": "6789",
            "routing_number": "110000000"
          }
        },
        "receipt_email": null,
        "receipt_number": null,
        "receipt_url": null,
        "refunded": false,
        "refunds": {
          "object": "list",
          "data": [

          ],
          "has_more": false,
          "total_count": 0,
          "url": "/v1/charges/py_17F8CPDyDglZKgWE3uzOAUe9/refunds"
        },
        "review": null,
        "shipping": null,
        "source": null,
        "source_transfer": null,
        "statement_descriptor": null,
        "statement_descriptor_suffix": null,
        "status": "pending",
        "transfer_data": null,
        "transfer_group": null
      }
    ],
    "has_more": false,
    "total_count": 1,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP_secret_m38BKZvm3vSdpGdra360hPEov",
  "confirmation_method": "automatic",
  "created": 1647447792,
  "currency": "usd",
  "customer": "cus_LKe65xcPnrCiTZ",
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "processing",
  "transfer_data": null,
  "transfer_group": null
}
```

ACH Direct Debit is a *delayed notification payment method* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods). This means that it can take up to four business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.

The PaymentIntent you create initially has a status of `processing`. After the payment succeeds, the PaymentIntent status is updated from `processing` to `succeeded`. Learn about the events that are sent when the [PaymentIntent status is updated](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#web-confirm-paymentintent-succeeded).
Available in: US
Not sure about which Financial Connections integration to use? See our [overview of integration options](https://docs.stripe.com/financial-connections/use-cases.md).

Financial Connections lets your users securely share their financial data by linking their external financial accounts to your business. You can use Financial Connections to access user-permissioned financial data such as tokenized account and routing numbers, account balances, account owner information, and historical transactions.

Some common examples of how you can use Financial Connections to improve product experiences for your users include:

- Mitigate fraud when onboarding a customer or business by verifying the [ownership](https://docs.stripe.com/financial-connections/ownership.md) information of an account, such as the name and address of the bank account holder.
- Help your users track expenses, handle bills, manage their finances and take control of their financial well-being with [transactions](https://docs.stripe.com/financial-connections/transactions.md) data.
- Speed up underwriting and improve access to credit and other financial services with transactions and balances data.
- Enable your users to connect their accounts in fewer steps with Link, allowing them to save and reuse their bank account details across Stripe businesses.

## Set up Stripe [Server-side]

[Register](https://dashboard.stripe.com/financial-connections/application) for Financial Connections after we approve your account for live-mode access.

Use our official libraries for access to the Stripe 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 or retrieve an account holder [Server-side]

Create a [Customer object](https://docs.stripe.com/api/customers/object.md) when users create an account with your business. By providing an email address, Financial Connections can optimize the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow) by dynamically showing a streamlined user interface, for returning [Link](https://support.stripe.com/questions/link-for-financial-connections-support-for-businesses) users.

```curl
curl https://api.stripe.com/v1/customers \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d email={{CUSTOMER_EMAIL}} \
  -d name={{CUSTOMER_NAME}}
```

## Create a Financial Connections Session [Server-side]

Before you can retrieve data from a user’s bank account with Financial Connections, your user must authenticate their account with the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow).

Your user begins the authentication flow when they want to connect their account to your site or application. Insert a button or link on your site or in your application, which allows a user to link their account—for example, your button might say “Link your bank account”.

Create a Financial Connections Session by posting to `/v1/financial_connections/sessions`:

To create a hosted Session, you must specify the API Version and add the string `; financial_connections_hosted_beta=v1` to your request.

```curl
curl https://api.stripe.com/v1/financial_connections/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -H "Stripe-Version: 2026-04-22.preview; financial_connections_hosted_beta=v1" \
  -d "account_holder[type]=customer" \
  -d "account_holder[customer]={{CUSTOMER_ID}}" \
  -d ui_mode=hosted \
  --data-urlencode "hosted[return_url]=https://www.example.com" \
  -d "permissions[]=balances" \
  -d "permissions[]=ownership" \
  -d "permissions[]=payment_method" \
  -d "permissions[]=transactions"
```

1. Set `account_holder[customer]` to the Customer `id`.
1. Set the data `permissions` parameter to include the data required to fulfill your use case.
1. Set the `ui_mode` parameter to `hosted`.
1. Set the `hosted[return_url]` parameter to the URL to redirect your customer back to after they link their accounts or cancel this Session.
1. (Optional) set the `prefetch` parameter for retrieving the data on account creation.

The [permissions](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-permissions) parameter controls which account data you can access. You must request at least one permission. When completing the authentication flow, your user can see the data you’ve requested access to, and provide their consent to share it.

Consider the data required to fulfill your use case and request permission to access only the data you need. Requesting permissions that go well beyond your application’s scope might erode your users’ trust in how you use their data. For example, if you’re building a personal financial management application or a lending product, you might request `transactions` data. If you’re mitigating fraud such as account takeovers, you might want to request `ownership` details.

After your user authenticates their account, you can expand data permissions only by creating a new Financial Connections Session and specifying a new value for the `permissions` parameter. Your user must complete the authentication flow again, where they’ll see the additional data you’ve requested permission to access, and provide consent to share their data.

The optional [prefetch parameter](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-prefetch) controls which data you retrieve immediately after the user connects their account. Use this option if you know you always want a certain type of data. It removes the need to make an extra API call to initiate a [data refresh](https://docs.stripe.com/api/financial_connections/accounts/refresh.md).

To preserve the option to [accept ACH Direct Debit payments](https://docs.stripe.com/financial-connections/other-data-powered-products.md#accept-ach-direct-debit), request the `payment_method` permission.

## Redirect to Stripe Financial Connections [Client-side]

Use the returned [url](https://docs.stripe.com/api/financial_connections/sessions/object.md#financial_connections_session_object-url) to redirect your user to a Stripe-hosted page to connect their accounts.

After the completion of the authentication flow, we’ll redirect your customer to the URL specified in the `hosted[return_url]` parameter when creating the Session. To retrieve the accounts linked by the customer, call the [List Accounts](https://docs.stripe.com/api/financial_connections/accounts/list.md) endpoint using the [account_holder](https://docs.stripe.com/api/financial_connections/accounts/list.md#financial_connections_list_accounts-account_holder) filter.

Successful completion of the authentication flow also sends one `financial_connections.account.created` webhook per account connected.

## Retrieve data on a Financial Connections account [Server-side]

After your user has successfully completed the authentication flow, access or refresh the account data you’ve specified in the `permissions` parameter of the Financial Connections Session.

To protect the privacy of your user’s data, account data accessible to you is limited to the data you’ve specified in the `permissions` parameter.

Follow the guides for [balances](https://docs.stripe.com/financial-connections/balances.md), [ownership](https://docs.stripe.com/financial-connections/ownership.md) and [transactions](https://docs.stripe.com/financial-connections/transactions.md) to start retrieving account data.

## Optional: Accept an ACH Direct Debit payment from a Financial Connections account

You can optionally accept ACH Direct Debit payments on a previously collected Financial Connections account as long as the `supported_payment_method_types` attribute on the account includes `us_bank_account`.

Only US bank accounts are eligible to accept ACH Direct Debits such as a checking or savings account.

To accept an ACH Direct Debit payment on a previously collected account, you must have specified `payment_method` in the [data permissions](https://docs.stripe.com/financial-connections/fundamentals.md#data-permissions) parameter on the Financial Connections Session.

### Create a Payment Intent or Setup Intent

Use the *Payment Intents API* (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) to accept an ACH Direct payment for an account you want to charge now. Use the *Setup Intents API* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) to save details for future ACH Direct Debit payments. [Learn more about the difference between a](https://support.stripe.com/questions/payment-intents-api-vs-setup-intents-api) Payment Intent and Setup Intent.

This path shows you how to accept an ACH Direct Debit payment using the Payment Intents API.

Use the Financial Connections account ID and the customer ID to create a Payment Intent:

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method_types[]=us_bank_account" \
  -d "payment_method_data[us_bank_account][financial_connections_account]={{FINANCIALCONNECTIONSACCOUNT_ID}}" \
  -d "payment_method_data[type]=us_bank_account" \
  -d "payment_method_data[billing_details][name]=J. Customer" \
  -d amount=100 \
  -d currency=usd
```

This will return a Payment Intent similar to:

```json

{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_details": {
    "tip": {
      "amount": null
    }
  },
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [

    ],
    "has_more": false,
    "total_count": 0,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "confirmation_method": "automatic",
  "created": 1651010665,
  "currency": "usd",
  "customer": null,
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "requires_confirmation",
  "transfer_data": null,
  "transfer_group": null
}
```

### Collect mandate acknowledgement and submit the payment

Before you can initiate the payment, you must obtain authorization from your customer by displaying mandate terms for them to accept.

To be compliant with Nacha rules, you must obtain authorization from your customer before you can initiate payment by displaying mandate terms for them to accept. For more information on mandates, see [Mandates](https://docs.stripe.com/payments/ach-direct-debit.md#mandates).

### Confirm the Payment Intent

To confirm this Payment Intent, you need to provide either a [`mandate` parameter](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate) with an existing Mandate ID, or provide [mandate_data](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate_data) to create a new mandate.

For example, if you have a checkout page with a “Place Order” button, clicking that button could trigger a `POST` to an endpoint on your server. That endpoint could extract the request’s User Agent and the client’s IP address, and then make the following request to Stripe’s API:

```curl
curl https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}}/confirm \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "mandate_data[customer_acceptance][accepted_at]=1647448692" \
  -d "mandate_data[customer_acceptance][type]=online" \
  -d "mandate_data[customer_acceptance][online][ip_address]=71.183.194.54" \
  --data-urlencode "mandate_data[customer_acceptance][online][user_agent]=Mozilla/5.0 ..."
```

After successfully confirming the Payment Intent, it will look similar to:

```json
{
  "id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
  "object": "payment_intent",
  "amount": 100,
  "amount_capturable": 0,
  "amount_received": 0,
  "application": null,
  "application_fee_amount": null,
  "automatic_payment_methods": null,
  "canceled_at": null,
  "cancellation_reason": null,
  "capture_method": "automatic",
  "charges": {
    "object": "list",
    "data": [
      {
        "id": "py_17F8CPDyDglZKgWE3uzOAUe9",
        "object": "charge",
        "amount": 100,
        "amount_captured": 100,
        "amount_refunded": 0,
        "application": null,
        "application_fee": null,
        "application_fee_amount": null,
        "balance_transaction": null,
        "billing_details": {
          "address": {
            "city": null,
            "country": null,
            "line1": null,
            "line2": null,
            "postal_code": null,
            "state": null
          },
          "email": null,
          "name": "J. Customer",
          "phone": null
        },
        "calculated_statement_descriptor": null,
        "captured": true,
        "created": 1647448692,
        "currency": "usd",
        "customer": "cus_LKe65xcPnrCiTZ",
        "description": null,
        "destination": null,
        "dispute": null,
        "disputed": false,
        "failure_code": null,
        "failure_message": null,
        "fraud_details": {
        },
        "invoice": null,
        "livemode": false,
        "metadata": {
        },
        "on_behalf_of": null,
        "order": null,
        "outcome": null,
        "paid": false,
        "payment_intent": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
        "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
        "payment_method_details": {
          "type": "us_bank_account",
          "us_bank_account": {
            "account_holder_type": "individual",
            "account_type": "checking",
            "bank_name": "STRIPE TEST BANK",
            "fingerprint": "QnXqpAeqjjh8pPFa",
            "last4": "6789",
            "routing_number": "110000000"
          }
        },
        "receipt_email": null,
        "receipt_number": null,
        "receipt_url": null,
        "refunded": false,
        "refunds": {
          "object": "list",
          "data": [

          ],
          "has_more": false,
          "total_count": 0,
          "url": "/v1/charges/py_17F8CPDyDglZKgWE3uzOAUe9/refunds"
        },
        "review": null,
        "shipping": null,
        "source": null,
        "source_transfer": null,
        "statement_descriptor": null,
        "statement_descriptor_suffix": null,
        "status": "pending",
        "transfer_data": null,
        "transfer_group": null
      }
    ],
    "has_more": false,
    "total_count": 1,
    "url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
  },
  "client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP_secret_m38BKZvm3vSdpGdra360hPEov",
  "confirmation_method": "automatic",
  "created": 1647447792,
  "currency": "usd",
  "customer": "cus_LKe65xcPnrCiTZ",
  "description": null,
  "invoice": null,
  "last_payment_error": null,
  "livemode": false,
  "metadata": {
  },
  "next_action": null,
  "on_behalf_of": null,
  "payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
  "payment_method_options": {
    "us_bank_account": {
      "verification_method": "automatic"
    }
  },
  "payment_method_types": [
    "us_bank_account"
  ],
  "processing": null,
  "receipt_email": null,
  "review": null,
  "setup_future_usage": null,
  "shipping": null,
  "source": null,
  "statement_descriptor": null,
  "statement_descriptor_suffix": null,
  "status": "processing",
  "transfer_data": null,
  "transfer_group": null
}
```

ACH Direct Debit is a *delayed notification payment method* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods). This means that it can take up to four business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.

The PaymentIntent you create initially has a status of `processing`. After the payment succeeds, the PaymentIntent status is updated from `processing` to `succeeded`. Learn about the events that are sent when the [PaymentIntent status is updated](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#web-confirm-paymentintent-succeeded).
