From 1058d1a32b0f0901c430099dc434ba3cf28fa30b Mon Sep 17 00:00:00 2001 From: Andrea Di Lisio Date: Fri, 20 Dec 2024 15:12:00 +0100 Subject: [PATCH] feat(ACL-251): Adds method to generate SU+ Authentication URI for FI payments (#332) --- .github/pull_request_template.md | 1 + build.gradle | 2 +- gradle.properties | 2 +- .../java/signupplus/ISignupPlusApi.java | 10 ++-- .../java/signupplus/ISignupPlusHandler.java | 4 ++ .../java/signupplus/SignupPlusHandler.java | 7 +++ .../entities/GenerateAuthUriRequest.java | 16 +++++++ .../entities/GenerateAuthUriResponse.java | 11 +++++ .../acceptance/SignupPlusAcceptanceTests.java | 35 ++++++++++++-- .../SignupPlusIntegrationTests.java | 47 +++++++++++++++---- .../signupplus/SignupPlusHandlerTests.java | 13 ++++- .../signup_plus/200.generate_auth_uri.json | 3 ++ 12 files changed, 131 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/truelayer/java/signupplus/entities/GenerateAuthUriRequest.java create mode 100644 src/main/java/com/truelayer/java/signupplus/entities/GenerateAuthUriResponse.java create mode 100644 src/test/resources/__files/signup_plus/200.generate_auth_uri.json diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index afa48ab5..ce04185e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -15,6 +15,7 @@ Please select multiple options if required. # Checklist: +- [ ] I have updated the `gradle.properties` file with the new version - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code where necessary diff --git a/build.gradle b/build.gradle index 27677723..e9de11f1 100644 --- a/build.gradle +++ b/build.gradle @@ -108,7 +108,7 @@ dependencies { implementation group: 'org.tinylog', name: 'tinylog-impl', version: tinyLogVersion // JUnit test framework. - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.11.3' + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.11.4' // Mocking libraries testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.14.2' diff --git a/gradle.properties b/gradle.properties index b63fa437..7a39c856 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Main properties group=com.truelayer archivesBaseName=truelayer-java -version=16.2.0 +version=16.3.0 # Artifacts properties sonatype_repository_url=https://s01.oss.sonatype.org/service/local/ diff --git a/src/main/java/com/truelayer/java/signupplus/ISignupPlusApi.java b/src/main/java/com/truelayer/java/signupplus/ISignupPlusApi.java index ca3c0487..6cf731df 100644 --- a/src/main/java/com/truelayer/java/signupplus/ISignupPlusApi.java +++ b/src/main/java/com/truelayer/java/signupplus/ISignupPlusApi.java @@ -2,11 +2,11 @@ import com.truelayer.java.entities.RequestScopes; import com.truelayer.java.http.entities.ApiResponse; +import com.truelayer.java.signupplus.entities.GenerateAuthUriRequest; +import com.truelayer.java.signupplus.entities.GenerateAuthUriResponse; import com.truelayer.java.signupplus.entities.UserData; import java.util.concurrent.CompletableFuture; -import retrofit2.http.GET; -import retrofit2.http.Query; -import retrofit2.http.Tag; +import retrofit2.http.*; public interface ISignupPlusApi { @@ -20,4 +20,8 @@ public interface ISignupPlusApi { @GET("/signup-plus/payments") CompletableFuture> getUserDataByPayment( @Tag RequestScopes scopes, @Query("payment_id") String paymentId); + + @POST("/signup-plus/authuri") + CompletableFuture> generateAuthUri( + @Tag RequestScopes scopes, @Body GenerateAuthUriRequest request); } diff --git a/src/main/java/com/truelayer/java/signupplus/ISignupPlusHandler.java b/src/main/java/com/truelayer/java/signupplus/ISignupPlusHandler.java index 64ebd68f..58cbe502 100644 --- a/src/main/java/com/truelayer/java/signupplus/ISignupPlusHandler.java +++ b/src/main/java/com/truelayer/java/signupplus/ISignupPlusHandler.java @@ -1,10 +1,14 @@ package com.truelayer.java.signupplus; import com.truelayer.java.http.entities.ApiResponse; +import com.truelayer.java.signupplus.entities.GenerateAuthUriRequest; +import com.truelayer.java.signupplus.entities.GenerateAuthUriResponse; import com.truelayer.java.signupplus.entities.UserData; import java.util.concurrent.CompletableFuture; public interface ISignupPlusHandler { CompletableFuture> getUserDataByPayment(String paymentId); + + CompletableFuture> generateAuthUri(GenerateAuthUriRequest request); } diff --git a/src/main/java/com/truelayer/java/signupplus/SignupPlusHandler.java b/src/main/java/com/truelayer/java/signupplus/SignupPlusHandler.java index ca766b68..291e0f2e 100644 --- a/src/main/java/com/truelayer/java/signupplus/SignupPlusHandler.java +++ b/src/main/java/com/truelayer/java/signupplus/SignupPlusHandler.java @@ -5,6 +5,8 @@ import com.truelayer.java.IAuthenticatedHandler; import com.truelayer.java.entities.RequestScopes; import com.truelayer.java.http.entities.ApiResponse; +import com.truelayer.java.signupplus.entities.GenerateAuthUriRequest; +import com.truelayer.java.signupplus.entities.GenerateAuthUriResponse; import com.truelayer.java.signupplus.entities.UserData; import java.util.concurrent.CompletableFuture; import lombok.Builder; @@ -26,4 +28,9 @@ public RequestScopes getRequestScopes() { public CompletableFuture> getUserDataByPayment(String paymentId) { return signupPlusApi.getUserDataByPayment(getRequestScopes(), paymentId); } + + @Override + public CompletableFuture> generateAuthUri(GenerateAuthUriRequest request) { + return signupPlusApi.generateAuthUri(getRequestScopes(), request); + } } diff --git a/src/main/java/com/truelayer/java/signupplus/entities/GenerateAuthUriRequest.java b/src/main/java/com/truelayer/java/signupplus/entities/GenerateAuthUriRequest.java new file mode 100644 index 00000000..a5299ed6 --- /dev/null +++ b/src/main/java/com/truelayer/java/signupplus/entities/GenerateAuthUriRequest.java @@ -0,0 +1,16 @@ +package com.truelayer.java.signupplus.entities; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Builder +@Getter +@ToString +@EqualsAndHashCode +public class GenerateAuthUriRequest { + private String paymentId; + + private String state; +} diff --git a/src/main/java/com/truelayer/java/signupplus/entities/GenerateAuthUriResponse.java b/src/main/java/com/truelayer/java/signupplus/entities/GenerateAuthUriResponse.java new file mode 100644 index 00000000..0b5a40ff --- /dev/null +++ b/src/main/java/com/truelayer/java/signupplus/entities/GenerateAuthUriResponse.java @@ -0,0 +1,11 @@ +package com.truelayer.java.signupplus.entities; + +import java.net.URI; +import lombok.ToString; +import lombok.Value; + +@ToString +@Value +public class GenerateAuthUriResponse { + URI authUri; +} diff --git a/src/test/java/com/truelayer/java/acceptance/SignupPlusAcceptanceTests.java b/src/test/java/com/truelayer/java/acceptance/SignupPlusAcceptanceTests.java index 648caff8..74b26e87 100644 --- a/src/test/java/com/truelayer/java/acceptance/SignupPlusAcceptanceTests.java +++ b/src/test/java/com/truelayer/java/acceptance/SignupPlusAcceptanceTests.java @@ -5,6 +5,8 @@ import com.truelayer.java.TestUtils; import com.truelayer.java.entities.*; +import com.truelayer.java.entities.accountidentifier.AccountIdentifier; +import com.truelayer.java.entities.accountidentifier.IbanAccountIdentifier; import com.truelayer.java.entities.accountidentifier.SortCodeAccountNumberAccountIdentifier; import com.truelayer.java.http.entities.ApiResponse; import com.truelayer.java.merchantaccounts.entities.MerchantAccount; @@ -14,6 +16,8 @@ import com.truelayer.java.payments.entities.paymentmethod.PaymentMethod; import com.truelayer.java.payments.entities.providerselection.ProviderSelection; import com.truelayer.java.payments.entities.schemeselection.preselected.SchemeSelection; +import com.truelayer.java.signupplus.entities.GenerateAuthUriRequest; +import com.truelayer.java.signupplus.entities.GenerateAuthUriResponse; import com.truelayer.java.signupplus.entities.UserData; import java.net.URI; import java.time.LocalDate; @@ -45,10 +49,34 @@ public void itShouldAuthorizeAUkPaymentAndThenQueryTheAssociatedIdentityData() { assertNotError(userDataResponse); } + @Test + @DisplayName("It should create a payment and generate an auth URI via Signup+ in FI") + @SneakyThrows + public void itShouldAuthorizeAFinPaymentAndThenGenerateAnAuthUri() { + // Create and authorize a payment + String paymentId = createAndAuthorizePayment("mock-payments-fi-redirect", CurrencyCode.EUR, RETURN_URI); + waitForPaymentStatusUpdate(tlClient, paymentId, Status.EXECUTED); + + ApiResponse generateAuthUriResponse = tlClient.signupPlus() + .generateAuthUri( + GenerateAuthUriRequest.builder().paymentId(paymentId).build()) + .get(); + assertNotError(generateAuthUriResponse); + } + @SneakyThrows private String createAndAuthorizePayment(String providerId, CurrencyCode currencyCode, URI returnUri) { MerchantAccount account = getMerchantAccount(currencyCode); + AccountIdentifier accountIdentifier = SortCodeAccountNumberAccountIdentifier.builder() + .accountNumber("31510604") + .sortCode("100000") + .build(); + if (currencyCode == CurrencyCode.EUR) { + accountIdentifier = + IbanAccountIdentifier.iban().iban("FI4950009420028730").build(); + } + CreatePaymentRequest paymentRequest = CreatePaymentRequest.builder() .amountInMinor(ThreadLocalRandom.current().nextInt(50, 500)) .currency(currencyCode) @@ -58,11 +86,8 @@ private String createAndAuthorizePayment(String providerId, CurrencyCode currenc .schemeSelection( SchemeSelection.instantPreferred().build()) .remitter(Remitter.builder() - .accountHolderName("Andrea Di Lisio") - .accountIdentifier(SortCodeAccountNumberAccountIdentifier.builder() - .accountNumber("12345678") - .sortCode("123456") - .build()) + .accountHolderName("John Doe") + .accountIdentifier(accountIdentifier) .build()) .build()) .beneficiary(Beneficiary.merchantAccount() diff --git a/src/test/java/com/truelayer/java/integration/SignupPlusIntegrationTests.java b/src/test/java/com/truelayer/java/integration/SignupPlusIntegrationTests.java index 7949f95e..1f3a5333 100644 --- a/src/test/java/com/truelayer/java/integration/SignupPlusIntegrationTests.java +++ b/src/test/java/com/truelayer/java/integration/SignupPlusIntegrationTests.java @@ -3,12 +3,12 @@ import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.truelayer.java.Constants.Scopes.SIGNUP_PLUS; import static com.truelayer.java.TestUtils.assertNotError; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.truelayer.java.TestUtils.RequestStub; import com.truelayer.java.http.entities.ApiResponse; -import com.truelayer.java.signupplus.entities.Address; -import com.truelayer.java.signupplus.entities.Sex; -import com.truelayer.java.signupplus.entities.UserData; +import com.truelayer.java.signupplus.entities.*; import java.util.Collections; import lombok.SneakyThrows; import org.junit.jupiter.api.Assertions; @@ -51,9 +51,8 @@ public void shouldGetUserDataByPaymentUk() { Assertions.assertEquals("Holmes", response.getData().getLastName().orElseThrow()); Assertions.assertEquals( "1854-01-06", response.getData().getDateOfBirth().orElseThrow()); - Assertions.assertTrue(response.getData().getSex().isEmpty()); - Assertions.assertTrue( - response.getData().getNationalIdentificationNumber().isEmpty()); + assertTrue(response.getData().getSex().isEmpty()); + assertTrue(response.getData().getNationalIdentificationNumber().isEmpty()); Address address = response.getData().getAddress().orElseThrow(); Assertions.assertEquals("221B Baker St", address.getAddressLine1().orElseThrow()); Assertions.assertEquals("Flat 2", address.getAddressLine2().orElseThrow()); @@ -89,7 +88,7 @@ public void shouldGetUserDataByPaymentFinland() { verifyGeneratedToken(Collections.singletonList(SIGNUP_PLUS)); assertNotError(response); - Assertions.assertTrue(response.getData().getTitle().isEmpty()); + assertTrue(response.getData().getTitle().isEmpty()); Assertions.assertEquals("Väinö", response.getData().getFirstName().orElseThrow()); Assertions.assertEquals("Tunnistus", response.getData().getLastName().orElseThrow()); Assertions.assertEquals( @@ -100,10 +99,40 @@ public void shouldGetUserDataByPaymentFinland() { response.getData().getNationalIdentificationNumber().orElseThrow()); Address address = response.getData().getAddress().orElseThrow(); Assertions.assertEquals("Sepänkatu 11 A 5", address.getAddressLine1().orElseThrow()); - Assertions.assertTrue(address.getAddressLine2().isEmpty()); + assertTrue(address.getAddressLine2().isEmpty()); Assertions.assertEquals("KUOPIO", address.getCity().orElseThrow()); - Assertions.assertTrue(address.getState().isEmpty()); + assertTrue(address.getState().isEmpty()); Assertions.assertEquals("70100", address.getZip().orElseThrow()); Assertions.assertEquals("FI", address.getCountry().orElseThrow()); } + + @Test + @DisplayName("It should generate an auth URI for a payment in Finland") + @SneakyThrows + public void shouldGenerateAuthUriByPaymentFinland() { + String jsonResponseFile = "signup_plus/200.generate_auth_uri.json"; + RequestStub.New() + .method("post") + .path(urlPathEqualTo("/connect/token")) + .status(200) + .bodyFile("auth/200.access_token.json") + .build(); + RequestStub.New() + .method("post") + .path(urlPathEqualTo("/signup-plus/authuri")) + .withAuthorization() + .status(200) + .bodyFile(jsonResponseFile) + .build(); + + ApiResponse response = tlClient.signupPlus() + .generateAuthUri( + GenerateAuthUriRequest.builder().paymentId(A_PAYMENT_ID).build()) + .get(); + + verifyGeneratedToken(Collections.singletonList(SIGNUP_PLUS)); + assertNotError(response); + assertNotNull(response.getData().getAuthUri()); + assertTrue(response.getData().getAuthUri().toString().contains("truelayer.com")); + } } diff --git a/src/test/java/com/truelayer/java/signupplus/SignupPlusHandlerTests.java b/src/test/java/com/truelayer/java/signupplus/SignupPlusHandlerTests.java index 49b9ae52..1d29dcd0 100644 --- a/src/test/java/com/truelayer/java/signupplus/SignupPlusHandlerTests.java +++ b/src/test/java/com/truelayer/java/signupplus/SignupPlusHandlerTests.java @@ -5,6 +5,7 @@ import com.truelayer.java.Constants; import com.truelayer.java.entities.RequestScopes; +import com.truelayer.java.signupplus.entities.GenerateAuthUriRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -32,9 +33,19 @@ public void setup() { @Test @DisplayName("It should call the get user data by payment endpoint") - public void shouldCallCreatePayoutEndpoint() { + public void shouldCallGetUserDataByPaymentEndpoint() { sut.getUserDataByPayment(A_PAYMENT_ID); verify(signupPlusApiMock, times(1)).getUserDataByPayment(SCOPES, A_PAYMENT_ID); } + + @Test + @DisplayName("It should call the generate auth URI endpoint") + public void shouldCallGenerateAuthUriEndpoint() { + var generateAuthUriRequest = + GenerateAuthUriRequest.builder().paymentId(A_PAYMENT_ID).build(); + sut.generateAuthUri(generateAuthUriRequest); + + verify(signupPlusApiMock, times(1)).generateAuthUri(SCOPES, generateAuthUriRequest); + } } diff --git a/src/test/resources/__files/signup_plus/200.generate_auth_uri.json b/src/test/resources/__files/signup_plus/200.generate_auth_uri.json new file mode 100644 index 00000000..90297dc9 --- /dev/null +++ b/src/test/resources/__files/signup_plus/200.generate_auth_uri.json @@ -0,0 +1,3 @@ +{ + "auth_uri": "https://truelayer.com/redirect?id=863619242079485953&uuid=b912cc0d-149b-40a2-8a24-79a9d1f0913e" +} \ No newline at end of file