diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/order/OrderSubMerchant.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/order/OrderSubMerchant.java index 7a19421e1..7b982ebab 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/order/OrderSubMerchant.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/order/OrderSubMerchant.java @@ -17,19 +17,22 @@ public final class OrderSubMerchant { /** - * The unique identifier of the sub-merchant + * The unique identifier for the sub-merchant. + * [Optional] */ private String id; /** - * The category of products or services offered by the sub-merchant + * The product category for the sub-merchant. + * [Optional] */ private String productCategory; /** - * The number of trades or transactions the sub-merchant has conducted + * The number of orders the sub-merchant has processed. + * [Optional] */ - private Integer numberOfTrades; + private Integer numberOfSales; /** * The date the sub-merchant was registered. @@ -37,4 +40,4 @@ public final class OrderSubMerchant { * Format: yyyy-MM-dd */ private LocalDate registrationDate; -} \ No newline at end of file +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/PaymentMethods.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/PaymentMethods.java index 51466d726..a7eb5b070 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/PaymentMethods.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/PaymentMethods.java @@ -58,6 +58,12 @@ @AllArgsConstructor public final class PaymentMethods { + /** + * The payment method that automatically confirms the payment when it is ready, for example "card". + * [Optional] + */ + private String autoConfirm; + /** * Instrument (saved payment source) configuration. * [Optional] diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/applepay/ApplePay.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/applepay/ApplePay.java index 82b2195bd..4061a735b 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/applepay/ApplePay.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/applepay/ApplePay.java @@ -3,10 +3,13 @@ import com.checkout.common.Phone; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.CardAccountHolder; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodBase; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentSetupScheme; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.WalletToken; +import com.checkout.payments.PreferredSchema; import lombok.Data; import lombok.EqualsAndHashCode; -import java.time.Instant; +import java.util.List; /** * The Apple Pay payment method's details and configuration. @@ -17,26 +20,20 @@ public final class ApplePay extends PaymentMethodBase { /** * The Apple Pay encrypted payment token data. - * [Optional] + * [Optional] writeOnly */ private ApplePayTokenData tokenData; /** - * The Apple Pay payment token string. + * The Checkout.com Apple Pay token. * [Optional] */ - private String token; + private WalletToken token; /** - * The date and time the token expires. - * [Optional] - * Format: date-time (ISO 8601) - */ - private Instant expiresOn; - - /** - * Set to true if you intend to reuse the payment credentials in subsequent payments. - * [Optional] + * Set to true if you intend to reuse the payment credentials in subsequent payments. If set to + * false, a payment instrument will not be included in the payment response. + * [Optional] writeOnly */ private Boolean storeForFutureUse; @@ -51,4 +48,16 @@ public final class ApplePay extends PaymentMethodBase { * [Optional] */ private CardAccountHolder accountHolder; + + /** + * The list of supported card scheme names. + * [Optional] readOnly + */ + private List schemes; + + /** + * The preferred card scheme to process the payment. + * [Optional] + */ + private PreferredSchema preferredScheme; } diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/Card.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/Card.java index d2956f63a..265f866bb 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/Card.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/Card.java @@ -3,82 +3,37 @@ import com.checkout.common.Phone; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.CardAccountHolder; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodBase; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentSetupScheme; +import com.checkout.payments.PreferredSchema; import lombok.Data; import lombok.EqualsAndHashCode; -import java.time.Instant; +import java.util.List; /** - * The Card payment method's details and configuration. + * The Card payment method's details and configuration. The card data lives under + * {@link #getDetails()}, which maps {@code card.details}. */ @Data @EqualsAndHashCode(callSuper = true) public final class Card extends PaymentMethodBase { /** - * The card number (without separators). Write-only. + * The Checkout.com card token to use instead of the raw card details. * [Optional] */ - private String number; + private CardToken token; /** - * The last four digits of the card number. Read-only. + * The card details. * [Optional] */ - private String last4; + private CardDetails details; /** - * The card issuer's Bank Identification Number (BIN). Read-only. - * [Optional] - */ - private String bin; - - /** - * The card scheme. Read-only. - * [Optional] - */ - private String scheme; - - /** - * The expiry month of the card. - * [Optional] - */ - private Integer expiryMonth; - - /** - * The expiry year of the card. - * [Optional] - */ - private Integer expiryYear; - - /** - * The cardholder's name. Write-only. - * [Optional] - */ - private String name; - - /** - * The card verification value/code. 3 digits, except for American Express (4 digits). Write-only. - * [Optional] - */ - private String cvv; - - /** - * Set to true for payments that use stored card details. Write-only. - * [Optional] - */ - private Boolean stored; - - /** - * The time by which the card details must be confirmed. Read-only. - * [Optional] - * Format: date-time (ISO 8601) - */ - private Instant expiresOn; - - /** - * Set to true if you intend to reuse the payment credentials in subsequent payments. Write-only. - * [Optional] + * Set to true if you intend to reuse the payment credentials in subsequent payments. If set to + * false, a payment instrument will not be included in the payment response. + * [Optional] writeOnly */ private Boolean storeForFutureUse; @@ -95,8 +50,14 @@ public final class Card extends PaymentMethodBase { private CardAccountHolder accountHolder; /** - * Indicates whether to use the Real-Time Account Updater to update the card information. + * The list of supported card scheme names. + * [Optional] readOnly + */ + private List schemes; + + /** + * The preferred card scheme to process the payment. * [Optional] */ - private Boolean allowUpdate; + private PreferredSchema preferredScheme; } diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/CardDetails.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/CardDetails.java new file mode 100644 index 000000000..74ab53f80 --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/CardDetails.java @@ -0,0 +1,87 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.card; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +/** + * The card details of the card payment method. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class CardDetails { + + /** + * The card number (without separators). + * [Optional] writeOnly + */ + private String number; + + /** + * The last four digits of the card number. + * [Optional] readOnly + */ + private String last4; + + /** + * The card issuer's Bank Identification Number (BIN). + * [Optional] readOnly + */ + private String bin; + + /** + * The card scheme. + * [Optional] readOnly + */ + private String scheme; + + /** + * The expiry month of the card. + * [Optional] + */ + private Integer expiryMonth; + + /** + * The expiry year of the card. + * [Optional] + */ + private Integer expiryYear; + + /** + * The cardholder's name. + * [Optional] writeOnly + */ + private String name; + + /** + * The card verification value/code. 3 digits, except for American Express (4 digits). + * [Optional] writeOnly + */ + private String cvv; + + /** + * Indicates whether this card is being submitted from your own stored card-on-file system, rather + * than being entered by the customer at the time of payment. Set to true when you provide card + * details that you already have on file for the customer. + * [Optional] writeOnly + */ + private Boolean stored; + + /** + * The time by which the card details must be confirmed. + * [Optional] readOnly + * Format: date-time (ISO 8601) + */ + private Instant expiresOn; + + /** + * Indicates whether to use the Real-Time Account Updater to update the card information. + * [Optional] + */ + private Boolean allowUpdate; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/CardToken.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/CardToken.java new file mode 100644 index 000000000..248bc0775 --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/card/CardToken.java @@ -0,0 +1,22 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.card; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The Checkout.com card token to use for the card payment method. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class CardToken { + + /** + * The Checkout.com card token identifier. + * [Optional] + */ + private String id; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/CardAccountHolder.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/CardAccountHolder.java index 3f9c1669b..01837d0e4 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/CardAccountHolder.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/CardAccountHolder.java @@ -39,7 +39,14 @@ public final class CardAccountHolder { private String middleName; /** - * Indicates whether the ANI (account name inquiry) check is performed with the card scheme. + * The card account holder's company name. + * [Optional] + */ + private String companyName; + + /** + * Indicates whether the ANI (account name inquiry) check is performed with the card scheme + * (Visa or Mastercard). * [Optional] */ private Boolean accountNameInquiry; diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/PaymentMethodStatus.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/PaymentMethodStatus.java index f4d597f81..e4dd4ac61 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/PaymentMethodStatus.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/PaymentMethodStatus.java @@ -22,7 +22,12 @@ public enum PaymentMethodStatus { /** * Not defined by the current API specification. Kept for backward compatibility. + * + * @deprecated not part of the PaymentMethodStatus enum in the API specification, which lists + * unavailable, action_required, ready, initialization_required and invalid. It will be + * removed in the next major version. */ + @Deprecated @SerializedName("pending") PENDING, @@ -34,7 +39,12 @@ public enum PaymentMethodStatus { /** * Not defined by the current API specification. Kept for backward compatibility. + * + * @deprecated not part of the PaymentMethodStatus enum in the API specification, which lists + * unavailable, action_required, ready, initialization_required and invalid. It will be + * removed in the next major version. */ + @Deprecated @SerializedName("available") AVAILABLE, diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/PaymentSetupScheme.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/PaymentSetupScheme.java new file mode 100644 index 000000000..d759ccc00 --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/PaymentSetupScheme.java @@ -0,0 +1,62 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common; + +import com.google.gson.annotations.SerializedName; + +/** + * A card scheme name, as listed in the schemes of a Payment Setup card, Apple Pay or Google Pay + * payment method. + */ +public enum PaymentSetupScheme { + + @SerializedName("accel") + ACCEL, + + @SerializedName("amex") + AMEX, + + @SerializedName("cartes_bancaires") + CARTES_BANCAIRES, + + @SerializedName("china_union_pay") + CHINA_UNION_PAY, + + @SerializedName("diners") + DINERS, + + @SerializedName("discover") + DISCOVER, + + @SerializedName("jcb") + JCB, + + @SerializedName("mada") + MADA, + + @SerializedName("maestro") + MAESTRO, + + @SerializedName("mastercard") + MASTERCARD, + + @SerializedName("nyce") + NYCE, + + @SerializedName("omannet") + OMANNET, + + @SerializedName("pulse") + PULSE, + + @SerializedName("shazam") + SHAZAM, + + @SerializedName("star") + STAR, + + @SerializedName("upi") + UPI, + + @SerializedName("visa") + VISA + +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/WalletToken.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/WalletToken.java new file mode 100644 index 000000000..767f1f0a3 --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/common/WalletToken.java @@ -0,0 +1,32 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; + +/** + * The Checkout.com token of an Apple Pay or Google Pay payment method. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class WalletToken { + + /** + * The token identifier, prefixed by tok_. + * [Required] + * Pattern: ^(tok)_(\w{26})$ + */ + private String id; + + /** + * The date and time the token expires. + * [Optional] + * Format: date-time (ISO 8601) + */ + private Instant expiresOn; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/googlepay/GooglePay.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/googlepay/GooglePay.java index abdc97375..f4af0aa36 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/googlepay/GooglePay.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/googlepay/GooglePay.java @@ -3,10 +3,12 @@ import com.checkout.common.Phone; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.CardAccountHolder; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodBase; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentSetupScheme; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.WalletToken; import lombok.Data; import lombok.EqualsAndHashCode; -import java.time.Instant; +import java.util.List; /** * The Google Pay payment method's details and configuration. @@ -17,26 +19,20 @@ public final class GooglePay extends PaymentMethodBase { /** * The Google Pay encrypted payment token data. - * [Optional] + * [Optional] writeOnly */ private GooglePayTokenData tokenData; /** - * The Google Pay payment token string. + * The Checkout.com Google Pay token. * [Optional] */ - private String token; + private WalletToken token; /** - * The date and time the token expires. - * [Optional] - * Format: date-time (ISO 8601) - */ - private Instant expiresOn; - - /** - * Set to true if you intend to reuse the payment credentials in subsequent payments. - * [Optional] + * Set to true if you intend to reuse the payment credentials in subsequent payments. If set to + * false, a payment instrument will not be included in the payment response. + * [Optional] writeOnly */ private Boolean storeForFutureUse; @@ -51,4 +47,16 @@ public final class GooglePay extends PaymentMethodBase { * [Optional] */ private CardAccountHolder accountHolder; + + /** + * The list of supported card scheme names. + * [Optional] readOnly + */ + private List schemes; + + /** + * The preferred card scheme to process the payment. + * [Optional] + */ + private GooglePayPreferredScheme preferredScheme; } diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/googlepay/GooglePayPreferredScheme.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/googlepay/GooglePayPreferredScheme.java new file mode 100644 index 000000000..e0fce9c7a --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/googlepay/GooglePayPreferredScheme.java @@ -0,0 +1,15 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.googlepay; + +import com.google.gson.annotations.SerializedName; + +/** + * A card scheme Google Pay accepts as the preferred scheme. + */ +public enum GooglePayPreferredScheme { + + @SerializedName("visa") + VISA, + + @SerializedName("mastercard") + MASTERCARD +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/instrument/Instrument.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/instrument/Instrument.java index d04e0c15e..332a3ab69 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/instrument/Instrument.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/instrument/Instrument.java @@ -18,9 +18,15 @@ public final class Instrument extends PaymentMethodBase { */ private String id; + /** + * The type of the instrument, for example "bacs". + * [Optional] readOnly + */ + private String type; + /** * The next available action for the payment method. - * [Optional] + * [Optional] readOnly */ private PaymentMethodAction action; } diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/klarna/Klarna.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/klarna/Klarna.java index 900b9d272..f44e753e0 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/klarna/Klarna.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/klarna/Klarna.java @@ -14,14 +14,16 @@ public final class Klarna extends PaymentMethodBase { /** - * The initialization state of the payment method. Defaults to disabled. + * The initialization state of the payment method. When you create a Payment Setup, this + * defaults to disabled. * [Optional] + * Default: disabled */ private PaymentMethodInitialization initialization = PaymentMethodInitialization.DISABLED; /** - * The next available action. Contains type, client_token, and session_id. - * [Optional] + * The next available action for the payment method. Contains type, client_token, and session_id. + * [Optional] readOnly */ private PaymentMethodAction action; @@ -30,4 +32,10 @@ public final class Klarna extends PaymentMethodBase { * [Optional] readOnly */ private KlarnaAccountHolder accountHolder; + + /** + * The IBAN of the account used to complete Klarna verification. + * [Optional] readOnly + */ + private String iban; } diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/settings/Settings.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/settings/Settings.java index d367c16e2..3a31ea0dd 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/settings/Settings.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/settings/Settings.java @@ -5,8 +5,10 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.util.List; + /** - * Payment setup configuration settings + * Settings for the Payment Setup. */ @Data @Builder @@ -15,16 +17,33 @@ public final class Settings { /** - * <= 255 characters * The URL to redirect the customer to, if the payment is successful. * For payment methods with a redirect, this value overrides the default success redirect URL configured on your account. + * [Optional] + * Format: uri + * max 255 characters */ private String successUrl; /** - * <= 255 characters * The URL to redirect the customer to, if the payment is unsuccessful. - * For payment methods with a redirect, this value overrides the default failure redirect URL configured on your account. + * For payment methods with a redirect, this value overrides the default failure redirect URL configured on your account. + * [Optional] + * Format: uri + * max 255 characters */ private String failureUrl; -} \ No newline at end of file + + /** + * Indicates whether to capture the payment immediately. + * [Optional] + * Default: true + */ + private Boolean capture; + + /** + * The list of payment methods excluded from the Payment Setup, for example "card", "klarna" or "stcpay". + * [Optional] + */ + private List excludedPaymentMethods; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java index d9c73db1b..1efaaeec1 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java @@ -18,6 +18,8 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.util.Map; + /** * Payment setup request */ @@ -58,12 +60,14 @@ public final class PaymentSetupsRequest { /** * A reference you can use to identify the payment. For example, an order number * [Optional] + * max 80 */ private String reference; /** * A description of the payment setup * [Optional] + * max 100 */ private String description; @@ -133,4 +137,13 @@ public final class PaymentSetupsRequest { * [Optional] */ private PaymentSetupRisk risk; -} \ No newline at end of file + + /** + * A set of key/value pairs you can attach to a payment. It can be useful for storing additional + * information about the payment. + * Each value is a string (max 255 characters), a number or a boolean. + * [Optional] + * max 20 properties + */ + private Map metadata; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java index 1b6726380..e589a4579 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java @@ -15,6 +15,7 @@ import com.checkout.payments.PaymentType; import java.util.List; +import java.util.Map; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -33,7 +34,7 @@ public final class PaymentSetupsConfirmResponse extends Resource { /** * The unique identifier of the payment setup. - * [Optional] + * [Optional] readOnly */ private String id; @@ -66,12 +67,14 @@ public final class PaymentSetupsConfirmResponse extends Resource { /** * A reference you can use to identify the payment setup, such as an order number. * [Optional] + * max 80 */ private String reference; /** * A description of the payment setup. * [Optional] + * max 100 */ private String description; @@ -114,7 +117,7 @@ public final class PaymentSetupsConfirmResponse extends Resource { /** * An ordered list of available payment method names. The order indicates the recommended * presentation priority, with the first item being the highest priority. - * [Optional] + * [Optional] readOnly */ private List availablePaymentMethods; @@ -149,4 +152,13 @@ public final class PaymentSetupsConfirmResponse extends Resource { * [Optional] */ private PaymentSetupTerminal terminal; + + /** + * A set of key/value pairs you can attach to a payment. It can be useful for storing additional + * information about the payment. + * Each value is a string (max 255 characters), a number or a boolean. + * [Optional] + * max 20 properties + */ + private Map metadata; } diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java index fd8e4cf85..9df38f19f 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java @@ -16,6 +16,7 @@ import com.checkout.payments.PaymentType; import java.util.List; +import java.util.Map; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -34,7 +35,7 @@ public final class PaymentSetupsResponse extends Resource { /** * The unique identifier of the payment setup. - * [Optional] + * [Optional] readOnly */ private String id; @@ -67,12 +68,14 @@ public final class PaymentSetupsResponse extends Resource { /** * A reference you can use to identify the payment setup, such as an order number. * [Optional] + * max 80 */ private String reference; /** * A description of the payment setup. * [Optional] + * max 100 */ private String description; @@ -115,7 +118,7 @@ public final class PaymentSetupsResponse extends Resource { /** * An ordered list of available payment method names. The order indicates the recommended * presentation priority, with the first item being the highest priority. - * [Optional] + * [Optional] readOnly */ private List availablePaymentMethods; @@ -156,4 +159,13 @@ public final class PaymentSetupsResponse extends Resource { * [Optional] */ private PaymentSetupRisk risk; + + /** + * A set of key/value pairs you can attach to a payment. It can be useful for storing additional + * information about the payment. + * Each value is a string (max 255 characters), a number or a boolean. + * [Optional] + * max 20 properties + */ + private Map metadata; } diff --git a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentMethodsSerializationTest.java b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentMethodsSerializationTest.java index 0951d5827..4bf5bdd5d 100644 --- a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentMethodsSerializationTest.java +++ b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentMethodsSerializationTest.java @@ -1,21 +1,40 @@ package com.checkout.handlepaymentsandpayouts.setups; import com.checkout.GsonSerializer; +import com.checkout.common.Phone; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.PaymentMethods; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.applepay.ApplePay; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.applepay.ApplePayTokenData; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.applepay.ApplePayTokenDataHeader; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.bizum.Bizum; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.card.Card; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.card.CardDetails; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.card.CardToken; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.CardAccountHolder; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.CardAccountHolderType; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodAction; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodBase; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodInitialization; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodStatus; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentSetupScheme; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.WalletToken; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.googlepay.GooglePay; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.googlepay.GooglePayPreferredScheme; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.googlepay.GooglePayTokenData; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.instrument.Instrument; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.klarna.Klarna; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.paypal.PayPal; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.paypal.PayPalShippingPreference; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.paypal.PayPalUserAction; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.stcpay.Stcpay; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.tabby.Tabby; +import com.checkout.payments.PreferredSchema; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; +import java.time.Instant; import java.util.Arrays; import java.util.Collections; @@ -43,7 +62,8 @@ void shouldDeserializeKlarnaWithAction() { + "\"type\":\"sdk\"," + "\"client_token\":\"ct_klarna_abc\"," + "\"session_id\":\"sess_111\"" - + "}" + + "}," + + "\"iban\":\"DE89370400440532013000\"" + "}"; final Klarna klarna = serializer.fromJson(json, Klarna.class); @@ -57,6 +77,7 @@ void shouldDeserializeKlarnaWithAction() { assertEquals("ct_klarna_abc", klarna.getAction().getClientToken()); assertEquals("sess_111", klarna.getAction().getSessionId()); assertNull(klarna.getAction().getOrderId()); + assertEquals("DE89370400440532013000", klarna.getIban()); } @Test @@ -71,10 +92,14 @@ void shouldRoundTripKlarnaWithAllProperties() { .sessionId("sid") .orderId(null) .build()); + original.setIban("DE89370400440532013000"); final String json = serializer.toJson(original); final Klarna restored = serializer.fromJson(json, Klarna.class); + assertEquals("DE89370400440532013000", JsonParser.parseString(json).getAsJsonObject().get("iban").getAsString()); + assertEquals(original.getIban(), restored.getIban()); + assertNotNull(restored); assertEquals(original.getStatus(), restored.getStatus()); assertEquals(original.getFlags(), restored.getFlags()); @@ -89,7 +114,7 @@ void shouldRoundTripKlarnaWithAllProperties() { @Test void shouldDeserializeStcpayWithOtpAction() { final String json = "{" - + "\"status\":\"pending\"," + + "\"status\":\"action_required\"," + "\"flags\":[\"stc_flag\"]," + "\"initialization\":\"enabled\"," + "\"otp\":\"123456\"," @@ -99,7 +124,7 @@ void shouldDeserializeStcpayWithOtpAction() { final Stcpay stcpay = serializer.fromJson(json, Stcpay.class); assertNotNull(stcpay); - assertEquals(PaymentMethodStatus.PENDING, stcpay.getStatus()); + assertEquals(PaymentMethodStatus.ACTION_REQUIRED, stcpay.getStatus()); assertEquals(Collections.singletonList("stc_flag"), stcpay.getFlags()); assertEquals(PaymentMethodInitialization.ENABLED, stcpay.getInitialization()); assertEquals("123456", stcpay.getOtp()); @@ -113,7 +138,7 @@ void shouldDeserializeStcpayWithOtpAction() { @Test void shouldSerializeStcpayOtp() { final Stcpay stcpay = new Stcpay(); - stcpay.setStatus(PaymentMethodStatus.AVAILABLE); + stcpay.setStatus(PaymentMethodStatus.READY); stcpay.setFlags(Collections.emptyList()); stcpay.setInitialization(PaymentMethodInitialization.DISABLED); stcpay.setOtp("654321"); @@ -127,7 +152,7 @@ void shouldSerializeStcpayOtp() { @Test void shouldRoundTripStcpayWithAllProperties() { final Stcpay original = new Stcpay(); - original.setStatus(PaymentMethodStatus.AVAILABLE); + original.setStatus(PaymentMethodStatus.INITIALIZATION_REQUIRED); original.setFlags(Collections.singletonList("f")); original.setInitialization(PaymentMethodInitialization.DISABLED); original.setOtp("111222"); @@ -184,7 +209,7 @@ void shouldRoundTripTabbyWithAllProperties() { @Test void shouldDeserializePayPalWithAction() { final String json = "{" - + "\"status\":\"available\"," + + "\"status\":\"ready\"," + "\"flags\":[\"paypal_flag\"]," + "\"initialization\":\"enabled\"," + "\"user_action\":\"pay_now\"," @@ -199,7 +224,7 @@ void shouldDeserializePayPalWithAction() { final PayPal paypal = serializer.fromJson(json, PayPal.class); assertNotNull(paypal); - assertEquals(PaymentMethodStatus.AVAILABLE, paypal.getStatus()); + assertEquals(PaymentMethodStatus.READY, paypal.getStatus()); assertEquals(Collections.singletonList("paypal_flag"), paypal.getFlags()); assertEquals(PaymentMethodInitialization.ENABLED, paypal.getInitialization()); assertEquals(PayPalUserAction.PAY_NOW, paypal.getUserAction()); @@ -215,7 +240,7 @@ void shouldDeserializePayPalWithAction() { @Test void shouldRoundTripPayPalWithAllProperties() { final PayPal original = new PayPal(); - original.setStatus(PaymentMethodStatus.PENDING); + original.setStatus(PaymentMethodStatus.INVALID); original.setFlags(Collections.singletonList("p")); original.setInitialization(PaymentMethodInitialization.ENABLED); original.setUserAction(PayPalUserAction.CONTINUE); @@ -281,7 +306,7 @@ void shouldSerializePaymentMethodsContainer() { klarna.setInitialization(PaymentMethodInitialization.ENABLED); final PayPal paypal = new PayPal(); - paypal.setStatus(PaymentMethodStatus.AVAILABLE); + paypal.setStatus(PaymentMethodStatus.READY); paypal.setFlags(Collections.emptyList()); paypal.setInitialization(PaymentMethodInitialization.DISABLED); paypal.setBrandName("Shop"); @@ -296,7 +321,6 @@ void shouldSerializePaymentMethodsContainer() { assertTrue(json.contains("\"klarna\"")); assertTrue(json.contains("\"paypal\"")); assertTrue(json.contains("\"ready\"")); - assertTrue(json.contains("\"available\"")); assertTrue(json.contains("Shop")); } @@ -345,7 +369,7 @@ void shouldDeserializeFullPaymentMethodsFromSwaggerExample() { + "}" + "}," + "\"stcpay\":{" - + "\"status\":\"pending\"," + + "\"status\":\"action_required\"," + "\"flags\":[]," + "\"initialization\":\"disabled\"," + "\"action\":{\"type\":\"otp\"}" @@ -357,7 +381,7 @@ void shouldDeserializeFullPaymentMethodsFromSwaggerExample() { + "\"payment_types\":[\"pay_later\"]" + "}," + "\"paypal\":{" - + "\"status\":\"available\"," + + "\"status\":\"ready\"," + "\"flags\":[]," + "\"initialization\":\"enabled\"," + "\"user_action\":\"pay_now\"," @@ -443,16 +467,34 @@ void shouldRoundTripPaymentMethodActionWithAllProperties() { assertTrue(json.contains("\"order_id\"")); } + /** + * PENDING and AVAILABLE are deprecated (not in the PaymentMethodStatus enum of the spec) but still + * deserialize, so a response carrying them does not break existing integrations. + */ @Test + @SuppressWarnings("deprecation") void shouldDeserializeAllPaymentMethodStatusWireValues() { assertEquals(PaymentMethodStatus.UNAVAILABLE, serializer.fromJson("{\"status\":\"unavailable\"}", Klarna.class).getStatus()); assertEquals(PaymentMethodStatus.ACTION_REQUIRED, serializer.fromJson("{\"status\":\"action_required\"}", Klarna.class).getStatus()); assertEquals(PaymentMethodStatus.PENDING, serializer.fromJson("{\"status\":\"pending\"}", Klarna.class).getStatus()); assertEquals(PaymentMethodStatus.READY, serializer.fromJson("{\"status\":\"ready\"}", Klarna.class).getStatus()); assertEquals(PaymentMethodStatus.AVAILABLE, serializer.fromJson("{\"status\":\"available\"}", Klarna.class).getStatus()); + assertEquals(PaymentMethodStatus.INITIALIZATION_REQUIRED, serializer.fromJson("{\"status\":\"initialization_required\"}", Klarna.class).getStatus()); + assertEquals(PaymentMethodStatus.INVALID, serializer.fromJson("{\"status\":\"invalid\"}", Klarna.class).getStatus()); + } + + @Test + @SuppressWarnings("deprecation") + void shouldMarkOnlyTheStatusValuesOutsideTheSpecAsDeprecated() throws NoSuchFieldException { + for (final PaymentMethodStatus status : PaymentMethodStatus.values()) { + final boolean deprecated = PaymentMethodStatus.class.getField(status.name()).isAnnotationPresent(Deprecated.class); + final boolean inSpec = status != PaymentMethodStatus.PENDING && status != PaymentMethodStatus.AVAILABLE; + assertEquals(!inSpec, deprecated, status.name()); + } } @Test + @SuppressWarnings("deprecation") void shouldSerializeAllPaymentMethodStatusValues() { for (final PaymentMethodStatus status : PaymentMethodStatus.values()) { final Klarna k = new Klarna(); @@ -496,6 +538,287 @@ void shouldRoundTripPaymentMethodInitialization() { } } + // ------------------------------------------------------------------------ + // Card + // PaymentSetup.payment_methods.card: the card data lives under details, + // token is an object, plus schemes and preferred_scheme. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripCardWithEveryProperty() { + final Card original = buildSampleCard(); + + final String json = serializer.toJson(original); + final JsonObject card = JsonParser.parseString(json).getAsJsonObject(); + final JsonObject details = card.getAsJsonObject("details"); + + assertEquals("tok_ubfj2q76miwundwlk72vxt2i7q", card.getAsJsonObject("token").get("id").getAsString()); + assertEquals(11, details.size()); + assertEquals("4242424242424242", details.get("number").getAsString()); + assertEquals("4242", details.get("last4").getAsString()); + assertEquals("424242", details.get("bin").getAsString()); + assertEquals("visa", details.get("scheme").getAsString()); + assertEquals(12, details.get("expiry_month").getAsInt()); + assertEquals(2030, details.get("expiry_year").getAsInt()); + assertEquals("John Smith", details.get("name").getAsString()); + assertEquals("100", details.get("cvv").getAsString()); + assertTrue(details.get("stored").getAsBoolean()); + assertEquals("2030-01-01T10:00:00Z", details.get("expires_on").getAsString()); + assertTrue(details.get("allow_update").getAsBoolean()); + assertTrue(card.get("store_for_future_use").getAsBoolean()); + assertPhoneAndAccountHolder(card); + assertEquals("cartes_bancaires", card.getAsJsonArray("schemes").get(1).getAsString()); + assertEquals("china_union_pay", card.getAsJsonArray("schemes").get(2).getAsString()); + assertEquals("cartes_bancaires", card.get("preferred_scheme").getAsString()); + assertFalse(card.has("number")); + assertFalse(card.has("cvv")); + assertFalse(card.has("expiry_month")); + assertEquals(original, serializer.fromJson(json, Card.class)); + } + + @Test + void shouldDeserializeCardSwaggerExample() { + final String json = "{" + + "\"status\":\"ready\"," + + "\"flags\":[]," + + "\"token\":{\"id\":\"tok_ubfj2q76miwundwlk72vxt2i7q\"}," + + "\"details\":{\"last4\":\"4242\",\"bin\":\"424242\",\"scheme\":\"visa\"," + + "\"expiry_month\":12,\"expiry_year\":2030,\"expires_on\":\"2030-01-01T10:00:00Z\"," + + "\"allow_update\":true}," + + "\"schemes\":[\"visa\",\"mastercard\"]," + + "\"preferred_scheme\":\"visa\"" + + "}"; + + final Card card = serializer.fromJson(json, Card.class); + + assertEquals(PaymentMethodStatus.READY, card.getStatus()); + assertEquals("tok_ubfj2q76miwundwlk72vxt2i7q", card.getToken().getId()); + assertEquals("4242", card.getDetails().getLast4()); + assertEquals("424242", card.getDetails().getBin()); + assertEquals("visa", card.getDetails().getScheme()); + assertEquals(Integer.valueOf(12), card.getDetails().getExpiryMonth()); + assertEquals(Integer.valueOf(2030), card.getDetails().getExpiryYear()); + assertEquals(Instant.parse("2030-01-01T10:00:00Z"), card.getDetails().getExpiresOn()); + assertEquals(Boolean.TRUE, card.getDetails().getAllowUpdate()); + assertEquals(Arrays.asList(PaymentSetupScheme.VISA, PaymentSetupScheme.MASTERCARD), card.getSchemes()); + assertEquals(PreferredSchema.VISA, card.getPreferredScheme()); + } + + @Test + void shouldSerializeEveryPaymentSetupSchemeValue() { + for (final PaymentSetupScheme scheme : PaymentSetupScheme.values()) { + final Card card = new Card(); + card.setSchemes(Collections.singletonList(scheme)); + final JsonObject json = JsonParser.parseString(serializer.toJson(card)).getAsJsonObject(); + assertEquals(scheme.name().toLowerCase(), json.getAsJsonArray("schemes").get(0).getAsString()); + assertEquals(scheme, serializer.fromJson(serializer.toJson(card), Card.class).getSchemes().get(0)); + } + assertEquals(17, PaymentSetupScheme.values().length); + } + + // ------------------------------------------------------------------------ + // Apple Pay and Google Pay + // token is an object {id, expires_on}, no longer a bare string with + // expires_on next to it; plus schemes and preferred_scheme. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripApplePayWithEveryProperty() { + final ApplePay original = new ApplePay(); + original.setStatus(PaymentMethodStatus.READY); + original.setFlags(Collections.emptyList()); + original.setTokenData(ApplePayTokenData.builder() + .version("EC_v1") + .data("t7GeajLB9skXB6QSWfEpPA4WPhDqB7ekdd+F7588arLzve") + .signature("MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwEAAKCAMIID4jCCA4igAwIBAgIIJEPyqAad9XcwCgYIKoZIzj0EAwIwejEuMCwGA1UEAwwlQXBwbGUgQXBwbGljYXRpb24gSW50ZWdyYXRpb24gQ0EgLSBHMzEmMCQGA1UECwwdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTE0MDkyNTIyMDYxMVoXDTE5MDkyNDIyMDYxMVowXzElMCMGA1UEAwwcZWNjLXNtcC1icm9rZXItc2lnbl9VQzQtUFJPRDEUMBIGA1UECwwLaU9TIFN5c3RlbXMxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwhV37evWx7Ihj2jdcJChIY3HsL1vLCg9hGCV2Ur0pUEbg0IO2BHzQH6DMx8cVMP36zIg1rrV1O/0komJPnwPE6OCAhEwggINMEUGCCsGAQUFBwEBBDkwNzA1BggrBgEFBQcwAYYpaHR0cDovL29jc3AuYXBwbGUuY29tL29jc3AwNC1hcHBsZWFpY2EzMDEwHQYDVR0OBBYEFJRX22/VdIGGiYl2L35XhQfnm1gkMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUI/JJxE+T5O8n5sT2KGw/orv9LkswggEdBgNVHSAEggEUMIIBEDCCAQwGCSqGSIb3Y2QFATCB/jCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA2BggrBgEFBQcCARYqaHR0cDovL3d3dy5hcHBsZS5jb20vY2VydGlmaWNhdGVhdXRob3JpdHkvMDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwuYXBwbGUuY29tL2FwcGxlYWljYTMuY3JsMA4GA1UdDwEB/wQEAwIHgDAPBgkqhkiG92NkBh0EAgUAMAoGCCqGSM49BAMCA0gAMEUCIHKKnw+Soyq5mXQr1V62c0BXKpaHodYu9TWXEPUWPpbpAiEAkTecfW6+W5l0r0ADfzTCPq2YtbS39w01XIayqBNy8bEwggLuMIICdaADAgECAghJbS+/OpjalzAKBggqhkjOPQQDAjBnMRswGQYDVQQDDBJBcHBsZSBSb290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5") + .header(ApplePayTokenDataHeader.builder() + .ephemeralPublicKey("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEX") + .publicKeyHash("tqYV+tmG9aMh+l/K6cicUnPqkb1gUiLjSTM9gEz6Nl0=") + .transactionId("3cee89679130a4b2617c76118a1790e2b3c0bf9a0e6e4d0c9b1f4b2a4b1e3b2d") + .build()) + .build()); + original.setToken(WalletToken.builder() + .id("tok_ubfj2q76miwundwlk72vxt2i7q") + .expiresOn(Instant.parse("2030-01-01T10:00:00Z")) + .build()); + original.setStoreForFutureUse(true); + original.setPhone(samplePhone()); + original.setAccountHolder(sampleAccountHolder()); + original.setSchemes(Arrays.asList(PaymentSetupScheme.VISA, PaymentSetupScheme.CARTES_BANCAIRES)); + original.setPreferredScheme(PreferredSchema.CARTES_BANCAIRES); + + final String json = serializer.toJson(original); + final JsonObject applePay = JsonParser.parseString(json).getAsJsonObject(); + + assertTrue(applePay.get("token").isJsonObject()); + assertEquals("tok_ubfj2q76miwundwlk72vxt2i7q", applePay.getAsJsonObject("token").get("id").getAsString()); + assertEquals("2030-01-01T10:00:00Z", applePay.getAsJsonObject("token").get("expires_on").getAsString()); + assertFalse(applePay.has("expires_on")); + assertEquals("EC_v1", applePay.getAsJsonObject("token_data").get("version").getAsString()); + assertTrue(applePay.get("store_for_future_use").getAsBoolean()); + assertPhoneAndAccountHolder(applePay); + assertEquals("cartes_bancaires", applePay.getAsJsonArray("schemes").get(1).getAsString()); + assertEquals("cartes_bancaires", applePay.get("preferred_scheme").getAsString()); + assertEquals(original, serializer.fromJson(json, ApplePay.class)); + } + + @Test + void shouldRoundTripGooglePayWithEveryProperty() { + final GooglePay original = new GooglePay(); + original.setStatus(PaymentMethodStatus.READY); + original.setFlags(Collections.emptyList()); + original.setTokenData(GooglePayTokenData.builder() + .protocolVersion("ECv2") + .signature("MEQCIH6Q4OwQ0jAceFEkGF0JID6sJNXxOEi4r+mA7biRxqBQAiAondqoUpU/bdsrAOpZIsrHQS9nwiiNwOrr24RyPeHA0Q==") + .signedMessage("{\"encryptedMessage\":\"abc\"}") + .tokenizationKey("pk_sbox_7za2ndl3ahw2k4cmptr5qgyiv4q") + .build()); + original.setToken(WalletToken.builder() + .id("tok_ubfj2q76miwundwlk72vxt2i7q") + .expiresOn(Instant.parse("2030-01-01T10:00:00Z")) + .build()); + original.setStoreForFutureUse(false); + original.setPhone(samplePhone()); + original.setAccountHolder(sampleAccountHolder()); + original.setSchemes(Arrays.asList(PaymentSetupScheme.VISA, PaymentSetupScheme.MASTERCARD)); + original.setPreferredScheme(GooglePayPreferredScheme.MASTERCARD); + + final String json = serializer.toJson(original); + final JsonObject googlePay = JsonParser.parseString(json).getAsJsonObject(); + + assertTrue(googlePay.get("token").isJsonObject()); + assertEquals("tok_ubfj2q76miwundwlk72vxt2i7q", googlePay.getAsJsonObject("token").get("id").getAsString()); + assertEquals("2030-01-01T10:00:00Z", googlePay.getAsJsonObject("token").get("expires_on").getAsString()); + assertFalse(googlePay.has("expires_on")); + assertEquals("ECv2", googlePay.getAsJsonObject("token_data").get("protocol_version").getAsString()); + assertFalse(googlePay.get("store_for_future_use").getAsBoolean()); + assertPhoneAndAccountHolder(googlePay); + assertEquals("mastercard", googlePay.get("preferred_scheme").getAsString()); + assertEquals(original, serializer.fromJson(json, GooglePay.class)); + } + + @Test + void shouldDeserializeWalletTokenSwaggerExample() { + final String json = "{\"status\":\"ready\",\"flags\":[]," + + "\"token\":{\"id\":\"tok_ubfj2q76miwundwlk72vxt2i7q\",\"expires_on\":\"2030-01-01T10:00:00Z\"}," + + "\"schemes\":[\"visa\",\"mastercard\"],\"preferred_scheme\":\"visa\"}"; + + final ApplePay applePay = serializer.fromJson(json, ApplePay.class); + final GooglePay googlePay = serializer.fromJson(json, GooglePay.class); + + assertEquals("tok_ubfj2q76miwundwlk72vxt2i7q", applePay.getToken().getId()); + assertEquals(Instant.parse("2030-01-01T10:00:00Z"), applePay.getToken().getExpiresOn()); + assertEquals(Arrays.asList(PaymentSetupScheme.VISA, PaymentSetupScheme.MASTERCARD), applePay.getSchemes()); + assertEquals(PreferredSchema.VISA, applePay.getPreferredScheme()); + assertEquals("tok_ubfj2q76miwundwlk72vxt2i7q", googlePay.getToken().getId()); + assertEquals(Instant.parse("2030-01-01T10:00:00Z"), googlePay.getToken().getExpiresOn()); + assertEquals(GooglePayPreferredScheme.VISA, googlePay.getPreferredScheme()); + } + + @Test + void shouldSerializeEveryGooglePayPreferredSchemeValue() { + assertEquals(2, GooglePayPreferredScheme.values().length); + for (final GooglePayPreferredScheme scheme : GooglePayPreferredScheme.values()) { + final GooglePay googlePay = new GooglePay(); + googlePay.setPreferredScheme(scheme); + final String json = serializer.toJson(googlePay); + assertEquals(scheme.name().toLowerCase(), + JsonParser.parseString(json).getAsJsonObject().get("preferred_scheme").getAsString()); + assertEquals(scheme, serializer.fromJson(json, GooglePay.class).getPreferredScheme()); + } + } + + // ------------------------------------------------------------------------ + // Instrument + // PaymentSetupInstrument: id, the read-only type and action. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripInstrumentWithEveryProperty() { + final Instrument original = new Instrument(); + original.setStatus(PaymentMethodStatus.READY); + original.setFlags(Collections.emptyList()); + original.setId("src_wmlfc3zttb4uzmk6snpwb43jbi"); + original.setType("bacs"); + original.setAction(PaymentMethodAction.builder().type("set_instrument").build()); + + final String json = serializer.toJson(original); + final JsonObject instrument = JsonParser.parseString(json).getAsJsonObject(); + + assertEquals("src_wmlfc3zttb4uzmk6snpwb43jbi", instrument.get("id").getAsString()); + assertEquals("bacs", instrument.get("type").getAsString()); + assertEquals("set_instrument", instrument.getAsJsonObject("action").get("type").getAsString()); + assertEquals(original, serializer.fromJson(json, Instrument.class)); + } + + @Test + void shouldDeserializeInstrumentSwaggerExample() { + final String json = "{\"status\":\"ready\",\"flags\":[],\"id\":\"src_wmlfc3zttb4uzmk6snpwb43jbi\"," + + "\"type\":\"bacs\",\"action\":{\"type\":\"set_instrument\"}}"; + + final Instrument instrument = serializer.fromJson(json, Instrument.class); + + assertEquals("src_wmlfc3zttb4uzmk6snpwb43jbi", instrument.getId()); + assertEquals("bacs", instrument.getType()); + assertEquals("set_instrument", instrument.getAction().getType()); + } + + private static Card buildSampleCard() { + final Card c = new Card(); + c.setStatus(PaymentMethodStatus.READY); + c.setFlags(Collections.emptyList()); + c.setToken(CardToken.builder().id("tok_ubfj2q76miwundwlk72vxt2i7q").build()); + c.setDetails(CardDetails.builder() + .number("4242424242424242") + .last4("4242") + .bin("424242") + .scheme("visa") + .expiryMonth(12) + .expiryYear(2030) + .name("John Smith") + .cvv("100") + .stored(true) + .expiresOn(Instant.parse("2030-01-01T10:00:00Z")) + .allowUpdate(true) + .build()); + c.setStoreForFutureUse(true); + c.setPhone(samplePhone()); + c.setAccountHolder(sampleAccountHolder()); + c.setSchemes(Arrays.asList(PaymentSetupScheme.VISA, PaymentSetupScheme.CARTES_BANCAIRES, PaymentSetupScheme.CHINA_UNION_PAY)); + c.setPreferredScheme(PreferredSchema.CARTES_BANCAIRES); + return c; + } + + private static Phone samplePhone() { + return Phone.builder().countryCode("+44").number("207 946 0000").build(); + } + + private static CardAccountHolder sampleAccountHolder() { + return CardAccountHolder.builder() + .type(CardAccountHolderType.CORPORATE) + .firstName("John") + .lastName("Smith") + .middleName("Paul") + .companyName("Acme Corporation") + .accountNameInquiry(true) + .build(); + } + + private static void assertPhoneAndAccountHolder(final JsonObject paymentMethod) { + final JsonObject phone = paymentMethod.getAsJsonObject("phone"); + assertEquals(2, phone.size()); + assertEquals("+44", phone.get("country_code").getAsString()); + assertEquals("207 946 0000", phone.get("number").getAsString()); + final JsonObject accountHolder = paymentMethod.getAsJsonObject("account_holder"); + assertEquals(6, accountHolder.size()); + assertEquals("corporate", accountHolder.get("type").getAsString()); + assertEquals("John", accountHolder.get("first_name").getAsString()); + assertEquals("Smith", accountHolder.get("last_name").getAsString()); + assertEquals("Paul", accountHolder.get("middle_name").getAsString()); + assertEquals("Acme Corporation", accountHolder.get("company_name").getAsString()); + assertTrue(accountHolder.get("account_name_inquiry").getAsBoolean()); + } + private static Klarna buildSampleKlarna() { final Klarna k = new Klarna(); k.setStatus(PaymentMethodStatus.READY); @@ -507,7 +830,7 @@ private static Klarna buildSampleKlarna() { private static Stcpay buildSampleStcpay() { final Stcpay s = new Stcpay(); - s.setStatus(PaymentMethodStatus.PENDING); + s.setStatus(PaymentMethodStatus.INITIALIZATION_REQUIRED); s.setFlags(Collections.emptyList()); s.setInitialization(PaymentMethodInitialization.DISABLED); s.setOtp("999888"); @@ -517,7 +840,7 @@ private static Stcpay buildSampleStcpay() { private static Tabby buildSampleTabby() { final Tabby t = new Tabby(); - t.setStatus(PaymentMethodStatus.AVAILABLE); + t.setStatus(PaymentMethodStatus.INVALID); t.setFlags(Collections.singletonList("tf")); t.setInitialization(PaymentMethodInitialization.ENABLED); t.setPaymentTypes(Collections.singletonList("pay_later")); diff --git a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsNewFieldsSerializationTest.java b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsNewFieldsSerializationTest.java index e2b1b5833..aa365ca03 100644 --- a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsNewFieldsSerializationTest.java +++ b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsNewFieldsSerializationTest.java @@ -25,11 +25,19 @@ import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.stablecoin.Stablecoin; import com.checkout.handlepaymentsandpayouts.setups.entities.presentmentDetails.PaymentSetupPresentmentDetails; import com.checkout.handlepaymentsandpayouts.setups.entities.terminal.PaymentSetupTerminal; +import com.checkout.handlepaymentsandpayouts.setups.entities.order.OrderSubMerchant; +import com.checkout.handlepaymentsandpayouts.setups.entities.settings.Settings; +import com.checkout.handlepaymentsandpayouts.setups.requests.PaymentSetupsRequest; +import com.checkout.handlepaymentsandpayouts.setups.responses.PaymentSetupsConfirmResponse; +import com.checkout.handlepaymentsandpayouts.setups.responses.PaymentSetupsResponse; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.junit.jupiter.api.Test; import java.time.LocalDate; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -211,4 +219,136 @@ void shouldDeserializeCustomerSwaggerExample() { assertEquals("207 946 0000", customer.getPhone().getNumber()); assertEquals("en_GB", customer.getDevice().getLocale()); } + + // ------------------------------------------------------------------------ + // Settings + // PaymentSetup.settings: all 4 properties. capture and + // excluded_payment_methods were missing from the model. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripSettingsWithEveryProperty() { + final Settings original = Settings.builder() + .successUrl("https://example.com/payments/success") + .failureUrl("https://example.com/payments/fail") + .capture(false) + .excludedPaymentMethods(Arrays.asList("card", "klarna", "stcpay")) + .build(); + + final String json = serializer.toJson(original); + final JsonObject settings = JsonParser.parseString(json).getAsJsonObject(); + + assertEquals(4, settings.size()); + assertEquals("https://example.com/payments/success", settings.get("success_url").getAsString()); + assertEquals("https://example.com/payments/fail", settings.get("failure_url").getAsString()); + assertFalse(settings.get("capture").getAsBoolean()); + assertEquals(3, settings.getAsJsonArray("excluded_payment_methods").size()); + assertEquals("klarna", settings.getAsJsonArray("excluded_payment_methods").get(1).getAsString()); + assertEquals(original, serializer.fromJson(json, Settings.class)); + } + + @Test + void shouldDeserializeSettingsSwaggerExample() { + final String json = "{\"success_url\":\"https://example.com/payments/success\"," + + "\"failure_url\":\"https://example.com/payments/fail\"," + + "\"capture\":true," + + "\"excluded_payment_methods\":[\"card\",\"klarna\",\"stcpay\"]}"; + + final Settings settings = serializer.fromJson(json, Settings.class); + + assertEquals("https://example.com/payments/success", settings.getSuccessUrl()); + assertEquals("https://example.com/payments/fail", settings.getFailureUrl()); + assertEquals(Boolean.TRUE, settings.getCapture()); + assertEquals(Arrays.asList("card", "klarna", "stcpay"), settings.getExcludedPaymentMethods()); + } + + // ------------------------------------------------------------------------ + // Metadata and auto_confirm + // PaymentSetup.metadata (top level, request and response) and + // PaymentSetup.payment_methods.auto_confirm, both missing from the model. + // ------------------------------------------------------------------------ + + @Test + void shouldSerializeMetadataAndAutoConfirmOnTheRequest() { + final Map metadata = new HashMap<>(); + metadata.put("coupon_code", "NY2018"); + metadata.put("partner_id", 123989.5); + metadata.put("vip", true); + final PaymentSetupsRequest original = PaymentSetupsRequest.builder() + .processingChannelId("pc_q4dbxom5jbgudnjzjpz7j2z6uq") + .amount(1000L) + .currency(Currency.GBP) + .paymentMethods(PaymentMethods.builder().autoConfirm("card").build()) + .metadata(metadata) + .build(); + + final String json = serializer.toJson(original); + final JsonObject request = JsonParser.parseString(json).getAsJsonObject(); + + assertEquals("card", request.getAsJsonObject("payment_methods").get("auto_confirm").getAsString()); + assertFalse(request.getAsJsonObject("payment_methods").has("autoConfirm")); + assertEquals("NY2018", request.getAsJsonObject("metadata").get("coupon_code").getAsString()); + assertEquals(123989.5, request.getAsJsonObject("metadata").get("partner_id").getAsDouble()); + assertTrue(request.getAsJsonObject("metadata").get("vip").getAsBoolean()); + + final PaymentSetupsRequest restored = serializer.fromJson(json, PaymentSetupsRequest.class); + assertEquals("card", restored.getPaymentMethods().getAutoConfirm()); + assertEquals(metadata, restored.getMetadata()); + } + + @Test + void shouldDeserializeMetadataAndAutoConfirmOnTheResponses() { + final String json = "{\"id\":\"ps_2Un2jYAVxLsfUfpOvLhEHRW8Ft1\"," + + "\"payment_methods\":{\"auto_confirm\":\"card\"}," + + "\"metadata\":{\"coupon_code\":\"NY2018\"}}"; + + final PaymentSetupsResponse response = serializer.fromJson(json, PaymentSetupsResponse.class); + final PaymentSetupsConfirmResponse confirm = serializer.fromJson(json, PaymentSetupsConfirmResponse.class); + + assertEquals("card", response.getPaymentMethods().getAutoConfirm()); + assertEquals("NY2018", response.getMetadata().get("coupon_code")); + assertEquals("card", confirm.getPaymentMethods().getAutoConfirm()); + assertEquals("NY2018", confirm.getMetadata().get("coupon_code")); + } + + // ------------------------------------------------------------------------ + // OrderSubMerchant + // PaymentSetupSubMerchant.number_of_sales. The model sent number_of_trades, + // a key the API does not define. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripOrderSubMerchantWithEveryProperty() { + final OrderSubMerchant original = OrderSubMerchant.builder() + .id("SUB12345") + .productCategory("Electronics") + .numberOfSales(500) + .registrationDate(LocalDate.of(2023, 1, 15)) + .build(); + + final String json = serializer.toJson(original); + final JsonObject subMerchant = JsonParser.parseString(json).getAsJsonObject(); + + assertEquals(4, subMerchant.size()); + assertEquals("SUB12345", subMerchant.get("id").getAsString()); + assertEquals("Electronics", subMerchant.get("product_category").getAsString()); + assertEquals(500, subMerchant.get("number_of_sales").getAsInt()); + assertFalse(subMerchant.has("number_of_trades")); + assertEquals("2023-01-15", subMerchant.get("registration_date").getAsString()); + assertEquals(original, serializer.fromJson(json, OrderSubMerchant.class)); + } + + @Test + void shouldDeserializeOrderSubMerchantSwaggerExample() { + final String json = "{\"id\":\"SUB12345\",\"product_category\":\"Electronics\"," + + "\"number_of_sales\":500,\"registration_date\":\"2023-01-15\"}"; + + final OrderSubMerchant subMerchant = serializer.fromJson(json, OrderSubMerchant.class); + + assertEquals("SUB12345", subMerchant.getId()); + assertEquals("Electronics", subMerchant.getProductCategory()); + assertEquals(Integer.valueOf(500), subMerchant.getNumberOfSales()); + assertEquals(LocalDate.of(2023, 1, 15), subMerchant.getRegistrationDate()); + } + } diff --git a/src/test/java/com/checkout/schema/LocalDateFieldsRegressionTest.java b/src/test/java/com/checkout/schema/LocalDateFieldsRegressionTest.java index 20743951e..83229a925 100644 --- a/src/test/java/com/checkout/schema/LocalDateFieldsRegressionTest.java +++ b/src/test/java/com/checkout/schema/LocalDateFieldsRegressionTest.java @@ -327,7 +327,7 @@ void orderSubMerchant_allProperties_roundTrip() { OrderSubMerchant original = OrderSubMerchant.builder() .id("SUB-FULL-001") .productCategory("Electronics & Gadgets") - .numberOfTrades(250) + .numberOfSales(250) .registrationDate(LocalDate.of(2021, 8, 22)) .build(); @@ -337,7 +337,8 @@ void orderSubMerchant_allProperties_roundTrip() { assertNotNull(deserialized); assertEquals("SUB-FULL-001", deserialized.getId()); assertEquals("Electronics & Gadgets", deserialized.getProductCategory()); - assertEquals(250, deserialized.getNumberOfTrades()); + assertEquals(250, deserialized.getNumberOfSales()); + assertFalse(json.contains("number_of_trades"), "number_of_trades is not an API field: " + json); assertEquals(LocalDate.of(2021, 8, 22), deserialized.getRegistrationDate()); } @@ -357,14 +358,14 @@ void orderSubMerchant_registrationDate_shouldSerializeAsDateOnly() { @Test void orderSubMerchant_registrationDate_shouldDeserializeFromSwaggerExample() { - String json = "{\"id\":\"SUB-123\",\"product_category\":\"Fashion\",\"number_of_trades\":50,\"registration_date\":\"2023-01-15\"}"; + String json = "{\"id\":\"SUB-123\",\"product_category\":\"Fashion\",\"number_of_sales\":50,\"registration_date\":\"2023-01-15\"}"; OrderSubMerchant subMerchant = serializer.fromJson(json, OrderSubMerchant.class); assertNotNull(subMerchant); assertEquals("SUB-123", subMerchant.getId()); assertEquals("Fashion", subMerchant.getProductCategory()); - assertEquals(50, subMerchant.getNumberOfTrades()); + assertEquals(50, subMerchant.getNumberOfSales()); assertEquals(LocalDate.of(2023, 1, 15), subMerchant.getRegistrationDate()); } }