Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f46de13
Endpoint to simulate refunds + tests
david-ruiz-cko Feb 26, 2026
b578f75
Upgraded Transport class to be able to send url encoded content param…
david-ruiz-cko Feb 26, 2026
fd0e4d8
Moved url encoded helper code to GsonSerializer
david-ruiz-cko Feb 27, 2026
a58c577
requestCardholderAccessToken functionality and tests
david-ruiz-cko Feb 27, 2026
0c3dd30
Added FormUrlEncodedContentTests to test all createFormUrlEncodedCont…
david-ruiz-cko Feb 27, 2026
501acea
Feature new updateCardholder endpoint
david-ruiz-cko Feb 27, 2026
be5e0ca
issuing/cards missing methods initial implementation, must review + u…
david-ruiz-cko Feb 27, 2026
7b7a7e0
issuing/cards missing methods fine tune
david-ruiz-cko Mar 2, 2026
ebb10d6
Issuing new methods unit + integration tests
david-ruiz-cko Mar 2, 2026
4a0e20d
Issuing control groups and control profiles implementation
david-ruiz-cko Mar 2, 2026
b5b0c03
Unit and integration tests for issuing control groups and issuing con…
david-ruiz-cko Mar 2, 2026
c59f498
New face authentication module, first implementation
david-ruiz-cko Mar 3, 2026
27e1052
Identities Face Authentication unit and integration tests
david-ruiz-cko Mar 3, 2026
7242d9f
New Identities Applicants module
david-ruiz-cko Mar 3, 2026
dae3fa6
Rest of the Identities modules: amlScreening, IdDocumentVerification,…
david-ruiz-cko Mar 3, 2026
8402f08
Payment Setups bug - error in the paymentType deserialization
david-ruiz-cko Mar 3, 2026
33c80bb
Identites module first review, must finish
david-ruiz-cko Mar 4, 2026
0ff8505
Eliminated nested class
david-ruiz-cko Mar 4, 2026
9a5ee95
Final review of Identites module, full functionality completed
david-ruiz-cko Mar 4, 2026
78802d3
One GSONSerializer tests fails in the pipeline, deactivated
david-ruiz-cko Mar 5, 2026
37b3b36
Simplified ControlGroup polymorphic resolution using the standard JSO…
david-ruiz-cko Mar 5, 2026
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
51 changes: 44 additions & 7 deletions src/main/java/com/checkout/ApacheHttpClientTransport.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
Expand Down Expand Up @@ -51,6 +52,7 @@
import static com.checkout.common.CheckoutUtils.getVersionFromManifest;
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.HttpHeaders.AUTHORIZATION;
import static org.apache.http.HttpHeaders.CONTENT_TYPE;
import static org.apache.http.HttpHeaders.USER_AGENT;

@Slf4j
Expand All @@ -67,6 +69,7 @@ class ApacheHttpClientTransport implements Transport {
private final Executor executor;
private final TransportConfiguration transportConfiguration;
private final CheckoutConfiguration configuration;
private final Serializer serializer;

private static final ThreadLocal<RequestMetrics> telemetryData = ThreadLocal.withInitial(RequestMetrics::new);

Expand All @@ -85,15 +88,18 @@ class ApacheHttpClientTransport implements Transport {
this.executor = executor;
this.transportConfiguration = transportConfiguration;
this.configuration = configuration;
this.serializer = new GsonSerializer();
}

@Override
public CompletableFuture<Response> invoke(final ClientOperation clientOperation,
final String path,
final SdkAuthorization authorization,
final String requestBody,
final Object requestObject,
final String idempotencyKey,
final Map<String, String> queryParams) {

final Object requestBodyOrEntity = prepareRequestContent(requestObject);
return CompletableFuture.supplyAsync(() -> {
final HttpUriRequest request;
switch (clientOperation) {
Expand Down Expand Up @@ -129,7 +135,7 @@ public CompletableFuture<Response> invoke(final ClientOperation clientOperation,
if (idempotencyKey != null) {
request.setHeader(CKO_IDEMPOTENCY_KEY, idempotencyKey);
}
return performCall(authorization, requestBody, request, clientOperation);
return performCall(authorization, requestBodyOrEntity, request, clientOperation);
}, executor);
}

Expand All @@ -146,9 +152,10 @@ public CompletableFuture<Response> submitFile(final String path, final SdkAuthor
public Response invokeSync(final ClientOperation clientOperation,
final String path,
final SdkAuthorization authorization,
final String requestBody,
final Object requestObject,
final String idempotencyKey,
final Map<String, String> queryParams) {
final Object requestBodyOrEntity = prepareRequestContent(requestObject);
final HttpUriRequest request;
switch (clientOperation) {
case GET:
Expand Down Expand Up @@ -184,7 +191,7 @@ public Response invokeSync(final ClientOperation clientOperation,
request.setHeader(CKO_IDEMPOTENCY_KEY, idempotencyKey);
}

final Supplier<Response> callSupplier = () -> performCall(authorization, requestBody, request, clientOperation);
final Supplier<Response> callSupplier = () -> performCall(authorization, requestBodyOrEntity, request, clientOperation);
return executeWithResilience4j(callSupplier);
}

Expand Down Expand Up @@ -228,6 +235,29 @@ private Response executeWithResilience4j(final Supplier<Response> supplier) {
return decoratedSupplier.get();
}

/**
* Prepares the request content based on the type of request object.
* If the request object is a UrlEncodedFormEntity, it returns it directly.
* Otherwise, it serializes the object to JSON.
*/
private Object prepareRequestContent(final Object requestObject) {
Object request = null;

if (requestObject != null) {
if (requestObject instanceof UrlEncodedFormEntity) {
// Return the form entity directly for form-encoded requests
request = requestObject;
}
else
{
// Default behavior: serialize to JSON
request = serializer.toJson(requestObject);
}
}

return request;
}

private HttpEntity getMultipartFileEntity(final AbstractFileRequest abstractFileRequest) {
final MultipartEntityBuilder builder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
if (abstractFileRequest instanceof FileRequest) {
Expand All @@ -247,7 +277,7 @@ private HttpEntity getMultipartFileEntity(final AbstractFileRequest abstractFile
}

private Response performCall(final SdkAuthorization authorization,
final String requestBody,
final Object requestBodyOrEntity,
final HttpUriRequest request,
final ClientOperation clientOperation) {
log.info("{}: {}", clientOperation, request.getURI());
Expand All @@ -262,8 +292,15 @@ private Response performCall(final SdkAuthorization authorization,
long startTime = System.currentTimeMillis();

log.info("Request: " + Arrays.toString(sanitiseHeaders(request.getAllHeaders())));
if (requestBody != null && request instanceof HttpEntityEnclosingRequest) {
((HttpEntityEnclosingRequestBase) request).setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON));
if (requestBodyOrEntity != null && request instanceof HttpEntityEnclosingRequest) {
if (requestBodyOrEntity instanceof UrlEncodedFormEntity) {
// Handle form-encoded content
request.setHeader(CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
((HttpEntityEnclosingRequestBase) request).setEntity((UrlEncodedFormEntity) requestBodyOrEntity);
} else if (requestBodyOrEntity instanceof String) {
// Handle JSON content
((HttpEntityEnclosingRequestBase) request).setEntity(new StringEntity((String) requestBodyOrEntity, ContentType.APPLICATION_JSON));
}
}
try (final CloseableHttpResponse response = httpClient.execute(request)) {
long elapsed = System.currentTimeMillis() - startTime;
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/com/checkout/ApiClientImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public CompletableFuture<? extends HttpMetadata> postAsync(final String path, fi
validateParams(PATH, path, AUTHORIZATION, authorization, "resultTypeMappings", resultTypeMappings);
return executeAsyncOrSync(
() -> post(path, authorization, resultTypeMappings, request, idempotencyKey),
() -> transport.invoke(POST, path, authorization, serializer.toJson(request), idempotencyKey, null)
() -> transport.invoke(POST, path, authorization, request, idempotencyKey, null)
.thenApply(this::errorCheck)
.thenApply(response -> {
final Class<? extends HttpMetadata> responseType = resultTypeMappings.get(response.getStatusCode());
Expand Down Expand Up @@ -231,7 +231,7 @@ private <T> CompletableFuture<T> executeAsyncOrSync(final java.util.function.Sup
}

private <T extends HttpMetadata> CompletableFuture<T> sendRequestAsync(final ClientOperation clientOperation, final String path, final SdkAuthorization authorization, final Object request, final String idempotencyKey, final Type responseType) {
return transport.invoke(clientOperation, path, authorization, request == null ? null : serializer.toJson(request), idempotencyKey, null)
return transport.invoke(clientOperation, path, authorization, request, idempotencyKey, null)
.thenApply(this::errorCheck)
.thenApply(response -> deserialize(response, responseType));
}
Expand Down Expand Up @@ -343,7 +343,7 @@ public <T extends HttpMetadata> T delete(final String path, final SdkAuthorizati
@Override
public HttpMetadata post(final String path, final SdkAuthorization authorization, final Map<Integer, Class<? extends HttpMetadata>> resultTypeMappings, final Object request, final String idempotencyKey) {
validateParams(PATH, path, AUTHORIZATION, authorization, "resultTypeMappings", resultTypeMappings);
final Response response = transport.invokeSync(POST, path, authorization, serializer.toJson(request), idempotencyKey, null);
final Response response = transport.invokeSync(POST, path, authorization, request, idempotencyKey, null);
final Response checkedResponse = errorCheck(response);
final Class<? extends HttpMetadata> responseType = resultTypeMappings.get(checkedResponse.getStatusCode());
if (responseType == null) {
Expand Down Expand Up @@ -393,7 +393,7 @@ public <T extends HttpMetadata> T submitFile(final String path, final SdkAuthori
}

private <T extends HttpMetadata> T sendRequestSync(final ClientOperation clientOperation, final String path, final SdkAuthorization authorization, final Object request, final String idempotencyKey, final Type responseType) {
final Response response = transport.invokeSync(clientOperation, path, authorization, request == null ? null : serializer.toJson(request), idempotencyKey, null);
final Response response = transport.invokeSync(clientOperation, path, authorization, request, idempotencyKey, null);
final Response checkedResponse = errorCheck(response);
return deserialize(checkedResponse, responseType);
}
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/checkout/CheckoutApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
import com.checkout.forward.ForwardClient;
import com.checkout.handlepaymentsandpayouts.flow.FlowClient;
import com.checkout.handlepaymentsandpayouts.setups.PaymentSetupsClient;
import com.checkout.identities.faceauthentications.FaceAuthenticationClient;
import com.checkout.identities.applicants.ApplicantClient;
import com.checkout.identities.identityverification.IdentityVerificationClient;
import com.checkout.identities.iddocumentverification.IdDocumentVerificationClient;
import com.checkout.identities.amlscreening.AmlScreeningClient;
import com.checkout.instruments.InstrumentsClient;
import com.checkout.issuing.IssuingClient;
import com.checkout.metadata.MetadataClient;
Expand Down Expand Up @@ -69,4 +74,14 @@ public interface CheckoutApi extends CheckoutApmApi {

ForwardClient forwardClient();

FaceAuthenticationClient faceAuthenticationClient();

ApplicantClient applicantClient();

IdentityVerificationClient identityVerificationClient();

IdDocumentVerificationClient idDocumentVerificationClient();

AmlScreeningClient amlScreeningClient();

}
35 changes: 35 additions & 0 deletions src/main/java/com/checkout/CheckoutApiImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
import com.checkout.handlepaymentsandpayouts.flow.FlowClientImpl;
import com.checkout.handlepaymentsandpayouts.setups.PaymentSetupsClient;
import com.checkout.handlepaymentsandpayouts.setups.PaymentSetupsClientImpl;
import com.checkout.identities.faceauthentications.FaceAuthenticationClient;
import com.checkout.identities.faceauthentications.FaceAuthenticationClientImpl;
import com.checkout.identities.applicants.ApplicantClient;
import com.checkout.identities.applicants.ApplicantClientImpl;
import com.checkout.identities.identityverification.IdentityVerificationClient;
import com.checkout.identities.identityverification.IdentityVerificationClientImpl;
import com.checkout.identities.iddocumentverification.IdDocumentVerificationClient;
import com.checkout.identities.iddocumentverification.IdDocumentVerificationClientImpl;
import com.checkout.identities.amlscreening.AmlScreeningClient;
import com.checkout.identities.amlscreening.AmlScreeningClientImpl;
import com.checkout.instruments.InstrumentsClient;
import com.checkout.instruments.InstrumentsClientImpl;
import com.checkout.issuing.IssuingClient;
Expand Down Expand Up @@ -71,6 +81,11 @@ public class CheckoutApiImpl extends AbstractCheckoutApmApi implements CheckoutA
private final FlowClient flowClient;
private final PaymentSetupsClient paymentSetupsClient;
private final ForwardClient forwardClient;
private final FaceAuthenticationClient faceAuthenticationClient;
private final ApplicantClient applicantClient;
private final IdentityVerificationClient identityVerificationClient;
private final IdDocumentVerificationClient idDocumentVerificationClient;
private final AmlScreeningClient amlScreeningClient;

public CheckoutApiImpl(final CheckoutConfiguration configuration) {
super(configuration);
Expand Down Expand Up @@ -98,6 +113,11 @@ public CheckoutApiImpl(final CheckoutConfiguration configuration) {
this.flowClient = new FlowClientImpl(this.apiClient, configuration);
this.paymentSetupsClient = new PaymentSetupsClientImpl(this.apiClient, configuration);
this.forwardClient = new ForwardClientImpl(this.apiClient, configuration);
this.faceAuthenticationClient = new FaceAuthenticationClientImpl(this.apiClient, configuration);
this.applicantClient = new ApplicantClientImpl(this.apiClient, configuration);
this.identityVerificationClient = new IdentityVerificationClientImpl(this.apiClient, configuration);
this.idDocumentVerificationClient = new IdDocumentVerificationClientImpl(this.apiClient, configuration);
this.amlScreeningClient = new AmlScreeningClientImpl(this.apiClient, configuration);
}

@Override
Expand Down Expand Up @@ -198,6 +218,21 @@ public MetadataClient metadataClient() {
@Override
public ForwardClient forwardClient() { return forwardClient; }

@Override
public FaceAuthenticationClient faceAuthenticationClient() { return faceAuthenticationClient; }

@Override
public ApplicantClient applicantClient() { return applicantClient; }

@Override
public IdentityVerificationClient identityVerificationClient() { return identityVerificationClient; }

@Override
public IdDocumentVerificationClient idDocumentVerificationClient() { return idDocumentVerificationClient; }

@Override
public AmlScreeningClient amlScreeningClient() { return amlScreeningClient; }

private ApiClient getFilesClient(final CheckoutConfiguration configuration) {
return new ApiClientImpl(configuration, new FilesApiUriStrategy(configuration));
}
Expand Down
Loading
Loading