最近在写一个聚合支付的系统,支付宝比较好掌控,可是微信支付却有着一大堆的问题亟需解决。
微信支付
微信支付除了需要传订单信息,还需要传递当前支付用户的openid,这就导致了我们不能直接通过接口在服务器创建支付单,只能自己构建“预下单”接口先在系统中进行预下单,然后在确定当前的支付对象,然后构建H5/小程序链接,在H5/小程序中获取用户openid,返回给后端进行绑定,然后进行下单支付。
对于小程序而言,获取用户的openid还是比较方便的,但是难题是怎么将订单信息与用户的openid相关联起来,同时在查询到openid之后要怎么进行下单里调用什么接口,我想这是我现在面临的难题吧!
于是,我决定把微信支付接口的调用分为以下的几个流程。
- 用户下单
- 服务器接收订单,返回订单对应的小程序链接
- 首先验证订单的合法性(API调用凭据、用户金额、支付方式等)
- 生成系统订单号
- 将系统订单号(由服务端生成)、商户订单号、商品名称等相关信息存入数据库中
- 返回带有订单号的小程序链接。
- 用户扫描小程序码
- 小程序app.js中首先获取用户的openid,ip等信息,并且存储到小程序的data中
- 在orderPay的onload中获取订单号的信息,同时验证该订单是否已经被扫描或者支付过,此时服务端应做好查重处理
- 用户点击支付,携带openid,订单号向服务器发起请求,获取支付信息,完成当前支付。
- 用户支付
- Adapay回调
- 回调给客户网站
支付宝支付
支付宝支付就相对简单了,我们只需要直接生成支付宝链接传回给前端即可完成支付。
一码聚合支付
如果想做一码聚合支付的话,就要考虑到支付宝和微信等浏览器的鉴别问题。
可行的方法有以下几种:
- 借助js完成识别,统统使用H5支付,需要做默认报错页面
- 借助小程序绑定域名完成,域名不需要做默认页面
- 两者结合
最终选择的是微信使用小程序域名跳转,支付宝采用H5跳转进行下单,这种方法需要制作非支付宝的默认的支付页面,这里就不再赘述了。
微信支付的改造
貌似微信支付不需要进行改造
支付宝的改造
这里采取和微信支付相同的策略,在链接中传递orderID,在浏览器识别到支付宝浏览器环境之后,模仿微信支付那样进行下单和其他操作。
- 用户下单
- 服务器接收订单,返回订单对应的链接
- 首先验证订单的合法性(API调用凭据、用户金额、支付方式等)
- 生成系统订单号
- 将系统订单号(由服务端生成)、商户订单号、商品名称等相关信息存入数据库中
- 返回带有订单号的链接
- 用户扫描该链接对应的二维码
- 浏览器UA判断
- 跳转到支付宝对应的页面
- 查询对应的订单信息,跳转支付宝支付页面
- 用户支付
- Adapay回调
- 回调给客户网站
至此,支付流程可以跑的通了!
附录,小程序代码:
这里只是写一下小程序相关的js代码吧,wxss和wxml代码就自行调试吧
// app.js
App({
globalData: {
openid:'',
payapiUrl:'https://pay-api.example.com',
wxapiUrl:'https://wx-api.example.com'
},
onLaunch() {
console.log(this.globalData)
var _this =this;
// 登录
wx.login({
success: res => {
//console.log(res.code)
wx.request({
url: this.globalData.wxapiUrl + '/login',
method:"POST",
data:{
code:res.code
},
dataType:"json",
header: {
"Content-Type": "application/x-www-form-urlencoded"
}
,success:function(result){
// console.log(result.data.openid)
_this.globalData.openid = result.data.openid
}
})
}
})
}
})
// pages/orderPay/orderPay.js
var app = getApp();
Page({
data: {
orderNo:"",
payMoney:"",
name:"",
merchant:""
},
onLoad: function (options) {
if(options.q){
var _this=this;
var link = decodeURIComponent(options.q);
var paramArr = link.split('=');
this.setData({
orderNo:paramArr[1]
})
wx.request({
url: app.globalData.apiUrl + '/order/getinfo',
data:{
id:paramArr[1]
},
success:function(e){
console.log(e.data)
if(!e.data.amt){
wx.redirectTo({
url: '/pages/info/error',
})
return;
}
//订单状态为已经支付或者已经扫码
//TODO:具体订单报错信息
if(e.data.status != 0){
wx.redirectTo({
url: '/pages/info/error',
})
return;
}
_this.setData({
payInfo:{
payMoney:e.data.amt,
name:e.data.name,
merchant:e.data.merchant
}
})
}
})
}else{
wx.redirectTo({
url: '/pages/info/error',
})
}
},
//支付函数
handlePay() {
var _this = this;
const {
payMoney
} = this.data
if (payMoney === ''|| payMoney ==0) return
wx.showLoading({
title: '加载中',
})
wx.request({
url: app.globalData.apiUrl + '/newpay/wechatPay',
dataType: "json",
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
method: "POST",
data: {
openid: app.globalData.openid,
orderNo:_this.data.orderNo
},
success: function (e) {
wx.hideLoading()
var rst = e.data.data;
// var rst = JSON.parse(firstRst);
wx.requestPayment({
timeStamp: rst.timeStamp,
nonceStr: rst.nonceStr,
package: rst.package,
signType: rst.signType,
paySign: rst.paySign,
success: function (res) {
wx.redirectTo({
url: '/pages/result/success',
})
return;
},
fail: function (res) {
_this.setData({
showNotice: true
})
},
complete: function (res) {}
})
}
})
}
})