diff --git a/config/config.example.toml b/config/config.example.toml index 904b7ecdd8..8ccd7ccf42 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -471,6 +471,7 @@ square = { long_lived_token = false, payment_method = "card" } braintree = { long_lived_token = false, payment_method = "card" } gocardless = { long_lived_token = true, payment_method = "bank_debit" } billwerk = { long_lived_token = false, payment_method = "card" } +globalpay = { long_lived_token = false, payment_method = "card", flow = "mandates" } [debit_routing_config] # Debit Routing configs supported_currencies = "USD" # Supported currencies for debit routing diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 835c15ce20..662674331b 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -756,6 +756,7 @@ square = { long_lived_token = false, payment_method = "card" } stax = { long_lived_token = true, payment_method = "card,bank_debit" } stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { list = "google_pay", type = "disable_only" } } billwerk = {long_lived_token = false, payment_method = "card"} +globalpay = { long_lived_token = false, payment_method = "card", flow = "mandates" } [webhooks] outgoing_enabled = true diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 89d5b2370e..d7021cad93 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -766,6 +766,7 @@ square = { long_lived_token = false, payment_method = "card" } stax = { long_lived_token = true, payment_method = "card,bank_debit" } stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { list = "google_pay", type = "disable_only" } } billwerk = {long_lived_token = false, payment_method = "card"} +globalpay = { long_lived_token = false, payment_method = "card", flow = "mandates" } [webhooks] outgoing_enabled = true diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 5c603c5dd9..6add5cd4c4 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -772,6 +772,7 @@ square = { long_lived_token = false, payment_method = "card" } stax = { long_lived_token = true, payment_method = "card,bank_debit" } stripe = { long_lived_token = false, payment_method = "wallet", payment_method_type = { list = "google_pay", type = "disable_only" } } billwerk = {long_lived_token = false, payment_method = "card"} +globalpay = { long_lived_token = false, payment_method = "card", flow = "mandates" } [webhooks] outgoing_enabled = true diff --git a/config/development.toml b/config/development.toml index e2ed4eb7f2..0a4ef25114 100644 --- a/config/development.toml +++ b/config/development.toml @@ -904,6 +904,7 @@ braintree = { long_lived_token = false, payment_method = "card" } payme = { long_lived_token = false, payment_method = "card" } gocardless = { long_lived_token = true, payment_method = "bank_debit" } billwerk = { long_lived_token = false, payment_method = "card" } +globalpay = { long_lived_token = false, payment_method = "card", flow = "mandates" } [temp_locker_enable_config] stripe = { payment_method = "bank_transfer" } diff --git a/config/docker_compose.toml b/config/docker_compose.toml index b172dfd5f1..a5d8958254 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -394,6 +394,7 @@ hipay = { long_lived_token = false, payment_method = "card" } braintree = { long_lived_token = false, payment_method = "card" } gocardless = { long_lived_token = true, payment_method = "bank_debit" } billwerk = { long_lived_token = false, payment_method = "card" } +globalpay = { long_lived_token = false, payment_method = "card", flow = "mandates" } [temp_locker_enable_config] stripe = { payment_method = "bank_transfer" } diff --git a/crates/hyperswitch_connectors/src/connectors/globalpay.rs b/crates/hyperswitch_connectors/src/connectors/globalpay.rs index 7e9ad1aa82..215c8322df 100644 --- a/crates/hyperswitch_connectors/src/connectors/globalpay.rs +++ b/crates/hyperswitch_connectors/src/connectors/globalpay.rs @@ -34,7 +34,7 @@ use hyperswitch_domain_models::{ types::{ PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData, PaymentsCompleteAuthorizeRouterData, PaymentsSyncRouterData, RefundSyncRouterData, - RefundsRouterData, + RefundsRouterData, TokenizationRouterData, }, }; use hyperswitch_interfaces::{ @@ -49,7 +49,7 @@ use hyperswitch_interfaces::{ types::{ PaymentsAuthorizeType, PaymentsCaptureType, PaymentsCompleteAuthorizeType, PaymentsSyncType, PaymentsVoidType, RefreshTokenType, RefundExecuteType, RefundSyncType, - Response, + Response, TokenizationType, }, webhooks::{IncomingWebhook, IncomingWebhookRequestDetails}, }; @@ -61,6 +61,7 @@ use response::{ use serde_json::Value; use crate::{ + connectors::globalpay::response::GlobalpayPaymentMethodsResponse, constants::headers, types::{RefreshTokenRouterData, ResponseRouterData}, utils::{ @@ -381,7 +382,79 @@ impl api::PaymentToken for Globalpay {} impl ConnectorIntegration for Globalpay { - // Not Implemented (R) + fn get_headers( + &self, + req: &TokenizationRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + fn get_url( + &self, + _req: &TokenizationRouterData, + connectors: &Connectors, + ) -> CustomResult { + Ok(format!("{}/payment-methods", self.base_url(connectors),)) + } + + fn build_request( + &self, + req: &TokenizationRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&TokenizationType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(TokenizationType::get_headers(self, req, connectors)?) + .set_body(TokenizationType::get_request_body(self, req, connectors)?) + .build(), + )) + } + + fn get_request_body( + &self, + req: &TokenizationRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let connector_req = requests::GlobalPayPaymentMethodsRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn handle_response( + &self, + data: &TokenizationRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: GlobalpayPaymentMethodsResponse = res + .response + .parse_struct("GlobalpayPaymentMethodsResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + .change_context(errors::ConnectorError::ResponseHandlingFailed) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } } impl api::MandateSetup for Globalpay {} diff --git a/crates/hyperswitch_connectors/src/connectors/globalpay/requests.rs b/crates/hyperswitch_connectors/src/connectors/globalpay/requests.rs index 4574933208..056e546db2 100644 --- a/crates/hyperswitch_connectors/src/connectors/globalpay/requests.rs +++ b/crates/hyperswitch_connectors/src/connectors/globalpay/requests.rs @@ -16,78 +16,17 @@ pub struct GlobalpayCancelRouterData { #[derive(Debug, Serialize)] pub struct GlobalpayPaymentsRequest { - /// A meaningful label for the merchant account set by Global Payments. pub account_name: Secret, - /// The amount to transfer between Payer and Merchant for a SALE or a REFUND. It is always - /// represented in the lowest denomiation of the related currency. pub amount: Option, - /// Indicates if the merchant would accept an authorization for an amount less than the - /// requested amount. This is available for CP channel - /// only where the balance not authorized can be processed again using a different card. - pub authorization_mode: Option, - /// Indicates whether the transaction is to be captured automatically, later or later using - /// more than 1 partial capture. - pub capture_mode: Option, - /// The amount of the transaction that relates to cashback.It is always represented in the - /// lowest denomiation of the related currency. - pub cashback_amount: Option, - /// Describes whether the transaction was processed in a face to face(CP) scenario or a - /// Customer Not Present (CNP) scenario. - pub channel: Channel, - /// The amount that reflects the charge the merchant applied to the transaction for availing - /// of a more convenient purchase.It is always represented in the lowest denomiation of the - /// related currency. - pub convenience_amount: Option, - /// The country in ISO-3166-1(alpha-2 code) format. - pub country: api_models::enums::CountryAlpha2, - /// The currency of the amount in ISO-4217(alpha-3) pub currency: String, - - pub currency_conversion: Option, - /// Merchant defined field to describe the transaction. - pub description: Option, - - pub device: Option, - /// The amount of the gratuity for a transaction.It is always represented in the lowest - /// denomiation of the related currency. - pub gratuity_amount: Option, - /// Indicates whether the Merchant or the Payer initiated the creation of a transaction. - pub initiator: Option, - /// Indicates the source IP Address of the system used to create the transaction. - pub ip_address: Option>, - /// Indicates the language the transaction was executed in. In the format ISO-639-1 (alpha-2) - /// or ISO-639-1 (alpha-2)_ISO-3166(alpha-2) - pub language: Option, - - pub lodging: Option, - /// Indicates to Global Payments where the merchant wants to receive notifications of certain - /// events that occur on the Global Payments system. - pub notifications: Option, - - pub order: Option, - /// The merchant's payer reference for the transaction - pub payer_reference: Option, - pub payment_method: PaymentMethod, - /// Merchant defined field to reference the transaction. pub reference: String, - /// A merchant defined reference for the location that created the transaction. - pub site_reference: Option, - /// Stored data information used to create a transaction. + pub country: api_models::enums::CountryAlpha2, + pub capture_mode: Option, + pub notifications: Option, + pub payment_method: GlobalPayPaymentMethodData, + pub channel: Channel, + pub initiator: Option, pub stored_credential: Option, - /// The amount that reflects the additional charge the merchant applied to the transaction - /// for using a specific payment method.It is always represented in the lowest denomiation of - /// the related currency. - pub surcharge_amount: Option, - /// Indicates the total or expected total of captures that will executed against a - /// transaction flagged as being captured multiple times. - pub total_capture_count: Option, - /// Describes whether the transaction is a SALE, that moves funds from Payer to Merchant, or - /// a REFUND where funds move from Merchant to Payer. - #[serde(rename = "type")] - pub globalpay_payments_request_type: Option, - /// The merchant's user reference for the transaction. This represents the person who - /// processed the transaction on the merchant's behalf like a clerk or cashier reference. - pub user_reference: Option, } #[derive(Debug, Serialize)] @@ -98,141 +37,40 @@ pub struct GlobalpayRefreshTokenRequest { pub grant_type: String, } -#[derive(Debug, Serialize, Deserialize)] -pub struct CurrencyConversion { - /// A unique identifier generated by Global Payments to identify the currency conversion. It - /// can be used to reference a currency conversion when processing a sale or a refund - /// transaction. - pub id: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Device { - pub capabilities: Option, - - pub entry_modes: Option>>, - /// Describes whether a device prompts a payer for a gratuity when the payer is entering - /// their payment method details to the device. - pub gratuity_prompt_mode: Option, - /// Describes the receipts a device prints when processing a transaction. - pub print_receipt_mode: Option, - /// The sequence number from the device used to align with processing platform. - pub sequence_number: Option>, - /// A unique identifier for the physical device. This value persists with the device even if - /// it is repurposed. - pub serial_number: Option>, - /// The time from the device in ISO8601 format - pub time: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Capabilities { - pub authorization_modes: Option>, - /// The number of lines that can be used to display information on the device. - pub display_line_count: Option, - - pub enabled_response: Option>, - - pub entry_modes: Option>, - - pub fraud: Option>, - - pub mobile: Option>, - - pub payer_verifications: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Lodging { - /// A reference that identifies the booking reference for a lodging stay. - pub booking_reference: Option, - /// The amount charged for one nights lodging. - pub daily_rate_amount: Option, - /// A reference that identifies the booking reference for a lodging stay. - pub date_checked_in: Option, - /// The check out date for a lodging stay. - pub date_checked_out: Option, - /// The total number of days of the lodging stay. - pub duration_days: Option, - #[serde(rename = "lodging.charge_items")] - pub lodging_charge_items: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct LodgingChargeItem { - pub payment_method_program_codes: Option>, - /// A reference that identifies the charge item, such as a lodging folio number. - pub reference: Option, - /// The total amount for the list of charge types for a charge item. - pub total_amount: Option, - - pub types: Option>, -} - -/// Indicates to Global Payments where the merchant wants to receive notifications of certain -/// events that occur on the Global Payments system. #[derive(Debug, Serialize, Deserialize, Default)] pub struct Notifications { - /// The merchant URL that will receive the notification when the customer has completed the - /// authentication. - pub challenge_return_url: Option, - /// The merchant URL that will receive the notification when the customer has completed the - /// authentication when the authentication is decoupled and separate to the purchase. - pub decoupled_challenge_return_url: Option, - /// The merchant URL to return the payer to, once the payer has completed payment using the - /// payment method. This returns control of the payer's payment experience to the merchant. pub return_url: Option, - /// The merchant URL to notify the merchant of the latest status of the transaction. pub status_url: Option, - /// The merchant URL that will receive the notification when the 3DS ACS successfully gathers - /// de ice informatiSon and tonotification_configurations.cordingly. - pub three_ds_method_return_url: Option, - /// The URL on merchant's website to which the customer should be redirected in the event of - /// the customer canceling the transaction. pub cancel_url: Option, } -#[derive(Debug, Serialize, Deserialize)] -pub struct Order { - /// Merchant defined field common to all transactions that are part of the same order. - pub reference: Option, -} - #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum PaymentMethodData { Card(Card), Apm(Apm), - BankTransfer(BankTransfer), DigitalWallet(DigitalWallet), + Token(TokenizationData), } #[derive(Debug, Serialize, Deserialize)] -pub struct PaymentMethod { +pub struct CommonPaymentMethodData { #[serde(flatten)] pub payment_method_data: PaymentMethodData, - pub authentication: Option, - pub encryption: Option, - /// Indicates how the payment method information was obtained by the Merchant for this - /// transaction. pub entry_mode: PaymentMethodEntryMode, - /// Indicates whether to execute the fingerprint signature functionality. - pub fingerprint_mode: Option, - /// Specify the first name of the owner of the payment method. - pub first_name: Option>, - /// Unique Global Payments generated id used to reference a stored payment method on the - /// Global Payments system. Often referred to as the payment method token. This value can be - /// used instead of payment method details such as a card number and expiry date. - pub id: Option>, - /// Specify the surname of the owner of the payment method. - pub last_name: Option>, - /// The full name of the owner of the payment method. - pub name: Option>, - /// Contains the value a merchant wishes to appear on the payer's payment method statement - /// for this transaction - pub narrative: Option, - /// Indicates whether to store the card as part of a transaction. - pub storage_mode: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct MandatePaymentMethodData { + pub entry_mode: PaymentMethodEntryMode, + pub id: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum GlobalPayPaymentMethodData { + Common(CommonPaymentMethodData), + Mandate(MandatePaymentMethodData), } #[derive(Debug, Serialize, Deserialize)] @@ -242,112 +80,17 @@ pub struct Apm { pub provider: Option, } -/// Information outlining the degree of authentication executed related to a transaction. -#[derive(Debug, Serialize, Deserialize)] -pub struct Authentication { - /// Information outlining the degree of 3D Secure authentication executed. - pub three_ds: Option, - /// A message authentication code that is used to confirm the security and integrity of the - /// messaging to Global Payments. - pub mac: Option, -} - -/// Information outlining the degree of 3D Secure authentication executed. -#[derive(Debug, Serialize, Deserialize)] -pub struct ThreeDs { - /// The reference created by the 3DSecure Directory Server to identify the specific - /// authentication attempt. - pub ds_trans_reference: Option, - /// An indication of the degree of the authentication and liability shift obtained for this - /// transaction. It is determined during the 3D Secure process. 2 or 1 for Mastercard - /// indicates the merchant has a liability shift. 5 or 6 for Visa or Amex indicates the - /// merchant has a liability shift. However for Amex if the payer is not enrolled the eci may - /// still be 6 but liability shift has not bee achieved. - pub eci: Option, - /// Indicates if any exemptions apply to this transaction. - pub exempt_status: Option, - /// Indicates the version of 3DS - pub message_version: Option, - /// The reference created by the 3DSecure provider to identify the specific authentication - /// attempt. - pub server_trans_reference: Option, - /// The authentication value created as part of the 3D Secure process. - pub value: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct BankTransfer { - /// The number or reference for the payer's bank account. - pub account_number: Option>, - - pub bank: Option, - /// The number or reference for the check - pub check_reference: Option>, - /// The type of bank account associated with the payer's bank account. - pub number_type: Option, - /// Indicates how the transaction was authorized by the merchant. - pub sec_code: Option, -} -#[derive(Debug, Serialize, Deserialize)] -pub struct Bank { - pub address: Option
, - /// The local identifier code for the bank. - pub code: Option>, - /// The name of the bank. - pub name: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Address { - /// Merchant defined field common to all transactions that are part of the same order. - pub city: Option>, - /// The country in ISO-3166-1(alpha-2 code) format. - pub country: Option, - /// First line of the address. - pub line_1: Option>, - /// Second line of the address. - pub line_2: Option>, - /// Third line of the address. - pub line_3: Option>, - /// The city or town of the address. - pub postal_code: Option>, - /// The state or region of the address. ISO 3166-2 minus the country code itself. For - /// example, US Illinois = IL, or in the case of GB counties Wiltshire = WI or Aberdeenshire - /// = ABD - pub state: Option, +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct Card { + pub cvv: Secret, + pub expiry_month: Secret, + pub expiry_year: Secret, + pub number: cards::CardNumber, } #[derive(Debug, Default, Serialize, Deserialize)] -pub struct Card { - /// The card providers description of their card product. - pub account_type: Option, - /// Code generated when the card is successfully authorized. - pub authcode: Option>, - /// First line of the address associated with the card. - pub avs_address: Option, - /// Postal code of the address associated with the card. - pub avs_postal_code: Option, - /// The unique reference created by the brands/schemes to uniquely identify the transaction. +pub struct TokenizationData { pub brand_reference: Option, - /// Indicates if a fallback mechanism was used to obtain the card information when EMV/chip - /// did not work as expected. - pub chip_condition: Option, - /// The numeric value printed on the physical card. - pub cvv: Secret, - /// The 2 digit expiry date month of the card. - pub expiry_month: Secret, - /// The 2 digit expiry date year of the card. - pub expiry_year: Secret, - /// Indicates whether the card is a debit or credit card. - pub funding: Option, - /// The card account number used to authorize the transaction. Also known as PAN. - pub number: cards::CardNumber, - /// Contains the pin block info, relating to the pin code the Payer entered. - pub pin_block: Option>, - /// The full card tag data for an EMV/chip card transaction. - pub tag: Option>, - /// Data from magnetic stripe of a card - pub track: Option>, } #[derive(Debug, Serialize, Deserialize)] @@ -358,16 +101,6 @@ pub struct DigitalWallet { pub payment_token: Option, } -#[derive(Debug, Serialize, Deserialize)] -pub struct Encryption { - /// The encryption info used when sending encrypted card data to Global Payments. - pub info: Option>, - /// The encryption method used when sending encrypted card data to Global Payments. - pub method: Option, - /// The version of encryption being used. - pub version: Option, -} - /// Stored data information used to create a transaction. #[derive(Debug, Serialize, Deserialize)] pub struct StoredCredential { @@ -379,22 +112,6 @@ pub struct StoredCredential { pub sequence: Option, } -/// Indicates if the merchant would accept an authorization for an amount less than the -/// requested amount. This is available for CP channel -/// only where the balance not authorized can be processed again using a different card. -/// -/// Describes the instruction a device can indicate to the clerk in the case of fraud. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum AuthorizationMode { - /// Indicates merchant would accept an authorization for an amount less than the - /// requested amount. - /// pub example: PARTIAL - /// - /// Describes whether the device can process partial authorizations. - Partial, -} - /// Indicates whether the transaction is to be captured automatically, later or later using /// more than 1 partial capture. #[derive(Debug, Serialize, Deserialize)] @@ -432,88 +149,6 @@ pub enum Channel { CustomerPresent, } -/// Describes the data the device can handle when it receives a response for a card -/// authorization. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum EnabledResponse { - Avs, - BrandReference, - Cvv, - MaskedNumberLast4, -} - -/// Describes the entry mode capabilities a device has. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum CapabilitiesEntryMode { - Chip, - Contactless, - ContactlessSwipe, - Manual, - Swipe, -} - -/// Describes the mobile features a device has -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum Mobile { - IntegratedCardReader, - SeparateCardReader, -} - -/// Describes the capabilities a device has to verify a payer. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum PayerVerification { - ContactlessSignature, - PayerDevice, - Pinpad, -} - -/// Describes the allowed entry modes to obtain payment method information from the payer as -/// part of a transaction request. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum DeviceEntryMode { - Chip, - Contactless, - Manual, - Swipe, -} - -/// Describes whether a device prompts a payer for a gratuity when the payer is entering -/// their payment method details to the device. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum GratuityPromptMode { - NotRequired, - Prompt, -} - -/// Describes the receipts a device prints when processing a transaction. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum PrintReceiptMode { - Both, - Merchant, - None, - Payer, -} - -/// Describes whether the transaction is a SALE, that moves funds from Payer to Merchant, or -/// a REFUND where funds move from Merchant to Payer. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum GlobalpayPaymentsRequestType { - /// indicates the movement, or the attempt to move, funds from merchant to the - /// payer. - Refund, - /// indicates the movement, or the attempt to move, funds from payer to a - /// merchant. - Sale, -} - /// Indicates whether the Merchant or the Payer initiated the creation of a transaction. #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] @@ -525,45 +160,6 @@ pub enum Initiator { Payer, } -/// Indicates the language the transaction was executed in. In the format ISO-639-1 (alpha-2) -/// or ISO-639-1 (alpha-2)_ISO-3166(alpha-2) -#[derive(Debug, Serialize, Deserialize)] -pub enum Language { - #[serde(rename = "fr")] - Fr, - #[serde(rename = "fr_CA")] - FrCa, - #[serde(rename = "ISO-639(alpha-2)")] - Iso639Alpha2, - #[serde(rename = "ISO-639(alpha-2)_ISO-3166(alpha-2)")] - Iso639alpha2Iso3166alpha2, -} - -/// Describes the payment method programs, typically run by card brands such as Amex, Visa -/// and MC. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum PaymentMethodProgramCode { - AssuredReservation, - CardDeposit, - Other, - Purchase, -} - -/// Describes the types of charges associated with a transaction. This can be one or more -/// than more charge type. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum TypeElement { - GiftShop, - Laundry, - MiniBar, - NoShow, - Other, - Phone, - Restaurant, -} - /// A string used to identify the payment method provider being used to execute this /// transaction. #[derive(Debug, Serialize, Deserialize)] @@ -577,87 +173,6 @@ pub enum ApmProvider { Testpay, } -/// Indicates if any exemptions apply to this transaction. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ExemptStatus { - LowValue, - ScaDelegation, - SecureCorporatePayment, - TransactionRiskAnalysis, - TrustedMerchant, -} - -/// The type of bank account associated with the payer's bank account. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum NumberType { - Checking, - Savings, -} - -/// Indicates how the transaction was authorized by the merchant. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum SecCode { - /// Cash Concentration or Disbursement - Can be either a credit or debit application - /// where funds are wither distributed or consolidated between corporate entities. - #[serde(rename = "CCD")] - CashConcentrationOrDisbursement, - - /// Point of Sale Entry - Point of sale debit applications non-shared (POS) - /// environment. These transactions are most often initiated by the consumer via a plastic - /// access card. This is only support for normal ACH transactions - #[serde(rename = "POP")] - PointOfSaleEntry, - /// Prearranged Payment and Deposits - used to credit or debit a consumer account. - /// Popularity used for payroll direct deposits and pre-authorized bill payments. - #[serde(rename = "PPD")] - PrearrangedPaymentAndDeposits, - /// Telephone-Initiated Entry - Used for the origination of a single entry debit - /// transaction to a consumer's account pursuant to a verbal authorization obtained from the - /// consumer via the telephone. - #[serde(rename = "TEL")] - TelephoneInitiatedEntry, - /// Internet (Web)-Initiated Entry - Used for the origination of debit entries - /// (either Single or Recurring Entry) to a consumer's account pursuant to a to an - /// authorization that is obtained from the Receiver via the Internet. - #[serde(rename = "WEB")] - WebInitiatedEntry, -} - -/// Indicates if a fallback mechanism was used to obtain the card information when EMV/chip -/// did not work as expected. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ChipCondition { - /// indicates the previous transaction with this card failed. - PrevFailed, - /// indicates the previous transaction with this card was a success. - PrevSuccess, -} - -/// Indicates whether the card is a debit or credit card. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum Funding { - /// indicates the card is an, Electronic Benefits Transfer, for cash - /// benefits. - CashBenefits, - /// indicates the card is a credit card where the funds may be available on credit - /// to the payer to fulfill the transaction amount. - Credit, - /// indicates the card is a debit card where the funds may be present in an account - /// to fulfill the transaction amount. - Debit, - /// indicates the card is an, Electronic Benefits Transfer, for food stamps. - FoodStamp, - /// indicates the card is a prepaid card where the funds are loaded to the card - /// account to fulfill the transaction amount. Unlike a debit card, a prepaid is not linked - /// to a bank account. - Prepaid, -} - /// Identifies who provides the digital wallet for the Payer. #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] @@ -666,27 +181,6 @@ pub enum DigitalWalletProvider { PayByGoogle, } -/// Indicates if the actual card number or a token is being used to process the -/// transaction. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum TokenFormat { - /// The value in the digital wallet token field is a real card number - /// (PAN) - CardNumber, - /// The value in the digital wallet token field is a temporary token in the - /// format of a card number (PAN) but is not a real card number. - CardToken, -} - -/// The encryption method used when sending encrypted card data to Global Payments. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum Method { - Ksn, - Ktb, -} - /// Indicates how the payment method information was obtained by the Merchant for this /// transaction. #[derive(Debug, Default, Serialize, Deserialize)] @@ -727,30 +221,6 @@ pub enum PaymentMethodEntryMode { Swipe, } -/// Indicates whether to execute the fingerprint signature functionality. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum FingerprintMode { - /// Always check and create the fingerprint value regardless of the result of the - /// card authorization. - Always, - /// Always check and create the fingerprint value when the card authorization - /// is successful. - OnSuccess, -} - -/// Indicates whether to store the card as part of a transaction. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum CardStorageMode { - /// /// The card information is always stored irrespective of whether the payment - /// method authorization was successful or not. - Always, - /// The card information is only stored if the payment method authorization was - /// successful. - OnSuccess, -} - /// Indicates the transaction processing model being executed when using stored /// credentials. #[derive(Debug, Serialize, Deserialize)] @@ -775,17 +245,6 @@ pub enum Model { Unscheduled, } -/// The reason stored credentials are being used to create a transaction. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum Reason { - Delayed, - Incremental, - NoShow, - Reauthorization, - Resubmission, -} - /// Indicates the order of this transaction in the sequence of a planned repeating /// transaction processing model. #[derive(Debug, Serialize, Deserialize)] @@ -812,3 +271,34 @@ pub struct GlobalpayCaptureRequest { pub struct GlobalpayCancelRequest { pub amount: Option, } + +#[derive(Default, Debug, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum UsageMode { + /// This value must be used if using the Hosted Fields or the Drop-in UI integration types. + /// When creating the payment method token, this option ensures the payment method token is temporary and will be removed once a transaction is executed or after a short period of time. + #[default] + Single, + /// When creating the payment method token, this indicates it is permanent and can be used to create many transactions. + Multiple, + /// When using the payment method token to transaction process, this indicates to use the card number also known as the PAN or FPAN when both the card number and the network token are available. + UseCardNumber, + /// When using the payment method token to transaction process, this indicates to use the network token instead of the card number if both are available. + UseNetworkToken, +} + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct GlobalPayPayer { + /// Unique identifier for the Payer on the Global Payments system. + #[serde(rename = "id", skip_serializing_if = "Option::is_none")] + pub payer_id: Option, +} + +#[derive(Default, Debug, Serialize)] +pub struct GlobalPayPaymentMethodsRequest { + pub reference: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub usage_mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub card: Option, +} diff --git a/crates/hyperswitch_connectors/src/connectors/globalpay/response.rs b/crates/hyperswitch_connectors/src/connectors/globalpay/response.rs index 11bd9e1a04..daa629641e 100644 --- a/crates/hyperswitch_connectors/src/connectors/globalpay/response.rs +++ b/crates/hyperswitch_connectors/src/connectors/globalpay/response.rs @@ -3,75 +3,17 @@ use common_utils::types::StringMinorUnit; use masking::Secret; use serde::{Deserialize, Serialize}; -use super::requests; use crate::utils::deserialize_optional_currency; #[derive(Debug, Serialize, Deserialize)] pub struct GlobalpayPaymentsResponse { - /// A unique identifier for the merchant account set by Global Payments. - pub account_id: Option>, - /// A meaningful label for the merchant account set by Global Payments. - pub account_name: Option>, - /// Information about the Action executed. - pub action: Option, - /// The amount to transfer between Payer and Merchant for a SALE or a REFUND. It is always - /// represented in the lowest denomiation of the related currency. + pub status: GlobalpayPaymentStatus, + pub payment_method: Option, + pub id: String, pub amount: Option, - /// Indicates if the merchant would accept an authorization for an amount less than the - /// requested amount. This is available for CP channel - /// only where the balance not authorized can be processed again using a different card. - pub authorization_mode: Option, - /// A Global Payments created reference that uniquely identifies the batch. - pub batch_id: Option, - /// Indicates whether the transaction is to be captured automatically, later or later using - /// more than 1 partial capture. - pub capture_mode: Option, - /// Describes whether the transaction was processed in a face to face(CP) scenario or a - /// Customer Not Present (CNP) scenario. - pub channel: Option, - /// The country in ISO-3166-1(alpha-2 code) format. - pub country: Option, - /// The currency of the amount in ISO-4217(alpha-3) #[serde(deserialize_with = "deserialize_optional_currency")] pub currency: Option, - /// Information relating to a currency conversion. - pub currency_conversion: Option, - /// A unique identifier generated by Global Payments to identify the transaction. - pub id: String, - /// A unique identifier for the merchant set by Global Payments. - pub merchant_id: Option, - /// A meaningful label for the merchant set by Global Payments. - pub merchant_name: Option>, - pub payment_method: Option, - /// Merchant defined field to reference the transaction. pub reference: Option, - /// Indicates where a transaction is in its lifecycle. - pub status: GlobalpayPaymentStatus, - /// Global Payments time indicating when the object was created in ISO-8601 format. - pub time_created: Option, - /// Describes whether the transaction is a SALE, that moves funds from Payer to Merchant, or - /// a REFUND where funds move from Merchant to Payer. - #[serde(rename = "type")] - pub globalpay_payments_response_type: Option, -} - -/// Information about the Action executed. -#[derive(Debug, Serialize, Deserialize)] -pub struct Action { - /// The id of the app that was used to create the token. - pub app_id: Option>, - /// The name of the app the user gave to the application. - pub app_name: Option>, - /// A unique identifier for the object created by Global Payments. The first 3 characters - /// identifies the resource an id relates to. - pub id: Option>, - /// The result of the action executed. - pub result_code: Option, - /// Global Payments time indicating when the object was created in ISO-8601 format. - pub time_created: Option, - /// Indicates the action taken. - #[serde(rename = "type")] - pub action_type: Option, } #[derive(Debug, Deserialize, Serialize)] @@ -86,230 +28,25 @@ pub struct GlobalpayRefreshTokenErrorResponse { pub detailed_error_description: String, } -/// Information relating to a currency conversion. -#[derive(Debug, Serialize, Deserialize)] -pub struct CurrencyConversion { - /// The percentage commission taken for providing the currency conversion. - pub commission_percentage: Option, - /// The exchange rate used to convert one currency to another. - pub conversion_rate: Option, - /// The source of the base exchange rate was obtained to execute the currency conversion. - pub exchange_rate_source: Option, - /// The time the base exchange rate was obtained from the source. - pub exchange_source_time: Option, - /// The exchange rate used to convert one currency to another. - pub margin_rate_percentage: Option, - /// The amount that will affect the payer's account. - pub payer_amount: Option, - /// The currency of the amount that will affect the payer's account. - pub payer_currency: Option, -} - #[derive(Debug, Serialize, Deserialize)] pub struct PaymentMethod { - /// Data associated with the response of an APM transaction. pub apm: Option, - /// Information outlining the degree of authentication executed related to a transaction. - pub authentication: Option, - pub bank_transfer: Option, pub card: Option, - pub digital_wallet: Option, - /// Indicates how the payment method information was obtained by the Merchant for this - /// transaction. - pub entry_mode: Option, - /// If enabled, this field contains the unique fingerprint signature for that payment method - /// for that merchant. If the payment method is seen again this same value is generated. For - /// cards the primary account number is checked only. The expiry date or the CVV is not used - /// for this check. - pub fingerprint: Option>, - /// If enabled, this field indicates whether the payment method has been seen before or is - /// new. - /// * EXISTS - Indicates that the payment method was seen on the platform before by this - /// merchant. - /// * NEW - Indicates that the payment method was not seen on the platform before by this - /// merchant. - pub fingerprint_presence_indicator: Option, - /// Unique Global Payments generated id used to reference a stored payment method on the - /// Global Payments system. Often referred to as the payment method token. This value can be - /// used instead of payment method details such as a card number and expiry date. pub id: Option>, - /// Result message from the payment method provider corresponding to the result code. pub message: Option, - /// Result code from the payment method provider. - /// If a card authorization declines, the payment_method result and message include more detail from the Issuer on why it was declined. - /// For example, 51 - INSUFFICIENT FUNDS. This is generated by the issuing bank, who will provide decline codes in the response back to the authorization platform. pub result: Option, } /// Data associated with the response of an APM transaction. #[derive(Debug, Serialize, Deserialize)] pub struct Apm { - pub bank: Option, - /// A string generated by the payment method that represents to what degree the merchant is - /// funded for the transaction. - #[serde(skip_deserializing)] - pub fund_status: Option, - pub mandate: Option, - /// A string used to identify the payment method provider being used to execute this - /// transaction. - pub provider: Option, - /// A name of the payer from the payment method system. - pub provider_payer_name: Option>, - /// The time the payment method provider created the transaction at on their system. - pub provider_time_created: Option, - /// The reference the payment method provider created for the transaction. - pub provider_transaction_reference: Option, - /// URL to redirect the payer from the merchant's system to the payment method's system. - //1)paypal sends redirect_url as provider_redirect_url for require_customer_action - //2)bankredirects sends redirect_url as redirect_url for require_customer_action - //3)after completeauthorize in paypal it doesn't send redirect_url - //4)after customer action in bankredirects it sends empty string in redirect_url #[serde(alias = "provider_redirect_url")] pub redirect_url: Option, - /// A string generated by the payment method to represent the session created on the payment - /// method's platform to facilitate the creation of a transaction. - pub session_token: Option>, - pub payment_description: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Bank { - /// The local identifier of the bank account. - pub account_number: Option>, - /// The local identifier of the bank. - pub code: Option>, - /// The international identifier of the bank account. - pub iban: Option>, - /// The international identifier code for the bank. - pub identifier_code: Option>, - /// The name associated with the bank account - pub name: Option>, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Mandate { - /// The reference to identify the mandate. - pub code: Option>, -} - -/// Information outlining the degree of authentication executed related to a transaction. -#[derive(Debug, Serialize, Deserialize)] -pub struct Authentication { - /// Information outlining the degree of 3D Secure authentication executed. - pub three_ds: Option, -} - -/// Information outlining the degree of 3D Secure authentication executed. -#[derive(Debug, Serialize, Deserialize)] -pub struct ThreeDs { - /// The result of the three_ds value validation by the brands or issuing bank. - pub value_result: Option, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct BankTransfer { - /// The last 4 characters of the local reference for a bank account number. - pub masked_number_last4: Option, - /// The name of the bank. - pub name: Option>, - /// The type of bank account associated with the payer's bank account. - pub number_type: Option, } #[derive(Debug, Serialize, Deserialize)] pub struct Card { - /// Code generated when the card is successfully authorized. - pub authcode: Option>, - /// The recommended AVS action to be taken by the agent processing the card transaction. - pub avs_action: Option, - /// The result of the AVS address check. - pub avs_address_result: Option, - /// The result of the AVS postal code check. - pub avs_postal_code_result: Option, - /// Indicates the card brand that issued the card. - pub brand: Option, - /// The unique reference created by the brands/schemes to uniquely identify the transaction. pub brand_reference: Option>, - /// The time returned by the card brand indicating when the transaction was processed on - /// their system. - pub brand_time_reference: Option, - /// The result of the CVV check. - pub cvv_result: Option, - /// Masked card number with last 4 digits showing. - pub masked_number_last4: Option, - /// The result codes directly from the card issuer. - pub provider: Option, - /// The card EMV tag response data from the card issuer for a contactless or chip card - /// transaction. - pub tag_response: Option>, -} - -/// The result codes directly from the card issuer. -#[derive(Debug, Serialize, Deserialize)] -pub struct ProviderClass { - /// The result code of the AVS address check from the card issuer. - #[serde(rename = "card.provider.avs_address_result")] - pub card_provider_avs_address_result: Option, - /// The result of the AVS postal code check from the card issuer.. - #[serde(rename = "card.provider.avs_postal_code_result")] - pub card_provider_avs_postal_code_result: Option, - /// The result code of the AVS check from the card issuer. - #[serde(rename = "card.provider.avs_result")] - pub card_provider_avs_result: Option, - /// The result code of the CVV check from the card issuer. - #[serde(rename = "card.provider.cvv_result")] - pub card_provider_cvv_result: Option, - /// Result code from the card issuer. - #[serde(rename = "card.provider.result")] - pub card_provider_result: Option, -} - -/// The result of the action executed. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ResultCode { - Declined, - Success, - Pending, - Error, -} - -/// Indicates the specific action taken. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ActionType { - Adjust, - Authorize, - Capture, - Confirm, - Force, - Increment, - Initiate, - MultipleCapture, - Preauthorize, - PreauthorizeMultipleCapturere, - Authorization, - RedirectFrom, - RedirectTo, - Refund, - Hold, - Release, - Reverse, - Split, - StatusNotification, - TransactionList, - TransactionSingle, -} - -/// A string generated by the payment method that represents to what degree the merchant is -/// funded for the transaction. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum FundStatus { - Missing, - NotExpected, - Received, - Waiting, } /// A string used to identify the payment method provider being used to execute this @@ -325,49 +62,6 @@ pub enum ApmProvider { Testpay, } -/// The type of bank account associated with the payer's bank account. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum NumberType { - Checking, - Savings, -} - -/// The recommended AVS action to be taken by the agent processing the card transaction. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum AvsAction { - Accept, - Decline, - Prompt, -} - -/// The result of the AVS address check. -/// -/// The result of the AVS postal code check. -/// -/// The result of the CVV check. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum GlobalPayResult { - Matched, - NotChecked, - NotMatched, -} - -/// Indicates the card brand that issued the card. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum Brand { - Amex, - Cup, - Diners, - Discover, - Jcb, - Mastercard, - Visa, -} - /// Indicates where a transaction is in its lifecycle. #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] @@ -417,3 +111,22 @@ pub enum GlobalpayWebhookStatus { #[serde(other)] Unknown, } + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum GlobalpayPaymentMethodStatus { + /// The entity is ACTIVE and can be used. + Active, + /// The entity is INACTIVE and cannot be used. + Inactive, + /// The status is DELETED. Once returned in an action response for a resource. + /// The resource has been removed from the platform. + Delete, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct GlobalpayPaymentMethodsResponse { + #[serde(rename = "id")] + pub payment_method_token_id: Option>, + pub card: Card, +} diff --git a/crates/hyperswitch_connectors/src/connectors/globalpay/transformers.rs b/crates/hyperswitch_connectors/src/connectors/globalpay/transformers.rs index a7fd9abd2f..e4239d2839 100644 --- a/crates/hyperswitch_connectors/src/connectors/globalpay/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/globalpay/transformers.rs @@ -8,7 +8,7 @@ use common_utils::{ use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data, - router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, + router_data::{AccessToken, ConnectorAuthType, ErrorResponse, PaymentMethodToken, RouterData}, router_flow_types::{Execute, RSync}, router_request_types::ResponseId, router_response_types::{ @@ -17,6 +17,7 @@ use hyperswitch_domain_models::{ types::{ PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, RefreshTokenRouterData, RefundExecuteRouterData, RefundsRouterData, + TokenizationRouterData, }, }; use hyperswitch_interfaces::{ @@ -37,10 +38,14 @@ use super::{ response::{GlobalpayPaymentStatus, GlobalpayPaymentsResponse, GlobalpayRefreshTokenResponse}, }; use crate::{ + connectors::globalpay::{ + requests::CommonPaymentMethodData, response::GlobalpayPaymentMethodsResponse, + }, types::{PaymentsSyncResponseRouterData, RefundsResponseRouterData, ResponseRouterData}, utils::{ self, construct_captures_response_hashmap, CardData, ForeignTryFrom, - MultipleCaptureSyncResponse, PaymentsAuthorizeRequestData, RouterData as _, WalletData, + MultipleCaptureSyncResponse, PaymentMethodTokenizationRequestData, + PaymentsAuthorizeRequestData, RouterData as _, WalletData, }, }; @@ -82,65 +87,115 @@ impl TryFrom<&Option> for GlobalPayMeta { impl TryFrom<&GlobalPayRouterData<&PaymentsAuthorizeRouterData>> for GlobalpayPaymentsRequest { type Error = Error; + fn try_from( item: &GlobalPayRouterData<&PaymentsAuthorizeRouterData>, ) -> Result { + if item.router_data.is_three_ds() { + return Err(errors::ConnectorError::NotSupported { + message: "3DS flow".to_string(), + connector: "Globalpay", + } + .into()); + } + let metadata = GlobalPayMeta::try_from(&item.router_data.connector_meta_data)?; let account_name = metadata.account_name; - let (initiator, stored_credential, brand_reference) = - get_mandate_details(item.router_data)?; - let payment_method_data = get_payment_method_data(item.router_data, brand_reference)?; + + let (initiator, stored_credential, connector_mandate_id) = + if item.router_data.request.is_mandate_payment() { + let connector_mandate_id = + item.router_data + .request + .mandate_id + .as_ref() + .and_then(|mandate_ids| match &mandate_ids.mandate_reference_id { + Some(api_models::payments::MandateReferenceId::ConnectorMandateId( + connector_mandate_ids, + )) => connector_mandate_ids.get_connector_mandate_id(), + _ => None, + }); + + let initiator = Some(match item.router_data.request.off_session { + Some(true) => Initiator::Merchant, + _ => Initiator::Payer, + }); + + let stored_credential = Some(StoredCredential { + model: Some(if connector_mandate_id.is_some() { + requests::Model::Recurring + } else { + requests::Model::Unscheduled + }), + sequence: Some(if connector_mandate_id.is_some() { + Sequence::Subsequent + } else { + Sequence::First + }), + }); + + (initiator, stored_credential, connector_mandate_id) + } else { + (None, None, None) + }; + + let payment_method = match &item.router_data.request.payment_method_data { + payment_method_data::PaymentMethodData::Card(ccard) => { + requests::GlobalPayPaymentMethodData::Common(CommonPaymentMethodData { + payment_method_data: PaymentMethodData::Card(requests::Card { + number: ccard.card_number.clone(), + expiry_month: ccard.card_exp_month.clone(), + expiry_year: ccard.get_card_expiry_year_2_digit()?, + cvv: ccard.card_cvc.clone(), + }), + entry_mode: Default::default(), + }) + } + + payment_method_data::PaymentMethodData::Wallet(wallet_data) => { + requests::GlobalPayPaymentMethodData::Common(CommonPaymentMethodData { + payment_method_data: get_wallet_data(wallet_data)?, + entry_mode: Default::default(), + }) + } + + payment_method_data::PaymentMethodData::BankRedirect(bank_redirect) => { + requests::GlobalPayPaymentMethodData::Common(CommonPaymentMethodData { + payment_method_data: PaymentMethodData::try_from(bank_redirect)?, + entry_mode: Default::default(), + }) + } + + payment_method_data::PaymentMethodData::MandatePayment => { + requests::GlobalPayPaymentMethodData::Mandate(requests::MandatePaymentMethodData { + entry_mode: Default::default(), + id: connector_mandate_id, + }) + } + + _ => Err(errors::ConnectorError::NotImplemented( + "Payment methods".to_string(), + ))?, + }; + Ok(Self { account_name, amount: Some(item.amount.to_owned()), currency: item.router_data.request.currency.to_string(), - reference: item.router_data.connector_request_reference_id.to_string(), country: item.router_data.get_billing_country()?, capture_mode: Some(requests::CaptureMode::from( item.router_data.request.capture_method, )), - payment_method: requests::PaymentMethod { - payment_method_data, - authentication: None, - encryption: None, - entry_mode: Default::default(), - fingerprint_mode: None, - first_name: None, - id: None, - last_name: None, - name: None, - narrative: None, - storage_mode: None, - }, + payment_method, notifications: Some(requests::Notifications { return_url: get_return_url(item.router_data), - challenge_return_url: None, - decoupled_challenge_return_url: None, status_url: item.router_data.request.webhook_url.clone(), - three_ds_method_return_url: None, cancel_url: get_return_url(item.router_data), }), - authorization_mode: None, - cashback_amount: None, - channel: Default::default(), - convenience_amount: None, - currency_conversion: None, - description: None, - device: None, - gratuity_amount: None, - initiator, - ip_address: None, - language: None, - lodging: None, - order: None, - payer_reference: None, - site_reference: None, stored_credential, - surcharge_amount: None, - total_capture_count: None, - globalpay_payments_request_type: None, - user_reference: None, + channel: Default::default(), + initiator, }) } } @@ -176,6 +231,32 @@ impl TryFrom<&GlobalPayRouterData<&PaymentsCaptureRouterData>> } } +impl TryFrom<&TokenizationRouterData> for requests::GlobalPayPaymentMethodsRequest { + type Error = Error; + fn try_from(item: &TokenizationRouterData) -> Result { + if !item.request.is_mandate_payment() { + return Err(errors::ConnectorError::FlowNotSupported { + flow: "Tokenization apart from Mandates".to_string(), + connector: "Globalpay".to_string(), + } + .into()); + } + Ok(Self { + reference: item.connector_request_reference_id.clone(), + usage_mode: Some(requests::UsageMode::Multiple), + card: match &item.request.payment_method_data { + payment_method_data::PaymentMethodData::Card(card_data) => Some(requests::Card { + number: card_data.card_number.clone(), + expiry_month: card_data.card_exp_month.clone(), + expiry_year: card_data.get_card_expiry_year_2_digit()?, + cvv: card_data.card_cvc.clone(), + }), + _ => None, + }, + }) + } +} + impl TryFrom<&GlobalpayCancelRouterData<&PaymentsCancelRouterData>> for requests::GlobalpayCancelRequest { @@ -278,73 +359,18 @@ impl From> for requests::CaptureMode { } } -fn get_payment_response( - status: common_enums::AttemptStatus, - response: GlobalpayPaymentsResponse, - redirection_data: Option, - status_code: u16, -) -> Result> { - let mandate_reference = response.payment_method.as_ref().and_then(|pm| { - pm.card - .as_ref() - .and_then(|card| card.brand_reference.to_owned()) - .map(|id| MandateReference { - connector_mandate_id: Some(id.expose()), - payment_method_id: None, - mandate_metadata: None, - connector_mandate_request_reference_id: None, - }) - }); - match status { - common_enums::AttemptStatus::Failure => Err(Box::new(ErrorResponse { - message: response - .payment_method - .as_ref() - .and_then(|payment_method| payment_method.message.clone()) - .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()), - code: response - .payment_method - .as_ref() - .and_then(|payment_method| payment_method.result.clone()) - .unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()), - reason: response - .payment_method - .as_ref() - .and_then(|payment_method| payment_method.message.clone()), - status_code, - attempt_status: Some(status), - connector_transaction_id: Some(response.id), - network_decline_code: response - .payment_method - .as_ref() - .and_then(|payment_method| payment_method.result.clone()), - network_advice_code: None, - network_error_message: response - .payment_method - .as_ref() - .and_then(|payment_method| payment_method.message.clone()), - })), - _ => Ok(PaymentsResponseData::TransactionResponse { - resource_id: ResponseId::ConnectorTransactionId(response.id.clone()), - redirection_data: Box::new(redirection_data), - mandate_reference: Box::new(mandate_reference), - connector_metadata: None, - network_txn_id: None, - connector_response_reference_id: response.reference, - incremental_authorization_allowed: None, - charges: None, - }), - } -} +// } impl TryFrom> for RouterData { type Error = Error; + fn try_from( item: ResponseRouterData, ) -> Result { let status = common_enums::AttemptStatus::from(item.response.status); + let redirect_url = item .response .payment_method @@ -360,12 +386,92 @@ impl TryFrom Some(MandateReference { + connector_mandate_id: Some(token_string.clone().expose()), + payment_method_id: None, + mandate_metadata: None, + connector_mandate_request_reference_id: None, + }), + _ => None, + }); + + let response = match status { + common_enums::AttemptStatus::Failure => Err(Box::new(ErrorResponse { + message: item + .response + .payment_method + .as_ref() + .and_then(|payment_method| payment_method.message.clone()) + .unwrap_or_else(|| NO_ERROR_MESSAGE.to_string()), + code: item + .response + .payment_method + .as_ref() + .and_then(|payment_method| payment_method.result.clone()) + .unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()), + reason: item + .response + .payment_method + .as_ref() + .and_then(|payment_method| payment_method.message.clone()), + status_code, + attempt_status: Some(status), + connector_transaction_id: Some(item.response.id.clone()), + network_decline_code: item + .response + .payment_method + .as_ref() + .and_then(|payment_method| payment_method.result.clone()), + network_advice_code: None, + network_error_message: item + .response + .payment_method + .as_ref() + .and_then(|payment_method| payment_method.message.clone()), + })), + _ => Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(item.response.id.clone()), + redirection_data: Box::new(redirection_data), + mandate_reference: Box::new(mandate_reference), + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: item.response.reference.clone(), + incremental_authorization_allowed: None, + charges: None, + }), + }; + Ok(Self { status, - response: get_payment_response(status, item.response, redirection_data, status_code) - .map_err(|err| *err), + response: response.map_err(|err| *err), + ..item.data + }) + } +} + +impl TryFrom> + for RouterData +{ + type Error = Error; + fn try_from( + item: ResponseRouterData, + ) -> Result { + let token = item + .response + .payment_method_token_id + .clone() + .unwrap_or_default(); + + Ok(Self { + response: Ok(PaymentsResponseData::TokenizationResponse { + token: token.expose(), + }), ..item.data }) } @@ -467,39 +573,6 @@ pub struct GlobalpayErrorResponse { pub detailed_error_description: String, } -fn get_payment_method_data( - item: &PaymentsAuthorizeRouterData, - brand_reference: Option, -) -> Result { - match &item.request.payment_method_data { - payment_method_data::PaymentMethodData::Card(ccard) => { - Ok(PaymentMethodData::Card(requests::Card { - number: ccard.card_number.clone(), - expiry_month: ccard.card_exp_month.clone(), - expiry_year: ccard.get_card_expiry_year_2_digit()?, - cvv: ccard.card_cvc.clone(), - account_type: None, - authcode: None, - avs_address: None, - avs_postal_code: None, - brand_reference, - chip_condition: None, - funding: None, - pin_block: None, - tag: None, - track: None, - })) - } - payment_method_data::PaymentMethodData::Wallet(wallet_data) => get_wallet_data(wallet_data), - payment_method_data::PaymentMethodData::BankRedirect(bank_redirect) => { - PaymentMethodData::try_from(bank_redirect) - } - _ => Err(errors::ConnectorError::NotImplemented( - "Payment methods".to_string(), - ))?, - } -} - fn get_return_url(item: &PaymentsAuthorizeRouterData) -> Option { match item.request.payment_method_data.clone() { payment_method_data::PaymentMethodData::Wallet( @@ -516,36 +589,6 @@ fn get_return_url(item: &PaymentsAuthorizeRouterData) -> Option { } } -type MandateDetails = (Option, Option, Option); -fn get_mandate_details(item: &PaymentsAuthorizeRouterData) -> Result { - Ok(if item.request.is_mandate_payment() { - let connector_mandate_id = item.request.mandate_id.as_ref().and_then(|mandate_ids| { - match mandate_ids.mandate_reference_id.clone() { - Some(api_models::payments::MandateReferenceId::ConnectorMandateId( - connector_mandate_ids, - )) => connector_mandate_ids.get_connector_mandate_id(), - _ => None, - } - }); - ( - Some(match item.request.off_session { - Some(true) => Initiator::Merchant, - _ => Initiator::Payer, - }), - Some(StoredCredential { - model: Some(requests::Model::Recurring), - sequence: Some(match connector_mandate_id.is_some() { - true => Sequence::Subsequent, - false => Sequence::First, - }), - }), - connector_mandate_id, - ) - } else { - (None, None, None) - }) -} - fn get_wallet_data( wallet_data: &payment_method_data::WalletData, ) -> Result { diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index 7b9b314ce5..428329680c 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -2191,6 +2191,7 @@ impl PaymentsSetupMandateRequestData for SetupMandateRequestData { pub trait PaymentMethodTokenizationRequestData { fn get_browser_info(&self) -> Result; + fn is_mandate_payment(&self) -> bool; } impl PaymentMethodTokenizationRequestData for PaymentMethodTokenizationData { @@ -2199,6 +2200,15 @@ impl PaymentMethodTokenizationRequestData for PaymentMethodTokenizationData { .clone() .ok_or_else(missing_field_err("browser_info")) } + fn is_mandate_payment(&self) -> bool { + ((self.customer_acceptance.is_some() || self.setup_mandate_details.is_some()) + && (self.setup_future_usage == Some(FutureUsage::OffSession))) + || self + .mandate_id + .as_ref() + .and_then(|mandate_ids| mandate_ids.mandate_reference_id.as_ref()) + .is_some() + } } pub trait PaymentsCompleteAuthorizeRequestData { diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index c7ac366483..1c238ae50d 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -255,6 +255,10 @@ pub struct PaymentMethodTokenizationData { pub currency: storage_enums::Currency, pub amount: Option, pub split_payments: Option, + pub customer_acceptance: Option, + pub setup_future_usage: Option, + pub setup_mandate_details: Option, + pub mandate_id: Option, } impl TryFrom for PaymentMethodTokenizationData { @@ -267,6 +271,10 @@ impl TryFrom for PaymentMethodTokenizationData { currency: data.currency, amount: data.amount, split_payments: None, + customer_acceptance: data.customer_acceptance, + setup_future_usage: data.setup_future_usage, + setup_mandate_details: data.setup_mandate_details, + mandate_id: data.mandate_id, }) } } @@ -282,6 +290,10 @@ impl From<&RouterData for PaymentMethodTokenizationData { currency: data.currency, amount: Some(data.amount), split_payments: data.split_payments.clone(), + customer_acceptance: data.customer_acceptance, + setup_future_usage: data.setup_future_usage, + setup_mandate_details: data.setup_mandate_details, + mandate_id: data.mandate_id, }) } } @@ -315,6 +331,10 @@ impl TryFrom for PaymentMethodTokenizationData { currency: data.currency, amount: Some(data.amount), split_payments: None, + customer_acceptance: data.customer_acceptance, + setup_future_usage: data.setup_future_usage, + setup_mandate_details: data.setup_mandate_details, + mandate_id: data.mandate_id, }) } } diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index ae4c8a3cda..f381712ddf 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -620,6 +620,13 @@ pub struct PaymentMethodTokenFilter { pub payment_method_type: Option, pub long_lived_token: bool, pub apple_pay_pre_decrypt_flow: Option, + pub flow: Option, +} + +#[derive(Debug, Deserialize, Clone, PartialEq)] +#[serde(deny_unknown_fields, rename_all = "snake_case")] +pub enum PaymentFlow { + Mandates, } #[derive(Debug, Deserialize, Clone, Default)] diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 4a23f04cda..b9d1712924 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -3249,6 +3249,10 @@ async fn create_single_use_tokenization_flow( currency: api_models::enums::Currency::default(), amount: None, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }; let payment_method_session_address = types::PaymentAddress::new( @@ -3317,12 +3321,12 @@ async fn create_single_use_tokenization_flow( is_payment_id_from_merchant: None, }; - let payment_method_token_response = tokenization::add_token_for_payment_method( + let payment_method_token_response = Box::pin(tokenization::add_token_for_payment_method( &mut router_data, payment_method_data_request.clone(), state.clone(), &merchant_connector_account_details.clone(), - ) + )) .await?; let token_response = payment_method_token_response.token.map_err(|err| { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 5651dae946..a912dc27b8 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -106,7 +106,7 @@ use crate::core::routing::helpers as routing_helpers; #[cfg(all(feature = "v1", feature = "dynamic_routing"))] use crate::types::api::convert_connector_data_to_routable_connectors; use crate::{ - configs::settings::{ApplePayPreDecryptFlow, PaymentMethodTypeTokenFilter}, + configs::settings::{ApplePayPreDecryptFlow, PaymentFlow, PaymentMethodTypeTokenFilter}, consts, core::{ errors::{self, CustomResult, RouterResponse, RouterResult}, @@ -6157,6 +6157,7 @@ fn is_payment_method_tokenization_enabled_for_connector( payment_method: storage::enums::PaymentMethod, payment_method_type: Option, payment_method_token: Option<&PaymentMethodToken>, + mandate_flow_enabled: Option, ) -> RouterResult { let connector_tokenization_filter = state.conf.tokenization.0.get(connector_name); @@ -6175,10 +6176,29 @@ fn is_payment_method_tokenization_enabled_for_connector( payment_method_token, connector_filter.apple_pay_pre_decrypt_flow.clone(), ) + && is_payment_flow_allowed_for_connector( + mandate_flow_enabled, + connector_filter.flow.clone(), + ) }) .unwrap_or(false)) } +fn is_payment_flow_allowed_for_connector( + mandate_flow_enabled: Option, + payment_flow: Option, +) -> bool { + if payment_flow.is_none() { + true + } else { + matches!(payment_flow, Some(PaymentFlow::Mandates)) + && matches!( + mandate_flow_enabled, + Some(storage_enums::FutureUsage::OffSession) + ) + } +} + fn is_apple_pay_pre_decrypt_type_connector_tokenization( payment_method_type: Option, payment_method_token: Option<&PaymentMethodToken>, @@ -6504,6 +6524,10 @@ where .get_required_value("payment_method")?; let payment_method_type = payment_data.get_payment_attempt().payment_method_type; + let mandate_flow_enabled = payment_data + .get_payment_attempt() + .setup_future_usage_applied; + let is_connector_tokenization_enabled = is_payment_method_tokenization_enabled_for_connector( state, @@ -6511,6 +6535,7 @@ where payment_method, payment_method_type, payment_data.get_payment_method_token(), + mandate_flow_enabled, )?; let payment_method_action = decide_payment_method_tokenize_action( @@ -8349,7 +8374,7 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to get the common mandate reference")?; - let connector_mandate_details = connector_common_mandate_details.payments; + let connector_mandate_details = connector_common_mandate_details.payments.clone(); let mut connector_choice = None; diff --git a/crates/router/tests/connectors/square.rs b/crates/router/tests/connectors/square.rs index 81ea811227..760e8ebdae 100644 --- a/crates/router/tests/connectors/square.rs +++ b/crates/router/tests/connectors/square.rs @@ -68,6 +68,10 @@ fn token_details() -> Option { amount: None, currency: enums::Currency::USD, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }) } @@ -442,6 +446,10 @@ async fn should_fail_payment_for_incorrect_cvc() { amount: None, currency: enums::Currency::USD, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }), get_default_payment_info(None), ) @@ -474,6 +482,10 @@ async fn should_fail_payment_for_invalid_exp_month() { amount: None, currency: enums::Currency::USD, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }), get_default_payment_info(None), ) @@ -506,6 +518,10 @@ async fn should_fail_payment_for_incorrect_expiry_year() { amount: None, currency: enums::Currency::USD, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }), get_default_payment_info(None), ) diff --git a/crates/router/tests/connectors/stax.rs b/crates/router/tests/connectors/stax.rs index 93c2394354..89b1bc7f4b 100644 --- a/crates/router/tests/connectors/stax.rs +++ b/crates/router/tests/connectors/stax.rs @@ -73,6 +73,10 @@ fn token_details() -> Option { amount: None, currency: enums::Currency::USD, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }) } @@ -484,6 +488,10 @@ async fn should_fail_payment_for_incorrect_cvc() { amount: None, currency: enums::Currency::USD, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }), get_default_payment_info(connector_customer_id, None), ) @@ -523,6 +531,10 @@ async fn should_fail_payment_for_invalid_exp_month() { amount: None, currency: enums::Currency::USD, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }), get_default_payment_info(connector_customer_id, None), ) @@ -562,6 +574,10 @@ async fn should_fail_payment_for_incorrect_expiry_year() { amount: None, currency: enums::Currency::USD, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }), get_default_payment_info(connector_customer_id, None), ) diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index 65ca7fc70b..fc26835501 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -1122,6 +1122,10 @@ impl Default for TokenType { amount: Some(100), currency: enums::Currency::USD, split_payments: None, + mandate_id: None, + setup_future_usage: None, + customer_acceptance: None, + setup_mandate_details: None, }; Self(data) } diff --git a/cypress-tests/cypress/e2e/configs/Payment/Globalpay.js b/cypress-tests/cypress/e2e/configs/Payment/Globalpay.js index a0a2e8a2fe..f3babe9a55 100644 --- a/cypress-tests/cypress/e2e/configs/Payment/Globalpay.js +++ b/cypress-tests/cypress/e2e/configs/Payment/Globalpay.js @@ -4,10 +4,10 @@ import { getCustomExchange } from "./Modifiers"; // Test card details for successful non-3DS transactions // Based on Global Payments test cards const successfulNo3DSCardDetails = { - card_number: "4263970000005262", + card_number: "4111111111111111", card_exp_month: "12", card_exp_year: "2030", - card_holder_name: "Test Customer", + card_holder_name: "joseph Doe", card_cvc: "123", }; @@ -48,16 +48,16 @@ const multiUseMandateData = { // Payment method data for non-3DS card transactions const payment_method_data_no3ds = { card: { - last4: "5262", + last4: "1111", card_type: "CREDIT", card_network: "Visa", - card_issuer: "ALLIED IRISH BANKS, P.L.C.", - card_issuing_country: "IRELAND", - card_isin: "426397", + card_issuer: "JP Morgan", + card_issuing_country: "INDIA", + card_isin: "411111", card_extended_bin: null, card_exp_month: "12", card_exp_year: "2030", - card_holder_name: "Test Customer", + card_holder_name: "joseph Doe", payment_checks: null, authentication_data: null, }, @@ -262,15 +262,14 @@ export const connectorDetails = { setup_future_usage: "on_session", }, Response: { - status: 200, + status: 400, body: { - status: "failed", - error_code: "feature_not_supported", - error_message: - "3DS authentication is not supported by Globalpay connector", - unified_code: "UE_5004", - unified_message: - "The operation requested is not supported by the payment processor", + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "IR_19", + reason: "3DS flow is not supported by Globalpay", + }, }, }, }, @@ -286,15 +285,14 @@ export const connectorDetails = { setup_future_usage: "on_session", }, Response: { - status: 200, + status: 400, body: { - status: "failed", - error_code: "feature_not_supported", - error_message: - "3DS authentication is not supported by Globalpay connector", - unified_code: "UE_5004", - unified_message: - "The operation requested is not supported by the payment processor", + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "IR_19", + reason: "3DS flow is not supported by Globalpay", + }, }, }, }, @@ -412,7 +410,6 @@ export const connectorDetails = { status: 200, body: { status: "succeeded", - setup_future_usage: "off_session", }, }, }, @@ -429,7 +426,6 @@ export const connectorDetails = { status: 200, body: { status: "requires_capture", - setup_future_usage: "off_session", }, }, }, @@ -446,7 +442,6 @@ export const connectorDetails = { status: 200, body: { status: "succeeded", - setup_future_usage: "off_session", }, }, }, @@ -463,7 +458,6 @@ export const connectorDetails = { status: 200, body: { status: "requires_capture", - setup_future_usage: "off_session", }, }, }, @@ -478,15 +472,14 @@ export const connectorDetails = { authentication_type: "three_ds", }, Response: { - status: 200, + status: 400, body: { - status: "failed", - error_code: "feature_not_supported", - error_message: - "3DS authentication is not supported by Globalpay connector", - unified_code: "UE_5004", - unified_message: - "The operation requested is not supported by the payment processor", + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "IR_19", + reason: "3DS flow is not supported by Globalpay", + }, }, }, }, @@ -501,15 +494,14 @@ export const connectorDetails = { authentication_type: "three_ds", }, Response: { - status: 200, + status: 400, body: { - status: "failed", - error_code: "feature_not_supported", - error_message: - "3DS authentication is not supported by Globalpay connector", - unified_code: "UE_5004", - unified_message: - "The operation requested is not supported by the payment processor", + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "IR_19", + reason: "3DS flow is not supported by Globalpay", + }, }, }, }, @@ -524,15 +516,14 @@ export const connectorDetails = { authentication_type: "three_ds", }, Response: { - status: 200, + status: 400, body: { - status: "failed", - error_code: "feature_not_supported", - error_message: - "3DS authentication is not supported by Globalpay connector", - unified_code: "UE_5004", - unified_message: - "The operation requested is not supported by the payment processor", + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "IR_19", + reason: "3DS flow is not supported by Globalpay", + }, }, }, }, @@ -547,15 +538,14 @@ export const connectorDetails = { authentication_type: "three_ds", }, Response: { - status: 200, + status: 400, body: { - status: "failed", - error_code: "feature_not_supported", - error_message: - "3DS authentication is not supported by Globalpay connector", - unified_code: "UE_5004", - unified_message: - "The operation requested is not supported by the payment processor", + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "IR_19", + reason: "3DS flow is not supported by Globalpay", + }, }, }, }, @@ -568,7 +558,6 @@ export const connectorDetails = { status: 200, body: { status: "succeeded", - setup_future_usage: "off_session", }, }, }, @@ -581,7 +570,6 @@ export const connectorDetails = { status: 200, body: { status: "requires_capture", - setup_future_usage: "off_session", }, }, }, @@ -626,6 +614,7 @@ export const connectorDetails = { ZeroAuthConfirmPayment: { Request: { payment_type: "setup_mandate", + setup_future_usage: "off_session", payment_method: "card", payment_method_type: "credit", payment_method_data: { @@ -689,15 +678,14 @@ export const connectorDetails = { authentication_type: "three_ds", }, Response: { - status: 200, + status: 400, body: { - status: "failed", - error_code: "feature_not_supported", - error_message: - "3DS authentication is not supported by Globalpay connector", - unified_code: "UE_5004", - unified_message: - "The operation requested is not supported by the payment processor", + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "IR_19", + reason: "3DS flow is not supported by Globalpay", + }, }, }, }, @@ -713,15 +701,14 @@ export const connectorDetails = { authentication_type: "three_ds", }, Response: { - status: 200, + status: 400, body: { - status: "failed", - error_code: "feature_not_supported", - error_message: - "3DS authentication is not supported by Globalpay connector", - unified_code: "UE_5004", - unified_message: - "The operation requested is not supported by the payment processor", + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "IR_19", + reason: "3DS flow is not supported by Globalpay", + }, }, }, }, diff --git a/cypress-tests/cypress/e2e/configs/Payment/Utils.js b/cypress-tests/cypress/e2e/configs/Payment/Utils.js index 7065f127fd..f16fa38580 100644 --- a/cypress-tests/cypress/e2e/configs/Payment/Utils.js +++ b/cypress-tests/cypress/e2e/configs/Payment/Utils.js @@ -380,6 +380,7 @@ export const CONNECTOR_LISTS = { "facilitapay", "fiserv", "fiuu", + "globalpay", "jpmorgan", "nexinets", "payload", diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index 14517e55ad..08904ba688 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -595,6 +595,7 @@ mollie = { long_lived_token = false, payment_method = "card" } braintree = { long_lived_token = false, payment_method = "card" } gocardless = { long_lived_token = true, payment_method = "bank_debit" } billwerk = { long_lived_token = false, payment_method = "card" } +globalpay = { long_lived_token = false, payment_method = "card", flow = "mandates" } [connector_customer] connector_list = "adyen,facilitapay,gocardless,hyperswitch_vault,stax,stripe"