Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/main/java/com/checkout/ApacheHttpClientTransport.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
Expand Down Expand Up @@ -83,6 +85,15 @@ class ApacheHttpClientTransport implements Transport {
this.baseUri = baseUri;
this.httpClient = httpClientBuilder
.setRedirectStrategy(new CustomAwsRedirectStrategy())
.addInterceptorLast((HttpRequestInterceptor) (request, context) -> {
if (request instanceof HttpEntityEnclosingRequest) {
final HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
if (entity != null && entity.getContentType() != null) {
request.removeHeaders(HttpHeaders.CONTENT_TYPE);
request.setHeader(entity.getContentType());
}
}
})
.build();
this.executor = executor;
this.transportConfiguration = transportConfiguration;
Expand Down Expand Up @@ -152,7 +163,7 @@ public Response invokeSync(final ClientOperation clientOperation,
public Response submitFileSync(final String path, final SdkAuthorization authorization, final AbstractFileRequest fileRequest) {
final HttpPost request = new HttpPost(getRequestUrl(path));
request.setEntity(getMultipartFileEntity(fileRequest));

final Supplier<Response> callSupplier = () -> performCall(authorization, null, request, POST);
return executeWithResilience4j(callSupplier);
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/checkout/CheckoutApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.checkout.issuing.IssuingClient;
import com.checkout.metadata.MetadataClient;
import com.checkout.networktokens.NetworkTokensClient;
import com.checkout.onboardingsimulator.OnboardingSimulatorClient;
import com.checkout.paymentmethods.PaymentMethodsClient;
import com.checkout.payments.PaymentsClient;
import com.checkout.payments.contexts.PaymentContextsClient;
Expand Down Expand Up @@ -109,4 +110,9 @@ public interface CheckoutApi extends CheckoutApmApi {
*/
AgenticCommerceClient agenticCommerceClient();

/**
* Returns the client for the Onboarding Simulator (Sandbox only).
*/
OnboardingSimulatorClient onboardingSimulatorClient();

}
7 changes: 7 additions & 0 deletions src/main/java/com/checkout/CheckoutApiImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import com.checkout.metadata.MetadataClientImpl;
import com.checkout.networktokens.NetworkTokensClient;
import com.checkout.networktokens.NetworkTokensClientImpl;
import com.checkout.onboardingsimulator.OnboardingSimulatorClient;
import com.checkout.onboardingsimulator.OnboardingSimulatorClientImpl;
import com.checkout.paymentmethods.PaymentMethodsClient;
import com.checkout.paymentmethods.PaymentMethodsClientImpl;
import com.checkout.payments.PaymentsClient;
Expand Down Expand Up @@ -107,6 +109,7 @@ public class CheckoutApiImpl extends AbstractCheckoutApmApi implements CheckoutA
private final NetworkTokensClient networkTokensClient;
private final StandaloneAccountUpdaterClient standaloneAccountUpdaterClient;
private final AgenticCommerceClient agenticCommerceClient;
private final OnboardingSimulatorClient onboardingSimulatorClient;

public CheckoutApiImpl(final CheckoutConfiguration configuration) {
super(configuration);
Expand Down Expand Up @@ -146,6 +149,7 @@ public CheckoutApiImpl(final CheckoutConfiguration configuration) {
this.networkTokensClient = new NetworkTokensClientImpl(this.apiClient, configuration);
this.standaloneAccountUpdaterClient = new StandaloneAccountUpdaterClientImpl(this.apiClient, configuration);
this.agenticCommerceClient = new AgenticCommerceClientImpl(this.apiClient, configuration);
this.onboardingSimulatorClient = new OnboardingSimulatorClientImpl(this.apiClient, configuration);
}

@Override
Expand Down Expand Up @@ -284,6 +288,9 @@ public MetadataClient metadataClient() {
@Override
public AgenticCommerceClient agenticCommerceClient() { return agenticCommerceClient; }

@Override
public OnboardingSimulatorClient onboardingSimulatorClient() { return onboardingSimulatorClient; }

private ApiClient getFilesClient(final CheckoutConfiguration configuration) {
return new ApiClientImpl(configuration, new FilesApiUriStrategy(configuration));
}
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/checkout/GsonSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ public final class GsonSerializer implements Serializer {
}.getType();
private static final Type PAYMENT_ACTIONS_TYPE = new TypeToken<ItemsResponse<com.checkout.payments.PaymentAction>>() {
}.getType();
private static final Type SIMULATOR_AVAILABLE_REQUIREMENTS_TYPE = new TypeToken<ItemsResponse<com.checkout.onboardingsimulator.entities.SimulatorAvailableRequirement>>() {
}.getType();
private static final Type SIMULATOR_SCENARIOS_TYPE = new TypeToken<ItemsResponse<com.checkout.onboardingsimulator.entities.SimulatorScenario>>() {
}.getType();

private static final Gson DEFAULT_GSON = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
Expand All @@ -118,6 +122,7 @@ public final class GsonSerializer implements Serializer {
.registerSubtype(com.checkout.handlepaymentsandpayouts.payments.common.source.almasource.AlmaSource.class, identifier(com.checkout.handlepaymentsandpayouts.payments.common.source.SourceType.ALMA))
.registerSubtype(com.checkout.handlepaymentsandpayouts.payments.common.source.bancontactsource.BancontactSource.class, identifier(com.checkout.handlepaymentsandpayouts.payments.common.source.SourceType.BANCONTACT))
.registerSubtype(com.checkout.handlepaymentsandpayouts.payments.common.source.benefitsource.BenefitSource.class, identifier(com.checkout.handlepaymentsandpayouts.payments.common.source.SourceType.BENEFIT))
.registerSubtype(com.checkout.handlepaymentsandpayouts.payments.common.source.bliksource.BlikSource.class, identifier(com.checkout.handlepaymentsandpayouts.payments.common.source.SourceType.BLIK))
.registerSubtype(com.checkout.handlepaymentsandpayouts.payments.common.source.currencyaccountsource.CurrencyAccountSource.class, identifier(com.checkout.handlepaymentsandpayouts.payments.common.source.SourceType.CURRENCY_ACCOUNT))
.registerSubtype(com.checkout.handlepaymentsandpayouts.payments.common.source.cvconnectsource.CvconnectSource.class, identifier(com.checkout.handlepaymentsandpayouts.payments.common.source.SourceType.CVCONNECT))
.registerSubtype(com.checkout.handlepaymentsandpayouts.payments.common.source.danasource.DanaSource.class, identifier(com.checkout.handlepaymentsandpayouts.payments.common.source.SourceType.DANA))
Expand Down Expand Up @@ -233,6 +238,8 @@ public final class GsonSerializer implements Serializer {
.registerTypeAdapter(WEBHOOKS_TYPE, webhooksResponseDeserializer())
.registerTypeAdapter(PREVIOUS_PAYMENT_ACTIONS_TYPE, paymentActionsResponsePreviousDeserializer())
.registerTypeAdapter(PAYMENT_ACTIONS_TYPE, paymentActionsResponseDeserializer())
.registerTypeAdapter(SIMULATOR_AVAILABLE_REQUIREMENTS_TYPE, simulatorAvailableRequirementsResponseDeserializer())
.registerTypeAdapter(SIMULATOR_SCENARIOS_TYPE, simulatorScenariosResponseDeserializer())
.registerTypeAdapter(ProductResponse.class, getProductDeserializer())
.create();

Expand Down Expand Up @@ -344,6 +351,26 @@ private static JsonDeserializer<ItemsResponse<com.checkout.payments.PaymentActio
};
}

private static JsonDeserializer<ItemsResponse<com.checkout.onboardingsimulator.entities.SimulatorAvailableRequirement>> simulatorAvailableRequirementsResponseDeserializer() {
return (json, typeOfT, context) -> {
final ItemsResponse<com.checkout.onboardingsimulator.entities.SimulatorAvailableRequirement> response = new ItemsResponse<>();
if (json.isJsonArray()) {
response.setItems(deserializeJsonArray(json, com.checkout.onboardingsimulator.entities.SimulatorAvailableRequirement.class));
}
return response;
};
}

private static JsonDeserializer<ItemsResponse<com.checkout.onboardingsimulator.entities.SimulatorScenario>> simulatorScenariosResponseDeserializer() {
return (json, typeOfT, context) -> {
final ItemsResponse<com.checkout.onboardingsimulator.entities.SimulatorScenario> response = new ItemsResponse<>();
if (json.isJsonArray()) {
response.setItems(deserializeJsonArray(json, com.checkout.onboardingsimulator.entities.SimulatorScenario.class));
}
return response;
};
}

private static <T> List<T> deserializeJsonArray(final JsonElement json, final Class<T> itemsType) {
final JsonArray jsonArray = json.getAsJsonArray();
return IntStream
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/com/checkout/accounts/AccountsClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,33 @@ CompletableFuture<ReserveRuleCreateResponse> updateReserveRule(String entityId,

CompletableFuture<ReserveRulesResponse> getReserveRules(String entityId);

/**
* Retrieves the list of pending requirements that the sub-entity must resolve.
*
* @param entityId The sub-entity ID.
* @return a {@link CompletableFuture} resolving to the requirements list.
*/
CompletableFuture<EntityRequirementListResponse> getEntityRequirements(String entityId);

/**
* Retrieves detailed information for a single requirement.
*
* @param entityId The sub-entity ID.
* @param requirementId The requirement ID.
* @return a {@link CompletableFuture} resolving to the requirement details.
*/
CompletableFuture<EntityRequirementDetailsResponse> getEntityRequirementDetails(String entityId, String requirementId);

/**
* Submits a response to resolve a requirement.
*
* @param entityId The sub-entity ID.
* @param requirementId The requirement ID.
* @param updateRequest The response payload.
* @return a {@link CompletableFuture} resolving to the resolve response.
*/
CompletableFuture<EntityRequirementUpdateResponse> resolveEntityRequirement(String entityId, String requirementId, EntityRequirementUpdateRequest updateRequest);

// Synchronous methods
IdResponse submitFileSync(final AccountsFileRequest accountsFileRequest);

Expand Down Expand Up @@ -103,4 +130,19 @@ IdResponse updatePaymentInstrumentDetailsSync(final String entityId,

ReserveRulesResponse getReserveRulesSync(final String entityId);

/**
* Synchronous variant of {@link #getEntityRequirements(String)}.
*/
EntityRequirementListResponse getEntityRequirementsSync(final String entityId);

/**
* Synchronous variant of {@link #getEntityRequirementDetails(String, String)}.
*/
EntityRequirementDetailsResponse getEntityRequirementDetailsSync(final String entityId, final String requirementId);

/**
* Synchronous variant of {@link #resolveEntityRequirement(String, String, EntityRequirementUpdateRequest)}.
*/
EntityRequirementUpdateResponse resolveEntityRequirementSync(final String entityId, final String requirementId, final EntityRequirementUpdateRequest updateRequest);

}
57 changes: 57 additions & 0 deletions src/main/java/com/checkout/accounts/AccountsClientImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
private static final String PAYMENT_INSTRUMENTS_PATH = "payment-instruments";
private static final String MEMBERS_PATH = "members";
private static final String RESERVE_RULES_PATH = "reserve-rules";
private static final String REQUIREMENTS_PATH = "requirements";

private final ApiClient filesClient;

Expand Down Expand Up @@ -240,6 +241,34 @@
ReserveRulesResponse.class);
}

@Override
public CompletableFuture<EntityRequirementListResponse> getEntityRequirements(final String entityId) {
validateEntityId(entityId);
return apiClient.getAsync(
buildPath(ACCOUNTS_PATH, ENTITIES_PATH, entityId, REQUIREMENTS_PATH),
sdkAuthorization(),
EntityRequirementListResponse.class);
}

@Override
public CompletableFuture<EntityRequirementDetailsResponse> getEntityRequirementDetails(final String entityId, final String requirementId) {
validateParams("entityId", entityId, "requirementId", requirementId);

Check failure on line 255 in src/main/java/com/checkout/accounts/AccountsClientImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "requirementId" 4 times.

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-java&issues=AZ5o04yNU1aRbXyLJo_V&open=AZ5o04yNU1aRbXyLJo_V&pullRequest=602

Check failure on line 255 in src/main/java/com/checkout/accounts/AccountsClientImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "entityId" 18 times.

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-java&issues=AZ5o04yMU1aRbXyLJo_U&open=AZ5o04yMU1aRbXyLJo_U&pullRequest=602
return apiClient.getAsync(
buildPath(ACCOUNTS_PATH, ENTITIES_PATH, entityId, REQUIREMENTS_PATH, requirementId),
sdkAuthorization(),
EntityRequirementDetailsResponse.class);
}

@Override
public CompletableFuture<EntityRequirementUpdateResponse> resolveEntityRequirement(final String entityId, final String requirementId, final EntityRequirementUpdateRequest updateRequest) {
validateParams("entityId", entityId, "requirementId", requirementId, "updateRequest", updateRequest);
return apiClient.putAsync(
buildPath(ACCOUNTS_PATH, ENTITIES_PATH, entityId, REQUIREMENTS_PATH, requirementId),
sdkAuthorization(),
EntityRequirementUpdateResponse.class,
updateRequest);
}

// Synchronous methods
@Override
public IdResponse submitFileSync(final AccountsFileRequest accountsFileRequest) {
Expand Down Expand Up @@ -434,6 +463,34 @@
ReserveRulesResponse.class);
}

@Override
public EntityRequirementListResponse getEntityRequirementsSync(final String entityId) {
validateEntityId(entityId);
return apiClient.get(
buildPath(ACCOUNTS_PATH, ENTITIES_PATH, entityId, REQUIREMENTS_PATH),
sdkAuthorization(),
EntityRequirementListResponse.class);
}

@Override
public EntityRequirementDetailsResponse getEntityRequirementDetailsSync(final String entityId, final String requirementId) {
validateParams("entityId", entityId, "requirementId", requirementId);
return apiClient.get(
buildPath(ACCOUNTS_PATH, ENTITIES_PATH, entityId, REQUIREMENTS_PATH, requirementId),
sdkAuthorization(),
EntityRequirementDetailsResponse.class);
}

@Override
public EntityRequirementUpdateResponse resolveEntityRequirementSync(final String entityId, final String requirementId, final EntityRequirementUpdateRequest updateRequest) {
validateParams("entityId", entityId, "requirementId", requirementId, "updateRequest", updateRequest);
return apiClient.put(
buildPath(ACCOUNTS_PATH, ENTITIES_PATH, entityId, REQUIREMENTS_PATH, requirementId),
sdkAuthorization(),
EntityRequirementUpdateResponse.class,
updateRequest);
}

// Common methods
private Map<Currency, UpdateScheduleRequest> buildScheduleRequestMap(final Currency currency, final UpdateScheduleRequest updateScheduleRequest) {
final Map<Currency, UpdateScheduleRequest> request = new EnumMap<>(Currency.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.checkout.accounts;

import com.google.gson.annotations.SerializedName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Map;

/**
* Detailed information about a single requirement.
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)

Check notice

Code scanning / CodeQL

Missing Override annotation Note

This method overrides
EntityRequirementListItem.canEqual
; it is advisable to add an Override annotation.
@ToString(callSuper = true)
public final class EntityRequirementDetailsResponse extends EntityRequirementListItem {

/**
* A user-facing explanation of what is needed to resolve the requirement.
* [Optional]
*/
private String message;

/**
* JSON Schema that the value supplied to PUT /accounts/entities/{id}/requirements/{requirementId}
* must conform to. The shape varies by requirement type (for example: an identity document upload,
* a free-text response, or a structured object). May be {@code null} if no schema is registered
* for the requirement's field.
* [Optional]
*/
@SerializedName("_schema")
private Map<String, Object> schema;
}
Loading
Loading