# 开始使用 Connect 嵌入式组件 了解如何在您的网站中嵌入管理平台功能。 使用 Connect 嵌入式组件向您的网站添加关联账户管理平台功能。这些库及其支持的 API 允许您直接在您的管理平台和移动应用中为用户提供对 Stripe 产品的访问权限。 - **添加集成用户界面**:使用嵌入式组件向您的管理平台添加集成用户界面。请参阅[所有可用组件](https://docs.stripe.com/connect/supported-embedded-components.md)。 - **自定义外观**:调整[外观](https://docs.stripe.com/connect/customize-connect-embedded-components.md)以匹配您的品牌。 - **保持最新**:Connect 嵌入式组件与 Stripe API 保持同步,有助于让您的集成保持最新。 ## 设置 StripeConnect [客户端] [服务器端] Stripe 使用 [AccountSession](https://docs.stripe.com/api/account_sessions.md) 来表达您将 API 访问权限授予 Connect 子账户的意图。 AccountSessions API 返回一个*客户端密钥* (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),允许嵌入式组件访问关联账户的资源,就像您在为它们进行 API 调用一样。 ### 创建 AccountSession (Server) 您的应用必须向服务器发起请求以获取账户会话。您可以在服务器上创建一个新端点,将客户端私钥返回给应用: #### Ruby ```ruby require 'sinatra' require 'stripe' # This is a placeholder - it should be replaced with your API key. # Sign in to see your own test API key embedded in code samples. # Don't put any keys in code. We recommend using a restricted API key with access only to the account sessions resource. See https://docs.stripe.com/keys-best-practices client = Stripe::StripeClient.new('<>') post '/account_session' do content_type 'application/json' # Create an AccountSession begin account_session = client.v1.account_sessions.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 ``` ### 创建 Account Session API [创建账户会话 API](https://docs.stripe.com/api/account_sessions/create.md) 决定 Connect 嵌入组件的组件和功能访问权限。Stripe 会对与该账户会话对应的任何组件强制执行这些参数。如果您的应用支持多个用户角色,请确保该账户会话启用的组件和功能与当前用户的角色对应。例如,您可以仅为网站管理员启用[退款管理](https://docs.stripe.com/api/account_sessions/create.md#create_account_session-components-payments-features-refund_management)功能,而不允许其他用户访问。为了确保用户角色访问控制得到强制执行,您必须将网站的用户角色映射到账户会话组件上。 ### 安装 StripeConnect SDK (Client) [Stripe iOS SDK](https://github.com/stripe/stripe-ios) 是开源的,[有完整的文档](https://stripe.dev/stripe-ios/index.html),并且与支持 iOS 15 或更高版本操作系统的应用程序兼容。 #### Swift Package Manager 要安装 SDK,按这些步骤进行: 1. 在 Xcode 中,选择**文件** > **添加工具包依赖…**并输入 `https://github.com/stripe/stripe-ios-spm` 作为仓库 URL。 2. 从我们的[发布页面](https://github.com/stripe/stripe-ios/releases)选择最新的版本号。 3. 将 **StripeConnect** 产品添加到[您的目标应用程序](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app)。 #### CocoaPods 1. 如果您还没有[CocoaPods](https://guides.cocoapods.org/using/getting-started.html),请安装其最新版本。 2. 如果您目前没有 [Podfile](https://guides.cocoapods.org/syntax/podfile.html),请运行以下命令创建一个: ```bash pod init ``` 3. 将这一行代码添加到您的 `Podfile`: ```podfile pod 'StripeConnect' ``` 4. 运行以下命令: ```bash pod install ``` 5. 今后,一定记得用 `.xcworkspace` 文件来打开您在 Xcode 中的项目,不要使用 `.xcodeproj` 文件。 6. 将来,要更新到 SDK 的最新版本,运行: ```bash pod update StripeConnect ``` #### Carthage 1. 如果您还没有[Carthage](https://github.com/Carthage/Carthage#installing-carthage),请安装其最新版本。 2. 将这一行代码添加到您的 `Cartfile`。 ```cartfile github "stripe/stripe-ios" ``` 3. 按照 [Carthage 安装说明](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos)进行。确保嵌入[这里](https://github.com/stripe/stripe-ios/tree/master/StripeConnect#manual-linking)所列的所有必要框架。 4. 将来,要更新到 SDK 的最新版本,运行以下命令即可: ```bash carthage update stripe-ios --platform ios ``` #### 手动框架 1. 前往我们的 [GitHub 发布页面](https://github.com/stripe/stripe-ios/releases/latest),下载并解压缩 **Stripe.xcframework.zip**。 2. 将 **StripeConnect.xcframework** 拖拽到您的 Xcode 项目中 **General**(常规)设置的 **Embedded Binaries**(嵌入式二进制文件)部分。一定要选择 **Copy items if needed**(需要时复制项目)。 3. 为[这里](https://github.com/stripe/stripe-ios/tree/master/StripeConnect#manual-linking)所列的所有必要框架重复第 2 步。 4. 将来,要更新到 SDK 的最新版本,重复第 1-3 步。 > 有关最新 SDK 发布及过往版本的详细信息,请查看 GitHub 上的[发布](https://github.com/stripe/stripe-ios/releases)页面。要想在新版本发布时接收通知,请[查看仓库的发布](https://help.github.com/en/articles/watching-and-unwatching-releases-for-a-repository#watching-releases-for-a-repository)。 ### 设置相机授权 (Client-side) Stripe Connect iOS SDK 要求访问设备的摄像头来捕捉身份文件。要使您的应用能够请求相机访问权限,请执行以下操作: 1. 在 Xcode 中打开您项目的 **Info.plist**。 2. 添加 `NSCameraUsageDescription` 密钥。 3. 添加一个字符串值,向用户解释为什么您的应用程序需要相机访问权限,例如: > 该应用程序将使用相机拍摄您的身份证件照片。 查看 [Apple 文档](https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_ios),了解有关请求相机授权的更多信息。 ### 初始化 EmbeddedComponentManager (Client) 使用 `StripeAPI.shared` 设置您的可发布密钥,并使用一个闭包来实例化 [EmbeddedComponentManager](https://stripe.dev/stripe-ios/stripeconnect/documentation/stripeconnect/embeddedcomponentmanager),该闭包通过调用您在服务器上创建的新端点来检索客户端私钥。要创建组件,请在您上面实例化的 `EmbeddedComponentManager` 上调用相应的创建方法。 [账户入驻](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md)会返回一个控制器,用于管理其自身的展示。其他组件(如 [Payments](https://docs.stripe.com/connect/supported-embedded-components/payments.md))会返回一个 [UIViewController](https://developer.apple.com/documentation/uikit/uiviewcontroller),您可以应用程序中更灵活地展示该组件。 #### 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 [客户端] [查看参考文档:external:](https://stripe.dev/stripe-ios/stripeconnect/documentation/stripeconnect/embeddedcomponentmanager/init\(apiClient:appearance:fonts:fetchClientSecret:\))。 ### 自定义 Connect 嵌入式组件的外观 [嵌入式组件 Figma UI 工具包](https://www.figma.com/community/file/1438614134095442934)包含每个组件、常见模式和一个示例应用程序。您可以使用它来可视化和设计网站中的嵌入式用户界面。 我们提供了[一组选项](https://docs.stripe.com/connect/embedded-appearance-options.md)来定制 Connect 嵌入式组件的外观。这些定制会影响我们设计系统中的按钮、图标和其他元素。 > #### 必要的弹出窗口 > > 嵌入式组件中的某些行为(例如[用户验证](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#user-authentication-in-connect-embedded-components))必须在经过验证的 WebView 中呈现。您无法定制嵌入式组件以消除此类 WebView。 您可以在初始化 `EmbeddedComponentManager` 时,使用 [EmbeddedComponentManager.Appearance](https://stripe.dev/stripe-ios/stripeconnect/documentation/stripeconnect/embeddedcomponentmanager/appearance) 设置这些选项。 ```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.font.base = myFont appearance.typography.fontSizeBase = 16 // Unscaled font size appearance.colors.primary = UIColor { traitCollection in if traitCollection.userInterfaceStyle == .dark { return UIColor(red: 0.455, green: 0.424, blue: 1.000, alpha: 1.0) } else { return 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 ) ``` 当 Connect 嵌入式组件的 [UITraitCollection](https://developer.apple.com/documentation/uikit/uitraitcollection) 被更新时,使用[动态提供程序](https://developer.apple.com/documentation/uikit/uicolor/3238041-init)的外观颜色会自动应用于 Connect 嵌入式组件,包括[暗色模式](https://developer.apple.com/design/human-interface-guidelines/dark-mode)和[辅助功能对比度](https://developer.apple.com/documentation/uikit/uiaccessibilitycontrast)。默认外观不包括暗色模式颜色,因此您必须为 `EmbeddedComponentManager` 指定具有动态颜色的外观,才能在您的应用中支持暗色模式。 指定字体大小时,请使用针对设备的默认大小等级显示的未缩放字体大小。嵌入式组件会根据它的 [UITraitCollection](https://developer.apple.com/documentation/uikit/uitraitcollection) 自动缩放字号。 请参阅 iOS 上的 [外观选项完整列表](https://docs.stripe.com/connect/embedded-appearance-options.md)。 ### 使用自定义字体 如果您的应用中使用自定义字体(例如,来自嵌入应用二进制文件的 `.otf` 或 `.tff` 文件),则必须在初始化 `EmbeddedComponentManager` 时,在传递给 `fonts` 参数的 [CustomFontSource](https://stripe.dev/stripe-ios/stripeconnect/documentation/stripeconnect/embeddedcomponentmanager/customfontsource) 中指定字体文件。这使 Connect 嵌入式组件能够访问字体文件,从而正确渲染字体。 在 `appearance` 中指定的字体必须使用 [支持的系统字体](https://developer.apple.com/fonts/system-fonts/),或在初始化时传递给 `EmbeddedComponentManager` 的 [CustomFontSource](https://stripe.dev/stripe-ios/stripeconnect/documentation/stripeconnect/embeddedcomponentmanager/customfontsource),才能正确渲染。 [查看参考文档:external:](https://stripe.dev/stripe-ios/stripeconnect/documentation/stripeconnect/embeddedcomponentmanager/customfontsource)。 ### 初始化后更新 Connect 嵌入式组件 调用 `update` 方法,更改初始化后嵌入式组件的外观: ```swift var appearance = EmbeddedComponentManager.Appearance() appearance.colors.primary = UIColor.red manager.update(appearance: appearance) ``` ## 验证 我们提供了一组 API 来管理 Connect 嵌入式组件中的账户会话和用户凭证。 ### 刷新客户端私钥 在长时间运行的会话中,来自最初提供的*客户端私钥* (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)的会话可能会过期。当它过期时,我们会自动用 `fetchClientSecret` 检索新的客户端私钥并刷新会话。您不需要传入任何额外参数。 ```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 ) ``` ## 本地化 Connect 嵌入式组件支持以下地区: | 语言 | 区域代码 | | --- | --- | | 保加利亚语(保加利亚) | `bg-BG` | | 中文(简体) | `zh-Hans` | | 中文(繁体 - 香港) | `zh-Hant-HK` | | 中文(繁体 - 台湾) | `zh-Hant-TW` | | 克罗地亚语(克罗地亚) | `hr-HR` | | 捷克语(捷克) | `cs-CZ` | | 丹麦语(丹麦) | `da-DK` | | 荷兰语(荷兰) | `nl-NL` | | 英语(澳大利亚) | `en-AU` | | 英语(印度) | `en-IN` | | 英语(爱尔兰) | `en-IE` | | 英语(新西兰) | `en-NZ` | | 英语(新加坡) | `en-SG` | | 英语(英国) | `en-GB` | | 英语(美国) | `en-US` | | 爱沙尼亚语(爱沙尼亚) | `et-EE` | | 菲律宾语(菲律宾) | `fil-PH` | | 芬兰语(芬兰) | `fi-FI` | | 法语(加拿大) | `fr-CA` | | 法语(法国) | `fr-FR` | | 德语(德国) | `de-DE` | | 希腊语(希腊) | `el-GR` | | 匈牙利语(匈牙利) | `hu-HU` | | 印度尼西亚语(印度尼西亚) | `id-ID` | | 意大利语(意大利) | `it-IT` | | 日语(日本) | `ja-JP` | | 朝鲜语(韩国) | `ko-KR` | | 拉脱维亚语(拉脱维亚) | `lv-LV` | | 立陶宛语(立陶宛) | `lt-LT` | | 马来语(马来西亚) | `ms-MY` | | 马耳他语(马耳他) | `mt-MT` | | 挪威语(波克默尔语)(挪威) | `nb-NO` | | 波兰语(波兰) | `pl-PL` | | 葡萄牙语(巴西) | `pt-BR` | | 葡萄牙语(葡萄牙) | `pt-PT` | | 罗马尼亚语(罗马尼亚) | `ro-RO` | | 斯洛伐克语(斯洛伐克) | `sk-SK` | | 斯洛文尼亚语(斯洛文尼亚) | `sl-SI` | | 西班牙语(阿根廷) | `es-AR` | | 西班牙语(巴西) | `es-BR` | | 西班牙语(拉丁美洲) | `es-419` | | 西班牙语(墨西哥) | `es-MX` | | 西班牙语(西班牙) | `es-ES` | | 瑞典语(瑞典) | `sv-SE` | | 泰语(泰国) | `th-TH` | | 土耳其语(土耳其) | `tr-TR` | | 越南语(越南) | `vi-VN` | ## Connect 嵌入式组件中的用户身份验证 Connect 嵌入式组件通常不需要用户验证。在某些场景中,Connect 嵌入式组件要求关联账户在访问组件以提供必要功能之前使用其 Stripe 账户登录(例如,在[账户注册](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md)组件中向账户法律实体写入信息的情况)。其他组件可能在首次呈现后需要在组件内进行验证。 当要求变更时,若由 Stripe 负责收集更新后的信息,则关联账户需要进行验证。对于要求到期或变更时由您负责收集更新后的信息的关联账户(如 Custom 账户),Stripe 验证由 [disable_stripe_user_authentication](https://docs.stripe.com/api/account_sessions/create.md#create_account_session-components-account_onboarding-features-disable_stripe_user_authentication) 账户会话功能控制。我们建议将实施双重验证 (2FA) 或等效安全措施作为[最佳实践](https://docs.stripe.com/connect/risk-management/best-practices.md#prevent-account-take-overs)。对于支持此功能的账户配置(如 Custom 账户),如果关联账户无法偿还[负余额](https://docs.stripe.com/connect/risk-management/best-practices.md#decide-your-approach-to-negative-balance-liability),则由您承担责任。 ### 需要验证的组件 关联账户将在您的应用中看到经过验证的 [WebView](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession)。关联账户必须先进行验证,才能在 WebView 中继续其工作流程。 Stripe 托管的验证流程会显示您在 [Connect 设置](https://dashboard.stripe.com/account/applications/settings)中设置的品牌名称、颜色和图标,且在验证完成前不会使用来自[嵌入式组件管理器](https://docs.stripe.com/connect/get-started-connect-embedded-components.md#configuring-connect)的定制外观和字体。 以下组件要求关联账户在特定场景下进行验证: - [账户入驻](https://docs.stripe.com/connect/supported-embedded-components/account-onboarding.md) - [提现](https://docs.stripe.com/connect/supported-embedded-components/payouts.md) ## 处理加载错误 [客户端] 通过实现组件的 `onLoadError` 监听方法来响应组件加载失败。 由于组件加载失败的原因可能不同,`onLoadError` 方法可能会被多次调用,因此由 `onLoadError` 触发的任何逻辑都必须具备幂等性。 #### 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)'") } } ```