# Get started with Connect embedded components Learn how to embed dashboard functionality into your website. Use Connect embedded components to add connected account dashboard functionality to your website. These libraries and their supporting API allow you to grant your users access to Stripe products directly in your dashboard and mobile applications. For an immersive version of this guide, see the [Connect embedded components integration quickstart](https://docs.stripe.com/connect/connect-embedded-components/quickstart.md). You can also download a sample integration from there. To customize the appearance of Connect embedded components, use the `appearance` options when you initialize `StripeConnectInstance`. See the [full list of appearance parameters](https://docs.stripe.com/connect/customize-connect-embedded-components.md). ## Initialize Connect.js [Client-side] [Server-side] Stripe uses an [AccountSession](https://docs.stripe.com/api/account_sessions.md) to express your intent to delegate API access to your connected account. The AccountSessions API returns a *client secret* (The client secret is a unique string returned from Stripe as part of an AccountSession. This string lets the client access a specific Stripe account with Connect embedded components) that allows an embedded component to access a connected account’s resources as if you were making the API calls for them. ### Create an AccountSession (Server) In a single page application, your client initiates a request to obtain the account session to your server. You can create a new endpoint on your server that returns the client secret to the browser: #### Ruby ```ruby require 'sinatra' require 'stripe' # This is a placeholder - it should be replaced with your secret API key. # Sign in to see your own test API key embedded in code samples. # Don’t submit any personally identifiable information in requests made with this key. Stripe.api_key = '<>' post '/account_session' do content_type 'application/json' # Create an AccountSession begin account_session = Stripe::AccountSession.create({ account: {{CONNECTED_ACCOUNT_ID}}, components: { payments: { enabled: true, features: { refund_management: true, dispute_management: true, capture_payments: true } } } }) { client_secret: account_session[:client_secret] }.to_json rescue => error puts "An error occurred when calling the Stripe API to create an account session: #{error.message}"; return [500, { error: error.message }.to_json] end end ``` #### Python ```python import stripe # This is a placeholder - it should be replaced with your secret API key. # Sign in to see your own test API key embedded in code samples. # Don’t submit any personally identifiable information in requests made with this key. stripe.api_key = '<>' from flask import Flask, jsonify app = Flask(__name__) @app.route('/account_session', methods=['POST']) def account_session_handler(): try: account_session = stripe.AccountSession.create( account={{CONNECTED_ACCOUNT_ID}}, components={ "payments": { "enabled": True, "features": { "refund_management": True, "dispute_management": True, "capture_payments": True } }, }, ) return jsonify({ 'client_secret': account_session.client_secret, }) except Exception as e: print('An error occurred when calling the Stripe API to create an account session: ', e) return jsonify(error=str(e)), 500 ``` #### PHP ```php '<>', ]); try { $account_session = $stripe->accountSessions->create([ 'account' => '{{CONNECTED_ACCOUNT_ID}}', 'components' => [ 'payments' => [ 'enabled' => true, 'features' => [ 'refund_management' => true, 'dispute_management' => true, 'capture_payments' => true, ], ], ], ]); echo json_encode(array( 'client_secret' => $account_session->client_secret )); } catch (Exception $e) { error_log("An error occurred when calling the Stripe API to create an account session: {$e->getMessage()}"); http_response_code(500); echo json_encode(['error' => $e->getMessage()]); } ?> ``` #### Java ```java import com.google.gson.Gson; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import static spark.Spark.post; import static spark.Spark.port; import static spark.Spark.staticFiles; import com.stripe.Stripe; import com.stripe.model.AccountSession; public class Server { private static Gson gson = new Gson(); static class ErrorResponse { private String error; public ErrorResponse(String error) { this.error = error; } } static class CreateAccountSessionResponse { private String client_secret; public CreateAccountSessionResponse(String clientSecret) { this.client_secret = clientSecret; } } public static void main(String[] args) { port(4242); // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. Stripe.apiKey = '<>'; post("/account_session", (request, response) -> { response.type("application/json"); try { Map params = new HashMap<>(); params.put("account", "{{CONNECTED_ACCOUNT_ID}}"); Map payments = new HashMap<>(); payments.put("enabled", true); Map features = new HashMap<>(); features.put("refund_management", true); features.put("dispute_management", true); features.put("capture_payments", true); payments.put("features", features); Map components = new HashMap<>(); components.put("payments", payments); params.put("components", components); AccountSession accountSession = AccountSession.create(params); CreateAccountSessionResponse accountSessionResponse = new CreateAccountSessionResponse(accountSession.getClientSecret()); return gson.toJson(accountSessionResponse); } catch(Exception e) { System.out.println("An error occurred when calling the Stripe API to create an account session: " + e.getMessage()); response.status(500); return gson.toJson(new ErrorResponse(e.getMessage())); } }); } } ``` #### Node.js ```javascript const stripe = require("stripe")( // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. '<>', ); const express = require('express'); const app = express(); app.post('/account_session', async (req, res) => { try { const accountSession = await stripe.accountSessions.create({ account: '{{CONNECTED_ACCOUNT_ID}}', components: { payments: { enabled: true, features: { refund_management: true, dispute_management: true, capture_payments: true, } }, } }); res.json({ client_secret: accountSession.client_secret, }); } catch (error) { console.error('An error occurred when calling the Stripe API to create an account session', error); res.status(500); res.send({error: error.message}); } }); app.listen(3000, () => { console.log('Running on port 3000'); }); ``` #### Go ```go package main import ( "bytes" "encoding/json" "io" "log" "net/http" "github.com/stripe/stripe-go/v75" "github.com/stripe/stripe-go/v75/accountsession" ) func main() { // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. stripe.Key = '<>' http.Handle("/", http.FileServer(http.Dir("public"))) http.HandleFunc("/account_session", CreateAccountSession) addr := "localhost:4242" log.Printf("Listening on %s", addr) log.Fatal(http.ListenAndServe(addr, nil)) } func CreateAccountSession(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } accountSession, err := accountsession.New( &stripe.AccountSessionParams{ Account: stripe.String("{{CONNECTED_ACCOUNT_ID}}"), Components: &stripe.AccountSessionComponentsParams{ Payments: &stripe.AccountSessionComponentsPaymentsParams{ Enabled: stripe.Bool(true), Features: &stripe.AccountSessionComponentsPaymentsFeaturesParams{ RefundManagement: stripe.Bool(true), DisputeManagement: stripe.Bool(true), CapturePayments: stripe.Bool(true), }, }, }, }, ) if err != nil { log.Printf("An error occurred when calling the Stripe API to create an account session: %v", err) w.WriteHeader(http.StatusInternalServerError) if stripeErr, ok := err.(*stripe.Error); ok { writeJSON(w, struct { Error string `json:"error"` }{ Error: stripeErr.Msg, }) } else { writeJSON(w, struct { Error string `json:"error"` }{ Error: err.Error(), }) } return } writeJSON(w, struct { ClientSecret string `json:"client_secret"` }{ ClientSecret: accountSession.ClientSecret, }) } func writeJSON(w http.ResponseWriter, v interface{}) { var buf bytes.Buffer if err := json.NewEncoder(&buf).Encode(v); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) log.Printf("json.NewEncoder.Encode: %v", err) return } w.Header().Set("Content-Type", "application/json") if _, err := io.Copy(w, &buf); err != nil { log.Printf("io.Copy: %v", err) return } } ``` #### .NET ```csharp using System; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Stripe; namespace server.Controllers { public class Program { public static void Main(string[] args) { WebHost.CreateDefaultBuilder(args) .UseUrls("http://0.0.0.0:4242") .UseWebRoot("public") .UseStartup() .Build() .Run(); } } public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddNewtonsoftJson(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. StripeConfiguration.ApiKey = "<>"; if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseRouting(); app.UseStaticFiles(); app.UseEndpoints(endpoints => endpoints.MapControllers()); } } [Route("account_session")] [ApiController] public class AccountSessionApiController : Controller { [HttpPost] public ActionResult Post() { try { var service = new AccountSessionService(); AccountSession accountSession = service.Create( new AccountSessionCreateOptions { Account = {{CONNECTED_ACCOUNT_ID}}, Components = new AccountSessionComponentsOptions { Payments = new AccountSessionComponentsPaymentsOptions { Enabled = true, Features = new AccountSessionComponentsPaymentsFeaturesOptions { RefundManagement = true, DisputeManagement = true, CapturePayments = true } } } } ); return Json(new { client_secret = accountSession.ClientSecret }); } catch(Exception ex) { Console.Write("An error occurred when calling the Stripe API to create an account session: " + ex.Message); Response.StatusCode = 500; return Json(new { error = ex.Message }); } } } } ``` ### Create Account Session API The [Create Account Session API](https://docs.stripe.com/api/account_sessions/create.md) determines component and feature access for Connect embedded components. Stripe enforces these parameters for any components that correspond to the account session. If your site supports multiple user roles, make sure components and features that are enabled for that account session correspond to the current user’s role. For example, you can enable [refund management](https://docs.stripe.com/api/account_sessions/create.md#create_account_session-components-payments-features-refund_management) only for administrators of your site, but not for other users. To make sure user role access are enforced, you must map your site’s user role to account session components. ### Set up Connect.js (Client) We recommend setting up Connect.js with npm as shown in the following example, but it’s also possible [without npm](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#without-npm). #### HTML + JS Install the [npm package](https://github.com/stripe/connect-js) to use Connect.js as a module. ```bash npm install --save @stripe/connect-js ``` #### React Install Connect.js and the React Connect.js libraries from the [npm public registry](https://www.npmjs.com/package/@stripe/react-connect-js). ```bash npm install --save @stripe/connect-js @stripe/react-connect-js ``` ### Load and initialize Connect.js (Client) Call `loadConnectAndInitialize` with your publishable key and a function that retrieves a client secret by calling the new endpoint you created on your server. Use the returned `StripeConnectInstance` to create embedded components. After initializing Connect.js, you can mount components to or unmount components from the DOM at any time. That includes any elements rendered inside React or Vue portals. #### HTML + JS To create a component, call `create` on the `StripeConnectInstance` that you created above, then pass in the component name. This returns a [custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) that Connect.js registers and uses to automatically wire your DOM up to Stripe. You can then `append` this element to your DOM. Call `create` with `payments`, then add the result to your DOM to render a payments UI. #### React To use Connect embedded components with React wrappers, wrap your application in a `ConnectComponentsProvider` and pass in the `StripeConnectInstance` that you previously created. Use `ConnectPayments` on your page to render a payments UI. #### HTML + JS ```html

Payments

``` ```javascript import {loadConnectAndInitialize} from '@stripe/connect-js'; const fetchClientSecret = async () => { // Fetch the AccountSession client secret const response = await fetch('/account_session', { method: "POST" }); if (!response.ok) { // Handle errors on the client side here const {error} = await response.json(); console.error('An error occurred: ', error); document.querySelector('#error').removeAttribute('hidden'); return undefined; } else { const {client_secret: clientSecret} = await response.json(); document.querySelector('#error').setAttribute('hidden', ''); return clientSecret; } } const stripeConnectInstance = loadConnectAndInitialize({ // This is a placeholder - it should be replaced with your publishable API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. publishableKey: "<>", fetchClientSecret: fetchClientSecret, }); const paymentComponent = stripeConnectInstance.create("payments"); const container = document.getElementById("container"); container.appendChild(paymentComponent); ``` #### React ```html
``` ```jsx import App from './App'; ReactDOM.render(, document.getElementById('root')); ``` ```jsx import {loadConnectAndInitialize} from '@stripe/connect-js'; import { ConnectPayments, ConnectComponentsProvider, } from "@stripe/react-connect-js"; export default function App() { const [errorMessage, setErrorMessage] = useState(''); // We use `useState` to ensure the Connect instance is only initialized once const [stripeConnectInstance] = useState(() => { const fetchClientSecret = async () => { // Fetch the AccountSession client secret const response = await fetch('/account_session', { method: "POST" }); if (!response.ok) { // Handle errors on the client side here const {error} = await response.json(); console.error('An error occurred: ', error); setErrorMessage(error) return undefined; } else { const {client_secret: clientSecret} = await response.json(); return clientSecret; } } return loadConnectAndInitialize({ // This is a placeholder - it should be replaced with your publishable API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. publishableKey: "<>", fetchClientSecret: fetchClientSecret, }) }); return ( <> {errorMessage ? (
{`Error: ${errorMessage}`}
) : (
)} ); } ``` [See a complete list of supported embedded components →](https://docs.stripe.com/connect/supported-embedded-components.md) ## Configure Connect.js [Client-side] The `loadConnectAndInitialize` method on the client takes several different options to configure Connect.js. | Option | Description | | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | | `publishableKey` | The [publishable key](https://docs.stripe.com/keys.md) for your integration. | required | | `fetchClientSecret` | The function that retrieves the *client secret* (The client secret is a unique string returned from Stripe as part of an AccountSession. This string lets the client access a specific Stripe account with Connect embedded components) returned by `/v1/account_sessions`. This tells `StripeConnectInstance` which account to delegate access to. This function is also used to retrieve a client secret function to refresh the session when it expires. `fetchClientSecret` should always create a new account session and return a fresh `client_secret`. | required | | `appearance` | An object to customize the look of Connect embedded components. | optional | | `locale` | A parameter to specify the [locale](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#localization) that Connect embedded components use. The locale defaults to the browser language. If the specified locale isn’t directly supported, we use a reasonable alternative (for example `fr-be` might fall back to `fr-fr`). See [localization](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#localization) for the list of supported locales. | optional | | `fonts` | An array of custom fonts available for use by any embedded components created from a `StripeConnectInstance`. You can specify fonts as [CssFontSource](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#css-font-source) or [CustomFontSource](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#custom-font-source) objects. | optional | ### Customize the look of Connect embedded components The [embedded components Figma UI toolkit](https://www.figma.com/community/file/1438614134095442934) contains every component, common patterns, and an example application. You can use it to visualize and design embedded UIs on your website. We offer a [set of options](https://docs.stripe.com/connect/embedded-appearance-options.md) to customize the look and feel of Connect embedded components. These customizations affect buttons, icons, and other accents in our design system. > #### Necessary popups > > Some behavior in embedded components, such as [user authentication](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#user-authentication-in-connect-embedded-components), must be presented in a popup. You can’t customize the embedded component to eliminate such popups. You can set these options when initializing `StripeConnectInstance` by passing an Appearance to the `appearance` object. You can only use the [Connect.js options](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#configuring-connect) to modify styles in Connect embedded components. The font family and background color of Connect embedded components are inherited from the parent HTML container. You must explicitly set all other options. ```javascript const stripeConnectInstance = loadConnectAndInitialize({ publishableKey: "<>", fetchClientSecret: fetchClientSecret, fonts: [ { cssSrc: "https://myfonts.example.com/mycssfile.css", }, { src: `url(https://my-domain.com/assets/my-font-2.woff)`, family: 'My Font' } ], appearance: { // See all possible variables below overlays: "dialog", variables: { fontFamily: 'My Font', colorPrimary: "#FF0000", }, }, }); ``` See the [full list of appearance variables](https://docs.stripe.com/connect/embedded-appearance-options.md). ### The fonts object The `fonts` object in `stripeConnect.initialize` takes an array of [CssFontSource](https://docs.stripe.com/js/appendix/css_font_source_object) or [CustomFontSource](https://docs.stripe.com/js/appendix/custom_font_source_object) objects. If you use custom fonts on your page (for example, `.woff` or `.tff` files), you must specify the font files when initializing Connect embedded components. Doing so allows Connect embedded components to properly render the fonts. You can specify the files as: #### CssFontSource Use this object to pass a stylesheet URL that defines your custom fonts when creating a `StripeConnectInstance`. With a `CssFontSource` object, your [CSP configuration](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#csp-and-http-header-requirements) must allow fetching the domains associated with the CSS file URLs specified as CssFontSource. | Name | Type | Example value | | -------- | ----------------- | --------------------------------------------------- | | `cssSrc` | string `required` | `https://fonts.googleapis.com/css?family=Open+Sans` | A relative or absolute URL pointing to a CSS file with [@font-face](https://developer.mozilla.org/en/docs/Web/CSS/@font-face) definitions. The file must be hosted on https. If you use a [content security policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy), the file might require [additional directives](https://docs.stripe.com/security/guide.md#content-security-policy). | #### CustomFontSource Use this object to pass custom fonts when creating a `StripeConnectInstance`. | Name | Type | Example value | | -------------- | ----------------- | ----------------------------------------------- | | `family` | string `required` | `Avenir` | The name to give the font. | | `src` | string `required` | `url(https://my-domain.com/assets/avenir.woff)` | A valid [src](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/src) value pointing to your custom font file. This is usually (though not always) a link to a file with a `.woff` , `.otf`, or `.svg` suffix. The file must be hosted on https. | | `display` | string `optional` | `auto` | A valid [font-display](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display) value. | | `style` | string `optional` | `normal` | One of `normal`, `italic`, or `oblique`. | | `unicodeRange` | string `optional` | `U+0-7F` | A valid [unicode-range](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range) value. | | `weight` | string `optional` | `400` | A valid [font-weight](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). This is a string, not a number. | ### Update Connect embedded components after initialization The `update` method supports updating Connect embedded components after initialization. You can use it to switch appearance options at runtime (without refreshing the page). To do so, use the same `stripeConnectInstance` object you created with `initialize` and call the `update` method on it: ```javascript stripeConnectInstance.update({ appearance: { variables: { colorPrimary: "#FF0000", }, }, locale: 'en-US', }); ``` > Not all options (for example, `fonts`) are updatable. The supported options for this method are a subset of the options offered in `initialize`. This supports updating the `appearance` and `locale`. ### Width and height Connect embedded components behave like regular `block` HTML elements. By default, they take 100% of the `width` of their parent HTML element, and grow in height according to the content rendered inside. You can control the `width` of Connect embedded components by specifying the `width` of the HTML parent. You can’t directly control the `height` as that depends on the rendered content, however, you can limit the height with `maxHeight` and `overflow: scroll`, the same way you can with other HTML `block` elements. ## Authentication We offer a set of APIs to manage account sessions and user credentials in Connect embedded components. ### Refresh the client secret On long running sessions, the session from the initially provided *client secret* (The client secret is a unique string returned from Stripe as part of an AccountSession. This string lets the client access a specific Stripe account with Connect embedded components) might expire. When it expires, we automatically use `fetchClientSecret` to retrieve a new client secret and refresh the session. You don’t need to pass in any additional parameters. ```javascript import { loadConnectAndInitialize } from "@stripe/connect-js"; // Example method to retrieve the client secret from your server const fetchClientSecret = async () => { const response = await fetch('/account_session', { method: "POST" }); const {client_secret: clientSecret} = await response.json(); return clientSecret; } const stripeConnectInstance = loadConnectAndInitialize({ publishableKey: "{{PUBLISHABLE_KEY}}", fetchClientSecret: fetchClientSecret, }); ``` ### Log out We recommend that you call `logout` on the `stripeConnectInstance` to destroy the associated account session object after a user logs out of your app. This disables all Connect embedded components that link to that `stripeConnectInstance`. > #### Necessary popups > > Only call `logout` when your user logs out of your app. Don’t call logout when a component unmounts (when navigating to another page or closing the page) or when loading other components as this method entirely invalidates the current account session and [stripe user session](https://docs.stripe.com/connect/get-started-connect-embedded-components.md?platform=web#user-authentication-in-connect-embedded-components). After calling `logout`, components no longer render for the associated `stripeConnectInstance`. ```javascript // Call this when your user logs out stripeConnectInstance.logout(); ``` ## CSP and HTTP header requirements If your website implements a *Content Security Policy* (Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks), you need to update the policy by adding the following rules: - `frame-src` `https://connect-js.stripe.com` `https://js.stripe.com` - `img-src` `https://*.stripe.com` - `script-src` `https://connect-js.stripe.com` `https://js.stripe.com` - `style-src` `sha256-0hAheEzaMe6uXIKV4EehS9pu1am1lj/KnnzrOYqckXk=` (SHA of empty style element) If you’re using a CSS file to load [web fonts](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#fonts-object) for use with Connect embedded components, its URL must be allowed by your [connect-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src) CSP directive. Setting certain [HTTP response headers](https://developer.mozilla.org/en-US/docs/Glossary/Response_header) enables the full functionality of Connect embedded components: - [Cross-Origin-Opener-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy), `unsafe-none`. This (`unsafe-none`) is the default value of the header, so not setting this header works. Other values such as `same-origin` break [user authentication](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#user-authentication-in-connect-embedded-components) in Connect embedded components. ## Supported browsers We support the same set of browsers that the [Stripe Dashboard currently supports](https://docs.stripe.com/dashboard/basics.md): - The last 20 major versions of Chrome and Firefox - The last two major versions of Safari and Edge - The last two major versions of mobile Safari on iOS You can’t use Connect embedded components in embedded web views inside mobile or desktop applications. To use Connect embedded components in a mobile application, use the [iOS](https://docs.stripe.com/connect/get-started-connect-embedded-components.md?platform=ios), [Android](https://docs.stripe.com/connect/get-started-connect-embedded-components.md?platform=android), or [React Native](https://docs.stripe.com/connect/get-started-connect-embedded-components.md?platform=react-native) SDK. If an embedded component isn’t supported yet by our mobile SDKs, we recommend linking to a web browser where you can render the embedded components. ## Localization When initializing [Connect.js](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#account-sessions), you can pass a `locale` parameter. To match an embedded component’s locale to your website’s locale, pass the `locale` parameter with the locale of the UI your website renders. The default value of the `locale` parameter is determined by the browser configured locale. If the specified locale isn’t directly supported, a reasonable alternative is used (for example `fr-be` might fall back to `fr-fr`). Connect embedded components support the following locales: | Language | Locale code | | --------------------------------- | ------------ | | Bulgarian (Bulgaria) | `bg-BG` | | Chinese (Simplified) | `zh-Hans` | | Chinese (Traditional - Hong Kong) | `zh-Hant-HK` | | Chinese (Traditional - Taiwan) | `zh-Hant-TW` | | Croatian (Croatia) | `hr-HR` | | Czech (Czechia) | `cs-CZ` | | Danish (Denmark) | `da-DK` | | Dutch (Netherlands) | `nl-NL` | | English (Australia) | `en-AU` | | English (India) | `en-IN` | | English (Ireland) | `en-IE` | | English (New Zealand) | `en-NZ` | | English (Singapore) | `en-SG` | | English (United Kingdom) | `en-GB` | | English (United States) | `en-US` | | Estonian (Estonia) | `et-EE` | | Filipino (Philippines) | `fil-PH` | | Finnish (Finland) | `fi-FI` | | French (Canada) | `fr-CA` | | French (France) | `fr-FR` | | German (Germany) | `de-DE` | | Greek (Greece) | `el-GR` | | Hungarian (Hungary) | `hu-HU` | | Indonesian (Indonesia) | `id-ID` | | Italian (Italy) | `it-IT` | | Japanese (Japan) | `ja-JP` | | Korean (South Korea) | `ko-KR` | | Latvian (Latvia) | `lv-LV` | | Lithuanian (Lithuania) | `lt-LT` | | Malay (Malaysia) | `ms-MY` | | Maltese (Malta) | `mt-MT` | | Norwegian Bokmål (Norway) | `nb-NO` | | Polish (Poland) | `pl-PL` | | Portuguese (Brazil) | `pt-BR` | | Portuguese (Portugal) | `pt-PT` | | Romanian (Romania) | `ro-RO` | | Slovak (Slovakia) | `sk-SK` | | Slovenian (Slovenia) | `sl-SI` | | Spanish (Argentina) | `es-AR` | | Spanish (Brazil) | `es-BR` | | Spanish (Latin America) | `es-419` | | Spanish (Mexico) | `es-MX` | | Spanish (Spain) | `es-ES` | | Swedish (Sweden) | `sv-SE` | | Thai (Thailand) | `th-TH` | | Turkish (Türkiye) | `tr-TR` | | Vietnamese (Vietnam) | `vi-VN` | ## Handle load errors If a component fails to load, you can react to the failure by providing a load error handler to any embedded component. Depending on the cause of failure, the load error handler can be called multiple times. Any logic triggered by a load error handler must be idempotent. #### HTML + JS ```js // Load errors are emitted by all components. We use Balances as an example here. const balances = stripeConnectInstance.create('balances'); balances.setOnLoadError((loadError) => { const componentName = loadError.elementTagName const error = loadError.error console.log(componentName + " failed to load") console.log(`${error.type}: ${error.message}`); }); container.appendChild(balances); ``` #### React ```jsx // Load errors are emitted by all components. We use Balances as an example here. import { ConnectBalances, ConnectComponentsProvider, } from '@stripe/react-connect-js'; const BalancesUI = () => { const onLoadError = (loadError) => { const componentName = loadError.elementTagName const error = loadError.error console.log(componentName + " failed to load") console.log(`${error.type}: ${error.message}`); }; return ( ); }; ``` #### HTML + JS | Method | Description | Variables | | ---------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `setOnLoadError` | The component executes this callback function when a load failure occurs. | - `loadError.error`: See [the load error object](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#the-load-object) - `loadError.elementTagName`: The name of HTML tag used to render the component in the browser | #### React | React prop | Description | Variables | | ------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `onLoadError` | The component executes this callback function when a load failure occurs. | - `loadError.error`: See [the load error object](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#the-load-object) - `loadError.elementTagName`: The name of HTML tag used to render the component in the browser | #### The load `error` object Every time there’s a load failure, an `error` object is passed to the load error handler with the following properties. | Name | Type | Example value | | --------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------- | | `type` | See [types of load failures](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#the-load-object) | `authentication_error` | The type of error | | `message` | string | undefined | `Failed to fetch account session` | Further description about the error | #### Types of load failures When a component fails to load, we detect the type of failure and map it to one of the types below. If the load error type can’t be determined it is marked as an `api_error`. | Type | Description | | ------------------------------ | ------------------------------------------------------------------------------------------------ | | `api_connection_error` | Failure to connect to Stripe’s API | | `authentication_error` | Failure to perform the authentication flow within Connect embedded components | | `account_session_create_error` | Account session creation failed | | `invalid_request_error` | Request failed with an 4xx status code, typically caused by platform configuration issues | | `rate_limit_error` | Request failed because an abnormal request rate was detected | | `render_error` | Failure to render the component, typically caused by browser extensions or network issues | | `api_error` | API errors covering any other type of problem, such as a temporary problem with Stripe’s servers | #### UI for load errors In most cases, embedded components render an error message when they fail to load, so you don’t need to. You can use a load error handler for analytics or for other elements of your site that a load error might affect. However, embedded components don’t render any message for errors that occur prior to invoking the [onLoaderStart](https://docs.stripe.com/connect/get-started-connect-embedded-components.md?platform=web#reacting-to-component-display) callback, because that means they haven’t rendered any UI at all. In that case, your code should render the error UI. ## Detect the display of embedded components After a component is created, no UI is displayed to users until the javascript for the component is loaded and parsed in the browser. This can cause components to appear to pop-in after they complete loading. To avoid this, display your own loading UI before the component is created and hide the UI after the component is displayed. All embedded components can accept a callback function that is called immediately when any UI (including loading indicators) is displayed to the user. #### HTML + JS ```html
``` ```js // Loader start events are emitted by all components. We use Balances as an example here. const container = document.getElementById('balances-container'); const balances = stripeConnectInstance.create('balances'); balances.setOnLoaderStart((event) => { container.getElementById("spinner").display = "none"; console.log(`${event.elementTagName} is visible to users`) }); container.appendChild(balances); ``` #### React ```jsx // Loader start events are emitted by all components. We use Balances as an example here. import { ConnectBalances, ConnectComponentsProvider, } from '@stripe/react-connect-js'; const BalancesUI = () => { const [loading, setLoading] = useState(true); const onLoaderStart = (event) => { setLoading(false); console.log(`${event.elementTagName} is visible to users`) }; return ( {loading && } ); }; ``` #### HTML + JS | Method | Description | Variables | | ------------------ | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | | `setOnLoaderStart` | The component executes this callback function when any UI (including loading indicators) is displayed to the user. | - `event.elementTagName`: The name of HTML tag used to render the component in the browser | #### React | React prop | Description | Variables | | --------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | | `onLoaderStart` | The component executes this callback function when any UI (including loading indicators) is displayed to the user. | - `event.elementTagName`: The name of HTML tag used to render the component in the browser | ## Use Connect.js without npm We recommend integrating with our [javascript](https://github.com/stripe/connect-js) and [React component wrappers](https://github.com/stripe/react-connect-js), which simplify the loading of Connect embedded components and provide TypeScript definitions for our supported interfaces. If your build system currently doesn’t support taking a dependency on packages, you can integrate without these packages. Manually add the Connect.js script tag to the `` of each page on your site. ```html ``` After Connect.js completes loading, it initializes the global window variable `StripeConnect` and calls `StripeConnect.onLoad`, if defined. You can safely initialize Connect.js by setting up an `onload` function and calling `StripeConnect.init` with the same [Connect.js options](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#configuring-connect) as `loadConnectAndInitialize`. You can use the Connect instance returned by `init` in the same way you use the instance returned by `loadConnectAndInitialize` to create embedded components in an [HTML + JS integration](https://docs.stripe.com/connect/get-started-connect-embedded-components.md?client=js&platform=web#load-and-initialize-connect.js). ```javascript window.StripeConnect = window.StripeConnect || {}; StripeConnect.onLoad = () => { const stripeConnectInstance = StripeConnect.init({ // This is a placeholder - it should be replaced with your publishable API key. // Sign in to see your own test API key embedded in code samples. // Don't submit any personally identifiable information in requests made with this key. publishableKey: "<>", fetchClientSecret: fetchClientSecret, }); const payments = stripeConnectInstance.create('payments'); document.body.appendChild(payments); }; ``` ## User authentication in Connect embedded components Connect embedded components typically don’t require user authentication. In some scenarios, Connect embedded components require the connected account to sign in with their Stripe account before accessing the component to provide the necessary functionality (for example, writing information to the account legal entity in the case of the [account onboarding](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) component). Other components might require authentication within the component after they initially render. Authentication is required for connected accounts where Stripe is responsible for collecting updated information when requirements change. For connected accounts where you’re responsible for collecting updated information when requirements are due or change, such as Custom accounts, Stripe authentication is controlled by the [disable_stripe_user_authentication](https://docs.stripe.com/api/account_sessions/create.md#create_account_session-components-account_onboarding-features-disable_stripe_user_authentication) Account Session feature. We recommend implementing 2FA or equivalent security measures as a [best practice](https://docs.stripe.com/connect/risk-management/best-practices.md#prevent-account-take-overs). For account configurations that support this feature, like Custom, you assume liability for connected accounts if they can’t pay back [negative balances](https://docs.stripe.com/connect/risk-management/best-practices.md#decide-your-approach-to-negative-balance-liability). ### Components requiring authentication Authentication includes a popup to a Stripe-owned window. The connected account must authenticate before they can continue their workflow. The following components require connected accounts to authenticate in certain scenarios: - [Account Onboarding](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) - [Account Management](https://docs.stripe.com/connect/supported-embedded-components/account-management.md) - [Balances](https://docs.stripe.com/connect/supported-embedded-components/balances.md) - [Payouts](https://docs.stripe.com/connect/supported-embedded-components/payouts.md) - [Notification Banner](https://docs.stripe.com/connect/supported-embedded-components/notification-banner.md) - [Financial Account](https://docs.stripe.com/connect/supported-embedded-components/financial-account.md) - [Issuing Cards List](https://docs.stripe.com/connect/supported-embedded-components/issuing-cards-list.md) ## Performance best practices To make sure the load time of Connect embedded components is as low as possible, follow these recommendations: - **Call `loadConnectAndInitialize` as early as possible in your flow**. - **Create a single connect instance**: Create a single connect instance by only calling `loadConnectAndInitialize` once per session. Then reuse that instance to create and manage multiple components. A common mistake is to create one connect instance per component, or multiple connect instances per session. This causes additional resource consumption and API requests. If you’re using React, you can use a state management library or a React context to manage this instance. - **Use the latest version of the appropriate SDKs**: Use the latest version of the [connect-js](https://www.npmjs.com/package/@stripe/connect-js) or [react-connect-js](https://www.npmjs.com/package/@stripe/react-connect-js) npm package SDKs. These SDKs initialize embedded components in a way that maximizes performance. Performance improvements have been added to the SDKs, so we recommend upgrading if you’re using an old version. - **Load the `connect.js` script as soon as possible in your flow**: The earliest possible place to load the script is by including this script in your HTML `head`. You can also use the default behavior of our npm package SDKs, which load it when your page JavaScript first loads. ## Set up StripeConnect [Client-side] [Server-side] Stripe uses an [AccountSession](https://docs.stripe.com/api/account_sessions.md) to express your intent to delegate API access to your connected account. The AccountSessions API returns a *client secret* (The client secret is a unique string returned from Stripe as part of an AccountSession. This string lets the client access a specific Stripe account with Connect embedded components) that allows an embedded component to access a connected account’s resources as if you were making the API calls for them. ### Create an AccountSession (Server) Your app must initiate a request to your server to obtain the account session. You can create a new endpoint on your server that returns the client secret to the app: #### Ruby ```ruby require 'sinatra' require 'stripe' # This is a placeholder - it should be replaced with your secret API key. # Sign in to see your own test API key embedded in code samples. # Don’t submit any personally identifiable information in requests made with this key. Stripe.api_key = '<>' post '/account_session' do content_type 'application/json' # Create an AccountSession begin account_session = Stripe::AccountSession.create({ account: {{CONNECTED_ACCOUNT_ID}}, components: { account_onboarding: { enabled: true, features: { # We recommend disabling authentication for a better user experience when possible disable_stripe_user_authentication: true, } } } }) { client_secret: account_session[:client_secret] }.to_json rescue => error puts "An error occurred when calling the Stripe API to create an account session: #{error.message}"; return [500, { error: error.message }.to_json] end end ``` #### Python ```python import stripe # This is a placeholder - it should be replaced with your secret API key. # Sign in to see your own test API key embedded in code samples. # Don’t submit any personally identifiable information in requests made with this key. stripe.api_key = '<>' from flask import Flask, jsonify app = Flask(__name__) @app.route('/account_session', methods=['POST']) def account_session_handler(): try: account_session = stripe.AccountSession.create( account={{CONNECTED_ACCOUNT_ID}}, components={ "account_onboarding": { "enabled": True, "features": { # We recommend disabling authentication for a better user experience when possible "disable_stripe_user_authentication": True } }, }, ) return jsonify({ 'client_secret': account_session.client_secret, }) except Exception as e: print('An error occurred when calling the Stripe API to create an account session: ', e) return jsonify(error=str(e)), 500 ``` #### PHP ```php '<>', ]); try { $account_session = $stripe->accountSessions->create([ 'account' => '{{CONNECTED_ACCOUNT_ID}}', 'components' => [ 'account_onboarding' => [ 'enabled' => true, 'features' => [ // We recommend disabling authentication for a better user experience when possible 'disable_stripe_user_authentication' => true, ], ], ]); echo json_encode(array( 'client_secret' => $account_session->client_secret )); } catch (Exception $e) { error_log("An error occurred when calling the Stripe API to create an account session: {$e->getMessage()}"); http_response_code(500); echo json_encode(['error' => $e->getMessage()]); } ?> ``` #### Java ```java import com.google.gson.Gson; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import static spark.Spark.post; import static spark.Spark.port; import static spark.Spark.staticFiles; import com.stripe.Stripe; import com.stripe.model.AccountSession; public class Server { private static Gson gson = new Gson(); static class ErrorResponse { private String error; public ErrorResponse(String error) { this.error = error; } } static class CreateAccountSessionResponse { private String client_secret; public CreateAccountSessionResponse(String clientSecret) { this.client_secret = clientSecret; } } public static void main(String[] args) { port(4242); // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. Stripe.apiKey = '<>'; post("/account_session", (request, response) -> { response.type("application/json"); try { Map params = new HashMap<>(); params.put("account", "{{CONNECTED_ACCOUNT_ID}}"); Map onboarding = new HashMap<>(); onboarding.put("enabled", true); Map features = new HashMap<>(); // We recommend disabling authentication for a better user experience when possible features.put("disable_stripe_user_authentication", true); onboarding.put("features", features); Map components = new HashMap<>(); components.put("account_onboarding", onboarding); params.put("components", components); AccountSession accountSession = AccountSession.create(params); CreateAccountSessionResponse accountSessionResponse = new CreateAccountSessionResponse(accountSession.getClientSecret()); return gson.toJson(accountSessionResponse); } catch(Exception e) { System.out.println("An error occurred when calling the Stripe API to create an account session: " + e.getMessage()); response.status(500); return gson.toJson(new ErrorResponse(e.getMessage())); } }); } } ``` #### Node.js ```javascript const stripe = require("stripe")( // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. '<>', ); const express = require('express'); const app = express(); app.post('/account_session', async (req, res) => { try { const accountSession = await stripe.accountSessions.create({ account: '{{CONNECTED_ACCOUNT_ID}}', components: { account_onboarding: { enabled: true, features: { // We recommend disabling authentication for a better user experience when possible disable_stripe_user_authentication: true, } }, } }); res.json({ client_secret: accountSession.client_secret, }); } catch (error) { console.error('An error occurred when calling the Stripe API to create an account session', error); res.status(500); res.send({error: error.message}); } }); app.listen(3000, () => { console.log('Running on port 3000'); }); ``` #### Go ```go package main import ( "bytes" "encoding/json" "io" "log" "net/http" "github.com/stripe/stripe-go/v75" "github.com/stripe/stripe-go/v75/accountsession" ) func main() { // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. stripe.Key = '<>' http.Handle("/", http.FileServer(http.Dir("public"))) http.HandleFunc("/account_session", CreateAccountSession) addr := "localhost:4242" log.Printf("Listening on %s", addr) log.Fatal(http.ListenAndServe(addr, nil)) } func CreateAccountSession(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } accountSession, err := accountsession.New( &stripe.AccountSessionParams{ Account: stripe.String("{{CONNECTED_ACCOUNT_ID}}"), Components: &stripe.AccountSessionComponentsParams{ AccountOnboarding: &stripe.AccountSessionComponentsAccountOnboardingParams{ Enabled: stripe.Bool(true), Features: &stripe.AccountSessionComponentsAccountOnboardingFeaturesParams{ // We recommend disabling authentication for a better user experience when possible DisableStripeUserAuthentication: stripe.Bool(true), }, }, }, ) if err != nil { log.Printf("An error occurred when calling the Stripe API to create an account session: %v", err) w.WriteHeader(http.StatusInternalServerError) if stripeErr, ok := err.(*stripe.Error); ok { writeJSON(w, struct { Error string `json:"error"` }{ Error: stripeErr.Msg, }) } else { writeJSON(w, struct { Error string `json:"error"` }{ Error: err.Error(), }) } return } writeJSON(w, struct { ClientSecret string `json:"client_secret"` }{ ClientSecret: accountSession.ClientSecret, }) } func writeJSON(w http.ResponseWriter, v interface{}) { var buf bytes.Buffer if err := json.NewEncoder(&buf).Encode(v); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) log.Printf("json.NewEncoder.Encode: %v", err) return } w.Header().Set("Content-Type", "application/json") if _, err := io.Copy(w, &buf); err != nil { log.Printf("io.Copy: %v", err) return } } ``` #### .NET ```csharp using System; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Stripe; namespace server.Controllers { public class Program { public static void Main(string[] args) { WebHost.CreateDefaultBuilder(args) .UseUrls("http://0.0.0.0:4242") .UseWebRoot("public") .UseStartup() .Build() .Run(); } } public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddNewtonsoftJson(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. StripeConfiguration.ApiKey = "<>"; if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseRouting(); app.UseStaticFiles(); app.UseEndpoints(endpoints => endpoints.MapControllers()); } } [Route("account_session")] [ApiController] public class AccountSessionApiController : Controller { [HttpPost] public ActionResult Post() { try { var service = new AccountSessionService(); AccountSession accountSession = service.Create( new AccountSessionCreateOptions { Account = {{CONNECTED_ACCOUNT_ID}}, Components = new AccountSessionComponentsOptions { AccountOnboarding = new AccountSessionComponentsAccountOnboardingOptions { Enabled = true, Features = new AccountSessionComponentsAccountOnboardingFeaturesOptions { // We recommend disabling authentication for a better user experience when possible DisableStripeUserAuthentication = true } }, } } ); return Json(new { client_secret = accountSession.ClientSecret }); } catch(Exception ex) { Console.Write("An error occurred when calling the Stripe API to create an account session: " + ex.Message); Response.StatusCode = 500; return Json(new { error = ex.Message }); } } } } ``` ### Create Account Session API The [Create Account Session API](https://docs.stripe.com/api/account_sessions/create.md) determines component and feature access for Connect embedded components. Stripe enforces these parameters for any components that correspond to the account session. If your app supports multiple user roles, make sure components and features that are enabled for that account session correspond to the current user’s role. For example, you can enable [refund management](https://docs.stripe.com/api/account_sessions/create.md#create_account_session-components-payments-features-refund_management) only for administrators of your site, but not for other users. To make sure user role access are enforced, you must map your site’s user role to account session components. ### Install the StripeConnect SDK (Client) 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 15 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 **StripeConnect** 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 'StripeConnect' ``` 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 StripeConnect ``` #### 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/tree/master/StripeConnect#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 **StripeConnect.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/tree/master/StripeConnect#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. ### Set up camera authorization (Client-side) The Stripe Connect iOS SDK requires access to the device’s camera to capture identity documents. To enable your app to request camera permissions: 1. Open your project’s **Info.plist** in Xcode. 1. Add the `NSCameraUsageDescription` key. 1. Add a string value that explains to your users why your app requires camera permissions, something such as: > This app uses the camera to take a picture of your identity documents. See [Apple’s documentation](https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_ios) to learn more about requesting camera authorization. ### Initialize EmbeddedComponentManager (Client) Set your publishable key using `StripeAPI.shared` and instantiate an [EmbeddedComponentManager](https://stripe.dev/stripe-ios/stripeconnect/documentation/stripeconnect/embeddedcomponentmanager) with a closure that retrieves a client secret by calling the new endpoint you created on your server. To create a component, call the appropriate create method on the `EmbeddedComponentManager` that you instantiated above. [Account onboarding](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) returns a controller which manages its own presentation. Other components, such as [Payments](https://docs.stripe.com/connect/supported-embedded-components/payments.md) returns a [UIViewController](https://developer.apple.com/documentation/uikit/uiviewcontroller) that you can display in your app with more flexibility. #### UIKit ```swift import StripeConnect import UIKit class MyViewController: UIViewController { let errorView: UIView func fetchClientSecret() async -> String? { let url = URL(string: "https://{{YOUR_SERVER}}/account_session")! var request = URLRequest(url: url) request.httpMethod = "POST" do { // Fetch the AccountSession client secret let (data, _) = try await URLSession.shared.data(for: request) let json = try JSONSerialization.jsonObject(with: data) as? [String : Any] errorView.isHidden = true return json?["client_secret"] as? String } catch let error { // Handle errors on the client side here print("An error occurred: \(error)") errorView.isHidden = false return nil } } override func viewDidLoad() { super.viewDidLoad() // This is your test publishable API key. STPAPIClient.shared.publishableKey = "{{PUBLISHABLE_KEY}}", let embeddedComponentManager = EmbeddedComponentManager( fetchClientSecret: fetchClientSecret ) // Account onboarding presents modally and fullscreen let controller = embeddedComponentManager.createAccountOnboardingController() controller.title = "Onboard with Stripe" controller.present(from: self) // All other components can be flexibly presented let paymentsViewController = embeddedComponentManager.createPaymentsViewController() present(paymentsViewController) } } ``` ## Configure the Embedded Component Manager [Client-side] [See the code reference :external:](https://stripe.dev/stripe-ios/stripeconnect/documentation/stripeconnect/embeddedcomponentmanager/init\(apiClient:appearance:fonts:fetchClientSecret:\)). ### Customize the look of Connect embedded components The [embedded components Figma UI toolkit](https://www.figma.com/community/file/1438614134095442934) contains every component, common patterns, and an example application. You can use it to visualize and design embedded UIs on your website. We offer a [set of options](https://docs.stripe.com/connect/embedded-appearance-options.md) to customize the look and feel of Connect embedded components. These customizations affect buttons, icons, and other accents in our design system. > #### Necessary popups > > Some behavior in embedded components, such as [user authentication](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#user-authentication-in-connect-embedded-components), must be presented in an authenticated WebView. You can’t customize the embedded component to eliminate such WebViews. You can set these options using [EmbeddedComponentManager.Appearance](https://github.com/stripe/stripe-ios/blob/master/StripeConnect/StripeConnect/Source/EmbeddedComponentManager%2BAppearance.swift) when initializing `EmbeddedComponentManager`. ```swift func fetchClientSecret() async -> String? { let url = URL(string: "https://{{YOUR_SERVER}}/account_session")! var request = URLRequest(url: url) request.httpMethod = "POST" do { let (data, _) = try await URLSession.shared.data(for: request) let json = try JSONSerialization.jsonObject(with: data) as? [String : Any] return json?["client_secret"] as? String } catch { return nil } } // Specify custom fonts var customFonts: [CustomFontSource] = [] let myFont = UIFont(name: "My Font", size: 16)! let fontUrl = Bundle.main.url(forResource: "my-font-2", withExtension: "woff")! do { let customFontSource = try CustomFontSource(font: myFont, fileUrl: fontUrl) customFonts.append(customFontSource) } catch { print("Error loading custom font: \(error)") } // Customize appearance var appearance = EmbeddedComponentManager.Appearance() appearance.typography.fontfont.base = myFont appearance.typography.fontSizeBase = 16 // Unscaled font size appearance.colors.primary = UIColor { traitCollection in if traitCollection.userInterfaceStyle == .dark { UIColor(red: 0.455, green: 0.424, blue: 1.000, alpha: 1.0) } else { UIColor(red: 0.404, green: 0.365, blue: 1.000, alpha: 1.0) } } STPAPIClient.shared.publishableKey = "{{PUBLISHABLE_KEY}}", let embeddedComponentManager = EmbeddedComponentManager( appearance: appearance, fonts: customFonts, fetchClientSecret: fetchClientSecret ) ``` Appearance colors that use [dynamic providers](https://developer.apple.com/documentation/uikit/uicolor/3238041-init) are automatically applied to the Connect embedded component when its [UITraitCollection](https://developer.apple.com/documentation/uikit/uitraitcollection) is updated, including [Dark Mode](https://developer.apple.com/design/human-interface-guidelines/dark-mode) and [accessibility contrast](https://developer.apple.com/documentation/uikit/uiaccessibilitycontrast). The default appearance doesn’t include dark mode colors, so you must specify an appearance with dynamic colors to the `EmbeddedComponentManager` to support dark mode in your app. When specifying font sizes, use the unscaled font size that displays for the device’s default size class. The embedded component automatically scales the font size based on its [UITraitCollection](https://developer.apple.com/documentation/uikit/uitraitcollection). See the [full list of appearance options](https://docs.stripe.com/connect/embedded-appearance-options.md?platform=ios) on iOS. ### Use custom fonts If you use custom fonts in your app (for example, from `.otf` or `.tff` files embedded in your app binary), you must specify the font files in a [CustomFontSource](https://github.com/stripe/stripe-ios/blob/master/StripeConnect/StripeConnect/Source/CustomFontSource.swift) passed to the `fonts` argument when initializing `EmbeddedComponentManager`. This gives Connect embedded components access to the font files to properly render the fonts. Fonts specified in `appearance` must use either a [supported system font](https://developer.apple.com/fonts/system-fonts/) or a [CustomFontSource](https://github.com/stripe/stripe-ios/blob/master/StripeConnect/StripeConnect/Source/CustomFontSource.swift) passed to the `EmbeddedComponentManager` on initialization to properly render. [See the reference documentation :external:](https://github.com/stripe/stripe-ios/blob/master/StripeConnect/StripeConnect/Source/CustomFontSource.swift). ### Update Connect embedded components after initialization Call the `update` method to change the appearance of the embedded components after initialization: ```swift var appearance = EmbeddedComponentManager.Appearance() appearance.colors.primary = UIColor.red manager.update(appearance: appearance) ``` ## Authentication We offer a set of APIs to manage account sessions and user credentials in Connect embedded components. ### Refresh the client secret On long running sessions, the session from the initially provided *client secret* (The client secret is a unique string returned from Stripe as part of an AccountSession. This string lets the client access a specific Stripe account with Connect embedded components) might expire. When it expires, we automatically use `fetchClientSecret` to retrieve a new client secret and refresh the session. You don’t need to pass in any additional parameters. ```swift func fetchClientSecret() async -> String? { var request = URLRequest(url: URL(string: "https://{{YOUR_SERVER}}/account_session")!) request.httpMethod = "POST" do { let (data, _) = try await URLSession.shared.data(for: request) let json = try JSONSerialization.jsonObject(with: data) as? [String : Any] return json?["client_secret"] as? String } catch let error { return nil } } STPAPIClient.shared.publishableKey = "{{PUBLISHABLE_KEY}}", let embeddedComponentManager = EmbeddedComponentManager( fetchClientSecret: fetchClientSecret ) ``` ## Localization Connect embedded components support the following locales: | Language | Locale code | | --------------------------------- | ------------ | | Bulgarian (Bulgaria) | `bg-BG` | | Chinese (Simplified) | `zh-Hans` | | Chinese (Traditional - Hong Kong) | `zh-Hant-HK` | | Chinese (Traditional - Taiwan) | `zh-Hant-TW` | | Croatian (Croatia) | `hr-HR` | | Czech (Czechia) | `cs-CZ` | | Danish (Denmark) | `da-DK` | | Dutch (Netherlands) | `nl-NL` | | English (Australia) | `en-AU` | | English (India) | `en-IN` | | English (Ireland) | `en-IE` | | English (New Zealand) | `en-NZ` | | English (Singapore) | `en-SG` | | English (United Kingdom) | `en-GB` | | English (United States) | `en-US` | | Estonian (Estonia) | `et-EE` | | Filipino (Philippines) | `fil-PH` | | Finnish (Finland) | `fi-FI` | | French (Canada) | `fr-CA` | | French (France) | `fr-FR` | | German (Germany) | `de-DE` | | Greek (Greece) | `el-GR` | | Hungarian (Hungary) | `hu-HU` | | Indonesian (Indonesia) | `id-ID` | | Italian (Italy) | `it-IT` | | Japanese (Japan) | `ja-JP` | | Korean (South Korea) | `ko-KR` | | Latvian (Latvia) | `lv-LV` | | Lithuanian (Lithuania) | `lt-LT` | | Malay (Malaysia) | `ms-MY` | | Maltese (Malta) | `mt-MT` | | Norwegian Bokmål (Norway) | `nb-NO` | | Polish (Poland) | `pl-PL` | | Portuguese (Brazil) | `pt-BR` | | Portuguese (Portugal) | `pt-PT` | | Romanian (Romania) | `ro-RO` | | Slovak (Slovakia) | `sk-SK` | | Slovenian (Slovenia) | `sl-SI` | | Spanish (Argentina) | `es-AR` | | Spanish (Brazil) | `es-BR` | | Spanish (Latin America) | `es-419` | | Spanish (Mexico) | `es-MX` | | Spanish (Spain) | `es-ES` | | Swedish (Sweden) | `sv-SE` | | Thai (Thailand) | `th-TH` | | Turkish (Türkiye) | `tr-TR` | | Vietnamese (Vietnam) | `vi-VN` | ## User authentication in Connect embedded components Connect embedded components typically don’t require user authentication. In some scenarios, Connect embedded components require the connected account to sign in with their Stripe account before accessing the component to provide the necessary functionality (for example, writing information to the account legal entity in the case of the [account onboarding](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) component). Other components might require authentication within the component after they initially render. Authentication is required for connected accounts where Stripe is responsible for collecting updated information when requirements change. For connected accounts where you’re responsible for collecting updated information when requirements are due or change, such as Custom accounts, Stripe authentication is controlled by the [disable_stripe_user_authentication](https://docs.stripe.com/api/account_sessions/create.md#create_account_session-components-account_onboarding-features-disable_stripe_user_authentication) Account Session feature. We recommend implementing 2FA or equivalent security measures as a [best practice](https://docs.stripe.com/connect/risk-management/best-practices.md#prevent-account-take-overs). For account configurations that support this feature, like Custom, you assume liability for connected accounts if they can’t pay back [negative balances](https://docs.stripe.com/connect/risk-management/best-practices.md#decide-your-approach-to-negative-balance-liability). ### Components requiring authentication Connected accounts will be shown an authenticated [WebView](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession) within your application. The connected account must authenticate before they can continue their workflow within the WebView. The Stripe-hosted authentication flow shows your brand’s name, color, and icon as set in your [Connect settings](https://dashboard.stripe.com/account/applications/settings) and doesn’t use your custom appearance and fonts from the [Embedded Component Manager](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#configuring-connect) until authentication completes. The following component requires connected accounts to authenticate in certain scenarios: - [Account Onboarding](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) - [Payouts](https://docs.stripe.com/connect/supported-embedded-components/payouts.md) ## Handle load errors [Client-side] Respond to component load failures by implementing the component’s `onLoadError` listener method. Different failure causes might call the `onLoadError` method multiple times, so any logic triggered by the `onLoadError` must be idempotent. #### Swift ```swift // All components emit load errors. This example uses AccountOnboarding. // All components support didFailLoadWithError. class MyViewController: UIViewController, AccountOnboardingControllerDelegate { func openAccountOnboarding() { let accountOnboardingController = embeddedComponentManager.createAccountOnboardingController(); accountOnboardingController.delegate = self accountOnboardingController.present(from: self) } // MARK: - AccountOnboardingControllerDelegate func accountOnboarding(_ accountOnboarding: AccountOnboardingController, didFailLoadWithError error: Error) { print("Account onboarding failed to load with error '\(error)'") } } ``` ## Set up StripeConnect [Client-side] [Server-side] Stripe uses an [AccountSession](https://docs.stripe.com/api/account_sessions.md) to express your intent to delegate API access to your connected account. The AccountSessions API returns a *client secret* (The client secret is a unique string returned from Stripe as part of an AccountSession. This string lets the client access a specific Stripe account with Connect embedded components) that allows an embedded component to access a connected account’s resources as if you were making the API calls for them. ### Create an AccountSession (Server) Your app must initiate a request to your server to obtain the account session. You can create a new endpoint on your server that returns the client secret to the app: #### Ruby ```ruby require 'sinatra' require 'stripe' # This is a placeholder - it should be replaced with your secret API key. # Sign in to see your own test API key embedded in code samples. # Don’t submit any personally identifiable information in requests made with this key. Stripe.api_key = '<>' post '/account_session' do content_type 'application/json' # Create an AccountSession begin account_session = Stripe::AccountSession.create({ account: {{CONNECTED_ACCOUNT_ID}}, components: { account_onboarding: { enabled: true, features: { # We recommend disabling authentication for a better user experience when possible disable_stripe_user_authentication: true, } } } }) { client_secret: account_session[:client_secret] }.to_json rescue => error puts "An error occurred when calling the Stripe API to create an account session: #{error.message}"; return [500, { error: error.message }.to_json] end end ``` #### Python ```python import stripe # This is a placeholder - it should be replaced with your secret API key. # Sign in to see your own test API key embedded in code samples. # Don’t submit any personally identifiable information in requests made with this key. stripe.api_key = '<>' from flask import Flask, jsonify app = Flask(__name__) @app.route('/account_session', methods=['POST']) def account_session_handler(): try: account_session = stripe.AccountSession.create( account={{CONNECTED_ACCOUNT_ID}}, components={ "account_onboarding": { "enabled": True, "features": { # We recommend disabling authentication for a better user experience when possible "disable_stripe_user_authentication": True } }, }, ) return jsonify({ 'client_secret': account_session.client_secret, }) except Exception as e: print('An error occurred when calling the Stripe API to create an account session: ', e) return jsonify(error=str(e)), 500 ``` #### PHP ```php '<>', ]); try { $account_session = $stripe->accountSessions->create([ 'account' => '{{CONNECTED_ACCOUNT_ID}}', 'components' => [ 'account_onboarding' => [ 'enabled' => true, 'features' => [ // We recommend disabling authentication for a better user experience when possible 'disable_stripe_user_authentication' => true, ], ], ]); echo json_encode(array( 'client_secret' => $account_session->client_secret )); } catch (Exception $e) { error_log("An error occurred when calling the Stripe API to create an account session: {$e->getMessage()}"); http_response_code(500); echo json_encode(['error' => $e->getMessage()]); } ?> ``` #### Java ```java import com.google.gson.Gson; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import static spark.Spark.post; import static spark.Spark.port; import static spark.Spark.staticFiles; import com.stripe.Stripe; import com.stripe.model.AccountSession; public class Server { private static Gson gson = new Gson(); static class ErrorResponse { private String error; public ErrorResponse(String error) { this.error = error; } } static class CreateAccountSessionResponse { private String client_secret; public CreateAccountSessionResponse(String clientSecret) { this.client_secret = clientSecret; } } public static void main(String[] args) { port(4242); // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. Stripe.apiKey = '<>'; post("/account_session", (request, response) -> { response.type("application/json"); try { Map params = new HashMap<>(); params.put("account", "{{CONNECTED_ACCOUNT_ID}}"); Map onboarding = new HashMap<>(); onboarding.put("enabled", true); Map features = new HashMap<>(); // We recommend disabling authentication for a better user experience when possible features.put("disable_stripe_user_authentication", true); onboarding.put("features", features); Map components = new HashMap<>(); components.put("account_onboarding", onboarding); params.put("components", components); AccountSession accountSession = AccountSession.create(params); CreateAccountSessionResponse accountSessionResponse = new CreateAccountSessionResponse(accountSession.getClientSecret()); return gson.toJson(accountSessionResponse); } catch(Exception e) { System.out.println("An error occurred when calling the Stripe API to create an account session: " + e.getMessage()); response.status(500); return gson.toJson(new ErrorResponse(e.getMessage())); } }); } } ``` #### Node.js ```javascript const stripe = require("stripe")( // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. '<>', ); const express = require('express'); const app = express(); app.post('/account_session', async (req, res) => { try { const accountSession = await stripe.accountSessions.create({ account: '{{CONNECTED_ACCOUNT_ID}}', components: { account_onboarding: { enabled: true, features: { // We recommend disabling authentication for a better user experience when possible disable_stripe_user_authentication: true, } }, } }); res.json({ client_secret: accountSession.client_secret, }); } catch (error) { console.error('An error occurred when calling the Stripe API to create an account session', error); res.status(500); res.send({error: error.message}); } }); app.listen(3000, () => { console.log('Running on port 3000'); }); ``` #### Go ```go package main import ( "bytes" "encoding/json" "io" "log" "net/http" "github.com/stripe/stripe-go/v75" "github.com/stripe/stripe-go/v75/accountsession" ) func main() { // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. stripe.Key = '<>' http.Handle("/", http.FileServer(http.Dir("public"))) http.HandleFunc("/account_session", CreateAccountSession) addr := "localhost:4242" log.Printf("Listening on %s", addr) log.Fatal(http.ListenAndServe(addr, nil)) } func CreateAccountSession(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) return } accountSession, err := accountsession.New( &stripe.AccountSessionParams{ Account: stripe.String("{{CONNECTED_ACCOUNT_ID}}"), Components: &stripe.AccountSessionComponentsParams{ AccountOnboarding: &stripe.AccountSessionComponentsAccountOnboardingParams{ Enabled: stripe.Bool(true), Features: &stripe.AccountSessionComponentsAccountOnboardingFeaturesParams{ // We recommend disabling authentication for a better user experience when possible DisableStripeUserAuthentication: stripe.Bool(true), }, }, }, ) if err != nil { log.Printf("An error occurred when calling the Stripe API to create an account session: %v", err) w.WriteHeader(http.StatusInternalServerError) if stripeErr, ok := err.(*stripe.Error); ok { writeJSON(w, struct { Error string `json:"error"` }{ Error: stripeErr.Msg, }) } else { writeJSON(w, struct { Error string `json:"error"` }{ Error: err.Error(), }) } return } writeJSON(w, struct { ClientSecret string `json:"client_secret"` }{ ClientSecret: accountSession.ClientSecret, }) } func writeJSON(w http.ResponseWriter, v interface{}) { var buf bytes.Buffer if err := json.NewEncoder(&buf).Encode(v); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) log.Printf("json.NewEncoder.Encode: %v", err) return } w.Header().Set("Content-Type", "application/json") if _, err := io.Copy(w, &buf); err != nil { log.Printf("io.Copy: %v", err) return } } ``` #### .NET ```csharp using System; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Stripe; namespace server.Controllers { public class Program { public static void Main(string[] args) { WebHost.CreateDefaultBuilder(args) .UseUrls("http://0.0.0.0:4242") .UseWebRoot("public") .UseStartup() .Build() .Run(); } } public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddNewtonsoftJson(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // This is a placeholder - it should be replaced with your secret API key. // Sign in to see your own test API key embedded in code samples. // Don’t submit any personally identifiable information in requests made with this key. StripeConfiguration.ApiKey = "<>"; if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseRouting(); app.UseStaticFiles(); app.UseEndpoints(endpoints => endpoints.MapControllers()); } } [Route("account_session")] [ApiController] public class AccountSessionApiController : Controller { [HttpPost] public ActionResult Post() { try { var service = new AccountSessionService(); AccountSession accountSession = service.Create( new AccountSessionCreateOptions { Account = {{CONNECTED_ACCOUNT_ID}}, Components = new AccountSessionComponentsOptions { AccountOnboarding = new AccountSessionComponentsAccountOnboardingOptions { Enabled = true, Features = new AccountSessionComponentsAccountOnboardingFeaturesOptions { // We recommend disabling authentication for a better user experience when possible DisableStripeUserAuthentication = true } }, } } ); return Json(new { client_secret = accountSession.ClientSecret }); } catch(Exception ex) { Console.Write("An error occurred when calling the Stripe API to create an account session: " + ex.Message); Response.StatusCode = 500; return Json(new { error = ex.Message }); } } } } ``` ### Create Account Session API The [Create Account Session API](https://docs.stripe.com/api/account_sessions/create.md) determines component and feature access for Connect embedded components. Stripe enforces these parameters for any components that correspond to the account session. If your app supports multiple user roles, make sure components and features that are enabled for that account session correspond to the current user’s role. For example, you can enable [refund management](https://docs.stripe.com/api/account_sessions/create.md#create_account_session-components-payments-features-refund_management) only for administrators of your site, but not for other users. To make sure user role access are enforced, you must map your site’s user role to account session components. ### Install the StripeConnect SDK (Client) 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 `connect` 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 { // ... // Connect Android SDK implementation("com.stripe:connect:22.6.1") } ``` #### Groovy ```groovy apply plugin: 'com.android.application' android { ... } dependencies { // ... // Connect Android SDK implementation 'com.stripe:connect:22.6.1' } ``` > 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). ### Initialize EmbeddedComponentManager (Client) Instantiate an [EmbeddedComponentManager](https://stripe.dev/stripe-android/connect/com.stripe.android.connect/-embedded-component-manager/index.html) with your publishable key and a lambda that retrieves a client secret by calling the new endpoint you created on your server. To handle configuration changes, keep the `EmbeddedComponentManager` instance in an Activity or Fragment `ViewModel`. #### Kotlin ```kotlin class MyActivityViewModel : ViewModel() { val embeddedComponentManager: EmbeddedComponentManager = EmbeddedComponentManager( // This is a placeholder - it should be replaced with your publishable API key. // Sign in to see your own test API key embedded in code samples. // Don't submit any personally identifiable information in requests made with this key. publishableKey = "<>", fetchClientSecret = ::fetchClientSecret, ) private suspend fun fetchClientSecret(): String? = try { // Fetch the AccountSession client secret Fuel.post("https://{{YOUR_SERVER_BASE_URL}}/account_session") .awaitString() .let { JSONObject(it).getString("client_secret") } } catch (error: CancellationException) { throw error } catch (error: Exception) { // Handle errors on the client side here println("Error fetching client secret: ${error.message}") null } } ``` #### Java ```java public class MyActivityViewModel extends ViewModel { public EmbeddedComponentManager embeddedComponentManager = new EmbeddedComponentManager( // This is a placeholder - it should be replaced with your publishable API key. // Sign in to see your own test API key embedded in code samples. // Don't submit any personally identifiable information in requests made with this key. "<>", // publishableKey new FetchClientSecretTaskImpl() // fetchClientSecret ); private final class FetchClientSecretTaskImpl extends FetchClientSecretTask { @Override public void fetchClientSecret(@NonNull ResultCallback callback) { // Fetch the AccountSession client secret final var req = Fuel.INSTANCE.post("https://{{YOUR_SERVER_BASE_URL}}/account_session", List.of()) .responseString((request, response, result) -> { try { final var clientSecret = new JSONObject(result.get()).getString("client_secret"); callback.onResult(clientSecret); } catch (Exception e) { // Handle errors on the client side here System.out.println("Error fetching client secret: " + e.getMessage()); callback.onResult(null); } return null; }); addCloseable(req::cancel); } } } ``` To create a component, first call `EmbeddedComponentManager.onActivityCreate()` in your Activity’s `onCreate` method. Then, call the appropriate create method on the `EmbeddedComponentManager` that you instantiated above. [Account onboarding](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) returns a controller which manages its own presentation. Other components, such as [Payments](https://docs.stripe.com/connect/supported-embedded-components/payments.md) returns a [View](https://developer.android.com/reference/android/view/View) that you can display in your app with more flexibility. #### Rendering a controller #### Kotlin ```kotlin class MyActivity : FragmentActivity() { private val viewModel: MyActivityViewModel by viewModels() private lateinit var accountOnboardingController: AccountOnboardingController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) EmbeddedComponentManager.onActivityCreate(this) setContentView(R.layout.my_activity) accountOnboardingController = viewModel.embeddedComponentManager.createAccountOnboardingController(this) } private fun openAccountOnboarding() { accountOnboardingController.show() } } ``` #### Java ```java public class MyActivity extends FragmentActivity { private MyActivityViewModel viewModel; private AccountOnboardingController accountOnboardingController; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); EmbeddedComponentManager.onActivityCreate(this); setContentView(R.layout.my_activity); viewModel = new ViewModelProvider(this).get(MyActivityViewModel.class); accountOnboardingController = viewModel.embeddedComponentManager.createAccountOnboardingController(this); } private void openAccountOnboarding() { accountOnboardingController.show(); } } ``` #### Rendering a `View` #### Kotlin ```kotlin class MyActivity : FragmentActivity() { private val viewModel: MyActivityViewModel by viewModels() private lateinit var paymentsView: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) EmbeddedComponentManager.onActivityCreate(this) setContentView(R.layout.my_activity) paymentsView = viewModel.embeddedComponentManager.createPaymentsView(this) // Add the view to your layout val container = findViewById(R.id.payments_container) container.addView(paymentsView) } } ``` #### Java ```java public class MyActivity extends FragmentActivity { private MyActivityViewModel viewModel; private View paymentsView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); EmbeddedComponentManager.onActivityCreate(this); setContentView(R.layout.my_activity); viewModel = new ViewModelProvider(this).get(MyActivityViewModel.class); paymentsView = viewModel.embeddedComponentManager.createPaymentsView(this); // Add the view to your layout ViewGroup container = findViewById(R.id.payments_container); container.addView(paymentsView); } } ``` ## Configure the Embedded Component Manager [Client-side] [See the code reference :external:](https://stripe.dev/stripe-android/connect/com.stripe.android.connect/-embedded-component-manager/index.html). ### Customize the look of Connect embedded components The [embedded components Figma UI toolkit](https://www.figma.com/community/file/1438614134095442934) contains every component, common patterns, and an example application. You can use it to visualize and design embedded UIs on your website. We offer a [set of options](https://docs.stripe.com/connect/embedded-appearance-options.md) to customize the look and feel of Connect embedded components. These customizations affect buttons, icons, and other accents in our design system. > #### Necessary popups > > Some behavior in embedded components, such as [user authentication](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#user-authentication-in-connect-embedded-components), must be presented in an authenticated WebView. You can’t customize the embedded component to eliminate such WebViews. You can set these options using [Appearance](https://stripe.dev/stripe-android/connect/com.stripe.android.connect.appearance/-appearance/index.html) when initializing `EmbeddedComponentManager`. #### Kotlin ```kotlin // Specify custom fonts val customFonts = listOf( CustomFontSource( // Font file located in `assets/` folder assetsFilePath = "fonts/myCustomFont.ttf", name = "myCustomFont", weight = 1000, ) ) // Customize appearance val appearance = Appearance.Builder() .typography( Typography.Builder() .fontFamily("myCustomFont") // Same name as the custom font above .fontSizeBase(16f) // Unscaled font size .build() ) .colors( Colors.Builder() .primary(Color.RED) .build() ) .build() val embeddedComponentManager = EmbeddedComponentManager( publishableKey = "<>", fetchClientSecret = ::fetchClientSecret, appearance = appearance, customFonts = customFonts, ) ``` #### Java ```java // Specify custom fonts List customFonts = List.of( new CustomFontSource( // Font file located in `assets/` folder "fonts/myCustomFont.ttf", // assetsFilePath "myCustomFont", // name 1000 // weight ) ); // Customize appearance Appearance appearance = new Appearance.Builder() .typography( new Typography.Builder() .fontFamily("myCustomFont") // Same name as the custom font above .fontSizeBase(16f) // Unscaled font size .build() ) .colors( new Colors.Builder() .primary(Color.RED) .build() ) .build(); EmbeddedComponentManager embeddedComponentManager = new EmbeddedComponentManager( "<>", // publishableKey new FetchClientSecretTaskImpl() // fetchClientSecret appearance, // appearance customFonts // customFonts ); ``` When specifying font sizes, use the unscaled font size that displays for the device’s default size class. The embedded component automatically scales the font size based on the user’s [Accessibility font settings](https://support.google.com/accessibility/android/answer/11183305?sjid=3094445894544346025-NA#fontsize). See the [full list of appearance options](https://docs.stripe.com/connect/embedded-appearance-options.md?platform=android) on Android. ### Use custom fonts If you use custom fonts in your app (for example, from `.otf` or `.tff` files embedded in your app binary), you must specify the font files in a [CustomFontSource](https://stripe.dev/stripe-android/connect/com.stripe.android.connect.appearance.fonts/-custom-font-source/index.html) passed to the `customFonts` argument when initializing `EmbeddedComponentManager`. This gives Connect embedded components access to the font files to properly render the fonts. Fonts specified in `appearance` must use a [CustomFontSource](https://stripe.dev/stripe-android/connect/com.stripe.android.connect.appearance.fonts/-custom-font-source/index.html) passed to the `EmbeddedComponentManager` on initialization to properly render. [See the reference documentation :external:](https://stripe.dev/stripe-android/connect/com.stripe.android.connect.appearance.fonts/-custom-font-source/index.html). ### Update Connect embedded components after initialization Call the `update` method to change the appearance of the embedded components after initialization: #### Kotlin ```kotlin val appearance = Appearance.Builder() .colors( Colors.Builder() .primary(ContextCompat.getColor(context, R.color.primary)) .build() ) .build() embeddedComponentManager.update(appearance = appearance) ``` #### Java ```java Appearance appearance = new Appearance.Builder() .colors( new Colors.Builder() .primary(ContextCompat.getColor(context, R.color.primary)) .build() ) .build(); embeddedComponentManager.update(appearance); ``` ## Authentication We offer a set of APIs to manage account sessions and user credentials in Connect embedded components. ### Refresh the client secret On long running sessions, the session from the initially provided *client secret* (The client secret is a unique string returned from Stripe as part of an AccountSession. This string lets the client access a specific Stripe account with Connect embedded components) might expire. When it expires, we automatically use `fetchClientSecret` to retrieve a new client secret and refresh the session. You don’t need to pass in any additional parameters. #### Kotlin ```kotlin val embeddedComponentManager: EmbeddedComponentManager = EmbeddedComponentManager( publishableKey = "<>", fetchClientSecret = ::fetchClientSecret, ) private suspend fun fetchClientSecret(): String? = try { Fuel.post("https://{{YOUR_SERVER_BASE_URL}}/account_session") .awaitString() .let { JSONObject(it).getString("client_secret") } } catch (error: CancellationException) { throw error } catch (error: Exception) { null } ``` #### Java ```java public EmbeddedComponentManager embeddedComponentManager = new EmbeddedComponentManager( "<>", new FetchClientSecretTaskImpl() // fetchClientSecret ); private final class FetchClientSecretTaskImpl extends FetchClientSecretTask { @Override public void fetchClientSecret(@NonNull ResultCallback callback) { final var req = Fuel.INSTANCE.post("https://{{YOUR_SERVER_BASE_URL}}/account_session", List.of()) .responseString((request, response, result) -> { try { final var clientSecret = new JSONObject(result.get()).getString("client_secret"); callback.onResult(clientSecret); } catch (Exception e) { callback.onResult(null); } return null; }); addCloseable(req::cancel); } } ``` ## Localization Connect embedded components support the following locales: | Language | Locale code | | --------------------------------- | ------------ | | Bulgarian (Bulgaria) | `bg-BG` | | Chinese (Simplified) | `zh-Hans` | | Chinese (Traditional - Hong Kong) | `zh-Hant-HK` | | Chinese (Traditional - Taiwan) | `zh-Hant-TW` | | Croatian (Croatia) | `hr-HR` | | Czech (Czechia) | `cs-CZ` | | Danish (Denmark) | `da-DK` | | Dutch (Netherlands) | `nl-NL` | | English (Australia) | `en-AU` | | English (India) | `en-IN` | | English (Ireland) | `en-IE` | | English (New Zealand) | `en-NZ` | | English (Singapore) | `en-SG` | | English (United Kingdom) | `en-GB` | | English (United States) | `en-US` | | Estonian (Estonia) | `et-EE` | | Filipino (Philippines) | `fil-PH` | | Finnish (Finland) | `fi-FI` | | French (Canada) | `fr-CA` | | French (France) | `fr-FR` | | German (Germany) | `de-DE` | | Greek (Greece) | `el-GR` | | Hungarian (Hungary) | `hu-HU` | | Indonesian (Indonesia) | `id-ID` | | Italian (Italy) | `it-IT` | | Japanese (Japan) | `ja-JP` | | Korean (South Korea) | `ko-KR` | | Latvian (Latvia) | `lv-LV` | | Lithuanian (Lithuania) | `lt-LT` | | Malay (Malaysia) | `ms-MY` | | Maltese (Malta) | `mt-MT` | | Norwegian Bokmål (Norway) | `nb-NO` | | Polish (Poland) | `pl-PL` | | Portuguese (Brazil) | `pt-BR` | | Portuguese (Portugal) | `pt-PT` | | Romanian (Romania) | `ro-RO` | | Slovak (Slovakia) | `sk-SK` | | Slovenian (Slovenia) | `sl-SI` | | Spanish (Argentina) | `es-AR` | | Spanish (Brazil) | `es-BR` | | Spanish (Latin America) | `es-419` | | Spanish (Mexico) | `es-MX` | | Spanish (Spain) | `es-ES` | | Swedish (Sweden) | `sv-SE` | | Thai (Thailand) | `th-TH` | | Turkish (Türkiye) | `tr-TR` | | Vietnamese (Vietnam) | `vi-VN` | ## User authentication in Connect embedded components Connect embedded components typically don’t require user authentication. In some scenarios, Connect embedded components require the connected account to sign in with their Stripe account before accessing the component to provide the necessary functionality (for example, writing information to the account legal entity in the case of the [account onboarding](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) component). Other components might require authentication within the component after they initially render. Authentication is required for connected accounts where Stripe is responsible for collecting updated information when requirements change. For connected accounts where you’re responsible for collecting updated information when requirements are due or change, such as Custom accounts, Stripe authentication is controlled by the [disable_stripe_user_authentication](https://docs.stripe.com/api/account_sessions/create.md#create_account_session-components-account_onboarding-features-disable_stripe_user_authentication) Account Session feature. We recommend implementing 2FA or equivalent security measures as a [best practice](https://docs.stripe.com/connect/risk-management/best-practices.md#prevent-account-take-overs). For account configurations that support this feature, like Custom, you assume liability for connected accounts if they can’t pay back [negative balances](https://docs.stripe.com/connect/risk-management/best-practices.md#decide-your-approach-to-negative-balance-liability). ### Components requiring authentication Connected accounts will be shown an authenticated [WebView](https://developer.chrome.com/docs/android/custom-tabs) within your application. The connected account must authenticate before they can continue their workflow within the WebView. The Stripe-hosted authentication flow shows your brand’s name, color, and icon as set in your [Connect settings](https://dashboard.stripe.com/account/applications/settings) and doesn’t use your custom appearance and fonts from the [Embedded Component Manager](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#configuring-connect) until authentication completes. > #### Android limitation > > Due to a limitation within the Android APIs, embedded components can’t use custom fonts within the authenticated WebView, even after authentication completes. The following component requires connected accounts to authenticate in certain scenarios: - [Account Onboarding](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) - [Payouts](https://docs.stripe.com/connect/supported-embedded-components/payouts.md) ## Handle load errors [Client-side] Respond to component load failures by implementing the component’s `onLoadError` listener method. Different failure causes might call the `onLoadError` method multiple times, so any logic triggered by the `onLoadError` must be idempotent. #### Kotlin ```kotlin // All components emit load errors. This example uses AccountOnboarding. // All components support onLoadError. class MyActivity : FragmentActivity() { private lateinit var accountOnboardingController: AccountOnboardingController override fun onCreate(savedInstanceState: Bundle?) { accountOnboardingController = embeddedComponentManager.createAccountOnboardingController(this) accountOnboardingController.listener = MyAccountOnboardingListener() } private fun openAccountOnboarding() { accountOnboardingController.show() } private inner class MyAccountOnboardingListener : AccountOnboardingListener { override fun onLoadError(error: Throwable) { println("Error loading account onboarding: ${error.message}") } } } ``` #### Java ```java // All components emit load errors. This example uses AccountOnboarding. // All components support onLoadError. public class MyActivity extends FragmentActivity { private AccountOnboardingController accountOnboardingController; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { accountOnboardingController = embeddedComponentManager.createAccountOnboardingController(this); accountOnboardingController.setListener(new MyAccountOnboardingListener()); } private void openAccountOnboarding() { accountOnboardingController.show(); } class MyAccountOnboardingListener implements AccountOnboardingListener { @Override public void onLoadError(@NonNull Throwable error) { System.out.println("Error loading account onboarding: ${error.message}"); } } } ```