Docking java implementation of V3 interface from wechat cash withdrawal to change

Posted by Saviola on Mon, 20 Dec 2021 18:03:54 +0100

Docking java implementation of V3 interface from wechat cash withdrawal to change

Recently, the project needs to realize the function of withdrawing cash to change with wechat, but it is found that the wechat payment interface has been upgraded to V3 version, which means that the previous general methods and SDK of wechat payment are not available, so it is necessary to re correspond to the SDK of v3. WeChat's docs have been very popular, and make complaints about them.
Wechat cash withdrawal is connected to the change V3 interface. I divide it into two tasks: 1. Wechat payment platform certificate download; 2. Wechat cash withdrawal to the change V3 interface.
Here are some key codes listed. For the complete code, please refer to the java implementation demo of docking from wechat cash withdrawal to change V3 interface.

Wechat platform payment certificate Download

Because the WeChat payment platform certificate is changing in real time, so we need to download and get the latest certificate every once in a while. This design is really wonderful, and I love the simplicity and friendliness of Alipay.
I use the regular program to download once every hour. After downloading, I save the latest wechat platform payment certificate number to the local database, as shown in the following code example (this code refers to the official wechat example code).

//Timed task
@Component("wxpayCertDownTask")
@Lazy(false)
public class wxpayCertDownTask {
	@Autowired
	private ISysSetService sysSetService;
	private boolean isTest = Constant.PROGRAM_MODE;

	@Scheduled(cron = "${wechatpay.cert.down.task}")
	public void startWxpayCertDown() {
		
			CertificateDownloader downloader = new CertificateDownloader();
			downloader.startDownWachatPayCert();
			if (StringUtils.isNotEmpty(downloader.getWechatpayCertSerialNo())) {
				//... Omit some code
				System.out.println("---Wechat platform payment certificate serial number---:" + downloader.getWechatpayCertSerialNo());
				sysSetService.updateSysSet(s);//Update and save the serial number of wechat platform payment certificate
			}
	}
}

//Definition of Downloader
@Data
public class CertificateDownloader  {
    //Key for certificate decryption
    private String apiV3key;

    //Merchant number
    private String merchantId;

   //Merchant private key file
    private String privateKeyFilePath;

    //Merchant certificate serial number
    private String serialNo;

    //Certificate save path
    private String outputFilePath;

    //Wechat payment platform certificate for signature verification
    private String wechatpayCertificatePath;
    //Wechat payment platform certificate serial number
    private String wechatpayCertSerialNo;

    private static final String CertDownloadPath = "https://api.mch.weixin.qq.com/v3/certificates";

 

    private List<PlainCertificateItem> downloadCertificate() throws IOException, GeneralSecurityException {
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, serialNo,
                		PemUtil.loadPrivateKey(new FileInputStream(privateKeyFilePath)));

        if (wechatpayCertificatePath == null) {
            //No signature verification
            builder.withValidator(response -> true);
        } else {
            List<X509Certificate> certs = new ArrayList<>();
            certs.add(PemUtil.loadCertificate(new FileInputStream(wechatpayCertificatePath)));
            builder.withWechatpay(certs);
        }

        HttpGet httpGet = new HttpGet(CertDownloadPath);
        httpGet.addHeader("Accept", "application/json");

        try (CloseableHttpClient client = builder.build()) {
            CloseableHttpResponse response = client.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            String body = EntityUtils.toString(response.getEntity());
            if (statusCode == 200) {
                //System.out.println("body:" + body);
                CertificateList certList = JsonUtils.convertJsonToCertList(body);
                return decryptAndValidate(certList, response);
            } else {
                System.out.println("download failed,resp code=" + statusCode + ",body=" + body);
                throw new IOException("request failed");
            }
        }
    }

    private List<PlainCertificateItem> decryptAndValidate(CertificateList certList, CloseableHttpResponse response) throws GeneralSecurityException, IOException {
        List<PlainCertificateItem> plainCerts = new ArrayList<>();
        List<X509Certificate> x509Certs = new ArrayList<>();
        AesUtil decryptor = new AesUtil(apiV3key.getBytes(StandardCharsets.UTF_8));
        for (CertificateItem item : certList.getCerts()) {
            PlainCertificateItem plainCert = new PlainCertificateItem(
                    item.getSerialNo(), item.getEffectiveTime(), item.getExpireTime(),
                    decryptor.decryptToString(
                            item.getEncryptCertificate().getAssociatedData().getBytes(StandardCharsets.UTF_8),
                            item.getEncryptCertificate().getNonce().getBytes(StandardCharsets.UTF_8),
                            item.getEncryptCertificate().getCiphertext()));

            ByteArrayInputStream inputStream = new ByteArrayInputStream(plainCert.getPlainCertificate().getBytes(StandardCharsets.UTF_8));
            X509Certificate x509Cert = PemUtil.loadCertificate(inputStream);
            plainCerts.add(plainCert);
            x509Certs.add(x509Cert);
        }

        //From the downloaded certificate, obtain the certificate corresponding to the private key signing the message, and verify the signature to verify the accuracy of the certificate
        Verifier verifier = new CertificatesVerifier(x509Certs);
        Validator validator = new WechatPay2Validator(verifier);
        boolean isCorrectCert = validator.validate(response);
        //System.out.println(isCorrectCert ? "=== validate success ===" : "=== validate failed ===");
        return isCorrectCert ? plainCerts : null;
    }

    private void saveCertificate(List<PlainCertificateItem> cert) throws IOException {
        for (PlainCertificateItem item : cert) {
        	this.setWechatpayCertSerialNo(item.getSerialNo());
            String outputAbsoluteFilename = outputFilePath + File.separator + Constant.WXPAY_WECHATPAY_pem;
            try (BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(new FileOutputStream(outputAbsoluteFilename), StandardCharsets.UTF_8))) {
                writer.write(item.getPlainCertificate());
            }
        }
    }

    public void startDownWachatPayCert() {
    	this.setApiV3key(WechatConfigApp.apiV3Key);
    	this.setMerchantId(WechatConfigApp.mchID4M);
    	this.setSerialNo(WechatConfigApp.serialNo);
    	this.setPrivateKeyFilePath(
    			ClientCustomSSL.getProjectPath()+ File.separator +Constant.WXPAY_APP_CERT_APICLIENTKEY_PATH4M);
    	this.setOutputFilePath(ClientCustomSSL.getProjectPath());
        
        try {
            System.out.println("=== download begin ===");
            List<PlainCertificateItem> plainCerts = downloadCertificate();
            System.out.println("=== download done ===");

            if (plainCerts != null) {
                System.out.println("=== save begin ===");
                saveCertificate(plainCerts);
                System.out.println("=== save done ===");
            }
        } catch (IOException | GeneralSecurityException e) {
            e.printStackTrace();
        }
    }
}

Wechat cash withdrawal to change V3 interface

Before implementing the interface, you should carefully read the official documents. Here are some key codes, which are sorted out on the basis of the official SDK.
			// Wechat withdrawal 
    		// Call the payment interface to withdraw, and construct parameters according to the interface document
    		int  tatalamount =new BigDecimal(accamount).multiply(new BigDecimal(100)).intValue();//Unit to minute
    		Map<String,Object> transferDetail = new HashMap<String,Object>();
    		List<Map<String,Object>> detailList = new ArrayList<Map<String,Object>>();
    		transferDetail.put("out_detail_no", UUIDUtil.GeneratorUUIDKey());
    		transferDetail.put("transfer_amount", tatalamount);
    		transferDetail.put("transfer_remark", Constant.JIANGLI_REDBOX);
    		transferDetail.put("openid", u.getOpenid());
    		//User name encryption processing
    		String wechatpaycert = ...;
    		//Wechat payment platform certificate
    		X509Certificate cert = PemUtil.loadCertificate(PemUtil.loadCertificateInStream(wechatpaycert));
    		String rsaString = RsaCryptoUtil.encryptOAEP( u.getRealname(),cert);//Withdrawal user real name encryption
    		transferDetail.put("user_name", rsaString);
    		detailList.add(transferDetail);
    		//Wechat payment platform certificate serial number
    		//... Omit
    		String wechatPayserialNo  = ;//Obtain the saved wechat payment platform certificate serial number from the data
    		//Wechat withdrawal and transfer
    		String resp = WechatPay.weixinTransferBat(detailList, tatalamount,
    				Constant.JIANGLI_REDBOX, outBizNo, 1,wechatPayserialNo);

    		JsonParser parser = new JsonParser();
            JsonObject jsonObj = parser.parse(resp).getAsJsonObject();
            // Payment successful
    		if(jsonObj.get("batch_id")!=null){
    		//... Omit

--------------------------------------------------------------------
public static String weixinTransferBat(List<Map<String,Object>> transferDetail,int total_amount,String decript,
			String orderNo,int total_num,String wechatPayserialNo) throws Exception {  
		
        Map<String,Object> postMap=new HashMap<String,Object>();
        postMap.put("appid", WechatConfigApp.appID4M);
        postMap.put("out_batch_no", orderNo);
        postMap.put("batch_name", decript);
        postMap.put("batch_remark", decript);
        postMap.put("total_amount", total_amount);//Unit: minute
        postMap.put("total_num", total_num);
        postMap.put("transfer_detail_list", transferDetail);
        
        String resStr = HttpUtil.postTransBatRequest(WechatConfigApp.TRANSFER_BAT_API,
        		JsonUtil.beanToJson(postMap),wechatPayserialNo);
        
        System.out.println("weixinTransferBat ===== "+resStr);
        return resStr;  
	}
---------------------------------------------------------------------
/**
     * Initiate batch transfer API batch transfer to change
     * @param requestUrl
     * @param requestJson
     * @return
     */
    public static String postTransBatRequest(String requestUrl,String requestJson
    		,String wechatPayserialNo) {
        //CloseableHttpClient httpClient = null;
        CloseableHttpClient httpclient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try {
        	//Merchant private key certificate
      	  String privatekeypath = ...;
            HttpPost httpPost = new HttpPost(requestUrl);

            // NOTE: it is recommended to specify charset=utf-8. Less than 4.4 Version 6 of HttpCore cannot set the character set correctly, which may lead to signature errors
        	httpPost.addHeader("Content-Type", "application/json");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Wechatpay-Serial", wechatPayserialNo);//"55E551E614BAA5A3EA38AE03849A76D8C7DA735A");
            String strToken = VechatPayV3Util.getToken("POST", 
    				  "/v3/transfer/batches", requestJson,
    				  WechatConfigApp.mchID4M, //Merchant wechat payment merchant ID
    				  WechatConfigApp.serialNo,//Merchant wechat payment certificate serial number
    				   privatekeypath);//Merchant wechat payment certificate private key
            // Add authentication information
            httpPost.addHeader("Authorization",
          		  "WECHATPAY2-SHA256-RSA2048" + " "
  				  + strToken);
            httpPost.setEntity(new StringEntity(requestJson, "UTF-8"));  
            response = httpclient.execute(httpPost);
        	entity = response.getEntity();//Get the returned data
        	System.out.println("-----getHeaders.Request-ID:"+response.getHeaders("Request-ID"));
            return EntityUtils.toString(entity);
      	
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // Close flow
        }
        return null;
    }
    

Some codes and brief descriptions are listed. I hope I can help those in need and leave notes for myself. Also, the source code in the demo of docking java implementation of wechat cash withdrawal to change V3 interface does not have a package structure. Please organize the package structure yourself.

END

Topics: Java wechat