当一家公司业务扩张,就会产生新的业务线,当业务线带来营收,也意味着公司的财务交易来源又复杂了一分,当公司快速扩张,一切为了公司业务妥协的时候,就会累积各种问题。比如,业务线营收管理混乱,各业务线混杂导致难以正确统计营收,财务对账对不上等等,如果这时候有一个统一的收银台,那很多问题就可以迎刃而解。
本文主要是从如何通过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
和微信用户的身份是否一致,如果不一致,则会报这个问题: