使用的maven版本:2.9.11
由于ijpay中提供的实体类没有设置回调参数的属性, 这里是自定义一个实体类:InitiateBatchTransferRequest代码如下:
package com.foo.web.controller.pay.wxpay;import com.ijpay.wxpay.model.v3.TransferDetailInput;
import lombok.Getter;
import lombok.Setter;import java.util.List;public class InitiateBatchTransferRequest {private String appid; //小程序APPIDprivate String out_batch_no; //商户单号private String batch_name; //转账批次名称private String batch_remark; //转账批次备注private String notify_url; //回调地址private Integer total_amount; //转账总金额private Integer total_num; //转账总笔数private List<TransferDetailInput> transfer_detail_list;//转账明细列表private String transfer_scene_id;//转账场景idpublic InitiateBatchTransferRequest() {}public InitiateBatchTransferRequest(String appid, String out_batch_no, String batch_name, String batch_remark, Integer total_amount, Integer total_num, List<TransferDetailInput> transfer_detail_list, String transfer_scene_id) {this.appid = appid;this.out_batch_no = out_batch_no;this.batch_name = batch_name;this.batch_remark = batch_remark;this.total_amount = total_amount;this.total_num = total_num;this.transfer_detail_list = transfer_detail_list;this.transfer_scene_id = transfer_scene_id;}public String getAppid() {return this.appid;}public String getOut_batch_no() {return this.out_batch_no;}public String getBatch_name() {return this.batch_name;}public String getNotify_url() {return this.notify_url;}public String getBatch_remark() {return this.batch_remark;}public Integer getTotal_amount() {return this.total_amount;}public Integer getTotal_num() {return this.total_num;}public List<TransferDetailInput> getTransfer_detail_list() {return this.transfer_detail_list;}public String getTransfer_scene_id() {return this.transfer_scene_id;}public InitiateBatchTransferRequest setAppid(String appid) {this.appid = appid;return this;}public InitiateBatchTransferRequest setOut_batch_no(String out_batch_no) {this.out_batch_no = out_batch_no;return this;}public InitiateBatchTransferRequest setBatch_name(String batch_name) {this.batch_name = batch_name;return this;}public InitiateBatchTransferRequest setBatch_remark(String batch_remark) {this.batch_remark = batch_remark;return this;}public InitiateBatchTransferRequest setTotal_amount(Integer total_amount) {this.total_amount = total_amount;return this;}public InitiateBatchTransferRequest setTotal_num(Integer total_num) {this.total_num = total_num;return this;}public InitiateBatchTransferRequest setTransfer_detail_list(List<TransferDetailInput> transfer_detail_list) {this.transfer_detail_list = transfer_detail_list;return this;}public InitiateBatchTransferRequest setTransfer_scene_id(String transfer_scene_id) {this.transfer_scene_id = transfer_scene_id;return this;}public InitiateBatchTransferRequest setNotify_url(String notify_url) {this.notify_url = notify_url;return this;}
}
然后直接写接口此处是单笔转账方法:
@RequestMapping("/batchTransfer")@ResponseBodypublic String batchTransfer(String openId, BigDecimal fee) throws Exception {String out_batch_no = PayKit.generateStr();InitiateBatchTransferRequest batchTransferModel = new InitiateBatchTransferRequest().setAppid("APPID").setOut_batch_no(out_batch_no).setBatch_name("转账批次名称").setBatch_remark("批次备注").setTotal_amount(fee.multiply(new BigDecimal("100")).intValue()).setTotal_num(1).setNotify_url("回调地址").setTransfer_detail_list(Collections.singletonList(new TransferDetailInput().setOut_detail_no(PayKit.generateStr()).setTransfer_amount(fee.multiply(new BigDecimal("100")).intValue()).setTransfer_remark("明细备注").setOpenid(openId)));log.info("发起商家转账请求参数 {}", JSONUtil.toJsonStr(batchTransferModel));IJPayHttpResponse response = WxPayApi.v3(RequestMethodEnum.POST,WxDomainEnum.CHINA.toString(),TransferApiEnum.TRANSFER_BATCHES.toString(),"商户号",getSerialNumber(),null,"apiclient_key.pem",JSONUtil.toJsonStr(batchTransferModel));log.info("发起商家转账响应 {}", response);if (response.getStatus() == 200) {JSONObject jsonObject = JSONObject.parseObject(JSONUtil.toJsonStr(response.getBody()));System.out.println("batch_status:" + jsonObject.getString("batch_status"));System.out.println("batch_id:" + jsonObject.getString("batch_id"));//处理你的业务return "OK";} else {return "ERROR";}}
上述代码块中用到的方法getSerialNumber()如下:
private String getSerialNumber() {if (StrUtil.isEmpty(serialNo)) {// 获取证书序列号X509Certificate certificate = PayKit.getCertificate("apiclient_cert.pem");if (null != certificate) {serialNo = certificate.getSerialNumber().toString(16).toUpperCase();// 提前两天检查证书是否有效boolean isValid = PayKit.checkCertificateIsValid(certificate, "商户号", -2);log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));}}System.out.println("serialNo:" + serialNo);return serialNo;}
上述代码注意点, 用到的证书有两个:apiclient_cert.pem,apiclient_key.pem都是由商户API证书中下载所得
因为用户在调用转账接口后, 返回的订单状态都是已受理, 故而设置了回调地址, 起到监听管理员通过转账请求后刷新订单的作用
注意:由于回调中微信返回的为密文解密后才能使用, 所需需要验签和解密的过程,其中需要用到APIv3的密钥和平台证书
由于平台证书不能直接下载,此处先介绍证书的下载方法:
1、下载微信提供的CertificateDownloader.jar(下载地址:Releases · wechatpay-apiv3/CertificateDownloader (github.com))
2、编辑指令:java -jar CertificateDownloader.jar -k {apiV3key} -m {商户号} -f {apiclient_key.pem的物理路径} -s {证书序列号} -o {平台证书的导出路径}注意的是其中的证书序列号为商户API证书中显示的证书序列号 (替换关键字即可,指令中不需要{})
3、编写回调方法如下:
@RequestMapping(value = "/TransferNotify", method = {RequestMethod.POST, RequestMethod.GET})@ResponseBodypublic String TransferNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {Map<String, String> map = new HashMap<>(12);try {String timestamp = request.getHeader("Wechatpay-Timestamp");String nonce = request.getHeader("Wechatpay-Nonce");String serialNo = request.getHeader("Wechatpay-Serial");String signature = request.getHeader("Wechatpay-Signature");log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);String result = HttpKit.readData(request);log.info("支付通知密文 {}", result);// 需要通过证书序列号查找对应的证书, verifyNotify中有验证证书的序列号String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,"APIv3密钥", "通过指令导出的平台证书路径");log.info("支付通知明文 {}", plainText);if (StrUtil.isNotEmpty(plainText)) {JSONObject jsonObject = JSONObject.parseObject(plainText);System.out.println("batch_id:"+jsonObject.getString("batch_id"));System.out.println("batch_status:"+jsonObject.getString("batch_status"));response.setStatus(200);map.put("code", "200");map.put("message", "SUCCESS");} else {response.setStatus(500);map.put("code", "ERROR");map.put("message", "签名错误");}response.setHeader("Content-type", ContentType.JSON.toString());response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));response.flushBuffer();} catch (Exception e) {log.error("系统异常", e);}return null;}
到此微信支付的商家转账到零钱就结束了, 麻烦集中在用到的证书比较多容易混淆,其次就是平台证书导出时的问题!