diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/customer/MerchantAccount.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/customer/MerchantAccount.java index f1fda60f5..f0ec03ba5 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/customer/MerchantAccount.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/customer/MerchantAccount.java @@ -64,7 +64,8 @@ public final class MerchantAccount { /** * The amount of the customer's last payment with the merchant. + * The specification types this as a number (example 55.99); the API currently stores it as an integer. * [Optional] */ - private Long lastPaymentAmount; + private Double lastPaymentAmount; } \ No newline at end of file diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/order/Order.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/order/Order.java index f140c114a..b76d8c8f9 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/order/Order.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/order/Order.java @@ -10,7 +10,7 @@ import java.util.List; /** - * Order information for payment setup + * The customer's order details. */ @Data @Builder @@ -19,23 +19,27 @@ public final class Order { /** - * The items included in the order + * A list of items in the order. + * [Optional] */ private List items; /** - * The shipping information for the order + * The customer's shipping details. + * [Optional] */ private ShippingDetails shipping; /** - * Information about sub-merchants involved in the order + * The details of the sub-merchants. + * [Optional] */ private List subMerchants; /** - * >= 0 - * The discount amount applied to the order + * The discount amount the merchant applied to the transaction. + * [Optional] + * min 0 */ private Long discountAmount; @@ -48,33 +52,37 @@ public final class Order { /** * The total shipping amount for the order. * [Optional] - * >= 0 + * min 0 */ private Long shippingAmount; /** * The total surcharge amount for the order. * [Optional] - * >= 0 + * Format: int64 + * min 0 */ private Long surchargeAmount; /** * The total tax amount for the order. * [Optional] - * >= 0 + * min 0 */ private Long taxAmount; /** * The total tipping amount for the order. * [Optional] - * >= 0 + * Format: int64 + * min 0 */ private Long tippingAmount; /** - * The amount allocations representing the sub-entities on whose behalf the payment is processed + * The sub-entities that the payment is being processed on behalf of. + * [Optional] + * min 1 item, max 50 items */ private List amountAllocations; -} \ 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 a7eb5b070..916e9f7ad 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 @@ -50,7 +50,7 @@ import lombok.NoArgsConstructor; /** - * Payment methods configuration for a payment setup. + * The payment methods that are enabled on your account and available for use. */ @Data @Builder @@ -59,241 +59,242 @@ public final class PaymentMethods { /** - * The payment method that automatically confirms the payment when it is ready, for example "card". + * The payment method that automatically confirms the payment when it is ready. + * For example, card. * [Optional] */ private String autoConfirm; /** - * Instrument (saved payment source) configuration. + * The instrument payment method's details and configuration. * [Optional] */ private Instrument instrument; /** - * Klarna payment method configuration. + * The Klarna payment method's details and configuration. * [Optional] */ private Klarna klarna; /** - * STC Pay payment method configuration. + * The stc pay payment method's details and configuration. * [Optional] */ private Stcpay stcpay; /** - * Tabby payment method configuration. + * The Tabby payment method's details and configuration. * [Optional] */ private Tabby tabby; /** - * PayPal payment method configuration. + * The PayPal payment method's details and configuration. * [Optional] */ private PayPal paypal; /** - * Bizum payment method configuration. - * [Optional] + * The Bizum payment method's details and configuration. + * [Optional] readOnly */ private Bizum bizum; /** - * Blik payment method configuration. + * The Blik payment method's details and configuration. * [Optional] */ private Blik blik; /** - * PayNow payment method configuration. - * [Optional] + * The PayNow payment method's details and configuration. + * [Optional] readOnly */ private PayNow paynow; /** - * QPay payment method configuration. + * The QPay payment method's details and configuration. * [Optional] */ private Qpay qpay; /** - * EPS payment method configuration. - * [Optional] + * The EPS payment method's details and configuration. + * [Optional] readOnly */ private Eps eps; /** - * iDEAL payment method configuration. + * The iDEAL payment method's details and configuration. * [Optional] */ private Ideal ideal; /** - * KNET payment method configuration. + * The KNET payment method's details and configuration. * [Optional] */ private Knet knet; /** - * Bancontact payment method configuration. + * The Bancontact payment method's details and configuration. * [Optional] */ private Bancontact bancontact; /** - * Benefit payment method configuration. - * [Optional] + * The Benefit payment method's details and configuration. + * [Optional] readOnly */ private Benefit benefit; /** - * Vipps payment method configuration. - * [Optional] + * The Vipps payment method's details and configuration. + * [Optional] readOnly */ private Vipps vipps; /** - * Twint payment method configuration. - * [Optional] + * The Twint payment method's details and configuration. + * [Optional] readOnly */ private Twint twint; /** - * Alipay CN payment method configuration. + * The Alipay CN payment method's details and configuration. * [Optional] */ private AlipayCn alipayCn; /** - * Alipay HK payment method configuration. + * The Alipay HK payment method's details and configuration. * [Optional] */ private AlipayHk alipayHk; /** - * GCash payment method configuration. + * The GCash payment method's details and configuration. * [Optional] */ private GCash gcash; /** - * TNG payment method configuration. + * The TNG payment method's details and configuration. * [Optional] */ private Tng tng; /** - * Dana payment method configuration. + * The Dana payment method's details and configuration. * [Optional] */ private Dana dana; /** - * MobilePay payment method configuration. - * [Optional] + * The MobilePay payment method's details and configuration. + * [Optional] readOnly */ private MobilePay mobilepay; /** - * Tamara payment method configuration. + * The Tamara payment method's details and configuration. * [Optional] */ private Tamara tamara; /** - * MBWay payment method configuration. - * [Optional] + * The MBWay payment method's details and configuration. + * [Optional] readOnly */ private MBWay mbway; /** - * Multibanco payment method configuration. + * The Multibanco payment method's details and configuration. * [Optional] */ private Multibanco multibanco; /** - * WeChatPay payment method configuration. - * [Optional] + * The WeChatPay payment method's details and configuration. + * [Optional] readOnly */ private WeChatPay wechatpay; /** - * KakaoPay payment method configuration. + * The KakaoPay payment method's details and configuration. * [Optional] */ private KakaoPay kakaopay; /** - * TrueMoney payment method configuration. + * The TrueMoney payment method's details and configuration. * [Optional] */ private TrueMoney truemoney; /** - * Octopus payment method configuration. - * [Optional] + * The Octopus payment method's details and configuration. + * [Optional] readOnly */ private Octopus octopus; /** - * P24 (Przelewy24) payment method configuration. + * The P24 (Przelewy24) payment method's details and configuration. * [Optional] */ private P24 p24; /** - * Alma payment method configuration. + * The Alma payment method's details and configuration. * [Optional] */ private Alma alma; /** - * Swish payment method configuration. + * The Swish payment method's details and configuration. * [Optional] */ private Swish swish; /** - * Sequra payment method configuration. + * The Sequra payment method's details and configuration. * [Optional] */ private Sequra sequra; /** - * ACH (Direct Debit) payment method configuration. + * The ACH payment method's details and configuration. * [Optional] */ private Ach ach; /** - * SEPA (Direct Debit) payment method configuration. + * The SEPA payment method's details and configuration. * [Optional] */ private Sepa sepa; /** - * GooglePay payment method configuration. + * The Google Pay payment method's details and configuration. * [Optional] */ private GooglePay googlepay; /** - * ApplePay payment method configuration. + * The Apple Pay payment method's details and configuration. * [Optional] */ private ApplePay applepay; /** - * Card payment method configuration. + * The Card payment method's details and configuration. * [Optional] */ private Card card; /** - * Bacs payment method configuration. + * The Bacs payment method's details and configuration. * [Optional] */ private Bacs bacs; @@ -305,22 +306,23 @@ public final class PaymentMethods { private CardPresent cardPresent; /** - * Pay by Bank (Open Banking) payment method configuration. Serialized as {@code paybybank}. + * The Pay by Bank (Open Banking) payment method's details and configuration. + * Serialized as {@code paybybank}. * [Optional] */ @SerializedName("paybybank") private PayByBank payByBank; /** - * Stablecoin payment method configuration. + * The Stablecoin payment method's details and configuration. * [Optional] */ private Stablecoin stablecoin; /** - * The Cash App Pay payment method's details and configuration. The wire key is the single - * word cashapp, so keep this field name lowercase: the naming policy turns cashApp into - * cash_app. + * The Cash App payment method's details and configuration. + * The wire key is the single word cashapp, so keep this field name lowercase: the naming + * policy turns cashApp into cash_app. * [Optional] */ private CashApp cashapp; diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/bacs/Bacs.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/bacs/Bacs.java index fc87cbab5..32ccecb75 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/bacs/Bacs.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/bacs/Bacs.java @@ -14,8 +14,11 @@ public final class Bacs 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. On update the API replaces the payment methods, so send {@code ENABLED} again to + * keep the method enabled. The specification does not state this; it is the API's behaviour. * [Optional] + * Default: disabled */ private PaymentMethodInitialization initialization = PaymentMethodInitialization.DISABLED; @@ -57,8 +60,9 @@ public final class Bacs extends PaymentMethodBase { private String currency; /** - * Whether vault may accept a partial match when looking up an existing Bacs instrument for the - * supplied account details. + * Indicates whether the Bacs instrument is created when account validation returns a partial + * match. When true, the instrument is created on a partial match; when false, instrument + * creation fails on a partial match. * [Optional] * Default: false */ diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/bancontact/Bancontact.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/bancontact/Bancontact.java index b7ef329e9..36b492f05 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/bancontact/Bancontact.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/bancontact/Bancontact.java @@ -13,6 +13,7 @@ public final class Bancontact extends PaymentMethodBase { /** * The account holder's name. + * The specification marks this optional, but the API keeps the method invalid until it is sent. * [Optional] * min 3 characters, max 100 characters */ diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/cashapp/CashApp.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/cashapp/CashApp.java index 14c6b8200..0e3121f16 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/cashapp/CashApp.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/cashapp/CashApp.java @@ -15,8 +15,11 @@ public final class CashApp 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. On update the API replaces the payment methods, so send {@code ENABLED} again to + * keep the method enabled. The specification does not state this; it is the API's behaviour. * [Optional] + * Default: disabled */ private PaymentMethodInitialization initialization = PaymentMethodInitialization.DISABLED; diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/ideal/Ideal.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/ideal/Ideal.java index dc6ef3a5b..d5a98849f 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/ideal/Ideal.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/ideal/Ideal.java @@ -14,6 +14,7 @@ public final class Ideal extends PaymentMethodBase { /** * A description of the products or services being paid for. * Do not include HTML tags or special characters. + * The specification marks this optional, but the API keeps the method invalid until it is sent. * [Optional] * max 35 characters */ 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 f44e753e0..3f4a06b52 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 @@ -15,7 +15,8 @@ public final class Klarna extends PaymentMethodBase { /** * The initialization state of the payment method. When you create a Payment Setup, this - * defaults to disabled. + * defaults to disabled. On update the API replaces the payment methods, so send {@code ENABLED} again to + * keep the method enabled. The specification does not state this; it is the API's behaviour. * [Optional] * Default: disabled */ diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/knet/Knet.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/knet/Knet.java index 173c7a998..090785b0a 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/knet/Knet.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/knet/Knet.java @@ -13,6 +13,7 @@ public final class Knet extends PaymentMethodBase { /** * The customer's preferred language on the issuer's site. + * The specification marks this optional, but the API keeps the method invalid until it is sent. * [Optional] */ private KnetLanguage language; diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/paypal/PayPal.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/paypal/PayPal.java index 04c76d1e5..ea08c9ef8 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/paypal/PayPal.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/paypal/PayPal.java @@ -14,8 +14,11 @@ public final class PayPal 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. On update the API replaces the payment methods, so send {@code ENABLED} again to + * keep the method enabled. The specification does not state this; it is the API's behaviour. *

[Optional]

+ * Default: disabled */ private PaymentMethodInitialization initialization = PaymentMethodInitialization.DISABLED; @@ -28,7 +31,7 @@ public final class PayPal extends PaymentMethodBase { /** * The brand name to display in the PayPal checkout experience. *

[Optional]

- * Maximum length: 127 characters. + * max 127 characters */ private String brandName; @@ -39,8 +42,9 @@ public final class PayPal extends PaymentMethodBase { private PayPalShippingPreference shippingPreference; /** - * The next available action. Contains type {@code sdk} and {@code order_id}. - *

[Read-only]

+ * The next available action for the payment method. Contains type {@code sdk} and + * {@code order_id}. + *

[Optional] readOnly

*/ private PaymentMethodAction action; diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/stcpay/Stcpay.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/stcpay/Stcpay.java index 5446286d2..63811c173 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/stcpay/Stcpay.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/stcpay/Stcpay.java @@ -7,27 +7,30 @@ import lombok.EqualsAndHashCode; /** - * STC Pay payment method configuration. + * The stc pay payment method's details and configuration. */ @Data @EqualsAndHashCode(callSuper = true) public final class Stcpay 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. On update the API replaces the payment methods, so send {@code ENABLED} again to + * keep the method enabled. The specification does not state this; it is the API's behaviour. * [Optional] + * Default: disabled */ private PaymentMethodInitialization initialization = PaymentMethodInitialization.DISABLED; /** - * The one-time password (OTP) for STC Pay. Write-only. - * [Optional] + * The one-time password (OTP) for stc pay. + * [Optional] writeOnly */ private String otp; /** - * The next available action. Contains type otp when an OTP is required. - * [Optional] + * The next available action for the payment method. Contains type otp when an OTP is required. + * [Optional] readOnly */ private PaymentMethodAction action; } diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tabby/Tabby.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tabby/Tabby.java index fe3f5eb69..f06df9a29 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tabby/Tabby.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tabby/Tabby.java @@ -8,15 +8,18 @@ import java.util.List; /** - * Tabby payment method configuration. + * The Tabby payment method's details and configuration. */ @Data @EqualsAndHashCode(callSuper = true) public final class Tabby 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. On update the API replaces the payment methods, so send {@code ENABLED} again to + * keep the method enabled. The specification does not state this; it is the API's behaviour. * [Optional] + * Default: disabled */ private PaymentMethodInitialization initialization = PaymentMethodInitialization.DISABLED; diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tamara/Tamara.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tamara/Tamara.java index 933040163..30add8773 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tamara/Tamara.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tamara/Tamara.java @@ -1,6 +1,7 @@ package com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.tamara; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodBase; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodInitialization; import lombok.Data; import lombok.EqualsAndHashCode; @@ -10,4 +11,19 @@ @Data @EqualsAndHashCode(callSuper = true) public final class Tamara extends PaymentMethodBase { + + /** + * The initialization state of the payment method. When you create a Payment Setup, this + * defaults to disabled. On update the API replaces the payment methods, so send {@code ENABLED} again to + * keep the method enabled. The specification does not state this; it is the API's behaviour. + * [Optional] + * Default: disabled + */ + private PaymentMethodInitialization initialization = PaymentMethodInitialization.DISABLED; + + /** + * Risk signals about the customer, forwarded to Tamara with the payment. + * [Optional] + */ + private TamaraRiskAssessment riskAssessment; } diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tamara/TamaraRiskAssessment.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tamara/TamaraRiskAssessment.java new file mode 100644 index 000000000..356a6271e --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentMethods/tamara/TamaraRiskAssessment.java @@ -0,0 +1,41 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.tamara; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Risk signals about the customer, forwarded to Tamara with the payment. + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public final class TamaraRiskAssessment { + + /** + * Specifies whether the customer is a premium customer. + * [Optional] + */ + private Boolean premiumCustomer; + + /** + * The customer's risk score. + * [Optional] + */ + private Integer riskScore; + + /** + * The number of orders the customer has made in the last 24 hours. Serialized as + * {@code order_count_last24h}. + * [Optional] + */ + private Integer orderCountLast24h; + + /** + * The number of devices linked to the customer. + * [Optional] + */ + private Integer linkedDeviceCount; +} diff --git a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentType/PaymentSetupPaymentType.java b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentType/PaymentSetupPaymentType.java new file mode 100644 index 000000000..63d67e473 --- /dev/null +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/entities/paymentType/PaymentSetupPaymentType.java @@ -0,0 +1,28 @@ +package com.checkout.handlepaymentsandpayouts.setups.entities.paymentType; + +import com.google.gson.annotations.SerializedName; + +/** + * The type of payment of a Payment Setup. The Payment Setups API uses lowercase values, unlike + * {@link com.checkout.payments.PaymentType}, which the payments API uses. Default: regular. + */ +public enum PaymentSetupPaymentType { + + @SerializedName("regular") + REGULAR, + + @SerializedName("recurring") + RECURRING, + + @SerializedName("moto") + MOTO, + + @SerializedName("installment") + INSTALLMENT, + + @SerializedName("pay_later") + PAY_LATER, + + @SerializedName("unscheduled") + UNSCHEDULED +} 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 1efaaeec1..3446ac0f8 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/requests/PaymentSetupsRequest.java @@ -1,7 +1,6 @@ package com.checkout.handlepaymentsandpayouts.setups.requests; import com.checkout.common.Currency; -import com.checkout.payments.PaymentType; import com.checkout.handlepaymentsandpayouts.setups.entities.accountFundingTransaction.PaymentSetupAccountFundingTransaction; import com.checkout.handlepaymentsandpayouts.setups.entities.billing.PaymentSetupBilling; import com.checkout.handlepaymentsandpayouts.setups.entities.customer.Customer; @@ -9,6 +8,7 @@ import com.checkout.handlepaymentsandpayouts.setups.entities.order.Order; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.PaymentMethods; import com.checkout.handlepaymentsandpayouts.setups.entities.billingDescriptor.PaymentSetupBillingDescriptor; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentType.PaymentSetupPaymentType; import com.checkout.handlepaymentsandpayouts.setups.entities.presentmentDetails.PaymentSetupPresentmentDetails; import com.checkout.handlepaymentsandpayouts.setups.entities.risk.PaymentSetupRisk; import com.checkout.handlepaymentsandpayouts.setups.entities.settings.Settings; @@ -21,7 +21,7 @@ import java.util.Map; /** - * Payment setup request + * Payment setup request, the body of POST /payments/setups and PUT /payments/setups/{id}. */ @Data @Builder @@ -30,73 +30,74 @@ public final class PaymentSetupsRequest { /** - * The processing channel to be used for the payment setup. + * The processing channel to use for the payment. * [Required] * Pattern: ^(pc)_(\w{26})$ */ private String processingChannelId; /** - * The payment amount, in the minor currency unit. The exact format depends on the currency + * The payment amount, in the minor currency unit. * [Required] */ private Long amount; /** - * The currency of the payment, as a three-letter ISO currency code + * The currency of the payment, as a three-letter ISO currency code. * [Required] */ private Currency currency; /** - * The type of payment. Required for card payments where the cardholder is not present, - * such as recurring or MOTO payments. + * The type of payment. You must provide this field for card payments in which the cardholder + * is not present. For example, if the transaction is a recurring payment, or a mail + * order/telephone order (MOTO) payment. * [Optional] - * Enum: "Regular" "Recurring" "MOTO" "Installment" "PayLater" "Unscheduled" + * Default: regular */ @Builder.Default - private PaymentType paymentType = PaymentType.REGULAR; + private PaymentSetupPaymentType paymentType = PaymentSetupPaymentType.REGULAR; /** - * A reference you can use to identify the payment. For example, an order number + * A reference you can use to identify the payment. For example, an order number. * [Optional] - * max 80 + * max 80 characters */ private String reference; /** - * A description of the payment setup + * A description of the payment. * [Optional] - * max 100 + * max 100 characters */ private String description; /** - * The payment methods that are enabled on your account and available for use + * The payment methods that are enabled on your account and available for use. * [Optional] */ private PaymentMethods paymentMethods; /** - * The payment setup configuration settings + * Settings for the Payment Setup. * [Optional] */ private Settings settings; /** - * Details about the customer + * The customer's details. * [Optional] */ private Customer customer; /** - * Details about the order associated with this payment setup + * The customer's order details. * [Optional] */ private Order order; /** - * Details for specific industries, including airline and accommodation industries + * Industry-specific information. * [Optional] */ private Industry industry; @@ -114,7 +115,7 @@ public final class PaymentSetupsRequest { private PaymentSetupAccountFundingTransaction accountFundingTransaction; /** - * The billing descriptor for the payment. + * The billing descriptor for payment. * [Optional] */ private PaymentSetupBillingDescriptor billingDescriptor; 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 e589a4579..515e93042 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsConfirmResponse.java @@ -9,10 +9,11 @@ import com.checkout.handlepaymentsandpayouts.setups.entities.industry.Industry; import com.checkout.handlepaymentsandpayouts.setups.entities.order.Order; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.PaymentMethods; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentType.PaymentSetupPaymentType; import com.checkout.handlepaymentsandpayouts.setups.entities.presentmentDetails.PaymentSetupPresentmentDetails; +import com.checkout.handlepaymentsandpayouts.setups.entities.risk.PaymentSetupRisk; import com.checkout.handlepaymentsandpayouts.setups.entities.settings.Settings; import com.checkout.handlepaymentsandpayouts.setups.entities.terminal.PaymentSetupTerminal; -import com.checkout.payments.PaymentType; import java.util.List; import java.util.Map; @@ -39,71 +40,73 @@ public final class PaymentSetupsConfirmResponse extends Resource { private String id; /** - * The processing channel used for the payment setup. + * The processing channel to use for the payment. * [Required] * Pattern: ^(pc)_(\w{26})$ */ private String processingChannelId; /** - * The payment amount in the minor currency unit. + * The payment amount, in the minor currency unit. * [Required] */ private Long amount; /** - * The three-letter ISO currency code. + * The currency of the payment, as a three-letter ISO currency code. * [Required] */ private Currency currency; /** - * The type of payment. + * The type of payment. You must provide this field for card payments in which the cardholder + * is not present. For example, if the transaction is a recurring payment, or a mail + * order/telephone order (MOTO) payment. * [Optional] - * Enum: "Regular" "Recurring" "MOTO" "Installment" "PayLater" "Unscheduled" + * Default: regular */ - private PaymentType paymentType; + private PaymentSetupPaymentType paymentType; /** - * A reference you can use to identify the payment setup, such as an order number. + * A reference you can use to identify the payment. For example, an order number. * [Optional] - * max 80 + * max 80 characters */ private String reference; /** - * A description of the payment setup. + * A description of the payment. * [Optional] - * max 100 + * max 100 characters */ private String description; /** - * The payment method configuration for this setup. + * The payment methods that are enabled on your account and available for use. * [Optional] */ private PaymentMethods paymentMethods; /** - * The payment setup configuration settings. + * Settings for the Payment Setup. * [Optional] */ private Settings settings; /** - * Details about the customer. + * The customer's details. * [Optional] */ private Customer customer; /** - * Details about the order associated with this payment setup. + * The customer's order details. * [Optional] */ private Order order; /** - * Industry-specific information for specialized payment scenarios. + * Industry-specific information. * [Optional] */ private Industry industry; @@ -128,14 +131,14 @@ public final class PaymentSetupsConfirmResponse extends Resource { private PaymentSetupAccountFundingTransaction accountFundingTransaction; /** - * The billing descriptor for the payment. + * The billing descriptor for payment. * [Optional] */ private PaymentSetupBillingDescriptor billingDescriptor; /** - * The latest payment response from the gateway, populated when auto-confirm succeeds during setup - * creation. For the full response schema, see Request a Payment or Payout. + * The latest payment for this setup. Returned after a successful confirmation, including + * auto-confirm during setup creation. For the full response schema, see Get payment details. * [Optional] readOnly */ private Object latestPayment; @@ -153,6 +156,12 @@ public final class PaymentSetupsConfirmResponse extends Resource { */ private PaymentSetupTerminal terminal; + /** + * Risk assessment settings for the Payment Setup. + * [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. 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 9df38f19f..56e504615 100644 --- a/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java +++ b/src/main/java/com/checkout/handlepaymentsandpayouts/setups/responses/PaymentSetupsResponse.java @@ -9,11 +9,11 @@ import com.checkout.handlepaymentsandpayouts.setups.entities.industry.Industry; import com.checkout.handlepaymentsandpayouts.setups.entities.order.Order; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.PaymentMethods; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentType.PaymentSetupPaymentType; import com.checkout.handlepaymentsandpayouts.setups.entities.presentmentDetails.PaymentSetupPresentmentDetails; import com.checkout.handlepaymentsandpayouts.setups.entities.risk.PaymentSetupRisk; import com.checkout.handlepaymentsandpayouts.setups.entities.settings.Settings; import com.checkout.handlepaymentsandpayouts.setups.entities.terminal.PaymentSetupTerminal; -import com.checkout.payments.PaymentType; import java.util.List; import java.util.Map; @@ -40,71 +40,73 @@ public final class PaymentSetupsResponse extends Resource { private String id; /** - * The processing channel used for the payment setup. + * The processing channel to use for the payment. * [Required] * Pattern: ^(pc)_(\w{26})$ */ private String processingChannelId; /** - * The payment amount in the minor currency unit. + * The payment amount, in the minor currency unit. * [Required] */ private Long amount; /** - * The three-letter ISO currency code. + * The currency of the payment, as a three-letter ISO currency code. * [Required] */ private Currency currency; /** - * The type of payment. + * The type of payment. You must provide this field for card payments in which the cardholder + * is not present. For example, if the transaction is a recurring payment, or a mail + * order/telephone order (MOTO) payment. * [Optional] - * Enum: "Regular" "Recurring" "MOTO" "Installment" "PayLater" "Unscheduled" + * Default: regular */ - private PaymentType paymentType; + private PaymentSetupPaymentType paymentType; /** - * A reference you can use to identify the payment setup, such as an order number. + * A reference you can use to identify the payment. For example, an order number. * [Optional] - * max 80 + * max 80 characters */ private String reference; /** - * A description of the payment setup. + * A description of the payment. * [Optional] - * max 100 + * max 100 characters */ private String description; /** - * The payment method configuration for this setup. + * The payment methods that are enabled on your account and available for use. * [Optional] */ private PaymentMethods paymentMethods; /** - * The payment setup configuration settings. + * Settings for the Payment Setup. * [Optional] */ private Settings settings; /** - * Details about the customer. + * The customer's details. * [Optional] */ private Customer customer; /** - * Details about the order associated with this payment setup. + * The customer's order details. * [Optional] */ private Order order; /** - * Industry-specific information for specialized payment scenarios. + * Industry-specific information. * [Optional] */ private Industry industry; @@ -129,14 +131,14 @@ public final class PaymentSetupsResponse extends Resource { private PaymentSetupAccountFundingTransaction accountFundingTransaction; /** - * The billing descriptor for the payment. + * The billing descriptor for payment. * [Optional] */ private PaymentSetupBillingDescriptor billingDescriptor; /** - * The latest payment response from the gateway, populated when auto-confirm succeeds during setup - * creation. For the full response schema, see Request a Payment or Payout. + * The latest payment for this setup. Returned after a successful confirmation, including + * auto-confirm during setup creation. For the full response schema, see Get payment details. * [Optional] readOnly */ private Object latestPayment; diff --git a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsConfirmResponseSerializationTest.java b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsConfirmResponseSerializationTest.java index 53a916be6..e11150a64 100644 --- a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsConfirmResponseSerializationTest.java +++ b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsConfirmResponseSerializationTest.java @@ -3,12 +3,16 @@ import com.checkout.GsonSerializer; import com.checkout.common.Currency; import com.checkout.handlepaymentsandpayouts.setups.entities.customer.Customer; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentType.PaymentSetupPaymentType; +import com.checkout.handlepaymentsandpayouts.setups.entities.risk.PaymentSetupRisk; import com.checkout.handlepaymentsandpayouts.setups.responses.PaymentSetupsConfirmResponse; -import com.checkout.payments.PaymentType; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import org.junit.jupiter.api.Test; 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 PaymentSetupsConfirmResponseSerializationTest { @@ -21,8 +25,9 @@ void shouldDeserializeConfirmResponseWithSetupsEntitiesModels() { + "\"processing_channel_id\":\"pc_q4dbxom5jbgudnjzjpz7j2z6uq\"," + "\"amount\":10000," + "\"currency\":\"GBP\"," - + "\"payment_type\":\"Regular\"," + + "\"payment_type\":\"regular\"," + "\"reference\":\"REF-0987-475\"," + + "\"risk\":{\"enabled\":false}," + "\"customer\":{" + "\"name\":\"John Smith\"," + "\"email\":{\"address\":\"johnsmith@example.com\",\"verified\":true}" @@ -36,7 +41,8 @@ void shouldDeserializeConfirmResponseWithSetupsEntitiesModels() { assertEquals("psu_y3oqhf46pyzuxjbcn2giaqnb44", response.getId()); assertEquals(10000L, response.getAmount()); assertEquals(Currency.GBP, response.getCurrency()); - assertEquals(PaymentType.REGULAR, response.getPaymentType()); + assertEquals(PaymentSetupPaymentType.REGULAR, response.getPaymentType()); + assertEquals(Boolean.FALSE, response.getRisk().getEnabled()); // The customer must be the setups.entities model, whose email is a nested object // (postpayments Customer models email as a plain string and would fail to deserialize this shape). @@ -47,4 +53,23 @@ void shouldDeserializeConfirmResponseWithSetupsEntitiesModels() { assertEquals("johnsmith@example.com", customer.getEmail().getAddress()); assertEquals(true, customer.getEmail().getVerified()); } + + @Test + void shouldRoundTripConfirmResponseRisk() { + final PaymentSetupsConfirmResponse original = PaymentSetupsConfirmResponse.builder() + .id("psu_y3oqhf46pyzuxjbcn2giaqnb44") + .paymentType(PaymentSetupPaymentType.RECURRING) + .risk(PaymentSetupRisk.builder().enabled(true).build()) + .build(); + + final String json = serializer.toJson(original); + final JsonObject body = JsonParser.parseString(json).getAsJsonObject(); + + assertTrue(json.contains("\"risk\":{\"enabled\":true}")); + assertEquals("recurring", body.get("payment_type").getAsString()); + final PaymentSetupsConfirmResponse restored = serializer.fromJson(json, PaymentSetupsConfirmResponse.class); + assertEquals(original.getRisk(), restored.getRisk()); + assertEquals(original.getPaymentType(), restored.getPaymentType()); + assertEquals(original.getId(), restored.getId()); + } } diff --git a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsNewFieldsSerializationTest.java b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsNewFieldsSerializationTest.java index aa365ca03..de84ea2ee 100644 --- a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsNewFieldsSerializationTest.java +++ b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsNewFieldsSerializationTest.java @@ -30,18 +30,29 @@ import com.checkout.handlepaymentsandpayouts.setups.requests.PaymentSetupsRequest; import com.checkout.handlepaymentsandpayouts.setups.responses.PaymentSetupsConfirmResponse; import com.checkout.handlepaymentsandpayouts.setups.responses.PaymentSetupsResponse; +import com.checkout.handlepaymentsandpayouts.setups.entities.order.Order; +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.tamara.Tamara; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.tamara.TamaraRiskAssessment; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentType.PaymentSetupPaymentType; +import com.checkout.payments.ShippingDetails; +import com.checkout.payments.contexts.PaymentContextsItems; +import com.checkout.payments.contexts.PaymentContextsItemsType; 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.Collections; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class PaymentSetupsNewFieldsSerializationTest { @@ -184,7 +195,7 @@ void shouldRoundTripCustomerWithEveryProperty() { .firstTransactionDate(LocalDate.of(2023, 2, 20)) .lastTransactionDate(LocalDate.of(2024, 3, 9)) .totalOrderCount(5) - .lastPaymentAmount(1000L) + .lastPaymentAmount(55.99) .build()) .build(); @@ -351,4 +362,235 @@ void shouldDeserializeOrderSubMerchantSwaggerExample() { assertEquals(LocalDate.of(2023, 1, 15), subMerchant.getRegistrationDate()); } + // ------------------------------------------------------------------------ + // PaymentSetupPaymentType + // PaymentSetup.payment_type: the six lowercase values of the spec, on the + // request and on both responses. The payments PaymentType sent PascalCase + // values that the Payment Setups API rejects. + // ------------------------------------------------------------------------ + + private static final String[][] PAYMENT_SETUP_PAYMENT_TYPES = { + {"REGULAR", "regular"}, + {"RECURRING", "recurring"}, + {"MOTO", "moto"}, + {"INSTALLMENT", "installment"}, + {"PAY_LATER", "pay_later"}, + {"UNSCHEDULED", "unscheduled"} + }; + + @Test + void shouldWriteAndReadEveryPaymentSetupPaymentTypeAsItsSpecValue() { + assertEquals(PAYMENT_SETUP_PAYMENT_TYPES.length, PaymentSetupPaymentType.values().length); + for (final String[] pair : PAYMENT_SETUP_PAYMENT_TYPES) { + final PaymentSetupPaymentType type = PaymentSetupPaymentType.valueOf(pair[0]); + assertEquals("\"" + pair[1] + "\"", serializer.toJson(type)); + assertEquals(type, serializer.fromJson("\"" + pair[1] + "\"", PaymentSetupPaymentType.class)); + } + } + + @Test + void shouldSendRegularPaymentTypeByDefault() { + final PaymentSetupsRequest request = PaymentSetupsRequest.builder() + .processingChannelId("pc_q4dbxom5jbgudnjzjpz7j2z6uq") + .amount(1000L) + .currency(Currency.GBP) + .build(); + + final JsonObject json = JsonParser.parseString(serializer.toJson(request)).getAsJsonObject(); + + assertEquals(PaymentSetupPaymentType.REGULAR, request.getPaymentType()); + assertEquals("regular", json.get("payment_type").getAsString()); + } + + @Test + void shouldRoundTripEveryPaymentTypeOnRequestAndResponses() { + for (final String[] pair : PAYMENT_SETUP_PAYMENT_TYPES) { + final PaymentSetupPaymentType type = PaymentSetupPaymentType.valueOf(pair[0]); + final PaymentSetupsRequest request = PaymentSetupsRequest.builder().paymentType(type).build(); + + final String json = serializer.toJson(request); + + assertEquals(pair[1], JsonParser.parseString(json).getAsJsonObject().get("payment_type").getAsString()); + assertEquals(type, serializer.fromJson(json, PaymentSetupsRequest.class).getPaymentType()); + assertEquals(type, serializer.fromJson(json, PaymentSetupsResponse.class).getPaymentType()); + assertEquals(type, serializer.fromJson(json, PaymentSetupsConfirmResponse.class).getPaymentType()); + } + } + + @Test + void shouldNotReadPascalCasePaymentTypeTheApiDoesNotUse() { + final PaymentSetupsResponse response = + serializer.fromJson("{\"payment_type\":\"PayLater\"}", PaymentSetupsResponse.class); + + assertNull(response.getPaymentType()); + } + + // ------------------------------------------------------------------------ + // Order + // PaymentSetup.order: all 10 properties, including tipping_amount and + // surcharge_amount (int64, min 0). + // ------------------------------------------------------------------------ + + private Order createFullyPopulatedOrder() { + return Order.builder() + .items(Collections.singletonList(PaymentContextsItems.builder() + .type(PaymentContextsItemsType.DIGITAL) + .name("Battery Power Pack") + .quantity(1) + .unitPrice(1000) + .totalAmount(1000) + .reference("BA67A") + .discountAmount(150) + .url("https://example.com/products/my-item") + .imageUrl("https://example.com/products/my-item/image") + .build())) + .shipping(ShippingDetails.builder() + .firstName("Jane") + .lastName("Doe") + .email("jane.doe@example.com") + .delay(3) + .build()) + .subMerchants(Collections.singletonList(OrderSubMerchant.builder() + .id("SUB12345") + .productCategory("Electronics") + .numberOfSales(500) + .registrationDate(LocalDate.of(2023, 1, 15)) + .build())) + .discountAmount(10L) + .invoiceId("inv_123456789") + .shippingAmount(5L) + .surchargeAmount(2L) + .taxAmount(2L) + .tippingAmount(3L) + .amountAllocations(Collections.singletonList(PaymentSetupAmountAllocation.builder() + .id("ent_w4jelhppmfiufdnatam37wrfc4") + .amount(1000L) + .reference("ORD-5023-4E89") + .build())) + .build(); + } + + @Test + void shouldRoundTripOrderWithEveryProperty() { + final Order original = createFullyPopulatedOrder(); + + final String json = serializer.toJson(original); + final JsonObject order = JsonParser.parseString(json).getAsJsonObject(); + + assertEquals(10, order.size()); + assertEquals("Battery Power Pack", + order.getAsJsonArray("items").get(0).getAsJsonObject().get("name").getAsString()); + assertEquals("Jane", order.getAsJsonObject("shipping").get("first_name").getAsString()); + assertEquals("SUB12345", + order.getAsJsonArray("sub_merchants").get(0).getAsJsonObject().get("id").getAsString()); + assertEquals(10L, order.get("discount_amount").getAsLong()); + assertEquals("inv_123456789", order.get("invoice_id").getAsString()); + assertEquals(5L, order.get("shipping_amount").getAsLong()); + assertEquals(2L, order.get("surcharge_amount").getAsLong()); + assertEquals(2L, order.get("tax_amount").getAsLong()); + assertEquals(3L, order.get("tipping_amount").getAsLong()); + assertEquals("ent_w4jelhppmfiufdnatam37wrfc4", + order.getAsJsonArray("amount_allocations").get(0).getAsJsonObject().get("id").getAsString()); + assertEquals(original, serializer.fromJson(json, Order.class)); + } + + @Test + void shouldDeserializeOrderSwaggerExample() { + final String json = "{" + + "\"items\":[{\"name\":\"Battery Power Pack\",\"quantity\":1,\"unit_price\":1000," + + "\"total_amount\":1000,\"reference\":\"BA67A\",\"discount_amount\":150," + + "\"url\":\"https://example.com/products/my-item\"," + + "\"image_url\":\"https://example.com/products/my-item/image\",\"type\":\"digital\"}]," + + "\"shipping\":{\"first_name\":\"Jane\",\"last_name\":\"Doe\"," + + "\"email\":\"jane.doe@example.com\",\"delay\":3}," + + "\"sub_merchants\":[{\"id\":\"SUB12345\",\"product_category\":\"Electronics\"," + + "\"number_of_sales\":500,\"registration_date\":\"2023-01-15\"}]," + + "\"invoice_id\":\"inv_123456789\"," + + "\"shipping_amount\":5," + + "\"discount_amount\":10," + + "\"tax_amount\":2," + + "\"tipping_amount\":2," + + "\"surcharge_amount\":2," + + "\"amount_allocations\":[{\"id\":\"ent_w4jelhppmfiufdnatam37wrfc4\",\"amount\":1000," + + "\"reference\":\"ORD-5023-4E89\"}]" + + "}"; + + final Order order = serializer.fromJson(json, Order.class); + + assertEquals(1, order.getItems().size()); + assertEquals(PaymentContextsItemsType.DIGITAL, order.getItems().get(0).getType()); + assertEquals(Integer.valueOf(1000), order.getItems().get(0).getUnitPrice()); + assertEquals("Doe", order.getShipping().getLastName()); + assertEquals(Integer.valueOf(3), order.getShipping().getDelay()); + assertEquals(LocalDate.of(2023, 1, 15), order.getSubMerchants().get(0).getRegistrationDate()); + assertEquals("inv_123456789", order.getInvoiceId()); + assertEquals(Long.valueOf(5L), order.getShippingAmount()); + assertEquals(Long.valueOf(10L), order.getDiscountAmount()); + assertEquals(Long.valueOf(2L), order.getTaxAmount()); + assertEquals(Long.valueOf(2L), order.getTippingAmount()); + assertEquals(Long.valueOf(2L), order.getSurchargeAmount()); + assertEquals(Long.valueOf(1000L), order.getAmountAllocations().get(0).getAmount()); + } + + // ------------------------------------------------------------------------ + // Tamara + // PaymentSetup.payment_methods.tamara: status, flags, initialization and + // risk_assessment with its 4 properties. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripTamaraWithEveryProperty() { + final Tamara original = new Tamara(); + original.setStatus(PaymentMethodStatus.INITIALIZATION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setInitialization(PaymentMethodInitialization.ENABLED); + original.setRiskAssessment(TamaraRiskAssessment.builder() + .premiumCustomer(true) + .riskScore(42) + .orderCountLast24h(1) + .linkedDeviceCount(2) + .build()); + + final String json = serializer.toJson(PaymentMethods.builder().tamara(original).build()); + final JsonObject tamara = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("tamara"); + final JsonObject riskAssessment = tamara.getAsJsonObject("risk_assessment"); + + assertEquals(4, tamara.size()); + assertEquals("initialization_required", tamara.get("status").getAsString()); + assertEquals("customer.property_required", tamara.getAsJsonArray("flags").get(0).getAsString()); + assertEquals("enabled", tamara.get("initialization").getAsString()); + assertEquals(4, riskAssessment.size()); + assertTrue(riskAssessment.get("premium_customer").getAsBoolean()); + assertEquals(42, riskAssessment.get("risk_score").getAsInt()); + assertEquals(1, riskAssessment.get("order_count_last24h").getAsInt()); + assertEquals(2, riskAssessment.get("linked_device_count").getAsInt()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getTamara()); + } + + @Test + void shouldDeserializeTamaraSwaggerExample() { + final String json = "{\"tamara\":{" + + "\"status\":\"initialization_required\"," + + "\"flags\":[]," + + "\"initialization\":\"disabled\"," + + "\"risk_assessment\":{\"premium_customer\":false,\"risk_score\":42," + + "\"order_count_last24h\":0,\"linked_device_count\":2}" + + "}}"; + + final Tamara tamara = serializer.fromJson(json, PaymentMethods.class).getTamara(); + + assertEquals(PaymentMethodStatus.INITIALIZATION_REQUIRED, tamara.getStatus()); + assertTrue(tamara.getFlags().isEmpty()); + assertEquals(PaymentMethodInitialization.DISABLED, tamara.getInitialization()); + assertEquals(Boolean.FALSE, tamara.getRiskAssessment().getPremiumCustomer()); + assertEquals(Integer.valueOf(42), tamara.getRiskAssessment().getRiskScore()); + assertEquals(Integer.valueOf(0), tamara.getRiskAssessment().getOrderCountLast24h()); + assertEquals(Integer.valueOf(2), tamara.getRiskAssessment().getLinkedDeviceCount()); + } + + @Test + void shouldSendDisabledInitializationByDefaultForTamara() { + assertEquals("{\"initialization\":\"disabled\"}", serializer.toJson(new Tamara())); + } + } diff --git a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsPaymentMethodsSerializationTest.java b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsPaymentMethodsSerializationTest.java new file mode 100644 index 000000000..e5b5cf4dc --- /dev/null +++ b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsPaymentMethodsSerializationTest.java @@ -0,0 +1,835 @@ +package com.checkout.handlepaymentsandpayouts.setups; + +import com.checkout.GsonSerializer; +import com.checkout.common.CountryCode; +import com.checkout.common.Currency; +import com.checkout.handlepaymentsandpayouts.setups.entities.customer.MerchantAccount; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.PaymentMethods; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.ach.Ach; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.ach.AchAccountHolder; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.ach.AchAccountHolderType; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.ach.AchAccountType; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.ach.AchIdentification; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.alipay.AlipayCn; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.alipay.AlipayHk; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.alma.Alma; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.bacs.Bacs; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.bacs.BacsAccountHolder; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.bacs.BacsAccountHolderType; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.bancontact.Bancontact; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.benefit.Benefit; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.blik.Blik; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.OsType; +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.TerminalType; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.dana.Dana; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.eps.Eps; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.gcash.GCash; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.ideal.Ideal; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.kakaopay.KakaoPay; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.knet.Knet; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.knet.KnetLanguage; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.mbway.MBWay; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.mobilepay.MobilePay; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.multibanco.Multibanco; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.octopus.Octopus; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.p24.P24; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.p24.P24AccountHolder; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.paynow.PayNow; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.qpay.Qpay; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.sepa.Sepa; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.sepa.SepaAccountHolder; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.sepa.SepaAccountHolderType; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.sepa.SepaMandate; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.sepa.SepaMandateType; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.sequra.Sequra; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.swish.Swish; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.swish.SwishAccountHolder; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.tng.Tng; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.truemoney.TrueMoney; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.twint.Twint; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.vipps.Vipps; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.wechatpay.WeChatPay; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.LocalDate; +import java.util.Collections; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Serialization tests for the Payment Setup payment methods that had no dedicated test, and for + * {@link MerchantAccount#getLastPaymentAmount()}. Every method gets a round trip with every + * property, written under its literal wire key inside {@link PaymentMethods}, and a test that + * reads the spec example. The spec examples use the literal example values of the Payment Setups + * schemas; properties the spec gives no example for are covered by the round trip only. + */ +class PaymentSetupsPaymentMethodsSerializationTest { + + private final GsonSerializer serializer = new GsonSerializer(); + + // ------------------------------------------------------------------------ + // Methods that only carry status and flags + // PaymentSetup.payment_methods.{alma, benefit, eps, mbway, mobilepay, + // octopus, paynow, sequra, twint, vipps, wechatpay}. + // ------------------------------------------------------------------------ + + private static Stream statusAndFlagsMethods() { + return Stream.of( + statusAndFlags("alma", Alma::new, PaymentMethods::getAlma, + (b, m) -> b.alma((Alma) m)), + statusAndFlags("benefit", Benefit::new, PaymentMethods::getBenefit, + (b, m) -> b.benefit((Benefit) m)), + statusAndFlags("eps", Eps::new, PaymentMethods::getEps, + (b, m) -> b.eps((Eps) m)), + statusAndFlags("mbway", MBWay::new, PaymentMethods::getMbway, + (b, m) -> b.mbway((MBWay) m)), + statusAndFlags("mobilepay", MobilePay::new, PaymentMethods::getMobilepay, + (b, m) -> b.mobilepay((MobilePay) m)), + statusAndFlags("octopus", Octopus::new, PaymentMethods::getOctopus, + (b, m) -> b.octopus((Octopus) m)), + statusAndFlags("paynow", PayNow::new, PaymentMethods::getPaynow, + (b, m) -> b.paynow((PayNow) m)), + statusAndFlags("sequra", Sequra::new, PaymentMethods::getSequra, + (b, m) -> b.sequra((Sequra) m)), + statusAndFlags("twint", Twint::new, PaymentMethods::getTwint, + (b, m) -> b.twint((Twint) m)), + statusAndFlags("vipps", Vipps::new, PaymentMethods::getVipps, + (b, m) -> b.vipps((Vipps) m)), + statusAndFlags("wechatpay", WeChatPay::new, PaymentMethods::getWechatpay, + (b, m) -> b.wechatpay((WeChatPay) m))); + } + + private static Arguments statusAndFlags(final String wireKey, + final Supplier factory, + final Function getter, + final BiConsumer setter) { + return Arguments.of(wireKey, factory, getter, setter); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("statusAndFlagsMethods") + void shouldRoundTripStatusAndFlagsMethodUnderItsWireKey( + final String wireKey, + final Supplier factory, + final Function getter, + final BiConsumer setter) { + final PaymentMethodBase original = factory.get(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + final PaymentMethods.PaymentMethodsBuilder builder = PaymentMethods.builder(); + setter.accept(builder, original); + + final String json = serializer.toJson(builder.build()); + final JsonObject container = JsonParser.parseString(json).getAsJsonObject(); + final JsonObject method = container.getAsJsonObject(wireKey); + + assertEquals(1, container.size()); + assertEquals(2, method.size()); + assertEquals("action_required", method.get("status").getAsString()); + assertEquals("customer.property_required", method.getAsJsonArray("flags").get(0).getAsString()); + assertFalse(method.has("initialization")); + assertEquals(original, getter.apply(serializer.fromJson(json, PaymentMethods.class))); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("statusAndFlagsMethods") + void shouldDeserializeStatusAndFlagsMethodSwaggerExample( + final String wireKey, + final Supplier factory, + final Function getter, + final BiConsumer setter) { + final String json = "{\"" + wireKey + "\":{\"status\":\"ready\",\"flags\":[]}}"; + + final PaymentMethodBase method = getter.apply(serializer.fromJson(json, PaymentMethods.class)); + + assertInstanceOf(factory.get().getClass(), method); + assertEquals(PaymentMethodStatus.READY, method.getStatus()); + assertTrue(method.getFlags().isEmpty()); + } + + // ------------------------------------------------------------------------ + // Methods with terminal_type and os_type + // PaymentSetup.payment_methods.{alipay_cn, alipay_hk, gcash, tng, dana, + // kakaopay, truemoney}: status, flags, terminal_type and os_type. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripAlipayCnWithEveryProperty() { + final AlipayCn original = new AlipayCn(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setTerminalType(TerminalType.APP); + original.setOsType(OsType.IOS); + + final String json = serializer.toJson(PaymentMethods.builder().alipayCn(original).build()); + + assertTerminalMethodKeys(json, "alipay_cn"); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getAlipayCn()); + } + + @Test + void shouldDeserializeAlipayCnSwaggerExample() { + final AlipayCn method = serializer.fromJson(terminalSwaggerExample("alipay_cn"), PaymentMethods.class) + .getAlipayCn(); + + assertEquals(PaymentMethodStatus.READY, method.getStatus()); + assertEquals(TerminalType.WEB, method.getTerminalType()); + assertEquals(OsType.ANDROID, method.getOsType()); + } + + @Test + void shouldRoundTripAlipayHkWithEveryProperty() { + final AlipayHk original = new AlipayHk(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setTerminalType(TerminalType.APP); + original.setOsType(OsType.IOS); + + final String json = serializer.toJson(PaymentMethods.builder().alipayHk(original).build()); + + assertTerminalMethodKeys(json, "alipay_hk"); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getAlipayHk()); + } + + @Test + void shouldDeserializeAlipayHkSwaggerExample() { + final AlipayHk method = serializer.fromJson(terminalSwaggerExample("alipay_hk"), PaymentMethods.class) + .getAlipayHk(); + + assertEquals(PaymentMethodStatus.READY, method.getStatus()); + assertEquals(TerminalType.WEB, method.getTerminalType()); + assertEquals(OsType.ANDROID, method.getOsType()); + } + + @Test + void shouldRoundTripGCashWithEveryProperty() { + final GCash original = new GCash(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setTerminalType(TerminalType.APP); + original.setOsType(OsType.IOS); + + final String json = serializer.toJson(PaymentMethods.builder().gcash(original).build()); + + assertTerminalMethodKeys(json, "gcash"); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getGcash()); + } + + @Test + void shouldDeserializeGCashSwaggerExample() { + final GCash method = serializer.fromJson(terminalSwaggerExample("gcash"), PaymentMethods.class).getGcash(); + + assertEquals(PaymentMethodStatus.READY, method.getStatus()); + assertEquals(TerminalType.WEB, method.getTerminalType()); + assertEquals(OsType.ANDROID, method.getOsType()); + } + + @Test + void shouldRoundTripTngWithEveryProperty() { + final Tng original = new Tng(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setTerminalType(TerminalType.WAP); + original.setOsType(OsType.IOS); + + final String json = serializer.toJson(PaymentMethods.builder().tng(original).build()); + + assertEquals("wap", JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("tng") + .get("terminal_type").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getTng()); + } + + @Test + void shouldDeserializeTngSwaggerExample() { + final Tng method = serializer.fromJson(terminalSwaggerExample("tng"), PaymentMethods.class).getTng(); + + assertEquals(PaymentMethodStatus.READY, method.getStatus()); + assertEquals(TerminalType.WEB, method.getTerminalType()); + assertEquals(OsType.ANDROID, method.getOsType()); + } + + @Test + void shouldRoundTripDanaWithEveryProperty() { + final Dana original = new Dana(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setTerminalType(TerminalType.APP); + original.setOsType(OsType.IOS); + + final String json = serializer.toJson(PaymentMethods.builder().dana(original).build()); + + assertTerminalMethodKeys(json, "dana"); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getDana()); + } + + @Test + void shouldDeserializeDanaSwaggerExample() { + final Dana method = serializer.fromJson(terminalSwaggerExample("dana"), PaymentMethods.class).getDana(); + + assertEquals(PaymentMethodStatus.READY, method.getStatus()); + assertEquals(TerminalType.WEB, method.getTerminalType()); + assertEquals(OsType.ANDROID, method.getOsType()); + } + + @Test + void shouldRoundTripKakaoPayWithEveryProperty() { + final KakaoPay original = new KakaoPay(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setTerminalType(TerminalType.APP); + original.setOsType(OsType.IOS); + + final String json = serializer.toJson(PaymentMethods.builder().kakaopay(original).build()); + + assertTerminalMethodKeys(json, "kakaopay"); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getKakaopay()); + } + + @Test + void shouldDeserializeKakaoPaySwaggerExample() { + final KakaoPay method = serializer.fromJson(terminalSwaggerExample("kakaopay"), PaymentMethods.class) + .getKakaopay(); + + assertEquals(PaymentMethodStatus.READY, method.getStatus()); + assertEquals(TerminalType.WEB, method.getTerminalType()); + assertEquals(OsType.ANDROID, method.getOsType()); + } + + @Test + void shouldRoundTripTrueMoneyWithEveryProperty() { + final TrueMoney original = new TrueMoney(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setTerminalType(TerminalType.APP); + original.setOsType(OsType.IOS); + + final String json = serializer.toJson(PaymentMethods.builder().truemoney(original).build()); + + assertTerminalMethodKeys(json, "truemoney"); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getTruemoney()); + } + + @Test + void shouldDeserializeTrueMoneySwaggerExample() { + final TrueMoney method = serializer.fromJson(terminalSwaggerExample("truemoney"), PaymentMethods.class) + .getTruemoney(); + + assertEquals(PaymentMethodStatus.READY, method.getStatus()); + assertEquals(TerminalType.WEB, method.getTerminalType()); + assertEquals(OsType.ANDROID, method.getOsType()); + } + + // ------------------------------------------------------------------------ + // Ach + // PaymentSetup.payment_methods.ach: account_type, account_holder (with + // identification), account_number, bank_code and country. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripAchWithEveryProperty() { + final Ach original = new Ach(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setAccountType(AchAccountType.SAVINGS); + original.setAccountHolder(AchAccountHolder.builder() + .type(AchAccountHolderType.INDIVIDUAL) + .firstName("John") + .lastName("Smith") + .companyName("Acme Corporation") + .dateOfBirth("1985-05-15") + .identification(AchIdentification.builder() + .type("ssn") + .issuingCountry("US") + .number("123456789") + .build()) + .build()); + original.setAccountNumber("4099999992"); + original.setBankCode("011075150"); + original.setCountry("US"); + + final String json = serializer.toJson(PaymentMethods.builder().ach(original).build()); + final JsonObject ach = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("ach"); + final JsonObject accountHolder = ach.getAsJsonObject("account_holder"); + final JsonObject identification = accountHolder.getAsJsonObject("identification"); + + assertEquals(7, ach.size()); + assertEquals("action_required", ach.get("status").getAsString()); + assertEquals("customer.property_required", ach.getAsJsonArray("flags").get(0).getAsString()); + assertEquals("savings", ach.get("account_type").getAsString()); + assertEquals("4099999992", ach.get("account_number").getAsString()); + assertEquals("011075150", ach.get("bank_code").getAsString()); + assertEquals("US", ach.get("country").getAsString()); + assertEquals(6, accountHolder.size()); + assertEquals("individual", accountHolder.get("type").getAsString()); + assertEquals("John", accountHolder.get("first_name").getAsString()); + assertEquals("Smith", accountHolder.get("last_name").getAsString()); + assertEquals("Acme Corporation", accountHolder.get("company_name").getAsString()); + assertEquals("1985-05-15", accountHolder.get("date_of_birth").getAsString()); + assertEquals(3, identification.size()); + assertEquals("ssn", identification.get("type").getAsString()); + assertEquals("US", identification.get("issuing_country").getAsString()); + assertEquals("123456789", identification.get("number").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getAch()); + } + + @Test + void shouldDeserializeAchSwaggerExample() { + final String json = "{\"ach\":{\"status\":\"ready\",\"flags\":[],\"account_type\":\"current\"," + + "\"account_holder\":{\"type\":\"corporate\"}}}"; + + final Ach ach = serializer.fromJson(json, PaymentMethods.class).getAch(); + + assertEquals(PaymentMethodStatus.READY, ach.getStatus()); + assertTrue(ach.getFlags().isEmpty()); + assertEquals(AchAccountType.CURRENT, ach.getAccountType()); + assertEquals(AchAccountHolderType.CORPORATE, ach.getAccountHolder().getType()); + } + + @Test + void shouldWriteAndReadEveryAchEnumValue() { + assertEquals("\"savings\"", serializer.toJson(AchAccountType.SAVINGS)); + assertEquals("\"current\"", serializer.toJson(AchAccountType.CURRENT)); + assertEquals("\"cash\"", serializer.toJson(AchAccountType.CASH)); + assertEquals(3, AchAccountType.values().length); + assertEquals("\"individual\"", serializer.toJson(AchAccountHolderType.INDIVIDUAL)); + assertEquals("\"corporate\"", serializer.toJson(AchAccountHolderType.CORPORATE)); + assertEquals("\"government\"", serializer.toJson(AchAccountHolderType.GOVERNMENT)); + assertEquals(3, AchAccountHolderType.values().length); + assertEquals(AchAccountHolderType.GOVERNMENT, + serializer.fromJson("\"government\"", AchAccountHolderType.class)); + } + + // ------------------------------------------------------------------------ + // Sepa + // PaymentSetup.payment_methods.sepa: account_holder, account_number, + // country, currency and mandate. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripSepaWithEveryProperty() { + final Sepa original = new Sepa(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setAccountHolder(SepaAccountHolder.builder() + .type(SepaAccountHolderType.CORPORATE) + .firstName("John") + .lastName("Smith") + .companyName("Acme Corporation") + .build()); + original.setAccountNumber("DE89370400440532013000"); + original.setCountry("DE"); + original.setCurrency(Currency.EUR); + original.setMandate(SepaMandate.builder() + .id("mandate_123") + .type(SepaMandateType.B2B) + .dateOfSignature(LocalDate.of(2025, 1, 15)) + .build()); + + final String json = serializer.toJson(PaymentMethods.builder().sepa(original).build()); + final JsonObject sepa = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("sepa"); + final JsonObject accountHolder = sepa.getAsJsonObject("account_holder"); + final JsonObject mandate = sepa.getAsJsonObject("mandate"); + + assertEquals(7, sepa.size()); + assertEquals("action_required", sepa.get("status").getAsString()); + assertEquals("DE89370400440532013000", sepa.get("account_number").getAsString()); + assertEquals("DE", sepa.get("country").getAsString()); + assertEquals("EUR", sepa.get("currency").getAsString()); + assertEquals(4, accountHolder.size()); + assertEquals("corporate", accountHolder.get("type").getAsString()); + assertEquals("John", accountHolder.get("first_name").getAsString()); + assertEquals("Smith", accountHolder.get("last_name").getAsString()); + assertEquals("Acme Corporation", accountHolder.get("company_name").getAsString()); + assertEquals(3, mandate.size()); + assertEquals("mandate_123", mandate.get("id").getAsString()); + assertEquals("b2b", mandate.get("type").getAsString()); + assertEquals("2025-01-15", mandate.get("date_of_signature").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getSepa()); + } + + @Test + void shouldDeserializeSepaSwaggerExample() { + final String json = "{\"sepa\":{\"status\":\"ready\",\"flags\":[]," + + "\"account_holder\":{\"type\":\"individual\"}," + + "\"mandate\":{\"type\":\"core\"}}}"; + + final Sepa sepa = serializer.fromJson(json, PaymentMethods.class).getSepa(); + + assertEquals(PaymentMethodStatus.READY, sepa.getStatus()); + assertTrue(sepa.getFlags().isEmpty()); + assertEquals(SepaAccountHolderType.INDIVIDUAL, sepa.getAccountHolder().getType()); + assertEquals(SepaMandateType.CORE, sepa.getMandate().getType()); + } + + // ------------------------------------------------------------------------ + // Swish + // PaymentSetup.payment_methods.swish: billing_descriptor and + // account_holder. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripSwishWithEveryProperty() { + final Swish original = new Swish(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setBillingDescriptor("Acme order 1234"); + original.setAccountHolder(SwishAccountHolder.builder().firstName("John").lastName("Smith").build()); + + final String json = serializer.toJson(PaymentMethods.builder().swish(original).build()); + final JsonObject swish = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("swish"); + final JsonObject accountHolder = swish.getAsJsonObject("account_holder"); + + assertEquals(4, swish.size()); + assertEquals("action_required", swish.get("status").getAsString()); + assertEquals("Acme order 1234", swish.get("billing_descriptor").getAsString()); + assertEquals(2, accountHolder.size()); + assertEquals("John", accountHolder.get("first_name").getAsString()); + assertEquals("Smith", accountHolder.get("last_name").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getSwish()); + } + + @Test + void shouldDeserializeSwishSwaggerExample() { + final String json = "{\"swish\":{\"status\":\"ready\",\"flags\":[]," + + "\"account_holder\":{\"first_name\":\"John\",\"last_name\":\"Smith\"}}}"; + + final Swish swish = serializer.fromJson(json, PaymentMethods.class).getSwish(); + + assertEquals(PaymentMethodStatus.READY, swish.getStatus()); + assertEquals("John", swish.getAccountHolder().getFirstName()); + assertEquals("Smith", swish.getAccountHolder().getLastName()); + } + + // ------------------------------------------------------------------------ + // P24 + // PaymentSetup.payment_methods.p24: account_holder with name and email. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripP24WithEveryProperty() { + final P24 original = new P24(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setAccountHolder(P24AccountHolder.builder() + .name("John Smith") + .email("john.smith@example.com") + .build()); + + final String json = serializer.toJson(PaymentMethods.builder().p24(original).build()); + final JsonObject p24 = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("p24"); + final JsonObject accountHolder = p24.getAsJsonObject("account_holder"); + + assertEquals(3, p24.size()); + assertEquals("action_required", p24.get("status").getAsString()); + assertEquals(2, accountHolder.size()); + assertEquals("John Smith", accountHolder.get("name").getAsString()); + assertEquals("john.smith@example.com", accountHolder.get("email").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getP24()); + } + + @Test + void shouldDeserializeP24SwaggerExample() { + final String json = "{\"p24\":{\"status\":\"ready\",\"flags\":[]}}"; + + final P24 p24 = serializer.fromJson(json, PaymentMethods.class).getP24(); + + assertEquals(PaymentMethodStatus.READY, p24.getStatus()); + assertTrue(p24.getFlags().isEmpty()); + } + + // ------------------------------------------------------------------------ + // Qpay, Ideal, Knet, Bancontact, Multibanco and Blik + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripQpayWithEveryProperty() { + final Qpay original = new Qpay(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setNationalId("21234567890"); + original.setDescription("Order 1234"); + + final String json = serializer.toJson(PaymentMethods.builder().qpay(original).build()); + final JsonObject qpay = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("qpay"); + + assertEquals(4, qpay.size()); + assertEquals("action_required", qpay.get("status").getAsString()); + assertEquals("21234567890", qpay.get("national_id").getAsString()); + assertEquals("Order 1234", qpay.get("description").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getQpay()); + } + + @Test + void shouldDeserializeQpaySwaggerExample() { + final String json = "{\"qpay\":{\"status\":\"ready\",\"flags\":[],\"national_id\":\"21234567890\"}}"; + + final Qpay qpay = serializer.fromJson(json, PaymentMethods.class).getQpay(); + + assertEquals(PaymentMethodStatus.READY, qpay.getStatus()); + assertEquals("21234567890", qpay.getNationalId()); + } + + @Test + void shouldRoundTripIdealWithEveryProperty() { + final Ideal original = new Ideal(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setDescription("2 t-shirts"); + + final String json = serializer.toJson(PaymentMethods.builder().ideal(original).build()); + final JsonObject ideal = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("ideal"); + + assertEquals(3, ideal.size()); + assertEquals("action_required", ideal.get("status").getAsString()); + assertEquals("2 t-shirts", ideal.get("description").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getIdeal()); + } + + @Test + void shouldDeserializeIdealSwaggerExample() { + final String json = "{\"ideal\":{\"status\":\"ready\",\"flags\":[],\"description\":\"2 t-shirts\"}}"; + + final Ideal ideal = serializer.fromJson(json, PaymentMethods.class).getIdeal(); + + assertEquals(PaymentMethodStatus.READY, ideal.getStatus()); + assertEquals("2 t-shirts", ideal.getDescription()); + } + + @Test + void shouldRoundTripKnetWithEveryProperty() { + final Knet original = new Knet(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setLanguage(KnetLanguage.AR); + + final String json = serializer.toJson(PaymentMethods.builder().knet(original).build()); + final JsonObject knet = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("knet"); + + assertEquals(3, knet.size()); + assertEquals("action_required", knet.get("status").getAsString()); + assertEquals("ar", knet.get("language").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getKnet()); + } + + @Test + void shouldDeserializeKnetSwaggerExample() { + final String json = "{\"knet\":{\"status\":\"ready\",\"flags\":[],\"language\":\"en\"}}"; + + final Knet knet = serializer.fromJson(json, PaymentMethods.class).getKnet(); + + assertEquals(PaymentMethodStatus.READY, knet.getStatus()); + assertEquals(KnetLanguage.EN, knet.getLanguage()); + } + + @Test + void shouldRoundTripBancontactWithEveryProperty() { + final Bancontact original = new Bancontact(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setAccountHolderName("John Smith"); + + final String json = serializer.toJson(PaymentMethods.builder().bancontact(original).build()); + final JsonObject bancontact = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("bancontact"); + + assertEquals(3, bancontact.size()); + assertEquals("action_required", bancontact.get("status").getAsString()); + assertEquals("John Smith", bancontact.get("account_holder_name").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getBancontact()); + } + + @Test + void shouldDeserializeBancontactSwaggerExample() { + final String json = "{\"bancontact\":{\"status\":\"ready\",\"flags\":[]}}"; + + final Bancontact bancontact = serializer.fromJson(json, PaymentMethods.class).getBancontact(); + + assertEquals(PaymentMethodStatus.READY, bancontact.getStatus()); + assertTrue(bancontact.getFlags().isEmpty()); + } + + @Test + void shouldRoundTripMultibancoWithEveryProperty() { + final Multibanco original = new Multibanco(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setAccountHolderName("John Smith"); + + final String json = serializer.toJson(PaymentMethods.builder().multibanco(original).build()); + final JsonObject multibanco = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("multibanco"); + + assertEquals(3, multibanco.size()); + assertEquals("action_required", multibanco.get("status").getAsString()); + assertEquals("customer.property_required", multibanco.getAsJsonArray("flags").get(0).getAsString()); + assertEquals("John Smith", multibanco.get("account_holder_name").getAsString()); + assertFalse(multibanco.has("initialization")); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getMultibanco()); + } + + @Test + void shouldDeserializeMultibancoSwaggerExample() { + final String json = "{\"multibanco\":{\"status\":\"ready\",\"flags\":[]}}"; + + final Multibanco multibanco = serializer.fromJson(json, PaymentMethods.class).getMultibanco(); + + assertEquals(PaymentMethodStatus.READY, multibanco.getStatus()); + assertTrue(multibanco.getFlags().isEmpty()); + } + + @Test + void shouldRoundTripBlikWithEveryProperty() { + final Blik original = new Blik(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setPartnerCode("999111"); + + final String json = serializer.toJson(PaymentMethods.builder().blik(original).build()); + final JsonObject blik = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("blik"); + + assertEquals(3, blik.size()); + assertEquals("action_required", blik.get("status").getAsString()); + assertEquals("999111", blik.get("partner_code").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getBlik()); + } + + @Test + void shouldDeserializeBlikSwaggerExample() { + final String json = "{\"blik\":{\"status\":\"ready\",\"flags\":[],\"partner_code\":\"999111\"}}"; + + final Blik blik = serializer.fromJson(json, PaymentMethods.class).getBlik(); + + assertEquals(PaymentMethodStatus.READY, blik.getStatus()); + assertEquals("999111", blik.getPartnerCode()); + } + + // ------------------------------------------------------------------------ + // Bacs + // PaymentSetup.payment_methods.bacs: status, flags, initialization and + // the 7 Bacs properties. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripBacsWithEveryProperty() { + final Bacs original = new Bacs(); + original.setStatus(PaymentMethodStatus.ACTION_REQUIRED); + original.setFlags(Collections.singletonList("customer.property_required")); + original.setInitialization(PaymentMethodInitialization.ENABLED); + original.setInstrumentId("src_wkq7552u245upl5h75x24554xy"); + original.setAccountHolder(BacsAccountHolder.builder() + .type(BacsAccountHolderType.CORPORATE) + .firstName("John") + .lastName("Smith") + .companyName("Acme Corporation") + .email("john.smith@example.com") + .build()); + original.setAccountNumber("55779911"); + original.setBankCode("200000"); + original.setCountry(CountryCode.GB); + original.setCurrency("GBP"); + original.setAllowPartialMatch(true); + + final String json = serializer.toJson(PaymentMethods.builder().bacs(original).build()); + final JsonObject bacs = JsonParser.parseString(json).getAsJsonObject().getAsJsonObject("bacs"); + final JsonObject accountHolder = bacs.getAsJsonObject("account_holder"); + + assertEquals(10, bacs.size()); + assertEquals("action_required", bacs.get("status").getAsString()); + assertEquals("enabled", bacs.get("initialization").getAsString()); + assertEquals("src_wkq7552u245upl5h75x24554xy", bacs.get("instrument_id").getAsString()); + assertEquals("55779911", bacs.get("account_number").getAsString()); + assertEquals("200000", bacs.get("bank_code").getAsString()); + assertEquals("GB", bacs.get("country").getAsString()); + assertEquals("GBP", bacs.get("currency").getAsString()); + assertTrue(bacs.get("allow_partial_match").getAsBoolean()); + assertEquals(5, accountHolder.size()); + assertEquals("corporate", accountHolder.get("type").getAsString()); + assertEquals("John", accountHolder.get("first_name").getAsString()); + assertEquals("Smith", accountHolder.get("last_name").getAsString()); + assertEquals("Acme Corporation", accountHolder.get("company_name").getAsString()); + assertEquals("john.smith@example.com", accountHolder.get("email").getAsString()); + assertEquals(original, serializer.fromJson(json, PaymentMethods.class).getBacs()); + } + + @Test + void shouldDeserializeBacsSwaggerExample() { + final String json = "{\"bacs\":{\"status\":\"ready\",\"flags\":[],\"initialization\":\"disabled\"," + + "\"instrument_id\":\"src_wkq7552u245upl5h75x24554xy\"," + + "\"account_holder\":{\"type\":\"individual\",\"email\":\"john.smith@example.com\"}," + + "\"allow_partial_match\":false}}"; + + final Bacs bacs = serializer.fromJson(json, PaymentMethods.class).getBacs(); + + assertEquals(PaymentMethodStatus.READY, bacs.getStatus()); + assertEquals(PaymentMethodInitialization.DISABLED, bacs.getInitialization()); + assertEquals("src_wkq7552u245upl5h75x24554xy", bacs.getInstrumentId()); + assertEquals(BacsAccountHolderType.INDIVIDUAL, bacs.getAccountHolder().getType()); + assertEquals("john.smith@example.com", bacs.getAccountHolder().getEmail()); + assertEquals(Boolean.FALSE, bacs.getAllowPartialMatch()); + } + + // ------------------------------------------------------------------------ + // MerchantAccount.last_payment_amount + // PaymentSetupMerchantAccount.last_payment_amount: the spec types it as a + // number (example 55.99). The SDK uses that type; the API currently stores + // it as an integer. + // ------------------------------------------------------------------------ + + @Test + void shouldRoundTripLastPaymentAmountUnderItsWireKey() { + final MerchantAccount original = MerchantAccount.builder().lastPaymentAmount(55.99).build(); + + final String json = serializer.toJson(original); + + assertEquals("{\"last_payment_amount\":55.99}", json); + assertEquals(original, serializer.fromJson(json, MerchantAccount.class)); + } + + @Test + void shouldDeserializeMerchantAccountSwaggerExample() { + final String json = "{\"id\":\"1234\",\"returning_customer\":true,\"total_order_count\":6," + + "\"last_payment_amount\":55.99}"; + + final MerchantAccount account = serializer.fromJson(json, MerchantAccount.class); + + assertNotNull(account); + assertEquals("1234", account.getId()); + assertEquals(Boolean.TRUE, account.getReturningCustomer()); + assertEquals(Integer.valueOf(6), account.getTotalOrderCount()); + assertEquals(Double.valueOf(55.99), account.getLastPaymentAmount()); + } + + // ------------------------------------------------------------------------ + // Helpers + // ------------------------------------------------------------------------ + + private static String terminalSwaggerExample(final String wireKey) { + return "{\"" + wireKey + "\":{\"status\":\"ready\",\"flags\":[]," + + "\"terminal_type\":\"web\",\"os_type\":\"android\"}}"; + } + + private static void assertTerminalMethodKeys(final String json, final String wireKey) { + final JsonObject container = JsonParser.parseString(json).getAsJsonObject(); + final JsonObject method = container.getAsJsonObject(wireKey); + assertEquals(1, container.size()); + assertEquals(4, method.size()); + assertEquals("action_required", method.get("status").getAsString()); + assertEquals("customer.property_required", method.getAsJsonArray("flags").get(0).getAsString()); + assertEquals("app", method.get("terminal_type").getAsString()); + assertEquals("ios", method.get("os_type").getAsString()); + } +} diff --git a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsTestIT.java b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsTestIT.java index 397fc24ca..6e4b90d46 100644 --- a/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsTestIT.java +++ b/src/test/java/com/checkout/handlepaymentsandpayouts/setups/PaymentSetupsTestIT.java @@ -15,8 +15,8 @@ import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.OsType; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.common.PaymentMethodInitialization; import com.checkout.handlepaymentsandpayouts.setups.entities.paymentMethods.klarna.Klarna; +import com.checkout.handlepaymentsandpayouts.setups.entities.paymentType.PaymentSetupPaymentType; import com.checkout.handlepaymentsandpayouts.setups.entities.settings.Settings; -import com.checkout.payments.PaymentType; import com.checkout.handlepaymentsandpayouts.setups.requests.PaymentSetupsRequest; import com.checkout.handlepaymentsandpayouts.setups.responses.PaymentSetupsConfirmResponse; import com.checkout.handlepaymentsandpayouts.setups.responses.PaymentSetupsResponse; @@ -230,7 +230,7 @@ private PaymentSetupsRequest createValidPaymentSetupsRequest() { .processingChannelId(System.getenv("CHECKOUT_PROCESSING_CHANNEL_ID")) .amount(1000L) .currency(Currency.GBP) - .paymentType(PaymentType.REGULAR) + .paymentType(PaymentSetupPaymentType.REGULAR) .reference("TEST-REF-" + randomString(6)) .description("Integration test payment setup") .settings(Settings.builder() diff --git a/src/test/java/com/checkout/schema/LocalDateFieldsRegressionTest.java b/src/test/java/com/checkout/schema/LocalDateFieldsRegressionTest.java index 83229a925..9927e4528 100644 --- a/src/test/java/com/checkout/schema/LocalDateFieldsRegressionTest.java +++ b/src/test/java/com/checkout/schema/LocalDateFieldsRegressionTest.java @@ -259,7 +259,7 @@ void merchantAccount_allProperties_roundTrip() { .firstTransactionDate(LocalDate.of(2023, 9, 15)) .lastTransactionDate(LocalDate.of(2025, 3, 28)) .totalOrderCount(42) - .lastPaymentAmount(75000L) + .lastPaymentAmount(55.99) .build(); String json = serializer.toJson(original); @@ -273,7 +273,7 @@ void merchantAccount_allProperties_roundTrip() { assertEquals(LocalDate.of(2023, 9, 15), deserialized.getFirstTransactionDate()); assertEquals(LocalDate.of(2025, 3, 28), deserialized.getLastTransactionDate()); assertEquals(42, deserialized.getTotalOrderCount()); - assertEquals(75000L, deserialized.getLastPaymentAmount()); + assertEquals(Double.valueOf(55.99), deserialized.getLastPaymentAmount()); } @Test @@ -304,7 +304,7 @@ void merchantAccount_allDateFields_shouldDeserializeFromSwaggerExample() { "\"last_transaction_date\":\"2025-03-28\"," + "\"returning_customer\":true," + "\"total_order_count\":10," + - "\"last_payment_amount\":20000" + + "\"last_payment_amount\":55.99" + "}"; MerchantAccount account = serializer.fromJson(json, MerchantAccount.class); @@ -317,7 +317,7 @@ void merchantAccount_allDateFields_shouldDeserializeFromSwaggerExample() { assertEquals(LocalDate.of(2025, 3, 28), account.getLastTransactionDate()); assertEquals(true, account.getReturningCustomer()); assertEquals(10, account.getTotalOrderCount()); - assertEquals(20000L, account.getLastPaymentAmount()); + assertEquals(Double.valueOf(55.99), account.getLastPaymentAmount()); } // ── OrderSubMerchant — ALL properties ────────────────────────────────────