diff --git a/.github/workflows/acceptance-tests.yml b/.github/workflows/acceptance-tests.yml
index f862c838..9f2f2360 100644
--- a/.github/workflows/acceptance-tests.yml
+++ b/.github/workflows/acceptance-tests.yml
@@ -89,8 +89,8 @@ jobs:
"text": "I don't like that",
"emoji": true
},
- "image_url": "https://media.giphy.com/media/4cuyucPeVWbNS/giphy.gif",
- "alt_text": "marg"
+ "image_url": "https://media.giphy.com/media/jOpLbiGmHR9S0/giphy.gif",
+ "alt_text": "this is the worst"
}
]
}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 51884ba1..5e1a2915 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,7 @@
# Main properties
group=com.truelayer
archivesBaseName=truelayer-java
-version=10.3.1
+version=11.0.0
# Artifacts properties
sonatype_repository_url=https://s01.oss.sonatype.org/service/local/
diff --git a/src/main/java/com/truelayer/java/Constants.java b/src/main/java/com/truelayer/java/Constants.java
index 60c3efb1..af0df87d 100644
--- a/src/main/java/com/truelayer/java/Constants.java
+++ b/src/main/java/com/truelayer/java/Constants.java
@@ -12,6 +12,8 @@ public static final class Scopes {
public static final String PAYMENTS = "payments";
public static final String RECURRING_PAYMENTS_SWEEPING = "recurring_payments:sweeping";
+
+ public static final String RECURRING_PAYMENTS_COMMERCIAL = "recurring_payments:commercial";
}
/**
diff --git a/src/main/java/com/truelayer/java/ITrueLayerClient.java b/src/main/java/com/truelayer/java/ITrueLayerClient.java
index dd0d5db5..ff056f9b 100644
--- a/src/main/java/com/truelayer/java/ITrueLayerClient.java
+++ b/src/main/java/com/truelayer/java/ITrueLayerClient.java
@@ -1,8 +1,8 @@
package com.truelayer.java;
import com.truelayer.java.auth.IAuthenticationHandler;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersRequest;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersResponse;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnResponse;
import com.truelayer.java.hpp.IHostedPaymentPageLinkBuilder;
import com.truelayer.java.http.entities.ApiResponse;
import com.truelayer.java.mandates.IMandatesHandler;
@@ -65,6 +65,6 @@ public interface ITrueLayerClient {
* @return the response of the Submit payment returns parameters operation
* @see Submit payments return parameters API reference
*/
- CompletableFuture> submitPaymentReturnParameters(
- SubmitPaymentReturnParametersRequest request);
+ CompletableFuture> submitPaymentReturnParameters(
+ SubmitPaymentsProviderReturnRequest request);
}
diff --git a/src/main/java/com/truelayer/java/TrueLayerClient.java b/src/main/java/com/truelayer/java/TrueLayerClient.java
index e1c45b89..7e95b28d 100644
--- a/src/main/java/com/truelayer/java/TrueLayerClient.java
+++ b/src/main/java/com/truelayer/java/TrueLayerClient.java
@@ -2,8 +2,8 @@
import com.truelayer.java.auth.IAuthenticationHandler;
import com.truelayer.java.commonapi.ICommonHandler;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersRequest;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersResponse;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnResponse;
import com.truelayer.java.hpp.IHostedPaymentPageLinkBuilder;
import com.truelayer.java.http.entities.ApiResponse;
import com.truelayer.java.mandates.IMandatesHandler;
@@ -120,8 +120,8 @@ public IHostedPaymentPageLinkBuilder hpp() {
* {@inheritDoc}
*/
@Override
- public CompletableFuture> submitPaymentReturnParameters(
- SubmitPaymentReturnParametersRequest request) {
+ public CompletableFuture> submitPaymentReturnParameters(
+ SubmitPaymentsProviderReturnRequest request) {
return commonHandler.submitPaymentReturnParameters(request);
}
diff --git a/src/main/java/com/truelayer/java/commonapi/CommonHandler.java b/src/main/java/com/truelayer/java/commonapi/CommonHandler.java
index ea47a669..de16877a 100644
--- a/src/main/java/com/truelayer/java/commonapi/CommonHandler.java
+++ b/src/main/java/com/truelayer/java/commonapi/CommonHandler.java
@@ -3,8 +3,8 @@
import static com.truelayer.java.http.mappers.HeadersMapper.toMap;
import static java.util.Collections.emptyMap;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersRequest;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersResponse;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnResponse;
import com.truelayer.java.http.entities.ApiResponse;
import com.truelayer.java.http.entities.Headers;
import java.util.concurrent.CompletableFuture;
@@ -16,14 +16,14 @@ public class CommonHandler implements ICommonHandler {
ICommonApi commonApi;
@Override
- public CompletableFuture> submitPaymentReturnParameters(
- SubmitPaymentReturnParametersRequest request) {
+ public CompletableFuture> submitPaymentReturnParameters(
+ SubmitPaymentsProviderReturnRequest request) {
return commonApi.submitPaymentReturnParameters(emptyMap(), request);
}
@Override
- public CompletableFuture> submitPaymentReturnParameters(
- Headers headers, SubmitPaymentReturnParametersRequest request) {
+ public CompletableFuture> submitPaymentReturnParameters(
+ Headers headers, SubmitPaymentsProviderReturnRequest request) {
return commonApi.submitPaymentReturnParameters(toMap(headers), request);
}
}
diff --git a/src/main/java/com/truelayer/java/commonapi/ICommonApi.java b/src/main/java/com/truelayer/java/commonapi/ICommonApi.java
index e07486b3..c283ce39 100644
--- a/src/main/java/com/truelayer/java/commonapi/ICommonApi.java
+++ b/src/main/java/com/truelayer/java/commonapi/ICommonApi.java
@@ -1,7 +1,7 @@
package com.truelayer.java.commonapi;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersRequest;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersResponse;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnResponse;
import com.truelayer.java.http.entities.ApiResponse;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@@ -24,6 +24,6 @@ public interface ICommonApi {
* @see Submit payments return parameters API reference
*/
@POST("/payments-provider-return")
- CompletableFuture> submitPaymentReturnParameters(
- @HeaderMap Map headers, @Body SubmitPaymentReturnParametersRequest request);
+ CompletableFuture> submitPaymentReturnParameters(
+ @HeaderMap Map headers, @Body SubmitPaymentsProviderReturnRequest request);
}
diff --git a/src/main/java/com/truelayer/java/commonapi/ICommonHandler.java b/src/main/java/com/truelayer/java/commonapi/ICommonHandler.java
index 7ad0611c..fcc71c70 100644
--- a/src/main/java/com/truelayer/java/commonapi/ICommonHandler.java
+++ b/src/main/java/com/truelayer/java/commonapi/ICommonHandler.java
@@ -1,16 +1,16 @@
package com.truelayer.java.commonapi;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersRequest;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersResponse;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnResponse;
import com.truelayer.java.http.entities.ApiResponse;
import com.truelayer.java.http.entities.Headers;
import java.util.concurrent.CompletableFuture;
public interface ICommonHandler {
- CompletableFuture> submitPaymentReturnParameters(
- SubmitPaymentReturnParametersRequest request);
+ CompletableFuture> submitPaymentReturnParameters(
+ SubmitPaymentsProviderReturnRequest request);
- CompletableFuture> submitPaymentReturnParameters(
- Headers headers, SubmitPaymentReturnParametersRequest request);
+ CompletableFuture> submitPaymentReturnParameters(
+ Headers headers, SubmitPaymentsProviderReturnRequest request);
}
diff --git a/src/main/java/com/truelayer/java/commonapi/entities/MandateProviderReturnResource.java b/src/main/java/com/truelayer/java/commonapi/entities/MandateProviderReturnResource.java
new file mode 100644
index 00000000..b1d8272d
--- /dev/null
+++ b/src/main/java/com/truelayer/java/commonapi/entities/MandateProviderReturnResource.java
@@ -0,0 +1,12 @@
+package com.truelayer.java.commonapi.entities;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class MandateProviderReturnResource extends ProviderReturnResource {
+ Type type = Type.MANDATE;
+
+ String mandateId;
+}
diff --git a/src/main/java/com/truelayer/java/commonapi/entities/PaymentProviderReturnResource.java b/src/main/java/com/truelayer/java/commonapi/entities/PaymentProviderReturnResource.java
new file mode 100644
index 00000000..b845433c
--- /dev/null
+++ b/src/main/java/com/truelayer/java/commonapi/entities/PaymentProviderReturnResource.java
@@ -0,0 +1,12 @@
+package com.truelayer.java.commonapi.entities;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class PaymentProviderReturnResource extends ProviderReturnResource {
+ Type type = Type.PAYMENT;
+
+ String paymentId;
+}
diff --git a/src/main/java/com/truelayer/java/commonapi/entities/ProviderReturnResource.java b/src/main/java/com/truelayer/java/commonapi/entities/ProviderReturnResource.java
new file mode 100644
index 00000000..e43dde86
--- /dev/null
+++ b/src/main/java/com/truelayer/java/commonapi/entities/ProviderReturnResource.java
@@ -0,0 +1,65 @@
+package com.truelayer.java.commonapi.entities;
+
+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.Getter;
+import lombok.RequiredArgsConstructor;
+
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = PaymentProviderReturnResource.class)
+@JsonSubTypes({
+ @JsonSubTypes.Type(value = PaymentProviderReturnResource.class, name = "payment"),
+ @JsonSubTypes.Type(value = MandateProviderReturnResource.class, name = "mandate")
+})
+@Getter
+public abstract class ProviderReturnResource {
+ String type;
+
+ String paymentId;
+
+ @JsonIgnore
+ public abstract Type getType();
+
+ @JsonIgnore
+ public boolean isPaymentProviderReturnResponse() {
+ return this instanceof PaymentProviderReturnResource;
+ }
+
+ @JsonIgnore
+ public boolean isMandateProviderReturnResponse() {
+ return this instanceof MandateProviderReturnResource;
+ }
+
+ @JsonIgnore
+ public PaymentProviderReturnResource asPaymentProviderReturnResponse() {
+ if (!isPaymentProviderReturnResponse()) {
+ throw new TrueLayerException(buildErrorMessage());
+ }
+ return (PaymentProviderReturnResource) this;
+ }
+
+ @JsonIgnore
+ public MandateProviderReturnResource asMandateProviderReturnResponse() {
+ if (!isMandateProviderReturnResponse()) {
+ throw new TrueLayerException(buildErrorMessage());
+ }
+ return (MandateProviderReturnResource) this;
+ }
+
+ private String buildErrorMessage() {
+ return String.format(
+ "Provider return resource is of type %s.", this.getClass().getSimpleName());
+ }
+
+ @RequiredArgsConstructor
+ @Getter
+ public enum Type {
+ PAYMENT("payment"),
+ MANDATE("mandate");
+
+ @JsonValue
+ private final String type;
+ }
+}
diff --git a/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentReturnParametersResponse.java b/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentReturnParametersResponse.java
deleted file mode 100644
index 145d12eb..00000000
--- a/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentReturnParametersResponse.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.truelayer.java.commonapi.entities;
-
-import lombok.Value;
-
-@Value
-public class SubmitPaymentReturnParametersResponse {
-
- Resource resource;
-
- @Value
- public static class Resource {
- String type;
-
- String paymentId;
- }
-}
diff --git a/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentReturnParametersRequest.java b/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentsProviderReturnRequest.java
similarity index 83%
rename from src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentReturnParametersRequest.java
rename to src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentsProviderReturnRequest.java
index 77ec145a..7377175a 100644
--- a/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentReturnParametersRequest.java
+++ b/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentsProviderReturnRequest.java
@@ -9,7 +9,7 @@
@Getter
@ToString
@EqualsAndHashCode
-public class SubmitPaymentReturnParametersRequest {
+public class SubmitPaymentsProviderReturnRequest {
private String query;
private String fragment;
diff --git a/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentsProviderReturnResponse.java b/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentsProviderReturnResponse.java
new file mode 100644
index 00000000..6cd8b275
--- /dev/null
+++ b/src/main/java/com/truelayer/java/commonapi/entities/SubmitPaymentsProviderReturnResponse.java
@@ -0,0 +1,8 @@
+package com.truelayer.java.commonapi.entities;
+
+import lombok.Value;
+
+@Value
+public class SubmitPaymentsProviderReturnResponse {
+ ProviderReturnResource resource;
+}
diff --git a/src/test/java/com/truelayer/java/TestUtils.java b/src/test/java/com/truelayer/java/TestUtils.java
index 4fb88025..d40f92b2 100644
--- a/src/test/java/com/truelayer/java/TestUtils.java
+++ b/src/test/java/com/truelayer/java/TestUtils.java
@@ -4,13 +4,16 @@
import static com.truelayer.java.Constants.HeaderNames.*;
import static com.truelayer.java.Utils.getObjectMapper;
import static org.apache.commons.lang3.ObjectUtils.*;
-import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.*;
import com.github.tomakehurst.wiremock.client.MappingBuilder;
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
import com.github.tomakehurst.wiremock.matching.UrlPattern;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import com.truelayer.java.auth.entities.AccessToken;
+import com.truelayer.java.commonapi.entities.PaymentProviderReturnResource;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnResponse;
import com.truelayer.java.http.entities.ApiResponse;
import com.truelayer.java.http.entities.Headers;
import com.truelayer.java.versioninfo.VersionInfo;
@@ -20,8 +23,10 @@
import java.time.Duration;
import java.util.UUID;
import java.util.regex.Pattern;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
-import okhttp3.OkHttpClient;
+import okhttp3.*;
import org.apache.commons.lang3.RandomStringUtils;
public class TestUtils {
@@ -217,4 +222,75 @@ public static Headers buildTestHeaders() {
.xDeviceUserAgent("ADummyUserAgen")
.build();
}
+
+ @SneakyThrows
+ public static void runAndAssertHeadlessResourceAuthorisation(
+ TrueLayerClient tlClient, URI redirectUri, HeadlessResourceAuthorization headlessResourceAuthorization) {
+ assertNotNull(redirectUri);
+ String protocol = redirectUri.getScheme();
+ String host = redirectUri.getHost();
+ String resourceId =
+ redirectUri.getPath().substring(redirectUri.getPath().lastIndexOf("/") + 1);
+ var body = RequestBody.create(MediaType.get("application/json"), headlessResourceAuthorization.payload);
+ Request request = new Request.Builder()
+ .url(String.format(
+ "%s://%s/api/%s/%s/action", protocol, host, headlessResourceAuthorization.path, resourceId))
+ .post(body)
+ .addHeader(
+ "Authorization",
+ String.format("Bearer %s", redirectUri.getFragment().replaceFirst("token=", "")))
+ .build();
+ Response authorisationResponse =
+ getHttpClientInstance().newCall(request).execute();
+ assertTrue(authorisationResponse.isSuccessful());
+ String responseString = authorisationResponse.body().string();
+ assertNotNull(responseString);
+
+ // Grab the provider return query and fragment from the mock payment api response
+ URI responseUrl = URI.create(responseString);
+
+ // Mandates require some adjustments to the query string...
+ // TODO: review if we want this to be a permanent solution
+ String query = headlessResourceAuthorization == HeadlessResourceAuthorization.MANDATES
+ ? responseUrl.getQuery().replaceFirst("mandate-", "")
+ : responseUrl.getQuery();
+
+ SubmitPaymentsProviderReturnRequest submitProviderReturneRequest = SubmitPaymentsProviderReturnRequest.builder()
+ .query(query)
+ .fragment(responseUrl.getFragment())
+ .build();
+ ApiResponse submitPaymentReturnParametersResponse =
+ tlClient.submitPaymentReturnParameters(submitProviderReturneRequest)
+ .get();
+ assertNotError(submitPaymentReturnParametersResponse);
+
+ switch (headlessResourceAuthorization) {
+ case PAYMENTS:
+ assertEquals(
+ PaymentProviderReturnResource.Type.PAYMENT,
+ submitPaymentReturnParametersResponse
+ .getData()
+ .getResource()
+ .getType());
+ break;
+ case MANDATES:
+ assertEquals(
+ PaymentProviderReturnResource.Type.MANDATE,
+ submitPaymentReturnParametersResponse
+ .getData()
+ .getResource()
+ .getType());
+ break;
+ }
+ }
+
+ @RequiredArgsConstructor
+ @Getter
+ public enum HeadlessResourceAuthorization {
+ PAYMENTS("single-immediate-payments", "{\"action\":\"Execute\", \"redirect\": false}"),
+ MANDATES("vrp-consents", "{\"action\":\"Authorise\", \"redirect\": false}");
+
+ private final String path;
+ private final String payload;
+ }
}
diff --git a/src/test/java/com/truelayer/java/acceptance/MandatesAcceptanceTests.java b/src/test/java/com/truelayer/java/acceptance/MandatesAcceptanceTests.java
index 5791804d..d727dc14 100644
--- a/src/test/java/com/truelayer/java/acceptance/MandatesAcceptanceTests.java
+++ b/src/test/java/com/truelayer/java/acceptance/MandatesAcceptanceTests.java
@@ -1,5 +1,7 @@
package com.truelayer.java.acceptance;
+import static com.truelayer.java.Constants.Scopes.*;
+import static com.truelayer.java.TestUtils.*;
import static com.truelayer.java.TestUtils.assertNotError;
import static com.truelayer.java.TestUtils.getHttpClientInstance;
import static com.truelayer.java.mandates.entities.Constraints.PeriodicLimits.Limit.PeriodAlignment.CALENDAR;
@@ -8,10 +10,8 @@
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.*;
-import com.truelayer.java.entities.CurrencyCode;
-import com.truelayer.java.entities.ProviderFilter;
-import com.truelayer.java.entities.RelatedProducts;
-import com.truelayer.java.entities.User;
+import com.truelayer.java.*;
+import com.truelayer.java.entities.*;
import com.truelayer.java.entities.accountidentifier.AccountIdentifier;
import com.truelayer.java.http.entities.ApiResponse;
import com.truelayer.java.mandates.entities.*;
@@ -26,6 +26,7 @@
import com.truelayer.java.payments.entities.providerselection.ProviderSelection;
import com.truelayer.java.payments.entities.retry.Retry;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Objects;
import java.util.UUID;
@@ -37,7 +38,6 @@
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.commons.lang3.ObjectUtils;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@@ -49,17 +49,21 @@
public class MandatesAcceptanceTests extends AcceptanceTests {
public static final String RETURN_URI = "http://localhost:3000/callback";
- public static final String PROVIDER_ID = "ob-uki-mock-bank-sbox";
+ public static final String PROVIDER_ID = "mock-payments-gb-redirect";
- @Test
- @DisplayName("It should create a VRP sweeping mandate with preselected provider")
+ @ParameterizedTest
+ @DisplayName("It should create a VRP mandate with preselected provider")
+ @MethodSource("provideMandatesScopesAndTypes")
@SneakyThrows
- public void itShouldCreateASweepingMandateWithPreselectedProvider() {
+ public void itShouldCreateAMandateWithPreselectedProvider(String mandatesScope, Mandate.Type mandateType) {
+ // create client with required scopes
+ var tlClient = buildMandatesTlClient(mandatesScope);
+
// create mandate
ProviderSelection preselectedProvider =
ProviderSelection.preselected().providerId(PROVIDER_ID).build();
ApiResponse createMandateResponse = tlClient.mandates()
- .createMandate(createMandateRequest(SWEEPING, preselectedProvider))
+ .createMandate(createMandateRequest(mandateType, preselectedProvider))
.get();
assertNotError(createMandateResponse);
@@ -85,25 +89,6 @@ public void itShouldCreateASweepingMandateWithSignupPlusIntention() {
assertNotError(createMandateResponse);
}
- @Test
- @Disabled("The provider ob-uki-mock-bank-sbox does not support commercial VRP yet")
- @DisplayName("It should create a VRP commercial mandate with preselected provider")
- @SneakyThrows
- public void itShouldCreateACommercialMandateWithPreselectedProvider() {
- // create mandate
- ProviderSelection preselectedProvider =
- ProviderSelection.preselected().providerId(PROVIDER_ID).build();
- ApiResponse createMandateResponse = tlClient.mandates()
- .createMandate(createMandateRequest(COMMERCIAL, preselectedProvider))
- .get();
- assertNotError(createMandateResponse);
-
- // start auth flow
- ApiResponse startAuthorizationFlowResponse =
- startAuthFlowForMandate(createMandateResponse.getData().getId());
- assertNotError(startAuthorizationFlowResponse);
- }
-
@Test
@DisplayName("It should create a mandate with user_selected provider")
@SneakyThrows
@@ -143,15 +128,19 @@ public void itShouldGetAListOfMandates() {
assertNotError(listMandatesResponse);
}
- @Test
+ @ParameterizedTest
@DisplayName("It should get confirm funds")
+ @MethodSource("provideMandatesScopesAndTypes")
@SneakyThrows
- public void itShouldGetFunds() {
+ public void itShouldGetFunds(String mandatesScope, Mandate.Type mandateType) {
+ // create client with required scopes
+ var tlClient = buildMandatesTlClient(mandatesScope);
+
// create mandate
ProviderSelection preselectedProvider =
ProviderSelection.preselected().providerId(PROVIDER_ID).build();
ApiResponse createMandateResponse = tlClient.mandates()
- .createMandate(createMandateRequest(SWEEPING, preselectedProvider))
+ .createMandate(createMandateRequest(mandateType, preselectedProvider))
.get();
assertNotError(createMandateResponse);
@@ -169,8 +158,22 @@ public void itShouldGetFunds() {
assertNotError(startAuthorizationFlowResponse);
+ // we check the state of the mandate returned by the gateway
+ AuthorizationFlowResponse authorizationFlowResponse = startAuthorizationFlowResponse.getData();
+ assertTrue(
+ authorizationFlowResponse.isAuthorizing(),
+ "Mandate status is " + authorizationFlowResponse.getStatus().getStatus());
+
// authorize the created mandate without explicit user interaction
- authorizeMandate(startAuthorizationFlowResponse.getData());
+ URI redirectUri = authorizationFlowResponse
+ .asAuthorizing()
+ .getAuthorizationFlow()
+ .getActions()
+ .getNext()
+ .asRedirect()
+ .getUri();
+ runAndAssertHeadlessResourceAuthorisation(tlClient, redirectUri, HeadlessResourceAuthorization.MANDATES);
+
waitForMandateToBeAuthorized(createMandateResponse.getData().getId());
// finally make a confirmation of funds request for 1 penny
@@ -182,14 +185,18 @@ public void itShouldGetFunds() {
assertEquals(true, getConfirmationOfFundsResponseApiResponse.getData().getConfirmed());
}
- @Test
+ @ParameterizedTest
@DisplayName("It should get mandate constraints")
+ @MethodSource("provideMandatesScopesAndTypes")
@SneakyThrows
- public void itShouldGetConstraints() {
+ public void itShouldGetConstraints(String mandatesScope, Mandate.Type mandateType) {
+ // create client with required scopes
+ var tlClient = buildMandatesTlClient(mandatesScope);
+
// create mandate
ProviderSelection preselectedProvider =
ProviderSelection.preselected().providerId(PROVIDER_ID).build();
- CreateMandateRequest createMandateRequest = createMandateRequest(SWEEPING, preselectedProvider);
+ CreateMandateRequest createMandateRequest = createMandateRequest(mandateType, preselectedProvider);
ApiResponse createMandateResponse =
tlClient.mandates().createMandate(createMandateRequest).get();
assertNotError(createMandateResponse);
@@ -208,8 +215,22 @@ public void itShouldGetConstraints() {
assertNotError(startAuthorizationFlowResponse);
+ // we check the state of the mandate returned by the gateway
+ AuthorizationFlowResponse authorizationFlowResponse = startAuthorizationFlowResponse.getData();
+ assertTrue(
+ authorizationFlowResponse.isAuthorizing(),
+ "Mandate status is " + authorizationFlowResponse.getStatus().getStatus());
+
// authorize the created mandate without explicit user interaction
- authorizeMandate(startAuthorizationFlowResponse.getData());
+ URI redirectUri = authorizationFlowResponse
+ .asAuthorizing()
+ .getAuthorizationFlow()
+ .getActions()
+ .getNext()
+ .asRedirect()
+ .getUri();
+ runAndAssertHeadlessResourceAuthorisation(tlClient, redirectUri, HeadlessResourceAuthorization.MANDATES);
+
waitForMandateToBeAuthorized(createMandateResponse.getData().getId());
// finally make a Get constraints request
@@ -220,15 +241,19 @@ public void itShouldGetConstraints() {
assertNotError(getConstraintsResponseApiResponse);
}
- @Test
+ @ParameterizedTest
@DisplayName("It should create and revoke a mandate")
+ @MethodSource("provideMandatesScopesAndTypes")
@SneakyThrows
- public void itShouldCreateAndRevokeAMandate() {
+ public void itShouldCreateAndRevokeAMandate(String mandatesScope, Mandate.Type mandateType) {
+ // create client with required scopes
+ var tlClient = buildMandatesTlClient(mandatesScope);
+
// create mandate
ProviderSelection preselectedProvider =
ProviderSelection.preselected().providerId(PROVIDER_ID).build();
ApiResponse createMandateResponse = tlClient.mandates()
- .createMandate(createMandateRequest(SWEEPING, preselectedProvider))
+ .createMandate(createMandateRequest(mandateType, preselectedProvider))
.get();
assertNotError(createMandateResponse);
@@ -237,8 +262,22 @@ public void itShouldCreateAndRevokeAMandate() {
startAuthFlowForMandate(createMandateResponse.getData().getId());
assertNotError(startAuthorizationFlowResponse);
+ // we check the state of the mandate returned by the gateway
+ AuthorizationFlowResponse authorizationFlowResponse = startAuthorizationFlowResponse.getData();
+ assertTrue(
+ authorizationFlowResponse.isAuthorizing(),
+ "Mandate status is " + authorizationFlowResponse.getStatus().getStatus());
+
// authorize the created mandate without explicit user interaction
- authorizeMandate(startAuthorizationFlowResponse.getData());
+ URI redirectUri = authorizationFlowResponse
+ .asAuthorizing()
+ .getAuthorizationFlow()
+ .getActions()
+ .getNext()
+ .asRedirect()
+ .getUri();
+ runAndAssertHeadlessResourceAuthorisation(tlClient, redirectUri, HeadlessResourceAuthorization.MANDATES);
+
waitForMandateToBeAuthorized(createMandateResponse.getData().getId());
// revoke mandate by id
@@ -252,12 +291,15 @@ public void itShouldCreateAndRevokeAMandate() {
@ParameterizedTest(name = "with retry {0}")
@MethodSource("provideItShouldCreateAPaymentOnMandateTestParameters")
@SneakyThrows
- public void itShouldCreateAPaymentOnMandate(Retry retry) {
+ public void itShouldCreateAPaymentOnMandate(String mandatesScope, Mandate.Type mandateType, Retry retry) {
+ // create client with required scopes
+ var tlClient = buildMandatesTlClient(mandatesScope);
+
// create mandate
ProviderSelection preselectedProvider =
ProviderSelection.preselected().providerId(PROVIDER_ID).build();
ApiResponse createMandateResponse = tlClient.mandates()
- .createMandate(createMandateRequest(SWEEPING, preselectedProvider))
+ .createMandate(createMandateRequest(mandateType, preselectedProvider))
.get();
assertNotError(createMandateResponse);
@@ -275,8 +317,22 @@ public void itShouldCreateAPaymentOnMandate(Retry retry) {
assertNotError(startAuthorizationFlowResponse);
+ // we check the state of the mandate returned by the gateway
+ AuthorizationFlowResponse authorizationFlowResponse = startAuthorizationFlowResponse.getData();
+ assertTrue(
+ authorizationFlowResponse.isAuthorizing(),
+ "Mandate status is " + authorizationFlowResponse.getStatus().getStatus());
+
// authorize the created mandate without explicit user interaction
- authorizeMandate(startAuthorizationFlowResponse.getData());
+ URI redirectUri = authorizationFlowResponse
+ .asAuthorizing()
+ .getAuthorizationFlow()
+ .getActions()
+ .getNext()
+ .asRedirect()
+ .getUri();
+ runAndAssertHeadlessResourceAuthorisation(tlClient, redirectUri, HeadlessResourceAuthorization.MANDATES);
+
waitForMandateToBeAuthorized(createMandateResponse.getData().getId());
// get mandate by id
@@ -381,6 +437,7 @@ private ApiResponse startAuthFlowForMandate(String ma
}
@SneakyThrows
+ @Deprecated // TODO: cleanup
private void authorizeMandate(AuthorizationFlowResponse authorizationFlowResponse) {
// first we check the state of the mandate returned by the gateway
assertTrue(
@@ -395,6 +452,7 @@ private void authorizeMandate(AuthorizationFlowResponse authorizationFlowRespons
.getNext()
.asRedirect()
.getUri();
+
Response redirectResponse = getHttpClientInstance()
.newCall(new Request.Builder().url(redirectUri.toURL()).get().build())
.execute();
@@ -438,13 +496,57 @@ private void waitForMandateToBeAuthorized(String mandateId) {
});
}
- public static Stream provideItShouldCreateAPaymentOnMandateTestParameters() {
+ private static TrueLayerClient buildMandatesTlClient(String mandatesScope) {
+ return TrueLayerClient.New()
+ .environment(environment)
+ .clientCredentials(ClientCredentials.builder()
+ .clientId(System.getenv("TL_CLIENT_ID"))
+ .clientSecret(System.getenv("TL_CLIENT_SECRET"))
+ .build())
+ .signingOptions(SigningOptions.builder()
+ .keyId(System.getenv("TL_SIGNING_KEY_ID"))
+ .privateKey(System.getenv("TL_SIGNING_PRIVATE_KEY").getBytes(StandardCharsets.UTF_8))
+ .build())
+ .withGlobalScopes(RequestScopes.builder()
+ .scope(PAYMENTS)
+ .scope(mandatesScope)
+ .build())
+ .withHttpLogs()
+ .withCredentialsCaching()
+ .build();
+ }
+
+ private static Stream provideItShouldCreateAPaymentOnMandateTestParameters() {
+ return Stream.of(
+ Arguments.of(RECURRING_PAYMENTS_SWEEPING, SWEEPING, null),
+ Arguments.of(RECURRING_PAYMENTS_COMMERCIAL, COMMERCIAL, null),
+ Arguments.of(
+ RECURRING_PAYMENTS_SWEEPING,
+ SWEEPING,
+ Retry.standard().forDuration("30m").build()),
+ Arguments.of(
+ RECURRING_PAYMENTS_COMMERCIAL,
+ COMMERCIAL,
+ Retry.standard().forDuration("30m").build()),
+ Arguments.of(
+ RECURRING_PAYMENTS_SWEEPING,
+ SWEEPING,
+ Retry.smart()
+ .forDuration("90d")
+ .ensureMinimumBalanceInMinor(100)
+ .build()),
+ Arguments.of(
+ RECURRING_PAYMENTS_COMMERCIAL,
+ COMMERCIAL,
+ Retry.smart()
+ .forDuration("90d")
+ .ensureMinimumBalanceInMinor(100)
+ .build()));
+ }
+
+ private static Stream provideMandatesScopesAndTypes() {
return Stream.of(
- null,
- Arguments.of(Retry.standard().forDuration("30m").build()),
- Arguments.of(Retry.smart()
- .forDuration("90d")
- .ensureMinimumBalanceInMinor(100)
- .build()));
+ Arguments.of(RECURRING_PAYMENTS_SWEEPING, SWEEPING),
+ Arguments.of(RECURRING_PAYMENTS_COMMERCIAL, COMMERCIAL));
}
}
diff --git a/src/test/java/com/truelayer/java/acceptance/PaymentsAcceptanceTests.java b/src/test/java/com/truelayer/java/acceptance/PaymentsAcceptanceTests.java
index 58acc393..6fbbc829 100644
--- a/src/test/java/com/truelayer/java/acceptance/PaymentsAcceptanceTests.java
+++ b/src/test/java/com/truelayer/java/acceptance/PaymentsAcceptanceTests.java
@@ -4,8 +4,6 @@
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.*;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersRequest;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersResponse;
import com.truelayer.java.entities.*;
import com.truelayer.java.entities.Address;
import com.truelayer.java.entities.accountidentifier.AccountIdentifier;
@@ -348,21 +346,15 @@ public void shouldCompleteAnAuthorizationFlowForAPaymentWithProviderReturn() {
.get();
assertNotError(startAuthorizationFlowResponse);
- // Call the mock payment api response to trigger the executed state on the payment just created
- Response paymentResponse = callMockProviderRedirectUrl(startAuthorizationFlowResponse.getData());
- assertTrue(paymentResponse.isSuccessful());
- String responseString = paymentResponse.body().string();
- assertNotNull(responseString);
-
- // Grab the provider return query and fragment from the mock payment api response
- URI responseUrl = URI.create(responseString);
- SubmitPaymentReturnParametersRequest submitProviderReturn = SubmitPaymentReturnParametersRequest.builder()
- .query(responseUrl.getQuery())
- .fragment(responseUrl.getFragment())
- .build();
- ApiResponse submitPaymentReturnParametersResponse =
- tlClient.submitPaymentReturnParameters(submitProviderReturn).get();
- assertNotError(submitPaymentReturnParametersResponse);
+ URI redirectUri = startAuthorizationFlowResponse
+ .getData()
+ .asAuthorizing()
+ .getAuthorizationFlow()
+ .getActions()
+ .getNext()
+ .asRedirect()
+ .getUri();
+ runAndAssertHeadlessResourceAuthorisation(tlClient, redirectUri, HeadlessResourceAuthorization.PAYMENTS);
}
@SneakyThrows
@@ -394,21 +386,15 @@ public void shouldCreateAPaymentRefundAndGetRefundDetails() {
.get();
assertNotError(startAuthorizationFlowResponse);
- // Call the mock payment api response to trigger the executed state on the payment just created
- Response paymentResponse = callMockProviderRedirectUrl(startAuthorizationFlowResponse.getData());
- assertTrue(paymentResponse.isSuccessful());
- String responseString = paymentResponse.body().string();
- assertNotNull(responseString);
-
- // Grab the provider return query and fragment from the mock payment api response
- URI responseUrl = URI.create(responseString);
- SubmitPaymentReturnParametersRequest submitProviderReturn = SubmitPaymentReturnParametersRequest.builder()
- .query(responseUrl.getQuery())
- .fragment(responseUrl.getFragment())
- .build();
- ApiResponse submitPaymentReturnParametersResponse =
- tlClient.submitPaymentReturnParameters(submitProviderReturn).get();
- assertNotError(submitPaymentReturnParametersResponse);
+ URI redirectUri = startAuthorizationFlowResponse
+ .getData()
+ .asAuthorizing()
+ .getAuthorizationFlow()
+ .getActions()
+ .getNext()
+ .asRedirect()
+ .getUri();
+ runAndAssertHeadlessResourceAuthorisation(tlClient, redirectUri, HeadlessResourceAuthorization.PAYMENTS);
waitForPaymentToBeSettled(paymentId);
@@ -455,31 +441,6 @@ public void shouldCreateAPaymentRefundAndGetRefundDetails() {
paymentRefundApiResponse.getData().getMetadata());
}
- @SneakyThrows
- private Response callMockProviderRedirectUrl(AuthorizationFlowResponse startAuthorizationFlowResponse) {
- URI redirectUri = startAuthorizationFlowResponse
- .asAuthorizing()
- .getAuthorizationFlow()
- .getActions()
- .getNext()
- .asRedirect()
- .getUri();
- assertNotNull(redirectUri);
- String protocol = redirectUri.getScheme();
- String host = redirectUri.getHost();
- String bankPaymentId = redirectUri.getPath().replaceFirst("/login/", "");
- RequestBody body =
- RequestBody.create(MediaType.get("application/json"), "{\"action\":\"Execute\", \"redirect\": false}");
- Request request = new Request.Builder()
- .url(String.format("%s://%s/api/single-immediate-payments/%s/action", protocol, host, bankPaymentId))
- .post(body)
- .addHeader(
- "Authorization",
- String.format("Bearer %s", redirectUri.getFragment().replaceFirst("token=", "")))
- .build();
- return getHttpClientInstance().newCall(request).execute();
- }
-
private PreselectedProviderSelection buildPreselectedProviderSelection() {
return ProviderSelection.preselected()
.providerId(PROVIDER_ID)
diff --git a/src/test/java/com/truelayer/java/acceptance/PaymentsProvidersAcceptanceTests.java b/src/test/java/com/truelayer/java/acceptance/PaymentsProvidersAcceptanceTests.java
index 0b81863a..ed3df47c 100644
--- a/src/test/java/com/truelayer/java/acceptance/PaymentsProvidersAcceptanceTests.java
+++ b/src/test/java/com/truelayer/java/acceptance/PaymentsProvidersAcceptanceTests.java
@@ -6,7 +6,6 @@
import com.truelayer.java.http.entities.ApiResponse;
import com.truelayer.java.paymentsproviders.entities.PaymentsProvider;
import lombok.SneakyThrows;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@@ -17,8 +16,6 @@ public class PaymentsProvidersAcceptanceTests extends AcceptanceTests {
public static final String PROVIDER_ID = "mock-payments-gb-redirect";
@Test
- @Disabled(
- "Temporarily disabled because the mock is released in alpha, but the release channel enum deliberately doesn't have alpha")
@DisplayName("It should get by id a payments provider")
@SneakyThrows
public void shouldCreateAPaymentWithUserSelectionProvider() {
diff --git a/src/test/java/com/truelayer/java/commonapi/CommonHandlerTests.java b/src/test/java/com/truelayer/java/commonapi/CommonHandlerTests.java
index 0b448488..e876cbbd 100644
--- a/src/test/java/com/truelayer/java/commonapi/CommonHandlerTests.java
+++ b/src/test/java/com/truelayer/java/commonapi/CommonHandlerTests.java
@@ -6,7 +6,7 @@
import static org.mockito.Mockito.verify;
import com.truelayer.java.TestUtils;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersRequest;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
import com.truelayer.java.http.entities.Headers;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -19,7 +19,7 @@ class CommonHandlerTests {
public void shouldCallSubmitPaymentReturnParamsEndpoint() {
ICommonApi commonApi = Mockito.mock(ICommonApi.class);
CommonHandler sut = new CommonHandler(commonApi);
- SubmitPaymentReturnParametersRequest request = SubmitPaymentReturnParametersRequest.builder()
+ SubmitPaymentsProviderReturnRequest request = SubmitPaymentsProviderReturnRequest.builder()
.query("a-query")
.fragment("a-fragment")
.build();
@@ -35,7 +35,7 @@ public void shouldCallSubmitPaymentReturnParamsEndpointWithCustomHeaders() {
ICommonApi commonApi = Mockito.mock(ICommonApi.class);
CommonHandler sut = new CommonHandler(commonApi);
Headers customHeaders = TestUtils.buildTestHeaders();
- SubmitPaymentReturnParametersRequest request = SubmitPaymentReturnParametersRequest.builder()
+ SubmitPaymentsProviderReturnRequest request = SubmitPaymentsProviderReturnRequest.builder()
.query("a-query")
.fragment("a-fragment")
.build();
diff --git a/src/test/java/com/truelayer/java/commonapi/entities/ProviderReturnResourceTests.java b/src/test/java/com/truelayer/java/commonapi/entities/ProviderReturnResourceTests.java
new file mode 100644
index 00000000..5f7a01d1
--- /dev/null
+++ b/src/test/java/com/truelayer/java/commonapi/entities/ProviderReturnResourceTests.java
@@ -0,0 +1,77 @@
+package com.truelayer.java.commonapi.entities;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.truelayer.java.TrueLayerException;
+import java.util.UUID;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+public class ProviderReturnResourceTests {
+
+ @Test
+ @DisplayName("It should yield true if instance is of type PaymentProviderReturnResource")
+ public void shouldYieldTrueIfPaymentProviderReturnResource() {
+ ProviderReturnResource sut =
+ new PaymentProviderReturnResource(UUID.randomUUID().toString());
+
+ assertTrue(sut.isPaymentProviderReturnResponse());
+ }
+
+ @Test
+ @DisplayName("It should convert to an instance of class PaymentProviderReturnResource")
+ public void shouldConvertToPaymentProviderReturnResource() {
+ ProviderReturnResource sut =
+ new PaymentProviderReturnResource(UUID.randomUUID().toString());
+
+ assertDoesNotThrow(sut::asPaymentProviderReturnResponse);
+ }
+
+ @Test
+ @DisplayName("It should throw an error when converting to PaymentProviderReturnResource")
+ public void shouldNotConvertToPaymentProviderReturnResource() {
+ ProviderReturnResource sut =
+ new MandateProviderReturnResource(UUID.randomUUID().toString());
+
+ Throwable thrown = assertThrows(TrueLayerException.class, sut::asPaymentProviderReturnResponse);
+
+ assertEquals(
+ String.format(
+ "Provider return resource is of type %s.",
+ sut.getClass().getSimpleName()),
+ thrown.getMessage());
+ }
+
+ @Test
+ @DisplayName("It should yield true if instance is of type MandateProviderReturnResource")
+ public void shouldYieldTrueIfMandateProviderReturnResource() {
+ ProviderReturnResource sut =
+ new MandateProviderReturnResource(UUID.randomUUID().toString());
+
+ assertTrue(sut.isMandateProviderReturnResponse());
+ }
+
+ @Test
+ @DisplayName("It should convert to an instance of class MandateProviderReturnResource")
+ public void shouldConvertToMandateProviderReturnResource() {
+ ProviderReturnResource sut =
+ new MandateProviderReturnResource(UUID.randomUUID().toString());
+
+ assertDoesNotThrow(sut::asMandateProviderReturnResponse);
+ }
+
+ @Test
+ @DisplayName("It should throw an error when converting to MandateProviderReturnResource")
+ public void shouldNotConvertToMandateProviderReturnResource() {
+ ProviderReturnResource sut =
+ new PaymentProviderReturnResource(UUID.randomUUID().toString());
+
+ Throwable thrown = assertThrows(TrueLayerException.class, sut::asMandateProviderReturnResponse);
+
+ assertEquals(
+ String.format(
+ "Provider return resource is of type %s.",
+ sut.getClass().getSimpleName()),
+ thrown.getMessage());
+ }
+}
diff --git a/src/test/java/com/truelayer/java/integration/CommonApiIntegrationTests.java b/src/test/java/com/truelayer/java/integration/CommonApiIntegrationTests.java
index 17abb494..b95e4865 100644
--- a/src/test/java/com/truelayer/java/integration/CommonApiIntegrationTests.java
+++ b/src/test/java/com/truelayer/java/integration/CommonApiIntegrationTests.java
@@ -6,8 +6,8 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.truelayer.java.TestUtils.RequestStub;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersRequest;
-import com.truelayer.java.commonapi.entities.SubmitPaymentReturnParametersResponse;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnRequest;
+import com.truelayer.java.commonapi.entities.SubmitPaymentsProviderReturnResponse;
import com.truelayer.java.http.entities.ApiResponse;
import lombok.SneakyThrows;
import org.junit.jupiter.api.DisplayName;
@@ -28,15 +28,15 @@ public void shouldReturnAResourceWhenProviderReturnParamsSubmitted() {
.bodyFile(jsonResponseFile)
.build();
- ApiResponse submitPaymentReturnParametersResponse =
+ ApiResponse submitPaymentReturnParametersResponse =
tlClient.submitPaymentReturnParameters(
- SubmitPaymentReturnParametersRequest.builder().build())
+ SubmitPaymentsProviderReturnRequest.builder().build())
.get();
verify(exactly(0), postRequestedFor(urlPathEqualTo("/connect/token")));
assertNotError(submitPaymentReturnParametersResponse);
- SubmitPaymentReturnParametersResponse expected =
- deserializeJsonFileTo(jsonResponseFile, SubmitPaymentReturnParametersResponse.class);
+ SubmitPaymentsProviderReturnResponse expected =
+ deserializeJsonFileTo(jsonResponseFile, SubmitPaymentsProviderReturnResponse.class);
assertEquals(expected, submitPaymentReturnParametersResponse.getData());
}
}