diff --git a/src/main/java/com/checkout/ApacheHttpClientTransport.java b/src/main/java/com/checkout/ApacheHttpClientTransport.java index 6286c18a..31318827 100644 --- a/src/main/java/com/checkout/ApacheHttpClientTransport.java +++ b/src/main/java/com/checkout/ApacheHttpClientTransport.java @@ -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; @@ -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 @@ -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 telemetryData = ThreadLocal.withInitial(RequestMetrics::new); @@ -85,15 +88,18 @@ class ApacheHttpClientTransport implements Transport { this.executor = executor; this.transportConfiguration = transportConfiguration; this.configuration = configuration; + this.serializer = new GsonSerializer(); } @Override public CompletableFuture invoke(final ClientOperation clientOperation, final String path, final SdkAuthorization authorization, - final String requestBody, + final Object requestObject, final String idempotencyKey, final Map queryParams) { + + final Object requestBodyOrEntity = prepareRequestContent(requestObject); return CompletableFuture.supplyAsync(() -> { final HttpUriRequest request; switch (clientOperation) { @@ -129,7 +135,7 @@ public CompletableFuture 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); } @@ -146,9 +152,10 @@ public CompletableFuture 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 queryParams) { + final Object requestBodyOrEntity = prepareRequestContent(requestObject); final HttpUriRequest request; switch (clientOperation) { case GET: @@ -184,7 +191,7 @@ public Response invokeSync(final ClientOperation clientOperation, request.setHeader(CKO_IDEMPOTENCY_KEY, idempotencyKey); } - final Supplier callSupplier = () -> performCall(authorization, requestBody, request, clientOperation); + final Supplier callSupplier = () -> performCall(authorization, requestBodyOrEntity, request, clientOperation); return executeWithResilience4j(callSupplier); } @@ -228,6 +235,29 @@ private Response executeWithResilience4j(final Supplier 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) { @@ -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()); @@ -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; diff --git a/src/main/java/com/checkout/ApiClientImpl.java b/src/main/java/com/checkout/ApiClientImpl.java index 8f8f803a..b286d365 100644 --- a/src/main/java/com/checkout/ApiClientImpl.java +++ b/src/main/java/com/checkout/ApiClientImpl.java @@ -127,7 +127,7 @@ public CompletableFuture 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 responseType = resultTypeMappings.get(response.getStatusCode()); @@ -231,7 +231,7 @@ private CompletableFuture executeAsyncOrSync(final java.util.function.Sup } private CompletableFuture 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)); } @@ -343,7 +343,7 @@ public T delete(final String path, final SdkAuthorizati @Override public HttpMetadata post(final String path, final SdkAuthorization authorization, final Map> 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 responseType = resultTypeMappings.get(checkedResponse.getStatusCode()); if (responseType == null) { @@ -393,7 +393,7 @@ public T submitFile(final String path, final SdkAuthori } private 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); } diff --git a/src/main/java/com/checkout/CheckoutApi.java b/src/main/java/com/checkout/CheckoutApi.java index 24364608..408f5203 100644 --- a/src/main/java/com/checkout/CheckoutApi.java +++ b/src/main/java/com/checkout/CheckoutApi.java @@ -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; @@ -69,4 +74,14 @@ public interface CheckoutApi extends CheckoutApmApi { ForwardClient forwardClient(); + FaceAuthenticationClient faceAuthenticationClient(); + + ApplicantClient applicantClient(); + + IdentityVerificationClient identityVerificationClient(); + + IdDocumentVerificationClient idDocumentVerificationClient(); + + AmlScreeningClient amlScreeningClient(); + } diff --git a/src/main/java/com/checkout/CheckoutApiImpl.java b/src/main/java/com/checkout/CheckoutApiImpl.java index 0e74bf4a..14587a70 100644 --- a/src/main/java/com/checkout/CheckoutApiImpl.java +++ b/src/main/java/com/checkout/CheckoutApiImpl.java @@ -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; @@ -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); @@ -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 @@ -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)); } diff --git a/src/main/java/com/checkout/GsonSerializer.java b/src/main/java/com/checkout/GsonSerializer.java index 6434ccd0..fd4e87e0 100644 --- a/src/main/java/com/checkout/GsonSerializer.java +++ b/src/main/java/com/checkout/GsonSerializer.java @@ -43,7 +43,14 @@ import com.google.gson.typeadapters.RuntimeTypeAdapterFactory; import lombok.Getter; import org.apache.commons.lang3.EnumUtils; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.message.BasicNameValuePair; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; import java.lang.reflect.Type; import java.time.Instant; import java.time.LocalDate; @@ -54,7 +61,6 @@ import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.EnumMap; -import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -196,6 +202,11 @@ public class GsonSerializer implements Serializer { .registerTypeAdapterFactory(RuntimeTypeAdapterFactory.of(CardControlResponse.class, CheckoutUtils.CONTROL_TYPE) .registerSubtype(VelocityCardControlResponse.class, identifier(ControlType.VELOCITY_LIMIT)) .registerSubtype(MccCardControlResponse.class, identifier(ControlType.MCC_LIMIT))) + // Issuing CS2 - ControlGroupControl + .registerTypeAdapterFactory(RuntimeTypeAdapterFactory.of(com.checkout.issuing.controls.requests.controlgroup.ControlGroupControl.class, CheckoutUtils.CONTROL_TYPE) + .registerSubtype(com.checkout.issuing.controls.requests.controlgroup.VelocityControlGroupControl.class, identifier(ControlType.VELOCITY_LIMIT)) + .registerSubtype(com.checkout.issuing.controls.requests.controlgroup.MccControlGroupControl.class, identifier(ControlType.MCC_LIMIT)) + .registerSubtype(com.checkout.issuing.controls.requests.controlgroup.MidControlGroupControl.class, identifier(ControlType.MID_LIMIT))) // Adapters when API returns an array .registerTypeAdapter(EVENT_TYPES_TYPE, eventTypesResponseDeserializer()) .registerTypeAdapter(WORKFLOWS_EVENT_TYPES_TYPE, workflowEventTypesResponseDeserializer()) @@ -428,4 +439,99 @@ private static JsonDeserializer getProductDeserializer() { return productResponse; }; } + + /** + * Creates form URL encoded content from a request object using reflection + * Converts field names to snake_case and handles boolean values properly + * + * @param request The request object to convert + * @return UrlEncodedFormEntity containing the form data + * @throws UnsupportedEncodingException if encoding fails + */ + public static UrlEncodedFormEntity createFormUrlEncodedContent(Object request) throws UnsupportedEncodingException { + if (request == null) { + throw new IllegalArgumentException("Request object cannot be null"); + } + + List data = new ArrayList<>(); + + // Get all fields from the class (including inherited fields if needed) + Field[] fields = request.getClass().getDeclaredFields(); + + for (Field field : fields) { + try { + // Make the field accessible + field.setAccessible(true); + + // Get the field value + Object value = field.get(request); + + if (value != null) { + // Convert field name to snake_case + String fieldName = toSnakeCase(field.getName()); + + // Convert value to string, handling booleans specially + String stringValue; + if (value instanceof Boolean) { + stringValue = ((Boolean) value).toString().toLowerCase(); + } else { + stringValue = value.toString(); + } + + data.add(new BasicNameValuePair(fieldName, stringValue)); + } + + } catch (IllegalAccessException e) { + // Skip fields that cannot be accessed + continue; + } + } + + return new UrlEncodedFormEntity(data); + } + + /** + * Alternative method that uses Gson's FieldNamingPolicy for consistent naming + */ + public static UrlEncodedFormEntity createFormUrlEncodedContentWithGson(Object request) throws UnsupportedEncodingException { + if (request == null) { + throw new IllegalArgumentException("Request object cannot be null"); + } + + List data = new ArrayList<>(); + Field[] fields = request.getClass().getDeclaredFields(); + + for (Field field : fields) { + try { + field.setAccessible(true); + Object value = field.get(request); + + if (value != null) { + // Use Gson's naming policy for consistency with the rest of the SDK + String fieldName = FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES.translateName(field); + + String stringValue; + if (value instanceof Boolean) { + stringValue = ((Boolean) value).toString().toLowerCase(); + } else { + stringValue = value.toString(); + } + + data.add(new BasicNameValuePair(fieldName, stringValue)); + } + + } catch (IllegalAccessException e) { + continue; + } + } + + return new UrlEncodedFormEntity(data); + } + + /** + * Converts camelCase to snake_case + */ + private static String toSnakeCase(String camelCase) { + return camelCase.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase(); + } } diff --git a/src/main/java/com/checkout/Transport.java b/src/main/java/com/checkout/Transport.java index 2cbc6a26..3a1da5a8 100644 --- a/src/main/java/com/checkout/Transport.java +++ b/src/main/java/com/checkout/Transport.java @@ -7,11 +7,11 @@ public interface Transport { - CompletableFuture invoke(ClientOperation clientOperation, String path, SdkAuthorization authorization, String jsonRequest, String idempotencyKey, Map queryParams); + CompletableFuture invoke(ClientOperation clientOperation, String path, SdkAuthorization authorization, Object requestObject, String idempotencyKey, Map queryParams); CompletableFuture submitFile(String path, SdkAuthorization authorization, AbstractFileRequest fileRequest); - Response invokeSync(ClientOperation clientOperation, String path, SdkAuthorization authorization, String jsonRequest, String idempotencyKey, Map queryParams); + Response invokeSync(ClientOperation clientOperation, String path, SdkAuthorization authorization, Object requestObject, String idempotencyKey, Map queryParams); Response submitFileSync(String path, SdkAuthorization authorization, AbstractFileRequest fileRequest); diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java index 7a359dd3..84cafbd5 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java @@ -1,6 +1,5 @@ package com.checkout.handlepaymentsandpayouts.setups.requests; -import com.checkout.HttpMetadata; import com.checkout.common.Currency; import com.checkout.payments.PaymentType; import com.checkout.handlepaymentsandpayouts.setups.entities.customer.Customer; @@ -49,7 +48,6 @@ public class PaymentSetupsRequest { * [Optional] */ @Builder.Default - @SerializedName("payment_type") private PaymentType paymentType = PaymentType.REGULAR; /** diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java index 27a6be14..4d1436dc 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java @@ -1,9 +1,8 @@ package com.checkout.handlepaymentsandpayouts.setups.responses; -import com.checkout.HttpMetadata; +import com.checkout.common.Resource; import com.checkout.common.Currency; import com.checkout.payments.PaymentStatus; -import com.checkout.handlepaymentsandpayouts.setups.responses.PaymentSetupSource; import com.checkout.handlepaymentsandpayouts.payments.postpayments.responses.requestapaymentorpayoutresponsecreated.threeds.Threeds; import com.checkout.handlepaymentsandpayouts.payments.postpayments.responses.requestapaymentorpayoutresponsecreated.risk.Risk; import com.checkout.handlepaymentsandpayouts.payments.postpayments.responses.requestapaymentorpayoutresponsecreated.customer.Customer; @@ -27,7 +26,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class PaymentSetupsConfirmResponse extends HttpMetadata { +public class PaymentSetupsConfirmResponse extends Resource { /** * The payment's unique identifier diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java index 8e610a64..d78cd1d1 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java @@ -1,6 +1,6 @@ package com.checkout.handlepaymentsandpayouts.setups.responses; -import com.checkout.HttpMetadata; +import com.checkout.common.Resource; import com.checkout.common.Currency; import com.checkout.handlepaymentsandpayouts.setups.entities.customer.Customer; import com.checkout.handlepaymentsandpayouts.setups.entities.industry.Industry; @@ -8,7 +8,6 @@ import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.PaymentMethods; import com.checkout.handlepaymentsandpayouts.setups.entities.settings.Settings; import com.checkout.payments.PaymentType; -import com.google.gson.annotations.SerializedName; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -23,7 +22,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class PaymentSetupsResponse extends HttpMetadata { +public class PaymentSetupsResponse extends Resource { /** * The unique identifier of the payment setup. @@ -33,7 +32,6 @@ public class PaymentSetupsResponse extends HttpMetadata { /** * The processing channel used for the payment setup */ - @SerializedName("processing_channel_id") private String processingChannelId; /** @@ -49,7 +47,6 @@ public class PaymentSetupsResponse extends HttpMetadata { /** * The type of payment method */ - @SerializedName("payment_type") private PaymentType paymentType; /** @@ -65,7 +62,6 @@ public class PaymentSetupsResponse extends HttpMetadata { /** * The payment method configuration for this setup */ - @SerializedName("payment_methods") private PaymentMethods paymentMethods; /** diff --git a/src/main/java/com/checkout/identities/amlscreening/AmlScreeningClient.java b/src/main/java/com/checkout/identities/amlscreening/AmlScreeningClient.java new file mode 100644 index 00000000..898bc084 --- /dev/null +++ b/src/main/java/com/checkout/identities/amlscreening/AmlScreeningClient.java @@ -0,0 +1,49 @@ +package com.checkout.identities.amlscreening; + +import com.checkout.identities.amlscreening.requests.AmlScreeningRequest; +import com.checkout.identities.amlscreening.responses.AmlScreeningResponse; + +import java.util.concurrent.CompletableFuture; + +/** + * Defines the operations available on the Identities AML Screening API + */ +public interface AmlScreeningClient { + + // Async methods + + /** + * Create an AML screening + * + * @param amlScreeningRequest the {@link AmlScreeningRequest} + * @return a {@link CompletableFuture} of {@link AmlScreeningResponse} + */ + CompletableFuture createAmlScreeningAsync(AmlScreeningRequest amlScreeningRequest); + + /** + * Get an AML screening + * + * @param amlScreeningId the AML screening's unique identifier + * @return a {@link CompletableFuture} of {@link AmlScreeningResponse} + */ + CompletableFuture getAmlScreeningAsync(String amlScreeningId); + + // Sync methods + + /** + * Create an AML screening + * + * @param amlScreeningRequest the {@link AmlScreeningRequest} + * @return a {@link AmlScreeningResponse} + */ + AmlScreeningResponse createAmlScreening(AmlScreeningRequest amlScreeningRequest); + + /** + * Get an AML screening + * + * @param amlScreeningId the AML screening's unique identifier + * @return a {@link AmlScreeningResponse} + */ + AmlScreeningResponse getAmlScreening(String amlScreeningId); + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/amlscreening/AmlScreeningClientImpl.java b/src/main/java/com/checkout/identities/amlscreening/AmlScreeningClientImpl.java new file mode 100644 index 00000000..53e3698c --- /dev/null +++ b/src/main/java/com/checkout/identities/amlscreening/AmlScreeningClientImpl.java @@ -0,0 +1,83 @@ +package com.checkout.identities.amlscreening; + +import com.checkout.AbstractClient; +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorizationType; +import com.checkout.identities.amlscreening.requests.AmlScreeningRequest; +import com.checkout.identities.amlscreening.responses.AmlScreeningResponse; + +import java.util.concurrent.CompletableFuture; + +import static com.checkout.common.CheckoutUtils.validateParams; + +/** + * Implementation of the AML Screening client. + */ +public class AmlScreeningClientImpl extends AbstractClient implements AmlScreeningClient { + + private static final String AML_VERIFICATIONS_PATH = "aml-verifications"; + + public AmlScreeningClientImpl(final ApiClient apiClient, final CheckoutConfiguration configuration) { + super(apiClient, configuration, SdkAuthorizationType.SECRET_KEY_OR_OAUTH); + } + + // Async methods + + /** + * Create an AML screening + * + * @param amlScreeningRequest the AML screening request + * @return a {@link CompletableFuture} containing the {@link AmlScreeningResponse} + */ + @Override + public CompletableFuture createAmlScreeningAsync( + final AmlScreeningRequest amlScreeningRequest) { + validateParams("amlScreeningRequest", amlScreeningRequest); + return apiClient.postAsync(AML_VERIFICATIONS_PATH, sdkAuthorization(), AmlScreeningResponse.class, + amlScreeningRequest, null); + } + + /** + * Retrieve an AML screening + * + * @param amlVerificationId the AML verification ID + * @return a {@link CompletableFuture} containing the {@link AmlScreeningResponse} + */ + @Override + public CompletableFuture getAmlScreeningAsync( + final String amlVerificationId) { + validateParams("amlVerificationId", amlVerificationId); + return apiClient.getAsync(buildPath(AML_VERIFICATIONS_PATH, amlVerificationId), sdkAuthorization(), + AmlScreeningResponse.class); + } + + // Sync methods + + /** + * Create an AML screening + * + * @param amlScreeningRequest the AML screening request + * @return a {@link AmlScreeningResponse} + */ + @Override + public AmlScreeningResponse createAmlScreening(final AmlScreeningRequest amlScreeningRequest) { + validateParams("amlScreeningRequest", amlScreeningRequest); + return apiClient.post(AML_VERIFICATIONS_PATH, sdkAuthorization(), AmlScreeningResponse.class, + amlScreeningRequest, null); + } + + /** + * Retrieve an AML screening + * + * @param amlVerificationId the AML verification ID + * @return a {@link AmlScreeningResponse} + */ + @Override + public AmlScreeningResponse getAmlScreening(final String amlVerificationId) { + validateParams("amlVerificationId", amlVerificationId); + return apiClient.get(buildPath(AML_VERIFICATIONS_PATH, amlVerificationId), sdkAuthorization(), + AmlScreeningResponse.class); + } + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/amlscreening/requests/AmlScreeningRequest.java b/src/main/java/com/checkout/identities/amlscreening/requests/AmlScreeningRequest.java new file mode 100644 index 00000000..b3689912 --- /dev/null +++ b/src/main/java/com/checkout/identities/amlscreening/requests/AmlScreeningRequest.java @@ -0,0 +1,28 @@ +package com.checkout.identities.amlscreening.requests; + +import com.checkout.common.Resource; +import com.checkout.identities.entities.SearchParameters; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * Request for creating AML screening + */ +@Data +@Builder +@EqualsAndHashCode(callSuper = false) +@NoArgsConstructor +@AllArgsConstructor +public class AmlScreeningRequest extends Resource { + + private String applicantId; + + private SearchParameters searchParameters; + + private Boolean monitored; + +} diff --git a/src/main/java/com/checkout/identities/amlscreening/responses/AmlScreeningResponse.java b/src/main/java/com/checkout/identities/amlscreening/responses/AmlScreeningResponse.java new file mode 100644 index 00000000..47efd9ac --- /dev/null +++ b/src/main/java/com/checkout/identities/amlscreening/responses/AmlScreeningResponse.java @@ -0,0 +1,20 @@ +package com.checkout.identities.amlscreening.responses; + +import com.checkout.identities.entities.BaseIdentityResponseStatus; +import com.checkout.identities.entities.SearchParameters; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Response for AML screening operations + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class AmlScreeningResponse extends BaseIdentityResponseStatus { + private String applicantId; + + private SearchParameters searchParameters; + + private Boolean monitored; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/amlscreening/responses/AmlScreeningStatus.java b/src/main/java/com/checkout/identities/amlscreening/responses/AmlScreeningStatus.java new file mode 100644 index 00000000..8801e76e --- /dev/null +++ b/src/main/java/com/checkout/identities/amlscreening/responses/AmlScreeningStatus.java @@ -0,0 +1,25 @@ +package com.checkout.identities.amlscreening.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * The status of an AML screening + */ +public enum AmlScreeningStatus { + + @SerializedName("created") + CREATED, + + @SerializedName("screening_in_progress") + SCREENING_IN_PROGRESS, + + @SerializedName("approved") + APPROVED, + + @SerializedName("declined") + DECLINED, + + @SerializedName("review_required") + REVIEW_REQUIRED + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/applicants/ApplicantClient.java b/src/main/java/com/checkout/identities/applicants/ApplicantClient.java new file mode 100644 index 00000000..abdbe8cf --- /dev/null +++ b/src/main/java/com/checkout/identities/applicants/ApplicantClient.java @@ -0,0 +1,81 @@ +package com.checkout.identities.applicants; + +import com.checkout.identities.applicants.requests.ApplicantRequest; +import com.checkout.identities.applicants.requests.ApplicantUpdateRequest; +import com.checkout.identities.applicants.responses.ApplicantResponse; + +import java.util.concurrent.CompletableFuture; + +/** + * Client interface for Applicant operations. + */ +public interface ApplicantClient { + + /** + * Creates a new applicant profile. + * + * @param applicantRequest The applicant request + * @return CompletableFuture containing the applicant response + */ + CompletableFuture createApplicant(ApplicantRequest applicantRequest); + + /** + * Retrieves an applicant by ID. + * + * @param applicantId The applicant ID + * @return CompletableFuture containing the applicant response + */ + CompletableFuture getApplicant(String applicantId); + + /** + * Updates an existing applicant profile. + * + * @param applicantId The applicant ID + * @param applicantUpdateRequest The applicant update request + * @return CompletableFuture containing the applicant response + */ + CompletableFuture updateApplicant(String applicantId, ApplicantUpdateRequest applicantUpdateRequest); + + /** + * Anonymizes an applicant by removing personal data. + * + * @param applicantId The applicant ID + * @return CompletableFuture containing the applicant response + */ + CompletableFuture anonymizeApplicant(String applicantId); + + // Synchronous methods + + /** + * Creates a new applicant profile. + * + * @param applicantRequest The applicant request + * @return The applicant response + */ + ApplicantResponse createApplicantSync(ApplicantRequest applicantRequest); + + /** + * Retrieves an applicant by ID. + * + * @param applicantId The applicant ID + * @return The applicant response + */ + ApplicantResponse getApplicantSync(String applicantId); + + /** + * Updates an existing applicant profile. + * + * @param applicantId The applicant ID + * @param applicantUpdateRequest The applicant update request + * @return The applicant response + */ + ApplicantResponse updateApplicantSync(String applicantId, ApplicantUpdateRequest applicantUpdateRequest); + + /** + * Anonymizes an applicant by removing personal data. + * + * @param applicantId The applicant ID + * @return The applicant response + */ + ApplicantResponse anonymizeApplicantSync(String applicantId); +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/applicants/ApplicantClientImpl.java b/src/main/java/com/checkout/identities/applicants/ApplicantClientImpl.java new file mode 100644 index 00000000..84daf337 --- /dev/null +++ b/src/main/java/com/checkout/identities/applicants/ApplicantClientImpl.java @@ -0,0 +1,136 @@ +package com.checkout.identities.applicants; + +import com.checkout.AbstractClient; +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorizationType; +import com.checkout.identities.applicants.requests.ApplicantRequest; +import com.checkout.identities.applicants.requests.ApplicantUpdateRequest; +import com.checkout.identities.applicants.responses.ApplicantResponse; + +import java.util.concurrent.CompletableFuture; + +import static com.checkout.common.CheckoutUtils.validateParams; + +/** + * Implementation of the Applicant client. + */ +public class ApplicantClientImpl extends AbstractClient implements ApplicantClient { + + private static final String APPLICANTS_PATH = "applicants"; + private static final String ANONYMIZE_PATH = "anonymize"; + + public ApplicantClientImpl(final ApiClient apiClient, final CheckoutConfiguration configuration) { + super(apiClient, configuration, SdkAuthorizationType.SECRET_KEY_OR_OAUTH); + } + + /** + * Creates a new applicant profile. + * + * @param applicantRequest The applicant request + * @return CompletableFuture containing the applicant response + */ + @Override + public CompletableFuture createApplicant(final ApplicantRequest applicantRequest) { + validateParams("applicantRequest", applicantRequest); + return apiClient.postAsync(APPLICANTS_PATH, sdkAuthorization(), ApplicantResponse.class, + applicantRequest, null); + } + + /** + * Retrieves an applicant by ID. + * + * @param applicantId The applicant ID + * @return CompletableFuture containing the applicant response + */ + @Override + public CompletableFuture getApplicant(final String applicantId) { + validateParams("applicantId", applicantId); + return apiClient.getAsync(buildPath(APPLICANTS_PATH, applicantId), sdkAuthorization(), + ApplicantResponse.class); + } + + /** + * Updates an existing applicant profile. + * + * @param applicantId The applicant ID + * @param applicantUpdateRequest The applicant update request + * @return CompletableFuture containing the applicant response + */ + @Override + public CompletableFuture updateApplicant(final String applicantId, + final ApplicantUpdateRequest applicantUpdateRequest) { + validateParams("applicantId", applicantId, "applicantUpdateRequest", applicantUpdateRequest); + return apiClient.patchAsync(buildPath(APPLICANTS_PATH, applicantId), sdkAuthorization(), + ApplicantResponse.class, applicantUpdateRequest, null); + } + + /** + * Anonymizes an applicant by removing personal data. + * + * @param applicantId The applicant ID + * @return CompletableFuture containing the applicant response + */ + @Override + public CompletableFuture anonymizeApplicant(final String applicantId) { + validateParams("applicantId", applicantId); + return apiClient.postAsync(buildPath(APPLICANTS_PATH, applicantId, ANONYMIZE_PATH), + sdkAuthorization(), ApplicantResponse.class, null, null); + } + + // Synchronous methods + + /** + * Creates a new applicant profile. + * + * @param applicantRequest The applicant request + * @return The applicant response + */ + @Override + public ApplicantResponse createApplicantSync(final ApplicantRequest applicantRequest) { + validateParams("applicantRequest", applicantRequest); + return apiClient.post(APPLICANTS_PATH, sdkAuthorization(), ApplicantResponse.class, + applicantRequest, null); + } + + /** + * Retrieves an applicant by ID. + * + * @param applicantId The applicant ID + * @return The applicant response + */ + @Override + public ApplicantResponse getApplicantSync(final String applicantId) { + validateParams("applicantId", applicantId); + return apiClient.get(buildPath(APPLICANTS_PATH, applicantId), sdkAuthorization(), + ApplicantResponse.class); + } + + /** + * Updates an existing applicant profile. + * + * @param applicantId The applicant ID + * @param applicantUpdateRequest The applicant update request + * @return The applicant response + */ + @Override + public ApplicantResponse updateApplicantSync(final String applicantId, + final ApplicantUpdateRequest applicantUpdateRequest) { + validateParams("applicantId", applicantId, "applicantUpdateRequest", applicantUpdateRequest); + return apiClient.patch(buildPath(APPLICANTS_PATH, applicantId), sdkAuthorization(), + ApplicantResponse.class, applicantUpdateRequest, null); + } + + /** + * Anonymizes an applicant by removing personal data. + * + * @param applicantId The applicant ID + * @return The applicant response + */ + @Override + public ApplicantResponse anonymizeApplicantSync(final String applicantId) { + validateParams("applicantId", applicantId); + return apiClient.post(buildPath(APPLICANTS_PATH, applicantId, ANONYMIZE_PATH), + sdkAuthorization(), ApplicantResponse.class, null, null); + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/applicants/requests/ApplicantRequest.java b/src/main/java/com/checkout/identities/applicants/requests/ApplicantRequest.java new file mode 100644 index 00000000..a74ffabf --- /dev/null +++ b/src/main/java/com/checkout/identities/applicants/requests/ApplicantRequest.java @@ -0,0 +1,31 @@ +package com.checkout.identities.applicants.requests; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Applicant creation request + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ApplicantRequest { + + /** + * Your reference for the applicant. + */ + private String externalApplicantId; + + /** + * The applicant's email address. + */ + private String email; + + /** + * The applicant's full name. + */ + private String externalApplicantName; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/applicants/requests/ApplicantUpdateRequest.java b/src/main/java/com/checkout/identities/applicants/requests/ApplicantUpdateRequest.java new file mode 100644 index 00000000..fd458518 --- /dev/null +++ b/src/main/java/com/checkout/identities/applicants/requests/ApplicantUpdateRequest.java @@ -0,0 +1,26 @@ +package com.checkout.identities.applicants.requests; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Applicant update request + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ApplicantUpdateRequest { + + /** + * The applicant's email address. + */ + private String email; + + /** + * The applicant's full name. + */ + private String externalApplicantName; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/applicants/responses/ApplicantResponse.java b/src/main/java/com/checkout/identities/applicants/responses/ApplicantResponse.java new file mode 100644 index 00000000..8c3c4df3 --- /dev/null +++ b/src/main/java/com/checkout/identities/applicants/responses/ApplicantResponse.java @@ -0,0 +1,35 @@ +package com.checkout.identities.applicants.responses; + +import com.checkout.identities.entities.BaseIdentityResponse; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * Applicant response + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ApplicantResponse extends BaseIdentityResponse{ + + /** + * Your reference for the applicant. + */ + private String externalApplicantId; + + /** + * The applicant's email address. + */ + private String email; + + /** + * The applicant's full name. + */ + private String externalApplicantName; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/ApplicantSessionInformation.java b/src/main/java/com/checkout/identities/entities/ApplicantSessionInformation.java new file mode 100644 index 00000000..79dde1c6 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/ApplicantSessionInformation.java @@ -0,0 +1,20 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Session information + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ApplicantSessionInformation { + /** + * The applicant's IP address during the attempt. + */ + private String ipAddress; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/BaseAttemptResponse.java b/src/main/java/com/checkout/identities/entities/BaseAttemptResponse.java new file mode 100644 index 00000000..acdd829c --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/BaseAttemptResponse.java @@ -0,0 +1,33 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * Base class for attempt responses + * + * @param The status enum type + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public abstract class BaseAttemptResponse> extends BaseIdentityResponseStatus { + + /** + * The URL to redirect the applicant to after the attempt. + */ + private String redirectUrl; + + /** + * The applicant's details. + */ + private ClientInformation clientInformation; + + /** + * The details of the attempt. + */ + private ApplicantSessionInformation applicantSessionInformation; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/BaseIdentityResponse.java b/src/main/java/com/checkout/identities/entities/BaseIdentityResponse.java new file mode 100644 index 00000000..d73aed03 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/BaseIdentityResponse.java @@ -0,0 +1,35 @@ +package com.checkout.identities.entities; + +import com.checkout.common.Resource; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +/** + * Base class for all identity response objects containing common fields + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public abstract class BaseIdentityResponse extends Resource { + + /** + * The unique identifier. + */ + private String id; + + /** + * The date and time when the resource was created, in UTC. + */ + private Instant createdOn; + + /** + * The date and time when the resource was modified, in UTC. + */ + private Instant modifiedOn; + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/BaseIdentityResponseStatus.java b/src/main/java/com/checkout/identities/entities/BaseIdentityResponseStatus.java new file mode 100644 index 00000000..5f219787 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/BaseIdentityResponseStatus.java @@ -0,0 +1,29 @@ +package com.checkout.identities.entities; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * Base class for all identity response objects containing common fields + * + * @param The status enum type + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@AllArgsConstructor +public abstract class BaseIdentityResponseStatus> extends BaseIdentityResponse { + /** + * The status. + */ + private T status; + + /** + * One or more response codes that provide more information about the status. + */ + private List responseCodes; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/ClientInformation.java b/src/main/java/com/checkout/identities/entities/ClientInformation.java new file mode 100644 index 00000000..cc423584 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/ClientInformation.java @@ -0,0 +1,22 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ClientInformation { + /** + * The applicant's residence country. + */ + private String preSelectedResidenceCountry; + + /** + * The language you want to use for the user interface. + */ + private String preSelectedLanguage; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/DeclaredData.java b/src/main/java/com/checkout/identities/entities/DeclaredData.java new file mode 100644 index 00000000..70f62ae4 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/DeclaredData.java @@ -0,0 +1,18 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The personal details provided by the applicant + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DeclaredData { + + private String name; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/DocumentDetails.java b/src/main/java/com/checkout/identities/entities/DocumentDetails.java new file mode 100644 index 00000000..04a90c6a --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/DocumentDetails.java @@ -0,0 +1,56 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The applicant's identity document details. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DocumentDetails { + + private DocumentType documentType; + + private String documentIssuingCountry; + + private String frontImageSignedUrl; + + private String fullName; + + private String birthDate; + + private String firstNames; + + private String lastName; + + private String lastNameAtBirth; + + private String birthPlace; + + private String nationality; + + private Gender gender; + + private String personalNumber; + + private String taxIdentificationNumber; + + private String documentNumber; + + private String documentExpiryDate; + + private String documentIssueDate; + + private String documentIssuePlace; + + private String documentMrz; + + private String backImageSignedUrl; + + private String signatureImageSignedUrl; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/DocumentType.java b/src/main/java/com/checkout/identities/entities/DocumentType.java new file mode 100644 index 00000000..4a92d53f --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/DocumentType.java @@ -0,0 +1,22 @@ +package com.checkout.identities.entities; + +import com.google.gson.annotations.SerializedName; + +/** + * Enumeration of the type of identity document. + */ + +public enum DocumentType { + + @SerializedName("Driving licence") + DRIVING_LICENCE, + + @SerializedName("ID") + ID, + + @SerializedName("Passport") + PASSPORT, + + @SerializedName("Residence Permit") + RESIDENCE_PERMIT +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/Face.java b/src/main/java/com/checkout/identities/entities/Face.java new file mode 100644 index 00000000..5c6eb372 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/Face.java @@ -0,0 +1,20 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Face image details + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Face { + /** + * The URL to the face image. + */ + private String imageSignedUrl; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/Gender.java b/src/main/java/com/checkout/identities/entities/Gender.java new file mode 100644 index 00000000..1c4aeb41 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/Gender.java @@ -0,0 +1,16 @@ +package com.checkout.identities.entities; + +import com.google.gson.annotations.SerializedName; + +/** + * The applicant's gender + */ + +public enum Gender { + + @SerializedName("M") + M, + + @SerializedName("F") + F +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/ResponseCode.java b/src/main/java/com/checkout/identities/entities/ResponseCode.java new file mode 100644 index 00000000..124aff1b --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/ResponseCode.java @@ -0,0 +1,27 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Response code information for face authentication operations + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ResponseCode { + + /** + * The response code. + */ + private Integer code; + + /** + * The description of the response code. + */ + private String summary; + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/entities/SearchParameters.java b/src/main/java/com/checkout/identities/entities/SearchParameters.java new file mode 100644 index 00000000..8ac26fe6 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/SearchParameters.java @@ -0,0 +1,17 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Request for creating AML screening + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SearchParameters { + private String configurationIdentifier; +} diff --git a/src/main/java/com/checkout/identities/entities/VerifiedIdentity.java b/src/main/java/com/checkout/identities/entities/VerifiedIdentity.java new file mode 100644 index 00000000..aec319ff --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/VerifiedIdentity.java @@ -0,0 +1,31 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Verified identity details + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VerifiedIdentity { + private String fullName; + + private String birthDate; + + private String firstNames; + + private String lastName; + + private String lastNameAtBirth; + + private String birthPlace; + + private String nationality; + + private Gender gender; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClient.java b/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClient.java new file mode 100644 index 00000000..a5ee32a1 --- /dev/null +++ b/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClient.java @@ -0,0 +1,117 @@ +package com.checkout.identities.faceauthentications; + +import com.checkout.identities.faceauthentications.requests.FaceAuthenticationAttemptRequest; +import com.checkout.identities.faceauthentications.requests.FaceAuthenticationRequest; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptResponse; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptsResponse; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationResponse; + +import java.util.concurrent.CompletableFuture; + +/** + * Client interface for Face Authentication operations. + */ +public interface FaceAuthenticationClient { + + /** + * Creates a new face authentication for an applicant. + * + * @param faceAuthenticationRequest The face authentication request + * @return CompletableFuture containing the face authentication response + */ + CompletableFuture createFaceAuthentication(FaceAuthenticationRequest faceAuthenticationRequest); + + /** + * Retrieves a face authentication by ID. + * + * @param faceAuthenticationId The face authentication ID + * @return CompletableFuture containing the face authentication response + */ + CompletableFuture getFaceAuthentication(String faceAuthenticationId); + + /** + * Anonymizes a face authentication by removing personal data. + * + * @param faceAuthenticationId The face authentication ID + * @return CompletableFuture containing the face authentication response + */ + CompletableFuture anonymizeFaceAuthentication(String faceAuthenticationId); + + /** + * Creates a new face authentication attempt. + * + * @param faceAuthenticationId The face authentication ID + * @param attemptRequest The face authentication attempt request + * @return CompletableFuture containing the face authentication attempt response + */ + CompletableFuture createFaceAuthenticationAttempt(String faceAuthenticationId, FaceAuthenticationAttemptRequest attemptRequest); + + /** + * Retrieves all attempts for a specific face authentication. + * + * @param faceAuthenticationId The face authentication ID + * @return CompletableFuture containing the face authentication attempts response + */ + CompletableFuture getFaceAuthenticationAttempts(String faceAuthenticationId); + + /** + * Retrieves a specific attempt for a face authentication. + * + * @param faceAuthenticationId The face authentication ID + * @param attemptId The attempt ID + * @return CompletableFuture containing the face authentication attempt response + */ + CompletableFuture getFaceAuthenticationAttempt(String faceAuthenticationId, String attemptId); + + // Synchronous methods + + /** + * Creates a new face authentication for an applicant. + * + * @param faceAuthenticationRequest The face authentication request + * @return The face authentication response + */ + FaceAuthenticationResponse createFaceAuthenticationSync(FaceAuthenticationRequest faceAuthenticationRequest); + + /** + * Retrieves a face authentication by ID. + * + * @param faceAuthenticationId The face authentication ID + * @return The face authentication response + */ + FaceAuthenticationResponse getFaceAuthenticationSync(String faceAuthenticationId); + + /** + * Anonymizes a face authentication by removing personal data. + * + * @param faceAuthenticationId The face authentication ID + * @return The face authentication response + */ + FaceAuthenticationResponse anonymizeFaceAuthenticationSync(String faceAuthenticationId); + + /** + * Creates a new face authentication attempt. + * + * @param faceAuthenticationId The face authentication ID + * @param attemptRequest The face authentication attempt request + * @return The face authentication attempt response + */ + FaceAuthenticationAttemptResponse createFaceAuthenticationAttemptSync(String faceAuthenticationId, FaceAuthenticationAttemptRequest attemptRequest); + + /** + * Retrieves all attempts for a specific face authentication. + * + * @param faceAuthenticationId The face authentication ID + * @return The face authentication attempts response + */ + FaceAuthenticationAttemptsResponse getFaceAuthenticationAttemptsSync(String faceAuthenticationId); + + /** + * Retrieves a specific attempt for a face authentication. + * + * @param faceAuthenticationId The face authentication ID + * @param attemptId The attempt ID + * @return The face authentication attempt response + */ + FaceAuthenticationAttemptResponse getFaceAuthenticationAttemptSync(String faceAuthenticationId, String attemptId); +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImpl.java b/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImpl.java new file mode 100644 index 00000000..6df11584 --- /dev/null +++ b/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImpl.java @@ -0,0 +1,198 @@ +package com.checkout.identities.faceauthentications; + +import com.checkout.AbstractClient; +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorizationType; +import com.checkout.identities.faceauthentications.requests.FaceAuthenticationAttemptRequest; +import com.checkout.identities.faceauthentications.requests.FaceAuthenticationRequest; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptResponse; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptsResponse; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationResponse; + +import java.util.concurrent.CompletableFuture; + +import static com.checkout.common.CheckoutUtils.validateParams; + +/** + * Implementation of the Face Authentication client. + */ +public class FaceAuthenticationClientImpl extends AbstractClient implements FaceAuthenticationClient { + + private static final String FACE_AUTHENTICATIONS_PATH = "face-authentications"; + private static final String ANONYMIZE_PATH = "anonymize"; + private static final String ATTEMPTS_PATH = "attempts"; + + public FaceAuthenticationClientImpl(final ApiClient apiClient, final CheckoutConfiguration configuration) { + super(apiClient, configuration, SdkAuthorizationType.SECRET_KEY_OR_OAUTH); + } + + /** + * Creates a new face authentication for an applicant. + * + * @param faceAuthenticationRequest The face authentication request + * @return CompletableFuture containing the face authentication response + */ + @Override + public CompletableFuture createFaceAuthentication( + final FaceAuthenticationRequest faceAuthenticationRequest) { + validateParams("faceAuthenticationRequest", faceAuthenticationRequest); + return apiClient.postAsync(FACE_AUTHENTICATIONS_PATH, sdkAuthorization(), FaceAuthenticationResponse.class, + faceAuthenticationRequest, null); + } + + /** + * Retrieves a face authentication by ID. + * + * @param faceAuthenticationId The face authentication ID + * @return CompletableFuture containing the face authentication response + */ + @Override + public CompletableFuture getFaceAuthentication(final String faceAuthenticationId) { + validateParams("faceAuthenticationId", faceAuthenticationId); + return apiClient.getAsync(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId), sdkAuthorization(), + FaceAuthenticationResponse.class); + } + + /** + * Anonymizes a face authentication by removing personal data. + * + * @param faceAuthenticationId The face authentication ID + * @return CompletableFuture containing the face authentication response + */ + @Override + public CompletableFuture anonymizeFaceAuthentication(final String faceAuthenticationId) { + validateParams("faceAuthenticationId", faceAuthenticationId); + return apiClient.postAsync(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ANONYMIZE_PATH), + sdkAuthorization(), FaceAuthenticationResponse.class, null, null); + } + + /** + * Creates a new face authentication attempt. + * + * @param faceAuthenticationId The face authentication ID + * @param attemptRequest The face authentication attempt request + * @return CompletableFuture containing the face authentication attempt response + */ + @Override + public CompletableFuture createFaceAuthenticationAttempt( + final String faceAuthenticationId, final FaceAuthenticationAttemptRequest attemptRequest) { + validateParams("faceAuthenticationId", faceAuthenticationId, "attemptRequest", attemptRequest); + return apiClient.postAsync(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ATTEMPTS_PATH), + sdkAuthorization(), FaceAuthenticationAttemptResponse.class, attemptRequest, null); + } + + /** + * Retrieves all attempts for a specific face authentication. + * + * @param faceAuthenticationId The face authentication ID + * @return CompletableFuture containing the face authentication attempts response + */ + @Override + public CompletableFuture getFaceAuthenticationAttempts( + final String faceAuthenticationId) { + validateParams("faceAuthenticationId", faceAuthenticationId); + return apiClient.getAsync(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ATTEMPTS_PATH), + sdkAuthorization(), FaceAuthenticationAttemptsResponse.class); + } + + /** + * Retrieves a specific attempt for a face authentication. + * + * @param faceAuthenticationId The face authentication ID + * @param attemptId The attempt ID + * @return CompletableFuture containing the face authentication attempt response + */ + @Override + public CompletableFuture getFaceAuthenticationAttempt( + final String faceAuthenticationId, final String attemptId) { + validateParams("faceAuthenticationId", faceAuthenticationId, "attemptId", attemptId); + return apiClient.getAsync(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ATTEMPTS_PATH, attemptId), + sdkAuthorization(), FaceAuthenticationAttemptResponse.class); + } + + // Synchronous methods + + /** + * Creates a new face authentication for an applicant. + * + * @param faceAuthenticationRequest The face authentication request + * @return The face authentication response + */ + @Override + public FaceAuthenticationResponse createFaceAuthenticationSync( + final FaceAuthenticationRequest faceAuthenticationRequest) { + validateParams("faceAuthenticationRequest", faceAuthenticationRequest); + return apiClient.post(FACE_AUTHENTICATIONS_PATH, sdkAuthorization(), FaceAuthenticationResponse.class, + faceAuthenticationRequest, null); + } + + /** + * Retrieves a face authentication by ID. + * + * @param faceAuthenticationId The face authentication ID + * @return The face authentication response + */ + @Override + public FaceAuthenticationResponse getFaceAuthenticationSync(final String faceAuthenticationId) { + validateParams("faceAuthenticationId", faceAuthenticationId); + return apiClient.get(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId), sdkAuthorization(), + FaceAuthenticationResponse.class); + } + + /** + * Anonymizes a face authentication by removing personal data. + * + * @param faceAuthenticationId The face authentication ID + * @return The face authentication response + */ + @Override + public FaceAuthenticationResponse anonymizeFaceAuthenticationSync(final String faceAuthenticationId) { + validateParams("faceAuthenticationId", faceAuthenticationId); + return apiClient.post(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ANONYMIZE_PATH), + sdkAuthorization(), FaceAuthenticationResponse.class, null, null); + } + + /** + * Creates a new face authentication attempt. + * + * @param faceAuthenticationId The face authentication ID + * @param attemptRequest The face authentication attempt request + * @return The face authentication attempt response + */ + @Override + public FaceAuthenticationAttemptResponse createFaceAuthenticationAttemptSync( + final String faceAuthenticationId, final FaceAuthenticationAttemptRequest attemptRequest) { + validateParams("faceAuthenticationId", faceAuthenticationId, "attemptRequest", attemptRequest); + return apiClient.post(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ATTEMPTS_PATH), + sdkAuthorization(), FaceAuthenticationAttemptResponse.class, attemptRequest, null); + } + + /** + * Retrieves all attempts for a specific face authentication. + * + * @param faceAuthenticationId The face authentication ID + * @return The face authentication attempts response + */ + @Override + public FaceAuthenticationAttemptsResponse getFaceAuthenticationAttemptsSync(final String faceAuthenticationId) { + validateParams("faceAuthenticationId", faceAuthenticationId); + return apiClient.get(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ATTEMPTS_PATH), + sdkAuthorization(), FaceAuthenticationAttemptsResponse.class); + } + + /** + * Retrieves a specific attempt for a face authentication. + * + * @param faceAuthenticationId The face authentication ID + * @param attemptId The attempt ID + * @return The face authentication attempt response + */ + @Override + public FaceAuthenticationAttemptResponse getFaceAuthenticationAttemptSync( + final String faceAuthenticationId, final String attemptId) { + validateParams("faceAuthenticationId", faceAuthenticationId, "attemptId", attemptId); + return apiClient.get(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ATTEMPTS_PATH, attemptId), + sdkAuthorization(), FaceAuthenticationAttemptResponse.class); + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationAttemptRequest.java b/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationAttemptRequest.java new file mode 100644 index 00000000..bedddbba --- /dev/null +++ b/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationAttemptRequest.java @@ -0,0 +1,29 @@ +package com.checkout.identities.faceauthentications.requests; + +import com.checkout.identities.entities.ClientInformation; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Face authentication attempt creation request + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FaceAuthenticationAttemptRequest { + + /** + * The URL to redirect the applicant to after the attempt. + * [Required] + */ + private String redirectUrl; + + /** + * The applicant's details. + * [Optional] + */ + private ClientInformation clientInformation; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationRequest.java b/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationRequest.java new file mode 100644 index 00000000..4e51232f --- /dev/null +++ b/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationRequest.java @@ -0,0 +1,28 @@ +package com.checkout.identities.faceauthentications.requests; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Face authentication creation request + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FaceAuthenticationRequest { + + /** + * The applicant's unique identifier. + * [Required] + */ + private String applicantId; + + /** + * Your configuration ID. + * [Required] + */ + private String userJourneyId; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java new file mode 100644 index 00000000..cccde554 --- /dev/null +++ b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java @@ -0,0 +1,22 @@ +package com.checkout.identities.faceauthentications.responses; + +import com.checkout.identities.entities.BaseAttemptResponse; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Face authentication attempt response + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@AllArgsConstructor +public class FaceAuthenticationAttemptResponse extends BaseAttemptResponse { + + // This class inherits all necessary fields from BaseAttemptResponse + // No additional fields are currently needed for face authentication attempts + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptStatus.java b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptStatus.java new file mode 100644 index 00000000..97eb4149 --- /dev/null +++ b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptStatus.java @@ -0,0 +1,34 @@ +package com.checkout.identities.faceauthentications.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * Face authentication attempt status enumeration + */ +public enum FaceAuthenticationAttemptStatus { + + @SerializedName("capture_aborted") + CAPTURE_ABORTED, + + @SerializedName("capture_in_progress") + CAPTURE_IN_PROGRESS, + + @SerializedName("checks_inconclusive") + CHECKS_INCONCLUSIVE, + + @SerializedName("checks_in_progress") + CHECKS_IN_PROGRESS, + + @SerializedName("completed") + COMPLETED, + + @SerializedName("expired") + EXPIRED, + + @SerializedName("pending_redirection") + PENDING_REDIRECTION, + + @SerializedName("capture_refused") + CAPTURE_REFUSED + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptsResponse.java b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptsResponse.java new file mode 100644 index 00000000..35bb8437 --- /dev/null +++ b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptsResponse.java @@ -0,0 +1,40 @@ +package com.checkout.identities.faceauthentications.responses; + +import com.checkout.common.Resource; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import java.util.List; + +/** + * Face authentication attempts list response + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FaceAuthenticationAttemptsResponse extends Resource { + + /** + * The total number of attempts. + */ + private Integer totalCount; + + /** + * The number of attempts skipped. + */ + private Integer skip; + + /** + * The maximum number of attempts returned. + */ + private Integer limit; + + /** + * The list of attempts. + */ + private List data; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationResponse.java b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationResponse.java new file mode 100644 index 00000000..6cc2644c --- /dev/null +++ b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationResponse.java @@ -0,0 +1,43 @@ +package com.checkout.identities.faceauthentications.responses; + +import com.checkout.identities.entities.Face; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.util.List; + +import com.checkout.identities.entities.BaseIdentityResponseStatus; + +/** + * Face authentication response + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FaceAuthenticationResponse extends BaseIdentityResponseStatus { + + /** + * The applicant's unique identifier. + */ + private String applicantId; + + /** + * Your configuration ID. + */ + private String userJourneyId; + + /** + * One or more codes that provide more information about risks associated with the verification. + */ + private List riskLabels; + + /** + * The details of the image of the applicant's face extracted from the video. + */ + private Face face; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationStatus.java b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationStatus.java new file mode 100644 index 00000000..e67bce9b --- /dev/null +++ b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationStatus.java @@ -0,0 +1,37 @@ +package com.checkout.identities.faceauthentications.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * Face authentication status enumeration + */ +public enum FaceAuthenticationStatus { + + @SerializedName("approved") + APPROVED, + + @SerializedName("capture_in_progress") + CAPTURE_IN_PROGRESS, + + @SerializedName("checks_in_progress") + CHECKS_IN_PROGRESS, + + @SerializedName("created") + CREATED, + + @SerializedName("declined") + DECLINED, + + @SerializedName("inconclusive") + INCONCLUSIVE, + + @SerializedName("pending") + PENDING, + + @SerializedName("refused") + REFUSED, + + @SerializedName("retry_required") + RETRY_REQUIRED + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClient.java b/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClient.java new file mode 100644 index 00000000..72de6e5e --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClient.java @@ -0,0 +1,134 @@ +package com.checkout.identities.iddocumentverification; + +import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationAttemptRequest; +import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationRequest; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptsResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationReportResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationResponse; + +import java.util.concurrent.CompletableFuture; + +/** + * Client for ID document verification operations + */ +public interface IdDocumentVerificationClient { + + /** + * Create an ID document verification + * + * @param idDocumentVerificationRequest the ID document verification request + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationResponse} + */ + CompletableFuture createIdDocumentVerificationAsync(IdDocumentVerificationRequest idDocumentVerificationRequest); + + /** + * Retrieve an ID document verification + * + * @param idDocumentVerificationId the ID document verification ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationResponse} + */ + CompletableFuture getIdDocumentVerificationAsync(String idDocumentVerificationId); + + /** + * Anonymize an ID document verification + * + * @param idDocumentVerificationId the ID document verification ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationResponse} + */ + CompletableFuture anonymizeIdDocumentVerificationAsync(String idDocumentVerificationId); + + /** + * Create an ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param idDocumentVerificationAttemptRequest the attempt request + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptResponse} + */ + CompletableFuture createIdDocumentVerificationAttemptAsync(String idDocumentVerificationId, IdDocumentVerificationAttemptRequest idDocumentVerificationAttemptRequest); + + /** + * Retrieve all ID document verification attempts + * + * @param idDocumentVerificationId the ID document verification ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptsResponse} + */ + CompletableFuture getIdDocumentVerificationAttemptsAsync(String idDocumentVerificationId); + + /** + * Retrieve a specific ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param attemptId the attempt ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptResponse} + */ + CompletableFuture getIdDocumentVerificationAttemptAsync(String idDocumentVerificationId, String attemptId); + + /** + * Generate and download a PDF report + * + * @param idDocumentVerificationId the ID document verification ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationReportResponse} + */ + CompletableFuture getIdDocumentVerificationReportAsync(String idDocumentVerificationId); + + // Synchronous methods + + /** + * Create an ID document verification + * + * @param idDocumentVerificationRequest the ID document verification request + * @return the {@link IdDocumentVerificationResponse} + */ + IdDocumentVerificationResponse createIdDocumentVerification(IdDocumentVerificationRequest idDocumentVerificationRequest); + + /** + * Retrieve an ID document verification + * + * @param idDocumentVerificationId the ID document verification ID + * @return the {@link IdDocumentVerificationResponse} + */ + IdDocumentVerificationResponse getIdDocumentVerification(String idDocumentVerificationId); + + /** + * Anonymize an ID document verification + * + * @param idDocumentVerificationId the ID document verification ID + * @return the {@link IdDocumentVerificationResponse} + */ + IdDocumentVerificationResponse anonymizeIdDocumentVerification(String idDocumentVerificationId); + + /** + * Create an ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param idDocumentVerificationAttemptRequest the attempt request + * @return the {@link IdDocumentVerificationAttemptResponse} + */ + IdDocumentVerificationAttemptResponse createIdDocumentVerificationAttempt(String idDocumentVerificationId, IdDocumentVerificationAttemptRequest idDocumentVerificationAttemptRequest); + + /** + * Retrieve all ID document verification attempts + * + * @param idDocumentVerificationId the ID document verification ID + * @return the {@link IdDocumentVerificationAttemptsResponse} + */ + IdDocumentVerificationAttemptsResponse getIdDocumentVerificationAttempts(String idDocumentVerificationId); + + /** + * Retrieve a specific ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param attemptId the attempt ID + * @return the {@link IdDocumentVerificationAttemptResponse} + */ + IdDocumentVerificationAttemptResponse getIdDocumentVerificationAttempt(String idDocumentVerificationId, String attemptId); + + /** + * Generate and download a PDF report + * + * @param idDocumentVerificationId the ID document verification ID + * @return the {@link IdDocumentVerificationReportResponse} + */ + IdDocumentVerificationReportResponse getIdDocumentVerificationReport(String idDocumentVerificationId); +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImpl.java b/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImpl.java new file mode 100644 index 00000000..21d38e69 --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImpl.java @@ -0,0 +1,235 @@ +package com.checkout.identities.iddocumentverification; + +import com.checkout.AbstractClient; +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorizationType; +import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationAttemptRequest; +import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationRequest; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptsResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationReportResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationResponse; + +import java.util.concurrent.CompletableFuture; + +import static com.checkout.common.CheckoutUtils.validateParams; + +/** + * Implementation of the ID Document Verification client. + */ +public class IdDocumentVerificationClientImpl extends AbstractClient implements IdDocumentVerificationClient { + + private static final String ID_DOCUMENT_VERIFICATIONS_PATH = "id-document-verifications"; + private static final String ANONYMIZE_PATH = "anonymize"; + private static final String ATTEMPTS_PATH = "attempts"; + private static final String PDF_REPORT_PATH = "pdf-report"; + + public IdDocumentVerificationClientImpl(final ApiClient apiClient, final CheckoutConfiguration configuration) { + super(apiClient, configuration, SdkAuthorizationType.SECRET_KEY_OR_OAUTH); + } + + /** + * Create an ID document verification + * + * @param idDocumentVerificationRequest the ID document verification request + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationResponse} + */ + @Override + public CompletableFuture createIdDocumentVerificationAsync( + final IdDocumentVerificationRequest idDocumentVerificationRequest) { + validateParams("idDocumentVerificationRequest", idDocumentVerificationRequest); + return apiClient.postAsync(ID_DOCUMENT_VERIFICATIONS_PATH, sdkAuthorization(), IdDocumentVerificationResponse.class, + idDocumentVerificationRequest, null); + } + + /** + * Retrieve an ID document verification + * + * @param idDocumentVerificationId the ID document verification ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationResponse} + */ + @Override + public CompletableFuture getIdDocumentVerificationAsync( + final String idDocumentVerificationId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.getAsync(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId), sdkAuthorization(), + IdDocumentVerificationResponse.class); + } + + /** + * Anonymize an ID document verification + * + * @param idDocumentVerificationId the ID document verification ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationResponse} + */ + @Override + public CompletableFuture anonymizeIdDocumentVerificationAsync( + final String idDocumentVerificationId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.postAsync(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ANONYMIZE_PATH), + sdkAuthorization(), IdDocumentVerificationResponse.class, null, null); + } + + /** + * Create an ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param idDocumentVerificationAttemptRequest the attempt request + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptResponse} + */ + @Override + public CompletableFuture createIdDocumentVerificationAttemptAsync( + final String idDocumentVerificationId, + final IdDocumentVerificationAttemptRequest idDocumentVerificationAttemptRequest) { + validateParams("idDocumentVerificationId", idDocumentVerificationId, + "idDocumentVerificationAttemptRequest", idDocumentVerificationAttemptRequest); + return apiClient.postAsync(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), IdDocumentVerificationAttemptResponse.class, idDocumentVerificationAttemptRequest, + null); + } + + /** + * Retrieve all ID document verification attempts + * + * @param idDocumentVerificationId the ID document verification ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptsResponse} + */ + @Override + public CompletableFuture getIdDocumentVerificationAttemptsAsync( + final String idDocumentVerificationId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.getAsync(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), IdDocumentVerificationAttemptsResponse.class); + } + + /** + * Retrieve a specific ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param attemptId the attempt ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptResponse} + */ + @Override + public CompletableFuture getIdDocumentVerificationAttemptAsync( + final String idDocumentVerificationId, final String attemptId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId, "attemptId", attemptId); + return apiClient.getAsync(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH, attemptId), + sdkAuthorization(), IdDocumentVerificationAttemptResponse.class); + } + + /** + * Generate and download a PDF report + * + * @param idDocumentVerificationId the ID document verification ID + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationReportResponse} + */ + @Override + public CompletableFuture getIdDocumentVerificationReportAsync( + final String idDocumentVerificationId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.getAsync(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, PDF_REPORT_PATH), + sdkAuthorization(), IdDocumentVerificationReportResponse.class); + } + + // Synchronous methods + + /** + * Create an ID document verification + * + * @param idDocumentVerificationRequest the ID document verification request + * @return the {@link IdDocumentVerificationResponse} + */ + @Override + public IdDocumentVerificationResponse createIdDocumentVerification( + final IdDocumentVerificationRequest idDocumentVerificationRequest) { + validateParams("idDocumentVerificationRequest", idDocumentVerificationRequest); + return apiClient.post(ID_DOCUMENT_VERIFICATIONS_PATH, sdkAuthorization(), IdDocumentVerificationResponse.class, + idDocumentVerificationRequest, null); + } + + /** + * Retrieve an ID document verification + * + * @param idDocumentVerificationId the ID document verification ID + * @return the {@link IdDocumentVerificationResponse} + */ + @Override + public IdDocumentVerificationResponse getIdDocumentVerification(final String idDocumentVerificationId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.get(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId), sdkAuthorization(), + IdDocumentVerificationResponse.class); + } + + /** + * Anonymize an ID document verification + * + * @param idDocumentVerificationId the ID document verification ID + * @return the {@link IdDocumentVerificationResponse} + */ + @Override + public IdDocumentVerificationResponse anonymizeIdDocumentVerification(final String idDocumentVerificationId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.post(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ANONYMIZE_PATH), + sdkAuthorization(), IdDocumentVerificationResponse.class, null, null); + } + + /** + * Create an ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param idDocumentVerificationAttemptRequest the attempt request + * @return the {@link IdDocumentVerificationAttemptResponse} + */ + @Override + public IdDocumentVerificationAttemptResponse createIdDocumentVerificationAttempt( + final String idDocumentVerificationId, + final IdDocumentVerificationAttemptRequest idDocumentVerificationAttemptRequest) { + validateParams("idDocumentVerificationId", idDocumentVerificationId, + "idDocumentVerificationAttemptRequest", idDocumentVerificationAttemptRequest); + return apiClient.post(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), IdDocumentVerificationAttemptResponse.class, idDocumentVerificationAttemptRequest, + null); + } + + /** + * Retrieve all ID document verification attempts + * + * @param idDocumentVerificationId the ID document verification ID + * @return the {@link IdDocumentVerificationAttemptsResponse} + */ + @Override + public IdDocumentVerificationAttemptsResponse getIdDocumentVerificationAttempts(final String idDocumentVerificationId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.get(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), IdDocumentVerificationAttemptsResponse.class); + } + + /** + * Retrieve a specific ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param attemptId the attempt ID + * @return the {@link IdDocumentVerificationAttemptResponse} + */ + @Override + public IdDocumentVerificationAttemptResponse getIdDocumentVerificationAttempt( + final String idDocumentVerificationId, final String attemptId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId, "attemptId", attemptId); + return apiClient.get(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH, attemptId), + sdkAuthorization(), IdDocumentVerificationAttemptResponse.class); + } + + /** + * Generate and download a PDF report + * + * @param idDocumentVerificationId the ID document verification ID + * @return the {@link IdDocumentVerificationReportResponse} + */ + @Override + public IdDocumentVerificationReportResponse getIdDocumentVerificationReport(final String idDocumentVerificationId) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.get(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, PDF_REPORT_PATH), + sdkAuthorization(), IdDocumentVerificationReportResponse.class); + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/requests/IdDocumentVerificationAttemptRequest.java b/src/main/java/com/checkout/identities/iddocumentverification/requests/IdDocumentVerificationAttemptRequest.java new file mode 100644 index 00000000..f17b1ef9 --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/requests/IdDocumentVerificationAttemptRequest.java @@ -0,0 +1,27 @@ +package com.checkout.identities.iddocumentverification.requests; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * ID document verification attempt request + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IdDocumentVerificationAttemptRequest { + + /** + * The image of the front of the document to upload. + * [Required] + */ + private String documentFront; + + /** + * The image of the back of the document to upload. + */ + private String documentBack; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/requests/IdDocumentVerificationRequest.java b/src/main/java/com/checkout/identities/iddocumentverification/requests/IdDocumentVerificationRequest.java new file mode 100644 index 00000000..1b34423a --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/requests/IdDocumentVerificationRequest.java @@ -0,0 +1,35 @@ +package com.checkout.identities.iddocumentverification.requests; + +import com.checkout.identities.entities.DeclaredData; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * ID document verification request + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IdDocumentVerificationRequest { + + /** + * The applicant's unique identifier. + * [Required] + */ + private String applicantId; + + /** + * Your configuration ID. + * [Required] + */ + private String userJourneyId; + + /** + * The personal details provided by the applicant. + * [Required] + */ + private DeclaredData declaredData; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptResponse.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptResponse.java new file mode 100644 index 00000000..cf2fcd19 --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptResponse.java @@ -0,0 +1,15 @@ +package com.checkout.identities.iddocumentverification.responses; + +import com.checkout.identities.entities.BaseIdentityResponseStatus; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Response for ID document verification attempt operations + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class IdDocumentVerificationAttemptResponse extends BaseIdentityResponseStatus { + // All fields are inherited from BaseAttemptResponse and BaseIdentityResponse +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptStatus.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptStatus.java new file mode 100644 index 00000000..bff5b97e --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptStatus.java @@ -0,0 +1,45 @@ +package com.checkout.identities.iddocumentverification.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * Enumeration of ID document verification attempt statuses + */ +public enum IdDocumentVerificationAttemptStatus { + + /** + * Checks in progress + */ + @SerializedName("checks_in_progress") + CHECKS_IN_PROGRESS, + + /** + * Checks inconclusive + */ + @SerializedName("checks_inconclusive") + CHECKS_INCONCLUSIVE, + + /** + * Attempt completed + */ + @SerializedName("completed") + COMPLETED, + + /** + * Quality checks aborted + */ + @SerializedName("quality_checks_aborted") + QUALITY_CHECKS_ABORTED, + + /** + * Quality checks in progress + */ + @SerializedName("quality_checks_in_progress") + QUALITY_CHECKS_IN_PROGRESS, + + /** + * Attempt terminated + */ + @SerializedName("terminated") + TERMINATED +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptsResponse.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptsResponse.java new file mode 100644 index 00000000..2c3fdc14 --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptsResponse.java @@ -0,0 +1,23 @@ +package com.checkout.identities.iddocumentverification.responses; + +import com.checkout.common.Resource; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * Response for ID document verification attempts listing operations + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class IdDocumentVerificationAttemptsResponse extends Resource { + + private Integer totalCount; + + private Integer skip; + + private Integer limit; + + private List data; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationReportResponse.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationReportResponse.java new file mode 100644 index 00000000..9f8b0731 --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationReportResponse.java @@ -0,0 +1,15 @@ +package com.checkout.identities.iddocumentverification.responses; + +import com.checkout.common.Resource; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Response for ID document verification report operations + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class IdDocumentVerificationReportResponse extends Resource { + + private String signedUrl; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationResponse.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationResponse.java new file mode 100644 index 00000000..5b61cc3e --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationResponse.java @@ -0,0 +1,23 @@ +package com.checkout.identities.iddocumentverification.responses; + +import com.checkout.identities.entities.BaseIdentityResponseStatus; +import com.checkout.identities.entities.DeclaredData; +import com.checkout.identities.entities.DocumentDetails; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Response for ID document verification operations + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class IdDocumentVerificationResponse extends BaseIdentityResponseStatus { + + private String userJourneyId; + + private String applicantId; + + private DeclaredData declaredData; + + private DocumentDetails document; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationStatus.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationStatus.java new file mode 100644 index 00000000..87c640fa --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationStatus.java @@ -0,0 +1,51 @@ +package com.checkout.identities.iddocumentverification.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * Enumeration of ID document verification statuses + */ +public enum IdDocumentVerificationStatus { + + /** + * ID document verification created + */ + @SerializedName("created") + CREATED, + + /** + * Quality checks in progress + */ + @SerializedName("quality_checks_in_progress") + QUALITY_CHECKS_IN_PROGRESS, + + /** + * Checks in progress + */ + @SerializedName("checks_in_progress") + CHECKS_IN_PROGRESS, + + /** + * ID document verification approved + */ + @SerializedName("approved") + APPROVED, + + /** + * ID document verification declined + */ + @SerializedName("declined") + DECLINED, + + /** + * Retry required + */ + @SerializedName("retry_required") + RETRY_REQUIRED, + + /** + * ID document verification inconclusive + */ + @SerializedName("inconclusive") + INCONCLUSIVE +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClient.java b/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClient.java new file mode 100644 index 00000000..43de0ed2 --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClient.java @@ -0,0 +1,153 @@ +package com.checkout.identities.identityverification; + +import com.checkout.identities.identityverification.requests.CreateAndOpenIdentityVerificationRequest; +import com.checkout.identities.identityverification.requests.IdentityVerificationRequest; +import com.checkout.identities.identityverification.requests.IdentityVerificationAttemptRequest; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptsResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationReportResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationResponse; + +import java.util.concurrent.CompletableFuture; + +/** + * Client for identity verification operations + */ +public interface IdentityVerificationClient { + + // Async methods + + /** + * Create and open an identity verification session + * + * @param identityVerificationRequest the identity verification request + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationResponse} + */ + CompletableFuture createAndOpenIdentityVerificationAsync(CreateAndOpenIdentityVerificationRequest identityVerificationRequest); + + /** + * Create an identity verification session + * + * @param identityVerificationRequest the identity verification request + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationResponse} + */ + CompletableFuture createIdentityVerificationAsync(IdentityVerificationRequest identityVerificationRequest); + + /** + * Retrieve an identity verification session + * + * @param identityVerificationId the identity verification ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationResponse} + */ + CompletableFuture getIdentityVerificationAsync(String identityVerificationId); + + /** + * Anonymize an identity verification + * + * @param identityVerificationId the identity verification ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationResponse} + */ + CompletableFuture anonymizeIdentityVerificationAsync(String identityVerificationId); + + /** + * Retrieve all identity verification attempts + * + * @param identityVerificationId the identity verification ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationAttemptsResponse} + */ + CompletableFuture getIdentityVerificationAttemptsAsync(String identityVerificationId); + + /** + * Create an identity verification attempt + * + * @param identityVerificationId the identity verification ID + * @param identityVerificationAttemptRequest the attempt request + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationAttemptResponse} + */ + CompletableFuture createIdentityVerificationAttemptAsync(String identityVerificationId, IdentityVerificationAttemptRequest identityVerificationAttemptRequest); + + /** + * Retrieve a specific identity verification attempt + * + * @param identityVerificationId the identity verification ID + * @param attemptId the attempt ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationAttemptResponse} + */ + CompletableFuture getIdentityVerificationAttemptAsync(String identityVerificationId, String attemptId); + + /** + * Generate and download a PDF report + * + * @param identityVerificationId the identity verification ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationReportResponse} + */ + CompletableFuture generateIdentityVerificationReportAsync(String identityVerificationId); + + // Sync methods + + /** + * Create and open an identity verification session + * + * @param identityVerificationRequest the identity verification request + * @return the {@link IdentityVerificationResponse} + */ + IdentityVerificationResponse createAndOpenIdentityVerification(CreateAndOpenIdentityVerificationRequest identityVerificationRequest); + + /** + * Create an identity verification session + * + * @param identityVerificationRequest the identity verification request + * @return the {@link IdentityVerificationResponse} + */ + IdentityVerificationResponse createIdentityVerification(IdentityVerificationRequest identityVerificationRequest); + + /** + * Retrieve an identity verification session + * + * @param identityVerificationId the identity verification ID + * @return the {@link IdentityVerificationResponse} + */ + IdentityVerificationResponse getIdentityVerification(String identityVerificationId); + + /** + * Anonymize an identity verification + * + * @param identityVerificationId the identity verification ID + * @return the {@link IdentityVerificationResponse} + */ + IdentityVerificationResponse anonymizeIdentityVerification(String identityVerificationId); + + /** + * Retrieve all identity verification attempts + * + * @param identityVerificationId the identity verification ID + * @return the {@link IdentityVerificationAttemptsResponse} + */ + IdentityVerificationAttemptsResponse getIdentityVerificationAttempts(String identityVerificationId); + + /** + * Create an identity verification attempt + * + * @param identityVerificationId the identity verification ID + * @param identityVerificationAttemptRequest the attempt request + * @return the {@link IdentityVerificationAttemptResponse} + */ + IdentityVerificationAttemptResponse createIdentityVerificationAttempt(String identityVerificationId, IdentityVerificationAttemptRequest identityVerificationAttemptRequest); + + /** + * Retrieve a specific identity verification attempt + * + * @param identityVerificationId the identity verification ID + * @param attemptId the attempt ID + * @return the {@link IdentityVerificationAttemptResponse} + */ + IdentityVerificationAttemptResponse getIdentityVerificationAttempt(String identityVerificationId, String attemptId); + + /** + * Generate and download a PDF report + * + * @param identityVerificationId the identity verification ID + * @return the {@link IdentityVerificationReportResponse} + */ + IdentityVerificationReportResponse generateIdentityVerificationReport(String identityVerificationId); +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClientImpl.java b/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClientImpl.java new file mode 100644 index 00000000..d3e9ec41 --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClientImpl.java @@ -0,0 +1,268 @@ +package com.checkout.identities.identityverification; + +import com.checkout.AbstractClient; +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorizationType; +import com.checkout.identities.identityverification.requests.CreateAndOpenIdentityVerificationRequest; +import com.checkout.identities.identityverification.requests.IdentityVerificationRequest; +import com.checkout.identities.identityverification.requests.IdentityVerificationAttemptRequest; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptsResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationReportResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationResponse; + +import java.util.concurrent.CompletableFuture; + +import static com.checkout.common.CheckoutUtils.validateParams; + +/** + * Implementation of the Identity Verification client. + */ +public class IdentityVerificationClientImpl extends AbstractClient implements IdentityVerificationClient { + + private static final String IDENTITY_VERIFICATIONS_PATH = "identity-verifications"; + private static final String CREATE_AND_OPEN_PATH = "create-and-open-idv"; + private static final String ANONYMIZE_PATH = "anonymize"; + private static final String ATTEMPTS_PATH = "attempts"; + private static final String PDF_REPORT_PATH = "pdf-report"; + + public IdentityVerificationClientImpl(final ApiClient apiClient, final CheckoutConfiguration configuration) { + super(apiClient, configuration, SdkAuthorizationType.SECRET_KEY_OR_OAUTH); + } + + // Async methods + + /** + * Create and open an identity verification session + * + * @param identityVerificationRequest the identity verification request + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationResponse} + */ + @Override + public CompletableFuture createAndOpenIdentityVerificationAsync( + final CreateAndOpenIdentityVerificationRequest identityVerificationRequest) { + validateParams("identityVerificationRequest", identityVerificationRequest); + return apiClient.postAsync(CREATE_AND_OPEN_PATH, sdkAuthorization(), IdentityVerificationResponse.class, + identityVerificationRequest, null); + } + + /** + * Create an identity verification session + * + * @param identityVerificationRequest the identity verification request + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationResponse} + */ + @Override + public CompletableFuture createIdentityVerificationAsync( + final IdentityVerificationRequest identityVerificationRequest) { + validateParams("identityVerificationRequest", identityVerificationRequest); + return apiClient.postAsync(IDENTITY_VERIFICATIONS_PATH, sdkAuthorization(), IdentityVerificationResponse.class, + identityVerificationRequest, null); + } + + /** + * Retrieve an identity verification session + * + * @param identityVerificationId the identity verification ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationResponse} + */ + @Override + public CompletableFuture getIdentityVerificationAsync( + final String identityVerificationId) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.getAsync(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId), sdkAuthorization(), + IdentityVerificationResponse.class); + } + + /** + * Anonymize an identity verification + * + * @param identityVerificationId the identity verification ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationResponse} + */ + @Override + public CompletableFuture anonymizeIdentityVerificationAsync( + final String identityVerificationId) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.postAsync(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ANONYMIZE_PATH), + sdkAuthorization(), IdentityVerificationResponse.class, null, null); + } + + /** + * Create an identity verification attempt + * + * @param identityVerificationId the identity verification ID + * @param identityVerificationAttemptRequest the attempt request + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationAttemptResponse} + */ + @Override + public CompletableFuture createIdentityVerificationAttemptAsync( + final String identityVerificationId, + final IdentityVerificationAttemptRequest identityVerificationAttemptRequest) { + validateParams("identityVerificationId", identityVerificationId, "identityVerificationAttemptRequest", + identityVerificationAttemptRequest); + return apiClient.postAsync(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), IdentityVerificationAttemptResponse.class, identityVerificationAttemptRequest, + null); + } + + /** + * Retrieve all identity verification attempts + * + * @param identityVerificationId the identity verification ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationAttemptsResponse} + */ + @Override + public CompletableFuture getIdentityVerificationAttemptsAsync( + final String identityVerificationId) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.getAsync(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), IdentityVerificationAttemptsResponse.class); + } + + /** + * Retrieve a specific identity verification attempt + * + * @param identityVerificationId the identity verification ID + * @param attemptId the attempt ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationAttemptResponse} + */ + @Override + public CompletableFuture getIdentityVerificationAttemptAsync( + final String identityVerificationId, final String attemptId) { + validateParams("identityVerificationId", identityVerificationId, "attemptId", attemptId); + return apiClient.getAsync(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ATTEMPTS_PATH, attemptId), + sdkAuthorization(), IdentityVerificationAttemptResponse.class); + } + + /** + * Generate and download a PDF report + * + * @param identityVerificationId the identity verification ID + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationReportResponse} + */ + @Override + public CompletableFuture generateIdentityVerificationReportAsync( + final String identityVerificationId) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.getAsync(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, PDF_REPORT_PATH), + sdkAuthorization(), IdentityVerificationReportResponse.class); + } + + // Sync methods + + /** + * Create and open an identity verification session + * + * @param identityVerificationRequest the identity verification request + * @return the {@link IdentityVerificationResponse} + */ + @Override + public IdentityVerificationResponse createAndOpenIdentityVerification( + final CreateAndOpenIdentityVerificationRequest identityVerificationRequest) { + validateParams("identityVerificationRequest", identityVerificationRequest); + return apiClient.post(CREATE_AND_OPEN_PATH, sdkAuthorization(), IdentityVerificationResponse.class, + identityVerificationRequest, null); + } + + /** + * Create an identity verification session + * + * @param identityVerificationRequest the identity verification request + * @return the {@link IdentityVerificationResponse} + */ + @Override + public IdentityVerificationResponse createIdentityVerification( + final IdentityVerificationRequest identityVerificationRequest) { + validateParams("identityVerificationRequest", identityVerificationRequest); + return apiClient.post(IDENTITY_VERIFICATIONS_PATH, sdkAuthorization(), IdentityVerificationResponse.class, + identityVerificationRequest, null); + } + + /** + * Retrieve an identity verification session + * + * @param identityVerificationId the identity verification ID + * @return the {@link IdentityVerificationResponse} + */ + @Override + public IdentityVerificationResponse getIdentityVerification(final String identityVerificationId) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.get(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId), sdkAuthorization(), + IdentityVerificationResponse.class); + } + + /** + * Anonymize an identity verification + * + * @param identityVerificationId the identity verification ID + * @return the {@link IdentityVerificationResponse} + */ + @Override + public IdentityVerificationResponse anonymizeIdentityVerification(final String identityVerificationId) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.post(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ANONYMIZE_PATH), + sdkAuthorization(), IdentityVerificationResponse.class, null, null); + } + + /** + * Retrieve all identity verification attempts + * + * @param identityVerificationId the identity verification ID + * @return the {@link IdentityVerificationAttemptsResponse} + */ + @Override + public IdentityVerificationAttemptsResponse getIdentityVerificationAttempts(final String identityVerificationId) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.get(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), IdentityVerificationAttemptsResponse.class); + } + + /** + * Create an identity verification attempt + * + * @param identityVerificationId the identity verification ID + * @param identityVerificationAttemptRequest the attempt request + * @return the {@link IdentityVerificationAttemptResponse} + */ + @Override + public IdentityVerificationAttemptResponse createIdentityVerificationAttempt( + final String identityVerificationId, + final IdentityVerificationAttemptRequest identityVerificationAttemptRequest) { + validateParams("identityVerificationId", identityVerificationId, "identityVerificationAttemptRequest", + identityVerificationAttemptRequest); + return apiClient.post(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), IdentityVerificationAttemptResponse.class, identityVerificationAttemptRequest, + null); + } + + /** + * Retrieve a specific identity verification attempt + * + * @param identityVerificationId the identity verification ID + * @param attemptId the attempt ID + * @return the {@link IdentityVerificationAttemptResponse} + */ + @Override + public IdentityVerificationAttemptResponse getIdentityVerificationAttempt( + final String identityVerificationId, final String attemptId) { + validateParams("identityVerificationId", identityVerificationId, "attemptId", attemptId); + return apiClient.get(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ATTEMPTS_PATH, attemptId), + sdkAuthorization(), IdentityVerificationAttemptResponse.class); + } + + /** + * Generate and download a PDF report + * + * @param identityVerificationId the identity verification ID + * @return the {@link IdentityVerificationReportResponse} + */ + @Override + public IdentityVerificationReportResponse generateIdentityVerificationReport(final String identityVerificationId) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.get(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, PDF_REPORT_PATH), + sdkAuthorization(), IdentityVerificationReportResponse.class); + } + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/requests/CreateAndOpenIdentityVerificationRequest.java b/src/main/java/com/checkout/identities/identityverification/requests/CreateAndOpenIdentityVerificationRequest.java new file mode 100644 index 00000000..4f3f353b --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/requests/CreateAndOpenIdentityVerificationRequest.java @@ -0,0 +1,39 @@ +package com.checkout.identities.identityverification.requests; + +import com.checkout.identities.entities.DeclaredData; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Create and open identity verification request + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateAndOpenIdentityVerificationRequest { + + /** + * The personal details provided by the applicant. + * [Required] + */ + private DeclaredData declaredData; + + /** + * The URL to redirect the applicant to after the attempt. + * [Required] + */ + private String redirectUrl; + + /** + * Your configuration ID. + */ + private String userJourneyId; + + /** + * The applicant's unique identifier. + */ + private String applicantId; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationAttemptRequest.java b/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationAttemptRequest.java new file mode 100644 index 00000000..f789c8f0 --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationAttemptRequest.java @@ -0,0 +1,28 @@ +package com.checkout.identities.identityverification.requests; + +import com.checkout.identities.entities.ClientInformation; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Identity verification attempt request + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IdentityVerificationAttemptRequest { + + /** + * The URL to redirect the applicant to after the attempt. + * [Required] + */ + private String redirectUrl; + + /** + * The applicant's details. + */ + private ClientInformation clientInformation; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationRequest.java b/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationRequest.java new file mode 100644 index 00000000..e73d7688 --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationRequest.java @@ -0,0 +1,34 @@ +package com.checkout.identities.identityverification.requests; + +import com.checkout.identities.entities.DeclaredData; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Identity verification creation request + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IdentityVerificationRequest { + + /** + * The applicant's unique identifier. + * [Required] + */ + private String applicantId; + + /** + * The personal details provided by the applicant. + * [Required] + */ + private DeclaredData declaredData; + + /** + * Your configuration ID. + */ + private String userJourneyId; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptResponse.java b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptResponse.java new file mode 100644 index 00000000..e6a1b1b7 --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptResponse.java @@ -0,0 +1,10 @@ +package com.checkout.identities.identityverification.responses; + +import com.checkout.identities.entities.BaseAttemptResponse; + +/** + * Response for identity verification attempt operations + */ +public class IdentityVerificationAttemptResponse extends BaseAttemptResponse { + // All fields are inherited from BaseAttemptResponse and BaseIdentityResponseStatus +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptStatus.java b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptStatus.java new file mode 100644 index 00000000..2a4df959 --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptStatus.java @@ -0,0 +1,33 @@ +package com.checkout.identities.identityverification.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * Enumeration of identity verification attempt statuses + */ +public enum IdentityVerificationAttemptStatus { + + @SerializedName("capture_aborted") + CAPTURE_ABORTED, + + @SerializedName("capture_in_progress") + CAPTURE_IN_PROGRESS, + + @SerializedName("checks_inconclusive") + CHECKS_INCONCLUSIVE, + + @SerializedName("checks_in_progress") + CHECKS_IN_PROGRESS, + + @SerializedName("completed") + COMPLETED, + + @SerializedName("expired") + EXPIRED, + + @SerializedName("pending_redirection") + PENDING_REDIRECTION, + + @SerializedName("capture_refused") + CAPTURE_REFUSED +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptsResponse.java b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptsResponse.java new file mode 100644 index 00000000..eb664a19 --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptsResponse.java @@ -0,0 +1,42 @@ +package com.checkout.identities.identityverification.responses; + +import com.checkout.common.Resource; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * Response for identity verification attempts listing operations + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IdentityVerificationAttemptsResponse extends Resource { + + /** + * The total number of attempts. + */ + private Integer totalCount; + + /** + * The number of attempts you want to skip. + */ + private Integer skip; + + /** + * The maximum number of attempts you want returned. + */ + private Integer limit; + + /** + * The details of the attempts. + */ + private List data; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationReportResponse.java b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationReportResponse.java new file mode 100644 index 00000000..4d6c583b --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationReportResponse.java @@ -0,0 +1,25 @@ +package com.checkout.identities.identityverification.responses; + +import com.checkout.common.Resource; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * Response for identity verification report download operations + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IdentityVerificationReportResponse extends Resource { + + /** + * The pre-signed URL to the captured image of the document. + */ + private String signedUrl; + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationResponse.java b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationResponse.java new file mode 100644 index 00000000..672b0eff --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationResponse.java @@ -0,0 +1,66 @@ +package com.checkout.identities.identityverification.responses; + +import com.checkout.identities.entities.Face; +import com.checkout.identities.entities.VerifiedIdentity; +import com.checkout.identities.entities.DocumentDetails; +import com.checkout.identities.entities.BaseIdentityResponseStatus; +import com.checkout.identities.entities.DeclaredData; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * Response for identity verification operations + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IdentityVerificationResponse extends BaseIdentityResponseStatus { + + /** + * The applicant's unique identifier. + */ + private String applicantId; + + /** + * Your configuration ID. + */ + private String userJourneyId; + + /** + * The personal details provided by the applicant. + */ + private DeclaredData declaredData; + + /** + * The URL to redirect the applicant to after the attempt. + * (Only present in create-and-open response) + */ + private String redirectUrl; + + /** + * One or more codes that provide more information about risks associated with the verification. + */ + private List riskLabels; + + /** + * The details of the applicant's identity documents. + */ + private List documents; + + /** + * The details of the image of the applicant's face extracted from the video. + */ + private Face face; + + /** + * The details of the applicant's verified identity. + */ + private VerifiedIdentity verifiedIdentity; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationStatus.java b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationStatus.java new file mode 100644 index 00000000..dedd6a1b --- /dev/null +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationStatus.java @@ -0,0 +1,57 @@ +package com.checkout.identities.identityverification.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * Enumeration of identity verification statuses + */ +public enum IdentityVerificationStatus { + + /** + * Identity verification approved + */ + @SerializedName("approved") + APPROVED, + + /** + * Capture in progress + */ + @SerializedName("capture_in_progress") + CAPTURE_IN_PROGRESS, + + /** + * Checks in progress + */ + @SerializedName("checks_in_progress") + CHECKS_IN_PROGRESS, + + /** + * Identity verification declined + */ + @SerializedName("declined") + DECLINED, + + /** + * Identity verification inconclusive + */ + @SerializedName("inconclusive") + INCONCLUSIVE, + + /** + * Identity verification pending + */ + @SerializedName("pending") + PENDING, + + /** + * Identity verification refused + */ + @SerializedName("refused") + REFUSED, + + /** + * Retry required + */ + @SerializedName("retry_required") + RETRY_REQUIRED +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/IssuingClient.java b/src/main/java/com/checkout/issuing/IssuingClient.java index 5d9856d6..45901b64 100644 --- a/src/main/java/com/checkout/issuing/IssuingClient.java +++ b/src/main/java/com/checkout/issuing/IssuingClient.java @@ -2,28 +2,47 @@ import com.checkout.EmptyResponse; import com.checkout.common.IdResponse; +import com.checkout.issuing.cardholders.CardholderAccessTokenRequest; +import com.checkout.issuing.cardholders.CardholderAccessTokenResponse; import com.checkout.issuing.cardholders.CardholderCardsResponse; import com.checkout.issuing.cardholders.CardholderDetailsResponse; import com.checkout.issuing.cardholders.CardholderRequest; +import com.checkout.issuing.cardholders.CardholderUpdateRequest; import com.checkout.issuing.cardholders.CardholderResponse; +import com.checkout.issuing.cardholders.CardholderUpdateResponse; import com.checkout.issuing.cards.requests.create.CardRequest; import com.checkout.issuing.cards.requests.credentials.CardCredentialsQuery; import com.checkout.issuing.cards.requests.enrollment.ThreeDSEnrollmentRequest; import com.checkout.issuing.cards.requests.enrollment.ThreeDSUpdateRequest; +import com.checkout.issuing.cards.requests.renew.RenewCardRequest; +import com.checkout.issuing.cards.requests.revocation.ScheduleRevocationRequest; import com.checkout.issuing.cards.requests.revoke.RevokeCardRequest; import com.checkout.issuing.cards.requests.suspend.SuspendCardRequest; +import com.checkout.issuing.cards.requests.update.UpdateCardRequest; import com.checkout.issuing.cards.responses.CardDetailsResponse; import com.checkout.issuing.cards.responses.CardResponse; import com.checkout.issuing.cards.responses.credentials.CardCredentialsResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSEnrollmentDetailsResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSEnrollmentResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSUpdateResponse; +import com.checkout.issuing.cards.responses.renew.RenewCardResponse; +import com.checkout.issuing.cards.responses.update.UpdateCardResponse; import com.checkout.issuing.controls.requests.create.CardControlRequest; import com.checkout.issuing.controls.requests.query.CardControlsQuery; import com.checkout.issuing.controls.requests.update.UpdateCardControlRequest; +import com.checkout.issuing.controls.requests.controlgroup.CreateControlGroupRequest; +import com.checkout.issuing.controls.requests.controlgroup.ControlGroupQuery; +import com.checkout.issuing.controls.requests.controlprofile.CreateControlProfileRequest; +import com.checkout.issuing.controls.requests.controlprofile.ControlProfileQuery; +import com.checkout.issuing.controls.requests.controlprofile.UpdateControlProfileRequest; import com.checkout.issuing.controls.responses.create.CardControlResponse; import com.checkout.issuing.controls.responses.query.CardControlsQueryResponse; +import com.checkout.issuing.controls.responses.controlgroup.ControlGroupResponse; +import com.checkout.issuing.controls.responses.controlgroup.ControlGroupsQueryResponse; +import com.checkout.issuing.controls.responses.controlprofile.ControlProfileResponse; +import com.checkout.issuing.controls.responses.controlprofile.ControlProfilesQueryResponse; import com.checkout.issuing.testing.requests.CardAuthorizationClearingRequest; +import com.checkout.issuing.testing.requests.CardAuthorizationRefundsRequest; import com.checkout.issuing.testing.requests.CardAuthorizationIncrementingRequest; import com.checkout.issuing.testing.requests.CardAuthorizationRequest; import com.checkout.issuing.testing.requests.CardAuthorizationReversalRequest; @@ -36,8 +55,12 @@ public interface IssuingClient { + CompletableFuture requestCardholderAccessToken(CardholderAccessTokenRequest cardholderAccessTokenRequest); + CompletableFuture createCardholder(CardholderRequest cardholderRequest); + CompletableFuture updateCardholder(String cardholderId, CardholderUpdateRequest cardholderUpdateRequest); + CompletableFuture getCardholder(String cardholderId); CompletableFuture getCardholderCards(String cardholderId); @@ -70,6 +93,30 @@ public interface IssuingClient { CompletableFuture removeCardControl(final String controlId); + CompletableFuture createControlGroup(final CreateControlGroupRequest createControlGroupRequest); + + CompletableFuture getControlGroups(final ControlGroupQuery controlGroupQuery); + + CompletableFuture getControlGroupDetails(final String controlGroupId); + + CompletableFuture removeControlGroup(final String controlGroupId); + + // Async - Control Profile methods + + CompletableFuture createControlProfile(final CreateControlProfileRequest createControlProfileRequest); + + CompletableFuture getControlProfiles(final ControlProfileQuery controlProfileQuery); + + CompletableFuture getControlProfileDetails(final String controlProfileId); + + CompletableFuture updateControlProfile(final String controlProfileId, final UpdateControlProfileRequest updateControlProfileRequest); + + CompletableFuture removeControlProfile(final String controlProfileId); + + CompletableFuture addTargetToControlProfile(final String controlProfileId, final String targetId); + + CompletableFuture removeTargetFromControlProfile(final String controlProfileId, final String targetId); + CompletableFuture simulateAuthorization(final CardAuthorizationRequest cardAuthorizationRequest); CompletableFuture simulateIncrementingAuthorization( @@ -82,14 +129,31 @@ CompletableFuture simulateClearing( final CardAuthorizationClearingRequest cardAuthorizationClearingRequest ); + CompletableFuture simulateRefund( + final String authorizationId, + final CardAuthorizationRefundsRequest cardAuthorizationRefundsRequest + ); + CompletableFuture simulateReversal( final String authorizationId, final CardAuthorizationReversalRequest cardAuthorizationReversalRequest ); + CompletableFuture updateCard(final String cardId, final UpdateCardRequest updateCardRequest); + + CompletableFuture renewCard(final String cardId, final RenewCardRequest renewCardRequest); + + CompletableFuture scheduleCardRevocation(final String cardId, final ScheduleRevocationRequest scheduleRevocationRequest); + + CompletableFuture deleteScheduledRevocation(final String cardId); + // Synchronous methods + CardholderAccessTokenResponse requestCardholderAccessTokenSync(CardholderAccessTokenRequest cardholderAccessTokenRequest); + CardholderResponse createCardholderSync(CardholderRequest cardholderRequest); + CardholderUpdateResponse updateCardholderSync(String cardholderId, CardholderUpdateRequest cardholderUpdateRequest); + CardholderDetailsResponse getCardholderSync(String cardholderId); CardholderCardsResponse getCardholderCardsSync(String cardholderId); @@ -122,6 +186,30 @@ CompletableFuture simulateReversal( IdResponse removeCardControlSync(String controlId); + ControlGroupResponse createControlGroupSync(CreateControlGroupRequest createControlGroupRequest); + + ControlGroupsQueryResponse getControlGroupsSync(ControlGroupQuery controlGroupQuery); + + ControlGroupResponse getControlGroupDetailsSync(String controlGroupId); + + IdResponse removeControlGroupSync(String controlGroupId); + + // Sync - Control Profile methods + + ControlProfileResponse createControlProfileSync(CreateControlProfileRequest createControlProfileRequest); + + ControlProfilesQueryResponse getControlProfilesSync(ControlProfileQuery controlProfileQuery); + + ControlProfileResponse getControlProfileDetailsSync(String controlProfileId); + + ControlProfileResponse updateControlProfileSync(String controlProfileId, UpdateControlProfileRequest updateControlProfileRequest); + + EmptyResponse removeControlProfileSync(String controlProfileId); + + VoidResponse addTargetToControlProfileSync(String controlProfileId, String targetId); + + VoidResponse removeTargetFromControlProfileSync(String controlProfileId, String targetId); + CardAuthorizationResponse simulateAuthorizationSync(CardAuthorizationRequest cardAuthorizationRequest); CardAuthorizationIncrementingResponse simulateIncrementingAuthorizationSync( @@ -134,8 +222,21 @@ EmptyResponse simulateClearingSync( CardAuthorizationClearingRequest cardAuthorizationClearingRequest ); + EmptyResponse simulateRefundSync( + final String authorizationId, + final CardAuthorizationRefundsRequest cardAuthorizationRefundsRequest + ); + CardAuthorizationReversalResponse simulateReversalSync( String authorizationId, CardAuthorizationReversalRequest cardAuthorizationReversalRequest ); + + UpdateCardResponse updateCardSync(String cardId, UpdateCardRequest updateCardRequest); + + RenewCardResponse renewCardSync(String cardId, RenewCardRequest renewCardRequest); + + VoidResponse scheduleCardRevocationSync(String cardId, ScheduleRevocationRequest scheduleRevocationRequest); + + VoidResponse deleteScheduledRevocationSync(String cardId); } \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/IssuingClientImpl.java b/src/main/java/com/checkout/issuing/IssuingClientImpl.java index 49862849..8df74e07 100644 --- a/src/main/java/com/checkout/issuing/IssuingClientImpl.java +++ b/src/main/java/com/checkout/issuing/IssuingClientImpl.java @@ -4,30 +4,50 @@ import com.checkout.ApiClient; import com.checkout.CheckoutConfiguration; import com.checkout.EmptyResponse; +import com.checkout.GsonSerializer; import com.checkout.SdkAuthorizationType; import com.checkout.common.IdResponse; import com.checkout.issuing.cardholders.CardholderCardsResponse; import com.checkout.issuing.cardholders.CardholderDetailsResponse; +import com.checkout.issuing.cardholders.CardholderAccessTokenRequest; +import com.checkout.issuing.cardholders.CardholderAccessTokenResponse; import com.checkout.issuing.cardholders.CardholderRequest; +import com.checkout.issuing.cardholders.CardholderUpdateRequest; import com.checkout.issuing.cardholders.CardholderResponse; +import com.checkout.issuing.cardholders.CardholderUpdateResponse; import com.checkout.issuing.cards.requests.create.CardRequest; import com.checkout.issuing.cards.requests.credentials.CardCredentialsQuery; import com.checkout.issuing.cards.requests.enrollment.ThreeDSEnrollmentRequest; import com.checkout.issuing.cards.requests.enrollment.ThreeDSUpdateRequest; +import com.checkout.issuing.cards.requests.renew.RenewCardRequest; +import com.checkout.issuing.cards.requests.revocation.ScheduleRevocationRequest; import com.checkout.issuing.cards.requests.revoke.RevokeCardRequest; import com.checkout.issuing.cards.requests.suspend.SuspendCardRequest; +import com.checkout.issuing.cards.requests.update.UpdateCardRequest; import com.checkout.issuing.cards.responses.CardDetailsResponse; import com.checkout.issuing.cards.responses.CardResponse; import com.checkout.issuing.cards.responses.credentials.CardCredentialsResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSEnrollmentDetailsResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSEnrollmentResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSUpdateResponse; +import com.checkout.issuing.cards.responses.renew.RenewCardResponse; +import com.checkout.issuing.cards.responses.update.UpdateCardResponse; import com.checkout.issuing.controls.requests.create.CardControlRequest; import com.checkout.issuing.controls.requests.query.CardControlsQuery; import com.checkout.issuing.controls.requests.update.UpdateCardControlRequest; +import com.checkout.issuing.controls.requests.controlgroup.CreateControlGroupRequest; +import com.checkout.issuing.controls.requests.controlgroup.ControlGroupQuery; +import com.checkout.issuing.controls.requests.controlprofile.CreateControlProfileRequest; +import com.checkout.issuing.controls.requests.controlprofile.ControlProfileQuery; +import com.checkout.issuing.controls.requests.controlprofile.UpdateControlProfileRequest; import com.checkout.issuing.controls.responses.create.CardControlResponse; import com.checkout.issuing.controls.responses.query.CardControlsQueryResponse; +import com.checkout.issuing.controls.responses.controlgroup.ControlGroupResponse; +import com.checkout.issuing.controls.responses.controlgroup.ControlGroupsQueryResponse; +import com.checkout.issuing.controls.responses.controlprofile.ControlProfileResponse; +import com.checkout.issuing.controls.responses.controlprofile.ControlProfilesQueryResponse; import com.checkout.issuing.testing.requests.CardAuthorizationClearingRequest; +import com.checkout.issuing.testing.requests.CardAuthorizationRefundsRequest; import com.checkout.issuing.testing.requests.CardAuthorizationIncrementingRequest; import com.checkout.issuing.testing.requests.CardAuthorizationRequest; import com.checkout.issuing.testing.requests.CardAuthorizationReversalRequest; @@ -36,8 +56,13 @@ import com.checkout.issuing.testing.responses.CardAuthorizationReversalResponse; import com.checkout.payments.VoidResponse; +import io.vavr.concurrent.Task; + +import java.io.UnsupportedEncodingException; import java.util.concurrent.CompletableFuture; +import org.apache.http.client.entity.UrlEncodedFormEntity; + import static com.checkout.common.CheckoutUtils.validateParams; public class IssuingClientImpl extends AbstractClient implements IssuingClient { @@ -60,18 +85,45 @@ public class IssuingClientImpl extends AbstractClient implements IssuingClient { private static final String CONTROLS_PATH = "controls"; + private static final String CONTROL_GROUPS_PATH = "control-groups"; + + private static final String CONTROL_PROFILES_PATH = "control-profiles"; + private static final String SIMULATE_PATH = "simulate"; private static final String AUTHORIZATIONS_PATH = "authorizations"; private static final String PRESENTMENTS_PATH = "presentments"; + private static final String REFUNDS_PATH = "refunds"; + private static final String REVERSALS_PATH = "reversals"; + private static final String RENEW_PATH = "renew"; + + private static final String SCHEDULE_REVOCATION_PATH = "schedule-revocation"; + + private static final String ACCESS_TOKEN_PATH = "access/connect/token"; + public IssuingClientImpl(final ApiClient apiClient, final CheckoutConfiguration configuration) { super(apiClient, configuration, SdkAuthorizationType.SECRET_KEY_OR_OAUTH); } + @Override + public CompletableFuture requestCardholderAccessToken(final CardholderAccessTokenRequest cardholderAccessTokenRequest) { + validateParams("cardholderAccessTokenRequest", cardholderAccessTokenRequest); + + final UrlEncodedFormEntity form = createFormUrlEncodedContent(cardholderAccessTokenRequest); + + return apiClient.postAsync( + buildPath(ISSUING_PATH, ACCESS_TOKEN_PATH), + sdkAuthorization(), + CardholderAccessTokenResponse.class, + form, + null + ); + } + @Override public CompletableFuture createCardholder(final CardholderRequest cardholderRequest) { validateParams("cardholderRequest", cardholderRequest); @@ -94,6 +146,19 @@ public CompletableFuture getCardholder(final String c ); } + @Override + public CompletableFuture updateCardholder(final String cardholderId, + final CardholderUpdateRequest cardholderUpdateRequest) { + validateParams("cardholderId", cardholderId, "cardholderUpdateRequest", cardholderUpdateRequest); + return apiClient.patchAsync( + buildPath(ISSUING_PATH, CARDHOLDERS_PATH, cardholderId), + sdkAuthorization(), + CardholderUpdateResponse.class, + cardholderUpdateRequest, + null + ); + } + @Override public CompletableFuture getCardholderCards(final String cardholderId) { validateParams("cardholderId", cardholderId); @@ -278,6 +343,128 @@ public CompletableFuture removeCardControl(final String controlId) { ); } + @Override + public CompletableFuture createControlGroup(final CreateControlGroupRequest createControlGroupRequest) { + validateParams("createControlGroupRequest", createControlGroupRequest); + return apiClient.postAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_GROUPS_PATH), + sdkAuthorization(), + ControlGroupResponse.class, + createControlGroupRequest, + null + ); + } + + @Override + public CompletableFuture getControlGroups(final ControlGroupQuery controlGroupQuery) { + validateParams("controlGroupQuery", controlGroupQuery); + return apiClient.queryAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_GROUPS_PATH), + sdkAuthorization(), + controlGroupQuery, + ControlGroupsQueryResponse.class + ); + } + + @Override + public CompletableFuture getControlGroupDetails(final String controlGroupId) { + validateParams("controlGroupId", controlGroupId); + return apiClient.getAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_GROUPS_PATH, controlGroupId), + sdkAuthorization(), + ControlGroupResponse.class + ); + } + + @Override + public CompletableFuture removeControlGroup(final String controlGroupId) { + validateParams("controlGroupId", controlGroupId); + return apiClient.deleteAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_GROUPS_PATH, controlGroupId), + sdkAuthorization(), + IdResponse.class + ); + } + + @Override + public CompletableFuture createControlProfile(final CreateControlProfileRequest createControlProfileRequest) { + validateParams("createControlProfileRequest", createControlProfileRequest); + return apiClient.postAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH), + sdkAuthorization(), + ControlProfileResponse.class, + createControlProfileRequest, + null + ); + } + + @Override + public CompletableFuture getControlProfiles(final ControlProfileQuery controlProfileQuery) { + validateParams("controlProfileQuery", controlProfileQuery); + return apiClient.queryAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH), + sdkAuthorization(), + controlProfileQuery, + ControlProfilesQueryResponse.class + ); + } + + @Override + public CompletableFuture getControlProfileDetails(final String controlProfileId) { + validateParams("controlProfileId", controlProfileId); + return apiClient.getAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId), + sdkAuthorization(), + ControlProfileResponse.class + ); + } + + @Override + public CompletableFuture updateControlProfile(final String controlProfileId, final UpdateControlProfileRequest updateControlProfileRequest) { + validateParams("controlProfileId", controlProfileId, "updateControlProfileRequest", updateControlProfileRequest); + return apiClient.patchAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId), + sdkAuthorization(), + ControlProfileResponse.class, + updateControlProfileRequest, + null + ); + } + + @Override + public CompletableFuture removeControlProfile(final String controlProfileId) { + validateParams("controlProfileId", controlProfileId); + return apiClient.deleteAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId), + sdkAuthorization(), + EmptyResponse.class + ); + } + + @Override + public CompletableFuture addTargetToControlProfile(final String controlProfileId, final String targetId) { + validateParams("controlProfileId", controlProfileId, "targetId", targetId); + return apiClient.postAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId, "add", targetId), + sdkAuthorization(), + VoidResponse.class, + null, + null + ); + } + + @Override + public CompletableFuture removeTargetFromControlProfile(final String controlProfileId, final String targetId) { + validateParams("controlProfileId", controlProfileId, "targetId", targetId); + return apiClient.postAsync( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId, "remove", targetId), + sdkAuthorization(), + VoidResponse.class, + null, + null + ); + } + @Override public CompletableFuture simulateAuthorization(final CardAuthorizationRequest cardAuthorizationRequest) { validateParams("cardAuthorizationRequest", cardAuthorizationRequest); @@ -320,6 +507,22 @@ public CompletableFuture simulateClearing( ); } + @Override + public CompletableFuture simulateRefund( + String authorizationId, + CardAuthorizationRefundsRequest cardAuthorizationRefundsRequest + ) { + validateAuthorizationIdAndRefundRequest(authorizationId, cardAuthorizationRefundsRequest); + return apiClient.postAsync( + buildPath(ISSUING_PATH, SIMULATE_PATH, AUTHORIZATIONS_PATH, authorizationId, REFUNDS_PATH), + sdkAuthorization(), + EmptyResponse.class, + cardAuthorizationRefundsRequest, + null + ); + } + + @Override public CompletableFuture simulateReversal( String authorizationId, @@ -335,7 +538,68 @@ public CompletableFuture simulateReversal( ); } + @Override + public CompletableFuture updateCard(final String cardId, final UpdateCardRequest updateCardRequest) { + validateParams("cardId", cardId, "updateCardRequest", updateCardRequest); + return apiClient.patchAsync( + buildPath(ISSUING_PATH, CARDS_PATH, cardId), + sdkAuthorization(), + UpdateCardResponse.class, + updateCardRequest, + null + ); + } + + @Override + public CompletableFuture renewCard(final String cardId, final RenewCardRequest renewCardRequest) { + validateParams("cardId", cardId, "renewCardRequest", renewCardRequest); + return apiClient.postAsync( + buildPath(ISSUING_PATH, CARDS_PATH, cardId, RENEW_PATH), + sdkAuthorization(), + RenewCardResponse.class, + renewCardRequest, + null + ); + } + + @Override + public CompletableFuture scheduleCardRevocation(final String cardId, final ScheduleRevocationRequest scheduleRevocationRequest) { + validateParams("cardId", cardId, "scheduleRevocationRequest", scheduleRevocationRequest); + return apiClient.postAsync( + buildPath(ISSUING_PATH, CARDS_PATH, cardId, SCHEDULE_REVOCATION_PATH), + sdkAuthorization(), + VoidResponse.class, + scheduleRevocationRequest, + null + ); + } + + @Override + public CompletableFuture deleteScheduledRevocation(final String cardId) { + validateParams("cardId", cardId); + return apiClient.deleteAsync( + buildPath(ISSUING_PATH, CARDS_PATH, cardId, SCHEDULE_REVOCATION_PATH), + sdkAuthorization(), + VoidResponse.class + ); + } + // Synchronous methods + @Override + public CardholderAccessTokenResponse requestCardholderAccessTokenSync(final CardholderAccessTokenRequest cardholderAccessTokenRequest) { + validateParams("cardholderAccessTokenRequest", cardholderAccessTokenRequest); + + UrlEncodedFormEntity form = createFormUrlEncodedContent(cardholderAccessTokenRequest); + + return apiClient.post( + buildPath(ISSUING_PATH, ACCESS_TOKEN_PATH), + sdkAuthorization(), + CardholderAccessTokenResponse.class, + form, + null + ); + } + @Override public CardholderResponse createCardholderSync(final CardholderRequest cardholderRequest) { validateCardholderRequest(cardholderRequest); @@ -358,6 +622,18 @@ public CardholderDetailsResponse getCardholderSync(final String cardholderId) { ); } + @Override + public CardholderUpdateResponse updateCardholderSync(final String cardholderId, final CardholderUpdateRequest cardholderUpdateRequest) { + validateParams("cardholderId", cardholderId, "cardholderUpdateRequest", cardholderUpdateRequest); + return apiClient.patch( + buildPath(ISSUING_PATH, CARDHOLDERS_PATH, cardholderId), + sdkAuthorization(), + CardholderUpdateResponse.class, + cardholderUpdateRequest, + null + ); + } + @Override public CardholderCardsResponse getCardholderCardsSync(final String cardholderId) { validateCardholderId(cardholderId); @@ -542,6 +818,128 @@ public IdResponse removeCardControlSync(final String controlId) { ); } + @Override + public ControlGroupResponse createControlGroupSync(final CreateControlGroupRequest createControlGroupRequest) { + validateParams("createControlGroupRequest", createControlGroupRequest); + return apiClient.post( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_GROUPS_PATH), + sdkAuthorization(), + ControlGroupResponse.class, + createControlGroupRequest, + null + ); + } + + @Override + public ControlGroupsQueryResponse getControlGroupsSync(final ControlGroupQuery controlGroupQuery) { + validateParams("controlGroupQuery", controlGroupQuery); + return apiClient.query( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_GROUPS_PATH), + sdkAuthorization(), + controlGroupQuery, + ControlGroupsQueryResponse.class + ); + } + + @Override + public ControlGroupResponse getControlGroupDetailsSync(final String controlGroupId) { + validateParams("controlGroupId", controlGroupId); + return apiClient.get( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_GROUPS_PATH, controlGroupId), + sdkAuthorization(), + ControlGroupResponse.class + ); + } + + @Override + public IdResponse removeControlGroupSync(final String controlGroupId) { + validateParams("controlGroupId", controlGroupId); + return apiClient.delete( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_GROUPS_PATH, controlGroupId), + sdkAuthorization(), + IdResponse.class + ); + } + + @Override + public ControlProfileResponse createControlProfileSync(final CreateControlProfileRequest createControlProfileRequest) { + validateParams("createControlProfileRequest", createControlProfileRequest); + return apiClient.post( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH), + sdkAuthorization(), + ControlProfileResponse.class, + createControlProfileRequest, + null + ); + } + + @Override + public ControlProfilesQueryResponse getControlProfilesSync(final ControlProfileQuery controlProfileQuery) { + validateParams("controlProfileQuery", controlProfileQuery); + return apiClient.query( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH), + sdkAuthorization(), + controlProfileQuery, + ControlProfilesQueryResponse.class + ); + } + + @Override + public ControlProfileResponse getControlProfileDetailsSync(final String controlProfileId) { + validateParams("controlProfileId", controlProfileId); + return apiClient.get( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId), + sdkAuthorization(), + ControlProfileResponse.class + ); + } + + @Override + public ControlProfileResponse updateControlProfileSync(final String controlProfileId, final UpdateControlProfileRequest updateControlProfileRequest) { + validateParams("controlProfileId", controlProfileId, "updateControlProfileRequest", updateControlProfileRequest); + return apiClient.patch( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId), + sdkAuthorization(), + ControlProfileResponse.class, + updateControlProfileRequest, + null + ); + } + + @Override + public EmptyResponse removeControlProfileSync(final String controlProfileId) { + validateParams("controlProfileId", controlProfileId); + return apiClient.delete( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId), + sdkAuthorization(), + EmptyResponse.class + ); + } + + @Override + public VoidResponse addTargetToControlProfileSync(final String controlProfileId, final String targetId) { + validateParams("controlProfileId", controlProfileId, "targetId", targetId); + return apiClient.post( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId, "add", targetId), + sdkAuthorization(), + VoidResponse.class, + null, + null + ); + } + + @Override + public VoidResponse removeTargetFromControlProfileSync(final String controlProfileId, final String targetId) { + validateParams("controlProfileId", controlProfileId, "targetId", targetId); + return apiClient.post( + buildPath(ISSUING_PATH, CONTROLS_PATH, CONTROL_PROFILES_PATH, controlProfileId, "remove", targetId), + sdkAuthorization(), + VoidResponse.class, + null, + null + ); + } + @Override public CardAuthorizationResponse simulateAuthorizationSync(final CardAuthorizationRequest cardAuthorizationRequest) { validateCardAuthorizationRequest(cardAuthorizationRequest); @@ -584,6 +982,21 @@ public EmptyResponse simulateClearingSync( ); } + @Override + public EmptyResponse simulateRefundSync( + String authorizationId, + CardAuthorizationRefundsRequest cardAuthorizationRefundsRequest + ) { + validateAuthorizationIdAndRefundRequest(authorizationId, cardAuthorizationRefundsRequest); + return apiClient.post( + buildPath(ISSUING_PATH, SIMULATE_PATH, AUTHORIZATIONS_PATH, authorizationId, REFUNDS_PATH), + sdkAuthorization(), + EmptyResponse.class, + cardAuthorizationRefundsRequest, + null + ); + } + @Override public CardAuthorizationReversalResponse simulateReversalSync( String authorizationId, @@ -599,7 +1012,61 @@ public CardAuthorizationReversalResponse simulateReversalSync( ); } + @Override + public UpdateCardResponse updateCardSync(final String cardId, final UpdateCardRequest updateCardRequest) { + validateParams("cardId", cardId, "updateCardRequest", updateCardRequest); + return apiClient.patch( + buildPath(ISSUING_PATH, CARDS_PATH, cardId), + sdkAuthorization(), + UpdateCardResponse.class, + updateCardRequest, + null + ); + } + + @Override + public RenewCardResponse renewCardSync(final String cardId, final RenewCardRequest renewCardRequest) { + validateParams("cardId", cardId, "renewCardRequest", renewCardRequest); + return apiClient.post( + buildPath(ISSUING_PATH, CARDS_PATH, cardId, RENEW_PATH), + sdkAuthorization(), + RenewCardResponse.class, + renewCardRequest, + null + ); + } + + @Override + public VoidResponse scheduleCardRevocationSync(final String cardId, final ScheduleRevocationRequest scheduleRevocationRequest) { + validateParams("cardId", cardId, "scheduleRevocationRequest", scheduleRevocationRequest); + return apiClient.post( + buildPath(ISSUING_PATH, CARDS_PATH, cardId, SCHEDULE_REVOCATION_PATH), + sdkAuthorization(), + VoidResponse.class, + scheduleRevocationRequest, + null + ); + } + + @Override + public VoidResponse deleteScheduledRevocationSync(final String cardId) { + validateParams("cardId", cardId); + return apiClient.delete( + buildPath(ISSUING_PATH, CARDS_PATH, cardId, SCHEDULE_REVOCATION_PATH), + sdkAuthorization(), + VoidResponse.class + ); + } + // Common methods + private UrlEncodedFormEntity createFormUrlEncodedContent(final CardholderAccessTokenRequest cardholderAccessTokenRequest) { + try { + return GsonSerializer.createFormUrlEncodedContent(cardholderAccessTokenRequest); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Error creating form url encoded content", e); + } + } + private void validateCardholderRequest(final CardholderRequest cardholderRequest) { validateParams("cardholderRequest", cardholderRequest); } @@ -664,6 +1131,10 @@ private void validateAuthorizationIdAndClearingRequest(final String authorizatio validateParams("authorizationId", authorizationId, "cardAuthorizationClearingRequest", cardAuthorizationClearingRequest); } + private void validateAuthorizationIdAndRefundRequest(final String authorizationId, final CardAuthorizationRefundsRequest cardAuthorizationRefundsRequest) { + validateParams("authorizationId", authorizationId, "cardAuthorizationRefundsRequest", cardAuthorizationRefundsRequest); + } + private void validateAuthorizationIdAndReversalRequest(final String authorizationId, final CardAuthorizationReversalRequest cardAuthorizationReversalRequest) { validateParams("authorizationId", authorizationId, "cardAuthorizationReversalRequest", cardAuthorizationReversalRequest); } diff --git a/src/main/java/com/checkout/issuing/cardholders/CardholderAccessTokenRequest.java b/src/main/java/com/checkout/issuing/cardholders/CardholderAccessTokenRequest.java new file mode 100644 index 00000000..382ff6a7 --- /dev/null +++ b/src/main/java/com/checkout/issuing/cardholders/CardholderAccessTokenRequest.java @@ -0,0 +1,26 @@ +package com.checkout.issuing.cardholders; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class CardholderAccessTokenRequest { + + @SerializedName("grant_type") + private String grantType; + + @SerializedName("client_id") + private String clientID; + + @SerializedName("client_secret") + private String clientSecret; + + @SerializedName("cardholder_id") + private String cardholderId; + + @SerializedName("single_use") + private Boolean singleUse; + +} diff --git a/src/main/java/com/checkout/issuing/cardholders/CardholderAccessTokenResponse.java b/src/main/java/com/checkout/issuing/cardholders/CardholderAccessTokenResponse.java new file mode 100644 index 00000000..ca25ac50 --- /dev/null +++ b/src/main/java/com/checkout/issuing/cardholders/CardholderAccessTokenResponse.java @@ -0,0 +1,25 @@ +package com.checkout.issuing.cardholders; + +import com.checkout.common.Resource; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CardholderAccessTokenResponse extends Resource { + + @SerializedName("access_token") + private String accessToken; + + @SerializedName("token_type") + private String tokenType; + + @SerializedName("expires_in") + private Long expiresIn; + + @SerializedName("scope") + private String scope; +} diff --git a/src/main/java/com/checkout/issuing/cardholders/CardholderRequest.java b/src/main/java/com/checkout/issuing/cardholders/CardholderRequest.java index 12400e44..04439ae6 100644 --- a/src/main/java/com/checkout/issuing/cardholders/CardholderRequest.java +++ b/src/main/java/com/checkout/issuing/cardholders/CardholderRequest.java @@ -1,14 +1,14 @@ package com.checkout.issuing.cardholders; -import com.checkout.common.Address; -import com.checkout.common.Phone; import com.google.gson.annotations.SerializedName; -import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.SuperBuilder; @Data -@Builder -public class CardholderRequest { +@EqualsAndHashCode(callSuper = true) +@SuperBuilder +public class CardholderRequest extends CardholderUpdateRequest { private CardholderType type; @@ -16,29 +16,4 @@ public class CardholderRequest { @SerializedName("entity_id") private String entityId; - - @SerializedName("first_name") - private String firstName; - - @SerializedName("middle_name") - private String middleName; - - @SerializedName("last_name") - private String lastName; - - private String email; - - @SerializedName("phone_number") - private Phone phoneNumber; - - @SerializedName("date_of_birth") - private String dateOfBirth; - - @SerializedName("billing_address") - private Address billingAddress; - - @SerializedName("residency_address") - private Address residencyAddress; - - private CardholderDocument document; } diff --git a/src/main/java/com/checkout/issuing/cardholders/CardholderUpdateRequest.java b/src/main/java/com/checkout/issuing/cardholders/CardholderUpdateRequest.java new file mode 100644 index 00000000..158ad2de --- /dev/null +++ b/src/main/java/com/checkout/issuing/cardholders/CardholderUpdateRequest.java @@ -0,0 +1,37 @@ +package com.checkout.issuing.cardholders; + +import com.checkout.common.Address; +import com.checkout.common.Phone; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.experimental.SuperBuilder; + +@Data +@SuperBuilder +public class CardholderUpdateRequest { + + @SerializedName("first_name") + private String firstName; + + @SerializedName("middle_name") + private String middleName; + + @SerializedName("last_name") + private String lastName; + + private String email; + + @SerializedName("phone_number") + private Phone phoneNumber; + + @SerializedName("date_of_birth") + private String dateOfBirth; + + @SerializedName("billing_address") + private Address billingAddress; + + @SerializedName("residency_address") + private Address residencyAddress; + + private CardholderDocument document; +} diff --git a/src/main/java/com/checkout/issuing/cardholders/CardholderUpdateResponse.java b/src/main/java/com/checkout/issuing/cardholders/CardholderUpdateResponse.java new file mode 100644 index 00000000..b71bad5f --- /dev/null +++ b/src/main/java/com/checkout/issuing/cardholders/CardholderUpdateResponse.java @@ -0,0 +1,18 @@ +package com.checkout.issuing.cardholders; + +import com.checkout.common.Resource; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.Instant; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CardholderUpdateResponse extends Resource { + + @SerializedName("last_modified_date") + private Instant lastModifiedDate; +} diff --git a/src/main/java/com/checkout/issuing/cards/requests/renew/PhysicalCardRenewRequest.java b/src/main/java/com/checkout/issuing/cards/requests/renew/PhysicalCardRenewRequest.java new file mode 100644 index 00000000..197a9d72 --- /dev/null +++ b/src/main/java/com/checkout/issuing/cards/requests/renew/PhysicalCardRenewRequest.java @@ -0,0 +1,29 @@ +package com.checkout.issuing.cards.requests.renew; + +import com.checkout.issuing.cards.requests.create.ShippingInstruction; +import com.checkout.issuing.cards.requests.update.IssuingCardMetadata; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PhysicalCardRenewRequest extends RenewCardRequest { + + @SerializedName("shipping_instructions") + private ShippingInstruction shippingInstructions; + + @Builder + private PhysicalCardRenewRequest(final String displayName, + final String reference, + final IssuingCardMetadata metadata, + final ShippingInstruction shippingInstructions) { + super(displayName, reference, metadata); + this.shippingInstructions = shippingInstructions; + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/requests/renew/RenewCardRequest.java b/src/main/java/com/checkout/issuing/cards/requests/renew/RenewCardRequest.java new file mode 100644 index 00000000..b58ed4c8 --- /dev/null +++ b/src/main/java/com/checkout/issuing/cards/requests/renew/RenewCardRequest.java @@ -0,0 +1,24 @@ +package com.checkout.issuing.cards.requests.renew; + +import com.checkout.issuing.cards.requests.update.IssuingCardMetadata; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public abstract class RenewCardRequest { + + @SerializedName("display_name") + private String displayName; + + private String reference; + + private IssuingCardMetadata metadata; + + protected RenewCardRequest(final String displayName, + final String reference, + final IssuingCardMetadata metadata) { + this.displayName = displayName; + this.reference = reference; + this.metadata = metadata; + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/requests/renew/VirtualCardRenewRequest.java b/src/main/java/com/checkout/issuing/cards/requests/renew/VirtualCardRenewRequest.java new file mode 100644 index 00000000..beee7587 --- /dev/null +++ b/src/main/java/com/checkout/issuing/cards/requests/renew/VirtualCardRenewRequest.java @@ -0,0 +1,22 @@ +package com.checkout.issuing.cards.requests.renew; + +import com.checkout.issuing.cards.requests.update.IssuingCardMetadata; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class VirtualCardRenewRequest extends RenewCardRequest { + + @Builder + private VirtualCardRenewRequest(final String displayName, + final String reference, + final IssuingCardMetadata metadata) { + super(displayName, reference, metadata); + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/requests/revocation/ScheduleRevocationRequest.java b/src/main/java/com/checkout/issuing/cards/requests/revocation/ScheduleRevocationRequest.java new file mode 100644 index 00000000..781f5aaf --- /dev/null +++ b/src/main/java/com/checkout/issuing/cards/requests/revocation/ScheduleRevocationRequest.java @@ -0,0 +1,17 @@ +package com.checkout.issuing.cards.requests.revocation; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ScheduleRevocationRequest { + + @SerializedName("revocation_date") + private String revocationDate; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/requests/update/IssuingCardMetadata.java b/src/main/java/com/checkout/issuing/cards/requests/update/IssuingCardMetadata.java new file mode 100644 index 00000000..8d2a62a7 --- /dev/null +++ b/src/main/java/com/checkout/issuing/cards/requests/update/IssuingCardMetadata.java @@ -0,0 +1,29 @@ +package com.checkout.issuing.cards.requests.update; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IssuingCardMetadata { + + @SerializedName("udf1") + private String udf1; + + @SerializedName("udf2") + private String udf2; + + @SerializedName("udf3") + private String udf3; + + @SerializedName("udf4") + private String udf4; + + @SerializedName("udf5") + private String udf5; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/requests/update/UpdateCardRequest.java b/src/main/java/com/checkout/issuing/cards/requests/update/UpdateCardRequest.java new file mode 100644 index 00000000..29f4e627 --- /dev/null +++ b/src/main/java/com/checkout/issuing/cards/requests/update/UpdateCardRequest.java @@ -0,0 +1,24 @@ +package com.checkout.issuing.cards.requests.update; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UpdateCardRequest { + + private String reference; + + private IssuingCardMetadata metadata; + + @SerializedName("expiry_month") + private Integer expiryMonth; + + @SerializedName("expiry_year") + private Integer expiryYear; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/responses/renew/RenewCardResponse.java b/src/main/java/com/checkout/issuing/cards/responses/renew/RenewCardResponse.java new file mode 100644 index 00000000..c8343bb9 --- /dev/null +++ b/src/main/java/com/checkout/issuing/cards/responses/renew/RenewCardResponse.java @@ -0,0 +1,60 @@ +package com.checkout.issuing.cards.responses.renew; + +import com.checkout.common.CountryCode; +import com.checkout.common.Currency; +import com.checkout.common.Resource; +import com.checkout.issuing.cards.CardStatus; +import com.checkout.issuing.cards.CardType; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.Instant; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class RenewCardResponse extends Resource { + + @SerializedName("parent_card_id") + private String parentCardId; + + @SerializedName("cardholder_id") + private String cardholderId; + + private CardStatus status; + + private CardType type; + + private String id; + + @SerializedName("client_id") + private String clientId; + + @SerializedName("entity_id") + private String entityId; + + @SerializedName("last_four") + private String lastFour; + + @SerializedName("expiry_year") + private Integer expiryYear; + + @SerializedName("expiry_month") + private Integer expiryMonth; + + @SerializedName("display_name") + private String displayName; + + private String reference; + + @SerializedName("created_date") + private Instant createdDate; + + @SerializedName("billing_currency") + private Currency billingCurrency; + + @SerializedName("issuing_country") + private CountryCode issuingCountry; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/responses/update/UpdateCardResponse.java b/src/main/java/com/checkout/issuing/cards/responses/update/UpdateCardResponse.java new file mode 100644 index 00000000..785d737e --- /dev/null +++ b/src/main/java/com/checkout/issuing/cards/responses/update/UpdateCardResponse.java @@ -0,0 +1,18 @@ +package com.checkout.issuing.cards.responses.update; + +import com.checkout.common.Resource; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.Instant; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class UpdateCardResponse extends Resource { + + @SerializedName("last_modified_date") + private Instant lastModifiedDate; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/ControlType.java b/src/main/java/com/checkout/issuing/controls/requests/ControlType.java index 48a4b217..6424263b 100644 --- a/src/main/java/com/checkout/issuing/controls/requests/ControlType.java +++ b/src/main/java/com/checkout/issuing/controls/requests/ControlType.java @@ -7,6 +7,8 @@ public enum ControlType { @SerializedName("velocity_limit") VELOCITY_LIMIT, @SerializedName("mcc_limit") - MCC_LIMIT + MCC_LIMIT, + @SerializedName("mid_limit") + MID_LIMIT } diff --git a/src/main/java/com/checkout/issuing/controls/requests/MccControlType.java b/src/main/java/com/checkout/issuing/controls/requests/MccControlType.java deleted file mode 100644 index 3cb18550..00000000 --- a/src/main/java/com/checkout/issuing/controls/requests/MccControlType.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.checkout.issuing.controls.requests; - -import com.google.gson.annotations.SerializedName; - -public enum MccControlType { - - @SerializedName("allow") - ALLOW, - @SerializedName("block") - BLOCK - -} diff --git a/src/main/java/com/checkout/issuing/controls/requests/MccLimit.java b/src/main/java/com/checkout/issuing/controls/requests/MccLimit.java index 6c8a48e7..c6f37f8e 100644 --- a/src/main/java/com/checkout/issuing/controls/requests/MccLimit.java +++ b/src/main/java/com/checkout/issuing/controls/requests/MccLimit.java @@ -10,7 +10,7 @@ @Builder public class MccLimit { - private MccControlType type; + private String type; @SerializedName("mcc_list") private List mccList; diff --git a/src/main/java/com/checkout/issuing/controls/requests/MidLimit.java b/src/main/java/com/checkout/issuing/controls/requests/MidLimit.java new file mode 100644 index 00000000..196573a0 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/MidLimit.java @@ -0,0 +1,17 @@ +package com.checkout.issuing.controls.requests; + +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +public class MidLimit { + + private String type; + + @SerializedName("mid_list") + private List midList; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/VelocityLimit.java b/src/main/java/com/checkout/issuing/controls/requests/VelocityLimit.java index 8f7893cb..3a4cddf9 100644 --- a/src/main/java/com/checkout/issuing/controls/requests/VelocityLimit.java +++ b/src/main/java/com/checkout/issuing/controls/requests/VelocityLimit.java @@ -18,4 +18,7 @@ public class VelocityLimit { @SerializedName("mcc_list") private List mccList; + + @SerializedName("mid_list") + private List midList; } diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlgroup/ControlGroupControl.java b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/ControlGroupControl.java new file mode 100644 index 00000000..d8cf7548 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/ControlGroupControl.java @@ -0,0 +1,46 @@ +package com.checkout.issuing.controls.requests.controlgroup; + +import com.checkout.issuing.controls.requests.ControlType; +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +/** + * Base class for control group controls that define restrictions or permissions for card usage. + */ +@Data +public abstract class ControlGroupControl { + + /** + * The type of control being applied. + * Enum: "mcc_limit" "mid_limit" "velocity_limit" + * [Required] + */ + @SerializedName("control_type") + private final ControlType controlType; + + /** + * A description of the control. + * [Optional] + */ + private String description; + + /** + * Initializes a new instance of the ControlGroupControl class with the specified control type. + * + * @param controlType The type of control + * @param description A description of the control + */ + protected ControlGroupControl(ControlType controlType, String description) { + this.controlType = controlType; + this.description = description; + } + + /** + * Initializes a new instance of the ControlGroupControl class with the specified control type. + * + * @param controlType The type of control + */ + protected ControlGroupControl(ControlType controlType) { + this(controlType, null); + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlgroup/ControlGroupFailIf.java b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/ControlGroupFailIf.java new file mode 100644 index 00000000..ef574fde --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/ControlGroupFailIf.java @@ -0,0 +1,12 @@ +package com.checkout.issuing.controls.requests.controlgroup; + +import com.google.gson.annotations.SerializedName; + +public enum ControlGroupFailIf { + + @SerializedName("all_fail") + ALL_FAIL, + + @SerializedName("any_fail") + ANY_FAIL +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlgroup/ControlGroupQuery.java b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/ControlGroupQuery.java new file mode 100644 index 00000000..50305671 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/ControlGroupQuery.java @@ -0,0 +1,17 @@ +package com.checkout.issuing.controls.requests.controlgroup; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ControlGroupQuery { + + @SerializedName("target_id") + private String targetId; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlgroup/CreateControlGroupRequest.java b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/CreateControlGroupRequest.java new file mode 100644 index 00000000..a6b34383 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/CreateControlGroupRequest.java @@ -0,0 +1,26 @@ +package com.checkout.issuing.controls.requests.controlgroup; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateControlGroupRequest { + + @SerializedName("target_id") + private String targetId; + + @SerializedName("fail_if") + private ControlGroupFailIf failIf; + + private List controls; + + private String description; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlgroup/MccControlGroupControl.java b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/MccControlGroupControl.java new file mode 100644 index 00000000..34b7a978 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/MccControlGroupControl.java @@ -0,0 +1,43 @@ +package com.checkout.issuing.controls.requests.controlgroup; + +import com.checkout.issuing.controls.requests.ControlType; +import com.checkout.issuing.controls.requests.MccLimit; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * Control group control that applies MCC (Merchant Category Code) limits to restrict or allow transactions based on merchant categories. + */ +@Getter +@Setter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MccControlGroupControl extends ControlGroupControl { + + /** + * The MCC limit configuration that defines which merchant category codes to block or allow. + * [Required] + */ + @SerializedName("mcc_limit") + private MccLimit mccLimit; + + @Builder + private MccControlGroupControl( + final MccLimit mccLimit, + final String description + ) { + super(ControlType.MCC_LIMIT, description); + this.mccLimit = mccLimit; + } + + /** + * Default constructor for JSON deserialization. + */ + public MccControlGroupControl() { + super(ControlType.MCC_LIMIT); + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlgroup/MidControlGroupControl.java b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/MidControlGroupControl.java new file mode 100644 index 00000000..e0b13300 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/MidControlGroupControl.java @@ -0,0 +1,43 @@ +package com.checkout.issuing.controls.requests.controlgroup; + +import com.checkout.issuing.controls.requests.ControlType; +import com.checkout.issuing.controls.requests.MidLimit; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * Control group control that applies MID (Merchant Identifier) limits to restrict or allow transactions at specific merchants. + */ +@Getter +@Setter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MidControlGroupControl extends ControlGroupControl { + + /** + * The MID limit configuration that defines which merchant identifiers to block or allow. + * [Required] + */ + @SerializedName("mid_limit") + private MidLimit midLimit; + + @Builder + private MidControlGroupControl( + final MidLimit midLimit, + final String description + ) { + super(ControlType.MID_LIMIT, description); + this.midLimit = midLimit; + } + + /** + * Default constructor for JSON deserialization. + */ + public MidControlGroupControl() { + super(ControlType.MID_LIMIT); + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlgroup/VelocityControlGroupControl.java b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/VelocityControlGroupControl.java new file mode 100644 index 00000000..252e5ef4 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlgroup/VelocityControlGroupControl.java @@ -0,0 +1,44 @@ +package com.checkout.issuing.controls.requests.controlgroup; + +import com.checkout.issuing.controls.requests.ControlType; +import com.checkout.issuing.controls.requests.VelocityLimit; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * Control group control that applies velocity limits to restrict transaction frequency and amounts over time periods. + * Note: Control profiles cannot be a target for velocity_limit controls. + */ +@Getter +@Setter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class VelocityControlGroupControl extends ControlGroupControl { + + /** + * The velocity limit configuration that defines transaction frequency and amount restrictions over time periods. + * [Required] + */ + @SerializedName("velocity_limit") + private VelocityLimit velocityLimit; + + @Builder + private VelocityControlGroupControl( + final VelocityLimit velocityLimit, + final String description + ) { + super(ControlType.VELOCITY_LIMIT, description); + this.velocityLimit = velocityLimit; + } + + /** + * Default constructor for JSON deserialization. + */ + public VelocityControlGroupControl() { + super(ControlType.VELOCITY_LIMIT); + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlprofile/ControlProfileQuery.java b/src/main/java/com/checkout/issuing/controls/requests/controlprofile/ControlProfileQuery.java new file mode 100644 index 00000000..0066c274 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlprofile/ControlProfileQuery.java @@ -0,0 +1,17 @@ +package com.checkout.issuing.controls.requests.controlprofile; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ControlProfileQuery { + + @SerializedName("target_id") + private String targetId; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlprofile/CreateControlProfileRequest.java b/src/main/java/com/checkout/issuing/controls/requests/controlprofile/CreateControlProfileRequest.java new file mode 100644 index 00000000..3d208cfa --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlprofile/CreateControlProfileRequest.java @@ -0,0 +1,15 @@ +package com.checkout.issuing.controls.requests.controlprofile; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CreateControlProfileRequest { + + private String name; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/controlprofile/UpdateControlProfileRequest.java b/src/main/java/com/checkout/issuing/controls/requests/controlprofile/UpdateControlProfileRequest.java new file mode 100644 index 00000000..71ec351e --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/controlprofile/UpdateControlProfileRequest.java @@ -0,0 +1,15 @@ +package com.checkout.issuing.controls.requests.controlprofile; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UpdateControlProfileRequest { + + private String name; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/create/MidCardControlRequest.java b/src/main/java/com/checkout/issuing/controls/requests/create/MidCardControlRequest.java new file mode 100644 index 00000000..d0614343 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/requests/create/MidCardControlRequest.java @@ -0,0 +1,31 @@ +package com.checkout.issuing.controls.requests.create; + +import com.checkout.issuing.controls.requests.ControlType; +import com.checkout.issuing.controls.requests.MidLimit; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MidCardControlRequest extends CardControlRequest { + + @SerializedName("mid_limit") + private MidLimit midLimit; + + @Builder + private MidCardControlRequest( + final MidLimit midLimit, + final String description, + final String targetId + ) { + super(ControlType.MID_LIMIT, description, targetId); + this.midLimit = midLimit; + } + +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/requests/update/UpdateCardControlRequest.java b/src/main/java/com/checkout/issuing/controls/requests/update/UpdateCardControlRequest.java index b80b7c74..6b942a46 100644 --- a/src/main/java/com/checkout/issuing/controls/requests/update/UpdateCardControlRequest.java +++ b/src/main/java/com/checkout/issuing/controls/requests/update/UpdateCardControlRequest.java @@ -1,6 +1,7 @@ package com.checkout.issuing.controls.requests.update; import com.checkout.issuing.controls.requests.MccLimit; +import com.checkout.issuing.controls.requests.MidLimit; import com.checkout.issuing.controls.requests.VelocityLimit; import com.google.gson.annotations.SerializedName; import lombok.Builder; @@ -17,4 +18,7 @@ public class UpdateCardControlRequest { @SerializedName("mcc_limit") private MccLimit mccLimit; + + @SerializedName("mid_limit") + private MidLimit midLimit; } diff --git a/src/main/java/com/checkout/issuing/controls/responses/controlgroup/ControlGroupResponse.java b/src/main/java/com/checkout/issuing/controls/responses/controlgroup/ControlGroupResponse.java new file mode 100644 index 00000000..859f8b4d --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/responses/controlgroup/ControlGroupResponse.java @@ -0,0 +1,39 @@ +package com.checkout.issuing.controls.responses.controlgroup; + +import com.checkout.common.Resource; +import com.checkout.issuing.controls.requests.controlgroup.ControlGroupControl; +import com.checkout.issuing.controls.requests.controlgroup.ControlGroupFailIf; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.Instant; +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ControlGroupResponse extends Resource { + + private String id; + + @SerializedName("target_id") + private String targetId; + + @SerializedName("fail_if") + private ControlGroupFailIf failIf; + + private List controls; + + @SerializedName("is_editable") + private Boolean isEditable; + + @SerializedName("created_date") + private Instant createdDate; + + @SerializedName("last_modified_date") + private Instant lastModifiedDate; + + private String description; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/responses/controlgroup/ControlGroupsQueryResponse.java b/src/main/java/com/checkout/issuing/controls/responses/controlgroup/ControlGroupsQueryResponse.java new file mode 100644 index 00000000..a41862d9 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/responses/controlgroup/ControlGroupsQueryResponse.java @@ -0,0 +1,18 @@ +package com.checkout.issuing.controls.responses.controlgroup; + +import com.checkout.HttpMetadata; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ControlGroupsQueryResponse extends HttpMetadata { + + @SerializedName("control_groups") + private List controlGroups; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/responses/controlprofile/ControlProfileResponse.java b/src/main/java/com/checkout/issuing/controls/responses/controlprofile/ControlProfileResponse.java new file mode 100644 index 00000000..d442c9c9 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/responses/controlprofile/ControlProfileResponse.java @@ -0,0 +1,25 @@ +package com.checkout.issuing.controls.responses.controlprofile; + +import com.checkout.common.Resource; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.Instant; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ControlProfileResponse extends Resource { + + private String id; + + private String name; + + @SerializedName("created_date") + private Instant createdDate; + + @SerializedName("last_modified_date") + private Instant lastModifiedDate; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/responses/controlprofile/ControlProfilesQueryResponse.java b/src/main/java/com/checkout/issuing/controls/responses/controlprofile/ControlProfilesQueryResponse.java new file mode 100644 index 00000000..03447bc4 --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/responses/controlprofile/ControlProfilesQueryResponse.java @@ -0,0 +1,18 @@ +package com.checkout.issuing.controls.responses.controlprofile; + +import com.checkout.HttpMetadata; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ControlProfilesQueryResponse extends HttpMetadata { + + @SerializedName("control_profiles") + private List controlProfiles; +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/controls/responses/create/MccCardControlResponse.java b/src/main/java/com/checkout/issuing/controls/responses/create/MccCardControlResponse.java index 633c26e9..7c432ba4 100644 --- a/src/main/java/com/checkout/issuing/controls/responses/create/MccCardControlResponse.java +++ b/src/main/java/com/checkout/issuing/controls/responses/create/MccCardControlResponse.java @@ -25,6 +25,6 @@ private MccCardControlResponse(final MccLimit mccLimit) { } public MccCardControlResponse() { - super(ControlType.VELOCITY_LIMIT); + super(ControlType.MCC_LIMIT); } } diff --git a/src/main/java/com/checkout/issuing/controls/responses/create/MidCardControlResponse.java b/src/main/java/com/checkout/issuing/controls/responses/create/MidCardControlResponse.java new file mode 100644 index 00000000..b85b5e9b --- /dev/null +++ b/src/main/java/com/checkout/issuing/controls/responses/create/MidCardControlResponse.java @@ -0,0 +1,30 @@ +package com.checkout.issuing.controls.responses.create; + +import com.checkout.issuing.controls.requests.ControlType; +import com.checkout.issuing.controls.requests.MidLimit; +import com.google.gson.annotations.SerializedName; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MidCardControlResponse extends CardControlResponse { + + @SerializedName("mid_limit") + private MidLimit midLimit; + + @Builder + private MidCardControlResponse(final MidLimit midLimit) { + super(ControlType.MID_LIMIT); + this.midLimit = midLimit; + } + + public MidCardControlResponse() { + super(ControlType.MID_LIMIT); + } +} \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/testing/requests/CardAuthorizationRefundsRequest.java b/src/main/java/com/checkout/issuing/testing/requests/CardAuthorizationRefundsRequest.java new file mode 100644 index 00000000..ef209eac --- /dev/null +++ b/src/main/java/com/checkout/issuing/testing/requests/CardAuthorizationRefundsRequest.java @@ -0,0 +1,11 @@ +package com.checkout.issuing.testing.requests; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class CardAuthorizationRefundsRequest { + + private int amount; +} diff --git a/src/main/java/com/checkout/payments/PaymentProcessing.java b/src/main/java/com/checkout/payments/PaymentProcessing.java index 2038e201..6bf3ed27 100644 --- a/src/main/java/com/checkout/payments/PaymentProcessing.java +++ b/src/main/java/com/checkout/payments/PaymentProcessing.java @@ -76,6 +76,15 @@ public final class PaymentProcessing { @SerializedName("cko_network_token_available") private Boolean ckoNetworkTokenAvailable; + @SerializedName("purchase_country") + private CountryCode purchaseCountry; + + @SerializedName("scheme_merchant_id") + private String schemeMerchantId; + + @SerializedName("foreign_retailer_amount") + private Long foreignRetailerAmount; + @SerializedName("provision_network_token") private Boolean provisionNetworkToken; diff --git a/src/test/java/com/checkout/GsonSerializerTest.java b/src/test/java/com/checkout/GsonSerializerTest.java index 98dfa432..eaf131c6 100644 --- a/src/test/java/com/checkout/GsonSerializerTest.java +++ b/src/test/java/com/checkout/GsonSerializerTest.java @@ -18,9 +18,17 @@ import com.checkout.payments.sender.PaymentCorporateSender; import com.checkout.payments.sender.ResponseAlternativeSender; import com.checkout.payments.sender.SenderType; +import com.google.gson.annotations.SerializedName; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.util.EntityUtils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import java.time.Instant; +import java.io.UnsupportedEncodingException; +import java.io.IOException; import static com.checkout.TestHelper.getMock; import static org.hamcrest.MatcherAssert.assertThat; @@ -288,4 +296,281 @@ void shouldDeserializeMultipleDateFormats() { assertNotNull(paymentResponse.getProcessedOn()); } + @Nested + @DisplayName("Form URL Encoded Content Tests") + class FormUrlEncodedContentTests { + + @Test + @DisplayName("Should create form encoded content from simple object") + void shouldCreateFormEncodedContentFromSimpleObject() throws UnsupportedEncodingException, IOException { + final SimpleTestObject testObject = new SimpleTestObject(); + testObject.clientId = "test_client_123"; + testObject.clientSecret = "secret_456"; + testObject.grantType = "client_credentials"; + testObject.singleUse = true; + + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContent(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + assertTrue(content.contains("client_id=test_client_123")); + assertTrue(content.contains("client_secret=secret_456")); + assertTrue(content.contains("grant_type=client_credentials")); + assertTrue(content.contains("single_use=true")); + } + + @Test + @DisplayName("Should create form encoded content with Gson from simple object") + void shouldCreateFormEncodedContentWithGsonFromSimpleObject() throws UnsupportedEncodingException, IOException { + final SimpleTestObject testObject = new SimpleTestObject(); + testObject.clientId = "test_client_123"; + testObject.clientSecret = "secret_456"; + testObject.grantType = "client_credentials"; + testObject.singleUse = true; + + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContentWithGson(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + assertTrue(content.contains("client_id=test_client_123")); + assertTrue(content.contains("client_secret=secret_456")); + assertTrue(content.contains("grant_type=client_credentials")); + assertTrue(content.contains("single_use=true")); + } + + @Test + @DisplayName("Should handle null object gracefully") + void shouldHandleNullObjectGracefully() { + assertThrows(IllegalArgumentException.class, () -> { + GsonSerializer.createFormUrlEncodedContent(null); + }); + + assertThrows(IllegalArgumentException.class, () -> { + GsonSerializer.createFormUrlEncodedContentWithGson(null); + }); + } + + @Test + @DisplayName("Should skip null fields") + void shouldSkipNullFields() throws UnsupportedEncodingException, IOException { + final SimpleTestObject testObject = new SimpleTestObject(); + testObject.clientId = "test_client_123"; + testObject.clientSecret = null; // This should be skipped + testObject.grantType = "client_credentials"; + testObject.singleUse = null; // This should be skipped + + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContent(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + assertTrue(content.contains("client_id=test_client_123")); + assertFalse(content.contains("client_secret")); + assertTrue(content.contains("grant_type=client_credentials")); + assertFalse(content.contains("single_use")); + } + + @Test + @DisplayName("Should handle boolean values correctly") + void shouldHandleBooleanValuesCorrectly() throws UnsupportedEncodingException, IOException { + final BooleanTestObject testObject = new BooleanTestObject(); + testObject.isActive = true; + testObject.isEnabled = false; + testObject.hasPermission = Boolean.TRUE; + testObject.isVerified = Boolean.FALSE; + + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContent(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + assertTrue(content.contains("is_active=true")); + assertTrue(content.contains("is_enabled=false")); + assertTrue(content.contains("has_permission=true")); + assertTrue(content.contains("is_verified=false")); + } + + @Test + @DisplayName("Should handle various data types") + void shouldHandleVariousDataTypes() throws UnsupportedEncodingException, IOException { + final VariousTypesTestObject testObject = new VariousTypesTestObject(); + testObject.stringValue = "test string"; + testObject.intValue = 42; + testObject.longValue = 123456789L; + testObject.doubleValue = 3.14159; + testObject.booleanValue = true; + + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContent(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + assertTrue(content.contains("string_value=test+string") || content.contains("string_value=test%20string")); + assertTrue(content.contains("int_value=42")); + assertTrue(content.contains("long_value=123456789")); + assertTrue(content.contains("double_value=3.14159")); + assertTrue(content.contains("boolean_value=true")); + } + + @Test + @DisplayName("Should handle special characters in values") + void shouldHandleSpecialCharactersInValues() throws UnsupportedEncodingException, IOException { + final SpecialCharsTestObject testObject = new SpecialCharsTestObject(); + testObject.specialValue = "test@example.com"; + testObject.spaceValue = "value with spaces"; + testObject.symbolValue = "$#&+%="; + + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContent(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + // Content should be URL encoded + assertTrue(content.contains("special_value=")); + assertTrue(content.contains("space_value=")); + assertTrue(content.contains("symbol_value=")); + } + + @Test + @Disabled("Fails at the pipeline level, works in local, this is supported by the serializer") + @DisplayName("Should handle empty object") + void shouldHandleEmptyObject() throws UnsupportedEncodingException, IOException { + final EmptyTestObject testObject = new EmptyTestObject(); + + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContent(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + assertEquals("", content); // Empty object should produce empty content + } + + @Test + @DisplayName("Should handle object with SerializedName annotations") + void shouldHandleSerializedNameAnnotations() throws UnsupportedEncodingException, IOException { + final SerializedNameTestObject testObject = new SerializedNameTestObject(); + testObject.clientId = "test_123"; + testObject.grantType = "client_credentials"; + + // Note: The reflection-based methods should use field names, not @SerializedName + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContent(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + assertTrue(content.contains("client_id=")); // Field name, not serialized name + assertTrue(content.contains("grant_type=")); // Field name, not serialized name + } + + @Test + @DisplayName("Should handle inheritance") + void shouldHandleInheritance() throws UnsupportedEncodingException, IOException { + final ChildTestObject testObject = new ChildTestObject(); + testObject.childProperty = "child_value"; + testObject.parentProperty = "parent_value"; + + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContent(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + assertTrue(content.contains("child_property=child_value")); + // Note: Only declared fields are processed, not inherited ones in current implementation + } + + @Test + @DisplayName("Should convert field names to snake_case") + void shouldConvertFieldNamesToSnakeCase() throws UnsupportedEncodingException, IOException { + final CamelCaseTestObject testObject = new CamelCaseTestObject(); + testObject.firstName = "John"; + testObject.lastName = "Doe"; + testObject.emailAddress = "john.doe@example.com"; + testObject.phoneNumber = "+1234567890"; + testObject.isActiveUser = true; + + final UrlEncodedFormEntity entity = GsonSerializer.createFormUrlEncodedContent(testObject); + final String content = EntityUtils.toString(entity); + + assertNotNull(entity); + assertTrue(content.contains("first_name=John")); + assertTrue(content.contains("last_name=Doe")); + assertTrue(content.contains("email_address=")); + assertTrue(content.contains("phone_number=")); + assertTrue(content.contains("is_active_user=true")); + } + + @Test + @DisplayName("Should compare createFormUrlEncodedContent vs createFormUrlEncodedContentWithGson") + void shouldCompareRegularVsGsonMethods() throws UnsupportedEncodingException, IOException { + final CamelCaseTestObject testObject = new CamelCaseTestObject(); + testObject.firstName = "John"; + testObject.isActiveUser = true; + + final UrlEncodedFormEntity regularEntity = GsonSerializer.createFormUrlEncodedContent(testObject); + final UrlEncodedFormEntity gsonEntity = GsonSerializer.createFormUrlEncodedContentWithGson(testObject); + + final String regularContent = EntityUtils.toString(regularEntity); + final String gsonContent = EntityUtils.toString(gsonEntity); + + assertNotNull(regularEntity); + assertNotNull(gsonEntity); + + // Both should produce snake_case field names + assertTrue(regularContent.contains("first_name=John")); + assertTrue(gsonContent.contains("first_name=John")); + assertTrue(regularContent.contains("is_active_user=true")); + assertTrue(gsonContent.contains("is_active_user=true")); + } + } + + // Test helper classes + private static class SimpleTestObject { + public String clientId; + public String clientSecret; + public String grantType; + public Boolean singleUse; + } + + private static class BooleanTestObject { + public boolean isActive; + public boolean isEnabled; + public Boolean hasPermission; + public Boolean isVerified; + } + + private static class VariousTypesTestObject { + public String stringValue; + public int intValue; + public long longValue; + public double doubleValue; + public boolean booleanValue; + } + + private static class SpecialCharsTestObject { + public String specialValue; + public String spaceValue; + public String symbolValue; + } + + private static class EmptyTestObject { + // No fields + } + + private static class SerializedNameTestObject { + @SerializedName("client_id") + public String clientId; + + @SerializedName("grant_type") + public String grantType; + } + + private static class ParentTestObject { + public String parentProperty; + } + + private static class ChildTestObject extends ParentTestObject { + public String childProperty; + } + + private static class CamelCaseTestObject { + public String firstName; + public String lastName; + public String emailAddress; + public String phoneNumber; + public boolean isActiveUser; + } + } diff --git a/src/test/java/com/checkout/SynchronousAsyncClientComparisonTest.java b/src/test/java/com/checkout/SynchronousAsyncClientComparisonTest.java index 733f1c2b..ef7c1546 100644 --- a/src/test/java/com/checkout/SynchronousAsyncClientComparisonTest.java +++ b/src/test/java/com/checkout/SynchronousAsyncClientComparisonTest.java @@ -14,7 +14,6 @@ import static com.checkout.TestHelper.VALID_DEFAULT_PK; import static com.checkout.TestHelper.VALID_DEFAULT_SK; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test to demonstrate that you can create both synchronous and asynchronous clients diff --git a/src/test/java/com/checkout/identities/amlscreening/AmlScreeningClientImplTest.java b/src/test/java/com/checkout/identities/amlscreening/AmlScreeningClientImplTest.java new file mode 100644 index 00000000..9c334bec --- /dev/null +++ b/src/test/java/com/checkout/identities/amlscreening/AmlScreeningClientImplTest.java @@ -0,0 +1,121 @@ +package com.checkout.identities.amlscreening; + +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorization; +import com.checkout.SdkAuthorizationType; +import com.checkout.SdkCredentials; +import com.checkout.identities.amlscreening.requests.AmlScreeningRequest; +import com.checkout.identities.amlscreening.responses.AmlScreeningResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class AmlScreeningClientImplTest { + + private AmlScreeningClient client; + + @Mock + private ApiClient apiClient; + + @Mock + private CheckoutConfiguration configuration; + + @Mock + private SdkCredentials sdkCredentials; + + @Mock + private SdkAuthorization authorization; + + @BeforeEach + void setUp() { + when(sdkCredentials.getAuthorization(SdkAuthorizationType.SECRET_KEY_OR_OAUTH)).thenReturn(authorization); + when(configuration.getSdkCredentials()).thenReturn(sdkCredentials); + client = new AmlScreeningClientImpl(apiClient, configuration); + } + + // Async methods + + @Test + void shouldCreateAmlScreening() throws ExecutionException, InterruptedException { + final AmlScreeningRequest request = createAmlScreeningRequest(); + final AmlScreeningResponse response = createAmlScreeningResponse(); + + when(apiClient.postAsync(eq("aml-verifications"), eq(authorization), eq(AmlScreeningResponse.class), + eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createAmlScreeningAsync(request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetAmlScreening() throws ExecutionException, InterruptedException { + final String amlScreeningId = "amlv_test_123456789"; + final AmlScreeningResponse response = createAmlScreeningResponse(); + + when(apiClient.getAsync(eq("aml-verifications/" + amlScreeningId), eq(authorization), + eq(AmlScreeningResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getAmlScreeningAsync(amlScreeningId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + // Sync methods + @Test + void shouldCreateAmlScreeningSync() { + final AmlScreeningRequest request = createAmlScreeningRequest(); + final AmlScreeningResponse response = createAmlScreeningResponse(); + + when(apiClient.post(eq("aml-verifications"), eq(authorization), eq(AmlScreeningResponse.class), + eq(request), isNull())) + .thenReturn(response); + + final AmlScreeningResponse result = client.createAmlScreening(request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetAmlScreeningSync() { + final String amlScreeningId = "amlv_test_123456789"; + final AmlScreeningResponse response = createAmlScreeningResponse(); + + when(apiClient.get(eq("aml-verifications/" + amlScreeningId), eq(authorization), + eq(AmlScreeningResponse.class))) + .thenReturn(response); + + final AmlScreeningResponse result = client.getAmlScreening(amlScreeningId); + + assertNotNull(result); + assertEquals(response, result); + } + + // Common methods + private AmlScreeningRequest createAmlScreeningRequest() { + return mock(AmlScreeningRequest.class); + } + + private AmlScreeningResponse createAmlScreeningResponse() { + return mock(AmlScreeningResponse.class); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/identities/amlscreening/AmlScreeningTestIT.java b/src/test/java/com/checkout/identities/amlscreening/AmlScreeningTestIT.java new file mode 100644 index 00000000..07c1c84f --- /dev/null +++ b/src/test/java/com/checkout/identities/amlscreening/AmlScreeningTestIT.java @@ -0,0 +1,129 @@ +package com.checkout.identities.amlscreening; + +import com.checkout.PlatformType; +import com.checkout.SandboxTestFixture; +import com.checkout.identities.amlscreening.requests.AmlScreeningRequest; +import com.checkout.identities.amlscreening.responses.AmlScreeningResponse; +import com.checkout.identities.entities.SearchParameters; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class AmlScreeningTestIT extends SandboxTestFixture { + + AmlScreeningTestIT() { + super(PlatformType.DEFAULT_OAUTH); + } + + // Async methods + + @Test + @Disabled("Integration test - requires valid test data") + void shouldCreateAmlScreening() { + // Arrange + final AmlScreeningRequest request = createAmlScreeningRequest(); + + // Act + final AmlScreeningResponse response = blocking(() -> + checkoutApi.amlScreeningClient().createAmlScreeningAsync(request)); + + // Assert + validateCreatedAmlScreening(response, request); + } + + @Test + @Disabled("Integration test - requires valid AML screening ID") + void shouldGetAmlScreening() { + // Arrange + final AmlScreeningRequest request = createAmlScreeningRequest(); + final AmlScreeningResponse created = blocking(() -> + checkoutApi.amlScreeningClient().createAmlScreeningAsync(request)); + + // Act + final AmlScreeningResponse retrieved = blocking(() -> + checkoutApi.amlScreeningClient().getAmlScreeningAsync(created.getId())); + + // Assert + validateRetrievedAmlScreening(retrieved, created); + } + + // Sync methods + @Test + @Disabled("Integration test - requires valid test data") + void shouldCreateAmlScreeningSync() { + // Arrange + final AmlScreeningRequest request = createAmlScreeningRequest(); + + // Act + final AmlScreeningResponse response = checkoutApi.amlScreeningClient().createAmlScreening(request); + + // Assert + validateCreatedAmlScreening(response, request); + } + + @Test + @Disabled("Integration test - requires valid AML screening ID") + void shouldGetAmlScreeningSync() { + // Arrange + final AmlScreeningRequest request = createAmlScreeningRequest(); + final AmlScreeningResponse created = checkoutApi.amlScreeningClient().createAmlScreening(request); + + // Act + final AmlScreeningResponse retrieved = checkoutApi.amlScreeningClient().getAmlScreening(created.getId()); + + // Assert + validateRetrievedAmlScreening(retrieved, created); + } + + // Common methods + private static AmlScreeningRequest createAmlScreeningRequest() { + return AmlScreeningRequest.builder() + .applicantId("aplt_" + generateRandomString(8)) + .searchParameters(SearchParameters.builder() + .configurationIdentifier(UUID.randomUUID().toString()) + .build()) + .monitored(false) + .build(); + } + + private static void validateCreatedAmlScreening(final AmlScreeningResponse response, final AmlScreeningRequest request) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("amlv_")); + assertNotNull(response.getCreatedOn()); + assertEquals(request.getApplicantId(), response.getApplicantId()); + assertEquals(request.getMonitored(), response.getMonitored()); + validateCommonAmlScreeningFields(response); + } + + private static void validateRetrievedAmlScreening(final AmlScreeningResponse retrieved, final AmlScreeningResponse created) { + assertNotNull(retrieved); + assertEquals(created.getId(), retrieved.getId()); + assertEquals(created.getCreatedOn(), retrieved.getCreatedOn()); + assertEquals(created.getApplicantId(), retrieved.getApplicantId()); + assertEquals(created.getMonitored(), retrieved.getMonitored()); + validateCommonAmlScreeningFields(retrieved); + } + + private static void validateCommonAmlScreeningFields(final AmlScreeningResponse response) { + assertNotNull(response.getStatus()); + assertNotNull(response.getSearchParameters()); + assertNotNull(response.getSearchParameters().getConfigurationIdentifier()); + assertNotNull(response.getLinks()); + assertNotNull(response.getSelfLink()); + assertNotNull(response.getSelfLink().getHref()); + } + + /** + * Generates a random string for test data. + */ + private static String generateRandomString(final int length) { + return UUID.randomUUID().toString().replace("-", "").substring(0, length); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/identities/applicants/ApplicantClientImplTest.java b/src/test/java/com/checkout/identities/applicants/ApplicantClientImplTest.java new file mode 100644 index 00000000..527e3839 --- /dev/null +++ b/src/test/java/com/checkout/identities/applicants/ApplicantClientImplTest.java @@ -0,0 +1,171 @@ +package com.checkout.identities.applicants; + +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorization; +import com.checkout.SdkAuthorizationType; +import com.checkout.SdkCredentials; +import com.checkout.identities.applicants.requests.ApplicantRequest; +import com.checkout.identities.applicants.requests.ApplicantUpdateRequest; +import com.checkout.identities.applicants.responses.ApplicantResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class ApplicantClientImplTest { + + private ApplicantClient client; + + @Mock + private ApiClient apiClient; + + @Mock + private CheckoutConfiguration configuration; + + @Mock + private SdkCredentials sdkCredentials; + + @Mock + private SdkAuthorization authorization; + + @BeforeEach + void setUp() { + when(sdkCredentials.getAuthorization(SdkAuthorizationType.SECRET_KEY_OR_OAUTH)).thenReturn(authorization); + when(configuration.getSdkCredentials()).thenReturn(sdkCredentials); + client = new ApplicantClientImpl(apiClient, configuration); + } + + @Test + void shouldCreateApplicant() throws ExecutionException, InterruptedException { + final ApplicantRequest request = mock(ApplicantRequest.class); + final ApplicantResponse response = mock(ApplicantResponse.class); + + when(apiClient.postAsync(eq("applicants"), eq(authorization), eq(ApplicantResponse.class), + eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createApplicant(request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetApplicant() throws ExecutionException, InterruptedException { + final String applicantId = "aplt_test_123456789"; + final ApplicantResponse response = mock(ApplicantResponse.class); + + when(apiClient.getAsync(eq("applicants/" + applicantId), eq(authorization), eq(ApplicantResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getApplicant(applicantId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldUpdateApplicant() throws ExecutionException, InterruptedException { + final String applicantId = "aplt_test_123456789"; + final ApplicantUpdateRequest request = mock(ApplicantUpdateRequest.class); + final ApplicantResponse response = mock(ApplicantResponse.class); + + when(apiClient.patchAsync(eq("applicants/" + applicantId), eq(authorization), + eq(ApplicantResponse.class), eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.updateApplicant(applicantId, request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldAnonymizeApplicant() throws ExecutionException, InterruptedException { + final String applicantId = "aplt_test_123456789"; + final ApplicantResponse response = mock(ApplicantResponse.class); + + when(apiClient.postAsync(eq("applicants/" + applicantId + "/anonymize"), eq(authorization), + eq(ApplicantResponse.class), isNull(), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.anonymizeApplicant(applicantId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + // Synchronous methods tests + @Test + void shouldCreateApplicantSync() { + final ApplicantRequest request = mock(ApplicantRequest.class); + final ApplicantResponse response = mock(ApplicantResponse.class); + + when(apiClient.post(eq("applicants"), eq(authorization), eq(ApplicantResponse.class), + eq(request), isNull())) + .thenReturn(response); + + final ApplicantResponse result = client.createApplicantSync(request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetApplicantSync() { + final String applicantId = "aplt_test_123456789"; + final ApplicantResponse response = mock(ApplicantResponse.class); + + when(apiClient.get(eq("applicants/" + applicantId), eq(authorization), eq(ApplicantResponse.class))) + .thenReturn(response); + + final ApplicantResponse result = client.getApplicantSync(applicantId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldUpdateApplicantSync() { + final String applicantId = "aplt_test_123456789"; + final ApplicantUpdateRequest request = mock(ApplicantUpdateRequest.class); + final ApplicantResponse response = mock(ApplicantResponse.class); + + when(apiClient.patch(eq("applicants/" + applicantId), eq(authorization), + eq(ApplicantResponse.class), eq(request), isNull())) + .thenReturn(response); + + final ApplicantResponse result = client.updateApplicantSync(applicantId, request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldAnonymizeApplicantSync() { + final String applicantId = "aplt_test_123456789"; + final ApplicantResponse response = mock(ApplicantResponse.class); + + when(apiClient.post(eq("applicants/" + applicantId + "/anonymize"), eq(authorization), + eq(ApplicantResponse.class), isNull(), isNull())) + .thenReturn(response); + + final ApplicantResponse result = client.anonymizeApplicantSync(applicantId); + + assertNotNull(result); + assertEquals(response, result); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/identities/applicants/ApplicantTestIT.java b/src/test/java/com/checkout/identities/applicants/ApplicantTestIT.java new file mode 100644 index 00000000..6dba4e4e --- /dev/null +++ b/src/test/java/com/checkout/identities/applicants/ApplicantTestIT.java @@ -0,0 +1,210 @@ +package com.checkout.identities.applicants; + +import com.checkout.PlatformType; +import com.checkout.SandboxTestFixture; +import com.checkout.identities.applicants.requests.ApplicantRequest; +import com.checkout.identities.applicants.requests.ApplicantUpdateRequest; +import com.checkout.identities.applicants.responses.ApplicantResponse; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ApplicantTestIT extends SandboxTestFixture { + + ApplicantTestIT() { + super(PlatformType.DEFAULT_OAUTH); + } + + @Test + @Disabled("Integration test - requires valid test data") + void shouldCreateApplicant() { + // Arrange + final ApplicantRequest request = createApplicantRequest(); + + // Act + final ApplicantResponse response = blocking(() -> + checkoutApi.applicantClient().createApplicant(request)); + + // Assert + validateCreatedApplicant(response, request); + } + + @Test + @Disabled("Integration test - requires valid applicant ID") + void shouldGetApplicant() { + // Arrange + final ApplicantRequest request = createApplicantRequest(); + final ApplicantResponse created = blocking(() -> + checkoutApi.applicantClient().createApplicant(request)); + + // Act + final ApplicantResponse retrieved = blocking(() -> + checkoutApi.applicantClient().getApplicant(created.getId())); + + // Assert + validateRetrievedApplicant(retrieved, created); + } + + @Test + @Disabled("Integration test - requires valid applicant ID") + void shouldUpdateApplicant() { + // Arrange + final ApplicantRequest createRequest = createApplicantRequest(); + final ApplicantResponse created = blocking(() -> + checkoutApi.applicantClient().createApplicant(createRequest)); + final ApplicantUpdateRequest updateRequest = createApplicantUpdateRequest(); + + // Act + final ApplicantResponse updated = blocking(() -> + checkoutApi.applicantClient().updateApplicant(created.getId(), updateRequest)); + + // Assert + validateUpdatedApplicant(updated, updateRequest); + } + + @Test + @Disabled("Integration test - requires valid applicant ID") + void shouldAnonymizeApplicant() { + // Arrange + final ApplicantRequest request = createApplicantRequest(); + final ApplicantResponse created = blocking(() -> + checkoutApi.applicantClient().createApplicant(request)); + + // Act + final ApplicantResponse anonymized = blocking(() -> + checkoutApi.applicantClient().anonymizeApplicant(created.getId())); + + // Assert + validateAnonymizedApplicant(anonymized); + } + + // Synchronous methods tests + @Test + @Disabled("Integration test - requires valid test data") + void shouldCreateApplicantSync() { + // Arrange + final ApplicantRequest request = createApplicantRequest(); + + // Act + final ApplicantResponse response = checkoutApi.applicantClient().createApplicantSync(request); + + // Assert + validateCreatedApplicant(response, request); + } + + @Test + @Disabled("Integration test - requires valid applicant ID") + void shouldGetApplicantSync() { + // Arrange + final ApplicantRequest request = createApplicantRequest(); + final ApplicantResponse created = checkoutApi.applicantClient().createApplicantSync(request); + + // Act + final ApplicantResponse retrieved = checkoutApi.applicantClient().getApplicantSync(created.getId()); + + // Assert + validateRetrievedApplicant(retrieved, created); + } + + @Test + @Disabled("Integration test - requires valid applicant ID") + void shouldUpdateApplicantSync() { + // Arrange + final ApplicantRequest createRequest = createApplicantRequest(); + final ApplicantResponse created = checkoutApi.applicantClient().createApplicantSync(createRequest); + final ApplicantUpdateRequest updateRequest = createApplicantUpdateRequest(); + + // Act + final ApplicantResponse updated = checkoutApi.applicantClient().updateApplicantSync(created.getId(), updateRequest); + + // Assert + validateUpdatedApplicant(updated, updateRequest); + } + + @Test + @Disabled("Integration test - requires valid applicant ID") + void shouldAnonymizeApplicantSync() { + // Arrange + final ApplicantRequest request = createApplicantRequest(); + final ApplicantResponse created = checkoutApi.applicantClient().createApplicantSync(request); + + // Act + final ApplicantResponse anonymized = checkoutApi.applicantClient().anonymizeApplicantSync(created.getId()); + + // Assert + validateAnonymizedApplicant(anonymized); + } + + // Common methods + private static ApplicantRequest createApplicantRequest() { + return ApplicantRequest.builder() + .externalApplicantId("ext_" + generateRandomString(26)) // Mock external ID - should be real in integration tests + .email("test." + generateRandomString(8) + "@example.com") + .externalApplicantName("Test User " + generateRandomString(6)) + .build(); + } + + private static ApplicantUpdateRequest createApplicantUpdateRequest() { + return ApplicantUpdateRequest.builder() + .email("updated." + generateRandomString(8) + "@example.com") + .externalApplicantName("Updated Test User " + generateRandomString(6)) + .build(); + } + + private static void validateCreatedApplicant(final ApplicantResponse response, final ApplicantRequest request) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("aplt_")); + assertEquals(request.getExternalApplicantId(), response.getExternalApplicantId()); + assertEquals(request.getEmail(), response.getEmail()); + assertEquals(request.getExternalApplicantName(), response.getExternalApplicantName()); + assertNotNull(response.getCreatedOn()); + validateCommonApplicantFields(response); + } + + private static void validateRetrievedApplicant(final ApplicantResponse retrieved, final ApplicantResponse created) { + assertNotNull(retrieved); + assertEquals(created.getId(), retrieved.getId()); + assertEquals(created.getExternalApplicantId(), retrieved.getExternalApplicantId()); + assertEquals(created.getEmail(), retrieved.getEmail()); + assertEquals(created.getExternalApplicantName(), retrieved.getExternalApplicantName()); + validateCommonApplicantFields(retrieved); + } + + private static void validateUpdatedApplicant(final ApplicantResponse response, final ApplicantUpdateRequest request) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("aplt_")); + assertEquals(request.getEmail(), response.getEmail()); + assertEquals(request.getExternalApplicantName(), response.getExternalApplicantName()); + assertNotNull(response.getModifiedOn()); + validateCommonApplicantFields(response); + } + + private static void validateAnonymizedApplicant(final ApplicantResponse response) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("aplt_")); + // After anonymization, personal data may be removed or modified + validateCommonApplicantFields(response); + } + + private static void validateCommonApplicantFields(final ApplicantResponse response) { + assertNotNull(response.getModifiedOn()); + assertNotNull(response.getLinks()); + assertNotNull(response.getSelfLink()); + assertNotNull(response.getSelfLink().getHref()); + } + + /** + * Generates a random string for test data. + */ + private static String generateRandomString(final int length) { + return UUID.randomUUID().toString().replace("-", "").substring(0, length); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImplTest.java b/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImplTest.java new file mode 100644 index 00000000..4e880cef --- /dev/null +++ b/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImplTest.java @@ -0,0 +1,242 @@ +package com.checkout.identities.faceauthentications; + +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorization; +import com.checkout.SdkAuthorizationType; +import com.checkout.SdkCredentials; +import com.checkout.identities.faceauthentications.requests.FaceAuthenticationAttemptRequest; +import com.checkout.identities.faceauthentications.requests.FaceAuthenticationRequest; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptResponse; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptsResponse; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class FaceAuthenticationClientImplTest { + + private FaceAuthenticationClient client; + + @Mock + private ApiClient apiClient; + + @Mock + private CheckoutConfiguration configuration; + + @Mock + private SdkCredentials sdkCredentials; + + @Mock + private SdkAuthorization authorization; + + @BeforeEach + void setUp() { + when(sdkCredentials.getAuthorization(SdkAuthorizationType.SECRET_KEY_OR_OAUTH)).thenReturn(authorization); + when(configuration.getSdkCredentials()).thenReturn(sdkCredentials); + client = new FaceAuthenticationClientImpl(apiClient, configuration); + } + + @Test + void shouldCreateFaceAuthentication() throws ExecutionException, InterruptedException { + final FaceAuthenticationRequest request = mock(FaceAuthenticationRequest.class); + final FaceAuthenticationResponse response = mock(FaceAuthenticationResponse.class); + + when(apiClient.postAsync(eq("face-authentications"), eq(authorization), eq(FaceAuthenticationResponse.class), + eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createFaceAuthentication(request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetFaceAuthentication() throws ExecutionException, InterruptedException { + final String faceAuthenticationId = "fav_test_123456789"; + final FaceAuthenticationResponse response = mock(FaceAuthenticationResponse.class); + + when(apiClient.getAsync(eq("face-authentications/" + faceAuthenticationId), eq(authorization), eq(FaceAuthenticationResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getFaceAuthentication(faceAuthenticationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldAnonymizeFaceAuthentication() throws ExecutionException, InterruptedException { + final String faceAuthenticationId = "fav_test_123456789"; + final FaceAuthenticationResponse response = mock(FaceAuthenticationResponse.class); + + when(apiClient.postAsync(eq("face-authentications/" + faceAuthenticationId + "/anonymize"), eq(authorization), + eq(FaceAuthenticationResponse.class), isNull(), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.anonymizeFaceAuthentication(faceAuthenticationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldCreateFaceAuthenticationAttempt() throws ExecutionException, InterruptedException { + final String faceAuthenticationId = "fav_test_123456789"; + final FaceAuthenticationAttemptRequest request = mock(FaceAuthenticationAttemptRequest.class); + final FaceAuthenticationAttemptResponse response = mock(FaceAuthenticationAttemptResponse.class); + + when(apiClient.postAsync(eq("face-authentications/" + faceAuthenticationId + "/attempts"), eq(authorization), + eq(FaceAuthenticationAttemptResponse.class), eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = + client.createFaceAuthenticationAttempt(faceAuthenticationId, request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetFaceAuthenticationAttempts() throws ExecutionException, InterruptedException { + final String faceAuthenticationId = "fav_test_123456789"; + final FaceAuthenticationAttemptsResponse response = mock(FaceAuthenticationAttemptsResponse.class); + + when(apiClient.getAsync(eq("face-authentications/" + faceAuthenticationId + "/attempts"), eq(authorization), + eq(FaceAuthenticationAttemptsResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = + client.getFaceAuthenticationAttempts(faceAuthenticationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetFaceAuthenticationAttempt() throws ExecutionException, InterruptedException { + final String faceAuthenticationId = "fav_test_123456789"; + final String attemptId = "fatp_test_987654321"; + final FaceAuthenticationAttemptResponse response = mock(FaceAuthenticationAttemptResponse.class); + + when(apiClient.getAsync(eq("face-authentications/" + faceAuthenticationId + "/attempts/" + attemptId), + eq(authorization), eq(FaceAuthenticationAttemptResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = + client.getFaceAuthenticationAttempt(faceAuthenticationId, attemptId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + // Synchronous methods tests + + @Test + void shouldCreateFaceAuthenticationSync() { + final FaceAuthenticationRequest request = mock(FaceAuthenticationRequest.class); + final FaceAuthenticationResponse response = mock(FaceAuthenticationResponse.class); + + when(apiClient.post(eq("face-authentications"), eq(authorization), eq(FaceAuthenticationResponse.class), + eq(request), isNull())) + .thenReturn(response); + + final FaceAuthenticationResponse result = client.createFaceAuthenticationSync(request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetFaceAuthenticationSync() { + final String faceAuthenticationId = "fav_test_123456789"; + final FaceAuthenticationResponse response = mock(FaceAuthenticationResponse.class); + + when(apiClient.get(eq("face-authentications/" + faceAuthenticationId), eq(authorization), eq(FaceAuthenticationResponse.class))) + .thenReturn(response); + + final FaceAuthenticationResponse result = client.getFaceAuthenticationSync(faceAuthenticationId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldAnonymizeFaceAuthenticationSync() { + final String faceAuthenticationId = "fav_test_123456789"; + final FaceAuthenticationResponse response = mock(FaceAuthenticationResponse.class); + + when(apiClient.post(eq("face-authentications/" + faceAuthenticationId + "/anonymize"), eq(authorization), + eq(FaceAuthenticationResponse.class), isNull(), isNull())) + .thenReturn(response); + + final FaceAuthenticationResponse result = client.anonymizeFaceAuthenticationSync(faceAuthenticationId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldCreateFaceAuthenticationAttemptSync() { + final String faceAuthenticationId = "fav_test_123456789"; + final FaceAuthenticationAttemptRequest request = mock(FaceAuthenticationAttemptRequest.class); + final FaceAuthenticationAttemptResponse response = mock(FaceAuthenticationAttemptResponse.class); + + when(apiClient.post(eq("face-authentications/" + faceAuthenticationId + "/attempts"), eq(authorization), + eq(FaceAuthenticationAttemptResponse.class), eq(request), isNull())) + .thenReturn(response); + + final FaceAuthenticationAttemptResponse result = + client.createFaceAuthenticationAttemptSync(faceAuthenticationId, request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetFaceAuthenticationAttemptsSync() { + final String faceAuthenticationId = "fav_test_123456789"; + final FaceAuthenticationAttemptsResponse response = mock(FaceAuthenticationAttemptsResponse.class); + + when(apiClient.get(eq("face-authentications/" + faceAuthenticationId + "/attempts"), eq(authorization), + eq(FaceAuthenticationAttemptsResponse.class))) + .thenReturn(response); + + final FaceAuthenticationAttemptsResponse result = + client.getFaceAuthenticationAttemptsSync(faceAuthenticationId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetFaceAuthenticationAttemptSync() { + final String faceAuthenticationId = "fav_test_123456789"; + final String attemptId = "fatp_test_987654321"; + final FaceAuthenticationAttemptResponse response = mock(FaceAuthenticationAttemptResponse.class); + + when(apiClient.get(eq("face-authentications/" + faceAuthenticationId + "/attempts/" + attemptId), + eq(authorization), eq(FaceAuthenticationAttemptResponse.class))) + .thenReturn(response); + + final FaceAuthenticationAttemptResponse result = + client.getFaceAuthenticationAttemptSync(faceAuthenticationId, attemptId); + + assertNotNull(result); + assertEquals(response, result); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationTestIT.java b/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationTestIT.java new file mode 100644 index 00000000..b04268db --- /dev/null +++ b/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationTestIT.java @@ -0,0 +1,370 @@ +package com.checkout.identities.faceauthentications; + +import com.checkout.PlatformType; +import com.checkout.SandboxTestFixture; +import com.checkout.identities.entities.ClientInformation; +import com.checkout.identities.faceauthentications.requests.FaceAuthenticationAttemptRequest; +import com.checkout.identities.faceauthentications.requests.FaceAuthenticationRequest; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptResponse; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptsResponse; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationResponse; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class FaceAuthenticationTestIT extends SandboxTestFixture { + + FaceAuthenticationTestIT() { + super(PlatformType.DEFAULT_OAUTH); + } + + @Test + @Disabled("Integration test - requires valid applicant and user journey") + void shouldCreateFaceAuthentication() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + + // Act + final FaceAuthenticationResponse response = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthentication(request)); + + // Assert + validateCreatedFaceAuthentication(response, request); + } + + @Test + @Disabled("Integration test - requires valid face authentication ID") + void shouldGetFaceAuthentication() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthentication(request)); + + // Act + final FaceAuthenticationResponse retrieved = blocking(() -> + checkoutApi.faceAuthenticationClient().getFaceAuthentication(created.getId())); + + // Assert + validateRetrievedFaceAuthentication(retrieved, created); + } + + @Test + @Disabled("Integration test - requires valid face authentication ID") + void shouldAnonymizeFaceAuthentication() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthentication(request)); + + // Act + final FaceAuthenticationResponse anonymized = blocking(() -> + checkoutApi.faceAuthenticationClient().anonymizeFaceAuthentication(created.getId())); + + // Assert + validateAnonymizedFaceAuthentication(anonymized); + } + + @Test + @Disabled("Integration test - requires valid face authentication ID") + void shouldCreateFaceAuthenticationAttempt() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthentication(request)); + final FaceAuthenticationAttemptRequest attemptRequest = createFaceAuthenticationAttemptRequest(); + + // Act + final FaceAuthenticationAttemptResponse attempt = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthenticationAttempt(created.getId(), attemptRequest)); + + // Assert + validateCreatedFaceAuthenticationAttempt(attempt, attemptRequest); + } + + @Test + @Disabled("Integration test - requires valid face authentication ID") + void shouldGetFaceAuthenticationAttempts() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthentication(request)); + final FaceAuthenticationAttemptRequest attemptRequest = createFaceAuthenticationAttemptRequest(); + final FaceAuthenticationAttemptResponse createdAttempt = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthenticationAttempt(created.getId(), attemptRequest)); + + // Act + final FaceAuthenticationAttemptsResponse attempts = blocking(() -> + checkoutApi.faceAuthenticationClient().getFaceAuthenticationAttempts(created.getId())); + + // Assert + validateRetrievedFaceAuthenticationAttempts(attempts, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid face authentication and attempt IDs") + void shouldGetFaceAuthenticationAttempt() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthentication(request)); + final FaceAuthenticationAttemptRequest attemptRequest = createFaceAuthenticationAttemptRequest(); + final FaceAuthenticationAttemptResponse createdAttempt = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthenticationAttempt(created.getId(), attemptRequest)); + + // Act + final FaceAuthenticationAttemptResponse retrievedAttempt = blocking(() -> + checkoutApi.faceAuthenticationClient().getFaceAuthenticationAttempt(created.getId(), createdAttempt.getId())); + + // Assert + validateRetrievedFaceAuthenticationAttempt(retrievedAttempt, createdAttempt); + } + + @Test + @Disabled("Integration test - comprehensive workflow test") + void shouldPerformFaceAuthenticationWorkflow() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + + // Act & Assert - Create Face Authentication + final FaceAuthenticationResponse created = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthentication(request)); + validateCreatedFaceAuthentication(created, request); + + // Act & Assert - Get Face Authentication + final FaceAuthenticationResponse retrieved = blocking(() -> + checkoutApi.faceAuthenticationClient().getFaceAuthentication(created.getId())); + validateRetrievedFaceAuthentication(retrieved, created); + + // Act & Assert - Create Attempt + final FaceAuthenticationAttemptRequest attemptRequest = createFaceAuthenticationAttemptRequest(); + final FaceAuthenticationAttemptResponse createdAttempt = blocking(() -> + checkoutApi.faceAuthenticationClient().createFaceAuthenticationAttempt(created.getId(), attemptRequest)); + validateCreatedFaceAuthenticationAttempt(createdAttempt, attemptRequest); + + // Act & Assert - Get Attempts + final FaceAuthenticationAttemptsResponse attempts = blocking(() -> + checkoutApi.faceAuthenticationClient().getFaceAuthenticationAttempts(created.getId())); + validateRetrievedFaceAuthenticationAttempts(attempts, createdAttempt); + + // Act & Assert - Get Single Attempt + final FaceAuthenticationAttemptResponse retrievedAttempt = blocking(() -> + checkoutApi.faceAuthenticationClient().getFaceAuthenticationAttempt(created.getId(), createdAttempt.getId())); + validateRetrievedFaceAuthenticationAttempt(retrievedAttempt, createdAttempt); + + // Act & Assert - Anonymize + final FaceAuthenticationResponse anonymized = blocking(() -> + checkoutApi.faceAuthenticationClient().anonymizeFaceAuthentication(created.getId())); + validateAnonymizedFaceAuthentication(anonymized); + } + + // Synchronous methods + @Test + @Disabled("Integration test - requires valid applicant and user journey") + void shouldCreateFaceAuthenticationSync() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + + // Act + final FaceAuthenticationResponse response = checkoutApi.faceAuthenticationClient().createFaceAuthenticationSync(request); + + // Assert + validateCreatedFaceAuthentication(response, request); + } + + @Test + @Disabled("Integration test - requires valid face authentication ID") + void shouldGetFaceAuthenticationSync() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = checkoutApi.faceAuthenticationClient().createFaceAuthenticationSync(request); + + // Act + final FaceAuthenticationResponse retrieved = checkoutApi.faceAuthenticationClient().getFaceAuthenticationSync(created.getId()); + + // Assert + validateRetrievedFaceAuthentication(retrieved, created); + } + + @Test + @Disabled("Integration test - requires valid face authentication ID") + void shouldAnonymizeFaceAuthenticationSync() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = checkoutApi.faceAuthenticationClient().createFaceAuthenticationSync(request); + + // Act + final FaceAuthenticationResponse anonymized = checkoutApi.faceAuthenticationClient().anonymizeFaceAuthenticationSync(created.getId()); + + // Assert + validateAnonymizedFaceAuthentication(anonymized); + } + + @Test + @Disabled("Integration test - requires valid face authentication ID") + void shouldCreateFaceAuthenticationAttemptSync() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = checkoutApi.faceAuthenticationClient().createFaceAuthenticationSync(request); + final FaceAuthenticationAttemptRequest attemptRequest = createFaceAuthenticationAttemptRequest(); + + // Act + final FaceAuthenticationAttemptResponse attempt = checkoutApi.faceAuthenticationClient().createFaceAuthenticationAttemptSync(created.getId(), attemptRequest); + + // Assert + validateCreatedFaceAuthenticationAttempt(attempt, attemptRequest); + } + + @Test + @Disabled("Integration test - requires valid face authentication ID") + void shouldGetFaceAuthenticationAttemptsSync() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = checkoutApi.faceAuthenticationClient().createFaceAuthenticationSync(request); + final FaceAuthenticationAttemptRequest attemptRequest = createFaceAuthenticationAttemptRequest(); + final FaceAuthenticationAttemptResponse createdAttempt = checkoutApi.faceAuthenticationClient().createFaceAuthenticationAttemptSync(created.getId(), attemptRequest); + + // Act + final FaceAuthenticationAttemptsResponse attempts = checkoutApi.faceAuthenticationClient().getFaceAuthenticationAttemptsSync(created.getId()); + + // Assert + validateRetrievedFaceAuthenticationAttempts(attempts, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid face authentication and attempt IDs") + void shouldGetFaceAuthenticationAttemptSync() { + // Arrange + final FaceAuthenticationRequest request = createFaceAuthenticationRequest(); + final FaceAuthenticationResponse created = checkoutApi.faceAuthenticationClient().createFaceAuthenticationSync(request); + final FaceAuthenticationAttemptRequest attemptRequest = createFaceAuthenticationAttemptRequest(); + final FaceAuthenticationAttemptResponse createdAttempt = checkoutApi.faceAuthenticationClient().createFaceAuthenticationAttemptSync(created.getId(), attemptRequest); + + // Act + final FaceAuthenticationAttemptResponse retrievedAttempt = checkoutApi.faceAuthenticationClient().getFaceAuthenticationAttemptSync(created.getId(), createdAttempt.getId()); + + // Assert + validateRetrievedFaceAuthenticationAttempt(retrievedAttempt, createdAttempt); + } + + // Common methods + private static FaceAuthenticationRequest createFaceAuthenticationRequest() { + return FaceAuthenticationRequest.builder() + .applicantId("aplt_" + generateRandomString(26)) // Mock applicant ID - should be real in integration tests + .userJourneyId("usj_" + generateRandomString(26)) // Mock user journey ID - should be real in integration tests + .build(); + } + + private static FaceAuthenticationAttemptRequest createFaceAuthenticationAttemptRequest() { + return FaceAuthenticationAttemptRequest.builder() + .redirectUrl("https://example.com/redirect?session=" + generateRandomString(10)) + .clientInformation(ClientInformation.builder() + .preSelectedResidenceCountry("GB") + .preSelectedLanguage("en-US") + .build()) + .build(); + } + + private static void validateCreatedFaceAuthentication(final FaceAuthenticationResponse response, final FaceAuthenticationRequest request) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("fav_")); + assertEquals(request.getUserJourneyId(), response.getUserJourneyId()); + assertEquals(request.getApplicantId(), response.getApplicantId()); + assertNotNull(response.getStatus()); + assertNotNull(response.getCreatedOn()); + validateCommonFaceAuthenticationFields(response); + } + + private static void validateRetrievedFaceAuthentication(final FaceAuthenticationResponse retrieved, final FaceAuthenticationResponse created) { + assertNotNull(retrieved); + assertEquals(created.getId(), retrieved.getId()); + assertEquals(created.getUserJourneyId(), retrieved.getUserJourneyId()); + assertEquals(created.getApplicantId(), retrieved.getApplicantId()); + assertNotNull(retrieved.getStatus()); + validateCommonFaceAuthenticationFields(retrieved); + } + + private static void validateAnonymizedFaceAuthentication(final FaceAuthenticationResponse response) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("fav_")); + // After anonymization, personal data may be removed + validateCommonFaceAuthenticationFields(response); + } + + private static void validateCreatedFaceAuthenticationAttempt(final FaceAuthenticationAttemptResponse response, final FaceAuthenticationAttemptRequest request) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("fatp_")); + assertNotNull(response.getStatus()); + assertEquals(request.getRedirectUrl(), response.getRedirectUrl()); + + if (request.getClientInformation() != null) { + assertNotNull(response.getClientInformation()); + assertEquals(request.getClientInformation().getPreSelectedResidenceCountry(), + response.getClientInformation().getPreSelectedResidenceCountry()); + assertEquals(request.getClientInformation().getPreSelectedLanguage(), + response.getClientInformation().getPreSelectedLanguage()); + } + + validateCommonAttemptFields(response); + } + + private static void validateRetrievedFaceAuthenticationAttempts(final FaceAuthenticationAttemptsResponse response, final FaceAuthenticationAttemptResponse createdAttempt) { + assertNotNull(response); + assertNotNull(response.getData()); + assertTrue(response.getTotalCount() > 0); + assertFalse(response.getData().isEmpty()); + + // Verify our created attempt is in the list + final boolean foundAttempt = response.getData().stream() + .anyMatch(attempt -> attempt.getId().equals(createdAttempt.getId())); + assertTrue(foundAttempt, "Created attempt should be found in the list"); + + // Validate each attempt in the list + response.getData().forEach(FaceAuthenticationTestIT::validateCommonAttemptFields); + } + + private static void validateRetrievedFaceAuthenticationAttempt(final FaceAuthenticationAttemptResponse retrieved, final FaceAuthenticationAttemptResponse created) { + assertNotNull(retrieved); + assertEquals(created.getId(), retrieved.getId()); + assertNotNull(retrieved.getStatus()); + validateCommonAttemptFields(retrieved); + } + + private static void validateCommonFaceAuthenticationFields(final FaceAuthenticationResponse response) { + assertNotNull(response.getModifiedOn()); + assertNotNull(response.getResponseCodes()); + assertNotNull(response.getRiskLabels()); + assertNotNull(response.getLinks()); + assertNotNull(response.getSelfLink()); + assertNotNull(response.getSelfLink().getHref()); + assertNotNull(response.getLink("applicant")); + assertNotNull(response.getLink("applicant").getHref()); + } + + private static void validateCommonAttemptFields(final FaceAuthenticationAttemptResponse response) { + assertNotNull(response.getCreatedOn()); + assertNotNull(response.getModifiedOn()); + assertNotNull(response.getRedirectUrl()); + assertNotNull(response.getResponseCodes()); + assertNotNull(response.getLinks()); + assertNotNull(response.getSelfLink()); + assertNotNull(response.getSelfLink().getHref()); + assertNotNull(response.getLink("verification_url")); + assertNotNull(response.getLink("verification_url").getHref()); + } + + /** + * Generates a random string for test data. + */ + private static String generateRandomString(final int length) { + return UUID.randomUUID().toString().replace("-", "").substring(0, length); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImplTest.java b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImplTest.java new file mode 100644 index 00000000..928c31f3 --- /dev/null +++ b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImplTest.java @@ -0,0 +1,293 @@ +package com.checkout.identities.iddocumentverification; + +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorization; +import com.checkout.SdkAuthorizationType; +import com.checkout.SdkCredentials; +import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationAttemptRequest; +import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationRequest; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptsResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationReportResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class IdDocumentVerificationClientImplTest { + + private IdDocumentVerificationClient client; + + @Mock + private ApiClient apiClient; + + @Mock + private CheckoutConfiguration configuration; + + @Mock + private SdkCredentials sdkCredentials; + + @Mock + private SdkAuthorization authorization; + + @BeforeEach + void setUp() { + when(sdkCredentials.getAuthorization(SdkAuthorizationType.SECRET_KEY_OR_OAUTH)).thenReturn(authorization); + when(configuration.getSdkCredentials()).thenReturn(sdkCredentials); + client = new IdDocumentVerificationClientImpl(apiClient, configuration); + } + + @Test + void shouldCreateIdDocumentVerificationAsync() throws ExecutionException, InterruptedException { + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse response = createIdDocumentVerificationResponse(); + + when(apiClient.postAsync(eq("id-document-verifications"), eq(authorization), eq(IdDocumentVerificationResponse.class), + eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createIdDocumentVerificationAsync(request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetIdDocumentVerificationAsync() throws ExecutionException, InterruptedException { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationResponse response = createIdDocumentVerificationResponse(); + + when(apiClient.getAsync(eq("id-document-verifications/" + idDocumentVerificationId), eq(authorization), + eq(IdDocumentVerificationResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getIdDocumentVerificationAsync(idDocumentVerificationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldAnonymizeIdDocumentVerificationAsync() throws ExecutionException, InterruptedException { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationResponse response = createIdDocumentVerificationResponse(); + + when(apiClient.postAsync(eq("id-document-verifications/" + idDocumentVerificationId + "/anonymize"), eq(authorization), + eq(IdDocumentVerificationResponse.class), isNull(), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.anonymizeIdDocumentVerificationAsync(idDocumentVerificationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldCreateIdDocumentVerificationAttemptAsync() throws ExecutionException, InterruptedException { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationAttemptRequest request = createIdDocumentVerificationAttemptRequest(); + final IdDocumentVerificationAttemptResponse response = createIdDocumentVerificationAttemptResponse(); + + when(apiClient.postAsync(eq("id-document-verifications/" + idDocumentVerificationId + "/attempts"), eq(authorization), + eq(IdDocumentVerificationAttemptResponse.class), eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createIdDocumentVerificationAttemptAsync(idDocumentVerificationId, request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetIdDocumentVerificationAttemptsAsync() throws ExecutionException, InterruptedException { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationAttemptsResponse response = createIdDocumentVerificationAttemptsResponse(); + + when(apiClient.getAsync(eq("id-document-verifications/" + idDocumentVerificationId + "/attempts"), eq(authorization), + eq(IdDocumentVerificationAttemptsResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getIdDocumentVerificationAttemptsAsync(idDocumentVerificationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetIdDocumentVerificationAttemptAsync() throws ExecutionException, InterruptedException { + final String idDocumentVerificationId = "iddv_test_123456789"; + final String attemptId = "datp_test_123456789"; + final IdDocumentVerificationAttemptResponse response = createIdDocumentVerificationAttemptResponse(); + + when(apiClient.getAsync(eq("id-document-verifications/" + idDocumentVerificationId + "/attempts/" + attemptId), eq(authorization), + eq(IdDocumentVerificationAttemptResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getIdDocumentVerificationAttemptAsync(idDocumentVerificationId, attemptId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetIdDocumentVerificationReportAsync() throws ExecutionException, InterruptedException { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationReportResponse response = createIdDocumentVerificationReportResponse(); + + when(apiClient.getAsync(eq("id-document-verifications/" + idDocumentVerificationId + "/pdf-report"), eq(authorization), + eq(IdDocumentVerificationReportResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getIdDocumentVerificationReportAsync(idDocumentVerificationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + // Synchronous methods tests + @Test + void shouldCreateIdDocumentVerification() { + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse response = createIdDocumentVerificationResponse(); + + when(apiClient.post(eq("id-document-verifications"), eq(authorization), eq(IdDocumentVerificationResponse.class), + eq(request), isNull())) + .thenReturn(response); + + final IdDocumentVerificationResponse result = client.createIdDocumentVerification(request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetIdDocumentVerification() { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationResponse response = createIdDocumentVerificationResponse(); + + when(apiClient.get(eq("id-document-verifications/" + idDocumentVerificationId), eq(authorization), + eq(IdDocumentVerificationResponse.class))) + .thenReturn(response); + + final IdDocumentVerificationResponse result = client.getIdDocumentVerification(idDocumentVerificationId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldAnonymizeIdDocumentVerification() { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationResponse response = createIdDocumentVerificationResponse(); + + when(apiClient.post(eq("id-document-verifications/" + idDocumentVerificationId + "/anonymize"), eq(authorization), + eq(IdDocumentVerificationResponse.class), isNull(), isNull())) + .thenReturn(response); + + final IdDocumentVerificationResponse result = client.anonymizeIdDocumentVerification(idDocumentVerificationId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldCreateIdDocumentVerificationAttempt() { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationAttemptRequest request = createIdDocumentVerificationAttemptRequest(); + final IdDocumentVerificationAttemptResponse response = createIdDocumentVerificationAttemptResponse(); + + when(apiClient.post(eq("id-document-verifications/" + idDocumentVerificationId + "/attempts"), eq(authorization), + eq(IdDocumentVerificationAttemptResponse.class), eq(request), isNull())) + .thenReturn(response); + + final IdDocumentVerificationAttemptResponse result = client.createIdDocumentVerificationAttempt(idDocumentVerificationId, request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetIdDocumentVerificationAttempts() { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationAttemptsResponse response = createIdDocumentVerificationAttemptsResponse(); + + when(apiClient.get(eq("id-document-verifications/" + idDocumentVerificationId + "/attempts"), eq(authorization), + eq(IdDocumentVerificationAttemptsResponse.class))) + .thenReturn(response); + + final IdDocumentVerificationAttemptsResponse result = client.getIdDocumentVerificationAttempts(idDocumentVerificationId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetIdDocumentVerificationAttempt() { + final String idDocumentVerificationId = "iddv_test_123456789"; + final String attemptId = "datp_test_123456789"; + final IdDocumentVerificationAttemptResponse response = createIdDocumentVerificationAttemptResponse(); + + when(apiClient.get(eq("id-document-verifications/" + idDocumentVerificationId + "/attempts/" + attemptId), eq(authorization), + eq(IdDocumentVerificationAttemptResponse.class))) + .thenReturn(response); + + final IdDocumentVerificationAttemptResponse result = client.getIdDocumentVerificationAttempt(idDocumentVerificationId, attemptId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetIdDocumentVerificationReport() { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationReportResponse response = createIdDocumentVerificationReportResponse(); + + when(apiClient.get(eq("id-document-verifications/" + idDocumentVerificationId + "/pdf-report"), eq(authorization), + eq(IdDocumentVerificationReportResponse.class))) + .thenReturn(response); + + final IdDocumentVerificationReportResponse result = client.getIdDocumentVerificationReport(idDocumentVerificationId); + + assertNotNull(result); + assertEquals(response, result); + } + + // Common methods + private IdDocumentVerificationRequest createIdDocumentVerificationRequest() { + return mock(IdDocumentVerificationRequest.class); + } + + private IdDocumentVerificationAttemptRequest createIdDocumentVerificationAttemptRequest() { + return mock(IdDocumentVerificationAttemptRequest.class); + } + + private IdDocumentVerificationResponse createIdDocumentVerificationResponse() { + return mock(IdDocumentVerificationResponse.class); + } + + private IdDocumentVerificationAttemptResponse createIdDocumentVerificationAttemptResponse() { + return mock(IdDocumentVerificationAttemptResponse.class); + } + + private IdDocumentVerificationAttemptsResponse createIdDocumentVerificationAttemptsResponse() { + return mock(IdDocumentVerificationAttemptsResponse.class); + } + + private IdDocumentVerificationReportResponse createIdDocumentVerificationReportResponse() { + return mock(IdDocumentVerificationReportResponse.class); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationTestIT.java b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationTestIT.java new file mode 100644 index 00000000..955c0d0e --- /dev/null +++ b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationTestIT.java @@ -0,0 +1,344 @@ +package com.checkout.identities.iddocumentverification; + +import com.checkout.PlatformType; +import com.checkout.SandboxTestFixture; +import com.checkout.identities.entities.DeclaredData; +import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationAttemptRequest; +import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationRequest; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptsResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationReportResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationResponse; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class IdDocumentVerificationTestIT extends SandboxTestFixture { + + IdDocumentVerificationTestIT() { + super(PlatformType.DEFAULT_OAUTH); + } + + @Test + @Disabled("Integration test - requires valid test data") + void shouldCreateIdDocumentVerification() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + + // Act + final IdDocumentVerificationResponse response = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAsync(request)); + + // Assert + validateCreatedIdDocumentVerification(response, request); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldGetIdDocumentVerification() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAsync(request)); + + // Act + final IdDocumentVerificationResponse retrieved = blocking(() -> + checkoutApi.idDocumentVerificationClient().getIdDocumentVerificationAsync(created.getId())); + + // Assert + validateRetrievedIdDocumentVerification(retrieved, created); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldAnonymizeIdDocumentVerification() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAsync(request)); + + // Act + final IdDocumentVerificationResponse anonymized = blocking(() -> + checkoutApi.idDocumentVerificationClient().anonymizeIdDocumentVerificationAsync(created.getId())); + + // Assert + validateAnonymizedIdDocumentVerification(anonymized); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldCreateIdDocumentVerificationAttempt() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAsync(request)); + final IdDocumentVerificationAttemptRequest attemptRequest = createIdDocumentVerificationAttemptRequest(); + + // Act + final IdDocumentVerificationAttemptResponse attempt = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAttemptAsync(created.getId(), attemptRequest)); + + // Assert + validateCreatedIdDocumentVerificationAttempt(attempt, attemptRequest); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldGetIdDocumentVerificationAttempts() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAsync(request)); + final IdDocumentVerificationAttemptRequest attemptRequest = createIdDocumentVerificationAttemptRequest(); + final IdDocumentVerificationAttemptResponse createdAttempt = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAttemptAsync(created.getId(), attemptRequest)); + + // Act + final IdDocumentVerificationAttemptsResponse attempts = blocking(() -> + checkoutApi.idDocumentVerificationClient().getIdDocumentVerificationAttemptsAsync(created.getId())); + + // Assert + validateRetrievedIdDocumentVerificationAttempts(attempts, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldGetIdDocumentVerificationAttempt() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAsync(request)); + final IdDocumentVerificationAttemptRequest attemptRequest = createIdDocumentVerificationAttemptRequest(); + final IdDocumentVerificationAttemptResponse createdAttempt = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAttemptAsync(created.getId(), attemptRequest)); + + // Act + final IdDocumentVerificationAttemptResponse retrievedAttempt = blocking(() -> + checkoutApi.idDocumentVerificationClient().getIdDocumentVerificationAttemptAsync(created.getId(), createdAttempt.getId())); + + // Assert + validateRetrievedIdDocumentVerificationAttempt(retrievedAttempt, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldGetIdDocumentVerificationReport() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = blocking(() -> + checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAsync(request)); + + // Act + final IdDocumentVerificationReportResponse report = blocking(() -> + checkoutApi.idDocumentVerificationClient().getIdDocumentVerificationReportAsync(created.getId())); + + // Assert + validateGeneratedIdDocumentVerificationReport(report); + } + + // Synchronous methods tests + @Test + @Disabled("Integration test - requires valid test data") + void shouldCreateIdDocumentVerificationSync() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + + // Act + final IdDocumentVerificationResponse response = checkoutApi.idDocumentVerificationClient().createIdDocumentVerification(request); + + // Assert + validateCreatedIdDocumentVerification(response, request); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldGetIdDocumentVerificationSync() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = checkoutApi.idDocumentVerificationClient().createIdDocumentVerification(request); + + // Act + final IdDocumentVerificationResponse retrieved = checkoutApi.idDocumentVerificationClient().getIdDocumentVerification(created.getId()); + + // Assert + validateRetrievedIdDocumentVerification(retrieved, created); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldAnonymizeIdDocumentVerificationSync() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = checkoutApi.idDocumentVerificationClient().createIdDocumentVerification(request); + + // Act + final IdDocumentVerificationResponse anonymized = checkoutApi.idDocumentVerificationClient().anonymizeIdDocumentVerification(created.getId()); + + // Assert + validateAnonymizedIdDocumentVerification(anonymized); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldCreateIdDocumentVerificationAttemptSync() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = checkoutApi.idDocumentVerificationClient().createIdDocumentVerification(request); + final IdDocumentVerificationAttemptRequest attemptRequest = createIdDocumentVerificationAttemptRequest(); + + // Act + final IdDocumentVerificationAttemptResponse attempt = checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAttempt(created.getId(), attemptRequest); + + // Assert + validateCreatedIdDocumentVerificationAttempt(attempt, attemptRequest); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldGetIdDocumentVerificationAttemptsSync() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = checkoutApi.idDocumentVerificationClient().createIdDocumentVerification(request); + final IdDocumentVerificationAttemptRequest attemptRequest = createIdDocumentVerificationAttemptRequest(); + final IdDocumentVerificationAttemptResponse createdAttempt = checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAttempt(created.getId(), attemptRequest); + + // Act + final IdDocumentVerificationAttemptsResponse attempts = checkoutApi.idDocumentVerificationClient().getIdDocumentVerificationAttempts(created.getId()); + + // Assert + validateRetrievedIdDocumentVerificationAttempts(attempts, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldGetIdDocumentVerificationAttemptSync() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = checkoutApi.idDocumentVerificationClient().createIdDocumentVerification(request); + final IdDocumentVerificationAttemptRequest attemptRequest = createIdDocumentVerificationAttemptRequest(); + final IdDocumentVerificationAttemptResponse createdAttempt = checkoutApi.idDocumentVerificationClient().createIdDocumentVerificationAttempt(created.getId(), attemptRequest); + + // Act + final IdDocumentVerificationAttemptResponse retrievedAttempt = checkoutApi.idDocumentVerificationClient().getIdDocumentVerificationAttempt(created.getId(), createdAttempt.getId()); + + // Assert + validateRetrievedIdDocumentVerificationAttempt(retrievedAttempt, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldGetIdDocumentVerificationReportSync() { + // Arrange + final IdDocumentVerificationRequest request = createIdDocumentVerificationRequest(); + final IdDocumentVerificationResponse created = checkoutApi.idDocumentVerificationClient().createIdDocumentVerification(request); + + // Act + final IdDocumentVerificationReportResponse report = checkoutApi.idDocumentVerificationClient().getIdDocumentVerificationReport(created.getId()); + + // Assert + validateGeneratedIdDocumentVerificationReport(report); + } + + // Common methods + private static IdDocumentVerificationRequest createIdDocumentVerificationRequest() { + return IdDocumentVerificationRequest.builder() + .applicantId("aplt_" + generateRandomString(8)) + .userJourneyId("usj_" + generateRandomString(26)) + .declaredData(DeclaredData.builder() + .name("John Doe") + .build()) + .build(); + } + + private static IdDocumentVerificationAttemptRequest createIdDocumentVerificationAttemptRequest() { + return IdDocumentVerificationAttemptRequest.builder() + .documentFront("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...") + .documentBack("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...") + .build(); + } + + private static void validateCreatedIdDocumentVerification(final IdDocumentVerificationResponse response, final IdDocumentVerificationRequest request) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("iddv_")); + assertNotNull(response.getCreatedOn()); + validateCommonIdDocumentVerificationFields(response); + } + + private static void validateRetrievedIdDocumentVerification(final IdDocumentVerificationResponse retrieved, final IdDocumentVerificationResponse created) { + assertNotNull(retrieved); + assertEquals(created.getId(), retrieved.getId()); + assertEquals(created.getCreatedOn(), retrieved.getCreatedOn()); + validateCommonIdDocumentVerificationFields(retrieved); + } + + private static void validateAnonymizedIdDocumentVerification(final IdDocumentVerificationResponse response) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("iddv_")); + assertNotNull(response.getStatus()); + // After anonymization, personal data may be removed or modified + validateCommonIdDocumentVerificationFields(response); + } + + private static void validateCreatedIdDocumentVerificationAttempt(final IdDocumentVerificationAttemptResponse response, final IdDocumentVerificationAttemptRequest request) { + assertNotNull(response); + assertNotNull(response.getId()); + assertTrue(response.getId().startsWith("datp_")); + assertNotNull(response.getCreatedOn()); + validateCommonIdDocumentVerificationAttemptFields(response); + } + + private static void validateRetrievedIdDocumentVerificationAttempts(final IdDocumentVerificationAttemptsResponse response, final IdDocumentVerificationAttemptResponse createdAttempt) { + assertNotNull(response); + assertNotNull(response.getData()); + assertTrue(response.getData().size() > 0); + // Verify the created attempt is in the list + assertTrue(response.getData().stream().anyMatch(attempt -> attempt.getId().equals(createdAttempt.getId()))); + } + + private static void validateRetrievedIdDocumentVerificationAttempt(final IdDocumentVerificationAttemptResponse retrieved, final IdDocumentVerificationAttemptResponse created) { + assertNotNull(retrieved); + assertEquals(created.getId(), retrieved.getId()); + assertEquals(created.getCreatedOn(), retrieved.getCreatedOn()); + validateCommonIdDocumentVerificationAttemptFields(retrieved); + } + + private static void validateGeneratedIdDocumentVerificationReport(final IdDocumentVerificationReportResponse response) { + assertNotNull(response); + // May have signed_url and pdf_report depending on implementation + validateCommonIdDocumentVerificationReportFields(response); + } + + private static void validateCommonIdDocumentVerificationFields(final IdDocumentVerificationResponse response) { + assertNotNull(response.getStatus()); + assertNotNull(response.getLinks()); + assertNotNull(response.getSelfLink()); + assertNotNull(response.getSelfLink().getHref()); + } + + private static void validateCommonIdDocumentVerificationAttemptFields(final IdDocumentVerificationAttemptResponse response) { + assertNotNull(response.getStatus()); + assertNotNull(response.getLinks()); + assertNotNull(response.getSelfLink()); + assertNotNull(response.getSelfLink().getHref()); + } + + private static void validateCommonIdDocumentVerificationReportFields(final IdDocumentVerificationReportResponse response) { + assertNotNull(response.getLinks()); + assertNotNull(response.getSelfLink()); + assertNotNull(response.getSelfLink().getHref()); + } + + /** + * Generates a random string for test data. + */ + private static String generateRandomString(final int length) { + return UUID.randomUUID().toString().replace("-", "").substring(0, length); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/identities/identityverification/IdentityVerificationClientImplTest.java b/src/test/java/com/checkout/identities/identityverification/IdentityVerificationClientImplTest.java new file mode 100644 index 00000000..ca1c9f6d --- /dev/null +++ b/src/test/java/com/checkout/identities/identityverification/IdentityVerificationClientImplTest.java @@ -0,0 +1,332 @@ +package com.checkout.identities.identityverification; + +import com.checkout.ApiClient; +import com.checkout.CheckoutConfiguration; +import com.checkout.SdkAuthorization; +import com.checkout.SdkAuthorizationType; +import com.checkout.SdkCredentials; +import com.checkout.identities.identityverification.requests.CreateAndOpenIdentityVerificationRequest; +import com.checkout.identities.identityverification.requests.IdentityVerificationRequest; +import com.checkout.identities.identityverification.requests.IdentityVerificationAttemptRequest; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptsResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationReportResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class IdentityVerificationClientImplTest { + + private IdentityVerificationClient client; + + @Mock + private ApiClient apiClient; + + @Mock + private CheckoutConfiguration configuration; + + @Mock + private SdkCredentials sdkCredentials; + + @Mock + private SdkAuthorization authorization; + + @BeforeEach + void setUp() { + when(sdkCredentials.getAuthorization(SdkAuthorizationType.SECRET_KEY_OR_OAUTH)).thenReturn(authorization); + when(configuration.getSdkCredentials()).thenReturn(sdkCredentials); + client = new IdentityVerificationClientImpl(apiClient, configuration); + } + + // Async methods + + @Test + void shouldCreateAndOpenIdentityVerificationAsync() throws ExecutionException, InterruptedException { + final CreateAndOpenIdentityVerificationRequest request = createCreateAndOpenIdentityVerificationRequest(); + final IdentityVerificationResponse response = createIdentityVerificationResponse(); + + when(apiClient.postAsync(eq("create-and-open-idv"), eq(authorization), eq(IdentityVerificationResponse.class), + eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createAndOpenIdentityVerificationAsync(request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldCreateIdentityVerificationAsync() throws ExecutionException, InterruptedException { + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse response = createIdentityVerificationResponse(); + + when(apiClient.postAsync(eq("identity-verifications"), eq(authorization), eq(IdentityVerificationResponse.class), + eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createIdentityVerificationAsync(request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetIdentityVerificationAsync() throws ExecutionException, InterruptedException { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationResponse response = createIdentityVerificationResponse(); + + when(apiClient.getAsync(eq("identity-verifications/" + identityVerificationId), eq(authorization), + eq(IdentityVerificationResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getIdentityVerificationAsync(identityVerificationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldAnonymizeIdentityVerificationAsync() throws ExecutionException, InterruptedException { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationResponse response = createIdentityVerificationResponse(); + + when(apiClient.postAsync(eq("identity-verifications/" + identityVerificationId + "/anonymize"), eq(authorization), + eq(IdentityVerificationResponse.class), isNull(), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.anonymizeIdentityVerificationAsync(identityVerificationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetIdentityVerificationAttemptsAsync() throws ExecutionException, InterruptedException { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationAttemptsResponse response = createIdentityVerificationAttemptsResponse(); + + when(apiClient.getAsync(eq("identity-verifications/" + identityVerificationId + "/attempts"), eq(authorization), + eq(IdentityVerificationAttemptsResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getIdentityVerificationAttemptsAsync(identityVerificationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldCreateIdentityVerificationAttemptAsync() throws ExecutionException, InterruptedException { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationAttemptRequest request = createIdentityVerificationAttemptRequest(); + final IdentityVerificationAttemptResponse response = createIdentityVerificationAttemptResponse(); + + when(apiClient.postAsync(eq("identity-verifications/" + identityVerificationId + "/attempts"), eq(authorization), + eq(IdentityVerificationAttemptResponse.class), eq(request), isNull())) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createIdentityVerificationAttemptAsync(identityVerificationId, request); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGetIdentityVerificationAttemptAsync() throws ExecutionException, InterruptedException { + final String identityVerificationId = "idv_test_123456789"; + final String attemptId = "idva_test_987654321"; + final IdentityVerificationAttemptResponse response = createIdentityVerificationAttemptResponse(); + + when(apiClient.getAsync(eq("identity-verifications/" + identityVerificationId + "/attempts/" + attemptId), + eq(authorization), eq(IdentityVerificationAttemptResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getIdentityVerificationAttemptAsync(identityVerificationId, attemptId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + @Test + void shouldGenerateIdentityVerificationReportAsync() throws ExecutionException, InterruptedException { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationReportResponse response = createIdentityVerificationReportResponse(); + + when(apiClient.getAsync(eq("identity-verifications/" + identityVerificationId + "/pdf-report"), eq(authorization), + eq(IdentityVerificationReportResponse.class))) + .thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.generateIdentityVerificationReportAsync(identityVerificationId); + + assertNotNull(future.get()); + assertEquals(response, future.get()); + } + + // Synchronous methods tests + + @Test + void shouldCreateAndOpenIdentityVerification() { + final CreateAndOpenIdentityVerificationRequest request = createCreateAndOpenIdentityVerificationRequest(); + final IdentityVerificationResponse response = createIdentityVerificationResponse(); + + when(apiClient.post(eq("create-and-open-idv"), eq(authorization), eq(IdentityVerificationResponse.class), + eq(request), isNull())) + .thenReturn(response); + + final IdentityVerificationResponse result = client.createAndOpenIdentityVerification(request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldCreateIdentityVerification() { + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse response = createIdentityVerificationResponse(); + + when(apiClient.post(eq("identity-verifications"), eq(authorization), eq(IdentityVerificationResponse.class), + eq(request), isNull())) + .thenReturn(response); + + final IdentityVerificationResponse result = client.createIdentityVerification(request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetIdentityVerification() { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationResponse response = createIdentityVerificationResponse(); + + when(apiClient.get(eq("identity-verifications/" + identityVerificationId), eq(authorization), + eq(IdentityVerificationResponse.class))) + .thenReturn(response); + + final IdentityVerificationResponse result = client.getIdentityVerification(identityVerificationId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldAnonymizeIdentityVerification() { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationResponse response = createIdentityVerificationResponse(); + + when(apiClient.post(eq("identity-verifications/" + identityVerificationId + "/anonymize"), eq(authorization), + eq(IdentityVerificationResponse.class), isNull(), isNull())) + .thenReturn(response); + + final IdentityVerificationResponse result = client.anonymizeIdentityVerification(identityVerificationId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetIdentityVerificationAttempts() { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationAttemptsResponse response = createIdentityVerificationAttemptsResponse(); + + when(apiClient.get(eq("identity-verifications/" + identityVerificationId + "/attempts"), eq(authorization), + eq(IdentityVerificationAttemptsResponse.class))) + .thenReturn(response); + + final IdentityVerificationAttemptsResponse result = client.getIdentityVerificationAttempts(identityVerificationId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldCreateIdentityVerificationAttempt() { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationAttemptRequest request = createIdentityVerificationAttemptRequest(); + final IdentityVerificationAttemptResponse response = createIdentityVerificationAttemptResponse(); + + when(apiClient.post(eq("identity-verifications/" + identityVerificationId + "/attempts"), eq(authorization), + eq(IdentityVerificationAttemptResponse.class), eq(request), isNull())) + .thenReturn(response); + + final IdentityVerificationAttemptResponse result = client.createIdentityVerificationAttempt(identityVerificationId, request); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGetIdentityVerificationAttempt() { + final String identityVerificationId = "idv_test_123456789"; + final String attemptId = "idva_test_987654321"; + final IdentityVerificationAttemptResponse response = createIdentityVerificationAttemptResponse(); + + when(apiClient.get(eq("identity-verifications/" + identityVerificationId + "/attempts/" + attemptId), + eq(authorization), eq(IdentityVerificationAttemptResponse.class))) + .thenReturn(response); + + final IdentityVerificationAttemptResponse result = client.getIdentityVerificationAttempt(identityVerificationId, attemptId); + + assertNotNull(result); + assertEquals(response, result); + } + + @Test + void shouldGenerateIdentityVerificationReport() { + final String identityVerificationId = "idv_test_123456789"; + final IdentityVerificationReportResponse response = createIdentityVerificationReportResponse(); + + when(apiClient.get(eq("identity-verifications/" + identityVerificationId + "/pdf-report"), eq(authorization), + eq(IdentityVerificationReportResponse.class))) + .thenReturn(response); + + final IdentityVerificationReportResponse result = client.generateIdentityVerificationReport(identityVerificationId); + + assertNotNull(result); + assertEquals(response, result); + } + + // Common methods + + private CreateAndOpenIdentityVerificationRequest createCreateAndOpenIdentityVerificationRequest() { + return mock(CreateAndOpenIdentityVerificationRequest.class); + } + + private IdentityVerificationRequest createIdentityVerificationRequest() { + return mock(IdentityVerificationRequest.class); + } + + private IdentityVerificationAttemptRequest createIdentityVerificationAttemptRequest() { + return mock(IdentityVerificationAttemptRequest.class); + } + + private IdentityVerificationResponse createIdentityVerificationResponse() { + return mock(IdentityVerificationResponse.class); + } + + private IdentityVerificationAttemptResponse createIdentityVerificationAttemptResponse() { + return mock(IdentityVerificationAttemptResponse.class); + } + + private IdentityVerificationAttemptsResponse createIdentityVerificationAttemptsResponse() { + return mock(IdentityVerificationAttemptsResponse.class); + } + + private IdentityVerificationReportResponse createIdentityVerificationReportResponse() { + return mock(IdentityVerificationReportResponse.class); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/identities/identityverification/IdentityVerificationTestIT.java b/src/test/java/com/checkout/identities/identityverification/IdentityVerificationTestIT.java new file mode 100644 index 00000000..6ec8c3f9 --- /dev/null +++ b/src/test/java/com/checkout/identities/identityverification/IdentityVerificationTestIT.java @@ -0,0 +1,374 @@ +package com.checkout.identities.identityverification; + +import com.checkout.PlatformType; +import com.checkout.SandboxTestFixture; +import com.checkout.identities.entities.ClientInformation; +import com.checkout.identities.entities.DeclaredData; +import com.checkout.identities.identityverification.requests.CreateAndOpenIdentityVerificationRequest; +import com.checkout.identities.identityverification.requests.IdentityVerificationRequest; +import com.checkout.identities.identityverification.requests.IdentityVerificationAttemptRequest; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptsResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationReportResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationResponse; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class IdentityVerificationTestIT extends SandboxTestFixture { + + IdentityVerificationTestIT() { + super(PlatformType.DEFAULT_OAUTH); + } + + // Async methods + + @Test + @Disabled("Integration test - requires valid applicant and user journey") + void shouldCreateAndOpenIdentityVerificationAsync() { + // Arrange + final CreateAndOpenIdentityVerificationRequest request = createCreateAndOpenIdentityVerificationRequest(); + + // Act + final IdentityVerificationResponse response = blocking(() -> + checkoutApi.identityVerificationClient().createAndOpenIdentityVerificationAsync(request)); + + // Assert + validateCreatedAndOpenedIdentityVerification(response, request); + } + + @Test + @Disabled("Integration test - requires valid applicant and user journey") + void shouldCreateIdentityVerificationAsync() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + + // Act + final IdentityVerificationResponse response = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAsync(request)); + + // Assert + validateCreatedIdentityVerification(response, request); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldGetIdentityVerificationAsync() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAsync(request)); + + // Act + final IdentityVerificationResponse retrieved = blocking(() -> + checkoutApi.identityVerificationClient().getIdentityVerificationAsync(created.getId())); + + // Assert + validateRetrievedIdentityVerification(retrieved, created); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldAnonymizeIdentityVerificationAsync() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAsync(request)); + + // Act + final IdentityVerificationResponse anonymized = blocking(() -> + checkoutApi.identityVerificationClient().anonymizeIdentityVerificationAsync(created.getId())); + + // Assert + validateAnonymizedIdentityVerification(anonymized); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldGetIdentityVerificationAttemptsAsync() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAsync(request)); + final IdentityVerificationAttemptRequest attemptRequest = createIdentityVerificationAttemptRequest(); + final IdentityVerificationAttemptResponse createdAttempt = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAttemptAsync(created.getId(), attemptRequest)); + + // Act + final IdentityVerificationAttemptsResponse attempts = blocking(() -> + checkoutApi.identityVerificationClient().getIdentityVerificationAttemptsAsync(created.getId())); + + // Assert + validateRetrievedIdentityVerificationAttempts(attempts, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldCreateIdentityVerificationAttemptAsync() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAsync(request)); + final IdentityVerificationAttemptRequest attemptRequest = createIdentityVerificationAttemptRequest(); + + // Act + final IdentityVerificationAttemptResponse attempt = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAttemptAsync(created.getId(), attemptRequest)); + + // Assert + validateCreatedIdentityVerificationAttempt(attempt, attemptRequest); + } + + @Test + @Disabled("Integration test - requires valid identity verification and attempt IDs") + void shouldGetIdentityVerificationAttemptAsync() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAsync(request)); + final IdentityVerificationAttemptRequest attemptRequest = createIdentityVerificationAttemptRequest(); + final IdentityVerificationAttemptResponse createdAttempt = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAttemptAsync(created.getId(), attemptRequest)); + + // Act + final IdentityVerificationAttemptResponse retrievedAttempt = blocking(() -> + checkoutApi.identityVerificationClient().getIdentityVerificationAttemptAsync(created.getId(), createdAttempt.getId())); + + // Assert + validateRetrievedIdentityVerificationAttempt(retrievedAttempt, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldGenerateIdentityVerificationReportAsync() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = blocking(() -> + checkoutApi.identityVerificationClient().createIdentityVerificationAsync(request)); + + // Act + final IdentityVerificationReportResponse report = blocking(() -> + checkoutApi.identityVerificationClient().generateIdentityVerificationReportAsync(created.getId())); + + // Assert + validateGeneratedIdentityVerificationReport(report); + } + + // Synchronous methods + + @Test + @Disabled("Integration test - requires valid applicant and user journey") + void shouldCreateAndOpenIdentityVerification() { + // Arrange + final CreateAndOpenIdentityVerificationRequest request = createCreateAndOpenIdentityVerificationRequest(); + + // Act + final IdentityVerificationResponse response = checkoutApi.identityVerificationClient().createAndOpenIdentityVerification(request); + + // Assert + validateCreatedAndOpenedIdentityVerification(response, request); + } + + @Test + @Disabled("Integration test - requires valid applicant and user journey") + void shouldCreateIdentityVerification() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + + // Act + final IdentityVerificationResponse response = checkoutApi.identityVerificationClient().createIdentityVerification(request); + + // Assert + validateCreatedIdentityVerification(response, request); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldGetIdentityVerification() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = checkoutApi.identityVerificationClient().createIdentityVerification(request); + + // Act + final IdentityVerificationResponse retrieved = checkoutApi.identityVerificationClient().getIdentityVerification(created.getId()); + + // Assert + validateRetrievedIdentityVerification(retrieved, created); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldAnonymizeIdentityVerification() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = checkoutApi.identityVerificationClient().createIdentityVerification(request); + + // Act + final IdentityVerificationResponse anonymized = checkoutApi.identityVerificationClient().anonymizeIdentityVerification(created.getId()); + + // Assert + validateAnonymizedIdentityVerification(anonymized); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldGetIdentityVerificationAttempts() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = checkoutApi.identityVerificationClient().createIdentityVerification(request); + final IdentityVerificationAttemptRequest attemptRequest = createIdentityVerificationAttemptRequest(); + final IdentityVerificationAttemptResponse createdAttempt = checkoutApi.identityVerificationClient().createIdentityVerificationAttempt(created.getId(), attemptRequest); + + // Act + final IdentityVerificationAttemptsResponse attempts = checkoutApi.identityVerificationClient().getIdentityVerificationAttempts(created.getId()); + + // Assert + validateRetrievedIdentityVerificationAttempts(attempts, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldCreateIdentityVerificationAttempt() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = checkoutApi.identityVerificationClient().createIdentityVerification(request); + final IdentityVerificationAttemptRequest attemptRequest = createIdentityVerificationAttemptRequest(); + + // Act + final IdentityVerificationAttemptResponse attempt = checkoutApi.identityVerificationClient().createIdentityVerificationAttempt(created.getId(), attemptRequest); + + // Assert + validateCreatedIdentityVerificationAttempt(attempt, attemptRequest); + } + + @Test + @Disabled("Integration test - requires valid identity verification and attempt IDs") + void shouldGetIdentityVerificationAttempt() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = checkoutApi.identityVerificationClient().createIdentityVerification(request); + final IdentityVerificationAttemptRequest attemptRequest = createIdentityVerificationAttemptRequest(); + final IdentityVerificationAttemptResponse createdAttempt = checkoutApi.identityVerificationClient().createIdentityVerificationAttempt(created.getId(), attemptRequest); + + // Act + final IdentityVerificationAttemptResponse retrievedAttempt = checkoutApi.identityVerificationClient().getIdentityVerificationAttempt(created.getId(), createdAttempt.getId()); + + // Assert + validateRetrievedIdentityVerificationAttempt(retrievedAttempt, createdAttempt); + } + + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldGenerateIdentityVerificationReport() { + // Arrange + final IdentityVerificationRequest request = createIdentityVerificationRequest(); + final IdentityVerificationResponse created = checkoutApi.identityVerificationClient().createIdentityVerification(request); + + // Act + final IdentityVerificationReportResponse report = checkoutApi.identityVerificationClient().generateIdentityVerificationReport(created.getId()); + + // Assert + validateGeneratedIdentityVerificationReport(report); + } + + // Common methods + + private CreateAndOpenIdentityVerificationRequest createCreateAndOpenIdentityVerificationRequest() { + return CreateAndOpenIdentityVerificationRequest.builder() + .applicantId("app_test_" + UUID.randomUUID().toString().substring(0, 8)) + .userJourneyId("uj_test_" + UUID.randomUUID().toString().substring(0, 8)) + .declaredData(createDeclaredData()) + .build(); + } + + private IdentityVerificationRequest createIdentityVerificationRequest() { + return IdentityVerificationRequest.builder() + .applicantId("app_test_" + UUID.randomUUID().toString().substring(0, 8)) + .userJourneyId("uj_test_" + UUID.randomUUID().toString().substring(0, 8)) + .declaredData(createDeclaredData()) + .build(); + } + + private IdentityVerificationAttemptRequest createIdentityVerificationAttemptRequest() { + return IdentityVerificationAttemptRequest.builder() + .clientInformation(createClientInformation()) + .build(); + } + + private DeclaredData createDeclaredData() { + return DeclaredData.builder() + .name("John Doe") + .build(); + } + + private ClientInformation createClientInformation() { + return ClientInformation.builder() + .preSelectedResidenceCountry("GB") + .preSelectedLanguage("en") + .build(); + } + + private void validateCreatedAndOpenedIdentityVerification(final IdentityVerificationResponse response, + final CreateAndOpenIdentityVerificationRequest request) { + assertNotNull(response); + assertNotNull(response.getId()); + assertNotNull(response.getRedirectUrl()); + assertEquals(request.getApplicantId(), response.getApplicantId()); + assertEquals(request.getUserJourneyId(), response.getUserJourneyId()); + } + + private void validateCreatedIdentityVerification(final IdentityVerificationResponse response, + final IdentityVerificationRequest request) { + assertNotNull(response); + assertNotNull(response.getId()); + assertEquals(request.getApplicantId(), response.getApplicantId()); + assertEquals(request.getUserJourneyId(), response.getUserJourneyId()); + } + + private void validateRetrievedIdentityVerification(final IdentityVerificationResponse retrieved, + final IdentityVerificationResponse created) { + assertNotNull(retrieved); + assertEquals(created.getId(), retrieved.getId()); + assertEquals(created.getApplicantId(), retrieved.getApplicantId()); + assertEquals(created.getUserJourneyId(), retrieved.getUserJourneyId()); + } + + private void validateAnonymizedIdentityVerification(final IdentityVerificationResponse anonymized) { + assertNotNull(anonymized); + assertNotNull(anonymized.getId()); + // Add specific validation for anonymized data + } + + private void validateCreatedIdentityVerificationAttempt(final IdentityVerificationAttemptResponse attempt, + final IdentityVerificationAttemptRequest request) { + assertNotNull(attempt); + assertNotNull(attempt.getId()); + // Add specific validation for attempt data + } + + private void validateRetrievedIdentityVerificationAttempts(final IdentityVerificationAttemptsResponse attempts, + final IdentityVerificationAttemptResponse createdAttempt) { + assertNotNull(attempts); + assertNotNull(attempts.getData()); + assertTrue(attempts.getData().size() > 0); + assertTrue(attempts.getData().stream().anyMatch(a -> a.getId().equals(createdAttempt.getId()))); + } + + private void validateRetrievedIdentityVerificationAttempt(final IdentityVerificationAttemptResponse retrieved, + final IdentityVerificationAttemptResponse created) { + assertNotNull(retrieved); + assertEquals(created.getId(), retrieved.getId()); + // Add specific validation for attempt data + } + + private void validateGeneratedIdentityVerificationReport(final IdentityVerificationReportResponse report) { + assertNotNull(report); + assertNotNull(report.getSignedUrl()); + // Add specific validation for report data + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/issuing/IssuingCardholdersTestIT.java b/src/test/java/com/checkout/issuing/IssuingCardholdersTestIT.java index f63ec2b7..d7d73a48 100644 --- a/src/test/java/com/checkout/issuing/IssuingCardholdersTestIT.java +++ b/src/test/java/com/checkout/issuing/IssuingCardholdersTestIT.java @@ -2,10 +2,16 @@ import com.checkout.issuing.cardholders.CardholderCardsResponse; import com.checkout.issuing.cardholders.CardholderDetailsResponse; +import com.checkout.issuing.cardholders.CardholderAccessTokenRequest; +import com.checkout.issuing.cardholders.CardholderAccessTokenResponse; +import com.checkout.issuing.cardholders.CardholderUpdateRequest; +import com.checkout.issuing.cardholders.CardholderUpdateResponse; +import com.checkout.issuing.cardholders.CardholderRequest; import com.checkout.issuing.cardholders.CardholderResponse; import com.checkout.issuing.cardholders.CardholderStatus; import com.checkout.issuing.cardholders.CardholderType; import com.checkout.issuing.cards.responses.CardDetailsResponse; +import com.checkout.TestHelper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -46,6 +52,49 @@ void shouldGetCardholderCards() { validateCardholderCards(cardholderCards); } + @Test + void shouldRequestCardholderAccessToken() { + final CardholderAccessTokenRequest request = createCardholderAccessTokenRequest(); + + final CardholderAccessTokenResponse response = blocking(() -> + issuingApi.issuingClient().requestCardholderAccessToken(request)); + + validateCardholderAccessTokenResponse(response); + } + + @Test + void shouldUpdateCardholder() { + final CardholderUpdateRequest request = createCardholderUpdateRequest(); + + final CardholderUpdateResponse response = blocking(() -> + issuingApi.issuingClient().updateCardholder(cardholder.getId(), request)); + + validateCardholderUpdateResponse(response); + } + + @Test + void shouldCreateUpdateAndVerifyCardholder() { + // Create a new cardholder + final CardholderRequest createRequest = createCardholderRequest(); + final CardholderResponse createResponse = blocking(() -> + issuingApi.issuingClient().createCardholder(createRequest)); + + validateCardholderCreation(createResponse, createRequest); + + // Update the cardholder + final CardholderUpdateRequest updateRequest = createCardholderUpdateRequestForVerification(); + final CardholderUpdateResponse updateResponse = blocking(() -> + issuingApi.issuingClient().updateCardholder(createResponse.getId(), updateRequest)); + + validateCardholderUpdateResponse(updateResponse); + + // Get updated cardholder and verify changes + final CardholderDetailsResponse getResponse = blocking(() -> + issuingApi.issuingClient().getCardholder(createResponse.getId())); + + validateUpdatedCardholderDetails(getResponse, updateRequest); + } + // Synchronous methods @Test void shouldCreateCardholderSync() { @@ -70,7 +119,129 @@ void shouldGetCardholderCardsSync() { validateCardholderCards(cardholderCards); } + @Test + void shouldRequestCardholderAccessTokenSync() { + final CardholderAccessTokenRequest request = createCardholderAccessTokenRequest(); + + final CardholderAccessTokenResponse response = + issuingApi.issuingClient().requestCardholderAccessTokenSync(request); + + validateCardholderAccessTokenResponse(response); + } + + @Test + void shouldUpdateCardholderSync() { + final CardholderUpdateRequest request = createCardholderUpdateRequest(); + + final CardholderUpdateResponse response = + issuingApi.issuingClient().updateCardholderSync(cardholder.getId(), request); + + validateCardholderUpdateResponse(response); + } + + @Test + void shouldCreateUpdateAndVerifyCardholderSync() { + // Create a new cardholder + final CardholderRequest createRequest = createCardholderRequest(); + final CardholderResponse createResponse = + issuingApi.issuingClient().createCardholderSync(createRequest); + + validateCardholderCreation(createResponse, createRequest); + + // Update the cardholder + final CardholderUpdateRequest updateRequest = createCardholderUpdateRequestForVerification(); + final CardholderUpdateResponse updateResponse = + issuingApi.issuingClient().updateCardholderSync(createResponse.getId(), updateRequest); + + validateCardholderUpdateResponse(updateResponse); + + // Get updated cardholder and verify changes + final CardholderDetailsResponse getResponse = + issuingApi.issuingClient().getCardholderSync(createResponse.getId()); + + validateUpdatedCardholderDetails(getResponse, updateRequest); + } + // Common methods + private CardholderRequest createCardholderRequest() { + return CardholderRequest.builder() + .type(CardholderType.INDIVIDUAL) + .reference("X-TEST-" + System.currentTimeMillis()) + .entityId("ent_mujh2nia2ypezmw5fo2fofk7ka") + .firstName("John") + .middleName("Fitzgerald") + .lastName("Kennedy") + .email("john.kennedy@myemaildomain.com") + .phoneNumber(TestHelper.createPhone()) + .dateOfBirth("1985-05-15") + .billingAddress(TestHelper.createAddress()) + .residencyAddress(TestHelper.createAddress()) + .build(); + } + + private CardholderUpdateRequest createCardholderUpdateRequest() { + return CardholderUpdateRequest.builder() + .firstName("Jane") + .lastName("Smith") + .email("jane.smith@updatedemails.com") + .build(); + } + + private CardholderUpdateRequest createCardholderUpdateRequestForVerification() { + return CardholderUpdateRequest.builder() + .firstName("UpdatedJohn") + .middleName("UpdatedMiddle") + .lastName("UpdatedSurname") + .email("updated.john@newdomain.com") + .phoneNumber(TestHelper.createPhone()) + .dateOfBirth("1990-12-25") + .billingAddress(TestHelper.createAddress()) + .residencyAddress(TestHelper.createAddress()) + .build(); + } + private CardholderAccessTokenRequest createCardholderAccessTokenRequest() { + return CardholderAccessTokenRequest.builder() + .grantType("client_credentials") + .clientID(System.getenv("CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID")) + .clientSecret(System.getenv("CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET")) + .cardholderId(cardholder.getId()) + .singleUse(true) + .build(); + } + + private void validateCardholderAccessTokenResponse(CardholderAccessTokenResponse response) { + assertNotNull(response); + assertNotNull(response.getAccessToken()); + assertNotNull(response.getTokenType()); + assertNotNull(response.getExpiresIn()); + // Note: Scope might be null in some cases according to OAuth spec + } + + private void validateCardholderUpdateResponse(CardholderUpdateResponse response) { + assertNotNull(response); + assertNotNull(response.getLastModifiedDate()); + } + + private void validateUpdatedCardholderDetails(CardholderDetailsResponse response, CardholderUpdateRequest updateRequest) { + assertNotNull(response); + assertEquals(updateRequest.getFirstName(), response.getFirstName()); + assertEquals(updateRequest.getMiddleName(), response.getMiddleName()); + assertEquals(updateRequest.getLastName(), response.getLastName()); + assertEquals(updateRequest.getEmail(), response.getEmail()); + assertEquals(updateRequest.getDateOfBirth(), response.getDateOfBirth()); + assertNotNull(response.getLastModifiedDate()); + + if (updateRequest.getPhoneNumber() != null) { + assertNotNull(response.getPhoneNumber()); + } + if (updateRequest.getBillingAddress() != null) { + assertNotNull(response.getBillingAddress()); + } + if (updateRequest.getResidencyAddress() != null) { + assertNotNull(response.getResidencyAddress()); + } + } + private void validateCardholderCreation(CardholderResponse cardholderResponse) { assertNotNull(cardholderResponse); assertEquals(CardholderType.INDIVIDUAL, cardholderResponse.getType()); @@ -78,6 +249,13 @@ private void validateCardholderCreation(CardholderResponse cardholderResponse) { assertEquals("X-123456-N11", cardholderResponse.getReference()); } + private void validateCardholderCreation(CardholderResponse cardholderResponse, CardholderRequest originalRequest) { + assertNotNull(cardholderResponse); + assertEquals(CardholderType.INDIVIDUAL, cardholderResponse.getType()); + assertEquals(CardholderStatus.ACTIVE, cardholderResponse.getStatus()); + assertEquals(originalRequest.getReference(), cardholderResponse.getReference()); + } + private void validateCardholderDetails(CardholderDetailsResponse cardholderDetails) { assertNotNull(cardholderDetails); assertEquals(CardholderType.INDIVIDUAL, cardholderDetails.getType()); diff --git a/src/test/java/com/checkout/issuing/IssuingCardsTestIT.java b/src/test/java/com/checkout/issuing/IssuingCardsTestIT.java index 1eedb17d..5a912c61 100644 --- a/src/test/java/com/checkout/issuing/IssuingCardsTestIT.java +++ b/src/test/java/com/checkout/issuing/IssuingCardsTestIT.java @@ -5,16 +5,21 @@ import com.checkout.issuing.cards.requests.credentials.CardCredentialsQuery; import com.checkout.issuing.cards.requests.enrollment.PasswordThreeDSEnrollmentRequest; import com.checkout.issuing.cards.requests.enrollment.ThreeDSUpdateRequest; +import com.checkout.issuing.cards.requests.renew.RenewCardRequest; +import com.checkout.issuing.cards.requests.revocation.ScheduleRevocationRequest; import com.checkout.issuing.cards.requests.revoke.RevokeCardRequest; import com.checkout.issuing.cards.requests.revoke.RevokeReason; import com.checkout.issuing.cards.requests.suspend.SuspendCardRequest; import com.checkout.issuing.cards.requests.suspend.SuspendReason; +import com.checkout.issuing.cards.requests.update.UpdateCardRequest; import com.checkout.issuing.cards.responses.CardDetailsResponse; import com.checkout.issuing.cards.responses.CardResponse; import com.checkout.issuing.cards.responses.credentials.CardCredentialsResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSEnrollmentDetailsResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSEnrollmentResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSUpdateResponse; +import com.checkout.issuing.cards.responses.renew.RenewCardResponse; +import com.checkout.issuing.cards.responses.update.UpdateCardResponse; import com.checkout.payments.VoidResponse; import org.apache.http.HttpStatus; import org.junit.jupiter.api.BeforeAll; @@ -138,6 +143,55 @@ void shouldSuspendCard() { assertEquals(CardStatus.SUSPENDED, cardDetails.getStatus()); } + @Test + void shouldUpdateCard() { + final UpdateCardRequest request = createUpdateCardRequest(); + + final UpdateCardResponse updateResponse = blocking(() -> + issuingApi.issuingClient().updateCard(card.getId(), request)); + + validateUpdateCardResponse(updateResponse); + } + + @Test + void shouldRenewCard() { + final CardResponse cardResponse = createCard(cardholder.getId(), true); + + final RenewCardRequest request = createRenewCardRequest(); + + final RenewCardResponse renewResponse = blocking(() -> + issuingApi.issuingClient().renewCard(cardResponse.getId(), request)); + + validateRenewCardResponse(renewResponse, cardResponse.getId()); + } + + @Test + void shouldScheduleCardRevocation() { + final CardResponse cardResponse = createCard(cardholder.getId(), true); + + final ScheduleRevocationRequest request = createScheduleRevocationRequest(); + + final VoidResponse scheduleResponse = blocking(() -> + issuingApi.issuingClient().scheduleCardRevocation(cardResponse.getId(), request)); + + validateVoidResponse(scheduleResponse); + } + + @Test + void shouldDeleteScheduledRevocation() { + final CardResponse cardResponse = createCard(cardholder.getId(), true); + + // First schedule a revocation + final ScheduleRevocationRequest scheduleRequest = createScheduleRevocationRequest(); + blocking(() -> issuingApi.issuingClient().scheduleCardRevocation(cardResponse.getId(), scheduleRequest)); + + // Then delete it + final VoidResponse deleteResponse = blocking(() -> + issuingApi.issuingClient().deleteScheduledRevocation(cardResponse.getId())); + + validateVoidResponse(deleteResponse); + } + // Synchronous methods @Test void shouldCreateCardSync() { @@ -239,6 +293,55 @@ void shouldSuspendCardSync() { assertEquals(CardStatus.SUSPENDED, cardDetails.getStatus()); } + @Test + void shouldUpdateCardSync() { + final UpdateCardRequest request = createUpdateCardRequest(); + + final UpdateCardResponse updateResponse = + issuingApi.issuingClient().updateCardSync(card.getId(), request); + + validateUpdateCardResponse(updateResponse); + } + + @Test + void shouldRenewCardSync() { + final CardResponse cardResponse = createCard(cardholder.getId(), true); + + final RenewCardRequest request = createRenewCardRequest(); + + final RenewCardResponse renewResponse = + issuingApi.issuingClient().renewCardSync(cardResponse.getId(), request); + + validateRenewCardResponse(renewResponse, cardResponse.getId()); + } + + @Test + void shouldScheduleCardRevocationSync() { + final CardResponse cardResponse = createCard(cardholder.getId(), true); + + final ScheduleRevocationRequest request = createScheduleRevocationRequest(); + + final VoidResponse scheduleResponse = + issuingApi.issuingClient().scheduleCardRevocationSync(cardResponse.getId(), request); + + validateVoidResponse(scheduleResponse); + } + + @Test + void shouldDeleteScheduledRevocationSync() { + final CardResponse cardResponse = createCard(cardholder.getId(), true); + + // First schedule a revocation + final ScheduleRevocationRequest scheduleRequest = createScheduleRevocationRequest(); + issuingApi.issuingClient().scheduleCardRevocationSync(cardResponse.getId(), scheduleRequest); + + // Then delete it + final VoidResponse deleteResponse = + issuingApi.issuingClient().deleteScheduledRevocationSync(cardResponse.getId()); + + validateVoidResponse(deleteResponse); + } + // Common methods private PasswordThreeDSEnrollmentRequest createThreeDSEnrollmentRequest() { return PasswordThreeDSEnrollmentRequest.builder() @@ -271,6 +374,27 @@ private SuspendCardRequest createSuspendCardRequest() { .build(); } + private UpdateCardRequest createUpdateCardRequest() { + return UpdateCardRequest.builder() + .reference("Updated-Reference-987") + .expiryMonth(12) + .expiryYear(2025) + .build(); + } + + private RenewCardRequest createRenewCardRequest() { + return com.checkout.issuing.cards.requests.renew.VirtualCardRenewRequest.builder() + .displayName("John Kennedy Renewed") + .reference("Renewed-X-123456-N11") + .build(); + } + + private ScheduleRevocationRequest createScheduleRevocationRequest() { + return ScheduleRevocationRequest.builder() + .revocationDate("2025-12-31") + .build(); + } + private void validateCardCreation(CardResponse cardResponse, String expectedCardholderId) { assertNotNull(cardResponse); assertNotNull(cardResponse.getId()); @@ -304,4 +428,20 @@ private void validateCredentialsResponse(CardCredentialsResponse credentialsResp assertNotNull(credentialsResponse.getNumber()); assertNotNull(credentialsResponse.getCvc2()); } + + private void validateUpdateCardResponse(UpdateCardResponse updateResponse) { + assertNotNull(updateResponse); + assertNotNull(updateResponse.getLastModifiedDate()); + } + + private void validateRenewCardResponse(RenewCardResponse renewResponse, String parentCardId) { + assertNotNull(renewResponse); + assertNotNull(renewResponse.getId()); + assertEquals(parentCardId, renewResponse.getParentCardId()); + } + + private void validateVoidResponse(VoidResponse voidResponse) { + assertNotNull(voidResponse); + assertEquals(HttpStatus.SC_OK, voidResponse.getHttpStatusCode()); + } } diff --git a/src/test/java/com/checkout/issuing/IssuingClientImplTest.java b/src/test/java/com/checkout/issuing/IssuingClientImplTest.java index 4439d067..22f73cf3 100644 --- a/src/test/java/com/checkout/issuing/IssuingClientImplTest.java +++ b/src/test/java/com/checkout/issuing/IssuingClientImplTest.java @@ -9,27 +9,46 @@ import com.checkout.common.IdResponse; import com.checkout.issuing.cardholders.CardholderCardsResponse; import com.checkout.issuing.cardholders.CardholderDetailsResponse; +import com.checkout.issuing.cardholders.CardholderAccessTokenRequest; +import com.checkout.issuing.cardholders.CardholderAccessTokenResponse; import com.checkout.issuing.cardholders.CardholderRequest; +import com.checkout.issuing.cardholders.CardholderUpdateRequest; import com.checkout.issuing.cardholders.CardholderResponse; +import com.checkout.issuing.cardholders.CardholderUpdateResponse; import com.checkout.issuing.cards.requests.create.VirtualCardRequest; import com.checkout.issuing.cards.requests.credentials.CardCredentialsQuery; import com.checkout.issuing.cards.requests.enrollment.PasswordThreeDSEnrollmentRequest; import com.checkout.issuing.cards.requests.enrollment.ThreeDSUpdateRequest; +import com.checkout.issuing.cards.requests.renew.RenewCardRequest; +import com.checkout.issuing.cards.requests.revocation.ScheduleRevocationRequest; import com.checkout.issuing.cards.requests.revoke.RevokeCardRequest; import com.checkout.issuing.cards.requests.suspend.SuspendCardRequest; +import com.checkout.issuing.cards.requests.update.UpdateCardRequest; import com.checkout.issuing.cards.responses.CardDetailsResponse; import com.checkout.issuing.cards.responses.CardResponse; import com.checkout.issuing.cards.responses.credentials.CardCredentialsResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSEnrollmentDetailsResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSEnrollmentResponse; import com.checkout.issuing.cards.responses.enrollment.ThreeDSUpdateResponse; +import com.checkout.issuing.cards.responses.renew.RenewCardResponse; +import com.checkout.issuing.cards.responses.update.UpdateCardResponse; import com.checkout.issuing.controls.requests.create.CardControlRequest; import com.checkout.issuing.controls.requests.query.CardControlsQuery; import com.checkout.issuing.controls.requests.update.UpdateCardControlRequest; import com.checkout.issuing.controls.responses.create.CardControlResponse; import com.checkout.issuing.controls.responses.query.CardControlsQueryResponse; +import com.checkout.issuing.controls.requests.controlgroup.CreateControlGroupRequest; +import com.checkout.issuing.controls.requests.controlgroup.ControlGroupQuery; +import com.checkout.issuing.controls.responses.controlgroup.ControlGroupResponse; +import com.checkout.issuing.controls.responses.controlgroup.ControlGroupsQueryResponse; +import com.checkout.issuing.controls.requests.controlprofile.CreateControlProfileRequest; +import com.checkout.issuing.controls.requests.controlprofile.ControlProfileQuery; +import com.checkout.issuing.controls.requests.controlprofile.UpdateControlProfileRequest; +import com.checkout.issuing.controls.responses.controlprofile.ControlProfileResponse; +import com.checkout.issuing.controls.responses.controlprofile.ControlProfilesQueryResponse; import com.checkout.issuing.testing.requests.CardAuthorizationClearingRequest; import com.checkout.issuing.testing.requests.CardAuthorizationIncrementingRequest; +import com.checkout.issuing.testing.requests.CardAuthorizationRefundsRequest; import com.checkout.issuing.testing.requests.CardAuthorizationRequest; import com.checkout.issuing.testing.requests.CardAuthorizationReversalRequest; import com.checkout.issuing.testing.responses.CardAuthorizationIncrementingResponse; @@ -47,8 +66,12 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import org.apache.http.client.entity.UrlEncodedFormEntity; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -126,6 +149,42 @@ void shouldGetCardholderCards() throws ExecutionException, InterruptedException validateCardholderCardsResponse(response, future.get()); } + + @Test + void shouldRequestCardholderAccessToken() throws ExecutionException, InterruptedException { + final CardholderAccessTokenRequest request = createCardholderAccessTokenRequest(); + final CardholderAccessTokenResponse response = createCardholderAccessTokenResponse(); + + when(apiClient.postAsync( + eq("issuing/access/connect/token"), + eq(authorization), + eq(CardholderAccessTokenResponse.class), + any(UrlEncodedFormEntity.class), + eq(null) + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.requestCardholderAccessToken(request); + + validateCardholderAccessTokenResponse(response, future.get()); + } + + @Test + void shouldUpdateCardholder() throws ExecutionException, InterruptedException { + final CardholderUpdateRequest request = createCardholderUpdateRequest(); + final CardholderUpdateResponse response = createCardholderUpdateResponse(); + + when(apiClient.patchAsync( + "issuing/cardholders/cardholder_id", + authorization, + CardholderUpdateResponse.class, + request, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.updateCardholder("cardholder_id", request); + + validateCardholderUpdateResponse(response, future.get()); + } } @Nested @@ -284,6 +343,75 @@ void shouldSuspendCard() throws ExecutionException, InterruptedException { validateVoidResponse(response, future.get()); } + + @Test + void shouldUpdateCard() throws ExecutionException, InterruptedException { + final UpdateCardRequest request = createUpdateCardRequest(); + final UpdateCardResponse response = createUpdateCardResponse(); + + when(apiClient.patchAsync( + "issuing/cards/card_id", + authorization, + UpdateCardResponse.class, + request, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.updateCard("card_id", request); + + validateUpdateCardResponse(response, future.get()); + } + + @Test + void shouldRenewCard() throws ExecutionException, InterruptedException { + final RenewCardRequest request = createRenewCardRequest(); + final RenewCardResponse response = createRenewCardResponse(); + + when(apiClient.postAsync( + "issuing/cards/card_id/renew", + authorization, + RenewCardResponse.class, + request, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.renewCard("card_id", request); + + validateRenewCardResponse(response, future.get()); + } + + @Test + void shouldScheduleCardRevocation() throws ExecutionException, InterruptedException { + final ScheduleRevocationRequest request = createScheduleRevocationRequest(); + final VoidResponse response = createVoidResponse(); + + when(apiClient.postAsync( + "issuing/cards/card_id/schedule-revocation", + authorization, + VoidResponse.class, + request, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.scheduleCardRevocation("card_id", request); + + validateVoidResponse(response, future.get()); + } + + @Test + void shouldDeleteScheduledRevocation() throws ExecutionException, InterruptedException { + final VoidResponse response = createVoidResponse(); + + when(apiClient.deleteAsync( + "issuing/cards/card_id/schedule-revocation", + authorization, + VoidResponse.class + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.deleteScheduledRevocation("card_id"); + + validateVoidResponse(response, future.get()); + } } @Nested @@ -431,6 +559,25 @@ void shouldSimulateAuthorizationClearing() throws ExecutionException, Interrupte validateEmptyResponse(response, future.get()); } + @Test + void shouldSimulateAuthorizationRefund() throws ExecutionException, InterruptedException { + final CardAuthorizationRefundsRequest request = createCardAuthorizationRefundsRequest(); + final EmptyResponse response = createEmptyResponse(); + + when(apiClient.postAsync( + "issuing/simulate/authorizations/authorization_id/refunds", + authorization, + EmptyResponse.class, + request, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = + client.simulateRefund("authorization_id", request); + + validateEmptyResponse(response, future.get()); + } + @Test void shouldSimulateAuthorizationReversal() throws ExecutionException, InterruptedException { final CardAuthorizationReversalRequest request = createCardAuthorizationReversalRequest(); @@ -451,6 +598,196 @@ void shouldSimulateAuthorizationReversal() throws ExecutionException, Interrupte } } + @Nested + @DisplayName("Control Groups") + class ControlGroups { + @Test + void shouldCreateControlGroup() throws ExecutionException, InterruptedException { + final CreateControlGroupRequest request = createCreateControlGroupRequest(); + final ControlGroupResponse response = createControlGroupResponse(); + + when(apiClient.postAsync( + "issuing/controls/control-groups", + authorization, + ControlGroupResponse.class, + request, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createControlGroup(request); + + validateControlGroupResponse(response, future.get()); + } + + @Test + void shouldGetControlGroupDetails() throws ExecutionException, InterruptedException { + final ControlGroupResponse response = createControlGroupResponse(); + + when(apiClient.getAsync( + "issuing/controls/control-groups/group_id", + authorization, + ControlGroupResponse.class + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getControlGroupDetails("group_id"); + + validateControlGroupResponse(response, future.get()); + } + + @Test + void shouldQueryControlGroups() throws ExecutionException, InterruptedException { + final ControlGroupQuery query = createControlGroupQuery(); + final ControlGroupsQueryResponse response = createControlGroupsQueryResponse(); + + when(apiClient.queryAsync( + "issuing/controls/control-groups", + authorization, + query, + ControlGroupsQueryResponse.class + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getControlGroups(query); + + validateControlGroupsQueryResponse(response, future.get()); + } + + @Test + void shouldRemoveControlGroup() throws ExecutionException, InterruptedException { + final IdResponse response = createIdResponse(); + + when(apiClient.deleteAsync( + "issuing/controls/control-groups/group_id", + authorization, + IdResponse.class + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.removeControlGroup("group_id"); + + validateIdResponse(response, future.get()); + } + } + + @Nested + @DisplayName("Control Profiles") + class ControlProfiles { + @Test + void shouldCreateControlProfile() throws ExecutionException, InterruptedException { + final CreateControlProfileRequest request = createCreateControlProfileRequest(); + final ControlProfileResponse response = createControlProfileResponse(); + + when(apiClient.postAsync( + "issuing/controls/control-profiles", + authorization, + ControlProfileResponse.class, + request, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.createControlProfile(request); + + validateControlProfileResponse(response, future.get()); + } + + @Test + void shouldGetControlProfileDetails() throws ExecutionException, InterruptedException { + final ControlProfileResponse response = createControlProfileResponse(); + + when(apiClient.getAsync( + "issuing/controls/control-profiles/profile_id", + authorization, + ControlProfileResponse.class + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getControlProfileDetails("profile_id"); + + validateControlProfileResponse(response, future.get()); + } + + @Test + void shouldQueryControlProfiles() throws ExecutionException, InterruptedException { + final ControlProfileQuery query = createControlProfileQuery(); + final ControlProfilesQueryResponse response = createControlProfilesQueryResponse(); + + when(apiClient.queryAsync( + "issuing/controls/control-profiles", + authorization, + query, + ControlProfilesQueryResponse.class + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.getControlProfiles(query); + + validateControlProfilesQueryResponse(response, future.get()); + } + + @Test + void shouldUpdateControlProfile() throws ExecutionException, InterruptedException { + final UpdateControlProfileRequest request = createUpdateControlProfileRequest(); + final ControlProfileResponse response = createControlProfileResponse(); + + when(apiClient.patchAsync( + "issuing/controls/control-profiles/profile_id", + authorization, + ControlProfileResponse.class, + request, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.updateControlProfile("profile_id", request); + + validateControlProfileResponse(response, future.get()); + } + + @Test + void shouldRemoveControlProfile() throws ExecutionException, InterruptedException { + final EmptyResponse response = createEmptyResponse(); + + when(apiClient.deleteAsync( + "issuing/controls/control-profiles/profile_id", + authorization, + EmptyResponse.class + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.removeControlProfile("profile_id"); + + validateEmptyResponse(response, future.get()); + } + + @Test + void shouldAddTargetToControlProfile() throws ExecutionException, InterruptedException { + final VoidResponse response = createVoidResponse(); + + when(apiClient.postAsync( + "issuing/controls/control-profiles/profile_id/add/target_id", + authorization, + VoidResponse.class, + null, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.addTargetToControlProfile("profile_id", "target_id"); + + validateVoidResponse(response, future.get()); + } + + @Test + void shouldRemoveTargetFromControlProfile() throws ExecutionException, InterruptedException { + final VoidResponse response = createVoidResponse(); + + when(apiClient.postAsync( + "issuing/controls/control-profiles/profile_id/remove/target_id", + authorization, + VoidResponse.class, + null, + null + )).thenReturn(CompletableFuture.completedFuture(response)); + + final CompletableFuture future = client.removeTargetFromControlProfile("profile_id", "target_id"); + + validateVoidResponse(response, future.get()); + } + } + // Synchronous methods @Nested @DisplayName("Cardholders Sync") @@ -502,6 +839,42 @@ void shouldGetCardholderCardsSync() { validateCardholderCardsResponse(expectedResponse, actualResponse); } + + @Test + void shouldRequestCardholderAccessTokenSync() { + final CardholderAccessTokenRequest request = createCardholderAccessTokenRequest(); + final CardholderAccessTokenResponse expectedResponse = createCardholderAccessTokenResponse(); + + when(apiClient.post( + eq("issuing/access/connect/token"), + eq(authorization), + eq(CardholderAccessTokenResponse.class), + any(UrlEncodedFormEntity.class), + eq(null) + )).thenReturn(expectedResponse); + + final CardholderAccessTokenResponse actualResponse = client.requestCardholderAccessTokenSync(request); + + validateCardholderAccessTokenResponse(expectedResponse, actualResponse); + } + + @Test + void shouldUpdateCardholderSync() { + final CardholderUpdateRequest request = createCardholderUpdateRequest(); + final CardholderUpdateResponse expectedResponse = createCardholderUpdateResponse(); + + when(apiClient.patch( + "issuing/cardholders/cardholder_id", + authorization, + CardholderUpdateResponse.class, + request, + null + )).thenReturn(expectedResponse); + + final CardholderUpdateResponse actualResponse = client.updateCardholderSync("cardholder_id", request); + + validateCardholderUpdateResponse(expectedResponse, actualResponse); + } } @Nested @@ -660,6 +1033,75 @@ void shouldSuspendCardSync() { validateVoidResponse(expectedResponse, actualResponse); } + + @Test + void shouldUpdateCardSync() { + final UpdateCardRequest request = createUpdateCardRequest(); + final UpdateCardResponse expectedResponse = createUpdateCardResponse(); + + when(apiClient.patch( + "issuing/cards/card_id", + authorization, + UpdateCardResponse.class, + request, + null + )).thenReturn(expectedResponse); + + final UpdateCardResponse actualResponse = client.updateCardSync("card_id", request); + + validateUpdateCardResponse(expectedResponse, actualResponse); + } + + @Test + void shouldRenewCardSync() { + final RenewCardRequest request = createRenewCardRequest(); + final RenewCardResponse expectedResponse = createRenewCardResponse(); + + when(apiClient.post( + "issuing/cards/card_id/renew", + authorization, + RenewCardResponse.class, + request, + null + )).thenReturn(expectedResponse); + + final RenewCardResponse actualResponse = client.renewCardSync("card_id", request); + + validateRenewCardResponse(expectedResponse, actualResponse); + } + + @Test + void shouldScheduleCardRevocationSync() { + final ScheduleRevocationRequest request = createScheduleRevocationRequest(); + final VoidResponse expectedResponse = createVoidResponse(); + + when(apiClient.post( + "issuing/cards/card_id/schedule-revocation", + authorization, + VoidResponse.class, + request, + null + )).thenReturn(expectedResponse); + + final VoidResponse actualResponse = client.scheduleCardRevocationSync("card_id", request); + + validateVoidResponse(expectedResponse, actualResponse); + } + + @Test + void shouldDeleteScheduledRevocationSync() { + final VoidResponse expectedResponse = createVoidResponse(); + + when(apiClient.delete( + "issuing/cards/card_id/schedule-revocation", + authorization, + VoidResponse.class + )).thenReturn(expectedResponse); + + final VoidResponse actualResponse = client.deleteScheduledRevocationSync("card_id"); + + validateVoidResponse(expectedResponse, actualResponse); + } } @Nested @@ -806,6 +1248,24 @@ void shouldSimulateAuthorizationClearingSync() { validateEmptyResponse(expectedResponse, actualResponse); } + @Test + void shouldSimulateAuthorizationRefundSync() { + final CardAuthorizationRefundsRequest request = createCardAuthorizationRefundsRequest(); + final EmptyResponse expectedResponse = createEmptyResponse(); + + when(apiClient.post( + "issuing/simulate/authorizations/authorization_id/refunds", + authorization, + EmptyResponse.class, + request, + null + )).thenReturn(expectedResponse); + + final EmptyResponse actualResponse = client.simulateRefundSync("authorization_id", request); + + validateEmptyResponse(expectedResponse, actualResponse); + } + @Test void shouldSimulateAuthorizationReversalSync() { final CardAuthorizationReversalRequest request = createCardAuthorizationReversalRequest(); @@ -826,6 +1286,196 @@ void shouldSimulateAuthorizationReversalSync() { } } + @Nested + @DisplayName("Control Groups Sync") + class ControlGroupsSync { + @Test + void shouldCreateControlGroupSync() { + final CreateControlGroupRequest request = createCreateControlGroupRequest(); + final ControlGroupResponse expectedResponse = createControlGroupResponse(); + + when(apiClient.post( + "issuing/controls/control-groups", + authorization, + ControlGroupResponse.class, + request, + null + )).thenReturn(expectedResponse); + + final ControlGroupResponse actualResponse = client.createControlGroupSync(request); + + validateControlGroupResponse(expectedResponse, actualResponse); + } + + @Test + void shouldGetControlGroupDetailsSync() { + final ControlGroupResponse expectedResponse = createControlGroupResponse(); + + when(apiClient.get( + "issuing/controls/control-groups/group_id", + authorization, + ControlGroupResponse.class + )).thenReturn(expectedResponse); + + final ControlGroupResponse actualResponse = client.getControlGroupDetailsSync("group_id"); + + validateControlGroupResponse(expectedResponse, actualResponse); + } + + @Test + void shouldQueryControlGroupsSync() { + final ControlGroupQuery query = createControlGroupQuery(); + final ControlGroupsQueryResponse expectedResponse = createControlGroupsQueryResponse(); + + when(apiClient.query( + "issuing/controls/control-groups", + authorization, + query, + ControlGroupsQueryResponse.class + )).thenReturn(expectedResponse); + + final ControlGroupsQueryResponse actualResponse = client.getControlGroupsSync(query); + + validateControlGroupsQueryResponse(expectedResponse, actualResponse); + } + + @Test + void shouldRemoveControlGroupSync() { + final IdResponse expectedResponse = createIdResponse(); + + when(apiClient.delete( + "issuing/controls/control-groups/group_id", + authorization, + IdResponse.class + )).thenReturn(expectedResponse); + + final IdResponse actualResponse = client.removeControlGroupSync("group_id"); + + validateIdResponse(expectedResponse, actualResponse); + } + } + + @Nested + @DisplayName("Control Profiles Sync") + class ControlProfilesSync { + @Test + void shouldCreateControlProfileSync() { + final CreateControlProfileRequest request = createCreateControlProfileRequest(); + final ControlProfileResponse expectedResponse = createControlProfileResponse(); + + when(apiClient.post( + "issuing/controls/control-profiles", + authorization, + ControlProfileResponse.class, + request, + null + )).thenReturn(expectedResponse); + + final ControlProfileResponse actualResponse = client.createControlProfileSync(request); + + validateControlProfileResponse(expectedResponse, actualResponse); + } + + @Test + void shouldGetControlProfileDetailsSync() { + final ControlProfileResponse expectedResponse = createControlProfileResponse(); + + when(apiClient.get( + "issuing/controls/control-profiles/profile_id", + authorization, + ControlProfileResponse.class + )).thenReturn(expectedResponse); + + final ControlProfileResponse actualResponse = client.getControlProfileDetailsSync("profile_id"); + + validateControlProfileResponse(expectedResponse, actualResponse); + } + + @Test + void shouldQueryControlProfilesSync() { + final ControlProfileQuery query = createControlProfileQuery(); + final ControlProfilesQueryResponse expectedResponse = createControlProfilesQueryResponse(); + + when(apiClient.query( + "issuing/controls/control-profiles", + authorization, + query, + ControlProfilesQueryResponse.class + )).thenReturn(expectedResponse); + + final ControlProfilesQueryResponse actualResponse = client.getControlProfilesSync(query); + + validateControlProfilesQueryResponse(expectedResponse, actualResponse); + } + + @Test + void shouldUpdateControlProfileSync() { + final UpdateControlProfileRequest request = createUpdateControlProfileRequest(); + final ControlProfileResponse expectedResponse = createControlProfileResponse(); + + when(apiClient.patch( + "issuing/controls/control-profiles/profile_id", + authorization, + ControlProfileResponse.class, + request, + null + )).thenReturn(expectedResponse); + + final ControlProfileResponse actualResponse = client.updateControlProfileSync("profile_id", request); + + validateControlProfileResponse(expectedResponse, actualResponse); + } + + @Test + void shouldRemoveControlProfileSync() { + final EmptyResponse expectedResponse = createEmptyResponse(); + + when(apiClient.delete( + "issuing/controls/control-profiles/profile_id", + authorization, + EmptyResponse.class + )).thenReturn(expectedResponse); + + final EmptyResponse actualResponse = client.removeControlProfileSync("profile_id"); + + validateEmptyResponse(expectedResponse, actualResponse); + } + + @Test + void shouldAddTargetToControlProfileSync() { + final VoidResponse expectedResponse = createVoidResponse(); + + when(apiClient.post( + "issuing/controls/control-profiles/profile_id/add/target_id", + authorization, + VoidResponse.class, + null, + null + )).thenReturn(expectedResponse); + + final VoidResponse actualResponse = client.addTargetToControlProfileSync("profile_id", "target_id"); + + validateVoidResponse(expectedResponse, actualResponse); + } + + @Test + void shouldRemoveTargetFromControlProfileSync() { + final VoidResponse expectedResponse = createVoidResponse(); + + when(apiClient.post( + "issuing/controls/control-profiles/profile_id/remove/target_id", + authorization, + VoidResponse.class, + null, + null + )).thenReturn(expectedResponse); + + final VoidResponse actualResponse = client.removeTargetFromControlProfileSync("profile_id", "target_id"); + + validateVoidResponse(expectedResponse, actualResponse); + } + } + // Common methods private CardholderRequest createCardholderRequest() { return mock(CardholderRequest.class); @@ -835,6 +1485,22 @@ private CardholderResponse createCardholderResponse() { return mock(CardholderResponse.class); } + private CardholderAccessTokenRequest createCardholderAccessTokenRequest() { + return mock(CardholderAccessTokenRequest.class); + } + + private CardholderAccessTokenResponse createCardholderAccessTokenResponse() { + return mock(CardholderAccessTokenResponse.class); + } + + private CardholderUpdateRequest createCardholderUpdateRequest() { + return mock(CardholderUpdateRequest.class); + } + + private CardholderUpdateResponse createCardholderUpdateResponse() { + return mock(CardholderUpdateResponse.class); + } + private CardholderDetailsResponse createCardholderDetailsResponse() { return mock(CardholderDetailsResponse.class); } @@ -895,6 +1561,26 @@ private SuspendCardRequest createSuspendCardRequest() { return mock(SuspendCardRequest.class); } + private UpdateCardRequest createUpdateCardRequest() { + return mock(UpdateCardRequest.class); + } + + private UpdateCardResponse createUpdateCardResponse() { + return mock(UpdateCardResponse.class); + } + + private RenewCardRequest createRenewCardRequest() { + return mock(RenewCardRequest.class); + } + + private RenewCardResponse createRenewCardResponse() { + return mock(RenewCardResponse.class); + } + + private ScheduleRevocationRequest createScheduleRevocationRequest() { + return mock(ScheduleRevocationRequest.class); + } + private CardControlRequest createCardControlRequest() { return mock(CardControlRequest.class); } @@ -943,6 +1629,10 @@ private EmptyResponse createEmptyResponse() { return mock(EmptyResponse.class); } + private CardAuthorizationRefundsRequest createCardAuthorizationRefundsRequest() { + return mock(CardAuthorizationRefundsRequest.class); + } + private CardAuthorizationReversalRequest createCardAuthorizationReversalRequest() { return mock(CardAuthorizationReversalRequest.class); } @@ -956,6 +1646,16 @@ private void validateCardholderResponse(CardholderResponse expected, CardholderR assertEquals(expected, actual); } + private void validateCardholderAccessTokenResponse(CardholderAccessTokenResponse expected, CardholderAccessTokenResponse actual) { + assertNotNull(actual); + assertEquals(expected, actual); + } + + private void validateCardholderUpdateResponse(CardholderUpdateResponse expected, CardholderUpdateResponse actual) { + assertNotNull(actual); + assertEquals(expected, actual); + } + private void validateCardholderDetailsResponse(CardholderDetailsResponse expected, CardholderDetailsResponse actual) { assertNotNull(actual); assertEquals(expected, actual); @@ -1035,4 +1735,72 @@ private void validateCardAuthorizationReversalResponse(CardAuthorizationReversal assertNotNull(actual); assertEquals(expected, actual); } + + private void validateUpdateCardResponse(UpdateCardResponse expected, UpdateCardResponse actual) { + assertNotNull(actual); + assertEquals(expected, actual); + } + + private void validateRenewCardResponse(RenewCardResponse expected, RenewCardResponse actual) { + assertNotNull(actual); + assertEquals(expected, actual); + } + + // Control Group common methods + private CreateControlGroupRequest createCreateControlGroupRequest() { + return mock(CreateControlGroupRequest.class); + } + + private ControlGroupQuery createControlGroupQuery() { + return mock(ControlGroupQuery.class); + } + + private ControlGroupResponse createControlGroupResponse() { + return mock(ControlGroupResponse.class); + } + + private ControlGroupsQueryResponse createControlGroupsQueryResponse() { + return mock(ControlGroupsQueryResponse.class); + } + + private void validateControlGroupResponse(ControlGroupResponse expected, ControlGroupResponse actual) { + assertNotNull(actual); + assertEquals(expected, actual); + } + + private void validateControlGroupsQueryResponse(ControlGroupsQueryResponse expected, ControlGroupsQueryResponse actual) { + assertNotNull(actual); + assertEquals(expected, actual); + } + + // Control Profile common methods + private CreateControlProfileRequest createCreateControlProfileRequest() { + return mock(CreateControlProfileRequest.class); + } + + private ControlProfileQuery createControlProfileQuery() { + return mock(ControlProfileQuery.class); + } + + private UpdateControlProfileRequest createUpdateControlProfileRequest() { + return mock(UpdateControlProfileRequest.class); + } + + private ControlProfileResponse createControlProfileResponse() { + return mock(ControlProfileResponse.class); + } + + private ControlProfilesQueryResponse createControlProfilesQueryResponse() { + return mock(ControlProfilesQueryResponse.class); + } + + private void validateControlProfileResponse(ControlProfileResponse expected, ControlProfileResponse actual) { + assertNotNull(actual); + assertEquals(expected, actual); + } + + private void validateControlProfilesQueryResponse(ControlProfilesQueryResponse expected, ControlProfilesQueryResponse actual) { + assertNotNull(actual); + assertEquals(expected, actual); + } } diff --git a/src/test/java/com/checkout/issuing/IssuingControlGroupsTestIT.java b/src/test/java/com/checkout/issuing/IssuingControlGroupsTestIT.java new file mode 100644 index 00000000..1f0b630a --- /dev/null +++ b/src/test/java/com/checkout/issuing/IssuingControlGroupsTestIT.java @@ -0,0 +1,160 @@ +package com.checkout.issuing; + +import com.checkout.common.IdResponse; +import com.checkout.issuing.controls.requests.controlgroup.CreateControlGroupRequest; +import com.checkout.issuing.controls.requests.controlgroup.ControlGroupQuery; +import com.checkout.issuing.controls.requests.controlgroup.VelocityControlGroupControl; +import com.checkout.issuing.controls.responses.controlgroup.ControlGroupResponse; +import com.checkout.issuing.controls.responses.controlgroup.ControlGroupsQueryResponse; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Disabled("3ds not configured and should not create cards every time") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class IssuingControlGroupsTestIT extends BaseIssuingTestIT { + + private ControlGroupResponse controlGroup; + + @BeforeAll + void setUp() { + controlGroup = createControlGroup(); + } + + // Control Groups - Async Tests + @Test + void shouldCreateControlGroup() { + validateControlGroupCreation(controlGroup); + } + + @Test + void shouldGetControlGroupDetails() { + final ControlGroupResponse response = blocking(() -> + issuingApi.issuingClient().getControlGroupDetails(controlGroup.getId())); + + validateControlGroupResponse(response, controlGroup.getId()); + } + + @Test + void shouldGetControlGroups() { + final ControlGroupQuery query = createControlGroupQuery(); + + final ControlGroupsQueryResponse response = blocking(() -> + issuingApi.issuingClient().getControlGroups(query)); + + validateControlGroupsQueryResponse(response); + } + + @Test + void shouldRemoveControlGroup() { + final ControlGroupResponse tempControlGroup = createControlGroup(); + + final IdResponse response = blocking(() -> + issuingApi.issuingClient().removeControlGroup(tempControlGroup.getId())); + + validateIdResponse(response, tempControlGroup.getId()); + } + + // Control Groups - Sync Tests + @Test + void shouldCreateControlGroupSync() { + final ControlGroupResponse controlGroup = createControlGroupSync(); + validateControlGroupCreation(controlGroup); + } + + @Test + void shouldGetControlGroupDetailsSync() { + final ControlGroupResponse response = + issuingApi.issuingClient().getControlGroupDetailsSync(controlGroup.getId()); + + validateControlGroupResponse(response, controlGroup.getId()); + } + + @Test + void shouldGetControlGroupsSync() { + final ControlGroupQuery query = createControlGroupQuery(); + + final ControlGroupsQueryResponse response = + issuingApi.issuingClient().getControlGroupsSync(query); + + validateControlGroupsQueryResponse(response); + } + + @Test + void shouldRemoveControlGroupSync() { + final ControlGroupResponse tempControlGroup = createControlGroupSync(); + + final IdResponse response = + issuingApi.issuingClient().removeControlGroupSync(tempControlGroup.getId()); + + validateIdResponse(response, tempControlGroup.getId()); + } + + // Common methods + private ControlGroupQuery createControlGroupQuery() { + return ControlGroupQuery.builder().build(); + } + + private CreateControlGroupRequest createCreateControlGroupRequest() { + return CreateControlGroupRequest.builder() + .description("Test velocity control group") + .controls(java.util.List.of( + VelocityControlGroupControl.builder() + .description("Test velocity control") + .velocityLimit(com.checkout.issuing.controls.requests.VelocityLimit.builder() + .amountLimit(1000) + .velocityWindow(com.checkout.issuing.controls.requests.VelocityWindow.builder() + .type(com.checkout.issuing.controls.requests.VelocityWindowType.DAILY) + .build()) + .build()) + .build() + )) + .build(); + } + + private ControlGroupResponse createControlGroup() { + final CreateControlGroupRequest request = createCreateControlGroupRequest(); + + final ControlGroupResponse response = blocking(() -> + issuingApi.issuingClient().createControlGroup(request)); + + assertNotNull(response); + return response; + } + + private ControlGroupResponse createControlGroupSync() { + final CreateControlGroupRequest request = createCreateControlGroupRequest(); + + final ControlGroupResponse response = + issuingApi.issuingClient().createControlGroupSync(request); + + assertNotNull(response); + return response; + } + + // Validation methods + private void validateControlGroupCreation(ControlGroupResponse controlGroup) { + assertNotNull(controlGroup); + assertNotNull(controlGroup.getId()); + assertEquals("Test velocity control group", controlGroup.getDescription()); + } + + private void validateControlGroupResponse(ControlGroupResponse response, String expectedId) { + assertNotNull(response); + assertEquals(expectedId, response.getId()); + } + + private void validateControlGroupsQueryResponse(ControlGroupsQueryResponse response) { + assertNotNull(response); + // Note: May be empty if control groups were removed by other tests + } + + private void validateIdResponse(IdResponse response, String expectedId) { + assertNotNull(response); + assertEquals(expectedId, response.getId()); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/issuing/IssuingControlProfilesTestIT.java b/src/test/java/com/checkout/issuing/IssuingControlProfilesTestIT.java new file mode 100644 index 00000000..51e6f19d --- /dev/null +++ b/src/test/java/com/checkout/issuing/IssuingControlProfilesTestIT.java @@ -0,0 +1,221 @@ +package com.checkout.issuing; + +import com.checkout.EmptyResponse; +import com.checkout.issuing.cardholders.CardholderResponse; +import com.checkout.issuing.cards.responses.CardResponse; +import com.checkout.issuing.controls.requests.controlprofile.CreateControlProfileRequest; +import com.checkout.issuing.controls.requests.controlprofile.ControlProfileQuery; +import com.checkout.issuing.controls.requests.controlprofile.UpdateControlProfileRequest; +import com.checkout.issuing.controls.responses.controlprofile.ControlProfileResponse; +import com.checkout.issuing.controls.responses.controlprofile.ControlProfilesQueryResponse; +import com.checkout.payments.VoidResponse; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Disabled("3ds not configured and should not create cards every time") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class IssuingControlProfilesTestIT extends BaseIssuingTestIT { + + private CardResponse card; + private ControlProfileResponse controlProfile; + + @BeforeAll + void setUp() { + final CardholderResponse cardholder = createCardholder(); + card = createCard(cardholder.getId(), true); + controlProfile = createControlProfile(); + } + + // Control Profiles - Async Tests + @Test + void shouldCreateControlProfile() { + validateControlProfileCreation(controlProfile); + } + + @Test + void shouldGetControlProfileDetails() { + final ControlProfileResponse response = blocking(() -> + issuingApi.issuingClient().getControlProfileDetails(controlProfile.getId())); + + validateControlProfileResponse(response, controlProfile.getId()); + } + + @Test + void shouldGetControlProfiles() { + final ControlProfileQuery query = createControlProfileQuery(); + + final ControlProfilesQueryResponse response = blocking(() -> + issuingApi.issuingClient().getControlProfiles(query)); + + validateControlProfilesQueryResponse(response); + } + + @Test + void shouldUpdateControlProfile() { + final UpdateControlProfileRequest request = createUpdateControlProfileRequest(); + + final ControlProfileResponse response = blocking(() -> + issuingApi.issuingClient().updateControlProfile(controlProfile.getId(), request)); + + validateUpdatedControlProfileResponse(response, controlProfile.getId()); + } + + @Test + void shouldRemoveControlProfile() { + final ControlProfileResponse tempControlProfile = createControlProfile(); + + final EmptyResponse response = blocking(() -> + issuingApi.issuingClient().removeControlProfile(tempControlProfile.getId())); + + validateEmptyResponse(response); + } + + @Test + void shouldAddTargetToControlProfile() { + final VoidResponse response = blocking(() -> + issuingApi.issuingClient().addTargetToControlProfile(controlProfile.getId(), card.getId())); + + validateVoidResponse(response); + } + + @Test + void shouldRemoveTargetFromControlProfile() { + final VoidResponse response = blocking(() -> + issuingApi.issuingClient().removeTargetFromControlProfile(controlProfile.getId(), card.getId())); + + validateVoidResponse(response); + } + + // Control Profiles - Sync Tests + @Test + void shouldCreateControlProfileSync() { + final ControlProfileResponse controlProfile = createControlProfileSync(); + validateControlProfileCreation(controlProfile); + } + + @Test + void shouldGetControlProfileDetailsSync() { + final ControlProfileResponse response = + issuingApi.issuingClient().getControlProfileDetailsSync(controlProfile.getId()); + + validateControlProfileResponse(response, controlProfile.getId()); + } + + @Test + void shouldGetControlProfilesSync() { + final ControlProfileQuery query = createControlProfileQuery(); + + final ControlProfilesQueryResponse response = + issuingApi.issuingClient().getControlProfilesSync(query); + + validateControlProfilesQueryResponse(response); + } + + @Test + void shouldUpdateControlProfileSync() { + final UpdateControlProfileRequest request = createUpdateControlProfileRequest(); + + final ControlProfileResponse response = + issuingApi.issuingClient().updateControlProfileSync(controlProfile.getId(), request); + + validateUpdatedControlProfileResponse(response, controlProfile.getId()); + } + + @Test + void shouldRemoveControlProfileSync() { + final ControlProfileResponse tempControlProfile = createControlProfileSync(); + + final EmptyResponse response = + issuingApi.issuingClient().removeControlProfileSync(tempControlProfile.getId()); + + validateEmptyResponse(response); + } + + @Test + void shouldAddTargetToControlProfileSync() { + final VoidResponse response = + issuingApi.issuingClient().addTargetToControlProfileSync(controlProfile.getId(), card.getId()); + + validateVoidResponse(response); + } + + @Test + void shouldRemoveTargetFromControlProfileSync() { + final VoidResponse response = + issuingApi.issuingClient().removeTargetFromControlProfileSync(controlProfile.getId(), card.getId()); + + validateVoidResponse(response); + } + + // Common methods + private ControlProfileQuery createControlProfileQuery() { + return ControlProfileQuery.builder().build(); + } + + private UpdateControlProfileRequest createUpdateControlProfileRequest() { + return UpdateControlProfileRequest.builder() + .name("Updated Control Profile") + .build(); + } + + private CreateControlProfileRequest createCreateControlProfileRequest() { + return CreateControlProfileRequest.builder() + .name("Test Control Profile") + .build(); + } + + private ControlProfileResponse createControlProfile() { + final CreateControlProfileRequest request = createCreateControlProfileRequest(); + + final ControlProfileResponse response = blocking(() -> + issuingApi.issuingClient().createControlProfile(request)); + + assertNotNull(response); + return response; + } + + private ControlProfileResponse createControlProfileSync() { + final CreateControlProfileRequest request = createCreateControlProfileRequest(); + + final ControlProfileResponse response = + issuingApi.issuingClient().createControlProfileSync(request); + + assertNotNull(response); + return response; + } + + // Validation methods + private void validateControlProfileCreation(ControlProfileResponse controlProfile) { + assertNotNull(controlProfile); + assertNotNull(controlProfile.getId()); + assertEquals("Test Control Profile", controlProfile.getName()); + } + + private void validateControlProfileResponse(ControlProfileResponse response, String expectedId) { + assertNotNull(response); + assertEquals(expectedId, response.getId()); + } + + private void validateControlProfilesQueryResponse(ControlProfilesQueryResponse response) { + assertNotNull(response); + // Note: May be empty if control profiles were removed by other tests + } + + private void validateUpdatedControlProfileResponse(ControlProfileResponse response, String expectedId) { + assertNotNull(response); + assertEquals(expectedId, response.getId()); + } + + private void validateEmptyResponse(EmptyResponse response) { + assertNotNull(response); + } + + private void validateVoidResponse(VoidResponse response) { + assertNotNull(response); + } +} \ No newline at end of file diff --git a/src/test/java/com/checkout/issuing/IssuingTestingTestIT.java b/src/test/java/com/checkout/issuing/IssuingTestingTestIT.java index 571c6f86..b6de1316 100644 --- a/src/test/java/com/checkout/issuing/IssuingTestingTestIT.java +++ b/src/test/java/com/checkout/issuing/IssuingTestingTestIT.java @@ -6,6 +6,7 @@ import com.checkout.issuing.cards.responses.CardResponse; import com.checkout.issuing.testing.requests.CardAuthorizationClearingRequest; import com.checkout.issuing.testing.requests.CardAuthorizationIncrementingRequest; +import com.checkout.issuing.testing.requests.CardAuthorizationRefundsRequest; import com.checkout.issuing.testing.requests.CardAuthorizationRequest; import com.checkout.issuing.testing.requests.CardAuthorizationReversalRequest; import com.checkout.issuing.testing.requests.CardSimulation; @@ -64,6 +65,16 @@ void shouldSimulateClearing() { validateClearingResponse(response); } + @Test + void shouldSimulateRefund() { + final CardAuthorizationRefundsRequest request = createRefundRequest(); + + final EmptyResponse response = blocking(() -> + issuingApi.issuingClient().simulateRefund(transaction.getId(), request)); + + validateRefundResponse(response); + } + @Test void shouldSimulateReversal() { final CardAuthorizationReversalRequest request = createReversalRequest(); @@ -102,6 +113,17 @@ void shouldSimulateClearingSync() { validateClearingResponse(response); } + @Test + void shouldSimulateRefundSync() { + final CardAuthorizationResponse newTransaction = getCardAuthorizationResponse(); + final CardAuthorizationRefundsRequest request = createRefundRequest(); + + final EmptyResponse response = + issuingApi.issuingClient().simulateRefundSync(newTransaction.getId(), request); + + validateRefundResponse(response); + } + @Test void shouldSimulateReversalSync() { final CardAuthorizationResponse newTransaction = getCardAuthorizationResponse(); @@ -148,6 +170,12 @@ private CardAuthorizationClearingRequest createClearingRequest() { .build(); } + private CardAuthorizationRefundsRequest createRefundRequest() { + return CardAuthorizationRefundsRequest.builder() + .amount(1) + .build(); + } + private CardAuthorizationReversalRequest createReversalRequest() { return CardAuthorizationReversalRequest.builder() .amount(1) @@ -169,6 +197,11 @@ private void validateClearingResponse(EmptyResponse response) { assertEquals(202, response.getHttpStatusCode()); } + private void validateRefundResponse(EmptyResponse response) { + assertNotNull(response); + assertEquals(202, response.getHttpStatusCode()); + } + private void validateReversalResponse(CardAuthorizationReversalResponse response) { assertNotNull(response); assertEquals(ReversalStatus.REVERSED, response.getStatus());