订阅 Webhooks
Aghanim 的订阅 webhooks 通知您的游戏关于订阅生命周期事件,使您能够管理玩家对基于订阅的内容和服务的访问。
Webhook 事件
使用这些 Webhook 事件来授予、延长或撤销订阅访问权限。
| 事件 | 触发时间 | 你应该做的事情 |
|---|---|---|
subscription.activated | 订阅变为活跃状态 | 授予玩家订阅福利 |
subscription.updated | 订阅属性更改(状态、计划或账单日期) | 用更新的订阅数据同步内部状态 |
subscription.renewed | 经常性付款成功 | 通过更新 effective_until 延长访问权限。 (可选)在每次成功续订时授予奖励(如果适用) |
subscription.deactivated | 订阅已取消 | 立即撤销订阅福利 |
幂等性很重要
Webhook 事件可能会多次传递,因此处理程序必须是幂等的。
订阅状态
状态用于跟踪订阅的当前状态。
| 状态 | 描述 |
|---|---|
trial | 订阅处于试用期。 |
进行中 | 订阅已付费且状态良好。 |
canceled | 订阅不会续订。 它保持活跃状态直到 effective_until,然后过渡到 expired。 |
expired | 订阅不再活跃。 |
向前兼容性
未来可能会添加新的订阅状态。
为了保持你的集成具备向前兼容性,不要仅依赖订阅状态来授予或撤销权益。
始终依赖 event_type 和 effective_until 作为激活和停用订阅访问权限的真实来源。
不要在你的代码中将 status 建模为严格的枚举 — 将其视为字符串,以避免在引入新值时发生破坏。
常见场景
以下示例展示了典型的订阅流程以及每个阶段触发的事件:
试用期 → 付费
subscription.activated状态=trial→ 授权访问subscription.updated状态=进行中→ 试用期转换为付费(首次成功付款),延长effective_until
经常性付款
subscription.renewed状态=进行中→ 延长访问权限(更新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 | The trigger that caused the event to be sent. |
transaction_id | string | 阿哈利姆生成的交易标识符。在同一交易过程中触发的多个事件可能共享相同的交易 ID。 |
context | EventContext|null | 事件的相关上下文信息。 |
EventContext Schema
| 键名 | 类型 | 描述 |
|---|---|---|
order | OrderContext|null | 与事件相关的订单信息(如果适用)。 |
player | PlayerContext|null | (可选)玩家信息。如需启用,请在webhook设置中选择“添加玩家上下文”。 |
The EventData schema
id | string | Unique identifier for this subscription instance. Use this ID to track and manage the subscription throughout its lifecycle. |
| 键名 | 类型 | 描述 |
|---|---|---|
sku | string | Subscription unique SKU. |
name | string | Subscription display name. |
nested_items | NestedItem[] | Items included in this subscription. |
order_id | string | Order ID where the subscription was originally activated. Use this to correlate with order webhooks. |
user_id | string | 用户在阿哈利姆游戏枢纽中的唯一标识符。 |
player_id | string | 唯一的 用于玩家身份验证的玩家标识符. |
amount | number | Subscription amount in 最小货币单位. |
amount_decimal | number | Subscription amount in major currency units. |
currency | string | Subscription 货币单位. |
payment_method | string | Payment method used (cards, apple_pay, google_pay, paypal, etc.). |
status | string | Subscription status. See Subscription statuses above. |
due_at | number | Next payment due date in Unix epoch time (seconds). For trial subscriptions, this is when the first charge will occur. |
created_at | number | Subscription creation date in Unix epoch time (seconds). |
plan | Plan | Plan associated with the subscription. |
effective_until | number | The date until which the subscription is effective in Unix epoch time (seconds). This is the higher value between trial_due_at and paid_due_at, representing when the subscription benefits should remain accessible. |
trial_due_at | number|null | Trial end date in Unix epoch time (seconds). Null if subscription has no trial. |
paid_due_at | number|null | Date through which the subscription is paid (paid-through date) in Unix epoch time (seconds). After this date, the subscription must be renewed or it will become expired. Null during trial or before first payment. |
updated_at | number|null | Subscription last update date in Unix epoch time (seconds). Null if the subscription has not been updated. |
metadata | object|null | Custom key-value pairs attached to the subscription. Null if no metadata is set. |
The Plan schema
| 键名 | 类型 | 描述 |
|---|---|---|
key | string | Plan unique key within subscription. |
name | string | Plan display name. |
amount | number | Plan amount in 最小货币单位. |
amount_decimal | number | Plan amount in major currency units. |
currency | string | Plan 货币单位. |
offer | Offer|null | Offer applied to the plan. Null if no offer is applied. |
cycle_period | number|null | Recurring billing interval in days (e.g., 30 for monthly, 365 for yearly). |
grace_period | number|null | Number of days after a failed payment during which the subscription remains active while retry attempts are made. Null if no grace period is configured. |
trial_period | number|null | Trial period duration in days. Null if plan has no trial. |
nested_items | NestedItem[] | Items included in this plan. |
The Offer schema
| 键名 | 类型 | 描述 |
|---|---|---|
key | string | Offer unique key. |
name | string|null | Offer display name. |
description | string|null | Offer description. |
discount_percent | number|null | Discount percentage. |
grace_extension | number|null | Grace period extension in days. |
trial_extension | number|null | Trial period extension in days. |
NestedItem Schema
| 键名 | 类型 | 描述 |
|---|---|---|
id | string | 阿哈利姆生成的商品唯一标识符。 |
name | string | 商品名称。 |
description | string|null | 商品描述。 |
sku | string | 商品的 SKU 标识符,必须确保在游戏系统和阿哈利姆上保持一致。 |
quantity | number | 商品数量。 |
fallback_item | Item|null | 如果无法向玩家发放主要商品,系统将提供的备选商品。 |
metadata | object|null | Custom key-value pairs for the item to support additional logic on your end. |
需要技术支持?
联 系我们的集成技术团队: [email protected]