|
|
@@ -14,6 +14,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.security.KeyPair;
|
|
|
import java.security.KeyPairGenerator;
|
|
|
+import java.time.LocalDateTime;
|
|
|
import java.util.Base64;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.Map;
|
|
|
@@ -266,7 +267,10 @@ class HarNotifyServiceTest {
|
|
|
|
|
|
service.handle(paidNotify());
|
|
|
|
|
|
- // 通知报文不含实收/手续费,这些字段由主动查询补齐,不能被通知抹掉
|
|
|
+ // 这份报文里没有任何金额键 ⇒ 已有的实收金额不能被抹掉。
|
|
|
+ // 原注释写的是「通知报文不含实收/手续费」,**前半句已被证伪**(2026-09-28 实测
|
|
|
+ // 它带 actual_money);没被证伪的是这条不变式:**没送的那格不许被清空**。
|
|
|
+ // 手续费/渠道成本至今没在任何一份报文里出现过。
|
|
|
assertEquals(new BigDecimal("12.20"), o.getRcvAmount());
|
|
|
}
|
|
|
|
|
|
@@ -279,4 +283,89 @@ class HarNotifyServiceTest {
|
|
|
|
|
|
verify(callbackService).notifyEnterprise(o);
|
|
|
}
|
|
|
+
|
|
|
+ // ==================== 上游实际字段名(2026-09-28 实测) ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 2026-09-28 那条真实通知的报文,键名一个没改(只省掉 sign —— {@link #signed} 会
|
|
|
+ * 按上游同一套规则重签)。来源是 {@code HarNotifyService} 自己打的那行 WARN。
|
|
|
+ *
|
|
|
+ * 三处与规范对不上,都是这一次读数出来的:
|
|
|
+ * <pre>
|
|
|
+ * platform_trade_no ⇒ 实际叫 alipayOrderNo / alipay_order_no
|
|
|
+ * trade_time ⇒ 实际叫 tradeDate / trade_date
|
|
|
+ * 实收金额 ⇒ 实际叫 actual_money(只有 snake_case 这一份,没有孪生)
|
|
|
+ * </pre>
|
|
|
+ * 其余字段上游几乎都发 camelCase + snake_case **两份**,值一致。
|
|
|
+ */
|
|
|
+ private Map<String, String> realPaidNotify() {
|
|
|
+ Map<String, String> raw = new LinkedHashMap<>();
|
|
|
+ raw.put("actual_money", "0.01");
|
|
|
+ raw.put("alipayOrderNo", "2026092823001417741424011978");
|
|
|
+ raw.put("alipay_order_no", "2026092823001417741424011978");
|
|
|
+ raw.put("merchantCode", "142357140143751171");
|
|
|
+ raw.put("merchant_code", "142357140143751171");
|
|
|
+ raw.put("orderNo", "1156891538702077952");
|
|
|
+ raw.put("order_no", "1156891538702077952");
|
|
|
+ raw.put("status", "1");
|
|
|
+ raw.put("subject", "测试0928");
|
|
|
+ raw.put("thirdOrderNo", "HAR1301886673700786176");
|
|
|
+ raw.put("third_order_no", "HAR1301886673700786176");
|
|
|
+ raw.put("totalAmount", "0.01");
|
|
|
+ raw.put("total_amount", "0.01");
|
|
|
+ raw.put("tradeDate", "2026-09-28 09:54:51");
|
|
|
+ raw.put("trade_date", "2026-09-28 09:54:51");
|
|
|
+ return signed(raw);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void handle_realUpstreamNames_landPlatformTradeNoTradeTimeAndRcvAmount() {
|
|
|
+ HarOrderEntity o = order("HAR1301886673700786176", HarOrderStatus.WAIT_PAY);
|
|
|
+ when(orderService.getByOrderNo("HAR1301886673700786176")).thenReturn(o);
|
|
|
+
|
|
|
+ assertEquals("success", service.handle(realPaidNotify()));
|
|
|
+
|
|
|
+ assertEquals(HarOrderStatus.PAID, o.getTradeStatus());
|
|
|
+ assertEquals("1156891538702077952", o.getUpstreamOrderNo());
|
|
|
+ assertEquals("2026092823001417741424011978", o.getPlatformTradeNo());
|
|
|
+ // 直接比 LocalDateTime:不带时区(换台机器不该红),也不踩 toString 的坑 ——
|
|
|
+ // 秒为 0 时 LocalDateTime.toString() 会把秒整段省掉(实测 10:30:00 打成 10:30)
|
|
|
+ assertEquals(LocalDateTime.of(2026, 9, 28, 9, 54, 51), o.getTradeTime().toLocalDateTime());
|
|
|
+ assertEquals(new BigDecimal("0.01"), o.getRcvAmount());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void handle_camelCaseOnlyReport_isAcceptedToo() {
|
|
|
+ Map<String, String> raw = new LinkedHashMap<>();
|
|
|
+ raw.put("actual_money", "0.01");
|
|
|
+ raw.put("alipayOrderNo", "PT-9");
|
|
|
+ raw.put("orderNo", "UP-9");
|
|
|
+ raw.put("status", "1");
|
|
|
+ raw.put("thirdOrderNo", "HAR1");
|
|
|
+ raw.put("tradeDate", "2026-09-28 09:54:51");
|
|
|
+
|
|
|
+ HarOrderEntity o = order("HAR1", HarOrderStatus.WAIT_PAY);
|
|
|
+ when(orderService.getByOrderNo("HAR1")).thenReturn(o);
|
|
|
+
|
|
|
+ assertEquals("success", service.handle(signed(raw)));
|
|
|
+
|
|
|
+ assertEquals("UP-9", o.getUpstreamOrderNo());
|
|
|
+ assertEquals("PT-9", o.getPlatformTradeNo());
|
|
|
+ assertEquals(LocalDateTime.of(2026, 9, 28, 9, 54, 51), o.getTradeTime().toLocalDateTime());
|
|
|
+ assertEquals(new BigDecimal("0.01"), o.getRcvAmount());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void handle_documentedNamesStillAccepted() {
|
|
|
+ HarOrderEntity o = order("HAR1", HarOrderStatus.WAIT_PAY);
|
|
|
+ when(orderService.getByOrderNo("HAR1")).thenReturn(o);
|
|
|
+
|
|
|
+ service.handle(paidNotify());
|
|
|
+
|
|
|
+ // 规范里的 platform_trade_no / trade_time 不能删:实测两套名字上游都发,
|
|
|
+ // 没有任何读数说过哪一套会被撤掉 —— 按「只留本次实测的那套」去删,
|
|
|
+ // 就是又一次拿推理当读数,而这份规范已经错过三次。
|
|
|
+ assertEquals("PT-1", o.getPlatformTradeNo());
|
|
|
+ assertEquals(LocalDateTime.of(2026, 9, 16, 10, 30), o.getTradeTime().toLocalDateTime());
|
|
|
+ }
|
|
|
}
|