小程序云开发接入虚拟支付指引问题咐上代码
先说一下
目前云开发在对接虚拟支付功能上没有什么优势,只 因官方公告,不得不先进行一波适配
相关文档
虚拟支付接入指引文档
小程序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的转载

浙公网安备 33010602011771号