支付宝和微信聚合支付

1,672 阅读4分钟

场景

  • 给用户一个订单二维码,然后用户扫描二维码,如果是在微信中扫码,则进行微信支付,如果是在支付宝中扫码,则进行支付宝支付
  • 给用户一个支付链接,当用户在微信打开这个链接时,进行微信支付,当用户在支付宝中打开时,进行支付宝支付

聚合支付的定义

  • 聚合支付是指通过搭建一个h5的小程序,然后用户进入到这个小程序进行商品选择准备支付后,在小程序的内部判断用户此时是在微信打开这个小程序,还是在支付宝打开,如果是在微信打开,则调起微信JSAPI支付,如果是在支付宝打开,则调起支付宝JSAPI支付
  • 用户通过微信打开支付链接时,微信内部其实做了一个中转,才会跳回我们自己的h5小程序,这个需要后端进行处理
  • 判断用户是在微信打开,还是在支付宝打开,是通过判断当前用户打开这个支付链接的浏览器类型来实现
  • 综上,其实聚合支付就是将两种支付融合进一个h5小程序,在支付时通过判断打开这个h5小程序的浏览器环境,然后调起相应环境的JSAPI支付

微信和支付宝的JSAPI支付

引入支付宝支付APIap

<script src="https://gw.alipayobjects.com/as/g/h5-lib/alipayjsapi/3.1.1/alipayjsapi.inc.min.js"></script>

判断支付链接是在微信打开还是在支付宝打开

//由于微信和支付宝浏览器不支持Symbol类型,所以使用1,2代替
const wxSymbol = 1
const aliSymbol = 2
function judgeBrowser() {
    const browser = navigator.userAgent.toLowerCase();
    // 支付宝
    if (browser.match(/Alipay/i) == "alipay") {
        return aliSymbol;
        // 微信
    } else if (browser.match(/MicroMessenger/i) == "micromessenger") {
        return wxSymbol;
    }
}
export {
    wxSymbol, aliSymbol, judgeBrowser
}

通过路径参数获取订单编号

getQueryString(type) {
  //获取查询字符串
  const search = location.search;
  const urlSearchParams = new URLSearchParams(search);
  // 将查询字符串转换为对象
  this.orderOptions = Object.fromEntries(urlSearchParams.entries());
  if (type === aliSymbol) {
    if (this.orderOptions.orderNo) {
      this.orderOptions.state = this.orderOptions.orderNo;
    } else {
      const urlFragment = location.href.split("/");
      this.orderOptions.state = urlFragment[urlFragment.length - 1];
    }
    return;
  }
},

根据orderNo获取订单详细信息

支付宝环境下需要用户授权获取code

onGetAuthCode() {
  return new Promise((resolve, reject) => {
    // 已经授权过的,直接授权信息返回
    if (this.authInfo) {
      return resolve(this.authInfo);
    }
    ap.getAuthCode({ appId: "开放平台应用 id", scopes: ["授权类型"] }, (res) => {
      if (!res.error) {
        return resolve(res);
      }
      reject(res.error);
    });
  });
}

判断支付环境,然后调用相应的支付环境的统一下单接口

async onPayment() {
  const browserType = judgeBrowser();
  if (browserType === aliSymbol) {
      //支付宝先授权,再调用统一下单接口
    const authInfo = await this.onGetAuthCode();
    this.authInfo = authInfo;
    this.onAliPayUnifiedOrder();
  } else if (browserType === wxSymbol) {
    this.onWxPayUnifiedOrder();
  }
},

支付宝环境下的统一下单以及支付

async onAliPayUnifiedOrder() {
  let result = {};
  // 统一下单
  if (this.apiPayResult.payParam) {
      //已经调用过统一下单接口获取到支付所需要参数,不需要再次调用统一下单接口
    result = this.apiPayResult;
  } else {
     //判断是否已经授权,只有授权才能调用统计下单接口
    if (this.authInfo?.authCode) {
        //接口参数
      const params = {
        code: this.authInfo?.authCode,
        orderNo: this.orderOptions.state,
        payType: "alipay",
      };
      //调用后端提供的统一下单接口
      const res = await this.$u.api.aliPayUnifiedOrder(params);
      if (res.code === 200) {
        result = res.data;
        //保存统一下单接口返回的数据
        this.apiPayResult = res.data;
      }
    }
  }
  // 支付
  if (this.apiPayResult.payParam) {
    ap.tradePay({ tradeNO: this.apiPayResult.payParam }, (res) => {
      //支付完成的逻辑
      if (res.resultCode == 9000) {
        this.$emit("accomplish");
      }
    });
  }
}

微信环境下的统一下单以及支付

async onWxPayUnifiedOrder() {
  let result = {};
  if (this.wxPayResult.appId) {
  //已经调用过统一下单接口获取到支付所需要参数,不需要再次调用统一下单接口
    result = this.wxPayResult;
  } else {
    const params = {
      code: this.orderOptions.code,
      orderNo: this.orderOptions.state,
      payType: "weChat",
    };
    //调用后端提供的统一下单接口
    const res = await this.$u.api.wxPayUnifiedOrder(params);
    if (res.code === 200) {
      result = res.data.result;
      //保存统一下单接口返回的数据
      this.wxPayResult = res.data.result;
    }
  }
  // 支付
  if (result.appId) {
    WeixinJSBridge.invoke(
      "getBrandWCPayRequest",
      {
        ...result,
      },
      // 支付完成回调函数,支付完成后没有执行,不知道是什么原因
      function (payRes) {
        this.result = payRes;
        //支付完成的逻辑
        if (res.err_msg == "get_brand_wcpay_request:ok") {
          this.$emit("success");
        }
      }
    );
  }
},
  • 微信支付成功后,返回支付拉起页面,测试发现没有执行支付完成回调函数,暂时不清楚是什么原因

支付流程图

image.png

如何在vue生成二维码

下载依赖包

npm install qrcodejs2 -S

创建二维码

<template>
  <button @click="onCreateQrcodeHandle">生成二维码</button>
  <button @click="onClearQrcodeHandle">清除</button>
  <div class="box" ref="boxRef"></div>
</template>
<script lang="ts" setup>
import { ref, shallowRef } from "vue";
import QRCode from "qrcodejs2";
const boxRef = ref();
const qrcode= shallowRef<InstanceType<typeof QRCode>>()
const onCreateQrcodeHandle = () => {
  qrcode.value = new QRCode(boxRef.value, {
    text: "你好",
    // 指定二维码的宽高
    width:150,
    height:150,
    // 二维码背景颜色
    colorLight:"#00ff00",
    // 二维码前景颜色
    colorDark:"#ff0000",
    // 二维码容错级别
    correctLevel:QRCode.CorrectLevel.L
  });
};
//清除二维码
const onClearQrcodeHandle = ()=>{
  qrcode.value.clear();
}
</script>

参数API

QRCode参数

new QRCode(element, option)

名称默认值说明
element-承载二维码的DOM元素的ID
option-参数设置

Option参数

名称默认值说明备注
text-二维码承载的信息如果是string那没有什么好说的。 如果是url的话,为了微信和QQ可以识别,连接中的中文使用encodeURIComponent进行编码
width256二维码宽度单位像素(百分比不行)
height256二维码高度单位像素(百分比不行)
colorDark'#000000'二维码前景色英文\十六进制\rgb\rgba\transparent都可以
colorLight'#ffffff'二维码背景色英文\十六进制\rgb\rgba\transparent都可以
correctLevelQRCode.CorrectLevel.L容错级别由低到高 QRCode.CorrectLevel.L QRCode.CorrectLevel.M QRCode.CorrectLevel.Q QRCode.CorrectLevel.H

API接口

名称参数说明使用
clear-清除二维码qrcode.clear()
makeCodestring替换二维码(参数里面是新的二维码内容)qrcode.makeCode('new content')

清除二维码的方式

方案1

qrcode.clear()测试发现没有反应,希望得到大家帮忙处理这个问题

方案2

qrcode.value = null
boxRef.value.innerHTML = ""