OmgCheckMacValueTest.java 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package com.ruoyi.app.utils.omg;
  2. import org.junit.jupiter.api.Test;
  3. import java.util.Comparator;
  4. import java.util.LinkedHashMap;
  5. import java.util.Map;
  6. import static org.junit.jupiter.api.Assertions.assertEquals;
  7. import static org.junit.jupiter.api.Assertions.assertFalse;
  8. import static org.junit.jupiter.api.Assertions.assertNotEquals;
  9. import static org.junit.jupiter.api.Assertions.assertTrue;
  10. class OmgCheckMacValueTest {
  11. @Test
  12. void generatesOfficialEcpayLogisticsVector() {
  13. Map<String, String> params = new LinkedHashMap<>();
  14. params.put("MerchantID", "2000933");
  15. params.put("MerchantTradeNo", "A20130312153023");
  16. params.put("MerchantTradeDate", "2013/03/12 15:30:23");
  17. params.put("LogisticsType", "CVS");
  18. params.put("LogisticsSubType", "FAMIC2C");
  19. params.put("GoodsAmount", "1000");
  20. params.put("IsCollection", "N");
  21. params.put("ServerReplyURL", "https://www.ecpay.com.tw/ServerReplyURL");
  22. params.put("SenderName", "SteveJones");
  23. params.put("ReceiverName", "MarkHarrison");
  24. params.put("ReceiverStoreID", "001779");
  25. assertEquals("754C5D1365035DA34D2CD91CC256F18C",
  26. OmgCheckMacValue.generate(params, "XBERn1YOvpM9nfZc", "h1ONHk4P4yqbl5LK", "MD5"));
  27. }
  28. @Test
  29. void verifiesSha256WithoutDependingOnHexCase() {
  30. Map<String, String> params = new LinkedHashMap<>();
  31. params.put("MerchantID", "2000132");
  32. params.put("MerchantTradeNo", "OMG12345678901234567");
  33. String signature = OmgCheckMacValue.generate(params, "5294y06JbISpM5x9", "v77hoKGq4kWxNNIS");
  34. params.put("CheckMacValue", signature.toLowerCase());
  35. assertTrue(OmgCheckMacValue.verify(params, "5294y06JbISpM5x9", "v77hoKGq4kWxNNIS"));
  36. }
  37. @Test
  38. void rejectsMalformedOrIncorrectSignature() {
  39. Map<String, String> params = new LinkedHashMap<>();
  40. params.put("MerchantID", "2000132");
  41. params.put("CheckMacValue", "not-a-sha256");
  42. assertFalse(OmgCheckMacValue.verify(params, "key", "iv"));
  43. params.put("CheckMacValue", "0".repeat(64));
  44. assertFalse(OmgCheckMacValue.verify(params, "key", "iv"));
  45. }
  46. /**
  47. * 回归 bugB(2026-08-12):OMG QueryTradeInfo 响应字段大小写混合,服务端 CheckMacValue 按
  48. * 大小写不敏感排序计算。旧实现用 Comparator.naturalOrder()(ASCII 大小写敏感)排序,混合字段下
  49. * 与 OMG 分歧 → 验签失败(生产订单 991786433092835 实测 receivedCMV==ci_inc 坐实)。
  50. *
  51. * <p>本用例构造同形响应(首字母大写的 AlipayID/CustomField1/MerchantID 与首字母小写的
  52. * amount/auth_code/card4no/eci/gwsr/process_date/red_dan 并存),先用「大小写不敏感」生成 OMG 侧
  53. * 签名,再断言 verify() 通过。修复前 verify 走 naturalOrder 会算出不同签名而失败。
  54. */
  55. @Test
  56. void verifiesQueryTradeInfoResponseWithCaseInsensitiveKeyOrder() {
  57. Map<String, String> resp = new LinkedHashMap<>();
  58. resp.put("MerchantID", "1000031");
  59. resp.put("MerchantTradeNo", "OMG10C9AA21B1814EE29");
  60. resp.put("TradeAmt", "97");
  61. resp.put("TradeDate", "2026/08/11 17:06:42");
  62. resp.put("TradeNo", "2608111706428239");
  63. resp.put("TradeStatus", "10200095");
  64. resp.put("ItemName", "order 991786433092835");
  65. resp.put("HandlingCharge", "0");
  66. resp.put("PaymentTypeChargeFee", "0");
  67. // §2 额外回传(空值),首字母大小写混合 —— 触发排序分歧的关键
  68. resp.put("AlipayID", "");
  69. resp.put("CustomField1", "");
  70. resp.put("amount", "");
  71. resp.put("auth_code", "");
  72. resp.put("card4no", "");
  73. resp.put("eci", "");
  74. resp.put("gwsr", "");
  75. resp.put("process_date", "");
  76. resp.put("red_dan", "");
  77. String key = "5294y06JbISpM5x9";
  78. String iv = "v77hoKGq4kWxNNIS";
  79. // OMG 侧签名:大小写不敏感排序,保留空值字段
  80. String omgCmv = OmgCheckMacValue.generate(resp, key, iv,
  81. String.CASE_INSENSITIVE_ORDER, false);
  82. // 前置断言:本字段集必须使两种排序产生不同签名,否则用例无法锁定该 bug
  83. String sensitiveCmv = OmgCheckMacValue.generate(resp, key, iv,
  84. Comparator.naturalOrder(), false);
  85. assertNotEquals(sensitiveCmv, omgCmv,
  86. "测试字段集必须使大小写敏感/不敏感排序产生不同签名,否则用例无效");
  87. resp.put("CheckMacValue", omgCmv);
  88. assertTrue(OmgCheckMacValue.verify(resp, key, iv),
  89. "verify 必须按大小写不敏感排序以匹配 OMG 服务端算法");
  90. }
  91. }