From 8c4bbe85c231dce1f9c8d462e1b4c850799e4bee Mon Sep 17 00:00:00 2001
From: david ruiz
Date: Thu, 1 Oct 2026 12:49:54 +0200
Subject: [PATCH 1/8] Representative documents alignment
---
.../com/checkout/accounts/AccountPhone.java | 12 ++
.../com/checkout/accounts/AccountsClient.java | 50 +++++
.../accounts/AccountsFilePurpose.java | 19 +-
.../checkout/accounts/AdditionalDocument.java | 10 +
.../accounts/ArticlesOfAssociation.java | 12 +-
.../accounts/ArticlesOfAssociationType.java | 3 +
.../checkout/accounts/BankVerification.java | 13 ++
.../accounts/BankVerificationType.java | 3 +
.../CertifiedAuthorisedSignatory.java | 34 ++++
.../CertifiedAuthorisedSignatoryType.java | 13 ++
.../accounts/CompanyVerification.java | 18 +-
.../accounts/CompanyVerificationType.java | 5 +
.../com/checkout/accounts/ContactDetails.java | 27 +++
.../java/com/checkout/accounts/Document.java | 20 ++
.../accounts/EntityEmailAddresses.java | 8 +
.../accounts/FinancialStatements.java | 15 ++
.../accounts/FinancialStatementsType.java | 4 +
.../accounts/FinancialVerification.java | 15 ++
.../accounts/FinancialVerificationType.java | 4 +
.../com/checkout/accounts/Identification.java | 18 ++
.../com/checkout/accounts/Individual.java | 52 ++++-
.../java/com/checkout/accounts/Invitee.java | 10 +
.../OnboardEntityDetailsResponse.java | 46 +++++
.../accounts/OnboardSubEntityDocuments.java | 110 +++++++++++
.../checkout/accounts/ProofOfLegality.java | 13 ++
.../accounts/ProofOfLegalityType.java | 3 +
.../accounts/ProofOfPrincipalAddress.java | 13 ++
.../accounts/ProofOfPrincipalAddressType.java | 5 +
.../accounts/ProofOfRegistration.java | 33 ++++
.../accounts/ProofOfRegistrationType.java | 17 ++
.../accounts/ProofOfResidentialAddress.java | 33 ++++
.../ProofOfResidentialAddressType.java | 15 ++
.../com/checkout/accounts/Representative.java | 82 ++++++++
.../accounts/RepresentativeIndividual.java | 61 ++++++
.../accounts/ShareholderStructure.java | 14 ++
.../accounts/ShareholderStructureType.java | 3 +
.../checkout/accounts/TaxVerification.java | 17 +-
.../accounts/TaxVerificationType.java | 4 +
.../accounts/files/entities/FilePurpose.java | 3 +
.../files/request/FileUploadRequest.java | 10 +-
.../files/response/FileDetailsResponse.java | 29 ++-
.../files/response/FileUploadResponse.java | 16 +-
.../com/checkout/common/DocumentType.java | 3 +
.../com/checkout/accounts/AccountsTestIT.java | 66 +++++++
.../accounts/AccountsV3SerializationTest.java | 57 ++++++
...rdSubEntityDocumentsSerializationTest.java | 187 ++++++++++++++++++
46 files changed, 1194 insertions(+), 11 deletions(-)
create mode 100644 src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatory.java
create mode 100644 src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatoryType.java
create mode 100644 src/main/java/com/checkout/accounts/ProofOfRegistration.java
create mode 100644 src/main/java/com/checkout/accounts/ProofOfRegistrationType.java
create mode 100644 src/main/java/com/checkout/accounts/ProofOfResidentialAddress.java
create mode 100644 src/main/java/com/checkout/accounts/ProofOfResidentialAddressType.java
diff --git a/src/main/java/com/checkout/accounts/AccountPhone.java b/src/main/java/com/checkout/accounts/AccountPhone.java
index 770b3e0c..ccb5f8a0 100644
--- a/src/main/java/com/checkout/accounts/AccountPhone.java
+++ b/src/main/java/com/checkout/accounts/AccountPhone.java
@@ -6,14 +6,26 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * A phone number on the Accounts API: the sub-entity's contact phone, or a representative's phone.
+ * See {@link ContactDetails} for the per-variant number format.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class AccountPhone {
+ /**
+ * The ISO 3166-1 alpha-2 country where the number is registered, not the dialling code.
+ * [Required] on Accounts API v3.0; not part of the v2.0 schemas.
+ */
private CountryCode countryCode;
+ /**
+ * The phone number, without the country calling code.
+ * [Required]
+ */
private String number;
}
diff --git a/src/main/java/com/checkout/accounts/AccountsClient.java b/src/main/java/com/checkout/accounts/AccountsClient.java
index f9a1a9ba..e91dd26d 100644
--- a/src/main/java/com/checkout/accounts/AccountsClient.java
+++ b/src/main/java/com/checkout/accounts/AccountsClient.java
@@ -18,10 +18,35 @@
public interface AccountsClient {
+ /**
+ * Uploads a file to the Files API (POST /files on the Files host), as a multipart request. The
+ * returned ID is what document {@code front} and {@code back} fields take.
+ *
+ * @param accountsFileRequest the path to the file, its content type, and its purpose
+ * @return the ID of the uploaded file
+ */
CompletableFuture submitFile(AccountsFileRequest accountsFileRequest);
+ /**
+ * Creates a file upload for a sub-entity (POST /entities/{entityId}/files on the Files host).
+ * The response carries the file ID and an upload link; the file content itself is sent to that
+ * link, not in this request.
+ *
+ * @param entityId the ID of the sub-entity
+ * @param fileUploadRequest the purpose of the file upload
+ * @return the file ID, the maximum size allowed, the MIME types allowed for the purpose, and the
+ * upload link
+ */
CompletableFuture uploadFile(String entityId, FileUploadRequest fileUploadRequest);
+ /**
+ * Retrieves the details of a sub-entity's file (GET /entities/{entityId}/files/{fileId} on the
+ * Files host).
+ *
+ * @param entityId the ID of the sub-entity
+ * @param fileId the ID of the file
+ * @return the file's status, size, MIME type, upload date and purpose
+ */
CompletableFuture retrieveFile(String entityId, String fileId);
CompletableFuture createEntity(OnboardEntityRequest entityRequest);
@@ -99,10 +124,35 @@ CompletableFuture updateReserveRule(String entityId,
CompletableFuture resolveEntityRequirement(String entityId, String requirementId, EntityRequirementUpdateRequest updateRequest);
// Synchronous methods
+ /**
+ * Uploads a file to the Files API (POST /files on the Files host), as a multipart request. The
+ * returned ID is what document {@code front} and {@code back} fields take.
+ *
+ * @param accountsFileRequest the path to the file, its content type, and its purpose
+ * @return the ID of the uploaded file
+ */
IdResponse submitFileSync(final AccountsFileRequest accountsFileRequest);
+ /**
+ * Creates a file upload for a sub-entity (POST /entities/{entityId}/files on the Files host).
+ * The response carries the file ID and an upload link; the file content itself is sent to that
+ * link, not in this request.
+ *
+ * @param entityId the ID of the sub-entity
+ * @param fileUploadRequest the purpose of the file upload
+ * @return the file ID, the maximum size allowed, the MIME types allowed for the purpose, and the
+ * upload link
+ */
FileUploadResponse uploadFileSync(final String entityId, final FileUploadRequest fileUploadRequest);
+ /**
+ * Retrieves the details of a sub-entity's file (GET /entities/{entityId}/files/{fileId} on the
+ * Files host).
+ *
+ * @param entityId the ID of the sub-entity
+ * @param fileId the ID of the file
+ * @return the file's status, size, MIME type, upload date and purpose
+ */
FileDetailsResponse retrieveFileSync(final String entityId, final String fileId);
OnboardEntityResponse createEntitySync(final OnboardEntityRequest entityRequest);
diff --git a/src/main/java/com/checkout/accounts/AccountsFilePurpose.java b/src/main/java/com/checkout/accounts/AccountsFilePurpose.java
index 83a1a71a..434880fe 100644
--- a/src/main/java/com/checkout/accounts/AccountsFilePurpose.java
+++ b/src/main/java/com/checkout/accounts/AccountsFilePurpose.java
@@ -2,14 +2,31 @@
import lombok.Getter;
+/**
+ * The purpose of a file uploaded with {@link AccountsClient#submitFile(AccountsFileRequest)}. The
+ * values match the purposes the Accounts API accepts for onboarding documents
+ * ({@code PlatformsFileUpload}), plus the legacy {@link #IDENTIFICATION}.
+ */
public enum AccountsFilePurpose {
BANK_VERIFICATION("bank_verification"),
+ /**
+ * Legacy purpose, not among the onboarding upload purposes; use {@link #IDENTITY_VERIFICATION}.
+ */
IDENTIFICATION("identification"),
IDENTITY_VERIFICATION("identity_verification"),
COMPANY_VERIFICATION("company_verification"),
FINANCIAL_VERIFICATION("financial_verification"),
- TAX_VERIFICATION("tax_verification");
+ TAX_VERIFICATION("tax_verification"),
+ ADDITIONAL_DOCUMENT("additional_document"),
+ ARTICLES_OF_ASSOCIATION("articles_of_association"),
+ CERTIFIED_AUTHORISED_SIGNATORY("certified_authorised_signatory"),
+ COMPANY_OWNERSHIP("company_ownership"),
+ PROOF_OF_LEGALITY("proof_of_legality"),
+ PROOF_OF_PRINCIPAL_ADDRESS("proof_of_principal_address"),
+ SHAREHOLDER_STRUCTURE("shareholder_structure"),
+ PROOF_OF_RESIDENTIAL_ADDRESS("proof_of_residential_address"),
+ PROOF_OF_REGISTRATION("proof_of_registration");
@Getter
private final String purpose;
diff --git a/src/main/java/com/checkout/accounts/AdditionalDocument.java b/src/main/java/com/checkout/accounts/AdditionalDocument.java
index ead77536..029a26b2 100644
--- a/src/main/java/com/checkout/accounts/AdditionalDocument.java
+++ b/src/main/java/com/checkout/accounts/AdditionalDocument.java
@@ -5,12 +5,22 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Additional space for documents to be provided when requested. Carries a file ID only; the API
+ * defines no document type for it.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class AdditionalDocument {
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java b/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java
index 0b136f40..110edf0b 100644
--- a/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java
+++ b/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java
@@ -8,7 +8,8 @@
/**
* Memorandum or articles of association document, supplied when onboarding a sub-entity.
*
- * Required on the company full onboarding variants. The API expects an object carrying the
+ *
Required on EEA and GB Company Full (3.0); optional on US Company Full (3.0) and the US ISV
+ * Seller variants. The API expects an object carrying the
* document type and the uploaded file ID, which is why this class exists: the field on
* {@link OnboardSubEntityDocuments} used to be the {@link ArticlesOfAssociationType} enum, so
* the SDK serialized a bare string and the API rejected the request.
@@ -20,13 +21,16 @@
public final class ArticlesOfAssociation {
/**
- * The type of document being used as the memorandum or articles of association.
+ * The type of document used.
+ * [Required]
*/
private ArticlesOfAssociationType type;
/**
- * The ID of the front side of the document as represented within Checkout.com systems,
- * as returned when the file was uploaded.
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
*/
private String front;
diff --git a/src/main/java/com/checkout/accounts/ArticlesOfAssociationType.java b/src/main/java/com/checkout/accounts/ArticlesOfAssociationType.java
index 4ed853fa..31ab89ca 100644
--- a/src/main/java/com/checkout/accounts/ArticlesOfAssociationType.java
+++ b/src/main/java/com/checkout/accounts/ArticlesOfAssociationType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document types accepted as memorandum or articles of association.
+ */
public enum ArticlesOfAssociationType {
@SerializedName("memorandum_of_association")
diff --git a/src/main/java/com/checkout/accounts/BankVerification.java b/src/main/java/com/checkout/accounts/BankVerification.java
index 0e01c9f7..2201abb9 100644
--- a/src/main/java/com/checkout/accounts/BankVerification.java
+++ b/src/main/java/com/checkout/accounts/BankVerification.java
@@ -5,14 +5,27 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * A document showing transactions from the last 3 months.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class BankVerification {
+ /**
+ * The type of document being used as bank verification.
+ * [Required]
+ */
private BankVerificationType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/BankVerificationType.java b/src/main/java/com/checkout/accounts/BankVerificationType.java
index 2ab7293b..3bb2d005 100644
--- a/src/main/java/com/checkout/accounts/BankVerificationType.java
+++ b/src/main/java/com/checkout/accounts/BankVerificationType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as bank verification.
+ */
public enum BankVerificationType {
@SerializedName("bank_statement")
diff --git a/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatory.java b/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatory.java
new file mode 100644
index 00000000..938d3230
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatory.java
@@ -0,0 +1,34 @@
+package com.checkout.accounts;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Certified authorised signatory document. Required when the legal representative or other role
+ * owner is not registered on the certificate of incorporation. Representative documents only
+ * ({@code company.representatives[].documents}), EEA, GB and US Company Full (3.0) and US ISV
+ * Seller Company (3.0); not accepted at the top level.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public final class CertifiedAuthorisedSignatory {
+
+ /**
+ * The type of document.
+ * [Required]
+ */
+ private CertifiedAuthorisedSignatoryType type;
+
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
+ private String front;
+
+}
diff --git a/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatoryType.java b/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatoryType.java
new file mode 100644
index 00000000..9b3e7cbf
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatoryType.java
@@ -0,0 +1,13 @@
+package com.checkout.accounts;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The document type accepted as a representative's certified authorised signatory document.
+ */
+public enum CertifiedAuthorisedSignatoryType {
+
+ @SerializedName("power_of_attorney")
+ POWER_OF_ATTORNEY
+
+}
diff --git a/src/main/java/com/checkout/accounts/CompanyVerification.java b/src/main/java/com/checkout/accounts/CompanyVerification.java
index 2e718eb0..f4e44b30 100644
--- a/src/main/java/com/checkout/accounts/CompanyVerification.java
+++ b/src/main/java/com/checkout/accounts/CompanyVerification.java
@@ -5,13 +5,29 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The document to use to confirm the company's identity (certified by a power of attorney within
+ * the last 3 months).
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class CompanyVerification {
- private TaxVerificationType type;
+ /**
+ * The type of document used for company verification. {@code articles_of_association} is
+ * accepted on the US Company (2.0) variants only.
+ * [Required]
+ */
+ private CompanyVerificationType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
+
}
diff --git a/src/main/java/com/checkout/accounts/CompanyVerificationType.java b/src/main/java/com/checkout/accounts/CompanyVerificationType.java
index 6fa5271b..6471a501 100644
--- a/src/main/java/com/checkout/accounts/CompanyVerificationType.java
+++ b/src/main/java/com/checkout/accounts/CompanyVerificationType.java
@@ -2,6 +2,11 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document types accepted as company verification. {@code articles_of_association} is
+ * accepted on the US Company (2.0) variants only; articles of association sent as their own
+ * document use {@link ArticlesOfAssociationType} instead.
+ */
public enum CompanyVerificationType {
@SerializedName("incorporation_document")
diff --git a/src/main/java/com/checkout/accounts/ContactDetails.java b/src/main/java/com/checkout/accounts/ContactDetails.java
index 3d5f129e..ccb243b7 100644
--- a/src/main/java/com/checkout/accounts/ContactDetails.java
+++ b/src/main/java/com/checkout/accounts/ContactDetails.java
@@ -5,16 +5,43 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Contact details of the sub-entity.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public final class ContactDetails {
+ /**
+ * The phone number of the sub-entity.
+ * [Required] for every Accounts API v2.0 variant and the US ISV Seller variants; [Optional] for the
+ * other v3.0 variants. On v3.0 {@code countryCode} is required and is the ISO 3166-1 alpha-2
+ * country where the number is registered (for example {@code FR}), not the dialling code; v2.0
+ * takes {@code number} only. {@code number} is the number without the country calling code, and
+ * its format depends on the variant:
+ *
+ * - v3.0 EEA: ^[0-9]{6,13}$, min 6 characters, max 13 characters
+ * - v3.0 GB: ^[0-9]{7,11}$, min 7 characters, max 11 characters
+ * - v3.0 US and US ISV Seller: ^[1-9][0-9]{9,16}$, min 10 characters, max 16 characters
+ * - v2.0: ^[1-9][0-9]{7,15}$, min 8 characters, max 16 characters; on the US v2.0 variants
+ * ^[2-9]{1}[0-9]{9,15}$, min 10 characters
+ *
+ */
private AccountPhone phone;
+ /**
+ * Email addresses for this sub-entity.
+ * [Required] for every Accounts API v2.0 variant and the US ISV Seller variants; [Optional] for the
+ * other v3.0 variants.
+ */
private EntityEmailAddresses emailAddresses;
+ /**
+ * The details of the user responsible for onboarding the sub-entity.
+ * [Optional] (not part of the US ISV Seller variants)
+ */
private Invitee invitee;
}
diff --git a/src/main/java/com/checkout/accounts/Document.java b/src/main/java/com/checkout/accounts/Document.java
index ac39206b..4aea3d52 100644
--- a/src/main/java/com/checkout/accounts/Document.java
+++ b/src/main/java/com/checkout/accounts/Document.java
@@ -6,16 +6,36 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The document to use to confirm an individual's identity ({@code identity_verification}): on a
+ * representative (Accounts API v3.0), or at the top level of the v2.0 sole trader variants.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public final class Document {
+ /**
+ * The type of document used for identity verification.
+ * [Required]
+ */
private DocumentType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
+ /**
+ * The ID of the back side of the document as represented within Checkout.com systems.
+ * [Optional]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String back;
}
diff --git a/src/main/java/com/checkout/accounts/EntityEmailAddresses.java b/src/main/java/com/checkout/accounts/EntityEmailAddresses.java
index 0bbbd840..53a178e7 100644
--- a/src/main/java/com/checkout/accounts/EntityEmailAddresses.java
+++ b/src/main/java/com/checkout/accounts/EntityEmailAddresses.java
@@ -5,12 +5,20 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Email addresses for this sub-entity.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public final class EntityEmailAddresses {
+ /**
+ * The main email address for this sub-entity.
+ * [Required]
+ * Format: email
+ */
private String primary;
}
diff --git a/src/main/java/com/checkout/accounts/FinancialStatements.java b/src/main/java/com/checkout/accounts/FinancialStatements.java
index a93a815a..ae60b0f4 100644
--- a/src/main/java/com/checkout/accounts/FinancialStatements.java
+++ b/src/main/java/com/checkout/accounts/FinancialStatements.java
@@ -5,14 +5,29 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Audited or management-prepared financial statements (when applicable). US ISV Seller variants
+ * only. Not the same document as {@link FinancialVerification}, whose type is the singular
+ * {@code financial_statement}.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class FinancialStatements {
+ /**
+ * The type of document.
+ * [Required]
+ */
private FinancialStatementsType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/FinancialStatementsType.java b/src/main/java/com/checkout/accounts/FinancialStatementsType.java
index b373a10d..dc147912 100644
--- a/src/main/java/com/checkout/accounts/FinancialStatementsType.java
+++ b/src/main/java/com/checkout/accounts/FinancialStatementsType.java
@@ -2,6 +2,10 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as financial statements (US ISV Seller variants). Note the plural
+ * {@code financial_statements}; {@link FinancialVerificationType} is a different enum.
+ */
public enum FinancialStatementsType {
@SerializedName("financial_statements")
diff --git a/src/main/java/com/checkout/accounts/FinancialVerification.java b/src/main/java/com/checkout/accounts/FinancialVerification.java
index f7abbf14..efdb4b75 100644
--- a/src/main/java/com/checkout/accounts/FinancialVerification.java
+++ b/src/main/java/com/checkout/accounts/FinancialVerification.java
@@ -5,14 +5,29 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Financial statement document. Becomes mandatory depending on the answer provided for
+ * {@code annual_processing_volume}; the sub-entity's status changes to {@code requirements_due}
+ * when it is needed.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class FinancialVerification {
+ /**
+ * The type of the file.
+ * [Required]
+ */
private FinancialVerificationType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/FinancialVerificationType.java b/src/main/java/com/checkout/accounts/FinancialVerificationType.java
index d1b14f21..56dcb142 100644
--- a/src/main/java/com/checkout/accounts/FinancialVerificationType.java
+++ b/src/main/java/com/checkout/accounts/FinancialVerificationType.java
@@ -2,6 +2,10 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as financial verification. Note the singular
+ * {@code financial_statement}; {@link FinancialStatementsType} is a different enum.
+ */
public enum FinancialVerificationType {
@SerializedName("financial_statement")
diff --git a/src/main/java/com/checkout/accounts/Identification.java b/src/main/java/com/checkout/accounts/Identification.java
index 5331ba44..1e726d11 100644
--- a/src/main/java/com/checkout/accounts/Identification.java
+++ b/src/main/java/com/checkout/accounts/Identification.java
@@ -5,14 +5,32 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The identification of a representative or individual on the Accounts API v2.0 US variants.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class Identification {
+ /**
+ * Social Security Number (SSN), or Individual Taxpayer Identification Number (ITIN) for non-US
+ * citizens.
+ * [Required]
+ * ^\d{9}$
+ * 9 characters
+ */
private String nationalIdNumber;
+ /**
+ * Not defined by the Accounts API: the identification object carries {@code national_id_number}
+ * only. Retained so existing code keeps compiling.
+ *
+ * @deprecated Not part of any Accounts API schema; the API does not read it. Will be removed in a
+ * future major version.
+ */
+ @Deprecated
private Document document;
}
diff --git a/src/main/java/com/checkout/accounts/Individual.java b/src/main/java/com/checkout/accounts/Individual.java
index 8f7da8a9..bba1878b 100644
--- a/src/main/java/com/checkout/accounts/Individual.java
+++ b/src/main/java/com/checkout/accounts/Individual.java
@@ -4,28 +4,78 @@
import lombok.Builder;
import lombok.Data;
+/**
+ * The top-level {@code individual} of the Accounts API v2.0 sole trader variants.
+ */
@Data
@Builder
public final class Individual {
+ /**
+ * The individual's first name.
+ * [Required]
+ * min 2 characters, max 50 characters
+ */
private String firstName;
+ /**
+ * The individual's middle name. Required if it appears in official documents.
+ * [Optional]
+ * min 2 characters, max 50 characters
+ */
private String middleName;
+ /**
+ * The individual's last name.
+ * [Required]
+ * min 2 characters, max 50 characters
+ */
private String lastName;
+ /**
+ * The trading name of the sub-entity, also referred to as 'doing business as'.
+ * [Required]
+ * min 2 characters, max 300 characters
+ */
private String tradingName;
+ /**
+ * Not defined by any Accounts API schema. Retained so existing code keeps compiling.
+ *
+ * @deprecated Not defined by any Accounts API schema; the API does not read it. Will be removed in
+ * a future major version.
+ */
+ @Deprecated
private String nationalTaxId;
+ /**
+ * The registered address of the sole trader's business.
+ * [Required]
+ */
private Address registeredAddress;
+ /**
+ * The date of birth of the person according to the Gregorian calendar.
+ * [Required], except on GB Sole Trader Lite (2.0) where it is [Optional].
+ */
private DateOfBirth dateOfBirth;
+ /**
+ * The place of birth of the person.
+ * [Required] for EEA Sole Trader Full and Lite (2.0); not part of the other v2.0 variants.
+ */
private PlaceOfBirth placeOfBirth;
+ /**
+ * The individual's identification. US Sole Trader (2.0) only.
+ * [Required] for US Sole Trader Full (2.0); [Optional] for US Sole Trader Lite (2.0).
+ */
private Identification identification;
-
+
+ /**
+ * Seller financial questions and supporting documents. US Sole Trader (2.0) only.
+ * [Required] for US Sole Trader Full (2.0); [Optional] for US Sole Trader Lite (2.0).
+ */
private EntityFinancialDetails financialDetails;
}
diff --git a/src/main/java/com/checkout/accounts/Invitee.java b/src/main/java/com/checkout/accounts/Invitee.java
index c39ae45d..f9c137f9 100644
--- a/src/main/java/com/checkout/accounts/Invitee.java
+++ b/src/main/java/com/checkout/accounts/Invitee.java
@@ -5,11 +5,21 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The details of the user responsible for onboarding the sub-entity.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public final class Invitee {
+ /**
+ * The main email address for this sub-entity. Despite the spec's wording, this is the address of
+ * the invitee, the user responsible for onboarding the sub-entity.
+ * [Optional]
+ * Format: email
+ */
private String email;
+
}
diff --git a/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java b/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
index bf521a7d..81b13234 100644
--- a/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
+++ b/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
@@ -7,29 +7,75 @@
import java.util.List;
+/**
+ * The details of a sub-entity, as returned by GET /accounts/entities/{id}.
+ */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public final class OnboardEntityDetailsResponse extends Resource {
+ /**
+ * The ID of the sub-entity.
+ */
private String id;
+ /**
+ * A unique reference you can later use to identify the sub-entity.
+ */
private String reference;
+ /**
+ * The onboarding status of the sub-entity.
+ */
private OnboardingStatus status;
+ /**
+ * The capabilities of the entity.
+ */
private Capabilities capabilities;
+ /**
+ * List of requirements due in order to be onboarded.
+ */
private List requirementsDue;
+ /**
+ * Contact details of this sub-entity.
+ */
private ContactDetails contactDetails;
+ /**
+ * Information about the profile of the sub-entity, primarily regarding the products and services
+ * offered.
+ */
private Profile profile;
+ /**
+ * Information about the company represented by the sub-entity (company and v3.0 sole trader
+ * variants).
+ */
private Company company;
+ /**
+ * Information about the individual represented by the sub-entity (v2.0 sole trader variants).
+ */
private Individual individual;
+ /**
+ * The sub-entity's payment instruments.
+ */
private List instruments;
+ /**
+ * The sub-entity's expected processing (Accounts API v3.0).
+ */
+ private ProcessingDetails processingDetails;
+
+ /**
+ * The top-level documents used to support the verification of the sub-entity's details.
+ * Representative documents are on {@link Representative}, under {@code company}.
+ */
+ private OnboardSubEntityDocuments documents;
+
}
diff --git a/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java b/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java
index 1784e352..9394cc3a 100644
--- a/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java
+++ b/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java
@@ -6,39 +6,149 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Verification documents for a sub-entity. This one type serves two different objects on the
+ * Accounts API, which accept different keys:
+ *
+ * - The top-level request {@code documents}, on {@link OnboardEntityRequest}. The API
+ * ignores keys it does not recognise here rather than rejecting them, so a misplaced document is
+ * dropped silently.
+ * - A representative's {@code documents}, on {@link Representative}. This object is
+ * strict: it accepts only {@code identity_verification}, {@code certified_authorised_signatory},
+ * {@code proof_of_residential_address} and {@code proof_of_registration}, and rejects any other
+ * key.
+ *
+ * Each field below says which of the two it belongs to.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class OnboardSubEntityDocuments {
+ // Both
+
+ /**
+ * The document to use to confirm the individual's identity. Valid in both objects:
+ *
+ * - Representative: [Required] for the EEA, GB and US Sole Trader Full (3.0) variants;
+ * [Optional] for the company variants.
+ * - Top level: [Required] for the six sole trader variants of Accounts API v2.0, the only
+ * variants that take it there.
+ *
+ */
private Document identityVerification;
+ // Top level
+
+ /**
+ * The document to use to confirm the company's identity (certified by a power of attorney
+ * within the last 3 months). Top level only.
+ * [Required] for EEA Company Full (2.0 and 3.0) and GB Company Full (2.0); [Optional] for the
+ * other company variants and the US ISV Seller variants.
+ */
private CompanyVerification companyVerification;
+ /**
+ * Memorandum or Articles of Association document. Top level only.
+ * [Required] for EEA and GB Company Full (3.0); [Optional] for US Company Full (3.0) and the US
+ * ISV Seller variants.
+ */
private ArticlesOfAssociation articlesOfAssociation;
+ /**
+ * A document showing transactions from the last 3 months. Top level only.
+ * [Required] for EEA Company Full (3.0) and the EEA, GB and US Sole Trader Full (3.0) variants;
+ * [Optional] for GB and US Company Full (3.0) and EEA Company Full and Lite (2.0).
+ */
private BankVerification bankVerification;
+ /**
+ * Shareholder structure chart (including % of shares) certified by a competent authority
+ * individual and dated within the last 3 months. Top level only.
+ * [Required] for EEA and GB Company Full (3.0); [Optional] for US Company Full (3.0) and US ISV
+ * Seller Company (3.0).
+ */
private ShareholderStructure shareholderStructure;
+ /**
+ * A regulatory licence document required for the company to operate (when applicable). Top
+ * level only.
+ * [Optional] (EEA, GB and US Company Full (3.0) and the US ISV Seller variants)
+ */
private ProofOfLegality proofOfLegality;
+ /**
+ * Proof of the company's principal place of business. Top level only.
+ * [Optional] (EEA, GB and US Company Full (3.0) and the US ISV Seller variants)
+ */
private ProofOfPrincipalAddress proofOfPrincipalAddress;
+ /**
+ * Additional space for documents to be provided when requested. Top level only.
+ * [Optional] (EEA, GB and US Company and Sole Trader Full (3.0); not the US ISV Seller variants)
+ */
@SerializedName("additional_document1")
private AdditionalDocument additionalDocument1;
+ /**
+ * Additional space for documents to be provided when requested. Top level only.
+ * [Optional] (EEA, GB and US Company and Sole Trader Full (3.0); not the US ISV Seller variants)
+ */
@SerializedName("additional_document2")
private AdditionalDocument additionalDocument2;
+ /**
+ * Additional space for documents to be provided when requested. Top level only.
+ * [Optional] (EEA, GB and US Company and Sole Trader Full (3.0); not the US ISV Seller variants)
+ */
@SerializedName("additional_document3")
private AdditionalDocument additionalDocument3;
+ /**
+ * IRS-issued Employer Identification Number document used to verify the entity's tax
+ * identification. Top level only.
+ * [Optional] (US Company variants and the US ISV Seller variants only)
+ */
private TaxVerification taxVerification;
+ /**
+ * Financial statement document. Becomes mandatory depending on the answer provided for
+ * {@code annual_processing_volume}; the sub-entity's status changes to {@code requirements_due}
+ * when it is needed. Top level only.
+ * [Optional] (EEA Company Full and Lite (2.0) only)
+ */
private FinancialVerification financialVerification;
+ /**
+ * Audited or management-prepared financial statements (when applicable). Top level only.
+ * [Optional] (US ISV Seller variants only)
+ */
private FinancialStatements financialStatements;
+ // Representative only
+
+ /**
+ * Certified authorised signatory document. Required when the legal representative or other
+ * role owner is not registered on the certificate of incorporation. Representative only
+ * ({@code company.representatives[].documents}); not accepted at the top level.
+ * [Optional] (EEA, GB and US Company Full (3.0) and US ISV Seller Company (3.0))
+ */
+ private CertifiedAuthorisedSignatory certifiedAuthorisedSignatory;
+
+ /**
+ * Proof of residential address of the representative. Representative only
+ * ({@code company.representatives[].documents}); not accepted at the top level.
+ * [Required] for EEA Sole Trader Full (3.0), and only valid there.
+ */
+ private ProofOfResidentialAddress proofOfResidentialAddress;
+
+ /**
+ * Proof of the sole trader's registration, for example an extract from a trade register.
+ * Representative only ({@code company.representatives[].documents}); not accepted at the top
+ * level.
+ * [Required] for EEA Sole Trader Full (3.0), and only valid there.
+ */
+ private ProofOfRegistration proofOfRegistration;
+
}
diff --git a/src/main/java/com/checkout/accounts/ProofOfLegality.java b/src/main/java/com/checkout/accounts/ProofOfLegality.java
index 35263323..3ea7c861 100644
--- a/src/main/java/com/checkout/accounts/ProofOfLegality.java
+++ b/src/main/java/com/checkout/accounts/ProofOfLegality.java
@@ -5,14 +5,27 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * A regulatory licence document required for the company to operate (when applicable).
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class ProofOfLegality {
+ /**
+ * The type of document used for proof of legality.
+ * [Required]
+ */
private ProofOfLegalityType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/ProofOfLegalityType.java b/src/main/java/com/checkout/accounts/ProofOfLegalityType.java
index a1130bb3..3ba4f497 100644
--- a/src/main/java/com/checkout/accounts/ProofOfLegalityType.java
+++ b/src/main/java/com/checkout/accounts/ProofOfLegalityType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as proof of legality.
+ */
public enum ProofOfLegalityType {
@SerializedName("proof_of_legality")
diff --git a/src/main/java/com/checkout/accounts/ProofOfPrincipalAddress.java b/src/main/java/com/checkout/accounts/ProofOfPrincipalAddress.java
index b7488f84..3aa25a95 100644
--- a/src/main/java/com/checkout/accounts/ProofOfPrincipalAddress.java
+++ b/src/main/java/com/checkout/accounts/ProofOfPrincipalAddress.java
@@ -5,14 +5,27 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Proof of the company's principal place of business.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class ProofOfPrincipalAddress {
+ /**
+ * The type of document being used as address verification.
+ * [Required]
+ */
private ProofOfPrincipalAddressType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/ProofOfPrincipalAddressType.java b/src/main/java/com/checkout/accounts/ProofOfPrincipalAddressType.java
index e04df292..d247d496 100644
--- a/src/main/java/com/checkout/accounts/ProofOfPrincipalAddressType.java
+++ b/src/main/java/com/checkout/accounts/ProofOfPrincipalAddressType.java
@@ -2,6 +2,11 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as proof of the company's principal place of business. Carries the
+ * same {@code proof_of_address} value as {@link ProofOfResidentialAddressType}, but the API defines
+ * the two as separate enums on separate documents.
+ */
public enum ProofOfPrincipalAddressType {
@SerializedName("proof_of_address")
diff --git a/src/main/java/com/checkout/accounts/ProofOfRegistration.java b/src/main/java/com/checkout/accounts/ProofOfRegistration.java
new file mode 100644
index 00000000..7bbbe9fa
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/ProofOfRegistration.java
@@ -0,0 +1,33 @@
+package com.checkout.accounts;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Proof of the sole trader's registration, for example an extract from a trade register.
+ * Representative documents only ({@code company.representatives[].documents}), EEA Sole Trader
+ * Full (3.0); not accepted at the top level.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public final class ProofOfRegistration {
+
+ /**
+ * The type of document being used as proof of registration.
+ * [Required]
+ */
+ private ProofOfRegistrationType type;
+
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
+ private String front;
+
+}
diff --git a/src/main/java/com/checkout/accounts/ProofOfRegistrationType.java b/src/main/java/com/checkout/accounts/ProofOfRegistrationType.java
new file mode 100644
index 00000000..cb52e352
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/ProofOfRegistrationType.java
@@ -0,0 +1,17 @@
+package com.checkout.accounts;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The document types accepted as a sole trader's proof of registration (EEA Sole Trader Full
+ * (3.0)).
+ */
+public enum ProofOfRegistrationType {
+
+ @SerializedName("extract_from_trade_register")
+ EXTRACT_FROM_TRADE_REGISTER,
+
+ @SerializedName("other")
+ OTHER
+
+}
diff --git a/src/main/java/com/checkout/accounts/ProofOfResidentialAddress.java b/src/main/java/com/checkout/accounts/ProofOfResidentialAddress.java
new file mode 100644
index 00000000..a86e9fc5
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/ProofOfResidentialAddress.java
@@ -0,0 +1,33 @@
+package com.checkout.accounts;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Proof of residential address of the representative. Representative documents only
+ * ({@code company.representatives[].documents}), EEA Sole Trader Full (3.0); not accepted at the
+ * top level.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public final class ProofOfResidentialAddress {
+
+ /**
+ * The type of document being used as address verification.
+ * [Required]
+ */
+ private ProofOfResidentialAddressType type;
+
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
+ private String front;
+
+}
diff --git a/src/main/java/com/checkout/accounts/ProofOfResidentialAddressType.java b/src/main/java/com/checkout/accounts/ProofOfResidentialAddressType.java
new file mode 100644
index 00000000..800d4258
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/ProofOfResidentialAddressType.java
@@ -0,0 +1,15 @@
+package com.checkout.accounts;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The document type accepted as a representative's proof of residential address (EEA Sole Trader
+ * Full (3.0)). Carries the same {@code proof_of_address} value as {@link ProofOfPrincipalAddressType},
+ * but the API defines the two as separate enums on separate documents.
+ */
+public enum ProofOfResidentialAddressType {
+
+ @SerializedName("proof_of_address")
+ PROOF_OF_ADDRESS
+
+}
diff --git a/src/main/java/com/checkout/accounts/Representative.java b/src/main/java/com/checkout/accounts/Representative.java
index 9443d6f9..56a2437a 100644
--- a/src/main/java/com/checkout/accounts/Representative.java
+++ b/src/main/java/com/checkout/accounts/Representative.java
@@ -6,68 +6,150 @@
import java.util.List;
+/**
+ * A representative of the sub-entity. One class covers every shape the Accounts API defines:
+ *
+ * - v3.0 person of interest: {@code individual}, {@code roles}, {@code companyPosition},
+ * {@code ownershipPercentage}, {@code documents}.
+ * - v3.0 controlling company (EEA and GB Company Full): {@code company} and
+ * {@code ownershipPercentage}.
+ * - v2.0 company representatives: the deprecated flat person fields, {@code roles},
+ * {@code documents} and, on the US variants, {@code identification}.
+ *
+ */
@Data
@Builder
public final class Representative {
/**
+ * The representative's first name. Accounts API v2.0 only.
+ * [Required] (v2.0)
+ * min 2 characters, max 50 characters
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private String firstName;
/**
+ * The representative's middle name. Required if it appears in official documents. Accounts API
+ * v2.0 only.
+ * [Optional]
+ * min 2 characters, max 50 characters
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private String middleName;
/**
+ * The representative's last name. Accounts API v2.0 only.
+ * [Required] (v2.0)
+ * min 2 characters, max 50 characters
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private String lastName;
/**
+ * The representative's address. Accounts API v2.0 only.
+ * [Required] (v2.0)
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private Address address;
/**
+ * The representative's identification. Accounts API v2.0 US Company variants only.
+ * [Required] for US Company Full (2.0); [Optional] for US Company Lite (2.0).
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private Identification identification;
/**
+ * The representative's phone number. Accounts API v2.0 only.
+ * [Optional]
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private AccountPhone phone;
/**
+ * The date of birth of the person according to the Gregorian calendar. Accounts API v2.0 only.
+ * [Required] for the v2.0 Full variants; [Optional] for the v2.0 Lite variants.
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private DateOfBirth dateOfBirth;
/**
+ * The place of birth of the person. Accounts API v2.0 only.
+ * [Required] for EEA Company Full (2.0); [Optional] for EEA Company Lite (2.0). Not part of the
+ * other v2.0 variants.
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private PlaceOfBirth placeOfBirth;
+ /**
+ * The individual's roles within the company. For sole traders, must be {@code ubo} only.
+ * [Required] for every variant except EEA and US Company Lite (2.0), where it is [Optional].
+ */
private List roles;
+ /**
+ * Verification documents for the individual representative. The API validates this object
+ * strictly on v3.0: it accepts only {@code identity_verification},
+ * {@code certified_authorised_signatory}, {@code proof_of_residential_address} and
+ * {@code proof_of_registration}, and rejects any other key. See
+ * {@link OnboardSubEntityDocuments} for which apply to each variant.
+ * [Required] for the EEA, GB and US Sole Trader Full (3.0) variants and EEA Company Full (2.0);
+ * [Optional] otherwise.
+ */
private OnboardSubEntityDocuments documents;
+ /**
+ * Information about the individual representing the sub-entity.
+ * [Required] for every v3.0 person of interest.
+ */
private RepresentativeIndividual individual;
+ /**
+ * The representative's id.
+ * [Optional]
+ * ^rep_[a-z0-9]{26}$
+ * 30 characters
+ */
private String id;
+ /**
+ * The position of the representative within the company (required for the
+ * {@code control_person} role).
+ * [Optional] (EEA, GB and US Company Full (3.0) and US ISV Seller Company (3.0))
+ */
private CompanyPosition companyPosition;
+ /**
+ * The percentage ownership of the UBO or controlling company (required when over 25%).
+ * [Optional]
+ * min 25, max 100 on the EEA, GB and US Company Full (3.0) variants; min 0, max 100 on the US ISV
+ * Seller variants
+ */
private Integer ownershipPercentage;
+ /**
+ * The controlling company, when the representative is a company rather than an individual.
+ * [Required] for a controlling company representative (EEA and GB Company Full (3.0) only).
+ * The API reads only three fields here, all [Required]: {@code legalName}, {@code tradingName}
+ * and {@code registeredAddress}. Leave the other {@link Company} fields unset.
+ */
+ private Company company;
+
}
diff --git a/src/main/java/com/checkout/accounts/RepresentativeIndividual.java b/src/main/java/com/checkout/accounts/RepresentativeIndividual.java
index e35dc5e7..1cc74b74 100644
--- a/src/main/java/com/checkout/accounts/RepresentativeIndividual.java
+++ b/src/main/java/com/checkout/accounts/RepresentativeIndividual.java
@@ -8,32 +8,93 @@
import java.util.List;
+/**
+ * The personal details of a company representative ({@code company.representatives[].individual}),
+ * Accounts API v3.0.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class RepresentativeIndividual {
+ /**
+ * The representative's first name.
+ * [Required]
+ * min 2 characters, max 50 characters
+ */
private String firstName;
+ /**
+ * The representative's middle name. Required if it appears in official documents.
+ * [Optional]
+ * min 2 characters, max 50 characters
+ */
private String middleName;
+ /**
+ * The representative's last name.
+ * [Required]
+ * min 2 characters, max 50 characters
+ */
private String lastName;
+ /**
+ * The date of birth of the person according to the Gregorian calendar.
+ * [Required]
+ */
private DateOfBirth dateOfBirth;
+ /**
+ * The place of birth of the person.
+ * [Required]
+ */
private PlaceOfBirth placeOfBirth;
+ /**
+ * The list of citizenships or legal statuses for the representative.
+ * [Required] for the US ISV Seller variants only; not part of the other v3.0 schemas, leave unset
+ * for them.
+ */
private List citizenships;
+ /**
+ * The classification of the national identification number provided.
+ * [Required] for the US ISV Seller variants only; not part of the other v3.0 schemas, leave unset
+ * for them.
+ */
private NationalIdType nationalIdType;
+ /**
+ * The representative's national identification number. v3.0 only.
+ * [Required] for the US ISV Seller variants; [Optional] for the other v3.0 variants.
+ * The format depends on the variant:
+ *
+ * - US ISV Seller: the number for the {@code nationalIdType} given. ^[a-zA-Z0-9\-]+$, min 5
+ * characters, max 16 characters.
+ * - Other v3.0 variants: a Social Security Number (SSN) or Individual Taxpayer Identification
+ * Number (ITIN), US residents only. ^\d{9}$, 9 characters.
+ *
+ */
private String nationalIdNumber;
+ /**
+ * The representative's personal email address.
+ * [Required] for the US ISV Seller variants; [Optional] for the other v3.0 variants.
+ * Format: email
+ */
private String emailAddress;
+ /**
+ * The representative's phone number.
+ * [Required] for the US ISV Seller variants; [Optional] for the other v3.0 variants.
+ */
private AccountPhone phone;
+ /**
+ * The representative's address.
+ * [Required]
+ */
private Address address;
}
diff --git a/src/main/java/com/checkout/accounts/ShareholderStructure.java b/src/main/java/com/checkout/accounts/ShareholderStructure.java
index 9d9de8ab..cd504d4d 100644
--- a/src/main/java/com/checkout/accounts/ShareholderStructure.java
+++ b/src/main/java/com/checkout/accounts/ShareholderStructure.java
@@ -5,14 +5,28 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Shareholder structure chart (including % of shares) certified by a competent authority
+ * individual and dated within the last 3 months.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class ShareholderStructure {
+ /**
+ * The type of document.
+ * [Required]
+ */
private ShareholderStructureType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/ShareholderStructureType.java b/src/main/java/com/checkout/accounts/ShareholderStructureType.java
index c7a3ce7b..7e5cab0e 100644
--- a/src/main/java/com/checkout/accounts/ShareholderStructureType.java
+++ b/src/main/java/com/checkout/accounts/ShareholderStructureType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as a certified shareholder structure.
+ */
public enum ShareholderStructureType {
@SerializedName("certified_shareholder_structure")
diff --git a/src/main/java/com/checkout/accounts/TaxVerification.java b/src/main/java/com/checkout/accounts/TaxVerification.java
index 8b856e01..256bff68 100644
--- a/src/main/java/com/checkout/accounts/TaxVerification.java
+++ b/src/main/java/com/checkout/accounts/TaxVerification.java
@@ -5,13 +5,28 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * IRS-issued Employer Identification Number document used to verify the entity's tax
+ * identification (US variants).
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class TaxVerification {
- private CompanyVerificationType type;
+ /**
+ * The type of IRS-issued document used for tax verification.
+ * [Required]
+ */
+ private TaxVerificationType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
+
}
diff --git a/src/main/java/com/checkout/accounts/TaxVerificationType.java b/src/main/java/com/checkout/accounts/TaxVerificationType.java
index 096ea421..e1545e4c 100644
--- a/src/main/java/com/checkout/accounts/TaxVerificationType.java
+++ b/src/main/java/com/checkout/accounts/TaxVerificationType.java
@@ -2,6 +2,10 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as tax verification: an IRS-issued Employer Identification Number
+ * letter.
+ */
public enum TaxVerificationType {
@SerializedName("ein_letter")
diff --git a/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java b/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
index cb030fd0..06f25a5e 100644
--- a/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
+++ b/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The purpose of a sub-entity file upload (POST /entities/{entityId}/files).
+ */
public enum FilePurpose {
@SerializedName("additional_document")
ADDITIONAL_DOCUMENT,
diff --git a/src/main/java/com/checkout/accounts/files/request/FileUploadRequest.java b/src/main/java/com/checkout/accounts/files/request/FileUploadRequest.java
index 9f076e23..7685e003 100644
--- a/src/main/java/com/checkout/accounts/files/request/FileUploadRequest.java
+++ b/src/main/java/com/checkout/accounts/files/request/FileUploadRequest.java
@@ -8,11 +8,19 @@
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
+/**
+ * The request body of POST /entities/{entityId}/files.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class FileUploadRequest {
+ /**
+ * The purpose of the file upload.
+ * [Required]
+ */
private FilePurpose purpose;
-}
\ No newline at end of file
+
+}
diff --git a/src/main/java/com/checkout/accounts/files/response/FileDetailsResponse.java b/src/main/java/com/checkout/accounts/files/response/FileDetailsResponse.java
index 467a45ae..fbfa5755 100644
--- a/src/main/java/com/checkout/accounts/files/response/FileDetailsResponse.java
+++ b/src/main/java/com/checkout/accounts/files/response/FileDetailsResponse.java
@@ -11,23 +11,50 @@
import java.time.Instant;
import java.util.List;
+/**
+ * The details of a sub-entity's file, as returned by GET /entities/{entityId}/files/{fileId}.
+ */
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public final class FileDetailsResponse extends Resource {
+ /**
+ * The ID of the file.
+ */
private String id;
+ /**
+ * The current status of the file.
+ */
private String status;
+ /**
+ * If {@code status} is {@code invalid}, the reasons why the file was invalid; otherwise null.
+ */
private List statusReasons;
+ /**
+ * The size of the file, in KB.
+ */
private Long size;
+ /**
+ * The MIME type of the file.
+ */
private String mimeType;
+ /**
+ * The date and time the file was uploaded, in ISO 8601 UTC format.
+ * Format: date-time (RFC 3339)
+ */
private Instant uploadedOn;
+ /**
+ * The purpose of the file, as provided in the initial request. A value {@link FilePurpose} does
+ * not define deserializes to null.
+ */
private FilePurpose purpose;
-}
\ No newline at end of file
+
+}
diff --git a/src/main/java/com/checkout/accounts/files/response/FileUploadResponse.java b/src/main/java/com/checkout/accounts/files/response/FileUploadResponse.java
index da8c4ff2..4ff25d35 100644
--- a/src/main/java/com/checkout/accounts/files/response/FileUploadResponse.java
+++ b/src/main/java/com/checkout/accounts/files/response/FileUploadResponse.java
@@ -9,15 +9,29 @@
import java.util.List;
+/**
+ * The response of POST /entities/{entityId}/files: the file ID and the upload link. The file content
+ * itself is sent to that link, not in the request.
+ */
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public final class FileUploadResponse extends Resource {
+ /**
+ * The file identifier.
+ */
private String id;
+ /**
+ * The maximum file size allowed, in bytes.
+ */
private Long maximumSizeInBytes;
+ /**
+ * The MIME file types allowed for the document purpose provided on the initial request.
+ */
private List documentTypesForPurpose;
-}
\ No newline at end of file
+
+}
diff --git a/src/main/java/com/checkout/common/DocumentType.java b/src/main/java/com/checkout/common/DocumentType.java
index 40262589..81daab25 100644
--- a/src/main/java/com/checkout/common/DocumentType.java
+++ b/src/main/java/com/checkout/common/DocumentType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document types accepted to confirm an individual's identity.
+ */
public enum DocumentType {
@SerializedName("passport")
diff --git a/src/test/java/com/checkout/accounts/AccountsTestIT.java b/src/test/java/com/checkout/accounts/AccountsTestIT.java
index 95327a19..b8d278e8 100644
--- a/src/test/java/com/checkout/accounts/AccountsTestIT.java
+++ b/src/test/java/com/checkout/accounts/AccountsTestIT.java
@@ -19,6 +19,7 @@
import com.checkout.accounts.files.entities.FilePurpose;
import com.checkout.common.Address;
import com.checkout.common.CountryCode;
+import com.checkout.common.DocumentType;
import com.checkout.common.Currency;
import com.checkout.common.IdResponse;
import com.checkout.common.InstrumentType;
@@ -172,6 +173,59 @@ void shouldCreateGetAndUpdateOnboardCompanyEntitySync() {
// onboarding tests above (which pin schema_version to "2.0"); here createEntity/getEntity/
// updateEntity use the SDK default (3.0). They run through the accounts-scoped OAuth client,
// which is the one provisioned for v3.0 onboarding.
+ // The representative's documents on schema 3.0. The sandbox platform resolves to a company
+ // variant (GB/US scope, USD only), where identity_verification and certified_authorised_signatory
+ // are the representative documents the API accepts; the EEA Sole Trader keys are covered by
+ // OnboardSubEntityDocumentsSerializationTest, since this platform rejects them.
+ @Test
+ void shouldCreateEntityWithRepresentativeDocuments() throws URISyntaxException {
+ final CheckoutApi checkoutApi = accountsApi();
+ final IdResponse identityFile = submitAccountsFile(checkoutApi, AccountsFilePurpose.IDENTITY_VERIFICATION);
+ final IdResponse signatoryFile = submitAccountsFile(checkoutApi, AccountsFilePurpose.CERTIFIED_AUTHORISED_SIGNATORY);
+
+ final OnboardEntityRequest request = buildCompanyEntityV3(RandomStringUtils.random(15, true, true));
+ request.getCompany().getRepresentatives().get(0).setDocuments(OnboardSubEntityDocuments.builder()
+ .identityVerification(Document.builder()
+ .type(DocumentType.PASSPORT)
+ .front(identityFile.getId())
+ .build())
+ .certifiedAuthorisedSignatory(CertifiedAuthorisedSignatory.builder()
+ .type(CertifiedAuthorisedSignatoryType.POWER_OF_ATTORNEY)
+ .front(signatoryFile.getId())
+ .build())
+ .build());
+
+ final OnboardEntityResponse entityResponse = blocking(() -> checkoutApi.accountsClient().createEntity(request));
+ assertNotNull(entityResponse.getId());
+
+ // The documents are linked on the representative, not dropped: the API echoes them back.
+ final OnboardEntityDetailsResponse details = blocking(() -> checkoutApi.accountsClient().getEntity(entityResponse.getId()));
+ final OnboardSubEntityDocuments linked = details.getCompany().getRepresentatives().get(0).getDocuments();
+ assertEquals(DocumentType.PASSPORT, linked.getIdentityVerification().getType());
+ assertEquals(identityFile.getId(), linked.getIdentityVerification().getFront());
+ assertEquals(CertifiedAuthorisedSignatoryType.POWER_OF_ATTORNEY, linked.getCertifiedAuthorisedSignatory().getType());
+ assertEquals(signatoryFile.getId(), linked.getCertifiedAuthorisedSignatory().getFront());
+ }
+
+ // The two EEA Sole Trader representative documents need their own upload purposes before they
+ // can be linked. Goes through POST /entities/{id}/files, the endpoint whose request schema
+ // (PlatformsFileUpload) defines the purpose enum.
+ @Test
+ void shouldUploadRepresentativeProofFilesForEntity() {
+ final String entityId = createTestEntity();
+
+ for (final FilePurpose purpose : new FilePurpose[]{FilePurpose.PROOF_OF_RESIDENTIAL_ADDRESS, FilePurpose.PROOF_OF_REGISTRATION}) {
+ final FileUploadResponse uploadResponse = blocking(() -> accountsApi().accountsClient()
+ .uploadFile(entityId, FileUploadRequest.builder().purpose(purpose).build()));
+ validateFileUploadResponseForEntity(uploadResponse);
+
+ final FileDetailsResponse details = blocking(() -> accountsApi().accountsClient()
+ .retrieveFile(entityId, uploadResponse.getId()));
+ validateFileDetailsResponseForEntity(details, uploadResponse.getId());
+ assertEquals(purpose, details.getPurpose());
+ }
+ }
+
@Test
void shouldCreateGetAndUpdateOnboardCompanyEntityV3() {
final CheckoutApi checkoutApi = getAccountsCheckoutApi();
@@ -836,6 +890,18 @@ private IdResponse uploadFile() throws URISyntaxException {
return fileResponse;
}
+ private IdResponse submitAccountsFile(final CheckoutApi api, final AccountsFilePurpose purpose) throws URISyntaxException {
+ final File file = new File(getClass().getClassLoader().getResource("checkout.jpeg").toURI());
+ final IdResponse fileResponse = blocking(() -> api.accountsClient().submitFile(AccountsFileRequest.builder()
+ .file(file)
+ .contentType(ContentType.IMAGE_JPEG)
+ .purpose(purpose)
+ .build()));
+ assertNotNull(fileResponse);
+ assertNotNull(fileResponse.getId());
+ return fileResponse;
+ }
+
private CheckoutApi accountsApi() {
if (accountsApi == null) {
accountsApi = getAccountsCheckoutApi();
diff --git a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
index bb9ab2b6..88d4216c 100644
--- a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
+++ b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
@@ -1,8 +1,10 @@
package com.checkout.accounts;
import com.checkout.GsonSerializer;
+import com.checkout.common.Address;
import com.checkout.common.CountryCode;
import com.checkout.common.Currency;
+import com.google.gson.JsonParser;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
@@ -168,4 +170,59 @@ void shouldDeserializeNewEnumValuesToExactSwaggerStrings() {
assertEquals(CompanyPosition.CEO, serializer.fromJson("\"ceo\"", CompanyPosition.class));
assertEquals(CompanyPosition.OTHER_NON_EXECUTIVE_NON_SENIOR, serializer.fromJson("\"other_non_executive_non_senior\"", CompanyPosition.class));
}
+
+ // ------------------------------------------------------------------------
+ // Controlling company representative
+ // EEA and GB Company Full (3.0) allow a representative that is a company:
+ // { id, company: { legal_name, trading_name, registered_address },
+ // ownership_percentage }. The field was not modelled.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSerializeControllingCompanyRepresentative() {
+ final Representative representative = Representative.builder()
+ .company(Company.builder()
+ .legalName("Parent Holdings Ltd")
+ .tradingName("Parent Holdings")
+ .registeredAddress(Address.builder()
+ .addressLine1("1 Main Street")
+ .city("London")
+ .zip("W1T 4TJ")
+ .country(CountryCode.GB)
+ .build())
+ .build())
+ .ownershipPercentage(60)
+ .build();
+
+ assertEquals(JsonParser.parseString("{\"ownership_percentage\":60,\"company\":{"
+ + "\"legal_name\":\"Parent Holdings Ltd\",\"trading_name\":\"Parent Holdings\","
+ + "\"registered_address\":{\"address_line1\":\"1 Main Street\",\"city\":\"London\","
+ + "\"zip\":\"W1T 4TJ\",\"country\":\"GB\"}}}"),
+ JsonParser.parseString(serializer.toJson(representative)));
+ }
+
+ // ------------------------------------------------------------------------
+ // OnboardEntityDetailsResponse
+ // GET /accounts/entities/{id} returns documents and processing_details; neither
+ // was modelled, so the top-level documents could not be read back.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldDeserializeEntityDetailsDocumentsAndProcessingDetails() {
+ final OnboardEntityDetailsResponse response = serializer.fromJson("{"
+ + "\"id\":\"ent_aaaaaaaaaaaaaaaaaaaaaaaaaa\","
+ + "\"processing_details\":{\"currency\":\"USD\",\"annual_processing_volume\":1000000},"
+ + "\"documents\":{\"bank_verification\":{\"type\":\"bank_statement\","
+ + "\"front\":\"file_bankverificationaaaaaaaaaa\"}},"
+ + "\"company\":{\"representatives\":[{\"documents\":{\"proof_of_registration\":"
+ + "{\"type\":\"extract_from_trade_register\",\"front\":\"file_proofofregistrationaaaaaaa\"}}}]}}",
+ OnboardEntityDetailsResponse.class);
+
+ assertEquals(Currency.USD, response.getProcessingDetails().getCurrency());
+ assertEquals(1000000, response.getProcessingDetails().getAnnualProcessingVolume());
+ assertEquals(BankVerificationType.BANK_STATEMENT, response.getDocuments().getBankVerification().getType());
+ assertEquals("file_bankverificationaaaaaaaaaa", response.getDocuments().getBankVerification().getFront());
+ assertEquals(ProofOfRegistrationType.EXTRACT_FROM_TRADE_REGISTER, response.getCompany().getRepresentatives()
+ .get(0).getDocuments().getProofOfRegistration().getType());
+ }
}
diff --git a/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java b/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java
index b2182d88..77840cdf 100644
--- a/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java
+++ b/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java
@@ -1,8 +1,13 @@
package com.checkout.accounts;
import com.checkout.GsonSerializer;
+import com.checkout.common.DocumentType;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import org.junit.jupiter.api.Test;
+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.assertTrue;
@@ -82,4 +87,186 @@ void shouldDeserializeArticlesOfAssociation() {
assertEquals(ArticlesOfAssociationType.ARTICLES_OF_ASSOCIATION, documents.getArticlesOfAssociation().getType());
assertEquals("file_6lbss42ezvoufcb2beo76rvwly", documents.getArticlesOfAssociation().getFront());
}
+
+ // ------------------------------------------------------------------------
+ // Representative documents (company.representatives[].documents)
+ // The EEA Sole Trader (3.0) keys and the company-variant certified authorised
+ // signatory. The representative object is strict on the API, so the exact key
+ // set matters.
+ // ------------------------------------------------------------------------
+
+ // Regression: EEA Sole Trader (3.0) needs proof_of_residential_address and proof_of_registration
+ // on the representative, with bank_verification alone at the top level. Neither could be
+ // expressed on the representative before.
+ @Test
+ void shouldSerializeEeaSoleTraderRepresentativeDocuments() {
+ final OnboardEntityRequest request = OnboardEntityRequest.builder()
+ .reference("ref_sole_trader")
+ .company(Company.builder()
+ .businessType(BusinessType.INDIVIDUAL_OR_SOLE_PROPRIETORSHIP)
+ .representatives(Collections.singletonList(Representative.builder()
+ .individual(RepresentativeIndividual.builder().firstName("Jane").lastName("Doe").build())
+ .roles(Collections.singletonList(EntityRoles.UBO))
+ .documents(eeaSoleTraderRepresentativeDocuments())
+ .build()))
+ .build())
+ .documents(OnboardSubEntityDocuments.builder()
+ .bankVerification(BankVerification.builder()
+ .type(BankVerificationType.BANK_STATEMENT)
+ .front("file_bankverificationaaaaaaaaaa")
+ .build())
+ .build())
+ .build();
+
+ final String json = serializer.toJson(request);
+ final JsonObject body = JsonParser.parseString(json).getAsJsonObject();
+
+ assertEquals(JsonParser.parseString("{"
+ + "\"identity_verification\":{\"type\":\"passport\",\"front\":\"file_identityverificationaaaaaa\"},"
+ + "\"proof_of_residential_address\":{\"type\":\"proof_of_address\",\"front\":\"file_proofofresidentialaddressa\"},"
+ + "\"proof_of_registration\":{\"type\":\"extract_from_trade_register\",\"front\":\"file_proofofregistrationaaaaaaa\"}}"),
+ body.getAsJsonObject("company").getAsJsonArray("representatives").get(0)
+ .getAsJsonObject().get("documents"), json);
+ assertEquals(Collections.singleton("bank_verification"), body.getAsJsonObject("documents").keySet(), json);
+ // Key-level check on the raw body, so a naming-policy change cannot pass silently.
+ assertTrue(json.contains("\"proof_of_residential_address\":{"), json);
+ assertTrue(json.contains("\"proof_of_registration\":{"), json);
+ }
+
+ @Test
+ void shouldRoundTripRepresentativeDocuments() {
+ final OnboardSubEntityDocuments original = eeaSoleTraderRepresentativeDocuments();
+
+ final OnboardSubEntityDocuments roundTripped =
+ serializer.fromJson(serializer.toJson(original), OnboardSubEntityDocuments.class);
+
+ assertEquals(original, roundTripped);
+ }
+
+ @Test
+ void shouldDeserializeProofOfRegistrationOtherType() {
+ final OnboardSubEntityDocuments documents = serializer.fromJson(
+ "{\"proof_of_registration\":{\"type\":\"other\",\"front\":\"file_proofofregistrationaaaaaaa\"}}",
+ OnboardSubEntityDocuments.class);
+
+ assertEquals(ProofOfRegistrationType.OTHER, documents.getProofOfRegistration().getType());
+ }
+
+ @Test
+ void shouldSerializeCertifiedAuthorisedSignatoryWithTypeAndFrontOnly() {
+ final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
+ .certifiedAuthorisedSignatory(CertifiedAuthorisedSignatory.builder()
+ .type(CertifiedAuthorisedSignatoryType.POWER_OF_ATTORNEY)
+ .front("file_signatoryaaaaaaaaaaaaaaaaa")
+ .build())
+ .build();
+
+ assertEquals(JsonParser.parseString("{\"certified_authorised_signatory\":"
+ + "{\"type\":\"power_of_attorney\",\"front\":\"file_signatoryaaaaaaaaaaaaaaaaa\"}}"),
+ JsonParser.parseString(serializer.toJson(documents)));
+ }
+
+ // ------------------------------------------------------------------------
+ // Company and tax verification
+ // Regression: CompanyVerification carried TaxVerificationType and TaxVerification
+ // carried CompanyVerificationType, so incorporation_document (required on the
+ // EEA and GB Company Full variants) and ein_letter could not be sent.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSendCompanyAndTaxVerificationTypesUnderTheirOwnKeys() {
+ final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
+ .companyVerification(CompanyVerification.builder()
+ .type(CompanyVerificationType.INCORPORATION_DOCUMENT)
+ .front("file_aaaaaaaaaaaaaaaaaaaaaaaaaa")
+ .build())
+ .taxVerification(TaxVerification.builder()
+ .type(TaxVerificationType.EIN_LETTER)
+ .front("file_aaaaaaaaaaaaaaaaaaaaaaaaaa")
+ .build())
+ .build();
+
+ final JsonObject json = JsonParser.parseString(serializer.toJson(documents)).getAsJsonObject();
+
+ assertEquals("incorporation_document",
+ json.getAsJsonObject("company_verification").get("type").getAsString());
+ assertEquals("ein_letter", json.getAsJsonObject("tax_verification").get("type").getAsString());
+ }
+
+ // ------------------------------------------------------------------------
+ // Every field of OnboardSubEntityDocuments
+ // Exact JSON for all 16 fields, so a naming-policy change on any key cannot pass
+ // silently, then a full round trip.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSerializeAndRoundTripEveryDocumentsField() {
+ final String file = "file_aaaaaaaaaaaaaaaaaaaaaaaaaa";
+ final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
+ .identityVerification(Document.builder().type(DocumentType.PASSPORT).front(file).back(file).build())
+ .companyVerification(CompanyVerification.builder()
+ .type(CompanyVerificationType.INCORPORATION_DOCUMENT).front(file).build())
+ .articlesOfAssociation(ArticlesOfAssociation.builder()
+ .type(ArticlesOfAssociationType.ARTICLES_OF_ASSOCIATION).front(file).build())
+ .bankVerification(BankVerification.builder().type(BankVerificationType.BANK_STATEMENT).front(file).build())
+ .shareholderStructure(ShareholderStructure.builder()
+ .type(ShareholderStructureType.CERTIFIED_SHAREHOLDER_STRUCTURE).front(file).build())
+ .proofOfLegality(ProofOfLegality.builder().type(ProofOfLegalityType.PROOF_OF_LEGALITY).front(file).build())
+ .proofOfPrincipalAddress(ProofOfPrincipalAddress.builder()
+ .type(ProofOfPrincipalAddressType.PROOF_OF_ADDRESS).front(file).build())
+ .additionalDocument1(AdditionalDocument.builder().front(file).build())
+ .additionalDocument2(AdditionalDocument.builder().front(file).build())
+ .additionalDocument3(AdditionalDocument.builder().front(file).build())
+ .taxVerification(TaxVerification.builder().type(TaxVerificationType.EIN_LETTER).front(file).build())
+ .financialVerification(FinancialVerification.builder()
+ .type(FinancialVerificationType.FINANCIAL_STATEMENT).front(file).build())
+ .financialStatements(FinancialStatements.builder()
+ .type(FinancialStatementsType.FINANCIAL_STATEMENTS).front(file).build())
+ .certifiedAuthorisedSignatory(CertifiedAuthorisedSignatory.builder()
+ .type(CertifiedAuthorisedSignatoryType.POWER_OF_ATTORNEY).front(file).build())
+ .proofOfResidentialAddress(ProofOfResidentialAddress.builder()
+ .type(ProofOfResidentialAddressType.PROOF_OF_ADDRESS).front(file).build())
+ .proofOfRegistration(ProofOfRegistration.builder()
+ .type(ProofOfRegistrationType.EXTRACT_FROM_TRADE_REGISTER).front(file).build())
+ .build();
+
+ final String json = serializer.toJson(documents);
+
+ assertEquals(JsonParser.parseString("{"
+ + "\"identity_verification\":{\"type\":\"passport\",\"front\":\"" + file + "\",\"back\":\"" + file + "\"},"
+ + "\"company_verification\":{\"type\":\"incorporation_document\",\"front\":\"" + file + "\"},"
+ + "\"articles_of_association\":{\"type\":\"articles_of_association\",\"front\":\"" + file + "\"},"
+ + "\"bank_verification\":{\"type\":\"bank_statement\",\"front\":\"" + file + "\"},"
+ + "\"shareholder_structure\":{\"type\":\"certified_shareholder_structure\",\"front\":\"" + file + "\"},"
+ + "\"proof_of_legality\":{\"type\":\"proof_of_legality\",\"front\":\"" + file + "\"},"
+ + "\"proof_of_principal_address\":{\"type\":\"proof_of_address\",\"front\":\"" + file + "\"},"
+ + "\"additional_document1\":{\"front\":\"" + file + "\"},"
+ + "\"additional_document2\":{\"front\":\"" + file + "\"},"
+ + "\"additional_document3\":{\"front\":\"" + file + "\"},"
+ + "\"tax_verification\":{\"type\":\"ein_letter\",\"front\":\"" + file + "\"},"
+ + "\"financial_verification\":{\"type\":\"financial_statement\",\"front\":\"" + file + "\"},"
+ + "\"financial_statements\":{\"type\":\"financial_statements\",\"front\":\"" + file + "\"},"
+ + "\"certified_authorised_signatory\":{\"type\":\"power_of_attorney\",\"front\":\"" + file + "\"},"
+ + "\"proof_of_residential_address\":{\"type\":\"proof_of_address\",\"front\":\"" + file + "\"},"
+ + "\"proof_of_registration\":{\"type\":\"extract_from_trade_register\",\"front\":\"" + file + "\"}}"),
+ JsonParser.parseString(json), json);
+ assertEquals(documents, serializer.fromJson(json, OnboardSubEntityDocuments.class));
+ }
+
+ private static OnboardSubEntityDocuments eeaSoleTraderRepresentativeDocuments() {
+ return OnboardSubEntityDocuments.builder()
+ .identityVerification(Document.builder()
+ .type(DocumentType.PASSPORT)
+ .front("file_identityverificationaaaaaa")
+ .build())
+ .proofOfResidentialAddress(ProofOfResidentialAddress.builder()
+ .type(ProofOfResidentialAddressType.PROOF_OF_ADDRESS)
+ .front("file_proofofresidentialaddressa")
+ .build())
+ .proofOfRegistration(ProofOfRegistration.builder()
+ .type(ProofOfRegistrationType.EXTRACT_FROM_TRADE_REGISTER)
+ .front("file_proofofregistrationaaaaaaa")
+ .build())
+ .build();
+ }
}
From cf7d5b8cee73fa0dc42713ab1eb88327c717a3b8 Mon Sep 17 00:00:00 2001
From: david ruiz
Date: Thu, 1 Oct 2026 13:33:54 +0200
Subject: [PATCH 2/8] Docs and fixes
---
.../accounts/AccountsFileRequest.java | 17 ++++
.../java/com/checkout/accounts/Company.java | 85 +++++++++++++++++++
.../accounts/EntityProcessingDetails.java | 62 ++++++++++++++
.../OnboardEntityDetailsResponse.java | 5 +-
.../accounts/files/entities/FilePurpose.java | 4 +
.../checkout/common/AbstractFileRequest.java | 4 +
.../accounts/AccountsV3SerializationTest.java | 48 ++++++++++-
7 files changed, 222 insertions(+), 3 deletions(-)
create mode 100644 src/main/java/com/checkout/accounts/EntityProcessingDetails.java
diff --git a/src/main/java/com/checkout/accounts/AccountsFileRequest.java b/src/main/java/com/checkout/accounts/AccountsFileRequest.java
index 01e76dac..c5a9bd2b 100644
--- a/src/main/java/com/checkout/accounts/AccountsFileRequest.java
+++ b/src/main/java/com/checkout/accounts/AccountsFileRequest.java
@@ -10,14 +10,31 @@
import java.io.File;
+/**
+ * A file to upload with {@link AccountsClient#submitFile(AccountsFileRequest)} (POST /files on the
+ * Files host), sent as a multipart request. The returned ID is what document {@code front} and
+ * {@code back} fields take.
+ */
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public final class AccountsFileRequest extends AbstractFileRequest {
+ /**
+ * The purpose of the file upload: the onboarding document the file is for.
+ * [Required]
+ */
private AccountsFilePurpose purpose;
+ /**
+ * Creates a file upload request.
+ *
+ * @param file the file to upload (JPEG, PNG or PDF)
+ * @param contentType the file's content type; for PDF use
+ * {@code ContentType.create("application/pdf")}
+ * @param purpose the purpose of the file upload
+ */
@Builder
private AccountsFileRequest(final File file,
final ContentType contentType,
diff --git a/src/main/java/com/checkout/accounts/Company.java b/src/main/java/com/checkout/accounts/Company.java
index 38a5a454..00c8de6a 100644
--- a/src/main/java/com/checkout/accounts/Company.java
+++ b/src/main/java/com/checkout/accounts/Company.java
@@ -8,36 +8,121 @@
import java.util.List;
+/**
+ * Information about the company represented by the sub-entity: on every company and v3.0 sole
+ * trader variant, and as the controlling company of a {@link Representative} (where only
+ * {@code legalName}, {@code tradingName} and {@code registeredAddress} apply).
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class Company {
+ /**
+ * The legal name of the sub-entity.
+ * [Required] for every company variant and the controlling company; not part of the sole trader
+ * variants.
+ * min 2 characters, max 300 characters
+ */
private String legalName;
+ /**
+ * The trading name of the sub-entity, also referred to as 'doing business as'.
+ * [Required]
+ * min 2 characters, max 300 characters
+ */
private String tradingName;
+ /**
+ * The sub-entity's business registration number: a Commercial Registration or Ministry of Commerce
+ * certificate number, or an equivalent registration number.
+ * [Required] for the Full variants and US ISV Seller Company (3.0); [Optional] for the Lite (2.0)
+ * variants. Not part of the sole trader variants.
+ * The format depends on the variant:
+ *
+ * - EEA: min 2 characters, max 39 characters; a SIRET number for sub-entities based in France.
+ * - GB (3.0): a Companies House number,
+ * ^(((AC|CE|CS|FC|FE|GE|GS|IC|LP|NC|NF|NI|NL|NO|NP|OC|OE|PC|R0|RC|SA|SC|SE|SF|SG|SI|SL|SO|SR|SZ|ZC|\d{2})\d{6})|((IP|SP|RS)[A-Z\d]{6})|(SL\d{5}[\dA]))$,
+ * 8 characters. GB (2.0) accepts the same pattern case-insensitively.
+ * - US: an Employer Identification Number (EIN), ^[0-9]{9}$, 9 characters; US ISV Seller Company
+ * (3.0) also accepts the hyphenated form, ^[0-9]{2}-?[0-9]{7}$, min 9 characters, max 11
+ * characters.
+ *
+ */
private String businessRegistrationNumber;
+ /**
+ * The date the company was incorporated, or the date the sole trader started trading.
+ * [Required] for every v3.0 variant; [Optional] for EEA, GB and US Company Full (2.0).
+ */
private DateOfIncorporation dateOfIncorporation;
+ /**
+ * The regulatory licence number of the company.
+ * [Optional] (EEA Company Full (3.0) only)
+ * ^[a-zA-Z0-9\-]+$
+ * min 4 characters, max 32 characters
+ */
private String regulatoryLicenceNumber;
+ /**
+ * The primary location where business is performed.
+ * [Required] for every company and v3.0 sole trader variant.
+ */
private Address principalAddress;
+ /**
+ * The registered address of the company.
+ * [Required] for every company variant and the controlling company; not part of the sole trader
+ * variants.
+ */
private Address registeredAddress;
+ /**
+ * Information about the representatives of this company. See {@link Representative}.
+ * [Required]
+ * min 1 item; max 1 item for the sole trader variants (the individual themselves, with roles
+ * {@code [ubo]}), max 5 on v2.0, max 25 on EEA, GB and US Company Full (3.0), no maximum on US ISV
+ * Seller Company (3.0)
+ */
private List representatives;
+ /**
+ * Not defined by any Accounts API company schema. Retained so existing code keeps compiling.
+ *
+ * @deprecated Not part of any Accounts API schema; the API does not read it. Will be removed in a
+ * future major version.
+ */
+ @Deprecated
private EntityDocument document;
+ /**
+ * Seller financial questions and supporting documents.
+ * [Required] for EEA and US Company Full (2.0); [Optional] for EEA and US Company Lite (2.0). Not
+ * part of the other variants.
+ */
private EntityFinancialDetails financialDetails;
+ /**
+ * The legal type of the company. Must be {@code individual_or_sole_proprietorship} for the sole
+ * trader variants.
+ * [Required], except on EEA and US Company Lite (2.0) where it is [Optional]. Not part of GB
+ * Company Full and Lite (2.0).
+ */
private BusinessType businessType;
+ /**
+ * The collection of additional trading names for the sub-entity.
+ * [Optional] (US ISV Seller variants only)
+ */
private List additionalTradingNames;
+ /**
+ * Indicates whether the sub-entity is a registered legal entity. Must be {@code false} for US ISV
+ * Seller Sole Trader (3.0).
+ * [Required] for US ISV Seller Sole Trader (3.0); not part of the other variants.
+ */
private Boolean isRegisteredCompany;
}
diff --git a/src/main/java/com/checkout/accounts/EntityProcessingDetails.java b/src/main/java/com/checkout/accounts/EntityProcessingDetails.java
new file mode 100644
index 00000000..e4786cf2
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/EntityProcessingDetails.java
@@ -0,0 +1,62 @@
+package com.checkout.accounts;
+
+import com.checkout.common.Currency;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * The sub-entity's expected processing, as returned by GET /accounts/entities/{id}
+ * ({@code processing_details}, Accounts API v3.0).
+ *
+ * A response-only type, separate from the request's {@link ProcessingDetails}: the amounts are
+ * {@code Long} here because the API declares them as integers in minor units with no maximum, and
+ * an {@code Integer} would fail to read any value above 2,147,483,647.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public final class EntityProcessingDetails {
+
+ /**
+ * The country code (iso-3166-1 alpha-2) where the settlement bank account is located.
+ * Format: iso-3166-1-alpha-2
+ * 2 characters
+ */
+ private String settlementCountry;
+
+ /**
+ * Target country codes (iso-3166-1 alpha-2) with more than 10% expected volume processing with
+ * Checkout.com.
+ * min 1 item, max 10 items
+ */
+ private List targetCountries;
+
+ /**
+ * The estimated annual processing volume. In minor units without decimals.
+ * min 0
+ */
+ private Long annualProcessingVolume;
+
+ /**
+ * The expected average transaction value. In minor units without decimals.
+ * min 0
+ */
+ private Long averageTransactionValue;
+
+ /**
+ * The expected highest transaction value. In minor units without decimals.
+ * min 0
+ */
+ private Long highestTransactionValue;
+
+ /**
+ * The currency used for the processing details provided.
+ */
+ private Currency currency;
+
+}
diff --git a/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java b/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
index 81b13234..c2d65d1b 100644
--- a/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
+++ b/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
@@ -68,9 +68,10 @@ public final class OnboardEntityDetailsResponse extends Resource {
private List instruments;
/**
- * The sub-entity's expected processing (Accounts API v3.0).
+ * The sub-entity's expected processing (Accounts API v3.0). Amounts are {@code Long}; see
+ * {@link EntityProcessingDetails}.
*/
- private ProcessingDetails processingDetails;
+ private EntityProcessingDetails processingDetails;
/**
* The top-level documents used to support the verification of the sub-entity's details.
diff --git a/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java b/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
index 06f25a5e..22024de3 100644
--- a/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
+++ b/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
@@ -34,6 +34,10 @@ public enum FilePurpose {
PROOF_OF_RESIDENTIAL_ADDRESS,
@SerializedName("proof_of_registration")
PROOF_OF_REGISTRATION,
+ /**
+ * Not an onboarding upload purpose: POST /entities/{entityId}/files does not accept it
+ * ({@code PlatformsFileUpload} defines the other fourteen values only).
+ */
@SerializedName("dispute_evidence")
DISPUTE_EVIDENCE
}
\ No newline at end of file
diff --git a/src/main/java/com/checkout/common/AbstractFileRequest.java b/src/main/java/com/checkout/common/AbstractFileRequest.java
index 985ec79a..626b05a6 100644
--- a/src/main/java/com/checkout/common/AbstractFileRequest.java
+++ b/src/main/java/com/checkout/common/AbstractFileRequest.java
@@ -10,6 +10,10 @@
@AllArgsConstructor
public abstract class AbstractFileRequest {
+ /**
+ * The file to upload.
+ * [Required]
+ */
private File file;
/**
diff --git a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
index 88d4216c..d452715d 100644
--- a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
+++ b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
@@ -9,6 +9,8 @@
import java.util.Arrays;
import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -219,10 +221,54 @@ void shouldDeserializeEntityDetailsDocumentsAndProcessingDetails() {
OnboardEntityDetailsResponse.class);
assertEquals(Currency.USD, response.getProcessingDetails().getCurrency());
- assertEquals(1000000, response.getProcessingDetails().getAnnualProcessingVolume());
+ assertEquals(Long.valueOf(1000000), response.getProcessingDetails().getAnnualProcessingVolume());
assertEquals(BankVerificationType.BANK_STATEMENT, response.getDocuments().getBankVerification().getType());
assertEquals("file_bankverificationaaaaaaaaaa", response.getDocuments().getBankVerification().getFront());
assertEquals(ProofOfRegistrationType.EXTRACT_FROM_TRADE_REGISTER, response.getCompany().getRepresentatives()
.get(0).getDocuments().getProofOfRegistration().getType());
}
+
+ // Regression: processing_details amounts are integers in minor units with no maximum. Typed
+ // as Integer, any value above 2,147,483,647 (about 21.4 million in a two-decimal currency)
+ // made the whole GET /accounts/entities/{id} fail to deserialize.
+ @Test
+ void shouldDeserializeProcessingDetailsAmountsAboveIntegerRange() {
+ final OnboardEntityDetailsResponse response = serializer.fromJson("{\"processing_details\":{"
+ + "\"annual_processing_volume\":3000000000,"
+ + "\"average_transaction_value\":2500000000,"
+ + "\"highest_transaction_value\":9000000000}}",
+ OnboardEntityDetailsResponse.class);
+
+ assertEquals(Long.valueOf(3000000000L), response.getProcessingDetails().getAnnualProcessingVolume());
+ assertEquals(Long.valueOf(2500000000L), response.getProcessingDetails().getAverageTransactionValue());
+ assertEquals(Long.valueOf(9000000000L), response.getProcessingDetails().getHighestTransactionValue());
+ }
+
+ // ------------------------------------------------------------------------
+ // AccountsFilePurpose
+ // submitFile sends getPurpose() on the wire, so every value is asserted as a string.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldExposeEveryAccountsFilePurposeWireValue() {
+ final Map expected = new EnumMap<>(AccountsFilePurpose.class);
+ expected.put(AccountsFilePurpose.BANK_VERIFICATION, "bank_verification");
+ expected.put(AccountsFilePurpose.IDENTIFICATION, "identification");
+ expected.put(AccountsFilePurpose.IDENTITY_VERIFICATION, "identity_verification");
+ expected.put(AccountsFilePurpose.COMPANY_VERIFICATION, "company_verification");
+ expected.put(AccountsFilePurpose.FINANCIAL_VERIFICATION, "financial_verification");
+ expected.put(AccountsFilePurpose.TAX_VERIFICATION, "tax_verification");
+ expected.put(AccountsFilePurpose.ADDITIONAL_DOCUMENT, "additional_document");
+ expected.put(AccountsFilePurpose.ARTICLES_OF_ASSOCIATION, "articles_of_association");
+ expected.put(AccountsFilePurpose.CERTIFIED_AUTHORISED_SIGNATORY, "certified_authorised_signatory");
+ expected.put(AccountsFilePurpose.COMPANY_OWNERSHIP, "company_ownership");
+ expected.put(AccountsFilePurpose.PROOF_OF_LEGALITY, "proof_of_legality");
+ expected.put(AccountsFilePurpose.PROOF_OF_PRINCIPAL_ADDRESS, "proof_of_principal_address");
+ expected.put(AccountsFilePurpose.SHAREHOLDER_STRUCTURE, "shareholder_structure");
+ expected.put(AccountsFilePurpose.PROOF_OF_RESIDENTIAL_ADDRESS, "proof_of_residential_address");
+ expected.put(AccountsFilePurpose.PROOF_OF_REGISTRATION, "proof_of_registration");
+
+ assertEquals(AccountsFilePurpose.values().length, expected.size(), "every value must be asserted");
+ expected.forEach((purpose, wire) -> assertEquals(wire, purpose.getPurpose(), purpose.name()));
+ }
}
From 2635a2911616de12a049bebbe15ecc85e0f674ff Mon Sep 17 00:00:00 2001
From: david ruiz
Date: Thu, 1 Oct 2026 14:19:27 +0200
Subject: [PATCH 3/8] Docs extended + obsolete schemas identified
---
.../com/checkout/accounts/EntityDocument.java | 14 +++++++++
.../accounts/EntityFinancialDetails.java | 30 +++++++++++++++++++
.../accounts/EntityFinancialDocuments.java | 13 ++++++++
3 files changed, 57 insertions(+)
diff --git a/src/main/java/com/checkout/accounts/EntityDocument.java b/src/main/java/com/checkout/accounts/EntityDocument.java
index 388f9499..856777d3 100644
--- a/src/main/java/com/checkout/accounts/EntityDocument.java
+++ b/src/main/java/com/checkout/accounts/EntityDocument.java
@@ -5,14 +5,28 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Not defined by any Accounts API onboarding schema. Referenced only by {@link Company} {@code document}
+ * and {@link EntityFinancialDocuments}, both deprecated; retained so existing code keeps compiling.
+ *
+ * @deprecated Not part of any Accounts API onboarding schema. Will be removed in a future major
+ * version.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
+@Deprecated
public final class EntityDocument {
+ /**
+ * Not defined by any Accounts API onboarding schema.
+ */
private String type;
+ /**
+ * Not defined by any Accounts API onboarding schema.
+ */
private String fileId;
}
diff --git a/src/main/java/com/checkout/accounts/EntityFinancialDetails.java b/src/main/java/com/checkout/accounts/EntityFinancialDetails.java
index d6482ad0..7793add3 100644
--- a/src/main/java/com/checkout/accounts/EntityFinancialDetails.java
+++ b/src/main/java/com/checkout/accounts/EntityFinancialDetails.java
@@ -4,18 +4,48 @@
import lombok.Builder;
import lombok.Data;
+/**
+ * Seller financial questions ({@code financial_details}): on the company of EEA and US Company Full
+ * and Lite (2.0), and on the individual of US Sole Trader Full and Lite (2.0).
+ */
@Data
@Builder
public final class EntityFinancialDetails {
+ /**
+ * The estimated annual processing volume. In minor units without decimals.
+ * [Required] on the Full (2.0) variants; [Optional] on the Lite (2.0) variants.
+ * min 0
+ */
private Long annualProcessingVolume;
+ /**
+ * The expected average transaction value. In minor units without decimals.
+ * [Required] on the Full (2.0) variants; [Optional] on the Lite (2.0) variants.
+ * min 0
+ */
private Long averageTransactionValue;
+ /**
+ * The expected highest transaction value. In minor units without decimals.
+ * [Required] on the Full (2.0) variants; [Optional] on the Lite (2.0) variants.
+ * min 0
+ */
private Long highestTransactionValue;
+ /**
+ * Not defined by any Accounts API schema; the API does not read it. Supporting documents go on
+ * the top-level request documents ({@link OnboardSubEntityDocuments}) instead.
+ *
+ * @deprecated Not part of any Accounts API schema. Will be removed in a future major version.
+ */
+ @Deprecated
private EntityFinancialDocuments documents;
+ /**
+ * The currency used for the financial details provided.
+ * [Required] on US Company Full and US Sole Trader Full (2.0); [Optional] on the other variants.
+ */
private Currency currency;
}
diff --git a/src/main/java/com/checkout/accounts/EntityFinancialDocuments.java b/src/main/java/com/checkout/accounts/EntityFinancialDocuments.java
index 3890abeb..55f05d60 100644
--- a/src/main/java/com/checkout/accounts/EntityFinancialDocuments.java
+++ b/src/main/java/com/checkout/accounts/EntityFinancialDocuments.java
@@ -3,11 +3,24 @@
import lombok.Builder;
import lombok.Data;
+/**
+ * Not defined by any Accounts API schema: {@code financial_details} carries the three amounts and
+ * the currency only. Retained so existing code keeps compiling.
+ *
+ * @deprecated Not part of any Accounts API schema. Will be removed in a future major version.
+ */
@Data
@Builder
+@Deprecated
public final class EntityFinancialDocuments {
+ /**
+ * Not defined by any Accounts API schema.
+ */
private EntityDocument bankStatement;
+ /**
+ * Not defined by any Accounts API schema.
+ */
private EntityDocument financialStatement;
}
From 7335acfc2fa9222723fa44cfc480c30d07c102f8 Mon Sep 17 00:00:00 2001
From: david-ruiz-cko
Date: Fri, 2 Oct 2026 15:17:44 +0200
Subject: [PATCH 4/8] Bouncy Castle dependabot alert fix (#682)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 1cd2bbb6..7c884df9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@ plugins {
id 'maven-publish'
id 'io.github.gradle-nexus.publish-plugin' version '2.0.0'
id 'io.freefair.lombok' version '8.14.4'
- id "org.sonarqube" version "7.3.1.8318"
+ id "org.sonarqube" version "7.5.0.8588"
id 'jacoco'
}
From 6aa2d6108e80d1f0a4d1f698b28d25fe2b5eb04b Mon Sep 17 00:00:00 2001
From: david ruiz
Date: Thu, 1 Oct 2026 12:49:54 +0200
Subject: [PATCH 5/8] Representative documents alignment
---
.../com/checkout/accounts/AccountPhone.java | 12 ++
.../com/checkout/accounts/AccountsClient.java | 50 +++++
.../accounts/AccountsFilePurpose.java | 19 +-
.../checkout/accounts/AdditionalDocument.java | 10 +
.../accounts/ArticlesOfAssociation.java | 12 +-
.../accounts/ArticlesOfAssociationType.java | 3 +
.../checkout/accounts/BankVerification.java | 13 ++
.../accounts/BankVerificationType.java | 3 +
.../CertifiedAuthorisedSignatory.java | 34 ++++
.../CertifiedAuthorisedSignatoryType.java | 13 ++
.../accounts/CompanyVerification.java | 18 +-
.../accounts/CompanyVerificationType.java | 5 +
.../com/checkout/accounts/ContactDetails.java | 27 +++
.../java/com/checkout/accounts/Document.java | 20 ++
.../accounts/EntityEmailAddresses.java | 8 +
.../accounts/FinancialStatements.java | 15 ++
.../accounts/FinancialStatementsType.java | 4 +
.../accounts/FinancialVerification.java | 15 ++
.../accounts/FinancialVerificationType.java | 4 +
.../com/checkout/accounts/Identification.java | 18 ++
.../com/checkout/accounts/Individual.java | 52 ++++-
.../java/com/checkout/accounts/Invitee.java | 10 +
.../OnboardEntityDetailsResponse.java | 46 +++++
.../accounts/OnboardSubEntityDocuments.java | 110 +++++++++++
.../checkout/accounts/ProofOfLegality.java | 13 ++
.../accounts/ProofOfLegalityType.java | 3 +
.../accounts/ProofOfPrincipalAddress.java | 13 ++
.../accounts/ProofOfPrincipalAddressType.java | 5 +
.../accounts/ProofOfRegistration.java | 33 ++++
.../accounts/ProofOfRegistrationType.java | 17 ++
.../accounts/ProofOfResidentialAddress.java | 33 ++++
.../ProofOfResidentialAddressType.java | 15 ++
.../com/checkout/accounts/Representative.java | 82 ++++++++
.../accounts/RepresentativeIndividual.java | 61 ++++++
.../accounts/ShareholderStructure.java | 14 ++
.../accounts/ShareholderStructureType.java | 3 +
.../checkout/accounts/TaxVerification.java | 17 +-
.../accounts/TaxVerificationType.java | 4 +
.../accounts/files/entities/FilePurpose.java | 3 +
.../files/request/FileUploadRequest.java | 10 +-
.../files/response/FileDetailsResponse.java | 29 ++-
.../files/response/FileUploadResponse.java | 16 +-
.../com/checkout/common/DocumentType.java | 3 +
.../com/checkout/accounts/AccountsTestIT.java | 66 +++++++
.../accounts/AccountsV3SerializationTest.java | 57 ++++++
...rdSubEntityDocumentsSerializationTest.java | 187 ++++++++++++++++++
46 files changed, 1194 insertions(+), 11 deletions(-)
create mode 100644 src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatory.java
create mode 100644 src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatoryType.java
create mode 100644 src/main/java/com/checkout/accounts/ProofOfRegistration.java
create mode 100644 src/main/java/com/checkout/accounts/ProofOfRegistrationType.java
create mode 100644 src/main/java/com/checkout/accounts/ProofOfResidentialAddress.java
create mode 100644 src/main/java/com/checkout/accounts/ProofOfResidentialAddressType.java
diff --git a/src/main/java/com/checkout/accounts/AccountPhone.java b/src/main/java/com/checkout/accounts/AccountPhone.java
index 770b3e0c..ccb5f8a0 100644
--- a/src/main/java/com/checkout/accounts/AccountPhone.java
+++ b/src/main/java/com/checkout/accounts/AccountPhone.java
@@ -6,14 +6,26 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * A phone number on the Accounts API: the sub-entity's contact phone, or a representative's phone.
+ * See {@link ContactDetails} for the per-variant number format.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class AccountPhone {
+ /**
+ * The ISO 3166-1 alpha-2 country where the number is registered, not the dialling code.
+ * [Required] on Accounts API v3.0; not part of the v2.0 schemas.
+ */
private CountryCode countryCode;
+ /**
+ * The phone number, without the country calling code.
+ * [Required]
+ */
private String number;
}
diff --git a/src/main/java/com/checkout/accounts/AccountsClient.java b/src/main/java/com/checkout/accounts/AccountsClient.java
index f9a1a9ba..e91dd26d 100644
--- a/src/main/java/com/checkout/accounts/AccountsClient.java
+++ b/src/main/java/com/checkout/accounts/AccountsClient.java
@@ -18,10 +18,35 @@
public interface AccountsClient {
+ /**
+ * Uploads a file to the Files API (POST /files on the Files host), as a multipart request. The
+ * returned ID is what document {@code front} and {@code back} fields take.
+ *
+ * @param accountsFileRequest the path to the file, its content type, and its purpose
+ * @return the ID of the uploaded file
+ */
CompletableFuture submitFile(AccountsFileRequest accountsFileRequest);
+ /**
+ * Creates a file upload for a sub-entity (POST /entities/{entityId}/files on the Files host).
+ * The response carries the file ID and an upload link; the file content itself is sent to that
+ * link, not in this request.
+ *
+ * @param entityId the ID of the sub-entity
+ * @param fileUploadRequest the purpose of the file upload
+ * @return the file ID, the maximum size allowed, the MIME types allowed for the purpose, and the
+ * upload link
+ */
CompletableFuture uploadFile(String entityId, FileUploadRequest fileUploadRequest);
+ /**
+ * Retrieves the details of a sub-entity's file (GET /entities/{entityId}/files/{fileId} on the
+ * Files host).
+ *
+ * @param entityId the ID of the sub-entity
+ * @param fileId the ID of the file
+ * @return the file's status, size, MIME type, upload date and purpose
+ */
CompletableFuture retrieveFile(String entityId, String fileId);
CompletableFuture createEntity(OnboardEntityRequest entityRequest);
@@ -99,10 +124,35 @@ CompletableFuture updateReserveRule(String entityId,
CompletableFuture resolveEntityRequirement(String entityId, String requirementId, EntityRequirementUpdateRequest updateRequest);
// Synchronous methods
+ /**
+ * Uploads a file to the Files API (POST /files on the Files host), as a multipart request. The
+ * returned ID is what document {@code front} and {@code back} fields take.
+ *
+ * @param accountsFileRequest the path to the file, its content type, and its purpose
+ * @return the ID of the uploaded file
+ */
IdResponse submitFileSync(final AccountsFileRequest accountsFileRequest);
+ /**
+ * Creates a file upload for a sub-entity (POST /entities/{entityId}/files on the Files host).
+ * The response carries the file ID and an upload link; the file content itself is sent to that
+ * link, not in this request.
+ *
+ * @param entityId the ID of the sub-entity
+ * @param fileUploadRequest the purpose of the file upload
+ * @return the file ID, the maximum size allowed, the MIME types allowed for the purpose, and the
+ * upload link
+ */
FileUploadResponse uploadFileSync(final String entityId, final FileUploadRequest fileUploadRequest);
+ /**
+ * Retrieves the details of a sub-entity's file (GET /entities/{entityId}/files/{fileId} on the
+ * Files host).
+ *
+ * @param entityId the ID of the sub-entity
+ * @param fileId the ID of the file
+ * @return the file's status, size, MIME type, upload date and purpose
+ */
FileDetailsResponse retrieveFileSync(final String entityId, final String fileId);
OnboardEntityResponse createEntitySync(final OnboardEntityRequest entityRequest);
diff --git a/src/main/java/com/checkout/accounts/AccountsFilePurpose.java b/src/main/java/com/checkout/accounts/AccountsFilePurpose.java
index 83a1a71a..434880fe 100644
--- a/src/main/java/com/checkout/accounts/AccountsFilePurpose.java
+++ b/src/main/java/com/checkout/accounts/AccountsFilePurpose.java
@@ -2,14 +2,31 @@
import lombok.Getter;
+/**
+ * The purpose of a file uploaded with {@link AccountsClient#submitFile(AccountsFileRequest)}. The
+ * values match the purposes the Accounts API accepts for onboarding documents
+ * ({@code PlatformsFileUpload}), plus the legacy {@link #IDENTIFICATION}.
+ */
public enum AccountsFilePurpose {
BANK_VERIFICATION("bank_verification"),
+ /**
+ * Legacy purpose, not among the onboarding upload purposes; use {@link #IDENTITY_VERIFICATION}.
+ */
IDENTIFICATION("identification"),
IDENTITY_VERIFICATION("identity_verification"),
COMPANY_VERIFICATION("company_verification"),
FINANCIAL_VERIFICATION("financial_verification"),
- TAX_VERIFICATION("tax_verification");
+ TAX_VERIFICATION("tax_verification"),
+ ADDITIONAL_DOCUMENT("additional_document"),
+ ARTICLES_OF_ASSOCIATION("articles_of_association"),
+ CERTIFIED_AUTHORISED_SIGNATORY("certified_authorised_signatory"),
+ COMPANY_OWNERSHIP("company_ownership"),
+ PROOF_OF_LEGALITY("proof_of_legality"),
+ PROOF_OF_PRINCIPAL_ADDRESS("proof_of_principal_address"),
+ SHAREHOLDER_STRUCTURE("shareholder_structure"),
+ PROOF_OF_RESIDENTIAL_ADDRESS("proof_of_residential_address"),
+ PROOF_OF_REGISTRATION("proof_of_registration");
@Getter
private final String purpose;
diff --git a/src/main/java/com/checkout/accounts/AdditionalDocument.java b/src/main/java/com/checkout/accounts/AdditionalDocument.java
index ead77536..029a26b2 100644
--- a/src/main/java/com/checkout/accounts/AdditionalDocument.java
+++ b/src/main/java/com/checkout/accounts/AdditionalDocument.java
@@ -5,12 +5,22 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Additional space for documents to be provided when requested. Carries a file ID only; the API
+ * defines no document type for it.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class AdditionalDocument {
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java b/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java
index 0b136f40..110edf0b 100644
--- a/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java
+++ b/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java
@@ -8,7 +8,8 @@
/**
* Memorandum or articles of association document, supplied when onboarding a sub-entity.
*
- * Required on the company full onboarding variants. The API expects an object carrying the
+ *
Required on EEA and GB Company Full (3.0); optional on US Company Full (3.0) and the US ISV
+ * Seller variants. The API expects an object carrying the
* document type and the uploaded file ID, which is why this class exists: the field on
* {@link OnboardSubEntityDocuments} used to be the {@link ArticlesOfAssociationType} enum, so
* the SDK serialized a bare string and the API rejected the request.
@@ -20,13 +21,16 @@
public final class ArticlesOfAssociation {
/**
- * The type of document being used as the memorandum or articles of association.
+ * The type of document used.
+ * [Required]
*/
private ArticlesOfAssociationType type;
/**
- * The ID of the front side of the document as represented within Checkout.com systems,
- * as returned when the file was uploaded.
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
*/
private String front;
diff --git a/src/main/java/com/checkout/accounts/ArticlesOfAssociationType.java b/src/main/java/com/checkout/accounts/ArticlesOfAssociationType.java
index 4ed853fa..31ab89ca 100644
--- a/src/main/java/com/checkout/accounts/ArticlesOfAssociationType.java
+++ b/src/main/java/com/checkout/accounts/ArticlesOfAssociationType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document types accepted as memorandum or articles of association.
+ */
public enum ArticlesOfAssociationType {
@SerializedName("memorandum_of_association")
diff --git a/src/main/java/com/checkout/accounts/BankVerification.java b/src/main/java/com/checkout/accounts/BankVerification.java
index 0e01c9f7..2201abb9 100644
--- a/src/main/java/com/checkout/accounts/BankVerification.java
+++ b/src/main/java/com/checkout/accounts/BankVerification.java
@@ -5,14 +5,27 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * A document showing transactions from the last 3 months.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class BankVerification {
+ /**
+ * The type of document being used as bank verification.
+ * [Required]
+ */
private BankVerificationType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/BankVerificationType.java b/src/main/java/com/checkout/accounts/BankVerificationType.java
index 2ab7293b..3bb2d005 100644
--- a/src/main/java/com/checkout/accounts/BankVerificationType.java
+++ b/src/main/java/com/checkout/accounts/BankVerificationType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as bank verification.
+ */
public enum BankVerificationType {
@SerializedName("bank_statement")
diff --git a/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatory.java b/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatory.java
new file mode 100644
index 00000000..938d3230
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatory.java
@@ -0,0 +1,34 @@
+package com.checkout.accounts;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Certified authorised signatory document. Required when the legal representative or other role
+ * owner is not registered on the certificate of incorporation. Representative documents only
+ * ({@code company.representatives[].documents}), EEA, GB and US Company Full (3.0) and US ISV
+ * Seller Company (3.0); not accepted at the top level.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public final class CertifiedAuthorisedSignatory {
+
+ /**
+ * The type of document.
+ * [Required]
+ */
+ private CertifiedAuthorisedSignatoryType type;
+
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
+ private String front;
+
+}
diff --git a/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatoryType.java b/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatoryType.java
new file mode 100644
index 00000000..9b3e7cbf
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/CertifiedAuthorisedSignatoryType.java
@@ -0,0 +1,13 @@
+package com.checkout.accounts;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The document type accepted as a representative's certified authorised signatory document.
+ */
+public enum CertifiedAuthorisedSignatoryType {
+
+ @SerializedName("power_of_attorney")
+ POWER_OF_ATTORNEY
+
+}
diff --git a/src/main/java/com/checkout/accounts/CompanyVerification.java b/src/main/java/com/checkout/accounts/CompanyVerification.java
index 2e718eb0..f4e44b30 100644
--- a/src/main/java/com/checkout/accounts/CompanyVerification.java
+++ b/src/main/java/com/checkout/accounts/CompanyVerification.java
@@ -5,13 +5,29 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The document to use to confirm the company's identity (certified by a power of attorney within
+ * the last 3 months).
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class CompanyVerification {
- private TaxVerificationType type;
+ /**
+ * The type of document used for company verification. {@code articles_of_association} is
+ * accepted on the US Company (2.0) variants only.
+ * [Required]
+ */
+ private CompanyVerificationType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
+
}
diff --git a/src/main/java/com/checkout/accounts/CompanyVerificationType.java b/src/main/java/com/checkout/accounts/CompanyVerificationType.java
index 6fa5271b..6471a501 100644
--- a/src/main/java/com/checkout/accounts/CompanyVerificationType.java
+++ b/src/main/java/com/checkout/accounts/CompanyVerificationType.java
@@ -2,6 +2,11 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document types accepted as company verification. {@code articles_of_association} is
+ * accepted on the US Company (2.0) variants only; articles of association sent as their own
+ * document use {@link ArticlesOfAssociationType} instead.
+ */
public enum CompanyVerificationType {
@SerializedName("incorporation_document")
diff --git a/src/main/java/com/checkout/accounts/ContactDetails.java b/src/main/java/com/checkout/accounts/ContactDetails.java
index 3d5f129e..ccb243b7 100644
--- a/src/main/java/com/checkout/accounts/ContactDetails.java
+++ b/src/main/java/com/checkout/accounts/ContactDetails.java
@@ -5,16 +5,43 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Contact details of the sub-entity.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public final class ContactDetails {
+ /**
+ * The phone number of the sub-entity.
+ * [Required] for every Accounts API v2.0 variant and the US ISV Seller variants; [Optional] for the
+ * other v3.0 variants. On v3.0 {@code countryCode} is required and is the ISO 3166-1 alpha-2
+ * country where the number is registered (for example {@code FR}), not the dialling code; v2.0
+ * takes {@code number} only. {@code number} is the number without the country calling code, and
+ * its format depends on the variant:
+ *
+ * - v3.0 EEA: ^[0-9]{6,13}$, min 6 characters, max 13 characters
+ * - v3.0 GB: ^[0-9]{7,11}$, min 7 characters, max 11 characters
+ * - v3.0 US and US ISV Seller: ^[1-9][0-9]{9,16}$, min 10 characters, max 16 characters
+ * - v2.0: ^[1-9][0-9]{7,15}$, min 8 characters, max 16 characters; on the US v2.0 variants
+ * ^[2-9]{1}[0-9]{9,15}$, min 10 characters
+ *
+ */
private AccountPhone phone;
+ /**
+ * Email addresses for this sub-entity.
+ * [Required] for every Accounts API v2.0 variant and the US ISV Seller variants; [Optional] for the
+ * other v3.0 variants.
+ */
private EntityEmailAddresses emailAddresses;
+ /**
+ * The details of the user responsible for onboarding the sub-entity.
+ * [Optional] (not part of the US ISV Seller variants)
+ */
private Invitee invitee;
}
diff --git a/src/main/java/com/checkout/accounts/Document.java b/src/main/java/com/checkout/accounts/Document.java
index ac39206b..4aea3d52 100644
--- a/src/main/java/com/checkout/accounts/Document.java
+++ b/src/main/java/com/checkout/accounts/Document.java
@@ -6,16 +6,36 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The document to use to confirm an individual's identity ({@code identity_verification}): on a
+ * representative (Accounts API v3.0), or at the top level of the v2.0 sole trader variants.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public final class Document {
+ /**
+ * The type of document used for identity verification.
+ * [Required]
+ */
private DocumentType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
+ /**
+ * The ID of the back side of the document as represented within Checkout.com systems.
+ * [Optional]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String back;
}
diff --git a/src/main/java/com/checkout/accounts/EntityEmailAddresses.java b/src/main/java/com/checkout/accounts/EntityEmailAddresses.java
index 0bbbd840..53a178e7 100644
--- a/src/main/java/com/checkout/accounts/EntityEmailAddresses.java
+++ b/src/main/java/com/checkout/accounts/EntityEmailAddresses.java
@@ -5,12 +5,20 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Email addresses for this sub-entity.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public final class EntityEmailAddresses {
+ /**
+ * The main email address for this sub-entity.
+ * [Required]
+ * Format: email
+ */
private String primary;
}
diff --git a/src/main/java/com/checkout/accounts/FinancialStatements.java b/src/main/java/com/checkout/accounts/FinancialStatements.java
index a93a815a..ae60b0f4 100644
--- a/src/main/java/com/checkout/accounts/FinancialStatements.java
+++ b/src/main/java/com/checkout/accounts/FinancialStatements.java
@@ -5,14 +5,29 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Audited or management-prepared financial statements (when applicable). US ISV Seller variants
+ * only. Not the same document as {@link FinancialVerification}, whose type is the singular
+ * {@code financial_statement}.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class FinancialStatements {
+ /**
+ * The type of document.
+ * [Required]
+ */
private FinancialStatementsType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/FinancialStatementsType.java b/src/main/java/com/checkout/accounts/FinancialStatementsType.java
index b373a10d..dc147912 100644
--- a/src/main/java/com/checkout/accounts/FinancialStatementsType.java
+++ b/src/main/java/com/checkout/accounts/FinancialStatementsType.java
@@ -2,6 +2,10 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as financial statements (US ISV Seller variants). Note the plural
+ * {@code financial_statements}; {@link FinancialVerificationType} is a different enum.
+ */
public enum FinancialStatementsType {
@SerializedName("financial_statements")
diff --git a/src/main/java/com/checkout/accounts/FinancialVerification.java b/src/main/java/com/checkout/accounts/FinancialVerification.java
index f7abbf14..efdb4b75 100644
--- a/src/main/java/com/checkout/accounts/FinancialVerification.java
+++ b/src/main/java/com/checkout/accounts/FinancialVerification.java
@@ -5,14 +5,29 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Financial statement document. Becomes mandatory depending on the answer provided for
+ * {@code annual_processing_volume}; the sub-entity's status changes to {@code requirements_due}
+ * when it is needed.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class FinancialVerification {
+ /**
+ * The type of the file.
+ * [Required]
+ */
private FinancialVerificationType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/FinancialVerificationType.java b/src/main/java/com/checkout/accounts/FinancialVerificationType.java
index d1b14f21..56dcb142 100644
--- a/src/main/java/com/checkout/accounts/FinancialVerificationType.java
+++ b/src/main/java/com/checkout/accounts/FinancialVerificationType.java
@@ -2,6 +2,10 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as financial verification. Note the singular
+ * {@code financial_statement}; {@link FinancialStatementsType} is a different enum.
+ */
public enum FinancialVerificationType {
@SerializedName("financial_statement")
diff --git a/src/main/java/com/checkout/accounts/Identification.java b/src/main/java/com/checkout/accounts/Identification.java
index 5331ba44..1e726d11 100644
--- a/src/main/java/com/checkout/accounts/Identification.java
+++ b/src/main/java/com/checkout/accounts/Identification.java
@@ -5,14 +5,32 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The identification of a representative or individual on the Accounts API v2.0 US variants.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class Identification {
+ /**
+ * Social Security Number (SSN), or Individual Taxpayer Identification Number (ITIN) for non-US
+ * citizens.
+ * [Required]
+ * ^\d{9}$
+ * 9 characters
+ */
private String nationalIdNumber;
+ /**
+ * Not defined by the Accounts API: the identification object carries {@code national_id_number}
+ * only. Retained so existing code keeps compiling.
+ *
+ * @deprecated Not part of any Accounts API schema; the API does not read it. Will be removed in a
+ * future major version.
+ */
+ @Deprecated
private Document document;
}
diff --git a/src/main/java/com/checkout/accounts/Individual.java b/src/main/java/com/checkout/accounts/Individual.java
index 8f7da8a9..bba1878b 100644
--- a/src/main/java/com/checkout/accounts/Individual.java
+++ b/src/main/java/com/checkout/accounts/Individual.java
@@ -4,28 +4,78 @@
import lombok.Builder;
import lombok.Data;
+/**
+ * The top-level {@code individual} of the Accounts API v2.0 sole trader variants.
+ */
@Data
@Builder
public final class Individual {
+ /**
+ * The individual's first name.
+ * [Required]
+ * min 2 characters, max 50 characters
+ */
private String firstName;
+ /**
+ * The individual's middle name. Required if it appears in official documents.
+ * [Optional]
+ * min 2 characters, max 50 characters
+ */
private String middleName;
+ /**
+ * The individual's last name.
+ * [Required]
+ * min 2 characters, max 50 characters
+ */
private String lastName;
+ /**
+ * The trading name of the sub-entity, also referred to as 'doing business as'.
+ * [Required]
+ * min 2 characters, max 300 characters
+ */
private String tradingName;
+ /**
+ * Not defined by any Accounts API schema. Retained so existing code keeps compiling.
+ *
+ * @deprecated Not defined by any Accounts API schema; the API does not read it. Will be removed in
+ * a future major version.
+ */
+ @Deprecated
private String nationalTaxId;
+ /**
+ * The registered address of the sole trader's business.
+ * [Required]
+ */
private Address registeredAddress;
+ /**
+ * The date of birth of the person according to the Gregorian calendar.
+ * [Required], except on GB Sole Trader Lite (2.0) where it is [Optional].
+ */
private DateOfBirth dateOfBirth;
+ /**
+ * The place of birth of the person.
+ * [Required] for EEA Sole Trader Full and Lite (2.0); not part of the other v2.0 variants.
+ */
private PlaceOfBirth placeOfBirth;
+ /**
+ * The individual's identification. US Sole Trader (2.0) only.
+ * [Required] for US Sole Trader Full (2.0); [Optional] for US Sole Trader Lite (2.0).
+ */
private Identification identification;
-
+
+ /**
+ * Seller financial questions and supporting documents. US Sole Trader (2.0) only.
+ * [Required] for US Sole Trader Full (2.0); [Optional] for US Sole Trader Lite (2.0).
+ */
private EntityFinancialDetails financialDetails;
}
diff --git a/src/main/java/com/checkout/accounts/Invitee.java b/src/main/java/com/checkout/accounts/Invitee.java
index c39ae45d..f9c137f9 100644
--- a/src/main/java/com/checkout/accounts/Invitee.java
+++ b/src/main/java/com/checkout/accounts/Invitee.java
@@ -5,11 +5,21 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * The details of the user responsible for onboarding the sub-entity.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public final class Invitee {
+ /**
+ * The main email address for this sub-entity. Despite the spec's wording, this is the address of
+ * the invitee, the user responsible for onboarding the sub-entity.
+ * [Optional]
+ * Format: email
+ */
private String email;
+
}
diff --git a/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java b/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
index bf521a7d..81b13234 100644
--- a/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
+++ b/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
@@ -7,29 +7,75 @@
import java.util.List;
+/**
+ * The details of a sub-entity, as returned by GET /accounts/entities/{id}.
+ */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public final class OnboardEntityDetailsResponse extends Resource {
+ /**
+ * The ID of the sub-entity.
+ */
private String id;
+ /**
+ * A unique reference you can later use to identify the sub-entity.
+ */
private String reference;
+ /**
+ * The onboarding status of the sub-entity.
+ */
private OnboardingStatus status;
+ /**
+ * The capabilities of the entity.
+ */
private Capabilities capabilities;
+ /**
+ * List of requirements due in order to be onboarded.
+ */
private List requirementsDue;
+ /**
+ * Contact details of this sub-entity.
+ */
private ContactDetails contactDetails;
+ /**
+ * Information about the profile of the sub-entity, primarily regarding the products and services
+ * offered.
+ */
private Profile profile;
+ /**
+ * Information about the company represented by the sub-entity (company and v3.0 sole trader
+ * variants).
+ */
private Company company;
+ /**
+ * Information about the individual represented by the sub-entity (v2.0 sole trader variants).
+ */
private Individual individual;
+ /**
+ * The sub-entity's payment instruments.
+ */
private List instruments;
+ /**
+ * The sub-entity's expected processing (Accounts API v3.0).
+ */
+ private ProcessingDetails processingDetails;
+
+ /**
+ * The top-level documents used to support the verification of the sub-entity's details.
+ * Representative documents are on {@link Representative}, under {@code company}.
+ */
+ private OnboardSubEntityDocuments documents;
+
}
diff --git a/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java b/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java
index 1784e352..9394cc3a 100644
--- a/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java
+++ b/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java
@@ -6,39 +6,149 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Verification documents for a sub-entity. This one type serves two different objects on the
+ * Accounts API, which accept different keys:
+ *
+ * - The top-level request {@code documents}, on {@link OnboardEntityRequest}. The API
+ * ignores keys it does not recognise here rather than rejecting them, so a misplaced document is
+ * dropped silently.
+ * - A representative's {@code documents}, on {@link Representative}. This object is
+ * strict: it accepts only {@code identity_verification}, {@code certified_authorised_signatory},
+ * {@code proof_of_residential_address} and {@code proof_of_registration}, and rejects any other
+ * key.
+ *
+ * Each field below says which of the two it belongs to.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class OnboardSubEntityDocuments {
+ // Both
+
+ /**
+ * The document to use to confirm the individual's identity. Valid in both objects:
+ *
+ * - Representative: [Required] for the EEA, GB and US Sole Trader Full (3.0) variants;
+ * [Optional] for the company variants.
+ * - Top level: [Required] for the six sole trader variants of Accounts API v2.0, the only
+ * variants that take it there.
+ *
+ */
private Document identityVerification;
+ // Top level
+
+ /**
+ * The document to use to confirm the company's identity (certified by a power of attorney
+ * within the last 3 months). Top level only.
+ * [Required] for EEA Company Full (2.0 and 3.0) and GB Company Full (2.0); [Optional] for the
+ * other company variants and the US ISV Seller variants.
+ */
private CompanyVerification companyVerification;
+ /**
+ * Memorandum or Articles of Association document. Top level only.
+ * [Required] for EEA and GB Company Full (3.0); [Optional] for US Company Full (3.0) and the US
+ * ISV Seller variants.
+ */
private ArticlesOfAssociation articlesOfAssociation;
+ /**
+ * A document showing transactions from the last 3 months. Top level only.
+ * [Required] for EEA Company Full (3.0) and the EEA, GB and US Sole Trader Full (3.0) variants;
+ * [Optional] for GB and US Company Full (3.0) and EEA Company Full and Lite (2.0).
+ */
private BankVerification bankVerification;
+ /**
+ * Shareholder structure chart (including % of shares) certified by a competent authority
+ * individual and dated within the last 3 months. Top level only.
+ * [Required] for EEA and GB Company Full (3.0); [Optional] for US Company Full (3.0) and US ISV
+ * Seller Company (3.0).
+ */
private ShareholderStructure shareholderStructure;
+ /**
+ * A regulatory licence document required for the company to operate (when applicable). Top
+ * level only.
+ * [Optional] (EEA, GB and US Company Full (3.0) and the US ISV Seller variants)
+ */
private ProofOfLegality proofOfLegality;
+ /**
+ * Proof of the company's principal place of business. Top level only.
+ * [Optional] (EEA, GB and US Company Full (3.0) and the US ISV Seller variants)
+ */
private ProofOfPrincipalAddress proofOfPrincipalAddress;
+ /**
+ * Additional space for documents to be provided when requested. Top level only.
+ * [Optional] (EEA, GB and US Company and Sole Trader Full (3.0); not the US ISV Seller variants)
+ */
@SerializedName("additional_document1")
private AdditionalDocument additionalDocument1;
+ /**
+ * Additional space for documents to be provided when requested. Top level only.
+ * [Optional] (EEA, GB and US Company and Sole Trader Full (3.0); not the US ISV Seller variants)
+ */
@SerializedName("additional_document2")
private AdditionalDocument additionalDocument2;
+ /**
+ * Additional space for documents to be provided when requested. Top level only.
+ * [Optional] (EEA, GB and US Company and Sole Trader Full (3.0); not the US ISV Seller variants)
+ */
@SerializedName("additional_document3")
private AdditionalDocument additionalDocument3;
+ /**
+ * IRS-issued Employer Identification Number document used to verify the entity's tax
+ * identification. Top level only.
+ * [Optional] (US Company variants and the US ISV Seller variants only)
+ */
private TaxVerification taxVerification;
+ /**
+ * Financial statement document. Becomes mandatory depending on the answer provided for
+ * {@code annual_processing_volume}; the sub-entity's status changes to {@code requirements_due}
+ * when it is needed. Top level only.
+ * [Optional] (EEA Company Full and Lite (2.0) only)
+ */
private FinancialVerification financialVerification;
+ /**
+ * Audited or management-prepared financial statements (when applicable). Top level only.
+ * [Optional] (US ISV Seller variants only)
+ */
private FinancialStatements financialStatements;
+ // Representative only
+
+ /**
+ * Certified authorised signatory document. Required when the legal representative or other
+ * role owner is not registered on the certificate of incorporation. Representative only
+ * ({@code company.representatives[].documents}); not accepted at the top level.
+ * [Optional] (EEA, GB and US Company Full (3.0) and US ISV Seller Company (3.0))
+ */
+ private CertifiedAuthorisedSignatory certifiedAuthorisedSignatory;
+
+ /**
+ * Proof of residential address of the representative. Representative only
+ * ({@code company.representatives[].documents}); not accepted at the top level.
+ * [Required] for EEA Sole Trader Full (3.0), and only valid there.
+ */
+ private ProofOfResidentialAddress proofOfResidentialAddress;
+
+ /**
+ * Proof of the sole trader's registration, for example an extract from a trade register.
+ * Representative only ({@code company.representatives[].documents}); not accepted at the top
+ * level.
+ * [Required] for EEA Sole Trader Full (3.0), and only valid there.
+ */
+ private ProofOfRegistration proofOfRegistration;
+
}
diff --git a/src/main/java/com/checkout/accounts/ProofOfLegality.java b/src/main/java/com/checkout/accounts/ProofOfLegality.java
index 35263323..3ea7c861 100644
--- a/src/main/java/com/checkout/accounts/ProofOfLegality.java
+++ b/src/main/java/com/checkout/accounts/ProofOfLegality.java
@@ -5,14 +5,27 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * A regulatory licence document required for the company to operate (when applicable).
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class ProofOfLegality {
+ /**
+ * The type of document used for proof of legality.
+ * [Required]
+ */
private ProofOfLegalityType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/ProofOfLegalityType.java b/src/main/java/com/checkout/accounts/ProofOfLegalityType.java
index a1130bb3..3ba4f497 100644
--- a/src/main/java/com/checkout/accounts/ProofOfLegalityType.java
+++ b/src/main/java/com/checkout/accounts/ProofOfLegalityType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as proof of legality.
+ */
public enum ProofOfLegalityType {
@SerializedName("proof_of_legality")
diff --git a/src/main/java/com/checkout/accounts/ProofOfPrincipalAddress.java b/src/main/java/com/checkout/accounts/ProofOfPrincipalAddress.java
index b7488f84..3aa25a95 100644
--- a/src/main/java/com/checkout/accounts/ProofOfPrincipalAddress.java
+++ b/src/main/java/com/checkout/accounts/ProofOfPrincipalAddress.java
@@ -5,14 +5,27 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Proof of the company's principal place of business.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class ProofOfPrincipalAddress {
+ /**
+ * The type of document being used as address verification.
+ * [Required]
+ */
private ProofOfPrincipalAddressType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/ProofOfPrincipalAddressType.java b/src/main/java/com/checkout/accounts/ProofOfPrincipalAddressType.java
index e04df292..d247d496 100644
--- a/src/main/java/com/checkout/accounts/ProofOfPrincipalAddressType.java
+++ b/src/main/java/com/checkout/accounts/ProofOfPrincipalAddressType.java
@@ -2,6 +2,11 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as proof of the company's principal place of business. Carries the
+ * same {@code proof_of_address} value as {@link ProofOfResidentialAddressType}, but the API defines
+ * the two as separate enums on separate documents.
+ */
public enum ProofOfPrincipalAddressType {
@SerializedName("proof_of_address")
diff --git a/src/main/java/com/checkout/accounts/ProofOfRegistration.java b/src/main/java/com/checkout/accounts/ProofOfRegistration.java
new file mode 100644
index 00000000..7bbbe9fa
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/ProofOfRegistration.java
@@ -0,0 +1,33 @@
+package com.checkout.accounts;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Proof of the sole trader's registration, for example an extract from a trade register.
+ * Representative documents only ({@code company.representatives[].documents}), EEA Sole Trader
+ * Full (3.0); not accepted at the top level.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public final class ProofOfRegistration {
+
+ /**
+ * The type of document being used as proof of registration.
+ * [Required]
+ */
+ private ProofOfRegistrationType type;
+
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
+ private String front;
+
+}
diff --git a/src/main/java/com/checkout/accounts/ProofOfRegistrationType.java b/src/main/java/com/checkout/accounts/ProofOfRegistrationType.java
new file mode 100644
index 00000000..cb52e352
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/ProofOfRegistrationType.java
@@ -0,0 +1,17 @@
+package com.checkout.accounts;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The document types accepted as a sole trader's proof of registration (EEA Sole Trader Full
+ * (3.0)).
+ */
+public enum ProofOfRegistrationType {
+
+ @SerializedName("extract_from_trade_register")
+ EXTRACT_FROM_TRADE_REGISTER,
+
+ @SerializedName("other")
+ OTHER
+
+}
diff --git a/src/main/java/com/checkout/accounts/ProofOfResidentialAddress.java b/src/main/java/com/checkout/accounts/ProofOfResidentialAddress.java
new file mode 100644
index 00000000..a86e9fc5
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/ProofOfResidentialAddress.java
@@ -0,0 +1,33 @@
+package com.checkout.accounts;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Proof of residential address of the representative. Representative documents only
+ * ({@code company.representatives[].documents}), EEA Sole Trader Full (3.0); not accepted at the
+ * top level.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public final class ProofOfResidentialAddress {
+
+ /**
+ * The type of document being used as address verification.
+ * [Required]
+ */
+ private ProofOfResidentialAddressType type;
+
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
+ private String front;
+
+}
diff --git a/src/main/java/com/checkout/accounts/ProofOfResidentialAddressType.java b/src/main/java/com/checkout/accounts/ProofOfResidentialAddressType.java
new file mode 100644
index 00000000..800d4258
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/ProofOfResidentialAddressType.java
@@ -0,0 +1,15 @@
+package com.checkout.accounts;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The document type accepted as a representative's proof of residential address (EEA Sole Trader
+ * Full (3.0)). Carries the same {@code proof_of_address} value as {@link ProofOfPrincipalAddressType},
+ * but the API defines the two as separate enums on separate documents.
+ */
+public enum ProofOfResidentialAddressType {
+
+ @SerializedName("proof_of_address")
+ PROOF_OF_ADDRESS
+
+}
diff --git a/src/main/java/com/checkout/accounts/Representative.java b/src/main/java/com/checkout/accounts/Representative.java
index 9443d6f9..56a2437a 100644
--- a/src/main/java/com/checkout/accounts/Representative.java
+++ b/src/main/java/com/checkout/accounts/Representative.java
@@ -6,68 +6,150 @@
import java.util.List;
+/**
+ * A representative of the sub-entity. One class covers every shape the Accounts API defines:
+ *
+ * - v3.0 person of interest: {@code individual}, {@code roles}, {@code companyPosition},
+ * {@code ownershipPercentage}, {@code documents}.
+ * - v3.0 controlling company (EEA and GB Company Full): {@code company} and
+ * {@code ownershipPercentage}.
+ * - v2.0 company representatives: the deprecated flat person fields, {@code roles},
+ * {@code documents} and, on the US variants, {@code identification}.
+ *
+ */
@Data
@Builder
public final class Representative {
/**
+ * The representative's first name. Accounts API v2.0 only.
+ * [Required] (v2.0)
+ * min 2 characters, max 50 characters
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private String firstName;
/**
+ * The representative's middle name. Required if it appears in official documents. Accounts API
+ * v2.0 only.
+ * [Optional]
+ * min 2 characters, max 50 characters
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private String middleName;
/**
+ * The representative's last name. Accounts API v2.0 only.
+ * [Required] (v2.0)
+ * min 2 characters, max 50 characters
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private String lastName;
/**
+ * The representative's address. Accounts API v2.0 only.
+ * [Required] (v2.0)
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private Address address;
/**
+ * The representative's identification. Accounts API v2.0 US Company variants only.
+ * [Required] for US Company Full (2.0); [Optional] for US Company Lite (2.0).
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private Identification identification;
/**
+ * The representative's phone number. Accounts API v2.0 only.
+ * [Optional]
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private AccountPhone phone;
/**
+ * The date of birth of the person according to the Gregorian calendar. Accounts API v2.0 only.
+ * [Required] for the v2.0 Full variants; [Optional] for the v2.0 Lite variants.
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private DateOfBirth dateOfBirth;
/**
+ * The place of birth of the person. Accounts API v2.0 only.
+ * [Required] for EEA Company Full (2.0); [Optional] for EEA Company Lite (2.0). Not part of the
+ * other v2.0 variants.
+ *
* @deprecated Not used by the Accounts API v3.0 schema; use {@link #individual} instead.
*/
@Deprecated
private PlaceOfBirth placeOfBirth;
+ /**
+ * The individual's roles within the company. For sole traders, must be {@code ubo} only.
+ * [Required] for every variant except EEA and US Company Lite (2.0), where it is [Optional].
+ */
private List roles;
+ /**
+ * Verification documents for the individual representative. The API validates this object
+ * strictly on v3.0: it accepts only {@code identity_verification},
+ * {@code certified_authorised_signatory}, {@code proof_of_residential_address} and
+ * {@code proof_of_registration}, and rejects any other key. See
+ * {@link OnboardSubEntityDocuments} for which apply to each variant.
+ * [Required] for the EEA, GB and US Sole Trader Full (3.0) variants and EEA Company Full (2.0);
+ * [Optional] otherwise.
+ */
private OnboardSubEntityDocuments documents;
+ /**
+ * Information about the individual representing the sub-entity.
+ * [Required] for every v3.0 person of interest.
+ */
private RepresentativeIndividual individual;
+ /**
+ * The representative's id.
+ * [Optional]
+ * ^rep_[a-z0-9]{26}$
+ * 30 characters
+ */
private String id;
+ /**
+ * The position of the representative within the company (required for the
+ * {@code control_person} role).
+ * [Optional] (EEA, GB and US Company Full (3.0) and US ISV Seller Company (3.0))
+ */
private CompanyPosition companyPosition;
+ /**
+ * The percentage ownership of the UBO or controlling company (required when over 25%).
+ * [Optional]
+ * min 25, max 100 on the EEA, GB and US Company Full (3.0) variants; min 0, max 100 on the US ISV
+ * Seller variants
+ */
private Integer ownershipPercentage;
+ /**
+ * The controlling company, when the representative is a company rather than an individual.
+ * [Required] for a controlling company representative (EEA and GB Company Full (3.0) only).
+ * The API reads only three fields here, all [Required]: {@code legalName}, {@code tradingName}
+ * and {@code registeredAddress}. Leave the other {@link Company} fields unset.
+ */
+ private Company company;
+
}
diff --git a/src/main/java/com/checkout/accounts/RepresentativeIndividual.java b/src/main/java/com/checkout/accounts/RepresentativeIndividual.java
index e35dc5e7..1cc74b74 100644
--- a/src/main/java/com/checkout/accounts/RepresentativeIndividual.java
+++ b/src/main/java/com/checkout/accounts/RepresentativeIndividual.java
@@ -8,32 +8,93 @@
import java.util.List;
+/**
+ * The personal details of a company representative ({@code company.representatives[].individual}),
+ * Accounts API v3.0.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class RepresentativeIndividual {
+ /**
+ * The representative's first name.
+ * [Required]
+ * min 2 characters, max 50 characters
+ */
private String firstName;
+ /**
+ * The representative's middle name. Required if it appears in official documents.
+ * [Optional]
+ * min 2 characters, max 50 characters
+ */
private String middleName;
+ /**
+ * The representative's last name.
+ * [Required]
+ * min 2 characters, max 50 characters
+ */
private String lastName;
+ /**
+ * The date of birth of the person according to the Gregorian calendar.
+ * [Required]
+ */
private DateOfBirth dateOfBirth;
+ /**
+ * The place of birth of the person.
+ * [Required]
+ */
private PlaceOfBirth placeOfBirth;
+ /**
+ * The list of citizenships or legal statuses for the representative.
+ * [Required] for the US ISV Seller variants only; not part of the other v3.0 schemas, leave unset
+ * for them.
+ */
private List citizenships;
+ /**
+ * The classification of the national identification number provided.
+ * [Required] for the US ISV Seller variants only; not part of the other v3.0 schemas, leave unset
+ * for them.
+ */
private NationalIdType nationalIdType;
+ /**
+ * The representative's national identification number. v3.0 only.
+ * [Required] for the US ISV Seller variants; [Optional] for the other v3.0 variants.
+ * The format depends on the variant:
+ *
+ * - US ISV Seller: the number for the {@code nationalIdType} given. ^[a-zA-Z0-9\-]+$, min 5
+ * characters, max 16 characters.
+ * - Other v3.0 variants: a Social Security Number (SSN) or Individual Taxpayer Identification
+ * Number (ITIN), US residents only. ^\d{9}$, 9 characters.
+ *
+ */
private String nationalIdNumber;
+ /**
+ * The representative's personal email address.
+ * [Required] for the US ISV Seller variants; [Optional] for the other v3.0 variants.
+ * Format: email
+ */
private String emailAddress;
+ /**
+ * The representative's phone number.
+ * [Required] for the US ISV Seller variants; [Optional] for the other v3.0 variants.
+ */
private AccountPhone phone;
+ /**
+ * The representative's address.
+ * [Required]
+ */
private Address address;
}
diff --git a/src/main/java/com/checkout/accounts/ShareholderStructure.java b/src/main/java/com/checkout/accounts/ShareholderStructure.java
index 9d9de8ab..cd504d4d 100644
--- a/src/main/java/com/checkout/accounts/ShareholderStructure.java
+++ b/src/main/java/com/checkout/accounts/ShareholderStructure.java
@@ -5,14 +5,28 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Shareholder structure chart (including % of shares) certified by a competent authority
+ * individual and dated within the last 3 months.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class ShareholderStructure {
+ /**
+ * The type of document.
+ * [Required]
+ */
private ShareholderStructureType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
}
diff --git a/src/main/java/com/checkout/accounts/ShareholderStructureType.java b/src/main/java/com/checkout/accounts/ShareholderStructureType.java
index c7a3ce7b..7e5cab0e 100644
--- a/src/main/java/com/checkout/accounts/ShareholderStructureType.java
+++ b/src/main/java/com/checkout/accounts/ShareholderStructureType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as a certified shareholder structure.
+ */
public enum ShareholderStructureType {
@SerializedName("certified_shareholder_structure")
diff --git a/src/main/java/com/checkout/accounts/TaxVerification.java b/src/main/java/com/checkout/accounts/TaxVerification.java
index 8b856e01..256bff68 100644
--- a/src/main/java/com/checkout/accounts/TaxVerification.java
+++ b/src/main/java/com/checkout/accounts/TaxVerification.java
@@ -5,13 +5,28 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * IRS-issued Employer Identification Number document used to verify the entity's tax
+ * identification (US variants).
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class TaxVerification {
- private CompanyVerificationType type;
+ /**
+ * The type of IRS-issued document used for tax verification.
+ * [Required]
+ */
+ private TaxVerificationType type;
+ /**
+ * The ID of the front side of the document as represented within Checkout.com systems.
+ * [Required]
+ * ^file_[a-z2-7]{26}$
+ * 31 characters
+ */
private String front;
+
}
diff --git a/src/main/java/com/checkout/accounts/TaxVerificationType.java b/src/main/java/com/checkout/accounts/TaxVerificationType.java
index 096ea421..e1545e4c 100644
--- a/src/main/java/com/checkout/accounts/TaxVerificationType.java
+++ b/src/main/java/com/checkout/accounts/TaxVerificationType.java
@@ -2,6 +2,10 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document type accepted as tax verification: an IRS-issued Employer Identification Number
+ * letter.
+ */
public enum TaxVerificationType {
@SerializedName("ein_letter")
diff --git a/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java b/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
index cb030fd0..06f25a5e 100644
--- a/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
+++ b/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The purpose of a sub-entity file upload (POST /entities/{entityId}/files).
+ */
public enum FilePurpose {
@SerializedName("additional_document")
ADDITIONAL_DOCUMENT,
diff --git a/src/main/java/com/checkout/accounts/files/request/FileUploadRequest.java b/src/main/java/com/checkout/accounts/files/request/FileUploadRequest.java
index 9f076e23..7685e003 100644
--- a/src/main/java/com/checkout/accounts/files/request/FileUploadRequest.java
+++ b/src/main/java/com/checkout/accounts/files/request/FileUploadRequest.java
@@ -8,11 +8,19 @@
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
+/**
+ * The request body of POST /entities/{entityId}/files.
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class FileUploadRequest {
+ /**
+ * The purpose of the file upload.
+ * [Required]
+ */
private FilePurpose purpose;
-}
\ No newline at end of file
+
+}
diff --git a/src/main/java/com/checkout/accounts/files/response/FileDetailsResponse.java b/src/main/java/com/checkout/accounts/files/response/FileDetailsResponse.java
index 467a45ae..fbfa5755 100644
--- a/src/main/java/com/checkout/accounts/files/response/FileDetailsResponse.java
+++ b/src/main/java/com/checkout/accounts/files/response/FileDetailsResponse.java
@@ -11,23 +11,50 @@
import java.time.Instant;
import java.util.List;
+/**
+ * The details of a sub-entity's file, as returned by GET /entities/{entityId}/files/{fileId}.
+ */
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public final class FileDetailsResponse extends Resource {
+ /**
+ * The ID of the file.
+ */
private String id;
+ /**
+ * The current status of the file.
+ */
private String status;
+ /**
+ * If {@code status} is {@code invalid}, the reasons why the file was invalid; otherwise null.
+ */
private List statusReasons;
+ /**
+ * The size of the file, in KB.
+ */
private Long size;
+ /**
+ * The MIME type of the file.
+ */
private String mimeType;
+ /**
+ * The date and time the file was uploaded, in ISO 8601 UTC format.
+ * Format: date-time (RFC 3339)
+ */
private Instant uploadedOn;
+ /**
+ * The purpose of the file, as provided in the initial request. A value {@link FilePurpose} does
+ * not define deserializes to null.
+ */
private FilePurpose purpose;
-}
\ No newline at end of file
+
+}
diff --git a/src/main/java/com/checkout/accounts/files/response/FileUploadResponse.java b/src/main/java/com/checkout/accounts/files/response/FileUploadResponse.java
index da8c4ff2..4ff25d35 100644
--- a/src/main/java/com/checkout/accounts/files/response/FileUploadResponse.java
+++ b/src/main/java/com/checkout/accounts/files/response/FileUploadResponse.java
@@ -9,15 +9,29 @@
import java.util.List;
+/**
+ * The response of POST /entities/{entityId}/files: the file ID and the upload link. The file content
+ * itself is sent to that link, not in the request.
+ */
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public final class FileUploadResponse extends Resource {
+ /**
+ * The file identifier.
+ */
private String id;
+ /**
+ * The maximum file size allowed, in bytes.
+ */
private Long maximumSizeInBytes;
+ /**
+ * The MIME file types allowed for the document purpose provided on the initial request.
+ */
private List documentTypesForPurpose;
-}
\ No newline at end of file
+
+}
diff --git a/src/main/java/com/checkout/common/DocumentType.java b/src/main/java/com/checkout/common/DocumentType.java
index 40262589..81daab25 100644
--- a/src/main/java/com/checkout/common/DocumentType.java
+++ b/src/main/java/com/checkout/common/DocumentType.java
@@ -2,6 +2,9 @@
import com.google.gson.annotations.SerializedName;
+/**
+ * The document types accepted to confirm an individual's identity.
+ */
public enum DocumentType {
@SerializedName("passport")
diff --git a/src/test/java/com/checkout/accounts/AccountsTestIT.java b/src/test/java/com/checkout/accounts/AccountsTestIT.java
index 95327a19..b8d278e8 100644
--- a/src/test/java/com/checkout/accounts/AccountsTestIT.java
+++ b/src/test/java/com/checkout/accounts/AccountsTestIT.java
@@ -19,6 +19,7 @@
import com.checkout.accounts.files.entities.FilePurpose;
import com.checkout.common.Address;
import com.checkout.common.CountryCode;
+import com.checkout.common.DocumentType;
import com.checkout.common.Currency;
import com.checkout.common.IdResponse;
import com.checkout.common.InstrumentType;
@@ -172,6 +173,59 @@ void shouldCreateGetAndUpdateOnboardCompanyEntitySync() {
// onboarding tests above (which pin schema_version to "2.0"); here createEntity/getEntity/
// updateEntity use the SDK default (3.0). They run through the accounts-scoped OAuth client,
// which is the one provisioned for v3.0 onboarding.
+ // The representative's documents on schema 3.0. The sandbox platform resolves to a company
+ // variant (GB/US scope, USD only), where identity_verification and certified_authorised_signatory
+ // are the representative documents the API accepts; the EEA Sole Trader keys are covered by
+ // OnboardSubEntityDocumentsSerializationTest, since this platform rejects them.
+ @Test
+ void shouldCreateEntityWithRepresentativeDocuments() throws URISyntaxException {
+ final CheckoutApi checkoutApi = accountsApi();
+ final IdResponse identityFile = submitAccountsFile(checkoutApi, AccountsFilePurpose.IDENTITY_VERIFICATION);
+ final IdResponse signatoryFile = submitAccountsFile(checkoutApi, AccountsFilePurpose.CERTIFIED_AUTHORISED_SIGNATORY);
+
+ final OnboardEntityRequest request = buildCompanyEntityV3(RandomStringUtils.random(15, true, true));
+ request.getCompany().getRepresentatives().get(0).setDocuments(OnboardSubEntityDocuments.builder()
+ .identityVerification(Document.builder()
+ .type(DocumentType.PASSPORT)
+ .front(identityFile.getId())
+ .build())
+ .certifiedAuthorisedSignatory(CertifiedAuthorisedSignatory.builder()
+ .type(CertifiedAuthorisedSignatoryType.POWER_OF_ATTORNEY)
+ .front(signatoryFile.getId())
+ .build())
+ .build());
+
+ final OnboardEntityResponse entityResponse = blocking(() -> checkoutApi.accountsClient().createEntity(request));
+ assertNotNull(entityResponse.getId());
+
+ // The documents are linked on the representative, not dropped: the API echoes them back.
+ final OnboardEntityDetailsResponse details = blocking(() -> checkoutApi.accountsClient().getEntity(entityResponse.getId()));
+ final OnboardSubEntityDocuments linked = details.getCompany().getRepresentatives().get(0).getDocuments();
+ assertEquals(DocumentType.PASSPORT, linked.getIdentityVerification().getType());
+ assertEquals(identityFile.getId(), linked.getIdentityVerification().getFront());
+ assertEquals(CertifiedAuthorisedSignatoryType.POWER_OF_ATTORNEY, linked.getCertifiedAuthorisedSignatory().getType());
+ assertEquals(signatoryFile.getId(), linked.getCertifiedAuthorisedSignatory().getFront());
+ }
+
+ // The two EEA Sole Trader representative documents need their own upload purposes before they
+ // can be linked. Goes through POST /entities/{id}/files, the endpoint whose request schema
+ // (PlatformsFileUpload) defines the purpose enum.
+ @Test
+ void shouldUploadRepresentativeProofFilesForEntity() {
+ final String entityId = createTestEntity();
+
+ for (final FilePurpose purpose : new FilePurpose[]{FilePurpose.PROOF_OF_RESIDENTIAL_ADDRESS, FilePurpose.PROOF_OF_REGISTRATION}) {
+ final FileUploadResponse uploadResponse = blocking(() -> accountsApi().accountsClient()
+ .uploadFile(entityId, FileUploadRequest.builder().purpose(purpose).build()));
+ validateFileUploadResponseForEntity(uploadResponse);
+
+ final FileDetailsResponse details = blocking(() -> accountsApi().accountsClient()
+ .retrieveFile(entityId, uploadResponse.getId()));
+ validateFileDetailsResponseForEntity(details, uploadResponse.getId());
+ assertEquals(purpose, details.getPurpose());
+ }
+ }
+
@Test
void shouldCreateGetAndUpdateOnboardCompanyEntityV3() {
final CheckoutApi checkoutApi = getAccountsCheckoutApi();
@@ -836,6 +890,18 @@ private IdResponse uploadFile() throws URISyntaxException {
return fileResponse;
}
+ private IdResponse submitAccountsFile(final CheckoutApi api, final AccountsFilePurpose purpose) throws URISyntaxException {
+ final File file = new File(getClass().getClassLoader().getResource("checkout.jpeg").toURI());
+ final IdResponse fileResponse = blocking(() -> api.accountsClient().submitFile(AccountsFileRequest.builder()
+ .file(file)
+ .contentType(ContentType.IMAGE_JPEG)
+ .purpose(purpose)
+ .build()));
+ assertNotNull(fileResponse);
+ assertNotNull(fileResponse.getId());
+ return fileResponse;
+ }
+
private CheckoutApi accountsApi() {
if (accountsApi == null) {
accountsApi = getAccountsCheckoutApi();
diff --git a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
index bb9ab2b6..88d4216c 100644
--- a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
+++ b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
@@ -1,8 +1,10 @@
package com.checkout.accounts;
import com.checkout.GsonSerializer;
+import com.checkout.common.Address;
import com.checkout.common.CountryCode;
import com.checkout.common.Currency;
+import com.google.gson.JsonParser;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
@@ -168,4 +170,59 @@ void shouldDeserializeNewEnumValuesToExactSwaggerStrings() {
assertEquals(CompanyPosition.CEO, serializer.fromJson("\"ceo\"", CompanyPosition.class));
assertEquals(CompanyPosition.OTHER_NON_EXECUTIVE_NON_SENIOR, serializer.fromJson("\"other_non_executive_non_senior\"", CompanyPosition.class));
}
+
+ // ------------------------------------------------------------------------
+ // Controlling company representative
+ // EEA and GB Company Full (3.0) allow a representative that is a company:
+ // { id, company: { legal_name, trading_name, registered_address },
+ // ownership_percentage }. The field was not modelled.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSerializeControllingCompanyRepresentative() {
+ final Representative representative = Representative.builder()
+ .company(Company.builder()
+ .legalName("Parent Holdings Ltd")
+ .tradingName("Parent Holdings")
+ .registeredAddress(Address.builder()
+ .addressLine1("1 Main Street")
+ .city("London")
+ .zip("W1T 4TJ")
+ .country(CountryCode.GB)
+ .build())
+ .build())
+ .ownershipPercentage(60)
+ .build();
+
+ assertEquals(JsonParser.parseString("{\"ownership_percentage\":60,\"company\":{"
+ + "\"legal_name\":\"Parent Holdings Ltd\",\"trading_name\":\"Parent Holdings\","
+ + "\"registered_address\":{\"address_line1\":\"1 Main Street\",\"city\":\"London\","
+ + "\"zip\":\"W1T 4TJ\",\"country\":\"GB\"}}}"),
+ JsonParser.parseString(serializer.toJson(representative)));
+ }
+
+ // ------------------------------------------------------------------------
+ // OnboardEntityDetailsResponse
+ // GET /accounts/entities/{id} returns documents and processing_details; neither
+ // was modelled, so the top-level documents could not be read back.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldDeserializeEntityDetailsDocumentsAndProcessingDetails() {
+ final OnboardEntityDetailsResponse response = serializer.fromJson("{"
+ + "\"id\":\"ent_aaaaaaaaaaaaaaaaaaaaaaaaaa\","
+ + "\"processing_details\":{\"currency\":\"USD\",\"annual_processing_volume\":1000000},"
+ + "\"documents\":{\"bank_verification\":{\"type\":\"bank_statement\","
+ + "\"front\":\"file_bankverificationaaaaaaaaaa\"}},"
+ + "\"company\":{\"representatives\":[{\"documents\":{\"proof_of_registration\":"
+ + "{\"type\":\"extract_from_trade_register\",\"front\":\"file_proofofregistrationaaaaaaa\"}}}]}}",
+ OnboardEntityDetailsResponse.class);
+
+ assertEquals(Currency.USD, response.getProcessingDetails().getCurrency());
+ assertEquals(1000000, response.getProcessingDetails().getAnnualProcessingVolume());
+ assertEquals(BankVerificationType.BANK_STATEMENT, response.getDocuments().getBankVerification().getType());
+ assertEquals("file_bankverificationaaaaaaaaaa", response.getDocuments().getBankVerification().getFront());
+ assertEquals(ProofOfRegistrationType.EXTRACT_FROM_TRADE_REGISTER, response.getCompany().getRepresentatives()
+ .get(0).getDocuments().getProofOfRegistration().getType());
+ }
}
diff --git a/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java b/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java
index b2182d88..77840cdf 100644
--- a/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java
+++ b/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java
@@ -1,8 +1,13 @@
package com.checkout.accounts;
import com.checkout.GsonSerializer;
+import com.checkout.common.DocumentType;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import org.junit.jupiter.api.Test;
+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.assertTrue;
@@ -82,4 +87,186 @@ void shouldDeserializeArticlesOfAssociation() {
assertEquals(ArticlesOfAssociationType.ARTICLES_OF_ASSOCIATION, documents.getArticlesOfAssociation().getType());
assertEquals("file_6lbss42ezvoufcb2beo76rvwly", documents.getArticlesOfAssociation().getFront());
}
+
+ // ------------------------------------------------------------------------
+ // Representative documents (company.representatives[].documents)
+ // The EEA Sole Trader (3.0) keys and the company-variant certified authorised
+ // signatory. The representative object is strict on the API, so the exact key
+ // set matters.
+ // ------------------------------------------------------------------------
+
+ // Regression: EEA Sole Trader (3.0) needs proof_of_residential_address and proof_of_registration
+ // on the representative, with bank_verification alone at the top level. Neither could be
+ // expressed on the representative before.
+ @Test
+ void shouldSerializeEeaSoleTraderRepresentativeDocuments() {
+ final OnboardEntityRequest request = OnboardEntityRequest.builder()
+ .reference("ref_sole_trader")
+ .company(Company.builder()
+ .businessType(BusinessType.INDIVIDUAL_OR_SOLE_PROPRIETORSHIP)
+ .representatives(Collections.singletonList(Representative.builder()
+ .individual(RepresentativeIndividual.builder().firstName("Jane").lastName("Doe").build())
+ .roles(Collections.singletonList(EntityRoles.UBO))
+ .documents(eeaSoleTraderRepresentativeDocuments())
+ .build()))
+ .build())
+ .documents(OnboardSubEntityDocuments.builder()
+ .bankVerification(BankVerification.builder()
+ .type(BankVerificationType.BANK_STATEMENT)
+ .front("file_bankverificationaaaaaaaaaa")
+ .build())
+ .build())
+ .build();
+
+ final String json = serializer.toJson(request);
+ final JsonObject body = JsonParser.parseString(json).getAsJsonObject();
+
+ assertEquals(JsonParser.parseString("{"
+ + "\"identity_verification\":{\"type\":\"passport\",\"front\":\"file_identityverificationaaaaaa\"},"
+ + "\"proof_of_residential_address\":{\"type\":\"proof_of_address\",\"front\":\"file_proofofresidentialaddressa\"},"
+ + "\"proof_of_registration\":{\"type\":\"extract_from_trade_register\",\"front\":\"file_proofofregistrationaaaaaaa\"}}"),
+ body.getAsJsonObject("company").getAsJsonArray("representatives").get(0)
+ .getAsJsonObject().get("documents"), json);
+ assertEquals(Collections.singleton("bank_verification"), body.getAsJsonObject("documents").keySet(), json);
+ // Key-level check on the raw body, so a naming-policy change cannot pass silently.
+ assertTrue(json.contains("\"proof_of_residential_address\":{"), json);
+ assertTrue(json.contains("\"proof_of_registration\":{"), json);
+ }
+
+ @Test
+ void shouldRoundTripRepresentativeDocuments() {
+ final OnboardSubEntityDocuments original = eeaSoleTraderRepresentativeDocuments();
+
+ final OnboardSubEntityDocuments roundTripped =
+ serializer.fromJson(serializer.toJson(original), OnboardSubEntityDocuments.class);
+
+ assertEquals(original, roundTripped);
+ }
+
+ @Test
+ void shouldDeserializeProofOfRegistrationOtherType() {
+ final OnboardSubEntityDocuments documents = serializer.fromJson(
+ "{\"proof_of_registration\":{\"type\":\"other\",\"front\":\"file_proofofregistrationaaaaaaa\"}}",
+ OnboardSubEntityDocuments.class);
+
+ assertEquals(ProofOfRegistrationType.OTHER, documents.getProofOfRegistration().getType());
+ }
+
+ @Test
+ void shouldSerializeCertifiedAuthorisedSignatoryWithTypeAndFrontOnly() {
+ final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
+ .certifiedAuthorisedSignatory(CertifiedAuthorisedSignatory.builder()
+ .type(CertifiedAuthorisedSignatoryType.POWER_OF_ATTORNEY)
+ .front("file_signatoryaaaaaaaaaaaaaaaaa")
+ .build())
+ .build();
+
+ assertEquals(JsonParser.parseString("{\"certified_authorised_signatory\":"
+ + "{\"type\":\"power_of_attorney\",\"front\":\"file_signatoryaaaaaaaaaaaaaaaaa\"}}"),
+ JsonParser.parseString(serializer.toJson(documents)));
+ }
+
+ // ------------------------------------------------------------------------
+ // Company and tax verification
+ // Regression: CompanyVerification carried TaxVerificationType and TaxVerification
+ // carried CompanyVerificationType, so incorporation_document (required on the
+ // EEA and GB Company Full variants) and ein_letter could not be sent.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSendCompanyAndTaxVerificationTypesUnderTheirOwnKeys() {
+ final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
+ .companyVerification(CompanyVerification.builder()
+ .type(CompanyVerificationType.INCORPORATION_DOCUMENT)
+ .front("file_aaaaaaaaaaaaaaaaaaaaaaaaaa")
+ .build())
+ .taxVerification(TaxVerification.builder()
+ .type(TaxVerificationType.EIN_LETTER)
+ .front("file_aaaaaaaaaaaaaaaaaaaaaaaaaa")
+ .build())
+ .build();
+
+ final JsonObject json = JsonParser.parseString(serializer.toJson(documents)).getAsJsonObject();
+
+ assertEquals("incorporation_document",
+ json.getAsJsonObject("company_verification").get("type").getAsString());
+ assertEquals("ein_letter", json.getAsJsonObject("tax_verification").get("type").getAsString());
+ }
+
+ // ------------------------------------------------------------------------
+ // Every field of OnboardSubEntityDocuments
+ // Exact JSON for all 16 fields, so a naming-policy change on any key cannot pass
+ // silently, then a full round trip.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSerializeAndRoundTripEveryDocumentsField() {
+ final String file = "file_aaaaaaaaaaaaaaaaaaaaaaaaaa";
+ final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
+ .identityVerification(Document.builder().type(DocumentType.PASSPORT).front(file).back(file).build())
+ .companyVerification(CompanyVerification.builder()
+ .type(CompanyVerificationType.INCORPORATION_DOCUMENT).front(file).build())
+ .articlesOfAssociation(ArticlesOfAssociation.builder()
+ .type(ArticlesOfAssociationType.ARTICLES_OF_ASSOCIATION).front(file).build())
+ .bankVerification(BankVerification.builder().type(BankVerificationType.BANK_STATEMENT).front(file).build())
+ .shareholderStructure(ShareholderStructure.builder()
+ .type(ShareholderStructureType.CERTIFIED_SHAREHOLDER_STRUCTURE).front(file).build())
+ .proofOfLegality(ProofOfLegality.builder().type(ProofOfLegalityType.PROOF_OF_LEGALITY).front(file).build())
+ .proofOfPrincipalAddress(ProofOfPrincipalAddress.builder()
+ .type(ProofOfPrincipalAddressType.PROOF_OF_ADDRESS).front(file).build())
+ .additionalDocument1(AdditionalDocument.builder().front(file).build())
+ .additionalDocument2(AdditionalDocument.builder().front(file).build())
+ .additionalDocument3(AdditionalDocument.builder().front(file).build())
+ .taxVerification(TaxVerification.builder().type(TaxVerificationType.EIN_LETTER).front(file).build())
+ .financialVerification(FinancialVerification.builder()
+ .type(FinancialVerificationType.FINANCIAL_STATEMENT).front(file).build())
+ .financialStatements(FinancialStatements.builder()
+ .type(FinancialStatementsType.FINANCIAL_STATEMENTS).front(file).build())
+ .certifiedAuthorisedSignatory(CertifiedAuthorisedSignatory.builder()
+ .type(CertifiedAuthorisedSignatoryType.POWER_OF_ATTORNEY).front(file).build())
+ .proofOfResidentialAddress(ProofOfResidentialAddress.builder()
+ .type(ProofOfResidentialAddressType.PROOF_OF_ADDRESS).front(file).build())
+ .proofOfRegistration(ProofOfRegistration.builder()
+ .type(ProofOfRegistrationType.EXTRACT_FROM_TRADE_REGISTER).front(file).build())
+ .build();
+
+ final String json = serializer.toJson(documents);
+
+ assertEquals(JsonParser.parseString("{"
+ + "\"identity_verification\":{\"type\":\"passport\",\"front\":\"" + file + "\",\"back\":\"" + file + "\"},"
+ + "\"company_verification\":{\"type\":\"incorporation_document\",\"front\":\"" + file + "\"},"
+ + "\"articles_of_association\":{\"type\":\"articles_of_association\",\"front\":\"" + file + "\"},"
+ + "\"bank_verification\":{\"type\":\"bank_statement\",\"front\":\"" + file + "\"},"
+ + "\"shareholder_structure\":{\"type\":\"certified_shareholder_structure\",\"front\":\"" + file + "\"},"
+ + "\"proof_of_legality\":{\"type\":\"proof_of_legality\",\"front\":\"" + file + "\"},"
+ + "\"proof_of_principal_address\":{\"type\":\"proof_of_address\",\"front\":\"" + file + "\"},"
+ + "\"additional_document1\":{\"front\":\"" + file + "\"},"
+ + "\"additional_document2\":{\"front\":\"" + file + "\"},"
+ + "\"additional_document3\":{\"front\":\"" + file + "\"},"
+ + "\"tax_verification\":{\"type\":\"ein_letter\",\"front\":\"" + file + "\"},"
+ + "\"financial_verification\":{\"type\":\"financial_statement\",\"front\":\"" + file + "\"},"
+ + "\"financial_statements\":{\"type\":\"financial_statements\",\"front\":\"" + file + "\"},"
+ + "\"certified_authorised_signatory\":{\"type\":\"power_of_attorney\",\"front\":\"" + file + "\"},"
+ + "\"proof_of_residential_address\":{\"type\":\"proof_of_address\",\"front\":\"" + file + "\"},"
+ + "\"proof_of_registration\":{\"type\":\"extract_from_trade_register\",\"front\":\"" + file + "\"}}"),
+ JsonParser.parseString(json), json);
+ assertEquals(documents, serializer.fromJson(json, OnboardSubEntityDocuments.class));
+ }
+
+ private static OnboardSubEntityDocuments eeaSoleTraderRepresentativeDocuments() {
+ return OnboardSubEntityDocuments.builder()
+ .identityVerification(Document.builder()
+ .type(DocumentType.PASSPORT)
+ .front("file_identityverificationaaaaaa")
+ .build())
+ .proofOfResidentialAddress(ProofOfResidentialAddress.builder()
+ .type(ProofOfResidentialAddressType.PROOF_OF_ADDRESS)
+ .front("file_proofofresidentialaddressa")
+ .build())
+ .proofOfRegistration(ProofOfRegistration.builder()
+ .type(ProofOfRegistrationType.EXTRACT_FROM_TRADE_REGISTER)
+ .front("file_proofofregistrationaaaaaaa")
+ .build())
+ .build();
+ }
}
From fbf0f62a28c465c609b50f0533b5b131ab367a41 Mon Sep 17 00:00:00 2001
From: david ruiz
Date: Thu, 1 Oct 2026 13:33:54 +0200
Subject: [PATCH 6/8] Docs and fixes
---
.../accounts/AccountsFileRequest.java | 17 ++++
.../java/com/checkout/accounts/Company.java | 85 +++++++++++++++++++
.../accounts/EntityProcessingDetails.java | 62 ++++++++++++++
.../OnboardEntityDetailsResponse.java | 5 +-
.../accounts/files/entities/FilePurpose.java | 4 +
.../checkout/common/AbstractFileRequest.java | 4 +
.../accounts/AccountsV3SerializationTest.java | 48 ++++++++++-
7 files changed, 222 insertions(+), 3 deletions(-)
create mode 100644 src/main/java/com/checkout/accounts/EntityProcessingDetails.java
diff --git a/src/main/java/com/checkout/accounts/AccountsFileRequest.java b/src/main/java/com/checkout/accounts/AccountsFileRequest.java
index 01e76dac..c5a9bd2b 100644
--- a/src/main/java/com/checkout/accounts/AccountsFileRequest.java
+++ b/src/main/java/com/checkout/accounts/AccountsFileRequest.java
@@ -10,14 +10,31 @@
import java.io.File;
+/**
+ * A file to upload with {@link AccountsClient#submitFile(AccountsFileRequest)} (POST /files on the
+ * Files host), sent as a multipart request. The returned ID is what document {@code front} and
+ * {@code back} fields take.
+ */
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public final class AccountsFileRequest extends AbstractFileRequest {
+ /**
+ * The purpose of the file upload: the onboarding document the file is for.
+ * [Required]
+ */
private AccountsFilePurpose purpose;
+ /**
+ * Creates a file upload request.
+ *
+ * @param file the file to upload (JPEG, PNG or PDF)
+ * @param contentType the file's content type; for PDF use
+ * {@code ContentType.create("application/pdf")}
+ * @param purpose the purpose of the file upload
+ */
@Builder
private AccountsFileRequest(final File file,
final ContentType contentType,
diff --git a/src/main/java/com/checkout/accounts/Company.java b/src/main/java/com/checkout/accounts/Company.java
index 38a5a454..00c8de6a 100644
--- a/src/main/java/com/checkout/accounts/Company.java
+++ b/src/main/java/com/checkout/accounts/Company.java
@@ -8,36 +8,121 @@
import java.util.List;
+/**
+ * Information about the company represented by the sub-entity: on every company and v3.0 sole
+ * trader variant, and as the controlling company of a {@link Representative} (where only
+ * {@code legalName}, {@code tradingName} and {@code registeredAddress} apply).
+ */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public final class Company {
+ /**
+ * The legal name of the sub-entity.
+ * [Required] for every company variant and the controlling company; not part of the sole trader
+ * variants.
+ * min 2 characters, max 300 characters
+ */
private String legalName;
+ /**
+ * The trading name of the sub-entity, also referred to as 'doing business as'.
+ * [Required]
+ * min 2 characters, max 300 characters
+ */
private String tradingName;
+ /**
+ * The sub-entity's business registration number: a Commercial Registration or Ministry of Commerce
+ * certificate number, or an equivalent registration number.
+ * [Required] for the Full variants and US ISV Seller Company (3.0); [Optional] for the Lite (2.0)
+ * variants. Not part of the sole trader variants.
+ * The format depends on the variant:
+ *
+ * - EEA: min 2 characters, max 39 characters; a SIRET number for sub-entities based in France.
+ * - GB (3.0): a Companies House number,
+ * ^(((AC|CE|CS|FC|FE|GE|GS|IC|LP|NC|NF|NI|NL|NO|NP|OC|OE|PC|R0|RC|SA|SC|SE|SF|SG|SI|SL|SO|SR|SZ|ZC|\d{2})\d{6})|((IP|SP|RS)[A-Z\d]{6})|(SL\d{5}[\dA]))$,
+ * 8 characters. GB (2.0) accepts the same pattern case-insensitively.
+ * - US: an Employer Identification Number (EIN), ^[0-9]{9}$, 9 characters; US ISV Seller Company
+ * (3.0) also accepts the hyphenated form, ^[0-9]{2}-?[0-9]{7}$, min 9 characters, max 11
+ * characters.
+ *
+ */
private String businessRegistrationNumber;
+ /**
+ * The date the company was incorporated, or the date the sole trader started trading.
+ * [Required] for every v3.0 variant; [Optional] for EEA, GB and US Company Full (2.0).
+ */
private DateOfIncorporation dateOfIncorporation;
+ /**
+ * The regulatory licence number of the company.
+ * [Optional] (EEA Company Full (3.0) only)
+ * ^[a-zA-Z0-9\-]+$
+ * min 4 characters, max 32 characters
+ */
private String regulatoryLicenceNumber;
+ /**
+ * The primary location where business is performed.
+ * [Required] for every company and v3.0 sole trader variant.
+ */
private Address principalAddress;
+ /**
+ * The registered address of the company.
+ * [Required] for every company variant and the controlling company; not part of the sole trader
+ * variants.
+ */
private Address registeredAddress;
+ /**
+ * Information about the representatives of this company. See {@link Representative}.
+ * [Required]
+ * min 1 item; max 1 item for the sole trader variants (the individual themselves, with roles
+ * {@code [ubo]}), max 5 on v2.0, max 25 on EEA, GB and US Company Full (3.0), no maximum on US ISV
+ * Seller Company (3.0)
+ */
private List representatives;
+ /**
+ * Not defined by any Accounts API company schema. Retained so existing code keeps compiling.
+ *
+ * @deprecated Not part of any Accounts API schema; the API does not read it. Will be removed in a
+ * future major version.
+ */
+ @Deprecated
private EntityDocument document;
+ /**
+ * Seller financial questions and supporting documents.
+ * [Required] for EEA and US Company Full (2.0); [Optional] for EEA and US Company Lite (2.0). Not
+ * part of the other variants.
+ */
private EntityFinancialDetails financialDetails;
+ /**
+ * The legal type of the company. Must be {@code individual_or_sole_proprietorship} for the sole
+ * trader variants.
+ * [Required], except on EEA and US Company Lite (2.0) where it is [Optional]. Not part of GB
+ * Company Full and Lite (2.0).
+ */
private BusinessType businessType;
+ /**
+ * The collection of additional trading names for the sub-entity.
+ * [Optional] (US ISV Seller variants only)
+ */
private List additionalTradingNames;
+ /**
+ * Indicates whether the sub-entity is a registered legal entity. Must be {@code false} for US ISV
+ * Seller Sole Trader (3.0).
+ * [Required] for US ISV Seller Sole Trader (3.0); not part of the other variants.
+ */
private Boolean isRegisteredCompany;
}
diff --git a/src/main/java/com/checkout/accounts/EntityProcessingDetails.java b/src/main/java/com/checkout/accounts/EntityProcessingDetails.java
new file mode 100644
index 00000000..e4786cf2
--- /dev/null
+++ b/src/main/java/com/checkout/accounts/EntityProcessingDetails.java
@@ -0,0 +1,62 @@
+package com.checkout.accounts;
+
+import com.checkout.common.Currency;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * The sub-entity's expected processing, as returned by GET /accounts/entities/{id}
+ * ({@code processing_details}, Accounts API v3.0).
+ *
+ * A response-only type, separate from the request's {@link ProcessingDetails}: the amounts are
+ * {@code Long} here because the API declares them as integers in minor units with no maximum, and
+ * an {@code Integer} would fail to read any value above 2,147,483,647.
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public final class EntityProcessingDetails {
+
+ /**
+ * The country code (iso-3166-1 alpha-2) where the settlement bank account is located.
+ * Format: iso-3166-1-alpha-2
+ * 2 characters
+ */
+ private String settlementCountry;
+
+ /**
+ * Target country codes (iso-3166-1 alpha-2) with more than 10% expected volume processing with
+ * Checkout.com.
+ * min 1 item, max 10 items
+ */
+ private List targetCountries;
+
+ /**
+ * The estimated annual processing volume. In minor units without decimals.
+ * min 0
+ */
+ private Long annualProcessingVolume;
+
+ /**
+ * The expected average transaction value. In minor units without decimals.
+ * min 0
+ */
+ private Long averageTransactionValue;
+
+ /**
+ * The expected highest transaction value. In minor units without decimals.
+ * min 0
+ */
+ private Long highestTransactionValue;
+
+ /**
+ * The currency used for the processing details provided.
+ */
+ private Currency currency;
+
+}
diff --git a/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java b/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
index 81b13234..c2d65d1b 100644
--- a/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
+++ b/src/main/java/com/checkout/accounts/OnboardEntityDetailsResponse.java
@@ -68,9 +68,10 @@ public final class OnboardEntityDetailsResponse extends Resource {
private List instruments;
/**
- * The sub-entity's expected processing (Accounts API v3.0).
+ * The sub-entity's expected processing (Accounts API v3.0). Amounts are {@code Long}; see
+ * {@link EntityProcessingDetails}.
*/
- private ProcessingDetails processingDetails;
+ private EntityProcessingDetails processingDetails;
/**
* The top-level documents used to support the verification of the sub-entity's details.
diff --git a/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java b/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
index 06f25a5e..22024de3 100644
--- a/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
+++ b/src/main/java/com/checkout/accounts/files/entities/FilePurpose.java
@@ -34,6 +34,10 @@ public enum FilePurpose {
PROOF_OF_RESIDENTIAL_ADDRESS,
@SerializedName("proof_of_registration")
PROOF_OF_REGISTRATION,
+ /**
+ * Not an onboarding upload purpose: POST /entities/{entityId}/files does not accept it
+ * ({@code PlatformsFileUpload} defines the other fourteen values only).
+ */
@SerializedName("dispute_evidence")
DISPUTE_EVIDENCE
}
\ No newline at end of file
diff --git a/src/main/java/com/checkout/common/AbstractFileRequest.java b/src/main/java/com/checkout/common/AbstractFileRequest.java
index 985ec79a..626b05a6 100644
--- a/src/main/java/com/checkout/common/AbstractFileRequest.java
+++ b/src/main/java/com/checkout/common/AbstractFileRequest.java
@@ -10,6 +10,10 @@
@AllArgsConstructor
public abstract class AbstractFileRequest {
+ /**
+ * The file to upload.
+ * [Required]
+ */
private File file;
/**
diff --git a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
index 88d4216c..d452715d 100644
--- a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
+++ b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
@@ -9,6 +9,8 @@
import java.util.Arrays;
import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -219,10 +221,54 @@ void shouldDeserializeEntityDetailsDocumentsAndProcessingDetails() {
OnboardEntityDetailsResponse.class);
assertEquals(Currency.USD, response.getProcessingDetails().getCurrency());
- assertEquals(1000000, response.getProcessingDetails().getAnnualProcessingVolume());
+ assertEquals(Long.valueOf(1000000), response.getProcessingDetails().getAnnualProcessingVolume());
assertEquals(BankVerificationType.BANK_STATEMENT, response.getDocuments().getBankVerification().getType());
assertEquals("file_bankverificationaaaaaaaaaa", response.getDocuments().getBankVerification().getFront());
assertEquals(ProofOfRegistrationType.EXTRACT_FROM_TRADE_REGISTER, response.getCompany().getRepresentatives()
.get(0).getDocuments().getProofOfRegistration().getType());
}
+
+ // Regression: processing_details amounts are integers in minor units with no maximum. Typed
+ // as Integer, any value above 2,147,483,647 (about 21.4 million in a two-decimal currency)
+ // made the whole GET /accounts/entities/{id} fail to deserialize.
+ @Test
+ void shouldDeserializeProcessingDetailsAmountsAboveIntegerRange() {
+ final OnboardEntityDetailsResponse response = serializer.fromJson("{\"processing_details\":{"
+ + "\"annual_processing_volume\":3000000000,"
+ + "\"average_transaction_value\":2500000000,"
+ + "\"highest_transaction_value\":9000000000}}",
+ OnboardEntityDetailsResponse.class);
+
+ assertEquals(Long.valueOf(3000000000L), response.getProcessingDetails().getAnnualProcessingVolume());
+ assertEquals(Long.valueOf(2500000000L), response.getProcessingDetails().getAverageTransactionValue());
+ assertEquals(Long.valueOf(9000000000L), response.getProcessingDetails().getHighestTransactionValue());
+ }
+
+ // ------------------------------------------------------------------------
+ // AccountsFilePurpose
+ // submitFile sends getPurpose() on the wire, so every value is asserted as a string.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldExposeEveryAccountsFilePurposeWireValue() {
+ final Map expected = new EnumMap<>(AccountsFilePurpose.class);
+ expected.put(AccountsFilePurpose.BANK_VERIFICATION, "bank_verification");
+ expected.put(AccountsFilePurpose.IDENTIFICATION, "identification");
+ expected.put(AccountsFilePurpose.IDENTITY_VERIFICATION, "identity_verification");
+ expected.put(AccountsFilePurpose.COMPANY_VERIFICATION, "company_verification");
+ expected.put(AccountsFilePurpose.FINANCIAL_VERIFICATION, "financial_verification");
+ expected.put(AccountsFilePurpose.TAX_VERIFICATION, "tax_verification");
+ expected.put(AccountsFilePurpose.ADDITIONAL_DOCUMENT, "additional_document");
+ expected.put(AccountsFilePurpose.ARTICLES_OF_ASSOCIATION, "articles_of_association");
+ expected.put(AccountsFilePurpose.CERTIFIED_AUTHORISED_SIGNATORY, "certified_authorised_signatory");
+ expected.put(AccountsFilePurpose.COMPANY_OWNERSHIP, "company_ownership");
+ expected.put(AccountsFilePurpose.PROOF_OF_LEGALITY, "proof_of_legality");
+ expected.put(AccountsFilePurpose.PROOF_OF_PRINCIPAL_ADDRESS, "proof_of_principal_address");
+ expected.put(AccountsFilePurpose.SHAREHOLDER_STRUCTURE, "shareholder_structure");
+ expected.put(AccountsFilePurpose.PROOF_OF_RESIDENTIAL_ADDRESS, "proof_of_residential_address");
+ expected.put(AccountsFilePurpose.PROOF_OF_REGISTRATION, "proof_of_registration");
+
+ assertEquals(AccountsFilePurpose.values().length, expected.size(), "every value must be asserted");
+ expected.forEach((purpose, wire) -> assertEquals(wire, purpose.getPurpose(), purpose.name()));
+ }
}
From c93fb9abe6538b4eff73b1d8dd6679ecdfed1001 Mon Sep 17 00:00:00 2001
From: david ruiz
Date: Thu, 1 Oct 2026 14:19:27 +0200
Subject: [PATCH 7/8] Docs extended + obsolete schemas identified
---
.../com/checkout/accounts/EntityDocument.java | 14 +++++++++
.../accounts/EntityFinancialDetails.java | 30 +++++++++++++++++++
.../accounts/EntityFinancialDocuments.java | 13 ++++++++
3 files changed, 57 insertions(+)
diff --git a/src/main/java/com/checkout/accounts/EntityDocument.java b/src/main/java/com/checkout/accounts/EntityDocument.java
index 388f9499..856777d3 100644
--- a/src/main/java/com/checkout/accounts/EntityDocument.java
+++ b/src/main/java/com/checkout/accounts/EntityDocument.java
@@ -5,14 +5,28 @@
import lombok.Data;
import lombok.NoArgsConstructor;
+/**
+ * Not defined by any Accounts API onboarding schema. Referenced only by {@link Company} {@code document}
+ * and {@link EntityFinancialDocuments}, both deprecated; retained so existing code keeps compiling.
+ *
+ * @deprecated Not part of any Accounts API onboarding schema. Will be removed in a future major
+ * version.
+ */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
+@Deprecated
public final class EntityDocument {
+ /**
+ * Not defined by any Accounts API onboarding schema.
+ */
private String type;
+ /**
+ * Not defined by any Accounts API onboarding schema.
+ */
private String fileId;
}
diff --git a/src/main/java/com/checkout/accounts/EntityFinancialDetails.java b/src/main/java/com/checkout/accounts/EntityFinancialDetails.java
index d6482ad0..7793add3 100644
--- a/src/main/java/com/checkout/accounts/EntityFinancialDetails.java
+++ b/src/main/java/com/checkout/accounts/EntityFinancialDetails.java
@@ -4,18 +4,48 @@
import lombok.Builder;
import lombok.Data;
+/**
+ * Seller financial questions ({@code financial_details}): on the company of EEA and US Company Full
+ * and Lite (2.0), and on the individual of US Sole Trader Full and Lite (2.0).
+ */
@Data
@Builder
public final class EntityFinancialDetails {
+ /**
+ * The estimated annual processing volume. In minor units without decimals.
+ * [Required] on the Full (2.0) variants; [Optional] on the Lite (2.0) variants.
+ * min 0
+ */
private Long annualProcessingVolume;
+ /**
+ * The expected average transaction value. In minor units without decimals.
+ * [Required] on the Full (2.0) variants; [Optional] on the Lite (2.0) variants.
+ * min 0
+ */
private Long averageTransactionValue;
+ /**
+ * The expected highest transaction value. In minor units without decimals.
+ * [Required] on the Full (2.0) variants; [Optional] on the Lite (2.0) variants.
+ * min 0
+ */
private Long highestTransactionValue;
+ /**
+ * Not defined by any Accounts API schema; the API does not read it. Supporting documents go on
+ * the top-level request documents ({@link OnboardSubEntityDocuments}) instead.
+ *
+ * @deprecated Not part of any Accounts API schema. Will be removed in a future major version.
+ */
+ @Deprecated
private EntityFinancialDocuments documents;
+ /**
+ * The currency used for the financial details provided.
+ * [Required] on US Company Full and US Sole Trader Full (2.0); [Optional] on the other variants.
+ */
private Currency currency;
}
diff --git a/src/main/java/com/checkout/accounts/EntityFinancialDocuments.java b/src/main/java/com/checkout/accounts/EntityFinancialDocuments.java
index 3890abeb..55f05d60 100644
--- a/src/main/java/com/checkout/accounts/EntityFinancialDocuments.java
+++ b/src/main/java/com/checkout/accounts/EntityFinancialDocuments.java
@@ -3,11 +3,24 @@
import lombok.Builder;
import lombok.Data;
+/**
+ * Not defined by any Accounts API schema: {@code financial_details} carries the three amounts and
+ * the currency only. Retained so existing code keeps compiling.
+ *
+ * @deprecated Not part of any Accounts API schema. Will be removed in a future major version.
+ */
@Data
@Builder
+@Deprecated
public final class EntityFinancialDocuments {
+ /**
+ * Not defined by any Accounts API schema.
+ */
private EntityDocument bankStatement;
+ /**
+ * Not defined by any Accounts API schema.
+ */
private EntityDocument financialStatement;
}
From a50cae81e95189f2d400d12bae84d88ce3fa6b49 Mon Sep 17 00:00:00 2001
From: david ruiz
Date: Mon, 5 Oct 2026 13:56:30 +0200
Subject: [PATCH 8/8] V3 schema adjustments + test fine tune
---
.../accounts/ArticlesOfAssociation.java | 5 +-
.../com/checkout/accounts/ContactDetails.java | 12 +-
.../accounts/EntityEmailAddresses.java | 9 +-
.../java/com/checkout/accounts/Invitee.java | 6 +-
.../accounts/OnboardSubEntityDocuments.java | 10 +-
.../com/checkout/accounts/Representative.java | 9 +-
.../accounts/AccountsV3SerializationTest.java | 488 +++++++++++++++++-
...rdSubEntityDocumentsSerializationTest.java | 14 +-
.../files/AccountsFilesSerializationTest.java | 121 +++++
9 files changed, 619 insertions(+), 55 deletions(-)
create mode 100644 src/test/java/com/checkout/accounts/files/AccountsFilesSerializationTest.java
diff --git a/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java b/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java
index 110edf0b..0870034a 100644
--- a/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java
+++ b/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java
@@ -9,10 +9,7 @@
* Memorandum or articles of association document, supplied when onboarding a sub-entity.
*
* Required on EEA and GB Company Full (3.0); optional on US Company Full (3.0) and the US ISV
- * Seller variants. The API expects an object carrying the
- * document type and the uploaded file ID, which is why this class exists: the field on
- * {@link OnboardSubEntityDocuments} used to be the {@link ArticlesOfAssociationType} enum, so
- * the SDK serialized a bare string and the API rejected the request.
+ * Seller variants. The object carries the document type and the ID of the uploaded file.
*/
@Data
@Builder
diff --git a/src/main/java/com/checkout/accounts/ContactDetails.java b/src/main/java/com/checkout/accounts/ContactDetails.java
index ccb243b7..5e8c4dad 100644
--- a/src/main/java/com/checkout/accounts/ContactDetails.java
+++ b/src/main/java/com/checkout/accounts/ContactDetails.java
@@ -17,9 +17,9 @@ public final class ContactDetails {
/**
* The phone number of the sub-entity.
* [Required] for every Accounts API v2.0 variant and the US ISV Seller variants; [Optional] for the
- * other v3.0 variants. On v3.0 {@code countryCode} is required and is the ISO 3166-1 alpha-2
- * country where the number is registered (for example {@code FR}), not the dialling code; v2.0
- * takes {@code number} only. {@code number} is the number without the country calling code, and
+ * other v3.0 variants; not part of the hosted onboarding invite request. On v3.0
+ * {@code countryCode} is required and is the ISO 3166-1 alpha-2 country where the number is
+ * registered (for example {@code FR}), not the dialling code; v2.0 takes {@code number} only. {@code number} is the number without the country calling code, and
* its format depends on the variant:
*
* - v3.0 EEA: ^[0-9]{6,13}$, min 6 characters, max 13 characters
@@ -34,13 +34,15 @@ public final class ContactDetails {
/**
* Email addresses for this sub-entity.
* [Required] for every Accounts API v2.0 variant and the US ISV Seller variants; [Optional] for the
- * other v3.0 variants.
+ * other v3.0 variants; not part of the hosted onboarding invite request.
*/
private EntityEmailAddresses emailAddresses;
/**
* The details of the user responsible for onboarding the sub-entity.
- * [Optional] (not part of the US ISV Seller variants)
+ * [Required] in the hosted onboarding invite request, together with reference and is_draft;
+ * [Optional] in the full onboarding variants (every Full and Lite variant); not part of the US ISV
+ * Seller variants.
*/
private Invitee invitee;
diff --git a/src/main/java/com/checkout/accounts/EntityEmailAddresses.java b/src/main/java/com/checkout/accounts/EntityEmailAddresses.java
index 53a178e7..8add5e1e 100644
--- a/src/main/java/com/checkout/accounts/EntityEmailAddresses.java
+++ b/src/main/java/com/checkout/accounts/EntityEmailAddresses.java
@@ -16,9 +16,16 @@ public final class EntityEmailAddresses {
/**
* The main email address for this sub-entity.
- * [Required]
+ * [Required] in every variant that has email addresses.
* Format: email
*/
private String primary;
+ /**
+ * The email address of the person responsible for PCI compliance at this sub-entity.
+ * [Required] for the US ISV Seller variants (3.0), together with primary; not part of the other variants.
+ * Format: email
+ */
+ private String pciComplianceContact;
+
}
diff --git a/src/main/java/com/checkout/accounts/Invitee.java b/src/main/java/com/checkout/accounts/Invitee.java
index f9c137f9..a64caa93 100644
--- a/src/main/java/com/checkout/accounts/Invitee.java
+++ b/src/main/java/com/checkout/accounts/Invitee.java
@@ -15,9 +15,9 @@
public final class Invitee {
/**
- * The main email address for this sub-entity. Despite the spec's wording, this is the address of
- * the invitee, the user responsible for onboarding the sub-entity.
- * [Optional]
+ * The email of the user responsible for onboarding the sub-entity.
+ * [Required] in the hosted onboarding invite request (with reference and is_draft); [Optional] in
+ * the full onboarding variants (every Full and Lite variant); not part of the US ISV Seller variants.
* Format: email
*/
private String email;
diff --git a/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java b/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java
index 9394cc3a..508a43a4 100644
--- a/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java
+++ b/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java
@@ -13,10 +13,12 @@
* - The top-level request {@code documents}, on {@link OnboardEntityRequest}. The API
* ignores keys it does not recognise here rather than rejecting them, so a misplaced document is
* dropped silently.
- * - A representative's {@code documents}, on {@link Representative}. This object is
- * strict: it accepts only {@code identity_verification}, {@code certified_authorised_signatory},
- * {@code proof_of_residential_address} and {@code proof_of_registration}, and rejects any other
- * key.
+ * - A representative's {@code documents}, on {@link Representative}. It takes only
+ * {@code identity_verification}, {@code certified_authorised_signatory} (EEA, GB and US Company
+ * Full (3.0) and US ISV Seller Company (3.0)), {@code proof_of_residential_address} and
+ * {@code proof_of_registration}. On the EEA, GB and US Company Full (3.0) and Sole Trader Full
+ * (3.0) variants this object is strict and rejects any other key; it is not strict on the US ISV
+ * Seller variants (3.0) nor on any v2.0 variant.
*
* Each field below says which of the two it belongs to.
*/
diff --git a/src/main/java/com/checkout/accounts/Representative.java b/src/main/java/com/checkout/accounts/Representative.java
index 56a2437a..1de7078f 100644
--- a/src/main/java/com/checkout/accounts/Representative.java
+++ b/src/main/java/com/checkout/accounts/Representative.java
@@ -105,10 +105,11 @@ public final class Representative {
private List roles;
/**
- * Verification documents for the individual representative. The API validates this object
- * strictly on v3.0: it accepts only {@code identity_verification},
- * {@code certified_authorised_signatory}, {@code proof_of_residential_address} and
- * {@code proof_of_registration}, and rejects any other key. See
+ * Verification documents for the individual representative. On the EEA, GB and US Company Full
+ * (3.0) and Sole Trader Full (3.0) variants this object is strict: it accepts only
+ * {@code identity_verification}, {@code certified_authorised_signatory},
+ * {@code proof_of_residential_address} and {@code proof_of_registration}, and rejects any other
+ * key. It is not strict on the US ISV Seller variants (3.0) nor on any v2.0 variant. See
* {@link OnboardSubEntityDocuments} for which apply to each variant.
* [Required] for the EEA, GB and US Sole Trader Full (3.0) variants and EEA Company Full (2.0);
* [Optional] otherwise.
diff --git a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
index d452715d..d8395748 100644
--- a/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
+++ b/src/test/java/com/checkout/accounts/AccountsV3SerializationTest.java
@@ -4,6 +4,8 @@
import com.checkout.common.Address;
import com.checkout.common.CountryCode;
import com.checkout.common.Currency;
+import com.checkout.common.DocumentType;
+import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.junit.jupiter.api.Test;
@@ -41,17 +43,14 @@ void shouldSerializeProcessingDetailsWithPayments() {
final String json = serializer.toJson(processingDetails);
- assertTrue(json.contains("\"annual_processing_volume\""));
- assertTrue(json.contains("\"average_order_fulfillment_time\""));
- assertTrue(json.contains("\"highest_transaction_value\""));
- assertTrue(json.contains("\"settlement_country\""));
- assertTrue(json.contains("\"target_countries\""));
- assertTrue(json.contains("\"payments\""));
- assertTrue(json.contains("\"ach\""));
- assertTrue(json.contains("\"annual_ach_volume\""));
- assertTrue(json.contains("\"average_ach_transaction_size\""));
- assertTrue(json.contains("\"estimated_monthly_credit_volume\""));
- assertTrue(json.contains("\"average_credit_amount\""));
+ assertEquals(JsonParser.parseString("{\"settlement_country\":\"GB\",\"target_countries\":[\"GB\"],"
+ + "\"annual_processing_volume\":1000000,\"average_transaction_value\":5000,"
+ + "\"highest_transaction_value\":25000,\"currency\":\"GBP\","
+ + "\"average_order_fulfillment_time\":3,"
+ + "\"payments\":{\"ach\":{\"annual_ach_volume\":1000000,\"average_ach_transaction_size\":5000,"
+ + "\"estimated_monthly_credit_volume\":100000,\"average_credit_amount\":5000}}}"),
+ JsonParser.parseString(json), json);
+ assertEquals(processingDetails, serializer.fromJson(json, ProcessingDetails.class));
}
@Test
@@ -98,10 +97,13 @@ void shouldSerializeCompanyV3Fields() {
@Test
void shouldSerializeRepresentativeV3Fields() {
final Representative representative = Representative.builder()
- .id("rep_00000000000000000000000000")
+ .id("rep_r2y49v5j1skna5zx0swaprf2he")
.individual(RepresentativeIndividual.builder()
.firstName("John")
+ .middleName("Paul")
.lastName("Representative")
+ .dateOfBirth(DateOfBirth.builder().day(5).month(6).year(1995).build())
+ .placeOfBirth(PlaceOfBirth.builder().country(CountryCode.US).build())
.citizenships(Collections.singletonList(Citizenship.builder()
.type("citizenship")
.country(CountryCode.US)
@@ -109,6 +111,14 @@ void shouldSerializeRepresentativeV3Fields() {
.nationalIdType(NationalIdType.SSN)
.nationalIdNumber("AB123456C")
.emailAddress("john@example.com")
+ .phone(AccountPhone.builder().countryCode(CountryCode.US).number("4155678901").build())
+ .address(Address.builder()
+ .addressLine1("123 Main Street")
+ .city("San Francisco")
+ .state("CA")
+ .zip("94105")
+ .country(CountryCode.US)
+ .build())
.build())
.companyPosition(CompanyPosition.CEO)
.ownershipPercentage(100)
@@ -117,19 +127,20 @@ void shouldSerializeRepresentativeV3Fields() {
final String json = serializer.toJson(representative);
- assertTrue(json.contains("\"individual\""));
- assertTrue(json.contains("\"first_name\""));
- assertTrue(json.contains("\"citizenships\""));
- assertTrue(json.contains("\"country\""));
- assertTrue(json.contains("\"national_id_type\""));
- assertTrue(json.contains("ssn"));
- assertTrue(json.contains("\"national_id_number\""));
- assertTrue(json.contains("\"email_address\""));
- assertTrue(json.contains("\"company_position\""));
- assertTrue(json.contains("ceo"));
- assertTrue(json.contains("\"ownership_percentage\""));
- assertTrue(json.contains("director"));
- assertTrue(json.contains("control_person"));
+ assertEquals(JsonParser.parseString("{\"id\":\"rep_r2y49v5j1skna5zx0swaprf2he\","
+ + "\"roles\":[\"ubo\",\"authorised_signatory\",\"director\",\"control_person\"],"
+ + "\"company_position\":\"ceo\",\"ownership_percentage\":100,"
+ + "\"individual\":{\"first_name\":\"John\",\"middle_name\":\"Paul\",\"last_name\":\"Representative\","
+ + "\"date_of_birth\":{\"day\":5,\"month\":6,\"year\":1995},"
+ + "\"place_of_birth\":{\"country\":\"US\"},"
+ + "\"citizenships\":[{\"type\":\"citizenship\",\"country\":\"US\"}],"
+ + "\"national_id_type\":\"ssn\",\"national_id_number\":\"AB123456C\","
+ + "\"email_address\":\"john@example.com\","
+ + "\"phone\":{\"country_code\":\"US\",\"number\":\"4155678901\"},"
+ + "\"address\":{\"address_line1\":\"123 Main Street\",\"city\":\"San Francisco\","
+ + "\"state\":\"CA\",\"zip\":\"94105\",\"country\":\"US\"}}}"),
+ JsonParser.parseString(json), json);
+ assertEquals(representative, serializer.fromJson(json, Representative.class));
}
@Test
@@ -137,7 +148,7 @@ void shouldSerializeFinancialStatementsDocument() {
final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
.financialStatements(FinancialStatements.builder()
.type(FinancialStatementsType.FINANCIAL_STATEMENTS)
- .front("file_00000000000000000000000000")
+ .front("file_3g7msixotdi2bfqpuwrckgnotu")
.build())
.build();
@@ -148,6 +159,25 @@ void shouldSerializeFinancialStatementsDocument() {
assertTrue(json.contains("\"front\""));
}
+ @Test
+ void shouldSerializeAndRoundtripEntityEmailAddressesWithPciComplianceContact() {
+ final EntityEmailAddresses emailAddresses = EntityEmailAddresses.builder()
+ .primary("admin@example.com")
+ .pciComplianceContact("pci@example.com")
+ .build();
+
+ final String json = serializer.toJson(emailAddresses);
+
+ assertTrue(json.contains("\"primary\""));
+ assertTrue(json.contains("\"pci_compliance_contact\""));
+
+ final EntityEmailAddresses roundtrip = serializer.fromJson(json, EntityEmailAddresses.class);
+
+ assertEquals("admin@example.com", roundtrip.getPrimary());
+ assertEquals("pci@example.com", roundtrip.getPciComplianceContact());
+ assertEquals(emailAddresses, roundtrip);
+ }
+
@Test
void shouldSerializeOnboardEntityRequestWithAgreedTermsAndSellerCategory() {
final OnboardEntityRequest request = OnboardEntityRequest.builder()
@@ -212,7 +242,7 @@ void shouldSerializeControllingCompanyRepresentative() {
@Test
void shouldDeserializeEntityDetailsDocumentsAndProcessingDetails() {
final OnboardEntityDetailsResponse response = serializer.fromJson("{"
- + "\"id\":\"ent_aaaaaaaaaaaaaaaaaaaaaaaaaa\","
+ + "\"id\":\"ent_qx5bjrdqes9rxo9xi8fym2by6o\","
+ "\"processing_details\":{\"currency\":\"USD\",\"annual_processing_volume\":1000000},"
+ "\"documents\":{\"bank_verification\":{\"type\":\"bank_statement\","
+ "\"front\":\"file_bankverificationaaaaaaaaaa\"}},"
@@ -271,4 +301,408 @@ void shouldExposeEveryAccountsFilePurposeWireValue() {
assertEquals(AccountsFilePurpose.values().length, expected.size(), "every value must be asserted");
expected.forEach((purpose, wire) -> assertEquals(wire, purpose.getPurpose(), purpose.name()));
}
+
+ // ------------------------------------------------------------------------
+ // Spec request examples (USISVSellerCompany3-0, USISVSellerSoleTrader3-0)
+ // Each example is deserialized into OnboardEntityRequest and serialized back,
+ // and the two JSON trees must match exactly.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldRoundTripUsIsvSellerCompanyExample() {
+ final String example = "{"
+ + "\"reference\":\"isv-seller-example001\","
+ + "\"agreed_terms\":{\"date\":\"2026-07-02T10:30:00.0000000+00:00\",\"ip_address\":\"8.8.8.8\","
+ + "\"name\":\"Toby Arden\",\"email\":\"toby.arden@example.com\",\"version\":\"cko-platform-terms-1.0.0\"},"
+ + "\"seller_category\":\"cat_retail_001\","
+ + "\"processing_details\":{\"annual_processing_volume\":1000,\"average_transaction_value\":2000,"
+ + "\"average_order_fulfillment_time\":3,\"target_countries\":[\"US\"],\"currency\":\"USD\","
+ + "\"payments\":{\"ach\":{\"annual_ach_volume\":100000,\"average_ach_transaction_size\":5000,"
+ + "\"estimated_monthly_credit_volume\":50000,\"average_credit_amount\":2500}}},"
+ + "\"contact_details\":{\"phone\":{\"number\":\"4155678900\",\"country_code\":\"US\"},"
+ + "\"email_addresses\":{\"primary\":\"toby.arden@example.com\","
+ + "\"pci_compliance_contact\":\"pci.contact@example.com\"}},"
+ + "\"profile\":{\"urls\":[\"https://www.isv-seller-example.com\"],\"mccs\":[\"5551\"],"
+ + "\"holding_currencies\":[\"USD\"],\"default_holding_currency\":\"USD\"},"
+ + "\"company\":{\"business_registration_number\":\"12-3456789\",\"business_type\":\"private_corporation\","
+ + "\"legal_name\":\"ISV Seller Example Inc\",\"trading_name\":\"ISV Seller Example\","
+ + "\"registered_address\":{\"address_line1\":\"123 Main Street\",\"city\":\"San Francisco\",\"state\":\"CA\","
+ + "\"zip\":\"94105\",\"country\":\"US\"},\"principal_address\":{\"address_line1\":\"123 Main Street\","
+ + "\"city\":\"San Francisco\",\"state\":\"CA\",\"zip\":\"94105\",\"country\":\"US\"},"
+ + "\"date_of_incorporation\":{\"year\":2025,\"month\":10,\"day\":1},\"representatives\":[{\"roles\":[\"ubo\","
+ + "\"control_person\"],\"ownership_percentage\":25,\"company_position\":\"ceo\","
+ + "\"individual\":{\"first_name\":\"Toby\",\"last_name\":\"Arden\",\"email_address\":\"toby.arden@example.com\","
+ + "\"national_id_type\":\"ssn\",\"national_id_number\":\"123456789\",\"date_of_birth\":{\"day\":15,\"month\":1,"
+ + "\"year\":1990},\"place_of_birth\":{\"country\":\"US\"},\"citizenships\":[{\"country\":\"US\"}],"
+ + "\"phone\":{\"country_code\":\"US\",\"number\":\"4155678901\"},"
+ + "\"address\":{\"address_line1\":\"123 Main Street\",\"city\":\"San Francisco\",\"state\":\"CA\",\"zip\":\"94105\","
+ + "\"country\":\"US\"}}},{\"roles\":[\"authorised_signatory\"],\"individual\":{\"first_name\":\"Alex\","
+ + "\"last_name\":\"Morgan\",\"email_address\":\"alex.morgan@example.com\",\"national_id_type\":\"ssn\","
+ + "\"national_id_number\":\"987654321\",\"date_of_birth\":{\"day\":22,\"month\":6,\"year\":1985},"
+ + "\"place_of_birth\":{\"country\":\"US\"},\"citizenships\":[{\"country\":\"US\"}],"
+ + "\"phone\":{\"country_code\":\"US\",\"number\":\"4155678902\"},"
+ + "\"address\":{\"address_line1\":\"123 Main Street\",\"city\":\"San Francisco\",\"state\":\"CA\",\"zip\":\"94105\","
+ + "\"country\":\"US\"}}}]}"
+ + "}";
+
+ final JsonObject actual = JsonParser.parseString(
+ serializer.toJson(serializer.fromJson(example, OnboardEntityRequest.class))).getAsJsonObject();
+ // is_draft is a primitive boolean on the request, so it is always serialized.
+ actual.remove("is_draft");
+
+ assertEquals(JsonParser.parseString(example), actual);
+ }
+
+ @Test
+ void shouldRoundTripUsIsvSellerSoleTraderExample() {
+ final String example = "{"
+ + "\"reference\":\"isv-sole-trader-example001\","
+ + "\"agreed_terms\":{\"date\":\"2026-07-02T10:30:00.0000000+00:00\",\"ip_address\":\"8.8.8.8\","
+ + "\"name\":\"Hannah Bret\",\"email\":\"hannah.bret@example.com\",\"version\":\"cko-platform-terms-1.0.0\"},"
+ + "\"seller_category\":\"cat_retail_001\","
+ + "\"processing_details\":{\"annual_processing_volume\":1000,\"average_transaction_value\":2000,"
+ + "\"average_order_fulfillment_time\":3,\"target_countries\":[\"US\"],\"currency\":\"USD\","
+ + "\"payments\":{\"ach\":{\"annual_ach_volume\":100000,\"average_ach_transaction_size\":5000,"
+ + "\"estimated_monthly_credit_volume\":50000,\"average_credit_amount\":2500}}},"
+ + "\"contact_details\":{\"phone\":{\"number\":\"4155678900\",\"country_code\":\"US\"},"
+ + "\"email_addresses\":{\"primary\":\"hannah.bret@example.com\","
+ + "\"pci_compliance_contact\":\"pci.contact@example.com\"}},"
+ + "\"profile\":{\"urls\":[\"https://www.isv-sole-trader-example.com\"],\"mccs\":[\"5551\"],"
+ + "\"holding_currencies\":[\"USD\"],\"default_holding_currency\":\"USD\"},"
+ + "\"company\":{\"business_type\":\"individual_or_sole_proprietorship\",\"is_registered_company\":false,"
+ + "\"trading_name\":\"Hannah's Goods\",\"date_of_incorporation\":{\"year\":2025,\"month\":10,\"day\":1},"
+ + "\"principal_address\":{\"address_line1\":\"123 Main Street\",\"city\":\"San Francisco\",\"state\":\"CA\","
+ + "\"zip\":\"94105\",\"country\":\"US\"},\"representatives\":[{\"roles\":[\"ubo\"],\"ownership_percentage\":100,"
+ + "\"individual\":{\"first_name\":\"Hannah\",\"last_name\":\"Bret\","
+ + "\"email_address\":\"hannah.bret@example.com\",\"national_id_type\":\"ssn\","
+ + "\"national_id_number\":\"123456789\",\"date_of_birth\":{\"day\":15,\"month\":1,\"year\":1990},"
+ + "\"place_of_birth\":{\"country\":\"US\"},\"citizenships\":[{\"country\":\"US\"}],"
+ + "\"phone\":{\"country_code\":\"US\",\"number\":\"4155678901\"},"
+ + "\"address\":{\"address_line1\":\"123 Main Street\",\"city\":\"San Francisco\",\"state\":\"CA\",\"zip\":\"94105\","
+ + "\"country\":\"US\"}}}]}"
+ + "}";
+
+ final JsonObject actual = JsonParser.parseString(
+ serializer.toJson(serializer.fromJson(example, OnboardEntityRequest.class))).getAsJsonObject();
+ // is_draft is a primitive boolean on the request, so it is always serialized.
+ actual.remove("is_draft");
+
+ assertEquals(JsonParser.parseString(example), actual);
+ }
+
+ // ------------------------------------------------------------------------
+ // ContactDetails
+ // phone with country_code, email_addresses and invitee, none of which the
+ // spec examples reach together.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSerializeAndRoundTripContactDetailsWithInvitee() {
+ final ContactDetails contactDetails = ContactDetails.builder()
+ .phone(AccountPhone.builder().countryCode(CountryCode.GB).number("2072345678").build())
+ .emailAddresses(EntityEmailAddresses.builder()
+ .primary("admin@example.com")
+ .pciComplianceContact("pci@example.com")
+ .build())
+ .invitee(Invitee.builder().email("invitee@example.com").build())
+ .build();
+
+ final String json = serializer.toJson(contactDetails);
+
+ assertEquals(JsonParser.parseString("{\"phone\":{\"country_code\":\"GB\",\"number\":\"2072345678\"},"
+ + "\"email_addresses\":{\"primary\":\"admin@example.com\","
+ + "\"pci_compliance_contact\":\"pci@example.com\"},"
+ + "\"invitee\":{\"email\":\"invitee@example.com\"}}"),
+ JsonParser.parseString(json), json);
+ assertEquals(contactDetails, serializer.fromJson(json, ContactDetails.class));
+ }
+
+ // ------------------------------------------------------------------------
+ // Company
+ // Every spec property, including principal_address, regulatory_licence_number,
+ // financial_details and the full date_of_incorporation. The deprecated
+ // document field is absent from every variant and stays unset.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSerializeAndRoundTripEveryCompanyField() {
+ final Company company = Company.builder()
+ .legalName("Super Hero Masks Ltd")
+ .tradingName("Super Hero Masks")
+ .businessRegistrationNumber("01234567")
+ .dateOfIncorporation(DateOfIncorporation.builder().day(1).month(6).year(2010).build())
+ .regulatoryLicenceNumber("FRN123456")
+ .principalAddress(Address.builder()
+ .addressLine1("90 Tottenham Court Road")
+ .addressLine2("Floor 2")
+ .city("London")
+ .state("London")
+ .zip("W1T 4TJ")
+ .country(CountryCode.GB)
+ .build())
+ .registeredAddress(Address.builder()
+ .addressLine1("1 Main Street")
+ .city("London")
+ .zip("W1T 4TJ")
+ .country(CountryCode.GB)
+ .build())
+ .representatives(Collections.singletonList(Representative.builder()
+ .id("rep_xoo3xudh9mgxw6tv4140063pdi")
+ .individual(RepresentativeIndividual.builder().firstName("Jane").lastName("Doe").build())
+ .roles(Collections.singletonList(EntityRoles.UBO))
+ .ownershipPercentage(75)
+ .build()))
+ .financialDetails(EntityFinancialDetails.builder()
+ .annualProcessingVolume(120000000L)
+ .averageTransactionValue(10000L)
+ .highestTransactionValue(2500000L)
+ .currency(Currency.GBP)
+ .build())
+ .businessType(BusinessType.LIMITED_COMPANY)
+ .additionalTradingNames(Arrays.asList("SHM", "Hero Masks"))
+ .isRegisteredCompany(true)
+ .build();
+
+ final String json = serializer.toJson(company);
+
+ assertEquals(JsonParser.parseString("{\"legal_name\":\"Super Hero Masks Ltd\","
+ + "\"trading_name\":\"Super Hero Masks\",\"business_registration_number\":\"01234567\","
+ + "\"date_of_incorporation\":{\"day\":1,\"month\":6,\"year\":2010},"
+ + "\"regulatory_licence_number\":\"FRN123456\","
+ + "\"principal_address\":{\"address_line1\":\"90 Tottenham Court Road\",\"address_line2\":\"Floor 2\","
+ + "\"city\":\"London\",\"state\":\"London\",\"zip\":\"W1T 4TJ\",\"country\":\"GB\"},"
+ + "\"registered_address\":{\"address_line1\":\"1 Main Street\",\"city\":\"London\","
+ + "\"zip\":\"W1T 4TJ\",\"country\":\"GB\"},"
+ + "\"representatives\":[{\"id\":\"rep_xoo3xudh9mgxw6tv4140063pdi\",\"roles\":[\"ubo\"],"
+ + "\"ownership_percentage\":75,\"individual\":{\"first_name\":\"Jane\",\"last_name\":\"Doe\"}}],"
+ + "\"financial_details\":{\"annual_processing_volume\":120000000,\"average_transaction_value\":10000,"
+ + "\"highest_transaction_value\":2500000,\"currency\":\"GBP\"},"
+ + "\"business_type\":\"limited_company\",\"additional_trading_names\":[\"SHM\",\"Hero Masks\"],"
+ + "\"is_registered_company\":true}"),
+ JsonParser.parseString(json), json);
+ assertEquals(company, serializer.fromJson(json, Company.class));
+ }
+
+ // ------------------------------------------------------------------------
+ // Representative and Individual (v2.0)
+ // The v2.0 shape puts the person fields directly on the representative, and
+ // the v2.0 top-level individual carries identification and financial_details.
+ // ------------------------------------------------------------------------
+
+ @Test
+ @SuppressWarnings("deprecation")
+ void shouldSerializeAndRoundTripV2Representative() {
+ final Representative representative = Representative.builder()
+ .id("rep_r2y49v5j1skna5zx0swaprf2he")
+ .firstName("John")
+ .middleName("Paul")
+ .lastName("Doe")
+ .dateOfBirth(DateOfBirth.builder().day(5).month(6).year(1995).build())
+ .phone(AccountPhone.builder().number("2072345678").build())
+ .address(Address.builder()
+ .addressLine1("90 Tottenham Court Road")
+ .city("London")
+ .zip("W1T 4TJ")
+ .country(CountryCode.GB)
+ .build())
+ .placeOfBirth(PlaceOfBirth.builder().country(CountryCode.FR).build())
+ .identification(Identification.builder().nationalIdNumber("AB123456C").build())
+ .build();
+
+ final String json = serializer.toJson(representative);
+
+ assertEquals(JsonParser.parseString("{\"id\":\"rep_r2y49v5j1skna5zx0swaprf2he\","
+ + "\"first_name\":\"John\",\"middle_name\":\"Paul\",\"last_name\":\"Doe\","
+ + "\"date_of_birth\":{\"day\":5,\"month\":6,\"year\":1995},"
+ + "\"phone\":{\"number\":\"2072345678\"},"
+ + "\"address\":{\"address_line1\":\"90 Tottenham Court Road\",\"city\":\"London\","
+ + "\"zip\":\"W1T 4TJ\",\"country\":\"GB\"},"
+ + "\"place_of_birth\":{\"country\":\"FR\"},"
+ + "\"identification\":{\"national_id_number\":\"AB123456C\"}}"),
+ JsonParser.parseString(json), json);
+ assertEquals(representative, serializer.fromJson(json, Representative.class));
+ }
+
+ @Test
+ void shouldSerializeAndRoundTripV2Individual() {
+ final Individual individual = Individual.builder()
+ .firstName("Jane")
+ .middleName("Anne")
+ .lastName("Doe")
+ .tradingName("Jane's Crafts")
+ .registeredAddress(Address.builder()
+ .addressLine1("90 Tottenham Court Road")
+ .city("London")
+ .zip("W1T 4TJ")
+ .country(CountryCode.GB)
+ .build())
+ .dateOfBirth(DateOfBirth.builder().day(15).month(1).year(1990).build())
+ .placeOfBirth(PlaceOfBirth.builder().country(CountryCode.GB).build())
+ .identification(Identification.builder().nationalIdNumber("QQ123456C").build())
+ .financialDetails(EntityFinancialDetails.builder()
+ .annualProcessingVolume(5000000L)
+ .averageTransactionValue(2500L)
+ .highestTransactionValue(100000L)
+ .currency(Currency.GBP)
+ .build())
+ .build();
+
+ final String json = serializer.toJson(individual);
+
+ assertEquals(JsonParser.parseString("{\"first_name\":\"Jane\",\"middle_name\":\"Anne\","
+ + "\"last_name\":\"Doe\",\"trading_name\":\"Jane's Crafts\","
+ + "\"registered_address\":{\"address_line1\":\"90 Tottenham Court Road\",\"city\":\"London\","
+ + "\"zip\":\"W1T 4TJ\",\"country\":\"GB\"},"
+ + "\"date_of_birth\":{\"day\":15,\"month\":1,\"year\":1990},"
+ + "\"place_of_birth\":{\"country\":\"GB\"},"
+ + "\"identification\":{\"national_id_number\":\"QQ123456C\"},"
+ + "\"financial_details\":{\"annual_processing_volume\":5000000,\"average_transaction_value\":2500,"
+ + "\"highest_transaction_value\":100000,\"currency\":\"GBP\"}}"),
+ JsonParser.parseString(json), json);
+ assertEquals(individual, serializer.fromJson(json, Individual.class));
+ }
+
+ // ------------------------------------------------------------------------
+ // Identification
+ // The spec has national_id_number only; the deprecated document stays unset.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSerializeAndRoundTripIdentification() {
+ final Identification identification = Identification.builder().nationalIdNumber("AB123456C").build();
+
+ final String json = serializer.toJson(identification);
+
+ assertEquals(JsonParser.parseString("{\"national_id_number\":\"AB123456C\"}"), JsonParser.parseString(json), json);
+ assertEquals(identification, serializer.fromJson(json, Identification.class));
+ }
+
+ // ------------------------------------------------------------------------
+ // AccountsFileRequest
+ // The purpose is sent as a multipart text part built from getPurpose(), not
+ // through Gson, so the wire value is the getPurpose() string.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSendAccountsFileRequestPurposeWireValue() {
+ final AccountsFileRequest request = AccountsFileRequest.builder()
+ .purpose(AccountsFilePurpose.IDENTITY_VERIFICATION)
+ .build();
+
+ assertEquals("identity_verification", request.getPurpose().getPurpose());
+ }
+
+ // ------------------------------------------------------------------------
+ // Enum wire values
+ // Every value is serialized through Gson and compared to the spec enum string.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSerializeEveryIdentityVerificationDocumentTypeWireValue() {
+ final Map expected = new EnumMap<>(DocumentType.class);
+ expected.put(DocumentType.PASSPORT, "passport");
+ expected.put(DocumentType.NATIONAL_IDENTITY_CARD, "national_identity_card");
+ expected.put(DocumentType.DRIVING_LICENSE, "driving_license");
+ expected.put(DocumentType.CITIZEN_CARD, "citizen_card");
+ expected.put(DocumentType.RESIDENCE_PERMIT, "residence_permit");
+ expected.put(DocumentType.ELECTORAL_ID, "electoral_id");
+
+ assertEveryWireValue(DocumentType.class, expected);
+ }
+
+ @Test
+ void shouldSerializeEveryCompanyVerificationTypeWireValue() {
+ final Map expected = new EnumMap<>(CompanyVerificationType.class);
+ expected.put(CompanyVerificationType.INCORPORATION_DOCUMENT, "incorporation_document");
+ expected.put(CompanyVerificationType.ARTICLES_OF_ASSOCIATION, "articles_of_association");
+
+ assertEveryWireValue(CompanyVerificationType.class, expected);
+ }
+
+ @Test
+ void shouldSerializeEveryArticlesOfAssociationTypeWireValue() {
+ final Map expected = new EnumMap<>(ArticlesOfAssociationType.class);
+ expected.put(ArticlesOfAssociationType.ARTICLES_OF_ASSOCIATION, "articles_of_association");
+ expected.put(ArticlesOfAssociationType.MEMORANDUM_OF_ASSOCIATION, "memorandum_of_association");
+
+ assertEveryWireValue(ArticlesOfAssociationType.class, expected);
+ }
+
+ @Test
+ void shouldSerializeEveryNationalIdTypeWireValue() {
+ final Map expected = new EnumMap<>(NationalIdType.class);
+ expected.put(NationalIdType.SSN, "ssn");
+ expected.put(NationalIdType.ITIN, "itin");
+ expected.put(NationalIdType.PASSPORT, "passport");
+ expected.put(NationalIdType.DRIVING_LICENSE, "driving_license");
+ expected.put(NationalIdType.NATIONAL_ID_CARD, "national_id_card");
+ expected.put(NationalIdType.RESIDENCE_PERMIT, "residence_permit");
+ expected.put(NationalIdType.OTHER, "other");
+
+ assertEveryWireValue(NationalIdType.class, expected);
+ }
+
+ @Test
+ void shouldSerializeEveryEntityRolesWireValue() {
+ final Map expected = new EnumMap<>(EntityRoles.class);
+ expected.put(EntityRoles.UBO, "ubo");
+ expected.put(EntityRoles.LEGAL_REPRESENTATIVE, "legal_representative");
+ expected.put(EntityRoles.AUTHORISED_SIGNATORY, "authorised_signatory");
+ expected.put(EntityRoles.DIRECTOR, "director");
+ expected.put(EntityRoles.CONTROL_PERSON, "control_person");
+
+ assertEveryWireValue(EntityRoles.class, expected);
+ }
+
+ @Test
+ void shouldSerializeEveryCompanyPositionWireValue() {
+ final Map expected = new EnumMap<>(CompanyPosition.class);
+ expected.put(CompanyPosition.CEO, "ceo");
+ expected.put(CompanyPosition.CFO, "cfo");
+ expected.put(CompanyPosition.COO, "coo");
+ expected.put(CompanyPosition.MANAGING_MEMBER, "managing_member");
+ expected.put(CompanyPosition.GENERAL_PARTNER, "general_partner");
+ expected.put(CompanyPosition.PRESIDENT, "president");
+ expected.put(CompanyPosition.VICE_PRESIDENT, "vice_president");
+ expected.put(CompanyPosition.TREASURER, "treasurer");
+ expected.put(CompanyPosition.OTHER_SENIOR_MANAGEMENT, "other_senior_management");
+ expected.put(CompanyPosition.OTHER_EXECUTIVE_OFFICER, "other_executive_officer");
+ expected.put(CompanyPosition.OTHER_NON_EXECUTIVE_NON_SENIOR, "other_non_executive_non_senior");
+
+ assertEveryWireValue(CompanyPosition.class, expected);
+ }
+
+ @Test
+ void shouldSerializeEveryBusinessTypeWireValue() {
+ final Map expected = new EnumMap<>(BusinessType.class);
+ expected.put(BusinessType.GENERAL_PARTNERSHIP, "general_partnership");
+ expected.put(BusinessType.LIMITED_PARTNERSHIP, "limited_partnership");
+ expected.put(BusinessType.PUBLIC_LIMITED_COMPANY, "public_limited_company");
+ expected.put(BusinessType.LIMITED_COMPANY, "limited_company");
+ expected.put(BusinessType.PROFESSIONAL_ASSOCIATION, "professional_association");
+ expected.put(BusinessType.UNINCORPORATED_ASSOCIATION, "unincorporated_association");
+ expected.put(BusinessType.AUTO_ENTREPRENEUR, "auto_entrepreneur");
+ expected.put(BusinessType.INDIVIDUAL_OR_SOLE_PROPRIETORSHIP, "individual_or_sole_proprietorship");
+ expected.put(BusinessType.SCOTTISH_LIMITED_PARTNERSHIP, "scottish_limited_partnership");
+ expected.put(BusinessType.LIMITED_LIABILITY_CORPORATION, "limited_liability_corporation");
+ expected.put(BusinessType.PRIVATE_CORPORATION, "private_corporation");
+ expected.put(BusinessType.PUBLICLY_TRADED_CORPORATION, "publicly_traded_corporation");
+ expected.put(BusinessType.GOVERNMENT_AGENCY, "government_agency");
+ expected.put(BusinessType.NON_PROFIT_ENTITY, "non_profit_entity");
+ expected.put(BusinessType.TRUST, "trust");
+ expected.put(BusinessType.CLUB_OR_SOCIETY, "club_or_society");
+ expected.put(BusinessType.REGULATED_FINANCIAL_INSTITUTION, "regulated_financial_institution");
+ expected.put(BusinessType.CFTC_REGISTERED_ENTITY, "cftc_registered_entity");
+ expected.put(BusinessType.SEC_REGISTERED_ENTITY, "sec_registered_entity");
+
+ assertEveryWireValue(BusinessType.class, expected);
+ }
+
+ private > void assertEveryWireValue(final Class type, final Map expected) {
+ assertEquals(type.getEnumConstants().length, expected.size(), "every value must be asserted");
+ expected.forEach((value, wire) -> {
+ assertEquals("\"" + wire + "\"", serializer.toJson(value), value.name());
+ assertEquals(value, serializer.fromJson("\"" + wire + "\"", type), wire);
+ });
+ }
}
diff --git a/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java b/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java
index 77840cdf..8da1d0fd 100644
--- a/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java
+++ b/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java
@@ -63,11 +63,11 @@ void shouldSerializeBankVerificationAndShareholderStructureAsObjects() {
final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
.bankVerification(BankVerification.builder()
.type(BankVerificationType.BANK_STATEMENT)
- .front("file_bank")
+ .front("file_jj7e4kwpcenegfwy4dpscnf4kl")
.build())
.shareholderStructure(ShareholderStructure.builder()
.type(ShareholderStructureType.CERTIFIED_SHAREHOLDER_STRUCTURE)
- .front("file_shareholder")
+ .front("file_v2jnxxmuzhnmne2xemjvypx3lb")
.build())
.build();
@@ -91,8 +91,8 @@ void shouldDeserializeArticlesOfAssociation() {
// ------------------------------------------------------------------------
// Representative documents (company.representatives[].documents)
// The EEA Sole Trader (3.0) keys and the company-variant certified authorised
- // signatory. The representative object is strict on the API, so the exact key
- // set matters.
+ // signatory. The representative object is strict on the Company Full and Sole
+ // Trader Full (3.0) variants of EEA, GB and US, so the exact key set matters.
// ------------------------------------------------------------------------
// Regression: EEA Sole Trader (3.0) needs proof_of_residential_address and proof_of_registration
@@ -178,11 +178,11 @@ void shouldSendCompanyAndTaxVerificationTypesUnderTheirOwnKeys() {
final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
.companyVerification(CompanyVerification.builder()
.type(CompanyVerificationType.INCORPORATION_DOCUMENT)
- .front("file_aaaaaaaaaaaaaaaaaaaaaaaaaa")
+ .front("file_reoytgtkcvtxlnco2u4l2ewg37")
.build())
.taxVerification(TaxVerification.builder()
.type(TaxVerificationType.EIN_LETTER)
- .front("file_aaaaaaaaaaaaaaaaaaaaaaaaaa")
+ .front("file_reoytgtkcvtxlnco2u4l2ewg37")
.build())
.build();
@@ -201,7 +201,7 @@ void shouldSendCompanyAndTaxVerificationTypesUnderTheirOwnKeys() {
@Test
void shouldSerializeAndRoundTripEveryDocumentsField() {
- final String file = "file_aaaaaaaaaaaaaaaaaaaaaaaaaa";
+ final String file = "file_reoytgtkcvtxlnco2u4l2ewg37";
final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder()
.identityVerification(Document.builder().type(DocumentType.PASSPORT).front(file).back(file).build())
.companyVerification(CompanyVerification.builder()
diff --git a/src/test/java/com/checkout/accounts/files/AccountsFilesSerializationTest.java b/src/test/java/com/checkout/accounts/files/AccountsFilesSerializationTest.java
new file mode 100644
index 00000000..42671af7
--- /dev/null
+++ b/src/test/java/com/checkout/accounts/files/AccountsFilesSerializationTest.java
@@ -0,0 +1,121 @@
+package com.checkout.accounts.files;
+
+import com.checkout.GsonSerializer;
+import com.checkout.accounts.files.entities.FilePurpose;
+import com.checkout.accounts.files.request.FileUploadRequest;
+import com.checkout.accounts.files.response.FileDetailsResponse;
+import com.checkout.accounts.files.response.FileUploadResponse;
+import com.google.gson.JsonParser;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class AccountsFilesSerializationTest {
+
+ private final GsonSerializer serializer = new GsonSerializer();
+
+ // ------------------------------------------------------------------------
+ // FileUploadResponse (PlatformsFileUploadResponse)
+ // Built from the spec's per-field example values, including the upload link.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldDeserializeFileUploadResponseSwaggerExample() {
+ final String upload = "https://s3.eu-west-1.amazonaws.com/mp-files-api-staging-prod/ent_ociwguf5a5fe3ndmpnvpnwsi3e/"
+ + "file_6lbss42ezvoufcb2beo76rvwly?AWSAccessKeyId=ASIX4BFJOBCQFLAMPKU3&Expires=1661355993"
+ + "&x-amz-security-token=some_token";
+ final String self = "https://files.checkout.com/files/file_6lbss42ezvoufcb2beo76rvwly";
+
+ final FileUploadResponse response = serializer.fromJson("{"
+ + "\"id\":\"file_6lbss42ezvoufcb2beo76rvwly\","
+ + "\"maximum_size_in_bytes\":4194304,"
+ + "\"document_types_for_purpose\":[\"image/jpeg\",\"image/png\",\"image/jpg\"],"
+ + "\"_links\":{\"upload\":{\"href\":\"" + upload + "\"},\"self\":{\"href\":\"" + self + "\"}}}",
+ FileUploadResponse.class);
+
+ assertEquals("file_6lbss42ezvoufcb2beo76rvwly", response.getId());
+ assertEquals(Long.valueOf(4194304L), response.getMaximumSizeInBytes());
+ assertEquals(Arrays.asList("image/jpeg", "image/png", "image/jpg"), response.getDocumentTypesForPurpose());
+ assertEquals(2, response.getLinks().size());
+ assertEquals(upload, response.getLink("upload").getHref());
+ assertEquals(self, response.getSelfLink().getHref());
+ }
+
+ // ------------------------------------------------------------------------
+ // FileDetailsResponse (PlatformsFileRetrieveResponse)
+ // uploaded_on uses the spec's seven fractional digits with a +00:00 offset.
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldDeserializeFileDetailsResponseSwaggerExample() {
+ final String download = "https://s3.eu-west-1.amazonaws.com/mp-files-api-clean-prod/ent_ociwguf5a5fe3ndmpnvpnwsi3e/"
+ + "file_6lbss42ezvoufcb2beo76rvwly?X-Amz-Expires=3600&x-amz-security-token=some_token";
+ final String self = "https://files.checkout.com/files/file_6lbss42ezvoufcb2beo76rvwly";
+
+ final FileDetailsResponse response = serializer.fromJson("{"
+ + "\"id\":\"file_6lbss42ezvoufcb2beo76rvwly\","
+ + "\"status\":\"invalid\","
+ + "\"status_reasons\":[\"InvalidMimeType\"],"
+ + "\"size\":1024,"
+ + "\"mime_type\":\"application/pdf\","
+ + "\"uploaded_on\":\"2020-12-01T15:01:01.0000000+00:00\","
+ + "\"purpose\":\"identity_verification\","
+ + "\"_links\":{\"download\":{\"href\":\"" + download + "\"},\"self\":{\"href\":\"" + self + "\"}}}",
+ FileDetailsResponse.class);
+
+ assertEquals("file_6lbss42ezvoufcb2beo76rvwly", response.getId());
+ assertEquals("invalid", response.getStatus());
+ assertEquals(Collections.singletonList("InvalidMimeType"), response.getStatusReasons());
+ assertEquals(Long.valueOf(1024L), response.getSize());
+ assertEquals("application/pdf", response.getMimeType());
+ assertEquals(Instant.parse("2020-12-01T15:01:01Z"), response.getUploadedOn());
+ assertEquals(FilePurpose.IDENTITY_VERIFICATION, response.getPurpose());
+ assertEquals(2, response.getLinks().size());
+ assertEquals(download, response.getLink("download").getHref());
+ assertEquals(self, response.getSelfLink().getHref());
+ }
+
+ // ------------------------------------------------------------------------
+ // FileUploadRequest and FilePurpose
+ // ------------------------------------------------------------------------
+
+ @Test
+ void shouldSerializeFileUploadRequestPurposeOnly() {
+ final FileUploadRequest request = FileUploadRequest.builder().purpose(FilePurpose.IDENTITY_VERIFICATION).build();
+
+ assertEquals(JsonParser.parseString("{\"purpose\":\"identity_verification\"}"),
+ JsonParser.parseString(serializer.toJson(request)));
+ }
+
+ @Test
+ void shouldSerializeEveryFilePurposeWireValue() {
+ final Map expected = new EnumMap<>(FilePurpose.class);
+ expected.put(FilePurpose.ADDITIONAL_DOCUMENT, "additional_document");
+ expected.put(FilePurpose.ARTICLES_OF_ASSOCIATION, "articles_of_association");
+ expected.put(FilePurpose.BANK_VERIFICATION, "bank_verification");
+ expected.put(FilePurpose.CERTIFIED_AUTHORISED_SIGNATORY, "certified_authorised_signatory");
+ expected.put(FilePurpose.COMPANY_OWNERSHIP, "company_ownership");
+ expected.put(FilePurpose.COMPANY_VERIFICATION, "company_verification");
+ expected.put(FilePurpose.FINANCIAL_VERIFICATION, "financial_verification");
+ expected.put(FilePurpose.IDENTITY_VERIFICATION, "identity_verification");
+ expected.put(FilePurpose.PROOF_OF_LEGALITY, "proof_of_legality");
+ expected.put(FilePurpose.PROOF_OF_PRINCIPAL_ADDRESS, "proof_of_principal_address");
+ expected.put(FilePurpose.SHAREHOLDER_STRUCTURE, "shareholder_structure");
+ expected.put(FilePurpose.TAX_VERIFICATION, "tax_verification");
+ expected.put(FilePurpose.PROOF_OF_RESIDENTIAL_ADDRESS, "proof_of_residential_address");
+ expected.put(FilePurpose.PROOF_OF_REGISTRATION, "proof_of_registration");
+ expected.put(FilePurpose.DISPUTE_EVIDENCE, "dispute_evidence");
+
+ assertEquals(FilePurpose.values().length, expected.size(), "every value must be asserted");
+ expected.forEach((purpose, wire) -> {
+ assertEquals("\"" + wire + "\"", serializer.toJson(purpose), purpose.name());
+ assertEquals(purpose, serializer.fromJson("\"" + wire + "\"", FilePurpose.class), wire);
+ });
+ }
+}