前端付出杂烩 - 微信付出(H5付出、JSAPI付出、Native付出)、付出宝付出、Ap ...

打印 上一主题 下一主题

主题 1063|帖子 1063|积分 3189

概要

近来公司开展付出,大概总结下付出流程以及遇到的问题
使用工具

  使用 uniapp 模块搭建H5页面
微信付出

官方文档链接:微信付出
付出方式使用场景H5付出   H5付出主要是在手机、ipad等移动设备中通过第三方的浏览器来唤起微信付出的付出产品。JSAPI付出JSAPI付出主要是用户通过消息或扫描二维码在微信内部打开网页时,可以调用微信付出完成下单购买的流程。Native付出Native付出是指商户系统按微信付出协议生成付出二维码,用户再用微信“扫一扫”完成付出的模式,适用于PC网站、实体店单品或订单、媒体广告付出等场景APP付出APP付出又称移动端付出,是商户通过在移动端应用APP中集成开放SDK调起微信付出模块完成付出的模式。(安卓IOS原生使用) 小程序付出  小程序付出是专门被界说使用在小程序中的付出产品。目前在小程序中能且只能使用小程序付出的方式来唤起微信付出。付款码付出   付款码付出是用户展示微信钱包内的“刷卡条码/二维码”给商户系统扫描后直接完成付出的模式。主要应用线下面对面收银的场景。 本次涉及到:
  H5端的话,微信付出大概分为两种,一种是外部浏览器拉取微信付出--- H5付出,另外一种是微信内部浏览器拉取付出 --- JSAPI
PC端,主要是使用微信付出---Native
H5付出

H5付出主要用于触屏版的手机浏览器哀求微信付出的场景,方便从外部浏览器唤起微信付出。


  • 预备工作
    申请对应商户属性的APPID以及mchid等 (一般是管理微信商户平台账号的人去申请)
  • 后台搭建和设置开辟环境,前端预备好对应页面,此处需要在微信商户平台设定回调页面链接redirect_url (主要用于付出完成后跳转从微信回归到H5页面)。
业务流程

  • 前端调用付出接口,传递订单信息
  • 后台创建订单信息,获取到微信的付出链接,传递给到前端
  • 前端直接跳转该url拉取微信付出,付出成功之后,点击完成会自动跳转至回调页面redirect_url
  • 在该回调页面上,前端可调用查询付出效果接口,根据效果返回跳转成功或失败页面,展示对应的效果
回调页面 主要功能 — 1.调用查询付出效果的接口 2.根据后台返回的效果数据跳转成功或失败页面等待,展示效果。
官方流程图

JSAPI

JSAPI付出适用于线下场所、公众号场景和PC网站场景,主要是用于微信内部付出的调用。
同理
预备工作
申请对应商户属性的APPID以及mchid等 ,设置授权目录
业务流程类似,前端页面改变的地方是 需要调用微信内部方法获取code传递给后台,以便后台获取assess_token或openId
业务流程

  • 前端 Home 页面 ,创建订单,调用订单信息接口,获取到订单Id等;
  • 在 Home 页面,判定环境-是否是微信内部环境,假如 是 - 通过微信网页授权
    https://open.weixin.qq.com/connect/oauth2/authorize?appid=XXXXX&redirect_uri=${encodeURIComponent(需要跳转的页面)} &response_type=code&scope=snsapi_base&state=STATE#wechat_redirect
  • 微信携带授权code重定向到 Home 页面, 后台可将订单数据拼接在重定向的地点后面,也可单独哀求(注意:路由是history还是hash),将code传递给后台以便获取access_token、openid,生存本地;
  • pay页面中选择付出的方式,调用后台接口,判定是否时微信内部浏览器付出
    ①YES,调用接口传递订单信息及openId,获取微信JS-API付出需要用到的参数;前端通过JSAPI接口,拉取微信付出;
    ②NO,则可以直接跳转后台返回的付出链接,拉取对应的付出;
  • 同理,在回调页面上,前端可调用查询付出效果接口,根据效果返回跳转成功或失败页面,展示对应的效果
    微信官方文档-网页授权
网页授权流程

  • 引导用户进入授权页面同意授权,获取code
  • 通过code换取网页授权access_token(与基础支持中的access_token不同)
  • 假如需要,开辟者可以刷新网页授权access_token,制止逾期
  • 通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
Home 页面(一般进入H5页面就可以获取code)
  1. onShow() {
  2.    // 1. 判断环境
  3.    if(this.isWechat()){
  4.    // 2.获取code
  5.      this.getCode()
  6.    }
  7. },               
  8. methods: {
  9.    // 判断是否微信环境环境-(针对JS-API)
  10.    isWechat() {
  11.                  var ua = navigator.userAgent.toLowerCase()
  12.                  var isWXWork = ua.match(/wxwork/i) == 'wxwork'
  13.                  var isWeixin = !isWXWork && ua.match(/MicroMessenger/i) == 'micromessenger'
  14.                  return isWeixin                       
  15.    },
  16.    // 获取code
  17.    getCode() { // 非静默授权,第一次有弹框  静默授权的话,scope=snsapi_base
  18.         // 1.授权后重定向的回调链接地址(当前页面的地址(home 地址)) window.location.href
  19.             var redirect_URL = ' http://localhost:8080/#/Home?orderId=...' //  
  20.             // ps:该地址需要添加到公众号的一个设置里面去,作为授权的目录
  21.             
  22.             // 2.公众号ID:appid( 配置公众号时的信息,可自行查看)
  23.                 var appid = 'wxgongzhonghaopeizhi'
  24.                
  25.                 // 3.截取code
  26.                 this.code = this.getUrlCode().code // 截取code       
  27.                
  28.                 // 4.判断code的有无:
  29.                 // 无 - 直接跳转微信授权网页,来获取微信授权后携带code的回调地址链接;有 - 直接调用后台接口传递给后台
  30.                 if (this.code == null || this.code == '') {
  31.                         window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid +
  32.                                 "&redirect_uri=" + encodeURIComponent(redirect_URL ) + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
  33.                         // STATE可携带信息,具体看官方文档
  34.                 } else {
  35.                         // 获取code后自己的业务逻辑 code只能用一次,会报错,注意
  36.                         //此处调用后端提供的接口,传递给后台code,后台返回access_token、openid
  37.                         this.getWechatCodeFn(this.code)
  38.                 }
  39. },                       
  40.                 // 截取url中的code方法
  41.                 getUrlCode() {
  42.                         var url = location.search
  43.                         var theRequest = new Object()
  44.                         if (url.indexOf("?") != -1) {
  45.                                 var str = url.substr(1)
  46.                                 var strs = str.split("&")
  47.                                 for (var i = 0; i < strs.length; i++) {
  48.                                         theRequest[strs[i].split("=")[0]] = (strs[i].split("=")[1])
  49.                                 }
  50.                         }
  51.                         return theRequest
  52.                 },
  53.        // 传递code给到后台
  54.         async getWechatCodeFn(code) {
  55.             const res = await getWechatCode({
  56.                         code: code
  57.                 })
  58.                 this.openid= res.data.openid
  59.                 uni.setStorageSync('openid',this.openid) // 缓存该数据 - 支付完成后记得清除对应数据
  60.         },               
  61. }                       
复制代码
pay 页面
  1. onLoad(parms) {
  2.         this.openid = uni.getStorageSync('openid')  // 获取在首页存到缓存的openid
  3. },
  4. methods: {
  5.       // 支付按钮
  6.      async payBtn(payType) {
  7.          // 提交给后端的东西,以获取到微信支付用到的参数
  8.                 const res = await OrderApi({
  9.                         orderId: this.orderId,
  10.                         payType: payType, // 1-微信支付为 2-支付宝支付 3-applePay支付
  11.                         openid : this.openid // 这个是在首页的时候通过获取用户的code,然后后端返回的一个openid
  12.                 })
  13.            // 微信内部支付
  14.            if(this.isWechat()&& payType==1){
  15.                         if (typeof WeixinJSBridge === 'undefined') {
  16.                                 if (document.addEventListener) {
  17.                                         document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
  18.                                 } else if (document.attachEvent) {
  19.                                         document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
  20.                                         document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
  21.                                 }
  22.                         } else {
  23.                             // 发起微信支付
  24.                                 this.onBridgeReady(res.data);
  25.                         }
  26.                 }else{
  27.                 // 其他支付直接跳转后台返回的支付链接
  28.                         window.location.replace(res.data);
  29.                 }
  30.         },       
  31.            // 判断是否微信环境环境-(针对JS-API)
  32.     isWechat() {
  33.                  var ua = navigator.userAgent.toLowerCase()
  34.                  var isWXWork = ua.match(/wxwork/i) == 'wxwork'
  35.                  var isWeixin = !isWXWork && ua.match(/MicroMessenger/i) == 'micromessenger'
  36.                  return isWeixin                       
  37.     },
  38.      // 发起微信支付
  39.      onBridgeReady(data) {
  40.        // 需要处理data数据:后台返回的JSON字符串,利用JSON.parse 方法将JSON字符串 构造成 JavaScript 值或对象形式。
  41.        // 微信jsapi支付需要用到的参数,对应从返回的数据种获取即可
  42.                 let { appId,timeStamp,nonceStr,signType,paySign } = JSON.parse(data)
  43.                  WeixinJSBridge.invoke('getBrandWCPayRequest',{
  44.                                 appId: appId,//公众号ID,由商户传入
  45.                                 paySign: paySign,//微信签名方式
  46.                                 signType: signType,//微信签名
  47.                                 timeStamp: timeStamp,//时间戳,自1970年以来的秒数
  48.                                 nonceStr: nonceStr,// //随机串
  49.                                 package: JSON.parse(data).package, // 此处有坑,要小心后台返回的数据形式!!!
  50.                                 debug: true
  51.                         }, (res)=> {
  52.                             // 前端调用微信的api 返回的 res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
  53.                             // !!!所以仍然需要调用后台接口进行查询订单的状态结果
  54.                                 if (res.err_msg === 'get_brand_wcpay_request:ok') {
  55.                                         // 支付成功的处理逻辑
  56.                                         uni.showToast({
  57.                                                 title: '微信支付成功',
  58.                                                 icon: 'none'
  59.                                         })
  60.                                 } else if (res.err_msg == "get_brand_wcpay_request:cancel") {
  61.                                         uni.showToast({
  62.                                                 title: '取消支付',
  63.                                                 icon: 'none'
  64.                                         })
  65.                                         // 刷新当前页面,重新获取code(有问题)
  66.                                         // window.location.reload(xxxx);
  67.                                 } else {
  68.                                         uni.showToast({
  69.                                                 title: '支付失败',
  70.                                                 icon: 'none'
  71.                                         })
  72.                                 }
  73.                         });
  74.         }                               
复制代码
注意:
授权目录实在就是H5页面的域名,例如:https://www.haha.com/pay 的授权目录可以设定为https://www.haha.com;因为微信只校验顶级域名,假如付出授权目录设置为多级目录,就会进行全匹配
PS:code一般只能使用一次,下一次的刷新需要几分钟;
第一次成功获取了 assess_token 和 openid,那么这个 code 就已经被使用过,并且会失效。假如你再次尝试使用这个 code 去获取 session_key 和 openid,微信服务器会返回 “code been used” 的错误。
1. 确保 code 只被使用一次:在服务器端,确保对于每个 code 只发起一次哀求到微信服务器。假如由于某些原因(如重试机制)多次发送了相同的 code,就会遇到这个错误。
2. 检查 code 的生成和使用流程:确保 code 是在用户授权之后立刻生成并发送到服务器端的。不要在用户授权之前就尝试获取 code,也不要在用户授权后长时间生存 code 并尝试再次使用。
3. 存储和使用 openid:一旦成功从微信服务器获取了 openid,你应该在服务器端存储它们,并在后续的用户哀求中使用这些值,而不是每次都重新哀求 code。(后台可做处置处罚,前端可以生存到本地)
官方流程图
整个流程走下来,发现微信对于不同入口拉取的付出管理的蛮严格的,需要使用对应的接口,申请对应参数 等等,不外主要是后台做处置处罚,前端处置处罚的逻辑不多。

Native付出

文档链接:微信Native付出
应用场景:Native付出适用于PC网站、实体店单品或订单、媒体广告付出等场景。
预备工作:


  • 选择接入模式:平凡商户或平凡服务商
  • 申请参数:AppID、商户号
  • 设置应用
  • 下载并设置商户证书
  • Native付出计划指引(具体看官方文档)
业务流程


  • home页面选择微信付出,调用接口,传递后台订单信息;
  • 后台返回付出链接,前端通过① qrcode插件 将链接转换为二维码 或②后台直接转换成base64,前端进行展示;来供客户扫码进行付出;
  • 扫码付出成功后,通知前端已完成付出,关闭弹框
    方案①:使用轮询,轮询后台查询效果的接口链接,获取到效果就关闭弹框;
    方案②:使用webSocket进行监听付出效果,检查付出状态;
    前端生成付出二维码

    • 下载qrcode插件依赖包大概直接引入本地文件
      npm install qrcode --save-dev
    • 对应页面中引入该插件

  1. <template>
  2.   <div>
  3.     <button @click="getPhoto"></button>
  4.     <el-dialog v-model="dialogVisible" title="支付二维码" width="800" @closed="handleClose">
  5.         <canvas id="QRCode_Img" style="width: 280px; height: 280px"></canvas>
  6.     </el-dialog>
  7.   </div>
  8. </template>
  9. <script>
  10. import QRCode from "qrcode"; //引入生成二维码插件
  11. export default {
  12.   data() {
  13.     return {
  14.       payUrl:"支付链接",
  15.       dialogVisible:false
  16.     };
  17.   },
  18.   methods: {
  19.     getPhoto(){
  20.        // 获取支付链接
  21.        getTypeList({orderId}, this.headers).then(res => {
  22.         if(res.status == 0) {
  23.              this.dialogVisible = true // 打开弹窗
  24.              this.payUrl = res.data // 支付链接
  25.              // 此处需要使用 this.$nextTick 保证弹框出现,避免弹框未出现,导致无法绘制图片
  26.              this.$nextTick(()=>{
  27.                 generateQRCode()
  28.              })
  29.           }
  30.         }).catch(err=>{console.log(err)})
  31.     },
  32.     // 支付payUrl链接 转变为 二维码
  33.     generateQRCode() {  
  34.       // 设置qrcode参数
  35.       let opts = {
  36.         errorCorrectionLevel: "H", //容错级别
  37.         type: "image/png", //生成的二维码类型
  38.         quality: 0.3, //二维码质量
  39.         margin: 0, //二维码留白边距
  40.         width: 180, //宽
  41.         height: 180, //高
  42.         text: this.payUrl, //二维码内容(支付的链接)
  43.         color: {
  44.           dark: "#666", //前景色
  45.           light: "#fff", //背景色
  46.         },
  47.       };
  48.       let photo= document.getElementById("QRCode_Img");
  49.       // 绘制canvas
  50.       QRCode.toCanvas(photo, this.qrUrl, opts, function (error) {
  51.         if (error) {
  52.           console.log("二维码加载失败", error);
  53.         }
  54.       });
  55.       // 此处可开启 轮询后台接口,查询用户支付结果 或者 使用websocket
  56.     },
  57.     handleClose(){
  58.       this.dialogVisible = false
  59.    }
  60.   }
  61. };
  62. </script>
  63. <style>
复制代码
官方流程图

付出宝付出、ApplePay付出

付出宝文档:付出宝付出
ApplePay文档:ApplePay付出
流程和H5付出类似,前端的工作不多,主要是跳转 后台返回的付出链接拉取付出
其中,ApplePay付出后台流程比较复杂,需要接入对应的供应商才可,而且收的手续费比较高。
补充说明:
H5怎样判定所属的环境
使用场景:用户一扫码,就开始获取用户所使用哪种付出进行扫码,前端 默认选择到对应的付出方式
  1. getDefaultPay(){
  2.       // window.navigator.userAgent属性包含了浏览器类型、版本、操作系统类型、浏览器引擎类型等信息,这个属性可以用来判断浏览器类型
  3.           var ua = window.navigator.userAgent.toLowerCase();
  4.           //通过正则表达式匹配ua中是否含有MicroMessenger字符串
  5.           if(ua.match(/MicroMessenger/i) == 'micromessenger'){ // 微信扫码  
  6.                  this.payType = 1
  7.           }else if(ua.match(/AlipayClientHK/i) == 'alipayclienthk'){ // 支付宝港版扫码
  8.                 this.payType = 2
  9.           }else if(ua.match(/AlipayClient/i) == 'alipayclient'){ // 支付宝扫码
  10.                 this.payType = 3
  11.           }else{ // 浏览器扫码设置的默认值
  12.                 this.payType = 1
  13.           }
  14. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

尚未崩坏

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表