Asset 业务事件
支持 stripe/paypal/paddle/funnelfox 4 个平台的购买事件。
1. 事件 schema
| 字段名称 | 类型 | 说明 |
|---|---|---|
| id | string | 事件 id |
| time | int | 事件发生时间,毫秒级时间戳 |
| name | string | 事件名称,具体名称见本文档【事件列表】 |
| user_id | string | bytepower 用户 id |
| app_id | string | bytepower app id |
| platform | string | stripe/paypal/paddle/funnelfox |
| app_platform | string | bytepower console 上配置的 app platform |
| bundle_id | string | bytepower console 上配置的 bundle_id |
| bytepower_product_id | string | bytepower asset product_id |
| platform_product_id | string | 第三方平台的 product_id |
| client_ip | string | 用户的客户端 ip |
| environment | string | bytepower 服务环境,develop, debug 或 product |
| api_env | string | 第三方 API 的环境,sandbox 或 product |
| device_info | device_info object | device_info 对象,merge 了 用户购买时上传的 device_info 以及当前 session 关联的 account_ids 信息。 对于 funnelfox 平台来说,在 bytepower console 的 funnelfox 平台配置中的 extra order info 中的字段也会被 merge 到 device_info 里面。 |
| data | map of object | 事件关联的对象 |
| data.stripe_data_version | string | stripe 数据结构的版本号(详见本文档【Stripe 数据兼容方案】) |
1.1 业务对象字段说明
device_info object
device_info 由客户端在购买时上传。它会优先使用客户端在购买时通过购买接口上传的信息。如果 device_info 中某个信息字段不存在,而 account_ids 表中对应的该字段存在,就会将 account_ids 表中的数据 merge 过来。account_ids 表中的数据由 signup/login 接口上传,且会被购买接口上传的 device_info 更新。
subscription object
| 字段名称 | 类型 | 说明 |
|---|---|---|
| sub_id | string | 订阅 id |
| platform | string | stripe/paypal/paddle/funnelfox |
| status | string | 订阅状态,包括 active (活跃),canceled(已取消),finished(已结束,除 active 和 canceled 之外的所有状态) |
| is_free_trial | bool | 是否在免费试用期内,且订阅状态正常(status==active) |
| is_free_trial_cycle | bool | 是否在免费试用期 cycle 内,此时订阅状态不一定正常(status 不一定是 active) |
| is_trial | bool | 是否在试用期内,且订阅状态正常(status==active) |
| is_trial_cycle | bool | 是否在试用期 cycle 内,此时订阅状态不一定正常(status 不一定是 active) |
| platform_status | string | 订阅在第三方平台的状态,不同平台 (stripe/paypal/paddle/funnelfox) 的状态取值不同。对于 funnelfox,状态可能是多个值,此时用逗号隔开,如 AUTORENEW_OFF,INTRO。 |
| cycle_count | int | 目前是第几个周期(从 1 开始,试用期包括在内) |
| paid_cycle_count | int | 目前订阅成功支付几次,不包括免费试用期 |
| created_at | int | 订阅创建时间,毫秒时间戳 |
| updated_at | int | 订阅更新时间,毫秒时间戳 |
subscription_transaction object
| 字段名称 | 类型 | 说明 |
|---|---|---|
| transaction_id | string | 交易 id |
| payment_id | string | 支付 id |
| platform | string | stripe/paypal/paddle/funnelfox |
| status | string | transaction 状态,包括 succeeded, failed, pending, refunded |
| platform_status | string | 订阅在第三方平台的状态,不同平台 (stripe/paypal/paddle/funnelfox) 的状态取值不同。 |
| amount | int | 交易金额,标准单位乘以 100 万 |
| currency | string | 交易币种,如 usd |
| created_at | int | 创建时间,毫秒时间戳 |
| updated_at | int | 更新时间,毫秒时间戳 |
oneoff object
| 字段名称 | 类型 | 说明 |
|---|---|---|
| order_id | string | 订单 id |
| payment_id | string | 支付 id |
| platform | string | stripe/paypal/paddle/funnelfox |
| status | string | 订单状态,包括 succeeded (成功), failed(失败), pending(处理中), refunded(已全额退款) |
| platform_status | string | 订单在第三方平台的状态,不同平台 (stripe/paypal/paddle/funnelfox) 的状态取值不同 |
| amount | int | 交易金额,标准单位乘以 100 万 |
| currency | string | 交易币种,如 usd |
| created_at | int | 创建时间,毫秒时间戳 |
| updated_at | int | 更新时间,毫秒时间戳 |
refund object
| 字段名称 | 类型 | 说明 |
|---|---|---|
| id | string | refund id |
| platform | string | stripe/paypal/paddle/funnelfox |
| is_latest_payment_refund | bool | 是否是最近一期退款;对一次性购买总是 true |
| amount | int | 退款金额,标准单位乘以 100 万 |
| currency | string | 退款币种,如 usd |
| status | string | 退款状态,包括 succeeded(成功), failed(失败),pending(处理中) |
| platform_status | string | 退款在第三方平台的状态,不同第三方平台(stripe/paypal/paddle/funnelfox),该字段取值不一样 |
| created_at | int | 退款创建时间 |
| updated_at | int | 退款更新时间 |
assets 字段
assets 是一个列表,包含这一订单相关联的用户资产(注:不一定是用户当前的所有资产,因为用户可能有其它的购买),资产格式同 asset/me 接口。
2. 事件列表
| 事件名称 | 说明 | data 中的跨平台字段 | data 中的paypal平台原始数据字段 | data 中的stripe平台原始数据字段 | data 中的 paddle 平台原始数据字段 | data 中 funnelfox 平台原始数据字段 |
|---|---|---|---|---|---|---|
| asset.subscription.purchased | 订阅首次支付成功,金额可能为0(含免费试用期) | 1. subscription 2. subscription_transaction (paypal / funnelfox 平台免费试用期无此字段) 3. assets | 1. paypal_subscription 2. paypal_subscription_transaction(paypal 平台免费试用期无此字段) 3. paypal_subscription_payment (paypal 平台免费试用期无此字段) 4. paypal_subscription_plan | stripe_subscription stripe_transaction | paddle_subscription paddle_transaction | funnelfox_subscription funnelfox_transaction(免费试用期无此字段) funnelfox_transaction_v2(免费试用期无此字段) 注:transaction 和 v2 只有其一 |
| asset.subscription.renewed | 订阅续订成功 | 1. subscription 2. subscription_transaction 3. assets | 1. paypal_subscription 2. paypal_subscription_transaction 3. paypal_subscription_payment 4. paypal_subscription_plan | stripe_subscription stripe_transaction | paddle_subscription paddle_transaction | funnelfox_subscription funnelfox_transaction funnelfox_transaction_v2 注:transaction 和 v2 只有其一 |
| asset.subscription.canceled | 订阅已取消 | 1. subscription 2. subscription_transaction (paypal 免费试用期取消无此字段) 3. assets | 1. paypal_subscription 2. paypal_subscription_transaction(paypal 平台免费试用期无此字段) 3. paypal_subscription_payment (paypal 平台免费试用期无此字段) 4. paypal_subscription_plan | stripe_subscription | paddle_subscription paddle_transaction | funnelfox_subscription funnelfox_transaction(最近一次的付费订单信息,如果订阅没有进行过付费,没有此字段) funnelfox_transaction_v2 注:transaction 和 v2 只有其一 |
| asset.subscription.switched | 订阅升降级成功, paypal/stripe/paddle/funnelfox 支持 | 1. subscription 2. subscription_transaction(funnelfox 平台如果 switch (migrate)过程中没有产生费用,则不会有此字段) 3. assets | 1. paypal_subscription 2. paypal_subscription_transaction 3. paypal_subscription_payment 4. paypal_subscription_plan 5. paypal_previous_subscription (paypal 升降级之前的订阅) | stripe_subscription stripe_transaction | paddle_subscription paddle_transaction | funnelfox_subscription funnelfox_transaction(如果 switch 没有产生费用,没有此字段) funnelfox_transaction_v2(如果 switch 没有产生费用,没有此字段) funnelfox_previous_subscription(funnelfox switch 之前的订阅) 注:transaction 和 v2 只有其一 |
| asset.subscription.purchase_failed | 订阅首次购买支付失败 | 1. subscription 2. subscription_transaction (对于 paypal 来说,可能为空。实现时取的是最近的一个 transaction 信息,purchase 失败后,购买失败对应的 transaction 可能还没有创建) 3. assets 注: funnelfox 无此事件 | 1. paypal_subscription 2. paypal_subscription_transaction(对于 paypal 来说,可能为空。实现时取的是最近的一个 transaction 信息,purchase 失败后,购买失败对应的 transaction 可能还没有创建) 3. paypal_subscription_payment(对于 paypal 来说,可能为空。实现时取的是最近的一个 transaction 信息,purchase 失败后,购买失败对应的 transaction 可能还没有创建) 4. paypal_subscription_plan | stripe_subscription stripe_transaction | paddle 无此事件,因为首次支付失败时,订阅还没有创建 | 无 |
| asset.subscription.renew_failed | 订阅续订支付失败 | 1. subscription 2. subscription_transaction (对于 paypal 来说,可能为空。实现时取的是最近的一个 transaction 信息,purchase 失败后,购买失败对应的 transaction 可能还没有创建) 3. assets 注: funnelfox 无此事件 | 1. paypal_subscription 2. paypal_subscription_transaction(对于 paypal 来说,可能为空。实现时取的是最近的一个 transaction 信息,purchase 失败后,购买失败对应的 transaction 可能还没有创建) 3. paypal_subscription_payment(对于 paypal 来说,可能为空。实现时取的是最近的一个 transaction 信息,purchase 失败后,购买失败对应的 transaction 可能还没有创建) 4. paypal_subscription_plan | stripe_subscription stripe_transaction | paddle_subscription paddle_transaction | 无 |
| asset.subscription.refunded | 订阅退款 | 1. subscription 2. subscription_transaction 3. refund 4. assets | 1. paypal_subscription 2. paypal_subscription_transaction 3. paypal_subscription_payment 4. paypal_subscription_plan 5. paypal_subscription_v1_refund 6. paypal_subscription_v2_refund 注:v1_refund 和 v2_refund 有且只有一个 | stripe_subscription stripe_transaction stripe_refund | paddle_subscription paddle_transaction paddle_refund | funnelfox_subscription funnelfox_transaction funnelfox_transaction_v2 funnelfox_refund 注:transaction 和 v2 只有其一 |
| asset.oneoff.purchased | 一次性购买支付成功 | 1. oneoff 2. assets | 1. paypal_oneoff 2. paypal_oneoff_payment | stripe_oneoff | paddle_transaction | 1. funnelfox_oneoff |
| asset.oneoff.purchase_failed | 一次性购买支付失败 | 1. oneoff 2. assets | 1. paypal_oneoff 2. paypal_oneoff_payment | stripe_oneoff | 无,需求没有接入 | 暂未支持 |
| asset.oneoff.refunded | 一次性购买退款 | 1. oneoff 2. refund 3. assets | 1. paypal_oneoff 2. paypal_oneoff_payment 3. paypal_oneoff_v1_refund 4. paypal_oneoff_v2_refund 注:v1_refund 和 v2_refund 有且只有一个 | stripe_oneoff stripe_refund | paddle_transaction paddle_refund | 1. funnelfox_oneoff 2. funnelfox_refund |
FunnelFox 说明
funnelfoix 二期上线后,funnelfox_transaction 变为 funnelfox_transaction_v2,且数据结构发生了变化。两者只有其一。
3. 原始事件对应表
下表给出了 bytepower 事件和平台原始事件的对应关系
| 名称 | stripe | paypal | paddle | funnelfox |
|---|---|---|---|---|
| asset.subscription.purchased | invoice.paid | BILLING.SUBSCRIPTION.ACTIVATED | transaction.completed | event_type: subscription sub_type: starting_trial 或 convertion |
| asset.subscription.renewed | invoice.paid | PAYMENT.SALE.COMPLETED | transaction.completed | event_type: subscription sub_type: convertion 或 renewing |
| asset.subscription.canceled | customer.subscription.deleted | BILLING.SUBSCRIPTION.CANCELLED | subscription.canceled | event_type: subscription sub_type: unsubscription |
| asset.subscription.switched | invoice.paid | BILLING.SUBSCRIPTION.ACTIVATED | transaction.completed | event_type: subscription sub_type: starting_trial 或 convertion |
| asset.subscription.upgraded | - | - | 不支持,只有 switched 事件 | 不支持,只有 switched 事件 |
| asset.subscription.downgraded | - | - | 不支持,只有 switched 事件 | 不支持,只有 switched 事件 |
| asset.subscription.purchase_failed | invoice.payment_failed | BILLING.SUBSCRIPTION.PAYMENT.FAILED | 没有此事件,第三方无此 webhook | 没有此事件,第三方无此 webhook |
| asset.subscription.renew_failed | invoice.payment_failed | BILLING.SUBSCRIPTION.PAYMENT.FAILED | transaction.past_due | 没有此事件,第三方无此 webhook |
| asset.subscription.refunded | charge.refunded | PAYMENT.SALE.REFUNDED PAYMENT.CAPTURE.REFUNDED | adjustment.updated | event_type: refund sub_type: settled |
| asset.oneoff.purchased | payment_intent.succeeded | PAYMENT.CAPTURE.COMPLETED | 无,需求没有接入 | event_type: order sub_type: settled |
| asset.oneoff.purchase_failed | payment_intent.payment_failed | PAYMENT.CAPTURE.DECLINED | 无,需求没有接入 | 没有此事件,第三方无此 webhook |
| asset.oneoff.refunded | charge.refunded | PAYMENT.SALE.REFUNDED PAYMENT.CAPTURE.REFUNDED | 无,需求没有接入 | event_type: refund sub_type: settled |
4. 平台字段说明
4.1 PayPal 字段文档
| 字段名称 | paypal 文档链接 |
|---|---|
| paypal_subscription | Definition-Subscription |
| paypal_subscription_transaction | Definition-Transaction |
| paypal_subscription_payment | Definition-Capture |
| paypal_subscription_plan | Definition-Plan |
| paypal_previous_subscription | Definition-Subscription |
| paypal_subscription_v1_refund | Definition-Refund V1 |
| paypal_subscription_v2_refund | Definition-Refund V2 |
| paypal_oneoff | Definition-Order |
| paypal_oneoff_payment | Definition-Capture |
| paypal_oneoff_v1_refund | Definition-Refund V1 |
| paypal_oneoff_v2_refund | Definition-Refund V2 |
PayPal 常见问题
为什么 paypal 退款事件中有 v1 和 v2 两个版本的退款信息? 原因是 paypal 的退款回调有两个,这两个回调带回的退款信息版本不同,分为 v1 和 v2 两个版本,且不能互相转换。退款时触发哪个 paypal 回调,带回哪个版本的退款信息取决于退款的渠道。一次性购买的退款只返回 v2 版本的退款;paypal 后台的订阅型退款返回 v1 版本的退款信息,其它渠道的订阅型退款返回 v2 版本的退款信息。
为什么 paypal 订阅免费试用期的事件中没有 transaction 信息? 原因是 paypal 在免费试用期时,不会产生 transaction 对象。这和 stripe 不同,stripe 的免费试用期会返回一个支付金额为 0 的 transaction 对象。
为什么 paypal 的 asset.subscription.switched 事件中有一个 paypal_previous_subscription 字段? 原因是当前 paypal 订阅的升降级是通过购买新订阅,然后再取消并退款现有的老订阅来实现的,这个字段里的信息就是被取消的老订阅。
paypal_subscription_transaction 和 paypal_subscription_payment 有什么关系? 对于同一个交易来说,两者的 id 是一样的,代表同一笔交易。只不过这两者由 paypal 的两套 API 返回,返回的字段不同。
4.2 Stripe 说明
stripe_oneoff表示 stripe payment_intentstripe_subscription表示 stripe subscriptionstripe_transaction表示 stripe invoicestripe_refund表示 stripe refund- stripe 平台会添加
_stripe_event_raw表示 BP 实际收到并且处理的平台原始数据(仅在 BP debug 环境)
4.3 FunnelFox 数据示例
由于 FunnelFox 官方未提供数据结构说明文档,这里给出具体的例子。
Subscription 数据结构
funnelfox_subscription 和 funnelfox_previous_subscription 数据结构如下 :
{
"event_id": "33cc79d3-0f8c-4662-a137-759642e1f805",
"event_timestamp": "2025-12-18T05:33:48.025758",
"event_type": "subscription",
"external_id": "UUF5BJRD2KOBX",
"is_livemode": false,
"order": {
"amount": "2",
"created_at": "2025-12-18T05:33:44.814196",
"currency_code": "USD",
"decline_reason": null,
"external_id": "UUF5BJRD2KOBX",
"initial_order_metadata": {
"abc": "def",
"bp_sub_id": "BPSUB_851363003449749504",
"cde": "fgh",
"email": "user@example.com"
},
"oneoff_id": null,
"order_id": "cba3e784-c66f-4a44-b921-501d5fd888ff",
"psp": "stripe",
"retry_step": null,
"status": "settled",
"subs_id": "c81994c7-f80d-447b-93dd-d9a15d99ba2e",
"user_uuid": "bd1d39c2-359c-48e9-b708-a1e7cbbef4ca"
},
"subscription": {
"available_actions": [ "pause", "defer", "disable_autorenew", "create_discount", "migration" ],
"current_period_ends_at": "2025-12-19T07:33:30.815991",
"current_period_starts_at": "2025-12-18T07:33:30.815991",
"initial_order_metadata": {
"abc": "def",
"bp_sub_id": "BPSUB_851363003449749504",
"cde": "fgh",
"email": "user@example.com"
},
"is_active": true,
"iteration": 3,
"next_check_at": "2025-12-19T05:33:30.815991",
"price_point": {
"currency": { "code": "USD", "minor_units": 2, "symbol": "$", "title": "US Dollar" },
"features": [ { "ident": "product_01K99H50E9S6CZFPPNE3QGES8K" } ],
"ident": "price_01K99H5THCKZ98TJSJB02B711X",
"next_period": 1,
"next_period_duration": "days",
"next_price": "2"
},
"started_at": "2025-12-16T07:33:30.815991",
"status": [ "RECURRING" ],
"subs_id": "c81994c7-f80d-447b-93dd-d9a15d99ba2e"
},
"subtype": "renewing",
"user": {
"email": "user@example.com",
"external_id": "UUF5BJRD2KOBX"
}
}Transaction 数据结构
funnelfox_transaction (只在 funnelfox 二期上线前有此字段):
{
"event_id": "19d1962a-7103-4ad8-beff-0fb17311504c",
"event_timestamp": "2025-12-17T05:33:46.042209",
"event_type": "order",
"is_livemode": false,
"oneoff": null,
"order": {
"amount": "1.6",
// ...
"order_id": "18e7f77e-9822-4186-8005-b68a790f8c54",
"status": "settled",
"subs_id": "c81994c7-f80d-447b-93dd-d9a15d99ba2e"
}
}funnelfox_transaction_v2 (只在 funnelfox 二期上线后有此字段):
{
"order_id": "40cb1883-510e-431c-aeb0-7569d1b89dd1",
"amount": "2",
"currency_code": "USD",
"external_id": "UUF6XO3DIGEKL",
"subs_id": "7b2cb919-7c3f-41da-8370-9c2d8c2e6026",
"user_uuid": "8c8d5a42-8a4a-4dbb-92f8-5e516050a3e2",
"oneoff_id": null,
"initial_order_metadata": {
"abc": "def",
"bp_sub_id": "BPSUB_858986190014525440",
"cde": "fgh",
"email": "user@example.com"
},
"status": "authorized",
"decline_reason": null,
"retry_step": null,
"psp": "stripe",
"created_at": "2026-01-06T08:24:56.910284Z"
}Refund 数据结构
funnelfox_refund 数据结构如下 :
订阅退款,有 subscription 相关字段:
{
"amount_refunded": "1",
"created_at": "2025-12-16T09:26:34.988980",
"currency_code": "USD",
"event_id": "874eeca9-3a61-4be9-89e3-3f8e34b544e0",
"event_timestamp": "2025-12-16T09:26:34.988980",
"event_type": "refund",
"external_id": "UUF5BP2CBOXRM",
"is_livemode": false,
"oneoff": null,
"order": {
"amount": "2",
"created_at": "2025-12-16T09:23:07.172827",
"currency_code": "USD",
"decline_reason": null,
"external_id": "UUF5BP2CBOXRM",
"initial_order_metadata": {
"abc": "def",
"bp_sub_id": "BPSUB_851390623214219264",
"cde": "fgh",
"email": "user@example.com"
},
"oneoff_id": null,
"order_id": "3c2f1f7b-e0ea-4b86-ae0d-25702cdc62fc",
"retry_step": null,
"status": "settled",
"subs_id": "6f9a9a64-f26b-4834-851b-a8667bb6700f",
"user_uuid": "ed11dbf3-8a2d-4e0c-aaba-76b069d2d05f"
},
"order_id": "3c2f1f7b-e0ea-4b86-ae0d-25702cdc62fc",
"refund_at": "2025-12-16T09:26:32.907975",
"subs_id": "6f9a9a64-f26b-4834-851b-a8667bb6700f",
"subscription": {
"available_actions": [
"pause",
"defer",
"disable_autorenew",
"create_discount",
"migration"
],
"current_period_ends_at": "2025-12-17T09:23:31.292268",
"current_period_starts_at": "2025-12-16T09:23:31.292268",
"initial_order_metadata": {
"abc": "def",
"bp_sub_id": "BPSUB_851390623214219264",
"cde": "fgh",
"email": "user@example.com"
},
"is_active": true,
"iteration": 1,
"next_check_at": "2025-12-17T07:23:31.292268",
"price_point": {
"currency": {
"code": "USD",
"minor_units": 2,
"symbol": "$",
"title": "US Dollar"
},
"features": [
{
"ident": "product_01K99H50E9S6CZFPPNE3QGES8K"
}
],
"ident": "price_01K99H5THCKZ98TJSJB02B711X",
"intro_free_trial_period": null,
"intro_free_trial_period_duration": null,
"intro_paid_trial_period": null,
"intro_paid_trial_period_duration": null,
"intro_paid_trial_price": null,
"intro_type": "no_intro",
"lifetime_price": null,
"next_period": 1,
"next_period_duration": "days",
"next_price": "2"
},
"started_at": "2025-12-16T09:23:31.292268",
"status": [
"RECURRING"
],
"subs_id": "6f9a9a64-f26b-4834-851b-a8667bb6700f"
},
"subtype": "settled",
"trx_id": "9bf31d09-cbba-4e13-9d9a-a9f4802beb50",
"user": {
"email": "user@example.com",
"external_id": "UUF5BP2CBOXRM"
}
}一次性购买退款,有一次性购买相关字段:
{
"amount_refunded": "0.8",
"created_at": "2025-12-16T09:13:19.634004",
"currency_code": "USD",
"event_id": "62cba982-8f0a-4710-8d93-2e3f21beaafc",
"event_timestamp": "2025-12-16T09:13:19.634004",
"event_type": "refund",
"external_id": "UUF5BPBX4VUNO",
"is_livemode": false,
"oneoff": {
"initial_order_metadata": {
"abc": "def",
"bp_tx_id": "BPTX_851387278873341953",
"cde": "fgh",
"email": "user@example.com"
},
"is_active": true,
"oneoff_id": "faf3e396-09be-408f-9119-87eb7ef0c133",
"order_id": "4f67461d-74de-495c-953f-361c9306e45f",
"price_point": {
"currency": {
"code": "USD",
"minor_units": 2,
"symbol": "$",
"title": "US Dollar"
},
"features": [
{
"ident": "product_01KB4XKKVKV0YN1PJY4A1CDTFC"
}
],
"ident": "price_01KB4XM0RWXPP5C8XNFE5BWHMS",
"intro_free_trial_period": null,
"intro_free_trial_period_duration": null,
"intro_paid_trial_period": null,
"intro_paid_trial_period_duration": null,
"intro_paid_trial_price": null,
"intro_type": "no_intro",
"lifetime_price": "1.5",
"next_period": null,
"next_period_duration": null,
"next_price": null
},
"revoked_at": null,
"started_at": "2025-12-16T09:10:19.116300"
},
"order": {
"amount": "1.5",
"created_at": "2025-12-16T09:09:55.885353",
"currency_code": "USD",
"decline_reason": null,
"external_id": "UUF5BPBX4VUNO",
"initial_order_metadata": {
"abc": "def",
"bp_tx_id": "BPTX_851387278873341953",
"cde": "fgh",
"email": "user@example.com"
},
"oneoff_id": "faf3e396-09be-408f-9119-87eb7ef0c133",
"order_id": "4f67461d-74de-495c-953f-361c9306e45f",
"retry_step": null,
"status": "settled",
"subs_id": null,
"user_uuid": "b3a9ad65-f88f-402d-a9be-772878c6803b"
},
"order_id": "4f67461d-74de-495c-953f-361c9306e45f",
"refund_at": "2025-12-16T09:13:17.652425",
"subs_id": null,
"subscription": null,
"subtype": "settled",
"trx_id": "e9a5e58c-305c-40ee-923b-f8dd3b342da8",
"user": {
"email": "user@example.com",
"external_id": "UUF5BPBX4VUNO"
}
}funnelfox_oneoff
{
"order_id": "40cb1883-510e-431c-aeb0-7569d1b89dd1",
"amount": "2",
"currency_code": "USD",
"external_id": "UUF6XO3DIGEKL",
"subs_id": null,
"user_uuid": "8c8d5a42-8a4a-4dbb-92f8-5e516050a3e2",
"oneoff_id": "7b2cb919-7c3f-41da-8370-9c2d8c2e6026",
"initial_order_metadata": {
"abc": "def",
"bp_sub_id": "BPSUB_858986190014525440",
"cde": "fgh",
"email": "user@example.com"
},
"status": "authorized",
"decline_reason": null,
"retry_step": null,
"psp": "stripe",
"created_at": "2026-01-06T08:24:56.910284Z"
}5. Stripe 平台数据兼容方案
event.data 中的 stripe_subscription, stripe_transaction, stripe_refund, stripe_oneoff 数据结构是 stripe 平台的数据结构,而随着 stripe API 版本的不同,这些数据结构会发生一些变化,所以在解析这些数据结构时,需要先确定这些数据结构的版本,再进行解析。
在新版本的 bytepower 中(新版本待上线),添加了 data.stripe_data_version 字段,存储了上述 stripe 数据结构的版本号,如果程序在处理事件时用到了上述字段,需要做以下事情,以正确处理不同版本的事件数据结构:
- 如果事件没有
data.stripe_data_version字段(目前 bytepower 生产环境的状态),则说明事件是老版本事件,仍按现有的方式进行使用。 - 如果事件有
data.stripe_data_version字段(说明 bytepower 已升级为新版本),需要按照该字段存储的 stripe API 版本对上述 stripe 的数据结构进行解析。bytepower 待发布新版本使用的版本是2025-08-27.basil,如果后续再有版本升级,会提前发通知。
解析不同版本的 stripe 数据结构,需要用到 stripe 不同版本的 SDK,如解析 2025-08-27.basil,需要用到的 stripe go SDK 是 v82 版本。
5.1 Stripe Expand 机制
在已经上线的 bytepower 支持 stripe 新版本中,对以上 stripe 数据结构的部分内嵌数据结构进行了扩展(expand),具体情况如下:
| 字段名称 | 对应 stripe 的数据结构 | expand 的子数据结构 |
|---|---|---|
| data.stripe_subscription | subscription | latest_invoice latest_invoice.payments default_payment_method pending_setup_intent customer |
| data.stripe_transaction | invoice | payments payments.data.payment.charge payments.data.payment.payment_intent |
| data.stripe_oneoff | payment_intent | latest_charge latest_charge.refunds latest_charge.payment_intent |
| data.stripe_refund | refund | charge payment_intent |
stripe expand 机制文档参考:https://docs.stripe.com/api/expanding_objects
子数据结构 expand 与否,同一字段的数据类型会发生变化,现以 subscription (即 data.stripe_subscription 对应的数据结构)的 latest_invoice 举例如下:
不 expand latest_invoice,相关字段如下,可以看到 latest_invoice 字段是字符串类型,保存了 latest_invoice 的 id:
{
"id": "sub_1S6o4iJeDjdpBmRtD6s0yfH6",
"object": "subscription",
"latest_invoice": "in_1SCFDLJeDjdpBmRtImUzpEO1"
}expand latest_invoice 后,相关字段如下,可以看到 latest_invoice 字段变成了一个完整的数据结构,不再是字符串了:
{
"id": "sub_1S6o4iJeDjdpBmRtD6s0yfH6",
"object": "subscription",
"latest_invoice": {
"id": "in_1SCFDLJeDjdpBmRtImUzpEO1",
"object": "invoice",
"account_country": "GB",
"account_name": null,
"account_tax_ids": null,
"amount_due": 3000,
"amount_overpaid": 0,
"amount_paid": 3000,
"amount_remaining": 0,
"amount_shipping": 0,
"application": null,
"application_fee_amount": null,
"attempt_count": 1,
"attempted": true,
"auto_advance": false
// other fields...
}
}stripe SDK 隐藏了以上细节,如果使用 stripe SDK,在没有 expand 时,subscription.latest_invoice 只有 id 字段有值(即 subscription.latest_invoice.id 有值), latest_invoice 的其它字段都没有值。在 expand 后,subscription.latest_invoice 每个字段都有对应的值。需要注意的是,上面的例子只是 expand 了 latest_invoice,而 latest_invoice 下仍有嵌套的子数据结构,这些数据结构并没有展开。
6. 事件示例
{
"id": "NzYyNjAzNDExNTY4MzQwOTky",
"time": 1744708400720,
"name": "asset.subscription.purchased",
"user_id": "UUFJKT3ZYIHXS",
"app_id": "APPFXTBPTSARSU6B",
"platform": "paypal",
"app_platform": "android",
"bundle_id": "zhoufeng_test_app",
"byte_power_product_id": "BUY4GM3SSXAKHI56",
"client_ip": "103.100.64.122",
"environment": "develop",
"api_env": "sandbox",
"device_info": {
"uuid": "xxxx"
},
"data": {
"assets": [
{
"bp_product_id": "BUY4GM3SSXAKHI56",
"custom_expire_time": "0001-01-01T00:00:00Z",
"expire_time": "2025-04-17T09:13:16Z",
"is_auto_renewable": true,
"is_consumable": true,
"is_refund": false,
"is_trial_period": false,
"name": "superv",
"origin": "purchase",
"platform": "paypal",
"product_id": "PROD-7X917145DM3139335",
"quantity": 200,
"receipt_id": "I-5TN95H90FV7P",
"refund_time": "0001-01-01T00:00:00Z",
"sub_canceled": false,
"sub_canceled_time": "0001-01-01T00:00:00Z",
"sub_canceled_ts": 0,
"total_quantity": 200,
"type": "subscription",
"valid_seconds": 172773
},
{
"bp_product_id": "BUY4GM3SSXAKHI56",
"custom_expire_time": "0001-01-01T00:00:00Z",
"expire_time": "2025-04-17T09:13:16Z",
"is_auto_renewable": true,
"is_consumable": true,
"is_refund": false,
"is_trial_period": false,
"name": "vip",
"origin": "purchase",
"platform": "paypal",
"product_id": "PROD-7X917145DM3139335",
"quantity": 100,
"receipt_id": "I-5TN95H90FV7P",
"refund_time": "0001-01-01T00:00:00Z",
"sub_canceled": false,
"sub_canceled_time": "0001-01-01T00:00:00Z",
"sub_canceled_ts": 0,
"total_quantity": 100,
"type": "subscription",
"valid_seconds": 172773
},
{
"bp_product_id": "BUY4GM3SSXAKHI56",
"custom_expire_time": "0001-01-01T00:00:00Z",
"expire_time": "2025-04-17T09:13:16Z",
"is_auto_renewable": true,
"is_consumable": true,
"is_refund": false,
"is_trial_period": false,
"name": "vip1",
"origin": "purchase",
"platform": "paypal",
"product_id": "PROD-7X917145DM3139335",
"quantity": 150,
"receipt_id": "I-5TN95H90FV7P",
"refund_time": "0001-01-01T00:00:00Z",
"sub_canceled": false,
"sub_canceled_time": "0001-01-01T00:00:00Z",
"sub_canceled_ts": 0,
"total_quantity": 150,
"type": "subscription",
"valid_seconds": 172773
}
],
"paypal_subscription": {
"plan_id": "P-86E6357727421122EMXOZZOI",
"start_time": "2025-04-15T09:12:30Z",
"quantity": "1",
"shipping_amount": {
"currency_code": "USD",
"value": "0.0"
},
"subscriber": {
"payer_id": "DVQ47262CJSTW",
"shipping_address": {
"address": {
"address_line_1": "1 Main St",
"admin_area_1": "CA",
"admin_area_2": "San Jose",
"postal_code": "95131",
"country_code": "US"
}
},
"name": {
"given_name": "test",
"surname": "buyer"
},
"email_address": "zhoufengloop-buyer@gmail.com"
},
"custom_id": "{\"app_id\":\"APPFXTBPTSARSU6B\",\"ip\":\"103.100.64.122\",\"test_field\":\"a\",\"user_id\":\"UUFJKT3ZYIHXS\"}",
"id": "I-5TN95H90FV7P",
"status": "ACTIVE",
"status_update_time": "2025-04-15T09:13:17Z",
"billing_info": {
"outstanding_balance": {
"currency": "",
"value": "0.0"
},
"cycle_executions": [
{
"tenure_type": "REGULAR",
"sequence": 1,
"cycles_completed": 1
}
],
"last_payment": {
"amount": {
"currency_code": "USD",
"value": "10.0"
},
"time": "2025-04-15T09:13:16Z"
},
"next_billing_time": "2025-04-16T10:00:00Z"
},
"create_time": "2025-04-15T09:13:16Z",
"update_time": "2025-04-15T09:13:17Z",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-5TN95H90FV7P/cancel",
"rel": "cancel",
"method": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-5TN95H90FV7P",
"rel": "edit",
"method": "PATCH"
},
{
"href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-5TN95H90FV7P",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-5TN95H90FV7P/suspend",
"rel": "suspend",
"method": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-5TN95H90FV7P/capture",
"rel": "capture",
"method": "POST"
}
]
},
"paypal_subscription_payment": {
"status": "COMPLETED",
"id": "8KA609050M480543P",
"amount": {
"currency_code": "USD",
"value": "10.00"
},
"custom_id": "{\"app_id\":\"APPFXTBPTSARSU6B\",\"ip\":\"103.100.64.122\",\"test_field\":\"a\",\"user_id\":\"UUFJKT3ZYIHXS\"}",
"seller_protection": {
"status": "ELIGIBLE",
"dispute_categories": [
"ITEM_NOT_RECEIVED",
"UNAUTHORIZED_TRANSACTION"
]
},
"final_capture": true,
"seller_receivable_breakdown": {
"gross_amount": {
"currency_code": "USD",
"value": "10.00"
},
"paypal_fee": {
"currency_code": "USD",
"value": "0.84"
},
"net_amount": {
"currency_code": "USD",
"value": "9.16"
}
},
"links": [
{
"href": "https://api.sandbox.paypal.com/v2/payments/captures/8KA609050M480543P",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v2/payments/captures/8KA609050M480543P/refund",
"rel": "refund",
"method": "POST"
}
],
"update_time": "2025-04-15T09:13:16Z",
"create_time": "2025-04-15T09:13:16Z"
},
"paypal_subscription_transaction": {
"status": "COMPLETED",
"id": "8KA609050M480543P",
"amount_with_breakdown": {
"gross_amount": {
"currency_code": "USD",
"value": "10.00"
},
"fee_amount": {
"currency_code": "USD",
"value": "0.84"
},
"shipping_amount": {
"currency_code": "",
"value": ""
},
"tax_amount": {
"currency_code": "",
"value": ""
},
"net_amount": {
"currency_code": "USD",
"value": "9.16"
}
},
"payer_name": {
"given_name": "test",
"surname": "buyer"
},
"payer_email": "zhoufengloop-buyer@gmail.com",
"time": "2025-04-15T09:13:16Z"
},
"subscription": {
"sub_id": "I-5TN95H90FV7P",
"platform": "paypal",
"status": "active",
"platform_status": "ACTIVE",
"is_trial": false,
"cycle_count": 1,
"paid_cycle_count": 1,
"created_at": 1744708422241,
"updated_at": 1744708422241
},
"subscription_transaction": {
"transaction_id": "8KA609050M480543P",
"payment_id": "8KA609050M480543P",
"platform": "paypal",
"platform_status": "COMPLETED",
"status": "succeeded",
"amount": 9000000,
"currency": "usd",
"created_at": 1744708396000,
"updated_at": 1744708396000
}
}
}7. 其它配置
7.1 Server 配置
server 配置 (config.yaml) 中需要新增配置,向第三方平台(如 caspian) 上报事件
services:
webhook:
keys:
- name: caspian
key_id: fc140f5312b54a98
key_secret: d9558f440740406b960c57397a33ba697.2 Console 配置
webhook 系统设计文档:bytepower webhook 模块设计文档
