小程序云开发接入虚拟支付指引问题咐上代码

先说一下
目前云开发在对接虚拟支付功能上没有什么优势,只 因官方公告,不得不先进行一波适配

相关文档
虚拟支付接入指引文档
小程序api文档

基本配置
Appid、OfferId、沙箱AppKey、现网AppKey,这几个核心参数在 小程序后台-支付与交易-虚拟支付-基本配置 可以拿到

签名
虚拟支付涉及到两种签名

支付签名 paySig
用户态签名 signature
生成支付签名所需参数 说明
appKey 沙箱appKey或者现网appKey,取决于你当前的env
uri 如果是基础库的wx.requestVirtualPayment,uri固定传requestVirtualPayment; 如果是后端接口,uri就传入api路径,例如查询创建订单接口传xpay/query_order
signData 该参数是个json字符串,具体参考 wx.requestVirtualPayment的api文档 说明
生成用户态签名所需参数 说明
sessionKey 小程序会话密钥,通常情况下云开发不会用到这个参数,但是现在得对接了 小程序登录凭证校验
signData 参考 wx.requestVirtualPayment的api文档 说明
生成签名代码
const crypto = require('crypto');

/**

  • hmacSha256Hex
  • @param key
  • @param data
  • @returns {string}
    */
    hmacSha256Hex(key, data) {
    return crypto.createHmac('sha256', key).update(data, 'utf8').digest('hex');
    }

/**

  • 生成虚拟支付签名
  • @param uri
  • @param signData
  • @param sessionKey
  • @param appKey
  • @returns {{paySig: string, signature: string}}
    /
    generateVirtualPaymentSign({uri, signData, sessionKey, appKey}) {
    const paySig = this.hmacSha256Hex(appKey, ${uri}&${signData});
    const signature = this.hmacSha256Hex(sessionKey, signData);
    console.log("paySig->", paySig, "signature->", signature)
    return {paySig, signature};
    }
    获取session_key代码
    /
    *
  • 获取sessionKey
  • @param code wx.login获取的code
  • @param appid
  • @param secret 小程序秘钥
  • @returns {Promise<AxiosResponse|void>}
    */
    async code2Session({code, appid, secret}) {
    const url = https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=GRANT_TYPE;
    return await axios.get(url)
    .then(res => {
    console.log(res);
    return res.data.session_key;
    })
    .catch(err => {
    console.log(err);
    });
    }
    其中code调用 wx.login 可以得到,secret是小程序秘钥,在小程序后台-开发者管理可得

发起虚拟支付
云函数端(道具模式)
/**

  • 获取虚拟支付参数

  • @param params

  • @returns {Promise<*>}
    */
    async getVirtualPaymentData(params) {
    const {totalFee, mealCode, loginCode} = params;
    const openid = this.wxContext.OPENID;
    const appid = this.wxContext.APPID;
    //生成订单
    const orderInfo = await this._generalOrder(totalFee, appid, openid, mealCode, constants.ORDER_CATEGORY.NORMAL.code);
    //业务订单号
    const orderNo = orderInfo._id;

    //虚拟支付基本配置
    const virtualPaymentConfig = await this._getWxPaymentConfig(appid, "wxVirtualPayment");
    const {offerId, appKeyProd, appKeySandbox} = virtualPaymentConfig;

    //获取sessionKey
    const sessionKey = await this.code2Session({
    code: loginCode,
    appid: virtualPaymentConfig.appid,
    secret: virtualPaymentConfig.secret
    });

    //环境配置:0现网 1沙箱
    const env = 0;

    const signData = JSON.stringify({
    buyQuantity: 1,
    env,
    offerId,
    currencyType: "CNY",
    productId: mealCode,//道具ID
    goodsPrice: totalFee,//道具单价,要和道具ID对应
    outTradeNo: orderNo,
    attach: JSON.stringify({mealCode})//发货通知时会透传给开发者,根据自己需要透传数据
    });

    //获取支付签名、用户态签名
    const {paySig, signature} = this.generateVirtualPaymentSign({
    uri: "requestVirtualPayment",
    signData,
    sessionKey,
    appKey: env ? appKeySandbox : appKeyProd
    })

    return {paySig, signature, signData, orderNo};
    }
    小程序端
    /**

  • 发起虚拟支付请求

  • api.js是我自己封装的一些全局api

  • @param totalFee:支付金额(道具单价)

  • @param mealCode:套餐编码(道具ID)

  • @returns {Promise<*>}
    */
    const requestVirtualPayment = function (totalFee, mealCode) {
    return new Promise(function (resolve, reject) {
    api.login().then(loginCode => {
    let start = new Date().getTime();
    console.log("请求支付,订单信息:", totalFee, renewal, mealCode, activeCode);
    //请求云函数获取虚拟支付参数
    api.callCloudUserCenterFunction("PaymentHandler/getVirtualPaymentData", {
    loginCode,
    totalFee,
    mealCode
    }, res => {
    console.log("操作结果:", JSON.stringify(res), "耗时:", new Date().getTime() - start);
    console.log("获取到订单支付参数--->", res);
    if (res.result.success) {
    const {paySig, signature, signData, orderNo} = res.result.data;
    api.requestVirtualPayment({
    paymentData: {signData, paySig, signature, orderNo},
    success: resolve, fail: reject
    });
    } else {
    console.error("下单失败");
    reject();
    }
    }, reject, () => {

    });
    }).catch(reject);

    });
    }
    全局api.js
    /**

  • 登录

  • 调用接口获取登录凭证(code)。

  • 通过凭证进而换取用户登录态信息,包括用户在当前小程序的唯一标识(openid)、微信开放平台账号下的唯一标识(unionid,若当前小程序已绑定到微信开放平台账号)及本次登录的会话密钥(session_key)等。

  • 用户数据的加解密通讯需要依赖会话密钥完成。

  • @returns {Promise}
    */
    const login = function () {
    return new Promise((resolve, reject) => {
    wx.login({
    success(res) {
    console.log("登录结果:", res);
    const {code} = res;
    if (code) {
    console.log("登录成功:", res);
    resolve(code);
    } else {
    console.error("登陆失败", res);
    reject(res);
    }
    },
    fail: function (res) {
    console.error("登陆异常", res);
    reject(res);
    }
    });
    });
    }

/**

  • 请求虚拟支付
  • @param paymentData
  • @param mode
  • @param success
  • @param fail
  • @param complete
    */
    const requestVirtualPayment = function ({
    paymentData,
    mode = virtualPaymentMode.short_series_goods,
    success,
    fail,
    complete
    }) {
    wx.requestVirtualPayment({
    signData: paymentData.signData,
    paySig: paymentData.paySig,
    signature: paymentData.signature,
    mode,
    success(res) {
    console.log("支付成功", res);
    typeof success == 'function' && success(Object.assign(res, paymentData));
    },
    fail(res) {
    console.warn("支付失败", res);
    typeof fail == 'function' && fail(Object.assign(res, paymentData));
    },
    complete(res) {
    console.log("支付完成", res);
    typeof complete == 'function' && complete(Object.assign(res, paymentData));
    }
    })
    };
    到这里就能发起支付了

消息事件通知
现在云开发也支持接收虚拟支付消息了,需要在开发者工具上添加一下消息推送配置,具体参照下方截图

勾选需要接收的消息,比如道具模式一般配xpay_goods_deliver_notify xpay_refund_notify xpay_complaint_notify xpay_subscribe_ios_refund_query_notify这几个就够用了。

云函数消息推送的代码比较多,贴了一个代码片段,针对不同的通知事件用不同的处理器处理,可以参考一下

为了拿到支付结果通知,还需要对接一下 消息事件通知,云开发本来也是可以通过云函数来接收消息推送,遗憾的是目前仅支持客服消息推送。好在云函数支持HTTP访问,可以间接实现

  • 开启HTTP访问服务
    云函数如果要想接收支付结果通知(在虚拟支付功能里面是叫做发货通知)、退款结果通知、IOS退款问询通知等消息,得先到 腾讯云-cloudbase后台 给云开发环境开启HTTP访问服务,建议配一个自定义域名,然后域名关联你要收消息的云函数,操作如下:

  • 小程序后台开启消息推送
    后台-开发管理-开发设置-消息推送

这里配置的URL带了一个msgType参数,可以方便后续在云函数里面识别是微信推送的消息。

云函数消息推送的代码比较多,贴了一个代码片段,针对不同的通知事件用不同的处理器处理,可以参考一下

调试问题
调试过程中目前主要碰到以下问题:

虚拟支付道具如果是新增或者修改的话,大概需要10分钟时间才会生效,这期间这个道具调试的时候会报错:COIN_OR_PRODUCT_ID_CREATED_IN_RECENTLY
IOS最低支付金额1元,这点文档里面也有写
IOS得在现网环境调试,env=0
IOS退款问询响应拒绝退款,还是会时不时收到问询消息
小程序后台操作退款,退款通知还是走HTTP,并不是云函数的消息推送,可能还有bug

文章来自showms的转载

posted @ 2026-07-02 10:49  上海观智网络  阅读(35)  评论(0)    收藏  举报