移动支付之收银台

974 阅读6分钟

当一家公司业务扩张,就会产生新的业务线,当业务线带来营收,也意味着公司的财务交易来源又复杂了一分,当公司快速扩张,一切为了公司业务妥协的时候,就会累积各种问题。比如,业务线营收管理混乱,各业务线混杂导致难以正确统计营收,财务对账对不上等等,如果这时候有一个统一的收银台,那很多问题就可以迎刃而解。

本文主要是从如何通过h5完成一次收银台的支付入手,讲述各种移动支付方式的使用。 如上图,这个一个正常支付流程的流程图。

接入方与收银台

从接入方跳转到收银台的h5,来源途径包括APP嵌入web-view、公众号、小程序webview、M站等等。接入方接入时,需要提供必要的参数,比如:

订单号

获取订单的基础信息,用于做基本展示,并提供给后端,做订单状态更改与数据落库

业务线编码

区分业务线,保证营收与业务线、订单等一致性

回跳地址

支付成功后的跳转地址,保证收银台的闭环,来去自由~

安全验签

用于做安全校验,进入页面时做安全验证,若不通过,则提示用户签名不通过,后续不允许继续操作,回到 回跳地址,如果是小程序可回到小程序原页面,可节省小程序安全业务域名的限制,如有业务要求,也可跳转到指定的小程序页面,注:h5跳转到小程序,需要引入的jweixin,版本要在 1.3.2或以后的版本。

收银台页面的交互

收银台获取到接入方传来的信息后,需要查询与订单相关的信息,诸如:金额、优惠等等,建议调取接口的关键入参进行加密;由于每个业务线的特性,可根据业务线配置支持的支付方式,当然也要考虑使用场景,比如小程序打开h5的话,无法像微信内直接打开h5还可以用浏览器打开,所以小程序的话,最好直接配置成微信。

当基本信息获取完毕,用户点击 “付款” 时,收银台后端需要提供发起支付的相关参数:

支付宝

支付宝一般提供一个 form 表单,只需要把这个表单提交,支付宝就会捕捉发起的相关支付请求,一般是 alipays://... 这种,属于支付宝自己定义的通信协议,支付宝接入相对比较简单,因为支付宝已经帮你考虑到了各种场景,无论是你们公司自己的APP嵌入的webview,还是浏览器发起的支付,都是相同的处理方式,下面提供一种使用 vue 的代码实例:

const div = document.createElement('div');
/* 此处form就是后台返回接收到的数据 */
div.innerHTML = res.returnData;
document.body.appendChild(div);
/* 这里最好加个延时,否则部分浏览器的 appendChild 会表现出异步进行*/
setTimeout(() => {
  document.forms[0].submit();
}, 150);

另外,如果是自己公司的APP,则需要在APP内特殊处理下scheme,处理下通信协议。

微信

微信因为场景比较多,相对比较麻烦,APP、h5、公众号、小程序的处理方式都不一样,要根据浏览器信息判断是哪种场景:

公众号

需要微信支付的六要素,注意,凡是在微信内发起的支付,都需要appid与openid,并且配置微信支付目录,传的场景都是jsapi,网上比较多,仅贴部分代码

let getBrandWCPayRequestParams = {
  appId: appId, //公众号名称,由商户传入
  timeStamp: timeStamp + '', //时间戳,自1970年以来的秒数
  nonceStr: nonceStr, //随机串
  package: packageStr,
  signType: signType, //微信签名方式:
  paySign: paySign, //微信签名
};
WeixinJSBridge.invoke(
  'getBrandWCPayRequest',
  getBrandWCPayRequestParams,
  function(response) {
    if (response.err_msg == 'get_brand_wcpay_request:ok') {
      window.location.href = returnUrl;
    }
    if (response.err_msg == 'get_brand_wcpay_request:fail') {
      window.location.href = failUrl;
    }
    if (response.err_msg == 'get_brand_wcpay_request:cancel') {
      window.location.href = failUrl;
    }
  }
);
h5

需要配置微信支付域名,传入的场景为 h5,微信给出的数据中,有一个链接,用 mwebUrl 这个key来承载,还需要手动添加个回跳地址。

let payData = JSON.parse(res.returnData);
let wxUrl = payData.mwebUrl + redirect_url;
window.location.href = wxUrl;

由于微信的特殊处理,当进入支付页面时,如下:

微信会预加载你的回跳地址的页面,所以如果中途取消支付,而你又无法捕捉这个行为,所以只能跳转到一个中间页查询订单状态,或者带上流水号,重新进入收银台,查询订单状态

小程序

小程序的环境下,h5是无法支付的,所以实际的支付行为发生在小程序内,如果要介入收银台,则需要打开收银台页面后,获取到相应的数据,然后跳转到指定的小程序页面。另外,如果需要传递支付环境,尽量使用同步的方式获取

	  wx.miniProgram.getEnv(function(res) {
        //获取当前环境
        if (res.miniprogram) {
          _this.browserInfo.isInApplets = true;
        }
      });

h5 => 小程序

let payDataOpt = {
  appId: appId, //公众号名称,由商户传入
  timeStamp: timeStamp + '', //时间戳,自1970年以来的秒数
  nonceStr: nonceStr, //随机串
  package: packageStr,
  signType: signType, //微信签名方式:
  paySign: paySign, //微信签名
}
let params = encodeURIComponent(JSON.stringify(payDataOpt));
const url = `pages/pay/pay?params=${params}`;
wx.miniProgram.navigateTo({
  url: url,
});

小程序内唤起支付

Page({
  //h5传过来的参数
  onLoad: function(options) {
   let params = decodeURIComponent(options.params);
   let payData = JSON.parse(params);
   console.log(params)
   this.goPay(payData)
  },
  
  //微信支付
  goPay(payData) {
   wx.requestPayment({
    timeStamp: payData.timeStamp,
    nonceStr: payData.nonceStr,
    package: payData.package,
    signType: payData.signType,
    paySign: payData.paySign,
    success(res) {
     console.log("支付成功", res)
     //在这里支付成功以后,自行处理后续业务操作
     wx.navigateTo({
      url: '../index/index',
     })
    },
    fail(res) {
     console.log("支付失败", res) //请根据小程序接入方的 UI 风格自行提示
    }
   })
  }
 })
APP

实际上走的是h5的场景,不过跟支付宝一样,需要修改scheme

第三方支付平台

这种接入比较简单,比如快钱这种,后端直接给你跳转链接,其中回跳地址,你传给后端,后端会透传给第三方,支付完成后,会自动跳转。

还有储蓄卡和信用卡这种,也是类似的处理方式。

支付完成后

另外为防止收银台地址被劫持篡改,最好对该回跳地址做白名单管理,不在白名单,则不允许跳转出去,当然收银台比喻能保证自身逻辑闭环,有相应的成功页和失败页。

以上,就是收银台的基本功能还有各种支付方式

补充:针对实际使用过程中,可能会遇到的各种坑。

1、支付完成后,会默认推荐关注公众号,这个一般是支付金额超过5元,通过配置完成公众号推荐

2、尽量避免使用iframe嵌入的方式,接收银台,这样会导致在使用微信支付是,微信方面发起支付的页面及按钮被遮蔽隐藏,而导致直接跳过这一步。

3、由于精度的问题,收银台前端尽量避免对数值进行处理。

4、支付宝支付时,会返回一段表单的代码,里面含有document.form[0].submit(),很多时候它并不会执行,因为在插入html元素到你的页面时,这个行为表现的像个异步,此时执行这行脚本,页面可能还找不到对应的表单,自然无法成功提交

5、微信支付,如果再公众号内支付,则会需要openid,用于收银台向微信方下单,而用户实际付钱的时候,微信会再次校验下单的openid 和微信用户的身份是否一致,如果不一致,则会报这个问题: