diff --git a/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatConfig.java b/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatConfig.java index ace7aaab8..8be29a6e0 100644 --- a/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatConfig.java +++ b/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatConfig.java @@ -18,7 +18,7 @@ public class WechatConfig { * @param dto 微信支付配置 * @return 微信支付配置 */ - public static Config getConfig(WechatPayConfigDto dto) { + public static Config getRsaConfig(WechatPayConfigDto dto) { return new RSAPublicKeyConfig.Builder() .merchantId(dto.getMerchantId()) .privateKey(dto.getPrivateKey()) @@ -41,7 +41,7 @@ public class WechatConfig { * @return 文件上传服务 */ public static FileUploadService getFileUploadService(WechatPayConfigDto dto) { - return getFileUploadService(getConfig(dto)); + return getFileUploadService(getRsaConfig(dto)); } } diff --git a/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatEncrypt.java b/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatEncrypt.java index 82c2cd179..edca7803b 100644 --- a/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatEncrypt.java +++ b/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatEncrypt.java @@ -1,15 +1,45 @@ package com.czg.wechat; +import lombok.extern.slf4j.Slf4j; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.util.Base64; /** * 微信支付加解密 * @author yjjie * @date 2025/12/26 10:58 */ +@Slf4j public class WechatEncrypt { + public static String rsaEncryptOaep(String message, PublicKey publicKey) { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] data = message.getBytes(StandardCharsets.UTF_8); + byte[] cipherData = cipher.doFinal(data); + return Base64.getEncoder().encodeToString(cipherData); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + log.error("当前Java环境不支持RSA v1.5/OAEP"); + } catch (InvalidKeyException e) { + log.error("无效的证书"); + } catch (IllegalBlockSizeException | BadPaddingException e) { + log.error("加密原串的长度不能超过214字节"); + } + return ""; + } + public static String getFileBytesSha256(byte[] input) throws NoSuchAlgorithmException { return bytesToHex(calculateSha256(input)); } diff --git a/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatEntryManager.java b/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatEntryManager.java index 85e9e7c84..7524b156c 100644 --- a/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatEntryManager.java +++ b/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatEntryManager.java @@ -3,7 +3,15 @@ package com.czg.wechat; import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONWriter; import com.czg.wechat.dto.config.WechatPayConfigDto; -import com.czg.wechat.dto.req.entry.WechatEntryReqDto; +import com.czg.wechat.dto.req.entry.*; +import com.czg.wechat.dto.req.entry.business.WechatEntryBusinessReqDto; +import com.czg.wechat.dto.req.entry.business.WechatEntryIdentityReqDto; +import com.czg.wechat.dto.req.entry.business.WechatEntryLicenseReqDto; +import com.czg.wechat.dto.req.entry.business.sales.WechatEntrySalesInfoReqDto; +import com.czg.wechat.dto.req.entry.business.sales.WechatEntryStoreInfoReqDto; +import com.wechat.pay.java.core.Config; +import com.wechat.pay.java.core.cipher.PrivacyEncryptor; +import com.wechat.pay.java.core.cipher.Signer; import com.wechat.pay.java.service.file.FileUploadService; import com.wechat.pay.java.service.file.model.FileUploadResponse; import lombok.extern.slf4j.Slf4j; @@ -12,6 +20,8 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.util.Arrays; +import java.util.List; /** * 微信支付进件 管理 @@ -22,6 +32,21 @@ import java.net.http.HttpResponse; @Slf4j public class WechatEntryManager { + public static void entryMerchant(WechatPayConfigDto configDto, WechatEntryReqDto reqDto) { + String params = JSONObject.toJSONString(reqDto, JSONWriter.Feature.IgnoreEmpty); + +// String string = WechatReqUtils.encryptReqParam(configDto, "POST", "/v3/applyment4sub/applyment/", params); + + WechatReqUtils.postReq(configDto, "POST", "/v3/applyment4sub/applyment/", params); + } + + /** + * 上传图片 + * + * @param configDto 配置 + * @param url 图片URL + * @return 图片ID + */ public static String uploadImage(WechatPayConfigDto configDto, String url) { // 校验入参 if (configDto == null || url == null || url.trim().isEmpty()) { @@ -156,7 +181,78 @@ public class WechatEntryManager { // String string = uploadImage(dto, "https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png"); // log.info("图片上传成功:{}", string); - WechatEntryReqDto reqDto = new WechatEntryReqDto(); - System.out.println(JSONObject.toJSONString(reqDto, JSONWriter.Feature.IgnoreEmpty)); + Config config = WechatConfig.getRsaConfig(dto); + PrivacyEncryptor encryptor = config.createEncryptor(); + + WechatEntryReqDto reqDto = new WechatEntryReqDto() + .setBusinessCode("MER_20231025110010000010000000000001"); + + WechatEntryContactReqDto contactInfo = new WechatEntryContactReqDto() + .setContactType("LEGAL") + .setContactName(encryptor.encrypt("张三")) + .setContactIdType("IDCARD") + .setContactIdNumber(encryptor.encrypt("110101199001011234")) + .setContactIdDocCopy("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png") + .setContactIdDocCopyBack("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png") + .setContactPeriodBegin("2023-10-25") + .setContactPeriodEnd("2024-10-25") + .setMobilePhone(encryptor.encrypt("13888888888")) + .setContactEmail(encryptor.encrypt("123456@qq.com")); + + reqDto.setContactInfo(contactInfo); + + WechatEntrySubjectReqDto subjectInfo = new WechatEntrySubjectReqDto() + .setSubjectType("SUBJECT_TYPE_INDIVIDUAL"); + + WechatEntryLicenseReqDto licenseInfo = new WechatEntryLicenseReqDto() + .setLicenseCopy("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png") + .setLicenseNumber("110101199001011234") + .setMerchantName("张三商行") + .setLegalPerson(encryptor.encrypt("张三")) + .setLicenseAddress("北京") + .setPeriodBegin("2023-10-25") + .setPeriodEnd("2024-10-25"); + subjectInfo.setBusinessLicenseInfo(licenseInfo); + + WechatEntryIdentityReqDto identityInfo = new WechatEntryIdentityReqDto() + .setIdHolderType("LEGAL"); + subjectInfo.setIdentityInfo(identityInfo); + + reqDto.setSubjectInfo(subjectInfo); + + WechatEntryBusinessReqDto businessInfo = new WechatEntryBusinessReqDto() + .setMerchantShortname("张三商行") + .setServicePhone("13888888888"); + WechatEntrySalesInfoReqDto salesInfo = new WechatEntrySalesInfoReqDto() + .setSalesScenesType(List.of("SALES_SCENES_STORE")); + WechatEntryStoreInfoReqDto storeInfo = new WechatEntryStoreInfoReqDto() + .setBizStoreName("张三商行") + .setBizAddressCode("110101") + .setBizStoreAddress("北京") + .setStoreEntrancePic(List.of("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png")) + .setIndoorPic(List.of("https://czg-qr-order.oss-cn-beijing.aliyuncs.com/indexs/shuangbackground.png")); + salesInfo.setBizStoreInfo(storeInfo); + businessInfo.setSalesInfo(salesInfo); + + reqDto.setBusinessInfo(businessInfo); + + WechatEntrySettleReqDto settleInfo = new WechatEntrySettleReqDto() + .setSettlementId("719") + .setQualificationType("IDCARD") + .setQualifications(List.of("110101199001011234")) + .setActivitiesId("20191030111cff5b5e"); + + reqDto.setSettlementInfo(settleInfo); + + WechatEntryBankAccountReqDto bankAccountInfo = new WechatEntryBankAccountReqDto() + .setBankAccountType("BANK_ACCOUNT_TYPE_CORPORATE") + .setAccountBank("ICBC") + .setBankName("中国工商银行") + .setAccountName(encryptor.encrypt("张三")) + .setAccountNumber(encryptor.encrypt("110101199001011234")); + + reqDto.setBankAccountInfo(bankAccountInfo); + + entryMerchant(dto, reqDto); } } diff --git a/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatReqUtils.java b/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatReqUtils.java new file mode 100644 index 000000000..b92fb54fc --- /dev/null +++ b/cash-sdk/aggregation-pay/src/main/java/com/czg/wechat/WechatReqUtils.java @@ -0,0 +1,87 @@ +package com.czg.wechat; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpUtil; +import com.czg.wechat.dto.config.WechatPayConfigDto; +import com.wechat.pay.java.core.Config; +import com.wechat.pay.java.core.RSAPublicKeyConfig; +import com.wechat.pay.java.core.cipher.PrivacyEncryptor; +import com.wechat.pay.java.core.cipher.Signer; +import lombok.extern.slf4j.Slf4j; + +import java.util.UUID; + +/** + * 微信请求工具类 + * + * @author yjjie + * @date 2025/12/29 10:02 + */ +@Slf4j +public class WechatReqUtils { + + public static String postReq(WechatPayConfigDto configDto, String method, String url, String body) { + long timestamp = getTimestamp(); + String nonce = getNonceStr(); + + String signature = encryptReqParam(configDto, method, url, body, timestamp, nonce); + + String authorization = String.format("WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",signature=\"%s\",timestamp=\"%d\",serial_no=\"%s\"", + configDto.getMerchantId(), nonce, signature, timestamp, "4DE9BAC2EA584C3F274F694C9753CA814C4E9BF4"); + + log.info("authorization = {}", authorization); + + HttpRequest request = HttpUtil.createPost(configDto.getDomain() + url) + .header("Authorization", authorization) + .header("Content-Type", "application/json") + .header("Wechatpay-Serial", configDto.getPublicKeyId()) +// .header("Wechatpay-Serial", "4DE9BAC2EA584C3F274F694C9753CA814C4E9BF4") + .body(body); + + String s = request.execute().body(); + log.info("s = {}", s); + + return ""; + } + + /** + * 加密请求参数 + * @param configDto 配置 + * @param method 请求方法 + * @param url 请求地址 + * @param body 请求报文主体 + * @return 加密后的报文 + * + * 签名方法 + * HTTP请求方法\n + * URL\n + * 请求时间戳\n + * 请求随机串\n + * 请求报文主体\n + */ + public static String encryptReqParam(WechatPayConfigDto configDto, String method, String url, String body, long timestamp, String nonce) { + String encryptStr = String.format("%s\n%s\n%d\n%s\n%s\n", + method, url, timestamp, nonce, body); + System.out.println("encryptStr = \n" + encryptStr); + + Config config = WechatConfig.getRsaConfig(configDto); + Signer signer = config.createSigner(); + String signature = signer.sign(encryptStr).getSign(); + System.out.println("signature = " + signature); + return signature; + } + + /** + * 获取随机串 + */ + private static String getNonceStr() { + return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase(); + } + + /** + * 获取时间戳 + */ + private static long getTimestamp() { + return System.currentTimeMillis()/1000; + } +}