微信支付对接银行服务商
前言
大家好,我是许RR。最近为公司的聚合支付平台对接广发银行(广东发展银行)服务商的支付通道,遇到很多问题,特此记录一下
概念
估计很多17年前做微信支付的朋友会对现在的微信支付模式感觉到奇怪,比如出现了各种概念,商户平台,渠道商,之前的代理商概念也被换成了服务商,配置支付授权目录也从公众号后台迁移到了商户平台上,支付白名单也悄然消失,没有了配置的地方,微信文档也出现了三种对接模式,包括普通商户、服务商和银行服务商。经过我这几天的重新学习,总结了一点概念分享给大家。首先先介绍各种对接模式的区别。
普通商户接入
所谓普通商户的对接就是最朴实无华的开发模式,公众号主体开通普通商户类型的商户后关联到公众号,开发拿到公众号appId
、mch_id
这些参数到微信的接口去请求,这种模式的优势在于简单快速,开发起来也没有那么多的阻碍。但是缺点是不容易集成管理,没有可重复性。
服务商商户接入
服务商商户是种比较特殊的商户,17年前是没有的,但是可能是因为政策或者某些原因出现了,服务商是有能力开发支付软件的第三方公司。我举一个例子,比如说XX医院现在需要做线上就诊业务,需要开发一套公众号网站,但是只有自己的公众号,甚至连普通商户也没有,怎么办呢?医院就将开发线上就诊公众号软件的责任交给我司,我司通过在我司的公众号服务商商户中新建一个子商户(或者叫特约商户)并绑定该医院的公众号,让该医院根据微信指引开通各种东西,包括设置一个对公账户,通过这些设置,该医院成为我司服务商商户号下的一个子商户,服务商开发拿到一些和子商户有关的参数去微信的接口请求,最终金钱也是流向子商户的设置的银行账户。这种模式的优势在于开发的可重用性高,一套代码多次复用,并且可管理性高,并且服务商应该还有一些我这种开发不知道的好处。
银行服务商接入
其实银行服务商和普通服务商应该区别不大(微信文档中提到),但是具体差别在哪我也不清楚,这个只有银行的开发才知道了。
对接流程
准备参数
普通商户模式
普通商户需要的参数:
参数 | 参数名 | 参数说明 |
---|---|---|
appId | 公众号账号id | 微信支付分配的公众账号ID(企业号corpid即为此appId) |
mch_id | 商户号 | 微信支付分配的商户号(需要在微信的商户平台申请到普通商户账号,并关联到公众号) |
key | 商户平台api安全key | 微信退款时要用到,在商户平台的api安全设置中设置 |
准备测试环境
我司的业务是这样的,有一个医院想要做线上就诊业务,并且将开发公众号网站的任务交给了我们,我们代替医院开发公众号网站并且对接广发银行的服务商。第一步要做的就是准备测试环境(这里我建议要对接银行服务商的朋友开发时使用正式的公众号参数进行测试,微信提供的测试公众号还是与正式公众号差别较大,部署起来问题很多)。首先银行有一套自己测试环境,并且银行的测试环境对接微信的沙箱测试环境,银行服务商提供的测试环境其实相当完备。接下来我们聊聊整个测试环境的流程与准备。
进件
将要开发的公众号的主体名和appId
提供给银行服务商在测试环境进价,银行服务商在银联进件后会搭好和微信相关的沙箱环境。一般测试环境进件后银行方会提供一些银行方的参数(比如广发银行这里是提供了广发银行的商户id,广发银行的商户号和广发商户应用编号),他们接口的后端会自己去和微信的参数做关联,一般不会直接提供给对接方有关微信的参数(比如说微信的商户号)。
提供异步通知地址
微信支付完成后微信会将支付成功的结果异步通知到开发方。对接服务商的话,微信会先通知到服务商的后台,再由服务商后台异步通知到对接服务商的后台,所以需要对接服务商的后台开发提供异步通知地址给银行方,让银行方去配置,保证支付成功的结果能够通知到开发商户后台。
正式开发(实践)
加密验签通讯
我对接的银行服务商有一套加密解密,加签验签的模式,我觉得可以完全避免安全问题。
-
请求方生成随机字符串
encryptKey
-
请求方使用自己的私钥对请求体进行签名,生成
sign
-
请求方使用
encryptKey
作为密钥,用对称加密方法去加密请求体,生成加密体 -
请求方使用响应方的公钥对
encryptKey
进行非对称加密,生成encryptKey1
-
请求方用加密体作为请求体,把
encryptKey1
和sign
附在http请求头上发送给响应方 -
响应方接收请求
-
响应方从请求头上获取
encryptKey1
并且用自己的私钥解密,获取encryptKey
-
响应方用解密得到的
encryptKey
去解密加密体,得到请求体 -
响应方从请求头上获取
sign
,再用请求方额公钥对请求体进行验签,验签成功即代表成功这种加密加签方式可以防止类型的任何攻击,应该说是两方对接接口的最佳实践了。
接入预支付/下单接口
微信文档提到的预支付,其实意思就是在微信下单。调用大多数微信接口,需要保障的是微信和本身业务系统数据一致,举个例子,我现在做的是一个门诊的预约挂号业务,患者要先挂号成功了,生成了成功的挂号单后,再去调用微信下单接口进行下单,如果业务系统没有处理下单失败的结果,那么我们业务系统的数据就会乱了,因为按理说我挂号成功了,是包括下单成功的,假如我下单失败了,那么挂号业务也应该失败。我们系统内的实践是,一但下单服务失败,那么挂号也会失败,直接返回给患者,让患者手动重试,同时释放号源。
接入退款接口
退款是业务中的重中之重,假如业务单是成功状态,但是退款接口却给退款了,那么无疑是生产事故。最好的方法是先到微信查单,获取支付订单的最新状态,先判断支付单是否已经支付过,可否退款,再到业务系统中确认业务单是否可退款,如果业务系统是不可退款,那么自然不能允许这次请求。我们系统内的实践是,先进行微信接口查单对系统内的支付订单进行状态更新,先查询支付订单是否处于已支付状态,如果支付订单不是已支付状态那么不允许,如果支付订单是已支付状态,再去业务系统查询业务订单是否是可退款状态,如果不是可退款状态,那么自然不可以给他退款。
接入查单接口
查单接口需要进行系统内的支付订单和微信订单进行核对校验,保证系统内的支付订单是最新的状态,我们系统内的实践是,每次查单接口被调用,就发起一次微信查单请求,对比系统内的支付订单与微信订单的状态,如果不相同则更新数据库的支付订单状态。
接入支付
支付回调是用户成功对微信订单支付后,微信主动异步请求业务系统服务端接口的一种消息通知,有使用过消息队列的小伙伴可以把当成微信发的一条消息队列的消息,业务系统主动去消费。这个异步通知,因为是异步的,而且微信会多次通知,所以需要业务系统提供的接口实现幂等性,并且因为多次异步的通知,需要假如分布式锁。我们系统内的实践是,在接口被调用第一行上加上基于redis setnx的分布式锁,并设置过期时间为3秒,抢不到锁的请求一律返回失败,抢到锁后,先去数据库查询订单,核对订单状态,如果订单状态不是未支付的,也返回失败,如果订单状态是已支付,那么说明已经被通知过了,直接返回成功,保证幂等性。如果订单状态是未支付状态,那么核对金额,金额对不上也返回失败,直到经过了所以的校验,才对数据库的订单状态进行一个更新。
未完待续。。。