|
@@ -100,6 +100,8 @@ public class OmgPayController extends BaseController {
|
|
|
private IPosOrderOmgRefundService refundService;
|
|
private IPosOrderOmgRefundService refundService;
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private OrderLifecycleService orderLifecycleService;
|
|
private OrderLifecycleService orderLifecycleService;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private PaymentCreateGuardService paymentCreateGuardService;
|
|
|
|
|
|
|
|
@Value("${omg.base-url}")
|
|
@Value("${omg.base-url}")
|
|
|
private String baseUrl;
|
|
private String baseUrl;
|
|
@@ -110,6 +112,10 @@ public class OmgPayController extends BaseController {
|
|
|
@Value("${omg.payment-info-url}")
|
|
@Value("${omg.payment-info-url}")
|
|
|
private String paymentInfoUrl;
|
|
private String paymentInfoUrl;
|
|
|
|
|
|
|
|
|
|
+ /** create 复用新鲜期(分钟):窗口内且 trade_no 为空的活跃行复用 MTN,超期则轮换新建(T058 防堆积,stage 实测调整)。 */
|
|
|
|
|
+ @Value("${omg.create.reuse-fresh-minutes:3}")
|
|
|
|
|
+ private int reuseFreshMinutes;
|
|
|
|
|
+
|
|
|
// ============================ US1:发起 AIO 幕前支付 ============================
|
|
// ============================ US1:发起 AIO 幕前支付 ============================
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -126,6 +132,15 @@ public class OmgPayController extends BaseController {
|
|
|
if (invalidOrderRequest(request)) {
|
|
if (invalidOrderRequest(request)) {
|
|
|
return error(MessageUtils.message("no.order.id.error"));
|
|
return error(MessageUtils.message("no.order.id.error"));
|
|
|
}
|
|
}
|
|
|
|
|
+ return paymentCreateGuardService == null ? createUnderLock(token, request)
|
|
|
|
|
+ : paymentCreateGuardService.withLock(request.getOrderid(),
|
|
|
|
|
+ () -> createUnderLock(token, request));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private AjaxResult createUnderLock(String token, OmgOrderRequest request) {
|
|
|
|
|
+ if (invalidOrderRequest(request)) {
|
|
|
|
|
+ return error(MessageUtils.message("no.order.id.error"));
|
|
|
|
|
+ }
|
|
|
String orderid = request.getOrderid();
|
|
String orderid = request.getOrderid();
|
|
|
String userId;
|
|
String userId;
|
|
|
try {
|
|
try {
|
|
@@ -144,6 +159,9 @@ public class OmgPayController extends BaseController {
|
|
|
if (order.getUserId() == null || !userId.equals(String.valueOf(order.getUserId()))) {
|
|
if (order.getUserId() == null || !userId.equals(String.valueOf(order.getUserId()))) {
|
|
|
return error("无权操作该订单");
|
|
return error("无权操作该订单");
|
|
|
}
|
|
}
|
|
|
|
|
+ if (!PAY_TYPE_OMG.equals(order.getPayType())) {
|
|
|
|
|
+ return error("订单支付方式不是 OMG");
|
|
|
|
|
+ }
|
|
|
if (order.getState() != null && order.getState() == 4L) {
|
|
if (order.getState() != null && order.getState() == 4L) {
|
|
|
return error("订单已取消,不可重新支付");
|
|
return error("订单已取消,不可重新支付");
|
|
|
}
|
|
}
|
|
@@ -164,7 +182,7 @@ public class OmgPayController extends BaseController {
|
|
|
}
|
|
}
|
|
|
OmgPayConfig cfg = new OmgPayConfig(cred.getMerchantId(), cred.getHashKey(), cred.getHashIv());
|
|
OmgPayConfig cfg = new OmgPayConfig(cred.getMerchantId(), cred.getHashKey(), cred.getHashIv());
|
|
|
|
|
|
|
|
- String merchantTradeNo = createPaymentAttempt(order, cred);
|
|
|
|
|
|
|
+ String merchantTradeNo = resolveMerchantTradeNo(order, cred);
|
|
|
|
|
|
|
|
// OMG AIO 参数(contracts/api.md §A1/B1);InvoiceMark=N 固定(发票走 ezPay);EncryptType=1 固定(SHA256)
|
|
// OMG AIO 参数(contracts/api.md §A1/B1);InvoiceMark=N 固定(发票走 ezPay);EncryptType=1 固定(SHA256)
|
|
|
SimpleDateFormat fmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
|
SimpleDateFormat fmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
|
@@ -204,6 +222,25 @@ public class OmgPayController extends BaseController {
|
|
|
return success(form);
|
|
return success(form);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 解析本次发起用的 MerchantTradeNo(T058 防堆积核心)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>优先复用新鲜期内活跃未付且未取号(trade_no IS NULL)的旧 MTN(连点/重发起命中 → 不新建流水);
|
|
|
|
|
+ * 未命中则先把旧行轮换为历史(is_active 1→0,pay_status 不动,保留接迟到 notify 与审计),再新建活跃行。
|
|
|
|
|
+ * 必须在 create 的 @Transactional 与 PaymentCreateGuardService 分布式锁内调用。
|
|
|
|
|
+ */
|
|
|
|
|
+ private String resolveMerchantTradeNo(PosOrder order, PosStoreOmg cred) {
|
|
|
|
|
+ String ddId = String.valueOf(order.getDdId());
|
|
|
|
|
+ PosOrderOmgPayment reusable = paymentService.getActiveForReuse(ddId, reuseFreshMinutes);
|
|
|
|
|
+ if (reusable != null) {
|
|
|
|
|
+ log.info("[OMG] create reuse orderid={}, merchantTradeNo={} (fresh within {}min, trade_no null)",
|
|
|
|
|
+ order.getDdId(), reusable.getMerchantTradeNo(), reuseFreshMinutes);
|
|
|
|
|
+ return reusable.getMerchantTradeNo();
|
|
|
|
|
+ }
|
|
|
|
|
+ paymentService.markActiveHistorical(ddId);
|
|
|
|
|
+ return createPaymentAttempt(order, cred);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* MerchantTradeNo 生成:OMG 要求全平台永久唯一、≤20 字元、英数大小写混合,且不可复用。
|
|
* MerchantTradeNo 生成:OMG 要求全平台永久唯一、≤20 字元、英数大小写混合,且不可复用。
|
|
|
* 使用 "OMG" + UUID 随机片段,并依靠数据库唯一索引与冲突重试兜底;ddId 由支付流水反查。
|
|
* 使用 "OMG" + UUID 随机片段,并依靠数据库唯一索引与冲突重试兜底;ddId 由支付流水反查。
|