| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893 |
- package com.zsElectric.boot.business.service;
- import cn.hutool.core.collection.CollUtil;
- import cn.hutool.core.util.ObjectUtil;
- import com.baomidou.mybatisplus.core.toolkit.Wrappers;
- import com.google.gson.Gson;
- import com.google.gson.JsonObject;
- import com.zsElectric.boot.business.mapper.*;
- import com.zsElectric.boot.business.model.entity.*;
- 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.WechatUrlConstants;
- 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;
- import java.time.LocalDateTime;
- import java.time.format.DateTimeFormatter;
- import java.util.*;
- /**
- * 威富通支付服务
- * 用于处理威富通JSAPI支付接口
- *
- * @author zsElectric
- * @since 2025-12-29
- */
- @Slf4j
- @Service
- public class WFTOrderService {
- @Resource
- private UserOrderInfoMapper userOrderInfoMapper;
- @Resource
- private UserInfoMapper userInfoMapper;
- @Resource
- private RechargeLevelMapper rechargeLevelMapper;
- @Resource
- protected SwiftpassConfig swiftpassConfig;
- @Resource
- private UserAccountService userAccountService;
- @Resource
- private ChargeOrderInfoMapper chargeOrderInfoMapper;
- @Resource
- private UserRefundsOrderInfoMapper userRefundsOrderInfoMapper;
- /**
- * 创建商户订单号
- * 要求 32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
- * 组成 两位前缀 + 17位时间戳 + 9位id补零 + 4位随机数 合计32位
- *
- * @param head 例如 商品-SP 退款-TK 等等
- * @param id 用户id
- * @return
- */
- public String createOrderNo(String head, Long id) {
- StringBuilder uid = new StringBuilder(id.toString());
- Date date = new Date();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
- int length = uid.length();
- for (int i = 0; i < 9 - length; i++) {
- uid.insert(0, "0");
- }
- return head + sdf.format(date) + uid + (int) ((Math.random() * 9 + 1) * 1000);
- }
- /**
- * 金额元转分字符串
- *
- * @param cny 元
- * @return
- */
- public String amount_fee(BigDecimal cny) {
- BigDecimal b2 = new BigDecimal("100");
- return cny.multiply(b2).setScale(0, RoundingMode.DOWN).toString();
- }
- /**
- * 金额分转元
- *
- * @param fen 分
- * @return 元(保留两位小数)
- */
- public BigDecimal fee_amount(Integer fen) {
- if (fen == null) {
- return BigDecimal.ZERO;
- }
- return new BigDecimal(fen).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
- }
- public AppUserPayForm createOrder(@Valid AppLevelOrderForm appLevelOrderForm) {
- Long userId = SecurityUtils.getUserId();
- String userOpenId = userInfoMapper.getAppletUserInfo(userId).getOpenid();
- String orderNo = createOrderNo("SP", userId);
- //查询档位
- RechargeLevel level = rechargeLevelMapper.selectById(appLevelOrderForm.getLevelId());
- //创建订单
- UserOrderInfo orderInfo = new UserOrderInfo();
- orderInfo.setUserId(userId);
- orderInfo.setOrderNo(orderNo);
- orderInfo.setLevelId(appLevelOrderForm.getLevelId());
- orderInfo.setOpenid(userOpenId);
- orderInfo.setOrderMoney(level.getMoney());
- userOrderInfoMapper.insert(orderInfo);
- //构建支付表单返回给前端支撑JsApi支付调用
- AppUserPayForm payForm = new AppUserPayForm();
- payForm.setOrderId(orderInfo.getId()).setOrderNo(orderNo);
- PayUtill wx = new PayUtill();
- /**
- * 小程序支付
- */
- String payWay = "1";
- /**
- * 请求支付
- */
- try {
- log.debug("通知第三开始支付:");
- // 通知第三方支付----------------------------------------------------------
- SortedMap<String, String> map = new TreeMap<String, String>();
- // 订单编号
- map.put("out_trade_no", orderNo);
- // 商品描述
- map.put("body", "购买充电抵扣券");
- // 附加信息
- map.put("attach", "支付人" + userId);
- // pifList.get(0).setHydOrderPayMoney(new BigDecimal("0.01"));
- // 总金额(分)
- map.put("total_fee", amount_fee(level.getMoney()));
- // 终端ip
- map.put("mch_create_ip", appLevelOrderForm.getIp());
- // 签名方式
- 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, userOpenId, 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: {}, 订单号: {}", 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);
- }
- }
- 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" 表示处理失败
- */
- @Transactional(rollbackFor = Exception.class)
- 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 orderNo = notifyData.get("out_trade_no"); //商户订单号
- String outTradeNo = notifyData.get("out_transaction_id"); // 第三方订单号
- String transactionId = notifyData.get("transaction_id"); // 平台订单号
- String totalFeeStr = notifyData.get("total_fee"); // 总金额(分)
- BigDecimal payMoney = fee_amount(Integer.parseInt(totalFeeStr));
- String timeEnd = notifyData.get("time_end"); // 支付完成时间
- //转localDateTime
- LocalDateTime payTime = LocalDateTime.parse(timeEnd, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
- 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, orderNo)
- .last("FOR UPDATE") // 数据库行锁
- );
- if (orderInfo == null) {
- log.error("订单不存在: out_trade_no={}", orderNo);
- return "fail";
- }
- // 9. 幂等性检查 - 如果订单已支付,直接返回成功
- if (SystemConstants.STATUS_TWO.equals(orderInfo.getOrderStatus())) {
- log.info("订单已支付,幂等性处理: out_trade_no={}, transaction_id={}",
- outTradeNo, orderInfo.getTransactionId());
- return "success";
- }
- // 10. 验证订单金额
- String expectedFee = amount_fee(orderInfo.getOrderMoney());
- if (!expectedFee.equals(totalFeeStr)) {
- log.error("订单金额不匹配: expected={}, actual={}", expectedFee, totalFeeStr);
- return "fail";
- }
- // 11. 更新订单状态
- orderInfo.setOrderStatus(SystemConstants.STATUS_TWO); // 已支付
- orderInfo.setTransactionId(transactionId); // 平台订单号
- orderInfo.setPayMoney(payMoney); // 支付金额
- orderInfo.setOutTradeNo(outTradeNo); // 第三方订单号
- orderInfo.setPayTime(payTime);
- // 解析支付时间
- 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);
- //支付成功业务处理
- successPayOrder(orderInfo, transactionId, payTime, payMoney);
- return "success";
- } catch (Exception e) {
- log.error("处理支付通知异常", e);
- return "fail";
- } finally {
- log.info("========== 威富通支付通知处理完成 ==========");
- }
- }
- /**
- * 支付成功业务处理
- *
- * @param orderInfo
- * @param transactionId
- * @param payTime
- * @param orderMoney
- * @throws Exception
- */
- @Transactional(rollbackFor = Exception.class)
- protected void successPayOrder(UserOrderInfo orderInfo, String transactionId, LocalDateTime payTime, BigDecimal orderMoney) throws Exception {
- //修改订单状态为已支付
- orderInfo.setOrderStatus(SystemConstants.STATUS_TWO);
- orderInfo.setPayTime(payTime);
- orderInfo.setTransactionId(transactionId);
- orderInfo.setPayMoney(orderMoney);
- userOrderInfoMapper.updateById(orderInfo);
- //账户变动及日志记录
- Long userId = orderInfo.getUserId();
- UserAccount userAccount = userAccountService.updateAccountBalanceAndLog(
- userId,
- orderMoney,
- SystemConstants.CHANGE_TYPE_ADD,
- SystemConstants.ACCOUNT_LOG_PAY_NOTE,
- orderInfo.getId()
- );
- //平订单超充金额(使用更新后的账户信息)
- orderBackTax(userId, userAccount);
- }
- private void orderBackTax(Long userId, UserAccount userAccount) {
- //查询超充订单
- List<ChargeOrderInfo> chargeOrderInfoList = chargeOrderInfoMapper.selectList(Wrappers.<ChargeOrderInfo>lambdaQuery()
- .eq(ChargeOrderInfo::getUserId, userId)
- .eq(ChargeOrderInfo::getMaspStatus, SystemConstants.STATUS_ONE)
- .orderByAsc(ChargeOrderInfo::getCreateTime)
- );
- if (CollUtil.isNotEmpty(chargeOrderInfoList)) {
- //余额减去超充金额(使用更新后的余额)
- BigDecimal balance = userAccount.getBalance();
- for (ChargeOrderInfo chargeOrderInfo : chargeOrderInfoList) {
- if (balance.compareTo(BigDecimal.ZERO) > 0) {
- BigDecimal deductAmount; // 本次扣除金额
- if (balance.compareTo(chargeOrderInfo.getTotalMaspMoney()) < 0) {
- //余额不足,部分补缴
- deductAmount = balance;
- chargeOrderInfo.setAlreadyMaspMoney(deductAmount);
- chargeOrderInfo.setMaspDesc("部分补缴");
- chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO);
- chargeOrderInfoMapper.updateById(chargeOrderInfo);
- //账户变动及日志记录
- userAccountService.updateAccountBalanceAndLog(
- userId,
- deductAmount.negate(),
- SystemConstants.CHANGE_TYPE_REDUCE,
- SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE,
- chargeOrderInfo.getId()
- );
- balance = BigDecimal.ZERO;
- continue;
- }
- //余额足够,完成补缴
- deductAmount = chargeOrderInfo.getTotalMaspMoney();
- balance = balance.subtract(deductAmount);
- chargeOrderInfo.setAlreadyMaspMoney(deductAmount);
- chargeOrderInfo.setMaspDesc("完成补缴");
- chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO);
- chargeOrderInfoMapper.updateById(chargeOrderInfo);
- //账户变动及日志记录
- userAccountService.updateAccountBalanceAndLog(
- userId,
- deductAmount.negate(),
- SystemConstants.CHANGE_TYPE_REDUCE,
- SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE,
- chargeOrderInfo.getId()
- );
- }
- }
- }
- }
- /**
- * 订单是否支付成功查询
- *
- * 应用场景:
- * 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 (!"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);
- String transactionId = queryResult.get("transaction_id"); // 平台订单号
- String totalFeeStr = queryResult.get("total_fee"); // 总金额(分)
- BigDecimal payMoney = fee_amount(Integer.parseInt(totalFeeStr));
- String timeEnd = queryResult.get("time_end"); // 支付完成时间
- //转localDateTime
- LocalDateTime payTime = LocalDateTime.parse(timeEnd, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
- //支付成功业务处理
- successPayOrder(orderInfo, transactionId, payTime, payMoney);
- }
- 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;
- }
- }
- /**
- * 账户退款
- *
- * @return
- */
- @Transactional(rollbackFor = Exception.class)
- public String refundOrder() throws Exception {
- //查询账户余额
- UserAccount userAccount =
- userAccountService.getOne(Wrappers.<UserAccount>lambdaQuery().eq(UserAccount::getUserId, SecurityUtils.getUserId()).last("limit 1"));
- if (userAccount.getBalance().compareTo(BigDecimal.ZERO) == 0) {
- return "账户余额为 0";
- }
- BigDecimal refundMoney = userAccount.getBalance();
- //查询一年内已支付的所有券订单
- List<UserOrderInfo> userOrderInfoList = userOrderInfoMapper.selectList(Wrappers.<UserOrderInfo>lambdaQuery()
- .eq(UserOrderInfo::getOrderStatus, SystemConstants.STATUS_TWO)
- .between(UserOrderInfo::getCreateTime, LocalDateTime.now().minusYears(1), LocalDateTime.now())
- );
- if (CollUtil.isEmpty(userOrderInfoList)) {
- log.info("当前用户一年内未支付任何券订单,无法进行退款操作!");
- throw new BusinessException("无法进行退款操作,请联系客服处理!");
- }
- for (UserOrderInfo userOrderInfo : userOrderInfoList) {
- if(refundMoney.compareTo(BigDecimal.ZERO) == 0){
- break;
- }
- if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) > 0) {
- //退款金额大于订单金额,则直接退退款金额
- refundOrder(userOrderInfo,refundMoney, "账户退款", SystemConstants.STATUS_ONE);
- //账户变动及日志记录
- userAccountService.updateAccountBalanceAndLog(
- SecurityUtils.getUserId(),
- refundMoney,
- SystemConstants.CHANGE_TYPE_REDUCE,
- SystemConstants.ACCOUNT_LOG_REFUND_NOTE,
- userOrderInfo.getId()
- );
- //修改订单状态
- userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR);
- userOrderInfoMapper.updateById(userOrderInfo);
- refundMoney = BigDecimal.ZERO;
- break;
- }
- if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) < 0) {
- //退款金额小于订单金额,则先退订单金额
- refundOrder(userOrderInfo,userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()), "账户退款", SystemConstants.STATUS_ONE);
- //账户变动及日志记录
- userAccountService.updateAccountBalanceAndLog(
- SecurityUtils.getUserId(),
- userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()),
- SystemConstants.CHANGE_TYPE_REDUCE,
- SystemConstants.ACCOUNT_LOG_REFUND_NOTE,
- userOrderInfo.getId()
- );
- //修改订单状态
- userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR);
- userOrderInfoMapper.updateById(userOrderInfo);
- refundMoney = refundMoney.subtract(userOrderInfo.getOrderMoney());
- }
- }
- return "账户退款,预计3个工作日内分一笔或多笔退还!如未收到,请联系客服!";
- }
- public void refundOrder(UserOrderInfo userOrderInfo, BigDecimal refundAmount, String reason, Integer type) throws Exception {
- log.info("进入退款接口------>");
- log.info("执行操作的 原支付交易对应的商户订单号:{}", userOrderInfo.getOrderNo());
- //退款单号
- String out_refund_no = createOrderNo("TK",userOrderInfo.getId());
- // 创建退款订单
- UserRefundsOrderInfo userRefundsOrderInfo = new UserRefundsOrderInfo();
- userRefundsOrderInfo.setOrderId(userOrderInfo.getId());
- userRefundsOrderInfo.setOrderNo(userOrderInfo.getOrderNo());
- userRefundsOrderInfo.setOutRefundNo(out_refund_no);
- userRefundsOrderInfo.setReason(reason);
- userRefundsOrderInfo.setAmount(refundAmount);
- userRefundsOrderInfo.setType(type);
- userRefundsOrderInfo.setCreateTime(LocalDateTime.now());
- SortedMap<String,String> params = new TreeMap<>();
- params.put("out_trade_no", userOrderInfo.getOrderNo());//商户订单号
- params.put("out_refund_no", out_refund_no);//商户退款单号
- params.put("attach", reason);//退款原因
- params.put("total_fee", amount_fee(userOrderInfo.getOrderMoney()));//原订单金额
- params.put("refund_fee", amount_fee(refundAmount));//退款金额
- params.put("sign_type", "RSA_1_256");
- PayUtill payUtill = new PayUtill();
- Map<String, String> refund = payUtill.refund(params, swiftpassConfig);
- log.info("最终拿到的微信支付通知数据:" + refund);
- if (Objects.equals(refund.get("status"), "0")){
- log.info("退款调用成功!");
- if(Objects.equals(refund.get("result_code"), "0")){
- log.info("退款成功!");
- log.info("订单:{},退款成功!原因:{}", userOrderInfo.getOrderNo(), reason);
- //修改订单状态
- userOrderInfo.setOrderStatus(SystemConstants.STATUS_FIVE);
- userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount));
- userOrderInfo.setRefundTime(LocalDateTime.now());
- userOrderInfoMapper.updateById(userOrderInfo);
- } else{
- log.info("退款处理中");
- //修改订单状态
- userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount));
- userOrderInfoMapper.updateById(userOrderInfo);
- }
- //退款信息补充
- userRefundsOrderInfo.setRefundId(refund.get("refund_id"));
- userRefundsOrderInfo.setNotifyRequest(refund.toString());
- userRefundsOrderInfo.setTransactionId(userOrderInfo.getTransactionId());
- userRefundsOrderInfo.setAcceptedTime(LocalDateTime.now());
- userRefundsOrderInfoMapper.insert(userRefundsOrderInfo);
- }
- }
- }
|