Skip to content

Commit 8aa282f

Browse files
authored
[EWT-135] feat: adds support for X-Forwarded-For HTTP header in start auth flow for single payments (#195)
1 parent dd84dda commit 8aa282f

File tree

6 files changed

+69
-11
lines changed

6 files changed

+69
-11
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
plugins {
22
id 'java-library'
33
// to unleash the lombok magic
4-
id "io.freefair.lombok" version "6.6.3"
4+
id "io.freefair.lombok" version "8.0.1"
55
// to make our tests output more fancy
66
id 'com.adarshr.test-logger' version '3.2.0'
77
// to publish packages
@@ -10,7 +10,7 @@ plugins {
1010
id "com.diffplug.spotless" version "6.17.0"
1111
// test coverage
1212
id 'jacoco'
13-
id 'com.github.kt3k.coveralls' version '2.12.0'
13+
id 'com.github.kt3k.coveralls' version '2.12.2'
1414
// signing
1515
id "signing"
1616
// nexus publishing

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Main properties
22
group=com.truelayer
33
archivesBaseName=truelayer-java
4-
version=6.0.1
4+
version=6.1.0
55

66
# Artifacts properties
77
sonatype_repository_url=https://s01.oss.sonatype.org/service/local/

src/main/java/com/truelayer/java/Constants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public static final class HeaderNames {
2828
public static final String TL_SIGNATURE = "Tl-Signature";
2929
public static final String TL_AGENT = "TL-Agent";
3030
public static final String AUTHORIZATION = "Authorization";
31+
public static final String X_FORWARDED_FOR = "X-Forwarded-For";
3132
public static final String COOKIE = "Cookie";
3233
public static final String TL_CORRELATION_ID = "X-Tl-Correlation-Id";
3334
}

src/main/java/com/truelayer/java/payments/IPaymentsApi.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package com.truelayer.java.payments;
22

3+
import static com.truelayer.java.Constants.HeaderNames.X_FORWARDED_FOR;
4+
35
import com.truelayer.java.http.entities.ApiResponse;
46
import com.truelayer.java.payments.entities.*;
57
import com.truelayer.java.payments.entities.paymentdetail.PaymentDetail;
68
import java.util.concurrent.CompletableFuture;
7-
import retrofit2.http.Body;
8-
import retrofit2.http.GET;
9-
import retrofit2.http.POST;
10-
import retrofit2.http.Path;
9+
import retrofit2.http.*;
1110

1211
/**
1312
* Exposes all the payments related capabilities of the library.
@@ -45,6 +44,21 @@ public interface IPaymentsApi {
4544
CompletableFuture<ApiResponse<AuthorizationFlowResponse>> startAuthorizationFlow(
4645
@Path("id") String paymentId, @Body StartAuthorizationFlowRequest request);
4746

47+
/**
48+
* Starts an authorization flow for a given payment resource,
49+
* including the <code>X-Forwarded-For</code> HTTP header field to record the end-user IP address.
50+
* @param paymentId the payment identifier
51+
* @param request a start authorization flow request payload
52+
* @param xForwardedFor the end-user IP address
53+
* @return the response of the <i>Start Authorization Flow</i> operation
54+
* @see <a href="https://docs.truelayer.com/reference/start-payment-authorization-flow"><i>Start Authorization Flow</i> API reference</a>
55+
*/
56+
@POST("/payments/{id}/authorization-flow")
57+
CompletableFuture<ApiResponse<AuthorizationFlowResponse>> startAuthorizationFlow(
58+
@Path("id") String paymentId,
59+
@Body StartAuthorizationFlowRequest request,
60+
@Header(X_FORWARDED_FOR) String xForwardedFor);
61+
4862
/**
4963
* Submit the provider selection for a given payment resource.
5064
* @param paymentId the payment identifier

src/test/java/com/truelayer/java/TestUtils.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public static class RequestStub {
105105
private int status;
106106
private String bodyFile;
107107
private Integer delayMilliseconds;
108+
private String xForwardedForHeader;
108109

109110
private RequestStub() {}
110111

@@ -147,6 +148,11 @@ public RequestStub withSignature() {
147148
return this;
148149
}
149150

151+
public RequestStub withXForwardedForHeader(String ipAddress) {
152+
this.xForwardedForHeader = ipAddress;
153+
return this;
154+
}
155+
150156
public RequestStub delayMs(int delayMilliseconds) {
151157
this.delayMilliseconds = delayMilliseconds;
152158
return this;
@@ -167,6 +173,10 @@ public StubMapping build() {
167173
request.withHeader(IDEMPOTENCY_KEY, matching(UUID_REGEX_PATTERN));
168174
}
169175

176+
if (!isEmpty(xForwardedForHeader)) {
177+
request.withHeader(X_FORWARDED_FOR, equalTo(xForwardedForHeader));
178+
}
179+
170180
ResponseDefinitionBuilder response = aResponse()
171181
.withHeader(TL_CORRELATION_ID, UUID.randomUUID().toString())
172182
.withStatus(status);

src/test/java/com/truelayer/java/integration/PaymentsIntegrationTests.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.truelayer.java.integration;
22

3-
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
4-
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
3+
import static com.github.tomakehurst.wiremock.client.WireMock.*;
54
import static com.truelayer.java.TestUtils.assertNotError;
65
import static com.truelayer.java.payments.entities.paymentdetail.Status.*;
76
import static com.truelayer.java.payments.entities.paymentmethod.PaymentMethod.Type.BANK_TRANSFER;
@@ -110,7 +109,7 @@ public void shouldReturnAPaymentDetail(PaymentMethod.Type paymentMethodType, Sta
110109
.build();
111110
RequestStub.New()
112111
.method("get")
113-
.path(urlPathMatching("/payments/" + A_PAYMENT_ID))
112+
.path(urlPathEqualTo("/payments/" + A_PAYMENT_ID))
114113
.withAuthorization()
115114
.status(200)
116115
.bodyFile(jsonResponseFile)
@@ -207,7 +206,7 @@ public void shouldStartAnAuthorizationFlow(String status) {
207206
.build();
208207
RequestStub.New()
209208
.method("post")
210-
.path(urlPathMatching("/payments/" + A_PAYMENT_ID + "/authorization-flow"))
209+
.path(urlPathEqualTo("/payments/" + A_PAYMENT_ID + "/authorization-flow"))
211210
.withAuthorization()
212211
.withIdempotencyKey()
213212
.status(200)
@@ -226,6 +225,40 @@ public void shouldStartAnAuthorizationFlow(String status) {
226225
assertEquals(expected, response.getData());
227226
}
228227

228+
@SneakyThrows
229+
@Test
230+
@DisplayName("It should start an authorization flow with custom X-Forwarded-For HTTP header")
231+
public void shouldStartAnAuthorizationFlowWithXForwardedForHeader() {
232+
String jsonResponseFile = "payments/200.start_authorization_flow.authorizing.wait.json";
233+
String endUserIpAddress = "11.1.2.3";
234+
RequestStub.New()
235+
.method("post")
236+
.path(urlPathEqualTo("/connect/token"))
237+
.status(200)
238+
.bodyFile("auth/200.access_token.json")
239+
.build();
240+
RequestStub.New()
241+
.method("post")
242+
.path(urlPathEqualTo("/payments/" + A_PAYMENT_ID + "/authorization-flow"))
243+
.withAuthorization()
244+
.withIdempotencyKey()
245+
.withXForwardedForHeader(endUserIpAddress)
246+
.status(200)
247+
.bodyFile(jsonResponseFile)
248+
.build();
249+
StartAuthorizationFlowRequest request =
250+
StartAuthorizationFlowRequest.builder().build();
251+
252+
ApiResponse<AuthorizationFlowResponse> response = tlClient.payments()
253+
.startAuthorizationFlow(A_PAYMENT_ID, request, endUserIpAddress)
254+
.get();
255+
256+
assertNotError(response);
257+
AuthorizationFlowResponse expected =
258+
TestUtils.deserializeJsonFileTo(jsonResponseFile, AuthorizationFlowResponse.class);
259+
assertEquals(expected, response.getData());
260+
}
261+
229262
@SneakyThrows
230263
@ParameterizedTest(name = "and get a response of type {0}")
231264
@ValueSource(strings = {"AUTHORIZING", "FAILED"})

0 commit comments

Comments
 (0)