spring boot 接入微信支付 V3key

技术交流QQ群 729987144
个人博客 http://www.coderyj.com
最近在对接微信支付,遇到的坑和大家分享
微信支付官方github网址 https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient

0.私钥以及证书放在项目资源文件夹下

在这里插入图片描述

1.首先注册微信商户, 并且开通 JSAPI一定要确认

2.申请证书—> 之后得到文件, 这里会用到 aplicient_cert.pem

在这里插入图片描述

3.maven 引入 sdk
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.8</version>
</dependency>
4.初始化
// 加载私钥
    private PrivateKey getPrivateKey() {
        try {
            InputStream inputStream = new ClassPathResource(privateKeyPath).getInputStream();
            File file = new File("./abc");
            FileUtils.copyInputStreamToFile(inputStream, file);
            String privateKey = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
            // 加载商户私钥(privateKey:私钥字符串)
            PrivateKey merchantPrivateKey = PemUtil
                    .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
            return merchantPrivateKey;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
private CloseableHttpClient wechatInit(String mchId, String mchSerialNo, String apiV3Key) {
        try {
            PrivateKey merchantPrivateKey = getPrivateKey();
            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
                    new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
            // 从证书管理器中获取verifier
            Verifier verifier = certificatesManager.getVerifier(mchId);
            WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                    .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier));
            CloseableHttpClient httpClient = builder.build();
            return httpClient;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
5.下单接口
// 下单接口
    private  Object wxPayOrder(String orderNo, int amount, String desc, String openid, String notify_url, String orderId) {
        try {
            // 初始化商户
            CloseableHttpClient httpClient = wechatInit(mchId, mchSerialNo, apiv3key);
            if (httpClient == null) {
                return null;
            }
            // 下单
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();

            rootNode.put("mchid", mchId)
                    .put("appid", appid)
                    .put("description", desc)
                    // 回调url
                    .put("notify_url", notify_url)
                    .put("out_trade_no", orderNo)
                    .put("attach",orderId);
            rootNode.putObject("amount")
                    // 金额类型单位分
                    .put("total", amount)
                    // 支付金额类型
                    .put("currency", "CNY");
            rootNode.putObject("payer")
                    .put("openid", openid);
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            log.info("支付数据:" + bodyAsString);
            System.out.println(bodyAsString);
            // 拼装信息 返回给小程序或者native app
            JSONObject bodyAsJSON = JSONObject.parseObject(bodyAsString);
			 //判读下微信返回值是否正确
			 if (bodyAsJSON.containsKey("code")) {
			     return AjaxResult.error("错误", bodyAsJSON);
			 }
			final String prepay_id = bodyAsJSON.getString("prepay_id");
			final String timeStamp = String.valueOf(System.currentTimeMillis());
			 final String nonceStr = RandomStringGenerator.getRandomStringByLength(32);
			 StringBuffer stringBuffer = new StringBuffer();
			 stringBuffer.append(appid + "\n");
			 stringBuffer.append(timeStamp + "\n");
			 stringBuffer.append(nonceStr + "\n");
			 stringBuffer.append("prepay_id=" + prepay_id + "\n");
			 Signature signature = Signature.getInstance("SHA256withRSA");
			 PrivateKey merchantPrivateKey = getPrivateKey();
			 signature.initSign(merchantPrivateKey);
			 signature.update(stringBuffer.toString().getBytes("UTF-8"));
			 byte[] signBytes = signature.sign();
			 Base64.Encoder encoder = Base64.getEncoder();
			 String paySign = encoder.encodeToString(signBytes);
			
			 JSONObject params = new JSONObject();
			 params.put("appId", appid);
			 params.put("timeStamp", timeStamp);
			 params.put("nonceStr", nonceStr);
			 params.put("prepay_id", "prepay_id=" + prepay_id);
			 params.put("signType", "RSA");
			 params.put("paySign", paySign);
			 log.info("拼装后的信息:" + params);
            return params ;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
6.回调url,因为现在微信回调数据是加密的所以需要解密
@RequestMapping("callBack")
    public Object callBack(HttpServletRequest request) throws Exception {
        log.info("接口已被调用");
        ServletInputStream inputStream = request.getInputStream();
        String notifyJson = StreamUtils.inputStream2String(inputStream, "utf-8");
        JSONObject object = (JSONObject) JSONObject.parse(notifyJson);
        log.info("支付回调数据notify:" + object);
        //获取报文
        String body = getRequestBody(request);
        //随机串
        String nonceStr = request.getHeader("Wechatpay-Nonce");
        //微信传递过来的签名
        String signature = request.getHeader("Wechatpay-Signature");
        //证书序列号(微信平台)
        String serialNo = request.getHeader("Wechatpay-Serial");
        //时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        //构造签名串
        //应答时间戳\n
        //应答随机串\n
        //应答报文主体\n
        String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));
        try {
            //验证签名是否通过
            boolean result = verifiedSign(serialNo, signStr, signature);
            if(result){
                //解密数据
                String plainBody = decryptBody(body);
                log.info("解密后的明文:{}",plainBody);
                JSONObject parse = (JSONObject) JSONObject.parse(plainBody);
                log.info("parse:"+ parse);
                String trade_state = parse.get("trade_state").toString();
                if (trade_state.equals("SUCCESS")){
                    // 商户订单号
                    String out_trade_no = parse.get("out_trade_no").toString();
                    // 事务id
                    String transaction_id = parse.get("transaction_id").toString();
                    // 支付成功时间
                    String success_time = parse.get("success_time").toString();
                    // 传入的id
                    String orderId = parse.get("attach").toString();
                    log.info("支付成功....");
                }
                // 响应微信
                parse.put("code", "SUCCESS");
                parse.put("message", "成功");
                return parse;
            }
        } catch (Exception e) {
            log.error("微信支付回调异常:{}", e.getMessage());
            e.printStackTrace();
        }
        return null;
    }
  • 解密工具类
/**
     * 解密body的密文
     *
     * "resource": {
     *         "original_type": "transaction",
     *         "algorithm": "AEAD_AES_256_GCM",
     *         "ciphertext": "",
     *         "associated_data": "",
     *         "nonce": ""
     *     }
     *
     * @param body
     * @return
     */
    private String decryptBody(String body) throws UnsupportedEncodingException, GeneralSecurityException {
        AesUtil aesUtil = new AesUtil(apiv3key.getBytes("utf-8"));
        JSONObject object = JSONObject.parseObject(body);
        JSONObject resource = object.getJSONObject("resource");
        String ciphertext = resource.getString("ciphertext");
        String associatedData = resource.getString("associated_data");
        String nonce = resource.getString("nonce");
        return aesUtil.decryptToString(associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
    }
    /**
     * 验证签名
     *
     * @param serialNo  微信平台-证书序列号
     * @param signStr   自己组装的签名串
     * @param signature 微信返回的签名
     * @return
     * @throws UnsupportedEncodingException
     */
    private boolean verifiedSign(String serialNo, String signStr, String signature){
        try {
            PrivateKey merchantPrivateKey = getPrivateKey();
            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
                    new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiv3key.getBytes(StandardCharsets.UTF_8));
            // 从证书管理器中获取verifier
            Verifier verifier = certificatesManager.getVerifier(mchId);
            return verifier.verify(serialNo, signStr.getBytes("utf-8"), signature);
        }catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 读取请求数据流
     *
     * @param request
     * @return
     */
    private String getRequestBody(HttpServletRequest request) {
        StringBuffer sb = new StringBuffer();
        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error("读取数据流异常:{}", e);
        }
        return sb.toString();
    }
7.到此微信支付 V3key 已经对接完毕
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值