|
|
@@ -757,92 +757,103 @@ public class OmgPayController extends BaseController {
|
|
|
* @param source 调用来源标记("query" 被动补单 / "scheduled" 定时补单),仅用于日志
|
|
|
* @return {@code int[2]} = {payStatus, reconciled}:payStatus 0未付/1已付/2失败;reconciled 1=本次触发补单核销
|
|
|
*/
|
|
|
- public int[] reconcileByQuery(String ddId, String source) {
|
|
|
+ public int[] reconcileByQuery(String ddId, String source) {
|
|
|
PosOrder order = posOrderService.getOne(new QueryWrapper<PosOrder>().eq("dd_id", ddId));
|
|
|
if (order == null) {
|
|
|
return new int[]{0, 0};
|
|
|
}
|
|
|
- // 订单已取消:不查 OMG、不补单
|
|
|
if (order.getState() != null && order.getState() == 4L) {
|
|
|
return new int[]{0, 0};
|
|
|
}
|
|
|
- PosOrderOmgPayment payment = paymentService.getLatestByDdId(ddId);
|
|
|
- if (payment == null) {
|
|
|
- return new int[]{0, 0};
|
|
|
- }
|
|
|
- Integer ps = payment.getPayStatus();
|
|
|
- if (Integer.valueOf(1).equals(ps)) {
|
|
|
- // 流水已付:若订单未核销(跨事务中断残留)则自愈补推订单状态;否则幂等返回。
|
|
|
- // (state==4 已在方法入口拦截,此处订单必非取消态。)
|
|
|
- if (order.getPayStatus() == null || order.getPayStatus() == 0L) {
|
|
|
+
|
|
|
+ // ① 自愈:流水已付(pay_status IN(1,3,4))但订单未核销(跨事务中断残留)→ 补推订单状态
|
|
|
+ PosOrderOmgPayment paid = paymentService.getLatestPaidByDdId(ddId);
|
|
|
+ if (paid != null) {
|
|
|
+ boolean isPaid = Integer.valueOf(1).equals(paid.getPayStatus());
|
|
|
+ if (isPaid && (order.getPayStatus() == null || order.getPayStatus() == 0L)) {
|
|
|
handlePaymentSuccess(order);
|
|
|
}
|
|
|
- return new int[]{1, 0};
|
|
|
- }
|
|
|
- if (ps != null && ps != 0) {
|
|
|
- // 2失败/3已退/4退款中:终态,不再查询
|
|
|
- return new int[]{ps == 2 ? 2 : 0, 0};
|
|
|
+ return new int[]{isPaid ? 1 : 0, 0};
|
|
|
}
|
|
|
|
|
|
- PosStoreOmg cred = storeOmgService.getCredentialByMerchantIdAndStoreId(payment.getMerchantId(), payment.getStoreId());
|
|
|
- if (cred == null) {
|
|
|
- log.error("[OMG-{}] 门店凭证不可用: ddId={}, merchantId={}, storeId={}, mtcn={}",
|
|
|
- source, ddId, payment.getMerchantId(), payment.getStoreId(), payment.getMerchantTradeNo());
|
|
|
+ // ② 遍历全量未付(pay_status=0,含历史行)queryTrade,任一已付即补单核销(补较早 MTN 如 id=26)
|
|
|
+ List<PosOrderOmgPayment> unpaid = paymentService.listUnpaidByDdId(ddId);
|
|
|
+ if (unpaid == null || unpaid.isEmpty()) {
|
|
|
return new int[]{0, 0};
|
|
|
}
|
|
|
- OmgPayConfig cfg = new OmgPayConfig(cred.getMerchantId(), cred.getHashKey(), cred.getHashIv());
|
|
|
-
|
|
|
- Map<String, String> resp;
|
|
|
- try {
|
|
|
- resp = omgPay.queryTrade(baseUrl, cfg, payment.getMerchantTradeNo());
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("[OMG-{}] queryTrade 失败(下轮重试): ddId={}, mtcn={}, err={}: {}",
|
|
|
- source, ddId, payment.getMerchantTradeNo(), e.getClass().getName(), e.getMessage());
|
|
|
- // 落库诊断:原始响应字符串已由 OmgPay 层 log.error 记录,此处记结构化异常摘要,供定位 id=26 类问题
|
|
|
- Map<String, String> errLog = new LinkedHashMap<>();
|
|
|
- errLog.put("ddId", ddId);
|
|
|
- errLog.put("mtcn", payment.getMerchantTradeNo());
|
|
|
- errLog.put("source", source);
|
|
|
- errLog.put("error", e.getClass().getName() + ": " + e.getMessage());
|
|
|
- IpnLog ipnLog = new IpnLog();
|
|
|
- ipnLog.setType("omg_query_error");
|
|
|
- ipnLog.setIpnLog(JSON.toJSONString(errLog));
|
|
|
- ipnLogService.insertIpnLog(ipnLog);
|
|
|
- return new int[]{0, 0};
|
|
|
- }
|
|
|
- String tradeStatus = resp.get("TradeStatus");
|
|
|
- String tradeNo = resp.get("TradeNo");
|
|
|
- String paymentType = resp.get("PaymentType");
|
|
|
- int respAmt = toInt(resp.get("TradeAmt"), -1);
|
|
|
- if ("1".equals(tradeStatus)) {
|
|
|
- if (tradeNo == null || tradeNo.isEmpty()
|
|
|
- || payment.getAmount() == null || respAmt != payment.getAmount()
|
|
|
- || order.getAmount() == null || order.getAmount().intValue() != payment.getAmount()) {
|
|
|
- log.error("[OMG-{}] 补单金额/字段校验失败: ddId={}, paymentAmt={}, orderAmt={}, queryAmt={}, tradeNo={}",
|
|
|
- source, ddId, payment.getAmount(), order.getAmount(), respAmt, tradeNo);
|
|
|
- orderLogHelper.logSync(ddId, 0, null, "系统", "OMG查询补单金额/字段不符,需人工核对");
|
|
|
- return new int[]{0, 0};
|
|
|
+ int lastStatus = 0;
|
|
|
+ for (PosOrderOmgPayment p : unpaid) {
|
|
|
+ PosStoreOmg cred = storeOmgService.getCredentialByMerchantIdAndStoreId(p.getMerchantId(), p.getStoreId());
|
|
|
+ if (cred == null) {
|
|
|
+ log.error("[OMG-{}] 门店凭证不可用: ddId={}, mtcn={}", source, ddId, p.getMerchantTradeNo());
|
|
|
+ continue;
|
|
|
}
|
|
|
- Date payTime = parsePayTime(resp.get("PaymentDate"));
|
|
|
- Date tradeDate = parsePayTime(resp.get("TradeDate"));
|
|
|
- if (payTime == null || tradeDate == null) {
|
|
|
- log.warn("[OMG-{}] 补单响应日期非法: ddId={}", source, ddId);
|
|
|
- return new int[]{0, 0};
|
|
|
+ OmgPayConfig cfg = new OmgPayConfig(cred.getMerchantId(), cred.getHashKey(), cred.getHashIv());
|
|
|
+ Map<String, String> resp;
|
|
|
+ try {
|
|
|
+ resp = omgPay.queryTrade(baseUrl, cfg, p.getMerchantTradeNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[OMG-{}] queryTrade 失败(下轮重试): ddId={}, mtcn={}, err={}: {}",
|
|
|
+ source, ddId, p.getMerchantTradeNo(), e.getClass().getName(), e.getMessage());
|
|
|
+ Map<String, String> errLog = new LinkedHashMap<>();
|
|
|
+ errLog.put("ddId", ddId);
|
|
|
+ errLog.put("mtcn", p.getMerchantTradeNo());
|
|
|
+ errLog.put("source", source);
|
|
|
+ errLog.put("error", e.getClass().getName() + ": " + e.getMessage());
|
|
|
+ IpnLog ipnLog = new IpnLog();
|
|
|
+ ipnLog.setType("omg_query_error");
|
|
|
+ ipnLog.setIpnLog(JSON.toJSONString(errLog));
|
|
|
+ ipnLogService.insertIpnLog(ipnLog);
|
|
|
+ // OMG 按 MerchantID 限流(HTTP 403)时停止遍历,避免连发罚(T055 令牌桶节流完整前的过渡)
|
|
|
+ if (e.getMessage() != null && e.getMessage().contains("HTTP 403")) {
|
|
|
+ log.warn("[OMG-{}] queryTrade 触发 OMG 403 限流,停止本单遍历(下轮重试): ddId={}", source, ddId);
|
|
|
+ return new int[]{lastStatus, 0};
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String tradeStatus = resp.get("TradeStatus");
|
|
|
+ if ("1".equals(tradeStatus)) {
|
|
|
+ String tradeNo = resp.get("TradeNo");
|
|
|
+ String paymentType = resp.get("PaymentType");
|
|
|
+ int respAmt = toInt(resp.get("TradeAmt"), -1);
|
|
|
+ if (tradeNo == null || tradeNo.isEmpty()
|
|
|
+ || p.getAmount() == null || respAmt != p.getAmount()
|
|
|
+ || order.getAmount() == null || order.getAmount().intValue() != p.getAmount()) {
|
|
|
+ log.error("[OMG-{}] 补单金额/字段校验失败: ddId={}, mtcn={}, paymentAmt={}, orderAmt={}, queryAmt={}, tradeNo={}",
|
|
|
+ source, ddId, p.getMerchantTradeNo(), p.getAmount(), order.getAmount(), respAmt, tradeNo);
|
|
|
+ orderLogHelper.logSync(ddId, 0, null, "系统", "OMG查询补单金额/字段不符,需人工核对");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Date payTime = parsePayTime(resp.get("PaymentDate"));
|
|
|
+ Date tradeDate = parsePayTime(resp.get("TradeDate"));
|
|
|
+ if (payTime == null || tradeDate == null) {
|
|
|
+ log.warn("[OMG-{}] 补单响应日期非法: ddId={}, mtcn={}", source, ddId, p.getMerchantTradeNo());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ applyPaidResult(p, order, tradeNo, paymentType, 1, resp.get("RtnMsg"),
|
|
|
+ null, payTime, tradeDate, auditJson(resp));
|
|
|
+ orderLogHelper.logSync(ddId, 0, null, "系统", "OMG查询补单成功");
|
|
|
+ log.info("[OMG-{}] 补单成功: ddId={}, mtcn={}, tradeNo={}", source, ddId, p.getMerchantTradeNo(), tradeNo);
|
|
|
+ return new int[]{1, 1};
|
|
|
+ } else if ("10200095".equals(tradeStatus)) {
|
|
|
+ String tradeNo = resp.get("TradeNo");
|
|
|
+ if (tradeNo == null || tradeNo.isEmpty()) {
|
|
|
+ // 从未与 OMG 建立交易(未取号)→ markFail(0→2,CAS 允许 2→1 复活)
|
|
|
+ paymentService.markFail(p.getId(), null, "OMG查询返回失败(10200095)", auditJson(resp));
|
|
|
+ orderLogHelper.logSync(ddId, 0, null, "系统", "OMG查询补单:交易失败(10200095),mtcn=" + p.getMerchantTradeNo());
|
|
|
+ lastStatus = 2;
|
|
|
+ } else {
|
|
|
+ // 已取号(可能 OMG 延迟建案/结算滞后)→ 不标失败,下轮再查
|
|
|
+ log.warn("[OMG-{}] queryTrade 返回 10200095 但已取号(tradeNo={}),不标失败: ddId={}, mtcn={}",
|
|
|
+ source, tradeNo, ddId, p.getMerchantTradeNo());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // TradeStatus=0 未付(延期支付付款前常态)或未知 → no-op,定时任务下轮再查
|
|
|
+ log.warn("[OMG-{}] queryTrade 返回未付/未知: ddId={}, mtcn={}, TradeStatus={}",
|
|
|
+ source, ddId, p.getMerchantTradeNo(), tradeStatus);
|
|
|
}
|
|
|
- applyPaidResult(payment, order, tradeNo, paymentType, 1, resp.get("RtnMsg"),
|
|
|
- null, payTime, tradeDate, auditJson(resp));
|
|
|
- orderLogHelper.logSync(ddId, 0, null, "系统", "OMG查询补单成功");
|
|
|
- log.info("[OMG-{}] 补单成功: ddId={}, tradeNo={}", source, ddId, tradeNo);
|
|
|
- return new int[]{1, 1};
|
|
|
- } else if ("10200095".equals(tradeStatus)) {
|
|
|
- paymentService.markFail(payment.getId(), null, "OMG查询返回失败(10200095)", auditJson(resp));
|
|
|
- orderLogHelper.logSync(ddId, 0, null, "系统", "OMG查询补单:交易失败(10200095)");
|
|
|
- return new int[]{2, 0};
|
|
|
- }
|
|
|
- // TradeStatus=0 未付(延期支付付款前常态)或其他未知值:保持未支付,定时任务下轮再查
|
|
|
- log.warn("[OMG-{}] queryTrade返回未付/未知状态: ddId={}, mtcn={}, TradeStatus={}",
|
|
|
- source, ddId, payment.getMerchantTradeNo(), tradeStatus);
|
|
|
- return new int[]{0, 0};
|
|
|
+ }
|
|
|
+ return new int[]{lastStatus, 0};
|
|
|
}
|
|
|
|
|
|
/**
|