小游戏支付服务端接入文档
一、支付时序图
二、接口列表
快速导航
接口名 | 资源路径 | 请求方式 | 是否必接 | 描述 | 备注 |
---|---|---|---|---|---|
下单接口 | /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);
?>