From 85437da5af2b2e60500b4c71fdc1e79a34af2c3d Mon Sep 17 00:00:00 2001 From: david ruiz Date: Fri, 18 Sep 2026 11:10:43 +0200 Subject: [PATCH 1/5] Add verification attempt-assets endpoints and card scheduled_activation_date --- src/main/java/com/checkout/ApiClient.java | 4 + src/main/java/com/checkout/ApiClientImpl.java | 15 + .../AddressDocumentVerificationClient.java | 44 ++ ...AddressDocumentVerificationClientImpl.java | 67 +++ .../responses/Address.java | 4 +- ...dressDocumentVerificationAttemptAsset.java | 32 ++ ...sDocumentVerificationAttemptAssetType.java | 13 + ...mentVerificationAttemptAssetsResponse.java | 45 ++ ...essDocumentVerificationReportResponse.java | 7 +- .../AddressDocumentVerificationResponse.java | 26 + .../entities/ApplicantSessionInformation.java | 36 +- .../entities/AttemptsQueryFilter.java | 31 + .../entities/BaseAttemptResponse.java | 15 +- .../identities/entities/Certification.java | 29 + .../entities/CertificationType.java | 13 + .../entities/ClientInformation.java | 22 +- .../identities/entities/DeclaredData.java | 25 +- .../entities/DiatfCertificationData.java | 36 ++ .../identities/entities/DocumentDetails.java | 146 ++++- .../identities/entities/DocumentType.java | 17 +- .../identities/entities/Gpg45Profile.java | 17 + .../entities/IdentityDeclaredData.java | 40 ++ ...IdentityVerificationClientInformation.java | 36 ++ .../identities/entities/IdvAddress.java | 61 ++ .../identities/entities/InitialDevice.java | 15 + .../entities/LevelOfConfidence.java | 15 + .../identities/entities/PhoneNumber.java | 33 ++ .../identities/entities/RiskLabel.java | 17 + .../identities/entities/SelectedDocument.java | 32 ++ .../identities/entities/VerifiedIdentity.java | 50 +- .../FaceAuthenticationClient.java | 20 + .../FaceAuthenticationClientImpl.java | 32 ++ .../FaceAuthenticationAttemptRequest.java | 7 + .../FaceAuthenticationAttemptResponse.java | 17 +- .../FaceAuthenticationAttemptStatus.java | 5 +- .../responses/FaceAuthenticationResponse.java | 4 +- .../IdDocumentVerificationClient.java | 44 ++ .../IdDocumentVerificationClientImpl.java | 67 +++ .../IdDocumentVerificationAttemptAsset.java | 33 ++ ...dDocumentVerificationAttemptAssetType.java | 15 + ...mentVerificationAttemptAssetsResponse.java | 45 ++ .../IdDocumentVerificationReportResponse.java | 7 +- .../IdentityVerificationClient.java | 20 + .../IdentityVerificationClientImpl.java | 32 ++ ...ateAndOpenIdentityVerificationRequest.java | 4 +- .../IdentityVerificationAttemptRequest.java | 15 +- .../requests/IdentityVerificationRequest.java | 4 +- .../IdentityVerificationAttemptResponse.java | 16 +- .../IdentityVerificationAttemptStatus.java | 5 +- .../IdentityVerificationReportResponse.java | 6 +- .../IdentityVerificationResponse.java | 21 +- .../responses/IdentityVerificationStatus.java | 3 + .../com/checkout/issuing/IssuingClient.java | 27 + .../checkout/issuing/IssuingClientImpl.java | 29 + .../cards/requests/create/CardRequest.java | 52 +- .../requests/create/PhysicalCardRequest.java | 8 +- .../requests/create/VirtualCardRequest.java | 9 +- .../requests/update/CardUpdateHeaders.java | 51 ++ .../requests/update/UpdateCardRequest.java | 37 +- .../cards/responses/CardDetailsResponse.java | 90 ++- .../responses/update/UpdateCardResponse.java | 16 +- .../IdentitiesSerializationTest.java | 541 ++++++++++++++++++ ...ttemptAssetsResponseSerializationTest.java | 138 +++++ ...essDocumentVerificationClientImplTest.java | 45 +- .../FaceAuthenticationClientImplTest.java | 13 + .../FaceAuthenticationTestIT.java | 4 +- ...ttemptAssetsResponseSerializationTest.java | 149 +++++ .../IdDocumentVerificationClientImplTest.java | 51 ++ .../IdentityVerificationClientImplTest.java | 13 + .../IdentityVerificationTestIT.java | 19 +- .../checkout/issuing/IssuingCardsTestIT.java | 2 +- ...duledActivationDateSerializationTest.java} | 58 +- .../CardUpdateResponseSerializationTest.java | 104 ++++ 73 files changed, 2718 insertions(+), 103 deletions(-) create mode 100644 src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAsset.java create mode 100644 src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAssetType.java create mode 100644 src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAssetsResponse.java create mode 100644 src/main/java/com/checkout/identities/entities/AttemptsQueryFilter.java create mode 100644 src/main/java/com/checkout/identities/entities/Certification.java create mode 100644 src/main/java/com/checkout/identities/entities/CertificationType.java create mode 100644 src/main/java/com/checkout/identities/entities/DiatfCertificationData.java create mode 100644 src/main/java/com/checkout/identities/entities/Gpg45Profile.java create mode 100644 src/main/java/com/checkout/identities/entities/IdentityDeclaredData.java create mode 100644 src/main/java/com/checkout/identities/entities/IdentityVerificationClientInformation.java create mode 100644 src/main/java/com/checkout/identities/entities/IdvAddress.java create mode 100644 src/main/java/com/checkout/identities/entities/InitialDevice.java create mode 100644 src/main/java/com/checkout/identities/entities/LevelOfConfidence.java create mode 100644 src/main/java/com/checkout/identities/entities/PhoneNumber.java create mode 100644 src/main/java/com/checkout/identities/entities/RiskLabel.java create mode 100644 src/main/java/com/checkout/identities/entities/SelectedDocument.java create mode 100644 src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAsset.java create mode 100644 src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAssetType.java create mode 100644 src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAssetsResponse.java create mode 100644 src/main/java/com/checkout/issuing/cards/requests/update/CardUpdateHeaders.java create mode 100644 src/test/java/com/checkout/identities/IdentitiesSerializationTest.java create mode 100644 src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationAttemptAssetsResponseSerializationTest.java create mode 100644 src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationAttemptAssetsResponseSerializationTest.java rename src/test/java/com/checkout/issuing/cards/{CardActivationDateSerializationTest.java => CardScheduledActivationDateSerializationTest.java} (63%) create mode 100644 src/test/java/com/checkout/issuing/cards/CardUpdateResponseSerializationTest.java diff --git a/src/main/java/com/checkout/ApiClient.java b/src/main/java/com/checkout/ApiClient.java index 536a53bf..5e50a7e7 100644 --- a/src/main/java/com/checkout/ApiClient.java +++ b/src/main/java/com/checkout/ApiClient.java @@ -28,6 +28,8 @@ public interface ApiClient { CompletableFuture patchAsync(String path, SdkAuthorization authorization, Class responseType, Object request, String idempotencyKey); + CompletableFuture patchAsync(String path, SdkAuthorization authorization, Class responseType, Object request, String idempotencyKey, IHeaders headers); + CompletableFuture postAsync(String path, SdkAuthorization authorization, Map> resultTypeMappings, Object request, String idempotencyKey); CompletableFuture deleteAsync(String path, SdkAuthorization authorization); @@ -53,6 +55,8 @@ public interface ApiClient { T patch(String path, SdkAuthorization authorization, Class responseType, Object request, String idempotencyKey); + T patch(String path, SdkAuthorization authorization, Class responseType, Object request, String idempotencyKey, IHeaders headers); + T patch(String path, SdkAuthorization authorization, Type type, Object request, String idempotencyKey); T post(String path, SdkAuthorization authorization, Class responseType, Object request, String idempotencyKey); diff --git a/src/main/java/com/checkout/ApiClientImpl.java b/src/main/java/com/checkout/ApiClientImpl.java index c1bfff0d..0478c195 100644 --- a/src/main/java/com/checkout/ApiClientImpl.java +++ b/src/main/java/com/checkout/ApiClientImpl.java @@ -95,6 +95,15 @@ public CompletableFuture patchAsync(final String pat ); } + @Override + public CompletableFuture patchAsync(final String path, final SdkAuthorization authorization, final Class responseType, final Object request, final String idempotencyKey, final IHeaders headers) { + validateParams(PATH, path, AUTHORIZATION, authorization); + return executeAsyncOrSync( + () -> patch(path, authorization, responseType, request, idempotencyKey, headers), + () -> sendRequestAsync(PATCH, path, authorization, request, idempotencyKey, responseType, headers) + ); + } + @Override public CompletableFuture patchAsync(final String path, final SdkAuthorization authorization, final Type type, final Object request, final String idempotencyKey) { validateParams(PATH, path, AUTHORIZATION, authorization, "type", type, "request", request); @@ -368,6 +377,12 @@ public T patch(final String path, final SdkAuthorizatio return sendRequestSync(PATCH, path, authorization, request, idempotencyKey, responseType); } + @Override + public T patch(final String path, final SdkAuthorization authorization, final Class responseType, final Object request, final String idempotencyKey, final IHeaders headers) { + validateParams(PATH, path, AUTHORIZATION, authorization); + return sendRequestSync(PATCH, path, authorization, request, idempotencyKey, responseType, headers); + } + @Override public T patch(final String path, final SdkAuthorization authorization, final Type type, final Object request, final String idempotencyKey) { validateParams(PATH, path, AUTHORIZATION, authorization, "type", type, "request", request); diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClient.java b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClient.java index 77f6dcc3..06fa4d32 100644 --- a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClient.java +++ b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClient.java @@ -3,10 +3,14 @@ import com.checkout.identities.addressdocumentverification.requests.AddressDocumentVerificationAttemptRequest; import com.checkout.identities.addressdocumentverification.requests.AddressDocumentVerificationRequest; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptResponse; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptAssetsResponse; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptsResponse; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationReportResponse; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationResponse; +import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; + import java.util.concurrent.CompletableFuture; /** @@ -55,6 +59,15 @@ public interface AddressDocumentVerificationClient { */ CompletableFuture getAddressDocumentVerificationAttempts(String addressDocumentVerificationId); + /** + * Retrieve a page of address document verification attempts + * + * @param addressDocumentVerificationId the address document verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationAttemptsResponse} + */ + CompletableFuture getAddressDocumentVerificationAttempts(String addressDocumentVerificationId, AttemptsQueryFilter queryFilter); + /** * Retrieve a specific address document verification attempt * @@ -72,6 +85,17 @@ public interface AddressDocumentVerificationClient { */ CompletableFuture getAddressDocumentVerificationReport(String addressDocumentVerificationId); + /** + * Retrieve the assets (the document image) uploaded for an address document verification attempt. + * Beta. + * + * @param addressDocumentVerificationId the address document verification ID + * @param attemptId the attempt ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationAttemptAssetsResponse} + */ + CompletableFuture getAddressDocumentVerificationAttemptAssets(String addressDocumentVerificationId, String attemptId, AttemptAssetsQueryFilter queryFilter); + // Synchronous methods /** @@ -115,6 +139,15 @@ public interface AddressDocumentVerificationClient { */ AddressDocumentVerificationAttemptsResponse getAddressDocumentVerificationAttemptsSync(String addressDocumentVerificationId); + /** + * Retrieve a page of address document verification attempts + * + * @param addressDocumentVerificationId the address document verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link AddressDocumentVerificationAttemptsResponse} + */ + AddressDocumentVerificationAttemptsResponse getAddressDocumentVerificationAttemptsSync(String addressDocumentVerificationId, AttemptsQueryFilter queryFilter); + /** * Retrieve a specific address document verification attempt * @@ -131,4 +164,15 @@ public interface AddressDocumentVerificationClient { * @return the {@link AddressDocumentVerificationReportResponse} */ AddressDocumentVerificationReportResponse getAddressDocumentVerificationReportSync(String addressDocumentVerificationId); + + /** + * Retrieve the assets (the document image) uploaded for an address document verification attempt. + * Beta. + * + * @param addressDocumentVerificationId the address document verification ID + * @param attemptId the attempt ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link AddressDocumentVerificationAttemptAssetsResponse} + */ + AddressDocumentVerificationAttemptAssetsResponse getAddressDocumentVerificationAttemptAssetsSync(String addressDocumentVerificationId, String attemptId, AttemptAssetsQueryFilter queryFilter); } diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java index fea8d874..f24b72b8 100644 --- a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java +++ b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java @@ -7,10 +7,14 @@ import com.checkout.identities.addressdocumentverification.requests.AddressDocumentVerificationAttemptRequest; import com.checkout.identities.addressdocumentverification.requests.AddressDocumentVerificationRequest; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptResponse; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptAssetsResponse; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptsResponse; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationReportResponse; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationResponse; +import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; + import java.util.concurrent.CompletableFuture; import static com.checkout.common.CheckoutUtils.validateParams; @@ -23,6 +27,7 @@ public class AddressDocumentVerificationClientImpl extends AbstractClient implem private static final String ADDRESS_DOCUMENT_VERIFICATIONS_PATH = "address-document-verifications"; private static final String ANONYMIZE_PATH = "anonymize"; private static final String ATTEMPTS_PATH = "attempts"; + private static final String ASSETS_PATH = "assets"; private static final String PDF_REPORT_PATH = "pdf-report"; public AddressDocumentVerificationClientImpl(final ApiClient apiClient, final CheckoutConfiguration configuration) { @@ -72,6 +77,21 @@ public CompletableFuture getAddress sdkAuthorization(), AddressDocumentVerificationAttemptsResponse.class); } + /** + * Retrieve a page of address document verification attempts + * + * @param addressDocumentVerificationId the address document verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationAttemptsResponse} + */ + @Override + public CompletableFuture getAddressDocumentVerificationAttempts( + final String addressDocumentVerificationId, final AttemptsQueryFilter queryFilter) { + validateParams("addressDocumentVerificationId", addressDocumentVerificationId); + return apiClient.queryAsync(buildPath(ADDRESS_DOCUMENT_VERIFICATIONS_PATH, addressDocumentVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), queryFilter, AddressDocumentVerificationAttemptsResponse.class); + } + @Override public CompletableFuture getAddressDocumentVerificationAttempt( final String addressDocumentVerificationId, final String attemptId) { @@ -88,6 +108,22 @@ public CompletableFuture getAddressDo sdkAuthorization(), AddressDocumentVerificationReportResponse.class); } + /** + * Retrieve the assets (the document image) uploaded for an address document verification attempt + * + * @param addressDocumentVerificationId the address document verification ID + * @param attemptId the attempt ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationAttemptAssetsResponse} + */ + @Override + public CompletableFuture getAddressDocumentVerificationAttemptAssets( + final String addressDocumentVerificationId, final String attemptId, final AttemptAssetsQueryFilter queryFilter) { + validateParams("addressDocumentVerificationId", addressDocumentVerificationId, "attemptId", attemptId); + return apiClient.queryAsync(buildPath(ADDRESS_DOCUMENT_VERIFICATIONS_PATH, addressDocumentVerificationId, ATTEMPTS_PATH, attemptId, ASSETS_PATH), + sdkAuthorization(), queryFilter, AddressDocumentVerificationAttemptAssetsResponse.class); + } + // Synchronous methods @Override @@ -130,6 +166,21 @@ public AddressDocumentVerificationAttemptsResponse getAddressDocumentVerificatio sdkAuthorization(), AddressDocumentVerificationAttemptsResponse.class); } + /** + * Retrieve a page of address document verification attempts + * + * @param addressDocumentVerificationId the address document verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link AddressDocumentVerificationAttemptsResponse} + */ + @Override + public AddressDocumentVerificationAttemptsResponse getAddressDocumentVerificationAttemptsSync( + final String addressDocumentVerificationId, final AttemptsQueryFilter queryFilter) { + validateParams("addressDocumentVerificationId", addressDocumentVerificationId); + return apiClient.query(buildPath(ADDRESS_DOCUMENT_VERIFICATIONS_PATH, addressDocumentVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), queryFilter, AddressDocumentVerificationAttemptsResponse.class); + } + @Override public AddressDocumentVerificationAttemptResponse getAddressDocumentVerificationAttemptSync( final String addressDocumentVerificationId, final String attemptId) { @@ -144,4 +195,20 @@ public AddressDocumentVerificationReportResponse getAddressDocumentVerificationR return apiClient.get(buildPath(ADDRESS_DOCUMENT_VERIFICATIONS_PATH, addressDocumentVerificationId, PDF_REPORT_PATH), sdkAuthorization(), AddressDocumentVerificationReportResponse.class); } + + /** + * Retrieve the assets (the document image) uploaded for an address document verification attempt + * + * @param addressDocumentVerificationId the address document verification ID + * @param attemptId the attempt ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link AddressDocumentVerificationAttemptAssetsResponse} + */ + @Override + public AddressDocumentVerificationAttemptAssetsResponse getAddressDocumentVerificationAttemptAssetsSync( + final String addressDocumentVerificationId, final String attemptId, final AttemptAssetsQueryFilter queryFilter) { + validateParams("addressDocumentVerificationId", addressDocumentVerificationId, "attemptId", attemptId); + return apiClient.query(buildPath(ADDRESS_DOCUMENT_VERIFICATIONS_PATH, addressDocumentVerificationId, ATTEMPTS_PATH, attemptId, ASSETS_PATH), + sdkAuthorization(), queryFilter, AddressDocumentVerificationAttemptAssetsResponse.class); + } } diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/responses/Address.java b/src/main/java/com/checkout/identities/addressdocumentverification/responses/Address.java index 11f96a60..fc377676 100644 --- a/src/main/java/com/checkout/identities/addressdocumentverification/responses/Address.java +++ b/src/main/java/com/checkout/identities/addressdocumentverification/responses/Address.java @@ -1,5 +1,7 @@ package com.checkout.identities.addressdocumentverification.responses; +import com.checkout.common.CountryCode; + import lombok.Data; /** @@ -42,5 +44,5 @@ public final class Address { * The two-letter ISO country code of the address. * max 2 characters */ - private String country; + private CountryCode country; } diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAsset.java b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAsset.java new file mode 100644 index 00000000..a6965f25 --- /dev/null +++ b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAsset.java @@ -0,0 +1,32 @@ +package com.checkout.identities.addressdocumentverification.responses; + +import com.checkout.identities.entities.AttemptAssetLinks; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * An asset (the document image) uploaded for an address document verification attempt. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class AddressDocumentVerificationAttemptAsset { + + /** + * The type of asset. + * [Required] + */ + private AddressDocumentVerificationAttemptAssetType type; + + /** + * The links related to the asset. + * [Required] + */ + @SerializedName("_links") + private AttemptAssetLinks links; + +} diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAssetType.java b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAssetType.java new file mode 100644 index 00000000..a24e400d --- /dev/null +++ b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAssetType.java @@ -0,0 +1,13 @@ +package com.checkout.identities.addressdocumentverification.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * The type of asset uploaded for an address document verification attempt. + */ +public enum AddressDocumentVerificationAttemptAssetType { + + @SerializedName("document") + DOCUMENT + +} diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAssetsResponse.java b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAssetsResponse.java new file mode 100644 index 00000000..6916da4f --- /dev/null +++ b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationAttemptAssetsResponse.java @@ -0,0 +1,45 @@ +package com.checkout.identities.addressdocumentverification.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; + +/** + * The paginated assets uploaded for an address document verification attempt. + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class AddressDocumentVerificationAttemptAssetsResponse extends Resource { + + /** + * The total number of assets. + * [Required] + */ + private Integer totalCount; + + /** + * The number of assets skipped. + * [Required] + */ + private Integer skip; + + /** + * The maximum number of assets returned. + * [Required] + */ + private Integer limit; + + /** + * The list of assets for the current page. + * [Required] + */ + private List data; +} diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationReportResponse.java b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationReportResponse.java index 5b15bc57..c703d5cf 100644 --- a/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationReportResponse.java +++ b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationReportResponse.java @@ -11,5 +11,10 @@ @EqualsAndHashCode(callSuper = true) public final class AddressDocumentVerificationReportResponse extends Resource { - private String signedUrl; + /** + * The pre-signed URL to the PDF report. + * [Required] + * Format: uri + */ + private String pdfReport; } diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationResponse.java b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationResponse.java index f348793e..fc02cc26 100644 --- a/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationResponse.java +++ b/src/main/java/com/checkout/identities/addressdocumentverification/responses/AddressDocumentVerificationResponse.java @@ -2,9 +2,12 @@ import com.checkout.identities.entities.BaseIdentityResponseStatus; import com.checkout.identities.entities.DeclaredData; +import com.checkout.identities.entities.RiskLabel; import lombok.Data; import lombok.EqualsAndHashCode; +import java.util.List; + /** * Response for address document verification operations */ @@ -12,11 +15,34 @@ @EqualsAndHashCode(callSuper = true) public final class AddressDocumentVerificationResponse extends BaseIdentityResponseStatus { + /** + * Your configuration ID. + * [Required] + */ private String userJourneyId; + /** + * The applicant's unique identifier. + * [Required] + */ private String applicantId; + /** + * The personal details provided by the applicant. + * [Optional] + */ private DeclaredData declaredData; + /** + * One or more codes that provide more information about risks associated with the + * verification. + * [Optional] + */ + private List riskLabels; + + /** + * The result of the address document check. + * [Optional] + */ private AddressDocumentResult addressDocument; } diff --git a/src/main/java/com/checkout/identities/entities/ApplicantSessionInformation.java b/src/main/java/com/checkout/identities/entities/ApplicantSessionInformation.java index 4b038188..418c7742 100644 --- a/src/main/java/com/checkout/identities/entities/ApplicantSessionInformation.java +++ b/src/main/java/com/checkout/identities/entities/ApplicantSessionInformation.java @@ -5,16 +5,48 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; + /** - * Session information + * The details of the attempt. */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public final class ApplicantSessionInformation { + /** * The applicant's IP address during the attempt. + * [Optional] + * Example: 123.4.5.6 */ private String ipAddress; -} \ No newline at end of file + + /** + * The number of sessions the applicant opened during the attempt. + * [Optional] + * Example: 3 + */ + private Integer numberOfSessions; + + /** + * The user agent of the browser the applicant used during the attempt. + * [Optional] + */ + private String userAgent; + + /** + * The type of device the applicant used to start the attempt. + * [Optional] + */ + private InitialDevice initialDevice; + + /** + * The documents the applicant selected in order. + * Returned for identity verification attempts only; not returned for face authentication + * attempts. + * [Optional] + */ + private List selectedDocuments; +} diff --git a/src/main/java/com/checkout/identities/entities/AttemptsQueryFilter.java b/src/main/java/com/checkout/identities/entities/AttemptsQueryFilter.java new file mode 100644 index 00000000..27910371 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/AttemptsQueryFilter.java @@ -0,0 +1,31 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Query parameters for retrieving the attempts made for a verification. + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public final class AttemptsQueryFilter { + + /** + * The number of attempts to skip. + * [Optional] + * Default: 0 + */ + private Integer skip; + + /** + * The maximum number of attempts to return. + * [Optional] + * Default: 10 + */ + private Integer limit; + +} diff --git a/src/main/java/com/checkout/identities/entities/BaseAttemptResponse.java b/src/main/java/com/checkout/identities/entities/BaseAttemptResponse.java index acdd829c..5d5e44c1 100644 --- a/src/main/java/com/checkout/identities/entities/BaseAttemptResponse.java +++ b/src/main/java/com/checkout/identities/entities/BaseAttemptResponse.java @@ -6,7 +6,11 @@ import lombok.NoArgsConstructor; /** - * Base class for attempt responses + * Base class for attempt responses. + * Extended only by the identity verification and face authentication attempt responses, which are + * the two attempt types that carry a redirect URL, session information and a phone number. The + * address and ID document verification attempt responses extend + * {@link BaseIdentityResponseStatus} directly, because their schemas declare none of these. * * @param The status enum type */ @@ -18,16 +22,19 @@ public abstract class BaseAttemptResponse> extends BaseIdentit /** * The URL to redirect the applicant to after the attempt. + * [Required] */ private String redirectUrl; /** - * The applicant's details. + * The applicant's mobile phone number, if sharing the attempt URL via SMS. + * [Optional] */ - private ClientInformation clientInformation; + private PhoneNumber phoneNumber; /** * The details of the attempt. + * [Optional] */ private ApplicantSessionInformation applicantSessionInformation; -} \ No newline at end of file +} diff --git a/src/main/java/com/checkout/identities/entities/Certification.java b/src/main/java/com/checkout/identities/entities/Certification.java new file mode 100644 index 00000000..7080768f --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/Certification.java @@ -0,0 +1,29 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The details of a certification associated with the identity verification. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class Certification { + + /** + * The certification type. + * [Optional] + */ + private CertificationType type; + + /** + * The certification data. The properties returned depend on the certification type. + * [Optional] + */ + private DiatfCertificationData data; + +} diff --git a/src/main/java/com/checkout/identities/entities/CertificationType.java b/src/main/java/com/checkout/identities/entities/CertificationType.java new file mode 100644 index 00000000..b1d6c5ac --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/CertificationType.java @@ -0,0 +1,13 @@ +package com.checkout.identities.entities; + +import com.google.gson.annotations.SerializedName; + +/** + * The certification type. + */ +public enum CertificationType { + + @SerializedName("diatf") + DIATF + +} diff --git a/src/main/java/com/checkout/identities/entities/ClientInformation.java b/src/main/java/com/checkout/identities/entities/ClientInformation.java index 8e1766de..8673edf8 100644 --- a/src/main/java/com/checkout/identities/entities/ClientInformation.java +++ b/src/main/java/com/checkout/identities/entities/ClientInformation.java @@ -1,22 +1,34 @@ package com.checkout.identities.entities; +import com.checkout.common.CountryCode; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +/** + * The applicant's details. + */ @Data -@Builder +@SuperBuilder @NoArgsConstructor @AllArgsConstructor -public final class ClientInformation { +public class ClientInformation { + /** * The applicant's residence country. + * [Optional] + * Standard: ISO 3166-1 alpha-2 country code + * Pattern: ^[A-Z]{2} + * Example: FR */ - private String preSelectedResidenceCountry; + private CountryCode preSelectedResidenceCountry; /** * The language you want to use for the user interface. + * [Optional] + * Format: IETF BCP 47 language tag + * Example: en-US */ 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 index e22cf323..c00f9db4 100644 --- a/src/main/java/com/checkout/identities/entities/DeclaredData.java +++ b/src/main/java/com/checkout/identities/entities/DeclaredData.java @@ -1,18 +1,33 @@ package com.checkout.identities.entities; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; /** - * The personal details provided by the applicant + * The personal details provided by the applicant. */ @Data -@Builder +@SuperBuilder @NoArgsConstructor @AllArgsConstructor -public final class DeclaredData { +public class DeclaredData { + /** + * The applicant's name. + * [Required] + * min 2 characters, max 255 characters + * Example: Hannah Bret + */ private String name; -} \ No newline at end of file + + /** + * The applicant's birth date, as provided by the applicant. This is the declared value, not + * the value extracted from the verified document. + * [Optional] + * Format: date (YYYY-MM-DD) + * Example: 1994-10-15 + */ + private String birthDate; +} diff --git a/src/main/java/com/checkout/identities/entities/DiatfCertificationData.java b/src/main/java/com/checkout/identities/entities/DiatfCertificationData.java new file mode 100644 index 00000000..f3af85d4 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/DiatfCertificationData.java @@ -0,0 +1,36 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The certification data returned for the diatf certification type. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class DiatfCertificationData { + + /** + * The GPG 45 identity profile the verification meets. + * [Optional] + */ + private Gpg45Profile gpg45Profile; + + /** + * The level of confidence in the verified identity. + * [Optional] + */ + private LevelOfConfidence levelOfConfidence; + + /** + * The outcome of the applicant's right to work check. + * [Optional] + * Example: GRANTED + */ + private String rightToWork; + +} diff --git a/src/main/java/com/checkout/identities/entities/DocumentDetails.java b/src/main/java/com/checkout/identities/entities/DocumentDetails.java index f3d828a0..4c74345f 100644 --- a/src/main/java/com/checkout/identities/entities/DocumentDetails.java +++ b/src/main/java/com/checkout/identities/entities/DocumentDetails.java @@ -1,5 +1,6 @@ package com.checkout.identities.entities; +import com.checkout.common.CountryCode; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -14,43 +15,178 @@ @AllArgsConstructor public final class DocumentDetails { + /** + * The type of identity document. + * [Required] + */ private DocumentType documentType; - private String documentIssuingCountry; - + /** + * The country that issued the document. + * [Required] + * Standard: ISO 3166-1 alpha-2 country code + * Pattern: ^[A-Za-z]{2}$ + */ + private CountryCode documentIssuingCountry; + + /** + * The pre-signed URL to the captured image of the front of the document. + * [Required] + * Format: uri + */ private String frontImageSignedUrl; + /** + * The applicant's full name, extracted from the verified document. + * [Required] + * min 2 characters, max 510 characters + */ private String fullName; + /** + * The applicant's birth date, extracted from the verified document. + * [Required] + * Format: date (YYYY-MM-DD) + */ private String birthDate; + /** + * The applicant's first names. + * [Optional] + * min 1 character, max 255 characters + */ private String firstNames; + /** + * The applicant's last name. + * [Optional] + * min 1 character, max 255 characters + */ private String lastName; + /** + * The applicant's last name at birth. + * [Optional] + * min 1 character, max 255 characters + */ private String lastNameAtBirth; + /** + * The applicant's birth place. + * [Optional] + * min 1 character, max 255 characters + */ private String birthPlace; - private String nationality; - + /** + * The applicant's nationality. + * [Optional] + * Standard: ISO 3166-1 alpha-2 country code + * Pattern: ^[A-Za-z]{2}$ + */ + private CountryCode nationality; + + /** + * The applicant's gender. + * [Optional] + */ private Gender gender; + /** + * The applicant's personal number. This depends on the document type. + * [Optional] + * max 255 characters + */ private String personalNumber; + /** + * The tax identification number (TIN) extracted from the document. + * [Optional] + * max 255 characters + */ private String taxIdentificationNumber; + /** + * The document number extracted from the document. + * [Optional] + * max 255 characters + */ private String documentNumber; + /** + * The document expiry date extracted from the document. + * [Optional] + * Format: date (YYYY-MM-DD) + */ private String documentExpiryDate; + /** + * The document issue date extracted from the document. + * [Optional] + * Format: date (YYYY-MM-DD) + */ private String documentIssueDate; + /** + * The document's place of issue extracted from the document. + * [Optional] + * max 255 characters + */ private String documentIssuePlace; + /** + * The machine-readable zone (MRZ) data extracted from the document. + * [Optional] + */ private String documentMrz; + /** + * The address extracted from the document. This is a flat string, not a structured address. + * [Optional] + * max 1000 characters + * Example: 123 Main Street, London, SW1A 1AA + */ + private String address; + + /** + * The date the residence permit was obtained, extracted from the document. + * [Optional] + * Format: date (YYYY-MM-DD) + */ + private String permitObtainingDate; + + /** + * The residence permit expiry date extracted from the document. + * [Optional] + * Format: date (YYYY-MM-DD) + */ + private String permitExpiryDate; + + /** + * The detailed residence permit type extracted from the document. + * [Optional] + * max 255 characters + */ + private String permitTypeDetailed; + + /** + * The residence permit remarks extracted from the document. + * [Optional] + * max 255 characters + */ + private String permitTypeRemarks; + + /** + * The pre-signed URL to the captured image of the back of the document. + * [Optional] + * Format: uri + */ private String backImageSignedUrl; + /** + * The pre-signed URL to the captured image of the signature. + * [Optional] + * Format: uri + */ 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 index 4a92d53f..8cd6abb5 100644 --- a/src/main/java/com/checkout/identities/entities/DocumentType.java +++ b/src/main/java/com/checkout/identities/entities/DocumentType.java @@ -3,9 +3,8 @@ import com.google.gson.annotations.SerializedName; /** - * Enumeration of the type of identity document. + * The type of identity document. */ - public enum DocumentType { @SerializedName("Driving licence") @@ -14,9 +13,19 @@ public enum DocumentType { @SerializedName("ID") ID, + @SerializedName("Other") + OTHER, + @SerializedName("Passport") PASSPORT, @SerializedName("Residence Permit") - RESIDENCE_PERMIT -} \ No newline at end of file + RESIDENCE_PERMIT, + + @SerializedName("Travel Document") + TRAVEL_DOCUMENT, + + @SerializedName("Visa") + VISA + +} diff --git a/src/main/java/com/checkout/identities/entities/Gpg45Profile.java b/src/main/java/com/checkout/identities/entities/Gpg45Profile.java new file mode 100644 index 00000000..16ea691d --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/Gpg45Profile.java @@ -0,0 +1,17 @@ +package com.checkout.identities.entities; + +import com.google.gson.annotations.SerializedName; + +/** + * The GPG 45 identity profile the verification meets. + */ +public enum Gpg45Profile { + + @SerializedName("M1A") + M1A, + @SerializedName("M1C") + M1C, + @SerializedName("H1A") + H1A + +} diff --git a/src/main/java/com/checkout/identities/entities/IdentityDeclaredData.java b/src/main/java/com/checkout/identities/entities/IdentityDeclaredData.java new file mode 100644 index 00000000..903b50d3 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/IdentityDeclaredData.java @@ -0,0 +1,40 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * The personal details provided by the applicant for an identity verification. + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class IdentityDeclaredData extends DeclaredData { + + /** + * The applicant's mobile phone number, if sharing the attempt URL via SMS. + * [Optional] + */ + private PhoneNumber phoneNumber; + + /** + * The applicant's email address. + * [Optional] + * Format: email + * Example: hannah.bret@example.com + */ + private String email; + + /** + * The applicant's address. + * [Optional] + */ + private IdvAddress address; +} diff --git a/src/main/java/com/checkout/identities/entities/IdentityVerificationClientInformation.java b/src/main/java/com/checkout/identities/entities/IdentityVerificationClientInformation.java new file mode 100644 index 00000000..5de7ea7c --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/IdentityVerificationClientInformation.java @@ -0,0 +1,36 @@ +package com.checkout.identities.entities; + +import com.checkout.common.CountryCode; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +/** + * The applicant's details for an identity verification attempt. + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class IdentityVerificationClientInformation extends ClientInformation { + + /** + * The country that issued the applicant's identity document. + * [Optional] + * Standard: ISO 3166-1 alpha-2 country code + * Pattern: ^[A-Z]{2} + * Example: FR + */ + private CountryCode preSelectedDocumentIssuingCountry; + + /** + * The type of identity document the applicant uses for the attempt. + * [Optional] + */ + private DocumentType preSelectedDocumentType; +} diff --git a/src/main/java/com/checkout/identities/entities/IdvAddress.java b/src/main/java/com/checkout/identities/entities/IdvAddress.java new file mode 100644 index 00000000..9eace7de --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/IdvAddress.java @@ -0,0 +1,61 @@ +package com.checkout.identities.entities; + +import com.checkout.common.CountryCode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The applicant's address. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class IdvAddress { + + /** + * The first line of the address. + * [Optional] + * max 250 characters + */ + private String addressLine1; + + /** + * The second line of the address. + * [Optional] + * max 250 characters + */ + private String addressLine2; + + /** + * The city or town. + * [Optional] + * max 50 characters + */ + private String city; + + /** + * The state, county, or province. + * [Optional] + * max 50 characters + */ + private String state; + + /** + * The postal or ZIP code. + * [Optional] + * max 50 characters + */ + private String zip; + + /** + * The two-letter ISO country code of the address. + * [Optional] + * Standard: ISO 3166-1 alpha-2 country code + * max 2 characters + */ + private CountryCode country; + +} diff --git a/src/main/java/com/checkout/identities/entities/InitialDevice.java b/src/main/java/com/checkout/identities/entities/InitialDevice.java new file mode 100644 index 00000000..bb9d92f9 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/InitialDevice.java @@ -0,0 +1,15 @@ +package com.checkout.identities.entities; + +import com.google.gson.annotations.SerializedName; + +/** + * The type of device the applicant used to start the attempt. + */ +public enum InitialDevice { + + @SerializedName("desktop") + DESKTOP, + @SerializedName("mobile") + MOBILE + +} diff --git a/src/main/java/com/checkout/identities/entities/LevelOfConfidence.java b/src/main/java/com/checkout/identities/entities/LevelOfConfidence.java new file mode 100644 index 00000000..ae14c525 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/LevelOfConfidence.java @@ -0,0 +1,15 @@ +package com.checkout.identities.entities; + +import com.google.gson.annotations.SerializedName; + +/** + * The level of confidence in the verified identity. + */ +public enum LevelOfConfidence { + + @SerializedName("medium") + MEDIUM, + @SerializedName("high") + HIGH + +} diff --git a/src/main/java/com/checkout/identities/entities/PhoneNumber.java b/src/main/java/com/checkout/identities/entities/PhoneNumber.java new file mode 100644 index 00000000..e5f21ff2 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/PhoneNumber.java @@ -0,0 +1,33 @@ +package com.checkout.identities.entities; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The applicant's mobile phone number, if sharing the attempt URL via SMS. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class PhoneNumber { + + /** + * The international phone country code. This is a dialling prefix, not an ISO country code. + * [Required] + * Pattern: ^\+(\d+)$ + * Example: +33 + */ + private String countryCode; + + /** + * The applicant's mobile number, without the country code. + * [Required] + * Pattern: ^\d{1,14}$ + * Example: 5555550102 + */ + private String number; + +} diff --git a/src/main/java/com/checkout/identities/entities/RiskLabel.java b/src/main/java/com/checkout/identities/entities/RiskLabel.java new file mode 100644 index 00000000..c00e5c25 --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/RiskLabel.java @@ -0,0 +1,17 @@ +package com.checkout.identities.entities; + +import com.google.gson.annotations.SerializedName; + +/** + * A code that provides more information about a risk associated with the verification. + */ +public enum RiskLabel { + + @SerializedName("multiple_faces_detected") + MULTIPLE_FACES_DETECTED, + @SerializedName("mcc_not_confident") + MCC_NOT_CONFIDENT, + @SerializedName("risky_document_format") + RISKY_DOCUMENT_FORMAT + +} diff --git a/src/main/java/com/checkout/identities/entities/SelectedDocument.java b/src/main/java/com/checkout/identities/entities/SelectedDocument.java new file mode 100644 index 00000000..e48f2bff --- /dev/null +++ b/src/main/java/com/checkout/identities/entities/SelectedDocument.java @@ -0,0 +1,32 @@ +package com.checkout.identities.entities; + +import com.checkout.common.CountryCode; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The details of the document the applicant selected. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class SelectedDocument { + + /** + * The country that issued the selected document. + * [Optional] + * Standard: ISO 3166-1 alpha-2 country code + * Pattern: ^[A-Za-z]{2}$ + */ + private CountryCode country; + + /** + * The type of identity document. + * [Optional] + */ + private DocumentType documentType; + +} diff --git a/src/main/java/com/checkout/identities/entities/VerifiedIdentity.java b/src/main/java/com/checkout/identities/entities/VerifiedIdentity.java index f4a81aeb..32ba3c29 100644 --- a/src/main/java/com/checkout/identities/entities/VerifiedIdentity.java +++ b/src/main/java/com/checkout/identities/entities/VerifiedIdentity.java @@ -1,31 +1,75 @@ package com.checkout.identities.entities; +import com.checkout.common.CountryCode; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** - * Verified identity details + * The details of the applicant's verified identity, extracted from the verified document. */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public final class VerifiedIdentity { + + /** + * The applicant's full name. + * [Required] + * min 2 characters, max 510 characters + */ private String fullName; + /** + * The applicant's birth date, extracted from the verified document. This is the verified + * value, not the value the applicant declared. + * [Required] + * Format: date (YYYY-MM-DD) + */ private String birthDate; + /** + * The applicant's first names. + * [Optional] + * min 1 character, max 255 characters + */ private String firstNames; + /** + * The applicant's last name. + * [Optional] + * min 1 character, max 255 characters + */ private String lastName; + /** + * The applicant's last name at birth. + * [Optional] + * min 1 character, max 255 characters + */ private String lastNameAtBirth; + /** + * The applicant's birth place. + * [Optional] + * min 1 character, max 255 characters + */ private String birthPlace; - private String nationality; + /** + * The applicant's nationality. + * [Optional] + * Standard: ISO 3166-1 alpha-2 country code + * Pattern: ^[A-Za-z]{2}$ + */ + private CountryCode nationality; + /** + * The applicant's gender. + * [Optional] + */ 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 index fa0a0b4d..b98f5d10 100644 --- a/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClient.java +++ b/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClient.java @@ -8,6 +8,8 @@ import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptsResponse; import com.checkout.identities.faceauthentications.responses.FaceAuthenticationResponse; +import com.checkout.identities.entities.AttemptsQueryFilter; + import java.util.concurrent.CompletableFuture; /** @@ -56,6 +58,15 @@ public interface FaceAuthenticationClient { */ CompletableFuture getFaceAuthenticationAttempts(String faceAuthenticationId); + /** + * Retrieve a page of face authentication attempts + * + * @param faceAuthenticationId the face authentication ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link FaceAuthenticationAttemptsResponse} + */ + CompletableFuture getFaceAuthenticationAttempts(String faceAuthenticationId, AttemptsQueryFilter queryFilter); + /** * Retrieves a specific attempt for a face authentication. * @@ -118,6 +129,15 @@ public interface FaceAuthenticationClient { */ FaceAuthenticationAttemptsResponse getFaceAuthenticationAttemptsSync(String faceAuthenticationId); + /** + * Retrieve a page of face authentication attempts + * + * @param faceAuthenticationId the face authentication ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link FaceAuthenticationAttemptsResponse} + */ + FaceAuthenticationAttemptsResponse getFaceAuthenticationAttemptsSync(String faceAuthenticationId, AttemptsQueryFilter queryFilter); + /** * Retrieves a specific attempt for a face authentication. * diff --git a/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImpl.java b/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImpl.java index 33144a92..0c1234fb 100644 --- a/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImpl.java +++ b/src/main/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImpl.java @@ -12,6 +12,8 @@ import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptsResponse; import com.checkout.identities.faceauthentications.responses.FaceAuthenticationResponse; +import com.checkout.identities.entities.AttemptsQueryFilter; + import java.util.concurrent.CompletableFuture; import static com.checkout.common.CheckoutUtils.validateParams; @@ -99,6 +101,21 @@ public CompletableFuture getFaceAuthenticati sdkAuthorization(), FaceAuthenticationAttemptsResponse.class); } + /** + * Retrieve a page of face authentication attempts + * + * @param faceAuthenticationId the face authentication ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link FaceAuthenticationAttemptsResponse} + */ + @Override + public CompletableFuture getFaceAuthenticationAttempts( + final String faceAuthenticationId, final AttemptsQueryFilter queryFilter) { + validateParams("faceAuthenticationId", faceAuthenticationId); + return apiClient.queryAsync(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ATTEMPTS_PATH), + sdkAuthorization(), queryFilter, FaceAuthenticationAttemptsResponse.class); + } + /** * Retrieves a specific attempt for a face authentication. * @@ -200,6 +217,21 @@ public FaceAuthenticationAttemptsResponse getFaceAuthenticationAttemptsSync(fina sdkAuthorization(), FaceAuthenticationAttemptsResponse.class); } + /** + * Retrieve a page of face authentication attempts + * + * @param faceAuthenticationId the face authentication ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link FaceAuthenticationAttemptsResponse} + */ + @Override + public FaceAuthenticationAttemptsResponse getFaceAuthenticationAttemptsSync( + final String faceAuthenticationId, final AttemptsQueryFilter queryFilter) { + validateParams("faceAuthenticationId", faceAuthenticationId); + return apiClient.query(buildPath(FACE_AUTHENTICATIONS_PATH, faceAuthenticationId, ATTEMPTS_PATH), + sdkAuthorization(), queryFilter, FaceAuthenticationAttemptsResponse.class); + } + /** * Retrieves a specific attempt for a face authentication. * diff --git a/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationAttemptRequest.java b/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationAttemptRequest.java index 4f3150d3..a6916cd2 100644 --- a/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationAttemptRequest.java +++ b/src/main/java/com/checkout/identities/faceauthentications/requests/FaceAuthenticationAttemptRequest.java @@ -1,6 +1,7 @@ package com.checkout.identities.faceauthentications.requests; import com.checkout.identities.entities.ClientInformation; +import com.checkout.identities.entities.PhoneNumber; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -21,6 +22,12 @@ public final class FaceAuthenticationAttemptRequest { */ private String redirectUrl; + /** + * The applicant's mobile phone number, if sharing the attempt URL via SMS. + * [Optional] + */ + private PhoneNumber phoneNumber; + /** * The applicant's details. * [Optional] diff --git a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java index 311b9584..417e8da8 100644 --- a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java +++ b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java @@ -1,22 +1,27 @@ package com.checkout.identities.faceauthentications.responses; import com.checkout.identities.entities.BaseAttemptResponse; +import com.checkout.identities.entities.ClientInformation; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; /** * Face authentication attempt response */ @Data @EqualsAndHashCode(callSuper = true) -@Builder +@ToString(callSuper = true) +@NoArgsConstructor @AllArgsConstructor public final 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 + /** + * The applicant's details. + * [Optional] + */ + private ClientInformation clientInformation; +} diff --git a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptStatus.java b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptStatus.java index 97eb4149..b1e3a8dd 100644 --- a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptStatus.java +++ b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptStatus.java @@ -29,6 +29,9 @@ public enum FaceAuthenticationAttemptStatus { PENDING_REDIRECTION, @SerializedName("capture_refused") - CAPTURE_REFUSED + CAPTURE_REFUSED, + + @SerializedName("terminated") + TERMINATED } \ 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 index e95615a1..d4a00ca1 100644 --- a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationResponse.java +++ b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationResponse.java @@ -1,5 +1,7 @@ package com.checkout.identities.faceauthentications.responses; +import com.checkout.identities.entities.RiskLabel; + import com.checkout.identities.entities.Face; import lombok.AllArgsConstructor; import lombok.Builder; @@ -34,7 +36,7 @@ public final class FaceAuthenticationResponse extends BaseIdentityResponseStatus /** * One or more codes that provide more information about risks associated with the verification. */ - private List riskLabels; + private List riskLabels; /** * The details of the image of the applicant's face extracted from the video. diff --git a/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClient.java b/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClient.java index a4631c3f..34989729 100644 --- a/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClient.java +++ b/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClient.java @@ -3,10 +3,14 @@ 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.IdDocumentVerificationAttemptAssetsResponse; import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptsResponse; import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationReportResponse; import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationResponse; +import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; + import java.util.concurrent.CompletableFuture; /** @@ -55,6 +59,15 @@ public interface IdDocumentVerificationClient { */ CompletableFuture getIdDocumentVerificationAttempts(String idDocumentVerificationId); + /** + * Retrieve a page of ID document verification attempts + * + * @param idDocumentVerificationId the ID document verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptsResponse} + */ + CompletableFuture getIdDocumentVerificationAttempts(String idDocumentVerificationId, AttemptsQueryFilter queryFilter); + /** * Retrieve a specific ID document verification attempt * @@ -72,6 +85,17 @@ public interface IdDocumentVerificationClient { */ CompletableFuture getIdDocumentVerificationReport(String idDocumentVerificationId); + /** + * Retrieve the assets (the front and back images of the document) uploaded for an ID document verification attempt. + * Beta. + * + * @param idDocumentVerificationId the ID document verification ID + * @param attemptId the attempt ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptAssetsResponse} + */ + CompletableFuture getIdDocumentVerificationAttemptAssets(String idDocumentVerificationId, String attemptId, AttemptAssetsQueryFilter queryFilter); + // Synchronous methods /** @@ -115,6 +139,15 @@ public interface IdDocumentVerificationClient { */ IdDocumentVerificationAttemptsResponse getIdDocumentVerificationAttemptsSync(String idDocumentVerificationId); + /** + * Retrieve a page of ID document verification attempts + * + * @param idDocumentVerificationId the ID document verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link IdDocumentVerificationAttemptsResponse} + */ + IdDocumentVerificationAttemptsResponse getIdDocumentVerificationAttemptsSync(String idDocumentVerificationId, AttemptsQueryFilter queryFilter); + /** * Retrieve a specific ID document verification attempt * @@ -131,4 +164,15 @@ public interface IdDocumentVerificationClient { * @return the {@link IdDocumentVerificationReportResponse} */ IdDocumentVerificationReportResponse getIdDocumentVerificationReportSync(String idDocumentVerificationId); + + /** + * Retrieve the assets (the front and back images of the document) uploaded for an ID document verification attempt. + * Beta. + * + * @param idDocumentVerificationId the ID document verification ID + * @param attemptId the attempt ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link IdDocumentVerificationAttemptAssetsResponse} + */ + IdDocumentVerificationAttemptAssetsResponse getIdDocumentVerificationAttemptAssetsSync(String idDocumentVerificationId, String attemptId, AttemptAssetsQueryFilter queryFilter); } \ 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 index b410c0dd..9b8ca660 100644 --- a/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImpl.java +++ b/src/main/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImpl.java @@ -7,10 +7,14 @@ 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.IdDocumentVerificationAttemptAssetsResponse; import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptsResponse; import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationReportResponse; import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationResponse; +import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; + import java.util.concurrent.CompletableFuture; import static com.checkout.common.CheckoutUtils.validateParams; @@ -23,6 +27,7 @@ public class IdDocumentVerificationClientImpl extends AbstractClient implements 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 ASSETS_PATH = "assets"; private static final String PDF_REPORT_PATH = "pdf-report"; public IdDocumentVerificationClientImpl(final ApiClient apiClient, final CheckoutConfiguration configuration) { @@ -103,6 +108,21 @@ public CompletableFuture getIdDocumentVe sdkAuthorization(), IdDocumentVerificationAttemptsResponse.class); } + /** + * Retrieve a page of ID document verification attempts + * + * @param idDocumentVerificationId the ID document verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptsResponse} + */ + @Override + public CompletableFuture getIdDocumentVerificationAttempts( + final String idDocumentVerificationId, final AttemptsQueryFilter queryFilter) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.queryAsync(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), queryFilter, IdDocumentVerificationAttemptsResponse.class); + } + /** * Retrieve a specific ID document verification attempt * @@ -132,6 +152,22 @@ public CompletableFuture getIdDocumentVeri sdkAuthorization(), IdDocumentVerificationReportResponse.class); } + /** + * Retrieve the assets (the front and back images of the document) uploaded for an ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param attemptId the attempt ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link IdDocumentVerificationAttemptAssetsResponse} + */ + @Override + public CompletableFuture getIdDocumentVerificationAttemptAssets( + final String idDocumentVerificationId, final String attemptId, final AttemptAssetsQueryFilter queryFilter) { + validateParams("idDocumentVerificationId", idDocumentVerificationId, "attemptId", attemptId); + return apiClient.queryAsync(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH, attemptId, ASSETS_PATH), + sdkAuthorization(), queryFilter, IdDocumentVerificationAttemptAssetsResponse.class); + } + // Synchronous methods /** @@ -205,6 +241,21 @@ public IdDocumentVerificationAttemptsResponse getIdDocumentVerificationAttemptsS sdkAuthorization(), IdDocumentVerificationAttemptsResponse.class); } + /** + * Retrieve a page of ID document verification attempts + * + * @param idDocumentVerificationId the ID document verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link IdDocumentVerificationAttemptsResponse} + */ + @Override + public IdDocumentVerificationAttemptsResponse getIdDocumentVerificationAttemptsSync( + final String idDocumentVerificationId, final AttemptsQueryFilter queryFilter) { + validateParams("idDocumentVerificationId", idDocumentVerificationId); + return apiClient.query(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), queryFilter, IdDocumentVerificationAttemptsResponse.class); + } + /** * Retrieve a specific ID document verification attempt * @@ -232,4 +283,20 @@ public IdDocumentVerificationReportResponse getIdDocumentVerificationReportSync( return apiClient.get(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, PDF_REPORT_PATH), sdkAuthorization(), IdDocumentVerificationReportResponse.class); } + + /** + * Retrieve the assets (the front and back images of the document) uploaded for an ID document verification attempt + * + * @param idDocumentVerificationId the ID document verification ID + * @param attemptId the attempt ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link IdDocumentVerificationAttemptAssetsResponse} + */ + @Override + public IdDocumentVerificationAttemptAssetsResponse getIdDocumentVerificationAttemptAssetsSync( + final String idDocumentVerificationId, final String attemptId, final AttemptAssetsQueryFilter queryFilter) { + validateParams("idDocumentVerificationId", idDocumentVerificationId, "attemptId", attemptId); + return apiClient.query(buildPath(ID_DOCUMENT_VERIFICATIONS_PATH, idDocumentVerificationId, ATTEMPTS_PATH, attemptId, ASSETS_PATH), + sdkAuthorization(), queryFilter, IdDocumentVerificationAttemptAssetsResponse.class); + } } \ No newline at end of file diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAsset.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAsset.java new file mode 100644 index 00000000..103cb914 --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAsset.java @@ -0,0 +1,33 @@ +package com.checkout.identities.iddocumentverification.responses; + +import com.checkout.identities.entities.AttemptAssetLinks; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * An asset (the front or back image of the document) uploaded for an ID document verification + * attempt. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class IdDocumentVerificationAttemptAsset { + + /** + * The type of asset. + * [Required] + */ + private IdDocumentVerificationAttemptAssetType type; + + /** + * The links related to the asset. + * [Required] + */ + @SerializedName("_links") + private AttemptAssetLinks links; + +} diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAssetType.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAssetType.java new file mode 100644 index 00000000..eb3fc216 --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAssetType.java @@ -0,0 +1,15 @@ +package com.checkout.identities.iddocumentverification.responses; + +import com.google.gson.annotations.SerializedName; + +/** + * The type of asset uploaded for an ID document verification attempt. + */ +public enum IdDocumentVerificationAttemptAssetType { + + @SerializedName("document_front_image") + DOCUMENT_FRONT_IMAGE, + @SerializedName("document_back_image") + DOCUMENT_BACK_IMAGE + +} diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAssetsResponse.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAssetsResponse.java new file mode 100644 index 00000000..f6158df1 --- /dev/null +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationAttemptAssetsResponse.java @@ -0,0 +1,45 @@ +package com.checkout.identities.iddocumentverification.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; + +/** + * The paginated assets uploaded for an ID document verification attempt. + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class IdDocumentVerificationAttemptAssetsResponse extends Resource { + + /** + * The total number of assets. + * [Required] + */ + private Integer totalCount; + + /** + * The number of assets skipped. + * [Required] + */ + private Integer skip; + + /** + * The maximum number of assets returned. + * [Required] + */ + private Integer limit; + + /** + * The list of assets for the current page. + * [Required] + */ + private List data; +} diff --git a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationReportResponse.java b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationReportResponse.java index 01814c17..1750e685 100644 --- a/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationReportResponse.java +++ b/src/main/java/com/checkout/identities/iddocumentverification/responses/IdDocumentVerificationReportResponse.java @@ -11,5 +11,10 @@ @EqualsAndHashCode(callSuper = true) public final class IdDocumentVerificationReportResponse extends Resource { - private String signedUrl; + /** + * The pre-signed URL to the PDF report. + * [Required] + * Format: uri + */ + private String pdfReport; } \ 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 index 8cbcd482..219841da 100644 --- a/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClient.java +++ b/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClient.java @@ -10,6 +10,8 @@ import com.checkout.identities.identityverification.responses.IdentityVerificationReportResponse; import com.checkout.identities.identityverification.responses.IdentityVerificationResponse; +import com.checkout.identities.entities.AttemptsQueryFilter; + import java.util.concurrent.CompletableFuture; /** @@ -59,6 +61,15 @@ public interface IdentityVerificationClient { */ CompletableFuture getIdentityVerificationAttempts(String identityVerificationId); + /** + * Retrieve a page of identity verification attempts + * + * @param identityVerificationId the identity verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationAttemptsResponse} + */ + CompletableFuture getIdentityVerificationAttempts(String identityVerificationId, AttemptsQueryFilter queryFilter); + /** * Create an identity verification attempt * @@ -137,6 +148,15 @@ public interface IdentityVerificationClient { */ IdentityVerificationAttemptsResponse getIdentityVerificationAttemptsSync(String identityVerificationId); + /** + * Retrieve a page of identity verification attempts + * + * @param identityVerificationId the identity verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link IdentityVerificationAttemptsResponse} + */ + IdentityVerificationAttemptsResponse getIdentityVerificationAttemptsSync(String identityVerificationId, AttemptsQueryFilter queryFilter); + /** * Create an identity verification attempt * diff --git a/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClientImpl.java b/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClientImpl.java index 42a9145f..5e0b1a38 100644 --- a/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClientImpl.java +++ b/src/main/java/com/checkout/identities/identityverification/IdentityVerificationClientImpl.java @@ -14,6 +14,8 @@ import com.checkout.identities.identityverification.responses.IdentityVerificationReportResponse; import com.checkout.identities.identityverification.responses.IdentityVerificationResponse; +import com.checkout.identities.entities.AttemptsQueryFilter; + import java.util.concurrent.CompletableFuture; import static com.checkout.common.CheckoutUtils.validateParams; @@ -124,6 +126,21 @@ public CompletableFuture getIdentityVerifi sdkAuthorization(), IdentityVerificationAttemptsResponse.class); } + /** + * Retrieve a page of identity verification attempts + * + * @param identityVerificationId the identity verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return a {@link CompletableFuture} containing the {@link IdentityVerificationAttemptsResponse} + */ + @Override + public CompletableFuture getIdentityVerificationAttempts( + final String identityVerificationId, final AttemptsQueryFilter queryFilter) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.queryAsync(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), queryFilter, IdentityVerificationAttemptsResponse.class); + } + /** * Retrieve a specific identity verification attempt * @@ -238,6 +255,21 @@ public IdentityVerificationAttemptsResponse getIdentityVerificationAttemptsSync( sdkAuthorization(), IdentityVerificationAttemptsResponse.class); } + /** + * Retrieve a page of identity verification attempts + * + * @param identityVerificationId the identity verification ID + * @param queryFilter the pagination query parameters (skip and limit) + * @return the {@link IdentityVerificationAttemptsResponse} + */ + @Override + public IdentityVerificationAttemptsResponse getIdentityVerificationAttemptsSync( + final String identityVerificationId, final AttemptsQueryFilter queryFilter) { + validateParams("identityVerificationId", identityVerificationId); + return apiClient.query(buildPath(IDENTITY_VERIFICATIONS_PATH, identityVerificationId, ATTEMPTS_PATH), + sdkAuthorization(), queryFilter, IdentityVerificationAttemptsResponse.class); + } + /** * Create an identity verification attempt * diff --git a/src/main/java/com/checkout/identities/identityverification/requests/CreateAndOpenIdentityVerificationRequest.java b/src/main/java/com/checkout/identities/identityverification/requests/CreateAndOpenIdentityVerificationRequest.java index 39721237..d33ec3a6 100644 --- a/src/main/java/com/checkout/identities/identityverification/requests/CreateAndOpenIdentityVerificationRequest.java +++ b/src/main/java/com/checkout/identities/identityverification/requests/CreateAndOpenIdentityVerificationRequest.java @@ -1,6 +1,6 @@ package com.checkout.identities.identityverification.requests; -import com.checkout.identities.entities.DeclaredData; +import com.checkout.identities.entities.IdentityDeclaredData; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -19,7 +19,7 @@ public final class CreateAndOpenIdentityVerificationRequest { * The personal details provided by the applicant. * [Required] */ - private DeclaredData declaredData; + private IdentityDeclaredData declaredData; /** * The URL to redirect the applicant to after the attempt. diff --git a/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationAttemptRequest.java b/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationAttemptRequest.java index b81621d9..2207871a 100644 --- a/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationAttemptRequest.java +++ b/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationAttemptRequest.java @@ -1,6 +1,7 @@ package com.checkout.identities.identityverification.requests; -import com.checkout.identities.entities.ClientInformation; +import com.checkout.identities.entities.IdentityVerificationClientInformation; +import com.checkout.identities.entities.PhoneNumber; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -18,11 +19,19 @@ public final class IdentityVerificationAttemptRequest { /** * The URL to redirect the applicant to after the attempt. * [Required] + * Format: uri */ private String redirectUrl; + /** + * The applicant's mobile phone number, if sharing the attempt URL via SMS. + * [Optional] + */ + private PhoneNumber phoneNumber; + /** * The applicant's details. + * [Optional] */ - private ClientInformation clientInformation; -} \ No newline at end of file + private IdentityVerificationClientInformation clientInformation; +} diff --git a/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationRequest.java b/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationRequest.java index 3e1b7548..33dd9799 100644 --- a/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationRequest.java +++ b/src/main/java/com/checkout/identities/identityverification/requests/IdentityVerificationRequest.java @@ -1,6 +1,6 @@ package com.checkout.identities.identityverification.requests; -import com.checkout.identities.entities.DeclaredData; +import com.checkout.identities.entities.IdentityDeclaredData; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -25,7 +25,7 @@ public final class IdentityVerificationRequest { * The personal details provided by the applicant. * [Required] */ - private DeclaredData declaredData; + private IdentityDeclaredData declaredData; /** * Your configuration ID. diff --git a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptResponse.java b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptResponse.java index b67c4db8..5d3cdd54 100644 --- a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptResponse.java +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptResponse.java @@ -1,10 +1,22 @@ package com.checkout.identities.identityverification.responses; import com.checkout.identities.entities.BaseAttemptResponse; +import com.checkout.identities.entities.IdentityVerificationClientInformation; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; /** * Response for identity verification attempt operations */ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) public final class IdentityVerificationAttemptResponse extends BaseAttemptResponse { - // All fields are inherited from BaseAttemptResponse and BaseIdentityResponseStatus -} \ No newline at end of file + + /** + * The applicant's details. + * [Optional] + */ + private IdentityVerificationClientInformation clientInformation; +} diff --git a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptStatus.java b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptStatus.java index 2a4df959..c88bf10f 100644 --- a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptStatus.java +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationAttemptStatus.java @@ -29,5 +29,8 @@ public enum IdentityVerificationAttemptStatus { PENDING_REDIRECTION, @SerializedName("capture_refused") - CAPTURE_REFUSED + CAPTURE_REFUSED, + + @SerializedName("terminated") + TERMINATED } \ 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 index 7090c307..babb336e 100644 --- a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationReportResponse.java +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationReportResponse.java @@ -18,8 +18,10 @@ public final class IdentityVerificationReportResponse extends Resource { /** - * The pre-signed URL to the captured image of the document. + * The pre-signed URL to the PDF report. + * [Required] + * Format: uri */ - private String signedUrl; + private String pdfReport; } \ 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 index 19200154..e81980c2 100644 --- a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationResponse.java +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationResponse.java @@ -4,7 +4,9 @@ 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 com.checkout.identities.entities.Certification; +import com.checkout.identities.entities.IdentityDeclaredData; +import com.checkout.identities.entities.RiskLabel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -36,7 +38,7 @@ public final class IdentityVerificationResponse extends BaseIdentityResponseStat /** * The personal details provided by the applicant. */ - private DeclaredData declaredData; + private IdentityDeclaredData declaredData; /** * The URL to redirect the applicant to after the attempt. @@ -46,8 +48,21 @@ public final class IdentityVerificationResponse extends BaseIdentityResponseStat /** * One or more codes that provide more information about risks associated with the verification. + * [Required] */ - private List riskLabels; + private List riskLabels; + + /** + * The certifications associated with the identity verification. + * [Optional] + */ + private List certifications; + + /** + * The version of the verification policy applied. Only returned for certified verifications. + * [Optional] + */ + private String verificationPolicyVersion; /** * The details of the applicant's identity documents. diff --git a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationStatus.java b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationStatus.java index dedd6a1b..19940796 100644 --- a/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationStatus.java +++ b/src/main/java/com/checkout/identities/identityverification/responses/IdentityVerificationStatus.java @@ -25,6 +25,9 @@ public enum IdentityVerificationStatus { @SerializedName("checks_in_progress") CHECKS_IN_PROGRESS, + @SerializedName("created") + CREATED, + /** * Identity verification declined */ diff --git a/src/main/java/com/checkout/issuing/IssuingClient.java b/src/main/java/com/checkout/issuing/IssuingClient.java index d9b1eae8..4f542b22 100644 --- a/src/main/java/com/checkout/issuing/IssuingClient.java +++ b/src/main/java/com/checkout/issuing/IssuingClient.java @@ -26,6 +26,7 @@ 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.requests.update.CardUpdateHeaders; import com.checkout.issuing.cards.responses.update.UpdateCardResponse; import com.checkout.issuing.controls.requests.create.CardControlRequest; import com.checkout.issuing.controls.requests.query.CardControlsQuery; @@ -154,6 +155,19 @@ CompletableFuture simulateReversal( CompletableFuture updateCard(final String cardId, final UpdateCardRequest updateCardRequest); + /** + * Update a card's details, sending the optional return-encrypted-cvv and Encryption-Key + * headers. Set return-encrypted-cvv to true to receive the card's encrypted CVV in the + * response; the API returns a 422 with error code encryption_key_required if Encryption-Key + * is not also supplied. + * + * @param cardId the card ID + * @param updateCardRequest the card fields to update + * @param headers the optional return-encrypted-cvv and Encryption-Key headers + * @return a {@link CompletableFuture} containing the {@link UpdateCardResponse} + */ + CompletableFuture updateCard(String cardId, UpdateCardRequest updateCardRequest, CardUpdateHeaders headers); + CompletableFuture renewCard(final String cardId, final RenewCardRequest renewCardRequest); CompletableFuture createDispute(final CreateDisputeRequest createDisputeRequest, String idempotencyKey); @@ -283,6 +297,19 @@ CardAuthorizationReversalResponse simulateReversalSync( UpdateCardResponse updateCardSync(String cardId, UpdateCardRequest updateCardRequest); + /** + * Update a card's details, sending the optional return-encrypted-cvv and Encryption-Key + * headers. Set return-encrypted-cvv to true to receive the card's encrypted CVV in the + * response; the API returns a 422 with error code encryption_key_required if Encryption-Key + * is not also supplied. + * + * @param cardId the card ID + * @param updateCardRequest the card fields to update + * @param headers the optional return-encrypted-cvv and Encryption-Key headers + * @return the {@link UpdateCardResponse} + */ + UpdateCardResponse updateCardSync(String cardId, UpdateCardRequest updateCardRequest, CardUpdateHeaders headers); + RenewCardResponse renewCardSync(String cardId, RenewCardRequest renewCardRequest); DisputeResponse createDisputeSync(CreateDisputeRequest createDisputeRequest, String idempotencyKey); diff --git a/src/main/java/com/checkout/issuing/IssuingClientImpl.java b/src/main/java/com/checkout/issuing/IssuingClientImpl.java index f1ccdc3f..b1377873 100644 --- a/src/main/java/com/checkout/issuing/IssuingClientImpl.java +++ b/src/main/java/com/checkout/issuing/IssuingClientImpl.java @@ -30,6 +30,7 @@ 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.requests.update.CardUpdateHeaders; import com.checkout.issuing.cards.responses.update.UpdateCardResponse; import com.checkout.issuing.controls.requests.create.CardControlRequest; import com.checkout.issuing.controls.requests.query.CardControlsQuery; @@ -577,6 +578,20 @@ public CompletableFuture updateCard(final String cardId, fin ); } + @Override + public CompletableFuture updateCard(final String cardId, final UpdateCardRequest updateCardRequest, + final CardUpdateHeaders headers) { + validateParams("cardId", cardId, "updateCardRequest", updateCardRequest); + return apiClient.patchAsync( + buildPath(ISSUING_PATH, CARDS_PATH, cardId), + sdkAuthorization(), + UpdateCardResponse.class, + updateCardRequest, + null, + headers + ); + } + @Override public CompletableFuture renewCard(final String cardId, final RenewCardRequest renewCardRequest) { validateParams("cardId", cardId, "renewCardRequest", renewCardRequest); @@ -1135,6 +1150,20 @@ public UpdateCardResponse updateCardSync(final String cardId, final UpdateCardRe ); } + @Override + public UpdateCardResponse updateCardSync(final String cardId, final UpdateCardRequest updateCardRequest, + final CardUpdateHeaders headers) { + validateParams("cardId", cardId, "updateCardRequest", updateCardRequest); + return apiClient.patch( + buildPath(ISSUING_PATH, CARDS_PATH, cardId), + sdkAuthorization(), + UpdateCardResponse.class, + updateCardRequest, + null, + headers + ); + } + @Override public RenewCardResponse renewCardSync(final String cardId, final RenewCardRequest renewCardRequest) { validateParams("cardId", cardId, "renewCardRequest", renewCardRequest); diff --git a/src/main/java/com/checkout/issuing/cards/requests/create/CardRequest.java b/src/main/java/com/checkout/issuing/cards/requests/create/CardRequest.java index 58c75869..d1a16caa 100644 --- a/src/main/java/com/checkout/issuing/cards/requests/create/CardRequest.java +++ b/src/main/java/com/checkout/issuing/cards/requests/create/CardRequest.java @@ -9,12 +9,31 @@ @Data public abstract class CardRequest { + /** + * The card type. + * [Required] + */ private final CardType type; + /** + * The cardholder's unique identifier. + * [Required] + * Pattern: ^crh_[a-z0-9]{26}$ + * min 30 characters, max 30 characters + */ private String cardholderId; + /** + * The duration of time during which the card will accept incoming transactions. + * [Optional] + */ private CardLifetime lifetime; + /** + * Your reference. + * [Optional] + * max 256 characters + */ private String reference; /** @@ -26,30 +45,47 @@ public abstract class CardRequest { private IssuingCardMetadata metadata; /** - * Date for the card to be automatically revoked. Must be after the current date. + * Date scheduling the card's automatic revocation. *

* [Optional] *

- * Format: yyyy-MM-dd + * Format: date (YYYY-MM-DD, time is midnight UTC) + * Example: 2027-03-12 */ private LocalDate revocationDate; /** - * ISO 8601 date scheduling the card's activation. Two formats are supported: - * date only (YYYY-MM-DD, treated as midnight UTC), or date with round hour - * (YYYY-MM-DDTHH:mmZ in UTC, or YYYY-MM-DDTHH:mm±HH:mm with offset). Only round hours are - * allowed when a time is provided (HH:00). The value must be at least the next round hour - * after the request time. + * Date scheduling the card's first activation. Only applies to the initial activation of a + * card. Two formats are supported: date only (YYYY-MM-DD, treated as midnight UTC), or date + * with round hour (YYYY-MM-DDTHH:mmZ in UTC, or YYYY-MM-DDTHH:mm+HH:mm with offset). Only + * round hours are allowed when a time is provided (HH:00). The value must be at least the next + * round hour after the request time. *

* [Optional] *

+ * Example: 2026-06-01T10:00Z */ - private String activationDate; + private String scheduledActivationDate; + /** + * The card product's unique identifier. Required if the entity has more than one card + * product. + * [Required] + */ private String cardProductId; + /** + * The name to display on the card. + * [Optional] + * Pattern: ^[0-9a-zA-Z.\- ]{2,26}$ + * min 2 characters, max 26 characters + */ private String displayName; + /** + * Sets whether to activate the newly created card upon creation. + * [Optional] + */ private Boolean activateCard; protected CardRequest(final CardType type, diff --git a/src/main/java/com/checkout/issuing/cards/requests/create/PhysicalCardRequest.java b/src/main/java/com/checkout/issuing/cards/requests/create/PhysicalCardRequest.java index f3209dfd..2d383756 100644 --- a/src/main/java/com/checkout/issuing/cards/requests/create/PhysicalCardRequest.java +++ b/src/main/java/com/checkout/issuing/cards/requests/create/PhysicalCardRequest.java @@ -16,6 +16,10 @@ @ToString(callSuper = true) public final class PhysicalCardRequest extends CardRequest { + /** + * The shipping instructions for the physical card. + * [Required] + */ private ShippingInstruction shippingInstructions; @Builder @@ -28,12 +32,12 @@ private PhysicalCardRequest(final String cardholderId, final ShippingInstruction shippingInstructions, final IssuingCardMetadata metadata, final LocalDate revocationDate, - final String activationDate) { + final String scheduledActivationDate) { super(CardType.PHYSICAL, cardholderId, lifetime, reference, cardProductId, displayName, activateCard); this.shippingInstructions = shippingInstructions; setMetadata(metadata); setRevocationDate(revocationDate); - setActivationDate(activationDate); + setScheduledActivationDate(scheduledActivationDate); } } \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/requests/create/VirtualCardRequest.java b/src/main/java/com/checkout/issuing/cards/requests/create/VirtualCardRequest.java index 23e20fa7..7c47cea7 100644 --- a/src/main/java/com/checkout/issuing/cards/requests/create/VirtualCardRequest.java +++ b/src/main/java/com/checkout/issuing/cards/requests/create/VirtualCardRequest.java @@ -18,6 +18,11 @@ @ToString(callSuper = true) public final class VirtualCardRequest extends CardRequest { + /** + * Sets whether the virtual card should expire after a single use. + * [Optional] + * Default: false + */ private Boolean isSingleUse; /** @@ -48,7 +53,7 @@ private VirtualCardRequest(final String cardholderId, final Boolean isSingleUse, final IssuingCardMetadata metadata, final LocalDate revocationDate, - final String activationDate, + final String scheduledActivationDate, final List returnCredentials, final List controlProfiles, final List controls) { @@ -59,6 +64,6 @@ private VirtualCardRequest(final String cardholderId, this.controls = controls; setMetadata(metadata); setRevocationDate(revocationDate); - setActivationDate(activationDate); + setScheduledActivationDate(scheduledActivationDate); } } \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/requests/update/CardUpdateHeaders.java b/src/main/java/com/checkout/issuing/cards/requests/update/CardUpdateHeaders.java new file mode 100644 index 00000000..c3fbc405 --- /dev/null +++ b/src/main/java/com/checkout/issuing/cards/requests/update/CardUpdateHeaders.java @@ -0,0 +1,51 @@ +package com.checkout.issuing.cards.requests.update; + +import com.checkout.IHeaders; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * The optional HTTP headers accepted when updating a card's details. + * + * @see PATCH /issuing/cards/{cardId} + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CardUpdateHeaders implements IHeaders { + + /** + * The return-encrypted-cvv HTTP header. Set to true to retrieve the card's encrypted + * credentials in the response. Requires an RSA public key to be provided in the + * Encryption-Key header. + * [Optional] + */ + private Boolean returnEncryptedCvv; + + /** + * The Encryption-Key HTTP header. The RSA public key used to encrypt returned credentials. + * Required when the return-encrypted-cvv header is set to true. Provide the public key with + * the BEGIN PUBLIC KEY and END PUBLIC KEY headers and any newline characters removed, + * encoded as Base64. + * [Optional] + */ + private String encryptionKey; + + @Override + public Map getHeaders() { + final Map headers = new LinkedHashMap<>(); + if (returnEncryptedCvv != null) { + headers.put("return-encrypted-cvv", String.valueOf(returnEncryptedCvv)); + } + if (encryptionKey != null) { + headers.put("Encryption-Key", encryptionKey); + } + return headers; + } +} 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 index 5d0992a8..23cf05e9 100644 --- a/src/main/java/com/checkout/issuing/cards/requests/update/UpdateCardRequest.java +++ b/src/main/java/com/checkout/issuing/cards/requests/update/UpdateCardRequest.java @@ -13,32 +13,53 @@ @AllArgsConstructor public final class UpdateCardRequest { + /** + * Your reference. + * [Optional] + * max 256 characters + */ private String reference; + /** + * User's metadata. + * [Optional] + */ private IssuingCardMetadata metadata; + /** + * The card's expiration month. + * [Optional] + * min 1, max 12 + */ private Integer expiryMonth; + /** + * The card's expiration year. + * [Optional] + * min 4 characters, max 4 characters + */ private Integer expiryYear; /** - * ISO 8601 date scheduling the card's activation. Two formats are supported: - * date only (YYYY-MM-DD, treated as midnight UTC), or date with round hour - * (YYYY-MM-DDTHH:mmZ in UTC, or YYYY-MM-DDTHH:mm±HH:mm with offset). Only round hours are - * allowed when a time is provided (HH:00). The value must be at least the next round hour - * after the request time. + * Date scheduling the card's first activation. Only applies to the initial activation of a + * card. Two formats are supported: date only (YYYY-MM-DD, treated as midnight UTC), or date + * with round hour (YYYY-MM-DDTHH:mmZ in UTC, or YYYY-MM-DDTHH:mm+HH:mm with offset). Only + * round hours are allowed when a time is provided (HH:00). The value must be at least the next + * round hour after the request time. *

* [Optional] *

+ * Example: 2026-06-01T10:00Z */ - private String activationDate; + private String scheduledActivationDate; /** - * Date for the card to be automatically revoked. Must be after the current date. + * Date scheduling the card's automatic revocation. *

* [Optional] *

- * Format: yyyy-MM-dd + * Format: date (YYYY-MM-DD, time is midnight UTC) + * Example: 2027-03-12 */ private LocalDate revocationDate; } \ No newline at end of file diff --git a/src/main/java/com/checkout/issuing/cards/responses/CardDetailsResponse.java b/src/main/java/com/checkout/issuing/cards/responses/CardDetailsResponse.java index b465c0c8..9402a320 100644 --- a/src/main/java/com/checkout/issuing/cards/responses/CardDetailsResponse.java +++ b/src/main/java/com/checkout/issuing/cards/responses/CardDetailsResponse.java @@ -19,14 +19,42 @@ @ToString(callSuper = true) public abstract class CardDetailsResponse extends Resource { + /** + * The card type. + * [Required] + */ protected final CardType type; + /** + * The card's unique identifier. + * [Required] + * Pattern: ^crd_[a-z0-9]{26}$ + * min 30 characters, max 30 characters + */ protected String id; + /** + * The cardholder's unique identifier. + * [Required] + * Pattern: ^crh_[a-z0-9]{26}$ + * min 30 characters, max 30 characters + */ protected String cardholderId; + /** + * The card product's unique identifier. + * [Required] + * Pattern: ^pro_[a-z0-9]{26}$ + * min 30 characters, max 30 characters + */ protected String cardProductId; + /** + * The client's unique identifier. + * [Required] + * Pattern: ^cli_[a-z0-9]{26}$ + * min 30 characters, max 30 characters + */ protected String clientId; /** @@ -45,20 +73,59 @@ public abstract class CardDetailsResponse extends Resource { */ protected String userId; + /** + * The last four digits of the card number, also known as the PAN. + * [Required] + * Pattern: ^[0-9]{4}$ + */ protected String lastFour; + /** + * The card's expiration month. + * [Required] + * min 1, max 12 + */ protected Integer expiryMonth; + /** + * The card's expiration year. + * [Required] + * min 4 characters, max 4 characters + */ protected Integer expiryYear; + /** + * The card's status, which determines whether it can approve incoming transactions. + * [Required] + */ protected CardStatus status; + /** + * The name to display on the card. + * [Optional] + * Pattern: ^[0-9a-zA-Z.\- ]{2,26}$ + * min 2 characters, max 26 characters + */ protected String displayName; + /** + * The issuing currency, as a three-letter ISO 4217 currency code. + * [Required] + */ protected Currency billingCurrency; + /** + * The issuing country, as a two-letter ISO 3166-1 alpha-2 country code. + * [Required] + * min 2 characters, max 2 characters + */ protected CountryCode issuingCountry; + /** + * Your reference. + * [Optional] + * max 256 characters + */ protected String reference; /** @@ -67,25 +134,38 @@ public abstract class CardDetailsResponse extends Resource { */ protected IssuingCardMetadata metadata; + /** + * The date and time when the card was created, in UTC. + * [Optional] + */ protected Instant createdDate; + /** + * The date and time when the card was last modified, in UTC. + * [Optional] + */ protected Instant lastModifiedDate; /** - * ISO 8601 date scheduling the card's activation. + * Date scheduling the card's first activation. Only applies to the initial activation of a + * card. Two formats are supported: date only (YYYY-MM-DD, treated as midnight UTC), or date + * with round hour (YYYY-MM-DDTHH:mmZ in UTC, or YYYY-MM-DDTHH:mm+HH:mm with offset). Only + * round hours are allowed when a time is provided (HH:00). The value must be at least the next + * round hour after the request time. *

* [Optional] *

+ * Example: 2026-06-01T10:00Z */ - protected String activationDate; + protected String scheduledActivationDate; /** - * Date for the card to be automatically revoked. Must be after the current date and date only in the - * form yyyy-mm-dd. + * Date scheduling the card's automatic revocation. *

* [Optional] *

- * Format: yyyy-MM-dd + * Format: date (YYYY-MM-DD, time is midnight UTC) + * Example: 2027-03-12 */ protected LocalDate revocationDate; 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 index 7b057b62..b5569b39 100644 --- a/src/main/java/com/checkout/issuing/cards/responses/update/UpdateCardResponse.java +++ b/src/main/java/com/checkout/issuing/cards/responses/update/UpdateCardResponse.java @@ -7,10 +7,24 @@ import java.time.Instant; +/** + * The response returned when a card's details are updated. + */ @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public final class UpdateCardResponse extends Resource { + /** + * The date and time when the card was last modified, in UTC. + * [Required] + * Format: date-time (RFC 3339) + */ private Instant lastModifiedDate; -} \ No newline at end of file + + /** + * The card's encrypted CVV, returned when the return-encrypted-cvv header is true. + * [Optional] + */ + private String encryptedCvv; +} diff --git a/src/test/java/com/checkout/identities/IdentitiesSerializationTest.java b/src/test/java/com/checkout/identities/IdentitiesSerializationTest.java new file mode 100644 index 00000000..c917b9d3 --- /dev/null +++ b/src/test/java/com/checkout/identities/IdentitiesSerializationTest.java @@ -0,0 +1,541 @@ +package com.checkout.identities; + +import com.checkout.GsonSerializer; +import com.checkout.common.CountryCode; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationReportResponse; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationResponse; +import com.checkout.identities.entities.ApplicantSessionInformation; +import com.checkout.identities.entities.Certification; +import com.checkout.identities.entities.CertificationType; +import com.checkout.identities.entities.ClientInformation; +import com.checkout.identities.entities.DeclaredData; +import com.checkout.identities.entities.DiatfCertificationData; +import com.checkout.identities.entities.DocumentDetails; +import com.checkout.identities.entities.DocumentType; +import com.checkout.identities.entities.Gpg45Profile; +import com.checkout.identities.entities.IdentityDeclaredData; +import com.checkout.identities.entities.IdentityVerificationClientInformation; +import com.checkout.identities.entities.IdvAddress; +import com.checkout.identities.entities.InitialDevice; +import com.checkout.identities.entities.LevelOfConfidence; +import com.checkout.identities.entities.PhoneNumber; +import com.checkout.identities.entities.RiskLabel; +import com.checkout.identities.entities.SelectedDocument; +import com.checkout.identities.faceauthentications.requests.FaceAuthenticationAttemptRequest; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationAttemptResponse; +import com.checkout.identities.faceauthentications.responses.FaceAuthenticationResponse; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationReportResponse; +import com.checkout.identities.identityverification.requests.IdentityVerificationAttemptRequest; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationAttemptStatus; +import com.checkout.identities.identityverification.responses.IdentityVerificationReportResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationResponse; +import com.checkout.identities.identityverification.responses.IdentityVerificationStatus; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.Arrays; +import java.util.Collections; + +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.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * GSON serialization tests for the Identities domain types changed by swagger 2026-09-02. + * + * Covers the IdvPdf signed_url to pdf_report rename, the additive IDV and FAV verification fields, + * the typed country codes, and the enum and IdvDocument additions that the swagger changelog does + * not report. + */ +class IdentitiesSerializationTest { + + private final GsonSerializer serializer = new GsonSerializer(); + + // ------------------------------------------------------------------------ + // Part C: IdvPdf signed_url replaced by pdf_report. + // The previous spec declared both keys and the SDK modelled only signed_url, + // so pdf_report never populated. signed_url is now gone from the spec. + // ------------------------------------------------------------------------ + + @Test + void shouldDeserializePdfReportOnIdentityVerificationReportResponse() { + final IdentityVerificationReportResponse response = serializer.fromJson( + "{\"pdf_report\":\"https://example.com/report.pdf\"}", + IdentityVerificationReportResponse.class); + + assertNotNull(response); + assertEquals("https://example.com/report.pdf", response.getPdfReport()); + } + + @Test + void shouldDeserializePdfReportOnAddressDocumentVerificationReportResponse() { + final AddressDocumentVerificationReportResponse response = serializer.fromJson( + "{\"pdf_report\":\"https://example.com/adv-report.pdf\"}", + AddressDocumentVerificationReportResponse.class); + + assertNotNull(response); + assertEquals("https://example.com/adv-report.pdf", response.getPdfReport()); + } + + @Test + void shouldDeserializePdfReportOnIdDocumentVerificationReportResponse() { + final IdDocumentVerificationReportResponse response = serializer.fromJson( + "{\"pdf_report\":\"https://example.com/iddv-report.pdf\"}", + IdDocumentVerificationReportResponse.class); + + assertNotNull(response); + assertEquals("https://example.com/iddv-report.pdf", response.getPdfReport()); + } + + @Test + void shouldNotMapTheRemovedSignedUrlKeyOnReportResponses() { + final IdentityVerificationReportResponse response = serializer.fromJson( + "{\"signed_url\":\"https://example.com/report.pdf\"}", + IdentityVerificationReportResponse.class); + + assertNotNull(response); + assertNull(response.getPdfReport()); + } + + @Test + void shouldSerializePdfReportUsingTheSwaggerKey() { + final IdentityVerificationReportResponse response = IdentityVerificationReportResponse.builder() + .pdfReport("https://example.com/report.pdf") + .build(); + + final String json = serializer.toJson(response); + + assertTrue(json.contains("\"pdf_report\"")); + assertFalse(json.contains("\"signed_url\"")); + } + + // ------------------------------------------------------------------------ + // Part E1: risk_labels is now a typed enum, not a bare string list, and is + // declared per response class rather than on a shared base. + // ------------------------------------------------------------------------ + + @ParameterizedTest + @CsvSource({ + "MULTIPLE_FACES_DETECTED,multiple_faces_detected", + "MCC_NOT_CONFIDENT,mcc_not_confident", + "RISKY_DOCUMENT_FORMAT,risky_document_format" + }) + void shouldRoundTripEachRiskLabelValue(final RiskLabel label, final String expected) { + final IdentityVerificationResponse response = IdentityVerificationResponse.builder() + .riskLabels(Collections.singletonList(label)) + .build(); + + final String json = serializer.toJson(response); + final IdentityVerificationResponse deserialized = + serializer.fromJson(json, IdentityVerificationResponse.class); + + assertTrue(json.contains("\"" + expected + "\"")); + assertNotNull(deserialized.getRiskLabels()); + assertEquals(1, deserialized.getRiskLabels().size()); + assertEquals(label, deserialized.getRiskLabels().get(0)); + } + + @Test + void shouldDeserializeRiskLabelsOnAddressDocumentVerificationResponse() { + final AddressDocumentVerificationResponse response = serializer.fromJson( + "{\"risk_labels\":[\"mcc_not_confident\",\"risky_document_format\"]}", + AddressDocumentVerificationResponse.class); + + assertNotNull(response); + assertEquals(2, response.getRiskLabels().size()); + assertEquals(RiskLabel.MCC_NOT_CONFIDENT, response.getRiskLabels().get(0)); + assertEquals(RiskLabel.RISKY_DOCUMENT_FORMAT, response.getRiskLabels().get(1)); + } + + @Test + void shouldDeserializeRiskLabelsOnFaceAuthenticationResponse() { + final FaceAuthenticationResponse response = serializer.fromJson( + "{\"risk_labels\":[\"multiple_faces_detected\"]}", + FaceAuthenticationResponse.class); + + assertNotNull(response); + assertEquals(1, response.getRiskLabels().size()); + assertEquals(RiskLabel.MULTIPLE_FACES_DETECTED, response.getRiskLabels().get(0)); + } + + // ------------------------------------------------------------------------ + // Part E2: certifications and verification_policy_version, nested two deep. + // ------------------------------------------------------------------------ + + @Test + void shouldDeserializeCertificationsAndVerificationPolicyVersion() { + final String json = "{" + + "\"verification_policy_version\":\"2.1\"," + + "\"certifications\":[{\"type\":\"diatf\",\"data\":{" + + " \"gpg45_profile\":\"M1C\"," + + " \"level_of_confidence\":\"high\"," + + " \"right_to_work\":\"GRANTED\"}}]" + + "}"; + + final IdentityVerificationResponse response = + serializer.fromJson(json, IdentityVerificationResponse.class); + + assertNotNull(response); + assertEquals("2.1", response.getVerificationPolicyVersion()); + assertNotNull(response.getCertifications()); + assertEquals(1, response.getCertifications().size()); + assertEquals(CertificationType.DIATF, response.getCertifications().get(0).getType()); + assertNotNull(response.getCertifications().get(0).getData()); + assertEquals(Gpg45Profile.M1C, response.getCertifications().get(0).getData().getGpg45Profile()); + assertEquals(LevelOfConfidence.HIGH, response.getCertifications().get(0).getData().getLevelOfConfidence()); + assertEquals("GRANTED", response.getCertifications().get(0).getData().getRightToWork()); + } + + @Test + void shouldRoundTripCertifications() { + final IdentityVerificationResponse original = IdentityVerificationResponse.builder() + .verificationPolicyVersion("1.0") + .certifications(Collections.singletonList(Certification.builder() + .type(CertificationType.DIATF) + .data(DiatfCertificationData.builder() + .gpg45Profile(Gpg45Profile.H1A) + .levelOfConfidence(LevelOfConfidence.MEDIUM) + .rightToWork("GRANTED") + .build()) + .build())) + .build(); + + final String json = serializer.toJson(original); + final IdentityVerificationResponse deserialized = + serializer.fromJson(json, IdentityVerificationResponse.class); + + assertTrue(json.contains("\"verification_policy_version\":\"1.0\"")); + assertTrue(json.contains("\"gpg45_profile\":\"H1A\"")); + assertTrue(json.contains("\"level_of_confidence\":\"medium\"")); + assertEquals(Gpg45Profile.H1A, deserialized.getCertifications().get(0).getData().getGpg45Profile()); + assertEquals(LevelOfConfidence.MEDIUM, deserialized.getCertifications().get(0).getData().getLevelOfConfidence()); + } + + // ------------------------------------------------------------------------ + // Part E3: declared_data gains birth_date everywhere, plus phone_number, + // email and address on identity verifications only. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripBirthDateOnDeclaredData() { + final DeclaredData original = DeclaredData.builder() + .name("Hannah Bret") + .birthDate("1994-10-15") + .build(); + + final String json = serializer.toJson(original); + final DeclaredData deserialized = serializer.fromJson(json, DeclaredData.class); + + assertTrue(json.contains("\"birth_date\":\"1994-10-15\"")); + assertEquals("Hannah Bret", deserialized.getName()); + assertEquals("1994-10-15", deserialized.getBirthDate()); + } + + @Test + void shouldRoundTripAllIdentityDeclaredDataFields() { + final IdentityDeclaredData original = IdentityDeclaredData.builder() + .name("Hannah Bret") + .birthDate("1994-10-15") + .email("hannah.bret@example.com") + .phoneNumber(PhoneNumber.builder().countryCode("+33").number("5555550102").build()) + .address(IdvAddress.builder() + .addressLine1("123 Main Street") + .addressLine2("Apt 4B") + .city("London") + .state("Greater London") + .zip("SW1A 1AA") + .country(CountryCode.GB) + .build()) + .build(); + + final String json = serializer.toJson(original); + final IdentityDeclaredData deserialized = serializer.fromJson(json, IdentityDeclaredData.class); + + assertTrue(json.contains("\"country_code\":\"+33\"")); + assertTrue(json.contains("\"address_line1\":\"123 Main Street\"")); + assertEquals("Hannah Bret", deserialized.getName()); + assertEquals("1994-10-15", deserialized.getBirthDate()); + assertEquals("hannah.bret@example.com", deserialized.getEmail()); + assertEquals("+33", deserialized.getPhoneNumber().getCountryCode()); + assertEquals("5555550102", deserialized.getPhoneNumber().getNumber()); + assertEquals("123 Main Street", deserialized.getAddress().getAddressLine1()); + assertEquals("Apt 4B", deserialized.getAddress().getAddressLine2()); + assertEquals("London", deserialized.getAddress().getCity()); + assertEquals("Greater London", deserialized.getAddress().getState()); + assertEquals("SW1A 1AA", deserialized.getAddress().getZip()); + assertEquals(CountryCode.GB, deserialized.getAddress().getCountry()); + } + + // ------------------------------------------------------------------------ + // Part E5: phone_number on the IDV and FAV attempt request and response. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripPhoneNumberOnIdentityVerificationAttemptRequest() { + final IdentityVerificationAttemptRequest original = IdentityVerificationAttemptRequest.builder() + .redirectUrl("https://example.com/redirect") + .phoneNumber(PhoneNumber.builder().countryCode("+33").number("5555550102").build()) + .build(); + + final String json = serializer.toJson(original); + final IdentityVerificationAttemptRequest deserialized = + serializer.fromJson(json, IdentityVerificationAttemptRequest.class); + + assertTrue(json.contains("\"phone_number\"")); + assertTrue(json.contains("\"number\":\"5555550102\"")); + assertEquals("+33", deserialized.getPhoneNumber().getCountryCode()); + } + + @Test + void shouldRoundTripPhoneNumberOnFaceAuthenticationAttemptRequest() { + final FaceAuthenticationAttemptRequest original = FaceAuthenticationAttemptRequest.builder() + .redirectUrl("https://example.com/redirect") + .phoneNumber(PhoneNumber.builder().countryCode("+44").number("7700900000").build()) + .build(); + + final String json = serializer.toJson(original); + final FaceAuthenticationAttemptRequest deserialized = + serializer.fromJson(json, FaceAuthenticationAttemptRequest.class); + + assertTrue(json.contains("\"phone_number\"")); + assertEquals("7700900000", deserialized.getPhoneNumber().getNumber()); + } + + @Test + void shouldDeserializePhoneNumberOnIdentityVerificationAttemptResponse() { + final IdentityVerificationAttemptResponse response = serializer.fromJson( + "{\"phone_number\":{\"country_code\":\"+33\",\"number\":\"5555550102\"}}", + IdentityVerificationAttemptResponse.class); + + assertNotNull(response); + assertEquals("+33", response.getPhoneNumber().getCountryCode()); + assertEquals("5555550102", response.getPhoneNumber().getNumber()); + } + + @Test + void shouldDeserializePhoneNumberOnFaceAuthenticationAttemptResponse() { + final FaceAuthenticationAttemptResponse response = serializer.fromJson( + "{\"phone_number\":{\"country_code\":\"+44\",\"number\":\"7700900000\"}}", + FaceAuthenticationAttemptResponse.class); + + assertNotNull(response); + assertEquals("+44", response.getPhoneNumber().getCountryCode()); + } + + // ------------------------------------------------------------------------ + // Part E6: applicant_session_information gains number_of_sessions, + // user_agent and initial_device. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripAllApplicantSessionInformationFields() { + final ApplicantSessionInformation original = ApplicantSessionInformation.builder() + .ipAddress("123.4.5.6") + .numberOfSessions(3) + .userAgent("Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW)") + .initialDevice(InitialDevice.MOBILE) + .selectedDocuments(Collections.singletonList(SelectedDocument.builder() + .country(CountryCode.GB) + .documentType(DocumentType.PASSPORT) + .build())) + .build(); + + final String json = serializer.toJson(original); + final ApplicantSessionInformation deserialized = + serializer.fromJson(json, ApplicantSessionInformation.class); + + assertTrue(json.contains("\"number_of_sessions\":3")); + assertTrue(json.contains("\"user_agent\"")); + assertTrue(json.contains("\"initial_device\":\"mobile\"")); + assertEquals("123.4.5.6", deserialized.getIpAddress()); + assertEquals(3, deserialized.getNumberOfSessions()); + assertTrue(deserialized.getUserAgent().startsWith("Mozilla/5.0")); + assertEquals(InitialDevice.MOBILE, deserialized.getInitialDevice()); + assertEquals(1, deserialized.getSelectedDocuments().size()); + assertEquals(CountryCode.GB, deserialized.getSelectedDocuments().get(0).getCountry()); + } + + @ParameterizedTest + @CsvSource({"DESKTOP,desktop", "MOBILE,mobile"}) + void shouldSerializeEachInitialDeviceValue(final InitialDevice device, final String expected) { + final ApplicantSessionInformation session = + ApplicantSessionInformation.builder().initialDevice(device).build(); + + assertTrue(serializer.toJson(session).contains("\"initial_device\":\"" + expected + "\"")); + } + + // ------------------------------------------------------------------------ + // Part E7: client_information forked. The identity verification variant + // gains two fields; the face authentication variant does not. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripAllIdentityVerificationClientInformationFields() { + final IdentityVerificationClientInformation original = IdentityVerificationClientInformation.builder() + .preSelectedResidenceCountry(CountryCode.FR) + .preSelectedLanguage("en-US") + .preSelectedDocumentIssuingCountry(CountryCode.FR) + .preSelectedDocumentType(DocumentType.PASSPORT) + .build(); + + final String json = serializer.toJson(original); + final IdentityVerificationClientInformation deserialized = + serializer.fromJson(json, IdentityVerificationClientInformation.class); + + assertTrue(json.contains("\"pre_selected_document_issuing_country\":\"FR\"")); + assertTrue(json.contains("\"pre_selected_document_type\":\"Passport\"")); + assertEquals(CountryCode.FR, deserialized.getPreSelectedResidenceCountry()); + assertEquals("en-US", deserialized.getPreSelectedLanguage()); + assertEquals(CountryCode.FR, deserialized.getPreSelectedDocumentIssuingCountry()); + assertEquals(DocumentType.PASSPORT, deserialized.getPreSelectedDocumentType()); + } + + @Test + void shouldNotCarryIdentityOnlyFieldsOnTheFaceAuthenticationClientInformation() { + final ClientInformation faceAuthenticationClientInformation = ClientInformation.builder() + .preSelectedResidenceCountry(CountryCode.FR) + .preSelectedLanguage("en-US") + .build(); + + final String json = serializer.toJson(faceAuthenticationClientInformation); + + assertFalse(json.contains("pre_selected_document_issuing_country")); + assertFalse(json.contains("pre_selected_document_type")); + } + + // ------------------------------------------------------------------------ + // Part F M2: document_type gained Other, Travel Document and Visa. + // ------------------------------------------------------------------------ + + @ParameterizedTest + @CsvSource({ + "DRIVING_LICENCE,Driving licence", + "ID,ID", + "OTHER,Other", + "PASSPORT,Passport", + "RESIDENCE_PERMIT,Residence Permit", + "TRAVEL_DOCUMENT,Travel Document", + "VISA,Visa" + }) + void shouldRoundTripEachDocumentTypeValue(final DocumentType type, final String expected) { + final SelectedDocument original = SelectedDocument.builder().documentType(type).build(); + + final String json = serializer.toJson(original); + final SelectedDocument deserialized = serializer.fromJson(json, SelectedDocument.class); + + assertTrue(json.contains("\"document_type\":\"" + expected + "\"")); + assertEquals(type, deserialized.getDocumentType()); + } + + // ------------------------------------------------------------------------ + // Part F M3 and M4: new status values. Java keeps two copies of the shared + // IdvAttemptStatuses enum, so terminated lands on both. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripTheTerminatedAttemptStatus() { + final IdentityVerificationAttemptResponse response = serializer.fromJson( + "{\"status\":\"terminated\"}", IdentityVerificationAttemptResponse.class); + + assertNotNull(response); + assertEquals(IdentityVerificationAttemptStatus.TERMINATED, response.getStatus()); + assertTrue(serializer.toJson(response).contains("\"status\":\"terminated\"")); + } + + @Test + void shouldRoundTripTheCreatedVerificationStatus() { + final IdentityVerificationResponse response = serializer.fromJson( + "{\"status\":\"created\"}", IdentityVerificationResponse.class); + + assertNotNull(response); + assertEquals(IdentityVerificationStatus.CREATED, response.getStatus()); + assertTrue(serializer.toJson(response).contains("\"status\":\"created\"")); + } + + // ------------------------------------------------------------------------ + // Part F M5: IdvDocument gained address and four permit fields. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripTheNewDocumentDetailsFields() { + final DocumentDetails original = DocumentDetails.builder() + .documentType(DocumentType.RESIDENCE_PERMIT) + .documentIssuingCountry(CountryCode.GB) + .frontImageSignedUrl("https://example.com/front.png") + .address("123 Main Street, London, SW1A 1AA") + .permitObtainingDate("2020-01-15") + .permitExpiryDate("2030-01-14") + .permitTypeDetailed("Indefinite leave to remain") + .permitTypeRemarks("No work restrictions") + .build(); + + final String json = serializer.toJson(original); + final DocumentDetails deserialized = serializer.fromJson(json, DocumentDetails.class); + + assertTrue(json.contains("\"address\":\"123 Main Street, London, SW1A 1AA\"")); + assertTrue(json.contains("\"permit_obtaining_date\":\"2020-01-15\"")); + assertTrue(json.contains("\"permit_expiry_date\":\"2030-01-14\"")); + assertTrue(json.contains("\"permit_type_detailed\":\"Indefinite leave to remain\"")); + assertTrue(json.contains("\"permit_type_remarks\":\"No work restrictions\"")); + assertEquals("123 Main Street, London, SW1A 1AA", deserialized.getAddress()); + assertEquals("2020-01-15", deserialized.getPermitObtainingDate()); + assertEquals("2030-01-14", deserialized.getPermitExpiryDate()); + assertEquals("Indefinite leave to remain", deserialized.getPermitTypeDetailed()); + assertEquals("No work restrictions", deserialized.getPermitTypeRemarks()); + assertEquals(DocumentType.RESIDENCE_PERMIT, deserialized.getDocumentType()); + } + + // ------------------------------------------------------------------------ + // D3: country codes are typed. The phone prefix is not a country code. + // ------------------------------------------------------------------------ + + @Test + void shouldSerializeCountryCodesToBareIsoAlpha2Values() { + final DocumentDetails doc = DocumentDetails.builder() + .documentIssuingCountry(CountryCode.GB) + .nationality(CountryCode.FR) + .build(); + + final String json = serializer.toJson(doc); + + assertTrue(json.contains("\"document_issuing_country\":\"GB\"")); + assertTrue(json.contains("\"nationality\":\"FR\"")); + } + + @Test + void shouldRoundTripTypedCountryCodes() { + final DocumentDetails doc = serializer.fromJson( + "{\"document_issuing_country\":\"GB\",\"nationality\":\"FR\"}", DocumentDetails.class); + + assertEquals(CountryCode.GB, doc.getDocumentIssuingCountry()); + assertEquals(CountryCode.FR, doc.getNationality()); + } + + @Test + void shouldKeepThePhonePrefixAsAStringNotACountryCode() { + // IdvPhoneNumber.country_code is a dialling prefix (pattern ^\+(\d+)$, for example +33), + // not an ISO country code, so it stays a String. + final PhoneNumber phone = PhoneNumber.builder().countryCode("+33").number("5555550102").build(); + + assertTrue(serializer.toJson(phone).contains("\"country_code\":\"+33\"")); + } + + // ------------------------------------------------------------------------ + // The attempts pagination query serializes to the exact skip and limit keys. + // ------------------------------------------------------------------------ + + @Test + void shouldSerializeAttemptsQueryFilterToTheSwaggerQueryKeys() { + final com.checkout.identities.entities.AttemptsQueryFilter query = + com.checkout.identities.entities.AttemptsQueryFilter.builder().skip(6).limit(5).build(); + + final String json = serializer.toJson(query); + + assertTrue(json.contains("\"skip\":6")); + assertTrue(json.contains("\"limit\":5")); + } +} diff --git a/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationAttemptAssetsResponseSerializationTest.java b/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationAttemptAssetsResponseSerializationTest.java new file mode 100644 index 00000000..8096ffe7 --- /dev/null +++ b/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationAttemptAssetsResponseSerializationTest.java @@ -0,0 +1,138 @@ +package com.checkout.identities.addressdocumentverification; + +import com.checkout.GsonSerializer; +import com.checkout.common.Link; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptAsset; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptAssetType; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptAssetsResponse; +import com.checkout.identities.entities.AttemptAssetLinks; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * GSON serialization tests for AddressDocumentVerificationAttemptAssetsResponse. + * + * Swagger reference: + * GET /address-document-verifications/{address_document_verification_id}/attempts/{attempt_id}/assets + * Schemas: AdvAttemptAssets, AdvAttemptAsset. + */ +class AddressDocumentVerificationAttemptAssetsResponseSerializationTest { + + private final GsonSerializer serializer = new GsonSerializer(); + + @Test + void shouldSerializeWithAllProperties() { + final AddressDocumentVerificationAttemptAssetsResponse response = + AddressDocumentVerificationAttemptAssetsResponse.builder() + .totalCount(1) + .skip(0) + .limit(10) + .data(Collections.singletonList(AddressDocumentVerificationAttemptAsset.builder() + .type(AddressDocumentVerificationAttemptAssetType.DOCUMENT) + .links(AttemptAssetLinks.builder() + .assetUrl(new Link("https://example.com/address-document.png", null, null)) + .build()) + .build())) + .build(); + + assertDoesNotThrow(() -> serializer.toJson(response)); + } + + /** + * Deserializes the spec's adv_attempt_assets_response_body example verbatim. The next and + * previous hrefs are truncated with "?..." in the specification itself. + */ + @Test + void shouldDeserializeFromSwaggerExample() { + final String json = "{" + + "\"total_count\":1," + + "\"skip\":0," + + "\"limit\":10," + + "\"data\":[" + + " {\"type\":\"document\",\"_links\":{\"asset_url\":{\"href\":\"https://storage-b.env.ubble.ai/ubble-ai/NDYOOVHGZPAQ/a54b3393-f02a-47c9-a9c5-2f6ee73560e1/bb603e2f-5de9-40f2-9631-8285a33c24c0/address_document.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600\"}}}" + + "]," + + "\"_links\":{" + + " \"self\":{\"href\":\"https://identity-verification.checkout.com/address-document-verifications/adv_tkoi5db4hryu5cei5vwoabr7we/attempts/adva_tkoi5db4hryu5cei5vwoabr7we/assets\"}," + + " \"next\":{\"href\":\"https://identity-verification.checkout.com/address-document-verifications/adv_tkoi5db4hryu5cei5vwoabr7we/attempts/adva_tkoi5db4hryu5cei5vwoabr7we/assets?...\"}," + + " \"previous\":{\"href\":\"https://identity-verification.checkout.com/address-document-verifications/adv_tkoi5db4hryu5cei5vwoabr7we/attempts/adva_tkoi5db4hryu5cei5vwoabr7we/assets?...\"}" + + "}" + + "}"; + + final AddressDocumentVerificationAttemptAssetsResponse response = + serializer.fromJson(json, AddressDocumentVerificationAttemptAssetsResponse.class); + + assertNotNull(response); + assertEquals(1, response.getTotalCount()); + assertEquals(0, response.getSkip()); + assertEquals(10, response.getLimit()); + assertNotNull(response.getData()); + assertEquals(1, response.getData().size()); + assertEquals(AddressDocumentVerificationAttemptAssetType.DOCUMENT, response.getData().get(0).getType()); + assertNotNull(response.getData().get(0).getLinks()); + assertNotNull(response.getData().get(0).getLinks().getAssetUrl()); + assertTrue(response.getData().get(0).getLinks().getAssetUrl().getHref().contains("address_document.png")); + } + + @Test + void shouldRoundTripSerialize() { + final AddressDocumentVerificationAttemptAssetsResponse original = + AddressDocumentVerificationAttemptAssetsResponse.builder() + .totalCount(3) + .skip(6) + .limit(5) + .data(Collections.singletonList(AddressDocumentVerificationAttemptAsset.builder() + .type(AddressDocumentVerificationAttemptAssetType.DOCUMENT) + .links(AttemptAssetLinks.builder() + .assetUrl(new Link("https://example.com/address-document.png", null, null)) + .build()) + .build())) + .build(); + + final String json = serializer.toJson(original); + final AddressDocumentVerificationAttemptAssetsResponse deserialized = + serializer.fromJson(json, AddressDocumentVerificationAttemptAssetsResponse.class); + + assertTrue(json.contains("\"total_count\":3")); + assertTrue(json.contains("\"asset_url\"")); + assertEquals(3, deserialized.getTotalCount()); + assertEquals(6, deserialized.getSkip()); + assertEquals(5, deserialized.getLimit()); + assertEquals(1, deserialized.getData().size()); + assertEquals(AddressDocumentVerificationAttemptAssetType.DOCUMENT, deserialized.getData().get(0).getType()); + assertEquals("https://example.com/address-document.png", + deserialized.getData().get(0).getLinks().getAssetUrl().getHref()); + } + + /** + * data has minItems 0, so an empty page is valid and must not be asserted non empty. + */ + @Test + void shouldDeserializeEmptyDataPage() { + final String json = "{\"total_count\":0,\"skip\":0,\"limit\":10,\"data\":[]}"; + + final AddressDocumentVerificationAttemptAssetsResponse response = + serializer.fromJson(json, AddressDocumentVerificationAttemptAssetsResponse.class); + + assertNotNull(response); + assertEquals(0, response.getTotalCount()); + assertNotNull(response.getData()); + assertTrue(response.getData().isEmpty()); + } + + @Test + void shouldSerializeTheOnlyAssetTypeToItsSwaggerValue() { + final AddressDocumentVerificationAttemptAsset asset = AddressDocumentVerificationAttemptAsset.builder() + .type(AddressDocumentVerificationAttemptAssetType.DOCUMENT) + .build(); + + final String json = serializer.toJson(asset); + + assertTrue(json.contains("\"type\":\"document\"")); + } +} diff --git a/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImplTest.java b/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImplTest.java index 86f7acbe..b198e635 100644 --- a/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImplTest.java +++ b/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImplTest.java @@ -11,6 +11,9 @@ import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptsResponse; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationReportResponse; import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationResponse; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptAssetsResponse; +import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; import com.checkout.identities.entities.DeclaredData; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -143,4 +146,44 @@ private AddressDocumentVerificationRequest createRequest() { .declaredData(DeclaredData.builder().name("Hannah Bret").build()) .build(); } -} + @Test + void shouldGetAttemptsPaginated() throws ExecutionException, InterruptedException { + final AddressDocumentVerificationAttemptsResponse response = new AddressDocumentVerificationAttemptsResponse(); + final AttemptsQueryFilter query = AttemptsQueryFilter.builder().skip(6).limit(5).build(); + + when(apiClient.queryAsync("address-document-verifications/" + ADV_ID + "/attempts", authorization, + query, AddressDocumentVerificationAttemptsResponse.class)) + .thenReturn(CompletableFuture.completedFuture(response)); + + assertEquals(response, client.getAddressDocumentVerificationAttempts(ADV_ID, query).get()); + } + + @Test + void shouldGetAttemptAssets() throws ExecutionException, InterruptedException { + final AddressDocumentVerificationAttemptAssetsResponse response = + new AddressDocumentVerificationAttemptAssetsResponse(); + final AttemptAssetsQueryFilter query = AttemptAssetsQueryFilter.builder().skip(0).limit(10).build(); + + when(apiClient.queryAsync( + "address-document-verifications/" + ADV_ID + "/attempts/" + ATTEMPT_ID + "/assets", + authorization, query, AddressDocumentVerificationAttemptAssetsResponse.class)) + .thenReturn(CompletableFuture.completedFuture(response)); + + assertEquals(response, + client.getAddressDocumentVerificationAttemptAssets(ADV_ID, ATTEMPT_ID, query).get()); + } + + @Test + void shouldGetAttemptAssetsSync() { + final AddressDocumentVerificationAttemptAssetsResponse response = + new AddressDocumentVerificationAttemptAssetsResponse(); + final AttemptAssetsQueryFilter query = AttemptAssetsQueryFilter.builder().limit(10).build(); + + when(apiClient.query( + "address-document-verifications/" + ADV_ID + "/attempts/" + ATTEMPT_ID + "/assets", + authorization, query, AddressDocumentVerificationAttemptAssetsResponse.class)) + .thenReturn(response); + + assertNotNull(client.getAddressDocumentVerificationAttemptAssetsSync(ADV_ID, ATTEMPT_ID, query)); + } +} \ 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 index 0caef866..22a8fa1b 100644 --- a/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImplTest.java +++ b/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationClientImplTest.java @@ -5,6 +5,7 @@ import com.checkout.SdkAuthorization; import com.checkout.SdkAuthorizationType; import com.checkout.SdkCredentials; +import com.checkout.identities.entities.AttemptsQueryFilter; import com.checkout.identities.entities.AttemptAssetsQueryFilter; import com.checkout.identities.faceauthentications.requests.FaceAuthenticationAttemptRequest; import com.checkout.identities.faceauthentications.requests.FaceAuthenticationRequest; @@ -275,4 +276,16 @@ void shouldGetFaceAuthenticationAttemptAssetsSync() { assertNotNull(result); assertEquals(response, result); } + @Test + void shouldGetAttemptsPaginated() throws ExecutionException, InterruptedException { + final FaceAuthenticationAttemptsResponse response = new FaceAuthenticationAttemptsResponse(); + final AttemptsQueryFilter query = AttemptsQueryFilter.builder().skip(6).limit(5).build(); + final String id = "faceAuthenticationId_test"; + + when(apiClient.queryAsync("face-authentications/" + id + "/attempts", authorization, + query, FaceAuthenticationAttemptsResponse.class)) + .thenReturn(CompletableFuture.completedFuture(response)); + + assertEquals(response, client.getFaceAuthenticationAttempts(id, query).get()); + } } \ 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 index f94135f9..cfa44289 100644 --- a/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationTestIT.java +++ b/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationTestIT.java @@ -1,5 +1,7 @@ package com.checkout.identities.faceauthentications; +import com.checkout.common.CountryCode; + import com.checkout.PlatformType; import com.checkout.SandboxTestFixture; import com.checkout.identities.entities.AttemptAssetsQueryFilter; @@ -287,7 +289,7 @@ private static FaceAuthenticationAttemptRequest createFaceAuthenticationAttemptR return FaceAuthenticationAttemptRequest.builder() .redirectUrl("https://example.com/redirect?session=" + generateRandomString(10)) .clientInformation(ClientInformation.builder() - .preSelectedResidenceCountry("GB") + .preSelectedResidenceCountry(CountryCode.GB) .preSelectedLanguage("en-US") .build()) .build(); diff --git a/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationAttemptAssetsResponseSerializationTest.java b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationAttemptAssetsResponseSerializationTest.java new file mode 100644 index 00000000..212771f3 --- /dev/null +++ b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationAttemptAssetsResponseSerializationTest.java @@ -0,0 +1,149 @@ +package com.checkout.identities.iddocumentverification; + +import com.checkout.GsonSerializer; +import com.checkout.common.Link; +import com.checkout.identities.entities.AttemptAssetLinks; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptAsset; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptAssetType; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptAssetsResponse; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.util.Arrays; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * GSON serialization tests for IdDocumentVerificationAttemptAssetsResponse. + * + * Swagger reference: + * GET /id-document-verifications/{id_document_verification_id}/attempts/{attempt_id}/assets + * Schemas: IddvAttemptAssets, IddvAttemptAsset. + */ +class IdDocumentVerificationAttemptAssetsResponseSerializationTest { + + private final GsonSerializer serializer = new GsonSerializer(); + + @Test + void shouldSerializeWithAllProperties() { + final IdDocumentVerificationAttemptAssetsResponse response = + IdDocumentVerificationAttemptAssetsResponse.builder() + .totalCount(2) + .skip(0) + .limit(10) + .data(Arrays.asList( + IdDocumentVerificationAttemptAsset.builder() + .type(IdDocumentVerificationAttemptAssetType.DOCUMENT_FRONT_IMAGE) + .links(AttemptAssetLinks.builder() + .assetUrl(new Link("https://example.com/document_front.png", null, null)) + .build()) + .build(), + IdDocumentVerificationAttemptAsset.builder() + .type(IdDocumentVerificationAttemptAssetType.DOCUMENT_BACK_IMAGE) + .links(AttemptAssetLinks.builder() + .assetUrl(new Link("https://example.com/document_back.png", null, null)) + .build()) + .build())) + .build(); + + assertDoesNotThrow(() -> serializer.toJson(response)); + } + + /** + * Deserializes the spec's iddv_attempt_assets_response_body example verbatim. The next and + * previous hrefs are truncated with "?..." in the specification itself. + */ + @Test + void shouldDeserializeFromSwaggerExample() { + final String json = "{" + + "\"total_count\":2," + + "\"skip\":0," + + "\"limit\":10," + + "\"data\":[" + + " {\"type\":\"document_front_image\",\"_links\":{\"asset_url\":{\"href\":\"https://storage-b.env.ubble.ai/ubble-ai/NDYOOVHGZPAQ/a54b3393-f02a-47c9-a9c5-2f6ee73560e1/bb603e2f-5de9-40f2-9631-8285a33c24c0/document_front.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600\"}}}," + + " {\"type\":\"document_back_image\",\"_links\":{\"asset_url\":{\"href\":\"https://storage-b.env.ubble.ai/ubble-ai/NDYOOVHGZPAQ/a54b3393-f02a-47c9-a9c5-2f6ee73560e1/bb603e2f-5de9-40f2-9631-8285a33c24c0/document_back.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600\"}}}" + + "]," + + "\"_links\":{" + + " \"self\":{\"href\":\"https://identity-verification.checkout.com/id-document-verifications/iddv_tkoi5db4hryu5cei5vwoabr7we/attempts/datp_tkoi5db4hryu5cei5vwoabraio/assets\"}," + + " \"next\":{\"href\":\"https://identity-verification.checkout.com/id-document-verifications/iddv_tkoi5db4hryu5cei5vwoabr7we/attempts/datp_tkoi5db4hryu5cei5vwoabraio/assets?...\"}," + + " \"previous\":{\"href\":\"https://identity-verification.checkout.com/id-document-verifications/iddv_tkoi5db4hryu5cei5vwoabr7we/attempts/datp_tkoi5db4hryu5cei5vwoabraio/assets?...\"}" + + "}" + + "}"; + + final IdDocumentVerificationAttemptAssetsResponse response = + serializer.fromJson(json, IdDocumentVerificationAttemptAssetsResponse.class); + + assertNotNull(response); + assertEquals(2, response.getTotalCount()); + assertEquals(0, response.getSkip()); + assertEquals(10, response.getLimit()); + assertEquals(2, response.getData().size()); + assertEquals(IdDocumentVerificationAttemptAssetType.DOCUMENT_FRONT_IMAGE, response.getData().get(0).getType()); + assertTrue(response.getData().get(0).getLinks().getAssetUrl().getHref().contains("document_front.png")); + assertEquals(IdDocumentVerificationAttemptAssetType.DOCUMENT_BACK_IMAGE, response.getData().get(1).getType()); + assertTrue(response.getData().get(1).getLinks().getAssetUrl().getHref().contains("document_back.png")); + } + + @Test + void shouldRoundTripSerialize() { + final IdDocumentVerificationAttemptAssetsResponse original = + IdDocumentVerificationAttemptAssetsResponse.builder() + .totalCount(2) + .skip(1) + .limit(4) + .data(Collections.singletonList(IdDocumentVerificationAttemptAsset.builder() + .type(IdDocumentVerificationAttemptAssetType.DOCUMENT_BACK_IMAGE) + .links(AttemptAssetLinks.builder() + .assetUrl(new Link("https://example.com/document_back.png", null, null)) + .build()) + .build())) + .build(); + + final String json = serializer.toJson(original); + final IdDocumentVerificationAttemptAssetsResponse deserialized = + serializer.fromJson(json, IdDocumentVerificationAttemptAssetsResponse.class); + + assertTrue(json.contains("\"total_count\":2")); + assertTrue(json.contains("\"asset_url\"")); + assertEquals(2, deserialized.getTotalCount()); + assertEquals(1, deserialized.getSkip()); + assertEquals(4, deserialized.getLimit()); + assertEquals(IdDocumentVerificationAttemptAssetType.DOCUMENT_BACK_IMAGE, + deserialized.getData().get(0).getType()); + } + + /** + * data has minItems 0, so an empty page is valid and must not be asserted non empty. + */ + @Test + void shouldDeserializeEmptyDataPage() { + final String json = "{\"total_count\":0,\"skip\":0,\"limit\":10,\"data\":[]}"; + + final IdDocumentVerificationAttemptAssetsResponse response = + serializer.fromJson(json, IdDocumentVerificationAttemptAssetsResponse.class); + + assertNotNull(response); + assertEquals(0, response.getTotalCount()); + assertTrue(response.getData().isEmpty()); + } + + @ParameterizedTest + @CsvSource({ + "DOCUMENT_FRONT_IMAGE,document_front_image", + "DOCUMENT_BACK_IMAGE,document_back_image" + }) + void shouldSerializeEachAssetTypeToSwaggerValue(final IdDocumentVerificationAttemptAssetType type, + final String expected) { + final IdDocumentVerificationAttemptAsset asset = + IdDocumentVerificationAttemptAsset.builder().type(type).build(); + + final String json = serializer.toJson(asset); + + assertTrue(json.contains("\"type\":\"" + expected + "\"")); + } +} diff --git a/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImplTest.java b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImplTest.java index 54d31e74..cfa7a3e1 100644 --- a/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImplTest.java +++ b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationClientImplTest.java @@ -7,6 +7,9 @@ import com.checkout.SdkCredentials; import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationAttemptRequest; import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationRequest; +import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptAssetsResponse; import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptResponse; import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptsResponse; import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationReportResponse; @@ -288,4 +291,52 @@ private IdDocumentVerificationAttemptsResponse createIdDocumentVerificationAttem private IdDocumentVerificationReportResponse createIdDocumentVerificationReportResponse() { return mock(IdDocumentVerificationReportResponse.class); } + + @Test + void shouldGetAttemptsPaginated() throws ExecutionException, InterruptedException { + final String idDocumentVerificationId = "iddv_test_123456789"; + final IdDocumentVerificationAttemptsResponse response = new IdDocumentVerificationAttemptsResponse(); + final AttemptsQueryFilter query = AttemptsQueryFilter.builder().skip(6).limit(5).build(); + + when(apiClient.queryAsync("id-document-verifications/" + idDocumentVerificationId + "/attempts", + authorization, query, IdDocumentVerificationAttemptsResponse.class)) + .thenReturn(CompletableFuture.completedFuture(response)); + + assertEquals(response, + client.getIdDocumentVerificationAttempts(idDocumentVerificationId, query).get()); + } + + @Test + void shouldGetAttemptAssets() throws ExecutionException, InterruptedException { + final String idDocumentVerificationId = "iddv_test_123456789"; + final String attemptId = "datp_test_123456789"; + final IdDocumentVerificationAttemptAssetsResponse response = + new IdDocumentVerificationAttemptAssetsResponse(); + final AttemptAssetsQueryFilter query = AttemptAssetsQueryFilter.builder().skip(0).limit(10).build(); + + when(apiClient.queryAsync( + "id-document-verifications/" + idDocumentVerificationId + "/attempts/" + attemptId + "/assets", + authorization, query, IdDocumentVerificationAttemptAssetsResponse.class)) + .thenReturn(CompletableFuture.completedFuture(response)); + + assertEquals(response, client + .getIdDocumentVerificationAttemptAssets(idDocumentVerificationId, attemptId, query).get()); + } + + @Test + void shouldGetAttemptAssetsSync() { + final String idDocumentVerificationId = "iddv_test_123456789"; + final String attemptId = "datp_test_123456789"; + final IdDocumentVerificationAttemptAssetsResponse response = + new IdDocumentVerificationAttemptAssetsResponse(); + final AttemptAssetsQueryFilter query = AttemptAssetsQueryFilter.builder().limit(10).build(); + + when(apiClient.query( + "id-document-verifications/" + idDocumentVerificationId + "/attempts/" + attemptId + "/assets", + authorization, query, IdDocumentVerificationAttemptAssetsResponse.class)) + .thenReturn(response); + + assertNotNull(client + .getIdDocumentVerificationAttemptAssetsSync(idDocumentVerificationId, attemptId, query)); + } } \ 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 index 10096cf7..be67eb7c 100644 --- a/src/test/java/com/checkout/identities/identityverification/IdentityVerificationClientImplTest.java +++ b/src/test/java/com/checkout/identities/identityverification/IdentityVerificationClientImplTest.java @@ -5,6 +5,7 @@ import com.checkout.SdkAuthorization; import com.checkout.SdkAuthorizationType; import com.checkout.SdkCredentials; +import com.checkout.identities.entities.AttemptsQueryFilter; import com.checkout.identities.entities.AttemptAssetsQueryFilter; import com.checkout.identities.identityverification.requests.CreateAndOpenIdentityVerificationRequest; import com.checkout.identities.identityverification.requests.IdentityVerificationRequest; @@ -365,4 +366,16 @@ private IdentityVerificationAttemptsResponse createIdentityVerificationAttemptsR private IdentityVerificationReportResponse createIdentityVerificationReportResponse() { return mock(IdentityVerificationReportResponse.class); } + @Test + void shouldGetAttemptsPaginated() throws ExecutionException, InterruptedException { + final IdentityVerificationAttemptsResponse response = new IdentityVerificationAttemptsResponse(); + final AttemptsQueryFilter query = AttemptsQueryFilter.builder().skip(6).limit(5).build(); + final String id = "identityVerificationId_test"; + + when(apiClient.queryAsync("identity-verifications/" + id + "/attempts", authorization, + query, IdentityVerificationAttemptsResponse.class)) + .thenReturn(CompletableFuture.completedFuture(response)); + + assertEquals(response, client.getIdentityVerificationAttempts(id, query).get()); + } } \ 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 index 05a43ffc..ce34d445 100644 --- a/src/test/java/com/checkout/identities/identityverification/IdentityVerificationTestIT.java +++ b/src/test/java/com/checkout/identities/identityverification/IdentityVerificationTestIT.java @@ -1,5 +1,11 @@ package com.checkout.identities.identityverification; +import com.checkout.identities.entities.IdentityVerificationClientInformation; + +import com.checkout.identities.entities.IdentityDeclaredData; + +import com.checkout.common.CountryCode; + import com.checkout.PlatformType; import com.checkout.SandboxTestFixture; import com.checkout.identities.entities.AttemptAssetsQueryFilter; @@ -320,15 +326,16 @@ private IdentityVerificationAttemptRequest createIdentityVerificationAttemptRequ .build(); } - private DeclaredData createDeclaredData() { - return DeclaredData.builder() + private IdentityDeclaredData createDeclaredData() { + return IdentityDeclaredData.builder() .name("John Doe") + .birthDate("1994-10-15") .build(); } - private ClientInformation createClientInformation() { - return ClientInformation.builder() - .preSelectedResidenceCountry("GB") + private IdentityVerificationClientInformation createClientInformation() { + return IdentityVerificationClientInformation.builder() + .preSelectedResidenceCountry(CountryCode.GB) .preSelectedLanguage("en") .build(); } @@ -387,7 +394,7 @@ private void validateRetrievedIdentityVerificationAttempt(final IdentityVerifica private void validateGeneratedIdentityVerificationReport(final IdentityVerificationReportResponse report) { assertNotNull(report); - assertNotNull(report.getSignedUrl()); + assertNotNull(report.getPdfReport()); // Add specific validation for report data } } \ No newline at end of file diff --git a/src/test/java/com/checkout/issuing/IssuingCardsTestIT.java b/src/test/java/com/checkout/issuing/IssuingCardsTestIT.java index 58af5173..c4968d06 100644 --- a/src/test/java/com/checkout/issuing/IssuingCardsTestIT.java +++ b/src/test/java/com/checkout/issuing/IssuingCardsTestIT.java @@ -324,7 +324,7 @@ private UpdateCardRequest createUpdateCardRequest() { .reference("Updated-Reference-987") .expiryMonth(12) .expiryYear(2025) - .activationDate("2026-06-01T10:00Z") + .scheduledActivationDate("2026-06-01T10:00Z") .revocationDate(java.time.LocalDate.of(2026, 7, 1)) .build(); } diff --git a/src/test/java/com/checkout/issuing/cards/CardActivationDateSerializationTest.java b/src/test/java/com/checkout/issuing/cards/CardScheduledActivationDateSerializationTest.java similarity index 63% rename from src/test/java/com/checkout/issuing/cards/CardActivationDateSerializationTest.java rename to src/test/java/com/checkout/issuing/cards/CardScheduledActivationDateSerializationTest.java index 911d2da8..e30e2ea8 100644 --- a/src/test/java/com/checkout/issuing/cards/CardActivationDateSerializationTest.java +++ b/src/test/java/com/checkout/issuing/cards/CardScheduledActivationDateSerializationTest.java @@ -13,44 +13,52 @@ import java.util.Collections; 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.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** - * GSON serialization tests for the 2026-06-29 card activation/revocation date changes. - * activation_date supports a round-hour datetime; revocation_date is date-only (yyyy-MM-dd). + * GSON serialization tests for the issuing card scheduled_activation_date and revocation_date + * fields. + * + * Swagger 2026-09-02 replaced activation_date with scheduled_activation_date on add-card-request, + * get-card-response and update-card-request. The old key is gone from the API, so these tests + * assert the new key is emitted and the old one is neither emitted nor mapped. + * scheduled_activation_date supports a round-hour datetime; revocation_date is date-only + * (yyyy-MM-dd). */ -class CardActivationDateSerializationTest { +class CardScheduledActivationDateSerializationTest { private final GsonSerializer serializer = new GsonSerializer(); @Test - void shouldSerializeActivationAndRevocationDateOnCreateRequest() { + void shouldSerializeScheduledActivationAndRevocationDateOnCreateRequest() { final VirtualCardRequest request = VirtualCardRequest.builder() .cardholderId("crh_test") - .activationDate("2026-06-01T10:00Z") + .scheduledActivationDate("2026-06-01T10:00Z") .revocationDate(LocalDate.of(2026, 7, 1)) .build(); final String json = serializer.toJson(request); - assertTrue(json.contains("\"activation_date\"")); + assertTrue(json.contains("\"scheduled_activation_date\"")); assertTrue(json.contains("2026-06-01T10:00Z")); assertTrue(json.contains("\"revocation_date\"")); assertTrue(json.contains("2026-07-01")); } @Test - void shouldSerializeActivationAndRevocationDateOnUpdateRequest() { + void shouldSerializeScheduledActivationAndRevocationDateOnUpdateRequest() { final UpdateCardRequest request = UpdateCardRequest.builder() .reference("X-123") - .activationDate("2026-06-01T10:00Z") + .scheduledActivationDate("2026-06-01T10:00Z") .revocationDate(LocalDate.of(2026, 7, 1)) .build(); final String json = serializer.toJson(request); - assertTrue(json.contains("\"activation_date\"")); + assertTrue(json.contains("\"scheduled_activation_date\"")); assertTrue(json.contains("\"revocation_date\"")); assertTrue(json.contains("2026-07-01")); } @@ -58,29 +66,29 @@ void shouldSerializeActivationAndRevocationDateOnUpdateRequest() { @Test void shouldRoundTripUpdateCardRequest() { final UpdateCardRequest original = UpdateCardRequest.builder() - .activationDate("2026-06-01T10:00Z") + .scheduledActivationDate("2026-06-01T10:00Z") .revocationDate(LocalDate.of(2026, 7, 1)) .build(); final UpdateCardRequest deserialized = serializer.fromJson(serializer.toJson(original), UpdateCardRequest.class); - assertEquals(original.getActivationDate(), deserialized.getActivationDate()); + assertEquals(original.getScheduledActivationDate(), deserialized.getScheduledActivationDate()); assertEquals(original.getRevocationDate(), deserialized.getRevocationDate()); } @Test - void shouldDeserializeActivationDateOnCardResponse() { + void shouldDeserializeScheduledActivationDateOnCardResponse() { final String json = "{\"type\":\"virtual\",\"id\":\"crd_test\",\"entity_id\":\"ent_test\"," + "\"user_id\":\"usr_test\",\"scheme\":\"mastercard\",\"root_card_id\":\"crd_root\"," + "\"parent_card_id\":\"crd_parent\",\"revocation_date\":\"2026-07-01\"," - + "\"activation_date\":\"2026-06-01T10:00Z\"}"; + + "\"scheduled_activation_date\":\"2026-06-01T10:00Z\"}"; final VirtualCardDetailsResponse response = serializer.fromJson(json, VirtualCardDetailsResponse.class); assertNotNull(response); - assertEquals("2026-06-01T10:00Z", response.getActivationDate()); + assertEquals("2026-06-01T10:00Z", response.getScheduledActivationDate()); assertEquals("ent_test", response.getEntityId()); assertEquals("usr_test", response.getUserId()); assertEquals(com.checkout.issuing.cards.IssuingScheme.MASTERCARD, response.getScheme()); @@ -109,4 +117,26 @@ void shouldSerializeVirtualCardControlFieldsOnCreateRequest() { assertTrue(json.contains("\"controls\"")); assertTrue(json.contains("\"control_type\":\"mcc_limit\"")); } + + @Test + void shouldNotSerializeTheRemovedActivationDateKey() { + final UpdateCardRequest request = UpdateCardRequest.builder() + .scheduledActivationDate("2026-06-01T10:00Z") + .build(); + + final String json = serializer.toJson(request); + + assertTrue(json.contains("\"scheduled_activation_date\"")); + assertFalse(json.contains("\"activation_date\"")); + } + + @Test + void shouldNotMapTheRemovedActivationDateKeyOnUpdateRequest() { + final String json = "{\"activation_date\":\"2026-06-01T10:00Z\"}"; + + final UpdateCardRequest request = serializer.fromJson(json, UpdateCardRequest.class); + + assertNotNull(request); + assertNull(request.getScheduledActivationDate()); + } } diff --git a/src/test/java/com/checkout/issuing/cards/CardUpdateResponseSerializationTest.java b/src/test/java/com/checkout/issuing/cards/CardUpdateResponseSerializationTest.java new file mode 100644 index 00000000..b11157ec --- /dev/null +++ b/src/test/java/com/checkout/issuing/cards/CardUpdateResponseSerializationTest.java @@ -0,0 +1,104 @@ +package com.checkout.issuing.cards; + +import com.checkout.GsonSerializer; +import com.checkout.issuing.cards.requests.update.CardUpdateHeaders; +import com.checkout.issuing.cards.responses.update.UpdateCardResponse; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +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.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * GSON serialization tests for UpdateCardResponse and CardUpdateHeaders. + * + * Swagger reference: PATCH /issuing/cards/{cardId} + * Schema: update-card-response. Only last_modified_date is required; encrypted_cvv is returned + * solely when the return-encrypted-cvv header is set to true. + */ +class CardUpdateResponseSerializationTest { + + private final GsonSerializer serializer = new GsonSerializer(); + + @Test + void shouldDeserializeWithEncryptedCvv() { + final String json = "{" + + "\"last_modified_date\":\"2026-06-01T10:00:00Z\"," + + "\"encrypted_cvv\":\"ZW5jcnlwdGVkLWN2dg==\"," + + "\"_links\":{\"self\":{\"href\":\"https://api.checkout.com/issuing/cards/crd_test\"}}" + + "}"; + + final UpdateCardResponse response = serializer.fromJson(json, UpdateCardResponse.class); + + assertNotNull(response); + assertNotNull(response.getLastModifiedDate()); + assertEquals("ZW5jcnlwdGVkLWN2dg==", response.getEncryptedCvv()); + } + + /** + * encrypted_cvv is optional: a plain update that did not opt in must leave it null. + */ + @Test + void shouldDeserializeWithoutEncryptedCvv() { + final UpdateCardResponse response = serializer.fromJson( + "{\"last_modified_date\":\"2026-06-01T10:00:00Z\"}", UpdateCardResponse.class); + + assertNotNull(response); + assertNotNull(response.getLastModifiedDate()); + assertNull(response.getEncryptedCvv()); + } + + /** + * Gson HTML-escapes by default, so a Base64 value containing '=' is emitted as \\u003d. The key + * name is what this test pins; the exact value is verified by the round trip below. + */ + @Test + void shouldSerializeEncryptedCvvUsingTheSwaggerKey() { + final UpdateCardResponse response = new UpdateCardResponse(); + response.setEncryptedCvv("ZW5jcnlwdGVkLWN2dg=="); + + final String json = serializer.toJson(response); + + assertTrue(json.contains("\"encrypted_cvv\"")); + assertFalse(json.contains("\"encryptedCvv\"")); + } + + @Test + void shouldRoundTripEncryptedCvvExactly() { + final UpdateCardResponse original = new UpdateCardResponse(); + original.setEncryptedCvv("ZW5jcnlwdGVkLWN2dg=="); + + final UpdateCardResponse deserialized = + serializer.fromJson(serializer.toJson(original), UpdateCardResponse.class); + + assertEquals("ZW5jcnlwdGVkLWN2dg==", deserialized.getEncryptedCvv()); + } + + @Test + void shouldExposeTheUpdateHeadersUsingTheExactSwaggerHeaderNames() { + final CardUpdateHeaders headers = CardUpdateHeaders.builder() + .returnEncryptedCvv(true) + .encryptionKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A") + .build(); + + final Map map = headers.getHeaders(); + + // Header names are case sensitive: return-encrypted-cvv is lower case, + // Encryption-Key is title case. + assertEquals("true", map.get("return-encrypted-cvv")); + assertEquals("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A", map.get("Encryption-Key")); + assertEquals(2, map.size()); + } + + @Test + void shouldOmitUnsetUpdateHeaders() { + final Map map = CardUpdateHeaders.builder().returnEncryptedCvv(true).build().getHeaders(); + + assertEquals(1, map.size()); + assertTrue(map.containsKey("return-encrypted-cvv")); + } +} From f06afe5cba059c63ea5c15d3187c085db39bbae1 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Fri, 18 Sep 2026 11:32:07 +0200 Subject: [PATCH 2/5] Integration tests --- ...AddressDocumentVerificationClientImpl.java | 88 ++++++++++++++ .../FaceAuthenticationAttemptResponse.java | 2 + .../checkout/issuing/IssuingClientImpl.java | 36 ++++++ .../IdentitiesSerializationTest.java | 42 +++++++ .../AddressDocumentVerificationTestIT.java | 114 ++++++++++++++++++ .../FaceAuthenticationTestIT.java | 21 ++++ .../IdDocumentVerificationTestIT.java | 43 +++++++ .../IdentityVerificationTestIT.java | 21 ++++ 8 files changed, 367 insertions(+) create mode 100644 src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationTestIT.java diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java index f24b72b8..21cfbb23 100644 --- a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java +++ b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java @@ -34,6 +34,12 @@ public AddressDocumentVerificationClientImpl(final ApiClient apiClient, final Ch super(apiClient, configuration, SdkAuthorizationType.SECRET_KEY_OR_OAUTH); } + /** + * Create an address document verification + * + * @param addressDocumentVerificationRequest the address document verification request + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationResponse} + */ @Override public CompletableFuture createAddressDocumentVerification( final AddressDocumentVerificationRequest addressDocumentVerificationRequest) { @@ -42,6 +48,12 @@ public CompletableFuture createAddressDocum AddressDocumentVerificationResponse.class, addressDocumentVerificationRequest, null); } + /** + * Retrieve an address document verification + * + * @param addressDocumentVerificationId the address document verification ID + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationResponse} + */ @Override public CompletableFuture getAddressDocumentVerification( final String addressDocumentVerificationId) { @@ -50,6 +62,12 @@ public CompletableFuture getAddressDocument sdkAuthorization(), AddressDocumentVerificationResponse.class); } + /** + * Anonymize an address document verification + * + * @param addressDocumentVerificationId the address document verification ID + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationResponse} + */ @Override public CompletableFuture anonymizeAddressDocumentVerification( final String addressDocumentVerificationId) { @@ -58,6 +76,13 @@ public CompletableFuture anonymizeAddressDo sdkAuthorization(), AddressDocumentVerificationResponse.class, null, null); } + /** + * Create an address document verification attempt + * + * @param addressDocumentVerificationId the address document verification ID + * @param attemptRequest the attempt request + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationAttemptResponse} + */ @Override public CompletableFuture createAddressDocumentVerificationAttempt( final String addressDocumentVerificationId, @@ -69,6 +94,12 @@ public CompletableFuture createAddre null); } + /** + * Retrieve all address document verification attempts + * + * @param addressDocumentVerificationId the address document verification ID + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationAttemptsResponse} + */ @Override public CompletableFuture getAddressDocumentVerificationAttempts( final String addressDocumentVerificationId) { @@ -92,6 +123,13 @@ public CompletableFuture getAddress sdkAuthorization(), queryFilter, AddressDocumentVerificationAttemptsResponse.class); } + /** + * Retrieve a specific address document verification attempt + * + * @param addressDocumentVerificationId the address document verification ID + * @param attemptId the attempt ID + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationAttemptResponse} + */ @Override public CompletableFuture getAddressDocumentVerificationAttempt( final String addressDocumentVerificationId, final String attemptId) { @@ -100,6 +138,12 @@ public CompletableFuture getAddressD sdkAuthorization(), AddressDocumentVerificationAttemptResponse.class); } + /** + * Generate and download a PDF report + * + * @param addressDocumentVerificationId the address document verification ID + * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationReportResponse} + */ @Override public CompletableFuture getAddressDocumentVerificationReport( final String addressDocumentVerificationId) { @@ -126,6 +170,12 @@ public CompletableFuture getAd // Synchronous methods + /** + * Create an address document verification + * + * @param addressDocumentVerificationRequest the address document verification request + * @return the {@link AddressDocumentVerificationResponse} + */ @Override public AddressDocumentVerificationResponse createAddressDocumentVerificationSync( final AddressDocumentVerificationRequest addressDocumentVerificationRequest) { @@ -134,6 +184,12 @@ public AddressDocumentVerificationResponse createAddressDocumentVerificationSync AddressDocumentVerificationResponse.class, addressDocumentVerificationRequest, null); } + /** + * Retrieve an address document verification + * + * @param addressDocumentVerificationId the address document verification ID + * @return the {@link AddressDocumentVerificationResponse} + */ @Override public AddressDocumentVerificationResponse getAddressDocumentVerificationSync(final String addressDocumentVerificationId) { validateParams("addressDocumentVerificationId", addressDocumentVerificationId); @@ -141,6 +197,12 @@ public AddressDocumentVerificationResponse getAddressDocumentVerificationSync(fi sdkAuthorization(), AddressDocumentVerificationResponse.class); } + /** + * Anonymize an address document verification + * + * @param addressDocumentVerificationId the address document verification ID + * @return the {@link AddressDocumentVerificationResponse} + */ @Override public AddressDocumentVerificationResponse anonymizeAddressDocumentVerificationSync(final String addressDocumentVerificationId) { validateParams("addressDocumentVerificationId", addressDocumentVerificationId); @@ -148,6 +210,13 @@ public AddressDocumentVerificationResponse anonymizeAddressDocumentVerificationS sdkAuthorization(), AddressDocumentVerificationResponse.class, null, null); } + /** + * Create an address document verification attempt + * + * @param addressDocumentVerificationId the address document verification ID + * @param attemptRequest the attempt request + * @return the {@link AddressDocumentVerificationAttemptResponse} + */ @Override public AddressDocumentVerificationAttemptResponse createAddressDocumentVerificationAttemptSync( final String addressDocumentVerificationId, @@ -159,6 +228,12 @@ public AddressDocumentVerificationAttemptResponse createAddressDocumentVerificat null); } + /** + * Retrieve all address document verification attempts + * + * @param addressDocumentVerificationId the address document verification ID + * @return the {@link AddressDocumentVerificationAttemptsResponse} + */ @Override public AddressDocumentVerificationAttemptsResponse getAddressDocumentVerificationAttemptsSync(final String addressDocumentVerificationId) { validateParams("addressDocumentVerificationId", addressDocumentVerificationId); @@ -181,6 +256,13 @@ public AddressDocumentVerificationAttemptsResponse getAddressDocumentVerificatio sdkAuthorization(), queryFilter, AddressDocumentVerificationAttemptsResponse.class); } + /** + * Retrieve a specific address document verification attempt + * + * @param addressDocumentVerificationId the address document verification ID + * @param attemptId the attempt ID + * @return the {@link AddressDocumentVerificationAttemptResponse} + */ @Override public AddressDocumentVerificationAttemptResponse getAddressDocumentVerificationAttemptSync( final String addressDocumentVerificationId, final String attemptId) { @@ -189,6 +271,12 @@ public AddressDocumentVerificationAttemptResponse getAddressDocumentVerification sdkAuthorization(), AddressDocumentVerificationAttemptResponse.class); } + /** + * Generate and download a PDF report + * + * @param addressDocumentVerificationId the address document verification ID + * @return the {@link AddressDocumentVerificationReportResponse} + */ @Override public AddressDocumentVerificationReportResponse getAddressDocumentVerificationReportSync(final String addressDocumentVerificationId) { validateParams("addressDocumentVerificationId", addressDocumentVerificationId); diff --git a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java index 417e8da8..9f56ec9b 100644 --- a/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java +++ b/src/main/java/com/checkout/identities/faceauthentications/responses/FaceAuthenticationAttemptResponse.java @@ -4,6 +4,7 @@ import com.checkout.identities.entities.ClientInformation; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -15,6 +16,7 @@ @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) +@Builder @NoArgsConstructor @AllArgsConstructor public final class FaceAuthenticationAttemptResponse extends BaseAttemptResponse { diff --git a/src/main/java/com/checkout/issuing/IssuingClientImpl.java b/src/main/java/com/checkout/issuing/IssuingClientImpl.java index b1377873..4da2d35c 100644 --- a/src/main/java/com/checkout/issuing/IssuingClientImpl.java +++ b/src/main/java/com/checkout/issuing/IssuingClientImpl.java @@ -566,6 +566,13 @@ public CompletableFuture simulateReversal( ); } + /** + * Update a card's details + * + * @param cardId the card ID + * @param updateCardRequest the card fields to update + * @return a {@link CompletableFuture} containing the {@link UpdateCardResponse} + */ @Override public CompletableFuture updateCard(final String cardId, final UpdateCardRequest updateCardRequest) { validateParams("cardId", cardId, "updateCardRequest", updateCardRequest); @@ -578,6 +585,17 @@ public CompletableFuture updateCard(final String cardId, fin ); } + /** + * Update a card's details, sending the optional return-encrypted-cvv and Encryption-Key + * headers. Set return-encrypted-cvv to true to receive the card's encrypted CVV in the + * response; the API returns a 422 with error code encryption_key_required if Encryption-Key + * is not also supplied. + * + * @param cardId the card ID + * @param updateCardRequest the card fields to update + * @param headers the optional return-encrypted-cvv and Encryption-Key headers + * @return a {@link CompletableFuture} containing the {@link UpdateCardResponse} + */ @Override public CompletableFuture updateCard(final String cardId, final UpdateCardRequest updateCardRequest, final CardUpdateHeaders headers) { @@ -1138,6 +1156,13 @@ public CardAuthorizationReversalResponse simulateReversalSync( ); } + /** + * Update a card's details + * + * @param cardId the card ID + * @param updateCardRequest the card fields to update + * @return the {@link UpdateCardResponse} + */ @Override public UpdateCardResponse updateCardSync(final String cardId, final UpdateCardRequest updateCardRequest) { validateParams("cardId", cardId, "updateCardRequest", updateCardRequest); @@ -1150,6 +1175,17 @@ public UpdateCardResponse updateCardSync(final String cardId, final UpdateCardRe ); } + /** + * Update a card's details, sending the optional return-encrypted-cvv and Encryption-Key + * headers. Set return-encrypted-cvv to true to receive the card's encrypted CVV in the + * response; the API returns a 422 with error code encryption_key_required if Encryption-Key + * is not also supplied. + * + * @param cardId the card ID + * @param updateCardRequest the card fields to update + * @param headers the optional return-encrypted-cvv and Encryption-Key headers + * @return the {@link UpdateCardResponse} + */ @Override public UpdateCardResponse updateCardSync(final String cardId, final UpdateCardRequest updateCardRequest, final CardUpdateHeaders headers) { diff --git a/src/test/java/com/checkout/identities/IdentitiesSerializationTest.java b/src/test/java/com/checkout/identities/IdentitiesSerializationTest.java index c917b9d3..05503206 100644 --- a/src/test/java/com/checkout/identities/IdentitiesSerializationTest.java +++ b/src/test/java/com/checkout/identities/IdentitiesSerializationTest.java @@ -515,6 +515,48 @@ void shouldRoundTripTypedCountryCodes() { assertEquals(CountryCode.FR, doc.getNationality()); } + /** + * The tolerant behaviour that makes typing these fields safe: an unrecognised code + * deserializes to null rather than throwing or silently becoming the first enum constant, and + * it does not disturb sibling fields. + */ + @Test + void shouldReadAnUnknownCountryAsNullWithoutDisturbingSiblings() { + final DocumentDetails doc = serializer.fromJson( + "{\"nationality\":\"ZZ\",\"document_issuing_country\":\"GB\"}", DocumentDetails.class); + + assertNotNull(doc); + assertNull(doc.getNationality()); + assertEquals(CountryCode.GB, doc.getDocumentIssuingCountry()); + } + + /** + * CountryCode declares an alpha-3 alternate for every value, so a three-letter code resolves. + * This is a Java-only capability; the .NET enum accepts alpha-2 only. + */ + @Test + void shouldResolveTheAlphaThreeAlternate() { + final DocumentDetails doc = serializer.fromJson("{\"nationality\":\"GBR\"}", DocumentDetails.class); + + assertEquals(CountryCode.GB, doc.getNationality()); + } + + /** + * Pins a known limitation rather than asserting desired behaviour. The spec pattern for + * IdvDocument.nationality and document_issuing_country is ^[A-Za-z]{2}$, so a lowercase code is + * valid per the specification, but CountryCode declares only uppercase values and uppercase + * alpha-3 alternates, so a lowercase code reads as null. Before this row these two fields were + * plain Strings and would have carried the raw value through. Reported internally; if the API + * is confirmed to emit lowercase codes, CountryCode needs lowercase alternates, which is a + * change to a type shared across the whole SDK. + */ + @Test + void shouldCurrentlyReadALowercaseCountryAsNull() { + final DocumentDetails doc = serializer.fromJson("{\"nationality\":\"gb\"}", DocumentDetails.class); + + assertNull(doc.getNationality()); + } + @Test void shouldKeepThePhonePrefixAsAStringNotACountryCode() { // IdvPhoneNumber.country_code is a dialling prefix (pattern ^\+(\d+)$, for example +33), diff --git a/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationTestIT.java b/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationTestIT.java new file mode 100644 index 00000000..0e979736 --- /dev/null +++ b/src/test/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationTestIT.java @@ -0,0 +1,114 @@ +package com.checkout.identities.addressdocumentverification; + +import com.checkout.PlatformType; +import com.checkout.SandboxTestFixture; +import com.checkout.identities.addressdocumentverification.requests.AddressDocumentVerificationAttemptRequest; +import com.checkout.identities.addressdocumentverification.requests.AddressDocumentVerificationRequest; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptAssetsResponse; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptResponse; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationAttemptsResponse; +import com.checkout.identities.addressdocumentverification.responses.AddressDocumentVerificationResponse; +import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; +import com.checkout.identities.entities.DeclaredData; +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; + +/** + * Integration tests for the address document verification attempt-assets endpoint and the + * paginated attempts endpoint added by swagger 2026-09-02. + * + * Scoped to the capabilities this row introduced. The rest of the address document verification + * surface has no integration coverage in this SDK, which predates this row. + */ +class AddressDocumentVerificationTestIT extends SandboxTestFixture { + + AddressDocumentVerificationTestIT() { + super(PlatformType.DEFAULT_OAUTH); + } + + @Test + @Disabled("Integration test - requires valid address document verification and attempt IDs") + void shouldGetAddressDocumentVerificationAttemptAssetsSync() { + // Arrange + final AddressDocumentVerificationResponse created = checkoutApi.addressDocumentVerificationClient() + .createAddressDocumentVerificationSync(createAddressDocumentVerificationRequest()); + final AddressDocumentVerificationAttemptResponse createdAttempt = checkoutApi.addressDocumentVerificationClient() + .createAddressDocumentVerificationAttemptSync(created.getId(), createAttemptRequest()); + final AttemptAssetsQueryFilter queryFilter = AttemptAssetsQueryFilter.builder().skip(0).limit(10).build(); + + // Act + final AddressDocumentVerificationAttemptAssetsResponse assets = checkoutApi.addressDocumentVerificationClient() + .getAddressDocumentVerificationAttemptAssetsSync(created.getId(), createdAttempt.getId(), queryFilter); + + // Assert + assertNotNull(assets); + assertNotNull(assets.getData()); + assertEquals(10, assets.getLimit()); + } + + @Test + @Disabled("Integration test - requires valid address document verification and attempt IDs") + void shouldGetAddressDocumentVerificationAttemptAssetsAsync() { + // Arrange + final AddressDocumentVerificationResponse created = blocking(() -> + checkoutApi.addressDocumentVerificationClient() + .createAddressDocumentVerification(createAddressDocumentVerificationRequest())); + final AddressDocumentVerificationAttemptResponse createdAttempt = blocking(() -> + checkoutApi.addressDocumentVerificationClient() + .createAddressDocumentVerificationAttempt(created.getId(), createAttemptRequest())); + final AttemptAssetsQueryFilter queryFilter = AttemptAssetsQueryFilter.builder().skip(0).limit(10).build(); + + // Act + final AddressDocumentVerificationAttemptAssetsResponse assets = blocking(() -> + checkoutApi.addressDocumentVerificationClient() + .getAddressDocumentVerificationAttemptAssets(created.getId(), createdAttempt.getId(), queryFilter)); + + // Assert + assertNotNull(assets); + assertNotNull(assets.getData()); + } + + @Test + @Disabled("Integration test - requires valid address document verification ID") + void shouldGetAddressDocumentVerificationAttemptsPaginatedSync() { + // Arrange + final AddressDocumentVerificationResponse created = checkoutApi.addressDocumentVerificationClient() + .createAddressDocumentVerificationSync(createAddressDocumentVerificationRequest()); + checkoutApi.addressDocumentVerificationClient() + .createAddressDocumentVerificationAttemptSync(created.getId(), createAttemptRequest()); + final AttemptsQueryFilter queryFilter = AttemptsQueryFilter.builder().skip(0).limit(5).build(); + + // Act + final AddressDocumentVerificationAttemptsResponse attempts = checkoutApi.addressDocumentVerificationClient() + .getAddressDocumentVerificationAttemptsSync(created.getId(), queryFilter); + + // Assert + assertNotNull(attempts); + assertNotNull(attempts.getData()); + assertEquals(5, attempts.getLimit()); + } + + // Common methods + private static AddressDocumentVerificationRequest createAddressDocumentVerificationRequest() { + return AddressDocumentVerificationRequest.builder() + .applicantId("aplt_" + UUID.randomUUID().toString().replace("-", "").substring(0, 26)) + .userJourneyId("usj_" + UUID.randomUUID().toString().replace("-", "").substring(0, 26)) + .declaredData(DeclaredData.builder() + .name("Hannah Bret") + .birthDate("1994-10-15") + .build()) + .build(); + } + + private static AddressDocumentVerificationAttemptRequest createAttemptRequest() { + return AddressDocumentVerificationAttemptRequest.builder() + .document("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...") + .build(); + } +} diff --git a/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationTestIT.java b/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationTestIT.java index cfa44289..178673fa 100644 --- a/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationTestIT.java +++ b/src/test/java/com/checkout/identities/faceauthentications/FaceAuthenticationTestIT.java @@ -5,6 +5,7 @@ import com.checkout.PlatformType; import com.checkout.SandboxTestFixture; import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; import com.checkout.identities.entities.ClientInformation; import com.checkout.identities.faceauthentications.requests.FaceAuthenticationAttemptRequest; import com.checkout.identities.faceauthentications.requests.FaceAuthenticationRequest; @@ -278,6 +279,26 @@ void shouldGetFaceAuthenticationAttemptSync() { } // Common methods + @Test + @Disabled("Integration test - requires valid face authentication ID") + void shouldGetFaceAuthenticationAttemptsPaginatedSync() { + // Arrange + final FaceAuthenticationResponse created = checkoutApi.faceAuthenticationClient() + .createFaceAuthenticationSync(createFaceAuthenticationRequest()); + checkoutApi.faceAuthenticationClient() + .createFaceAuthenticationAttemptSync(created.getId(), createFaceAuthenticationAttemptRequest()); + final AttemptsQueryFilter queryFilter = AttemptsQueryFilter.builder().skip(0).limit(5).build(); + + // Act + final FaceAuthenticationAttemptsResponse attempts = checkoutApi.faceAuthenticationClient() + .getFaceAuthenticationAttemptsSync(created.getId(), queryFilter); + + // Assert + assertNotNull(attempts); + assertNotNull(attempts.getData()); + assertEquals(5, attempts.getLimit()); + } + private static FaceAuthenticationRequest createFaceAuthenticationRequest() { return FaceAuthenticationRequest.builder() .applicantId("aplt_" + generateRandomString(26)) // Mock applicant ID - should be real in integration tests diff --git a/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationTestIT.java b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationTestIT.java index a837d69f..9fcccfeb 100644 --- a/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationTestIT.java +++ b/src/test/java/com/checkout/identities/iddocumentverification/IdDocumentVerificationTestIT.java @@ -2,6 +2,9 @@ import com.checkout.PlatformType; import com.checkout.SandboxTestFixture; +import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; +import com.checkout.identities.iddocumentverification.responses.IdDocumentVerificationAttemptAssetsResponse; import com.checkout.identities.entities.DeclaredData; import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationAttemptRequest; import com.checkout.identities.iddocumentverification.requests.IdDocumentVerificationRequest; @@ -244,6 +247,46 @@ void shouldGetIdDocumentVerificationReportSync() { validateGeneratedIdDocumentVerificationReport(report); } + @Test + @Disabled("Integration test - requires valid ID document verification and attempt IDs") + void shouldGetIdDocumentVerificationAttemptAssetsSync() { + // Arrange + final IdDocumentVerificationResponse created = checkoutApi.idDocumentVerificationClient() + .createIdDocumentVerificationSync(createIdDocumentVerificationRequest()); + final IdDocumentVerificationAttemptResponse createdAttempt = checkoutApi.idDocumentVerificationClient() + .createIdDocumentVerificationAttemptSync(created.getId(), createIdDocumentVerificationAttemptRequest()); + final AttemptAssetsQueryFilter queryFilter = AttemptAssetsQueryFilter.builder().skip(0).limit(10).build(); + + // Act + final IdDocumentVerificationAttemptAssetsResponse assets = checkoutApi.idDocumentVerificationClient() + .getIdDocumentVerificationAttemptAssetsSync(created.getId(), createdAttempt.getId(), queryFilter); + + // Assert + assertNotNull(assets); + assertNotNull(assets.getData()); + assertEquals(10, assets.getLimit()); + } + + @Test + @Disabled("Integration test - requires valid ID document verification ID") + void shouldGetIdDocumentVerificationAttemptsPaginatedSync() { + // Arrange + final IdDocumentVerificationResponse created = checkoutApi.idDocumentVerificationClient() + .createIdDocumentVerificationSync(createIdDocumentVerificationRequest()); + checkoutApi.idDocumentVerificationClient() + .createIdDocumentVerificationAttemptSync(created.getId(), createIdDocumentVerificationAttemptRequest()); + final AttemptsQueryFilter queryFilter = AttemptsQueryFilter.builder().skip(0).limit(5).build(); + + // Act + final IdDocumentVerificationAttemptsResponse attempts = checkoutApi.idDocumentVerificationClient() + .getIdDocumentVerificationAttemptsSync(created.getId(), queryFilter); + + // Assert + assertNotNull(attempts); + assertNotNull(attempts.getData()); + assertEquals(5, attempts.getLimit()); + } + // Common methods private static IdDocumentVerificationRequest createIdDocumentVerificationRequest() { return IdDocumentVerificationRequest.builder() diff --git a/src/test/java/com/checkout/identities/identityverification/IdentityVerificationTestIT.java b/src/test/java/com/checkout/identities/identityverification/IdentityVerificationTestIT.java index ce34d445..0952265d 100644 --- a/src/test/java/com/checkout/identities/identityverification/IdentityVerificationTestIT.java +++ b/src/test/java/com/checkout/identities/identityverification/IdentityVerificationTestIT.java @@ -9,6 +9,7 @@ import com.checkout.PlatformType; import com.checkout.SandboxTestFixture; import com.checkout.identities.entities.AttemptAssetsQueryFilter; +import com.checkout.identities.entities.AttemptsQueryFilter; import com.checkout.identities.entities.ClientInformation; import com.checkout.identities.entities.DeclaredData; import com.checkout.identities.identityverification.requests.CreateAndOpenIdentityVerificationRequest; @@ -312,6 +313,26 @@ private CreateAndOpenIdentityVerificationRequest createCreateAndOpenIdentityVeri .build(); } + @Test + @Disabled("Integration test - requires valid identity verification ID") + void shouldGetIdentityVerificationAttemptsPaginatedSync() { + // Arrange + final IdentityVerificationResponse created = checkoutApi.identityVerificationClient() + .createIdentityVerificationSync(createIdentityVerificationRequest()); + checkoutApi.identityVerificationClient() + .createIdentityVerificationAttemptSync(created.getId(), createIdentityVerificationAttemptRequest()); + final AttemptsQueryFilter queryFilter = AttemptsQueryFilter.builder().skip(0).limit(5).build(); + + // Act + final IdentityVerificationAttemptsResponse attempts = checkoutApi.identityVerificationClient() + .getIdentityVerificationAttemptsSync(created.getId(), queryFilter); + + // Assert + assertNotNull(attempts); + assertNotNull(attempts.getData()); + assertEquals(5, attempts.getLimit()); + } + private IdentityVerificationRequest createIdentityVerificationRequest() { return IdentityVerificationRequest.builder() .applicantId("app_test_" + UUID.randomUUID().toString().substring(0, 8)) From 4c75e6fc8dfeb05211b3653fbac8441038b757e2 Mon Sep 17 00:00:00 2001 From: david-ruiz-cko Date: Tue, 22 Sep 2026 18:04:00 +0200 Subject: [PATCH 3/5] Potential fix for pull request finding 'CodeQL / Spurious Javadoc @param tags' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .../AddressDocumentVerificationClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java index 21cfbb23..71b53919 100644 --- a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java +++ b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java @@ -80,7 +80,7 @@ public CompletableFuture anonymizeAddressDo * Create an address document verification attempt * * @param addressDocumentVerificationId the address document verification ID - * @param attemptRequest the attempt request + * @param addressDocumentVerificationAttemptRequest the attempt request * @return a {@link CompletableFuture} containing the {@link AddressDocumentVerificationAttemptResponse} */ @Override From 9808c91cf010f2277b9d8591f8e7640a1a019738 Mon Sep 17 00:00:00 2001 From: david-ruiz-cko Date: Tue, 22 Sep 2026 18:04:11 +0200 Subject: [PATCH 4/5] Potential fix for pull request finding 'CodeQL / Spurious Javadoc @param tags' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .../AddressDocumentVerificationClientImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java index 71b53919..d13fe834 100644 --- a/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java +++ b/src/main/java/com/checkout/identities/addressdocumentverification/AddressDocumentVerificationClientImpl.java @@ -214,7 +214,7 @@ public AddressDocumentVerificationResponse anonymizeAddressDocumentVerificationS * Create an address document verification attempt * * @param addressDocumentVerificationId the address document verification ID - * @param attemptRequest the attempt request + * @param addressDocumentVerificationAttemptRequest the attempt request * @return the {@link AddressDocumentVerificationAttemptResponse} */ @Override From db4b4f43f852de71aa3892d0482cf554ea359dd1 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Tue, 22 Sep 2026 19:22:28 +0200 Subject: [PATCH 5/5] Test currency fix --- .../java/com/checkout/accounts/AccountsTestIT.java | 13 +++++++++---- .../java/com/checkout/payments/PayoutsTestIT.java | 6 +++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/checkout/accounts/AccountsTestIT.java b/src/test/java/com/checkout/accounts/AccountsTestIT.java index b86632e0..895f4c04 100644 --- a/src/test/java/com/checkout/accounts/AccountsTestIT.java +++ b/src/test/java/com/checkout/accounts/AccountsTestIT.java @@ -708,9 +708,14 @@ private static OnboardEntityRequest buildCompanyEntity(final String randomRefere // Builds an onboarding request that conforms to the Accounts API v3.0 schema: the representative // carries nested `individual` details + `roles`, the company has `businessType` + - // `dateOfIncorporation`, and `processingDetails` is populated. The holding-currency scope is - // configured on the platform (USD here) while `processingDetails.currency` reflects the - // sub-entity region (GBP) — the two are independent. + // `dateOfIncorporation`, and `processingDetails` is populated. + // + // Every currency here has to sit inside the platform's currency scope, which is USD only. This + // previously set `processingDetails.currency` to GBP on the theory that the profile reflects the + // platform while processing details reflect the sub-entity region, and that the two are + // independent. The API rejects that with `processing_details_currency_invalid_for_currency_scope`. + // Widening the profile to GBP instead is also rejected, because the scope itself does not permit + // GBP. The GB addresses and `settlementCountry` are unaffected and still accepted. private static OnboardEntityRequest buildCompanyEntityV3(final String randomReference) { final Address address = Address.builder() .addressLine1("90 Tottenham Court Road") @@ -774,7 +779,7 @@ private static OnboardEntityRequest buildCompanyEntityV3(final String randomRefe .averageTransactionValue(5000) .averageOrderFulfillmentTime(3) .highestTransactionValue(25000) - .currency(Currency.GBP) + .currency(Currency.USD) .settlementCountry(CountryCode.GB.name()) .targetCountries(Collections.singletonList(CountryCode.GB.name())) .payments(ProcessingDetailsPayments.builder() diff --git a/src/test/java/com/checkout/payments/PayoutsTestIT.java b/src/test/java/com/checkout/payments/PayoutsTestIT.java index 846b4538..ba707c9d 100644 --- a/src/test/java/com/checkout/payments/PayoutsTestIT.java +++ b/src/test/java/com/checkout/payments/PayoutsTestIT.java @@ -50,7 +50,11 @@ private PayoutRequest createPayoutRequest() { .source(createPayoutSource()) .destination(createCardDestination()) .amount(10L) - .currency(Currency.EUR) + // The sandbox currency account this draws from (ca_qcc7x4...) settles GBP only. + // Asking for EUR is rejected with a 422 carrying an empty error_codes array, so the + // cause is not visible in the response. GBP also matches the GB sender and billing + // addresses below. + .currency(Currency.GBP) .sender(createPayoutSender()) .reference("Pay-out to Card - Money Transfer") .billingDescriptor(createPayoutBillingDescriptor())