> For the complete documentation index, see [llms.txt](https://docs.ggwp.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ggwp.com/chinese-simplified/webhook/jie-shou-shi-jian.md).

# 接收事件

## **事件处理的一般流程**

1. 当事件发生（触发）时，GGWP webhook 会向目标 URL 发送载荷（POST 请求）。
   1. v1.0：发送的载荷将采用 JSON 字符串格式，键和值对之间可能包含空白字符。 `Content-Type`  标头设置为 `application/json` .
   2. v1.1：发送的载荷将采用 JSON 字符串格式，不含空白字符。这减少了在进行 HMAC 验证前对载荷去除空白字符的额外步骤。 `Content-Type`  标头设置为 `application/octet-stream` .
2. 通过 HMAC 验证传入请求。此外，服务器还可以依赖自定义标头（在创建订阅时设置）进行身份验证。
3. 一旦目标 URL 收到载荷，目标端应返回一个 **200 状态码** 以表示已成功接收载荷。
4. 如果在 1 分钟内未返回 200 状态码，则该次尝试视为失败。之后会重试三次，然后才最终放弃对该事件的 URL 调用。&#x20;

要接收订阅事件，客户需要编写能够接收 GGWP Webhooks 发送载荷的 API。

## 创建目标 URL

当任一事件触发时，GGWP 会通过调用目标 URL 来传递信息。目标 URL 预计通过支持 `POST` API 上的方法。

## 请求验证

所有发往目标 URL 的传入请求都需要进行验证，以确保它们来自 GGWP。这可以通过计算数字签名来完成。每个 webhook 请求都包含一个 base64 编码的 `x-ggwp-hmac-sha256` 标头，它使用订阅密钥以及请求中发送的数据生成。

{% hint style="warning" %}
对于 v1.0 实现，为确保签名验证准确，在计算 HMAC 摘要之前必须去除载荷数据中的所有空白字符。这是因为在格式化或传输过程中可能会引入额外的空白字符，它们不会影响实际内容，但如果不加以处理，可能会导致签名不匹配。
{% endhint %}

要进行验证，请按照以下算法计算 HMAC 摘要，如果 HMAC 摘要与标头值匹配，则 webhook 事件来自 GGWP。

{% code lineNumbers="true" fullWidth="false" %}

```python
# Python 示例

import base64
import hashlib
import hmac
import json


# 创建 webhook 订阅时收到的密钥
SECRET_KEY = 'my_secret_key'

def verify_webhook(payload, hmac_header):
    digest = hmac.new(
        SECRET_KEY.encode('utf-8'), 
        json.dumps(payload, separators=(',', ':')).encode("utf-8"),
        digestmod=hashlib.sha256
    ).digest()
    computed_hmac = base64.b64encode(digest)

    return hmac.compare_digest(computed_hmac, hmac_header.encode('utf-8'))

```

{% endcode %}

> 对于基于 GoLang 的服务器，建议仅使用版本 1.1，因为 1.0 上的 hmac 验证无法工作。HMAC 计算始终基于紧凑的 JSON（不含空格）进行。

{% code lineNumbers="true" %}

```go
// Go 示例

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
)

// 创建 webhook 订阅时收到的密钥
const SECRET_KEY = "my_secret_key"

func verifyWebhook(payload string, hmacHeader string) bool {
	mac := hmac.New(sha256.New, []byte(SECRET_KEY))
	mac.Write([]byte(payload))
	digest := mac.Sum(nil)
	computedHmac := base64.StdEncoding.EncodeToString(digest)
	return hmac.Equal([]byte(computedHmac), []byte(hmacHeader))
}

```

{% endcode %}
