背景
由于业务属性,需要开发一个简单的商城H5,期望是能够实现商品的购买等一系列简单的闭环流程。开始以为就是开发一个简单的h5页面也没放在心上。本以为事情都会随着理想的状态发展,但是人生总有事与愿违。好巧不巧就遇到了各种坑,其中最大的坑莫过于支付的问题了。因为是要嵌入到微信小程序,以为就是和往常一样调用下微信支付就行。结果就不行,至于为什么不行和其他注意事项,且看下面陈述!
对接支付
起初因为在pc上写了购物商城,对接的微信支付和支付宝支付,使用的是支付二维码。这个对接的过程可能太过丝滑,导致对微信嵌入h5的支付功能造成了误判,看到微信有h5支付,于是果断对接h5的支付,当我满心期待去测试的时候,微信模拟器给我弹出了个大大的警告,微信小程序不支持h5方式支付的调用。于是我在社区逛了逛,又看看了看官方api接口文档。果不其然,你在小程序里用,就得使用小程序的支付调用。所以坑来。。。
内嵌的h5调用支付的方式只能是小程序的微信支付
本来是想着能支持多种支付方式,看了下文档发现想支持支付宝是不可能了,于是果断舍弃。那就只能用微信支付了啊,本以为可以快速接上微信h5支付,然后说不能用h5支付有图为证
那这没办法了啊,之前以为是不管是app还是微信小程序只是嵌入个h5,提供一个容器而已,并不会干扰h5自己的逻辑,那这个图一出现,就得重新想一个方式了,于是乎就有了跳来跳去
使用小程序唤起微信支付
由于微信支付的限制,嵌入到小程序中的h5页面只能通过跳转到微信小程序的页面来唤起,所以如果在宿主小程序中嵌入h5支付,那么你得对宿主小程序进行改造(本来以为嵌入h5链接就好了),先要添加一个微信小程序的h5支付页,比如
当嵌入的h5调用微信支付的时候就跳转到这个页面来进行唤起,然后这里需要注意的地方,就是需要判断出当前的h5所在的是在哪个环境中。因为h5有可能是一般的浏览器打开,也有可能是微信浏览器打开(微信直接打开),也有可能是嵌入到微信小程序或者app中,所以接下来看看如何判断所在环境。
判断h5所在环境
总的来说就3种情况(微信小程序内、微信浏览器内、其他),微信浏览器是指直接在微信中打开或者在微信公众号。那一般来说想判断宿主环境都是通过ua来判断的,那这里也不例外,话不多说直接上代码!
let UA = navigator.useAgent.toLowerCase();
if(UA.match(/MicroMessenger/i)=="micromessenger"){
wx.miniProgram.getEnv(res => {
if(res.miniprogram){
// 微信小程序环境
}else {
// 微信浏览器内(公众号/微信打开)
}
})
} else {
// 这里就是其他任何浏览器了
}
在内嵌在微信小程序中使用微信支付
在上面已经准备好了供h5使用的h5Pay页面,这里就开始实现这个唤起支付的逻辑,首先看微信支付文档。前面一系列的准备默认都已准备好,这里直接开始接入支付功能。从文档看到小程序要调用支付有两步。
- 调用微信的下单接口生成与预支付订单
- 然后通过wx.requestPayment发起微信支付带上需要的参数
看下这个唤起支付的接口需要的参数
这里的参数可以参考文档,业务中只需要获取到package和paySign。package参数是上个生成预订单接口生成的,paySign参数则是微信的加签接口看文档 pay.weixin.qq.com/wiki/doc/ap…
就是通过一系列的算法生成的一个签名值,在h5端调用后端的接口把支付需要的参数准备好然后通过跳转到小程序h5Pay支付页把参数带过去在加载webview看下代码实现
// 获取支付参数
const postPay = async () => {
// 获取31位随机数
const nonceStrVal = generateRandomString(31);
// 获取时间戳
const timeStampVal = String(Math.floor(moment().valueOf() / 1000));
// 签名类型
const signTypeVal = "RSA";
// 请求预下单接口
const packageRes = await postConsumerMallOrderMiniProgramPrePay({
orderCode: orderDetail?.orderCode,
includeAppInfo: true,
wechatAppId: "xxxxwechatAppId",
});
// 请求签名接口
const paySignRes = await postConsumerMallOrderMerchantPaySign({
content: `xxxxwechatAppId\n${timeStampVal}\n${nonceStrVal}\n${`prepay_id=${packageRes?.data}`}\n`,
});
// 通过h5的jsapi跳转到小程序支付页面带上支付所需要的参数
wx.miniProgram.navigateTo({
url: `/subPages/service/h5pay/index?timeStamp=${timeStampVal}&nonceStr=${nonceStrVal}&package=${encodeURIComponent(
packageRes?.data
)}&signType=${signTypeVal}&paySign=${encodeURIComponent(
paySignRes.data
)}&orderCode=${orderDetail?.orderCode}`,
});
};
这里有一个需要注意的点,就是传url参数的时候要encodeURIComponent一下,不然到小程序那边拿到的签名参数会有问题。
然后在小程序那边就可以利用支付参数唤起微信支付了,因为用的是taro开发的就用它举例子了,原生小程序或者uniapp开发的思路应该是一样的
export default () => {
// 获取url带的参数
const params = Taro.getCurrentInstance().router?.params as {
[key: string]: string;
};
// 进入页面开始请求支付接口
useDidShow(() => {
const {
nonceStr,
package: packageVal,
paySign,
signType,
timeStamp,
orderCode,
} = params;
// 调用小程序支付api
Taro.requestPayment({
nonceStr,
package: `prepay_id=${decodeURIComponent(packageVal)}`,
paySign: decodeURIComponent(paySign),
signType,
timeStamp,
success(res) {
set('payStatus', { ...res, payCode: 1, orderCode });
// 返回到打开h5的webview
RouterUtil.navigateBack();
},
fail() {
set('payStatus', { payCode: 0, orderCode });
// 返回到打开h5的webview
RouterUtil.navigateBack();
},
});
});
return <div />;
};
这里有一个细节就是set('payStatus', { ...res, payCode: 1, orderCode }),请求失败的时候调用的set方法相同,这个是定义的一个全局变量,让webview知道支付的结果状态好在返回到h5页面的时候根据参数跳转到支付结果页,看下webview的逻辑
useDidShow(() => {
if (get('payStatus') && get('payStatus').payCode) {
setViewUrl(
`${process.env.H5_URL}/pages/payStatus/index?status=${
get('payStatus').payCode === 1
}&payType=2&orderCode=${get('payStatus').orderCode}`,
);
set('payStatus', null);
}
});
这里就是重新定义了webview的url然后根据支付的状态来确定是否跳转到支付结果页,然后再清空掉支付结果的全局变量。
小结
整体的小程序中嵌入的h5调用微信支付的逻辑就结束了,通过这样的方法来实现h5调用微信支付,希望对大家有帮助,有帮助希望给个赞鼓励一下!!!