小游戏支付服务端接入文档

一、支付时序图

小游戏客户端 开发者服务器 小游戏SDK B站游戏平台服务器 创建订单 创建订单 返回支付参数 返回支付参数 支付 支付 支付结果 支付结果 通知支付结果 订单信息校验 订单状态查询 返回订单信息 订单信息校验 小游戏客户端 开发者服务器 小游戏SDK B站游戏平台服务器

二、接口列表

接口名 资源路径 请求方式 是否必接 描述 备注
下单接口 /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_keyaccess_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 签名代码参考

JAVA示例代码:
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示例代码:

        <?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);
        ?>