OmgStoreCredentialVerifierTest.java 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. package com.ruoyi.app.omgpay;
  2. import org.junit.jupiter.api.BeforeEach;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  5. import java.net.URLEncoder;
  6. import java.nio.charset.StandardCharsets;
  7. import java.time.Clock;
  8. import java.time.Instant;
  9. import java.time.ZoneOffset;
  10. import java.util.LinkedHashMap;
  11. import java.util.Map;
  12. import java.util.Set;
  13. import static org.junit.jupiter.api.Assertions.assertEquals;
  14. import static org.junit.jupiter.api.Assertions.assertNotNull;
  15. import static org.junit.jupiter.api.Assertions.assertThrows;
  16. import static org.mockito.ArgumentMatchers.anyMap;
  17. import static org.mockito.Mockito.mock;
  18. import static org.mockito.Mockito.verify;
  19. import static org.mockito.Mockito.when;
  20. class OmgStoreCredentialVerifierTest {
  21. private static final String HASH_KEY = "5294y06JbISpM5x9";
  22. private static final String HASH_IV = "v77hoKGq4kWxNNIS";
  23. private OmgPaymentQueryGateway gateway;
  24. private OmgCheckMacSigner signer;
  25. private OmgStoreCredentialVerifier verifier;
  26. @BeforeEach
  27. void setUp() {
  28. gateway = mock(OmgPaymentQueryGateway.class);
  29. signer = new OmgCheckMacSigner();
  30. verifier = new OmgStoreCredentialVerifier(gateway, new OmgQueryResponseParser(), signer,
  31. Clock.fixed(Instant.ofEpochSecond(1_786_598_400L), ZoneOffset.UTC));
  32. }
  33. @Test
  34. void signsAStageProbeAndVerifiesEveryReturnedField() {
  35. when(gateway.query(anyMap())).thenAnswer(invocation -> signedResponse(invocation.getArgument(0)));
  36. Map<String, String> response = verifier.verify("1000031", HASH_KEY, HASH_IV);
  37. assertEquals("0", response.get("TradeStatus"));
  38. @SuppressWarnings("unchecked")
  39. var request = org.mockito.ArgumentCaptor.forClass(Map.class);
  40. verify(gateway).query(request.capture());
  41. assertEquals(Set.of("MerchantID", "MerchantTradeNo", "TimeStamp", "CheckMacValue"),
  42. request.getValue().keySet());
  43. assertEquals("1000031", request.getValue().get("MerchantID"));
  44. assertEquals("OMGVERIFY1786598400", request.getValue().get("MerchantTradeNo"));
  45. assertEquals("1786598400", request.getValue().get("TimeStamp"));
  46. LinkedHashMap<String, String> signingFields = new LinkedHashMap<>(request.getValue());
  47. String actual = signingFields.remove("CheckMacValue");
  48. assertEquals(signer.sign(signingFields, HASH_KEY, HASH_IV), actual);
  49. }
  50. @Test
  51. void rejectsAnyTamperedExtraResponseField() {
  52. when(gateway.query(anyMap())).thenAnswer(invocation ->
  53. signedResponse(invocation.getArgument(0)).replace("FutureEmptyField=", "FutureEmptyField=tampered"));
  54. assertThrows(IllegalArgumentException.class,
  55. () -> verifier.verify("1000031", HASH_KEY, HASH_IV));
  56. }
  57. @Test
  58. void springContextCreatesCredentialVerifierWithDependencies() {
  59. try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
  60. context.registerBean(OmgPaymentQueryGateway.class, () -> gateway);
  61. context.registerBean(OmgQueryResponseParser.class);
  62. context.registerBean(OmgCheckMacSigner.class);
  63. context.register(OmgStoreCredentialVerifier.class);
  64. context.refresh();
  65. assertNotNull(context.getBean(OmgStoreCredentialVerifier.class));
  66. }
  67. }
  68. private String signedResponse(Map<String, String> request) {
  69. LinkedHashMap<String, String> fields = new LinkedHashMap<>();
  70. fields.put("MerchantID", request.get("MerchantID"));
  71. fields.put("MerchantTradeNo", request.get("MerchantTradeNo"));
  72. fields.put("TradeStatus", "0");
  73. fields.put("RtnMsg", "Not found");
  74. fields.put("FutureEmptyField", "");
  75. fields.put("CheckMacValue", signer.sign(fields, HASH_KEY, HASH_IV));
  76. return fields.entrySet().stream()
  77. .map(entry -> encode(entry.getKey()) + "=" + encode(entry.getValue()))
  78. .collect(java.util.stream.Collectors.joining("&"));
  79. }
  80. private static String encode(String value) {
  81. return URLEncoder.encode(value, StandardCharsets.UTF_8);
  82. }
  83. }