From c42deb23737a84454be29ce54d0f2828d29e846e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Wed, 16 Sep 2026 16:14:40 +0200 Subject: [PATCH] feat(payments): add PaymentSetup accommodation/airline fields Adds totalNumberOfGuests, refundable, deliveryRecipient and host to AccommodationData, and totalNumberOfPassengers, travelType, tripType, refundable, deliveryRecipient, ancillaries and insurance to AirlineData (POST/GET/PUT /payments/setups, POST /payments/setups/{id}/confirm/{payment_method_name}). Also fixes industry.airline and industry.accommodation, which serialized under the wrong keys (airline_data/accommodation_data) because of a missing @SerializedName, so the fields never reached the API correctly. Splits accommodation modelling out of the shared PaymentContextsAccommodationData type, which is also used by the unrelated /payments contexts domain. --- .../entities/industry/AccommodationData.java | 97 ++++++++++++++++ .../entities/industry/AccommodationGuest.java | 37 ++++++ .../entities/industry/AccommodationHost.java | 31 +++++ .../entities/industry/AccommodationRoom.java | 34 ++++++ .../setups/entities/industry/AirlineData.java | 42 +++++++ .../entities/industry/AirlineInsurance.java | 34 ++++++ .../industry/AirlineInsurancePrice.java | 29 +++++ .../setups/entities/industry/Industry.java | 8 +- ...aymentSetupsIndustrySerializationTest.java | 108 ++++++++++++++++++ 9 files changed, 417 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationData.java create mode 100644 src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationGuest.java create mode 100644 src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationHost.java create mode 100644 src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationRoom.java create mode 100644 src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineInsurance.java create mode 100644 src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineInsurancePrice.java create mode 100644 src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsIndustrySerializationTest.java diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationData.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationData.java new file mode 100644 index 00000000..d4917e7f --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationData.java @@ -0,0 +1,97 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.industry; + +import com.checkout.common.Address; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.List; + +/** + * Accommodation industry-specific data for hotel and cruise bookings and related payments. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class AccommodationData { + + /** + * For lodging, the lodging name that appears on the storefront and customer receipts. + * For cruise, the ship name booked for the cruise. + * [Optional] + */ + private String name; + + /** + * A unique identifier for the booking. + * [Optional] + */ + private String bookingReference; + + /** + * For lodging bookings, the customer's check-in date. + * For cruise bookings, the cruise departure date (sail date). + * [Optional] + * Format: yyyy-MM-dd + */ + private LocalDate checkInDate; + + /** + * For lodging bookings, the customer's check-out date. + * For cruise bookings, the cruise return date. + * [Optional] + * Format: yyyy-MM-dd + */ + private LocalDate checkOutDate; + + /** + * The accommodation's address. + * [Optional] + */ + private Address address; + + /** + * The total number of rooms booked for the accommodation. + * [Optional] + */ + private Long numberOfRooms; + + /** + * The list of guests staying at the accommodation. + * [Optional] + */ + private List guests; + + /** + * The list of rooms booked by the customer. + * [Optional] + */ + private List room; + + /** + * The total number of guests on the booking. + * [Optional] + */ + private Long totalNumberOfGuests; + + /** + * Specifies whether the booking is refundable. + * [Optional] + */ + private Boolean refundable; + + /** + * The recipient the booking confirmation is delivered to. + * [Optional] + */ + private String deliveryRecipient; + + /** + * Details about the host of the accommodation. + * [Optional] + */ + private AccommodationHost host; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationGuest.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationGuest.java new file mode 100644 index 00000000..7c671db7 --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationGuest.java @@ -0,0 +1,37 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.industry; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +/** + * A guest staying at the accommodation. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class AccommodationGuest { + + /** + * The first name of the guest. + * [Optional] + */ + private String firstName; + + /** + * The last name of the guest. + * [Optional] + */ + private String lastName; + + /** + * The date of birth of the guest. + * [Optional] + * Format: yyyy-MM-dd + */ + private LocalDate dateOfBirth; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationHost.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationHost.java new file mode 100644 index 00000000..bb909a7a --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationHost.java @@ -0,0 +1,31 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.industry; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +/** + * Details about the host of the accommodation. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class AccommodationHost { + + /** + * The date the host registered. + * [Optional] + * Format: yyyy-MM-dd + */ + private LocalDate registrationDate; + + /** + * The total number of reservations made by the host. + * [Optional] + */ + private Long totalReservationCount; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationRoom.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationRoom.java new file mode 100644 index 00000000..a5bc5ca7 --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AccommodationRoom.java @@ -0,0 +1,34 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.industry; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * A room booked by the customer as part of an accommodation booking. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class AccommodationRoom { + + /** + * The rate of the room. + * [Optional] + */ + private Double rate; + + /** + * The number of nights booked at this room rate. + * [Optional] + */ + private Long numberOfNights; + + /** + * The type of room, for example "deluxe". + * [Optional] + */ + private String type; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineData.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineData.java index 8529bb49..8483036e 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineData.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineData.java @@ -33,4 +33,46 @@ public final class AirlineData { * Details of each leg of the flight journey */ private List flightLegDetails; + + /** + * The total number of passengers on the booking. + * [Optional] + */ + private Long totalNumberOfPassengers; + + /** + * The type of travel, for example "international" or "domestic". + * [Optional] + */ + private String travelType; + + /** + * The type of trip, for example "one_way" or "round_trip". + * [Optional] + */ + private String tripType; + + /** + * Specifies whether the booking is refundable. + * [Optional] + */ + private Boolean refundable; + + /** + * The recipient the ticket is delivered to. + * [Optional] + */ + private String deliveryRecipient; + + /** + * Any additional add-ons purchased with the booking, for example "extra_baggage". + * [Optional] + */ + private String ancillaries; + + /** + * Details about the travel insurance purchased with the booking. + * [Optional] + */ + private AirlineInsurance insurance; } \ No newline at end of file diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineInsurance.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineInsurance.java new file mode 100644 index 00000000..80c5d851 --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineInsurance.java @@ -0,0 +1,34 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.industry; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Details about the travel insurance purchased with the booking. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class AirlineInsurance { + + /** + * The type of insurance purchased. + * [Optional] + */ + private String type; + + /** + * The name of the insurance company. + * [Optional] + */ + private String company; + + /** + * The price of the insurance. + * [Optional] + */ + private AirlineInsurancePrice price; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineInsurancePrice.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineInsurancePrice.java new file mode 100644 index 00000000..5fcde970 --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/AirlineInsurancePrice.java @@ -0,0 +1,29 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.industry; + +import com.checkout.common.Currency; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The price of the travel insurance purchased with the booking. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class AirlineInsurancePrice { + + /** + * The insurance amount. + * [Optional] + */ + private Double amount; + + /** + * The 3-letter ISO currency code of the insurance amount. + * [Optional] + */ + private Currency currency; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/Industry.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/Industry.java index 19246266..2e10c8c6 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/Industry.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/industry/Industry.java @@ -1,6 +1,6 @@ package com.checkout.handlepaymentsandpayouts.setups.entities.industry; -import com.checkout.payments.contexts.PaymentContextsAccommodationData; +import com.google.gson.annotations.SerializedName; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -20,10 +20,12 @@ public final class Industry { /** * Airline industry-specific data for flight bookings and related payments */ + @SerializedName("airline") private AirlineData airlineData; /** - * Accommodation industry-specific data for hotel bookings and related payments + * Accommodation industry-specific data for hotel and cruise bookings and related payments */ - private List accommodationData; + @SerializedName("accommodation") + private List accommodationData; } \ No newline at end of file diff --git a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsIndustrySerializationTest.java b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsIndustrySerializationTest.java new file mode 100644 index 00000000..feba555f --- /dev/null +++ b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsIndustrySerializationTest.java @@ -0,0 +1,108 @@ +package com.checkout.handlepaymentsandpayouts.setups; + +import com.checkout.GsonSerializer; +import com.checkout.common.Currency; +import com.checkout.handlepaymentsandpayouts.setups.entities.industry.AccommodationData; +import com.checkout.handlepaymentsandpayouts.setups.entities.industry.AccommodationHost; +import com.checkout.handlepaymentsandpayouts.setups.entities.industry.AirlineData; +import com.checkout.handlepaymentsandpayouts.setups.entities.industry.AirlineInsurance; +import com.checkout.handlepaymentsandpayouts.setups.entities.industry.AirlineInsurancePrice; +import com.checkout.handlepaymentsandpayouts.setups.entities.industry.Industry; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PaymentSetupsIndustrySerializationTest { + + private final GsonSerializer serializer = new GsonSerializer(); + + @Test + void shouldSerializeIndustryUnderCorrectJsonKeys() { + final Industry industry = Industry.builder() + .airlineData(AirlineData.builder().totalNumberOfPassengers(2L).build()) + .accommodationData(Collections.singletonList( + AccommodationData.builder().name("Grand Hotel").build())) + .build(); + + final String json = serializer.toJson(industry); + + // The API exposes these as "airline" / "accommodation", not "airline_data" / "accommodation_data". + assertTrue(json.contains("\"airline\"")); + assertTrue(json.contains("\"accommodation\"")); + assertTrue(!json.contains("\"airline_data\"")); + assertTrue(!json.contains("\"accommodation_data\"")); + } + + @Test + void shouldSerializeAndDeserializeNewAccommodationFields() { + final AccommodationData accommodation = AccommodationData.builder() + .name("Grand Hotel") + .bookingReference("BOOK-123") + .checkInDate(LocalDate.of(2026, 10, 1)) + .checkOutDate(LocalDate.of(2026, 10, 5)) + .numberOfRooms(2L) + .totalNumberOfGuests(4L) + .refundable(true) + .deliveryRecipient("jane.smith@example.com") + .host(AccommodationHost.builder() + .registrationDate(LocalDate.of(2020, 1, 1)) + .totalReservationCount(150L) + .build()) + .build(); + + final String json = serializer.toJson(accommodation); + assertTrue(json.contains("\"total_number_of_guests\":4")); + assertTrue(json.contains("\"refundable\":true")); + assertTrue(json.contains("\"delivery_recipient\":\"jane.smith@example.com\"")); + assertTrue(json.contains("\"registration_date\":\"2020-01-01\"")); + assertTrue(json.contains("\"total_reservation_count\":150")); + + final AccommodationData deserialized = serializer.fromJson(json, AccommodationData.class); + assertEquals(4L, deserialized.getTotalNumberOfGuests()); + assertEquals(true, deserialized.getRefundable()); + assertEquals("jane.smith@example.com", deserialized.getDeliveryRecipient()); + assertNotNull(deserialized.getHost()); + assertEquals(150L, deserialized.getHost().getTotalReservationCount()); + } + + @Test + void shouldSerializeAndDeserializeNewAirlineFields() { + final AirlineData airline = AirlineData.builder() + .totalNumberOfPassengers(3L) + .travelType("international") + .tripType("round_trip") + .refundable(false) + .deliveryRecipient("jane.smith@example.com") + .ancillaries("extra_baggage") + .insurance(AirlineInsurance.builder() + .type("travel") + .company("Acme Insurance") + .price(AirlineInsurancePrice.builder().amount(25.5).currency(Currency.USD).build()) + .build()) + .build(); + + final String json = serializer.toJson(airline); + assertTrue(json.contains("\"total_number_of_passengers\":3")); + assertTrue(json.contains("\"travel_type\":\"international\"")); + assertTrue(json.contains("\"trip_type\":\"round_trip\"")); + assertTrue(json.contains("\"refundable\":false")); + assertTrue(json.contains("\"delivery_recipient\":\"jane.smith@example.com\"")); + assertTrue(json.contains("\"ancillaries\":\"extra_baggage\"")); + assertTrue(json.contains("\"insurance\"")); + + final AirlineData deserialized = serializer.fromJson(json, AirlineData.class); + assertEquals(3L, deserialized.getTotalNumberOfPassengers()); + assertEquals("international", deserialized.getTravelType()); + assertEquals("round_trip", deserialized.getTripType()); + assertEquals(false, deserialized.getRefundable()); + assertEquals("extra_baggage", deserialized.getAncillaries()); + assertNotNull(deserialized.getInsurance()); + assertEquals("Acme Insurance", deserialized.getInsurance().getCompany()); + assertEquals(Currency.USD, deserialized.getInsurance().getPrice().getCurrency()); + } +}