UserOrderInfoServiceImpl.java 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  1. package com.zsElectric.boot.business.service.impl;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.util.ObjectUtil;
  4. import com.aliyun.oss.ServiceException;
  5. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  6. import com.google.gson.Gson;
  7. import com.google.gson.JsonObject;
  8. import com.zsElectric.boot.business.model.entity.UserRefundsOrderInfo;
  9. import com.zsElectric.boot.business.mapper.*;
  10. import com.zsElectric.boot.business.model.entity.*;
  11. import com.zsElectric.boot.business.model.form.applet.AppLevelOrderForm;
  12. import com.zsElectric.boot.business.model.form.applet.AppUserPayForm;
  13. import com.zsElectric.boot.business.model.query.applet.AppUserOrderInfoQuery;
  14. import com.zsElectric.boot.business.model.vo.applet.AppUserInfoVO;
  15. import com.zsElectric.boot.business.model.vo.applet.WechatPayParamsVO;
  16. import com.zsElectric.boot.business.service.*;
  17. import com.zsElectric.boot.common.constant.SystemConstants;
  18. import com.zsElectric.boot.core.exception.BusinessException;
  19. import com.zsElectric.boot.core.pay.*;
  20. import com.zsElectric.boot.security.util.SecurityUtils;
  21. import jakarta.servlet.http.HttpServletRequest;
  22. import jakarta.servlet.http.HttpServletResponse;
  23. import lombok.RequiredArgsConstructor;
  24. import lombok.extern.slf4j.Slf4j;
  25. import org.apache.commons.lang3.RandomStringUtils;
  26. import org.apache.commons.lang3.StringUtils;
  27. import org.springframework.stereotype.Service;
  28. import com.baomidou.mybatisplus.core.metadata.IPage;
  29. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  30. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  31. import com.zsElectric.boot.business.model.form.UserOrderInfoForm;
  32. import com.zsElectric.boot.business.model.query.UserOrderInfoQuery;
  33. import com.zsElectric.boot.business.model.vo.UserOrderInfoVO;
  34. import com.zsElectric.boot.business.converter.UserOrderInfoConverter;
  35. import java.math.BigDecimal;
  36. import java.math.RoundingMode;
  37. import java.text.DecimalFormat;
  38. import java.text.SimpleDateFormat;
  39. import java.time.LocalDateTime;
  40. import java.util.*;
  41. import java.util.concurrent.locks.ReentrantLock;
  42. import cn.hutool.core.lang.Assert;
  43. import cn.hutool.core.util.StrUtil;
  44. import org.springframework.transaction.annotation.Transactional;
  45. /**
  46. * 用户支付订单信息服务实现类
  47. *
  48. * @author zsElectric
  49. * @since 2025-12-16 16:25
  50. */
  51. @Slf4j
  52. @Service
  53. @RequiredArgsConstructor
  54. public class UserOrderInfoServiceImpl extends ServiceImpl<UserOrderInfoMapper, UserOrderInfo> implements UserOrderInfoService {
  55. private final UserOrderInfoConverter userOrderInfoConverter;
  56. private final UserInfoMapper userInfoMapper;
  57. private final RechargeLevelMapper rechargeLevelMapper;
  58. private final UserAccountMapper userAccountMapper;
  59. private final UserAccountService userAccountService;
  60. private final WechatPayV3Utils wechatPayV3Utils;
  61. private final ChargeOrderInfoMapper chargeOrderInfoMapper;
  62. private final UserRefundsOrderInfoMapper userRefundsOrderInfoMapper;
  63. // 声明一个可重入锁
  64. private final ReentrantLock lock = new ReentrantLock();
  65. /**
  66. * 获取用户支付订单信息分页列表
  67. *
  68. * @param queryParams 查询参数
  69. * @return {@link IPage<UserOrderInfoVO>} 用户支付订单信息分页列表
  70. */
  71. @Override
  72. public IPage<UserOrderInfoVO> getUserOrderInfoPage(UserOrderInfoQuery queryParams) {
  73. Page<UserOrderInfoVO> pageVO = this.baseMapper.getUserOrderInfoPage(
  74. new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
  75. queryParams
  76. );
  77. return pageVO;
  78. }
  79. /**
  80. * 获取用户支付订单信息表单数据
  81. *
  82. * @param id 用户支付订单信息ID
  83. * @return 用户支付订单信息表单数据
  84. */
  85. @Override
  86. public UserOrderInfoForm getUserOrderInfoFormData(Long id) {
  87. UserOrderInfo entity = this.getById(id);
  88. return userOrderInfoConverter.toForm(entity);
  89. }
  90. /**
  91. * 新增用户支付订单信息
  92. *
  93. * @param formData 用户支付订单信息表单对象
  94. * @return 是否新增成功
  95. */
  96. @Override
  97. public boolean saveUserOrderInfo(UserOrderInfoForm formData) {
  98. UserOrderInfo entity = userOrderInfoConverter.toEntity(formData);
  99. return this.save(entity);
  100. }
  101. /**
  102. * 更新用户支付订单信息
  103. *
  104. * @param id 用户支付订单信息ID
  105. * @param formData 用户支付订单信息表单对象
  106. * @return 是否修改成功
  107. */
  108. @Override
  109. public boolean updateUserOrderInfo(Long id, UserOrderInfoForm formData) {
  110. UserOrderInfo entity = userOrderInfoConverter.toEntity(formData);
  111. return this.updateById(entity);
  112. }
  113. /**
  114. * 删除用户支付订单信息
  115. *
  116. * @param ids 用户支付订单信息ID,多个以英文逗号(,)分割
  117. * @return 是否删除成功
  118. */
  119. @Override
  120. public boolean deleteUserOrderInfos(String ids) {
  121. Assert.isTrue(StrUtil.isNotBlank(ids), "删除的用户支付订单信息数据为空");
  122. // 逻辑删除
  123. List<Long> idList = Arrays.stream(ids.split(","))
  124. .map(Long::parseLong)
  125. .toList();
  126. return this.removeByIds(idList);
  127. }
  128. @Override
  129. public IPage<UserOrderInfoVO> getTicketRecords(AppUserOrderInfoQuery queryParams) {
  130. UserOrderInfoQuery userOrderInfoQuery = new UserOrderInfoQuery();
  131. userOrderInfoQuery.setUserId(SecurityUtils.getUserId());
  132. Page<UserOrderInfoVO> pageVO = this.baseMapper.getUserOrderInfoPage(
  133. new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
  134. userOrderInfoQuery
  135. );
  136. return pageVO;
  137. }
  138. @Override
  139. public AppUserInfoVO getAppletUserInfo(Long userId){
  140. return userInfoMapper.getAppletUserInfo(userId);
  141. }
  142. /**
  143. * 创建订单
  144. *
  145. * @param appLevelOrderForm
  146. * @return
  147. */
  148. @Override
  149. public AppUserPayForm createOrder(AppLevelOrderForm appLevelOrderForm) {
  150. Long userId = SecurityUtils.getUserId();
  151. String userOpenId = this.getAppletUserInfo(userId).getOpenid();
  152. String orderNo = createOrderNo("SP", userId);
  153. //创建订单
  154. UserOrderInfo orderInfo = new UserOrderInfo();
  155. orderInfo.setUserId(userId);
  156. orderInfo.setOrderNo(orderNo);
  157. orderInfo.setLevelId(appLevelOrderForm.getLevelId());
  158. orderInfo.setOpenid(userOpenId);
  159. this.save(orderInfo);
  160. //构建支付表单返回给前端支撑JsApi支付调用
  161. AppUserPayForm payForm = new AppUserPayForm();
  162. payForm.setOrderId(orderInfo.getId()).setOrderNo(orderNo);
  163. //查询档位
  164. RechargeLevel level = rechargeLevelMapper.selectById(appLevelOrderForm.getLevelId());
  165. Map<String, Object> result = payment(userOpenId, orderNo, level.getMoney());
  166. payForm.setParams(result);
  167. return payForm;
  168. }
  169. /**
  170. * 支付订单
  171. *
  172. * @param orderId
  173. * @return
  174. */
  175. @Override
  176. public AppUserPayForm payOrder(String orderId) {
  177. UserOrderInfo orderInfo = this.getById(orderId);
  178. //构建支付表单
  179. AppUserPayForm payForm = new AppUserPayForm();
  180. payForm.setOrderId(orderInfo.getId()).setOrderNo(orderInfo.getOrderNo());
  181. //查询档位
  182. RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId());
  183. Map<String,Object> result = payment(orderInfo.getOpenid(), orderInfo.getOrderNo(), level.getMoney());
  184. payForm.setParams(result);
  185. return payForm;
  186. }
  187. /**
  188. * 订单查询
  189. *
  190. * @param orderNo
  191. * @return
  192. */
  193. @Override
  194. public String orderQuery(String orderNo) throws Exception {
  195. //查询订单
  196. UserOrderInfo orderInfo = this.getById(Wrappers.<UserOrderInfo>lambdaQuery().eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1"));
  197. if (ObjectUtil.isEmpty(orderInfo)) {
  198. throw new RuntimeException("当前订单不存在");
  199. }
  200. //null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
  201. JsonObject res = orderQueryByOutTradeNo(orderNo, WechatConstants.WECHAT_MCH_ID);
  202. String s = res == null ? null : res.get("trade_state").getAsString();
  203. // String s = "SUCCESS";
  204. if ("SUCCESS".equals(s)) {
  205. if (ObjectUtil.isNotEmpty(orderInfo) && Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) {
  206. String transactionId = res.get("transaction_id").getAsString();
  207. LocalDateTime payTime = LocalDateTime.now();
  208. // 微信返回的金额单位是分,需要转换为元
  209. BigDecimal payMoney = new BigDecimal(res.get("amount").getAsJsonObject().get("total").getAsString())
  210. .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
  211. //执行业务操作
  212. this.successPayOrder(orderInfo, transactionId, payTime, payMoney);
  213. }
  214. return "100001";//支付成功
  215. }
  216. if (s == null) {
  217. //查询订单
  218. return "100002";//查询失败
  219. }
  220. if ("USERPAYING".equals(s) || "ACCEPT".equals(s)) {
  221. //查询订单
  222. return "100003";//查询中
  223. }
  224. return "100004";//支付失败
  225. }
  226. /**
  227. * 微信支付回调
  228. *
  229. * @param request
  230. * @param response
  231. * @return
  232. */
  233. @Override
  234. public Map<String, String> wechatPayNotify(HttpServletRequest request, HttpServletResponse response) {
  235. Map<String, String> result = new HashMap<>(2);
  236. //验签及解析返回数据
  237. JsonObject res = wechatPayV3Utils.getCallbackData(request);
  238. if (res == null) {
  239. result.put("code", "FAIL");
  240. result.put("message", "失败");
  241. return result;
  242. }
  243. log.info("最终拿到的微信支付通知数据:" + res);
  244. String orderNo = res.get("out_trade_no").getAsString();
  245. if (lock.tryLock()) {
  246. // 处理支付成功后的业务 例如 将订单状态修改为已支付 具体参数键值可参考文档 注意!!! 微信可能会多次发送重复的通知 因此要判断业务是否已经处理过了 避免重复处理
  247. try {
  248. //查询订单,判断是否已修改为已支付状态
  249. UserOrderInfo orderInfo = this.getOne(Wrappers.<UserOrderInfo>lambdaQuery().eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1"));
  250. if (ObjectUtil.isNotEmpty(orderInfo)) {
  251. if (Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_TWO)) {
  252. result.put("code", "SUCCESS");
  253. result.put("message", "OK");
  254. return result;
  255. }
  256. if (Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) {
  257. String transactionId = res.get("transaction_id").getAsString();
  258. LocalDateTime payTime = LocalDateTime.now();
  259. // 微信返回的金额单位是分,需要转换为元
  260. BigDecimal payMoney = new BigDecimal(res.get("amount").getAsJsonObject().get("total").getAsString())
  261. .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
  262. //执行业务操作
  263. this.successPayOrder(orderInfo, transactionId, payTime, payMoney);
  264. }
  265. }
  266. result.put("code", "SUCCESS");
  267. result.put("message", "OK");
  268. return result;
  269. } catch (Exception e) {
  270. log.error("微信支付回调异常:" + e.getMessage());
  271. result.put("code", "FAIL");
  272. result.put("message", "失败");
  273. return result;
  274. } finally {
  275. lock.unlock();
  276. }
  277. } else {
  278. result.put("code", "FAIL");
  279. result.put("message", "失败");
  280. return result;
  281. }
  282. }
  283. /**
  284. * 支付成功业务处理
  285. *
  286. * @param orderInfo
  287. * @param transactionId
  288. * @param payTime
  289. * @param orderMoney
  290. * @throws Exception
  291. */
  292. @Transactional(rollbackFor = Exception.class)
  293. protected void successPayOrder(UserOrderInfo orderInfo, String transactionId, LocalDateTime payTime, BigDecimal orderMoney) throws Exception {
  294. //修改订单状态为已支付
  295. orderInfo.setOrderStatus(SystemConstants.STATUS_TWO);
  296. orderInfo.setPayTime(payTime);
  297. orderInfo.setTransactionId(transactionId);
  298. orderInfo.setPayMoney(orderMoney);
  299. this.updateById(orderInfo);
  300. //账户变动及日志记录
  301. Long userId = orderInfo.getUserId();
  302. UserAccount userAccount = userAccountService.updateAccountBalanceAndLog(
  303. userId,
  304. orderMoney,
  305. SystemConstants.CHANGE_TYPE_ADD,
  306. SystemConstants.ACCOUNT_LOG_PAY_NOTE,
  307. orderInfo.getId()
  308. );
  309. //平订单超充金额(使用更新后的账户信息)
  310. orderBackTax(userId, userAccount);
  311. }
  312. private void orderBackTax(Long userId, UserAccount userAccount) {
  313. //查询超充订单
  314. List<ChargeOrderInfo> chargeOrderInfoList = chargeOrderInfoMapper.selectList(Wrappers.<ChargeOrderInfo>lambdaQuery()
  315. .eq(ChargeOrderInfo::getUserId, userId)
  316. .eq(ChargeOrderInfo::getMaspStatus, SystemConstants.STATUS_ONE)
  317. .orderByAsc(ChargeOrderInfo::getCreateTime)
  318. );
  319. if (CollUtil.isNotEmpty(chargeOrderInfoList)) {
  320. //余额减去超充金额(使用更新后的余额)
  321. BigDecimal balance = userAccount.getBalance();
  322. for (ChargeOrderInfo chargeOrderInfo : chargeOrderInfoList) {
  323. if (balance.compareTo(BigDecimal.ZERO) > 0) {
  324. BigDecimal deductAmount; // 本次扣除金额
  325. if (balance.compareTo(chargeOrderInfo.getTotalMaspMoney()) < 0) {
  326. //余额不足,部分补缴
  327. deductAmount = balance;
  328. chargeOrderInfo.setAlreadyMaspMoney(deductAmount);
  329. chargeOrderInfo.setMaspDesc("部分补缴");
  330. chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO);
  331. chargeOrderInfoMapper.updateById(chargeOrderInfo);
  332. //账户变动及日志记录
  333. userAccountService.updateAccountBalanceAndLog(
  334. userId,
  335. deductAmount.negate(),
  336. SystemConstants.CHANGE_TYPE_REDUCE,
  337. SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE,
  338. chargeOrderInfo.getId()
  339. );
  340. balance = BigDecimal.ZERO;
  341. continue;
  342. }
  343. //余额足够,完成补缴
  344. deductAmount = chargeOrderInfo.getTotalMaspMoney();
  345. balance = balance.subtract(deductAmount);
  346. chargeOrderInfo.setAlreadyMaspMoney(deductAmount);
  347. chargeOrderInfo.setMaspDesc("完成补缴");
  348. chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO);
  349. chargeOrderInfoMapper.updateById(chargeOrderInfo);
  350. //账户变动及日志记录
  351. userAccountService.updateAccountBalanceAndLog(
  352. userId,
  353. deductAmount.negate(),
  354. SystemConstants.CHANGE_TYPE_REDUCE,
  355. SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE,
  356. chargeOrderInfo.getId()
  357. );
  358. }
  359. }
  360. }
  361. }
  362. /**
  363. * 取消订单
  364. *
  365. * @param orderId
  366. * @return
  367. */
  368. @Override
  369. public String cancelOrder(String orderId) {
  370. UserOrderInfo orderInfo = this.getById(orderId);
  371. if (ObjectUtil.isNotEmpty(orderInfo)) {
  372. if (orderInfo.getOrderStatus() == 1) {
  373. log.info("修改订单:{},支付状态为已取消", orderId);
  374. orderInfo.setOrderStatus(SystemConstants.STATUS_THREE);
  375. this.updateById(orderInfo);
  376. }
  377. return "取消成功!";
  378. }
  379. return "取消失败,请刷新后重试!";
  380. }
  381. /**
  382. * 账户退款
  383. *
  384. * @return
  385. */
  386. @Override
  387. @Transactional(rollbackFor = Exception.class)
  388. public String refundOrder() throws Exception {
  389. //查询账户余额
  390. UserAccount userAccount = userAccountMapper.selectOne(Wrappers.<UserAccount>lambdaQuery().eq(UserAccount::getUserId, SecurityUtils.getUserId()).last("limit 1"));
  391. if (userAccount.getBalance().compareTo(BigDecimal.ZERO) == 0) {
  392. return "账户余额为 0";
  393. }
  394. BigDecimal refundMoney = userAccount.getBalance();
  395. //查询一年内已支付的所有券订单
  396. List<UserOrderInfo> userOrderInfoList = baseMapper.selectList(Wrappers.<UserOrderInfo>lambdaQuery()
  397. .eq(UserOrderInfo::getUserId, userAccount.getId())
  398. .eq(UserOrderInfo::getOrderStatus, SystemConstants.STATUS_TWO)
  399. .between(UserOrderInfo::getCreateTime, LocalDateTime.now().minusYears(1), LocalDateTime.now())
  400. );
  401. if (CollUtil.isEmpty(userOrderInfoList)) {
  402. log.info("当前用户一年内未支付任何券订单,无法进行退款操作!");
  403. throw new BusinessException("无法进行退款操作,请联系客服处理!");
  404. }
  405. for (UserOrderInfo userOrderInfo : userOrderInfoList) {
  406. if(refundMoney.compareTo(BigDecimal.ZERO) == 0){
  407. break;
  408. }
  409. if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) < 0) {
  410. //退款金额大于订单金额,则直接退退款金额
  411. refundOrder(userOrderInfo,refundMoney, "账户退款", SystemConstants.STATUS_ONE);
  412. //账户变动及日志记录
  413. userAccountService.updateAccountBalanceAndLog(
  414. SecurityUtils.getUserId(),
  415. refundMoney,
  416. SystemConstants.CHANGE_TYPE_REDUCE,
  417. SystemConstants.ACCOUNT_LOG_REFUND_NOTE,
  418. userOrderInfo.getId()
  419. );
  420. //修改订单状态
  421. userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR);
  422. this.updateById(userOrderInfo);
  423. refundMoney = BigDecimal.ZERO;
  424. break;
  425. }
  426. if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) > 0) {
  427. //退款金额小于订单金额,则先退订单金额
  428. refundOrder(userOrderInfo,userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()), "账户退款", SystemConstants.STATUS_ONE);
  429. //账户变动及日志记录
  430. userAccountService.updateAccountBalanceAndLog(
  431. SecurityUtils.getUserId(),
  432. userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()),
  433. SystemConstants.CHANGE_TYPE_REDUCE,
  434. SystemConstants.ACCOUNT_LOG_REFUND_NOTE,
  435. userOrderInfo.getId()
  436. );
  437. //修改订单状态
  438. userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR);
  439. this.updateById(userOrderInfo);
  440. refundMoney = refundMoney.subtract(userOrderInfo.getOrderMoney());
  441. }
  442. }
  443. return "账户退款,预计3个工作日内分一笔或多笔退还!如未收到,请联系客服!";
  444. }
  445. public void refundOrder(UserOrderInfo userOrderInfo, BigDecimal refundAmount, String reason, Integer type) {
  446. log.info("进入退款接口------>");
  447. log.info("执行操作的 原支付交易对应的商户订单号:{}", userOrderInfo.getOrderNo());
  448. //退款单号
  449. String out_refund_no = createOrderNo("TK",userOrderInfo.getId());
  450. // 创建退款订单
  451. UserRefundsOrderInfo userRefundsOrderInfo = new UserRefundsOrderInfo();
  452. userRefundsOrderInfo.setOrderId(userOrderInfo.getId());
  453. userRefundsOrderInfo.setOrderNo(userOrderInfo.getOrderNo());
  454. userRefundsOrderInfo.setOutRefundNo(out_refund_no);
  455. userRefundsOrderInfo.setReason(reason);
  456. userRefundsOrderInfo.setAmount(refundAmount);
  457. userRefundsOrderInfo.setType(type);
  458. userRefundsOrderInfo.setCreateTime(LocalDateTime.now());
  459. Map<String,Object> params = new HashMap<String,Object>();
  460. params.put("transaction_id", userOrderInfo.getTransactionId());
  461. params.put("out_trade_no", userOrderInfo.getOrderNo());//商户订单号
  462. params.put("out_refund_no", out_refund_no);//商户退款单号
  463. params.put("reason", reason);//退款原因
  464. params.put("notify_url", WechatUrlConstants.PAY_V3_REFUND_NOTIFY);//退款通知
  465. Map<String,Object> amount = new HashMap<String,Object>();
  466. amount.put("refund", amount_fee(refundAmount));//退款金额
  467. amount.put("currency", "CNY");
  468. amount.put("total", amount_fee(userOrderInfo.getOrderMoney()));//原订单金额
  469. params.put("amount", amount);
  470. // 执行请求POST 请求发送到微信退款接口
  471. Gson gson = new Gson();
  472. JsonObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_REFUND, gson.toJsonTree(params).getAsJsonObject());
  473. log.info("最终拿到的微信支付通知数据:" + res);
  474. final String status = res.get("status").getAsString();
  475. switch (status) {
  476. case "SUCCESS":
  477. log.info("订单:{},退款成功!原因:{}", userOrderInfo.getOrderNo(), reason);
  478. //修改订单状态
  479. userOrderInfo.setOrderStatus(SystemConstants.STATUS_FIVE);
  480. userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount));
  481. userOrderInfo.setRefundTime(LocalDateTime.now());
  482. this.updateById(userOrderInfo);
  483. break;
  484. case "CLOSED":
  485. log.info("退款关闭");
  486. break;
  487. case "PROCESSING":
  488. log.info("退款处理中");
  489. //修改订单状态
  490. userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount));
  491. this.updateById(userOrderInfo);
  492. break;
  493. case "ABNORMAL":
  494. log.info("订单:{},退款异常", userOrderInfo.getOrderNo());
  495. break;
  496. }
  497. userRefundsOrderInfo.setRefundId(res.get("refund_id").getAsString());
  498. userRefundsOrderInfo.setNotifyRequest(res.toString());
  499. userRefundsOrderInfo.setTransactionId(res.get("transaction_id").getAsString());
  500. userRefundsOrderInfo.setAcceptedTime(LocalDateTime.now());
  501. userRefundsOrderInfoMapper.insert(userRefundsOrderInfo);
  502. }
  503. @Override
  504. public Map<String, Object> refundCallback(HttpServletRequest request, HttpServletResponse response) {
  505. WechatRefundCallback refundCallback = new WechatRefundCallback() {
  506. @Override
  507. public void success(WechatCallbackRefundData refundData) {
  508. log.info("微信支付退款成功!");
  509. UserOrderInfo userOrderInfo = baseMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, refundData.getOrderNo()));
  510. userOrderInfo.setOrderStatus(SystemConstants.STATUS_FIVE);
  511. userOrderInfo.setRefundTime(refundData.getSuccessTime());
  512. baseMapper.updateById(userOrderInfo);
  513. UserRefundsOrderInfo userRefundsOrderInfo = userRefundsOrderInfoMapper.selectOne(Wrappers.<UserRefundsOrderInfo>lambdaQuery()
  514. .eq(UserRefundsOrderInfo::getOrderId, userOrderInfo.getId())
  515. .eq(UserRefundsOrderInfo::getRefundId, refundData.getTransactionRefundId())
  516. .last("limit 1")
  517. );
  518. userRefundsOrderInfo.setStatus(refundData.getStatus());
  519. userRefundsOrderInfo.setSuccessTime(refundData.getSuccessTime());
  520. userRefundsOrderInfoMapper.updateById(userRefundsOrderInfo);
  521. }
  522. @Override
  523. public void fail(WechatCallbackRefundData refundData) {
  524. log.info("微信支付退款失败!");
  525. UserOrderInfo userOrderInfo = baseMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, refundData.getOrderNo()));
  526. UserRefundsOrderInfo userRefundsOrderInfo = userRefundsOrderInfoMapper.selectOne(Wrappers.<UserRefundsOrderInfo>lambdaQuery()
  527. .eq(UserRefundsOrderInfo::getOrderId, userOrderInfo.getId())
  528. .eq(UserRefundsOrderInfo::getRefundId, refundData.getTransactionRefundId())
  529. .last("limit 1")
  530. );
  531. userRefundsOrderInfo.setStatus(refundData.getStatus());
  532. userRefundsOrderInfo.setSuccessTime(refundData.getSuccessTime());
  533. userRefundsOrderInfoMapper.updateById(userRefundsOrderInfo);
  534. }
  535. };
  536. Map<String, Object> result = new HashMap<>();
  537. if (lock.tryLock()) {
  538. // 2.签名验证
  539. //验签及解析返回数据
  540. JsonObject res = wechatPayV3Utils.getCallbackData(request);
  541. if (res == null) {
  542. result.put("code", "FAIL");
  543. result.put("message", "失败");
  544. return result;
  545. }
  546. log.info("最终拿到的微信支付通知数据:" + res);
  547. // 4.封装微信返回的数据
  548. WechatCallbackRefundData refundData = getRefundCallbackData(res);
  549. if ("SUCCESS".equals(refundData.getStatus())) {
  550. refundCallback.success(refundData);
  551. } else {
  552. // 特殊情况退款失败业务处理,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款
  553. refundCallback.fail(refundData);
  554. }
  555. // 5.成功应答
  556. response.setStatus(200);
  557. result.put("code", "SUCCESS");
  558. result.put("message", "成功");
  559. } else {
  560. result.put("code", "FAIL");
  561. result.put("message", "失败");
  562. }
  563. return result;
  564. }
  565. private static WechatCallbackRefundData getRefundCallbackData(JsonObject res) {
  566. WechatCallbackRefundData refundData = new WechatCallbackRefundData();
  567. String successTime = res.get("success_time").getAsString();
  568. if (StringUtils.isNoneBlank(successTime)) {
  569. refundData.setSuccessTime(successTime);
  570. }
  571. refundData.setOrderNo(res.get("out_trade_no").getAsString());
  572. refundData.setRefundId(res.get("out_refund_no").getAsString());
  573. refundData.setTransactionId(res.get("transaction_id").getAsString());
  574. refundData.setTransactionRefundId(res.get("refund_id").getAsString());
  575. refundData.setChannel(res.get("channel").getAsString());
  576. final String status = res.get("refund_status").getAsString();
  577. refundData.setStatus(status);
  578. String refundMoney = res.getAsJsonObject("amount").get("refund").getAsString();
  579. refundData.setRefundMoney(new BigDecimal(refundMoney).movePointLeft(2));
  580. log.info("refundData:{}", refundData);
  581. return refundData;
  582. }
  583. /**
  584. * 通过商户订单号查询订单在微信侧支付状态
  585. *
  586. * @param out_trade_no 发起支付时创建的商户订单号
  587. * @return null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败
  588. */
  589. public JsonObject orderQueryByOutTradeNo(String out_trade_no, String subMchId) {
  590. String url = WechatUrlConstants.PAY_V3_QUERY_OUT;
  591. url = url.replace("{out_trade_no}", WXPayUtility.urlEncode(out_trade_no));
  592. Map<String, Object> args = new HashMap<>();
  593. args.put("sub_mchid", subMchId);
  594. url = url + "?" + WXPayUtility.urlEncode(args);
  595. return wechatPayV3Utils.sendGet(url);
  596. }
  597. /**
  598. * 构建支付表单返回给前端支撑JsApi支付调用
  599. *
  600. * @param openId
  601. * @param orderNo
  602. * @param amount
  603. * @return
  604. */
  605. private Map<String, Object> payment(String openId, String orderNo, BigDecimal amount) {
  606. //15分钟超时限制
  607. Calendar calendar = Calendar.getInstance();
  608. calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) + 15);
  609. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
  610. //构建微信支付参数
  611. Map<String, Object> params = new HashMap<>();
  612. params.put("appid", WechatConstants.WECHAT_APPID); //小程序appid
  613. params.put("mchid", WechatConstants.WECHAT_MCH_ID); //商户号
  614. params.put("description", "订单业务"); //商品描述
  615. params.put("out_trade_no", orderNo); //商户订单号
  616. params.put("time_expire", sdf.format(calendar.getTime())); //交易结束时间 选填 时间到了之后将不能再支付 遵循rfc3339标准格式
  617. params.put("attach", orderNo); //附加数据 选填
  618. // 在查询API和支付通知中原样返回 可作为自定义参数使用
  619. params.put("notify_url", WechatUrlConstants.PAY_V3_NOTIFY); //支付结果异步通知接口
  620. //订单金额信息
  621. Map<String, Object> amount_json = new HashMap<>();
  622. //支付金额 单位:分
  623. // amount_json.put("total", Integer.parseInt(amount_fee(Double.valueOf("0.1"))));测试用例
  624. amount_json.put("total", Integer.parseInt(amount_fee(amount)));
  625. params.put("amount", amount_json);
  626. //支付者信息
  627. Map<String, Object> payer = new HashMap<>();
  628. //用户在小程序侧的openid
  629. payer.put("openid", openId);
  630. params.put("payer", payer);
  631. //小程序支付拉起参数
  632. Gson gson = new Gson();
  633. return wechatPay(gson.toJsonTree(params).getAsJsonObject());
  634. }
  635. /**
  636. * 小程序支付拉起
  637. *
  638. * @param params
  639. * @return
  640. * @throws ServiceException
  641. */
  642. public Map<String, Object> wechatPay(JsonObject params) throws ServiceException {
  643. //发起请求
  644. JsonObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_JSAPI, params);
  645. log.info("wechatPay res:{}", res.toString());
  646. if (StrUtil.isEmpty(res.get("prepay_id").getAsString())) {
  647. throw new ServiceException("支付发起失败");
  648. }
  649. StringBuilder sb = new StringBuilder();
  650. //返回给小程序拉起微信支付的参数
  651. Map<String, Object> result = new HashMap();
  652. // result.setAppId(WechatConstants.WECHAT_APPID); //小程序appid
  653. // sb.append(result.getAppId()).append("\n");
  654. // result.setTimeStamp((new Date().getTime() / 1000) + ""); //时间戳
  655. // sb.append(result.getTimeStamp()).append("\n");
  656. // result.setNonceStr(RandomStringUtils.randomAlphanumeric(32)); //32位随机字符串
  657. // sb.append(result.getNonceStr()).append("\n");
  658. // result.setPackageValue("prepay_id=" + res.get("prepay_id").getAsString()); //预支付id 格式为 prepay_id=xxx
  659. // sb.append(result.getPackageValue()).append("\n");
  660. // //签名
  661. // result.setPaySign(wechatPayV3Utils.signRSA(sb.toString()));
  662. // result.setSignType("RSA"); //加密方式 固定RSA
  663. // result.setOutTradeNo(params.get("out_trade_no").getAsString()); //商户订单号 此参数不是小程序拉起支付所需的参数 因此不参与签名
  664. return result;
  665. }
  666. /**
  667. * 创建商户订单号
  668. * 要求 32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
  669. * 组成 两位前缀 + 17位时间戳 + 9位id补零 + 4位随机数 合计32位
  670. *
  671. * @param head 例如 商品-SP 退款-TK 等等
  672. * @param id 用户id
  673. * @return
  674. */
  675. public String createOrderNo(String head, Long id) {
  676. StringBuilder uid = new StringBuilder(id.toString());
  677. Date date = new Date();
  678. SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
  679. int length = uid.length();
  680. for (int i = 0; i < 9 - length; i++) {
  681. uid.insert(0, "0");
  682. }
  683. return head + sdf.format(date) + uid + (int) ((Math.random() * 9 + 1) * 1000);
  684. }
  685. /**
  686. * 金额元转分字符串
  687. *
  688. * @param cny 元
  689. * @return
  690. */
  691. public String amount_fee(BigDecimal cny) {
  692. BigDecimal b2 = new BigDecimal("100");
  693. return cny.multiply(b2).setScale(0, RoundingMode.DOWN).toString();
  694. }
  695. public static String amount_fee(double price) {
  696. DecimalFormat df = new DecimalFormat("#.00");
  697. price = Double.valueOf(df.format(price));
  698. int money = (int) (price * 100);
  699. return money + "";
  700. }
  701. }