Skip to content

附3. 技能内付费

wangjiale edited this page Dec 21, 2021 · 2 revisions

1. 小度技能内付费介绍

DuerOS支持技能内的商品售卖,包括道具,关卡,会员等等。技能付费云端文档可以参考DuerOS开放平台-技能内付费。本文主要介绍在小度设备端,App如何上报付费请求,处理付款结果。

2. 选择购买指令

DuerOS目前支持两种购买指令:Buy指令(推荐)和Charge指令。开发者可以根据需要自行选择任一指令:

  • Buy指令
  1. 开发者预先在DuerOS中注册商品
  2. 可以自定义购买流程中涉及商品展现的相关提示
  3. DuerOS负责根据商品信息完成下单,收款,并通知技能付款完成
  4. 开发者可以直接调用DuerOS提供的api,获得商品列表以及用户的购买状态,降低开发成本
  • Charge指令
  1. 开发者自行管理商品
  2. 购买流程中涉及商品展现的相关提示都为标准化配置,技能不能自行配置
  3. DuerOS仅负责完成指令中指定金额的收款,并通知技能付款完成
  4. 技能负责下单以及维护用户购买状态

3. 使用Buy指令

版本依赖:BotSdk版本号>=1.35.0, 小度系统版本号大于Sp35.

开发者需要预先在DuerOS中注册商品

3.1 支付流程如下图。

Buy指令-支付流程

3.2 App代码示例

    /**
     * 调用{@link RequestBotSdkUtil#requestBuyProduct(String, String)}发起购买流程,上报购买事件
     * @param sellerOrderId 买家id
     * @param productId 商品id
     */
    private void requestBuyProduct(@NonNull final String sellerOrderId, @NonNull final String productId) {
        RequestBotSdkUtil.requestBuyProduct(sellerOrderId, productId);
    }

调用上述代码逻辑,会拉起小度收银台。用户在小度收银台完成扫码支付。

3.3 处理Buy指令返回结果

支付结果封装在com.baidu.duer.botsdk.IAccountChargeMsgListener 这个接口中,在需要处理支付结果的地方,通过BotSdk.getInstance().setAccountAndChargeListener(this);注册回调,通过onBuyStatusUpdated方法处理支付结果。购买结果,需要App向自己的后端二次确认

    /**
     * Buy流程结果回调
     * @param purchaseResult 支付结果"SUCCESS" 支付成功,"ERROR"支付失败
     * @param productId 商品Id
     * @param baiduOrderId 百度侧订单id
     * @param sellerOrderId 买方订单id
     * @param msg msg
     */
    @Override
    public void onBuyStatusUpdated(@NonNull String purchaseResult, @NonNull String productId, @NonNull String baiduOrderId,
                                   @NonNull String sellerOrderId, @Nullable String msg) {
        Log.i("OnBuyStatusUpdated", "purchaseResult:" + purchaseResult + "; productId:" + productId
                + "; baiduOrderId:" + baiduOrderId + "; sellerOrderId:" + sellerOrderId + "; msg:" + msg);
    }

4. 使用Charge指令

4.1 支付流程图

Charge指令-支付流程图

4.2 App代码调用示例

使用Charge指令,需要自行构造金额信息和卖方订单信息,调用BotSdk.getInstance(context).requireCharge方法完成支付流程。代码示例:

    /**
     * 产生一个固定的金额信息
     * @return amountinfo
     */
    public static AmountInfo mockAmountInfo() {
        AmountInfo amountInfo = new AmountInfo();
        amountInfo.amount = "0.01";
        amountInfo.currencyCode = "CNY";
        return amountInfo;
    }

    /**
     * 构造卖家订单信息
     * @return
     */
    public static SellerOrderStructure mockSellerOlrderStructure() {
        SellerOrderStructure sellerOrderStructure = new SellerOrderStructure();
        sellerOrderStructure.productId = "111" + System.currentTimeMillis();
        sellerOrderStructure.description = "测试商品介绍";
        sellerOrderStructure.sellerOrderId = "222" + System.currentTimeMillis();
        sellerOrderStructure.productName = "测试商品名称";
        sellerOrderStructure.sellerNote = "测试商品备注";
        return sellerOrderStructure;
    }

    /**
     * 发起Charge支付请求
     * @param amountInfo 金额信息
     * @param sellerOrderStructure 买方订单信息
     * @param sellerAuthorizationNote 备注信息
     */
    public static void requireCharge(@NonNull final AmountInfo amountInfo, @NonNull final SellerOrderStructure sellerOrderStructure, 
                                     @Nullable String sellerAuthorizationNote) {
        BotSdk.getInstance().requireCharge(amountInfo, sellerOrderStructure, "测试订单");
    }

4.3 处理Charge支付返回结果

支付结果封装在com.baidu.duer.botsdk.IAccountChargeMsgListener 这个接口中,在需要处理支付结果的地方,通过BotSdk.getInstance().setAccountAndChargeListener(this);注册回调,通过onChargeStatusUpdated方法处理支付结果。支付结果,需要App向自己的后端二次确认

    /**
     * Charge指令回调结果
     * @param purchaseResult 支付结果 SUCCESS支付成功; ERROR 支付失败
     * @param amountInfo 应收金额
     * @param capturedAmount 实收金额(扣除折扣,优惠券等之后的实际金额)
     * @param creationTimestamp 订单创建时间
     * @param baiduOrderId 百度侧订单id
     * @param sellerOrderId 卖方订单id
     * @param msg 订单备注
     */
    @Override
    public void onChargeStatusUpdated(String purchaseResult, AmountInfo amountInfo, AmountInfo capturedAmount, long creationTimestamp,
                                      String baiduOrderId, String sellerOrderId, String msg) {
        Log.i("onChargeStatusUpdated", "purchaseResult:" + purchaseResult + "; amountInfo:" + amountInfo + "; capturedAmount:" 
                + capturedAmount + "; creationTimestamp:" + creationTimestamp + "; baiduOrderId:" 
                + baiduOrderId + "; selllerOrderId:" + sellerOrderId + "; msg:" + msg)
    }

5. 支付成功回调,处理HTTP API成功回调

请求URL

由第三方事先提供,使用HTTPS协议。 DuerOS开放平台->控制台->选择技能->配置服务->支付配置

请求方法

GET

参数

名称 格式 含义 例子
sellerOrderId string 第三方请求下单时生成的第三方内部订单id ab502301c2ka
baiduOrderReferenceId string 百度内部的订单id,与sellerOrderId唯一关联 367446032047198208
sellerAmount int 第三方请求扣款的具体金额,币种人民币,单位分 1900
payAmount int 用户实际支付的具体金额(有可能使用了代金券,所以payAmount <= sellerAmount),币种人民币,单位分 1900
payTime int 用户付款完成时间,以秒为单位的、10位整数unix时间戳 1559318400
requestTime int 本次通知请求的生成时间,以秒为单位的、10位整数unix时间戳 1559318402
sign string 对上述参数的签名验证结果,采用SHA-256算法 7fb0955900263511d9a0941030629e44782601a3ca1ec2c22ba8cea7cfbd57dc

返回结果

    {
        "status": 0,
        "msg": "error message"
    }

返回status为0,则通知第三方成功,百度侧不再继续通知;否则,百度侧会重试通知一定次数。

签名方法

  • 除了sign外的所有参数按照参数名称字母顺序排序后,以 k1=v1&k2=v2&k3=v3… 的方式进行拼接,生成字符串s1;
  • 在s1后附加百度事先分配的签名密钥字符串KEY,生成字符串s2;
  • 计算sign值:signValue = sha256(s2);
  • 在s1字符串后拼接sign:s1&sign=signValue;
  • 附php签名代码demo
/*
 * 生成支付通知的sign
 * $param array 参数字段数组
 * $payCallbackKey string 签名密钥字符串KEY
 * @return string
 */
public function getSign($param, $payCallbackKey) {
    ksort($param);
    $s1 = http_build_query($param);
    $s2 = $s1 . $payCallbackKey;
    $sign = hash('sha256', $s2);
    return $sign;
}

请求样例

GET https://xx.yy.zz/pay/notify?sellerOrderId=ab502301c2ka&baiduOrderReferenceId=367446032047198208&sellerAmount=1900&payAmount=1900&payTime=1559318400&requestTime=1559318402&sign=xxxx

注意事项

开发者的订单状态,应在HTTP API成功回调接口中更新,且只信任该回调。BotApkSdk.onChargeStatusUpdated的回调仅用在APK前端页面的刷新。