Commit 3ab3cdfd821d673c5f6f43bbe315459d7ee5ba6d

Authored by huanggang
1 parent ea665fba

upgrade after testing

cashier-boss/src/main/java/com/diligrp/cashier/boss/Constants.java
@@ -6,7 +6,7 @@ public final class Constants { @@ -6,7 +6,7 @@ public final class Constants {
6 // 商户信息过期时长-单位秒 - 1个小时 6 // 商户信息过期时长-单位秒 - 1个小时
7 public static final int MERCHANT_TIMEOUT_SECONDS = 60 * 60; 7 public static final int MERCHANT_TIMEOUT_SECONDS = 60 * 60;
8 // TOKEN信息缓存Key 8 // TOKEN信息缓存Key
9 - public static final String TOKEN_REDIS_KEY = "cashier:token:%s"; 9 + public static final String TRADE_REDIS_KEY = "cashier:trade:%s";
10 // TOKEN参数名称 10 // TOKEN参数名称
11 public static final String TOKEN_PARAM_NAME = "token"; 11 public static final String TOKEN_PARAM_NAME = "token";
12 // TOKEN签名算法 12 // TOKEN签名算法
cashier-boss/src/main/java/com/diligrp/cashier/boss/service/impl/CashierDeskServiceImpl.java
@@ -56,7 +56,7 @@ public class CashierDeskServiceImpl implements ICashierDeskService { @@ -56,7 +56,7 @@ public class CashierDeskServiceImpl implements ICashierDeskService {
56 CashierOrderToken orderToken = new CashierOrderToken(merchant.getMchId(), tradeId, order.getType().getCode(), 56 CashierOrderToken orderToken = new CashierOrderToken(merchant.getMchId(), tradeId, order.getType().getCode(),
57 order.getUserId(), order.getRedirectUrl()); 57 order.getUserId(), order.getRedirectUrl());
58 58
59 - String tokenKey = String.format(Constants.TOKEN_REDIS_KEY, token); 59 + String tokenKey = String.format(Constants.TRADE_REDIS_KEY, tradeId);
60 stringRedisTemplate.opsForValue().set(tokenKey, orderToken.toString(), Constants.TOKEN_TIMEOUT_SECONDS, TimeUnit.SECONDS); 60 stringRedisTemplate.opsForValue().set(tokenKey, orderToken.toString(), Constants.TOKEN_TIMEOUT_SECONDS, TimeUnit.SECONDS);
61 MerchantParams params = merchant.getParams(); 61 MerchantParams params = merchant.getParams();
62 String paymentUrl = switch (order.getType()) { 62 String paymentUrl = switch (order.getType()) {
@@ -70,8 +70,8 @@ public class CashierDeskServiceImpl implements ICashierDeskService { @@ -70,8 +70,8 @@ public class CashierDeskServiceImpl implements ICashierDeskService {
70 70
71 @Override 71 @Override
72 public CashierOrderVO getCashierOrderByToken(String token) { 72 public CashierOrderVO getCashierOrderByToken(String token) {
73 - CashierOrderToken.decode(token, cashierDeskProperties.getSecretKey());  
74 - String tokenKey = String.format(Constants.TOKEN_REDIS_KEY, token); 73 + long tradeId = CashierOrderToken.decode(token, cashierDeskProperties.getSecretKey());
  74 + String tokenKey = String.format(Constants.TRADE_REDIS_KEY, tradeId);
75 String payload = stringRedisTemplate.opsForValue().get(tokenKey); 75 String payload = stringRedisTemplate.opsForValue().get(tokenKey);
76 if (ObjectUtils.isEmpty(payload)) { 76 if (ObjectUtils.isEmpty(payload)) {
77 throw new BossServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "TOKEN超时过期,不能完成支付"); 77 throw new BossServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "TOKEN超时过期,不能完成支付");
@@ -83,9 +83,6 @@ public class CashierDeskServiceImpl implements ICashierDeskService { @@ -83,9 +83,6 @@ public class CashierDeskServiceImpl implements ICashierDeskService {
83 throw new BossServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "收银台订单已完成,不能进行支付"); 83 throw new BossServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "收银台订单已完成,不能进行支付");
84 } 84 }
85 List<PaymentPipeline> pipelines = paymentPipelineManager.listPipelines(orderToken.getMchId(), PaymentPipeline.class); 85 List<PaymentPipeline> pipelines = paymentPipelineManager.listPipelines(orderToken.getMchId(), PaymentPipeline.class);
86 - if (pipelines.isEmpty()) {  
87 - throw new BossServiceException(ErrorCode.ILLEGAL_ARGUMENT_ERROR, "商户无可用的支付通道");  
88 - }  
89 List<CashierOrderVO.PaymentPipeline> pipelineList = pipelines.stream().map(pipeline -> 86 List<CashierOrderVO.PaymentPipeline> pipelineList = pipelines.stream().map(pipeline ->
90 new CashierOrderVO.PaymentPipeline(pipeline.pipelineId(), pipeline.supportedChannel())).toList(); 87 new CashierOrderVO.PaymentPipeline(pipeline.pipelineId(), pipeline.supportedChannel())).toList();
91 return new CashierOrderVO(orderToken.getUserId(), orderToken.getRedirectUrl(), pipelineList); 88 return new CashierOrderVO(orderToken.getUserId(), orderToken.getRedirectUrl(), pipelineList);
@@ -102,7 +99,7 @@ public class CashierDeskServiceImpl implements ICashierDeskService { @@ -102,7 +99,7 @@ public class CashierDeskServiceImpl implements ICashierDeskService {
102 OnlinePaymentStatus paymentStatus = cashierPaymentService.doPayment(payment); 99 OnlinePaymentStatus paymentStatus = cashierPaymentService.doPayment(payment);
103 // 只要提交了收银台支付,无论是否支付成功,都使token失效,收银台页面将无法重新打开 100 // 只要提交了收银台支付,无论是否支付成功,都使token失效,收银台页面将无法重新打开
104 String token = CashierOrderToken.encode(Long.valueOf(payment.getTradeId()), cashierDeskProperties.getSecretKey()); 101 String token = CashierOrderToken.encode(Long.valueOf(payment.getTradeId()), cashierDeskProperties.getSecretKey());
105 - String tokenKey = String.format(Constants.TOKEN_REDIS_KEY, token); 102 + String tokenKey = String.format(Constants.TRADE_REDIS_KEY, token);
106 stringRedisTemplate.delete(tokenKey); 103 stringRedisTemplate.delete(tokenKey);
107 return paymentStatus; 104 return paymentStatus;
108 } 105 }
cashier-pipeline/src/main/java/com/diligrp/cashier/pipeline/Constants.java
@@ -18,4 +18,7 @@ public final class Constants { @@ -18,4 +18,7 @@ public final class Constants {
18 public static final long TEN_MINUTES = 10 * ONE_MINUTE; 18 public static final long TEN_MINUTES = 10 * ONE_MINUTE;
19 19
20 public static final long ONE_SECOND = 1000; 20 public static final long ONE_SECOND = 1000;
  21 +
  22 + // 支付通道最小超时时间(单位秒), 一分钟
  23 + public static final long MIN_PIPELINE_TIMEOUT = 60;
21 } 24 }
cashier-pipeline/src/main/java/com/diligrp/cashier/pipeline/client/WechatDirectHttpClient.java
@@ -240,7 +240,7 @@ public class WechatDirectHttpClient extends WechatHttpClient { @@ -240,7 +240,7 @@ public class WechatDirectHttpClient extends WechatHttpClient {
240 params.put("mchid", wechatConfig.getMchId()); 240 params.put("mchid", wechatConfig.getMchId());
241 params.put("description", request.getGoods()); 241 params.put("description", request.getGoods());
242 params.put("out_trade_no", request.getPaymentId()); 242 params.put("out_trade_no", request.getPaymentId());
243 - params.put("time_expire", DateUtils.formatDateTime(request.getWhen().plusMinutes(WechatConstants.PAY_DURATION_MINS), 243 + params.put("time_expire", DateUtils.formatDateTime(request.getWhen().plusSeconds(request.getTimeout()),
244 WechatConstants.RFC3339_FORMAT)); 244 WechatConstants.RFC3339_FORMAT));
245 params.put("notify_url", notifyUri); 245 params.put("notify_url", notifyUri);
246 Map<String, Object> amount = new LinkedHashMap<>(); 246 Map<String, Object> amount = new LinkedHashMap<>();
@@ -258,7 +258,7 @@ public class WechatDirectHttpClient extends WechatHttpClient { @@ -258,7 +258,7 @@ public class WechatDirectHttpClient extends WechatHttpClient {
258 params.put("mchid", wechatConfig.getMchId()); 258 params.put("mchid", wechatConfig.getMchId());
259 params.put("description", request.getGoods()); 259 params.put("description", request.getGoods());
260 params.put("out_trade_no", request.getPaymentId()); 260 params.put("out_trade_no", request.getPaymentId());
261 - params.put("time_expire", DateUtils.formatDateTime(request.getWhen().plusMinutes(WechatConstants.PAY_DURATION_MINS), 261 + params.put("time_expire", DateUtils.formatDateTime(request.getWhen().plusSeconds(request.getTimeout()),
262 WechatConstants.RFC3339_FORMAT)); 262 WechatConstants.RFC3339_FORMAT));
263 params.put("notify_url", notifyUri); 263 params.put("notify_url", notifyUri);
264 Map<String, Object> amount = new LinkedHashMap<>(); 264 Map<String, Object> amount = new LinkedHashMap<>();
cashier-pipeline/src/main/java/com/diligrp/cashier/pipeline/client/WechatPartnerHttpClient.java
@@ -256,7 +256,7 @@ public class WechatPartnerHttpClient extends WechatHttpClient { @@ -256,7 +256,7 @@ public class WechatPartnerHttpClient extends WechatHttpClient {
256 params.put("sub_mchid", subMchId); 256 params.put("sub_mchid", subMchId);
257 params.put("description", request.getGoods()); 257 params.put("description", request.getGoods());
258 params.put("out_trade_no", request.getPaymentId()); 258 params.put("out_trade_no", request.getPaymentId());
259 - params.put("time_expire", DateUtils.formatDateTime(request.getWhen().plusMinutes(WechatConstants.PAY_DURATION_MINS), 259 + params.put("time_expire", DateUtils.formatDateTime(request.getWhen().plusSeconds(request.getTimeout()),
260 WechatConstants.RFC3339_FORMAT)); 260 WechatConstants.RFC3339_FORMAT));
261 params.put("notify_url", notifyUri); 261 params.put("notify_url", notifyUri);
262 Map<String, Object> amount = new LinkedHashMap<>(); 262 Map<String, Object> amount = new LinkedHashMap<>();
@@ -278,7 +278,7 @@ public class WechatPartnerHttpClient extends WechatHttpClient { @@ -278,7 +278,7 @@ public class WechatPartnerHttpClient extends WechatHttpClient {
278 params.put("sub_mchid", subMchId); 278 params.put("sub_mchid", subMchId);
279 params.put("description", request.getGoods()); 279 params.put("description", request.getGoods());
280 params.put("out_trade_no", request.getPaymentId()); 280 params.put("out_trade_no", request.getPaymentId());
281 - params.put("time_expire", DateUtils.formatDateTime(request.getWhen().plusMinutes(WechatConstants.PAY_DURATION_MINS), 281 + params.put("time_expire", DateUtils.formatDateTime(request.getWhen().plusSeconds(request.getTimeout()),
282 WechatConstants.RFC3339_FORMAT)); 282 WechatConstants.RFC3339_FORMAT));
283 params.put("notify_url", notifyUri); 283 params.put("notify_url", notifyUri);
284 Map<String, Object> amount = new LinkedHashMap<>(); 284 Map<String, Object> amount = new LinkedHashMap<>();
cashier-pipeline/src/main/java/com/diligrp/cashier/pipeline/domain/MiniProPrepayRequest.java
@@ -5,13 +5,21 @@ import java.time.LocalDateTime; @@ -5,13 +5,21 @@ import java.time.LocalDateTime;
5 public class MiniProPrepayRequest extends OnlinePaymentRequest { 5 public class MiniProPrepayRequest extends OnlinePaymentRequest {
6 // 小程序openId 6 // 小程序openId
7 private final String openId; 7 private final String openId;
  8 + // 超时时间-秒
  9 + private final Long timeout;
8 10
9 - public MiniProPrepayRequest(String paymentId, long amount, String goods, String description, LocalDateTime when, String openId) { 11 + public MiniProPrepayRequest(String paymentId, long amount, String goods, String description, LocalDateTime when,
  12 + String openId, Long timeout) {
10 super(paymentId, amount, goods, description, when); 13 super(paymentId, amount, goods, description, when);
11 this.openId = openId; 14 this.openId = openId;
  15 + this.timeout = timeout;
12 } 16 }
13 17
14 public String getOpenId() { 18 public String getOpenId() {
15 return openId; 19 return openId;
16 } 20 }
  21 +
  22 + public Long getTimeout() {
  23 + return timeout;
  24 + }
17 } 25 }
cashier-pipeline/src/main/java/com/diligrp/cashier/pipeline/domain/NativePrepayRequest.java
@@ -3,7 +3,16 @@ package com.diligrp.cashier.pipeline.domain; @@ -3,7 +3,16 @@ package com.diligrp.cashier.pipeline.domain;
3 import java.time.LocalDateTime; 3 import java.time.LocalDateTime;
4 4
5 public class NativePrepayRequest extends OnlinePaymentRequest { 5 public class NativePrepayRequest extends OnlinePaymentRequest {
6 - public NativePrepayRequest(String paymentId, long amount, String goods, String description, LocalDateTime when) { 6 + // 超时时间-秒
  7 + private final Long timeout;
  8 +
  9 + public NativePrepayRequest(String paymentId, long amount, String goods, String description, LocalDateTime when,
  10 + Long timeout) {
7 super(paymentId, amount, goods, description, when); 11 super(paymentId, amount, goods, description, when);
  12 + this.timeout = timeout;
  13 + }
  14 +
  15 + public Long getTimeout() {
  16 + return timeout;
8 } 17 }
9 } 18 }
cashier-pipeline/src/main/java/com/diligrp/cashier/pipeline/type/OutPaymentType.java
@@ -13,6 +13,8 @@ import java.util.stream.Stream; @@ -13,6 +13,8 @@ import java.util.stream.Stream;
13 */ 13 */
14 public enum OutPaymentType implements IEnumType { 14 public enum OutPaymentType implements IEnumType {
15 15
  16 + NOP("未知方式", 0),
  17 +
16 WXPAY("微信支付", 10), 18 WXPAY("微信支付", 10),
17 19
18 ALIPAY("支付宝支付", 11), 20 ALIPAY("支付宝支付", 11),
cashier-pipeline/src/main/java/com/diligrp/cashier/pipeline/util/WechatConstants.java
@@ -52,6 +52,4 @@ public class WechatConstants { @@ -52,6 +52,4 @@ public class WechatConstants {
52 public static final String REFUND_PROCESSING = "PROCESSING"; // 退款处理中 52 public static final String REFUND_PROCESSING = "PROCESSING"; // 退款处理中
53 public static final String REFUND_ABNORMAL = "ABNORMAL"; // 退款异常 53 public static final String REFUND_ABNORMAL = "ABNORMAL"; // 退款异常
54 54
55 - // 预支付订单有效期持续时间,超过这个时间将不能支付  
56 - public static final int PAY_DURATION_MINS = 8;  
57 } 55 }
cashier-shared/src/main/java/com/diligrp/cashier/shared/util/DateUtils.java
@@ -8,6 +8,7 @@ import java.time.LocalDate; @@ -8,6 +8,7 @@ import java.time.LocalDate;
8 import java.time.LocalDateTime; 8 import java.time.LocalDateTime;
9 import java.time.ZoneOffset; 9 import java.time.ZoneOffset;
10 import java.time.format.DateTimeFormatter; 10 import java.time.format.DateTimeFormatter;
  11 +import java.time.temporal.ChronoUnit;
11 import java.util.Date; 12 import java.util.Date;
12 13
13 /** 14 /**
@@ -55,8 +56,7 @@ public class DateUtils { @@ -55,8 +56,7 @@ public class DateUtils {
55 56
56 public static LocalDateTime addDays(long amount) { 57 public static LocalDateTime addDays(long amount) {
57 LocalDateTime localDateTime = LocalDateTime.now(); 58 LocalDateTime localDateTime = LocalDateTime.now();
58 - localDateTime.plusDays(amount);  
59 - return localDateTime; 59 + return localDateTime.plusDays(amount);
60 } 60 }
61 61
62 public static String format(Date date, String format) { 62 public static String format(Date date, String format) {
@@ -134,4 +134,14 @@ public class DateUtils { @@ -134,4 +134,14 @@ public class DateUtils {
134 public static long timestampInSeconds() { 134 public static long timestampInSeconds() {
135 return Instant.now().getEpochSecond(); 135 return Instant.now().getEpochSecond();
136 } 136 }
  137 +
  138 + /**
  139 + * 两个时间点间隔的秒数
  140 + *
  141 + * 2026-01-07 10:00:00 - 2026-01-07 10:00:30, 返回 30
  142 + * 2026-01-07 10:00:30 - 2026-01-07 10:00:00, 返回 -30
  143 + */
  144 + public static long secondsDiff(LocalDateTime startTime, LocalDateTime endTime) {
  145 + return ChronoUnit.SECONDS.between(startTime, endTime);
  146 + }
137 } 147 }
cashier-trade/src/main/java/com/diligrp/cashier/trade/service/impl/CashierPaymentServiceImpl.java
@@ -145,7 +145,8 @@ public class CashierPaymentServiceImpl implements ICashierPaymentService { @@ -145,7 +145,8 @@ public class CashierPaymentServiceImpl implements ICashierPaymentService {
145 .type(TradeType.TRADE).paymentId(paymentId).channelId(onlinePipeline.supportedChannel()) 145 .type(TradeType.TRADE).paymentId(paymentId).channelId(onlinePipeline.supportedChannel())
146 .payType(PaymentType.MINI_PRO).pipelineId(onlinePipeline.pipelineId()).goods(trade.getGoods()) 146 .payType(PaymentType.MINI_PRO).pipelineId(onlinePipeline.pipelineId()).goods(trade.getGoods())
147 .amount(trade.getAmount()).payerId(request.getOpenId()).outTradeNo(response.getOutTradeNo()) 147 .amount(trade.getAmount()).payerId(request.getOpenId()).outTradeNo(response.getOutTradeNo())
148 - .state(response.getState()).version(0).createdTime(now).modifiedTime(now).build(); 148 + .outPayType(OutPaymentType.NOP).finishTime(null).state(response.getState())
  149 + .notifyUrl(trade.getNotifyUrl()).description(null).version(0).createdTime(now).modifiedTime(now).build();
149 onlinePaymentDao.insertOnlinePayment(payment); 150 onlinePaymentDao.insertOnlinePayment(payment);
150 return response; 151 return response;
151 } else if (pipeline instanceof DiliCardPipeline cardPipeline) { // 园区卡支付通道 152 } else if (pipeline instanceof DiliCardPipeline cardPipeline) { // 园区卡支付通道
@@ -160,10 +161,10 @@ public class CashierPaymentServiceImpl implements ICashierPaymentService { @@ -160,10 +161,10 @@ public class CashierPaymentServiceImpl implements ICashierPaymentService {
160 OnlinePayment payment = OnlinePayment.builder().outMchId(outMchId).tradeId(trade.getTradeId()) 161 OnlinePayment payment = OnlinePayment.builder().outMchId(outMchId).tradeId(trade.getTradeId())
161 .type(TradeType.TRADE).paymentId(paymentId).channelId(cardPipeline.supportedChannel()) 162 .type(TradeType.TRADE).paymentId(paymentId).channelId(cardPipeline.supportedChannel())
162 .payType(PaymentType.DIRECT).pipelineId(cardPipeline.pipelineId()).goods(trade.getGoods()) 163 .payType(PaymentType.DIRECT).pipelineId(cardPipeline.pipelineId()).goods(trade.getGoods())
163 - .amount(trade.getAmount()).payerId(response.getPayerId())  
164 - .finishTime(response.getWhen()).outTradeNo(response.getOutTradeNo())  
165 - .outPayType(response.getOutPayType()).state(response.getState()).notifyUrl(trade.getNotifyUrl())  
166 - .description(response.getMessage()).version(0).createdTime(now).modifiedTime(now).build(); 164 + .amount(trade.getAmount()).payerId(response.getPayerId()).outTradeNo(response.getOutTradeNo())
  165 + .outPayType(response.getOutPayType()).finishTime(response.getWhen()).state(response.getState())
  166 + .notifyUrl(trade.getNotifyUrl()).description(response.getMessage()).version(0)
  167 + .createdTime(now).modifiedTime(now).build();
167 onlinePaymentDao.insertOnlinePayment(payment); 168 onlinePaymentDao.insertOnlinePayment(payment);
168 169
169 TradeStateDTO tradeStateDTO = TradeStateDTO.of(trade.getTradeId(), TradeState.SUCCESS, 170 TradeStateDTO tradeStateDTO = TradeStateDTO.of(trade.getTradeId(), TradeState.SUCCESS,
cashier-trade/src/main/java/com/diligrp/cashier/trade/util/MiniProPaymentConverter.java
@@ -3,6 +3,7 @@ package com.diligrp.cashier.trade.util; @@ -3,6 +3,7 @@ package com.diligrp.cashier.trade.util;
3 import com.diligrp.cashier.pipeline.Constants; 3 import com.diligrp.cashier.pipeline.Constants;
4 import com.diligrp.cashier.pipeline.domain.MiniProPrepayRequest; 4 import com.diligrp.cashier.pipeline.domain.MiniProPrepayRequest;
5 import com.diligrp.cashier.shared.codec.IConverter; 5 import com.diligrp.cashier.shared.codec.IConverter;
  6 +import com.diligrp.cashier.shared.util.DateUtils;
6 import com.diligrp.cashier.trade.domain.CashierPayment; 7 import com.diligrp.cashier.trade.domain.CashierPayment;
7 import com.diligrp.cashier.trade.model.TradeOrder; 8 import com.diligrp.cashier.trade.model.TradeOrder;
8 import org.springframework.util.Assert; 9 import org.springframework.util.Assert;
@@ -31,8 +32,11 @@ public class MiniProPaymentConverter implements IConverter&lt;CashierPayment, MiniP @@ -31,8 +32,11 @@ public class MiniProPaymentConverter implements IConverter&lt;CashierPayment, MiniP
31 String openId = (String) params.get(Constants.PARAM_OPEN_ID); 32 String openId = (String) params.get(Constants.PARAM_OPEN_ID);
32 Assert.notNull(openId, "params.openId missed"); 33 Assert.notNull(openId, "params.openId missed");
33 34
  35 + // 根据Trade超时时间,计算得出第三方支付通道的超时时间
  36 + long seconds = DateUtils.secondsDiff(tradeOrder.getCreatedTime(), when);
  37 + long timeout = Math.max(tradeOrder.getTimeout() - seconds, Constants.MIN_PIPELINE_TIMEOUT);
34 MiniProPrepayRequest prepayRequest = new MiniProPrepayRequest(paymentId, tradeOrder.getMaxAmount(), 38 MiniProPrepayRequest prepayRequest = new MiniProPrepayRequest(paymentId, tradeOrder.getMaxAmount(),
35 - tradeOrder.getGoods(), tradeOrder.getDescription(), when, openId); 39 + tradeOrder.getGoods(), tradeOrder.getDescription(), when, openId, timeout);
36 prepayRequest.putParams(params); 40 prepayRequest.putParams(params);
37 return prepayRequest; 41 return prepayRequest;
38 } 42 }