Skip to content

Commit 1570649

Browse files
authored
[ACL-305] Adds support for Verified Payouts (#380)
1 parent 2cb4f94 commit 1570649

40 files changed

Lines changed: 1047 additions & 43 deletions

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/).
77

8+
## [17.5.0]
9+
### Added
10+
* Add support for Verified Payouts with user-determined beneficiary type
11+
* Add HPP link builder support for payouts
12+
813
## [17.4.0] - 2025-08-07
914
### Added
1015
* Add support for `scheme_id` field in merchant account transaction responses for Payout and Refund transactions

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=17.4.0
4+
version=17.5.0
55

66
# Artifacts properties
77
project_name=TrueLayer Java

src/main/java/com/truelayer/java/Environment.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public class Environment {
1414

1515
private final URI hppUri;
1616

17+
private final URI hp2Uri;
18+
1719
private static final String PAYMENTS_API_DEFAULT_VERSION = "v3";
1820

1921
/**
@@ -24,7 +26,8 @@ public static Environment development() {
2426
return new Environment(
2527
URI.create("https://auth.t7r.dev"),
2628
URI.create(MessageFormat.format("https://api.t7r.dev/{0}/", PAYMENTS_API_DEFAULT_VERSION)),
27-
URI.create("https://payment.t7r.dev"));
29+
URI.create("https://payment.t7r.dev"),
30+
URI.create("https://app.t7r.dev"));
2831
}
2932

3033
/**
@@ -36,7 +39,8 @@ public static Environment sandbox() {
3639
URI.create("https://auth.truelayer-sandbox.com"),
3740
URI.create(
3841
MessageFormat.format("https://api.truelayer-sandbox.com/{0}/", PAYMENTS_API_DEFAULT_VERSION)),
39-
URI.create("https://payment.truelayer-sandbox.com"));
42+
URI.create("https://payment.truelayer-sandbox.com"),
43+
URI.create("https://app.truelayer-sandbox.com"));
4044
}
4145

4246
/**
@@ -47,17 +51,19 @@ public static Environment live() {
4751
return new Environment(
4852
URI.create("https://auth.truelayer.com"),
4953
URI.create(MessageFormat.format("https://api.truelayer.com/{0}/", PAYMENTS_API_DEFAULT_VERSION)),
50-
URI.create("https://payment.truelayer.com"));
54+
URI.create("https://payment.truelayer.com"),
55+
URI.create("https://app.truelayer.com"));
5156
}
5257

5358
/**
5459
* Custom environment builder. Meant for testing purposes
5560
* @param authApiUri the authentication API endpoint
5661
* @param paymentsApiUri the Payments API endpoint
5762
* @param hppUri the <i>Hosted Payment Page</i> endpoint
63+
* @param hp2Uri the new <i>Hosted Payment Page</i> endpoint
5864
* @return a custom environment object
5965
*/
60-
public static Environment custom(URI authApiUri, URI paymentsApiUri, URI hppUri) {
61-
return new Environment(authApiUri, paymentsApiUri, hppUri);
66+
public static Environment custom(URI authApiUri, URI paymentsApiUri, URI hppUri, URI hp2Uri) {
67+
return new Environment(authApiUri, paymentsApiUri, hppUri, hp2Uri);
6268
}
6369
}

src/main/java/com/truelayer/java/HostedPaymentPageLinkBuilder.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public URI build() {
6464

6565
URI hppLink = URI.create(MessageFormat.format(
6666
"{0}/{1}#{2}={3}&resource_token={4}&return_uri={5}",
67-
environment.getHppUri(),
67+
getHppLinkForResourceType(resourceType, environment),
6868
resourceType.getHppLinkPath(),
6969
resourceType.getHppLinkQueryParameter(),
7070
resourceId,
@@ -81,4 +81,18 @@ public URI build() {
8181

8282
return hppLink;
8383
}
84+
85+
private URI getHppLinkForResourceType(ResourceType resourceType, Environment environment) {
86+
URI hostedPageUri;
87+
switch (resourceType.getHostedPageType()) {
88+
case HP2:
89+
hostedPageUri = environment.getHp2Uri();
90+
break;
91+
case HPP:
92+
default:
93+
hostedPageUri = environment.getHppUri();
94+
break;
95+
}
96+
return hostedPageUri;
97+
}
8498
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.truelayer.java.entities;
2+
3+
public enum HostedPageType {
4+
HPP,
5+
HP2
6+
}
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package com.truelayer.java.entities;
22

3+
import static com.truelayer.java.entities.HostedPageType.HP2;
4+
import static com.truelayer.java.entities.HostedPageType.HPP;
5+
36
import lombok.Getter;
47
import lombok.RequiredArgsConstructor;
58

69
@RequiredArgsConstructor
710
@Getter
811
public enum ResourceType {
9-
PAYMENT("payments", "payment_id"),
10-
MANDATE("mandates", "mandate_id"),
12+
PAYMENT("payments", "payment_id", HPP),
13+
MANDATE("mandates", "mandate_id", HPP),
14+
PAYOUT("payouts", "payout_id", HP2),
1115
;
1216

1317
private final String hppLinkPath;
1418
private final String hppLinkQueryParameter;
19+
private final HostedPageType hostedPageType;
1520
}

src/main/java/com/truelayer/java/entities/beneficiary/Beneficiary.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
@JsonSubTypes({
2121
@JsonSubTypes.Type(value = ExternalAccount.class, name = "external_account"),
2222
@JsonSubTypes.Type(value = BusinessAccount.class, name = "business_account"),
23-
@JsonSubTypes.Type(value = PaymentSource.class, name = "payment_source")
23+
@JsonSubTypes.Type(value = PaymentSource.class, name = "payment_source"),
24+
@JsonSubTypes.Type(value = UserDetermined.class, name = "user_determined")
2425
})
2526
@ToString
2627
@EqualsAndHashCode
@@ -45,6 +46,11 @@ public boolean isBusinessAccount() {
4546
return this instanceof BusinessAccount;
4647
}
4748

49+
@JsonIgnore
50+
public boolean isUserDetermined() {
51+
return this instanceof UserDetermined;
52+
}
53+
4854
@JsonIgnore
4955
public BusinessAccount asBusinessAccount() {
5056
if (!isBusinessAccount()) {
@@ -69,6 +75,14 @@ public PaymentSource asPaymentSource() {
6975
return (PaymentSource) this;
7076
}
7177

78+
@JsonIgnore
79+
public UserDetermined asUserDetermined() {
80+
if (!isUserDetermined()) {
81+
throw new TrueLayerException(buildErrorMessage());
82+
}
83+
return (UserDetermined) this;
84+
}
85+
7286
private String buildErrorMessage() {
7387
return String.format("Beneficiary is of type %s.", this.getClass().getSimpleName());
7488
}
@@ -78,7 +92,8 @@ private String buildErrorMessage() {
7892
public enum Type {
7993
EXTERNAL_ACCOUNT("external_account"),
8094
PAYMENT_SOURCE("payment_source"),
81-
BUSINESS_ACCOUNT("business_account");
95+
BUSINESS_ACCOUNT("business_account"),
96+
USER_DETERMINED("user_determined");
8297

8398
@JsonValue
8499
private final String type;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.truelayer.java.entities.beneficiary;
2+
3+
import static com.truelayer.java.entities.beneficiary.Beneficiary.Type.USER_DETERMINED;
4+
5+
import com.truelayer.java.merchantaccounts.entities.transactions.accountidentifier.AccountIdentifier;
6+
import com.truelayer.java.payouts.entities.PayoutUser;
7+
import com.truelayer.java.payouts.entities.beneficiary.Verification;
8+
import java.util.List;
9+
import lombok.EqualsAndHashCode;
10+
import lombok.Value;
11+
12+
@Value
13+
@EqualsAndHashCode(callSuper = false)
14+
public class UserDetermined extends Beneficiary {
15+
Type type = USER_DETERMINED;
16+
17+
String reference;
18+
19+
String accountHolderName;
20+
21+
List<AccountIdentifier> accountIdentifiers;
22+
23+
PayoutUser user;
24+
25+
Verification verification;
26+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.truelayer.java.payouts.entities;
2+
3+
import com.truelayer.java.payouts.entities.beneficiary.Beneficiary;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.Getter;
6+
import lombok.ToString;
7+
8+
@Getter
9+
@ToString(callSuper = true)
10+
@EqualsAndHashCode(callSuper = true)
11+
public class AuthorizationRequiredPayout extends CreatePayoutResponse {
12+
private final Status status = Status.AUTHORIZATION_REQUIRED;
13+
private String resourceToken;
14+
private PayoutUser user;
15+
private Beneficiary beneficiary;
16+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.truelayer.java.payouts.entities;
2+
3+
import lombok.EqualsAndHashCode;
4+
import lombok.Value;
5+
6+
@Value
7+
@EqualsAndHashCode(callSuper = false)
8+
public class AuthorizationRequiredPayoutDetail extends Payout {
9+
Status status = Status.AUTHORIZATION_REQUIRED;
10+
}

0 commit comments

Comments
 (0)