ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

微信v3支付和回调数据校验

2022-03-01 12:01:15  阅读:383  来源: 互联网

标签:return String 微信 校验 v3 put new log


导入微信官方推荐maven jar
    <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.3.0</version>
    </dependency>
获取微信证书,并生成前端调起支付需要的参数(示例是合单支付)
    /**
     * 商户号
     * url https://pay.weixin.qq.com/index.php/core/account/info
     */
    private static final String merchantId = "xxxxx";

    /**
     * 商户证书序列号。
     * url https://pay.weixin.qq.com/index.php/core/cert/api_cert#/
     */
    private static final String merchantSerialNumber = "xxxxx";

    /**
     * API V3密钥
     * url https://pay.weixin.qq.com/index.php/core/cert/api_cert#/
     */
    private static final String apiV3Key = "xxxxx";

    /**
     * 商户API V3私钥证书地址(从微信商户平台下载,放到本地的地址)
     * url https://pay.weixin.qq.com/index.php/core/cert/api_cert#/
     */
    private static final String privateKeyPath = "xxxxx";
    private PrivateKey merchantPrivateKey;
    private ScheduledUpdateCertificatesVerifier verifier;
    private CloseableHttpClient httpClient;
  	@PostConstruct
  	//初始化微信证书
    private void initHttpClient() {
        log.info("--------微信证书v3初始化开始--------");
        try (FileInputStream inputStream = new FileInputStream(privateKeyPath)){
            merchantPrivateKey = PemUtil.loadPrivateKey(inputStream);
            verifier = new ScheduledUpdateCertificatesVerifier(
                    new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
                    apiV3Key.getBytes(StandardCharsets.UTF_8));
            httpClient =  WechatPayHttpClientBuilder.create()
                    .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier))
                    .build();
            log.info("--------微信证书v3初始化成功--------");
        }catch (IOException e){
            log.error("微信证书v3初始化失败,证书地址:{}",privateKeyPath,e);
        }
    }
    /**
     * 微信合单支付
     * @return
     */
    public Map<String, String> combineTransactionsApp(){
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/combine-transactions/app");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");
        JSONObject requestBody = new JSONObject();
        requestBody.put("combine_appid","xxxxx");
        requestBody.put("combine_mchid", merchantId);
        requestBody.put("combine_out_trade_no", "xxxxx");

        JSONArray subOrders = new JSONArray();
        JSONObject subOrderJsonObject = new JSONObject();
        subOrderJsonObject.put("mchid",merchantId);
        subOrderJsonObject.put("attach","合单支付");

        JSONObject amountJsonObject = new JSONObject();
        amountJsonObject.put("total_amount",100);
        amountJsonObject.put("currency","CNY");
        subOrderJsonObject.put("amount",amountJsonObject);

        subOrderJsonObject.put("out_trade_no", "xxxxx");
        subOrderJsonObject.put("sub_mchid", "xxxxx");
        subOrderJsonObject.put("description","商品购买");
        subOrders.add(subOrderJsonObject);
        requestBody.put("sub_orders",subOrders);
        requestBody.put("notify_url", "xxxxx");
        String requestJsonString = JSONObject.toJSONString(requestBody);
        log.info("请求参数:{}",requestJsonString);
        httpPost.setEntity(new StringEntity(requestJsonString, "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)){
            String bodyAsString = EntityUtils.toString(response.getEntity());
            log.info("response:{}",bodyAsString);
            JSONObject responseJson = JSONObject.parseObject(bodyAsString);
            String prepayId = Optional.ofNullable(responseJson.getString("prepay_id"))
                    .orElseThrow(() -> new BestPayException(responseJson.getString("message")));
            return this.buildSign(prepayId);
        } catch (IOException e) {
            log.error("微信合单支付V3接口请求失败",e);
        }
        return null;
    }
      /**
     * 生成返回给前端的数据
     * @param prepayId  预支付id
     * @return
     */
    public Map<String, String> buildSign(String prepayId) {
        Map<String, String> resultMap = new LinkedHashMap<>(8);
        resultMap.put("appid", "xxxxx");
        resultMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        resultMap.put("noncestr", "xxxxx");
        resultMap.put("prepayid", prepayId);
        resultMap.put("sign", this.signSHA256(resultMap,merchantPrivateKey));
        resultMap.put("partnerid", merchantId);
        resultMap.put("package", "Sign=WXPay");
        return resultMap;
    }
    /**
     * 微信v3支付签名
     */
    public String signSHA256(Map<String, String> params, PrivateKey privateKey) {
        StringBuilder content = new StringBuilder();
        for (String key : params.keySet()) {
            String value = params.get(key);
            if (StringUtils.isNotEmpty(value)) {
                content.append(value).append("\n");
            }
        }
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey);
            sign.update(content.toString().getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            log.error("sha256_HMAC加密失败",e);
        }
        return null;
    }
回调数据校验
 	@PostMapping(value = "wxMergePayNotify")
    public Map<String, String> wxMergePayNotify(HttpServletRequest request) {
        //给微信的回应
        Map<String, String> result = new HashMap<>(2);
        //解密数据
        String data = this.payRequestDecryption(request);
        if (data == null){
            result.put("code","FAILED");
            result.put("message","失败");
            return result;
        }
        log.info("微信支付处理后的数据data={}", data);
        JSONObject response = JSONObject.parseObject(data);
        //TODO .......业务逻辑处理
        log.info("微信支付回调成功");
        result.put("code","SUCCESS");
        result.put("message","成功");
        return result;
    }
       /**
     * v3支付回调数据校验
     */
    public String payRequestDecryption(HttpServletRequest request){
        //校验签名
        String data = this.verifySign(request);
        if (data == null){
            return null;
        }
        JSONObject jsonObject = JSONObject.parseObject(data);
        //重复通知不处理,redis示例
        String id = jsonObject.getString("id");
        String key = "WXV3PayId:" + id;
        if (Objects.equals(redisUtil.get(key),id)){
            log.info("重复通知不处理:{}",data);
            return null;
        }
        //10分钟以内的请求不处理
        redisUtil.set(key,1,60*10);
        //非支付通知不处理
        String eventType = jsonObject.getString("event_type");
        String resourceType = jsonObject.getString("resource_type");
        if (!Objects.equals(eventType,"TRANSACTION.SUCCESS") && Objects.equals(resourceType,"encrypt-resource")){
            log.info("不是支付通知不处理:{}",data);
            return null;
        }
        //参数解密
        JSONObject resource = jsonObject.getJSONObject("resource");
        String ciphertext = resource.getString("ciphertext");
        String nonce = resource.getString("nonce");
        String associatedData = resource.getString("associated_data");
        AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
        String result = null;
        try {
            result = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),nonce.getBytes(StandardCharsets.UTF_8),ciphertext);
        } catch (GeneralSecurityException e) {
            log.error("微信v3解密异常",e);
        }
        return result;
    }

	public static final String REQUEST_ID = "Request-ID";
    public static final String WECHAT_PAY_SERIAL = "Wechatpay-Serial";
    public static final String WECHAT_PAY_SIGNATURE = "Wechatpay-Signature";
    public static final String WECHAT_PAY_TIMESTAMP = "Wechatpay-Timestamp";
    public static final String WECHAT_PAY_NONCE = "Wechatpay-Nonce";
    /**
     * 签名校验
     * url https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_13.shtml
     */
    public String verifySign(HttpServletRequest request){
        //检查header
        String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
        for (String headerName : headers) {
            if (request.getHeader(headerName) == null) {
                log.info("{} is null", headerName);
                return null;
            }
        }
        //检查时间
        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
        Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp));
        if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) {
            log.info("超过应答时间");
            return null;
        }
        //获取微信返回的参数
        String data;
        try {
            data = request.getReader().lines().collect(Collectors.joining());
        } catch (IOException e) {
            log.error("获取微信V3回调参数失败",e);
            return null;
        }
        //校验签名
        String nonce = request.getHeader(WECHAT_PAY_NONCE);
        String message =  timestamp + "\n" + nonce + "\n" + data + "\n";
        String serial = request.getHeader(WECHAT_PAY_SERIAL);
        String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
        if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
            log.info("签名校验失败");
            return null;
        }
        return data;
    }

标签:return,String,微信,校验,v3,put,new,log
来源: https://blog.csdn.net/A221217/article/details/123201876

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有