Skip to content

Commit

Permalink
Adds support for Signup+ endpoints and onboards other PayIns improvem…
Browse files Browse the repository at this point in the history
…ents (#322)
  • Loading branch information
dili91 authored Oct 18, 2024
1 parent 709dfe9 commit 7b669a0
Show file tree
Hide file tree
Showing 38 changed files with 976 additions and 96 deletions.
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
plugins {
id 'java-library'
// to unleash the lombok magic
id "io.freefair.lombok" version "8.10"
id "io.freefair.lombok" version "8.10.2"
// to make our tests output more fancy
id 'com.adarshr.test-logger' version '4.0.0'
// to publish packages
Expand Down Expand Up @@ -108,10 +108,10 @@ dependencies {
implementation group: 'org.tinylog', name: 'tinylog-impl', version: tinyLogVersion

// JUnit test framework.
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.11.0'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.11.2'

// Mocking libraries
testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.13.0'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.14.2'
testImplementation group: 'org.wiremock', name: 'wiremock', version: '3.9.1'

// Wait test utility
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Main properties
group=com.truelayer
archivesBaseName=truelayer-java
version=14.3.0
version=15.0.0

# Artifacts properties
sonatype_repository_url=https://s01.oss.sonatype.org/service/local/
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
21 changes: 12 additions & 9 deletions gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#

##############################################################################
#
Expand Down Expand Up @@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Expand Down Expand Up @@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
Expand Down Expand Up @@ -145,15 +148,15 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
Expand Down Expand Up @@ -202,11 +205,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.

set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
Expand Down
22 changes: 12 additions & 10 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem

@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
Expand Down Expand Up @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

Expand All @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2

goto fail

Expand Down
4 changes: 2 additions & 2 deletions justfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
alias b := build
alias l := lint
alias f := format
alias ut := unit-test
alias it := integration-test
alias at := acceptance-test
Expand All @@ -8,7 +8,7 @@ alias t := test
build:
./gradlew build -x test

lint:
format:
./gradlew spotlessApply

unit-test:
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/truelayer/java/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public static final class Scopes {
public static final String RECURRING_PAYMENTS_SWEEPING = "recurring_payments:sweeping";

public static final String RECURRING_PAYMENTS_COMMERCIAL = "recurring_payments:commercial";

public static final String SIGNUP_PLUS = "signupplus";
}

/**
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/com/truelayer/java/ITrueLayerClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.truelayer.java.payments.IPaymentsHandler;
import com.truelayer.java.paymentsproviders.IPaymentsProvidersHandler;
import com.truelayer.java.payouts.IPayoutsHandler;
import com.truelayer.java.signupplus.ISignupPlusHandler;
import java.util.concurrent.CompletableFuture;

/**
Expand Down Expand Up @@ -53,6 +54,12 @@ public interface ITrueLayerClient {
*/
IPayoutsHandler payouts();

/**
* Entrypoint for signup+ endpoints.
* @return a utility to interact with signup+ endpoints.
*/
ISignupPlusHandler signupPlus();

/**
* Entrypoint for Hosted Payment Page related services.
* @return a utility to build a Hosted Payment Page URL.
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/com/truelayer/java/TrueLayerClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.truelayer.java.payments.IPaymentsHandler;
import com.truelayer.java.paymentsproviders.IPaymentsProvidersHandler;
import com.truelayer.java.payouts.IPayoutsHandler;
import com.truelayer.java.signupplus.ISignupPlusHandler;
import java.util.concurrent.CompletableFuture;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
Expand All @@ -30,17 +31,20 @@ public class TrueLayerClient implements ITrueLayerClient {
private IMerchantAccountsHandler merchantAccountsHandler;
private IMandatesHandler mandatesHandler;
private IPayoutsHandler payoutsHandler;
private ISignupPlusHandler signupPlusHandler;
private ICommonHandler commonHandler;

private IHostedPaymentPageLinkBuilder hostedPaymentPageLinkBuilder;

public TrueLayerClient(
IAuthenticationHandler authenticationHandler,
IHostedPaymentPageLinkBuilder hostedPaymentPageLinkBuilder,
ICommonHandler commonHandler) {
ICommonHandler commonHandler,
ISignupPlusHandler signupPlusHandler) {
this.authenticationHandler = authenticationHandler;
this.hostedPaymentPageLinkBuilder = hostedPaymentPageLinkBuilder;
this.commonHandler = commonHandler;
this.signupPlusHandler = signupPlusHandler;
}

/**
Expand Down Expand Up @@ -108,6 +112,14 @@ public IPayoutsHandler payouts() {
return payoutsHandler;
}

@Override
public ISignupPlusHandler signupPlus() {
if (ObjectUtils.isEmpty(signupPlusHandler)) {
throw buildInitializationException("signup plus");
}
return signupPlusHandler;
}

/**
* {@inheritDoc}
*/
Expand Down
31 changes: 25 additions & 6 deletions src/main/java/com/truelayer/java/TrueLayerClientBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
import com.truelayer.java.payouts.IPayoutsApi;
import com.truelayer.java.payouts.IPayoutsHandler;
import com.truelayer.java.payouts.PayoutsHandler;
import com.truelayer.java.signupplus.ISignupPlusApi;
import com.truelayer.java.signupplus.ISignupPlusHandler;
import com.truelayer.java.signupplus.SignupPlusHandler;
import com.truelayer.java.versioninfo.LibraryInfoLoader;
import java.time.Clock;
import java.time.Duration;
Expand Down Expand Up @@ -218,30 +221,45 @@ public TrueLayerClient build() {
OkHttpClient baseHttpClient = httpClientFactory.buildBaseApiClient(
timeout, connectionPoolOptions, requestExecutor, logMessageConsumer, proxyConfiguration);

OkHttpClient authHttpClient = httpClientFactory.buildAuthApiClient(baseHttpClient, clientCredentials);
OkHttpClient authServerApiHttpClient =
httpClientFactory.buildAuthServerApiClient(baseHttpClient, clientCredentials);

IAuthenticationHandler authenticationHandler = AuthenticationHandler.New()
.clientCredentials(clientCredentials)
.httpClient(RetrofitFactory.build(authHttpClient, environment.getAuthApiUri()))
.httpClient(RetrofitFactory.build(authServerApiHttpClient, environment.getAuthApiUri()))
.build();

IHostedPaymentPageLinkBuilder hppLinkBuilder =
HostedPaymentPageLinkBuilder.New().uri(environment.getHppUri()).build();

// We're reusing a client with only User agent and Idempotency key interceptors and give it our base payment
// endpoint
ICommonApi commonApi = RetrofitFactory.build(authHttpClient, environment.getPaymentsApiUri())
ICommonApi commonApi = RetrofitFactory.build(authServerApiHttpClient, environment.getPaymentsApiUri())
.create(ICommonApi.class);
ICommonHandler commonHandler = new CommonHandler(commonApi);

// We're building a client which has the authentication handler and the options to cache the token.
// this one represents the baseline for the client used for Signup+ and Payments
OkHttpClient authenticatedApiClient = httpClientFactory.buildAuthenticatedApiClient(
authServerApiHttpClient, authenticationHandler, credentialsCache);
ISignupPlusApi signupPlusApi = RetrofitFactory.build(authenticatedApiClient, environment.getPaymentsApiUri())
.create(ISignupPlusApi.class);
SignupPlusHandler.SignupPlusHandlerBuilder signupPlusHandlerBuilder =
SignupPlusHandler.builder().signupPlusApi(signupPlusApi);
if (customScopesPresent()) {
signupPlusHandlerBuilder.scopes(globalScopes);
}
ISignupPlusHandler signupPlusHandler = signupPlusHandlerBuilder.build();

// As per our RFC, if signing options is not configured we create a client which is able to interact
// with the Authentication API only
if (isEmpty(signingOptions)) {
return new TrueLayerClient(authenticationHandler, hppLinkBuilder, commonHandler);
return new TrueLayerClient(authenticationHandler, hppLinkBuilder, commonHandler, signupPlusHandler);
}

OkHttpClient paymentsHttpClient = httpClientFactory.buildPaymentsApiClient(
authHttpClient, authenticationHandler, signingOptions, credentialsCache);
// The client used for PayIn endpoints has the authenticated as baseline, but adds the signature manager
OkHttpClient paymentsHttpClient =
httpClientFactory.buildPaymentsApiClient(authenticatedApiClient, signingOptions);

IPaymentsApi paymentsApi = RetrofitFactory.build(paymentsHttpClient, environment.getPaymentsApiUri())
.create(IPaymentsApi.class);
Expand Down Expand Up @@ -299,6 +317,7 @@ public TrueLayerClient build() {
merchantAccountsHandler,
mandatesHandler,
payoutsHandler,
signupPlusHandler,
commonHandler,
hppLinkBuilder);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/truelayer/java/entities/Address.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class Address {
private String countryCode;

@JsonGetter
private Optional<String> getAddressLine2() {
public Optional<String> getAddressLine2() {
return Optional.ofNullable(addressLine2);
}
}
8 changes: 4 additions & 4 deletions src/main/java/com/truelayer/java/entities/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ public class User {
private Address address;

@JsonGetter
private Optional<String> getEmail() {
public Optional<String> getEmail() {
return Optional.ofNullable(email);
}

@JsonGetter
private Optional<String> getPhone() {
public Optional<String> getPhone() {
return Optional.ofNullable(phone);
}

@JsonGetter
private Optional<LocalDate> getDateOfBirth() {
public Optional<LocalDate> getDateOfBirth() {
return Optional.ofNullable(dateOfBirth);
}

@JsonGetter
private Optional<Address> getAddress() {
public Optional<Address> getAddress() {
return Optional.ofNullable(address);
}
}
Loading

0 comments on commit 7b669a0

Please sign in to comment.