你需要知道的H5嵌入微信小程序调用微信支付

2,937 阅读5分钟

背景

由于业务属性,需要开发一个简单的商城H5,期望是能够实现商品的购买等一系列简单的闭环流程。开始以为就是开发一个简单的h5页面也没放在心上。本以为事情都会随着理想的状态发展,但是人生总有事与愿违。好巧不巧就遇到了各种坑,其中最大的坑莫过于支付的问题了。因为是要嵌入到微信小程序,以为就是和往常一样调用下微信支付就行。结果就不行,至于为什么不行和其他注意事项,且看下面陈述!

对接支付

起初因为在pc上写了购物商城,对接的微信支付和支付宝支付,使用的是支付二维码。这个对接的过程可能太过丝滑,导致对微信嵌入h5的支付功能造成了误判,看到微信有h5支付,于是果断对接h5的支付,当我满心期待去测试的时候,微信模拟器给我弹出了个大大的警告,微信小程序不支持h5方式支付的调用。于是我在社区逛了逛,又看看了看官方api接口文档。果不其然,你在小程序里用,就得使用小程序的支付调用。所以坑来。。。

内嵌的h5调用支付的方式只能是小程序的微信支付

本来是想着能支持多种支付方式,看了下文档发现想支持支付宝是不可能了,于是果断舍弃。那就只能用微信支付了啊,本以为可以快速接上微信h5支付,然后说不能用h5支付有图为证

WeChat6e2ec88f8f003b7042105fa5410ee21e.jpg 那这没办法了啊,之前以为是不管是app还是微信小程序只是嵌入个h5,提供一个容器而已,并不会干扰h5自己的逻辑,那这个图一出现,就得重新想一个方式了,于是乎就有了跳来跳去

使用小程序唤起微信支付

由于微信支付的限制,嵌入到小程序中的h5页面只能通过跳转到微信小程序的页面来唤起,所以如果在宿主小程序中嵌入h5支付,那么你得对宿主小程序进行改造(本来以为嵌入h5链接就好了),先要添加一个微信小程序的h5支付页,比如

image.png

当嵌入的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页面,这里就开始实现这个唤起支付的逻辑,首先看微信支付文档。前面一系列的准备默认都已准备好,这里直接开始接入支付功能。从文档看到小程序要调用支付有两步。

  1. 调用微信的下单接口生成与预支付订单
  2. 然后通过wx.requestPayment发起微信支付带上需要的参数

看下这个唤起支付的接口需要的参数

image.png

这里的参数可以参考文档,业务中只需要获取到package和paySign。package参数是上个生成预订单接口生成的,paySign参数则是微信的加签接口看文档 pay.weixin.qq.com/wiki/doc/ap…

image.png

就是通过一系列的算法生成的一个签名值,在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调用微信支付,希望对大家有帮助,有帮助希望给个赞鼓励一下!!!