diff --git a/mdx-models/src/main/java/com/mx/path/model/mdx/model/MdxLogMasker.java b/mdx-models/src/main/java/com/mx/path/model/mdx/model/MdxLogMasker.java index e4ebbda7..04410531 100644 --- a/mdx-models/src/main/java/com/mx/path/model/mdx/model/MdxLogMasker.java +++ b/mdx-models/src/main/java/com/mx/path/model/mdx/model/MdxLogMasker.java @@ -52,6 +52,7 @@ private static void registerPayloadPatterns() { buildIdentificationPayloadPatterns(); buildManagedCardsPayloadPatterns(); buildOriginationPayloadPatterns(); + buildP2PTransferPayloadPatterns(); buildPaymentPayloadPatterns(); buildPayoutPayloadPatterns(); buildProfilePayloadPatterns(); @@ -274,6 +275,12 @@ private static void buildOriginationPayloadPatterns() { MDX_PAYLOAD_REGEX.add(LogValueRegex.jsonString("login_token")); } + private static void buildP2PTransferPayloadPatterns() { + // P2P Transfer - https://developer.mx.com/drafts/mdx/p2p_transfer/index.html#p2p-transfers + MDX_PAYLOAD_REGEX.add(LogValueRegex.jsonString("recipient_verification_answer")); + MDX_PAYLOAD_REGEX.add(LogValueRegex.jsonString("recipient_verification_question")); + } + private static void buildPaymentPayloadPatterns() { // Payee - https://developer.mx.com/drafts/mdx/payment/#payees-payee-fields MDX_PAYLOAD_REGEX.add(LogValueRegex.jsonString("account_number")); diff --git a/mdx-models/src/main/java/com/mx/path/model/mdx/model/p2p_transfer/RecurringP2PTransfer.java b/mdx-models/src/main/java/com/mx/path/model/mdx/model/p2p_transfer/RecurringP2PTransfer.java index 45e6384b..8e117048 100644 --- a/mdx-models/src/main/java/com/mx/path/model/mdx/model/p2p_transfer/RecurringP2PTransfer.java +++ b/mdx-models/src/main/java/com/mx/path/model/mdx/model/p2p_transfer/RecurringP2PTransfer.java @@ -7,12 +7,15 @@ import lombok.EqualsAndHashCode; import com.mx.path.model.mdx.model.MdxBase; +import com.mx.path.model.mdx.model.MdxList; +import com.mx.path.model.mdx.model.challenges.Challenge; @Data @EqualsAndHashCode(callSuper = true) public class RecurringP2PTransfer extends MdxBase { private String id; private BigDecimal amount; + private MdxList challenges; private String confirmationId; private String deliveryMethod; private String durationType; diff --git a/mdx-models/src/test/groovy/com/mx/path/model/mdx/model/MdxLogMaskerTest.groovy b/mdx-models/src/test/groovy/com/mx/path/model/mdx/model/MdxLogMaskerTest.groovy index c38788e0..b211dc75 100644 --- a/mdx-models/src/test/groovy/com/mx/path/model/mdx/model/MdxLogMaskerTest.groovy +++ b/mdx-models/src/test/groovy/com/mx/path/model/mdx/model/MdxLogMaskerTest.groovy @@ -526,6 +526,20 @@ class MdxLogMaskerTest extends Specification { "\"login_token\":\"123456789\"" || "\"login_token\":\"**MASKED**\"" } + @Unroll + def "maskPayload() masks P2P Transfer JSON fields"() { + when: + String result = MdxLogMasker.maskPayload(payload) + + then: + result == expectedResult + + where: + payload || expectedResult + "\"recipient_verification_answer\":\"an answer\"" || "\"recipient_verification_answer\":\"**MASKED**\"" + "\"recipient_verification_question\":\"a question\"" || "\"recipient_verification_question\":\"**MASKED**\"" + } + @Unroll def "maskPayload() masks Payee JSON fields"() { when: diff --git a/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/P2PTransfersController.java b/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/P2PTransfersController.java index 2861377d..1bcb2f0d 100644 --- a/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/P2PTransfersController.java +++ b/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/P2PTransfersController.java @@ -18,7 +18,14 @@ public class P2PTransfersController extends BaseController { @RequestMapping(value = "/users/{userId}/p2p_transfers", method = RequestMethod.POST, consumes = BaseController.MDX_MEDIA) public final ResponseEntity create(@RequestBody P2PTransfer p2pTransferRequest) { AccessorResponse response = gateway().p2pTransfers().create(p2pTransferRequest); - return new ResponseEntity<>(response.getResult().wrapped(), createMultiMapForResponse(response.getHeaders()), HttpStatus.OK); + + // Return 202 if there are challenges + P2PTransfer result = response.getResult(); + HttpStatus status = HttpStatus.OK; + if (result.getChallenges() != null && !result.getChallenges().isEmpty()) { + status = HttpStatus.ACCEPTED; + } + return new ResponseEntity<>(response.getResult().wrapped(), createMultiMapForResponse(response.getHeaders()), status); } @RequestMapping(value = "/users/{userId}/p2p_transfers/{id}/cancel", method = RequestMethod.PUT) @@ -42,6 +49,13 @@ public final ResponseEntity> list() { @RequestMapping(value = "/users/{userId}/p2p_transfers/{id}", method = RequestMethod.PUT, consumes = BaseController.MDX_MEDIA) public final ResponseEntity update(@PathVariable("id") String p2pTransferId, @RequestBody P2PTransfer p2pTransferRequest) { AccessorResponse response = gateway().p2pTransfers().update(p2pTransferId, p2pTransferRequest); - return new ResponseEntity<>(response.getResult().wrapped(), createMultiMapForResponse(response.getHeaders()), HttpStatus.OK); + + // Return 202 if there are challenges + P2PTransfer result = response.getResult(); + HttpStatus status = HttpStatus.OK; + if (result.getChallenges() != null && !result.getChallenges().isEmpty()) { + status = HttpStatus.ACCEPTED; + } + return new ResponseEntity<>(response.getResult().wrapped(), createMultiMapForResponse(response.getHeaders()), status); } } diff --git a/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/RecurringP2PTransfersController.java b/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/RecurringP2PTransfersController.java index fe1df3d1..516fc9be 100644 --- a/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/RecurringP2PTransfersController.java +++ b/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/RecurringP2PTransfersController.java @@ -18,7 +18,14 @@ public class RecurringP2PTransfersController extends BaseController { @RequestMapping(value = "/users/{userId}/recurring_p2p_transfers", method = RequestMethod.POST, consumes = BaseController.MDX_MEDIA) public final ResponseEntity create(@RequestBody RecurringP2PTransfer p2pTransferRequest) { AccessorResponse response = gateway().p2pTransfers().recurring().create(p2pTransferRequest); - return new ResponseEntity<>(response.getResult().wrapped(), createMultiMapForResponse(response.getHeaders()), HttpStatus.OK); + + // Return 202 if there are challenges + RecurringP2PTransfer result = response.getResult(); + HttpStatus status = HttpStatus.OK; + if (result.getChallenges() != null && !result.getChallenges().isEmpty()) { + status = HttpStatus.ACCEPTED; + } + return new ResponseEntity<>(response.getResult().wrapped(), createMultiMapForResponse(response.getHeaders()), status); } @RequestMapping(value = "/users/{userId}/recurring_p2p_transfers/{id}/cancel", method = RequestMethod.PUT) @@ -42,6 +49,13 @@ public final ResponseEntity> list() { @RequestMapping(value = "/users/{userId}/recurring_p2p_transfers/{id}", method = RequestMethod.PUT, consumes = BaseController.MDX_MEDIA) public final ResponseEntity update(@PathVariable("id") String p2pTransferId, @RequestBody RecurringP2PTransfer p2pTransferRequest) { AccessorResponse response = gateway().p2pTransfers().recurring().update(p2pTransferId, p2pTransferRequest); - return new ResponseEntity<>(response.getResult().wrapped(), createMultiMapForResponse(response.getHeaders()), HttpStatus.OK); + + // Return 202 if there are challenges + RecurringP2PTransfer result = response.getResult(); + HttpStatus status = HttpStatus.OK; + if (result.getChallenges() != null && !result.getChallenges().isEmpty()) { + status = HttpStatus.ACCEPTED; + } + return new ResponseEntity<>(response.getResult().wrapped(), createMultiMapForResponse(response.getHeaders()), status); } } diff --git a/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/P2PTransfersControllerTest.groovy b/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/P2PTransfersControllerTest.groovy index 915ae9b2..ade7763d 100644 --- a/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/P2PTransfersControllerTest.groovy +++ b/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/P2PTransfersControllerTest.groovy @@ -9,6 +9,7 @@ import com.mx.path.gateway.accessor.AccessorResponse import com.mx.path.gateway.api.Gateway import com.mx.path.gateway.api.p2p_transfer.P2PTransferGateway import com.mx.path.model.mdx.model.MdxList +import com.mx.path.model.mdx.model.challenges.Challenge import com.mx.path.model.mdx.model.p2p_transfer.P2PTransfer import org.springframework.http.HttpStatus @@ -45,6 +46,26 @@ class P2PTransfersControllerTest extends Specification { verify(p2pTransferGateway).create(p2pTransfer) || true } + def "create with challenges"() { + given: + BaseController.setGateway(gateway) + + def p2pTransfer = new P2PTransfer().tap { + setChallenges(new MdxList().tap { + add(new Challenge()) + }) + } + doReturn(new AccessorResponse().withResult(p2pTransfer)).when(p2pTransferGateway).create(p2pTransfer) + + when: + def result = subject.create(p2pTransfer) + + then: + result.statusCode == HttpStatus.ACCEPTED + result.body == p2pTransfer + verify(p2pTransferGateway).create(p2pTransfer) || true + } + def "cancel interacts with gateway"() { given: BaseController.setGateway(gateway) @@ -107,4 +128,24 @@ class P2PTransfersControllerTest extends Specification { result.body == p2pTransfer verify(p2pTransferGateway).update(id, p2pTransfer) || true } + + def "update with challenges"() { + given: + BaseController.setGateway(gateway) + def id = "transfer-1234" + def p2pTransfer = new P2PTransfer().tap { + setChallenges(new MdxList().tap { + add(new Challenge()) + }) + } + doReturn(new AccessorResponse().withResult(p2pTransfer)).when(p2pTransferGateway).update(id, p2pTransfer) + + when: + def result = subject.update(id, p2pTransfer) + + then: + result.statusCode == HttpStatus.ACCEPTED + result.body == p2pTransfer + verify(p2pTransferGateway).update(id, p2pTransfer) || true + } } diff --git a/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/RecurringP2PTransfersControllerTest.groovy b/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/RecurringP2PTransfersControllerTest.groovy index 8755f307..4af98d03 100644 --- a/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/RecurringP2PTransfersControllerTest.groovy +++ b/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/RecurringP2PTransfersControllerTest.groovy @@ -1,6 +1,5 @@ package com.mx.path.model.mdx.web.controller - import static org.mockito.Mockito.doReturn import static org.mockito.Mockito.mock import static org.mockito.Mockito.spy @@ -11,6 +10,7 @@ import com.mx.path.gateway.api.Gateway import com.mx.path.gateway.api.p2p_transfer.P2PTransferGateway import com.mx.path.gateway.api.p2p_transfer.RecurringP2PTransferGateway import com.mx.path.model.mdx.model.MdxList +import com.mx.path.model.mdx.model.challenges.Challenge import com.mx.path.model.mdx.model.p2p_transfer.RecurringP2PTransfer import org.springframework.http.HttpStatus @@ -51,6 +51,26 @@ class RecurringP2PTransfersControllerTest extends Specification { verify(recurringP2PTransferGateway).create(p2pTransfer) || true } + def "create with challenges"() { + given: + BaseController.setGateway(gateway) + + def p2pTransfer = new RecurringP2PTransfer().tap { + setChallenges(new MdxList().tap { + add(new Challenge()) + }) + } + doReturn(new AccessorResponse().withResult(p2pTransfer)).when(recurringP2PTransferGateway).create(p2pTransfer) + + when: + def result = subject.create(p2pTransfer) + + then: + result.statusCode == HttpStatus.ACCEPTED + result.body == p2pTransfer + verify(recurringP2PTransferGateway).create(p2pTransfer) || true + } + def "cancel interacts with gateway"() { given: BaseController.setGateway(gateway) @@ -113,4 +133,24 @@ class RecurringP2PTransfersControllerTest extends Specification { result.body == p2pTransfer verify(recurringP2PTransferGateway).update(id, p2pTransfer) || true } + + def "update with challenges"() { + given: + BaseController.setGateway(gateway) + def id = "transfer-1234" + def p2pTransfer = new RecurringP2PTransfer().tap { + setChallenges(new MdxList().tap { + add(new Challenge()) + }) + } + doReturn(new AccessorResponse().withResult(p2pTransfer)).when(recurringP2PTransferGateway).update(id, p2pTransfer) + + when: + def result = subject.update(id, p2pTransfer) + + then: + result.statusCode == HttpStatus.ACCEPTED + result.body == p2pTransfer + verify(recurringP2PTransferGateway).update(id, p2pTransfer) || true + } }