一个二维码,同时实现支付宝和微信付款,这感觉是一个不难实现的功能,就像很多文章写的,只需要判断user-agent信息就可以了,但实践下来,发现这只是走了1%的路,后面的坑主要集中在两个平台的API上,以下就是个人一条路走下来技术内容的总结。
此文写于2020年初,后端使用PHP,个人非职业程序员,仅供参考。
基本条件
微信:公众号开通微信支付
支付宝:建立一个Web应用
这里的细节与技术无关,不多叙述,不过有几个点:
- 微信公众号和支付宝web应用设置中的回调地址一定要设置好,否则无法下单。
- 下单这一步可以使用微信开发者工具调试,支付宝的开发工具似乎不能调试Web应用。
- 用户付款貌似只能在手机上调试。
- 微信支付的货币单位为__分__(不同接口亦有不同),支付宝的货币单位为__元__,注意转换
使用的API
首先,最重要的,两个平台都有不同的支付产品,要实现一码多付的功能需要开通的API是:
微信:JSAPI支付
只能对公账户,开通微信支付需要进行公众号认证,300元一年。费率0.6%(非信用卡)
支付宝:当面付
可以对公亦可以对私,无其他费用。费率0.6%(非信用卡)
基本流程
这并不是一般的扫码支付情况,因为展示的二维码指向的是自己的地址,而不是微信/支付宝的付款二维码。所以应该是用微信/支付宝客户端打开对应页面后,页面通过对应的Js Bridge调起支付流程。支付宝把接口放在· 当面付 ·里,逻辑上有点不太对。
虽然用的支付产品名称完全不同,不过两家的流程是一模一样的,所以业务逻辑可以尽量统一。以下是一路成功的流程,其他情况自行补全。
- [前端] 用户扫码
- [后端] 获得用户的Open ID(微信)/User ID(支付宝)
- [前端] 用户在客户端输入金额/核对金额
- [前端] 客户点击支付按钮
- [后端] 向微信/支付宝服务器下单
- [后端] 将微信/支付宝返回的下单信息返回到前段
- [前端] 调起客户端的支付界面
- [前端] 用户支付
- [前端] 向后端查询支付是否成功
- [后端] 接收平台通知/主动向平台查询
- [前端] 显示支付成功信息
获得用户的Open ID(微信)/User ID(支付宝)
因为后面的下单需要用户的Open ID/User ID,所以在用户扫码这一步就要完成获取。
对应文档:
微信
微信SDK提供GetOpenid可以直接获得用户的OpenID
支付宝
支付宝需要先自行获得auth_code,再使用接口获得User ID
//拼接当前URL并进行Url Encode
$baseUrl = urlencode(
“https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]”);
//拼接请求URL
$url = "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=$alipay_app_id&scope=auth_base&redirect_uri=$baseUrl";
//重定向
Header("Location: $url");
//返回的请求通过GET参数传递auth_code,需自行判读是否返回了auth_code再执行以上操作
获得了auth_code就可以通过Alipay.System.Oauth.Token接口换取User ID了。可以详见支付宝对应文档。(支付宝授权的借口有好几个,亲自试过,只有这个可以用)
成功获得了用户的OpenID/UserID后,可以存储在Session中,当然也可以由前端再返回。
向微信/支付宝服务器下单
这是最核心的一步,不过却最容易实现,因为两方SDK都提供了完善的接口。需要注意微信以分为单位,支付宝以元为单位。
微信接口:统一下单,获得一串jsapi支付参数
支付宝接口:Alipay.Trade.Ctreate,获得一个Trade NO.
调起客户端的支付界面
使用后端获得的交易参数,就可以在前端(微信/支付宝客户端内)发起支付流程,以下代码主要来自官方SDK,略微整合,使用callWxPay()和callAlipay()可以分别在网页中调起微信支付和支付宝支付。
var jsApiParameters = '微信JSAPI支付参数';
var alipay_trade_no = '支付宝交易号';
function callWxPay() {
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
} else {
jsApiCall();
}
}
//调用微信JS api 支付
function jsApiCall() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
jsApiParameters,
function (res) {
if (res.err_msg == 'get_brand_wcpay_request:ok') {
//微信客户端返回支付成功
} else {
};
}
);
}
//微信模块结束--//
//--支付宝模块开始--//
function callAlipay() {
if (window.AlipayJSBridge) {
AlipayTradePay();
} else {
document.addEventListener('AlipayJSBridgeReady', AlipayTradePay, false);
}
}
function AlipayTradePay() {
// 通过传入交易号唤起快捷调用方式(注意tradeNO大小写严格)
AlipayJSBridge.call("tradePay", {
tradeNO: alipay_trade_no
}, function (data) {
if ("9000" == data.resultCode) {
//支付宝客户端返回成功
} else {
};
});
}
//--支付宝模块结束--//
向后端查询支付是否成功
用户确认支付后,微信和支付宝客户端都会返回支付成功,然而完全依赖前端信息并不安全,要有一个查询是否支付成功的机制, 所以当客户端返回支付成功后,就要开始向后台轮询支付状态
接收平台通知/主动向平台查询
接收支付通知
关于接收支付状态通知,微信文档写的比较明白,SDK也都有,这里不再不详述,不过支付宝的通知由于其文档提到“异步通知仅用于扫码支付”,尝试了一次确实也没有收到通知,所以没有做进一步的尝试,只能依赖向服务器查询订单支付状态了。
主动查询接口
微信:https://api.mch.weixin.qq.com/pay/orderquery
支付宝:Alipay.Trade.Query
官方参考文档链接
微信
微信文档虽然也有很多坑,不过相对清晰,不一一列举
pay.weixin.qq.com/wiki/doc/ap…
支付宝
支付宝的文档不好找,看了好几天只能求助客服,技术客服参差不齐,有些完全不听你说什么,像机器人一样回答问题,估计跟“支付宝小二”这个没有专业性的职位称呼有关,不过最终还是遇到明白的客服,一下给出下面这篇不在目录之内却极具针对性的文档(虽然我是动态码),基本上只看这篇就够了
线下静态码支付方案 alipay.open.taobao.com/docs/doc.ht…
PC 网页内获取用户信息 docs.open.alipay.com/284/web/
换取授权访问令牌 docs.open.alipay.com/api_9/alipa…
统一收单交易创建接口 docs.open.alipay.com/api_1/alipa…
JSAPI唤起收银台支付 docs.open.alipay.com/common/1055…