# 当客户用某种支付方式进行付款时，保存该支付方式

了解如何在确认 PaymentIntent 时保存客户的付款方式。

# Checkout Sessions API

> This is a Checkout Sessions API for when payment-ui is embedded-components. View the full page at https://docs.stripe.com/payments/save-during-payment?payment-ui=embedded-components.

Use the [Checkout Sessions API](https://docs.stripe.com/api/checkout/sessions.md) to save payment details during a purchase. This is useful for situations such as:

- Charging a customer for an e-commerce order and storing the payment details for future purchases.
- Initiating the first payment in a series of recurring payments.
- Charging a deposit and storing the payment details to charge the full amount later.

## Compliance

在保存客户的支付详情以备将来使用时，您有责任遵守所有适用的法律、法规和网络规则，比如在结账流程中向客户显示其支付方式以便未来购买，或在客户未主动使用您的网站或应用时向他们收费。在保存或向客户的支付方式收费之前，请确保您：

- 在您的网站或应用中添加说明您计划如何保存支付方式详情的条款，例如：
  - 客户协议允许您代表他们针对指定交易发起支付或一系列支付。
  - 预期的付款时间和频率（例如，收款是计划的分期付款、订阅付款还是计划外充值）。
  - 如何确定付款金额。
  - 如果支付方式是用于支付订阅服务，那即同意您的取消政策。
- 仅出于条款中所述的目的，使用保存的支付方式。
- 为此特定用途收集客户的明确同意。例如，可以添加一个“保存我的支付方式以备将来使用”的复选框。
- 记录客户对您的条款的书面同意。

> When using Elements with the Checkout Sessions API, only cards and ACH Direct Debit are supported for saved payment methods. You can’t save other payment methods, such as bank accounts.

## Prerequisites

1. Follow the Checkout guide to [accept a payment](https://docs.stripe.com/payments/accept-a-payment.md?payment-ui=checkout&ui=stripe-hosted).
1. 按照本指南操作，以保存支付过程中使用的支付方式，以便在相同客户进行未来支付时使用。

## Enable saved payment methods

> 全球隐私法律复杂且微妙。在实现存储客户支付方式详情的功能之前，请与法律团队共同确保其符合您的隐私和合规框架。

如果允许客户保存其支付方式以供将来使用，请在创建 Checkout Session 时指定 [saved_payment_method_options.payment_method_save](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-saved_payment_method_options-payment_method_save) 参数。

> #### 使用 Accounts v2 API 表示客户
> 
> Accounts v2 API 通常面向 Connect 用户开放，并对其他 Stripe 用户开放公开预览。如果您参与了 Accounts v2 预览，您需要在代码中[指定预览版本](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning)。
> 
> 如需申请 Accounts v2 预览版的访问权限，
> 
> 在大多数应用场景下，我们建议[将您的客户建模为客户配置的 Account 对象](https://docs.stripe.com/connect/use-accounts-as-customers.md)，而不是使用 [Customer](https://docs.stripe.com/api/customers.md) 对象。

Saving a payment method requires an object that represents your customer. This object can be a customer-configured [Account](https://docs.stripe.com/api/v2/core/accounts/object.md) if you use the Accounts v2 API, or a [Customer](https://docs.stripe.com/api/customers/object.md) if you use the Customers API. Pass an existing customer or create a new one by setting [customer_creation](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-customer_creation) to `always` on the Checkout Session.

```curl
curl https://api.stripe.com/v1/checkout/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "line_items[0][price]={{PRICE_ID}}" \
  -d "line_items[0][quantity]=2" \
  -d mode=payment \
  -d ui_mode=elements \
  -d customer_creation=always \
  -d "saved_payment_method_options[payment_method_save]=enabled"
```

创建 Checkout Session 后，使用响应中返回的[客户端私钥](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-client_secret)来[构建您的结账页面](https://docs.stripe.com/payments/quickstart-checkout-sessions.md)。

> In the latest version of Stripe.js, specifying `enableSave` to `auto` is optional because that’s the default value when saved payment methods are enabled on the Checkout Session.

#### HTML + JS

The Payment Element automatically displays a consent collection checkbox when saved payment methods are enabled on the Checkout Session. You can explicitly configure this behavior using [elementsOptions](https://docs.stripe.com/js/custom_checkout/init#custom_checkout_init-options-elementsOptions) on `initCheckoutElementsSdk`.

```javascript
const checkout = stripe.initCheckoutElementsSdk({
  clientSecret,
  elementsOptions: {savedPaymentMethod: {
      // Default is 'auto' in the latest version of Stripe.js - this configuration is optional
      enableSave: 'auto',
    }
  }
});
```

#### React

The Payment Element automatically displays a consent collection checkbox when saved payment methods are enabled on the Checkout Session. You can explicitly configure this behavior using [elementsOptions](https://docs.stripe.com/js/react_stripe_js/checkout/checkout_provider#react_checkout_provider-options-elementsOptions) on the `CheckoutElementsProvider`.

```jsx
import React from 'react';
import {CheckoutElementsProvider} from '@stripe/react-stripe-js/checkout';
import CheckoutForm from './CheckoutForm';

const App = () => {
  const clientSecret = fetch('/create-checkout-session', {method: 'POST'})
    .then((response) => response.json())
    .then((json) => json.checkoutSessionClientSecret);

  return (
    <CheckoutElementsProvider
      stripe={stripe}
      options={{
        clientSecret,
        elementsOptions: {savedPaymentMethod: {
            // Default is 'auto' in the latest version of Stripe.js - this configuration is optional
            enableSave: 'auto',
          },
        },
      }}
    >
      <CheckoutForm />
    </CheckoutElementsProvider>
  );
};

export default App;
```

## 重复使用之前保存的支付方式

Each saved payment method is linked to an object that represents your customer. This object can be a customer-configured [Account](https://docs.stripe.com/api/v2/core/accounts/object.md) if you use the Accounts v2 API, or a [Customer](https://docs.stripe.com/api/customers/object.md) if you use the Customers API. Before creating the Checkout Session, authenticate your customer, and pass the corresponding object ID to the Checkout Session.

> In the latest version of Stripe.js, `enableRedisplay` defaults to `auto` when saved payment methods are enabled on the Checkout Session.

The Payment Element automatically redisplays previously saved payment methods for your customer to use during checkout when saved payment methods are enabled on the Checkout Session.

#### Accounts v2

```curl
curl https://api.stripe.com/v1/checkout/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "line_items[0][price]={{PRICE_ID}}" \
  -d "line_items[0][quantity]=2" \
  -d mode=payment \
  -d ui_mode=elements \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}"
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/checkout/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "line_items[0][price]={{PRICE_ID}}" \
  -d "line_items[0][quantity]=2" \
  -d mode=payment \
  -d ui_mode=elements \
  -d "customer={{CUSTOMER_ID}}"
```

#### HTML + JS

You can explicitly configure the redisplay behavior using [elementsOptions](https://docs.stripe.com/js/custom_checkout/init#custom_checkout_init-options-elementsOptions) on `initCheckoutElementsSdk`.

```javascript
const checkout = stripe.initCheckoutElementsSdk({
  clientSecret,
  elementsOptions: {
    savedPaymentMethod: {
      // Default is 'auto' in the latest version of Stripe.js - this configuration is optional
      enableSave: 'auto',// Default is 'auto' in the latest version of Stripe.js - this configuration is optional
      enableRedisplay: 'auto',
    }
  }
});
```

#### React

You can explicitly configure the redisplay behavior using [elementsOptions](https://docs.stripe.com/js/react_stripe_js/checkout/checkout_provider#react_checkout_provider-options-elementsOptions) on the `CheckoutElementsProvider`.

```jsx
import React from 'react';
import {CheckoutElementsProvider} from '@stripe/react-stripe-js/checkout';
import CheckoutForm from './CheckoutForm';

const App = () => {
  const clientSecret = fetch('/create-checkout-session', {method: 'POST'})
    .then((response) => response.json())
    .then((json) => json.checkoutSessionClientSecret)

  return (
    <CheckoutElementsProvider
      stripe={stripe}
      options={{
        clientSecret,
        elementsOptions: {
          savedPaymentMethod: {
            // Default is 'auto' in the latest version of Stripe.js - this configuration is optional
            enableSave: 'auto',// Default is 'auto' in the latest version of Stripe.js - this configuration is optional
            enableRedisplay: 'auto',
          }
        },
      }}
    >
      <CheckoutForm />
    </CheckoutElementsProvider>
  );
};

export default App;
```

## Optional: Build a saved payment method UI

#### HTML + JS

You can build your own saved payment method UI instead of using the built-in UI provided by the Payment Element.

To prevent the Payment Element from handling consent collection and displaying the previously saved payment methods, pass in additional [elementsOptions](https://docs.stripe.com/js/custom_checkout/init#custom_checkout_init-options-elementsOptions) on `initCheckoutElementsSdk`.

```javascript
const checkout = stripe.initCheckoutElementsSdk({
  clientSecret,
  elementsOptions: {savedPaymentMethod: {
      enableSave: 'never',
      enableRedisplay: 'never',
    },
  }
});
```

#### React

You can build your own saved payment method UI instead of using the built-in UI provided by the Payment Element. To prevent the Payment Element from handling consent collection and displaying the previously saved payment methods, pass in additional [elementsOptions](https://docs.stripe.com/js/react_stripe_js/checkout/checkout_provider#react_checkout_provider-options-elementsOptions) on the `CheckoutElementsProvider`.

```jsx
import React from 'react';
import {CheckoutElementsProvider} from '@stripe/react-stripe-js/checkout';
import CheckoutForm from './CheckoutForm';

const App = () => {
  const clientSecret = fetch('/create-checkout-session', {method: 'POST'})
    .then((response) => response.json())
    .then((json) => json.checkoutSessionClientSecret)

  return (
    <CheckoutElementsProvider
      stripe={stripe}
      options={{
        clientSecret,
        elementsOptions: {savedPaymentMethod: {
            enableSave: 'never',
            enableRedisplay: 'never',
          },
        },
      }}
    >
      <CheckoutForm />
    </CheckoutElementsProvider>
  );
};

export default App;
```

### Collect consent

> 全球隐私法律复杂且微妙。在实现存储客户支付方式详情的功能之前，请与法律团队共同确保其符合您的隐私和合规框架。

In most cases, you must collect a customer’s consent before you save their payment method details. The following example shows how to obtain consent using a checkbox.

#### HTML + JS

```html
<label>
  <input type="checkbox" id="save-payment-method-checkbox" />
  Save my payment information for future purchases
</label>
<button id="pay-button">Pay</button>
<div id="confirm-errors"></div>
```

#### React

```jsx
import React from 'react';

type Props = {
  savePaymentMethod: boolean;
  onSavePaymentMethodChange: (save: boolean) => void;
}
const ConsentCollection = (props: Props) => {
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    props.onSavePaymentMethodChange(e.target.checked);
  };
  return (
    <label>
      <input
        type="checkbox"
        checked={props.savePaymentMethod}
        onChange={handleChange}
      />
      Save my payment information for future purchases
    </label>
  );
};

export default ConsentCollection;
```

When you call [confirm](https://docs.stripe.com/js/custom_checkout/confirm), you can indicate to Stripe that your customer has provided consent by passing the `savePaymentMethod` parameter. When you save a customer’s payment details, you’re responsible for complying with all applicable laws, regulations, and network rules.

#### HTML + JS

```js
const checkout = stripe.initCheckoutElementsSdk({clientSecret});
const button = document.getElementById('pay-button');
const errors = document.getElementById('confirm-errors');
const checkbox = document.getElementById('save-payment-method-checkbox');
const loadActionsResult = await checkout.loadActions();
if (loadActionsResult.type === 'success') {
  const {actions} = loadActionsResult;
  button.addEventListener('click', () => {
    // Clear any validation errors
    errors.textContent = '';
const savePaymentMethod = checkbox.checked;
    actions.confirm({savePaymentMethod}).then((result) => {
      if (result.type === 'error') {
        errors.textContent = result.error.message;
      }
    });
  });
}
```

#### React

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

type Props = {
  savePaymentMethod: boolean;
}
const PayButton = (props: Props) => {
  const checkoutState = useCheckout();
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState(null);

  if (checkoutState.type === 'loading') {
    return (
      <div>Loading...</div>
    );
  } else if (checkoutState.type === 'error') {
    return (
      <div>Error: {checkoutState.error.message}</div>
    );
  }
  const {confirm} = checkoutState.checkout;
  const handleClick = () => {
    setLoading(true);confirm({savePaymentMethod: props.savePaymentMethod}).then((result) => {
      if (result.type === 'error') {
        setError(result.error)
      }
      setLoading(false);
    })
  };

  return (
    <div>
      <button disabled={!loading} onClick={handleClick}>
        Pay
      </button>
      {error && <div>{error.message}</div>}
    </div>
  )
};

export default PayButton;
```

### Render saved payment methods

Use the [savedPaymentMethods](https://docs.stripe.com/js/custom_checkout/session_object#custom_checkout_session_object-savedPaymentMethods) array on the front end to render the customer’s available payment methods.

> The `savedPaymentMethods` array includes only the payment methods that have [allow_redisplay](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-allow_redisplay) set to `always`. Follow the steps for [collecting consent](https://docs.stripe.com/payments/save-during-payment.md#collect-consent) from your customer and make sure to properly set the `allow_redisplay` parameter.

#### HTML + JS

```html
<div id="saved-payment-methods"></div>
```

```js
const checkout = stripe.initCheckoutElementsSdk({clientSecret});
const loadActionsResult = await checkout.loadActions();
if (loadActionsResult.type === 'success') {
  const container = document.getElementById('saved-payment-methods');
  const {actions} = loadActionsResult;
  actions.getSession().savedPaymentMethods.forEach((pm) => {
    const label = document.createElement('label');
    const radio = document.createElement('input');

    radio.type = 'radio';
    radio.value = pm.id;

    label.appendChild(radio);
    label.appendChild(document.createTextNode(`Card ending in ${pm.card.last4}`));
    container.appendChild(label);
  });
}
```

#### React

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

type Props = {
  selectedPaymentMethod: string | null;
  onSelectPaymentMethod: (paymentMethod: string) => void;
};
const PaymentMethods = (props: Props) => {const checkoutState = useCheckout();

  if (checkoutState.type === 'loading') {
    return (
      <div>Loading...</div>
    );
  } else if (checkoutState.type === 'error') {
    return (
      <div>Error: {checkoutState.error.message}</div>
    );
  }const {savedPaymentMethods} = checkoutState.checkout;

  const handleOptionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    props.onSelectPaymentMethod(e.target.value);
  };

  return (
    <div>
      {savedPaymentMethods.map((pm) => (
        <label key={pm.id}>
          <input
            type="radio"
            value={pm.id}
            checked={props.selectedPaymentMethod === pm.id}
            onChange={handleOptionChange}
          />
          Card ending in {pm.card.last4}
        </label>
      ))}
    </div>
  );
};

export default PaymentMethods;
```

### Confirm with a saved payment method

When your customer selects a saved payment method and is ready to complete checkout, call [confirm](https://docs.stripe.com/js/custom_checkout/confirm) and pass in the [paymentMethod](https://docs.stripe.com/js/custom_checkout/confirm#custom_checkout_session_confirm-options-paymentMethod) ID.

#### HTML + JS

```html
<button id="pay-button">Pay</button>
```

```js
const checkout = stripe.initCheckoutElementsSdk({clientSecret});

const loadActionsResult = await checkout.loadActions();
if (loadActionsResult.type === 'success') {
  const container = document.getElementById('saved-payment-methods');
  const {actions} = loadActionsResult;
  actions.getSession().savedPaymentMethods.forEach((pm) => {
    const label = document.createElement('label');
    const radio = document.createElement('input');

    radio.type = 'radio';
    radio.value = pm.id;

    label.appendChild(radio);
    label.appendChild(document.createTextNode(`Card ending in ${pm.card.last4}`));
    container.appendChild(label);
  });
}
```

#### React

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

type Props = {
  selectedPaymentMethod: string | null;
}
const PayButton = (props: Props) => {
  const checkoutState = useCheckout();
  const [loading, setLoading] = React.useState(false);

  if (checkoutState.type === 'loading') {
    return (
      <div>Loading...</div>
    );
  } else if (checkoutState.type === 'error') {
    return (
      <div>Error: {checkoutState.error.message}</div>
    );
  }
  const {confirm} = checkoutState.checkout;

  const handleClick = () => {
    setLoading(true);confirm({paymentMethod: props.selectedPaymentMethod}).then((result) => {
      if (result.error) {
        // Confirmation failed. Display the error message.
      }
      setLoading(false);
    })
  };

  return (
    <button disabled={loading} onClick={handleClick}>
      Pay
    </button>
  )
};

export default PayButton;
```


# Payment Intents API

> This is a Payment Intents API for when payment-ui is elements. View the full page at https://docs.stripe.com/payments/save-during-payment?payment-ui=elements.

用 [Payment Intents API](https://docs.stripe.com/api/payment_intents.md) 保存购物时输入的付款详情。有几个用例：

- 对客户的网上订单扣款并存储支付信息，用于以后的购物。
- 发起一系列经常性付款中的第一笔付款。
- 收取押金并存储详情，便于以后收取完整金额。

> #### 有卡交易
> 
> 有卡交易（例如通过 Stripe Terminal 支付）使用不同的流程来保存支付方式。有关详情，请查看 [Terminal 文档](https://docs.stripe.com/terminal/features/saving-payment-details/save-after-payment.md)。

## 合规

在保存客户的支付详情以备将来使用时，您有责任遵守所有适用的法律、法规和网络规则，比如在结账流程中向客户显示其支付方式以便未来购买，或在客户未主动使用您的网站或应用时向他们收费。在保存或向客户的支付方式收费之前，请确保您：

- 在您的网站或应用中添加说明您计划如何保存支付方式详情的条款，例如：
  - 客户协议允许您代表他们针对指定交易发起支付或一系列支付。
  - 预期的付款时间和频率（例如，收款是计划的分期付款、订阅付款还是计划外充值）。
  - 如何确定付款金额。
  - 如果支付方式是用于支付订阅服务，那即同意您的取消政策。
- 仅出于条款中所述的目的，使用保存的支付方式。
- 为此特定用途收集客户的明确同意。例如，可以添加一个“保存我的支付方式以备将来使用”的复选框。
- 记录客户对您的条款的书面同意。

## Prerequisites

1. Follow the Payment Intents API guide to [accept a payment](https://docs.stripe.com/payments/accept-a-payment.md?payment-ui=elements&api-integration=paymentintents).
1. 按照本指南操作，以保存支付过程中使用的支付方式，以便在相同客户进行未来支付时使用。

## Save payment methods during payment [服务器端]

您可以配置 Payment Element 来保存客户的支付方式以备将来使用。本部分向您展示如何集成[保存的支付方式功能](https://docs.stripe.com/payments/save-customer-payment-methods.md)，从而让 Payment Element：

- 提示买家同意保存支付方式
- 买家同意时，存储支付方式
- 向买家显示已存储的支付方式，以方便日后购物使用
- 买家更换时[自动更新丢失或过期的银行卡](https://docs.stripe.com/payments/cards/overview.md#automatic-card-updates)
![Payment Element 和保存的支付方式复选框](https://b.stripecdn.com/docs-statics-srv/assets/spm-save.fe0b24afd0f0a06e0cf4eecb0ce2403a.png)

保存支付方式
![已选择保存了支付方式的 Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/spm-saved.5dba5a8a190a9a0e9f1a99271bed3f4b.png)

复用以前保存的支付方式。

### 允许在 Payment Element 中保存支付方式

在服务端创建 [PaymentIntent](https://docs.stripe.com/api/payment_intents/.md) 时，需同步创建 [ CustomerSession ](https://docs.stripe.com/api/customer_sessions/.md)：提供客户 ID（`Customer` 对象使用 `customer`，客户配置型 `Account` 对象使用 `customer_account`），并为会话启用 [payment_element](https://docs.stripe.com/api/customer_sessions/object.md#customer_session_object-components-payment_element) 组件。配置需要启用的已保存支付方式[功能](https://docs.stripe.com/api/customer_sessions/create.md#create_customer_session-components-payment_element-features)。例如，启用 [payment_method_save](https://docs.stripe.com/api/customer_sessions/create.md#create_customer_session-components-payment_element-features-payment_method_save) 后，页面将展示勾选框，允许客户保存支付信息，用于后续付款。

您可在 PaymentIntent 或 Checkout Session 中指定 `setup_future_usage`，以此覆盖保存支付方式的默认行为。这一配置会自动保存支付方式供后续使用，即便客户未主动选择保存。若您已指定 `setup_future_usage`，请勿在同一笔支付交易中同时配置 `payment_method_save_usage`，否则会引发集成错误。

> #### 使用 Accounts v2 API 表示客户
> 
> Accounts v2 API 通常面向 Connect 用户开放，并对其他 Stripe 用户开放公开预览。如果您参与了 Accounts v2 预览，您需要在代码中[指定预览版本](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning)。
> 
> 如需申请 Accounts v2 预览版的访问权限，
> 
> 在大多数应用场景下，我们建议[将您的客户建模为客户配置的 Account 对象](https://docs.stripe.com/connect/use-accounts-as-customers.md)，而不是使用 [Customer](https://docs.stripe.com/api/customers.md) 对象。

#### Accounts v2

#### Ruby

```ruby

# 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('<<YOUR_SECRET_KEY>>')

post '/create-intent-and-customer-session' do
  intent = client.v1.payment_intents.create({
    amount: 1099,
    currency: 'usd',
    automatic_payment_methods: {enabled: true},
    customer_account: {{CUSTOMER_ACCOUNT_ID}},
  })
  customer_session = client.v1.customer_sessions.create({
    customer_account: {{CUSTOMER_ACCOUNT_ID}},
    components: {
      payment_element: {
          enabled: true,
          features: {
            payment_method_redisplay: 'enabled',
            payment_method_save: 'enabled',
            payment_method_save_usage: 'off_session',
            payment_method_remove: 'enabled',
          },
        },
    },
  })
  {
    client_secret: intent.client_secret,
    customer_session_client_secret: customer_session.client_secret
  }.to_json
end
```

#### Customers v1

#### Ruby

```ruby

# 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('<<YOUR_SECRET_KEY>>')

post '/create-intent-and-customer-session' do
  intent = client.v1.payment_intents.create({
    amount: 1099,
    currency: 'usd',
    # In the latest version of the API, specifying the `automatic_payment_methods` parameter
    # is optional because Stripe enables its functionality by default.
    automatic_payment_methods: {enabled: true},
    customer: {{CUSTOMER_ID}},
  })
  customer_session = client.v1.customer_sessions.create({
    customer: {{CUSTOMER_ID}},
    components: {
      payment_element: {
          enabled: true,
          features: {
            payment_method_redisplay: 'enabled',
            payment_method_save: 'enabled',
            payment_method_save_usage: 'off_session',
            payment_method_remove: 'enabled',
          },
        },
    },
  })
  {
    client_secret: intent.client_secret,
    customer_session_client_secret: customer_session.client_secret
  }.to_json
end
```

您的 Element 实例使用 CustomerSession 的*客户端私钥* (A client secret is used with your publishable key to authenticate a request for a single object. Each client secret is unique to the object it's associated with)来访问该客户保存的支付方式。创建 CustomerSession 时正确[处理错误](https://docs.stripe.com/error-handling.md)。如果发生错误，您不需要向 Element 实例提供 CustomerSession 客户端私钥，因为它是可选的。

使用PaymentIntent 和 CustomerSession 的客户端私钥创建 Element 实例。然后，用这个 Element 实例创建一个 Payment Element。

```javascript
// Create the CustomerSession and obtain its clientSecret
const res = await fetch("/create-intent-and-customer-session", {
  method: "POST"
});

const {
  customer_session_client_secret: customerSessionClientSecret
} = await res.json();

const elementsOptions = {
  clientSecret: '{{CLIENT_SECRET}}',customerSessionClientSecret,
  // Fully customizable with appearance API.
  appearance: {/*...*/},
};

// Set up Stripe.js and Elements to use in checkout form, passing the client secret
// and CustomerSession's client secret obtained in a previous step
const elements = stripe.elements(elementsOptions);

// Create and mount the Payment Element
const paymentElementOptions = { layout: 'accordion'};
const paymentElement = elements.create('payment', paymentElementOptions);
paymentElement.mount('#payment-element');
```

确认 PaymentIntent 时，Stripe.js 会自动控制 PaymentIntent 上的 [setup_future_usage](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-setup_future_usage) 设置和 PaymentMethod 上的 [allow_redisplay](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-allow_redisplay)，具体取决于客户是否勾选保存其付款详情的框。

### 强制重新收集卡安全码 (CVC)

可以选择在 [when creating the PaymentIntent](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-card-require_cvc_recollection) 指定 `require_cvc_recollection`，以在客户用银行卡支付时强制重新收集 CVC。

### 检测选择的已保存支付方式

要在选择了保存的支付方式时控制动态内容，请侦听 Payment Element `change` 事件（该事件会填充上所选的支付方式）。

```javascript
paymentElement.on('change', function(event) {
  if (event.value.payment_method) {
    // Control dynamic content if a saved payment method is selected
  }
})
```

## Save only payment methods that support reuse [服务器端]

You can’t make all payment methods [reusable](https://docs.stripe.com/payments/accept-a-payment.md#save-payment-method-details) by enabling `setup_future_usage` in the Payment Intent. See [Payment method support](https://docs.stripe.com/payments/payment-methods/payment-method-support.md#additional-api-supportability) to learn more about which payment methods are compatible with `setup_future_usage`.

Instead, you can save payment method details only when a customer selects a payment method that supports it. For example, if you accept both card payments and Giropay (which you can’t reuse), configure `setup_future_usage=off_session` on the `payment_method_options[card]` object.

如果客户拒绝保存他们的付款详情，则请在服务器端的 PaymentIntent 中禁用 `setup_future_usage`。我们不支持在客户端进行这种调整。

#### 从管理平台管理支付方式

您可以从[管理平台](https://dashboard.stripe.com/settings/payment_methods)管理支付方式。Stripe 根据交易金额、货币和支付流程等因素处理符合条件的支付方式的退货。

#### curl

```bash
curl https://api.stripe.com/v1/payment_intents \
  -u <<YOUR_SECRET_KEY>>: \
  -d "amount"=1099 \
  -d "currency"="usd" \
  -d "automatic_payment_methods[enabled]"="true" \
  -d "payment_method_options[card][setup_future_usage]"="off_session"
```

#### 手动列出支付方式

也可以如以下示例所示，用[支付方式类型](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_types) 列出 `card` 和 `giropay`。

#### curl

```bash
curl https://api.stripe.com/v1/payment_intents \
  -u <<YOUR_SECRET_KEY>>: \
  -d "amount"=1099 \
  -d "currency"="usd" \
  -d "payment_method_types[]"="card" \
  -d "payment_method_types[]"="giropay" \
  -d "payment_method_options[card][setup_future_usage]"="off_session"
```

## 以后对保存的支付方式扣款 [服务器端]

> `bancontact`、`ideal` 和 `sofort` 是默认的一次性支付方式。设置为将来使用时，它们会生成一个 `sepa_debit` 可重复使用的支付方式类型，因此您可以用 `sepa_debit` 来查询保存的支付方式。

> #### 合规
> 
> 保存客户的支付详情时，您有责任遵守所有适用的法律、法规和卡组织规则。向您的最终客户呈现之前用过的支付方式以供未来购物使用时，确保您所列出的支付方式已从客户那里收集保存支付方式详情以供将来具体使用的同意书。对于绑定到客户的支付方式，要区分哪些可以哪些不可以作为保存的支付方式供未来购物使用，请使用 [allow_redisplay](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-allow_redisplay) 参数。

要查找用于收款的支付方式，请列出与客户关联的支付方式。本示例列出了卡片，但您可以列出任何支持的[类型](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-type)。

> #### 使用 Accounts v2 API 表示客户
> 
> Accounts v2 API 通常面向 Connect 用户开放，并对其他 Stripe 用户开放公开预览。如果您参与了 Accounts v2 预览，您需要在代码中[指定预览版本](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning)。
> 
> 如需申请 Accounts v2 预览版的访问权限，
> 
> 在大多数应用场景下，我们建议[将您的客户建模为客户配置的 Account 对象](https://docs.stripe.com/connect/use-accounts-as-customers.md)，而不是使用 [Customer](https://docs.stripe.com/api/customers.md) 对象。

#### Accounts v2

```curl
curl -G https://api.stripe.com/v1/payment_methods \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d type=card
```

#### Customers v1

```curl
curl -G https://api.stripe.com/v1/payment_methods \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d type=card
```

当您准备向客户发起*会话外* (A payment is described as off-session if it occurs without the direct involvement of the customer, using previously-collected payment information)收款时，请使用客户 ID 与 `PaymentMethod` ID，并传入付款金额和币种参数创建 `PaymentIntent`；同时配置其余相关参数，即可完成会话外收款：

- 将 [off_session](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-off_session) 设置为 `true`，表示客户在尝试支付期间不在您的结账流程中，无法履行由合作伙伴（如发卡行、银行或其他支付机构）提出的身份验证请求。如果在您的结账流程中，合作伙伴请求身份验证，Stripe 会使用之前*会话内* (A payment is described as on-session if it occurs while the customer is actively in your checkout flow and able to authenticate the payment method)交易中的客户信息请求豁免。如果不符合豁免条件，`PaymentIntent` 可能会抛出错误。
- 将 `PaymentIntent` 的 [confirm](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-confirm) 属性值设置为 true，这会导致在创建 `PaymentIntent` 时立即进行确认。
- 将 [payment_method](https://docs.stripe.com/api.md#create_payment_intent-payment_method) 设置为 `PaymentMethod` 的 ID。
- 根据您在集成中如何代表客户，请将 [customer_account](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer_account) 设为客户配置型 `Account` 的 ID，或将 [customer](https://docs.stripe.com/api.md#create_payment_intent-customer) 设为 `Customer` 的 ID。

#### 账户 v2

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d "automatic_payment_methods[enabled]=true" \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d payment_method={{PAYMENT_METHOD_ID}} \
  --data-urlencode "return_url=https://example.com/order/123/complete" \
  -d off_session=true \
  -d confirm=true
```

#### 客户 v1

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d "automatic_payment_methods[enabled]=true" \
  -d "customer={{CUSTOMER_ID}}" \
  -d payment_method={{PAYMENT_METHOD_ID}} \
  --data-urlencode "return_url=https://example.com/order/123/complete" \
  -d off_session=true \
  -d confirm=true
```

## 测试集成

用测试付款详情和测试重定向页面验证您的集成。点击下方选项卡，查看每个支付方式的详情。

#### 银行卡

| 支付方式 | 场景                                                                                                                                                                                                                                                                 | 如何测试                                                           |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
| 信用卡  | 银行卡设置成功，不要求*验证* (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)。 | 填写我们的信用卡表单，卡号是 `4242 4242 4242 4242`，有效期、CVC（银行卡安全码）以及邮编可任意填写。 |
| 信用卡  | 该卡在初始设置时需要身份验证，在后续付款时会直接成功。                                                                                                                                                                                                                                        | 填写我们的信用卡表单，卡号是 `4000 0025 0000 3155`，有效期、CVC（银行卡安全码）以及邮编可任意填写。 |
| 信用卡  | 该卡在初始设置时需要身份验证，在后续付款时也需要身份验证。                                                                                                                                                                                                                                      | 填写我们的信用卡表单，卡号是 `4000 0027 6000 3184`，有效期、CVC（银行卡安全码）以及邮编可任意填写。 |
| 信用卡  | 设置过程中卡被拒绝了。                                                                                                                                                                                                                                                        | 填写我们的信用卡表单，卡号是 `4000 0000 0000 9995`，有效期、CVC（银行卡安全码）以及邮编可任意填写。 |

#### 银行重定向

| 支付方式       | 场景                                                                                       | 如何测试                                                                                |
| ---------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Bancontact | 您的客户用 Bancontact 成功设置了一个 SEPA 直接借记支付方式，供未来使用。                                            | 在 Bancontact 表单中使用一个任意的名称，然后在重定向页面上点击**授权付款设置**。                                    |
| Bancontact | 您的客户未在 Bancontact 重定向页面进行验证。                                                             | 在 Bancontact 表单中使用一个任意的名称，然后在重定向页面上点击**让测试设置失败**。                                   |
| BECS 直接借记  | 您的客户成功用 BECS 直接借记完成付款。                                                                   | 用账号 `900123456` 填写表单。确认的 PaymentIntent 最初变为 `processing`，然后 3 分钟后变为 `succeeded` 状态。 |
| BECS 直接借记  | 您的客户付款失败，错误代码为：`account_closed`。                                                         | 用账号 `111111113` 填写表单。                                                               |
| iDEAL      | 您的客户用 iDEAL 成功设置了 [SEPA 直接借记](https://docs.stripe.com/payments/sepa-debit.md)支付方式，供未来使用。 | 在 iDEAL 表单中使用任意的名称和银行，然后在重定向页面上点击**授权测试设置**。                                        |
| iDEAL      | 您的客户未在 iDEAL 重定向页面进行验证。                                                                  | 选择银行，并在 iDEAL 表单中使用一个任意的名称，然后在重定向页面上点击**让测试设置失败**。                                  |

#### 银行借记

| 支付方式      | 场景                                                      | 如何测试                                                                  |
| --------- | ------------------------------------------------------- | --------------------------------------------------------------------- |
| SEPA 直接借记 | 您的客户成功用 SEPA 直接借记完成付款。                                  | 用账号 `AT321904300235473204` 填写表单。确认的 PaymentIntent 最初变为处理中，三分钟后变为成功状态。 |
| SEPA 直接借记 | 您的客户的付款意图状态从 `processing` 变为 `requires_payment_method`。 | 用账号 `AT861904300235473202` 填写表单。                                      |

### 测试对保存的 SEPA 借记 PaymentMethod 的收款

用 iDEAL、Bancontact 或 Sofort 确认 PaymentIntent ，生成 [SEPA 直接借记](https://docs.stripe.com/payments/sepa-debit.md) *PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs)。SEPA 直接借记是一种[延迟通知型](https://docs.stripe.com/payments/payment-methods.md#payment-notification)支付方式，它几天后会变为 `succeeded` 或 `requires_payment_method` 状态，在此之前会变为一个中间 `processing` 状态。

#### 邮箱

将 `payment_method.billing_details.email` 设置为以下值之一，以测试 `PaymentIntent` 的状态转换。您可以在电子邮箱地址开头添加自定义文本，后面跟一个下划线。例如，使用 `test_1_generatedSepaDebitIntentsFail@example.com` 将会生成一个在用于 `PaymentIntent` 时始终失败的 SEPA 直接借记 PaymentMethod。

| 邮件地址                                                               | 描述                                                                                             |
| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
| `generatedSepaDebitIntentsSucceed@example.com`                     | `PaymentIntent` 的状态从 `processing` 转换为 `succeeded`。                                             |
| `generatedSepaDebitIntentsSucceedDelayed@example.com`              | 至少三分钟后，`PaymentIntent` 的状态从 `processing` 转换为 `succeeded`。                                      |
| `generatedSepaDebitIntentsFail@example.com`                        | `PaymentIntent` 的状态从 `processing` 转换为 `requires_payment_method`。                               |
| `generatedSepaDebitIntentsFailDelayed@example.com`                 | 至少三分钟后，`PaymentIntent` 的状态从 `processing` 转换为 `requires_payment_method`。                        |
| `generatedSepaDebitIntentsSucceedDisputed@example.com`             | `PaymentIntent` 的状态从 `processing` 转换为 `succeeded`，但会立即产生争议。                                    |
| `generatedSepaDebitIntentsFailsDueToInsufficientFunds@example.com` | `PaymentIntent` 的状态从 `processing` 转换为 `requires_payment_method`，并带有 `insufficient_funds` 失败代码。 |

#### PaymentMethod

使用这些 PaymentMethod 来测试 `PaymentIntent` 的状态转换。这些令牌对于自动化测试非常有用，可立即在服务器上将 PaymentMethod 附加到SetupIntent。

| 支付方式                                                                 | 描述                                                                                             |
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `pm_bancontact_generatedSepaDebitIntentsSucceed`                     | `PaymentIntent` 的状态从 `processing` 转换为 `succeeded`。                                             |
| `pm_bancontact_generatedSepaDebitIntentsSucceedDelayed`              | 至少三分钟后，`PaymentIntent` 的状态从 `processing` 转换为 `succeeded`。                                      |
| `pm_bancontact_generatedSepaDebitIntentsFail`                        | `PaymentIntent` 的状态从 `processing` 转换为 `requires_payment_method`。                               |
| `pm_bancontact_generatedSepaDebitIntentsFailDelayed`                 | 至少三分钟后，`PaymentIntent` 的状态从 `processing` 转换为 `requires_payment_method`。                        |
| `pm_bancontact_generatedSepaDebitIntentsSucceedDisputed`             | `PaymentIntent` 的状态从 `processing` 转换为 `succeeded`，但会立即产生争议。                                    |
| `pm_bancontact_generatedSepaDebitIntentsFailsDueToInsufficientFunds` | `PaymentIntent` 的状态从 `processing` 转换为 `requires_payment_method`，并带有 `insufficient_funds` 失败代码。 |

## See also

- [在应用内付款期间保存付款详情](https://docs.stripe.com/payments/mobile/save-during-payment.md)
- [在 Checkout 会话中保存付款详情](https://docs.stripe.com/payments/checkout/how-checkout-works.md#save-payment-methods)
- [设置未来付款](https://docs.stripe.com/payments/save-and-reuse.md)
- [Handle post-payment events](https://docs.stripe.com/webhooks/handling-payment-events.md)

