|
|
@@ -1,5 +1,7 @@
|
|
|
package com.zsElectric.boot.business.service;
|
|
|
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.zsElectric.boot.business.mapper.RechargeLevelMapper;
|
|
|
import com.zsElectric.boot.business.mapper.UserInfoMapper;
|
|
|
import com.zsElectric.boot.business.mapper.UserOrderInfoMapper;
|
|
|
@@ -7,16 +9,26 @@ import com.zsElectric.boot.business.model.entity.RechargeLevel;
|
|
|
import com.zsElectric.boot.business.model.entity.UserOrderInfo;
|
|
|
import com.zsElectric.boot.business.model.form.applet.AppLevelOrderForm;
|
|
|
import com.zsElectric.boot.business.model.form.applet.AppUserPayForm;
|
|
|
+import com.zsElectric.boot.business.model.vo.applet.WFTOrderVO;
|
|
|
import com.zsElectric.boot.business.model.vo.applet.WechatPayParamsVO;
|
|
|
+import com.zsElectric.boot.common.constant.SystemConstants;
|
|
|
+import com.zsElectric.boot.core.exception.BusinessException;
|
|
|
import com.zsElectric.boot.core.pay.WFT.WFTConstants;
|
|
|
import com.zsElectric.boot.core.pay.swiftpass.config.SwiftpassConfig;
|
|
|
import com.zsElectric.boot.core.pay.swiftpass.util.PayUtill;
|
|
|
+import com.zsElectric.boot.core.pay.swiftpass.util.SignUtil;
|
|
|
+import com.zsElectric.boot.core.pay.swiftpass.util.SignUtils;
|
|
|
+import com.zsElectric.boot.core.pay.swiftpass.util.XmlUtils;
|
|
|
import com.zsElectric.boot.security.util.SecurityUtils;
|
|
|
import jakarta.annotation.Resource;
|
|
|
+import jakarta.servlet.ServletException;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
import jakarta.validation.Valid;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
+import java.io.IOException;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.math.RoundingMode;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
@@ -112,7 +124,7 @@ public class WFTOrderService {
|
|
|
log.debug("通知第三开始支付:");
|
|
|
// 通知第三方支付----------------------------------------------------------
|
|
|
|
|
|
- SortedMap<String,String> map = new TreeMap<String,String>();
|
|
|
+ SortedMap<String, String> map = new TreeMap<String, String>();
|
|
|
// 订单编号
|
|
|
map.put("out_trade_no", orderNo);
|
|
|
// 商品描述
|
|
|
@@ -136,21 +148,536 @@ public class WFTOrderService {
|
|
|
// 是否小程序支付--值为1,表示小程序支付;不传或值不为1,表示公众账号内支付
|
|
|
map.put("is_minipg", "1");
|
|
|
map.put("sub_appid", "wx9894a01b9e92c368");
|
|
|
- }else if(payWay.equals("2")){
|
|
|
+ } else if (payWay.equals("2")) {
|
|
|
map.put("is_minipg", "2");
|
|
|
}
|
|
|
|
|
|
// --------微信支付请求
|
|
|
- Map<String, Object> wxMap = wx.pay(map, userOpenId,swiftpassConfig);
|
|
|
+ Map<String, Object> wxMap = wx.pay(map, userOpenId, swiftpassConfig);
|
|
|
+ log.info("威富通支付返回结果: {}", wxMap);
|
|
|
if (wxMap.get("status").toString().equals("200")) {
|
|
|
- payForm.setParams((WechatPayParamsVO) wxMap);
|
|
|
+ payForm.setParams(wxMap);
|
|
|
return payForm;
|
|
|
} else {
|
|
|
- throw new RuntimeException("请求支付失败。");
|
|
|
+ String errorMsg = wxMap.get("msg") != null ? wxMap.get("msg").toString() : "未知错误";
|
|
|
+ log.error("请求支付失败,返回结果: {}", wxMap);
|
|
|
+ throw new RuntimeException("请求支付失败:" + errorMsg);
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
- e.printStackTrace();
|
|
|
+ log.error("创建订单异常,用户ID: {}, 订单号: {}", userId, orderNo, e);
|
|
|
+ throw new RuntimeException("请求支付失败:" + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public AppUserPayForm payOrder(String orderId, String ip) {
|
|
|
+ UserOrderInfo orderInfo = userOrderInfoMapper.selectById(orderId);
|
|
|
+ //构建支付表单
|
|
|
+ AppUserPayForm payForm = new AppUserPayForm();
|
|
|
+ payForm.setOrderId(orderInfo.getId()).setOrderNo(orderInfo.getOrderNo());
|
|
|
+
|
|
|
+ //查询档位
|
|
|
+ RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId());
|
|
|
+
|
|
|
+ PayUtill wx = new PayUtill();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 小程序支付
|
|
|
+ */
|
|
|
+ String payWay = "1";
|
|
|
+
|
|
|
+ try {
|
|
|
+ log.debug("通知第三开始支付:");
|
|
|
+ // 通知第三方支付----------------------------------------------------------
|
|
|
+
|
|
|
+ SortedMap<String, String> map = new TreeMap<String, String>();
|
|
|
+ // 订单编号
|
|
|
+ map.put("out_trade_no", orderInfo.getOrderNo());
|
|
|
+ // 商品描述
|
|
|
+ map.put("body", "购买充电抵扣券");
|
|
|
+ // 附加信息
|
|
|
+ map.put("attach", "支付人" + orderInfo.getUserId());
|
|
|
+
|
|
|
+ // pifList.get(0).setHydOrderPayMoney(new BigDecimal("0.01"));
|
|
|
+ // 总金额(分)
|
|
|
+
|
|
|
+ map.put("total_fee", amount_fee(level.getMoney()));
|
|
|
+ // 终端ip
|
|
|
+ map.put("mch_create_ip", ip);
|
|
|
+ // 签名方式
|
|
|
+ map.put("sign_type", "RSA_1_256");
|
|
|
+ // 回调地址
|
|
|
+ map.put("notify_url", swiftpassConfig.getNotify_url());
|
|
|
+ // 公众账号或小程序ID
|
|
|
+ map.put("sub_appid", "wx9894a01b9e92c368");
|
|
|
+ if (payWay.equals("1")) {// 支付渠道(1 微信 2支付宝支付 4建行支付 6微信小程序支付)
|
|
|
+ // 是否小程序支付--值为1,表示小程序支付;不传或值不为1,表示公众账号内支付
|
|
|
+ map.put("is_minipg", "1");
|
|
|
+ map.put("sub_appid", "wx9894a01b9e92c368");
|
|
|
+ } else if (payWay.equals("2")) {
|
|
|
+ map.put("is_minipg", "2");
|
|
|
+ }
|
|
|
+
|
|
|
+ // --------微信支付请求
|
|
|
+ Map<String, Object> wxMap = wx.pay(map, orderInfo.getOpenid(), swiftpassConfig);
|
|
|
+ log.info("威富通支付返回结果: {}", wxMap);
|
|
|
+ if (wxMap.get("status").toString().equals("200")) {
|
|
|
+ payForm.setParams(wxMap);
|
|
|
+ return payForm;
|
|
|
+ } else {
|
|
|
+ String errorMsg = wxMap.get("msg") != null ? wxMap.get("msg").toString() : "未知错误";
|
|
|
+ log.error("支付订单失败,返回结果: {}", wxMap);
|
|
|
+ throw new RuntimeException("支付失败:" + errorMsg);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("支付订单异常,订单ID: {}", orderId, e);
|
|
|
+ throw new RuntimeException("支付失败:" + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将威富通支付返回的Map转换为WechatPayParamsVO
|
|
|
+ *
|
|
|
+ * @param wxMap 威富通返回的支付参数Map
|
|
|
+ * @return WechatPayParamsVO
|
|
|
+ */
|
|
|
+ private WechatPayParamsVO convertMapToWechatPayParamsVO(Map<String, Object> wxMap) {
|
|
|
+ WechatPayParamsVO vo = new WechatPayParamsVO();
|
|
|
+ // 威富通返回的字段名可能不同,需要根据实际返回调整
|
|
|
+ if (wxMap.get("appId") != null ) {
|
|
|
+ vo.setAppId(wxMap.get("appId").toString());
|
|
|
+ }
|
|
|
+ if (wxMap.get("timeStamp") != null ) {
|
|
|
+ vo.setTimeStamp(wxMap.get("timeStamp").toString());
|
|
|
+ }
|
|
|
+ if (wxMap.get("nonceStr") != null ) {
|
|
|
+ vo.setNonceStr(wxMap.get("nonceStr").toString());
|
|
|
+ }
|
|
|
+ if (wxMap.get("package") != null ) {
|
|
|
+ vo.setPackageValue(wxMap.get("package").toString());
|
|
|
+ }
|
|
|
+ if (wxMap.get("paySign") != null ) {
|
|
|
+ vo.setPaySign(wxMap.get("paySign").toString());
|
|
|
+ }
|
|
|
+ if (wxMap.get("signType") != null ) {
|
|
|
+ vo.setSignType(wxMap.get("signType").toString());
|
|
|
+ }
|
|
|
+ if (wxMap.get("out_trade_no") != null) {
|
|
|
+ vo.setOutTradeNo(wxMap.get("out_trade_no").toString());
|
|
|
+ }
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Boolean closeOrder(String orderNo) {
|
|
|
+ UserOrderInfo userOrderInfo = userOrderInfoMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1"));
|
|
|
+ if(ObjectUtil.isNull(userOrderInfo)){
|
|
|
+ throw new BusinessException("订单不存在");
|
|
|
+ }
|
|
|
+ userOrderInfo.setOrderStatus(SystemConstants.STATUS_THREE);
|
|
|
+ int i = userOrderInfoMapper.updateById(userOrderInfo);
|
|
|
+ if(i > 0){
|
|
|
+ return Boolean.TRUE;
|
|
|
+ }
|
|
|
+ return Boolean.FALSE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理威富通支付通知回调
|
|
|
+ *
|
|
|
+ * 重要说明:
|
|
|
+ * 1. 接收威富通POST的XML数据流
|
|
|
+ * 2. 验证签名(支持RSA_1_256、RSA_1_1、MD5)
|
|
|
+ * 3. 校验订单号和金额
|
|
|
+ * 4. 幂等性处理,防止重复通知
|
|
|
+ * 5. 使用数据库锁避免并发问题
|
|
|
+ * 6. 更新订单状态
|
|
|
+ * 7. 必须返回纯字符串"success"或"fail"
|
|
|
+ *
|
|
|
+ * 通知重试机制:0/15/15/30/180/1800/1800/1800/1800/3600秒
|
|
|
+ *
|
|
|
+ * @param request HTTP请求对象
|
|
|
+ * @return "success" 表示处理成功,"fail" 表示处理失败
|
|
|
+ */
|
|
|
+ public String handlePayNotify(HttpServletRequest request) {
|
|
|
+ log.info("========== 开始处理威富通支付通知 ==========");
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 1. 读取POST的XML数据
|
|
|
+ String xmlData = XmlUtils.parseRequst(request);
|
|
|
+ log.info("接收到的XML通知数据: {}", xmlData);
|
|
|
+
|
|
|
+ if (xmlData == null || xmlData.trim().isEmpty()) {
|
|
|
+ log.error("通知数据为空");
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 解析XML为Map
|
|
|
+ Map<String, String> notifyData = XmlUtils.toMap(xmlData.getBytes("UTF-8"), "UTF-8");
|
|
|
+ log.info("解析后的通知参数: {}", notifyData);
|
|
|
+
|
|
|
+ // 3. 验证基本参数
|
|
|
+ String status = notifyData.get("status");
|
|
|
+ String resultCode = notifyData.get("result_code");
|
|
|
+ String signType = notifyData.get("sign_type");
|
|
|
+ String sign = notifyData.get("sign");
|
|
|
+
|
|
|
+ if (status == null || resultCode == null || signType == null || sign == null) {
|
|
|
+ log.error("通知参数缺失: status={}, result_code={}, sign_type={}, sign={}",
|
|
|
+ status, resultCode, signType, sign);
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 验证签名
|
|
|
+ boolean signValid = SignUtil.verifySign(sign, signType, notifyData, swiftpassConfig);
|
|
|
+ if (!signValid) {
|
|
|
+ log.error("签名验证失败");
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+ log.info("签名验证成功");
|
|
|
+
|
|
|
+ // 5. 检查通信状态和业务结果
|
|
|
+ if (!"0".equals(status)) {
|
|
|
+ log.error("通信状态失败: status={}, message={}", status, notifyData.get("message"));
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!"0".equals(resultCode)) {
|
|
|
+ log.error("业务结果失败: result_code={}, err_code={}, err_msg={}",
|
|
|
+ resultCode, notifyData.get("err_code"), notifyData.get("err_msg"));
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 提取支付结果参数
|
|
|
+ String payResult = notifyData.get("pay_result");
|
|
|
+ if (!"0".equals(payResult)) {
|
|
|
+ log.error("支付结果失败: pay_result={}, pay_info={}",
|
|
|
+ payResult, notifyData.get("pay_info"));
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 提取订单相关信息
|
|
|
+ String outTradeNo = notifyData.get("out_trade_no"); // 商户订单号
|
|
|
+ String transactionId = notifyData.get("transaction_id"); // 平台订单号
|
|
|
+ String totalFeeStr = notifyData.get("total_fee"); // 总金额(分)
|
|
|
+ String timeEnd = notifyData.get("time_end"); // 支付完成时间
|
|
|
+ String openid = notifyData.get("openid"); // 用户标识
|
|
|
+ String attach = notifyData.get("attach"); // 附加信息
|
|
|
+
|
|
|
+ if (outTradeNo == null || transactionId == null || totalFeeStr == null) {
|
|
|
+ log.error("订单关键参数缺失: out_trade_no={}, transaction_id={}, total_fee={}",
|
|
|
+ outTradeNo, transactionId, totalFeeStr);
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("订单信息: out_trade_no={}, transaction_id={}, total_fee={}, time_end={}",
|
|
|
+ outTradeNo, transactionId, totalFeeStr, timeEnd);
|
|
|
+
|
|
|
+ // 8. 查询订单并加锁(防止并发)
|
|
|
+ UserOrderInfo orderInfo = userOrderInfoMapper.selectOne(
|
|
|
+ Wrappers.lambdaQuery(UserOrderInfo.class)
|
|
|
+ .eq(UserOrderInfo::getOrderNo, outTradeNo)
|
|
|
+ .last("FOR UPDATE") // 数据库行锁
|
|
|
+ );
|
|
|
+
|
|
|
+ if (orderInfo == null) {
|
|
|
+ log.error("订单不存在: out_trade_no={}", outTradeNo);
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 9. 幂等性检查 - 如果订单已支付,直接返回成功
|
|
|
+ if (SystemConstants.STATUS_TWO.equals(orderInfo.getOrderStatus())) {
|
|
|
+ log.info("订单已支付,幂等性处理: out_trade_no={}, transaction_id={}",
|
|
|
+ outTradeNo, orderInfo.getTransactionId());
|
|
|
+ return "success";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 10. 验证订单金额
|
|
|
+ RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId());
|
|
|
+ if (level == null) {
|
|
|
+ log.error("档位不存在: level_id={}", orderInfo.getLevelId());
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ String expectedFee = amount_fee(level.getMoney());
|
|
|
+ if (!expectedFee.equals(totalFeeStr)) {
|
|
|
+ log.error("订单金额不匹配: expected={}, actual={}", expectedFee, totalFeeStr);
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 11. 更新订单状态
|
|
|
+ orderInfo.setOrderStatus(SystemConstants.STATUS_TWO); // 已支付
|
|
|
+ orderInfo.setTransactionId(transactionId); // 平台订单号
|
|
|
+ orderInfo.setPayMoney(level.getMoney()); // 支付金额
|
|
|
+ orderInfo.setOutTradeNo(transactionId); // 第三方订单号
|
|
|
+ // 解析支付时间
|
|
|
+ if (timeEnd != null && timeEnd.length() == 14) {
|
|
|
+ try {
|
|
|
+ java.time.format.DateTimeFormatter formatter =
|
|
|
+ java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
|
|
+ orderInfo.setPayTime(java.time.LocalDateTime.parse(timeEnd, formatter));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析支付时间失败: time_end={}", timeEnd, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ int updateCount = userOrderInfoMapper.updateById(orderInfo);
|
|
|
+ if (updateCount <= 0) {
|
|
|
+ log.error("更新订单状态失败: out_trade_no={}", outTradeNo);
|
|
|
+ return "fail";
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("订单支付成功: out_trade_no={}, transaction_id={}, total_fee={}",
|
|
|
+ outTradeNo, transactionId, totalFeeStr);
|
|
|
+
|
|
|
+ // TODO: 这里可以添加业务逻辑,例如:
|
|
|
+ // - 发送支付成功通知
|
|
|
+ // - 更新用户余额/会员等级
|
|
|
+ // - 记录支付日志
|
|
|
+
|
|
|
+ return "success";
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("处理支付通知异常", e);
|
|
|
+ return "fail";
|
|
|
+ } finally {
|
|
|
+ log.info("========== 威富通支付通知处理完成 ==========");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单是否支付成功查询
|
|
|
+ *
|
|
|
+ * 应用场景:
|
|
|
+ * 1. 用户支付后前端轮询查询订单状态
|
|
|
+ * 2. 后台定时任务查询未支付订单
|
|
|
+ * 3. 支付通知未收到时的补偿查询
|
|
|
+ *
|
|
|
+ * 查询策略建议:
|
|
|
+ * - 方案一:以订单创建时间为基准,间隔5秒/30秒/1分钟/3分钟/5分钟/10分钟/30分钟查询
|
|
|
+ * - 方案二:定时任务每30秒查询最近10分钟内未支付订单,最多查询10次
|
|
|
+ *
|
|
|
+ * @param orderNo 商户订单号
|
|
|
+ * @return true-已支付成功,false-未支付或查询失败
|
|
|
+ */
|
|
|
+ public Boolean queryOrder(String orderNo) {
|
|
|
+ log.info("========== 开始查询订单支付状态 ==========");
|
|
|
+ log.info("查询订单号: {}", orderNo);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 1. 查询本地订单信息
|
|
|
+ UserOrderInfo orderInfo = userOrderInfoMapper.selectOne(
|
|
|
+ Wrappers.lambdaQuery(UserOrderInfo.class)
|
|
|
+ .eq(UserOrderInfo::getOrderNo, orderNo)
|
|
|
+ );
|
|
|
+
|
|
|
+ if (orderInfo == null) {
|
|
|
+ log.error("订单不存在: orderNo={}", orderNo);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 如果订单已经是已支付状态,直接返回true
|
|
|
+ if (SystemConstants.STATUS_TWO.equals(orderInfo.getOrderStatus())) {
|
|
|
+ log.info("订单已支付: orderNo={}, transactionId={}", orderNo, orderInfo.getTransactionId());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 如果订单已取消,返回false
|
|
|
+ if (SystemConstants.STATUS_THREE.equals(orderInfo.getOrderStatus())) {
|
|
|
+ log.info("订单已取消: orderNo={}", orderNo);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 构建查询请求参数
|
|
|
+ SortedMap<String, String> queryParams = new TreeMap<>();
|
|
|
+ queryParams.put("out_trade_no", orderNo); // 商户订单号
|
|
|
+ queryParams.put("sign_type", "RSA_1_256"); // 签名方式
|
|
|
+
|
|
|
+ // 5. 调用威富通订单查询API
|
|
|
+ Map<String, String> queryResult = queryOrderFromWFT(queryParams);
|
|
|
+
|
|
|
+ if (queryResult == null || !"200".equals(queryResult.get("status"))) {
|
|
|
+ log.error("查询订单失败: orderNo={}, result={}", orderNo, queryResult);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 解析查询结果
|
|
|
+ String tradeState = queryResult.get("trade_state");
|
|
|
+ String payResult = queryResult.get("pay_result");
|
|
|
+
|
|
|
+ log.info("订单查询结果: orderNo={}, trade_state={}, pay_result={}",
|
|
|
+ orderNo, tradeState, payResult);
|
|
|
+
|
|
|
+ // 7. 判断支付状态
|
|
|
+ // trade_state: SUCCESS-支付成功, NOTPAY-未支付, CLOSED-已关闭, REFUND-转入退款
|
|
|
+ // pay_result: 0-成功
|
|
|
+ boolean isPaid = "SUCCESS".equals(tradeState) || "0".equals(payResult);
|
|
|
+
|
|
|
+ // 8. 如果支付成功但本地订单状态未更新,同步更新订单状态
|
|
|
+ if (isPaid && !SystemConstants.STATUS_TWO.equals(orderInfo.getOrderStatus())) {
|
|
|
+ log.info("同步更新订单支付状态: orderNo={}", orderNo);
|
|
|
+ syncOrderStatus(orderInfo, queryResult);
|
|
|
+ }
|
|
|
+
|
|
|
+ return isPaid;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("查询订单支付状态异常: orderNo={}", orderNo, e);
|
|
|
+ return false;
|
|
|
+ } finally {
|
|
|
+ log.info("========== 订单支付状态查询完成 ==========");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 调用威富通订单查询API
|
|
|
+ *
|
|
|
+ * @param queryParams 查询参数
|
|
|
+ * @return 查询结果
|
|
|
+ */
|
|
|
+ private Map<String, String> queryOrderFromWFT(SortedMap<String, String> queryParams) {
|
|
|
+ log.info("调用威富通订单查询API: params={}", queryParams);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 设置公共参数
|
|
|
+ queryParams.put("service", "unified.trade.query");
|
|
|
+ queryParams.put("version", "2.0");
|
|
|
+ queryParams.put("charset", "UTF-8");
|
|
|
+ queryParams.put("mch_id", swiftpassConfig.getMch_id());
|
|
|
+ queryParams.put("nonce_str", String.valueOf(new Date().getTime()));
|
|
|
+
|
|
|
+ // 生成签名
|
|
|
+ Map<String, String> params = SignUtils.paraFilter(queryParams);
|
|
|
+ StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
|
|
|
+ SignUtils.buildPayParams(buf, params, false);
|
|
|
+ String preStr = buf.toString();
|
|
|
+ String signType = queryParams.get("sign_type");
|
|
|
+ String sign = SignUtil.getSign(signType, preStr, swiftpassConfig);
|
|
|
+ queryParams.put("sign", sign);
|
|
|
+
|
|
|
+ log.info("查询请求XML: {}", XmlUtils.toXml(queryParams));
|
|
|
+
|
|
|
+ // 发送HTTP请求
|
|
|
+ String reqUrl = swiftpassConfig.getReq_url();
|
|
|
+ org.apache.http.client.methods.HttpPost httpPost =
|
|
|
+ new org.apache.http.client.methods.HttpPost(reqUrl);
|
|
|
+ org.apache.http.entity.StringEntity entityParams =
|
|
|
+ new org.apache.http.entity.StringEntity(XmlUtils.parseXML(queryParams), "utf-8");
|
|
|
+ httpPost.setEntity(entityParams);
|
|
|
+ httpPost.setHeader("Content-Type", "text/xml;utf-8");
|
|
|
+
|
|
|
+ org.apache.http.impl.client.CloseableHttpClient client =
|
|
|
+ org.apache.http.impl.client.HttpClients.createDefault();
|
|
|
+ org.apache.http.client.methods.CloseableHttpResponse response = client.execute(httpPost);
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (response != null && response.getEntity() != null) {
|
|
|
+ // 解析响应
|
|
|
+ Map<String, String> resultMap = XmlUtils.toMap(
|
|
|
+ org.apache.http.util.EntityUtils.toByteArray(response.getEntity()),
|
|
|
+ "utf-8");
|
|
|
+
|
|
|
+ log.info("查询响应结果: {}", resultMap);
|
|
|
+
|
|
|
+ // 验证签名
|
|
|
+ String reSign = resultMap.get("sign");
|
|
|
+ String reSignType = resultMap.get("sign_type");
|
|
|
+
|
|
|
+ if (resultMap.containsKey("sign")) {
|
|
|
+ boolean signValid = SignUtil.verifySign(reSign, reSignType, resultMap, swiftpassConfig);
|
|
|
+ if (!signValid) {
|
|
|
+ log.error("查询结果签名验证失败");
|
|
|
+ resultMap.put("status", "500");
|
|
|
+ resultMap.put("msg", "签名验证失败");
|
|
|
+ return resultMap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查通信状态和业务结果
|
|
|
+ if ("0".equals(resultMap.get("status")) &&
|
|
|
+ "0".equals(resultMap.get("result_code"))) {
|
|
|
+ resultMap.put("status", "200");
|
|
|
+ return resultMap;
|
|
|
+ } else {
|
|
|
+ log.error("查询失败: status={}, result_code={}, err_code={}, err_msg={}",
|
|
|
+ resultMap.get("status"), resultMap.get("result_code"),
|
|
|
+ resultMap.get("err_code"), resultMap.get("err_msg"));
|
|
|
+ resultMap.put("status", "500");
|
|
|
+ return resultMap;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.error("查询响应为空");
|
|
|
+ Map<String, String> errorResult = new HashMap<>();
|
|
|
+ errorResult.put("status", "500");
|
|
|
+ errorResult.put("msg", "查询响应为空");
|
|
|
+ return errorResult;
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ response.close();
|
|
|
+ client.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("调用威富通查询API异常", e);
|
|
|
+ Map<String, String> errorResult = new HashMap<>();
|
|
|
+ errorResult.put("status", "500");
|
|
|
+ errorResult.put("msg", "系统异常: " + e.getMessage());
|
|
|
+ return errorResult;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 同步订单支付状态
|
|
|
+ * 当查询到订单已支付但本地状态未更新时,同步更新本地订单状态
|
|
|
+ *
|
|
|
+ * @param orderInfo 订单信息
|
|
|
+ * @param queryResult 查询结果
|
|
|
+ */
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void syncOrderStatus(UserOrderInfo orderInfo, Map<String, String> queryResult) {
|
|
|
+ log.info("同步订单状态: orderNo={}", orderInfo.getOrderNo());
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 提取支付信息
|
|
|
+ String transactionId = queryResult.get("transaction_id");
|
|
|
+ String totalFeeStr = queryResult.get("total_fee");
|
|
|
+ String timeEnd = queryResult.get("time_end");
|
|
|
+
|
|
|
+ // 查询档位信息验证金额
|
|
|
+ RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId());
|
|
|
+ if (level != null) {
|
|
|
+ String expectedFee = amount_fee(level.getMoney());
|
|
|
+ if (expectedFee.equals(totalFeeStr)) {
|
|
|
+ // 更新订单状态
|
|
|
+ orderInfo.setOrderStatus(SystemConstants.STATUS_TWO);
|
|
|
+ orderInfo.setTransactionId(transactionId);
|
|
|
+ orderInfo.setPayMoney(level.getMoney());
|
|
|
+ orderInfo.setOutTradeNo(transactionId);
|
|
|
+
|
|
|
+ // 解析支付时间
|
|
|
+ if (timeEnd != null && timeEnd.length() == 14) {
|
|
|
+ try {
|
|
|
+ java.time.format.DateTimeFormatter formatter =
|
|
|
+ java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
|
|
+ orderInfo.setPayTime(java.time.LocalDateTime.parse(timeEnd, formatter));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析支付时间失败: time_end={}", timeEnd, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ int updateCount = userOrderInfoMapper.updateById(orderInfo);
|
|
|
+ if (updateCount > 0) {
|
|
|
+ log.info("订单状态同步成功: orderNo={}, transactionId={}",
|
|
|
+ orderInfo.getOrderNo(), transactionId);
|
|
|
+ } else {
|
|
|
+ log.error("订单状态同步失败: orderNo={}", orderInfo.getOrderNo());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.error("订单金额不匹配,不同步状态: expected={}, actual={}",
|
|
|
+ expectedFee, totalFeeStr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("同步订单状态异常: orderNo={}", orderInfo.getOrderNo(), e);
|
|
|
+ throw e;
|
|
|
}
|
|
|
- throw new RuntimeException("请求支付失败。");
|
|
|
}
|
|
|
}
|