微信支付V3版本接入代码

要实现微信支付,必须要有一个载体,目前可选的载体有公众号、开发平台APP和小程序,每个载体都有一个appId。原来的微信支付V2版本,现在需要用的是V3版本,过程踩坑无数,特特将接入流程记录供其他记录员阅读。注:微信支付V3版本都需要用到证书,请参考
1、引入微信官方jar包
  <dependency>
       <groupId>com.github.wechatpay-apiv3</groupId>
       <artifactId>wechatpay-apache-httpclient</artifactId>
       <version>0.3.0</version>
  </dependency>

2、自动注入处理类

@Configuration
@Slf4j
public class WechatPayBeanConfig {

    @Autowired
    WechatConfig wechatConfig;

    /**
     * 加载秘钥
     *
     * @return
     */
    @Bean
    public ScheduledUpdateCertificatesVerifier getCertificatesVerifier() throws IOException {
        // 使用定时更新的签名验证器,不需要传入证书
        ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
                new WechatPay2Credentials(wechatConfig.getWechatPayMchId(),
                        new PrivateKeySigner(wechatConfig.wechatPayCertSerialNo,
                                WechatPayV3Util.getPrivateKey())),
                wechatConfig.getWechatPayMchKey().getBytes(StandardCharsets.UTF_8));
        return verifier;
    }

    @Bean("wechatPayClient")
    public CloseableHttpClient getWechatPayClient(ScheduledUpdateCertificatesVerifier verifier) throws IOException {
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(wechatConfig.getWechatPayMchId(),wechatConfig.wechatPayCertSerialNo , WechatPayV3Util.getPrivateKey())
                .withValidator(new WechatPay2Validator(verifier));
        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }
}

public class WechatPayV3Util {
     private static final Logger logger = LoggerFactory.getLogger(WechatPayV3Util.class);
      public static String random(int len) {
        Random r = new Random();
        String[] chars = new String[]{"a", "b", "c", "d", "e", "f", "g", "l", "m", "n", "o", "p", "q", "r", "s", "t",
                "u", "v", "w", "x", "y", "z"};
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < len; i++) {
            int ran1 = r.nextInt(chars.length);
            str.append(chars[ran1]);
        }
        return str.toString().toUpperCase();
    }

    @SneakyThrows
    public static String sign(byte[] message) {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey());
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    @SneakyThrows
    public static PrivateKey getPrivateKey() {
        WechatConfig wechatConfig = MySpringContext.getContext().getBean(WechatConfig.class);
        String content = new String(Files.readAllBytes(Paths.get(wechatConfig.getWechatPayKeyFileName())), StandardCharsets.UTF_8);
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            PrivateKey finalPrivateKey = kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
            return finalPrivateKey;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }
}

二、生成JSAPI预支付单号
public String requestPrePayId(RequestPrePayDto requestPrePayDto) {
        AdvancePayDto advancePayDto = new AdvancePayDto();
        advancePayDto.setAppid("公众号appId");
        advancePayDto.setMchid("商户mchID");
        advancePayDto.setDescription("商品描述");
        advancePayDto.setOutTradeNo("商户单号");
        advancePayDto.setNotifyUrl("https://");//回调地址
        // 订单金额信息
        AdvanceAmountDto advanceAmountDto = new AdvanceAmountDto();
        advanceAmountDto.setTotal(1);
        advancePayDto.setAmount(advanceAmountDto);
        // 支付者信息
        AdvancePayerDto advancePayerDto = new AdvancePayerDto();
        advancePayerDto.setOpenid(requestPrePayDto.getPaOpenId());
        advancePayDto.setPayer(advancePayerDto);
        HttpPost postRequest = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        postRequest.addHeader("Accept", "application/json");
        postRequest.addHeader("Content-Type", "application/json");
        //获取微信PayHttpClient,可以@Autowrite
        CloseableHttpClient wechatPayClient = MySpringContext.getApplicationContext().getBean("wechatPayClient", CloseableHttpClient.class);
        postRequest.setEntity(new StringEntity(JsonUtil.toJsonString(advancePayDto)));
        try (CloseableHttpResponse response = wechatPayClient.execute(postRequest)) {
            //响应体
            String responseStr = EntityUtils.toString(response.getEntity());
            return responseStr;
        } catch (Exception ex) {
            logger.error("生成预支付订单失败:{}", ex.getMessage());
            throw new GlobalApiException("生成预支付订单失败");
        }
 }


三、支付回调
1、签名验证
default void checkPaySign(HttpServletRequest request, String bodyContent) {
        ScheduledUpdateCertificatesVerifier verifier = MySpringContext.getApplicationContext().getBean(ScheduledUpdateCertificatesVerifier.class);
        //随机串
        String nonceStr = request.getHeader("Wechatpay-Nonce");
        logger.info("Wechatpay-Nonce:{}", nonceStr);
        //微信传递过来的签名
        String signature = request.getHeader("Wechatpay-Signature");
        logger.info("Wechatpay-Signature:{}", signature);
        //证书序列号(微信平台)
        String serialNo = request.getHeader("Wechatpay-Serial");
        logger.info("Wechatpay-Serial:{}", serialNo);
        //时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        logger.info("Wechatpay-Timestamp:{}", timestamp);
        //构造签名串
        //应答时间戳\n
        //应答随机串\n
        //应答报文主体\n
        String signStr = Stream.of(timestamp, nonceStr, bodyContent).collect(Collectors.joining("\n", "", "\n"));
        logger.info("signStr:{}", signStr);
        if (!verifier.verify(serialNo, signStr.getBytes(StandardCharsets.UTF_8), signature)) {
            logger.error("签名验证失败");
            throw new GlobalApiException("签名验证失败");
        }
    }


2、数据解密
default PrePayInfo callBack(WechatPayNotifyInfoDto wechatPayNotifyInfoDto) {
        PrePayInfoDao prePayInfoDao = MySpringContext.getBean(PrePayInfoDao.class);
        WechatConfig wechatConfig = MySpringContext.getBean(WechatConfig.class);
        AesUtil aesUtil = new AesUtil(wechatConfig.getWechatPayMchKey().getBytes(StandardCharsets.UTF_8));
        String str = aesUtil.decryptToString(wechatPayNotifyInfoDto.getResource().getAssociatedData().getBytes(StandardCharsets.UTF_8),
                wechatPayNotifyInfoDto.getResource().getNonce().getBytes(StandardCharsets.UTF_8),
                wechatPayNotifyInfoDto.getResource().getCiphertext());
        HashMap<String, Object> resourceObj = (HashMap<String, Object>) JsonUtil.parseJsonObject(str, HashMap.class);
        logger.info("解密成功:{}", str);
        String outTradeNo = (String) resourceObj.get("out_trade_no");
        String payState = (String) resourceObj.get("trade_state");
}


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值