V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hzqcn
V2EX  ›  前端开发

从前端角度从零开始对接微信支付

  •  
  •   hzqcn · 2022-05-17 15:16:50 +08:00 · 1551 次点击
    这是一个创建于 925 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    微信支付业务,针对小程序、微信浏览器和非微信浏览器中的网页的三种场景,我们可以分别通过官方提供的 小程序支付、JSAPI 支付、H5 支付来开发。

    准备工作

    开通微信商户号、微信公众号然后按照步骤准备一堆资料审核,然后设置相关配置。所以最好提前准备资料审核以免耽误开发进度。配置的步骤:官方文档,直接按照官方文档配置就行了。需要特别注意的是配置商户号的支付授权目录和公众号的授权域名必须一致,不然会调起支付失败的!

    参考资料:
    JSAPI 支付配置文档
    H5 支付配置文档
    小程序支付
    JS-SDK

    H5 支付

    开发流程

    1. 请求创建订单接口拿到订单数据(orderId,订单号,支付金额)
    2. orderId请求支付接口,获得 mweb_url
    3. 跳转到微信支付中间页 mweb_url ,然后自动调用微信支付
    4. 支付成功后跳转到配置的返回页(请求支付时携带的参数redirectUrl

    参考文档

    实现代码

    伪代码😂

    async wxPayByH5() {
      // 商品信息
      let goodsList = { goodsId: 1 };
      let params = {
        goodsList: goodsList,
      }
      // 1. 创建订单
      let data = await createOrder(params);
      // 获得 订单 id:orderId ;订单总金额:orderTotalPrice ;订单号:orderNo
      let { orderId, orderTotalPrice, orderNo } = data;
      
      let paramsPay = {
        orderId,
        // redirectUrl: 支付完成后返回的页面
        redirectUrl: `${location.origin}/orderList`
      };
      // 2.请求支付
      let { mweb_url } = await wxPay(params);
      // 3.跳转微信支付中间页
      window.location.replace(mweb_url);
    },
    

    注意事项

    • 商户号的支付授权目录和公众号的授权域名必须一致
    • 需对 redirect_url 进行 urlencode 处理(让后端处理吧🤭)
    • 调试需在线上环境(需要部署到公网服务器并映射到公众号配置的安全域名)
    • H5 支付只能在非微信浏览器中调起,JSAPI 支付是在微信浏览器环境调起的

    JSAPI 支付

    开发流程

    1. 请求创建订单接口拿到订单数据(订单 id ,订单号,支付金额)
    2. 通过微信网页授权,携带授权 code 重定向到订单支付页,并把订单数据拼接在重定向的地址后面
    3. 到支付页后
      1. 获取地址栏上的 code、订单数据(orderId),
      2. 然后请求支付接口获得我们需要的数据(该数据保函了wx.configwx.chooseWXPay两个方法需要的传参)
      3. 通过 js-sdk 提供的方法发起支付
        1. 先通过 js-sdk 提供的 wx.config() 注入权限验证配置
        2. 再通过 wx.ready() 接口处理成功验证
        3. 再通过 wx.checkJsApi() 判断客户端版本是否支持指定 JS 接口
        4. 再在 wx.checkJsApi() 里成功回调函数中调用 wx.chooseWXPay() 发起微信支付请求
    4. 通过 wx.chooseWXPay() 支付成功回调:cancel: function(res){},支付失败回调:fail: function(err){},取消支付回调 cancel: function(res){}分别处理不同支付结果

    参考文档 JS-SDK

    实现代码

    封装好获取微信授权 code 的方法和获取地址栏中指定参数的方法

    /**
     * @description 截取 url 中的指定参数
     * @param {*} queryName 需要截取的参数
     * @returns 
     */
    export const getUrlParam = (queryName) => {
      return decodeURIComponent((new RegExp('[?|&]' + queryName + '=' + '([^&;]+?)(&|#|;|$)').exec(location.href) || [, ""])[1].replace(/\+/g, '%20')) || null
    }
    
    /**
     * 获取微信支付的 code ,并传入回调地址
     * @param {*} url 
     */
    export function getWxCode(url) {
      let wxUrlStart = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' + wechatAppId + '&redirect_uri=';
      let wxUrlEnd = '&response_type=code&scope=snsapi_base&state=STATE&connect_redirect=1#wechat_redirect';
      let redirect_uri = encodeURIComponent(url);
      let allUrl = wxUrlStart + encodeURIComponent(redirect_uri) + wxUrlEnd;
      window.location.replace(allUrl);
    }
    

    订单页创建订单,并且微信授权拿到 code ,重定向到订单支付页(地址栏携带 orderId ,订单金额等订单数据和 code )

    async createOrder() {
        // 商品信息
        let goodsList = { goodsId: 1 };
        let params = {
            goodsList: goodsList,
        }
        // 1. 创建订单
        let data = await createOrder(params);
        // 获得 订单 id:orderId ;订单总金额:orderTotalPrice ;订单号:orderNo
        let { orderId, orderTotalPrice, orderNo } = data;
        // 微信网页授权后的重定向地址
        let url = `${location.origin}/pay?orderTotalPrice=${orderTotalPrice}&orderNo=${orderNo}&orderId=${orderId}`
        // 上面封装的微信授权网页方法
        getWxCode(url);
    },
    
    

    支付页的代码

    // jsapi 支付
    async wxPayByJsApi() {
      let _this = this;
      // 获取订单数据和授权 code
      const { orderTotalPrice, orderNo, orderId} = _this.$route.query;
      this.orderInfo = {
        orderTotalPrice, orderNo, orderId, orderSource
      }
      _this.code = getUrlParam('code');
      
      let params = {
        orderId: _this.orderInfo.orderId,
        code: _this.code,  // 授权微信拿到的 code
      }
      // 请求后端接口支付
      let resp = await wxPay(params);
      // 调用后台接口
      wx.config({
        debug: false, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来,若要查看传入的参数,可以在 pc 端打开,参数信息会通过 log 打出,仅在 pc 端时才会打印。
        appId: wechatAppId, // 必填,公众号的唯一标识
        timestamp: resp.timeStamp, // 必填,生成签名的时间戳
        nonceStr: resp.nonceStr, // 必填,生成签名的随机串
        signature: resp.signature,// 必填,签名
        jsApiList: ['chooseWXPay'] // 必填,需要使用的 JS 接口列表
      });
      // doc: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#1
      // 注入权限验证配置
      wx.ready(function () {
        // 判断当前客户端版本是否支持指定 JS 接口
        wx.checkJsApi({
          jsApiList: ['chooseWXPay'], // 需要检测的 JS 接口列表,所有 JS 接口列表见附录 2,
          success: (res) => {
            // 以键值对的形式返回,可用的 api 值 true ,不可用为 false
            // 如:{ "checkResult": { "chooseImage": true }, "errMsg": "checkJsApi:ok" }
            // 发起微信支付请求
            wx.chooseWXPay({
              timestamp: resp.timeStamp, // 支付签名时间戳,注意微信 jssdk 中的所有使用 timestamp 字段均为小写。但最新版的支付后台生成签名使用的 timeStamp 字段名需大写其中的 S 字符
              nonceStr: resp.nonceStr, // 支付签名随机串,不长于 32 位
              package: resp.package, // 统一支付接口返回的 prepay_id 参数值,提交格式如:prepay_id=\*\*\*)
              signType: resp.signType, // 微信支付 V3 的传入 RSA,微信支付 V2 的传入格式与 V2 统一下单的签名格式保持一致
              paySign: resp.paySign, // 支付签名
              success: (res) => {
                // 支付成功后的回调函数
                _this.$router.push({
                  path: '/payResult',
                  query: {
                    orderTotalPrice: _this.orderInfo.amount,
                    orderNo: _this.orderInfo.orderNo,
                    orderId: _this.orderInfo.orderId
                  }
                });
              },
              fail: (err) => {
                _this.$router.go(-1);
              },
              cancel: function (err) {
                // 用户取消支付
                _this.$router.go(-1);
              },
            });
          }
        });
      });
      wx.error(err => {
        _this.$router.go(-1);
      })
    },
    

    小程序支付

    开发流程

    1. 小程序端请求创建订单接口,后端统一下单获取 orderId 并返回
    2. 小程序端获取通过 wx.login() 获取 code
    3. 小程序端拿这 codeorderId 请求后端接口,获取支付所需数据
    4. 获取支付所需数据之后,小程序端调用 wx.requestPayment() 接口,直接调用起支付页面
    5. 判断是否支付成功后的逻辑

    小程序文档 wx.login()
    小程序文档 wx.requestPayment()

    实现代码

    async function wxPay(goodId) {
      // 1. 创建订单 获取 orderId
      let CreateTheOrder = {
        goodId, // 商品 id
      }
      let orderId = await createOrder(params);
      // 2. 获得 code
      let code = await wxlogin(); // 基于 pr 封装的 wx.login()方法
      // 3. 获取支付的数据
      let paramsPay = {
        orderId,
        code,
      }
      let payData = await wxXcxPay(paramsPay);
      // 4. 发起支付
      let res = await payment(payData); // 基于 pr 封装的 wx.requestPayment()方法
      // 5. 判断是否支付成功
      let payResult = res.errMsg;
      if (payResult == "requestPayment:ok") {
        console.log("支付成功");
      } else if (payResult == "requestPayment:fail cancel") {
        console.log("用户取消支付");
      } else {
        console.log("支付失败");
      }
    }
    

    注意事项

    1. 申请微信小程序账号申请成功可拿到 AppID(小程序 id )和 AppSecret(小程序密钥)申请类型为企业性质,否则无法接入微信支付
    2. 微信小程序认证通过认证的小程序才能接入微信支付和绑定商户平台
    3. 申请商户平台账号需要第一步申请的 AppID 申请成功可拿到 MchID(商户 id )和 MchKey(商户密钥)
    4. 信小程序关联商户号微信和商户都认证成功后,在微信后台微信支付菜单中进行关联接入微信支付
    5. 在微信后台微信支付菜单中进行接入

    写在最后

    我是 AndyHu,目前暂时是一枚前端搬砖工程师。

    文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注呀😊

    未经许可禁止转载💌

    speak less ,do more.

    第 1 条附言  ·  2022-05-17 17:49:13 +08:00

    刚刚有朋友提醒我 hash 模式下文中的JSAPI支付方式中步骤二:授权微信网页获取code会失败。
    如果你是hash模式的话步骤二建议看这篇文章

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1011 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 21:51 · PVG 05:51 · LAX 13:51 · JFK 16:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.