# 使用 Webhook 处理支付事件

如何用 Webhook 响应离线支付事件。

*Webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) 是一个 HTTP 端点，用于从 Stripe 接收事件。

通过 Webhook，您可以接收付款流程以外发生的支付事件的通知，例如：

- 成功付款 (`payment_intent.succeeded`)
- 有争议的付款 (`charge.dispute.created`)
- Stripe 账户可用余额 (`balance.available`)

您可以使用管理平台进行一次性操作，例如退款或更新客户信息，尽管 Webhook 可以扩展您的支付集成和处理大量重要业务事件。

## 构建您自己的 Webhook

可在自己的服务器上构建一个 Webhook 处理程序，用以管理您的所有线下付款流程。首先公开一个可以接收 Stripe 的请求的端点并用 CLI 在本地测试您的集成。来自 Stripe 的每个请求都会包含一个 [Event](https://docs.stripe.com/api/events/object.md) 对象，其中引用了 Stripe 上修改的对象。

## 创建 Webhook 端点

在您的应用中添加新端点。您可通过检查请求体内发送的事件对象的 `type` 字段来操作某些事件。然后，您可以通过标准输出打印，确保您的 Webhook 能正常工作。

添加完新的端点后启动服务器。

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

require 'stripe'
require 'sinatra'
require 'json'

# Using the Sinatra framework
set :port, 4242

post '/webhook' do
  payload = request.body.read
  event = nil

  begin
    event = Stripe::Event.construct_from(
      JSON.parse(payload, symbolize_names: true)
    )
  rescue JSON::ParserError => e
    # Invalid payload
    status 400
    return
  end

  # Handle the event
  case event.type
  when 'payment_intent.succeeded'
    payment_intent = event.data.object # contains a Stripe::PaymentIntent
    puts 'PaymentIntent was successful!'
  when 'payment_method.attached'
    payment_method = event.data.object # contains a Stripe::PaymentMethod
    puts 'PaymentMethod was attached to a Customer!'
  # ... handle other event types
  else
    puts "Unhandled event type: #{event.type}"
  end

  status 200
end
```

## 安装并设置 Stripe CLI

使用 [npm](https://www.npmjs.com/) 安装 Stripe CLI：

```bash
npm install -g @stripe/cli@latest
```

> 如需了解适用于 Windows、macOS、Linux 和 Docker 的更多安装选项，请查看 GitHub 上的 [Stripe CLI readme](https://github.com/stripe/stripe-cli#installation)。

安装后，通过登录将 CLI 关联到您的 Stripe 账户：

```bash
stripe login
```

CLI 会显示配对码，并打开 Stripe 管理平台。您可以在管理平台中批准对账户、沙盒和真实模式的访问权限。CLI 会将您的凭据存储在操作系统的安全凭据存储库中（如果可用），并自动刷新会话。如需改为使用 API 密钥进行身份验证，请运行 `stripe login --interactive`，使用 `stripe config`，传递 `--api-key`，或者设置 `STRIPE_API_KEY` 环境变量。

## 在本地测试您的 Webhook

通过 `listen` 指令，用 CLI 将事件转到您的本地 Webhook 端点。

假设您的应用的运行端口是 4242，运行：

```bash
stripe listen --forward-to http://localhost:4242/webhook
```

在另一个终端选项卡中，用 `trigger` CLI 指令触发一个模拟 Webhook 事件。

```bash
stripe trigger payment_intent.succeeded
```

您的 `listen` 选项卡中会显示以下事件：

```bash
[200 POST] OK payment_intent.succeeded
```

“PaymentIntent 已成功！”出现在您的服务器运行的终端选项卡中。

## Optional: 检查 Webhook 签名

Stripe 在每个事件的 `Stripe-Signature` 头内都包含了一个签名。这样可表示事件是由 Stripe 发送的，而非第三方。要验证签名，可以使用我们的官方库，也可以用您自己的方案[手动验证签名](https://docs.stripe.com/webhooks.md#verify-manually)。

首先，找出您的 Webhook 端点密钥并将它作为 `endpoint_secret` 添加到您的 Webhook 处理程序。由于您仍在用 Stripe CLI 在本地部署您的端点，因此需要用 `trigger` 命令从 CLI 获取 Webhook 端点密钥。

```bash
stripe listen
```

Webhook 端点密钥以 `whsec_` 开头，后跟一串数字和字母。妥善保管此 Webhook 端点，切勿公开。

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

require 'stripe'
require 'sinatra'

# If you are testing your webhook locally with the Stripe CLI you
# can find the endpoint's secret by running `stripe listen`
# Otherwise, find your endpoint's secret in your webhook settings in
# the Developer Dashboard
endpoint_secret = 'whsec_...'

# Using the Sinatra framework
set :port, 4242

post '/my/webhook/url' do
  payload = request.body.read
  sig_header = request.env['HTTP_STRIPE_SIGNATURE']
  event = nil

  begin
    event = Stripe::Webhook.construct_event(
      payload, sig_header, endpoint_secret
    )
  rescue JSON::ParserError => e
    # Invalid payload
    puts "Error parsing payload: #{e.message}"
    status 400
    return
  rescue Stripe::SignatureVerificationError => e
    # Invalid signature
    puts "Error verifying webhook signature: #{e.message}"
    status 400
    return
  end

  # Handle the event
  case event.type
  when 'payment_intent.succeeded'
    payment_intent = event.data.object # contains a Stripe::PaymentIntent
    puts 'PaymentIntent was successful!'
  when 'payment_method.attached'
    payment_method = event.data.object # contains a Stripe::PaymentMethod
    puts 'PaymentMethod was attached to a Customer!'
  # ... handle other event types
  else
    puts "Unhandled event type: #{event.type}"
  end

  status 200
end
```

## 部署您的 Webhook 端点

准备好将 Webhook 端点部署到生产时，您需要进行以下操作：

1. 使用您的[真实模式 API 密钥](https://docs.stripe.com/keys.md#test-live-modes)，不要使用测试模式的密钥。
2. 在 [Workbench](https://docs.stripe.com/workbench.md) 中或通过 API 配置 Webhook 端点。
3. 要在 Workbench 中配置端点，请转到 [Webhook 选项卡](https://dashboard.stripe.com/workbench/webhooks)。
4. 点击**添加目标**，然后输入 Stripe API 版本和您希望 Stripe 发送的特定事件。点击**继续**，然后从可用目标类型列表中选择 **Webhook 端点**。点击**继续**，然后输入端点的 URL、可选名称和可选描述。点击 **创建目标**。
5. 将应用程序中的 Webhook 端点密钥替换为 Workbench 的目标详情视图中为端点显示的新密钥。

您的应用现在可以进行真实事件了。有关配置 Webhook 端点的详细信息，请参阅 [Webhook Endpoint](https://docs.stripe.com/api/webhook_endpoints.md) API。要在沙盒中进行测试，[请参阅我们的开发指南](https://docs.stripe.com/webhooks.md)。
