# 不进行银行验证即保存银行卡

收集客户的银行卡详情，之后再对客户扣款。

Stripe 允许先收集客户的银行卡详情，之后再对客户扣款。在某些地区，银行会要求第二重验证，例如输入发送到手机的验证码。如果您的客户并未主动使用您的网站或应用，会因无法进行验证而放弃购买，则此额外的步骤就会降低转化。

​​如果您的业务主要在美国和加拿大，这里的银行不要求验证，因此您可以使用这个简易集成。在对银行卡保存操作要求进行身份验证的国家（如印度），这种集成是不兼容的，构建这个集成就意味着向其他国家扩展或添加其他支付方式时会面临很大的困难。了解如何[保存要求验证的银行卡](https://docs.stripe.com/payments/save-and-reuse.md)。

> #### 合规
> 
> 保存客户的支付详情时，您有责任遵守所有适用的法律、法规和卡组织规则。比如，您想保存他们的支付方式以供将来使用，例如在他们不经常使用您的网站或应用程序的情况下对其收费。在您的网站或应用程序中添加条款，说明您打算如何保存支付方式详情，并允许客户选择加入。如果您想在他们不在线时向他们收费，请确保您的条款中包括以下内容：
> 
> - 客户同意您代其对指定的交易发起一次或一系列付款。
- 预期的付款时间和频率（例如，收款是计划的分期付款、订阅付款还是计划外充值）。
- 如何确定付款金额。
- 如果支付方式是用于支付订阅服务，那即同意您的取消政策。
> 
> 请务必让客户书面同意这些条款并做好记录。

## 收集银行卡详情 [客户端]

开始本指南之前，您需要先建立一个 Stripe 账户。[立即注册](https://dashboard.stripe.com/register)。

构建一个结账页面，收集您客户的银行卡详情。用一个 UI 库 [Stripe Elements](https://docs.stripe.com/payments/elements.md) 帮助构建您的自定义支付表单。要开始使用 Element，请将 Stripe.js 库连同以下脚本包含到您的结账页面上。

```html
<script src="https://js.stripe.com/dahlia/stripe.js"></script>
```

为保持 PCI 合规，始终从 js.stripe.com 加载 Stripe.js。不要把脚本打包或自行保留副本。

要充分利用 Stripe [高级欺诈功能](https://docs.stripe.com/radar.md)，请在您网站的每一页都包含这个脚本，而不是只在结账页面。每一页都包含脚本[可让 Stripe 检测可疑行为](https://docs.stripe.com/disputes/prevention/advanced-fraud-detection.md)，这种可疑行为可能表明用户浏览您的网站时存在欺诈问题。

### 将 Elements 添加到您的页面

为了让您安全地从客户那里收集银行卡详情，Elements 为您创建了一个由 Stripe 托管的 UI 组件。然后它们被置入您的支付表单，而非由您直接创建。要确定在哪里插入这些组件，用您的支付表单中的唯一 ID 创建一个空的 DOM Elements（容器）。

```html
<input id="cardholder-name" type="text">
<!-- placeholder for Elements -->
<div id="card-element"></div>
<div id="card-result"></div>
<button id="card-button">Save Card</button>
```

接下来，创建一个 [Stripe 对象](https://docs.stripe.com/js.md#stripe-function)实例，提供您的 [API 公钥](https://docs.stripe.com/keys.md)作为第一个参数。之后，创建一个 [Elements 对象](https://docs.stripe.com/js.md#stripe-elements)实例，并用其在 DOM 中装载一个 `card` 元素。

`card` Element 通过插入一个可安全地收集所有必要的银行卡和账单信息的单一灵活的输入字段，对支付表单进行了简化，并且将需要的字段数降到了最低。

否则，对 `cardNumber`、`cardExpiry`、和 `cardCvc` Elements 加以组合，获取灵活的多重输入银行卡表单。

> 始终要收集邮编，以提高卡的接受率并减少欺诈。
> 
> 此[单行 Card Element](https://docs.stripe.com/js/element/other_element?type=card) 会自动收集邮编并将其发给 Stripe。如果您用分割的 Elements（[卡号](https://docs.stripe.com/js/element/other_element?type=cardNumber)、[有效期](https://docs.stripe.com/js/element/other_element?type=cardExpiry)、[CVC](https://docs.stripe.com/js/element/other_element?type=cardCvc)）创建支付表单，那么为客户的邮编添加一个单独的输入字段。

```javascript
const stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>');

const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
```

Stripe Element 中包含一个 iframe，它通过一个 HTTPS 连接安全地将支付信息发送到 Stripe。结账页面上的地址也必须以 `https://` 开头，不能是 `http://`，否则您的集成不能工作。

您可以在不使用 HTTPS 的情况下测试您的集成。在准备好进行真实收款时，将它[启用](https://docs.stripe.com/security/guide.md#tls)。

```javascript
const cardholderName = document.getElementById('cardholder-name');
const cardButton = document.getElementById('card-button');
const resultContainer = document.getElementById('card-result');

cardButton.addEventListener('click', async (ev) => {
  const {paymentMethod, error} = await stripe.createPaymentMethod({
      type: 'card',
      card: cardElement,
      billing_details: {
        name: cardholderName.value,
      },
    }
  );

  if (error) {
    // Display error.message in your UI.
    resultContainer.textContent = error.message;
  } else {
    // You have successfully created a new PaymentMethod
    resultContainer.textContent = "Created payment method: " + paymentMethod.id;
  }
});
```

将产生的 *PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) ID 发送到您的服务器。

## 设置 Stripe [服务器端]

用我们的官方库从您的应用程序访问 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'
```

## 保存卡 [服务器端]

通过将 PaymentMethod 绑定到 *客户* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments)}，即可保存银行卡。您可以使用 `Customer` 对象存储客户的其他信息，例如配送详情和邮箱地址。

```curl
curl https://api.stripe.com/v1/customers \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d payment_method={{PAYMENT_METHOD_ID}}
```

如果您当前有 Customer，可改为将 PaymentMethod 绑定到那个对象。

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

这时，将 Customer ID 和 PaymentMethod ID 关联到您自己对此客户使用的内部名称（如果有）。

## 对保存的卡扣款 [服务器端]

准备好后，可获取 PaymentMethod 和客户 ID 进行收款。您可以通过将两者的 ID 存储在数据库中，或者使用客户 ID 查询所有客户可用的 PaymentMethod，来完成此操作。

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

使用 PaymentMethod ID 和客户 ID 创建一个新的 PaymentIntent。将 [error_on_requires_action](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-error_on_requires_action)设置为 `true`，以拒绝需要客户采取任何行动的支付，比如双重验证。

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d "customer={{CUSTOMER_ID}}" \
  -d payment_method={{PAYMENT_METHOD_ID}} \
  -d error_on_requires_action=true \
  -d confirm=true \
  --data-urlencode "return_url=https://example.com/return"
```

付款尝试失败时，该请求也会失败，并显示 402 HTTP 状态码，同时 Stripe 会抛出一个错误。您需要通知客户返回到您的应用（例如，发送邮件）来完成付款。请查看 Stripe API 库引发的 [错误](https://docs.stripe.com/api/errors/handling.md) 的代码，或者查看 PaymentIntent 上的 [last_payment_error.decline_code](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-last_payment_error-decline_code) 来了解发卡行拒绝付款的原因。

## 处理任意银行卡错误

通知客户支付失败，并引导他们到您在第 1 步中填写的支付表单，在那里他们可以输入新的卡信息。将新的 PaymentMethod ID 发送到您的服务器，以[绑定](https://docs.stripe.com/api/payment_methods/attach.md)到客户对象并重新发起支付。

或者，如果您已经创建了客户，也可以创建一个 PaymentIntent，并在一次 API 调用中保存一张银行卡。

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1099 \
  -d currency=usd \
  -d "customer={{CUSTOMER_ID}}" \
  -d payment_method={{PAYMENT_METHOD_ID}} \
  -d error_on_requires_action=true \
  -d confirm=true \
  -d setup_future_usage=on_session \
  --data-urlencode "return_url=https://example.com/return"
```

将 [setup_future_usage](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-setup_future_usage) 设置为 `on_session`，即向 Stripe 表明您希望保存该银行卡以便后续使用，且不会触发不必要的身份验证。

## 测试集成

Stripe 提供[测试卡](https://docs.stripe.com/testing.md)，您可以在沙盒中使用，以模拟不同银行卡的行为。未来可将这些卡与任何 CVC、邮政编码和有效期一起使用。

| 卡号 | 描述 |
| --- | --- |
| 4242424242424242 | 成功并且立即处理付款。 |
| 4000000000009995 | 始终会失败，显示拒付码 `insufficient_funds`。 |
| 4000002500003155 | 需要进行身份验证，在此集成中身份验证会拒绝，并返回代码 `authentication_required`。 |

## Optional: 重新收集 CVC

对保存的银行卡创建后续付款时，您可能希望重新收集银行卡的 CVC，以此作为验证用户的额外欺诈防范措施。

首先，在您的服务器上创建一个包含支付金额和币种的 PaymentIntent，并将 [Customer](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer) 设置为您客户的 ID。然后，[列出](https://docs.stripe.com/api/payment_methods/list.md)与您客户关联的 PaymentMethods，以确定向您的用户显示哪些 PaymentMethods 用于重新收集银行卡安全码 (CVC)。

将 PaymentIntent 的客户端密钥传递到浏览器后，即可在您的客户端用 Stripe Element 重新收集 CVC 信息。用 `cardCvc` Element 向您的用户重新收集 CVC 值，然后用 [stripe.confirmCardPayment](https://docs.stripe.com/js.md#stripe-confirm-card-payment) 从您的客户端确认付款。分别将 `payment_method` 和 `payment_method_options[card][cvc]` 设置为您的 PaymentMethod ID 和 `cardCvc` Element。

```javascript
const result = await stripe.confirmCardPayment(clientSecret, {
  payment_method: '{{PAYMENT_METHOD_ID}}',
  payment_method_options: {
    card: {
      cvc: cardCvcElement
    }
  },
});

if (result.error) {
  // Show error to your customer
  console.log(result.error.message);
} else {
  if (result.paymentIntent.status === 'succeeded') {
    // Show a success message to your customer
    // There's a risk of the customer closing the window before callback
    // execution. Set up a webhook or plugin to listen for the
    // payment_intent.succeeded event that handles any business critical
    // post-payment actions.
  }
}
```

即使未通过 CVC 检查，付款也可能成功。为防止出现此问题，可配置您的 [Radar 规则](https://docs.stripe.com/radar/rules.md#traditional-bank-checks) 以在 CVC 验证失败时阻止付款。

## 将集成升级以处理银行卡验证

此集成将_将拒绝支付过程中需要身份验证的银行卡_。如果您开始在管理平台看到大量列为 `Failed` 的支付，就说明需要[升级您的集成](https://docs.stripe.com/payments/payment-intents/upgrade-to-handle-actions.md)了。Stripe 的全球集成将处理这些支付，而非自动拒绝。
