diff --git a/checkout_sdk/payments/setups/setups.py b/checkout_sdk/payments/setups/setups.py index 7f7746a8..ed4fd97b 100644 --- a/checkout_sdk/payments/setups/setups.py +++ b/checkout_sdk/payments/setups/setups.py @@ -4,7 +4,6 @@ from checkout_sdk.common.common import Address, Phone from checkout_sdk.common.enums import Currency -from checkout_sdk.payments.contexts.contexts import PaymentContextsTicket from checkout_sdk.payments.payments import PaymentType, ShippingDetails @@ -582,15 +581,263 @@ class Order: # Industry entities -class AirlineData: - ticket: PaymentContextsTicket - passengers: list # list of PaymentContextsPassenger - flight_leg_details: list # list of PaymentContextsFlightLegDetails + +class PaymentSetupAccommodationAddress: + """The accommodation's address.""" + # The first line of the address. + # [Optional] + address_line1: str + # The address city. + # [Optional] + city: str + # The address state or county. + # [Optional] + state: str + # The address country, in ISO 3166-1 alpha-2 format. + # [Optional] + country: str + # The zip code or postal code. + # [Optional] + zip: str + + +class PaymentSetupAccommodationGuest: + """A guest staying at the accommodation.""" + # The guest's first name. + # [Optional] + first_name: str + # The guest's last name. + # [Optional] + last_name: str + # The guest's date of birth. + # [Optional] + # format: date (YYYY-MM-DD) + date_of_birth: str + + +class PaymentSetupAccommodationRoom: + """A room booked by the customer.""" + # The rate or cost of the room per day. + # [Optional] + rate: float + # The number of nights the room is booked for. + # [Optional] + number_of_nights: int + # The room class or type booked. For example, `deluxe`. Free-form string, not an enum. + # [Optional] + type: str + + +class PaymentSetupAccommodationHost: + """Details about the host of the accommodation.""" + # The date the host registered. + # [Optional] + # format: date (YYYY-MM-DD) + registration_date: str + # The total number of reservations the host has received. + # [Optional] + total_reservation_count: int + + +class PaymentSetupAccommodation: + """Details about the accommodation booking. For lodging or cruise bookings.""" + # For lodging, contains the lodging name that appears on the storefront/customer receipts. + # For cruise, contains the ship name booked for the cruise. + # [Optional] + name: str + # A unique identifier for the booking. + # [Optional] + booking_reference: str + # For lodging bookings, the customer's check-in date. + # For cruise bookings, the cruise departure date (sail date). + # [Optional] + # format: date (YYYY-MM-DD) + check_in_date: str + # For lodging bookings, the customer's check-out date. + # For cruise bookings, the cruise return date. + # [Optional] + # format: date (YYYY-MM-DD) + check_out_date: str + # The accommodation's address. + # [Optional] + address: PaymentSetupAccommodationAddress + # The total number of rooms booked for the accommodation. + # [Optional] + number_of_rooms: int + # The list of guests staying at the accommodation. + # [Optional] + guests: list # list of PaymentSetupAccommodationGuest + # The list of rooms booked by the customer. + # [Optional] + room: list # list of PaymentSetupAccommodationRoom + # The total number of guests on the booking. + # [Optional] + total_number_of_guests: int + # Specifies whether the booking is refundable. + # [Optional] + refundable: bool + # The recipient the booking confirmation is delivered to. + # [Optional] + delivery_recipient: str + # Details about the host of the accommodation. + # [Optional] + host: PaymentSetupAccommodationHost + + +class PaymentSetupAirlineTicket: + """Details about the airline ticket.""" + # The ticket's unique identifier. + # [Optional] + number: str + # The date the airline ticket was issued. + # [Optional] + # format: date (YYYY-MM-DD) + issue_date: str + # The carrier code of the ticket issuer. + # [Optional] + issuing_carrier_code: str + # The travel package indicator, as one of the following codes: `A` (Airline flight + # reservation), `B` (Car rental and airline reservation), `C` (Car rental reservation), + # `N` (Unknown). Free-form string in the spec, not a typed enum. + # [Optional] + travel_package_indicator: str + # The name of the travel agency. + # [Optional] + travel_agency_name: str + # The IATA or ARC unique identifier for the travel agency that issues the ticket. + # [Optional] + travel_agency_code: str + + +class PaymentSetupAirlinePassengerAddress: + """Details about the passenger's address.""" + # The two-letter ISO country code of the passenger's country of residence. + # [Optional] + country: str + + +class PaymentSetupAirlinePassenger: + """A passenger on the flight.""" + # The passenger's first name. + # [Optional] + first_name: str + # The passenger's last name. + # [Optional] + last_name: str + # The passenger's date of birth. + # [Optional] + # format: date (YYYY-MM-DD) + date_of_birth: str + # Details about the passenger's address. + # [Optional] + address: PaymentSetupAirlinePassengerAddress + + +class PaymentSetupFlightLegDetails: + """A flight leg booked by the customer.""" + # The flight identifier. + # [Optional] + flight_number: str + # The IATA 2-letter accounting code (PAX) that identifies the carrier. Required if the + # airline data includes leg details. + # [Optional] + carrier_code: str + # A one-letter identifier for the travel class. For example: `F` (First class), + # `J` (Business class), `W` (Premium economy class), `Y` (Economy class). + # [Optional] + class_of_travelling: str + # The IATA three-letter airport code for the departure airport. Required if the + # airline data includes leg details. + # [Optional] + departure_airport: str + # The date of the scheduled take-off. + # [Optional] + # format: date (YYYY-MM-DD) + departure_date: str + # The time of the scheduled take-off. + # [Optional] + departure_time: str + # The IATA three-letter airport code for the destination airport. Required if the + # airline data includes leg details. + # [Optional] + arrival_airport: str + # A one-letter code that indicates whether the passenger is entitled to make a stopover. + # Specify `O` or a blank space if the passenger is entitled. Specify `X` if not entitled. + # [Optional] + stop_over_code: str + # The alphanumeric fare basis code. + # [Optional] + fare_basis_code: str + + +class PaymentSetupAirlineInsurancePrice: + """The price of the travel insurance.""" + # The insurance amount, in the minor currency unit. + # [Optional] + amount: float + # The currency of the insurance amount, as a three-letter ISO currency code. + # [Optional] + currency: str + + +class PaymentSetupAirlineInsurance: + """Details about the travel insurance purchased with the booking.""" + # The type of insurance. + # [Optional] + type: str + # The name of the insurance company. + # [Optional] + company: str + # The price of the insurance. + # [Optional] + price: PaymentSetupAirlineInsurancePrice + + +class PaymentSetupAirline: + """Details about the airline ticket and flights the customer booked.""" + # Details about the airline ticket. + # [Optional] + ticket: PaymentSetupAirlineTicket + # The list of passengers on the flight. + # [Optional] + passengers: list # list of PaymentSetupAirlinePassenger + # The list of flight legs booked by the customer. + # [Optional] + flight_leg_details: list # list of PaymentSetupFlightLegDetails + # The total number of passengers on the booking. + # [Optional] + total_number_of_passengers: int + # The type of travel. For example, `domestic` or `international`. Free-form string, + # not a typed enum. + # [Optional] + travel_type: str + # The type of trip. For example, `one_way` or `round_trip`. Free-form string, + # not a typed enum. + # [Optional] + trip_type: str + # Specifies whether the booking is refundable. + # [Optional] + refundable: bool + # The recipient the ticket is delivered to. + # [Optional] + delivery_recipient: str + # Any additional add-ons purchased with the booking. For example, `extra_baggage`. + # A single string per the spec, not an array, despite the plural name. + # [Optional] + ancillaries: str + # Details about the travel insurance purchased with the booking. + # [Optional] + insurance: PaymentSetupAirlineInsurance class Industry: - airline_data: AirlineData - accommodation_data: list # list of AccommodationData + """Industry-specific information.""" + # The list of airline bookings associated with the payment. + # [Optional] + airline: list # list of PaymentSetupAirline + # The list of accommodation bookings associated with the payment. + # [Optional] + accommodation: list # list of PaymentSetupAccommodation # Billing entity diff --git a/tests/payments/setups/payment_setups_serialization_test.py b/tests/payments/setups/payment_setups_serialization_test.py index b8cc0162..6e1cd860 100644 --- a/tests/payments/setups/payment_setups_serialization_test.py +++ b/tests/payments/setups/payment_setups_serialization_test.py @@ -14,6 +14,11 @@ CardPresentPin, PayByBank, PayByBankAction, PayByBankActionType, PayByBankBank, Stablecoin, PaymentSetupBillingDescriptor, PaymentSetupPresentmentDetails, PaymentSetupTerminal, PaymentSetupAmountAllocation, AmountAllocationCommission, Order, + Industry, PaymentSetupAccommodation, PaymentSetupAccommodationAddress, + PaymentSetupAccommodationGuest, PaymentSetupAccommodationRoom, PaymentSetupAccommodationHost, + PaymentSetupAirline, PaymentSetupAirlineTicket, PaymentSetupAirlinePassenger, + PaymentSetupAirlinePassengerAddress, PaymentSetupFlightLegDetails, + PaymentSetupAirlineInsurance, PaymentSetupAirlineInsurancePrice, ) @@ -364,3 +369,159 @@ def test_order_serializes_amount_allocations(self): 'commission': {'amount': 100, 'percentage': 2.5}, }] } + + # ── INT-1696 additions ─────────────────────────────────────────────────── + + def test_industry_accommodation_serializes_all_fields(self): + address = PaymentSetupAccommodationAddress() + address.address_line1 = '123 High Street' + address.city = 'London' + address.state = 'Greater London' + address.country = 'GB' + address.zip = 'NE1 1CK' + + guest = PaymentSetupAccommodationGuest() + guest.first_name = 'John' + guest.last_name = 'Smith' + guest.date_of_birth = '1970-03-19' + + room = PaymentSetupAccommodationRoom() + room.rate = 42.3 + room.number_of_nights = 5 + room.type = 'deluxe' + + host = PaymentSetupAccommodationHost() + host.registration_date = '2020-01-01' + host.total_reservation_count = 150 + + accommodation = PaymentSetupAccommodation() + accommodation.name = 'Checkout Lodge' + accommodation.booking_reference = 'REF9083748' + accommodation.check_in_date = '2025-04-11' + accommodation.check_out_date = '2025-04-18' + accommodation.address = address + accommodation.number_of_rooms = 2 + accommodation.guests = [guest] + accommodation.room = [room] + accommodation.total_number_of_guests = 2 + accommodation.refundable = True + accommodation.delivery_recipient = 'jane.smith@example.com' + accommodation.host = host + + industry = Industry() + industry.accommodation = [accommodation] + + assert _serialize(industry) == { + 'accommodation': [{ + 'name': 'Checkout Lodge', + 'booking_reference': 'REF9083748', + 'check_in_date': '2025-04-11', + 'check_out_date': '2025-04-18', + 'address': { + 'address_line1': '123 High Street', + 'city': 'London', + 'state': 'Greater London', + 'country': 'GB', + 'zip': 'NE1 1CK', + }, + 'number_of_rooms': 2, + 'guests': [{'first_name': 'John', 'last_name': 'Smith', 'date_of_birth': '1970-03-19'}], + 'room': [{'rate': 42.3, 'number_of_nights': 5, 'type': 'deluxe'}], + 'total_number_of_guests': 2, + 'refundable': True, + 'delivery_recipient': 'jane.smith@example.com', + 'host': {'registration_date': '2020-01-01', 'total_reservation_count': 150}, + }] + } + + def test_industry_airline_serializes_all_fields(self): + ticket = PaymentSetupAirlineTicket() + ticket.number = '0742464639523' + ticket.issue_date = '2025-05-01' + ticket.issuing_carrier_code = '042' + ticket.travel_package_indicator = 'A' + ticket.travel_agency_name = 'Checkout Travel Agents' + ticket.travel_agency_code = '91114362' + + passenger_address = PaymentSetupAirlinePassengerAddress() + passenger_address.country = 'GB' + passenger = PaymentSetupAirlinePassenger() + passenger.first_name = 'John' + passenger.last_name = 'Smith' + passenger.date_of_birth = '1990-10-31' + passenger.address = passenger_address + + leg = PaymentSetupFlightLegDetails() + leg.flight_number = 'BA1483' + leg.carrier_code = 'BA' + leg.class_of_travelling = 'W' + leg.departure_airport = 'LHR' + leg.departure_date = '2025-10-13' + leg.departure_time = '18:30' + leg.arrival_airport = 'JFK' + leg.stop_over_code = 'X' + leg.fare_basis_code = 'WUP14B' + + price = PaymentSetupAirlineInsurancePrice() + price.amount = 500 + price.currency = 'SAR' + insurance = PaymentSetupAirlineInsurance() + insurance.type = 'travel' + insurance.company = 'AXA' + insurance.price = price + + airline = PaymentSetupAirline() + airline.ticket = ticket + airline.passengers = [passenger] + airline.flight_leg_details = [leg] + airline.total_number_of_passengers = 1 + airline.travel_type = 'international' + airline.trip_type = 'one_way' + airline.refundable = True + airline.delivery_recipient = 'jane.smith@example.com' + airline.ancillaries = 'extra_baggage' + airline.insurance = insurance + + industry = Industry() + industry.airline = [airline] + + assert _serialize(industry) == { + 'airline': [{ + 'ticket': { + 'number': '0742464639523', + 'issue_date': '2025-05-01', + 'issuing_carrier_code': '042', + 'travel_package_indicator': 'A', + 'travel_agency_name': 'Checkout Travel Agents', + 'travel_agency_code': '91114362', + }, + 'passengers': [{ + 'first_name': 'John', + 'last_name': 'Smith', + 'date_of_birth': '1990-10-31', + 'address': {'country': 'GB'}, + }], + 'flight_leg_details': [{ + 'flight_number': 'BA1483', + 'carrier_code': 'BA', + 'class_of_travelling': 'W', + 'departure_airport': 'LHR', + 'departure_date': '2025-10-13', + 'departure_time': '18:30', + 'arrival_airport': 'JFK', + 'stop_over_code': 'X', + 'fare_basis_code': 'WUP14B', + }], + 'total_number_of_passengers': 1, + 'travel_type': 'international', + 'trip_type': 'one_way', + 'refundable': True, + 'delivery_recipient': 'jane.smith@example.com', + 'ancillaries': 'extra_baggage', + 'insurance': { + 'type': 'travel', + 'company': 'AXA', + 'price': {'amount': 500, 'currency': 'SAR'}, + }, + }] + }