一、开发环境
-
开发前准备材料
- 服务商平台中开通【收付通】工具箱。
- 商户私钥:商户申请商户API证书时,会生成商户私钥,并保存在本地证书文件夹的文件apiclient_key.pem中。
- 商户API证书:商户API证书是指由商户申请的,包含商户的商户号、公司名称、公钥信息的证书
- 商户证书序列号:每个证书都有一个由CA颁发的唯一编号,即证书序列号。
- 服务商商户号,在服务商平台进行查看。
- APIV3秘钥:为了保证安全性,微信支付在回调通知和平台证书下载接口中,对关键信息进行了AES-256-GCM加密。API v3密钥是加密时使用的对称密钥。商户可以在【商户平台】->【API安全】的页面设置该密钥。
- 微信支付平台证书:平台证书是指由微信支付负责申请的,包含微信支付平台标识、公钥信息的证书。商户可以使用平台证书中的公钥进行应答签名的验证。
- 不同的商户,对应的微信支付平台证书是不一样的。
- 平台证书会周期性更换。商户应定时通过API下载新的证书。不要依赖人工更新证书。
- 商户签名使用商户私钥,证书序列号包含在请求HTTP头部的
Authorization
的serial_no
。 - 微信支付签名使用微信支付平台私钥,证书序列号包含在应答HTTP头部的
Wechatpay-Serial
。 - 商户上送敏感信息时使用微信支付平台公钥加密,证书序列号包含在请求HTTP头部的
Wechatpay-Serial
。
- 验签原理
-
开发环境准备
-
新建springboot项目
-
将准备好的私密信息存入配置项目配置文件中
-
引入微信支付依赖sdk
<dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-apache-httpclient</artifactId> <version>0.4.7</version> </dependency>
-
引入配置文件,加载商户私钥文件对象;获取微信支付平台证书管理器实例,使用代码不定时来更新下载微信支付平台证书;
根据加载过的私钥文件和微信平台证书信息生成微信支付http请求对象; -
包装通用的get请求、post请求响应结果
-
二、进件业务
-
概念:帮助二级商户进件成为微信支付商户.
-
关键点
-
区分微信用户、二级商户和微信支付商户。
微信用户:众所周知。 二级商户:能提供商户进件材料的并且在电商平台中注册的微信用户。 微信支付商户:二级商户提交商户进件材料并经由微信支付系统审核通过的二级商户,拥有自己的商户号,和“商家助手”小程序登录认证。
-
在执行进件请求的时候,需要将敏感信息加密,商户上送敏感信息时使用微信支付平台公钥加密,证书序列号包含在请求HTTP头部的Wechatpay-Serial。
-
在上传身份证正反面照片及其他材料图片的时候,需要先将其转为MediaId.
-
进件为个人卖家的时候,补充材料说明必填,示例:“该商户已持续从事电子商务经营活动满6个月,且期间经营收入累计超过20万元。”
-
在进件为个人卖家或者小微卖家的时候,店铺二维码可以随便填。
-
-
业务实施流程
-
二级商户提交材料,调用进件接口,返回微信支付申请单号和业务申请编号,根据这些信息将数据落库。
-
提供查询进件状态接口,来查看进件单审核情况,引导二级商户签约。
-
定时器根据进件单状态定时更新数据库中进件单的数据。
-
三、支付业务
-
业务实施流程
-
微信用户提交订单材料向电商平台发起调起支付请求,获得支付签名信息
-
电商平台向微信电商支付系统发起调起支付请求,获得支付签名信息
-
电商支付系统向微信支付系统发起调起预支付请求,获得预支付信息【prepare_id】
-
电商支付系统根据预支付信息获取支付签名信息
{ "signMap": { "timeStamp": "1677678531", "package": "prepay_id=wx01214853363370cefa21219c0643530000", "paySign": "WvEL/CJOWYab3Fn2jLTmcCUIieb85hHFnze2nk91FOs088K/eQby6DHyfLOMTkuYoKFxO6RScniP82WeonZHEGxPFQOHp09DX83j6x+m4IjOSZH5+Odm4+ab8meJQU0x+HWcrWPjP7vkNJN+QjS2TKWJ5tNXaYoLX9+GqRPpepITUVhPFhzFQL8usvVeNI3dFeNbv53BpRnqS/tdPmjUEKDH4RtRcUJaUlf0MdA3YNlWwH/w6MBcbNt2yGl/HOQmAjaiddEWuQet47nQBwcmQy12h78PZ8H3Pv/NvQH+SV7kPQ2lBdHYJScqtE81h67UkbzIe1b4O0SdI1vqgiYXAQ==", "appId": "xxxxx", //微信开放平台绑定的应用的APPID "signType": "RSA", "nonceStr": "1677678531686" }, "type": "jsapi", "prepay_id": "wx01214853363370cefa21219c0643530000", "status": 200 }
-
电商平台根据支付签名信息生成付款信息
-
用户进行付款
-
付款成功之后,微信支付系统向电商支付系统发起回调请求
-
电商支付系统向电商平台发起回调业务处理请求
-
回调业务处理请求正常响应之后,电商支付系统向微信支付系统发起回调响应
-
-
关键点
-
在进行支付签名的时候,apiV3使用的是RSA签名
public String sign(Map<String, Object> map) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { String nonceStr = (String) map.get("nonceStr"); String timeStamp = (String) map.get("timeStamp"); String appId = (String) map.get("appId"); String packageStr = (String) map.get("package"); StringBuilder builder = new StringBuilder(); builder.append(appId) .append("\n") .append(timeStamp) .append("\n") .append(nonceStr) .append("\n") .append(packageStr) .append("\n"); Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(wechatPayConfig.getPrivateKey(keyPath)); sign.update(builder.toString().getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(sign.sign()); }
-
在进行回调通知的时候要进行幂等性处理,幂等性简单来说就是指,在前提数据不变的前提下,无论执行多少次业务请求,所产生的结果是一致的。在这里主要是指电商支付系统在对电商平台发起回调业务处理的时候要保证幂等性,这里所采取的方案是先查询订单状态是否已经变更,确认变更后再进行回调业务的调用处理。
-
在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱,这里使用的是可重入所ReentrantLock。
-
在下单的时候,要打上分账标志。
//结算信息————指定确认分账 Map<String, Object> settleInfoMap = new HashMap<>(4); settleInfoMap.put("profit_sharing", true); params.put("settle_info", settleInfoMap);
-
微信支付系统向电商支付系统发起通知请求的时候,电商支付系统要先对响应信息进行验签处理来保障返回的响应数据的安全性。
-
在成功发起支付请求后,可以通过订单查询接口查询订单信息。
{ "transaction_id": "4200001724202303014556793243", //微信支付系统交易流水号 "amount": { "payer_total": 2500, "total": 2500, "currency": "CNY", "payer_currency": "CNY" }, "trade_state": "SUCCESS", "bank_type": "CCB_DEBIT", "promotion_detail": [], "sp_mchid": "xxxx", //服务商的商户号 "success_time": "2023-03-01T15:40:33+08:00", "payer": { "sp_openid": "xxxx", //支付者的唯一标识,在这里用的是openID,具体使用方法参考电商收付通官方文档 "sub_openid": "" }, "sp_appid": "xxxx", 微信开放平台绑定的应用的APPID "out_trade_no": "FZ2023030188888888", //电商支付系统订单号 "trade_state_desc": "支付成功", "trade_type": "JSAPI", "attach": "name=20230301测试分账请求", "sub_mchid": "xxxx", //经过进件后得到的收款方二级商户号 "scene_info": { "device_id": "127.0.0.1" }, "status": 200 }
-
四、退款业务
-
概念:当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家账户上。
-
关键点
- 退款数额单位默认是分
- 退款回调业务处理也要注意幂等性
- 发起退款申请后,当请求信息经过微信支付系统审核之后,钱款就会原路返还给买家
-
业务实施流程
- 用户支付完成后,申请部分退款或者全额退款
- 电商平台向电商支付系统发起申请退款的接口,注意请求接口的参数编写,注意各个单号不要填错了
- 电商支付系统向微信支付系统发起申请退款的接口,等待微信支付系统异步处理请求
- 微信支付系统向电商支付系统发起回调通知请求,电商支付系统经过验签,业务幂等性验证,处理业务等流程,回馈微信支付系统正确的响应信息
- 如果中间业务处理失败,电商支付系统向微信支付系统回馈异常的响应信息,等待微信支付系统再次回调
五、分账业务
-
概念:微信订单支付成功后,由电商平台发起分账请求,将结算后的资金分给分账接收方。
-
关键点
注意: • 微信订单支付成功后,服务商代特约商户发起分账请求,将结算后的钱分到分账接收方。 • 对同一笔订单最多能发起50次分账请求,每次请求最多分给50个接收方。 • 此接口采用异步处理模式,即在接收到商户请求后,会先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取。 • 分账资金的冻结期默认是180天。从订单支付成功之日起,180天内需要发起分账,若180天内未发起分账,待分账资金将会自动解冻给分账方。 • 电商平台需确保向微信支付传输用户身份信息和账号标识信息做一致性校验已合法征得用户授权 接口限频: 1、单个服务商(请求分账) 2000QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。 2、单个交易收款商户(请求分账) 300QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。同时,建议对同一主体的商户拆分多个商户号进行交易,避免交易集中到单个商户。
-
在订单支付完成之后,服务商发起分账请求,此时,资金在卖家二级商户资金池中处于被冻结状态,不可提现,但是可以看到资金明细。
-
在发起分账请求之前,要确保分账接收方的存在,需要调用微信支付系统的添加分账接收方接口存入分账接收方。
-
服务商发起分账金额比例,不得超过出资方的30%。
-
同一笔分账单号,多次请求等同一次。
-
在请求参数中有个字段比较重要
finish 是否分账完成 必填 1、如果为true,该笔订单剩余未分账的金额会解冻回电商平台二级商户; 2、如果为false,该笔订单剩余未分账的金额不会解冻回电商平台二级商户,可以对该笔订单再次进行分账。 需要测试,在发起分账请求的时候finish字段的值为“true”的时候,是否此次分账请求就等同于当此字段为“false”的时候,发起分账请求并完结分账
-
在成功发起分账请求后会得到微信支付系统提供的响应结果,其中order_id是微信分账单号,微信支付系统返回的唯一标识,在调用完结分账API的时候,需要将此作为分账单号放入接口请求的请求体中,否则会报错。
{ "sub_mchid": "xxxx", //二级商户商户号 "transaction_id": "4208450740201411110007820472", // 交易流水号 "out_order_no": "P20150806125346", // 电商平台分账单号 "order_id": "3008450740201411110007820472", //微信支付系统提供的分账单号 "receivers": [ { "amount": 100, "description": "分给商户xxx", "detail_id": "36011111111111111111111", // 微信支付系统提供的每一笔分账记录的唯一标识 "fail_reason": "ACCOUNT_ABNORMAL", "finish_time": "2015-05-20T13:29:35.120+08:00", "receiver_account": "xxx", // 分账接收方的账户信息 "receiver_mchid": "xxx", //分账接收方的商户号,当分账接收方为服务商的时候,这里可以填服务商商户号 "result": "SUCCESS", "type": "MERCHANT_ID" } ], "status": "PROCESSING" }
-
需要测试,对于同一支付订单,当发起多笔分账请求的时候,执行完结分账的参数变化情况,是否每一次完结分账所需要的分账单号都是上一次发起分账请求得到响应的order_id。
-
需要测试,对于同一支付订单,当发起多笔分账请求的时候,根据其中一个分账单号执行完结分账,是否所有的分账请求都会完结分账。
-
需要测试,当一笔订单被服务商发起分账请求之后,在执行完完结分账之后是否还可以再次发起分账请求,如果不会,会得到微信支付系统什么样的响应结果。
-
-
业务实施流程
- 用户下单,订单上打上分账标志。
- 订单经过电商平台的业务处理,买家确认收货。
- 买家确认收货后,电商平台向电商支付系统发起分账并完结分账的请求,得到响应信息。
- 电商支付系统内部处理发起分账请求,完结分账请求,并将得到的响应结果数据落库到自己的数据库中。
- 通过定时器定时查询暂未处理完全的分账单,将最新的响应结果及时的更新到对应的数据库表格中。