小游戏支付服务端接入文档
一、支付时序图
二、接口列表
快速导航
| 接口名 | 资源路径 | 请求方式 | 是否必接 | 描述 | 备注 |
|---|---|---|---|---|---|
| 下单接口 | /open/open_api/v1/platform/order/create | POST | 是 | 创建B站支付订单 | |
| 订单查询接口 | /open/open_api/v1/platform/order/query | GET | 否 | 查询订单状态信息 | 建议接入 |
| 退款接口 | /open/open_api/v1/platform/order/refund | POST | 是 | 针对已完成支付的订单,发起退款 | |
| 签名计算准则 | - | - | 是 | 接口签名算法说明 | 必须了解 |
三、接口文档
下单接口与回调
1 接口说明
开发者根据自身业务,定制多样的支付场景,本接口用于支付场景之前的创单行为。
2 请求说明
2.1 基础信息
| 属性 | 说明 |
|---|---|
| 域名 | https://miniapp.bilibili.com |
| 路径 | /open/open_api/v1/platform/order/create |
| Method | POST |
| content-type | application/json |
2.2 请求参数
| 参数 | 类型 | 必填 | 位置(query、header、body) | 是否参与签名 | 示例 | 说明 |
|---|---|---|---|---|---|---|
| access_key | String | 是 | query | 否 | b6dj2f1e785149fjp2dedbiad68dwl9y | access_key,接入前申请 |
| sign | String | 是 | query | 否 | WbGNoWSnhogpKzilnQfPciPYdJgiTc2w6T2BI7Bcpo4B | 签名,参考【签名计算准则】 |
| ts | Long | 是 | query | 是 | 1736739534331 | 时间戳,单位毫秒 |
| app_id | String | 是 | body | 是 | bili388fh0g748hdj | 小程序 app_id |
| open_id | String | 是 | body | 是 | 1736739534331 | 小程序 open_id |
| dev_order_id | String | 是 | body | 是 | m123456789 | 开发者业务订单号(
<=32 字符), 【app_id,dev_order_id】二元组每次请求必须唯一,业务方自行保证 |
| amount | Long | 是 | body | 是 | 100 | 订单金额,单位分(>0),如果是 B 币支付,则一定是 100 倍数即整元,否则无法拉取支付,最高金额<5000元 |
| product_type | Integer | 是 | body | 是 | 5 | 商品类型(固定为5) |
| product_id | String | 是 | body | 是 | p123456789 | 商品 id(<=32 字符) |
| product_name | String | 是 | body | 是 | 天龙八部 | 商品名称,不要包含如%&等特殊字符(<=32 字符) |
| product_desc | String | 否 | body | 是 | 以宋哲宗时代为背景,各国之间的武林恩怨和民族矛盾,对人生和社会进行审视和描写 | 商品描述,不要包含如%&等特殊字符(<=64 字符) |
| notify_url | String | 否 | body | 是 | 支付状态回调地址 | 不要携带任何参数,系统回调时会自动拼接相关签名参数,建议接入(<=128 字符) |
| extra_data | String | 否 | body | 是 | {"a":1,"b":"4567dd"} | 拓展信息,开放平台不感知,回调时会原样返回(<=512 字符) |
3 响应说明
3.1 响应信息
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
| code | Long | 0 | 错误码,0 表示成功,其他表示异常,具体异常信息见 message 字段,详细错误码参考【状态码】介绍 |
| message | String | success | 错误信息 |
| data | OrderCreateRes | 见完整示例 | 接口返回值,将该字段完全透传给支付 SDK 即可。 为了解决用户不能及时支付但后续重新支付的问题,业务方可以留存在自己系统中,根据自身业务逻辑选择重新下单还是重新支付(15 分钟内有效) |
OrderCreateRes.order_id |
String | 10001 | 小程序开放平台订单号,建议留存,退款流程会使用 |
OrderCreateRes.pay_info |
String | - | 支付信息(15 分钟内有效),开发者无需关心内容 |
3.2 错误码
| code | 说明(具体错误信息见 message 字段) |
|---|---|
| 0 | 请求成功 |
| 100 | 系统繁忙,请稍后再试 |
| 102 | 程序内部错误,可联系 B 站相关同学排查 |
| 104 | 参数异常,如接口参数不符合规范等 |
| 105 | unauthorized,如小程序访问未授权的剧集等 |
| 106 | invalid sign,如接口参数签名不正确等 |
| 107 | 解析签名参数失败,如解析接口签名参数异常等 |
| 200 | 数据不存在,如未查询到有效的小程序等 |
| 201 | 非法数据,如不符合业务逻辑安全性检查等 |
备注:后续有新增或者变化,会持续新增
3.3 完整示例
3.3.1 请求示例
URL:
{API}?access_key=b6dj2f1e785149fjp2dedbiad68dwl9y&ts=1736739534331&sign=WbGNoWSnhogpKzilnQfPciPYdJgiTc2w6T2BI7Bcpo4B
Body:
{
"open_id": "12345678900101",
"dev_order_id": "m123456789",
"app_id": "bili388fh0g748hdj",
"amount": 100,
"product_type": 5,
"product_id": "1234567",
"product_name": "天龙八部",
"product_desc": "以宋哲宗时代为背景,各国之间的武林恩怨和民族矛盾,对人生和社会进行审视和描写",
"notify_url": "https://demo.com/order/callback",
"extra_data": "{\"a\":1,\"b\":\"4567dd\"}"
}
3.3.2 响应示例
{
"code": 0,
"message": "success",
"data": {
"order_id": "123456789",
"pay_info": "57Gz5YiH5bCU5bCx6KW6JeP5a6a939fjd3KudH5pel6YGt5YX5Zyw6ZyH5ZCR5Lit5Zu95pS5bqc"
}
}
4 回调通知
回调通知通常发生在支付成功等交易场景,建议开发者接入。
4.1 回调参数
回调形式
| 属性 | 说明 |
|---|---|
| Method | POST |
| content-type | application/json |
回调地址
开放平台会根据开发者创单时使用的access_token进行数据签名,比如创单时的notify_url=https://demo.com/order/callback,
则实际开放平台回调的 URL
是https://demo.com/order/callback?ts=1736750625059&sign=11GNoWSnBtgpK59lnip06iPYdJgiTc2w6T2BI7Bcpo4B,开发者根据相关参数进行参数签名校验。
详细回调请求
| 字段 | 类型 | 位置(query、header、body) | 是否参与签名 | 示例 | 说明 |
|---|---|---|---|---|---|
| sign | String | query | 否 | 11GNoWSnBtgpK59lnip06iPYdJgiTc2w6T2BI7Bcpo4B | 签名 |
| ts | Long | query | 是 | 1736750625059 | 时间戳(毫秒) |
| order_id | String | body | 是 | 123456789 | 小程序开放平台订单号 |
| dev_order_id | String | body | 是 | m123456789 | 开发者系统业务订单号 |
| amount | Long | body | 是 | 100 | 订单金额,单位分 |
| pay_amount | Long | body | 是 | 100 | 实付金额,单位分,目前等于订单金额 |
| pay_time | Long | body | 是 | 1736752136959 | 支付时间 |
| pay_status | Integer | body | 是 | 1 | 支付状态,1:成功,目前仅在支付成功后回调 |
| pay_channel | Integer | body | 是 | 1 | 支付渠道,1:支付宝,2:微信,3:B 币支付 |
| extra_data | String | body | 是 | {"a":1,"b":"4567dd"} | 创单时的自定义拓展参数 |
确保响应体的结构如下,其中code=0则认为回调成功,否则认为回调失败
{
"code": 0,
"message": "success"
}
请尽可能保证回调服务的稳定性和幂等性,开放平台保证在支付成功后及时回调,如果该接口回调失败,则依次按照 5s, 10s, 20s, 1min, 3min, 5min, 30min, 1hour 的梯度重试,所有重试梯度使用完之后,放弃回调
业务方在校验签名时,要兼容后续开放平台返回增量字段的能力,因此建议使用JSON接收回调信息,然后生成签名和校验签名,签名验证通过后,再反序列化自身关注的字段。
4.2 完整示例
4.2.1 请求示例
{
"order_id": "123456789",
"dev_order_id": "m123456789",
"amount": 100,
"pay_amount": 100,
"pay_time": 1736752136959,
"pay_status": 1,
"extra_data": "{\"a\":1,\"b\":\"4567dd\"}"
}
4.2.2 响应示例
{
"code": 0,
"message": "success"
}
订单查询
1 接口说明
该接口用于查询已创建的订单详情。
2 请求说明
2.1 基础信息
| 属性 | 说明 |
|---|---|
| 域名 | https://miniapp.bilibili.com |
| 路径 | /open/open_api/v1/platform/order/query |
| Method | GET |
| content-type | application/json |
2.2 请求参数
| 参数 | 类型 | 必填 | 位置(query、header、body) | 是否参与签名 | 示例 | 说明 |
|---|---|---|---|---|---|---|
| access_key | String | 是 | query | 否 | b6dj2f1e785149fjp2dedbiad68dwl9y | access_key,接入前申请 |
| sign | String | 是 | query | 否 | WbGNoWSnhogpKzilnQfPciPYdJgiTc2w6T2BI7Bcpo4B | 签名,参考【签名计算准则】 |
| ts | Long | 是 | query | 是 | 1736739534331 | 时间戳,单位毫秒 |
| app_id | String | 是 | query | 是 | bili388fh0g748hdj | 小程序app_id |
| order_id | String | 是 | query | 是 | 123456789 | 小程序订单号 |
3 响应说明
3.1 响应信息
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
| code | Long | 0 | 错误码,0表示成功,其他表示异常,具体异常信息见message字段,详细错误码参考【状态码】介绍 |
| message | String | success | 错误信息 |
| data | OrderDetailRes | 见完整示例 | 接口返回值,包含订单详情信息 |
OrderDetailRes.open_id |
String | 12345678900101 | 小程序open_id |
OrderDetailRes.dev_order_id |
String | m123456789 | 开发者业务订单号 |
OrderDetailRes.pay_status |
Integer | 1 | 支付状态,1:成功,2:失败 |
OrderDetailRes.amount |
Long | 100 | 订单金额,单位分 |
OrderDetailRes.pay_amount |
Long | 100 | 实付金额,单位分 |
OrderDetailRes.pay_time |
Long | 1736752136959 | 支付时间 |
OrderDetailRes.product_type |
Integer | 1 | 商品类型 |
OrderDetailRes.product_id |
String | 1234567 | 商品id |
OrderDetailRes.product_name |
String | 天龙八部 | 商品名称 |
OrderDetailRes.extra_data |
String | {"a":1,"b":"4567dd"} | 拓展信息 |
OrderDetailRes.pay_channel |
Integer | 1 | 支付渠道,1:支付宝,2:微信,3:B币支付 |
3.2 错误码
| code | 说明(具体错误信息见message字段) |
|---|---|
| 0 | 请求成功 |
| 100 | 系统繁忙,请稍后再试 |
| 102 | 程序内部错误,可联系B站相关同学排查 |
| 104 | 参数异常,如接口参数不符合规范等 |
| 105 | unauthorized,如小程序访问未授权的剧集等 |
| 106 | invalid sign,如接口参数签名不正确等 |
| 107 | 解析签名参数失败,如解析接口签名参数异常等 |
| 200 | 数据不存在,如未查询到有效的小程序等 |
| 201 | 非法数据,如不符合业务逻辑安全性检查等 |
备注:后续有新增或者变化,会持续新增
3.3 完整示例
3.3.1 请求示例
URL:
{API}?access_key=b6dj2f1e785149fjp2dedbiad68dwl9y&ts=1736739534331&sign=WbGNoWSnhogpKzilnQfPciPYdJgiTc2w6T2BI7Bcpo4B&app_id=bili388fh0g748hdj&order_id=123456789
3.3.2 响应示例
{
"data": {
"open_id": "df4e9eec68e41e2b0165eb004af65555",
"dev_order_id": "8153013733091739780840044",
"pay_status": 0,
"amount": 10,
"pay_amount": 0,
"product_type": 1,
"product_id": "81530",
"product_name": "测试剧集名称",
"extra_data": "{\"alb_id\":\"123\",\"epe_id\":\"123\"}"
},
"code": 0,
"message": "success",
"current_time": 1739871452085
}
退款与回调
1 接口说明
针对已完成支付的订单,发起退款。
2 请求说明
2.1 基础信息
| 属性 | 说明 |
|---|---|
| 域名 | https://miniapp.bilibili.com |
| 路径 | /open/open_api/v1/platform/order/refund |
| Method | POST |
| content-type | application/json |
2.2 请求参数
| 参数 | 类型 | 必填 | 位置(query、header、body) | 是否参与签名 | 示例 | 说明 |
|---|---|---|---|---|---|---|
| access_key | String | 是 | query | 否 | b6dj2f1e785149fjp2dedbiad68dwl9y | access_key,接入前申请 |
| sign | String | 是 | query | 否 | WbGNoWSnhogpKzilnQfPciPYdJgiTc2w6T2BI7Bcpo4B | 签名,参考【签名计算准则】 |
| ts | Long | 是 | query | 是 | 1736739534331 | 时间戳,单位毫秒 |
| order_id | String | 是 | body | 是 | 100001 | 小程序开放平台订单号 |
| app_id | String | 是 | body | 是 | bili388fh0g748hdj | 小程序app_id |
| dev_order_id | String | 是 | body | 是 | m123456789 | 开发者业务订单号(<=32字符), 【dev_order_id,dev_refund_id】二元组每次请求必须唯一,业务方自行保证 |
| dev_refund_id | String | 是 | body | 是 | d096544567ghd08 | 退款批次(<=32字符), 【dev_order_id,dev_refund_id】二元组每次请求必须唯一,业务方自行保证 |
| refund_amount | Long | 是 | body | 是 | 100 | 退款金额,单位分(>0 && <=订单总支付金额),如果是B币支付,则一定是100倍数即整元 |
| refund_desc | String | 是 | body | 是 | 用户对产品产生异议,申诉退款50% | 退款描述(<=32字符) |
| notify_url | String | 否 | body | 是 | 退款状态回调地址 | 不要携带任何参数,系统回调时会自动拼接相关签名参数,建议接入(<=128字符) |
| extra_data | String | 否 | body | 是 | {"a":1,"b":"4567dd"} | 拓展信息,开放平台不感知,回调时会原样返回(<=512字符) |
3 响应说明
3.1 响应信息
| 字段 | 类型 | 示例 | 说明 |
|---|---|---|---|
| code | Long | 0 | 错误码,0表示成功,其他表示异常,具体异常信息见message字段,详细错误码参考【状态码】介绍 |
| message | String | success | 错误信息 |
| data | OrderRefundRes | 见完整示例 | 接口返回值 |
OrderRefundRes.refund_status |
Integer | 1 | 退款状态,目前只会返回0:待退款 |
3.2 错误码
| code | 说明(具体错误信息见message字段) |
|---|---|
| 0 | 请求成功 |
| 100 | 系统繁忙,请稍后再试 |
| 102 | 程序内部错误,可联系B站相关同学排查 |
| 104 | 参数异常,如接口参数不符合规范等 |
| 105 | unauthorized,如小程序访问未授权的剧集等 |
| 106 | invalid sign,如接口参数签名不正确等 |
| 107 | 解析签名参数失败,如解析接口签名参数异常等 |
| 200 | 数据不存在,如未查询到有效的小程序等 |
| 201 | 非法数据,如不符合业务逻辑安全性检查等 |
备注:后续有新增或者变化,会持续新增
3.3 完整示例
3.3.1 请求示例
URL:
{API}?access_key=b6dj2f1e785149fjp2dedbiad68dwl9y&ts=1736739534331&sign=WbGNoWSnhogpKzilnQfPciPYdJgiTc2w6T2BI7Bcpo4B
Body:
{
"order_id": "100001",
"dev_order_id": "m123456789",
"app_id": "bili388fh0g748hdj",
"dev_refund_id": "d096544567ghd08",
"refund_amount": 100,
"refund_desc": "用户对产品产生异议,申诉退款50%",
"notify_url": "https://demo.com/order/refund/callback",
"extra_data": "{\"a\":1,\"b\":\"4567dd\"}"
}
3.3.2 响应示例
{
"code": 0,
"message": "success",
"data": {
"refund_status": 0
}
}
4 回调通知
回调通知通常发生在退款成功/失败等交易场景(如果是向金融结算机构发起退款请求失败,则不会通知),建议开发者接入。
4.1 回调参数
回调形式
| 属性 | 说明 |
|---|---|
| Method | POST |
| content-type | application/json |
回调地址
开放平台会根据开发者创单时使用的access_token进行数据签名,比如退款时的回调通知地址是notify_url=https://demo.com/order/refund/callback,
则实际开放平台回调的URL是https://demo.com/order/refund/callback?ts=1736750625059&sign=11GNoWSnBtgpK59lnip06iPYdJgiTc2w6T2BI7Bcpo4B,开发者根据相关参数进行参数签名校验。
详细回调请求
| 字段 | 类型 | 位置(query、header、body) | 是否参与签名 | 示例 | 说明 |
|---|---|---|---|---|---|
| sign | String | query | 否 | 11GNoWSnBtgpK59lnip06iPYdJgiTc2w6T2BI7Bcpo4B | 签名 |
| ts | Long | query | 是 | 1736750625059 | 时间戳(毫秒) |
| order_id | String | body | 是 | 123456789 | 小程序开放平台订单号 |
| dev_order_id | String | body | 是 | m123456789 | 开发者系统业务订单号 |
| dev_refund_id | String | body | 是 | d096544567ghd08 | 退款批次 |
| refund_amount | Long | body | 是 | 100 | 退款金额,单位分 |
| refund_time | Long | body | 是 | 1736752136959 | 退款时间 (毫秒) |
| refund_status | Integer | body | 是 | 3 | 退款状态,3:成功,4:失败 |
| extra_data | String | body | 是 | {"a":1,"b":"4567dd"} | 退款时的自定义拓展参数 |
确保响应体的结构如下,其中code=0则认为回调成功,否则认为回调失败
{
"code": 0,
"message": "success"
}
请尽可能保证回调服务的稳定性和幂等性,开放平台保证在退款成功或者失败后及时回调,如果该接口回调失败,则依次按照5s, 10s, 20s, 1min, 3min, 5min, 30min, 1hour的梯度重试,所有重试梯度使用完之后,放弃回调
业务方在校验签名时,要兼容后续开放平台返回增量字段的能力,因此建议使用JSON接收回调信息,然后生成签名和校验签名,签名验证通过后,再反序列化自身关注的字段。
4.2 完整示例
4.2.1 请求示例
{
"order_id": "123456789",
"dev_order_id": "m123456789",
"dev_refund_id": "d096544567ghd08",
"refund_amount": 100,
"refund_time": 1736752136959,
"refund_status": 3,
"extra_data": "{\"a\":1,\"b\":\"4567dd\"}"
}
4.2.2 响应示例
{
"code": 0,
"message": "success"
}
签名计算准则
1 签名算法
HMAC即基于哈希的消息认证码(Hash-based Message Authentication
Code),是一种使用密码散列函数,同时结合一个加密密钥,通过特定计算方式生成的消息认证码(MAC)。SHA-256(Secure Hash Algorithm
256)是散列函数(或哈希函数)的一种,能对一个任意长度的消息,计算出一个32字节长度的字符串(又称消息摘要)。
本系统所有接口的签名算法采用的是HMAC-SHA256,它是结合了SHA-256哈希函数和一个密钥的消息认证码算法,用于验证数据的完整性和真实性。
参考:
2 签名参数
以任何方式请求任何接口,必须在URL携带以下三个参数:
-
access_key:开发者在开放平台申请的调用凭证包括access_key和access_token。其中,access_token开放平台和开发者约定的签名密钥,而access_key作为凭证的key,通过该key匹配对应的access_token。 ts:请求调用行为发生时的时间戳(单位毫秒),该参数必须作为签名算法的一部分(后面示例中会讲到),且该参数具备有效期比如10s内的调用才是有效的调用。sign:根据ts、接口约定的签名参数以及access_token算出的签名摘要。
其他参与签名计算逻辑的参数因API而异,具体可以参考每个API的详细介绍。
3 签名示例
假如某接口需要的签名参数如下:
| 参数 | 数值 | 类型 | 说明 |
|---|---|---|---|
| app_id | bili123456789 | String | - |
| ss_id | 100052 | Number | - |
| p_name | bili_user_zhang | String | - |
| show_enable | true | Boolean | 布尔类型的参数,一定是小写的true/false |
| targets | 102,103,89 | List<Number> | 如果待签名参数是数组、List&Set等类型,则需要将元素通过,序列化为单一字符串 |
- 调用时时间戳
ts=1736257902605 - 已申请的
access_token=DsI5UxNG5NWuYTJlNDg1NGFkMzRl9Ukp
则结算过程如下:
(1)将每个参数对,拼接为name=value的结构,「app_id=bili123456789,ss_id=100052,p_name=bili_user_zhang,show_enable=true,targets=102,103,89,ts=1736257902605」。
任何需要签名的参数,无论何种数据类型,如果该字段缺省,则该参数不参与签名。需要注意的是,空字符串''和缺省是相同语义的即不参与签名。
(2)将上述参数对按照字典序升序排序,得到排序结果,「app_id=bili123456789,p_name=bili_user_zhang,show_enable=true,ss_id=100052,targets=102,103,89,ts=1736257902605」。
(3)将上述升序的参数对,通过&字符拼接,形成完整的待签名的数据,app_id=bili123456789&p_name=bili_user_zhang&show_enable=true&ss_id=100052&targets=102,103,89&ts=1736257902605。
(4)将access_token=DsI5UxNG5NWuYTJlNDg1NGFkMzRl9Ukp作为密钥创建基于HMAC-SHA256的算法实例(签名代码参考中会讲到),然后将步骤(3)中的数据进行散列,经过Base64编码&替换(将URL不友好的字符+/=替换为B),最终得到摘要sign=WbGNoWSnhogpKzilnQfPciPYdJgiTc2w6T2BI7Bcpo4B。
4 签名代码参考
public class SignGenerator {
private static final String ALGORITHM = "HmacSHA256";
public static String sign(List<Pair<String, String>> parameters, String accessToken) throws Exception {
String data = parameters.stream()
.map(p -> String.format("%s=%s", p.getKey(), p.getValue()))
.sorted(Comparator.comparing(Function.identity()))
.collect(Collectors.joining("&"));
Mac mac = Mac.getInstance(ALGORITHM);
mac.init(new SecretKeySpec(accessToken.getBytes(StandardCharsets.UTF_8), ALGORITHM));
byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
String rawSign = Base64.getEncoder().encodeToString(rawHmac);
//将可能导致URL安全隐患的特殊字符(+、/、=)替换为B
return rawSign.replaceAll("[+/=]", "B");
}
}
<?php
class SignGenerator {
private const ALGORITHM = 'sha256';
public static function sign(array $parameters, string $accessToken): string {
$mappedParams;
foreach ($parameters as $key => $value) {
$mappedParams[] = $key.'='.$value;
}
sort($mappedParams);
$data = implode('&', $mappedParams);
// Generate HMAC
$rawHmac = hash_hmac(self::ALGORITHM, $data, $accessToken, true);
$rawSign = base64_encode($rawHmac);
// Replace URL unsafe characters
return str_replace(['+', '/', '='], 'B', $rawSign);
}
}
// 需要签名的参数
$parameters = ['order_id' => '1000351', 'app_id' => 'bilixxxxxx', 'ts'=>1754539876259];
$signature = SignGenerator::sign($parameters, 'token');
print($signature);
?>