diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/PayController.java b/ruoyi-admin/src/main/java/com/ruoyi/api/PayController.java
index 431e5fb..871c77a 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/api/PayController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/api/PayController.java
@@ -31,8 +31,8 @@ public class PayController {
*/
@GetMapping("/jinsha-pay")
@ApiOperation("jinsha请求支付")
- public AjaxResult jinShaPay(Long gearId, String cardno, String cardname) throws Exception {
- PayResVO payResVO = jinShaService.jinShaPay(gearId, cardno, cardname);
+ public AjaxResult jinShaPay(Long gearId) throws Exception {
+ PayResVO payResVO = jinShaService.jinShaPay(gearId);
return AjaxResult.success(payResVO);
}
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index b369ff8..1268461 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -71,6 +71,12 @@
commons-io
+
+
+ commons-codec
+ commons-codec
+
+
org.apache.poi
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/RSAUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/RSAUtils.java
index 8229dd7..02170be 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/RSAUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/RSAUtils.java
@@ -1,14 +1,19 @@
package com.ruoyi.common.utils.sign;
import cn.hutool.json.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
@@ -16,25 +21,29 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
+@Slf4j
public class RSAUtils {
- // RSA最⼤加密明⽂⼤⼩
+ // RSA最大加密明文大小
private static final int MAX_ENCRYPT_BLOCK = 117;
- // 不仅可以使⽤DSA算法,同样也可以使⽤RSA算法做数字签名
+ // 不仅可以使用DSA算法,同样也可以使用RSA算法做数字签名
private static final String KEY_ALGORITHM = "RSA";
- public static String encryptByPrivateKey(String str, String privateKey) throws Exception {
+ public static String encryptByPrivateKey(String str, String privateKey)
+ throws InvalidKeySpecException, NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException, java.security.InvalidKeyException,
+ javax.crypto.IllegalBlockSizeException, javax.crypto.BadPaddingException, UnsupportedEncodingException {
// base64编码的公钥
byte[] keyBytes = decryptBASE64(privateKey);
- RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
+ RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM)
+ .generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
// RSA加密
Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, priKey);
byte[] data = str.getBytes("UTF-8");
- // 加密时超过117字节就报错。为此采⽤分段加密的办法来加密
+ // 加密时超过117字节就报错。为此采用分段加密的办法来加密
byte[] enBytes = null;
for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) {
- // 注意要使⽤2的倍数,否则会出现加密后的内容再解密时为乱码
+ // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码
byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK));
enBytes = ArrayUtils.addAll(enBytes, doFinal);
}
@@ -43,15 +52,22 @@ public class RSAUtils {
}
private static String encryptBASE64(byte[] data) {
- return new String(Base64.encode(data));
+ return new String(Base64.encodeBase64(data));
}
private static byte[] decryptBASE64(String data) {
- return Base64.decode(data);
+ return Base64.decodeBase64(data);
}
+ /**
+ * Verify signature
+ *
+ * @param params
+ * @return
+ */
public static boolean verifySign(JSONObject params, String publickey) {
String platSign = params.getStr("signature"); // sign
+ log.info("signature:" + platSign);
List paramNameList = new ArrayList<>();
for (String key : params.keySet()) {
if (!"signature".equals(key)) {
@@ -64,13 +80,14 @@ public class RSAUtils {
String name = paramNameList.get(i);
stringBuilder.append(params.getStr(name));
}
+ log.info("keys:" + stringBuilder);
String decryptSign = "";
try {
- decryptSign = publicDecrypt(platSign, getPublicKey(publickey)
- );
+ decryptSign = publicDecrypt(platSign, getPublicKey(publickey));
} catch (Exception e) {
- System.out.println(e.toString());
+ log.error("Error decrypting signature", e);
}
+ log.info("decryptSign:" + decryptSign);
if (!stringBuilder.toString().equalsIgnoreCase(decryptSign)) {
return false;
}
@@ -89,11 +106,12 @@ public class RSAUtils {
stringBuilder.append(createMap.get(key)); // 拼接参数
}
String keyStr = stringBuilder.toString(); // 得到待加密的字符串
+ log.info("keyStr:" + keyStr);
String signedStr = "";
try {
signedStr = privateEncrypt(keyStr, getPrivateKey(MCH_PRIVATE_KEY)); // 私钥加密
} catch (Exception e) {
- System.out.println(e.toString());
+ log.error("Error encrypting signature", e);
}
return signedStr;
}
@@ -110,47 +128,70 @@ public class RSAUtils {
stringBuilder.append(createMap.get(key)); // 拼接参数
}
String keyStr = stringBuilder.toString(); // 得到待加密的字符串
+ log.info("keyStr:" + keyStr);
String signedStr = "";
try {
signedStr = privateEncrypt(keyStr, getPrivateKey(MCH_PRIVATE_KEY)); // 私钥加密
} catch (Exception e) {
- System.out.println(e.toString());
+ log.error("Error encrypting signature", e);
}
return signedStr;
}
+ /**
+ * private key encryption
+ *
+ * @param data
+ * @param privateKey
+ * @return
+ */
public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
- return Base64.encode(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes("UTF-8"), privateKey.getModulus().bitLength()));
+ return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes("UTF-8"), privateKey.getModulus().bitLength()));
} catch (Exception e) {
- throw new RuntimeException("Exception encountered while encry pting string [" + data + "]", e);
+ throw new RuntimeException("Exception encountered while encrypting string [" + data + "]", e);
}
}
+ /**
+ * public key decryption
+ */
public static String publicDecrypt(String data, RSAPublicKey publicKey) {
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
- return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decode(data), publicKey.getModulus().bitLength()), "UTF-8");
+ return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), "UTF-8");
} catch (Exception e) {
- throw new RuntimeException("An exception was encountered whil e decrypting the string[" + data + "]", e);
+ throw new RuntimeException("An exception was encountered while decrypting the string[" + data + "]", e);
}
}
- public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception {
+ /**
+ * get private key
+ *
+ * @param privateKey key string (base64 encoded)
+ * @throws Exception
+ */
+ public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//Get the private key object through the PKCS#8 encoded Key instruction
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
+ PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
return key;
}
- public static RSAPublicKey getPublicKey(String publicKey) throws Exception {
+ /**
+ * get the public key
+ *
+ * @param publicKey key string (base64 encoded)
+ * @throws Exception
+ */
+ public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//Get the public key object through the X509 encoded Key instruction
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decode(publicKey));
+ X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
return key;
}
@@ -179,7 +220,7 @@ public class RSAUtils {
offSet = i * maxBlock;
}
} catch (Exception e) {
- throw new RuntimeException("An exception occurred when encryp ting and decrypting data whose threshold is [" + maxBlock + "]", e);
+ throw new RuntimeException("An exception occurred when encrypting and decrypting data whose threshold is [" + maxBlock + "]", e);
}
byte[] resultDatas = out.toByteArray();
IOUtils.closeQuietly(out);
diff --git a/ruoyi-system/src/main/java/com/ruoyi/ai/domain/AiTemplate.java b/ruoyi-system/src/main/java/com/ruoyi/ai/domain/AiTemplate.java
index 08611e0..6b74db8 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/ai/domain/AiTemplate.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/ai/domain/AiTemplate.java
@@ -43,6 +43,10 @@ public class AiTemplate extends BaseEntity {
@Excel(name = "模版图片URL")
private String imageUrl;
+ /** AI类型 */
+ @Excel(name = "AI类型")
+ private Long aiId;
+
/** 状态:0-禁用,1-启用 */
@Excel(name = "状态:0-禁用,1-启用")
private Integer status;
diff --git a/ruoyi-system/src/main/java/com/ruoyi/ai/service/IJinShaService.java b/ruoyi-system/src/main/java/com/ruoyi/ai/service/IJinShaService.java
index 20a975a..e995765 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/ai/service/IJinShaService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/ai/service/IJinShaService.java
@@ -9,10 +9,8 @@ public interface IJinShaService {
/**
* 支付
* @param gearId 档位ID
- * @param cardno 银行卡号
- * @param cardname 银行卡姓名
*/
- PayResVO jinShaPay(Long gearId, String cardno, String cardname) throws Exception;
+ PayResVO jinShaPay(Long gearId) throws Exception;
/**
diff --git a/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/JinShaService.java b/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/JinShaService.java
index 205ed50..0612fac 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/JinShaService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/JinShaService.java
@@ -53,15 +53,7 @@ public class JinShaService implements IJinShaService {
private IExchangeRateService exchangeRateService;
@Override
- public PayResVO jinShaPay(Long gearId, String cardno, String cardname) throws Exception {
-
- // 验证参数
- if (cardno == null || cardno.trim().isEmpty()) {
- throw new ServiceException("银行卡号不能为空", -1);
- }
- if (cardname == null || cardname.trim().isEmpty()) {
- throw new ServiceException("银行卡姓名不能为空", -1);
- }
+ public PayResVO jinShaPay(Long gearId) throws Exception {
//充值挡位查询
AiRechargeGiftGear aiRechargeGiftGear = aiRechargeGiftGearService.selectAiRechargeGiftGearById(gearId);
@@ -77,16 +69,20 @@ public class JinShaService implements IJinShaService {
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
String orderNo = dateTime + "js" + uuid;
- // 金额转换为int类型(去除小数部分)
- int amountInt = amount.intValue();
+ // 金额转换为number类型(保留两位小数)
+ String amountStr = String.format("%.2f", amount.doubleValue());
- // 1. 配置签名参数(按照key排序,不包含notify_url和sign)
+ // 构建回调地址和返回地址
+ String notifyUrlFull = notifyUrl + "/api/pay/jinsha-callBack";
+ String returnUrlFull = returnUrl + "/recharge";
+
+ // 1. 配置签名参数(按照key排序:appid, amount, orderno, notify_url, return_url,不包含sign)
TreeMap signParams = new TreeMap<>();
signParams.put("appid", appId);
- signParams.put("amount", amountInt);
+ signParams.put("amount", amountStr);
signParams.put("orderno", orderNo);
- signParams.put("cardno", cardno);
- signParams.put("cardname", cardname);
+ signParams.put("notify_url", notifyUrlFull);
+ signParams.put("return_url", returnUrlFull);
// 2. 生成签名
String sign = this.generateSign(signParams);
@@ -94,20 +90,21 @@ public class JinShaService implements IJinShaService {
// 3. 构建请求参数(使用FormBody.Builder自动处理URL编码)
FormBody.Builder formBuilder = new FormBody.Builder();
formBuilder.add("appid", appId);
- formBuilder.add("amount", String.valueOf(amountInt));
+ formBuilder.add("amount", amountStr);
formBuilder.add("orderno", orderNo);
- formBuilder.add("cardno", cardno);
- formBuilder.add("cardname", cardname);
- formBuilder.add("notify_url", notifyUrl + "/api/pay/jinsha-callBack");
+ formBuilder.add("notify_url", notifyUrlFull);
+ formBuilder.add("return_url", returnUrlFull);
formBuilder.add("sign", sign);
RequestBody requestBody = formBuilder.build();
- // 5. 构建 POST 请求
+
+ // 4. 构建 POST 请求
Request request = new Request.Builder()
.url(url + "/api/pay")
.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
.post(requestBody)
.build();
- // 6. 发送请求并获取响应
+
+ // 5. 发送请求并获取响应
Response response = OkHttpUtils.newCall(request).execute();
ResponseBody body = response.body();
if (null == body) {
@@ -126,6 +123,16 @@ public class JinShaService implements IJinShaService {
throw new ServiceException(msg != null ? msg : "支付请求失败", code != null ? code : -1);
}
+ // 检查返回的payurl
+ String payUrl = null;
+ if (jinShaBodyRes.getData() != null) {
+ payUrl = jinShaBodyRes.getData().getPayurl();
+ }
+ if (payUrl == null || payUrl.trim().isEmpty()) {
+ log.error("jinsha支付返回的payurl为空,订单号: {}", orderNo);
+ throw new ServiceException("支付返回的支付链接为空", -1);
+ }
+
// 创建充值管理
AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
AiRecharge aiRecharge = new AiRecharge();
@@ -137,12 +144,12 @@ public class JinShaService implements IJinShaService {
aiRecharge.setSource(userInfo.getSource());
AiRechargeGift aiRechargeGift = aiRechargeGiftService.selectAiRechargeGiftById(aiRechargeGiftGear.getRechargeId());
aiRecharge.setPayType(aiRechargeGift.getPayType());
- // jinsha支付成功返回后,不需要支付URL,订单状态由回调更新
+ aiRecharge.setPayUrl(payUrl);
aiRechargeService.insertAiRecharge(aiRecharge);
PayResVO payResVO = new PayResVO();
payResVO.setOrderNo(orderNo);
- // jinsha支付成功后不需要跳转支付页面,直接返回订单号
+ payResVO.setPayUrl(payUrl);
return payResVO;
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/YuZhouService.java b/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/YuZhouService.java
index b12b677..40edd36 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/YuZhouService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/YuZhouService.java
@@ -60,37 +60,111 @@ public class YuZhouService implements IYuZhouService {
String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date());
String orderNo = dateTime + "yz" + uuid;
+ // 获取用户信息
+ AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
+
+ // 处理用户姓名,如果没有则使用默认值
+ String firstName = "John";
+ String lastName = "Doe";
+ if (userInfo.getNickname() != null && !userInfo.getNickname().trim().isEmpty()) {
+ String[] nameParts = userInfo.getNickname().trim().split("\\s+");
+ if (nameParts.length >= 2) {
+ firstName = nameParts[0];
+ lastName = nameParts[nameParts.length - 1];
+ } else if (nameParts.length == 1) {
+ firstName = nameParts[0];
+ }
+ }
+
+ // 从用户表获取邮箱
+ String email = userInfo.getEmail();
+ if (email == null || email.trim().isEmpty()) {
+ log.warn("用户邮箱为空 userId: {}, nickname: {}", userInfo.getId(), userInfo.getNickname());
+ // 如果用户邮箱为空,使用默认邮箱格式
+ email = "user" + userInfo.getId() + "@example.com";
+ } else {
+ email = email.trim();
+ }
+
+ // 处理电话,如果没有则使用默认值
+ String phone = userInfo.getPhone();
+ if (phone == null || phone.trim().isEmpty()) {
+ phone = "1234567890";
+ } else {
+ // 确保电话是10位数字
+ phone = phone.replaceAll("[^0-9]", "");
+ if (phone.length() < 10) {
+ // 如果不足10位,用0补齐
+ phone = String.format("%010d", phone.length() > 0 ? Long.parseLong(phone) : 0);
+ } else if (phone.length() > 10) {
+ // 如果超过10位,取后10位
+ phone = phone.substring(phone.length() - 10);
+ }
+ }
+
+ // 构建请求参数
Map createMap = new HashMap<>();
createMap.put("merchantNo", appId);
createMap.put("orderNo", orderNo);
createMap.put("money", amount.toString());
- createMap.put("description", "test");
- createMap.put("name", "test");
- createMap.put("email", "test@gmail.com");
- createMap.put("callbackUrl", callbackUrl + "/api/pay/yuzhou-callBack");
- createMap.put("phone", "7383442114");
+ createMap.put("description", "Recharge");
+ createMap.put("email", email);
+ createMap.put("phone", phone);
+ createMap.put("notifyUrl", callbackUrl + "/api/pay/yuzhou-callBack");
createMap.put("expiredPeriod", "1440");
- createMap.put("redirectUrl", redirectUrl + "/recharge");
+ createMap.put("currency", "USD");
+ createMap.put("firstName", firstName);
+ createMap.put("lastName", lastName);
+ createMap.put("street", "123 Main Street");
+ createMap.put("city", "New York");
+ createMap.put("state", "NY");
+ createMap.put("country", userInfo.getCountry() != null && !userInfo.getCountry().trim().isEmpty() ? userInfo.getCountry() : "US");
+ createMap.put("postcode", "10001");
+
+ // 生成签名(注意:签名时不包括signature字段)
String signedStr = RSAUtils.getSignStr(createMap, secretKey);
createMap.put("signature", signedStr);
+
String postStr = JSONUtil.toJsonStr(createMap);
- String response = HttpRequest.post(url + "/gateway/order/US/payIn").header("Content-Type", "application/json")
- .body(postStr).execute().body();
+ log.info("yuzhou支付请求参数: {}", postStr);
+
+ // 调用新的接口地址
+ String response = HttpRequest.post(url + "/gateway/US/payIn")
+ .header("Content-Type", "application/json; charset=utf-8")
+ .body(postStr)
+ .execute()
+ .body();
+
+ log.info("yuzhou支付响应: {}", response);
+
JSONObject returnObj = JSONUtil.parseObj(response);
Integer code = returnObj.getInt("code");
if (200 != code) {
- log.error("yuzhou支付请求的响应异常 {}", returnObj);
- throw new Exception("yuzhoupay responsebody is null");
+ String msg = returnObj.getStr("msg");
+ log.error("yuzhou支付请求的响应异常 code: {}, msg: {}, response: {}", code, msg, returnObj);
+ throw new ServiceException("yuzhoupay request failed: " + (msg != null ? msg : "unknown error"), code);
}
+
JSONObject data = returnObj.getJSONObject("data");
+ if (data == null) {
+ log.error("yuzhou支付响应数据为空: {}", returnObj);
+ throw new ServiceException("yuzhoupay response data is null", -1);
+ }
+
String money = data.getStr("money");
String payUrl = data.getStr("url");
+ String platformOrderNo = data.getStr("platformOrderNo");
+
+ if (payUrl == null || payUrl.trim().isEmpty()) {
+ log.error("yuzhou支付URL为空: {}", data);
+ throw new ServiceException("yuzhoupay url is null", -1);
+ }
+
// 创建充值管理
- AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId());
AiRecharge aiRecharge = new AiRecharge();
aiRecharge.setOrderNum(orderNo);
aiRecharge.setUserId(SecurityUtils.getAiUserId());
- aiRecharge.setAmount(new BigDecimal(money));
+ aiRecharge.setAmount(new BigDecimal(money != null ? money : amount.toString()));
aiRecharge.setGearId(gearId);
aiRecharge.setSource(userInfo.getSource());
aiRecharge.setGearAmount(aiRechargeGiftGear.getRechargeAmount());
@@ -98,6 +172,7 @@ public class YuZhouService implements IYuZhouService {
aiRecharge.setPayType(aiRechargeGift.getPayType());
aiRecharge.setPayUrl(payUrl);
aiRechargeService.insertAiRecharge(aiRecharge);
+
PayResVO payResVO = new PayResVO();
payResVO.setOrderNo(orderNo);
payResVO.setPayUrl(payUrl);
@@ -106,31 +181,60 @@ public class YuZhouService implements IYuZhouService {
@Override
public String callBack(Map map) throws Exception {
- int code = Integer.parseInt(map.get("status").toString());
- String orderNo = map.get("orderNo").toString();
- if (10 != code) {
- log.error("yuzhou支付失败 {}", orderNo);
- return null;
+ log.info("yuzhou支付回调接收参数: {}", map);
+
+ // 验证必要参数
+ if (map == null || map.isEmpty()) {
+ log.error("yuzhou支付回调参数为空");
+ return "FAIL";
}
-// Map createMap = new HashMap<>();
-// createMap.put("orderNo", orderNo);
-// createMap.put("platOrderNo", map.get("platOrderNo"));
-// createMap.put("money", map.get("money"));
-// createMap.put("fee", map.get("fee"));
-// createMap.put("status", map.get("status"));
-// createMap.put("message", map.get("message"));
- String signature = map.get("signature").toString();
+
+ Object statusObj = map.get("status");
+ Object orderNoObj = map.get("orderNo");
+
+ if (statusObj == null || orderNoObj == null) {
+ log.error("yuzhou支付回调缺少必要参数: status={}, orderNo={}", statusObj, orderNoObj);
+ return "FAIL";
+ }
+
+ int status = Integer.parseInt(statusObj.toString());
+ String orderNo = orderNoObj.toString();
+
+ log.info("yuzhou支付回调 orderNo: {}, status: {}", orderNo, status);
+
+ // 验证签名
+ Object signatureObj = map.get("signature");
+ if (signatureObj == null) {
+ log.error("yuzhou支付回调缺少签名参数 orderNo: {}", orderNo);
+ return "FAIL";
+ }
+
JSONObject entries = JSONUtil.parseObj(map);
- boolean b = RSAUtils.verifySign(entries, publicKey);
- if (!b) {
- log.error("yuzhou支付回调签名错误 {}", orderNo);
- log.error("yuzhou支付回调签名 {}", signature);
- log.error("yuzhou支付回调报文 {}", map);
- return null;
+ boolean verifyResult = RSAUtils.verifySign(entries, publicKey);
+ if (!verifyResult) {
+ log.error("yuzhou支付回调签名验证失败 orderNo: {}", orderNo);
+ log.error("yuzhou支付回调签名: {}", signatureObj);
+ log.error("yuzhou支付回调完整报文: {}", map);
+ return "FAIL";
+ }
+
+ log.info("yuzhou支付回调签名验证成功 orderNo: {}", orderNo);
+
+ // 判断订单状态,10表示成功
+ if (10 != status) {
+ log.warn("yuzhou支付订单状态非成功 orderNo: {}, status: {}", orderNo, status);
+ return "SUCCESS"; // 即使失败也返回SUCCESS,避免重复回调
+ }
+
+ // 充值成功处理
+ try {
+ aiRechargeService.addRecharge(orderNo);
+ log.info("yuzhou支付回调处理成功 orderNo: {}", orderNo);
+ return "SUCCESS";
+ } catch (Exception e) {
+ log.error("yuzhou支付回调处理失败 orderNo: {}", orderNo, e);
+ return "FAIL";
}
- //充值成功处理
- aiRechargeService.addRecharge(orderNo);
- return "SUCCESS";
}
diff --git a/ruoyi-system/src/main/resources/mapper/system/AiTemplateMapper.xml b/ruoyi-system/src/main/resources/mapper/system/AiTemplateMapper.xml
index afb0aa7..9ca60f9 100644
--- a/ruoyi-system/src/main/resources/mapper/system/AiTemplateMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/AiTemplateMapper.xml
@@ -10,6 +10,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+
@@ -20,7 +21,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- select id, name, chinese_content, english_content, image_url, status, remark, create_time, create_by, update_by, update_time, del_flag from ai_template
+ select id, name, chinese_content, english_content, image_url, ai_id, status, remark, create_time, create_by, update_by, update_time, del_flag from ai_template
@@ -46,6 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
chinese_content,
english_content,
image_url,
+ ai_id,
status,
remark,
create_time,
@@ -59,6 +62,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{chineseContent},
#{englishContent},
#{imageUrl},
+ #{aiId},
#{status},
#{remark},
#{createTime},
@@ -76,6 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
chinese_content = #{chineseContent},
english_content = #{englishContent},
image_url = #{imageUrl},
+ ai_id = #{aiId},
status = #{status},
remark = #{remark},
create_time = #{createTime},
diff --git a/sql/aisql.sql b/sql/aisql.sql
index 8025d5f..f86ed1a 100644
--- a/sql/aisql.sql
+++ b/sql/aisql.sql
@@ -2866,7 +2866,7 @@ CREATE TABLE `ai_recharge` (
`gift_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '到账金额',
`give_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '赠送金额',
`pay_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '支付方式',
- `pay_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '支付链接',
+ `pay_url` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '支付链接',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 118 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '充值记录' ROW_FORMAT = DYNAMIC;