From 01e62224a6d0fd82199be7aa76542cfc2343466e 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 17:13:58 +0200 Subject: [PATCH] fix(googlepay): add merchant_id to the enrollment response Reported internally on behalf of a merchant: the Google Pay enrollment response is missing merchant_id, which is what the caller needs to initialise Google Pay on the client. The root cause is the spec, not this SDK. GooglePayEnrollmentResponse declares only tosAcceptedTime and state, both required, omits merchant_id and sets additionalProperties false. The real 201 from POST /googlepay/enrollments returns merchant_id, tos_accepted_time and state. The class was generated faithfully from a wrong schema. The spec fix is being raised separately, otherwise the next regeneration reintroduces this. tosAcceptedTime already worked here: GsonSerializer applies FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES, so it matches tos_accepted_time on the wire without a SerializedName. Four new tests. They deserialize the real sandbox body rather than the swagger example, since the swagger example is the thing that was wrong, and one asserts merchant_id survives on its own, because losing it is the entire defect. Refs INT-1690. --- .../GooglePayEnrollmentResponse.java | 12 ++++ .../googlepay/GooglePaySerializationTest.java | 60 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/googlepay/responses/GooglePayEnrollmentResponse.java b/src/main/java/com/checkout/handlepaymentsandpayouts/googlepay/responses/GooglePayEnrollmentResponse.java index c2a92faa..16164460 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/googlepay/responses/GooglePayEnrollmentResponse.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/googlepay/responses/GooglePayEnrollmentResponse.java @@ -9,12 +9,24 @@ /** * Response returned when enrolling an entity in Google Pay. + * + *
The real 201 body carries merchant_id, tos_accepted_time and state. The spec declares only + * {@code tosAcceptedTime} and {@code state}, with {@code additionalProperties false}, so + * {@code merchant_id} was missing here: this class was generated faithfully from a wrong schema. + * Reported by a merchant. The spec is being fixed separately; until then this class follows the + * live API.
*/ @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public final class GooglePayEnrollmentResponse extends HttpMetadata { + /** + * The Google Pay merchant identifier assigned to the entity, needed to initialise Google Pay + * on the client. Returned by the API but absent from the spec. + */ + private String merchantId; + /** * When the Google terms of service were accepted. *diff --git a/src/test/java/com/checkout/handlepaymentsandpayouts/googlepay/GooglePaySerializationTest.java b/src/test/java/com/checkout/handlepaymentsandpayouts/googlepay/GooglePaySerializationTest.java index 6f346d47..b97744a5 100644 --- a/src/test/java/com/checkout/handlepaymentsandpayouts/googlepay/GooglePaySerializationTest.java +++ b/src/test/java/com/checkout/handlepaymentsandpayouts/googlepay/GooglePaySerializationTest.java @@ -15,6 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -148,6 +149,65 @@ void shouldRoundTripGooglePayEnrollmentResponse() { assertEquals(original.getState(), deserialized.getState()); } + /** + * The body a real POST /googlepay/enrollments returns in sandbox, which is not what the + * swagger example below says: it also carries merchant_id, and the spec omits it while + * setting additionalProperties false. A merchant reported the field as missing. + */ + @Test + void shouldDeserializeTheRealGooglePayEnrollmentResponse() { + String json = "{" + + "\"merchant_id\":\"12345678901234567890\"," + + "\"tos_accepted_time\":\"2026-08-13T09:12:41Z\"," + + "\"state\":\"ACTIVE\"" + + "}"; + + GooglePayEnrollmentResponse response = serializer.fromJson(json, GooglePayEnrollmentResponse.class); + + assertNotNull(response); + assertEquals("12345678901234567890", response.getMerchantId()); + assertEquals(Instant.parse("2026-08-13T09:12:41Z"), response.getTosAcceptedTime()); + assertEquals(GooglePayEnrollmentState.ACTIVE, response.getState()); + } + + /** + * merchant_id is what the caller needs to initialise Google Pay on the client, so losing it + * is the whole defect. Asserted on its own to make that unmissable. + */ + @Test + void shouldNotSilentlyDropMerchantId() { + String json = "{" + + "\"merchant_id\":\"12345678901234567890\"," + + "\"tos_accepted_time\":\"2026-08-13T09:12:41Z\"," + + "\"state\":\"ACTIVE\"" + + "}"; + + assertNotNull(serializer.fromJson(json, GooglePayEnrollmentResponse.class).getMerchantId()); + } + + /** + * A missing merchant_id has to come back null rather than empty: a caller must be able to + * tell "not returned" from "returned blank". + */ + @Test + void shouldLeaveMerchantIdNullWhenAbsent() { + String json = "{\"tos_accepted_time\":\"2026-08-13T09:12:41Z\",\"state\":\"ACTIVE\"}"; + + GooglePayEnrollmentResponse response = serializer.fromJson(json, GooglePayEnrollmentResponse.class); + + assertNull(response.getMerchantId()); + assertEquals(GooglePayEnrollmentState.ACTIVE, response.getState()); + } + + @Test + void shouldSerializeMerchantIdAsSnakeCase() { + GooglePayEnrollmentResponse response = new GooglePayEnrollmentResponse(); + response.setMerchantId("12345678901234567890"); + response.setState(GooglePayEnrollmentState.ACTIVE); + + assertTrue(serializer.toJson(response).contains("\"merchant_id\":\"12345678901234567890\"")); + } + @Test void shouldDeserializeSwaggerExampleGooglePayEnrollmentResponse() { String swaggerJson = "{"