Skip to content

Commit 85f8e66

Browse files
committed
add hosted page and user consent support to payment creation
Enables hosted payment page configuration and user consent tracking for payment requests. Includes integration tests for both scenarios.
1 parent e042cfe commit 85f8e66

File tree

6 files changed

+160
-3
lines changed

6 files changed

+160
-3
lines changed

src/main/java/com/truelayer/java/payments/entities/CreatePaymentRequest.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.truelayer.java.entities.User;
66
import com.truelayer.java.payments.entities.paymentmethod.PaymentMethod;
77
import com.truelayer.java.payments.entities.submerchants.SubMerchants;
8+
import com.truelayer.java.payments.entities.userconsent.UserConsent;
89
import java.util.Map;
910
import lombok.Builder;
1011
import lombok.EqualsAndHashCode;
@@ -46,7 +47,13 @@ public class CreatePaymentRequest {
4647
*/
4748
private StartAuthorizationFlowRequest authorizationFlow;
4849

49-
// TODO: hosted page
50+
/**
51+
* Optional field for configuring the hosted payment page
52+
*/
53+
private HostedPageParameters hostedPage;
5054

51-
// TODO: user consent
55+
/**
56+
* Optional field for user consent details
57+
*/
58+
private UserConsent userConsent;
5259
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.truelayer.java.payments.entities;
2+
3+
import java.net.URI;
4+
import lombok.Builder;
5+
import lombok.EqualsAndHashCode;
6+
import lombok.Getter;
7+
import lombok.ToString;
8+
9+
@Builder
10+
@Getter
11+
@ToString
12+
@EqualsAndHashCode
13+
public class HostedPageParameters {
14+
private URI returnUri;
15+
16+
private CountryCode countryCode;
17+
18+
/**
19+
* A valid ISO 639-1 language code
20+
*/
21+
private String languageCode;
22+
23+
/**
24+
* The maximum time to wait for a result from the hosted page after the user has completed the authorization flow, in seconds.
25+
*/
26+
private int maxWaitForResults;
27+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.truelayer.java.payments.entities.userconsent;
2+
3+
import lombok.Builder;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.Getter;
6+
7+
@Builder
8+
@Getter
9+
@EqualsAndHashCode(callSuper = false)
10+
public class AuthorizationFlowCapturedConsent extends UserConsent {
11+
private final UserConsent.Type type = Type.AUTHORIZATION_FLOW_CAPTURED;
12+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.truelayer.java.payments.entities.userconsent;
2+
3+
import com.fasterxml.jackson.annotation.JsonFormat;
4+
import java.time.ZonedDateTime;
5+
import lombok.Builder;
6+
import lombok.EqualsAndHashCode;
7+
import lombok.Getter;
8+
9+
@Builder
10+
@Getter
11+
@EqualsAndHashCode(callSuper = false)
12+
public class PrecapturedConsent extends UserConsent {
13+
private final UserConsent.Type type = Type.PRECAPTURED;
14+
15+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
16+
ZonedDateTime capturedAt;
17+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.truelayer.java.payments.entities.userconsent;
2+
3+
import com.fasterxml.jackson.annotation.JsonSubTypes;
4+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
5+
import com.fasterxml.jackson.annotation.JsonValue;
6+
import lombok.EqualsAndHashCode;
7+
import lombok.Getter;
8+
import lombok.RequiredArgsConstructor;
9+
import lombok.ToString;
10+
11+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
12+
@JsonSubTypes({
13+
@JsonSubTypes.Type(value = AuthorizationFlowCapturedConsent.class, name = "authorization_flow_captured"),
14+
@JsonSubTypes.Type(value = PrecapturedConsent.class, name = "precaptured")
15+
})
16+
@ToString
17+
@EqualsAndHashCode
18+
@Getter
19+
public abstract class UserConsent {
20+
21+
@Getter
22+
@RequiredArgsConstructor
23+
public enum Type {
24+
PRECAPTURED("precaptured"),
25+
AUTHORIZATION_FLOW_CAPTURED("authorization_flow_captured");
26+
27+
@JsonValue
28+
private final String type;
29+
}
30+
}

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

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@
3333
import com.truelayer.java.payments.entities.schemeselection.userselected.SchemeSelection;
3434
import com.truelayer.java.payments.entities.submerchants.SubMerchants;
3535
import com.truelayer.java.payments.entities.submerchants.UltimateCounterparty;
36+
import com.truelayer.java.payments.entities.userconsent.AuthorizationFlowCapturedConsent;
37+
import com.truelayer.java.payments.entities.userconsent.PrecapturedConsent;
3638
import com.truelayer.java.payments.entities.verification.AutomatedVerification;
3739
import java.net.URI;
40+
import java.time.ZonedDateTime;
41+
import java.time.format.DateTimeFormatter;
3842
import java.util.*;
3943
import java.util.stream.Stream;
4044
import lombok.SneakyThrows;
@@ -660,6 +664,8 @@ public void shouldCreatePaymentWithAuthorizationFlow() {
660664
.bodyFile("payments/201.create_payment.AUTHORIZATION_REQUIRED.json")
661665
.build();
662666

667+
ZonedDateTime consentCapturedAt = ZonedDateTime.now();
668+
663669
CreatePaymentRequest paymentRequest = CreatePaymentRequest.builder()
664670
.amountInMinor(100)
665671
.currency(CurrencyCode.GBP)
@@ -696,6 +702,9 @@ public void shouldCreatePaymentWithAuthorizationFlow() {
696702
.build())
697703
.build())
698704
.riskAssessment(RiskAssessment.builder().segment("Flights").build())
705+
.userConsent(PrecapturedConsent.builder()
706+
.capturedAt(consentCapturedAt)
707+
.build())
699708
.build();
700709

701710
tlClient.payments().createPayment(paymentRequest).get();
@@ -729,6 +738,61 @@ public void shouldCreatePaymentWithAuthorizationFlow() {
729738
.withRequestBody(matchingJsonPath("$.sub_merchants.ultimate_counterparty.id", equalTo("an-id")))
730739
.withRequestBody(
731740
matchingJsonPath("$.sub_merchants.ultimate_counterparty.name", equalTo("business-name")))
732-
.withRequestBody(matchingJsonPath("$.risk_assessment.segment", equalTo("Flights"))));
741+
.withRequestBody(matchingJsonPath("$.risk_assessment.segment", equalTo("Flights")))
742+
.withRequestBody(matchingJsonPath("$.user_consent.type", equalTo("precaptured")))
743+
.withRequestBody(matchingJsonPath(
744+
"$.user_consent.captured_at",
745+
equalTo(consentCapturedAt
746+
.truncatedTo(java.time.temporal.ChronoUnit.SECONDS)
747+
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)))));
748+
}
749+
750+
@Test
751+
@DisplayName("It should create a payment with hosted_page_parameters")
752+
@SneakyThrows
753+
public void shouldCreatePaymentWithHostedPageParameters() {
754+
RequestStub.New()
755+
.method("post")
756+
.path(urlPathEqualTo("/connect/token"))
757+
.status(200)
758+
.bodyFile("auth/200.access_token.json")
759+
.build();
760+
RequestStub.New()
761+
.method("post")
762+
.path(urlPathEqualTo("/payments"))
763+
.withAuthorization()
764+
.withSignature()
765+
.withIdempotencyKey()
766+
.status(201)
767+
.bodyFile("payments/201.create_payment.AUTHORIZATION_REQUIRED.json")
768+
.build();
769+
770+
ZonedDateTime consentCapturedAt = ZonedDateTime.now();
771+
772+
CreatePaymentRequest paymentRequest = CreatePaymentRequest.builder()
773+
.amountInMinor(100)
774+
.currency(CurrencyCode.GBP)
775+
.paymentMethod(PaymentMethod.bankTransfer().build())
776+
.hostedPage(HostedPageParameters.builder()
777+
.returnUri(URI.create("https://example.com/callback"))
778+
.countryCode(CountryCode.GB)
779+
.languageCode("en")
780+
.maxWaitForResults(300)
781+
.build())
782+
.userConsent(AuthorizationFlowCapturedConsent.builder().build())
783+
.build();
784+
785+
tlClient.payments().createPayment(paymentRequest).get();
786+
787+
verifyGeneratedToken(Collections.singletonList(PAYMENTS));
788+
verify(postRequestedFor(urlPathEqualTo("/payments"))
789+
.withRequestBody(matchingJsonPath("$.amount_in_minor", equalTo("100")))
790+
.withRequestBody(matchingJsonPath("$.currency", equalTo("GBP")))
791+
.withRequestBody(matchingJsonPath("$.payment_method.type", equalTo("bank_transfer")))
792+
.withRequestBody(matchingJsonPath("$.hosted_page.return_uri", equalTo("https://example.com/callback")))
793+
.withRequestBody(matchingJsonPath("$.hosted_page.country_code", equalTo("GB")))
794+
.withRequestBody(matchingJsonPath("$.hosted_page.language_code", equalTo("en")))
795+
.withRequestBody(matchingJsonPath("$.hosted_page.max_wait_for_results", equalTo("300")))
796+
.withRequestBody(matchingJsonPath("$.user_consent.type", equalTo("authorization_flow_captured"))));
733797
}
734798
}

0 commit comments

Comments
 (0)