From 61571105883e341b9e1eb8430148f9e5ba0b611e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Tue, 18 Aug 2026 16:05:14 +0200 Subject: [PATCH] fix(accounts): send articles_of_association as an object, not a bare string OnboardSubEntityDocuments.articlesOfAssociation was typed as the enum ArticlesOfAssociationType, and no ArticlesOfAssociation class existed. The API requires an object with type and front, so the SDK serialized "articles_of_association": "articles_of_association" and the request was rejected. That document is required on the company full onboarding variants, so it could not be sent from Java at all. Found while verifying a report about the bank_verification document type, which Java already models correctly. Its siblings BankVerification and ShareholderStructure were objects all along; only this one was wrong. The field type changes, so this is source-breaking for anyone assigning the enum directly. They could not have been sending a valid request, since the old shape was rejected by the API, so nothing that worked stops working. Breaking because the API demands it, not by our choice: a minor, per the release rules, and called out in the release notes. One test asserts the old string shape is gone, not just that the new one is present. 2014 tests, 1 failure unrelated to this change: RefundPaymentsTestIT shouldRefundTokenPaymentSync fails with no_capture_balance_available_to_refund, a sandbox balance condition in payments. Refs INT-1691. --- .../accounts/ArticlesOfAssociation.java | 33 +++++++ .../accounts/OnboardSubEntityDocuments.java | 2 +- ...rdSubEntityDocumentsSerializationTest.java | 85 +++++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/checkout/accounts/ArticlesOfAssociation.java create mode 100644 src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java diff --git a/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java b/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java new file mode 100644 index 00000000..0b136f40 --- /dev/null +++ b/src/main/java/com/checkout/accounts/ArticlesOfAssociation.java @@ -0,0 +1,33 @@ +package com.checkout.accounts; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 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 + * 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.

+ */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class ArticlesOfAssociation { + + /** + * The type of document being used as the memorandum or articles of association. + */ + 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. + */ + private String front; + +} diff --git a/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java b/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java index 40f2fb3a..1784e352 100644 --- a/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java +++ b/src/main/java/com/checkout/accounts/OnboardSubEntityDocuments.java @@ -16,7 +16,7 @@ public final class OnboardSubEntityDocuments { private CompanyVerification companyVerification; - private ArticlesOfAssociationType articlesOfAssociation; + private ArticlesOfAssociation articlesOfAssociation; private BankVerification bankVerification; diff --git a/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java b/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java new file mode 100644 index 00000000..b2182d88 --- /dev/null +++ b/src/test/java/com/checkout/accounts/OnboardSubEntityDocumentsSerializationTest.java @@ -0,0 +1,85 @@ +package com.checkout.accounts; + +import com.checkout.GsonSerializer; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Covers the onboarding documents that carry a type plus a file ID. + * + *

articlesOfAssociation was typed as the ArticlesOfAssociationType enum and there was no + * ArticlesOfAssociation class, so the SDK serialized {@code "articles_of_association": + * "articles_of_association"} where the API requires an object. That document, which is required + * on the company full variants, could not be sent from Java at all.

+ */ +class OnboardSubEntityDocumentsSerializationTest { + + private final GsonSerializer serializer = new GsonSerializer(); + + @Test + void shouldSerializeArticlesOfAssociationAsAnObject() { + final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder() + .articlesOfAssociation(ArticlesOfAssociation.builder() + .type(ArticlesOfAssociationType.ARTICLES_OF_ASSOCIATION) + .front("file_6lbss42ezvoufcb2beo76rvwly") + .build()) + .build(); + + final String json = serializer.toJson(documents); + + assertTrue(json.contains("\"articles_of_association\":{"), json); + assertTrue(json.contains("\"type\":\"articles_of_association\""), json); + assertTrue(json.contains("\"front\":\"file_6lbss42ezvoufcb2beo76rvwly\""), json); + // The old shape. If this ever comes back, the API rejects the request. + assertFalse(json.contains("\"articles_of_association\":\"articles_of_association\""), json); + } + + @Test + void shouldSerializeMemorandumOfAssociation() { + final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder() + .articlesOfAssociation(ArticlesOfAssociation.builder() + .type(ArticlesOfAssociationType.MEMORANDUM_OF_ASSOCIATION) + .front("file_6lbss42ezvoufcb2beo76rvwly") + .build()) + .build(); + + assertTrue(serializer.toJson(documents).contains("\"type\":\"memorandum_of_association\"")); + } + + /** + * The sibling documents were already objects. Asserted here so the three stay consistent: + * they are the same shape in the API and a future edit should not split them apart again. + */ + @Test + void shouldSerializeBankVerificationAndShareholderStructureAsObjects() { + final OnboardSubEntityDocuments documents = OnboardSubEntityDocuments.builder() + .bankVerification(BankVerification.builder() + .type(BankVerificationType.BANK_STATEMENT) + .front("file_bank") + .build()) + .shareholderStructure(ShareholderStructure.builder() + .type(ShareholderStructureType.CERTIFIED_SHAREHOLDER_STRUCTURE) + .front("file_shareholder") + .build()) + .build(); + + final String json = serializer.toJson(documents); + + assertTrue(json.contains("\"bank_verification\":{\"type\":\"bank_statement\""), json); + assertTrue(json.contains("\"shareholder_structure\":{\"type\":\"certified_shareholder_structure\""), json); + } + + @Test + void shouldDeserializeArticlesOfAssociation() { + final String json = "{\"articles_of_association\":{\"type\":\"articles_of_association\"," + + "\"front\":\"file_6lbss42ezvoufcb2beo76rvwly\"}}"; + + final OnboardSubEntityDocuments documents = serializer.fromJson(json, OnboardSubEntityDocuments.class); + + assertEquals(ArticlesOfAssociationType.ARTICLES_OF_ASSOCIATION, documents.getArticlesOfAssociation().getType()); + assertEquals("file_6lbss42ezvoufcb2beo76rvwly", documents.getArticlesOfAssociation().getFront()); + } +}