新版微信支付ApiV3支付回调使用官方SDK实现签名检验以及参数解密,java版本实现

最近在接微信支付相关功能,支付结果回调折腾了比较久,微信官方文档里面只给了回调接口说明,包括:第一步验证签名,第二步参数解密,但是并未给具体实例,整体来说上手还是有点复杂,废话不多说,直接上代码;

前期准备:

微信支付,商家id,商家证书,证书SerialNo,apiKey, 这个是接入支付的准备,到回调这是,这些基本都已经ok了

依赖官方sdk:

<!--微信转账-->这个是支付的时候用到的sdk
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.2.14</version>
</dependency>
<!---回调通知签名校验以及参数解密相关--->
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.9</version>
</dependency>

支付回调的地址https://xxx/weixin/pay/notify,其中xxx是你服务器的域名,微信支付官方说明必须是https的

import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class TransferConfig {

    @Value("${transfer.merchantId}")
    public String merchantId;

    @Value("${transfer.marchant.keypath}")
    public String privateKeyPath;

    @Value("${transfer.merchantSerialNo}")
    public String merchantSerialNumber;

    @Value("${transfer.apiV3key}")
    public String apiV3Key;

}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hyhr.server.entity.TransferBatchInfo;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.java.core.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.Locale;

@Slf4j
@RestController
@RequestMapping("/weixin/pay")
public class WxPayController {

    @Autowired
    private TransferConfig transferConfig;

    @Autowired
    private WxTransferService wxTransferService;

    private CertificatesManager certificatesManager;

    @PostConstruct
    public void init() throws Exception {
        //获取商户私钥
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromPath(transferConfig.privateKeyPath);
        certificatesManager = CertificatesManager.getInstance();

        //向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(transferConfig.merchantId, new WechatPay2Credentials(transferConfig.merchantId,
                new PrivateKeySigner(transferConfig.merchantSerialNumber, merchantPrivateKey)),
                transferConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
        //从证书获取verifier
        Verifier verifier = certificatesManager.getVerifier(transferConfig.merchantId);
    }

    @PostMapping("/notify")
    public void callBack(HttpServletRequest request, HttpServletResponse response) {
        String characterEncoding = request.getCharacterEncoding();

        //从请求头获取验签字段
        String timeStamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signature = request.getHeader("Wechatpay-Signature");
        String certSn = request.getHeader("Wechatpay-Serial");

        log.info("wx callback params,timesStamp:{},nonce:{},signature:{},certSn:{}", timeStamp, nonce, signature, certSn);
        try(BufferedReader reader = new BufferedReader(new
                InputStreamReader(request.getInputStream()))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            String body = sb.toString();
            //检验签名
            Verifier verifier = certificatesManager.getVerifier(transferConfig.merchantId);
            String sn = verifier.getValidCertificate().getSerialNumber().toString(16).toUpperCase(Locale.ROOT);
            NotificationRequest notifyRequest = new NotificationRequest.Builder().withSerialNumber(sn)
                    .withNonce(nonce)
                    .withTimestamp(timeStamp)
                    .withSignature(signature)
                    .withBody(body)
                    .build();
            NotificationHandler handler = new NotificationHandler(verifier, transferConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));

            //验证和解析请求体
            Notification notification = handler.parse(notifyRequest);
            log.info("wx callback response:{}", JSON.toJSONString(notification));
            JSONObject res = JSON.parseObject(notification.getDecryptData());
            String outBatchNo = res.getString("out_batch_no");
            String batchStatus = res.getString("batch_status");

            //处理相关业务
            TransferBatchInfo transferBatchInfo = new TransferBatchInfo();
            transferBatchInfo.setOutBatchNo(outBatchNo);
            transferBatchInfo.setBatchStatus(batchStatus);
            wxTransferService.notifyTransferBatchStatus(transferBatchInfo);

            //响应微信服务器
            response.setStatus(200);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code", "SUCCESS");
            jsonObject.put("message", "SUCCESS");
            response.getOutputStream().write(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            log.error("wx callback error", e);
            response.setStatus(500);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code", "ERROR");
            jsonObject.put("message", "签名失败");
            try {
                response.getOutputStream().write(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
                response.flushBuffer();
            } catch (Exception ex) {
                log.error("微信回调失败",e);
            }
        }
    }
}

校验签名和参数解密,官方的NotificationHandler直接就给搞定了,大大简化了自己去校验签名和解密的麻烦

同时有一个坑,由于工程用的spring-boot版本比较就,里面使用jackson-databind的版本还是2.10的,和wechatpay-apache-httpclient里面使用的版本是2.13又冲突,我把spring-boot版本升级了,使使用jackson-databind版本在2.11之上就不会报错了

参考资料:

【微信支付】微信小程序支付-V3接口(一) - 知乎 (zhihu.com)

java微信支付v3系列——8.微信支付之退款成功回调_java 微信支付回调-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值