From 85036c8e905fd1e88f13ec362ca6dfcc20461bc3 Mon Sep 17 00:00:00 2001 From: Andrea Di Lisio Date: Tue, 10 Sep 2024 16:36:44 +0200 Subject: [PATCH] feat(ACL-162): supports for verified payins (#310) --- gradle.properties | 2 +- .../entities/beneficiary/MerchantAccount.java | 3 ++ .../verification/AutomatedVerification.java | 45 ++++++++++++++++ .../entities/verification/Verification.java | 44 ++++++++++++++++ .../acceptance/PaymentsAcceptanceTests.java | 51 +++++++++++++++++++ .../integration/PaymentsIntegrationTests.java | 46 +++++++++++++++++ .../verification/VerificationTests.java | 29 +++++++++++ ...ayment_by_id.bank_transfer.authorized.json | 7 ++- 8 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/truelayer/java/payments/entities/verification/AutomatedVerification.java create mode 100644 src/main/java/com/truelayer/java/payments/entities/verification/Verification.java create mode 100644 src/test/java/com/truelayer/java/payments/entities/verification/VerificationTests.java diff --git a/gradle.properties b/gradle.properties index 6c37d81f..cb557fa5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Main properties group=com.truelayer archivesBaseName=truelayer-java -version=13.1.2 +version=13.2.0 # Artifacts properties sonatype_repository_url=https://s01.oss.sonatype.org/service/local/ diff --git a/src/main/java/com/truelayer/java/payments/entities/beneficiary/MerchantAccount.java b/src/main/java/com/truelayer/java/payments/entities/beneficiary/MerchantAccount.java index c9e1deb4..28a018aa 100644 --- a/src/main/java/com/truelayer/java/payments/entities/beneficiary/MerchantAccount.java +++ b/src/main/java/com/truelayer/java/payments/entities/beneficiary/MerchantAccount.java @@ -1,5 +1,6 @@ package com.truelayer.java.payments.entities.beneficiary; +import com.truelayer.java.payments.entities.verification.Verification; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -15,4 +16,6 @@ public class MerchantAccount extends Beneficiary { private String accountHolderName; private String reference; + + private Verification verification; } diff --git a/src/main/java/com/truelayer/java/payments/entities/verification/AutomatedVerification.java b/src/main/java/com/truelayer/java/payments/entities/verification/AutomatedVerification.java new file mode 100644 index 00000000..74bf9608 --- /dev/null +++ b/src/main/java/com/truelayer/java/payments/entities/verification/AutomatedVerification.java @@ -0,0 +1,45 @@ +package com.truelayer.java.payments.entities.verification; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Value; + +@Getter +@Value +@EqualsAndHashCode(callSuper = false) +public class AutomatedVerification extends Verification { + Verification.Type type = Type.AUTOMATED; + + boolean remitterName; + + boolean remitterDateOfBirth; + + @JsonIgnore + public static AutomatedVerificationBuilder builder() { + return new AutomatedVerificationBuilder(); + } + + /** + * Custom builder for the AutomatedVerification class that prevents setting remitter flags to false + */ + public static class AutomatedVerificationBuilder { + private boolean remitterName; + + private boolean remitterDateOfBirth; + + public AutomatedVerification.AutomatedVerificationBuilder withRemitterName() { + this.remitterName = true; + return this; + } + + public AutomatedVerification.AutomatedVerificationBuilder withRemitterDateOfBirth() { + this.remitterDateOfBirth = true; + return this; + } + + public AutomatedVerification build() { + return new AutomatedVerification(remitterName, remitterDateOfBirth); + } + } +} diff --git a/src/main/java/com/truelayer/java/payments/entities/verification/Verification.java b/src/main/java/com/truelayer/java/payments/entities/verification/Verification.java new file mode 100644 index 00000000..e6d40e73 --- /dev/null +++ b/src/main/java/com/truelayer/java/payments/entities/verification/Verification.java @@ -0,0 +1,44 @@ +package com.truelayer.java.payments.entities.verification; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonValue; +import com.truelayer.java.TrueLayerException; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = AutomatedVerification.class) +@JsonSubTypes({@JsonSubTypes.Type(value = AutomatedVerification.class, name = "automated")}) +@ToString +@EqualsAndHashCode +@Getter +public abstract class Verification { + @JsonIgnore + public abstract Verification.Type getType(); + + @JsonIgnore + public boolean isAutomated() { + return this instanceof AutomatedVerification; + } + + @JsonIgnore + public AutomatedVerification asAutomated() { + if (!isAutomated()) { + throw new TrueLayerException( + String.format("Verification is of type %s.", this.getClass().getSimpleName())); + } + return (AutomatedVerification) this; + } + + @Getter + @RequiredArgsConstructor + public enum Type { + AUTOMATED("automated"); + + @JsonValue + private final String type; + } +} diff --git a/src/test/java/com/truelayer/java/acceptance/PaymentsAcceptanceTests.java b/src/test/java/com/truelayer/java/acceptance/PaymentsAcceptanceTests.java index 11fd5015..9e5652be 100644 --- a/src/test/java/com/truelayer/java/acceptance/PaymentsAcceptanceTests.java +++ b/src/test/java/com/truelayer/java/acceptance/PaymentsAcceptanceTests.java @@ -35,6 +35,8 @@ import com.truelayer.java.payments.entities.providerselection.ProviderSelection; import com.truelayer.java.payments.entities.providerselection.UserSelectedProviderSelection; import com.truelayer.java.payments.entities.schemeselection.SchemeSelection; +import com.truelayer.java.payments.entities.verification.AutomatedVerification; +import com.truelayer.java.payments.entities.verification.Verification; import com.truelayer.java.versioninfo.LibraryInfoLoader; import java.net.URI; import java.nio.charset.StandardCharsets; @@ -118,6 +120,47 @@ public void itShouldCreateAPaymentWithSignupPlusIntention() { assertNotError(createPaymentResponse); } + @ParameterizedTest + @DisplayName("It should create a payment with automated verification") + @MethodSource("provideAutomatedVerifications") + @SneakyThrows + public void itShouldCreateAPaymentWithAutomatedVerification(Verification verification) { + CurrencyCode currency = CurrencyCode.GBP; + MerchantAccount account = getMerchantAccount(currency); + CreatePaymentRequest.CreatePaymentRequestBuilder builder = CreatePaymentRequest.builder() + .amountInMinor(100) + .currency(currency) + .paymentMethod(PaymentMethod.bankTransfer() + .providerSelection(ProviderSelection.preselected() + .providerId(PROVIDER_ID) + .build()) + .beneficiary(Beneficiary.merchantAccount() + .merchantAccountId(account.getId()) + .reference(UUID.randomUUID().toString()) + .verification(verification) + .build()) + .build()) + .user(User.builder() + .name("Andrea Di Lisio") + .email("andrea@truelayer.com") + .dateOfBirth(LocalDate.now()) + .address(Address.builder() + .addressLine1("1 Hardwick Street") + .city("London") + .state("Greater London") + .zip("EC1R 4RB") + .countryCode("GB") + .build()) + .build()); + + ApiResponse createPaymentResponse = tlClient.payments() + .createPayment(buildPaymentRequestWithProviderSelection( + buildPreselectedProviderSelection(), CurrencyCode.GBP, null, null)) + .get(); + + assertNotError(createPaymentResponse); + } + @Test @DisplayName("It should create and get by id a payment with preselected provider") @SneakyThrows @@ -736,4 +779,12 @@ private static AuthorizationFlowResponse startAuthorizationFlowWithRetry(String } return mapper.readValue(responseBody, AuthorizationFlowResponse.class); } + + private static Stream provideAutomatedVerifications() { + return Stream.of( + Arguments.of(AutomatedVerification.builder().withRemitterName().build()), + Arguments.of(AutomatedVerification.builder() + .withRemitterDateOfBirth() + .build())); + } } diff --git a/src/test/java/com/truelayer/java/integration/PaymentsIntegrationTests.java b/src/test/java/com/truelayer/java/integration/PaymentsIntegrationTests.java index f07d1c1b..8c076311 100644 --- a/src/test/java/com/truelayer/java/integration/PaymentsIntegrationTests.java +++ b/src/test/java/com/truelayer/java/integration/PaymentsIntegrationTests.java @@ -19,6 +19,7 @@ import com.truelayer.java.http.entities.Headers; import com.truelayer.java.http.entities.ProblemDetails; import com.truelayer.java.payments.entities.*; +import com.truelayer.java.payments.entities.beneficiary.MerchantAccount; import com.truelayer.java.payments.entities.paymentdetail.PaymentDetail; import com.truelayer.java.payments.entities.paymentdetail.Status; import com.truelayer.java.payments.entities.paymentmethod.PaymentMethod; @@ -26,7 +27,9 @@ import com.truelayer.java.payments.entities.providerselection.ProviderSelection; import com.truelayer.java.payments.entities.providerselection.UserSelectedProviderSelection; import com.truelayer.java.payments.entities.schemeselection.SchemeSelection; +import com.truelayer.java.payments.entities.verification.AutomatedVerification; import java.util.Collections; +import java.util.UUID; import java.util.stream.Stream; import lombok.SneakyThrows; import org.junit.jupiter.api.*; @@ -99,6 +102,41 @@ public void shouldCreateAPaymentWithAdditionalProductIntention() { .withRequestBody(matchingJsonPath("$.related_products", equalToJson("{\"signup_plus\": {}}")))); } + @DisplayName("It should create a payment with automated verification") + @MethodSource("provideAutomatedVerifications") + @ParameterizedTest() + @SneakyThrows + public void shouldCreateAPaymentWithAutomatedVerification(AutomatedVerification verification) { + RequestStub.New() + .method("post") + .path(urlPathEqualTo("/connect/token")) + .status(200) + .bodyFile("auth/200.access_token.json") + .build(); + + CreatePaymentRequest paymentRequest = CreatePaymentRequest.builder() + .paymentMethod(PaymentMethod.bankTransfer() + .beneficiary(MerchantAccount.merchantAccount() + .verification(verification) + .merchantAccountId(UUID.randomUUID().toString()) + .build()) + .build()) + .amountInMinor(100) + .relatedProducts(RelatedProducts.builder() + .signupPlus(Collections.emptyMap()) + .build()) + .build(); + + tlClient.payments().createPayment(paymentRequest).get(); + + verifyGeneratedToken(Collections.singletonList(PAYMENTS)); + verify(postRequestedFor(urlPathEqualTo("/payments")) + .withRequestBody(matchingJsonPath( + "$.payment_method.beneficiary.verification", + equalToJson("{\"type\": \"automated\", \"remitter_name\": " + verification.isRemitterName() + + ", \"remitter_date_of_birth\": " + verification.isRemitterDateOfBirth() + "}")))); + } + @DisplayName("It should create payment with") @ParameterizedTest(name = "scheme_selection={0} and expected allow_remitter_fee={1}") @MethodSource("provideSchemeSelectionTestParameters") @@ -516,4 +554,12 @@ private static Stream provideSchemeSelectionTestParameters() { .build(), false)); } + + private static Stream provideAutomatedVerifications() { + return Stream.of( + Arguments.of(AutomatedVerification.builder().withRemitterName().build()), + Arguments.of(AutomatedVerification.builder() + .withRemitterDateOfBirth() + .build())); + } } diff --git a/src/test/java/com/truelayer/java/payments/entities/verification/VerificationTests.java b/src/test/java/com/truelayer/java/payments/entities/verification/VerificationTests.java new file mode 100644 index 00000000..d78cf549 --- /dev/null +++ b/src/test/java/com/truelayer/java/payments/entities/verification/VerificationTests.java @@ -0,0 +1,29 @@ +package com.truelayer.java.payments.entities.verification; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class VerificationTests { + @Test + @DisplayName("It should yield true if instance is of type AutomatedVerification") + public void shouldYieldTrueIfAutomatedVerification() { + Verification sut = + AutomatedVerification.builder().withRemitterDateOfBirth().build(); + + assertTrue(sut.isAutomated()); + } + + @Test + @DisplayName("It should convert to an instance of class AutomatedVerification") + public void shouldConvertToAutomatedVerification() { + Verification sut = AutomatedVerification.builder() + .withRemitterName() + .withRemitterDateOfBirth() + .build(); + + assertDoesNotThrow(sut::asAutomated); + } +} diff --git a/src/test/resources/__files/payments/200.get_payment_by_id.bank_transfer.authorized.json b/src/test/resources/__files/payments/200.get_payment_by_id.bank_transfer.authorized.json index a222ffff..f87f79dd 100644 --- a/src/test/resources/__files/payments/200.get_payment_by_id.bank_transfer.authorized.json +++ b/src/test/resources/__files/payments/200.get_payment_by_id.bank_transfer.authorized.json @@ -37,7 +37,12 @@ "type":"merchant_account", "merchant_account_id":"e83c4c20-b2ad-4b73-8a32-***", "account_holder_name":"john smith", - "reference": "a-reference" + "reference": "a-reference", + "verification": { + "type": "automated", + "remitter_date_of_birth": true, + "remitter_name": false + } } }, "created_at":"2022-01-17T17:13:18.214924Z",