java SpringBoot 使用ijpay对接微信支付-商家转账到零钱

使用的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;           //小程序APPID

    private 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;//转账场景id

    public 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")
    @ResponseBody
    public 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})
    @ResponseBody
    public 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;
    }

到此微信支付的商家转账到零钱就结束了, 麻烦集中在用到的证书比较多容易混淆,其次就是平台证书导出时的问题!

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值