Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve deserialisation and payment contexts updates #450

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions src/main/java/com/checkout/GsonSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -273,27 +273,48 @@ private static JsonDeserializer<GetScheduleResponse> getScheduleResponseDeserial

private static JsonDeserializer<Instant> getInstantJsonDeserializer() {
return (json, typeOfT, context) -> {
final String dateString = json.getAsString();
String dateString;

// Check if the JSON is a number or a string
if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isNumber()) {
dateString = String.valueOf(json.getAsLong()); // Convert numeric value to string
} else {
dateString = json.getAsString(); // Use the string value directly
}

try {
// Try parsing the string as an ISO-8601 Instant
return Instant.parse(dateString);
} catch (final DateTimeParseException ex) {
if (dateString.length() == 8) {
if (dateString.matches("\\d{8}")) { // Handle numeric format yyyyMMdd
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDateTime dateTime = LocalDate.parse(dateString, formatter).atStartOfDay();
return dateTime.toInstant(ZoneOffset.UTC);
} catch (final DateTimeParseException e) {
throw new JsonParseException("Failed to parse date in format yyyyMMdd: " + dateString, e);
throw new JsonParseException("Failed to parse numeric date in format yyyyMMdd: " + dateString, e);
}
}
// Explicitly handle the yyyy-MM-dd format
if (dateString.length() == 10) { // Handle format yyyy-MM-dd
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(dateString, formatter);
return date.atStartOfDay().toInstant(ZoneOffset.UTC);
} catch (final DateTimeParseException e) {
throw new JsonParseException("Failed to parse date in format yyyy-MM-dd: " + dateString, e);
}
}
// Attempt parsing with the DEFAULT_FORMATTERS
for (final DateTimeFormatter formatter : DEFAULT_FORMATTERS) {
try {
final LocalDateTime dateTime = LocalDateTime.parse(dateString, formatter);
return dateTime.toInstant(ZoneOffset.UTC);
} catch (final DateTimeParseException ignored) {
// continue to next formatter
// Continue with the next formatter
}
}
// Rethrow the original exception if no format matches
throw ex;
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/checkout/payments/AirlineData.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public final class AirlineData {

private Ticket ticket;

private Passenger passenger;
private List<Passenger> passenger;

@SerializedName("flight_leg_details")
private List<FlightLegDetails> flightLegDetails;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
@AllArgsConstructor
public final class PaymentContextsAirlineData {

private List<PaymentContextsTicket> ticket;
private PaymentContextsTicket ticket;

private List<PaymentContextsPassenger> passenger;

Expand Down
34 changes: 34 additions & 0 deletions src/test/java/com/checkout/GsonSerializerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import com.checkout.payments.previous.response.PaymentResponse;
import com.checkout.payments.previous.response.destination.PaymentResponseAlternativeDestination;
import com.checkout.payments.previous.response.destination.PaymentResponseCardDestination;
import com.checkout.payments.response.source.AlternativePaymentSourceResponse;
import com.checkout.payments.response.source.contexts.PaymentContextsKlarnaResponseSource;
import com.checkout.payments.response.source.contexts.PaymentContextsPayPalResponseSource;
import com.checkout.payments.sender.PaymentCorporateSender;
import com.checkout.payments.sender.ResponseAlternativeSender;
Expand Down Expand Up @@ -121,6 +123,16 @@ void shouldGetFinancial() {
assertNotNull(actionsQueryResponse.getData().get(0).getRequestedOn());
}

@Test
void shouldSerializePaymentKlarnaDetailsResponseFromJson() {

final com.checkout.payments.response.GetPaymentResponse paymentKlarnaResponseSource = serializer.fromJson(getMock("/mocks/payments/response/get_payment_klarna_response.json"), com.checkout.payments.response.GetPaymentResponse.class);

assertNotNull(paymentKlarnaResponseSource);
assertTrue(paymentKlarnaResponseSource.getSource() instanceof AlternativePaymentSourceResponse);
assertEquals(PaymentSourceType.KLARNA, paymentKlarnaResponseSource.getSource().getType());
}

@Test
void shouldSerializePaymentContextsPayPalDetailsResponseFromJson() {

Expand All @@ -131,6 +143,16 @@ void shouldSerializePaymentContextsPayPalDetailsResponseFromJson() {
assertEquals(PaymentSourceType.PAYPAL, paymentContextsPayPalResponseSource.getPaymentRequest().getSource().getType());
}

@Test
void shouldSerializePaymentContextsKlarnaDetailsResponseFromJson() {

final PaymentContextDetailsResponse paymentKlarnaResponseSource = serializer.fromJson(getMock("/mocks/payments/response/contexts/payment_context_klarna_details_response.json"), PaymentContextDetailsResponse.class);

assertNotNull(paymentKlarnaResponseSource);
assertTrue(paymentKlarnaResponseSource.getPaymentRequest().getSource() instanceof PaymentContextsKlarnaResponseSource);
assertEquals(PaymentSourceType.KLARNA, paymentKlarnaResponseSource.getPaymentRequest().getSource().getType());
}

@Test
void shouldSerializePaymentContextsResponseFromJson() {

Expand Down Expand Up @@ -160,6 +182,18 @@ void shouldDeserializeMultipleDateFormats() {
assertNotNull(paymentResponse.getProcessedOn());
assertEquals(instant, paymentResponse.getProcessedOn());

// Test format yyyyMMdd number
paymentResponse = serializer.fromJson("{\"processed_on\":20210608}", PaymentResponse.class);
assertNotNull(paymentResponse);
assertNotNull(paymentResponse.getProcessedOn());
assertEquals(instant, paymentResponse.getProcessedOn());

// Test format yyyy-MM-dd
paymentResponse = serializer.fromJson("{\"processed_on\":\"2021-06-08\"}", PaymentResponse.class);
assertNotNull(paymentResponse);
assertNotNull(paymentResponse.getProcessedOn());
assertEquals(instant, paymentResponse.getProcessedOn());

// Test other valid formats
paymentResponse = serializer.fromJson("{\"processed_on\":\"2021-06-08T12:25:01.000Z\"}", PaymentResponse.class);
assertNotNull(paymentResponse);
Expand Down
29 changes: 0 additions & 29 deletions src/test/java/com/checkout/payments/CaptureTestIT.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.checkout.payments;

import com.checkout.TestHelper;
import com.checkout.common.CountryCode;
import com.checkout.payments.request.PaymentCustomerRequest;
import com.checkout.payments.response.PaymentResponse;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -81,34 +80,6 @@ void shouldCaptureCardPayment() {
.locale("en-US")
.shippingPreference(ShippingPreference.SET_PROVIDED_ADDRESS)
.userAction(UserAction.PAY_NOW)
.airlineData(Collections.singletonList(AirlineData.builder()
.ticket(Ticket.builder()
.number("123654")
.issueDate("stringstri")
.issuingCarrierCode("st")
.travelAgencyName("agency")
.travelAgencyCode("code")
.build())
.passenger(Passenger.builder()
.name(PassengerName.builder()
.fullName("a passenger")
.build())
.dateOfBirth("birth")
.countryCode(CountryCode.ES)
.build())
.flightLegDetails(Collections.singletonList(FlightLegDetails.builder()
.flightNumber(0L)
.carrierCode("carrier")
.serviceClass("service")
.departureDate("date")
.departureTime("time")
.departureAirport("airport")
.arrivalAirport("arrival")
.stopoverCode("stopover")
.fareBasisCode("fare")
.build()))

.build()))
.lineOfBusiness("Flights")
.build())
.metadata(metadata)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
{
"status": "Approved",
"payment_request": {
"source": {
"type": "klarna"
},
"amount": 6540,
"currency": "USD",
"payment_type": "Recurring",
"capture": true,
"customer": {
"email": "[email protected]",
"name": "John Smith"
},
"shipping": {
"first_name": "John",
"last_name": "Smith",
"email": "[email protected]",
"address": {
"address_line1": "123 High St.",
"address_line2": "Flat 456",
"city": "London",
"state": "str",
"zip": "SW1A 1AA",
"country": "GB"
},
"phone": {
"country_code": "+1",
"number": "415 555 2671"
},
"from_address_zip": "123456",
"timeframe": "SameDay",
"method": "BillingAddress",
"delay": 5
},
"processing": {
"plan": {
"type": "MERCHANT_INITIATED_BILLING",
"skip_shipping_address": true,
"immutable_shipping_address": true
},
"discount_amount": 5,
"shipping_amount": 300,
"tax_amount": 3000,
"invoice_id": "string",
"brand_name": "Acme Corporation",
"locale": "en-US",
"shipping_preference": "set_provided_address",
"user_action": "pay_now",
"partner_customer_risk_data": {
"key": "string",
"value": "string"
},
"custom_payment_method_ids": [
"string"
],
"airline_data": [
{
"ticket": {
"number": "045-21351455613",
"issue_date": "2023-05-20",
"issuing_carrier_code": "AI",
"travel_package_indicator": "B",
"travel_agency_name": "World Tours",
"travel_agency_code": "01"
},
"passenger": [
{
"first_name": "John",
"last_name": "White",
"date_of_birth": "1990-05-26",
"address": {
"country": null
}
}
],
"flight_leg_details": [
{
"flight_number": "101",
"carrier_code": "BA",
"class_of_travelling": "J",
"departure_airport": "LHR",
"departure_date": "2023-06-19",
"departure_time": "15:30",
"arrival_airport": "LAX",
"stop_over_code": "x",
"fare_basis_code": "SPRSVR"
}
]
}
],
"accommodation_data": [
{
"name": "The Sea View Hotel",
"booking_reference": "HOTEL123",
"check_in_date": "2023-06-20",
"check_out_date": "2023-06-23",
"address": {
"address_line1": "123 Beach Road",
"zip": 10001
},
"state": "FL",
"country": "USA",
"city": "Los Angeles",
"number_of_rooms": 2,
"guests": [
{
"first_name": "Jane",
"last_name": "Doe",
"date_of_birth": "1985-07-14"
}
],
"room": [
{
"rate": "70",
"number_of_nights_at_room_rate": "3"
}
]
}
]
},
"processing_channel_id": "pc_q4dbxom5jbgudnjzjpz7j2z6uq",
"reference": "ORD-5023-4E89",
"description": "Set of 3 masks",
"success_url": "https://example.com/payments/success",
"failure_url": "https://example.com/payments/fail",
"items": [
{
"type": "string",
"name": "Test item",
"quantity": 2,
"unit_price": 50,
"reference": "858818ac",
"total_amount": 29000,
"tax_rate": 2000,
"tax_amount": 1000,
"discount_amount": 1000,
"url": "string",
"image_url": "string"
}
]
},
"partner_metadata": {
"order_id": "test_order_123",
"customer_id": "cus_123",
"session_id": "feb0096d-8486-400d-89fa-2fa716b4521f",
"client_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyMzA1ZWJjLWI4MTEtMzYzNy1hYTRjLTY2ZWNhMTg3NGYzZCJ9.eyJzZXNzaW9uX2lkIjoiY2RiNDI0MGQtMTdkYy01MjJkLWJlYjEtYjAyYjRlMzllYTdhIiwiYmFzZV91cmwiOiJodHRwczovL2pzLnBsYXlncm91bmQua2xhcm5hLmNvbS9ldS9rcCIsImRlc2lnbiI6ImtsYXJuYSIsImxhbmd1YWdlIjoiZW4iLCJwdXJjaGFzZV9jb3VudHJ5IjoiREUiLCJlbnZpcm9ubWVudCI6InBsYXlncm91bmQiLCJtZXJjaGFudF9uYW1lIjoiQ2hlY2tvdXQuY29tIiwic2Vzc2lvbl90eXBlIjoiUEFZTUVOVFMiLCJjbGllbnRfZXZlbnRfYmFzZV91cmwiOiJodHRwczovL2V1LnBsYXlncm91bmQua2xhcm5hZXZ0LmNvbSIsInNjaGVtZSI6dHJ1ZSwiZXhwZXJpbWVudHMiOlt7Im5hbWUiOiJrcC1jbGllbnQtb25lLXB1cmNoYXNlLWZsb3ciLCJ2YXJpYXRlIjoidmFyaWF0ZS0xIn0seyJuYW1lIjoia3AtY2xpZW50LXV0b3BpYS1mbG93IiwidmFyaWF0ZSI6InZhcmlhdGUtMSJ9LHsibmFtZSI6ImtwYy0xay1zZXJ2aWNlIiwidmFyaWF0ZSI6InZhcmlhdGUtMSJ9LHsibmFtZSI6ImtwLWNsaWVudC11dG9waWEtc3RhdGljLXdpZGdldCIsInZhcmlhdGUiOiJpbmRleCIsInBhcmFtZXRlcnMiOnsiZHluYW1pYyI6InRydWUifX0seyJuYW1lIjoiaW4tYXBwLXNkay1uZXctaW50ZXJuYWwtYnJvd3NlciIsInBhcmFtZXRlcnMiOnsidmFyaWF0ZV9pZCI6Im5ldy1pbnRlcm5hbC1icm93c2VyLWVuYWJsZSJ9fSx7Im5hbWUiOiJrcC1jbGllbnQtdXRvcGlhLXNkay1mbG93IiwidmFyaWF0ZSI6InZhcmlhdGUtMSJ9LHsibmFtZSI6ImtwLWNsaWVudC11dG9waWEtd2Vidmlldy1mbG93IiwidmFyaWF0ZSI6InZhcmlhdGUtMSJ9LHsibmFtZSI6ImluLWFwcC1zZGstY2FyZC1zY2FubmluZyIsInBhcmFtZXRlcnMiOnsidmFyaWF0ZV9pZCI6ImNhcmQtc2Nhbm5pbmctZW5hYmxlIn19XSwicmVnaW9uIjoiZXUiLCJvcmRlcl9hbW91bnQiOjIwMDAsIm9mZmVyaW5nX29wdHMiOjAsIm9vIjoiY3ciLCJ2ZXJzaW9uIjoidjEuMTAuMC0xNTkwLWczZWJjMzkwNyJ9.ZxThQVo5e0Fck1vwOQ-WWAr7Zw-dsiEhjd3CW-E5p8atUhnBhrzIs6WCBeOOv3ci2VvpykVnDw2qycCTA-7TpTq1wn_kPxxixDQnYJYJzrZMoUhPLZo5pfy2a1S_t2owEQks0THRzu2wRlZwCN4ewDnVqsut60X4r3B92cJENIDtEC4Fs55CjFEmYUtsLXSspNLKvpZe2zg4O_M5yjH7XsBs5YJalZLexf4545G5vJrmVHgiA322mbgWC7BH0IU1A-ql5sFvs190_cjGaLsAvrkh9CVczUPy3X-jr3A5z1YRVfywPpvwKFWL8GZtgrdUZmgiU5_8ZZLkEPzrSucHrQ"
}
}
Loading
Loading