接入微信支付分---授权流程设计

2,233 阅读7分钟

前言:上一篇文章中介绍了,因我们公司的行业/场景仅支持需确认模式。接下来我们就细讲一下我们与微信支付分之间的爱恨情仇~

1、微信支付分必看文档。

   1)支付分开发指引(需确认模式)

   2)API列表

   3)  需确认模式特有API-小程序调起支付分小程序-确认订单

2、产品设计。

(1)在每次点击开门购物时,创建微信支付分订单并创建自己的订单(状态为【初始化】)。

(2)创单成功,商户小程序调起微信支付分小程序,用户确认订单后返回商户小程序;微信支付系统成功回调确认信息给商户系统。 (状态由【初始化】改为【准备中】)

(3)后台收到微信支付系统的确认订单回调后,直接调用三方开门接口。

3、项目经验及踩坑。

3.1  微信支付系统回调确认订单信息慢或者不回调场景的处理。

解决方案: 建立主查机制。 在不同的场景下调用后处理逻辑也不同。

3.2 用户在微信支付分中有多笔进行中的订单,微信支付分不允许确认场景的处理。

解决方案:  自己系统的创单接口做限制,初始化和进行中的订单只允许有一笔。

注: 我们只能限制自己系统不允许在微信支付分侧创建多笔进行中的订单, 无法限制该用户使用其他的小程序在支付分有进行中的订单。 

3.3 & 3.4 前言: ``支付分返回商家侧小程序时,可在 App.onLaunch,App.onShow 中获取到query_id参数,只有用户点击支付分页面内返回按钮时,才会带上返回参数;如果用户点击页面左上角的返回图标按钮,则不会带上返回参数。

3.3 用户在支付分小程序,没确认订单,直接点击左上角返回按钮场景的处理。

前端判断是微信小程序返回,若不存在query_id,表示用户点击左上角返回,微信支付分订单未授权,需取消订单。

解决方案:前端调用取消订单接口。

3.4 用户因支付分不满足、进行中订单过多,点击页面返回按钮场景的处理。

前端判断是微信小程序返回,若存在query_id,表示用户因支付分不满足、进行中订单过多,点击支付分页面内返回按钮;或点击确认订单后自动返回。 因此前端无法判断该订单是否授权。

解决方案:前端调用验证支付分订单授权接口,后台调用微信支付系统查询订单接口。

3.5 进入支付分小程序后,直接杀死进程。 商户小程序走后续订单流程。

解决方案:每30秒执行一次定时任务, 查询订单状态为初始化且创单时间大于5分钟的订单, 调用微信支付系统查询订单接口。

3.6 解决小米10手机, 如果不是重新跳转页面或者小程序跳回的,执行onShow的参数会携带上次数据。 执行上次的逻辑,造成bug。

解决方案: 前端做逻辑处理, 存储query_id, 用存储的query_id和本次query_id对比,相同直接return; 不同更新存储的query_id,执行后续逻辑。

3、后台接口设计。

3.1 创建订单接口。

(1) 校验若同一个用户有初始化和进行中的订单则抛异常提示: 您有进行中的订单,请稍后重试。

(2)调用【微信支付系统】创建支付分订单,创建成功。 在订单表根据自己定义的订单号规则创建订单并记录支付分订单创建接口返回的信息,此时订单状态为【初始化】。

pay.weixin.qq.com/wiki/doc/ap…

(3)生成商户小程序调起微信支付分小程序的一些必要参数。

pay.weixin.qq.com/wiki/doc/ap…

3.2 取消订单接口。

判断该订单是【初始化】状态,调用微信支付系统取消支付分订单并将自己的订单状态由【初始化】改为【用户主动取消】。

3.3 微信支付分订单主查接口。

主要查询微信支付分订单的状态。

3.4 校验支付分订单是否授权接口。

调用微信支付系统查询订单接口。

若服务订单状态state为【CREATED:商户已创建服务订单】表示未授权,则调用微信支付系统取消支付分订单,并将自己的订单状态由【初始化】改为【用户主动取消】,返回前端未授权。

若服务订单状态state为【DOING:服务订单进行中】表示已授权, 则调用三方开门接口, 并将自己的订单状态从【初始化】改为【准备中】,返回前端已授权。

3.5 定时取消支付分订单接口。

每30秒执行一次定时任务, 查询订单状态为初始化且创单时间大于5分钟的订单, 调用微信支付系统查询订单接口。

若服务订单状态state为【CREATED:商户已创建服务订单】表示未授权,则调用微信支付系统取消支付分订单,并将自己的订单状态改为【用户主动取消】。

若服务订单状态state为【DOING:服务订单进行中】表示已授权, 不做处理。

4 前端调用

4.1 创单接口调用

用户商品页,点击立即开门,调用创单接口,创建成功调起支付分小程序。

// 点击立即开门,调用创单接口,创单成功调起微信支付分确认订单。
  onClickOpenDoor = async () => {
    Taro.showLoading({
      title: "加载中",
    });
    const userId = Taro.getStorageSync(`${config.env}userId`);
    const openId = Taro.getStorageSync(`${config.env}openId`);
    const createOrderResult = await createOrder({
      userId,
      openId,
      ...this.props.fridgeUrlParmas,
    });
    Taro.hideLoading();
    if (createOrderResult.code != 0) {
      return toast(createOrderResult.msg);
    }
    const data = createOrderResult.data || {};
    //  跳转微信支付分小程序
    Taro.navigateToMiniProgram({
      appId: "wxd8f3793ea3b935b8",
      path: "pages/use/use",
      extraData: data.sign || {},
    });
  };

4.2 取消订单接口调用 && 校验支付分订单是否授权接口调用

App.js 微信支付分小程序返回商户小程序

import { openTheDoor } from "./utils/webSocket";
componentDidShow(res) {
    if (res.scene === 1038) {
      // 场景值1038:从被打开的小程序返回
      const { appId, extraData } = res.referrerInfo;
      // 判断是微信支付分小程序wxd8f3793ea3b935b8跳回
      if (appId == "wxd8f3793ea3b935b8") {
        // 根据是否有query_id,处理逻辑
        return openTheDoor(extraData);
      }
    }
  }

webSocket.js 处理用户因支付分不满足或进行中订单过多点击页面返回、确认支付分订单、 页面左上角返回等业务场景的处理。

// 注意: 只有用户点击支付分页面内返回按钮时,才会带上返回参数;如果用户点击页面左上角的返回图标按钮,则不会带上返回参数。
// 商户会在app.js中的onshow的res中收到query_id,商户可以使用query_id去查询订单状态。
// 1、有query_id,调用校验支付分订单是否授权接口
// 2、无query_id,调用取消支付分订单。
export function openTheDoor(extraData = {}) {
  if (extraData && extraData.query_id) {
    const dispatch = store.getDispatch();
    // 校验支付分订单是否授权
    dispatch({
       type: "Order/checkOpenTheDoor",
       payload: extraData
     });
  } else {
     // 取消订单
     dispatch({
       type: "Order/cancelOrder",
     });
  }
}

orderDva.js 使用dva管理订单相关的数据状态。

// 场景:因支付分不满足或进行中订单过多,返回首页,并提示“您暂时无法使用零售柜哦~”
// 场景:确认订单,不做处理。
  *checkOpenTheDoor({ payload = {} }, { select, call, put }) {
      const { zffExtraData } = yield select((state) => state.Order);
      // 解决小米10手机,如果不是重新跳转页面或者小程序跳回的,执行onShow的参数携带上次数据。 所以扫码后一直进不去商品页。
      if (zffExtraData.query_id == payload.query_id) {
        return;
      }
      yield put({
        type: "updateAction",
        payload: {
          zffExtraData: payload,
        },
      });
      const res = yield call(getWechatOrderDoing, {
        orderNo: payload.query_id,
      });
      // false 表示未授权,跳转首页
      if (res.data != true) {
        Taro.switchTab({
          url: "/pages/index/index",
        });
        toast("您暂时无法使用零售柜哦~");
        return;
      }
    },