# 使用 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 --global @stripe/cli
```

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

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

```bash
stripe login
```

The CLI displays a pairing code and opens the Stripe Dashboard, where you approve access to accounts, sandboxes, and live mode. The CLI stores your credentials in your operating system’s secure credential store (when available) and refreshes the session automatically. To authenticate with an API key instead, run `stripe login --interactive`, use `stripe config`, pass `--api-key`, or set the `STRIPE_API_KEY` environment variable.

## 在本地测试您的 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)。
