# Accept in-app payments Build a customized payments integration in your iOS, Android, or React Native app using the Payment Sheet. The Payment Sheet is a customizable component that displays a list of payment methods and collects payment details in your app using a bottom sheet. > #### Accounts v2 API 支持 > > Payment Sheet 不支持 *客户配置的账户* (Account configurations represent role-based functionality that you can enable for accounts, such as merchant, customer, or recipient),仅支持 `Customer` 对象。 # Accept a payment The Payment Element allows you to accept multiple payment methods using a single integration. In this integration, you build a custom payment flow where you render the Payment Element, create the *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods), and confirm the payment in your app. To confirm the payment on the server instead, see [Finalize payments on the server](https://docs.stripe.com/payments/finalize-payments-on-the-server.md). ## 设置 Stripe [服务器端] [客户端] 首先,您需要有 Stripe 账户。[立即注册](https://dashboard.stripe.com/register)。 ### 服务器端 该集成要求您的服务器上的端点与 Stripe API 通讯。请用官方库从您的服务器访问 Stripe API: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### 客户端 [Stripe Android SDK](https://github.com/stripe/stripe-android) 是开源的,且[有完整的文档](https://stripe.dev/stripe-android/)。 安装 SDK 时,将 `stripe-android` 添加到您的 [app/build.gradle](https://developer.android.com/studio/build/dependencies) 文件的 `dependencies` 块中: #### Kotlin ```kotlin plugins { id("com.android.application") } android { ... } dependencies { // ... // Stripe Android SDK implementation("com.stripe:stripe-android:23.11.1") // Include the financial connections SDK to support US bank account as a payment method implementation("com.stripe:financial-connections:23.11.1") } ``` > 有关最新 SDK 发布及过往版本的详细信息,请查看 GitHub 上的[发布](https://github.com/stripe/stripe-android/releases)页面。要想在新版本发布时接收通知,请[查看仓库的发布情况](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository)。 You also need to set your [publishable key](https://dashboard.stripe.com/apikeys) so that the SDK can make API calls to Stripe. To get started quickly, you can hardcode this on the client while you’re integrating, but fetch the publishable key from your server in production. ```kotlin // Set your publishable key: remember to change this to your live publishable key in production // See your keys here: https://dashboard.stripe.com/apikeys PaymentConfiguration.init(context, publishableKey = "<>") ``` ## 启用支付方式 查看您的[支付方式设置](https://dashboard.stripe.com/settings/payment_methods),启用您想支持的支付方式。您至少需要启用一个支付方式才能创建 *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods)。 默认情况下,Stripe 支持信用卡和其他常见的支付方式,可以帮助您获得更多客户,但建议您开启与您的业务和客户相关的其他支付方式。查看[支付方式支持](https://docs.stripe.com/payments/payment-methods/payment-method-support.md),了解支持的产品和支付方式,并查看我们的[定价页面](https://stripe.com/pricing/local-payment-methods)了解费用。 ## 收集付款详情 [客户端] We offer two styles of integration. | PaymentSheet | PaymentSheet.FlowController | | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ![PaymentSheet](https://b.stripecdn.com/docs-statics-srv/assets/android-overview.471eaf89a760f5b6a757fd96b6bb9b60.png) | ![PaymentSheet.FlowController](https://b.stripecdn.com/docs-statics-srv/assets/android-multi-step.84d8a0a44b1baa596bda491322b6d9fd.png) | | Displays a sheet to collect payment details and complete the payment. The button label is **Pay** and the amount. Clicking the button completes the payment. | Displays a sheet to only collect payment details. The button label is **Continue**. Clicking it returns the customer to your app, where your own button completes the payment. | #### PaymentSheet #### Views (Classic) ### Initialize the PaymentSheet Initialize the PaymentSheet and pass in a `CreateIntentCallback`. Leave the implementations empty for now. ```kotlin class MyCheckoutActivity : AppCompatActivity() { private lateinit var paymentSheet: PaymentSheet override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... paymentSheet = PaymentSheet.Builder(::onPaymentSheetResult) .createIntentCallback { confirmationToken -> TODO() // You'll implement this later } .build(this) } fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) { // You'll implement this later } } ``` ### Present the PaymentSheet Next, present the PaymentSheet by calling `presentWithIntentConfiguration()` and pass in an [IntentConfiguration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-intent-configuration/index.html). The `IntentConfiguration` contains details about the specific `PaymentIntent`, such as the amount and currency. ```kotlin class MyCheckoutActivity : AppCompatActivity() { // ... private fun handleCheckoutButtonPressed() {val intentConfig = PaymentSheet.IntentConfiguration(mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "usd",),// Other configuration options... ) paymentSheet.presentWithIntentConfiguration( intentConfiguration = intentConfig, // Optional configuration - See the "Customize the sheet" section in this guide configuration = PaymentSheet.Configuration.Builder( merchantDisplayName = "Example Inc.", ).build() ) } } ``` ### Confirm the Intent When your customer taps the **Pay** button in PaymentSheet, it calls the `CreateIntentCallback` you passed above with a [ConfirmationToken](https://stripe.dev/stripe-android/payments-core/com.stripe.android.model/-confirmation-token/index.html) that represents your customer’s payment details and preferences. Implement this method to send a request to your server with `confirmationToken.id`. Your server creates a PaymentIntent and returns its client secret. When you receive the response, return the response’s client secret or an error. The PaymentSheet confirms the PaymentIntent using the client secret, or displays the error in its UI. ```kotlin class MyCheckoutActivity : AppCompatActivity() { private lateinit var paymentSheet: PaymentSheet override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... paymentSheet = PaymentSheet.Builder(::onPaymentSheetResult) .createIntentCallback { confirmationToken ->// Make a request to your server to create aPaymentIntentand return its client secret val networkResult = myNetworkClient.createIntent( confirmationTokenId = confirmationToken.id, ) if (networkResult.isSuccess) { CreateIntentResult.Success(networkResult.clientSecret) } else { CreateIntentResult.Failure(networkResult.exception) } } .build(this) } fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) { // You'll implement this later } } ``` After your customer completes payment, the sheet closes, and PaymentSheet invokes the [PaymentSheetResultCallback](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet-result-callback/index.html) you provide with a [PaymentSheetResult](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet-result/index.html). ```kotlin class MyCheckoutActivity : AppCompatActivity() { // ... fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) {when(paymentSheetResult) { is PaymentSheetResult.Canceled -> { // Customer canceled - you should probably do nothing. } is PaymentSheetResult.Failed -> { print("Error: ${paymentSheetResult.error}") // PaymentSheet encountered an unrecoverable error. You can display the error to the user, log it, and so on } is PaymentSheetResult.Completed -> { // Display, for example, an order confirmation screen print("Completed") } } } } ``` #### Jetpack Compose ### Initialize the PaymentSheet Initialize the PaymentSheet using `remember` and pass in `PaymentSheet.Builder`. Leave the implementation of `resultCallback` and `createIntentCallback` empty for now. ```kotlin import androidx.compose.runtime.* import com.stripe.android.paymentsheet.PaymentSheet import com.stripe.android.paymentsheet.rememberPaymentSheet @Composable fun CheckoutScreen() { val paymentSheet = remember { Builder( resultCallback = { paymentSheetResult -> // You'll implement this later } ).createIntentCallback { confirmationToken -> // You'll implement this later } }.build() } ``` ### Present the PaymentSheet Next, present the PaymentSheet by calling `presentWithIntentConfiguration()` and pass an [IntentConfiguration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-intent-configuration/index.html). The `IntentConfiguration` contains details about the specific `PaymentIntent`, such as the amount and currency. ```kotlin @Composable fun CheckoutScreen() { val paymentSheet = remember { ... }.build() Button( onClick = { val intentConfig = PaymentSheet.IntentConfiguration( mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "usd", ), ) } ) { Text("Checkout") } } ``` ### Confirm the Intent When your customer taps the **Pay** button in the PaymentSheet, it calls `createIntentCallback` with a [ConfirmationToken](https://stripe.dev/stripe-android/payments-core/com.stripe.android.model/-confirmation-token/index.html) object that represents the customer’s payment details and preferences. Implement this callback to send a request to your server with `confirmationToken.id`. Your server creates a PaymentIntent and returns its client secret. When you receive the response, return the client secret or an error. The PaymentSheet confirms the PaymentIntent using the client secret, or displays the error in its UI. ```kotlin @Composable fun CheckoutScreen() { val paymentSheet = remember { Builder( resultCallback = { paymentSheetResult -> // You'll implement this later } ).createIntentCallback { confirmationToken -> // Make a request to your server to create a PaymentIntent and return its client secret try { val response = myNetworkClient.createIntent( confirmationTokenId = confirmationToken.id, ) CreateIntentResult.Success(response.clientSecret) } catch (e: Exception) { CreateIntentResult.Failure( cause = e, displayMessage = e.message ) } } }.build() Button( onClick = { val intentConfig = PaymentSheet.IntentConfiguration( mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "usd", ), ) paymentSheet.presentWithIntentConfiguration( intentConfiguration = intentConfig, configuration = PaymentSheet.Configuration.Builder( merchantDisplayName = "Example Inc.", ).build() ) } ) { Text("Checkout") } } ``` After your customer completes payment, the sheet closes, and the callback you pass to `PaymentSheet.Builder.resultCallback` is invoked with a [PaymentSheetResult](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet-result/index.html). ```kotlin @Composable fun CheckoutScreen() { val paymentSheet = remember { Builder( resultCallback = { paymentSheetResult -> when(paymentSheetResult) { is PaymentSheetResult.Canceled -> { // Customer canceled - you should probably do nothing. } is PaymentSheetResult.Failed -> { println("Error: ${paymentSheetResult.error}") // PaymentSheet encountered an unrecoverable error. You can display the error to the user, log it, and so on } is PaymentSheetResult.Completed -> { // Display, for example, an order confirmation screen println("Completed") } } } ).createIntentCallback { confirmationToken -> ... // previously implemented confirm intent code } }.build() ... // other code } ``` #### PaymentSheet.FlowController This integration assumes your checkout screen has two buttons: a **Payment Method** button that presents the PaymentSheet to collect payment details, and a **Buy** button that completes the payment. #### Views (Classic) ### Initialize the PaymentSheet Initialize the `PaymentSheet.FlowController` and pass in a `CreateIntentCallback`. Leave the implementations empty for now. ```kotlin class MyCheckoutActivity : AppCompatActivity() { private lateinit var flowController: PaymentSheet.FlowController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... flowController = PaymentSheet.FlowController.Builder( resultCallback = ::onPaymentSheetResult, paymentOptionCallback = ::onPaymentOption ) .createIntentCallback { confirmationToken -> TODO() // You'll implement this later } .build(this) } fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) { // Explained later } fun onPaymentOption(paymentOption: PaymentOption?) { // Explained later } } ``` After your checkout screen loads, configure the `PaymentSheet.FlowController` with an [IntentConfiguration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-intent-configuration/index.html). The `IntentConfiguration` contains details about the specific `PaymentIntent`, such as the amount and currency. ```kotlin fun handleCheckoutLoaded(cartTotal: Long, currency: String) {flowController.configureWithIntentConfiguration( intentConfiguration = PaymentSheet.IntentConfiguration(mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = cartTotal, currency = currency,),), // Optional configuration - See the "Customize the sheet" section in this guide configuration = PaymentSheet.Configuration.Builder( merchantDisplayName = "Example Inc.", ).build(), callback = { success, error -> // If success, the FlowController was initialized correctly. // Use flowController.getPaymentOption() to populate your payment // method button. }, ) } ``` When `PaymentSheet.FlowController` completes configuration, it invokes your callback. Then, you can populate your **Payment Method** button with `flowController.getPaymentOption()`, which includes an image and a label that represent your customer’s initial payment method selection. ### Present the PaymentSheet When your customer taps your **Payment Method** button, call `presentPaymentOptions()` to collect payment details. Then, update your UI using the `paymentOption` property. ```kotlin // ... flowController.presentPaymentOptions() // ... fun onPaymentOption(paymentOption: PaymentOption?) { if (paymentOption != null) { paymentMethodButton.text = paymentOption.label paymentMethodButton.setCompoundDrawablesRelativeWithIntrinsicBounds( paymentOption.drawableResourceId, 0, 0, 0 ) } else { paymentMethodButton.text = "Select" paymentMethodButton.setCompoundDrawablesRelativeWithIntrinsicBounds( null, null, null, null ) } } ``` ### Update payment details If the customer changes the payment details (for example, by applying a discount code or editing their cart), update the `PaymentSheet.FlowController` instance to reflect the new values by calling `configureWithIntentConfiguration()` again to reflect the new values. This keeps the values shown in the UI in sync. > Some payment methods, such as Google Pay, show the amount in the UI. If the customer changes the payment and you don’t update the `EmbeddedPaymentElement`, the UI displays incorrect values. During configuration, don’t call `presentPaymentOptions()` or `confirm()` on `PaymentSheet.FlowController`. Disable your **Buy** and **Payment method** buttons, then enable them when your `ConfigCallback` runs. If the update succeeds, use `flowController.getPaymentOption()` to update your UI because the customer’s previously selected payment method might be unavailable. If the update fails, retry it. ```kotlin fun handleCartChanged( newCartTotal: Long, currency: String, ) { // Disable your "Buy" and "Payment method" buttons paymentMethodSelectionButton.isEnabled = false payButton.isEnabled = false // Update FlowController by configuring it again flowController.configureWithIntentConfiguration( intentConfiguration = PaymentSheet.IntentConfiguration( mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = newCartTotal, currency = currency ), ), configuration = PaymentSheet.Configuration.Builder( merchantDisplayName = "Example Inc.", ).build(), callback = { success, error -> // If success, the FlowController was updated correctly if (success) { paymentMethodSelectionButton.isEnabled = true val canPay = flowController.getPaymentOption() != null payButton.isEnabled = canPay } else { // You must retry - until the update succeeds, the customer can't pay or select a payment method. // For example, you can automatically retry the update with an exponential back-off, or present the user with an alert that retries the update. } }, ) } ``` ### Confirm the Intent When your customer taps your **Buy** button, call `confirm()` to call the `CreateIntentCallback` you pass with a [ConfirmationToken](https://stripe.dev/stripe-android/payments-core/com.stripe.android.model/-confirmation-token/index.html) object that represents your customer’s payment details and preferences. Implement this method to send a request to your server with `confirmationToken.id`. Your server creates a PaymentIntent and returns its client secret. When you receive the response, return the client secret or an error. PaymentSheet confirms the PaymentIntent using the client secret, or displays the error in the UI. ```kotlin class MyCheckoutActivity : AppCompatActivity() { private lateinit var flowController: PaymentSheet.FlowController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // … flowController = PaymentSheet.FlowController.Builder( resultCallback = ::onPaymentSheetResult, paymentOptionCallback = ::onPaymentOption ) .createIntentCallback { confirmationToken ->// Make a request to your server to create aPaymentIntentand return its client secret try { val response = myNetworkClient.createIntent( confirmationTokenId = confirmationToken.id, // only required for server-side confirmation ) CreateIntentResult.Success(response.clientSecret) } catch (e: Exception) { CreateIntentResult.Failure( cause = e, displayMessage = e.message ) } } .build(this) } } ``` After your customer completes the payment, the sheet closes, and the [PaymentSheetResultCallback](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet-result-callback/index.html) is invoked with a [PaymentSheetResult](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet-result/index.html). ```kotlin class MyCheckoutActivity : AppCompatActivity() { // ... fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) {when(paymentSheetResult) { is PaymentSheetResult.Canceled -> { // Customer canceled - you should probably do nothing. } is PaymentSheetResult.Failed -> { print("Error: ${paymentSheetResult.error}") // PaymentSheet encountered an unrecoverable error. You can display the error to the user, log it, and so on } is PaymentSheetResult.Completed -> { // Display, for example, an order confirmation screen print("Completed") } } } } ``` The following step explains the server code. #### Jetpack Compose ### Initialize the PaymentSheet.FlowController Initialize the `PaymentSheet.FlowController` and pass in callbacks to `PaymentSheet.FlowController.Builder`. Leave the implementations empty for now. ```kotlin import androidx.compose.runtime.* import com.stripe.android.paymentsheet.PaymentSheet import com.stripe.android.paymentsheet.PaymentSheet.FlowController.Builder import com.stripe.android.paymentsheet.PaymentSheetResult import com.stripe.android.paymentsheet.model.PaymentOption private fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) { // You'll implement this later } private fun onPaymentOption(paymentOption: PaymentOption?) { // You'll implement this later } @Composable fun CheckoutScreen() { val flowController = remember{ Builder( resultCallback = ::onPaymentSheetResult, paymentOptionCallback = ::onPaymentOption, ).createIntentCallback { confirmationToken -> // You'll implement this later } }.build() } ``` After your checkout screen loads, configure the `PaymentSheet.FlowController` instance with an [IntentConfiguration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-intent-configuration/index.html). `IntentConfiguration` contains details about the specific `PaymentIntent`, such as the amount and currency. ```kotlin @Composable fun CheckoutScreen() { val flowController = remember{ ... }.build() LaunchedEffect(Unit) { flowController.configureWithIntentConfiguration( intentConfiguration = PaymentSheet.IntentConfiguration( mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "usd", ), ), configuration = PaymentSheet.Configuration.Builder( merchantDisplayName = "Example Inc.", ).build(), callback = { success, error -> // If success, the FlowController was initialized correctly. // Use flowController.getPaymentOption() to populate your payment // method button. }, ) } } ``` When the `PaymentSheet.FlowController` instance completes configuration, it calls `paymentOptionCallback`. At that point, `paymentOption` contains an image and a label that represent the customer’s initial payment method selection. ### Present the PaymentSheet When a customer taps the **Payment Method** button, call `presentPaymentOptions()` to collect payment details. After it completes, `paymentOptionCallback` updates `paymentOption`. Then, update your UI. ```kotlin ... flowController.presentPaymentOptions() ... fun onPaymentOption(paymentOption: PaymentOption?) { if (paymentOption != null) { paymentMethodButton.text = paymentOption.label paymentMethodButton.setCompoundDrawablesRelativeWithIntrinsicBounds( paymentOption.drawableResourceId, 0, 0, 0 ) } else { paymentMethodButton.text = "Select" paymentMethodButton.setCompoundDrawablesRelativeWithIntrinsicBounds( null, null, null, null ) } } ``` ### Update payment details If the customer changes the payment details (for example, by applying a discount code or editing their cart), update the `PaymentSheet.FlowController` instance to reflect the new values by calling `configureWithIntentConfiguration()` again to reflect the new values. This keeps the values shown in the UI in sync. > Some payment methods, such as Google Pay, show the amount in the UI. If the customer changes the payment and you don’t update the `PaymentSheet.FlowController`, the UI displays incorrect values. During configuration, don’t call `presentPaymentOptions()` or `confirm()` on the `PaymentSheet.FlowController`. If the update succeeds, `paymentOptionCallback` runs with the updated payment option. The selection might change if the customer’s previously selected payment method is no longer available. If the update fails, retry it. ```kotlin fun updateCart( flowController: PaymentSheet.FlowController, newAmount: Long, onComplete: (Boolean) -> Unit ) { flowController.configureWithIntentConfiguration( intentConfiguration = PaymentSheet.IntentConfiguration( mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = newAmount, currency = "usd" ), ), configuration = PaymentSheet.Configuration.Builder( merchantDisplayName = "Example Inc.", ).build(), callback = { success, error -> // If success, the FlowController was updated correctly if (success) { paymentMethodSelectionButton.isEnabled = true val canPay = flowController.getPaymentOption() != null payButton.isEnabled = canPay } else { // You must retry - until the update succeeds, the customer can't pay or select a payment method. // For example, you can automatically retry the update with an exponential back-off, or present the user with an alert that retries the update. } }, ) } ``` ### Confirm the Intent When your customer taps your **Buy** button, call `confirm()`. The PaymentSheet calls the `createIntentCallback` you pass with a [ConfirmationToken](https://stripe.dev/stripe-android/payments-core/com.stripe.android.model/-confirmation-token/index.html) that represents your customer’s payment details and preferences. ```kotlin @Composable fun CheckoutScreen() { val flowController = remember{ Builder( resultCallback = ::onPaymentSheetResult, paymentOptionCallback = ::onPaymentOption, ).createIntentCallback { confirmationToken -> // Make a request to your server to create a PaymentIntent and return its client secret try { val response = myNetworkClient.createIntent( confirmationTokenId = confirmationToken.id, ) CreateIntentResult.Success(response.clientSecret) } catch (e: Exception) { CreateIntentResult.Failure( cause = e, displayMessage = e.message ) } } }.build() // Previous flowcontroller configuration code Column { // Payment method button... Button( onClick = { flowController.confirm() }, enabled = paymentOption != null ) { Text("Buy") } } } ``` After your customer completes the payment, the sheet closes, and `PaymentSheet.FlowController` invokes the callback you pass to `PaymentSheet.FlowController.Builder.resultCallback` with a [PaymentSheetResult](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet-result/index.html). ```kotlin private fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) { when(paymentSheetResult) { is PaymentSheetResult.Canceled -> { // Customer canceled - you should probably do nothing. } is PaymentSheetResult.Failed -> { println("Error: ${(paymentSheetResult as PaymentSheetResult.Failed).error}") // PaymentSheet encountered an unrecoverable error. You can display the error to the user, log it, and so on } is PaymentSheetResult.Completed -> { // Display, for example, an order confirmation screen println("Completed") } null -> { // No result yet } } } ``` The following step explains the server code. ## 创建 PaymentIntent [服务器端] On your server, create a *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) with an amount and currency. You can manage payment methods from the [Dashboard](https://dashboard.stripe.com/settings/payment_methods). Stripe handles the return of eligible payment methods based on factors such as the transaction’s amount, currency, and payment flow. To prevent malicious customers from choosing their own prices, always decide how much to charge on the server-side (a trusted environment) and not the client. If the call succeeds, return the PaymentIntent *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)). If the call fails, [handle the error](https://docs.stripe.com/error-handling.md) and return an error message with a brief explanation for your customer. > Verify that all IntentConfiguration properties match your PaymentIntent (for example, `setup_future_usage`, `amount`, and `currency`). #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', automatic_payment_methods: {enabled: true}, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` ## 处理付款后事件 [服务器端] 付款完成时,Stripe 会发送一个 [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) 事件。使用 [管理平台 Webhook 工具](https://dashboard.stripe.com/webhooks)、或按照 [Webhook 指南](https://docs.stripe.com/webhooks/quickstart.md)来接收这些事件并运行操作,例如,向客户发送订单确认邮件、在数据库中记录订单,或启动配送流程。 侦听这些事件,而不是等待客户端回调。在客户端,客户可能会在执行回调之前关闭浏览器窗口或退出应用程序,并且恶意客户端可能会操纵响应。设置您的集成来侦听异步事件,这样才能用单一集成用用接受[不同类型的支付方式](https://stripe.com/payments/payment-methods-guide)。 除了处理 `payment_intent.succeeded` 事件外,建议在使用 Payment Element 收款时也处理其他的这些事件: | 事件 | 描述 | 操作 | | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.succeeded) | 客户成功完成付款时发送。 | 向客户发送订单确认通知,并*履行* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected)他们的订单。 | | [payment_intent.processing](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.processing) | 当客户成功发起付款但并未完成时发送。当客户发起银行借记时,通常会发送此事件。之后将会出现 `payment_intent.succeeded` 或 `payment_intent.payment_failed` 事件。 | 向客户发送订单确认,告知他们的付款正等待处理。对于数字商品,您可能想先履行订单,然后再等付款完成。 | | [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.payment_failed) | 在客户尝试付款但付款失败时发送。 | 如果一笔付款从 `processing` 变为 `payment_failed`,则让客户再尝试一次。 | ## 测试集成 #### 银行卡 | 卡号 | 场景 | 如何测试 | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | | 4242424242424242 | 该卡付款成功,不需要验证。 | 使用信用卡号以及有效期和 CVC 和邮编填写我们的信用卡表单。 | | 4000002500003155 | 该卡付款时需要*验证* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase)。 | 使用信用卡号以及有效期和 CVC 和邮编填写我们的信用卡表单。 | | 4000000000009995 | 该卡被拒绝,显示拒付代码,例如 `insufficient_funds`。 | 使用信用卡号以及有效期和 CVC 和邮编填写我们的信用卡表单。 | | 6205500000000000004 | 银联卡的长度为 13-19 位。 | 使用信用卡号以及有效期和 CVC 和邮编填写我们的信用卡表单。 | #### 银行重定向 | 支付方式 | 场景 | 如何测试 | | ----------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | Bancontact, iDEAL | 您的客户在基于重定向和立即通知型的支付方式的重定向页面验证失败。 | 选择基于重定向的任意支付方式,填写必要的信息,并确认付款。然后在重定向页面上点击**让测试付款失败**。 | | 通过银行支付 | 您的客户通过基于重定向和[延迟通知型](https://docs.stripe.com/payments/payment-methods.md#payment-notification)的支付方式成功付款。 | 选择支付方式,填写必要的信息,并确认付款。然后在重定向页面上点击**测试付款成功**。 | | 通过银行支付 | 您的客户在基于重定向和延迟通知型的支付方式的重定向页面验证失败。 | 选择支付方式,填写必要的信息,并确认付款。然后在重定向页面上点击**让测试付款失败**。 | | BLIK | 有多个 BLIK 支付失败的情况——立即失败(例如,代码已过期或无效)、延迟的错误(银行拒付)或超时(客户未及时响应)。 | 使用电子邮件模式[模拟不同的失败情况](https://docs.stripe.com/payments/blik/accept-a-payment.md#simulate-failures)。 | #### 银行借记 | 支付方式 | 场景 | 如何测试 | | --------- | ------------------------------------------------------- | --------------------------------------------------------------------- | | SEPA 直接借记 | 您的客户成功用 SEPA 直接借记完成付款。 | 用账号 `AT321904300235473204` 填写表单。确认的 PaymentIntent 最初变为处理中,三分钟后变为成功状态。 | | SEPA 直接借记 | 您的客户的付款意图状态从 `processing` 变为 `requires_payment_method`。 | 用账号 `AT861904300235473202` 填写表单。 | 有关测试您的集成的更多信息,请参阅[测试](https://docs.stripe.com/testing.md)部分。 ## Optional: Enable saved cards [服务器端] [客户端] `PaymentSheet` can allow the customer to save their card and can include the customer’s saved cards in available payment methods. The customer must have an associated customer-configured [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) object or a [Customer](https://docs.stripe.com/api/customers/create.md) object on your server. To enable a checkbox that allows the customer to save their card, create a [CustomerSession](https://docs.stripe.com/api/customer_sessions.md), with `payment_method_save` set to `enabled`. #### Accounts v2 ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require("stripe")("<>"); const express = require('express'); const app = express(); app.set('trust proxy', true); app.use(express.json()); app.post('/payment-sheet', async (req, res) => { // Use an existing Account ID if this is a returning customer. const customer_account = await stripe.v2.core.accounts.create(); const customerSession = await stripe.customerSessions.create({ customer_account: customer_account.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer_account: customer_account.id, }); }); ``` Next, present `PaymentSheet` with the customer’s ID and the `CustomerSession` client secret. ```kotlin val configuration = PaymentSheet.Configuration.Builder(merchantDisplayName = "Powdur") .customer( PaymentSheet.CustomerConfiguration.createWithCustomerSession( id = customerAccountId, clientSecret = customerSessionClientSecret, ) ) .build() paymentSheet.presentWithIntentConfiguration( intentConfiguration = // ... , configuration = configuration, ) ``` #### Customers v1 ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require("stripe")("<>"); const express = require('express'); const app = express(); app.set('trust proxy', true); app.use(express.json()); app.post('/payment-sheet', async (req, res) => { // Use an existing Customer ID if this is a returning customer. const customer = await stripe.customers.create(); const customerSession = await stripe.customerSessions.create({ customer: customer.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer: customer.id, }); }); ``` Next, present `PaymentSheet` with the customer’s ID and the `CustomerSession` client secret. ```kotlin val configuration = PaymentSheet.Configuration.Builder(merchantDisplayName = "Powdur") .customer( PaymentSheet.CustomerConfiguration.createWithCustomerSession( id = customerId, clientSecret = customerSessionClientSecret, ) ) .build() paymentSheet.presentWithIntentConfiguration( intentConfiguration = // ... , configuration = configuration, ) ``` ## Optional: Allow delayed payment methods [客户端] *Delayed payment methods* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods) don’t guarantee that you’ll receive funds from your customer at the end of checkout, either because they take time to settle (for example, US Bank Accounts, SEPA Debit, iDEAL, Bancontact, and Sofort) or because they require customer action to complete (for example, OXXO, Konbini, and Boleto). By default, `PaymentSheet` doesn’t display delayed payment methods. To include the delayed payment methods that `PaymentSheet` supports, set `allowsDelayedPaymentMethods` to true in your `PaymentSheet.Configuration`. ```kotlin val configuration = PaymentSheet.Configuration.Builder(merchantDisplayName = "Powdur") .allowsDelayedPaymentMethods(true) .build() ``` If the customer successfully uses a delayed payment method in a `PaymentSheet`, the payment result returned is `PaymentSheetResult.Completed`. ## Optional: 启用 Google Pay > If your checkout screen has a dedicated **Google Pay** button, follow the [Google Pay guide](https://docs.stripe.com/google-pay.md?platform=android). You can use Embedded Payment Element to handle other payment method types. ### 设置您的集成 要使用 Google Pay,先将以下内容添加到您的 **AndroidManifest.xml** 的 ``标签,来启用 Google Pay API: ```xml ... ``` 更多详情,请参见 Google Pay 针对 Android 设备编写的[设置 Google Pay API](https://developers.google.com/pay/api/android/guides/setup) 指南。 ### 添加 Google Pay 要向您的集成添加 Google Pay,初始化 [PaymentSheet.Configuration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-configuration/index.html) 时,传递您的 Google Pay 环境(生产或测试)下的 [PaymentSheet.GooglePayConfiguration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-google-pay-configuration/index.html) 和[贵公司的国家/地区代码](https://dashboard.stripe.com/settings/account)。 #### Kotlin ```kotlin val googlePayConfiguration = PaymentSheet.GooglePayConfiguration( environment = PaymentSheet.GooglePayConfiguration.Environment.Test, countryCode = "US", currencyCode = "USD" // Required for Setup Intents, optional for Payment Intents ) val configuration = PaymentSheet.Configuration.Builder(merchantDisplayName = "My merchant name") .googlePay(googlePayConfiguration) .build() ``` ### 测试 Google Pay Google 允许您通过其[测试卡套件](https://developers.google.com/pay/api/android/guides/resources/test-card-suite)进行测试付款。该测试套件支持使用 Stripe [测试卡](https://docs.stripe.com/testing.md)。 您必须在支持 Google Pay 的国家/地区使用 Android 实体设备(而不是模拟设备)测试 Google Pay。使用保存到 Google 钱包的真实银行卡登录测试设备上的 Google 账户。 ## Optional: 启用银行卡扫描 若要启用银行卡扫描支持,需在 [Google Pay & Wallet Console](https://pay.google.com/business/console?utm_source=devsite&utm_medium=devsite&utm_campaign=devsite) 中请求 Google Pay API 的[生产访问权限](https://developers.google.com/pay/api/android/guides/test-and-deploy/request-prod-access)。 - 如果您已启用 Google Pay,银行卡扫描功能会在符合条件的设备上自动在我们的用户界面中提供。想了解更多符合条件的设备,请参见[Google Pay API 限制](https://developers.google.com/pay/payment-card-recognition/debit-credit-card-recognition) - **重要提示:**银行卡扫描功能仅在与 [Google Pay & Wallet Console](https://pay.google.com/business/console) 中注册的签名密钥相同的构建中显示。使用不同签名密钥进行测试或调试的构建(例如通过Firebase App Tester 分发的构建)不会显示**扫描银行卡**选项。要在预发布构建中测试银行卡扫描功能,您必须采取以下措施之一: - 使用生产签名密钥对您的测试构建进行签名 - 将您的测试签名密钥指纹添加到 Google Pay & Wallet Console 如果您的应用不支持 Google Pay,您可以使用 Stripe 卡片扫描功能。 > Stripe 卡片扫描功能目前处于公开预览阶段。 为了启用卡片扫描功能,请将 `stripecardscan` 添加到您的 [app/build.gradle](https://developer.android.com/studio/build/dependencies) 文件的 `dependencies` 代码块中: #### Groovy ```groovy implementation 'com.stripe:stripecardscan:23.11.1' ``` ## Optional: 自定义表单 所有自定义操作均用 [PaymentSheet.Configuration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-configuration/index.html) 对象来配置。 ### 外观 使用 [Appearance API](https://docs.stripe.com/elements/appearance-api/mobile.md?platform=android) 自定义颜色、字体等,使其与应用程序的视觉风格相匹配。 ### 支付方式布局 使用 [paymentMethodLayout](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-configuration/-builder/index.html#2123253356%2FFunctions%2F2002900378) 配置表单中支付方式的布局。可以水平、垂直显示,也可以让 Stripe 自动优化布局。 ![](https://b.stripecdn.com/docs-statics-srv/assets/android-mpe-payment-method-layouts.3bcfe828ceaad1a94e0572a22d91733f.png) #### Kotlin ```kotlin PaymentSheet.Configuration.Builder("Example, Inc.") .paymentMethodLayout(PaymentSheet.PaymentMethodLayout.Automatic) .build() ``` ### 收集用户地址 用 [Address Element](https://docs.stripe.com/elements/address-element.md?platform=android) 收集客户的本地和国际收货地址或账单地址。 ### 商家显示名称 通过设置 [merchantDisplayName](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-configuration/index.html#-191101533%2FProperties%2F2002900378) 来指定一个向客户显示的商家名称。默认情况下,使用的是您的应用的名称。 #### Kotlin ```kotlin PaymentSheet.Configuration.Builder( merchantDisplayName = "My app, Inc." ).build() ``` ### 暗色模式 默认情况下,`PaymentSheet` 自动适应用户的整个系统的外观设置(明暗模式)。可通过在您的应用上设置亮色或暗色模式来更改: #### Kotlin ```kotlin // force dark AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) // force light AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) ``` ### 默认账单详情 要为支付表单中收集的账单详情设置默认值,请配置 `defaultBillingDetails` 属性。`paymentSheet` 会用您提供的值预先填充其字段。 #### Kotlin ```kotlin val address = PaymentSheet.Address(country = "US") val billingDetails = PaymentSheet.BillingDetails( address = address, email = "foo@bar.com" ) val configuration = PaymentSheet.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .defaultBillingDetails(billingDetails) .build() ``` ### 配置账单详情的收集 使用 `BillingDetailsCollectionConfiguration` 指定您希望如何在 PaymentSheet 中收集账单详情。 可以收集客户的姓名、邮件地址、电话号码和地址。 如果希望将默认账单详情关联到 PaymentMethod 对象(即使 UI 中未收集这些字段),请将 `billingDetailsCollectionConfiguration.attachDefaultsToPaymentMethod` 设置为 `true`。 #### Kotlin ```kotlin val billingDetails = PaymentSheet.BillingDetails( email = "foo@bar.com" ) val billingDetailsCollectionConfiguration = BillingDetailsCollectionConfiguration( attachDefaultsToPaymentMethod = true, name = BillingDetailsCollectionConfiguration.CollectionMode.Always, email = BillingDetailsCollectionConfiguration.CollectionMode.Never, address = BillingDetailsCollectionConfiguration.AddressCollectionMode.Full, ) val configuration = PaymentSheet.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .defaultBillingDetails(billingDetails) .billingDetailsCollectionConfiguration(billingDetailsCollectionConfiguration) .build() ``` > 请咨询律师,了解与收集信息有关的法律。仅在需要收集号码来完成交易时,才收集手机号码。 ## Optional: Enable CVC recollection on confirmation 要在确认 PaymentIntent 时重新收集已保存银行卡的安全码 (CVC),您的集成必须在创建 PaymentIntent 之前收集支付信息。 ### 更新意图配置 `PaymentSheet.IntentConfiguration` 接受控制是否重新收集已保存卡的 CVC 的可选参数。 ```kotlin val intentConfig = PaymentSheet.IntentConfiguration( mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "usd", ),requireCvcRecollection = true, ) ``` ### 更新意图创建参数 要在确认付款时重新收集 CVC,请在创建 PaymentIntent 的过程中包含 `customerId` 和 `require_cvc_recollection` 这两个参数。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. # Find your keys at https://dashboard.stripe.com/apikeys. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', automatic_payment_methods: {enabled: true},customer: customer.id, payment_method_options: { card: {require_cvc_recollection: true} }, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ```