From b2e3e1265d3a17fddbd2b85834dd28171215113d Mon Sep 17 00:00:00 2001 From: Tess Stoddard Date: Mon, 1 Dec 2025 10:26:33 -0700 Subject: [PATCH] feat: add p2p transfer duration endpoints --- .../path/model/mdx/accessor/BaseAccessor.java | 28 ++++++++++ .../p2p_transfer/DurationBaseAccessor.java | 31 +++++++++++ .../p2p_transfer/P2PTransferBaseAccessor.java | 39 ++++++++++++++ .../mx/path/model/mdx/model/Resources.java | 10 ++++ .../mdx/model/p2p_transfer/Duration.java | 14 +++++ .../P2PTransferDurationsController.java | 21 ++++++++ .../P2PTransferDurationsControllerTest.groovy | 54 +++++++++++++++++++ 7 files changed, 197 insertions(+) create mode 100644 mdx-models/src/main/java/com/mx/path/model/mdx/accessor/p2p_transfer/DurationBaseAccessor.java create mode 100644 mdx-models/src/main/java/com/mx/path/model/mdx/accessor/p2p_transfer/P2PTransferBaseAccessor.java create mode 100644 mdx-models/src/main/java/com/mx/path/model/mdx/model/p2p_transfer/Duration.java create mode 100644 mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/P2PTransferDurationsController.java create mode 100644 mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/P2PTransferDurationsControllerTest.groovy diff --git a/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/BaseAccessor.java b/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/BaseAccessor.java index a0a23805..40cd184d 100644 --- a/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/BaseAccessor.java +++ b/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/BaseAccessor.java @@ -20,6 +20,7 @@ import com.mx.path.model.mdx.accessor.location.LocationBaseAccessor; import com.mx.path.model.mdx.accessor.managed_card.ManagedCardBaseAccessor; import com.mx.path.model.mdx.accessor.origination.OriginationBaseAccessor; +import com.mx.path.model.mdx.accessor.p2p_transfer.P2PTransferBaseAccessor; import com.mx.path.model.mdx.accessor.payment.PaymentBaseAccessor; import com.mx.path.model.mdx.accessor.payout.PayoutBaseAccessor; import com.mx.path.model.mdx.accessor.products.ProductBaseAccessor; @@ -74,6 +75,10 @@ public abstract class BaseAccessor extends Accessor { @Getter(AccessLevel.PROTECTED) private OriginationBaseAccessor originations; + @GatewayAPI + @Getter(AccessLevel.PROTECTED) + private P2PTransferBaseAccessor p2pTransfers; + @GatewayAPI @Getter(AccessLevel.PROTECTED) private PaymentBaseAccessor payments; @@ -349,6 +354,29 @@ public void setOriginations(OriginationBaseAccessor originations) { this.originations = originations; } + /** + * Accessor for p2p transfer operations + * + * @return accessor + */ + @API + public P2PTransferBaseAccessor p2pTransfers() { + if (p2pTransfers != null) { + return p2pTransfers; + } + + throw new AccessorMethodNotImplementedException(); + } + + /** + * Set p2p transfer accessor + * + * @param p2pTransfers + */ + public void setP2pTransfers(P2PTransferBaseAccessor p2pTransfers) { + this.p2pTransfers = p2pTransfers; + } + /** * Accessor for payment operations * diff --git a/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/p2p_transfer/DurationBaseAccessor.java b/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/p2p_transfer/DurationBaseAccessor.java new file mode 100644 index 00000000..5e733d7e --- /dev/null +++ b/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/p2p_transfer/DurationBaseAccessor.java @@ -0,0 +1,31 @@ +package com.mx.path.model.mdx.accessor.p2p_transfer; + +import com.mx.path.core.common.accessor.API; +import com.mx.path.core.common.accessor.AccessorMethodNotImplementedException; +import com.mx.path.core.common.gateway.GatewayAPI; +import com.mx.path.core.common.gateway.GatewayClass; +import com.mx.path.gateway.accessor.Accessor; +import com.mx.path.gateway.accessor.AccessorResponse; +import com.mx.path.model.mdx.model.MdxList; +import com.mx.path.model.mdx.model.p2p_transfer.Duration; + +/** + * Accessor base for P2P transfer duration operations + */ +@GatewayClass +@API(specificationUrl = "https://developer.mx.com/drafts/mdx/p2p_transfer/index.html#durations") +public class DurationBaseAccessor extends Accessor { + public DurationBaseAccessor() { + } + + /** + * List all P2P transfer durations + * + * @return MdxList<Duration> + */ + @GatewayAPI + @API(description = "List all durations for P2P transfers") + public AccessorResponse> list() { + throw new AccessorMethodNotImplementedException(); + } +} diff --git a/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/p2p_transfer/P2PTransferBaseAccessor.java b/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/p2p_transfer/P2PTransferBaseAccessor.java new file mode 100644 index 00000000..4f68792d --- /dev/null +++ b/mdx-models/src/main/java/com/mx/path/model/mdx/accessor/p2p_transfer/P2PTransferBaseAccessor.java @@ -0,0 +1,39 @@ +package com.mx.path.model.mdx.accessor.p2p_transfer; + +import lombok.AccessLevel; +import lombok.Getter; + +import com.mx.path.core.common.accessor.API; +import com.mx.path.core.common.gateway.GatewayAPI; +import com.mx.path.core.common.gateway.GatewayClass; +import com.mx.path.gateway.accessor.Accessor; + +/** + * Accessor base for p2p transfer operations + * + */ +@GatewayClass +@API(specificationUrl = "https://developer.mx.com/drafts/mdx/p2p_transfer/index.html#p2p-transfers") +public class P2PTransferBaseAccessor extends Accessor { + @GatewayAPI + @Getter(AccessLevel.PROTECTED) + private DurationBaseAccessor durations; + + /** + * Accessor for duration operations + * + * @return accessor + */ + @API + public DurationBaseAccessor durations() { + return durations; + } + + /** + * Sets duration accessor + * @param durations + */ + public void setDurations(DurationBaseAccessor durations) { + this.durations = durations; + } +} diff --git a/mdx-models/src/main/java/com/mx/path/model/mdx/model/Resources.java b/mdx-models/src/main/java/com/mx/path/model/mdx/model/Resources.java index d140d542..63bb51bf 100644 --- a/mdx-models/src/main/java/com/mx/path/model/mdx/model/Resources.java +++ b/mdx-models/src/main/java/com/mx/path/model/mdx/model/Resources.java @@ -61,6 +61,7 @@ import com.mx.path.model.mdx.model.ondemand.mixins.TransactionMixIn; import com.mx.path.model.mdx.model.ondemand.mixins.TransactionsPageMixin; import com.mx.path.model.mdx.model.origination.Origination; +import com.mx.path.model.mdx.model.p2p_transfer.Duration; import com.mx.path.model.mdx.model.payment.Bill; import com.mx.path.model.mdx.model.payment.Enrollment; import com.mx.path.model.mdx.model.payment.Merchant; @@ -284,6 +285,8 @@ public static void registerResources(GsonBuilder builder) { registerAccountAlertModels(builder); // Register product models registerProductModels(builder); + // Register P2P transfer models + registerP2PTransferModels(builder); } private static void registerDeviceModels(GsonBuilder builder) { @@ -380,6 +383,13 @@ private static void registerDisputesModels(GsonBuilder builder) { }.getType(), new ModelWrappableSerializer("disputed_transactions")); } + private static void registerP2PTransferModels(GsonBuilder builder) { + // Duration + builder.registerTypeAdapter(Frequency.class, new ModelWrappableSerializer("duration")); + builder.registerTypeAdapter(new TypeToken>() { + }.getType(), new ModelWrappableSerializer("durations")); + } + private static void registerPaymentsModels(GsonBuilder builder) { // Merchant builder.registerTypeAdapter(Merchant.class, new ModelWrappableSerializer("merchant")); diff --git a/mdx-models/src/main/java/com/mx/path/model/mdx/model/p2p_transfer/Duration.java b/mdx-models/src/main/java/com/mx/path/model/mdx/model/p2p_transfer/Duration.java new file mode 100644 index 00000000..0325387d --- /dev/null +++ b/mdx-models/src/main/java/com/mx/path/model/mdx/model/p2p_transfer/Duration.java @@ -0,0 +1,14 @@ +package com.mx.path.model.mdx.model.p2p_transfer; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import com.mx.path.model.mdx.model.MdxBase; + +@Data +@EqualsAndHashCode(callSuper = true) +public class Duration extends MdxBase { + private String description; + private String name; + private String type; +} diff --git a/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/P2PTransferDurationsController.java b/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/P2PTransferDurationsController.java new file mode 100644 index 00000000..b3f2c6f9 --- /dev/null +++ b/mdx-web/src/main/java/com/mx/path/model/mdx/web/controller/P2PTransferDurationsController.java @@ -0,0 +1,21 @@ +package com.mx.path.model.mdx.web.controller; + +import com.mx.path.gateway.accessor.AccessorResponse; +import com.mx.path.model.mdx.model.MdxList; +import com.mx.path.model.mdx.model.p2p_transfer.Duration; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(value = "{clientId}", produces = BaseController.MDX_MEDIA) +public class P2PTransferDurationsController extends BaseController { + @RequestMapping(value = "/users/{userId}/p2p_transfers/durations", method = RequestMethod.GET) + public final ResponseEntity> list() { + AccessorResponse> response = gateway().p2pTransfers().durations().list(); + return new ResponseEntity<>(response.getResult().wrapped(), createMultiMapForResponse(response.getHeaders()), HttpStatus.OK); + } +} diff --git a/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/P2PTransferDurationsControllerTest.groovy b/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/P2PTransferDurationsControllerTest.groovy new file mode 100644 index 00000000..03fa8700 --- /dev/null +++ b/mdx-web/src/test/groovy/com/mx/path/model/mdx/web/controller/P2PTransferDurationsControllerTest.groovy @@ -0,0 +1,54 @@ +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 +import static org.mockito.Mockito.verify + +import com.mx.path.gateway.accessor.AccessorResponse +import com.mx.path.gateway.api.Gateway +import com.mx.path.gateway.api.p2p_transfer.DurationGateway +import com.mx.path.gateway.api.p2p_transfer.P2PTransferGateway +import com.mx.path.model.mdx.model.MdxList +import com.mx.path.model.mdx.model.p2p_transfer.Duration + +import org.springframework.http.HttpStatus + +import spock.lang.Specification + +class P2PTransferDurationsControllerTest extends Specification { + P2PTransferDurationsController subject + Gateway gateway + P2PTransferGateway p2pTransferGateway + DurationGateway durationGateway + + def setup() { + subject = new P2PTransferDurationsController() + p2pTransferGateway = mock(P2PTransferGateway) + durationGateway = mock(DurationGateway) + + doReturn(durationGateway).when(p2pTransferGateway).durations() + gateway = spy(Gateway.builder().clientId("client-1234").p2pTransfers(p2pTransferGateway).build()) + } + + def cleanup() { + BaseController.clearGateway() + } + + def "list interacts with gateway"() { + given: + BaseController.setGateway(gateway) + def durations = new MdxList().tap { + add(new Duration()) + } + doReturn(new AccessorResponse>().withResult(durations)).when(durationGateway).list() + + when: + def result = subject.list() + + then: + HttpStatus.OK == result.statusCode + result.body == durations + verify(durationGateway).list() || true + } +}