diff --git a/fastbee-common/src/main/java/com/fastbee/common/utils/pay/RSAUtil.java b/fastbee-common/src/main/java/com/fastbee/common/utils/pay/RSAUtil.java index d227cc9..40738d2 100644 --- a/fastbee-common/src/main/java/com/fastbee/common/utils/pay/RSAUtil.java +++ b/fastbee-common/src/main/java/com/fastbee/common/utils/pay/RSAUtil.java @@ -1,16 +1,19 @@ package com.fastbee.common.utils.pay; +import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.PublicKey; import java.security.Signature; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; public class RSAUtil { /** - * 验签方法 + * 验签方法---微信公钥 * @param wechatpaySignature Wechatpay-Signature 响应头中的签名 * @param wechatpayTimestamp Wechatpay-Timestamp 响应头中的时间戳 * @param wechatpayNonce Wechatpay-Nonce 响应头中的随机串 @@ -48,4 +51,43 @@ public class RSAUtil { } + + /** + * 验签方法---平台证书 + * @param wechatpaySignature Wechatpay-Signature 响应头中的签名 + * @param wechatpayTimestamp Wechatpay-Timestamp 响应头中的时间戳 + * @param wechatpayNonce Wechatpay-Nonce 响应头中的随机串 + * @param responseBody 应答报文主体 + * @param publicKey 微信支付公钥(PEM 格式,去掉头尾并解码为二进制) + * @return 是否验签成功 + * @throws Exception + */ + public static boolean verifySignatureByCertificate(String wechatpaySignature, + String wechatpayTimestamp, + String wechatpayNonce, + String responseBody, + String publicKey) throws Exception { + // 构造验签名串:应答时间戳\n应答随机串\n应答报文主体\n + String message = wechatpayTimestamp + "\n" + + wechatpayNonce + "\n" + + responseBody + "\n"; + + // 解码微信支付公钥(Base64 格式) + byte[] certBytes = Base64.getDecoder().decode(publicKey); + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + X509Certificate certificate = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(certBytes)); + PublicKey rsaPublicKey = certificate.getPublicKey(); + + // 初始化 Signature 对象,指定算法为 SHA256withRSA + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initVerify(rsaPublicKey); + + // 更新验签名串 + signature.update(message.getBytes(StandardCharsets.UTF_8)); + + // 验证签名 + byte[] signatureBytes = Base64.getDecoder().decode(wechatpaySignature); + return signature.verify(signatureBytes); + } + } diff --git a/fastbee-common/src/main/java/com/fastbee/common/utils/pay/damogang_platformCertificate.pem b/fastbee-common/src/main/java/com/fastbee/common/utils/pay/damogang_platformCertificate.pem new file mode 100644 index 0000000..4021720 --- /dev/null +++ b/fastbee-common/src/main/java/com/fastbee/common/utils/pay/damogang_platformCertificate.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3DCCAsSgAwIBAgIUWlvfWWX+OjBk6Z+u8PgNGNCJyHIwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjIxMTI1MDcxNzQ5WhcNMjcxMTI0MDcxNzQ5WjBuMRgwFgYDVQQDDA9U +ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl +bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo +ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4JVj9olHA76puWcKC +BLkkFKN4+NxexbtD/vw3A/1Lwh5uEGkekmIUgcJd6/gC7Bb4Ixx5wjJS3+gpjNjL +bET9KVgCR2KwPmpDIcnHh6ruJ7SeUMseizEVx3DLuQJDJJAojbgQUdddFzP48O1q +8Hd4qG45axB5dw9UUHJzM4kqqLTuqMs5eeKzGUMCCq8i5vpAg52IicTCG4AJXoIg +WK65c5d3QM/tZPgUHrj2sscZS09dP5oPeQKRZ4tWvOkHuh0+WMLdz6PEuZDBBgKz +vDk8a+JwsKh7O2TKDXgQTIswTM0coi3/gr82AxNjsKzou5GDAcFaGaAouq1BwkRj +ct/VAgMBAAGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwID+DBlBgNVHR8EXjBc +MFqgWKBWhlRodHRwOi8vZXZjYS5pdHJ1cy5jb20uY24vcHVibGljL2l0cnVzY3Js +P0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3NTQ5ODQ2QzAxQzNFOEVCRDIwDQYJ +KoZIhvcNAQELBQADggEBAHnekspTjNB66Vu7sL6nk9bPIYHIiQeCGz7BQbc1Gbfu +VrawOgubR1ahRJ06fVcsifdhbHvc0+TLmbQXvld1+SgAblsf/jmwqePVkQgHbaYO +MEP0jAQIJAcKnv9jpIW3zuaQzvkCf4OR6T27EUZNojUis+sDn+/AY96cmAXJppYF +02e0kQPewXQZdL+fPjTHZyB2i0Z9V24csQKmbb8KCslGsd/L3mh2BGx05K4Bt05A +3UXxW+QuhmDTzBMZkNNQIaPrKigTFX13KH4sbnK9LLkE/pTIO96twp23KG0otUPR +0nfdJpGhyyUO88JotIAWjTRShUtnIssfcUpjuF5BjMw= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/fastbee-common/src/main/java/com/fastbee/common/utils/pay/wxPayConfig.java b/fastbee-common/src/main/java/com/fastbee/common/utils/pay/wxPayConfig.java index 68c1704..a6d4c7a 100644 --- a/fastbee-common/src/main/java/com/fastbee/common/utils/pay/wxPayConfig.java +++ b/fastbee-common/src/main/java/com/fastbee/common/utils/pay/wxPayConfig.java @@ -4,11 +4,37 @@ import java.io.*; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; public class wxPayConfig { + /** + * 获取平台证书公钥 + * @param platformCertificatePath + * @return + */ + public static String getPublicKeyByCertificat(String platformCertificatePath) + { + try { + // 读取私钥文件内容 + String platformCertificateContent = readFile(platformCertificatePath); + // 去除私钥文件中的头尾信息 + platformCertificateContent = platformCertificateContent.replace("-----BEGIN CERTIFICATE-----", "") + .replace("-----END CERTIFICATE-----", "") + .replaceAll("\\s+", ""); + /*byte[] certBytes = Base64.getDecoder().decode(platformCertificateContent); + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + X509Certificate certificate = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(certBytes)); + String publicKey = certificate.getPublicKey().toString();*/ + return platformCertificateContent; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } // 获取微信支付公钥 public static String getPublicKey(String publicKeyPath) { diff --git a/fastbee-open-api/src/main/java/com/fastbee/data/controller/pay/WeChatPayController.java b/fastbee-open-api/src/main/java/com/fastbee/data/controller/pay/WeChatPayController.java index 0af17af..9ec7907 100644 --- a/fastbee-open-api/src/main/java/com/fastbee/data/controller/pay/WeChatPayController.java +++ b/fastbee-open-api/src/main/java/com/fastbee/data/controller/pay/WeChatPayController.java @@ -46,7 +46,8 @@ import com.fastbee.common.utils.pay.wxPayConfig; import static cn.hutool.core.util.XmlUtil.xmlToMap; import static com.fastbee.common.constant.Constants.LANGUAGE; import static com.fastbee.common.utils.pay.RSAUtil.verifySignature; -import static com.fastbee.rechargecard.service.impl.UserWechatPayServiceImpl.apiV3Key; +import static com.fastbee.common.utils.pay.RSAUtil.verifySignatureByCertificate; +import static com.fastbee.rechargecard.service.impl.UserWechatPayServiceImpl.*; /** * @author mijiupro @@ -67,7 +68,14 @@ public class WeChatPayController extends BaseController { @Autowired private IUserWechatPayService userWechatPayService; - + /** + * 获取平台证书 + * @return + */ + public AjaxResult getPlatformCertificat() + { + return success(userWechatPayService.getPlatformCertificat()); + } /** * 获取openId @@ -158,6 +166,10 @@ public class WeChatPayController extends BaseController { String wechatpaySerial = request.getHeader("Wechatpay-Serial");//序列号 String wechatpayTimestamp = request.getHeader("Wechatpay-Timestamp");//时间戳 String wechatpayNonce = request.getHeader("Wechatpay-Nonce");//随机字符串 + System.err.println("wechatpaySignature:"+wechatpaySignature); + System.err.println("wechatpaySerial:"+wechatpaySerial); + System.err.println("wechatpayTimestamp:"+wechatpayTimestamp); + System.err.println("wechatpayNonce"+wechatpayNonce); String json=jsonObject.toString(); System.out.println("微信回调报文:{"+json+"}"); // 构建应答报文 @@ -184,10 +196,15 @@ public class WeChatPayController extends BaseController { * responseBody 应答报文主体 * publicKey 微信支付公钥(PEM 格式,去掉头尾并解码为二进制) */ + //微信支付公钥验签 //String publicKey= wxPayConfig.getPublicKey(publicKeyPath);//读取并预处理后的微信支付公钥 - /*String Authorization= - String publicKey=userWechatPayService.getPublicKeyByCertificat(); - boolean isVerified = verifySignature(wechatpaySignature, wechatpayTimestamp, wechatpayNonce, json, publicKey); + //boolean isVerified = verifySignature(wechatpaySignature, wechatpayTimestamp, wechatpayNonce, json, publicKey);//微信支付公钥验签方式 + + //平台证书验签 + /*String publicKey=wxPayConfig.getPublicKeyByCertificat(platformCertificatePath);//读取预处理后的平台证书 + System.err.println("publickey:"+publicKey); + boolean isVerified = verifySignatureByCertificate(wechatpaySignature, wechatpayTimestamp, wechatpayNonce, json, publicKey); + if (isVerified) { System.out.println("签名验证成功"); } else { diff --git a/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/NgUserRechargeRecordsServiceImpl.java b/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/NgUserRechargeRecordsServiceImpl.java index eb7b74d..0706323 100644 --- a/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/NgUserRechargeRecordsServiceImpl.java +++ b/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/NgUserRechargeRecordsServiceImpl.java @@ -1,9 +1,13 @@ package com.fastbee.rechargecard.service.impl; import java.util.List; + +import com.fastbee.common.exception.ServiceException; import com.fastbee.common.utils.DateUtils; +import com.fastbee.rechargecard.domain.UserRechargeCards; import com.fastbee.rechargecard.domain.dto.RechargecardUser; import com.fastbee.rechargecard.domain.dto.WeChatRecharge; +import com.fastbee.rechargecard.mapper.UserRechargeCardsMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.fastbee.rechargecard.mapper.NgUserRechargeRecordsMapper; @@ -21,6 +25,8 @@ public class NgUserRechargeRecordsServiceImpl implements INgUserRechargeRecordsS { @Autowired private NgUserRechargeRecordsMapper ngUserRechargeRecordsMapper; + @Autowired + private UserRechargeCardsMapper userRechargeCardsMapper; @Override public NgUserRechargeRecords SelectRechargeRecodeByRechargeCode(String rechargeCode) { @@ -29,7 +35,21 @@ public class NgUserRechargeRecordsServiceImpl implements INgUserRechargeRecordsS @Override public int insertNgUserRechargeRecordsWeChat(WeChatRecharge rechargecardUser) { + // 根据id查询用户充值卡信息 + UserRechargeCards info = userRechargeCardsMapper.selectUserRechargeCardsByCardnumber(String.valueOf(rechargecardUser.getCardnumber())); + if (info == null) { + // 如果没有找到对应的记录,返回0或者一个错误码 + throw new ServiceException("卡号不存在!"); + } NgUserRechargeRecords ngUserRechargeRecords=new NgUserRechargeRecords(); + if(info.getUserId()!=null) + { + ngUserRechargeRecords.setUserId(info.getUserId()); + } + if(info.getUserName()!=null) + { + ngUserRechargeRecords.setUserName(info.getUserName()); + } ngUserRechargeRecords.setRechargeTime(DateUtils.getNowDate()); ngUserRechargeRecords.setBalance(rechargecardUser.getAmount()); ngUserRechargeRecords.setAmount(rechargecardUser.getAmount()); diff --git a/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/UserRechargeCardsServiceImpl.java b/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/UserRechargeCardsServiceImpl.java index d246de9..4f8ec2d 100644 --- a/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/UserRechargeCardsServiceImpl.java +++ b/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/UserRechargeCardsServiceImpl.java @@ -121,6 +121,10 @@ public class UserRechargeCardsServiceImpl implements IUserRechargeCardsService throw new Exception("订单信息不存在"); } ngUserRechargeRecords=userRechargeRecordsMapper.selectNgUserRechargeRecordsByRechargeCode(rechargeCode);//查询订单信息 + if(ngUserRechargeRecords.getStatus().equals(1)) + { + return 1; + } ngUserRechargeRecords.setStatus(1);//状态更改为已支付 userRechargeRecordsMapper.updateNgUserRechargeRecords(ngUserRechargeRecords); diff --git a/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/UserWechatPayServiceImpl.java b/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/UserWechatPayServiceImpl.java index a5fb33b..f3cf5db 100644 --- a/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/UserWechatPayServiceImpl.java +++ b/fastbee-service/fastbee-rechargecard-service/src/main/java/com/fastbee/rechargecard/service/impl/UserWechatPayServiceImpl.java @@ -2,6 +2,7 @@ package com.fastbee.rechargecard.service.impl; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; +import com.fastbee.common.utils.pay.AesUtil; import com.fastbee.common.utils.pay.wxPayConfig; import com.fastbee.rechargecard.domain.dto.WeChatRecharge; import com.fastbee.rechargecard.service.INgUserRechargeRecordsService; @@ -17,10 +18,13 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.Signature; import java.util.*; @@ -34,9 +38,9 @@ public class UserWechatPayServiceImpl implements IUserWechatPayService { public static String mchId = "1503198881"; /** 商户API私钥文件路径 */ //public static String privateKeyPath = "fastbee-common/src/main/java/com/fastbee/common/utils/pay/apiclient_key.pem"; - // public static String privateKeyPath = "fastbee-common/src/main/java/com/fastbee/common/utils/pay/damogang_apiclient_key.pem"; + public static String privateKeyPath = "fastbee-common/src/main/java/com/fastbee/common/utils/pay/damogang_apiclient_key.pem"; //TODO 生产环境私钥路径 - public static String privateKeyPath = "/home/soft/hzwmiot/fastbee-admin/target/damogang_apiclient_key.pem"; + //public static String privateKeyPath = "/home/soft/hzwmiot/fastbee-admin/target/damogang_apiclient_key.pem"; /** 商户API证书序列号 */ @@ -51,11 +55,13 @@ public class UserWechatPayServiceImpl implements IUserWechatPayService { public static String appSecret="7f591f559929a3bf2dbea4e156b08ae9"; //微信支付公钥地址 //public static String publicKeyPath="fastbee-common/src/main/java/com/fastbee/common/utils/pay/wechat_public_key.pem"; - //public static String publicKeyPath="fastbee-common/src/main/java/com/fastbee/common/utils/pay/wechat_public_key.pem"; + public static String publicKeyPath="fastbee-common/src/main/java/com/fastbee/common/utils/pay/wechat_public_key.pem"; + //平台证书地址 + public static String platformCertificatePath="fastbee-common/src/main/java/com/fastbee/common/utils/pay/damogang_platformCertificate.pem"; //支付结果回调地址 - // public static String notify_url="https://5f655ed0.r3.cpolar.cn/pay/getresult";//https://5f655ed0.r3.cpolar.cn + public static String notify_url="https://3ffb1c5f.r3.cpolar.cn/pay/getresult";//https://3ffb1c5f.r3.cpolar.cn //TODO 生产环境支付结果异步通知地址 - public static String notify_url="https://farmh5.hze2.com/prod-api/pay/getresult"; + //public static String notify_url="https://farmh5.hze2.com/prod-api/pay/getresult"; //https://5f655ed0.r3.cpolar.cn @Override /** @@ -86,7 +92,7 @@ public class UserWechatPayServiceImpl implements IUserWechatPayService { reqdata.put("notify_url",notify_url);//"\t\n" + reqdata.put("payer",payer); //String Signature=getSign(reqdata); - Map info=getSign(reqdata); + Map info=getSign(reqdata,"POST","/v3/pay/transactions/jsapi"); String timeStamp=info.get("timeStamp"); String nonce_str=info.get("nonce_str"); @@ -145,11 +151,11 @@ public class UserWechatPayServiceImpl implements IUserWechatPayService { /** * 生成签名 */ - private Map getSign(Map reqBody) throws Exception { + private Map getSign(Map reqBody,String reqMethod,String url) throws Exception { //获取时间戳 - String reqMethod="POST"; - String url="/v3/pay/transactions/jsapi"; + /* String reqMethod="POST"; + String url="/v3/pay/transactions/jsapi";*/ /*String reqMethod="GET"; String url="/v3/refund/domestic/refunds/123123123123";*/ String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); @@ -157,6 +163,10 @@ public class UserWechatPayServiceImpl implements IUserWechatPayService { String nonce_str= UUID.randomUUID().toString(); //nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242"; String reqParam= JSONUtil.toJsonStr(reqBody); + if(reqBody==null) + { + reqParam=""; + } /*HTTP请求方法\n URL\n @@ -165,6 +175,7 @@ public class UserWechatPayServiceImpl implements IUserWechatPayService { 请求报文主体\n */ String signStr=reqMethod+"\n"+url+"\n"+timeStamp+"\n"+nonce_str+"\n"+reqParam +"\n"; + System.err.println("signStr:"+signStr); //String signStr=reqMethod+"\n"+url+"\n"+timeStamp+"\n"+nonce_str+"\n\n"; String sign=sign(signStr.getBytes("utf-8")); Map info=new HashMap<>(); @@ -249,11 +260,74 @@ public class UserWechatPayServiceImpl implements IUserWechatPayService { * 获取平台证书 * @return */ - @Override - public String getPlatformCertificat() + /*@Override*/ + public String getPlatformCertificat() { String url="https://api.mch.weixin.qq.com/v3/certificates"; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet(url); + //生成签名 + Map signResult=getSign(null,"GET","/v3/certificates"); + Set keySet2 = signResult.keySet(); + // 遍历键集合 + for (String key : keySet2) { + System.err.println(key + ": " + signResult.get(key)); + } + String sign=signResult.get("sign").toString(); + String nonce_str=signResult.get("nonce_str").toString(); + String timeStamp=signResult.get("timeStamp").toString(); + String Authorization="WECHATPAY2-SHA256-RSA2048 mchid=\""+mchId+"\",nonce_str=\""+nonce_str+"\",signature=\""+sign+"\",timestamp=\""+timeStamp+"\",serial_no=\""+serial_no+"\""; + System.err.println(Authorization); + httpGet.setHeader("Accept", "application/json"); + httpGet.setHeader("Authorization",Authorization); - return null; + try (CloseableHttpResponse response = httpClient.execute(httpGet)) { + String responseString = EntityUtils.toString(response.getEntity()); + System.err.println(responseString); + Map responseMap = JSONUtil.toBean(responseString, Map.class); + // 使用Optional来避免null检查 + Optional optionalObj = Optional.ofNullable(responseMap.get("data")); + System.err.println(optionalObj); + // 链式调用map和flatMap方法来安全地转换和获取Map + Map map = optionalObj + .filter(List.class::isInstance) // 确保obj是List类型 + .map(List.class::cast) // 将obj转换为List + .filter(list -> !list.isEmpty() && list.get(0) instanceof Map) // 确保List不为空,并且第一个元素是Map类型 + .map(list -> (Map) list.get(0)) // 将List中的第一个元素转换为Map + .orElse(null); // 如果任何检查失败,则返回null + System.err.println(map); + //解密jsonObject对象 + Map encrypt_certificate= (Map) map.get("encrypt_certificate"); + System.err.println("111"); + System.err.println(encrypt_certificate); + String associated_data=encrypt_certificate.get("associated_data").toString();//加密证书的附加数据,固定为“certificate" + String ciphertext=encrypt_certificate.get("ciphertext").toString();//加密证书的随机串 + String nonce=encrypt_certificate.get("nonce").toString();//加密后的证书内容 + System.err.println("ciphertext:"+ciphertext); + System.err.println("nonce:"+nonce); + System.err.println("associated_data:"+associated_data); + //使用apiv3key解密 + String decryptData=""; + try{ + decryptData= new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8)).decryptToString + (associated_data.getBytes(StandardCharsets.UTF_8), + nonce.getBytes(StandardCharsets.UTF_8), + ciphertext); + System.out.println("解密成功:\n"+decryptData); + }catch (Exception e) + { + System.out.println("解密失败"); + } + + // 解析responseString以获取openid + return responseMap.toString(); // 这里返回的是整个响应字符串,需要自行解析出openid + } + } catch (IOException e) { + throw new RuntimeException(e); + } catch (Exception e) { + throw new RuntimeException(e); + } } + + }