微信V3普通商户入驻进件及申请状态查询

首先根据微信提供的源码将平台证书下载下来https://github.com/wechatpay-apiv3/CertificateDownloader,注意第一次下载平台证书的时候去掉CertificateDownloaderTest代码里面的 "-c", wechatpayCertificateFilePath

图片上传:

public static String uploadApplyment(String filePath, String mchid, String serial_no, String rsaPrivateKey, String nonce_str,String timestamp) {
      String result=null;
      try {

         File file =new File(filePath);
         String filename = file.getName();//文件名
         String fileSha256 = DigestUtils.sha256Hex(new FileInputStream(file));//文件sha256

         //拼签名串
         StringBuffer sb =new StringBuffer();
         sb.append("POST").append("\n");
         sb.append("/v3/merchant/media/upload").append("\n");
         sb.append(timestamp).append("\n");
         sb.append(nonce_str).append("\n");
         sb.append("{\"filename\":\""+filename+"\",\"sha256\":\""+fileSha256+"\"}").append("\n");
         System.out.println("签名原串:"+sb.toString());

         //计算签名
         String sign =new String(Base64.encodeBase64(signRSA(sb.toString(),rsaPrivateKey)));
         System.out.println("签名sign值:"+sign);

         //拼装http头的Authorization内容
         String authorization ="WECHATPAY2-SHA256-RSA2048 mchid=\""+mchid+"\",nonce_str=\""+nonce_str+"\",signature=\""+sign+"\",timestamp=\""+timestamp+"\",serial_no=\""+serial_no+"\"";
         System.out.println("authorization值:"+authorization);

         //接口URL https://api.mch.weixin.qq.com/v3/merchant/media/upload
         String url ="https://api.mch.weixin.qq.com/v3/merchant/media/upload";
         CloseableHttpClient httpclient = HttpClients.createDefault();
         HttpPost httpPost =new HttpPost(url);

         //设置头部
         httpPost.addHeader("Accept","application/json");
         httpPost.addHeader("Content-Type","multipart/form-data");
         httpPost.addHeader("Authorization", authorization);

         //创建MultipartEntityBuilder
         MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532);
         //设置boundary
         multipartEntityBuilder.setBoundary("");
         multipartEntityBuilder.setCharset(Charset.forName("UTF-8"));
         multipartEntityBuilder.setBoundary("boundary");
         //设置meta内容
         multipartEntityBuilder.addTextBody("meta","{\"filename\":\""+filename+"\",\"sha256\":\""+fileSha256+"\"}", ContentType.APPLICATION_JSON);
         //设置图片内容
         multipartEntityBuilder.addBinaryBody("file", file, ContentType.create("image/jpg"), filename);
         //放入内容
         httpPost.setEntity(multipartEntityBuilder.build());

         //获取返回内容
         CloseableHttpResponse response = httpclient.execute(httpPost);
         HttpEntity httpEntity = response.getEntity();

         String rescontent =new String(InputStreamTOByte(httpEntity.getContent()));
         System.out.println("返回内容:" + rescontent);

         
      }catch (Exception e) {
         System.out.println("发送POST请求异常!" + e);
         e.printStackTrace();
      }
      return result;
   }
   public static byte[] InputStreamTOByte(InputStream in)throws IOException{

      int BUFFER_SIZE =4096;
      ByteArrayOutputStream outStream =new ByteArrayOutputStream();
      byte[] data =new byte[BUFFER_SIZE];
      int count = -1;

      while((count = in.read(data,0,BUFFER_SIZE)) != -1)
         outStream.write(data,0, count);

      data =null;
      byte[] outByte = outStream.toByteArray();
      outStream.close();

      return outByte;
   }


   public static byte[] signRSA(String data, String priKey) throws Exception {



//签名的类型

      Signature sign = Signature.getInstance("SHA256withRSA");

      //读取商户私钥,该方法传入商户私钥证书的内容即可

      byte[] keyBytes = Base64.decodeBase64(priKey);

      PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

      KeyFactory keyFactory = KeyFactory.getInstance("RSA");

      PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

      sign.initSign(privateKey);

      sign.update(data.getBytes("UTF-8"));

      return sign.sign();

   }

接下来是进件和查询的代码

public class WechartTest {

    /***
     查询申请单
     */
    @Test
    public void test() throws Exception {

        String url = "https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/applyment_id/123456";
     
        String merchantId = "";
        String serialNo = WeChatUtil.getSerialNo(WeChatUtil.apiclient_cert);
        String token = WeChatUtil.getToken("GET", url, null, merchantId, serialNo);
        String authorization = "WECHATPAY2-SHA256-RSA2048 " + token;
        // 平台证书路径 是必须的
        serialNo = WeChatUtil.getSerialNo(WeChatUtil.publicKeyPath);
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Wechatpay-Serial", serialNo);
        httpGet.setHeader("Accept", "application/json");
        httpGet.setHeader("Content-Type", "application/json");
        httpGet.setHeader("user-agent", WeChatUtil.DEFAULT_USER_AGENT);
        httpGet.setHeader("Authorization", authorization);

        HttpClientBuilder httpClientBuilder = HttpClients.custom();
        CloseableHttpClient httpClient = httpClientBuilder.build();
        CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
        HttpEntity httpResponseEntity = httpResponse.getEntity();
        String responseEntityStr = EntityUtils.toString(httpResponseEntity);
        httpResponse.close();
        System.out.println(responseEntityStr);


    }

    /***
     进件接口
     */
    @Test
    public void test2() throws Exception {
        String url = "https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/";
        // TODO
        String merchantId = "";
        String serialNo = WeChatUtil.getSerialNo(WeChatUtil.apiclient_cert);
        Map<String,Object> requestMap = new HashMap<>(1);
        requestMap.put("business_code","业务申请编号");

        // 参数自己根据文档进行组装

        String requestParams = JSONObject.toJSONString(requestMap);
        String token = WeChatUtil.getToken("POST", url, requestParams, merchantId, serialNo);
        String authorization = "WECHATPAY2-SHA256-RSA2048 " + token;
        // 平台证书路径 是必须的
        serialNo = WeChatUtil.getSerialNo(WeChatUtil.publicKeyPath);
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader("Wechatpay-Serial", serialNo);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-Type", "application/json");
        httpPost.setHeader("user-agent", WeChatUtil.DEFAULT_USER_AGENT);
        httpPost.setHeader("Authorization", authorization);
        httpPost.setEntity(new StringEntity(requestParams, "UTF-8"));
        HttpClientBuilder httpClientBuilder = HttpClients.custom();
        CloseableHttpClient httpClient = httpClientBuilder.build();
        CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpResponseEntity = httpResponse.getEntity();
        String responseEntityStr = EntityUtils.toString(httpResponseEntity);
        httpResponse.close();
        System.out.println(responseEntityStr);

    }

    /**
     * 敏感字加密
     *
     * @throws IOException
     */
    @Test
    public void test1() throws IOException, IllegalBlockSizeException {
        X509Certificate certificate = WeChatUtil.getCertificate(WeChatUtil.publicKeyPath);
        Cipher cipher = WeChatUtil.getCipher(certificate);
        String s = WeChatUtil.rsaEncryptOAEP("张三", cipher);
        System.out.println(s);
    }
}
public class WeChatUtil {

    public static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36";

    // 商户证书路径
    public static final String apiclient_cert = "apiclient_cert.pem";
    // 平台证书路径
    public static final String publicKeyPath = "wechatpay.pem";
    // 商户私钥路径
    public static final String privageKeyPath = "apiclient_key.pem";

    /**
     * 获取证书序列号
     *
     * @param certPath 获取商户证书序列号 传递商号证书路径 apiclient_cert
     * @return
     * @throws IOException
     */
    public static String getSerialNo(String certPath) throws IOException {
        X509Certificate certificate = getCertificate(certPath);
        return certificate.getSerialNumber().toString(16).toUpperCase();
    }

    /**
     * 获取证书。
     *
     * @param filename 证书文件路径  (required)
     * @return X509证书
     */
    public static X509Certificate getCertificate(String filename) throws IOException {
        InputStream fis = new FileInputStream(filename);
        try (BufferedInputStream bis = new BufferedInputStream(fis)) {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        }
    }

    /**
     * 获取签名
     * method(请求类型GET、POST url(请求url)
     * body(请求body,GET请求时body传"",POST请求时body为请求参数的json串)
     * merchantId(商户号)
     * certSerialNo(API证书序列号)
     * keyPath(API证书路径)
     *
     * @param method       请求方式
     * @param url          请求路径
     * @param body         请求参数
     * @param merchantId   商户号
     * @param certSerialNo API证书序列号
     * @return
     * @throws Exception
     */
    public static String getToken(String method, String url, String body, String merchantId, String certSerialNo) throws Exception {
        String signStr = "";
        HttpUrl httpurl = HttpUrl.parse(url);
        String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
        long timestamp = System.currentTimeMillis() / 1000;
        if (StringUtils.isEmpty(body)) {
            body = "";
        }
        assert httpurl != null;
        String message = buildMessage(method, url, timestamp, nonceStr, body);
        String signature = sign(message.getBytes(StandardCharsets.UTF_8), privageKeyPath);
        signStr = "mchid=\"" + merchantId
                + "\",nonce_str=\"" + nonceStr
                + "\",timestamp=\"" + timestamp
                + "\",serial_no=\"" + certSerialNo
                + "\",signature=\"" + signature + "\"";
        return signStr;
    }

    public static String buildMessage(String method, String url, long timestamp, String nonceStr, String body) {
//        String canonicalUrl = url.encodedPath();
//        if (url.encodedQuery() != null) {
//            canonicalUrl += "?" + url.encodedQuery();
//        }
        return method + "\n"
                + url + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }


    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 filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String filename) throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

    public static Cipher getCipher(X509Certificate certificate) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
            return cipher;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的证书", e);
        }
    }

    /**
     * 加密
     *
     * @param message
     * @param cipher  通过getcipher方法获取
     * @return
     * @throws IllegalBlockSizeException
     */
    public static String rsaEncryptOAEP(String message, Cipher cipher) throws IllegalBlockSizeException {
        System.out.println("message:"+message);
        try {
            if (StringUtils.isBlank(message)) {
                return null;
            }
            byte[] data = message.getBytes(StandardCharsets.UTF_8);
            byte[] cipherdata = cipher.doFinal(data);
            return Base64.encodeBase64String(cipherdata);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
        }
    }


}

自己开发的时候遇到了很多问题查找了很多资料 ,希望大家不要踩雷

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值