diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 17b44a9810..f6f7408e3d 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -5379,6 +5379,7 @@ "multisafepay", "netcetera", "nexinets", + "nexixpay", "nmi", "noon", "novalnet", @@ -18570,6 +18571,7 @@ "mollie", "multisafepay", "nexinets", + "nexixpay", "nmi", "noon", "novalnet", diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index d91dbb9f82..d245600b81 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -8711,6 +8711,7 @@ "multisafepay", "netcetera", "nexinets", + "nexixpay", "nmi", "noon", "novalnet", @@ -22351,6 +22352,7 @@ "mollie", "multisafepay", "nexinets", + "nexixpay", "nmi", "noon", "novalnet", diff --git a/config/config.example.toml b/config/config.example.toml index a6aeba7427..b1c66be656 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -416,6 +416,7 @@ nmi = { payment_method = "card" } payme = { payment_method = "card" } deutschebank = { payment_method = "bank_debit" } paybox = { payment_method = "card" } +nexixpay = { payment_method = "card" } [dummy_connector] enabled = true # Whether dummy connector is enabled or not @@ -553,6 +554,10 @@ debit = { currency = "USD" } [pm_filters.klarna] klarna = { country = "AU,AT,BE,CA,CZ,DK,FI,FR,DE,GR,IE,IT,NL,NZ,NO,PL,PT,ES,SE,CH,GB,US", currency = "CHF,DKK,EUR,GBP,NOK,PLN,SEK,USD,AUD,NZD,CAD" } +[pm_filters.nexixpay] +credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } + [pm_filters.mifinity] mifinity = { country = "BR,CN,SG,MY,DE,CH,DK,GB,ES,AD,GI,FI,FR,GR,HR,IT,JP,MX,AR,CO,CL,PE,VE,UY,PY,BO,EC,GT,HN,SV,NI,CR,PA,DO,CU,PR,NL,NO,PL,PT,SE,RU,TR,TW,HK,MO,AX,AL,DZ,AS,AO,AI,AG,AM,AW,AU,AT,AZ,BS,BH,BD,BB,BE,BZ,BJ,BM,BT,BQ,BA,BW,IO,BN,BG,BF,BI,KH,CM,CA,CV,KY,CF,TD,CX,CC,KM,CG,CK,CI,CW,CY,CZ,DJ,DM,EG,GQ,ER,EE,ET,FK,FO,FJ,GF,PF,TF,GA,GM,GE,GH,GL,GD,GP,GU,GG,GN,GW,GY,HT,HM,VA,IS,IN,ID,IE,IM,IL,JE,JO,KZ,KE,KI,KW,KG,LA,LV,LB,LS,LI,LT,LU,MK,MG,MW,MV,ML,MT,MH,MQ,MR,MU,YT,FM,MD,MC,MN,ME,MS,MA,MZ,NA,NR,NP,NC,NZ,NE,NG,NU,NF,MP,OM,PK,PW,PS,PG,PH,PN,QA,RE,RO,RW,BL,SH,KN,LC,MF,PM,VC,WS,SM,ST,SA,SN,RS,SC,SL,SX,SK,SI,SB,SO,ZA,GS,KR,LK,SR,SJ,SZ,TH,TL,TG,TK,TO,TT,TN,TM,TC,TV,UG,UA,AE,UZ,VU,VN,VG,VI,WF,EH,ZM", currency = "AUD,CAD,CHF,CNY,CZK,DKK,EUR,GBP,INR,JPY,NOK,NZD,PLN,RUB,SEK,ZAR,USD,EGP,UYU,UZS" } diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 35385af597..aa97642f4f 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -307,6 +307,10 @@ apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } samsung_pay = { currency = "USD,GBP,EUR" } +[pm_filters.nexixpay] +credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } + [pm_filters.volt] open_banking_uk = {country = "DE,GB,AT,BE,CY,EE,ES,FI,FR,GR,HR,IE,IT,LT,LU,LV,MT,NL,PT,SI,SK,BG,CZ,DK,HU,NO,PL,RO,SE,AU,BR", currency = "EUR,GBP,DKK,NOK,PLN,SEK,AUD,BRL"} @@ -350,6 +354,7 @@ nmi.payment_method = "card" payme.payment_method = "card" deutschebank = { payment_method = "bank_debit" } paybox = { payment_method = "card" } +nexixpay = { payment_method = "card" } #tokenization configuration which describe token lifetime and payment method for specific connector [tokenization] diff --git a/config/deployments/production.toml b/config/deployments/production.toml index cab8317dfa..294b57f243 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -281,6 +281,10 @@ apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } samsung_pay = { currency = "USD,GBP,EUR" } +[pm_filters.nexixpay] +credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } + [pm_filters.braintree] paypal.currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" @@ -363,6 +367,7 @@ nmi.payment_method = "card" payme.payment_method = "card" deutschebank = { payment_method = "bank_debit" } paybox = { payment_method = "card" } +nexixpay = { payment_method = "card" } #tokenization configuration which describe token lifetime and payment method for specific connector [tokenization] diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 78972c0b29..701f1a79c9 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -284,6 +284,10 @@ apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } samsung_pay = { currency = "USD,GBP,EUR" } +[pm_filters.nexixpay] +credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } + [pm_filters.braintree] paypal.currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" @@ -367,6 +371,7 @@ nmi.payment_method = "card" payme.payment_method = "card" deutschebank = { payment_method = "bank_debit" } paybox = { payment_method = "card" } +nexixpay = { payment_method = "card" } #tokenization configuration which describe token lifetime and payment method for specific connector [tokenization] diff --git a/config/development.toml b/config/development.toml index d723e09ec4..40ec1c6016 100644 --- a/config/development.toml +++ b/config/development.toml @@ -452,6 +452,10 @@ apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } samsung_pay = { currency = "USD,GBP,EUR" } +[pm_filters.nexixpay] +credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } + [pm_filters.braintree] paypal = { currency = "AUD,BRL,CAD,CNY,CZK,DKK,EUR,HKD,HUF,ILS,JPY,MYR,MXN,TWD,NZD,NOK,PHP,PLN,GBP,RUB,SGD,SEK,CHF,THB,USD" } @@ -544,6 +548,7 @@ nmi = { payment_method = "card" } payme = { payment_method = "card" } deutschebank = { payment_method = "bank_debit" } paybox = { payment_method = "card" } +nexixpay = { payment_method = "card" } [connector_customer] connector_list = "gocardless,stax,stripe" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index fad5759648..b3845ead24 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -320,6 +320,7 @@ nmi = { payment_method = "card" } payme = { payment_method = "card" } deutschebank = { payment_method = "bank_debit" } paybox = { payment_method = "card" } +nexixpay = { payment_method = "card" } [dummy_connector] enabled = true @@ -418,6 +419,10 @@ apple_pay = { currency = "USD,GBP,EUR" } google_pay = { currency = "USD,GBP,EUR" } samsung_pay = { currency = "USD,GBP,EUR" } +[pm_filters.nexixpay] +credit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } +debit = { country = "AT,BE,CY,EE,FI,FR,DE,GR,IE,IT,LV,LT,LU,MT,NL,PT,SK,SI,ES,BG,HR,DK,GB,NO,PL,CZ,RO,SE,CH,HU", currency = "ARS,AUD,BHD,CAD,CLP,CNY,COP,HRK,CZK,DKK,HKD,HUF,INR,JPY,KZT,JOD,KRW,KWD,MYR,MXN,NGN,NOK,PHP,QAR,RUB,SAR,SGD,VND,ZAR,SEK,CHF,THB,AED,EGP,GBP,USD,TWD,BYN,RSD,AZN,RON,TRY,AOA,BGN,EUR,UAH,PLN,BRL" } + [pm_filters.helcim] credit = { currency = "USD" } debit = { currency = "USD" } diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index bb2860acb0..e01e7a529d 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -46,7 +46,6 @@ pub enum RoutingAlgorithm { #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] pub enum Connector { - // Nexixpay, Adyenplatform, #[cfg(feature = "dummy_connector")] #[serde(rename = "phonypay")] @@ -114,6 +113,7 @@ pub enum Connector { Multisafepay, Netcetera, Nexinets, + Nexixpay, Nmi, Noon, Novalnet, @@ -215,7 +215,6 @@ impl Connector { | Self::DummyConnector7 => false, Self::Aci // Add Separate authentication support for connectors - // | Self::Nexixpay // | Self::Fiuu | Self::Adyen | Self::Adyenplatform @@ -251,6 +250,7 @@ impl Connector { | Self::Mollie | Self::Multisafepay | Self::Nexinets + | Self::Nexixpay | Self::Novalnet | Self::Nuvei | Self::Opennode diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 067ef1000a..94955bb3da 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -160,7 +160,6 @@ pub enum AttemptStatus { #[strum(serialize_all = "snake_case")] /// Connectors eligible for payments routing pub enum RoutableConnectors { - // Nexixpay, Adyenplatform, #[cfg(feature = "dummy_connector")] #[serde(rename = "phonypay")] @@ -226,6 +225,7 @@ pub enum RoutableConnectors { Mollie, Multisafepay, Nexinets, + Nexixpay, Nmi, Noon, Novalnet, diff --git a/crates/connector_configs/src/connector.rs b/crates/connector_configs/src/connector.rs index 0c64f134b1..4401219d7e 100644 --- a/crates/connector_configs/src/connector.rs +++ b/crates/connector_configs/src/connector.rs @@ -188,6 +188,7 @@ pub struct ConnectorConfig { pub mollie: Option, pub multisafepay: Option, pub nexinets: Option, + pub nexixpay: Option, pub nmi: Option, pub noon: Option, pub novalnet: Option, @@ -349,6 +350,7 @@ impl ConnectorConfig { Connector::Mollie => Ok(connector_data.mollie), Connector::Multisafepay => Ok(connector_data.multisafepay), Connector::Nexinets => Ok(connector_data.nexinets), + Connector::Nexixpay => Ok(connector_data.nexixpay), Connector::Prophetpay => Ok(connector_data.prophetpay), Connector::Nmi => Ok(connector_data.nmi), Connector::Novalnet => Ok(connector_data.novalnet), diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index abe25e5c2b..2fefc5c957 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -1941,6 +1941,26 @@ required=true type="Radio" options=["Connector","Hyperswitch"] +[nexixpay] +[[nexixpay.credit]] + payment_method_type = "Mastercard" +[[nexixpay.credit]] + payment_method_type = "Visa" +[[nexixpay.credit]] + payment_method_type = "AmericanExpress" +[[nexixpay.credit]] + payment_method_type = "JCB" +[[nexixpay.debit]] + payment_method_type = "Mastercard" +[[nexixpay.debit]] + payment_method_type = "Visa" +[[nexixpay.debit]] + payment_method_type = "AmericanExpress" +[[nexixpay.debit]] + payment_method_type = "JCB" +[nexixpay.connector_auth.HeaderKey] +api_key="API Key" + [nmi] [[nmi.credit]] payment_method_type = "Mastercard" diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index 32d6d96bb9..660dd4fea2 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -1682,6 +1682,26 @@ required=true type="Radio" options=["Connector","Hyperswitch"] +[nexixpay] +[[nexixpay.credit]] + payment_method_type = "Mastercard" +[[nexixpay.credit]] + payment_method_type = "Visa" +[[nexixpay.credit]] + payment_method_type = "AmericanExpress" +[[nexixpay.credit]] + payment_method_type = "JCB" +[[nexixpay.debit]] + payment_method_type = "Mastercard" +[[nexixpay.debit]] + payment_method_type = "Visa" +[[nexixpay.debit]] + payment_method_type = "AmericanExpress" +[[nexixpay.debit]] + payment_method_type = "JCB" +[nexixpay.connector_auth.HeaderKey] +api_key="API Key" + [nmi] [[nmi.credit]] payment_method_type = "Mastercard" diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index b4de938bc8..2e27ff2bd1 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -1939,6 +1939,26 @@ required=true type="Radio" options=["Connector","Hyperswitch"] +[nexixpay] +[[nexixpay.credit]] + payment_method_type = "Mastercard" +[[nexixpay.credit]] + payment_method_type = "Visa" +[[nexixpay.credit]] + payment_method_type = "AmericanExpress" +[[nexixpay.credit]] + payment_method_type = "JCB" +[[nexixpay.debit]] + payment_method_type = "Mastercard" +[[nexixpay.debit]] + payment_method_type = "Visa" +[[nexixpay.debit]] + payment_method_type = "AmericanExpress" +[[nexixpay.debit]] + payment_method_type = "JCB" +[nexixpay.connector_auth.HeaderKey] +api_key="API Key" + [nmi] [[nmi.credit]] payment_method_type = "Mastercard" diff --git a/crates/hyperswitch_connectors/src/connectors/nexixpay.rs b/crates/hyperswitch_connectors/src/connectors/nexixpay.rs index b660445f3c..7608532416 100644 --- a/crates/hyperswitch_connectors/src/connectors/nexixpay.rs +++ b/crates/hyperswitch_connectors/src/connectors/nexixpay.rs @@ -1,5 +1,6 @@ pub mod transformers; +use common_enums::enums; use common_utils::{ errors::CustomResult, ext_traits::BytesExt, @@ -11,32 +12,42 @@ use hyperswitch_domain_models::{ router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, router_flow_types::{ access_token_auth::AccessTokenAuth, - payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void}, + payments::{ + Authorize, Capture, CompleteAuthorize, PSync, PaymentMethodToken, PreProcessing, + Session, SetupMandate, Void, + }, refunds::{Execute, RSync}, }, router_request_types::{ - AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData, - PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, - RefundsData, SetupMandateRequestData, + AccessTokenRequestData, CompleteAuthorizeData, PaymentMethodTokenizationData, + PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsPreProcessingData, + PaymentsSessionData, PaymentsSyncData, RefundsData, SetupMandateRequestData, }, router_response_types::{PaymentsResponseData, RefundsResponseData}, types::{ - PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, - RefundSyncRouterData, RefundsRouterData, + PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData, + PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData, + PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData, }, }; use hyperswitch_interfaces::{ api::{self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorValidation}, configs::Connectors, - errors, + consts, errors, events::connector_api_logs::ConnectorEvent, types::{self, Response}, webhooks, }; use masking::{ExposeInterface, Mask}; +use serde_json::Value; use transformers as nexixpay; +use uuid::Uuid; -use crate::{constants::headers, types::ResponseRouterData, utils}; +use crate::{ + constants::headers, + types::ResponseRouterData, + utils::{self, RefundsRequestData}, +}; #[derive(Clone)] pub struct Nexixpay { @@ -52,6 +63,7 @@ impl Nexixpay { } impl api::Payment for Nexixpay {} +impl api::PaymentsPreProcessing for Nexixpay {} impl api::PaymentSession for Nexixpay {} impl api::ConnectorAccessToken for Nexixpay {} impl api::MandateSetup for Nexixpay {} @@ -63,6 +75,7 @@ impl api::Refund for Nexixpay {} impl api::RefundExecute for Nexixpay {} impl api::RefundSync for Nexixpay {} impl api::PaymentToken for Nexixpay {} +impl api::PaymentsCompleteAuthorize for Nexixpay {} impl ConnectorIntegration for Nexixpay @@ -95,10 +108,7 @@ impl ConnectorCommon for Nexixpay { } fn get_currency_unit(&self) -> api::CurrencyUnit { - api::CurrencyUnit::Base - // TODO! Check connector documentation, on which unit they are processing the currency. - // If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor, - // if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base + api::CurrencyUnit::Minor } fn common_get_content_type(&self) -> &'static str { @@ -115,10 +125,16 @@ impl ConnectorCommon for Nexixpay { ) -> CustomResult)>, errors::ConnectorError> { let auth = nexixpay::NexixpayAuthType::try_from(auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; - Ok(vec![( - headers::AUTHORIZATION.to_string(), - auth.api_key.expose().into_masked(), - )]) + Ok(vec![ + ( + headers::X_API_KEY.to_string(), + auth.api_key.expose().into_masked(), + ), + ( + headers::CORRELATION_ID.to_string(), + Uuid::new_v4().to_string().into_masked(), + ), + ]) } fn build_error_response( @@ -126,19 +142,56 @@ impl ConnectorCommon for Nexixpay { res: Response, event_builder: Option<&mut ConnectorEvent>, ) -> CustomResult { - let response: nexixpay::NexixpayErrorResponse = res - .response - .parse_struct("NexixpayErrorResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: nexixpay::NexixpayErrorResponse = match res.status_code { + 401 => nexixpay::NexixpayErrorResponse { + errors: vec![nexixpay::NexixpayErrorBody { + code: Some(consts::NO_ERROR_CODE.to_string()), + description: Some("UNAUTHORIZED".to_string()), + }], + }, + 404 => nexixpay::NexixpayErrorResponse { + errors: vec![nexixpay::NexixpayErrorBody { + code: Some(consts::NO_ERROR_CODE.to_string()), + description: Some("NOT FOUND".to_string()), + }], + }, + _ => res + .response + .parse_struct("NexixpayErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?, + }; + + let concatenated_descriptions: Option = { + let descriptions: Vec = response + .errors + .iter() + .filter_map(|error| error.description.as_ref()) + .cloned() + .collect(); + + if descriptions.is_empty() { + None + } else { + Some(descriptions.join(", ")) + } + }; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); Ok(ErrorResponse { status_code: res.status_code, - code: response.code, - message: response.message, - reason: response.reason, + code: response + .errors + .first() + .and_then(|error| error.code.clone()) + .unwrap_or(consts::NO_ERROR_CODE.to_string()), + message: response + .errors + .first() + .and_then(|error| error.description.clone()) + .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), + reason: concatenated_descriptions, attempt_status: None, connector_transaction_id: None, }) @@ -146,12 +199,22 @@ impl ConnectorCommon for Nexixpay { } impl ConnectorValidation for Nexixpay { - //TODO: implement functions when support enabled + fn validate_capture_method( + &self, + capture_method: Option, + _pmt: Option, + ) -> CustomResult<(), errors::ConnectorError> { + let capture_method = capture_method.unwrap_or_default(); + match capture_method { + enums::CaptureMethod::Automatic | enums::CaptureMethod::Manual => Ok(()), + enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => Err( + utils::construct_not_implemented_error_report(capture_method, self.id()), + ), + } + } } -impl ConnectorIntegration for Nexixpay { - //TODO: implement sessions flow -} +impl ConnectorIntegration for Nexixpay {} impl ConnectorIntegration for Nexixpay {} @@ -160,6 +223,183 @@ impl ConnectorIntegration + for Nexixpay +{ + fn get_headers( + &self, + req: &PaymentsPreProcessingRouterData, + 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: &PaymentsPreProcessingRouterData, + connectors: &Connectors, + ) -> CustomResult { + Ok(format!( + "{}/orders/3steps/validation", + self.base_url(connectors) + )) + } + + fn get_request_body( + &self, + req: &PaymentsPreProcessingRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let connector_req = nexixpay::NexixpayPreProcessingRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &PaymentsPreProcessingRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&types::PaymentsPreProcessingType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::PaymentsPreProcessingType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsPreProcessingType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsPreProcessingRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: nexixpay::NexixpayPreProcessingResponse = res + .response + .parse_struct("NexixpayPreProcessingResponse") + .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, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration + for Nexixpay +{ + fn get_headers( + &self, + req: &PaymentsCompleteAuthorizeRouterData, + 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: &PaymentsCompleteAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult { + Ok(format!( + "{}/orders/3steps/payment", + self.base_url(connectors) + )) + } + + fn get_request_body( + &self, + req: &PaymentsCompleteAuthorizeRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let amount = utils::convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.currency, + )?; + let connector_router_data = nexixpay::NexixpayRouterData::from((amount, req)); + let connector_req = + nexixpay::NexixpayCompleteAuthorizeRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &PaymentsCompleteAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&types::PaymentsCompleteAuthorizeType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::PaymentsCompleteAuthorizeType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsCompleteAuthorizeType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsCompleteAuthorizeRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: nexixpay::NexixpayCompleteAuthorizeResponse = res + .response + .parse_struct("NexixpayCompleteAuthorizeResponse") + .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, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + impl ConnectorIntegration for Nexixpay { fn get_headers( &self, @@ -176,9 +416,9 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!("{}/orders/3steps/init", self.base_url(connectors))) } fn get_request_body( @@ -227,7 +467,7 @@ impl ConnectorIntegration CustomResult { let response: nexixpay::NexixpayPaymentsResponse = res .response - .parse_struct("Nexixpay PaymentsAuthorizeResponse") + .parse_struct("NexixpayPaymentsResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); @@ -262,10 +502,15 @@ impl ConnectorIntegration for Nex fn get_url( &self, - _req: &PaymentsSyncRouterData, - _connectors: &Connectors, + req: &PaymentsSyncRouterData, + connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + let connector_payment_id = get_payment_id((req.request.connector_meta.clone(), None))?; + Ok(format!( + "{}/operations/{}", + self.base_url(connectors), + connector_payment_id + )) } fn build_request( @@ -289,9 +534,9 @@ impl ConnectorIntegration for Nex event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { - let response: nexixpay::NexixpayPaymentsResponse = res + let response: nexixpay::NexixpayTransactionResponse = res .response - .parse_struct("nexixpay PaymentsSyncResponse") + .parse_struct("NexixpayTransactionResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); @@ -311,13 +556,42 @@ impl ConnectorIntegration for Nex } } +fn get_payment_id( + (metadata, payment_intent): (Option, Option), +) -> CustomResult { + let connector_metadata = metadata.ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "connector_meta", + })?; + let nexixpay_meta_data = + serde_json::from_value::(connector_metadata) + .change_context(errors::ConnectorError::ParsingFailed)?; + let payment_flow = payment_intent.unwrap_or(nexixpay_meta_data.psync_flow); + let payment_id = match payment_flow { + nexixpay::NexixpayPaymentIntent::Cancel => nexixpay_meta_data.cancel_operation_id, + nexixpay::NexixpayPaymentIntent::Capture => nexixpay_meta_data.capture_operation_id, + nexixpay::NexixpayPaymentIntent::Authorize => nexixpay_meta_data.authorization_operation_id, + }; + payment_id.ok_or_else(|| { + errors::ConnectorError::MissingRequiredField { + field_name: "operation_id", + } + .into() + }) +} + impl ConnectorIntegration for Nexixpay { fn get_headers( &self, req: &PaymentsCaptureRouterData, - connectors: &Connectors, + _connectors: &Connectors, ) -> CustomResult)>, errors::ConnectorError> { - self.build_headers(req, connectors) + let mut header = vec![( + headers::IDEMPOTENCY_KEY.to_string(), + Uuid::new_v4().to_string().into_masked(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) } fn get_content_type(&self) -> &'static str { @@ -326,18 +600,34 @@ impl ConnectorIntegration fo fn get_url( &self, - _req: &PaymentsCaptureRouterData, - _connectors: &Connectors, + req: &PaymentsCaptureRouterData, + connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + let connector_payment_id = get_payment_id(( + req.request.connector_meta.clone(), + Some(nexixpay::NexixpayPaymentIntent::Authorize), + ))?; + Ok(format!( + "{}/operations/{}/captures", + self.base_url(connectors), + connector_payment_id + )) } fn get_request_body( &self, - _req: &PaymentsCaptureRouterData, + req: &PaymentsCaptureRouterData, _connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + let amount = utils::convert_amount( + self.amount_converter, + req.request.minor_amount_to_capture, + req.request.currency, + )?; + let connector_router_data = nexixpay::NexixpayRouterData::from((amount, req)); + let connector_req = + nexixpay::NexixpayPaymentsCaptureRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) } fn build_request( @@ -366,9 +656,9 @@ impl ConnectorIntegration fo event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { - let response: nexixpay::NexixpayPaymentsResponse = res + let response: nexixpay::NexixpayOperationResponse = res .response - .parse_struct("Nexixpay PaymentsCaptureResponse") + .parse_struct("NexixpayOperationResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); @@ -388,15 +678,19 @@ impl ConnectorIntegration fo } } -impl ConnectorIntegration for Nexixpay {} - -impl ConnectorIntegration for Nexixpay { +impl ConnectorIntegration for Nexixpay { fn get_headers( &self, - req: &RefundsRouterData, - connectors: &Connectors, + req: &PaymentsCancelRouterData, + _connectors: &Connectors, ) -> CustomResult)>, errors::ConnectorError> { - self.build_headers(req, connectors) + let mut header = vec![( + headers::IDEMPOTENCY_KEY.to_string(), + Uuid::new_v4().to_string().into_masked(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) } fn get_content_type(&self) -> &'static str { @@ -405,10 +699,124 @@ impl ConnectorIntegration for Nexixpa fn get_url( &self, - _req: &RefundsRouterData, - _connectors: &Connectors, + req: &PaymentsCancelRouterData, + connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + let connector_payment_id = get_payment_id(( + req.request.connector_meta.clone(), + Some(nexixpay::NexixpayPaymentIntent::Authorize), + ))?; + Ok(format!( + "{}/operations/{}/refunds", + self.base_url(connectors), + connector_payment_id + )) + } + + fn get_request_body( + &self, + req: &PaymentsCancelRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let minor_amount = + req.request + .minor_amount + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "amount", + })?; + let currency = + req.request + .currency + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "currency", + })?; + let amount = utils::convert_amount(self.amount_converter, minor_amount, currency)?; + + let connector_router_data = nexixpay::NexixpayRouterData::from((amount, req)); + let connector_req = + nexixpay::NexixpayPaymentsCancelRequest::try_from(connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &PaymentsCancelRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?) + .set_body(types::PaymentsVoidType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsCancelRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: nexixpay::NexixpayOperationResponse = res + .response + .parse_struct("NexixpayOperationResponse") + .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, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration for Nexixpay { + fn get_headers( + &self, + req: &RefundsRouterData, + _connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + let mut header = vec![( + headers::IDEMPOTENCY_KEY.to_string(), + Uuid::new_v4().to_string().into_masked(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + req: &RefundsRouterData, + connectors: &Connectors, + ) -> CustomResult { + let connector_payment_id = get_payment_id(( + req.request.connector_metadata.clone(), + Some(nexixpay::NexixpayPaymentIntent::Capture), + ))?; + Ok(format!( + "{}/operations/{}/refunds", + self.base_url(connectors), + connector_payment_id + )) } fn get_request_body( @@ -454,7 +862,7 @@ impl ConnectorIntegration for Nexixpa ) -> CustomResult, errors::ConnectorError> { let response: nexixpay::RefundResponse = res .response - .parse_struct("nexixpay RefundResponse") + .parse_struct("RefundResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); @@ -489,10 +897,15 @@ impl ConnectorIntegration for Nexixpay fn get_url( &self, - _req: &RefundSyncRouterData, - _connectors: &Connectors, + req: &RefundSyncRouterData, + connectors: &Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + let connector_refund_id = req.request.get_connector_refund_id()?; + Ok(format!( + "{}/operations/{}", + self.base_url(connectors), + connector_refund_id + )) } fn build_request( @@ -519,9 +932,9 @@ impl ConnectorIntegration for Nexixpay event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { - let response: nexixpay::RefundResponse = res + let response: nexixpay::NexixpayRSyncResponse = res .response - .parse_struct("nexixpay RefundSyncResponse") + .parse_struct("NexixpayRSyncResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); diff --git a/crates/hyperswitch_connectors/src/connectors/nexixpay/transformers.rs b/crates/hyperswitch_connectors/src/connectors/nexixpay/transformers.rs index f0bcb1f249..54856d4dba 100644 --- a/crates/hyperswitch_connectors/src/connectors/nexixpay/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/nexixpay/transformers.rs @@ -1,31 +1,45 @@ -use common_enums::enums; -use common_utils::types::StringMinorUnit; +use std::collections::HashMap; + +use cards::CardNumber; +use common_enums::{enums, AttemptStatus, CaptureMethod, Currency, RefundStatus}; +use common_utils::{ + errors::CustomResult, ext_traits::ValueExt, request::Method, types::StringMinorUnit, +}; +use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::PaymentMethodData, router_data::{ConnectorAuthType, RouterData}, router_flow_types::refunds::{Execute, RSync}, - router_request_types::ResponseId, - router_response_types::{PaymentsResponseData, RefundsResponseData}, - types::{PaymentsAuthorizeRouterData, RefundsRouterData}, + router_request_types::{ + CompleteAuthorizeData, PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, + PaymentsPreProcessingData, PaymentsSyncData, ResponseId, + }, + router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData}, + types::{ + PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData, + PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData, RefundsRouterData, + }, }; use hyperswitch_interfaces::errors; -use masking::Secret; +use masking::{ExposeInterface, Secret}; use serde::{Deserialize, Serialize}; use crate::{ types::{RefundsResponseRouterData, ResponseRouterData}, - utils::PaymentsAuthorizeRequestData, + utils::{ + get_unimplemented_payment_method_error_message, to_connector_meta, + to_connector_meta_from_secret, CardData, PaymentsAuthorizeRequestData, + PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingRequestData, RouterData as _, + }, }; -//TODO: Fill the struct with respective fields pub struct NexixpayRouterData { - pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub amount: StringMinorUnit, pub router_data: T, } impl From<(StringMinorUnit, T)> for NexixpayRouterData { fn from((amount, item): (StringMinorUnit, T)) -> Self { - //Todo : use utils to convert the amount to the type of amount that a connector accepts Self { amount, router_data: item, @@ -33,20 +47,323 @@ impl From<(StringMinorUnit, T)> for NexixpayRouterData { } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Serialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct NexixpayPaymentsRequest { - amount: StringMinorUnit, + order: Order, card: NexixpayCard, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum NexixpayCaptureType { + Implicit, + Explicit, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayCompleteAuthorizeRequest { + order: Order, + card: NexixpayCard, + operation_id: String, + capture_type: Option, + three_d_s_auth_data: ThreeDSAuthData, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct OperationData { + operation_id: String, + operation_currency: Currency, + operation_result: NexixpayPaymentStatus, + operation_type: NexixpayOperationType, + order_id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayCompleteAuthorizeResponse { + operation: OperationData, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayPreProcessingRequest { + operation_id: Option, + three_d_s_auth_response: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Order { + order_id: String, + amount: StringMinorUnit, + currency: Currency, + description: Option, + customer_info: CustomerInfo, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CustomerInfo { + card_holder_name: Secret, + billing_address: Address, + shipping_address: Option
, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Address { + name: Secret, + street: Secret, + city: String, + post_code: Secret, + country: enums::CountryAlpha2, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct NexixpayCard { - number: cards::CardNumber, - expiry_month: Secret, - expiry_year: Secret, - cvc: Secret, - complete: bool, + pan: CardNumber, + expiry_date: Secret, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Recurrence { + action: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayPaymentsResponse { + operation: Operation, + three_d_s_auth_request: String, + three_d_s_auth_url: Secret, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ThreeDSAuthResult { + authentication_value: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +pub enum NexixpayPaymentIntent { + Capture, + Cancel, + Authorize, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayRedirectionRequest { + pub three_d_s_auth_url: String, + pub three_ds_request: String, + pub return_url: String, + pub transaction_id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayConnectorMetaData { + pub three_d_s_auth_result: Option, + pub three_d_s_auth_response: Option>, + pub authorization_operation_id: Option, + pub capture_operation_id: Option, + pub cancel_operation_id: Option, + pub psync_flow: NexixpayPaymentIntent, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UpdateNexixpayConnectorMetaData { + pub three_d_s_auth_result: Option, + pub three_d_s_auth_response: Option>, + pub authorization_operation_id: Option, + pub capture_operation_id: Option, + pub cancel_operation_id: Option, + pub psync_flow: Option, + pub meta_data: serde_json::Value, + pub is_auto_capture: bool, +} + +fn update_nexi_meta_data( + update_request: UpdateNexixpayConnectorMetaData, +) -> CustomResult { + let nexixpay_meta_data = + serde_json::from_value::(update_request.meta_data) + .change_context(errors::ConnectorError::ParsingFailed)?; + + Ok(serde_json::json!(NexixpayConnectorMetaData { + three_d_s_auth_result: nexixpay_meta_data + .three_d_s_auth_result + .or(update_request.three_d_s_auth_result), + three_d_s_auth_response: nexixpay_meta_data + .three_d_s_auth_response + .or(update_request.three_d_s_auth_response), + authorization_operation_id: nexixpay_meta_data + .authorization_operation_id + .clone() + .or(update_request.authorization_operation_id.clone()), + capture_operation_id: { + nexixpay_meta_data + .capture_operation_id + .or(if update_request.is_auto_capture { + nexixpay_meta_data + .authorization_operation_id + .or(update_request.authorization_operation_id.clone()) + } else { + update_request.capture_operation_id + }) + }, + cancel_operation_id: nexixpay_meta_data + .cancel_operation_id + .or(update_request.cancel_operation_id), + psync_flow: update_request + .psync_flow + .unwrap_or(nexixpay_meta_data.psync_flow) + })) +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ThreeDSAuthData { + three_d_s_auth_response: Option>, + authentication_value: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayPreProcessingResponse { + operation: Operation, + three_d_s_auth_result: ThreeDSAuthResult, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Operation { + additional_data: AdditionalData, + customer_info: CustomerInfo, + operation_amount: String, + operation_currency: Currency, + operation_id: String, + operation_result: NexixpayPaymentStatus, + operation_time: String, + operation_type: NexixpayOperationType, + order_id: String, + payment_method: String, + warnings: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AdditionalData { + masked_pan: String, + card_id: Secret, + card_id4: Option>, + card_expiry_date: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RedirectPayload { + #[serde(rename = "PaRes")] + pa_res: Option>, + + #[serde(rename = "paymentId")] + payment_id: Option, +} + +impl TryFrom<&PaymentsPreProcessingRouterData> for NexixpayPreProcessingRequest { + type Error = error_stack::Report; + fn try_from(item: &PaymentsPreProcessingRouterData) -> Result { + let redirect_response = item.request.redirect_response.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "redirect_response", + }, + )?; + let redirect_payload = redirect_response + .payload + .ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload { + field_name: "request.redirect_response.payload", + })? + .expose(); + let customer_details_encrypted: RedirectPayload = + serde_json::from_value::(redirect_payload.clone()).change_context( + errors::ConnectorError::MissingConnectorRedirectionPayload { + field_name: "redirection_payload", + }, + )?; + Ok(Self { + operation_id: customer_details_encrypted.payment_id, + three_d_s_auth_response: customer_details_encrypted.pa_res, + }) + } +} + +impl + TryFrom< + ResponseRouterData< + F, + NexixpayPreProcessingResponse, + PaymentsPreProcessingData, + PaymentsResponseData, + >, + > for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData< + F, + NexixpayPreProcessingResponse, + PaymentsPreProcessingData, + PaymentsResponseData, + >, + ) -> Result { + let three_ds_data = item.response.three_d_s_auth_result; + let customer_details_encrypted: RedirectPayload = item + .data + .request + .redirect_response + .as_ref() + .and_then(|res| res.payload.to_owned()) + .ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload { + field_name: "request.redirect_response.payload", + })? + .expose() + .parse_value("RedirectPayload") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let is_auto_capture = item.data.request.is_auto_capture()?; + let meta_data = to_connector_meta_from_secret(item.data.request.metadata.clone())?; + let connector_metadata = Some(update_nexi_meta_data(UpdateNexixpayConnectorMetaData { + three_d_s_auth_result: Some(three_ds_data), + three_d_s_auth_response: customer_details_encrypted.pa_res, + authorization_operation_id: None, + capture_operation_id: None, + cancel_operation_id: None, + psync_flow: None, + meta_data, + is_auto_capture, + })?); + + Ok(Self { + status: AttemptStatus::from(item.response.operation.operation_result), + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId( + item.response.operation.order_id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata, + network_txn_id: None, + connector_response_reference_id: Some(item.response.operation.order_id), + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } } impl TryFrom<&NexixpayRouterData<&PaymentsAuthorizeRouterData>> for NexixpayPaymentsRequest { @@ -54,27 +371,55 @@ impl TryFrom<&NexixpayRouterData<&PaymentsAuthorizeRouterData>> for NexixpayPaym fn try_from( item: &NexixpayRouterData<&PaymentsAuthorizeRouterData>, ) -> Result { - match item.router_data.request.payment_method_data.clone() { - PaymentMethodData::Card(req_card) => { + match item.router_data.request.payment_method_data { + PaymentMethodData::Card(ref req_card) => { let card = NexixpayCard { - number: req_card.card_number, - expiry_month: req_card.card_exp_month, - expiry_year: req_card.card_exp_year, - cvc: req_card.card_cvc, - complete: item.router_data.request.is_auto_capture()?, + pan: req_card.card_number.clone(), + expiry_date: req_card.get_expiry_date_as_mmyy()?, }; - Ok(Self { + let billing_address = Address { + name: item.router_data.get_billing_full_name()?, + street: item.router_data.get_billing_line1()?, + city: item.router_data.get_billing_city()?, + post_code: item.router_data.get_billing_zip()?, + country: item.router_data.get_billing_country()?, + }; + let customer_info = CustomerInfo { + card_holder_name: item.router_data.get_billing_full_name()?, + billing_address: billing_address.clone(), + shipping_address: Some(billing_address), + }; + let order = Order { + order_id: item.router_data.connector_request_reference_id.clone(), amount: item.amount.clone(), - card, - }) + currency: item.router_data.request.currency, + description: item.router_data.description.clone(), + customer_info, + }; + Ok(Self { order, card }) } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + PaymentMethodData::CardRedirect(_) + | PaymentMethodData::Wallet(_) + | PaymentMethodData::PayLater(_) + | PaymentMethodData::BankRedirect(_) + | PaymentMethodData::BankDebit(_) + | PaymentMethodData::BankTransfer(_) + | PaymentMethodData::Crypto(_) + | PaymentMethodData::MandatePayment + | PaymentMethodData::Reward + | PaymentMethodData::RealTimePayment(_) + | PaymentMethodData::Upi(_) + | PaymentMethodData::Voucher(_) + | PaymentMethodData::GiftCard(_) + | PaymentMethodData::OpenBanking(_) + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( + get_unimplemented_payment_method_error_message("nexixpay"), + ))?, } } } -//TODO: Fill the struct with respective fields -// Auth Struct pub struct NexixpayAuthType { pub(super) api_key: Secret, } @@ -90,50 +435,199 @@ impl TryFrom<&ConnectorAuthType> for NexixpayAuthType { } } } -// PaymentsResponse -//TODO: Append the remaining status flags -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "lowercase")] + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum NexixpayPaymentStatus { - Succeeded, + Authorized, + Executed, + Declined, + DeniedByRisk, + ThreedsValidated, + ThreedsFailed, + Pending, + Canceled, + Voided, + Refunded, Failed, - #[default] - Processing, } -impl From for common_enums::AttemptStatus { +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum NexixpayOperationType { + Authorization, + Capture, + Void, + Refund, + CardVerification, + Noshow, + Incremental, + DelayCharge, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum NexixpayRefundOperationType { + Refund, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum NexixpayRefundResultStatus { + Pending, + Voided, + Refunded, + Failed, + Executed, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayTransactionResponse { + order_id: String, + operation_id: String, + operation_result: NexixpayPaymentStatus, + operation_type: NexixpayOperationType, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayRSyncResponse { + order_id: String, + operation_id: String, + operation_result: NexixpayRefundResultStatus, + operation_type: NexixpayRefundOperationType, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayPaymentsCaptureRequest { + amount: StringMinorUnit, + currency: Currency, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayPaymentsCancelRequest { + description: Option, + amount: StringMinorUnit, + currency: Currency, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayOperationResponse { + operation_id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NexixpayRefundRequest { + pub amount: StringMinorUnit, + pub currency: Currency, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RefundResponse { + operation_id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayErrorBody { + pub code: Option, + pub description: Option, +} +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NexixpayErrorResponse { + pub errors: Vec, +} + +impl From for AttemptStatus { fn from(item: NexixpayPaymentStatus) -> Self { match item { - NexixpayPaymentStatus::Succeeded => Self::Charged, - NexixpayPaymentStatus::Failed => Self::Failure, - NexixpayPaymentStatus::Processing => Self::Authorizing, + NexixpayPaymentStatus::Declined + | NexixpayPaymentStatus::DeniedByRisk + | NexixpayPaymentStatus::ThreedsFailed + | NexixpayPaymentStatus::Failed => Self::Failure, + NexixpayPaymentStatus::Authorized => Self::Authorized, + NexixpayPaymentStatus::ThreedsValidated => Self::AuthenticationSuccessful, + NexixpayPaymentStatus::Executed => Self::Charged, + NexixpayPaymentStatus::Pending => Self::AuthenticationPending, // this is being used in authorization calls only. + NexixpayPaymentStatus::Canceled | NexixpayPaymentStatus::Voided => Self::Voided, + NexixpayPaymentStatus::Refunded => Self::AutoRefunded, } } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct NexixpayPaymentsResponse { - status: NexixpayPaymentStatus, - id: String, +fn get_nexixpay_capture_type( + item: Option, +) -> CustomResult, errors::ConnectorError> { + match item { + Some(CaptureMethod::Manual) => Ok(Some(NexixpayCaptureType::Explicit)), + Some(CaptureMethod::Automatic) | None => Ok(Some(NexixpayCaptureType::Implicit)), + Some(item) => Err(errors::ConnectorError::FlowNotSupported { + flow: item.to_string(), + connector: "Nexixpay".to_string(), + } + .into()), + } } -impl TryFrom> - for RouterData +impl + TryFrom< + ResponseRouterData< + F, + NexixpayPaymentsResponse, + PaymentsAuthorizeData, + PaymentsResponseData, + >, + > for RouterData { type Error = error_stack::Report; fn try_from( - item: ResponseRouterData, + item: ResponseRouterData< + F, + NexixpayPaymentsResponse, + PaymentsAuthorizeData, + PaymentsResponseData, + >, ) -> Result { + let complete_authorize_url = item.data.request.get_complete_authorize_url()?; + let operation_id: String = item.response.operation.operation_id; + let redirection_form = nexixpay_threeds_link(NexixpayRedirectionRequest { + three_d_s_auth_url: item.response.three_d_s_auth_url.expose().to_string(), + three_ds_request: item.response.three_d_s_auth_request.clone(), + return_url: complete_authorize_url.clone(), + transaction_id: operation_id.clone(), + })?; + let is_auto_capture = item.data.request.is_auto_capture()?; + let connector_metadata = Some(serde_json::json!(NexixpayConnectorMetaData { + three_d_s_auth_result: None, + three_d_s_auth_response: None, + authorization_operation_id: Some(operation_id.clone()), + cancel_operation_id: None, + capture_operation_id: { + if is_auto_capture { + Some(operation_id) + } else { + None + } + }, + psync_flow: NexixpayPaymentIntent::Authorize + })); Ok(Self { - status: common_enums::AttemptStatus::from(item.response.status), + status: AttemptStatus::from(item.response.operation.operation_result), response: Ok(PaymentsResponseData::TransactionResponse { - resource_id: ResponseId::ConnectorTransactionId(item.response.id), - redirection_data: None, + resource_id: ResponseId::ConnectorTransactionId( + item.response.operation.order_id.clone(), + ), + redirection_data: Some(redirection_form.clone()), mandate_reference: None, - connector_metadata: None, + connector_metadata, network_txn_id: None, - connector_response_reference_id: None, + connector_response_reference_id: Some(item.response.operation.order_id), incremental_authorization_allowed: None, charge_id: None, }), @@ -142,12 +636,19 @@ impl TryFrom CustomResult { + let mut form_fields = HashMap::::new(); + form_fields.insert(String::from("ThreeDsRequest"), request.three_ds_request); + form_fields.insert(String::from("ReturnUrl"), request.return_url); + form_fields.insert(String::from("transactionId"), request.transaction_id); + + Ok(RedirectForm::Form { + endpoint: request.three_d_s_auth_url, + method: Method::Post, + form_fields, + }) } impl TryFrom<&NexixpayRouterData<&RefundsRouterData>> for NexixpayRefundRequest { @@ -155,39 +656,23 @@ impl TryFrom<&NexixpayRouterData<&RefundsRouterData>> for NexixpayRefundRe fn try_from(item: &NexixpayRouterData<&RefundsRouterData>) -> Result { Ok(Self { amount: item.amount.to_owned(), + currency: item.router_data.request.currency, }) } } -// Type definition for Refund Response - -#[allow(dead_code)] -#[derive(Debug, Serialize, Default, Deserialize, Clone)] -pub enum RefundStatus { - Succeeded, - Failed, - #[default] - Processing, -} - -impl From for enums::RefundStatus { - fn from(item: RefundStatus) -> Self { +impl From for RefundStatus { + fn from(item: NexixpayRefundResultStatus) -> Self { match item { - RefundStatus::Succeeded => Self::Success, - RefundStatus::Failed => Self::Failure, - RefundStatus::Processing => Self::Pending, - //TODO: Review mapping + NexixpayRefundResultStatus::Voided + | NexixpayRefundResultStatus::Refunded + | NexixpayRefundResultStatus::Executed => Self::Success, + NexixpayRefundResultStatus::Pending => Self::Pending, + NexixpayRefundResultStatus::Failed => Self::Failure, } } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct RefundResponse { - id: String, - status: RefundStatus, -} - impl TryFrom> for RefundsRouterData { type Error = error_stack::Report; fn try_from( @@ -195,34 +680,319 @@ impl TryFrom> for RefundsRout ) -> Result { Ok(Self { response: Ok(RefundsResponseData { - connector_refund_id: item.response.id.to_string(), - refund_status: enums::RefundStatus::from(item.response.status), + connector_refund_id: item.response.operation_id, + refund_status: RefundStatus::Pending, // Refund call do not return status in their response. }), ..item.data }) } } -impl TryFrom> for RefundsRouterData { +impl TryFrom> for RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: RefundsResponseRouterData, + item: RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(RefundsResponseData { - connector_refund_id: item.response.id.to_string(), - refund_status: enums::RefundStatus::from(item.response.status), + connector_refund_id: item.response.operation_id, + refund_status: RefundStatus::from(item.response.operation_result), }), ..item.data }) } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] -pub struct NexixpayErrorResponse { - pub status_code: u16, - pub code: String, - pub message: String, - pub reason: Option, +impl + TryFrom< + ResponseRouterData< + F, + NexixpayCompleteAuthorizeResponse, + CompleteAuthorizeData, + PaymentsResponseData, + >, + > for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData< + F, + NexixpayCompleteAuthorizeResponse, + CompleteAuthorizeData, + PaymentsResponseData, + >, + ) -> Result { + let is_auto_capture = item.data.request.is_auto_capture()?; + let meta_data = to_connector_meta(item.data.request.connector_meta.clone())?; + let connector_metadata = Some(update_nexi_meta_data(UpdateNexixpayConnectorMetaData { + three_d_s_auth_result: None, + three_d_s_auth_response: None, + authorization_operation_id: Some(item.response.operation.operation_id.clone()), + capture_operation_id: None, + cancel_operation_id: None, + psync_flow: Some(NexixpayPaymentIntent::Authorize), + meta_data, + is_auto_capture, + })?); + + Ok(Self { + status: AttemptStatus::from(item.response.operation.operation_result), + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId( + item.response.operation.order_id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata, + network_txn_id: None, + connector_response_reference_id: Some(item.response.operation.order_id), + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } +} + +impl TryFrom<&NexixpayRouterData<&PaymentsCompleteAuthorizeRouterData>> + for NexixpayCompleteAuthorizeRequest +{ + type Error = error_stack::Report; + fn try_from( + item: &NexixpayRouterData<&PaymentsCompleteAuthorizeRouterData>, + ) -> Result { + let payment_method_data: PaymentMethodData = + item.router_data.request.payment_method_data.clone().ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "payment_method_data", + }, + )?; + let capture_type = get_nexixpay_capture_type(item.router_data.request.capture_method)?; + + let order_id = item.router_data.connector_request_reference_id.clone(); + let amount = item.amount.clone(); + let billing_address = Address { + name: item.router_data.get_billing_full_name()?, + street: item.router_data.get_billing_line1()?, + city: item.router_data.get_billing_city()?, + post_code: item.router_data.get_billing_zip()?, + country: item.router_data.get_billing_country()?, + }; + let customer_info = CustomerInfo { + card_holder_name: item.router_data.get_billing_full_name()?, + billing_address: billing_address.clone(), + shipping_address: Some(billing_address), + }; + let order_data = Order { + order_id, + amount, + currency: item.router_data.request.currency, + description: item.router_data.description.clone(), + customer_info, + }; + let connector_metadata = + to_connector_meta(item.router_data.request.connector_meta.clone())?; + let nexixpay_meta_data = + serde_json::from_value::(connector_metadata) + .change_context(errors::ConnectorError::ParsingFailed)?; + let operation_id = nexixpay_meta_data.authorization_operation_id.ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "authorization_operation_id", + }, + )?; + let authentication_value = nexixpay_meta_data + .three_d_s_auth_result + .and_then(|data| data.authentication_value); + let three_d_s_auth_data = ThreeDSAuthData { + three_d_s_auth_response: nexixpay_meta_data.three_d_s_auth_response, + authentication_value, + }; + let card: Result> = + match payment_method_data { + PaymentMethodData::Card(req_card) => Ok(NexixpayCard { + pan: req_card.card_number.clone(), + expiry_date: req_card.get_expiry_date_as_mmyy()?, + }), + PaymentMethodData::CardRedirect(_) + | PaymentMethodData::Wallet(_) + | PaymentMethodData::PayLater(_) + | PaymentMethodData::BankRedirect(_) + | PaymentMethodData::BankDebit(_) + | PaymentMethodData::BankTransfer(_) + | PaymentMethodData::Crypto(_) + | PaymentMethodData::MandatePayment + | PaymentMethodData::Reward + | PaymentMethodData::RealTimePayment(_) + | PaymentMethodData::Upi(_) + | PaymentMethodData::Voucher(_) + | PaymentMethodData::GiftCard(_) + | PaymentMethodData::OpenBanking(_) + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => { + Err(errors::ConnectorError::NotImplemented( + get_unimplemented_payment_method_error_message("nexixpay"), + ) + .into()) + } + }; + Ok(Self { + order: order_data, + card: card?, + operation_id, + capture_type, + three_d_s_auth_data, + }) + } +} + +impl + TryFrom< + ResponseRouterData, + > for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData< + F, + NexixpayTransactionResponse, + PaymentsSyncData, + PaymentsResponseData, + >, + ) -> Result { + Ok(Self { + status: AttemptStatus::from(item.response.operation_result), + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(item.response.order_id.clone()), + redirection_data: None, + mandate_reference: None, + connector_metadata: item.data.request.connector_meta.clone(), + network_txn_id: None, + connector_response_reference_id: Some(item.response.order_id.clone()), + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } +} + +impl TryFrom<&NexixpayRouterData<&PaymentsCaptureRouterData>> for NexixpayPaymentsCaptureRequest { + type Error = error_stack::Report; + fn try_from( + item: &NexixpayRouterData<&PaymentsCaptureRouterData>, + ) -> Result { + Ok(Self { + amount: item.amount.clone(), + currency: item.router_data.request.currency, + }) + } +} + +impl + TryFrom< + ResponseRouterData, + > for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData< + F, + NexixpayOperationResponse, + PaymentsCaptureData, + PaymentsResponseData, + >, + ) -> Result { + let meta_data = to_connector_meta(item.data.request.connector_meta.clone())?; + let connector_metadata = Some(update_nexi_meta_data(UpdateNexixpayConnectorMetaData { + three_d_s_auth_result: None, + three_d_s_auth_response: None, + authorization_operation_id: None, + capture_operation_id: Some(item.response.operation_id.clone()), + cancel_operation_id: None, + psync_flow: Some(NexixpayPaymentIntent::Capture), + meta_data, + is_auto_capture: false, + })?); + Ok(Self { + status: AttemptStatus::Pending, // Capture call do not return status in their response. + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId( + item.data.request.connector_transaction_id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata, + network_txn_id: None, + connector_response_reference_id: Some( + item.data.request.connector_transaction_id.clone(), + ), + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } +} + +impl TryFrom> for NexixpayPaymentsCancelRequest { + type Error = error_stack::Report; + fn try_from(item: NexixpayRouterData<&PaymentsCancelRouterData>) -> Result { + let description = item.router_data.request.cancellation_reason.clone(); + let currency = item.router_data.request.currency.ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "currency", + }, + )?; + Ok(Self { + amount: item.amount, + currency, + description, + }) + } +} + +impl + TryFrom< + ResponseRouterData, + > for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData< + F, + NexixpayOperationResponse, + PaymentsCancelData, + PaymentsResponseData, + >, + ) -> Result { + let meta_data = to_connector_meta(item.data.request.connector_meta.clone())?; + let connector_metadata = Some(update_nexi_meta_data(UpdateNexixpayConnectorMetaData { + three_d_s_auth_result: None, + three_d_s_auth_response: None, + authorization_operation_id: None, + capture_operation_id: None, + cancel_operation_id: Some(item.response.operation_id.clone()), + psync_flow: Some(NexixpayPaymentIntent::Cancel), + meta_data, + is_auto_capture: false, + })?); + Ok(Self { + status: AttemptStatus::Pending, // Cancel call do not return status in their response. + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId( + item.data.request.connector_transaction_id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata, + network_txn_id: None, + connector_response_reference_id: Some( + item.data.request.connector_transaction_id.clone(), + ), + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } } diff --git a/crates/hyperswitch_connectors/src/constants.rs b/crates/hyperswitch_connectors/src/constants.rs index 5b71a1bd29..6c57997700 100644 --- a/crates/hyperswitch_connectors/src/constants.rs +++ b/crates/hyperswitch_connectors/src/constants.rs @@ -19,4 +19,6 @@ pub(crate) mod headers { pub(crate) const X_RANDOM_VALUE: &str = "X-RandomValue"; pub(crate) const X_REQUEST_DATE: &str = "X-RequestDate"; pub(crate) const X_VERSION: &str = "X-Version"; + pub(crate) const X_API_KEY: &str = "X-Api-Key"; + pub(crate) const CORRELATION_ID: &str = "Correlation-Id"; } diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs index 0d43c263f9..398ac7f649 100644 --- a/crates/hyperswitch_connectors/src/default_implementations.rs +++ b/crates/hyperswitch_connectors/src/default_implementations.rs @@ -220,7 +220,6 @@ default_imp_for_complete_authorize!( connectors::Globepay, connectors::Helcim, connectors::Novalnet, - connectors::Nexixpay, connectors::Stax, connectors::Square, connectors::Taxjar, @@ -380,7 +379,6 @@ default_imp_for_pre_processing_steps!( connectors::Globepay, connectors::Helcim, connectors::Novalnet, - connectors::Nexixpay, connectors::Powertranz, connectors::Mollie, connectors::Stax, diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index f96d715f5b..0d74f1fb6e 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -21,7 +21,8 @@ use hyperswitch_domain_models::{ router_request_types::{ AuthenticationData, BrowserInformation, CompleteAuthorizeData, PaymentMethodTokenizationData, PaymentsAuthorizeData, PaymentsCancelData, - PaymentsCaptureData, PaymentsSyncData, RefundsData, ResponseId, SetupMandateRequestData, + PaymentsCaptureData, PaymentsPreProcessingData, PaymentsSyncData, RefundsData, ResponseId, + SetupMandateRequestData, }, }; use hyperswitch_interfaces::{api, errors}; @@ -744,6 +745,7 @@ pub trait CardData { fn get_expiry_date_as_mmyyyy(&self, delimiter: &str) -> Secret; fn get_expiry_year_4_digit(&self) -> Secret; fn get_expiry_date_as_yymm(&self) -> Result, errors::ConnectorError>; + fn get_expiry_date_as_mmyy(&self) -> Result, errors::ConnectorError>; fn get_expiry_month_as_i8(&self) -> Result, Error>; fn get_expiry_year_as_i32(&self) -> Result, Error>; } @@ -803,6 +805,11 @@ impl CardData for Card { let month = self.card_exp_month.clone().expose(); Ok(Secret::new(format!("{year}{month}"))) } + fn get_expiry_date_as_mmyy(&self) -> Result, errors::ConnectorError> { + let year = self.get_card_expiry_year_2_digit()?.expose(); + let month = self.card_exp_month.clone().expose(); + Ok(Secret::new(format!("{month}{year}"))) + } fn get_expiry_month_as_i8(&self) -> Result, Error> { self.card_exp_month .peek() @@ -1400,6 +1407,22 @@ impl PaymentsCompleteAuthorizeRequestData for CompleteAuthorizeData { } } +pub trait PaymentsPreProcessingRequestData { + fn is_auto_capture(&self) -> Result; +} + +impl PaymentsPreProcessingRequestData for PaymentsPreProcessingData { + fn is_auto_capture(&self) -> Result { + match self.capture_method { + Some(enums::CaptureMethod::Automatic) | None => Ok(true), + Some(enums::CaptureMethod::Manual) => Ok(false), + Some(enums::CaptureMethod::ManualMultiple) | Some(enums::CaptureMethod::Scheduled) => { + Err(errors::ConnectorError::CaptureMethodNotSupported.into()) + } + } + } +} + pub trait BrowserInformationData { fn get_accept_header(&self) -> Result; fn get_language(&self) -> Result; diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index 5a0d599dcb..d26f1975b7 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -279,6 +279,7 @@ pub struct PaymentsPreProcessingData { pub mandate_id: Option, pub related_transaction_id: Option, pub redirect_response: Option, + pub metadata: Option>, // New amount for amount frame work pub minor_amount: Option, @@ -308,6 +309,7 @@ impl TryFrom for PaymentsPreProcessingData { related_transaction_id: data.related_transaction_id, redirect_response: None, enrolled_for_3ds: data.enrolled_for_3ds, + metadata: data.metadata.map(Secret::new), }) } } @@ -336,6 +338,7 @@ impl TryFrom for PaymentsPreProcessingData { related_transaction_id: None, redirect_response: data.redirect_response, enrolled_for_3ds: true, + metadata: data.connector_meta.map(Secret::new), }) } } diff --git a/crates/hyperswitch_domain_models/src/types.rs b/crates/hyperswitch_domain_models/src/types.rs index a6be862403..507249e5ee 100644 --- a/crates/hyperswitch_domain_models/src/types.rs +++ b/crates/hyperswitch_domain_models/src/types.rs @@ -2,14 +2,14 @@ use crate::{ router_data::{AccessToken, RouterData}, router_flow_types::{ AccessTokenAuth, Authorize, AuthorizeSessionToken, CalculateTax, Capture, - CompleteAuthorize, CreateConnectorCustomer, PSync, PaymentMethodToken, RSync, SetupMandate, - Void, + CompleteAuthorize, CreateConnectorCustomer, PSync, PaymentMethodToken, PreProcessing, + RSync, SetupMandate, Void, }, router_request_types::{ AccessTokenRequestData, AuthorizeSessionTokenData, CompleteAuthorizeData, ConnectorCustomerData, PaymentMethodTokenizationData, PaymentsAuthorizeData, - PaymentsCancelData, PaymentsCaptureData, PaymentsSyncData, PaymentsTaxCalculationData, - RefundsData, SetupMandateRequestData, + PaymentsCancelData, PaymentsCaptureData, PaymentsPreProcessingData, PaymentsSyncData, + PaymentsTaxCalculationData, RefundsData, SetupMandateRequestData, }, router_response_types::{ PaymentsResponseData, RefundsResponseData, TaxCalculationResponseData, @@ -20,6 +20,8 @@ pub type PaymentsAuthorizeRouterData = RouterData; pub type PaymentsAuthorizeSessionTokenRouterData = RouterData; +pub type PaymentsPreProcessingRouterData = + RouterData; pub type PaymentsSyncRouterData = RouterData; pub type PaymentsCaptureRouterData = RouterData; pub type PaymentsCancelRouterData = RouterData; diff --git a/crates/router/src/configs/defaults.rs b/crates/router/src/configs/defaults.rs index 254a168eb3..3debb56722 100644 --- a/crates/router/src/configs/defaults.rs +++ b/crates/router/src/configs/defaults.rs @@ -5086,6 +5086,98 @@ impl Default for super::settings::RequiredFields { ), } ), + ( + enums::Connector::Nexixpay, + RequiredFieldFinal { + mandate: HashMap::new(), + non_mandate: HashMap::new(), + common: HashMap::from( + [ + ( + "payment_method_data.card.card_number".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.card.card_number".to_string(), + display_name: "card_number".to_string(), + field_type: enums::FieldType::UserCardNumber, + value: None, + } + ), + ( + "payment_method_data.card.card_exp_month".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.card.card_exp_month".to_string(), + display_name: "card_exp_month".to_string(), + field_type: enums::FieldType::UserCardExpiryMonth, + value: None, + } + ), + ( + "payment_method_data.card.card_exp_year".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.card.card_exp_year".to_string(), + display_name: "card_exp_year".to_string(), + field_type: enums::FieldType::UserCardExpiryYear, + value: None, + } + ), + ( + "billing.address.line1".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.line1".to_string(), + display_name: "line1".to_string(), + field_type: enums::FieldType::UserAddressLine1, + value: None, + } + ), + ( + "billing.address.line2".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.line2".to_string(), + display_name: "line1".to_string(), + field_type: enums::FieldType::UserAddressLine2, + value: None, + } + ), + ( + "billing.address.city".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.city".to_string(), + display_name: "city".to_string(), + field_type: enums::FieldType::UserAddressCity, + value: None, + } + ), + ( + "billing.address.zip".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.zip".to_string(), + display_name: "zip".to_string(), + field_type: enums::FieldType::UserAddressPincode, + value: None, + } + ), + ( + "billing.address.first_name".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.first_name".to_string(), + display_name: "first_name".to_string(), + field_type: enums::FieldType::UserFullName, + value: None, + } + ), + ( + "billing.address.last_name".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.billing.address.last_name".to_string(), + display_name: "last_name".to_string(), + field_type: enums::FieldType::UserFullName, + value: None, + } + ) + ] + ), + } + ), ( enums::Connector::Nmi, RequiredFieldFinal { diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 4da93e2cf0..c66e1f2f65 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -629,6 +629,7 @@ impl AddressData for api::Address { } pub trait PaymentsPreProcessingData { + fn get_redirect_response_payload(&self) -> Result; fn get_email(&self) -> Result; fn get_payment_method_type(&self) -> Result; fn get_currency(&self) -> Result; @@ -696,6 +697,17 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData { .clone() .ok_or_else(missing_field_err("complete_authorize_url")) } + fn get_redirect_response_payload(&self) -> Result { + self.redirect_response + .as_ref() + .and_then(|res| res.payload.to_owned()) + .ok_or( + errors::ConnectorError::MissingConnectorRedirectionPayload { + field_name: "request.redirect_response.payload", + } + .into(), + ) + } fn connector_mandate_id(&self) -> Option { self.mandate_id .as_ref() diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index d27b6b82fd..0803106cc2 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1415,6 +1415,10 @@ impl<'a> ConnectorAuthTypeAndMetadataValidation<'a> { nexinets::transformers::NexinetsAuthType::try_from(self.auth_type)?; Ok(()) } + api_enums::Connector::Nexixpay => { + nexixpay::transformers::NexixpayAuthType::try_from(self.auth_type)?; + Ok(()) + } api_enums::Connector::Nmi => { nmi::transformers::NmiAuthType::try_from(self.auth_type)?; Ok(()) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 6e785b55a3..769619d951 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2314,10 +2314,12 @@ where ) && router_data.status != common_enums::AttemptStatus::AuthenticationFailed; (router_data, should_continue) - } else if (connector.connector_name == router_types::Connector::Nuvei - || connector.connector_name == router_types::Connector::Shift4) - && router_data.auth_type == common_enums::AuthenticationType::ThreeDs - && !is_operation_complete_authorize(&operation) + } else if router_data.auth_type == common_enums::AuthenticationType::ThreeDs + && ((connector.connector_name == router_types::Connector::Nexixpay + && is_operation_complete_authorize(&operation)) + || ((connector.connector_name == router_types::Connector::Nuvei + || connector.connector_name == router_types::Connector::Shift4) + && !is_operation_complete_authorize(&operation))) { router_data = router_data.preprocessing_steps(state, connector).await?; (router_data, should_continue_payment) diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 2c5a88e69b..f96ead676a 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -2401,6 +2401,7 @@ impl TryFrom> for types::PaymentsPreProce mandate_id: payment_data.mandate_id, related_transaction_id: None, enrolled_for_3ds: true, + metadata: payment_data.payment_intent.metadata.map(Secret::new), }) } } diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 7444fea0c3..197c1e4c52 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -496,7 +496,7 @@ pub async fn send_request( )) }; - // We cannot clone the request type, because it has Form trait which is not clonable. So we are cloning the request builder here. + // We cannot clone the request type, because it has Form trait which is not cloneable. So we are cloning the request builder here. let cloned_send_request = request.try_clone().map(|cloned_request| async { cloned_request .send() @@ -570,7 +570,7 @@ pub async fn send_request( .await } None => { - logger::info!("Retrying request due to connection closed before message could complete failed as request is not clonable"); + logger::info!("Retrying request due to connection closed before message could complete failed as request is not cloneable"); Err(error) } } diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 5fb945daf8..c9aaed13d2 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -430,6 +430,9 @@ impl ConnectorData { } enums::Connector::Klarna => Ok(ConnectorEnum::Old(Box::new(&connector::Klarna))), enums::Connector::Mollie => Ok(ConnectorEnum::Old(Box::new(&connector::Mollie))), + enums::Connector::Nexixpay => { + Ok(ConnectorEnum::Old(Box::new(connector::Nexixpay::new()))) + } enums::Connector::Nmi => Ok(ConnectorEnum::Old(Box::new(connector::Nmi::new()))), enums::Connector::Noon => Ok(ConnectorEnum::Old(Box::new(connector::Noon::new()))), enums::Connector::Novalnet => { diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index b032906293..a75ef879da 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -289,7 +289,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { })? } api_enums::Connector::Nexinets => Self::Nexinets, - // api_enums::Connector::Nexixpay => Self::Nexixpay, + api_enums::Connector::Nexixpay => Self::Nexixpay, api_enums::Connector::Nmi => Self::Nmi, api_enums::Connector::Noon => Self::Noon, api_enums::Connector::Novalnet => Self::Novalnet, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Nexixpay.js b/cypress-tests/cypress/e2e/PaymentUtils/Nexixpay.js new file mode 100644 index 0000000000..ab9edc62c9 --- /dev/null +++ b/cypress-tests/cypress/e2e/PaymentUtils/Nexixpay.js @@ -0,0 +1,188 @@ +import { getCustomExchange } from "./Commons"; + +const successfulNo3DSCardDetails = { + card_number: "4012000033330026", + card_exp_month: "01", + card_exp_year: "25", + card_holder_name: "joseph Doe", + card_cvc: "123", +}; + +const successfulThreeDSTestCardDetails = { + card_number: "4349940199004549", + card_exp_month: "12", + card_exp_year: "30", + card_holder_name: "joseph Doe", + card_cvc: "396", +}; + +export const connectorDetails = { + card_pm: { + PaymentIntent: { + Request: { + currency: "EUR", + amount: 3545, + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }, + "3DSManualCapture": { + Request: { + payment_method: "card", + billing: { + address: { + line1: "1467", + line2: "CA", + line3: "CA", + city: "Florence", + state: "Tuscany", + zip: "12345", + country: "IT", + first_name: "Max", + last_name: "Mustermann" + }, + email: "mauro.morandi@nexi.it", + phone: { + number: "9123456789", + country_code: "+91" + } + }, + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_capture", + }, + }, + }, + "3DSAutoCapture": { + Request: { + payment_method: "card", + billing: { + address: { + line1: "1467", + line2: "CA", + line3: "CA", + city: "Florence", + state: "Tuscany", + zip: "12345", + country: "IT", + first_name: "Max", + last_name: "Mustermann" + }, + email: "mauro.morandi@nexi.it", + phone: { + number: "9123456789", + country_code: "+91" + } + }, + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", + }, + }, + }, + Capture: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "processing", + amount: 3545, + amount_capturable: 0, + amount_received: 3545, + }, + }, + }, + PartialCapture: { + Request: {}, + Response: { + status: 200, + body: { + status: "processing", + amount: 3545, + amount_capturable: 0, + amount_received: 100, + }, + }, + }, + Void: { + Request: {}, + Response: { + status: 200, + body: { + status: "cancelled", + }, + }, + }, + Refund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + PartialRefund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + SyncRefund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulThreeDSTestCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + }, +}; diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Utils.js b/cypress-tests/cypress/e2e/PaymentUtils/Utils.js index 03fc7fe972..18f4df3eb5 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Utils.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Utils.js @@ -10,6 +10,7 @@ import { connectorDetails as datatransConnectorDetails } from "./Datatrans.js"; import { connectorDetails as fiservemeaConnectorDetails } from "./Fiservemea.js"; import { connectorDetails as iatapayConnectorDetails } from "./Iatapay.js"; import { connectorDetails as itaubankConnectorDetails } from "./ItauBank.js"; +import { connectorDetails as nexixpayConnectorDetails } from "./Nexixpay.js"; import { connectorDetails as nmiConnectorDetails } from "./Nmi.js"; import { connectorDetails as novalnetConnectorDetails } from "./Novalnet.js"; import { connectorDetails as payboxConnectorDetails } from "./Paybox.js"; @@ -28,6 +29,7 @@ const connectorDetails = { fiservemea: fiservemeaConnectorDetails, iatapay: iatapayConnectorDetails, itaubank: itaubankConnectorDetails, + nexixpay: nexixpayConnectorDetails, nmi: nmiConnectorDetails, novalnet: novalnetConnectorDetails, paybox: payboxConnectorDetails,