订阅 Webhooks
Aghanim 的订阅 webhooks 通知您的游戏关于订阅生命周期事件,使您能够管理玩家对基于订阅的内容和服务的访问。
Webhook 事件
使用这些 Webhook 事件来授予、延长或撤销订阅访问权限。
| 事件 | 触发时间 | 你应该做的事情 |
|---|---|---|
subscription.activated | 订阅变为活跃状态 | 授予玩家订阅福利 |
subscription.updated | 订阅属性更改(状态、计划或账单日期) | 用更新的订阅数据同步内部状态 |
subscription.renewed | 经常性付款成功 | 通过更新 effective_until 延长访问权限。 (可选)在每次成功续订时授予奖励(如果适用) |
subscription.deactivated | 订阅被停用 | 立即撤销订阅福利 |
幂等性很重要
Webhook 事件可能会多次传递,因此处理程序必须是幂等的。
订阅状态
状态用于跟踪订阅的当前状态。
| 状态 | 描述 |
|---|---|
trial | 订阅处于试用期。 |
active | 订阅已付费且状态良好。 |
canceled | 订阅不会续订。 它保持活跃状态直到 effective_until,然后过渡到 expired。 |
expired | 订阅不再活跃。 |
向前兼容性
未来可能会添加新的订阅状态。
为了保持你的集成具备向前兼容性,不要仅依赖订阅状态来授予或撤销权益。
始终依赖 event_type 和 effective_until 作为激活和停用订阅访问权限的真实来源。
不要在你的代码中将 status 建模为严格的枚举 — 将其视为字符串,以避免在引入新值时发生破坏。
常见场景
以下示例展示了典型的订阅流程以及每个阶段触发的事件:
试用期 → 付费
subscription.activated状态=trial→ 授权访问subscription.updated状态=active→ 试用期转换为付费(首次成功付款),延长effective_until
经常性付款
subscription.renewed状态=active→ 延长访问权限(更新effective_until)- (可选)在每次成功续订时授予奖励(如果适用)
取消
subscription.updated状态=canceled→ 访问权限保持活跃直到effective_untilsubscription.deactivated→ 撤销访问权限
过期
subscription.deactivatedstatus=expired→ 撤销访问权限- (可选)如果未收到 Webhook,当
now >= effective_until时撤销访问
要求
如需使用 Aghanim 的订阅 Webhook,您应该将 Webhook 服务器配置如下:
- HTTPS 端点,可接收 POST Webhook 请求。
- 监听由 Aghanim 生成并签名的事件。
- 实现幂等性机制,利用请求中的
idempotency_key确保即使收到重复事件通知也只处理一次。 - 如果成功处理了订阅事件,则响应 2xx 状态码;如有错误,则用 4xx 或 5xx 状态码。
配置步骤
- 为订阅 Webhook 处理开发一个函数。
- 部署您的端点使其可访问。
- 在 Aghanim 帐户中选择您要处理的订阅事件类型以注册您的端点 → 游戏 → Webhooks → 新 Webhook。
或者,您也可以使用 Create Webhook API 方法在 Aghanim 中注册您的端点。
请求模式
下面是一个 subscription.activated Webhook 请求示例:
- HTTP
- cURL
POST /your/webhook/uri HTTP/1.1
Content-Type: application/json
Host: your-webhook-endpoint.com
User-Agent: Aghanim/0.1.0
X-Aghanim-Signature: 2e45ed4dede5e09506717490655d2f78e96d4261040ef48cc623a780bda38812
X-Aghanim-Signature-Timestamp: 1725548450
{
"event_type": "subscription.activated",
"event_data": {
"id": "sub_kMnoPqRsTuV",
"sku": "battle_pass",
"name": "战斗通行证",
"nested_items": [
{
"id": "itm_zzzzzzz",
"name": "独占皮肤",
"description": "限量版订阅者皮肤",
"sku": "exclusive_skin_001",
"quantity": 1,
"type": "item"
}
],
"order_id": "ord_eCacpFwavzi",
"user_id": "usr_eymySUreClx",
"player_id": "2D2R-OP3C",
"amount": 999,
"amount_decimal": 9.99,
"currency": "USD",
"payment_method": "cards",
"status": "active",
"due_at": 1706745600,
"created_at": 1704067200,
"plan": {
"key": "battle_pass_monthly",
"name": "战斗通行证每月",
"amount": 999,
"amount_decimal": 9.99,
"currency": "USD",
"offer": {
"key": "season_launch",
"name": "季节启动奖励",
"description": "季节启动的特别折扣",
"discount_percent": 20,
"grace_extension": 3,
"trial_extension": 7
},
"cycle_period": 30,
"grace_period": 3,
"trial_period": 7,
"nested_items": [
{
"id": "itm_xxxxxxx",
"name": "500金币奖励",
"description": "订阅者的每月金币奖励",
"sku": "bonus_gold_500",
"quantity": 1,
"type": "item"
},
{
"id": "itm_yyyyyyy",
"name": "经验值提升",
"description": "订阅期间激活的25%经验值提升",
"sku": "xp_boost_25",
"quantity": 1,
"type": "item"
}
]
},
"effective_until": 1705276800,
"trial_due_at": 1705276800,
"paid_due_at": 1704067200,
"updated_at": 1704067200,
"metadata": {
"tier": "高级",
"referral_code": "SUMMER2024"
}
},
"event_time": 1725548450,
"event_id": "whevt_eCacGbJVbvToOgzjXUgOCitkQE",
"idempotency_key": "idmpt_aXRlb...JkX2VFS",
"request_id": "d1593e9c-c291-4004-8846-6679c2e5810b",
"sandbox": false,
"trigger": "subscription.activated",
"transaction_id": "whtx_eCacGbJVbvT",
"context": null,
"game_id": "gm_exTAyxPsVwh"
}
curl "https://your-webhook-endpoint.com/your/webhook/uri" \
-X POST \
-H "Content-Type: application/json" \
-H "User-Agent: Aghanim/0.1.0" \
-H "X-Aghanim-Signature: 2e45ed4dede5e09506717490655d2f78e96d4261040ef48cc623a780bda38812" \
-H "X-Aghanim-Signature-Timestamp: 1725548450" \
-d '{
"event_type": "subscription.activated",
"event_data": {
"id": "sub_kMnoPqRsTuV",
"sku": "battle_pass",
"name": "战斗通行证",
"nested_items": [
{
"id": "itm_zzzzzzz",
"name": "独占皮肤",
"description": "限量版订阅者皮肤",
"sku": "exclusive_skin_001",
"quantity": 1,
"type": "item"
}
],
"order_id": "ord_eCacpFwavzi",
"user_id": "usr_eymySUreClx",
"player_id": "2D2R-OP3C",
"amount": 999,
"amount_decimal": 9.99,
"currency": "USD",
"payment_method": "cards",
"status": "active",
"due_at": 1706745600,
"created_at": 1704067200,
"plan": {
"key": "battle_pass_monthly",
"name": "战斗通行证每月",
"amount": 999,
"amount_decimal": 9.99,
"currency": "USD",
"offer": {
"key": "season_launch",
"name": "季节启动奖励",
"description": "季节启动的特别折扣",
"discount_percent": 20,
"grace_extension": 3,
"trial_extension": 7
},
"cycle_period": 30,
"grace_period": 3,
"trial_period": 7,
"nested_items": [
{
"id": "itm_xxxxxxx",
"name": "500金币奖励",
"description": "订阅者的每月金币奖励",
"sku": "bonus_gold_500",
"quantity": 1,
"type": "item"
},
{
"id": "itm_yyyyyyy",
"name": "经验值提升",
"description": "订阅期间激活的25%经验值提升",
"sku": "xp_boost_25",
"quantity": 1,
"type": "item"
}
]
},
"effective_until": 1705276800,
"trial_due_at": 1705276800,
"paid_due_at": 1704067200,
"updated_at": 1704067200,
"metadata": {
"tier": "高级",
"referral_code": "SUMMER2024"
}
},
"event_time": 1725548450,
"event_id": "whevt_eCacGbJVbvToOgzjXUgOCitkQE",
"idempotency_key": "idmpt_aXRlb...JkX2VFS",
"request_id": "d1593e9c-c291-4004-8846-6679c2e5810b",
"sandbox": false,
"trigger": "subscription.activated",
"transaction_id": "whtx_eCacGbJVbvT",
"context": null,
"game_id": "gm_exTAyxPsVwh"
}'
事件 Schema
| 键名 | 类型 | 描述 |
|---|---|---|
event_id | string | 阿哈利姆生成的唯一事件标识符。 |
game_id | string | 您的游戏在阿哈利姆中的唯一标识符。 |
event_type | string | 事件的类型, subscription.activated 在此情境下。 |
event_time | number | 以 Unix 时间戳表示的事件发生日期。 |
event_data | EventData | 包含事件特定数据的字段,其中可能包含用于继承对象的各种键值。 |
idempotency_key | string | 即使出现重试情况,也能确保 Webhook 操作只执行一次。 |
request_id | string|null | 如果事件是通过 API 请求触发的,此字段将包含对应的请求 ID。 |
sandbox | boolean | 标识事件是否来自沙盒测试环境的指示器。 |
trigger | string|null | 触发该事件发送的触发器。 |
transaction_id | string | 阿哈利姆生成的交易标识符。在同一交易过程中触发的多个事件可能共享相同的交易 ID。 |
context | EventContext|null | 事件的相关上下文信息。 |
EventContext Schema
| 键名 | 类型 | 描述 |
|---|---|---|
order | OrderContext|null | 与事件相关的订单信息(如果适用)。 |
player | PlayerContext|null | (可选)玩家信息。如需启用,请在webhook设置中选择“添加玩家上下文”。 |
EventData Schema
| 键名 | 类型 | 描述 |
|---|---|---|
id | string | 此订阅实例的唯一标识符。使用此 ID 可在整个生命周期内跟踪和管理该订阅。 |
sku | string | 订阅的唯一 SKU。 |
name | string | 订阅的显示名称。 |
nested_items | NestedItem[] | 此订阅中包含的商品。 |
order_id | string | 最初激活该订阅的订单 ID。可用于与订单 Webhook 进行关联。 |
user_id | string | 用户在阿哈利姆游戏枢纽中的唯一标识符。 |
player_id | string | 唯一的 用于玩家身份验证的玩家标识符. |
amount | number | 订阅金额使用 最小货币单位. |
amount_decimal | number | 订阅金额使用 主要货币单位. |
currency | string | 订阅 货币单位. |
payment_method | string | 使用的支付方式(cards、apple_pay、google_pay、paypal 等)。 |
status | string | 订阅状态。请参阅上文的订阅状态。 |
due_at | number | 以 Unix 时间戳(秒)表示的下次付款到期日期。对于试用订阅,这是首次扣款发生的时间。 |
created_at | number | 以 Unix 时间戳(秒)表示的订阅创建日期。 |
plan | Plan | 与该订阅关联的计划。 |
effective_until | number | 以 Unix 时间戳(秒)表示的订阅有效截止日期。此值为 trial_due_at 和 paid_due_at 中的较大者,代表订阅福利应保持可用的时间。 |
trial_due_at | number|null | 以 Unix 时间戳(秒)表示的试用期结束日期。若订阅没有试用期,则为 null。 |
paid_due_at | number|null | 以 Unix 时间戳(秒)表示的订阅已付费截止日期(paid-through 日期)。在此日期之后,订阅必须续订,否则将过期。在试用期内或首次付款之前为 null。 |
updated_at | number|null | 以 Unix 时间戳(秒)表示的订阅最后更新日期。若订阅从未更新过,则为 null。 |
metadata | object|null | 附加到该订阅的自定义键值对。若未设置任何元数据,则为 null。 |
Plan Schema
| 键名 | 类型 | 描述 |
|---|---|---|
key | string | 订阅内计划的唯一键。 |
name | string | 计划的显示名称。 |
amount | number | 计划金额使用 最小货币单位. |
amount_decimal | number | 计划金额使用 主要货币单位. |
currency | string | 计划 货币单位. |
offer | Offer|null | 应用于该计划的优惠。若未应用任何优惠,则为 null。 |
cycle_period | number|null | 以天为单位的定期计费周期(例如,30 表示每月,365 表示每年)。 |
grace_period | number|null | 付款失败后订阅仍保持活跃并进行重试尝试的天数。若未配置宽限期,则为 null。 |
trial_period | number|null | 以天为单位的试用期时长。若计划没有试用期,则为 null。 |
nested_items | NestedItem[] | 此计划中包含的商品。 |
Offer Schema
| 键名 | 类型 | 描述 |
|---|---|---|
key | string | 优惠的唯一键。 |
name | string|null | 优惠的显示名称。 |
description | string|null | 优惠的描述。 |
discount_percent | number|null | 折扣百分比。 |
grace_extension | number|null | 以天为单位的宽限期延长时长。 |
trial_extension | number|null | 以天为单位的试用期延长时长。 |
NestedItem Schema
| 键名 | 类型 | 描述 |
|---|---|---|
id | string | 阿哈利姆生成的商品唯一标识符。 |
name | string | 商品名称。 |
description | string|null | 商品描述。 |
sku | string | 商品的 SKU 标识符,必须确保在游戏系统和阿哈利姆上保持一致。 |
quantity | number | 商品数量。 |
fallback_item | Item|null | 如果无法向玩家发放主要商品,系统将提供的备选商品。 |
metadata | object|null | 该商品的自定义键值对,用于支持您端的额外逻辑。 |
需要技术支持?
联系我们的集成技术团队: [email protected]