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 871c77a..431e5fb 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) throws Exception { - PayResVO payResVO = jinShaService.jinShaPay(gearId); + public AjaxResult jinShaPay(Long gearId, String cardno, String cardname) throws Exception { + PayResVO payResVO = jinShaService.jinShaPay(gearId, cardno, cardname); return AjaxResult.success(payResVO); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/ai/domain/JinShaBodyReq.java b/ruoyi-system/src/main/java/com/ruoyi/ai/domain/JinShaBodyReq.java index 80c7a37..cb68c21 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/ai/domain/JinShaBodyReq.java +++ b/ruoyi-system/src/main/java/com/ruoyi/ai/domain/JinShaBodyReq.java @@ -21,9 +21,9 @@ public class JinShaBodyReq { private String notify_url; - private String return_url; - private String sign; + private String cardname; + private String cardno; /** * 档位ID 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 1fdd219..20a975a 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 @@ -8,8 +8,11 @@ public interface IJinShaService { /** * 支付 + * @param gearId 档位ID + * @param cardno 银行卡号 + * @param cardname 银行卡姓名 */ - PayResVO jinShaPay(Long gearId) throws Exception; + PayResVO jinShaPay(Long gearId, String cardno, String cardname) 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 13f2fdc..205ed50 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 @@ -48,10 +48,20 @@ public class JinShaService implements IJinShaService { @Autowired private IAiRechargeGiftGearService aiRechargeGiftGearService; @Autowired + private IAiRechargeGiftService aiRechargeGiftService; + @Autowired private IExchangeRateService exchangeRateService; @Override - public PayResVO jinShaPay(Long gearId) throws Exception { + 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); + } //充值挡位查询 AiRechargeGiftGear aiRechargeGiftGear = aiRechargeGiftGearService.selectAiRechargeGiftGearById(gearId); @@ -62,62 +72,86 @@ public class JinShaService implements IJinShaService { // 使用汇率服务转换金额(从CNY转换为目标货币,默认INR) amount = exchangeRateService.convertAmount(amount, "USD", "INR"); - JinShaBodyReq req = new JinShaBodyReq(); - req.setAppid(appId); - req.setReturn_url(returnUrl + "/recharge"); - req.setNotify_url(notifyUrl + "/api/pay/jinsha-callBack"); - req.setAmount(amount); - // 1. 配置请求参数 - TreeMap params = new TreeMap<>(); - params.put("appid", req.getAppid()); + // 生成订单号 String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8); String dateTime = new SimpleDateFormat("yyyyMMdd").format(new Date()); String orderNo = dateTime + "js" + uuid; - params.put("orderno", orderNo); - params.put("amount", amount); - params.put("notify_url", req.getNotify_url()); - params.put("return_url", req.getReturn_url()); - StringBuilder append = this.append(params); - String sign = this.sign(append); - String formData = append + "&sign=" + sign; - // 2. 构建请求体(form-urlencoded 格式) - RequestBody requestBody = RequestBody.create( - MediaType.get("application/x-www-form-urlencoded;charset=utf-8"), - formData - ); - // 3. 构建 POST 请求 + + // 金额转换为int类型(去除小数部分) + int amountInt = amount.intValue(); + + // 1. 配置签名参数(按照key排序,不包含notify_url和sign) + TreeMap signParams = new TreeMap<>(); + signParams.put("appid", appId); + signParams.put("amount", amountInt); + signParams.put("orderno", orderNo); + signParams.put("cardno", cardno); + signParams.put("cardname", cardname); + + // 2. 生成签名 + String sign = this.generateSign(signParams); + + // 3. 构建请求参数(使用FormBody.Builder自动处理URL编码) + FormBody.Builder formBuilder = new FormBody.Builder(); + formBuilder.add("appid", appId); + formBuilder.add("amount", String.valueOf(amountInt)); + formBuilder.add("orderno", orderNo); + formBuilder.add("cardno", cardno); + formBuilder.add("cardname", cardname); + formBuilder.add("notify_url", notifyUrl + "/api/pay/jinsha-callBack"); + formBuilder.add("sign", sign); + RequestBody requestBody = formBuilder.build(); + // 5. 构建 POST 请求 Request request = new Request.Builder() .url(url + "/api/pay") -// .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") + .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") .post(requestBody) .build(); - // 4. 发送请求并获取响应 + // 6. 发送请求并获取响应 Response response = OkHttpUtils.newCall(request).execute(); ResponseBody body = response.body(); if (null == body) { - log.error("kada支付请求的响应异常"); - throw new Exception("kadapay responsebody is null"); + log.error("jinsha支付请求的响应异常"); + throw new Exception("jinshapay responsebody is null"); } - JinShaBodyRes jinShaBodyRes = objectMapper.readValue(body.string(), JinShaBodyRes.class); + String responseBody = body.string(); + log.info("jinsha支付请求响应: {}", responseBody); + JinShaBodyRes jinShaBodyRes = objectMapper.readValue(responseBody, JinShaBodyRes.class); + + // 检查响应code,0表示成功,其他值表示失败 + Integer code = jinShaBodyRes.getCode(); + if (code == null || code != 0) { + String msg = jinShaBodyRes.getMsg(); + log.error("jinsha支付请求失败,code: {}, msg: {}", code, msg); + throw new ServiceException(msg != null ? msg : "支付请求失败", code != null ? code : -1); + } + // 创建充值管理 AiUser userInfo = aiUserService.getUserInfo(SecurityUtils.getAiUserId()); AiRecharge aiRecharge = new AiRecharge(); aiRecharge.setOrderNum(orderNo); aiRecharge.setUserId(SecurityUtils.getAiUserId()); aiRecharge.setAmount(amount); - aiRecharge.setGearId(req.getGearId()); + aiRecharge.setGearId(gearId); aiRecharge.setGearAmount(aiRechargeGiftGear.getRechargeAmount()); aiRecharge.setSource(userInfo.getSource()); + AiRechargeGift aiRechargeGift = aiRechargeGiftService.selectAiRechargeGiftById(aiRechargeGiftGear.getRechargeId()); + aiRecharge.setPayType(aiRechargeGift.getPayType()); + // jinsha支付成功返回后,不需要支付URL,订单状态由回调更新 aiRechargeService.insertAiRecharge(aiRecharge); + PayResVO payResVO = new PayResVO(); payResVO.setOrderNo(orderNo); - payResVO.setPayUrl(jinShaBodyRes.getData().getPayurl()); + // jinsha支付成功后不需要跳转支付页面,直接返回订单号 return payResVO; } @Override @Transactional public AjaxResult jinShaCall(JinShaBodyCall req) { + log.info("jinsha支付回调,订单号: {}, 状态: {}", req.getOrderno(), req.getStatus()); + + // 验证签名 TreeMap params = new TreeMap<>(); params.put("status", req.getStatus()); params.put("orderno", req.getOrderno()); @@ -126,14 +160,58 @@ public class JinShaService implements IJinShaService { StringBuilder append = this.append(params); String sign = this.sign(append); if (!sign.equals(req.getSign())) { - log.error("支付回调签名错误 {}", req.getOrderno()); - return AjaxResult.error(); + log.error("jinsha支付回调签名错误,订单号: {}, 期望签名: {}, 实际签名: {}", + req.getOrderno(), sign, req.getSign()); + return AjaxResult.error("签名验证失败"); + } + + // 处理订单状态 + // status:0=>处理中, status:1=>成功, status:2=>失败 + Integer status = req.getStatus(); + if (status == null) { + log.error("jinsha支付回调状态为空,订单号: {}", req.getOrderno()); + return AjaxResult.error("状态为空"); + } + + if (status == 1) { + // 充值成功处理 + log.info("jinsha支付成功,订单号: {}", req.getOrderno()); + aiRechargeService.addRecharge(req.getOrderno()); + return AjaxResult.success(); + } else if (status == 2) { + // 支付失败 + log.warn("jinsha支付失败,订单号: {}", req.getOrderno()); + // 这里可以添加失败处理逻辑,比如更新订单状态为失败 + return AjaxResult.success(); + } else { + // 处理中(status:0) + log.info("jinsha支付处理中,订单号: {}", req.getOrderno()); + return AjaxResult.success(); } - //充值成功处理 - aiRechargeService.addRecharge(req.getOrderno()); - return AjaxResult.success(); } + /** + * 生成签名 + * 按照文档要求:key=value&key=value&...&secret=secret,然后MD5 + */ + private String generateSign(TreeMap sortedParams) { + StringBuilder paramSb = new StringBuilder(); + for (Map.Entry entry : sortedParams.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + // 拼接格式:key=value& + paramSb.append(key).append("=").append(value).append("&"); + } + // 最后添加secret + paramSb.append("secret=").append(secret); + String signSource = paramSb.toString(); + log.debug("jinsha签名源字符串: {}", signSource); + return Md5Utils.hash(signSource); + } + + /** + * 旧的签名方法(回调使用) + */ private StringBuilder append(TreeMap sortedParams) { StringBuilder paramSb = new StringBuilder(); for (Map.Entry entry : sortedParams.entrySet()) {