Mini program WeChat payment process (mall balance payment, coupon payment, points offset, WeChat payment)

Flow chart of mini program WeChat payment:

1. Log in to the WeChat public platform and activate the WeChat payment function

This is the first step in preparation. Make sure that the payment function corresponding to the mini program has been turned on.

 

2. Log in to the WeChat merchant platform

This step requires obtaining two parameters, one is the merchant number and the other is the payment key, as shown in the figure below

Note that the secret key must be protected, which is equivalent to a payment password. This parameter is required for each signature. This parameter can only be seen when it is set, and cannot be seen at other times. So remember it!

3. After preparation, upload the code

 The merchant system of WeChat applet is generally developed in the form of an interface. The applet transfers parameters and receives data by calling the interface agreed with the backend. In the mini program payment aspect, you also need to interact with the WeChat server. The process is roughly like this:

 1. The applet calls wx.login() to obtain the code and passes it to the merchant server to obtain the user's openID.

We know that in the WeChat platform, the openID of the same official account is different. It is the id for user identification. In other words, we use openID to distinguish different users. Anyone with a WeChat development background should be familiar with this. In order to know who is paying, we need to get the openid of the current user first. So how to get the openID? Look at the picture below:

The applet calls wx.login() to obtain the temporary login credential code and sends it back to the developer server.
The developer server exchanges the code for the user's unique identifier openid and session key session_key.

Can not read it? Don’t worry, let me explain slowly. The business process is roughly that first you have to call wx.login() in the applet code to get the code from WeChat. After getting it, pass the code to the merchant server through request, and then The merchant server requests session_key and openID from the WeChat server through Sao operation.

The code is as follows (mini program):

getToken: function () {
    //调用登录接口
    wx.login({
      success: function (res) {
        var code = res.code;
        wx.request({
          url: "商户服务器登陆url", 
          data: {
            code: code
          },
          method: 'POST', 
          success: function (res) { 
            wx.setStorageSync('token', res.data.token); //存在小程序缓存中
          },
          fail: function (res) {
            console.log(res.data);
          }
        })
      }
    })
  }


By calling these lines of code, you can request the code from the WeChat server and pass the code to the merchant server. Remember that it is best to use post to send requests. I don’t need to talk about security, because it prevents others from abusing the interface. So we use token for verification. And store the token returned by the merchant server in the mini program cache.

So what should the server do?

We concatenate the code submitted through the mini program with the APPID and APPSECRET of the mini program and the following url, and use the url to make a get request.
The data returned by https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
is a json object. We use json_decode(JSON,true) to parse it into an array. The data includes users. After obtaining the openID and session_key, we should store the openID in the database. It represents the user's identity, so how should the token be generated?

2. Token generation and caching

We connect id and openid based on a user table. The id corresponding to openID is the user's uid. We can encapsulate it like this

//要缓存的数据数组
$cacheValue = $result;   //包含openID和session_key
$cacheValue['uid'] =$uid;   //用户id
 
$cacheValue['scope'] =ScopeEnum::User;   //用户权限级别

As a caching method, we can choose redis, memcache, file cache, etc., and use key-value pairs for storage. Remember to set the expiration time. We use token to assign the key here. The token can be generated in this way:

//获取32位随机字符串
$str = getRandChar(32);   //自定义方法生成32位随机串
//三组字符串进行md5加密
$timeStamp =$_SERVER['REQUEST_TIME_FLOAT'];
//salt
$salt = config('secure.token_salt'); //随机字符串
//返回token
 
return md5($str.$timeStamp.$salt);

This algorithm basically guarantees the uniqueness of the token. Because the value is the array where the openID and session_key we obtained are located, we need to convert the array into json to save it. In the future code, when we need openID or uid, etc., we can get it directly from the cache.

Third, call the unified ordering interface, obtain the prepay_id, and sign again (the backend is completed, no detailed explanation)

1. Click the 'Submit Order' button to create an order, clear the shopping cart, and complete payment.

<van-submit-bar
  price="{
   
   { allGoodsAndYunPrice*100 }}"
  suffix-label="+{
   
   {totalScoreToPay}} 积分"
  button-text="提交订单"
  bind:submit="goCreateOrder"
/>

2. Create an order, clear the shopping cart, and call the payment function 

const wxpay = require('../../utils/pay.js')'
//去创建订单
goCreateOrder(){
  //检测实名认证状态
 //创建订单 
this.createOrder(true)
}
//创建订单
createOrder(){
var that = this;
var loginToken = wx.getStorageSync('token') // 用户登录 token
var remark = this.data.remark; // 备注信息
let postData = { //创建订单需要的参数
      token: loginToken,//token
      goodsJsonStr: that.data.goodsJsonStr, //购买的商品数据列表
      remark: remark,//备注信息
      peisongType: that.data.peisongType// // 配送方式 kd,zq 分别表示快递/到店自取
    };
 //调用创建订单接口,传递此次订单相关数据   
WXAPI.orderCreate(postData).then(function (res) {
      that.data.pageIsEnd = true 
      if (res.code != 0) {
        that.data.pageIsEnd = false
        wx.showModal({
          title: '错误',
          content: res.msg,
          showCancel: false
        })
        return;
      }

    if ("buyNow" != that.data.orderType) { //订单类型,购物车下单或立即支付下单,默认是购物车,
        // 清空购物车数据
        WXAPI.shippingCarInfoRemoveAll(loginToken)
      }
        that.setData({
          totalScoreToPay: res.data.score, //积分
          isNeedLogistics: res.data.isNeedLogistics, 是否需要物流信息
          allGoodsAndYunPrice: res.data.amountReal,//总价=总商品价+总运费
          yunPrice: res.data.amountLogistics,//运费
          hasNoCoupons,//没有优惠券
          coupons,//优惠券
          couponAmount: res.data.couponAmount //优惠金额
        });
        that.data.pageIsEnd = false
        return;
      }
      //创建订单后的操作
      that.processAfterCreateOrder(res)
    })
   //创建订单后的操作  
  async processAfterCreateOrder(res) {
    // 直接弹出支付,取消支付的话,去订单列表
    const balance = this.data.balance
    if (balance || res.data.amountReal*1 == 0) {
      // 有余额
      const money = (res.data.amountReal * 1 - balance*1).toFixed(2)
      if (money <= 0) {
        // 余额足够
        wx.showModal({
          title: '请确认支付',
          content: `您当前可用余额¥${balance},使用余额支付¥${res.data.amountReal}?`,
          confirmText: "确认支付",
          cancelText: "暂不付款",
          success: res2 => {
            if (res2.confirm) {
              // 使用余额支付,传token,和订单id
              WXAPI.orderPay(wx.getStorageSync('token'), res.data.id).then(res3 => {
                if (res3.code != 0) {
                  wx.showToast({
                    title: res3.msg,
                    icon: 'none'
                  })
                  return
                }
                wx.redirectTo({
                  url: "/pages/order-list/index"
                })
              })
            } else {
              wx.redirectTo({
                url: "/pages/order-list/index"
              })
            }
          }
        })
      } else {
        // 余额不够
        wx.showModal({
          title: '请确认支付',
          content: `您当前可用余额¥${balance},仍需支付¥${money}`,
          confirmText: "确认支付",
          cancelText: "暂不付款",
          success: res2 => {
            if (res2.confirm) {
              // 使用余额支付
              wxpay.wxpay('order', money, res.data.id, "/pages/order-list/index");
            } else {
              wx.redirectTo({
                url: "/pages/order-list/index"
              })
            }
          }
        })
      }
    } else {
      // 没余额
      wxpay.wxpay('order', res.data.amountReal, res.data.id, "/pages/order-list/index");
    }
  },
//查看用户资产
  async userAmount() {
    const res = await WXAPI.userAmount(wx.getStorageSync('token'))
    if (res.code == 0) {
      this.setData({
        balance: res.data.balance
      })
    }
  },
}  
 4. After wx.requestPayment() obtains five parameters, it initiates payment
const WXAPI = require('apifm-wxapi')

/**
 * type: order 支付订单 recharge 充值 paybill 优惠买单
 * data: 扩展数据对象,用于保存参数
 */
function wxpay(type, money, orderId, redirectUrl, data) {

  WXAPI.wxpay(postData).then(function (res) {
    if (res.code == 0) {
      // 发起支付
      wx.requestPayment({
        timeStamp: res.data.timeStamp, //时间戳
        nonceStr: res.data.nonceStr,,//随机字符串
        package: res.data.package,//订单详情扩展字符串
        signType: res.data.signType//签名方式
        paySign: res.data.paySign,//签名
        fail: function (aaa) {
          console.error(aaa)
          wx.showToast({
            title: '支付失败:' + aaa
          })
        },
        success: function () {
          // 提示支付成功
          wx.showToast({
            title: '支付成功'
          })
          wx.redirectTo({
            url: redirectUrl
          });
        }
      })
    } else {
      wx.showModal({
        title: '出错了',
        content: JSON.stringify(res),
        showCancel: false
      })
    }
  })
}

module.exports = {
  wxpay: wxpay
}
5. Payment callback

Guess you like

Origin blog.csdn.net/2301_77456141/article/details/133868897