先上图
场景
本例的代付场景是,开发一款APP应用,APP中具有支付能力,按照微信
支付对接要求已经完成APP支付的对接。现在要玩点新花样,找人代付订
单。用户在APP中下单后选择找人代付,将分享H5页面到微信中的好
友。好友在微信中打开H5页面,H5页面展示订单信息,并完成代付。
业务分析
代付功能在某些2C的应用中会用到,当然涉及支付的应用都可以有代付
功能。本文分析如何实现微信代付功能的技术设计。
根据微信支付的官方文档,微信支付对接支持JSAPI支付、APP支付、H5
支付、Native支付、小程序支付等,实际业务中应当根据使用的API类型
完成支付和代付功能设计。需要注意:微信没有代付接口,所谓的代付功
能实际是想微信发起支付API的对接。
还需要注意,微信支付对接在开发之前需要根据使用场景做接入准备,具
体可以阅读微信支付官方文档。
统一下单与支付
微信支付前需要先向微信发起“统一下单”,如果统一下单请求正常微信会返回预支
付ID,预支付id是支付接口需要用到参数。
有个原则,如果使用JSAPI拉起支付,则需要用JSAPI先发起“统一下
单”,如果使用APP支付,则需要先请求APP支付的统一下单。
不同的支付对接不同混用“统一下单”。
在微信内完成代付
大多数代付,是通过分享完成的。注意分享是第一步,也是比较重要的一步。
为什么很多代付是基于微信支付实现的,因为社交软件的特点催生了好友代付的需
求场景。
微信内支付
另外出于对后安全性的考虑,分享出去的页面只能在微信中打开。于是可以选择的
代付支付方式有:
1.微信JSAPI支付:商户已有H5商城网站,用户通过消息或扫描二维码在微信内
打开网页时。
2.小程序支付:商户已有微信小程序,用户通过好友分享或扫描二维码在微信内
打开小程序时,可以调用微信支付完成下单购买的流程。
微信外支付可以阅读微信H5支付文档。
交互
APP与后端交互:
在订单下单,分享环节的设计主要围绕APP与后端系统的交互。
H5与后端的交互:
在分享完成后的,订单数据信息、用户数据信息的加载与展示,拉起支付等操
作。
设计上应当考虑H5页面对用户数据、订单数据的敏感性与安全性,这里主要考虑
接口的匿名访问权限设计与跨域问题。用户分享H5页面出去后,实际需要在访问
H5页面时通过Get请求携带一些不要的参数,为安全考虑参数应当在生成分享链
接前加密处理。
安全性设计
H5页面被浏览器渲染后,向后端请求用户数据和订单数据,后端必须进行安全性
的校验。首先用户信息和订单数据信息敏感,但是根据代付的分享业务设计,还
要具有可匿名访问和跨域的设计。
1.跨域问题:将H5页面的访问地址固化,可在后端对H5页面中的匿名请求进行校
验,注意Referer校验并不可靠,因为它可以伪造。
2.请求签名:应当设计签名算法和逻辑,防止伪造。
3.订单的“统一下单”一定要在后端实现
4.参数先加密,不要存在明文参数,不要直接使用base64将明文参数进行编码,
它不是加密算法。
5.多次分享后的支付问题,同一笔订单支付一定要做后端的幂等处理。
6.APP订单id与微信“统一下单”的预支付id一定要做数据库层面的逻辑关系绑
定。
Java 后端与H5代码实现
- 开发H5分享代付页面
由于分享功能是APP内实现的,H5页面实际是独立部署在后端服务器上的,对H5的访问实际是通过分享出去的访问链接地址在微信内部的浏览器打开的。
<html>
<head>
<style type="text/css"></style>
</head>
<script type="text/javascript">
//假设H5代付页面访问地址: http://h5.html?payId=xxxxxx加密串xx&sign=xx签名串xx
//微信重定向后地址: http://h5.html?payId=xxxxxx加密串xx&sign=xx签名串xx&CODE=XX微信返回XX
//微信静默授权地址:
//https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
//1.如果用户第一次打开分享链接,没有CODE参数会向微信请求授权,微信会重定向回来,并携带微信给的CODE参数
var code =getUrlParamByName('CODE');
if(!code){
window.location.href='https://open.weixin.qq.com/connect/oauth2/authorize?'
+'appid=xxxxxAPPIDxxxxxxxx'
+'&redirect_uri= '
//此H5页面访问地址
+ "http://h5.html?payId=xxxxxx加密串xx&sign=xx签名串xx"
//静默授权
+'&response_type=code&scope=SCOPE&state=STATE#wechat_redirect';
}
//获取请求地址中的参数
function getUrlParamByName(name){
var query = window.location.search.substring(1);
var vars = query.split("&");