• 微信支付分(三)--------授权/解除授权服务回调通知以及验签


    在此讲明,此回调文档,可通用于微信支付分的所有回调,在此就只写一次回调为例!!!!!!!!

    一.在做授权回调通知我们需要了解以下要求

    1.商户系统对于服务授权/解除授权结果通知的内容一定要做签名验证,并校验通知的信息是否与商户侧的信息一致,防止数据泄漏导致出现“假通知”,造成资金损失。(签名)

    2.该链接是通过联系微信支付运营人员配置[商户入驻配置申请表]提交service_notify_url设置,必须为https协议。如果链接无法访问,商户将无法接收到微信通知。 通知url必须为直接可访问的url,不能携带参数。示例: “https://pay.weixin.qq.com/wxpay/pay.action”(配置的回调地址,配置好,微信会自动回调)

    3.同时还需要设置APIV3密钥

    4.服务授权/解除授权结果通知是以POST方法访问商户设置的通知url,通知的数据以JSON格式通过请求主体(BODY)传输。通知的数据包括了加密的授权/解除授权结果详情。(对返回的数据进行解密)

      /**
         * 授权/解除授权服务回调
         *
         * @param params
         * @return
         */
        @RequestMapping(value = "/xx/impowerBack.do", method = RequestMethod.POST)
        @SuppressWarnings("unchecked")
        public String weixinCallback(@RequestBody Map<String, Object> params) {
            LOGGER.info("======================================进入授权/解除授权服务回调");
            JSONObject resource = new JSONObject(params.get("resource"));
    
            //获得验签字符串
            Map<String, String> certByAPI = null;
            try {
                certByAPI = Sign.getCertByAPIs(商户MCHID, “"https://api.mch.weixin.qq.com/v3/certificates"”, 10, null, 商户MCHSERIALNO, "D:\xx\xx\apiclient_key.pem私钥地址");
               
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            //拼接验签字符串
            String signStr = certByAPI.get("Wechatpay-Timestamp") + "
    " + certByAPI.get("Wechatpay-Nonce") + "
    " + certByAPI.get("result") + "
    ";
         
    
            //验签
            boolean verify = Sign.verify(signStr, certByAPI.get("Wechatpay-Signature"), "导出的公钥路径.txt");
            System.out.println("======================================verify=" + verify);
            if (verify == true) {
                try {
    
                    String data = AesUtil.decryptToString("商户设置Api3密钥".getBytes(), resource.get("associated_data").toString().getBytes(), resource.get("nonce").toString().getBytes(), resource.get("ciphertext").toString());          
                                
                } catch (GeneralSecurityException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return "";
        }
    
     public static Map<String, String> getCertByAPIs(String merchantId, String url, int timeout, String body, String certSerialNo, String keyPath) throws UnsupportedEncodingException, Exception {
            String result = "";
            //创建http请求
            HttpGet httpGet = new HttpGet(url);
            httpGet.addHeader("Content-Type", "application/json");
            httpGet.addHeader("Accept", "application/json");
    
            //设置认证信息
            httpGet.setHeader("Authorization", "WECHATPAY2-SHA256-RSA2048" + " " + getToken("GET", url, null, merchantId, certSerialNo, keyPath));
    
            //设置请求器配置:如超时限制等
            RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout * 1000).setConnectTimeout(timeout * 1000).build();
            httpGet.setConfig(config);
            List<X509Certificate> x509Certs = new ArrayList<X509Certificate>();
            try {
                CloseableHttpClient httpClient = HttpClients.createDefault();
                CloseableHttpResponse response = httpClient.execute(httpGet);
                Header[] allHeaders = response.getAllHeaders();
                Map<String, String> headers = new HashMap<String, String>();
                for (int i = 0; i < allHeaders.length; i++) {
                    String key = (String) allHeaders[i].getName();
                    String value = allHeaders[i].getValue();
                    headers.put(key, value);
                }
                String Nonce = headers.get("Wechatpay-Nonce");
                String Signature = headers.get("Wechatpay-Signature");
                String Timestamp = headers.get("Wechatpay-Timestamp");
                System.out.println(Timestamp);
    
                int statusCode = response.getStatusLine().getStatusCode();
                HttpEntity httpEntity = response.getEntity();
                result = EntityUtils.toString(httpEntity, "UTF-8");
                headers.put("result", result);
                //String result1 = Timestamp + "
    " + Nonce + "
    " + result + "
    ";
                System.out.println("下载平台证书返回结果:" + result);
    
    
                return headers;
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.error("下载平台证书返回结果:" + e);
            }
            return null;
        }
    

      

      //method(请求类型GET、POST url(请求url) body(请求body,GET请求时body传"",POST请求时body为请求参数的json串)  merchantId(商户号) certSerialNo(API证书序列号) keyPath(API证书路径)
        public static String getToken(String method, String url, String body, String merchantId, String certSerialNo, String keyPath) throws Exception {
            String signStr = "";
            HttpUrl httpurl = HttpUrl.parse(url);
    //        String nonceStr = getNonceStr();
            String nonceStr = "随机字符串";
            long timestamp = System.currentTimeMillis() / 1000;
            if (StringUtils.isEmpty(body)) {
                body = "";
            }
            String message = buildMessage(method, httpurl, timestamp, nonceStr, body);
            String signature = sign(message.getBytes("utf-8"), keyPath);
            signStr = "mchid="" + merchantId
                    + "",nonce_str="" + nonceStr
                    + "",timestamp="" + timestamp
                    + "",serial_no="" + certSerialNo
                    + "",signature="" + signature + """;
            LOGGER.info("Authorization Token:" + signStr);
            System.out.println("signStr:--" + signStr);
            return signStr;
        }
    
     public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
            String canonicalUrl = url.encodedPath();
            if (url.encodedQuery() != null) {
                canonicalUrl += "?" + url.encodedQuery();
            }
            return method + "
    "
                    + canonicalUrl + "
    "
                    + timestamp + "
    "
                    + nonceStr + "
    "
                    + body + "
    ";
        }
    

      

    public static String sign(byte[] message, String keyPath) throws Exception {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(getPrivateKey(keyPath));
            sign.update(message);
            return Base64.encodeBase64String(sign.sign());
        }
    

      

     /**
         * 验签
         *
         * @param srcData
         * @param signedData
         * @param publicKeyPath
         * @return
         */
        public static boolean verify(String srcData, String signedData, String publicKeyPath) {
            if (srcData == null || signedData == null || publicKeyPath == null) {
                return false;
            }
            try {
                PublicKey publicKey = readPublic(publicKeyPath);
                Signature sign = Signature.getInstance(ALGORITHM);
                sign.initVerify(publicKey);
                sign.update(srcData.getBytes(CHARSET_ENCODING));
                System.out.println("publicKey = " + publicKey);
                return sign.verify(java.util.Base64.getDecoder().decode(signedData));
            } catch (Exception e) {
                System.out.println("e = " + e);
                e.printStackTrace();
            }
            return false;
        }
    

      

     /**
         * 读取公钥
         *
         * @param publicKeyPath
         * @return
         */
        private static PublicKey readPublic(String publicKeyPath) {
            if (publicKeyPath == null) {
                return null;
            }
            PublicKey pk = null;
            FileInputStream bais = null;
            try {
                CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
                bais = new FileInputStream(publicKeyPath);
                X509Certificate cert = (X509Certificate) certificatefactory.generateCertificate(bais);
                pk = cert.getPublicKey();
            } catch (CertificateException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (bais != null) {
                    try {
                        bais.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return pk;
        }
    

      

    获取公钥代码

     public static List<X509Certificate> getCertByAPI(String merchantId, String url, int timeout, String body, String certSerialNo, String keyPath) throws UnsupportedEncodingException, Exception {
            String result = "";
            //创建http请求
            HttpGet httpGet = new HttpGet(url);
            httpGet.addHeader("Content-Type", "application/json");
            httpGet.addHeader("Accept", "application/json");
    
            //设置认证信息
            httpGet.setHeader("Authorization", "WECHATPAY2-SHA256-RSA2048" + " " + getToken("GET", url, null, merchantId, certSerialNo, keyPath));
    
            //设置请求器配置:如超时限制等
            RequestConfig config = RequestConfig.custom().setSocketTimeout(timeout * 1000).setConnectTimeout(timeout * 1000).build();
            httpGet.setConfig(config);
            List<X509Certificate> x509Certs = new ArrayList<X509Certificate>();
            try {
                CloseableHttpClient httpClient = HttpClients.createDefault();
                CloseableHttpResponse response = httpClient.execute(httpGet);
                int statusCode = response.getStatusLine().getStatusCode();
                HttpEntity httpEntity = response.getEntity();
                result = EntityUtils.toString(httpEntity, "UTF-8");
                if (statusCode == 200) {
                    LOGGER.info("下载平台证书返回结果:" + result);
                    List<CertificateItem> certList = new ArrayList<CertificateItem>();
                    JSONObject json = JSONObject.parseObject(result);
                    LOGGER.info("查询结果json字符串转证书List:" + json.get("data"));
                    JSONArray jsonArray = (JSONArray) json.get("data");
                    for (int i = 0; i < jsonArray.size(); i++) {
                        CertificateItem certificateItem = new CertificateItem();
                        EncryptedCertificateItem encryptCertificate = new EncryptedCertificateItem();
                        JSONObject bo = JSONObject.parseObject(jsonArray.get(i).toString());
                        certificateItem.setSerial_no(bo.get("serial_no").toString());
                        certificateItem.setEffective_time(bo.get("effective_time").toString());
                        certificateItem.setExpire_time(bo.get("expire_time").toString());
                        JSONObject encryptBo = JSONObject.parseObject(bo.get("encrypt_certificate").toString());
                        encryptCertificate.setAlgorithm(encryptBo.get("algorithm").toString());
                        encryptCertificate.setNonce(encryptBo.get("nonce").toString());
                        encryptCertificate.setAssociated_data(encryptBo.get("associated_data").toString());
                        encryptCertificate.setCiphertext(encryptBo.get("ciphertext").toString());
                        certificateItem.setEncrypt_certificate(encryptCertificate);
                        certList.add(certificateItem);
                    }
                    LOGGER.info("证书List:" + certList);
    
                    List<PlainCertificateItem> plainList = decrypt(certList, response);
                    if (CollectionUtils.isNotEmpty(plainList)) {
                        LOGGER.info("平台证书开始保存");
                        //x509Certs = saveCertificate(plainList);
                    }
                }
                response.close();
                httpClient.close(); //throw
                return x509Certs;
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.error("下载平台证书返回结果:" + e);
            }
            return x509Certs;
        }
    

      

     private static List<PlainCertificateItem> decrypt(List<CertificateItem> certList, CloseableHttpResponse response) throws GeneralSecurityException, IOException {
            List<PlainCertificateItem> plainCertificateList = new ArrayList<PlainCertificateItem>();
            AesUtil aesUtil = new AesUtil(("Api3密钥").getBytes(StandardCharsets.UTF_8));
            for (CertificateItem item : certList) {
                PlainCertificateItem bo = new PlainCertificateItem();
                bo.setSerialNo(item.getSerial_no());
                bo.setEffectiveTime(item.getEffective_time());
                bo.setExpireTime(item.getExpire_time());
                LOGGER.info("平台证书密文解密");
                bo.setPlainCertificate(aesUtil.decryptToString(item.getEncrypt_certificate().getAssociated_data().getBytes(StandardCharsets.UTF_8),
                        item.getEncrypt_certificate().getNonce().getBytes(StandardCharsets.UTF_8), item.getEncrypt_certificate().getCiphertext()));
                LOGGER.info("平台证书公钥明文:" + bo.getPlainCertificate());
                System.out.println("平台证书公钥明文:" + bo.getPlainCertificate());
                plainCertificateList.add(bo);
            }
            return plainCertificateList;
        }
    

      

    对微信返回数据进行解密

    /**
     * 对加密的授权/解除授权结果进行解密
     *
     * @param
     * @return
     */
    public class AesUtil {
        static final int KEY_LENGTH_BYTE = 32;
        static final int TAG_LENGTH_BIT = 128;
    
        public static String decryptToString(byte[] aesKey,byte[] associatedData, byte[] nonce, String ciphertext)
                throws GeneralSecurityException, IOException {
            if (aesKey.length != KEY_LENGTH_BYTE) {
                throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
            }
            try {
                Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    
                SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
                GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
    
                cipher.init(Cipher.DECRYPT_MODE, key, spec);
                cipher.updateAAD(associatedData);
    
                return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
            } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
                throw new IllegalStateException(e);
            } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
                throw new IllegalArgumentException(e);
            }
        }
    }
    

      

     

  • 相关阅读:
    JAVA 编写测试类出现的问题(二)使用junit4测试的时候出现java.lang.NullPointerException
    JAVA 编写测试类出现的问题(三)java.lang.NoClassDefFoundError: org/springframework/core/annotation/MergedAnnotations
    线程当中注入Service的时候,程序一直在报NullPointerException
    微服务当中使用openfeign做远程调用的时候,丢失请求头信息
    Interceptor拦截器当中注入Service报空指针异常
    sql server 数据库可疑解决方案 L
    server sql 无法访问本地127.0.0.1报错问题 yongqi
    vue 路由切换过渡动效 滑入 滑出效果
    物联网平台ThingsKit
    不重启服务器在线扩容FCSAN存储
  • 原文地址:https://www.cnblogs.com/wyrlzy/p/12695644.html
Copyright © 2020-2023  润新知