diff --git a/.typos.toml b/.typos.toml index 0316374cd6..d9b20ce32e 100644 --- a/.typos.toml +++ b/.typos.toml @@ -43,6 +43,7 @@ ws2tcpip = "ws2tcpip" # WinSock Extension ZAR = "ZAR" # South African Rand currency code JOD = "JOD" # Jordan currency code Payed = "Payed" # Paid status for digital virgo +SessionConnectorDatas = "SessionConnectorDatas" # Wrapper for List of SessionConnectorData Alo = "Alo" # Is iso representation of a state in France alo = "alo" # Is iso representation of a state in France BRE = "BRE" # Is iso representation of Brittany diff --git a/Cargo.lock b/Cargo.lock index 77a8968df4..debcaf5d28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4403,6 +4403,7 @@ version = "0.1.0" dependencies = [ "api_models", "common_enums", + "common_types", "common_utils", "criterion", "euclid", diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index d8e2646da4..34ca629725 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -205,13 +205,14 @@ pub struct MerchantAccountCreateWithoutOrgId { // The following struct is only used internally, so we can reuse the common // part of `create_merchant_account` without duplicating its code for v2 #[cfg(feature = "v2")] -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Serialize, ToSchema)] pub struct MerchantAccountCreate { pub merchant_name: Secret, pub merchant_details: Option, pub metadata: Option, pub organization_id: id_type::OrganizationId, /// Product Type of this merchant account + #[schema(value_type = Option)] pub product_type: Option, } diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index cc87b85c1c..693e9585dd 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -161,7 +161,7 @@ impl EuclidAnalysable for ConnectorSelection { } } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq)] pub struct ConnectorVolumeSplit { pub connector: RoutableConnectorChoice, pub split: u8, @@ -178,7 +178,7 @@ pub struct RoutableConnectorChoice { pub merchant_connector_id: Option, } -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema, PartialEq)] pub enum RoutableChoiceKind { OnlyConnector, FullStruct, @@ -337,7 +337,7 @@ impl TryFrom for RoutingAlgorithm { } } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema, PartialEq)] #[serde( tag = "type", content = "data", diff --git a/crates/common_enums/src/connector_enums.rs b/crates/common_enums/src/connector_enums.rs index 87595c3f6c..b45b3a6979 100644 --- a/crates/common_enums/src/connector_enums.rs +++ b/crates/common_enums/src/connector_enums.rs @@ -594,3 +594,115 @@ impl From for Connector { } } } + +impl TryFrom for RoutableConnectors { + type Error = &'static str; + + fn try_from(connector: Connector) -> Result { + match connector { + Connector::Adyenplatform => Ok(Self::Adyenplatform), + #[cfg(feature = "dummy_connector")] + Connector::DummyConnector1 => Ok(Self::DummyConnector1), + #[cfg(feature = "dummy_connector")] + Connector::DummyConnector2 => Ok(Self::DummyConnector2), + #[cfg(feature = "dummy_connector")] + Connector::DummyConnector3 => Ok(Self::DummyConnector3), + #[cfg(feature = "dummy_connector")] + Connector::DummyConnector4 => Ok(Self::DummyConnector4), + #[cfg(feature = "dummy_connector")] + Connector::DummyConnector5 => Ok(Self::DummyConnector5), + #[cfg(feature = "dummy_connector")] + Connector::DummyConnector6 => Ok(Self::DummyConnector6), + #[cfg(feature = "dummy_connector")] + Connector::DummyConnector7 => Ok(Self::DummyConnector7), + Connector::Aci => Ok(Self::Aci), + Connector::Adyen => Ok(Self::Adyen), + Connector::Airwallex => Ok(Self::Airwallex), + Connector::Authorizedotnet => Ok(Self::Authorizedotnet), + Connector::Bankofamerica => Ok(Self::Bankofamerica), + Connector::Billwerk => Ok(Self::Billwerk), + Connector::Bitpay => Ok(Self::Bitpay), + Connector::Bambora => Ok(Self::Bambora), + Connector::Bamboraapac => Ok(Self::Bamboraapac), + Connector::Bluesnap => Ok(Self::Bluesnap), + Connector::Boku => Ok(Self::Boku), + Connector::Braintree => Ok(Self::Braintree), + Connector::Cashtocode => Ok(Self::Cashtocode), + Connector::Chargebee => Ok(Self::Chargebee), + Connector::Checkout => Ok(Self::Checkout), + Connector::Coinbase => Ok(Self::Coinbase), + Connector::Coingate => Ok(Self::Coingate), + Connector::Cryptopay => Ok(Self::Cryptopay), + Connector::Cybersource => Ok(Self::Cybersource), + Connector::Datatrans => Ok(Self::Datatrans), + Connector::Deutschebank => Ok(Self::Deutschebank), + Connector::Digitalvirgo => Ok(Self::Digitalvirgo), + Connector::Dlocal => Ok(Self::Dlocal), + Connector::Ebanx => Ok(Self::Ebanx), + Connector::Elavon => Ok(Self::Elavon), + Connector::Fiserv => Ok(Self::Fiserv), + Connector::Fiservemea => Ok(Self::Fiservemea), + Connector::Fiuu => Ok(Self::Fiuu), + Connector::Forte => Ok(Self::Forte), + Connector::Globalpay => Ok(Self::Globalpay), + Connector::Globepay => Ok(Self::Globepay), + Connector::Gocardless => Ok(Self::Gocardless), + Connector::Helcim => Ok(Self::Helcim), + Connector::Iatapay => Ok(Self::Iatapay), + Connector::Itaubank => Ok(Self::Itaubank), + Connector::Jpmorgan => Ok(Self::Jpmorgan), + Connector::Klarna => Ok(Self::Klarna), + Connector::Mifinity => Ok(Self::Mifinity), + Connector::Mollie => Ok(Self::Mollie), + Connector::Moneris => Ok(Self::Moneris), + Connector::Multisafepay => Ok(Self::Multisafepay), + Connector::Nexinets => Ok(Self::Nexinets), + Connector::Nexixpay => Ok(Self::Nexixpay), + Connector::Nmi => Ok(Self::Nmi), + Connector::Nomupay => Ok(Self::Nomupay), + Connector::Noon => Ok(Self::Noon), + Connector::Novalnet => Ok(Self::Novalnet), + Connector::Nuvei => Ok(Self::Nuvei), + Connector::Opennode => Ok(Self::Opennode), + Connector::Paybox => Ok(Self::Paybox), + Connector::Payme => Ok(Self::Payme), + Connector::Payone => Ok(Self::Payone), + Connector::Paypal => Ok(Self::Paypal), + Connector::Paystack => Ok(Self::Paystack), + Connector::Payu => Ok(Self::Payu), + Connector::Placetopay => Ok(Self::Placetopay), + Connector::Powertranz => Ok(Self::Powertranz), + Connector::Prophetpay => Ok(Self::Prophetpay), + Connector::Rapyd => Ok(Self::Rapyd), + Connector::Razorpay => Ok(Self::Razorpay), + Connector::Riskified => Ok(Self::Riskified), + Connector::Shift4 => Ok(Self::Shift4), + Connector::Signifyd => Ok(Self::Signifyd), + Connector::Square => Ok(Self::Square), + Connector::Stax => Ok(Self::Stax), + Connector::Stripe => Ok(Self::Stripe), + Connector::Trustpay => Ok(Self::Trustpay), + Connector::Tsys => Ok(Self::Tsys), + Connector::Volt => Ok(Self::Volt), + Connector::Wellsfargo => Ok(Self::Wellsfargo), + Connector::Wise => Ok(Self::Wise), + Connector::Worldline => Ok(Self::Worldline), + Connector::Worldpay => Ok(Self::Worldpay), + Connector::Xendit => Ok(Self::Xendit), + Connector::Zen => Ok(Self::Zen), + Connector::Plaid => Ok(Self::Plaid), + Connector::Zsl => Ok(Self::Zsl), + Connector::Recurly => Ok(Self::Recurly), + Connector::Getnet => Ok(Self::Getnet), + Connector::Hipay => Ok(Self::Hipay), + Connector::Inespay => Ok(Self::Inespay), + Connector::Redsys => Ok(Self::Redsys), + Connector::CtpMastercard + | Connector::Gpayments + | Connector::Juspaythreedsserver + | Connector::Netcetera + | Connector::Taxjar + | Connector::Threedsecureio => Err("Invalid conversion. Not a routable connector"), + } + } +} diff --git a/crates/common_utils/src/macros.rs b/crates/common_utils/src/macros.rs index 339cf236c7..2ab0560cd4 100644 --- a/crates/common_utils/src/macros.rs +++ b/crates/common_utils/src/macros.rs @@ -381,20 +381,41 @@ macro_rules! create_list_wrapper { $($function_def: tt)* } ) => { + #[derive(Clone, Debug)] pub struct $wrapper_name(Vec<$type_name>); impl $wrapper_name { pub fn new(list: Vec<$type_name>) -> Self { Self(list) } - pub fn iter(&self) -> std::slice::Iter<'_, $type_name> { - self.0.iter() + pub fn with_capacity(size: usize) -> Self { + Self(Vec::with_capacity(size)) } $($function_def)* } - impl Iterator for $wrapper_name { + impl std::ops::Deref for $wrapper_name { + type Target = Vec<$type_name>; + fn deref(&self) -> &::Target { + &self.0 + } + } + impl std::ops::DerefMut for $wrapper_name { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + impl IntoIterator for $wrapper_name { type Item = $type_name; - fn next(&mut self) -> Option { - self.0.pop() + type IntoIter = std::vec::IntoIter<$type_name>; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } + } + + impl<'a> IntoIterator for &'a $wrapper_name { + type Item = &'a $type_name; + type IntoIter = std::slice::Iter<'a, $type_name>; + fn into_iter(self) -> Self::IntoIter { + self.0.iter() } } diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 27a572fa8e..6753afc2d9 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -559,6 +559,7 @@ pub struct PaymentIntentUpdateFields { #[diesel(table_name = payment_intent)] pub struct PaymentIntentUpdateInternal { pub status: Option, + pub prerouting_algorithm: Option, pub amount_captured: Option, pub modified_at: PrimitiveDateTime, pub active_attempt_id: Option>, diff --git a/crates/hyperswitch_connectors/src/connectors/coinbase.rs b/crates/hyperswitch_connectors/src/connectors/coinbase.rs index 9e998abebb..5a28044101 100644 --- a/crates/hyperswitch_connectors/src/connectors/coinbase.rs +++ b/crates/hyperswitch_connectors/src/connectors/coinbase.rs @@ -11,6 +11,7 @@ use common_utils::{ }; use error_stack::ResultExt; use hyperswitch_domain_models::{ + configs::Connectors, router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, router_flow_types::{ access_token_auth::AccessTokenAuth, @@ -36,7 +37,6 @@ use hyperswitch_interfaces::{ self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications, ConnectorValidation, }, - configs::Connectors, errors, events::connector_api_logs::ConnectorEvent, types::{PaymentsAuthorizeType, PaymentsSyncType, Response}, diff --git a/crates/hyperswitch_connectors/src/connectors/helcim.rs b/crates/hyperswitch_connectors/src/connectors/helcim.rs index 65927bb0f3..f625b09f68 100644 --- a/crates/hyperswitch_connectors/src/connectors/helcim.rs +++ b/crates/hyperswitch_connectors/src/connectors/helcim.rs @@ -10,6 +10,8 @@ use common_utils::{ }; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ + errors::api_error_response::ApiErrorResponse, + payments::payment_attempt::PaymentAttempt, router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, router_flow_types::{ access_token_auth::AccessTokenAuth, @@ -33,7 +35,7 @@ use hyperswitch_domain_models::{ use hyperswitch_interfaces::{ api::{ self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications, - ConnectorValidation, + ConnectorTransactionId, ConnectorValidation, }, configs::Connectors, consts::NO_ERROR_CODE, @@ -43,6 +45,8 @@ use hyperswitch_interfaces::{ webhooks::{IncomingWebhook, IncomingWebhookRequestDetails}, }; use lazy_static::lazy_static; +#[cfg(feature = "v2")] +use masking::PeekInterface; use masking::{ExposeInterface, Mask}; use transformers as helcim; @@ -917,3 +921,44 @@ impl ConnectorSpecifications for Helcim { Some(&*HELCIM_SUPPORTED_WEBHOOK_FLOWS) } } + +impl ConnectorTransactionId for Helcim { + #[cfg(feature = "v1")] + fn connector_transaction_id( + &self, + payment_attempt: PaymentAttempt, + ) -> Result, ApiErrorResponse> { + if payment_attempt.get_connector_payment_id().is_none() { + let metadata = + Self::connector_transaction_id(self, payment_attempt.connector_metadata.as_ref()); + metadata.map_err(|_| ApiErrorResponse::ResourceIdNotFound) + } else { + Ok(payment_attempt + .get_connector_payment_id() + .map(ToString::to_string)) + } + } + + #[cfg(feature = "v2")] + fn connector_transaction_id( + &self, + payment_attempt: PaymentAttempt, + ) -> Result, ApiErrorResponse> { + use hyperswitch_domain_models::errors::api_error_response::ApiErrorResponse; + + if payment_attempt.get_connector_payment_id().is_none() { + let metadata = Self::connector_transaction_id( + self, + payment_attempt + .connector_metadata + .as_ref() + .map(|connector_metadata| connector_metadata.peek()), + ); + metadata.map_err(|_| ApiErrorResponse::ResourceIdNotFound) + } else { + Ok(payment_attempt + .get_connector_payment_id() + .map(ToString::to_string)) + } + } +} diff --git a/crates/hyperswitch_connectors/src/connectors/nexinets.rs b/crates/hyperswitch_connectors/src/connectors/nexinets.rs index 69a8543286..7f3deb0e06 100644 --- a/crates/hyperswitch_connectors/src/connectors/nexinets.rs +++ b/crates/hyperswitch_connectors/src/connectors/nexinets.rs @@ -9,7 +9,9 @@ use common_utils::{ }; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{ + errors::api_error_response::ApiErrorResponse, payment_method_data::PaymentMethodData, + payments::payment_attempt::PaymentAttempt, router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, router_flow_types::{ access_token_auth::AccessTokenAuth, @@ -30,7 +32,7 @@ use hyperswitch_domain_models::{ use hyperswitch_interfaces::{ api::{ self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications, - ConnectorValidation, + ConnectorTransactionId, ConnectorValidation, }, configs::Connectors, errors, @@ -42,6 +44,8 @@ use hyperswitch_interfaces::{ webhooks::{IncomingWebhook, IncomingWebhookRequestDetails}, }; use masking::Mask; +#[cfg(feature = "v2")] +use masking::PeekInterface; use transformers as nexinets; use crate::{ @@ -746,3 +750,32 @@ impl ConnectorIntegration Result, ApiErrorResponse> { + let metadata = + Self::connector_transaction_id(self, payment_attempt.connector_metadata.as_ref()); + metadata.map_err(|_| ApiErrorResponse::ResourceIdNotFound) + } + + #[cfg(feature = "v2")] + fn connector_transaction_id( + &self, + payment_attempt: PaymentAttempt, + ) -> Result, ApiErrorResponse> { + use hyperswitch_domain_models::errors::api_error_response::ApiErrorResponse; + + let metadata = Self::connector_transaction_id( + self, + payment_attempt + .connector_metadata + .as_ref() + .map(|connector_metadata| connector_metadata.peek()), + ); + metadata.map_err(|_| ApiErrorResponse::ResourceIdNotFound) + } +} diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs index 00d771f7d1..734780a3a6 100644 --- a/crates/hyperswitch_connectors/src/default_implementations.rs +++ b/crates/hyperswitch_connectors/src/default_implementations.rs @@ -36,6 +36,9 @@ use hyperswitch_domain_models::{ }; use hyperswitch_domain_models::{ router_flow_types::{ + authentication::{ + Authentication, PostAuthentication, PreAuthentication, PreAuthenticationVersionCall, + }, dispute::{Accept, Defend, Evidence}, files::{Retrieve, Upload}, mandate_revoke::MandateRevoke, @@ -48,6 +51,7 @@ use hyperswitch_domain_models::{ Authenticate, AuthenticationConfirmation, PostAuthenticate, PreAuthenticate, }, router_request_types::{ + authentication, unified_authentication_service::{ UasAuthenticationRequestData, UasAuthenticationResponseData, UasConfirmationRequestData, UasPostAuthenticationRequestData, @@ -61,14 +65,15 @@ use hyperswitch_domain_models::{ SubmitEvidenceRequestData, UploadFileRequestData, VerifyWebhookSourceRequestData, }, router_response_types::{ - AcceptDisputeResponse, DefendDisputeResponse, MandateRevokeResponseData, - PaymentsResponseData, RetrieveFileResponse, SubmitEvidenceResponse, - TaxCalculationResponseData, UploadFileResponse, VerifyWebhookSourceResponseData, + AcceptDisputeResponse, AuthenticationResponseData, DefendDisputeResponse, + MandateRevokeResponseData, PaymentsResponseData, RetrieveFileResponse, + SubmitEvidenceResponse, TaxCalculationResponseData, UploadFileResponse, + VerifyWebhookSourceResponseData, }, }; #[cfg(feature = "frm")] use hyperswitch_interfaces::api::fraud_check::{ - FraudCheckCheckout, FraudCheckFulfillment, FraudCheckRecordReturn, FraudCheckSale, + FraudCheck, FraudCheckCheckout, FraudCheckFulfillment, FraudCheckRecordReturn, FraudCheckSale, FraudCheckTransaction, }; #[cfg(feature = "payouts")] @@ -83,6 +88,10 @@ use hyperswitch_interfaces::api::revenue_recovery::{ use hyperswitch_interfaces::{ api::{ self, + authentication::{ + ConnectorAuthentication, ConnectorPostAuthentication, ConnectorPreAuthentication, + ConnectorPreAuthenticationVersionCall, ExternalAuthentication, + }, disputes::{AcceptDispute, DefendDispute, Dispute, SubmitEvidence}, files::{FileUpload, RetrieveFile, UploadFile}, payments::{ @@ -92,9 +101,9 @@ use hyperswitch_interfaces::{ PaymentsPreProcessing, TaxCalculation, }, revenue_recovery::RevenueRecovery, - ConnectorIntegration, ConnectorMandateRevoke, ConnectorRedirectResponse, UasAuthentication, - UasAuthenticationConfirmation, UasPostAuthentication, UasPreAuthentication, - UnifiedAuthenticationService, + ConnectorIntegration, ConnectorMandateRevoke, ConnectorRedirectResponse, + ConnectorTransactionId, UasAuthentication, UasAuthenticationConfirmation, + UasPostAuthentication, UasPreAuthentication, UnifiedAuthenticationService, }, errors::ConnectorError, }; @@ -3460,104 +3469,6 @@ default_imp_for_uas_post_authentication!( connectors::Zsl ); -macro_rules! default_imp_for_uas_authentication { - ($($path:ident::$connector:ident),*) => { - $( impl UasAuthentication for $path::$connector {} - impl - ConnectorIntegration< - Authenticate, - UasAuthenticationRequestData, - UasAuthenticationResponseData - > for $path::$connector - {} - )* - }; -} - -default_imp_for_uas_authentication!( - connectors::Aci, - connectors::Adyen, - connectors::Airwallex, - connectors::Amazonpay, - connectors::Authorizedotnet, - connectors::Bambora, - connectors::Bamboraapac, - connectors::Bankofamerica, - connectors::Billwerk, - connectors::Bitpay, - connectors::Bluesnap, - connectors::Braintree, - connectors::Boku, - connectors::Cashtocode, - connectors::Chargebee, - connectors::Checkout, - connectors::Coinbase, - connectors::Coingate, - connectors::Cryptopay, - connectors::CtpMastercard, - connectors::Cybersource, - connectors::Datatrans, - connectors::Deutschebank, - connectors::Digitalvirgo, - connectors::Dlocal, - connectors::Elavon, - connectors::Fiserv, - connectors::Fiservemea, - connectors::Fiuu, - connectors::Forte, - connectors::Getnet, - connectors::Globalpay, - connectors::Globepay, - connectors::Gocardless, - connectors::Hipay, - connectors::Helcim, - connectors::Iatapay, - connectors::Inespay, - connectors::Itaubank, - connectors::Jpmorgan, - connectors::Klarna, - connectors::Nomupay, - connectors::Noon, - connectors::Novalnet, - connectors::Nexinets, - connectors::Nexixpay, - connectors::Opayo, - connectors::Opennode, - connectors::Nuvei, - connectors::Payeezy, - connectors::Payme, - connectors::Paystack, - connectors::Payu, - connectors::Powertranz, - connectors::Prophetpay, - connectors::Mifinity, - connectors::Mollie, - connectors::Moneris, - connectors::Multisafepay, - connectors::Paybox, - connectors::Paypal, - connectors::Placetopay, - connectors::Rapyd, - connectors::Razorpay, - connectors::Recurly, - connectors::Redsys, - connectors::Shift4, - connectors::Stax, - connectors::Square, - connectors::Stripebilling, - connectors::Taxjar, - connectors::Thunes, - connectors::Trustpay, - connectors::Tsys, - connectors::Wellsfargo, - connectors::Worldline, - connectors::Worldpay, - connectors::Volt, - connectors::Xendit, - connectors::Zen, - connectors::Zsl -); - macro_rules! default_imp_for_uas_authentication_confirmation { ($($path:ident::$connector:ident),*) => { $( impl UasAuthenticationConfirmation for $path::$connector {} @@ -3620,6 +3531,316 @@ default_imp_for_uas_authentication_confirmation!( connectors::Novalnet, connectors::Nexinets, connectors::Nexixpay, + connectors::Opayo, + connectors::Opennode, + connectors::Nuvei, + connectors::Payeezy, + connectors::Payme, + connectors::Paystack, + connectors::Payu, + connectors::Powertranz, + connectors::Prophetpay, + connectors::Mifinity, + connectors::Mollie, + connectors::Moneris, + connectors::Multisafepay, + connectors::Paybox, + connectors::Paypal, + connectors::Placetopay, + connectors::Rapyd, + connectors::Razorpay, + connectors::Recurly, + connectors::Redsys, + connectors::Shift4, + connectors::Stax, + connectors::Square, + connectors::Stripebilling, + connectors::Taxjar, + connectors::Thunes, + connectors::Trustpay, + connectors::Tsys, + connectors::Worldline, + connectors::Worldpay, + connectors::Wellsfargo, + connectors::Volt, + connectors::Xendit, + connectors::Zen, + connectors::Zsl +); + +macro_rules! default_imp_for_connector_request_id { + ($($path:ident::$connector:ident),*) => { + $( + impl ConnectorTransactionId for $path::$connector {} + )* + }; +} +default_imp_for_connector_request_id!( + connectors::Aci, + connectors::Adyen, + connectors::Airwallex, + connectors::Authorizedotnet, + connectors::Amazonpay, + connectors::Bambora, + connectors::Bamboraapac, + connectors::Bankofamerica, + connectors::Billwerk, + connectors::Bluesnap, + connectors::Bitpay, + connectors::Braintree, + connectors::Boku, + connectors::Cashtocode, + connectors::Chargebee, + connectors::Checkout, + connectors::Coinbase, + connectors::Coingate, + connectors::Cryptopay, + connectors::CtpMastercard, + connectors::Cybersource, + connectors::Datatrans, + connectors::Deutschebank, + connectors::Digitalvirgo, + connectors::Dlocal, + connectors::Elavon, + connectors::Fiserv, + connectors::Fiservemea, + connectors::Fiuu, + connectors::Forte, + connectors::Getnet, + connectors::Globalpay, + connectors::Globepay, + connectors::Gocardless, + connectors::Hipay, + connectors::Iatapay, + connectors::Inespay, + connectors::Itaubank, + connectors::Jpmorgan, + connectors::Juspaythreedsserver, + connectors::Klarna, + connectors::Nomupay, + connectors::Novalnet, + connectors::Noon, + connectors::Nexixpay, + connectors::Nuvei, + connectors::Opayo, + connectors::Opennode, + connectors::Payeezy, + connectors::Paystack, + connectors::Payu, + connectors::Payme, + connectors::Paypal, + connectors::Powertranz, + connectors::Prophetpay, + connectors::Mifinity, + connectors::Mollie, + connectors::Moneris, + connectors::Multisafepay, + connectors::Paybox, + connectors::Placetopay, + connectors::Rapyd, + connectors::Razorpay, + connectors::Recurly, + connectors::Redsys, + connectors::Shift4, + connectors::Stax, + connectors::Square, + connectors::Stripebilling, + connectors::Taxjar, + connectors::Thunes, + connectors::Trustpay, + connectors::Tsys, + connectors::UnifiedAuthenticationService, + connectors::Worldline, + connectors::Worldpay, + connectors::Wellsfargo, + connectors::Volt, + connectors::Xendit, + connectors::Zen, + connectors::Zsl +); + +#[cfg(feature = "frm")] +macro_rules! default_imp_for_fraud_check { + ($($path:ident::$connector:ident),*) => { + $( + impl FraudCheck for $path::$connector {} + )* + }; +} +#[cfg(feature = "frm")] +default_imp_for_fraud_check!( + connectors::Aci, + connectors::Adyen, + connectors::Airwallex, + connectors::Amazonpay, + connectors::Authorizedotnet, + connectors::Bambora, + connectors::Bamboraapac, + connectors::Bankofamerica, + connectors::Billwerk, + connectors::Bluesnap, + connectors::Bitpay, + connectors::Braintree, + connectors::Boku, + connectors::Cashtocode, + connectors::Chargebee, + connectors::Checkout, + connectors::Coinbase, + connectors::Coingate, + connectors::Cryptopay, + connectors::CtpMastercard, + connectors::Cybersource, + connectors::Datatrans, + connectors::Deutschebank, + connectors::Digitalvirgo, + connectors::Dlocal, + connectors::Elavon, + connectors::Fiserv, + connectors::Fiservemea, + connectors::Fiuu, + connectors::Forte, + connectors::Getnet, + connectors::Globalpay, + connectors::Globepay, + connectors::Gocardless, + connectors::Helcim, + connectors::Hipay, + connectors::Iatapay, + connectors::Inespay, + connectors::Itaubank, + connectors::Jpmorgan, + connectors::Juspaythreedsserver, + connectors::Klarna, + connectors::Nomupay, + connectors::Novalnet, + connectors::Noon, + connectors::Nexinets, + connectors::Nexixpay, + connectors::Nuvei, + connectors::Opayo, + connectors::Payeezy, + connectors::Paystack, + connectors::Paypal, + connectors::Payu, + connectors::Powertranz, + connectors::Prophetpay, + connectors::Mifinity, + connectors::Mollie, + connectors::Moneris, + connectors::Multisafepay, + connectors::Opennode, + connectors::Paybox, + connectors::Payme, + connectors::Placetopay, + connectors::Rapyd, + connectors::Razorpay, + connectors::Recurly, + connectors::Redsys, + connectors::Shift4, + connectors::Stax, + connectors::Square, + connectors::Stripebilling, + connectors::Taxjar, + connectors::Thunes, + connectors::Trustpay, + connectors::Tsys, + connectors::UnifiedAuthenticationService, + connectors::Worldline, + connectors::Worldpay, + connectors::Wellsfargo, + connectors::Volt, + connectors::Xendit, + connectors::Zen, + connectors::Zsl +); + +macro_rules! default_imp_for_connector_authentication { + ($($path:ident::$connector:ident),*) => { + $( impl ExternalAuthentication for $path::$connector {} + impl ConnectorAuthentication for $path::$connector {} + impl ConnectorPreAuthentication for $path::$connector {} + impl ConnectorPreAuthenticationVersionCall for $path::$connector {} + impl ConnectorPostAuthentication for $path::$connector {} + impl + ConnectorIntegration< + Authentication, + authentication::ConnectorAuthenticationRequestData, + AuthenticationResponseData, + > for $path::$connector + {} + impl + ConnectorIntegration< + PreAuthentication, + authentication::PreAuthNRequestData, + AuthenticationResponseData, + > for $path::$connector + {} + impl + ConnectorIntegration< + PreAuthenticationVersionCall, + authentication::PreAuthNRequestData, + AuthenticationResponseData, + > for $path::$connector + {} + impl + ConnectorIntegration< + PostAuthentication, + authentication::ConnectorPostAuthenticationRequestData, + AuthenticationResponseData, + > for $path::$connector + {} + )* + }; +} + +default_imp_for_connector_authentication!( + connectors::Aci, + connectors::Adyen, + connectors::Airwallex, + connectors::Amazonpay, + connectors::Authorizedotnet, + connectors::Bambora, + connectors::Bamboraapac, + connectors::Bankofamerica, + connectors::Billwerk, + connectors::Bluesnap, + connectors::Bitpay, + connectors::Braintree, + connectors::Boku, + connectors::Cashtocode, + connectors::Chargebee, + connectors::Checkout, + connectors::Coinbase, + connectors::Coingate, + connectors::Cryptopay, + connectors::CtpMastercard, + connectors::Cybersource, + connectors::Datatrans, + connectors::Deutschebank, + connectors::Digitalvirgo, + connectors::Dlocal, + connectors::Elavon, + connectors::Fiserv, + connectors::Fiservemea, + connectors::Fiuu, + connectors::Forte, + connectors::Getnet, + connectors::Globalpay, + connectors::Globepay, + connectors::Gocardless, + connectors::Hipay, + connectors::Helcim, + connectors::Iatapay, + connectors::Inespay, + connectors::Itaubank, + connectors::Jpmorgan, + connectors::Juspaythreedsserver, + connectors::Klarna, + connectors::Nomupay, + connectors::Noon, + connectors::Novalnet, + connectors::Nexinets, + connectors::Nexixpay, connectors::Nuvei, connectors::Opayo, connectors::Opennode, @@ -3648,6 +3869,7 @@ default_imp_for_uas_authentication_confirmation!( connectors::Thunes, connectors::Trustpay, connectors::Tsys, + connectors::UnifiedAuthenticationService, connectors::Worldline, connectors::Worldpay, connectors::Wellsfargo, @@ -3657,6 +3879,103 @@ default_imp_for_uas_authentication_confirmation!( connectors::Zsl ); +macro_rules! default_imp_for_uas_authentication { + ($($path:ident::$connector:ident),*) => { + $( impl UasAuthentication for $path::$connector {} + impl + ConnectorIntegration< + Authenticate, + UasAuthenticationRequestData, + UasAuthenticationResponseData + > for $path::$connector + {} + )* + }; +} +default_imp_for_uas_authentication!( + connectors::Aci, + connectors::Adyen, + connectors::Airwallex, + connectors::Amazonpay, + connectors::Authorizedotnet, + connectors::Bambora, + connectors::Bamboraapac, + connectors::Bankofamerica, + connectors::Billwerk, + connectors::Bitpay, + connectors::Bluesnap, + connectors::Braintree, + connectors::Boku, + connectors::Cashtocode, + connectors::Chargebee, + connectors::Checkout, + connectors::Coinbase, + connectors::Coingate, + connectors::Cryptopay, + connectors::CtpMastercard, + connectors::Cybersource, + connectors::Datatrans, + connectors::Deutschebank, + connectors::Digitalvirgo, + connectors::Dlocal, + connectors::Elavon, + connectors::Fiserv, + connectors::Fiservemea, + connectors::Fiuu, + connectors::Forte, + connectors::Getnet, + connectors::Globalpay, + connectors::Globepay, + connectors::Gocardless, + connectors::Helcim, + connectors::Hipay, + connectors::Iatapay, + connectors::Inespay, + connectors::Itaubank, + connectors::Jpmorgan, + connectors::Klarna, + connectors::Nomupay, + connectors::Novalnet, + connectors::Nexinets, + connectors::Nexixpay, + connectors::Nuvei, + connectors::Payeezy, + connectors::Paypal, + connectors::Paystack, + connectors::Payu, + connectors::Powertranz, + connectors::Prophetpay, + connectors::Mifinity, + connectors::Mollie, + connectors::Moneris, + connectors::Multisafepay, + connectors::Noon, + connectors::Opayo, + connectors::Opennode, + connectors::Paybox, + connectors::Payme, + connectors::Placetopay, + connectors::Rapyd, + connectors::Razorpay, + connectors::Recurly, + connectors::Redsys, + connectors::Shift4, + connectors::Stax, + connectors::Square, + connectors::Stripebilling, + connectors::Taxjar, + connectors::Thunes, + connectors::Trustpay, + connectors::Tsys, + connectors::Wellsfargo, + connectors::Worldline, + connectors::Worldpay, + connectors::Volt, + connectors::Xendit, + connectors::Zen, + connectors::Zsl +); + #[cfg(all(feature = "v2", feature = "revenue_recovery"))] macro_rules! default_imp_for_revenue_recovery_record_back { ($($path:ident::$connector:ident),*) => { diff --git a/crates/hyperswitch_connectors/src/default_implementations_v2.rs b/crates/hyperswitch_connectors/src/default_implementations_v2.rs index a3a196afa7..864f9e4498 100644 --- a/crates/hyperswitch_connectors/src/default_implementations_v2.rs +++ b/crates/hyperswitch_connectors/src/default_implementations_v2.rs @@ -5,9 +5,12 @@ use hyperswitch_domain_models::{ DisputesFlowData, MandateRevokeFlowData, PaymentFlowData, RefundFlowData, WebhookSourceVerifyData, }, - AccessTokenFlowData, FilesFlowData, + AccessTokenFlowData, ExternalAuthenticationFlowData, FilesFlowData, }, router_flow_types::{ + authentication::{ + Authentication, PostAuthentication, PreAuthentication, PreAuthenticationVersionCall, + }, dispute::{Accept, Defend, Evidence}, files::{Retrieve, Upload}, mandate_revoke::MandateRevoke, @@ -22,10 +25,10 @@ use hyperswitch_domain_models::{ AccessTokenAuth, }, router_request_types::{ - AcceptDisputeRequestData, AccessTokenRequestData, AuthorizeSessionTokenData, - CompleteAuthorizeData, ConnectorCustomerData, DefendDisputeRequestData, - MandateRevokeRequestData, PaymentMethodTokenizationData, PaymentsApproveData, - PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, + authentication, AcceptDisputeRequestData, AccessTokenRequestData, + AuthorizeSessionTokenData, CompleteAuthorizeData, ConnectorCustomerData, + DefendDisputeRequestData, MandateRevokeRequestData, PaymentMethodTokenizationData, + PaymentsApproveData, PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsIncrementalAuthorizationData, PaymentsPostProcessingData, PaymentsPostSessionTokensData, PaymentsPreProcessingData, PaymentsRejectData, PaymentsSessionData, PaymentsSyncData, PaymentsTaxCalculationData, RefundsData, @@ -33,9 +36,10 @@ use hyperswitch_domain_models::{ SubmitEvidenceRequestData, UploadFileRequestData, VerifyWebhookSourceRequestData, }, router_response_types::{ - AcceptDisputeResponse, DefendDisputeResponse, MandateRevokeResponseData, - PaymentsResponseData, RefundsResponseData, RetrieveFileResponse, SubmitEvidenceResponse, - TaxCalculationResponseData, UploadFileResponse, VerifyWebhookSourceResponseData, + AcceptDisputeResponse, AuthenticationResponseData, DefendDisputeResponse, + MandateRevokeResponseData, PaymentsResponseData, RefundsResponseData, RetrieveFileResponse, + SubmitEvidenceResponse, TaxCalculationResponseData, UploadFileResponse, + VerifyWebhookSourceResponseData, }, }; #[cfg(feature = "frm")] @@ -61,7 +65,7 @@ use hyperswitch_domain_models::{ #[cfg(feature = "frm")] use hyperswitch_interfaces::api::fraud_check_v2::{ FraudCheckCheckoutV2, FraudCheckFulfillmentV2, FraudCheckRecordReturnV2, FraudCheckSaleV2, - FraudCheckTransactionV2, + FraudCheckTransactionV2, FraudCheckV2, }; #[cfg(feature = "payouts")] use hyperswitch_interfaces::api::payouts_v2::{ @@ -70,6 +74,10 @@ use hyperswitch_interfaces::api::payouts_v2::{ }; use hyperswitch_interfaces::{ api::{ + authentication_v2::{ + ConnectorAuthenticationV2, ConnectorPostAuthenticationV2, ConnectorPreAuthenticationV2, + ConnectorPreAuthenticationVersionCallV2, ExternalAuthenticationV2, + }, disputes_v2::{AcceptDisputeV2, DefendDisputeV2, DisputeV2, SubmitEvidenceV2}, files_v2::{FileUploadV2, RetrieveFileV2, UploadFileV2}, payments_v2::{ @@ -2457,3 +2465,174 @@ default_imp_for_new_connector_integration_revoking_mandates!( connectors::Zen, connectors::Zsl ); + +#[cfg(feature = "frm")] +macro_rules! default_imp_for_new_connector_integration_frm { + ($($path:ident::$connector:ident),*) => { + $( + impl FraudCheckV2 for $path::$connector {} + )* + }; +} + +#[cfg(feature = "frm")] +default_imp_for_new_connector_integration_frm!( + connectors::Airwallex, + connectors::Amazonpay, + connectors::Bambora, + connectors::Bamboraapac, + connectors::Billwerk, + connectors::Bitpay, + connectors::Bluesnap, + connectors::Boku, + connectors::Cashtocode, + connectors::Coinbase, + connectors::Cryptopay, + connectors::CtpMastercard, + connectors::Datatrans, + connectors::Deutschebank, + connectors::Digitalvirgo, + connectors::Dlocal, + connectors::Elavon, + connectors::Fiserv, + connectors::Fiservemea, + connectors::Fiuu, + connectors::Forte, + connectors::Globepay, + connectors::Hipay, + connectors::Gocardless, + connectors::Helcim, + connectors::Inespay, + connectors::Jpmorgan, + connectors::Juspaythreedsserver, + connectors::Nomupay, + connectors::Novalnet, + connectors::Nexinets, + connectors::Nexixpay, + connectors::Paybox, + connectors::Payeezy, + connectors::Payu, + connectors::Placetopay, + connectors::Powertranz, + connectors::Prophetpay, + connectors::Mollie, + connectors::Multisafepay, + connectors::Rapyd, + connectors::Razorpay, + connectors::Redsys, + connectors::Shift4, + connectors::Stax, + connectors::Square, + connectors::Taxjar, + connectors::Thunes, + connectors::Tsys, + connectors::UnifiedAuthenticationService, + connectors::Worldline, + connectors::Volt, + connectors::Worldpay, + connectors::Xendit, + connectors::Zen, + connectors::Zsl +); + +macro_rules! default_imp_for_new_connector_integration_connector_authentication { + ($($path:ident::$connector:ident),*) => { + $( impl ExternalAuthenticationV2 for $path::$connector {} + impl ConnectorAuthenticationV2 for $path::$connector {} + impl ConnectorPreAuthenticationV2 for $path::$connector {} + impl ConnectorPreAuthenticationVersionCallV2 for $path::$connector {} + impl ConnectorPostAuthenticationV2 for $path::$connector {} + impl + ConnectorIntegrationV2< + Authentication, + ExternalAuthenticationFlowData, + authentication::ConnectorAuthenticationRequestData, + AuthenticationResponseData, + > for $path::$connector + {} + impl + ConnectorIntegrationV2< + PreAuthentication, + ExternalAuthenticationFlowData, + authentication::PreAuthNRequestData, + AuthenticationResponseData, + > for $path::$connector + {} + impl + ConnectorIntegrationV2< + PreAuthenticationVersionCall, + ExternalAuthenticationFlowData, + authentication::PreAuthNRequestData, + AuthenticationResponseData, + > for $path::$connector + {} + impl + ConnectorIntegrationV2< + PostAuthentication, + ExternalAuthenticationFlowData, + authentication::ConnectorPostAuthenticationRequestData, + AuthenticationResponseData, + > for $path::$connector + {} + )* + }; +} + +default_imp_for_new_connector_integration_connector_authentication!( + connectors::Airwallex, + connectors::Amazonpay, + connectors::Bambora, + connectors::Bamboraapac, + connectors::Billwerk, + connectors::Bitpay, + connectors::Bluesnap, + connectors::Boku, + connectors::Cashtocode, + connectors::Coinbase, + connectors::Cryptopay, + connectors::CtpMastercard, + connectors::Datatrans, + connectors::Deutschebank, + connectors::Digitalvirgo, + connectors::Dlocal, + connectors::Elavon, + connectors::Fiserv, + connectors::Fiservemea, + connectors::Fiuu, + connectors::Forte, + connectors::Globepay, + connectors::Gocardless, + connectors::Helcim, + connectors::Hipay, + connectors::Inespay, + connectors::Jpmorgan, + connectors::Juspaythreedsserver, + connectors::Nomupay, + connectors::Novalnet, + connectors::Nexinets, + connectors::Nexixpay, + connectors::Paybox, + connectors::Payeezy, + connectors::Payu, + connectors::Placetopay, + connectors::Powertranz, + connectors::Prophetpay, + connectors::Mollie, + connectors::Multisafepay, + connectors::Rapyd, + connectors::Razorpay, + connectors::Redsys, + connectors::Shift4, + connectors::Stax, + connectors::Square, + connectors::Taxjar, + connectors::Thunes, + connectors::Tsys, + connectors::UnifiedAuthenticationService, + connectors::Worldline, + connectors::Volt, + connectors::Worldpay, + connectors::Xendit, + connectors::Zen, + connectors::Zsl +); diff --git a/crates/hyperswitch_domain_models/Cargo.toml b/crates/hyperswitch_domain_models/Cargo.toml index 3de5358a2b..c27d1f6bf9 100644 --- a/crates/hyperswitch_domain_models/Cargo.toml +++ b/crates/hyperswitch_domain_models/Cargo.toml @@ -17,6 +17,7 @@ v2 = ["api_models/v2", "diesel_models/v2", "common_utils/v2"] v1 = ["api_models/v1", "diesel_models/v1", "common_utils/v1"] customer_v2 = ["api_models/customer_v2", "diesel_models/customer_v2"] payment_methods_v2 = ["api_models/payment_methods_v2", "diesel_models/payment_methods_v2"] +dummy_connector = [] revenue_recovery= [] [dependencies] diff --git a/crates/hyperswitch_domain_models/src/configs.rs b/crates/hyperswitch_domain_models/src/configs.rs new file mode 100644 index 0000000000..811db2cbec --- /dev/null +++ b/crates/hyperswitch_domain_models/src/configs.rs @@ -0,0 +1,208 @@ +//! Configs interface +use common_enums::ApplicationError; +use masking::Secret; +use router_derive; +use serde::Deserialize; +// struct Connectors +#[allow(missing_docs, missing_debug_implementations)] +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct Connectors { + pub aci: ConnectorParams, + pub adyen: AdyenParamsWithThreeBaseUrls, + pub adyenplatform: ConnectorParams, + pub airwallex: ConnectorParams, + pub amazonpay: ConnectorParams, + pub applepay: ConnectorParams, + pub authorizedotnet: ConnectorParams, + pub bambora: ConnectorParams, + pub bamboraapac: ConnectorParams, + pub bankofamerica: ConnectorParams, + pub billwerk: ConnectorParams, + pub bitpay: ConnectorParams, + pub bluesnap: ConnectorParamsWithSecondaryBaseUrl, + pub boku: ConnectorParams, + pub braintree: ConnectorParams, + pub cashtocode: ConnectorParams, + pub chargebee: ConnectorParams, + pub checkout: ConnectorParams, + pub coinbase: ConnectorParams, + pub coingate: ConnectorParams, + pub cryptopay: ConnectorParams, + pub ctp_mastercard: NoParams, + pub cybersource: ConnectorParams, + pub datatrans: ConnectorParamsWithSecondaryBaseUrl, + pub deutschebank: ConnectorParams, + pub digitalvirgo: ConnectorParams, + pub dlocal: ConnectorParams, + #[cfg(feature = "dummy_connector")] + pub dummyconnector: ConnectorParams, + pub ebanx: ConnectorParams, + pub elavon: ConnectorParams, + pub fiserv: ConnectorParams, + pub fiservemea: ConnectorParams, + pub fiuu: ConnectorParamsWithThreeUrls, + pub forte: ConnectorParams, + pub getnet: ConnectorParams, + pub globalpay: ConnectorParams, + pub globepay: ConnectorParams, + pub gocardless: ConnectorParams, + pub gpayments: ConnectorParams, + pub helcim: ConnectorParams, + pub hipay: ConnectorParamsWithThreeUrls, + pub iatapay: ConnectorParams, + pub inespay: ConnectorParams, + pub itaubank: ConnectorParams, + pub jpmorgan: ConnectorParams, + pub juspaythreedsserver: ConnectorParams, + pub klarna: ConnectorParams, + pub mifinity: ConnectorParams, + pub mollie: ConnectorParams, + pub moneris: ConnectorParams, + pub multisafepay: ConnectorParams, + pub netcetera: ConnectorParams, + pub nexinets: ConnectorParams, + pub nexixpay: ConnectorParams, + pub nmi: ConnectorParams, + pub nomupay: ConnectorParams, + pub noon: ConnectorParamsWithModeType, + pub novalnet: ConnectorParams, + pub nuvei: ConnectorParams, + pub opayo: ConnectorParams, + pub opennode: ConnectorParams, + pub paybox: ConnectorParamsWithSecondaryBaseUrl, + pub payeezy: ConnectorParams, + pub payme: ConnectorParams, + pub payone: ConnectorParams, + pub paypal: ConnectorParams, + pub paystack: ConnectorParams, + pub payu: ConnectorParams, + pub placetopay: ConnectorParams, + pub plaid: ConnectorParams, + pub powertranz: ConnectorParams, + pub prophetpay: ConnectorParams, + pub rapyd: ConnectorParams, + pub razorpay: ConnectorParamsWithKeys, + pub recurly: ConnectorParams, + pub redsys: ConnectorParams, + pub riskified: ConnectorParams, + pub shift4: ConnectorParams, + pub signifyd: ConnectorParams, + pub square: ConnectorParams, + pub stax: ConnectorParams, + pub stripe: ConnectorParamsWithFileUploadUrl, + pub stripebilling: ConnectorParams, + pub taxjar: ConnectorParams, + pub threedsecureio: ConnectorParams, + pub thunes: ConnectorParams, + pub trustpay: ConnectorParamsWithMoreUrls, + pub tsys: ConnectorParams, + pub unified_authentication_service: ConnectorParams, + pub volt: ConnectorParams, + pub wellsfargo: ConnectorParams, + pub wellsfargopayout: ConnectorParams, + pub wise: ConnectorParams, + pub worldline: ConnectorParams, + pub worldpay: ConnectorParams, + pub xendit: ConnectorParams, + pub zen: ConnectorParams, + pub zsl: ConnectorParams, +} + +/// struct ConnectorParams +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct ConnectorParams { + /// base url + pub base_url: String, + /// secondary base url + pub secondary_base_url: Option, +} + +///struct No Param for connectors with no params +#[derive(Debug, Deserialize, Clone, Default)] +pub struct NoParams; + +impl NoParams { + /// function to satisfy connector param validation macro + pub fn validate(&self, _parent_field: &str) -> Result<(), ApplicationError> { + Ok(()) + } +} + +/// struct ConnectorParamsWithKeys +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct ConnectorParamsWithKeys { + /// base url + pub base_url: String, + /// api key + pub api_key: Secret, + /// merchant ID + pub merchant_id: Secret, +} + +/// struct ConnectorParamsWithModeType +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct ConnectorParamsWithModeType { + /// base url + pub base_url: String, + /// secondary base url + pub secondary_base_url: Option, + /// Can take values like Test or Live for Noon + pub key_mode: String, +} + +/// struct ConnectorParamsWithMoreUrls +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct ConnectorParamsWithMoreUrls { + /// base url + pub base_url: String, + /// base url for bank redirects + pub base_url_bank_redirects: String, +} + +/// struct ConnectorParamsWithFileUploadUrl +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct ConnectorParamsWithFileUploadUrl { + /// base url + pub base_url: String, + /// base url for file upload + pub base_url_file_upload: String, +} + +/// struct ConnectorParamsWithThreeBaseUrls +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct AdyenParamsWithThreeBaseUrls { + /// base url + pub base_url: String, + /// secondary base url + #[cfg(feature = "payouts")] + pub payout_base_url: String, + /// third base url + pub dispute_base_url: String, +} +/// struct ConnectorParamsWithSecondaryBaseUrl +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct ConnectorParamsWithSecondaryBaseUrl { + /// base url + pub base_url: String, + /// secondary base url + pub secondary_base_url: String, +} +/// struct ConnectorParamsWithThreeUrls +#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] +#[serde(default)] +pub struct ConnectorParamsWithThreeUrls { + /// base url + pub base_url: String, + /// secondary base url + pub secondary_base_url: String, + /// third base url + pub third_base_url: String, +} diff --git a/crates/hyperswitch_domain_models/src/consts.rs b/crates/hyperswitch_domain_models/src/consts.rs index b1d9229951..c2ebd839fe 100644 --- a/crates/hyperswitch_domain_models/src/consts.rs +++ b/crates/hyperswitch_domain_models/src/consts.rs @@ -1,10 +1,34 @@ //! Constants that are used in the domain models. +use std::collections::HashSet; + +use router_env::once_cell::sync::Lazy; + #[cfg(feature = "v1")] pub const API_VERSION: common_enums::ApiVersion = common_enums::ApiVersion::V1; #[cfg(feature = "v2")] pub const API_VERSION: common_enums::ApiVersion = common_enums::ApiVersion::V2; +pub static ROUTING_ENABLED_PAYMENT_METHODS: Lazy> = + Lazy::new(|| { + let mut set = HashSet::new(); + set.insert(common_enums::PaymentMethod::BankTransfer); + set.insert(common_enums::PaymentMethod::BankDebit); + set.insert(common_enums::PaymentMethod::BankRedirect); + set + }); + +pub static ROUTING_ENABLED_PAYMENT_METHOD_TYPES: Lazy> = + Lazy::new(|| { + let mut set = HashSet::new(); + set.insert(common_enums::PaymentMethodType::GooglePay); + set.insert(common_enums::PaymentMethodType::ApplePay); + set.insert(common_enums::PaymentMethodType::Klarna); + set.insert(common_enums::PaymentMethodType::Paypal); + set.insert(common_enums::PaymentMethodType::SamsungPay); + set + }); + /// Length of the unique reference ID generated for connector mandate requests pub const CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH: usize = 18; diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index 12a128269f..030e660ce7 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -5,6 +5,7 @@ pub mod bulk_tokenization; pub mod business_profile; pub mod callback_mapper; pub mod card_testing_guard_data; +pub mod configs; pub mod consts; pub mod customer; pub mod disputes; @@ -29,6 +30,7 @@ pub mod router_data_v2; pub mod router_flow_types; pub mod router_request_types; pub mod router_response_types; +pub mod routing; pub mod type_encryption; pub mod types; #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 464009a84a..1c4946a496 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -726,10 +726,35 @@ common_utils::create_list_wrapper!( MerchantConnectorAccounts, MerchantConnectorAccount, impl_functions: { + fn filter_and_map<'a, T>( + &'a self, + filter: impl Fn(&'a MerchantConnectorAccount) -> bool, + func: impl Fn(&'a MerchantConnectorAccount) -> T, + ) -> rustc_hash::FxHashSet + where + T: std::hash::Hash + Eq, + { + self.0 + .iter() + .filter(|mca| filter(mca)) + .map(func) + .collect::>() + } + + pub fn filter_by_profile<'a, T>( + &'a self, + profile_id: &'a id_type::ProfileId, + func: impl Fn(&'a MerchantConnectorAccount) -> T, + ) -> rustc_hash::FxHashSet + where + T: std::hash::Hash + Eq, + { + self.filter_and_map(|mca| mca.profile_id == *profile_id, func) + } #[cfg(feature = "v2")] pub fn get_connector_and_supporting_payment_method_type_for_session_call( &self, - ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType)> { + ) -> Vec<(&MerchantConnectorAccount, common_enums::PaymentMethodType, common_enums::PaymentMethod)> { // This vector is created to work around lifetimes let ref_vector = Vec::default(); @@ -738,12 +763,12 @@ common_utils::create_list_wrapper!( .payment_methods_enabled.as_ref() .unwrap_or(&Vec::default()) .iter() - .flat_map(|payment_method_types| payment_method_types.payment_method_subtypes.as_ref().unwrap_or(&ref_vector)) - .filter(|payment_method_types_enabled| { + .flat_map(|payment_method_types| payment_method_types.payment_method_subtypes.as_ref().unwrap_or(&ref_vector).iter().map(|payment_method_subtype| (payment_method_subtype, payment_method_types.payment_method_type)).collect::>()) + .filter(|(payment_method_types_enabled, _)| { payment_method_types_enabled.payment_experience == Some(api_models::enums::PaymentExperience::InvokeSdkClient) }) - .map(|payment_method_types| { - (connector_account, payment_method_types.payment_method_subtype) + .map(|(payment_method_subtypes, payment_method_type)| { + (connector_account, payment_method_subtypes.payment_method_subtype, payment_method_type) }) .collect::>() }).collect(); diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index c8e9e5444e..dbbee0efa0 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -7,12 +7,13 @@ use common_types::primitive_wrappers::{ AlwaysRequestExtendedAuthorization, RequestExtendedAuthorizationBool, }; #[cfg(feature = "v2")] -use common_utils::ext_traits::{OptionExt, ValueExt}; +use common_utils::ext_traits::OptionExt; use common_utils::{ self, crypto::Encryptable, encryption::Encryption, errors::CustomResult, + ext_traits::ValueExt, id_type, pii, types::{keymanager::ToEncryptable, MinorUnit}, }; @@ -41,7 +42,7 @@ use self::payment_attempt::PaymentAttempt; #[cfg(feature = "v2")] use crate::{ address::Address, business_profile, customer, errors, merchant_account, - merchant_connector_account, payment_address, payment_method_data, + merchant_connector_account, payment_address, payment_method_data, routing, ApiModelToDieselModelConvertor, }; #[cfg(feature = "v1")] @@ -209,6 +210,19 @@ impl PaymentIntent { .change_context(errors::api_error_response::ApiErrorResponse::InternalServerError) .attach_printable("Error creating finish redirection url") } + + pub fn parse_and_get_metadata( + &self, + type_name: &'static str, + ) -> CustomResult, common_utils::errors::ParsingError> + where + T: for<'de> masking::Deserialize<'de>, + { + self.metadata + .clone() + .map(|metadata| metadata.parse_value(type_name)) + .transpose() + } } #[cfg(feature = "v2")] @@ -439,7 +453,7 @@ pub struct PaymentIntent { /// Authentication type that is requested by the merchant for this payment. pub authentication_type: Option, /// This contains the pre routing results that are done when routing is done during listing the payment methods. - pub prerouting_algorithm: Option, + pub prerouting_algorithm: Option, /// The organization id for the payment. This is derived from the merchant account pub organization_id: id_type::OrganizationId, /// Denotes the request by the merchant whether to enable a payment link for this payment. diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 54f6799c47..1345264944 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1,4 +1,8 @@ #[cfg(feature = "v2")] +use common_enums::RequestIncrementalAuthorization; +#[cfg(feature = "v2")] +use common_utils::errors::ParsingError; +#[cfg(feature = "v2")] use common_utils::ext_traits::{Encode, ValueExt}; use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, @@ -13,6 +17,8 @@ use common_utils::{ MinorUnit, }, }; +#[cfg(feature = "v2")] +use diesel_models::PaymentLinkConfigRequestForPayments; use diesel_models::{ PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, }; @@ -28,12 +34,16 @@ use super::payment_attempt::PaymentAttempt; use super::PaymentIntent; #[cfg(feature = "v2")] use crate::address::Address; +#[cfg(feature = "v2")] +use crate::routing; use crate::{ behaviour, errors, merchant_key_store::MerchantKeyStore, type_encryption::{crypto_operation, CryptoOperation}, RemoteStorageObject, }; +#[cfg(feature = "v2")] +use crate::{FeatureMetadata, OrderDetailsWithAmount}; #[async_trait::async_trait] pub trait PaymentIntentInterface { @@ -340,6 +350,11 @@ pub enum PaymentIntentUpdate { amount_captured: Option, updated_by: String, }, + /// Update the payment intent details on payment sdk session call, before calling the connector. + SessionIntentUpdate { + prerouting_algorithm: routing::PaymentRoutingInfo, + updated_by: String, + }, RecordUpdate { status: common_enums::IntentStatus, feature_metadata: Box>, @@ -396,16 +411,18 @@ pub struct PaymentIntentUpdateInternal { // This conversion is used in the `update_payment_intent` function #[cfg(feature = "v2")] -impl From for diesel_models::PaymentIntentUpdateInternal { - fn from(payment_intent_update: PaymentIntentUpdate) -> Self { +impl TryFrom for diesel_models::PaymentIntentUpdateInternal { + type Error = error_stack::Report; + fn try_from(payment_intent_update: PaymentIntentUpdate) -> Result { match payment_intent_update { PaymentIntentUpdate::ConfirmIntent { status, active_attempt_id, updated_by, - } => Self { + } => Ok(Self { status: Some(status), active_attempt_id: Some(active_attempt_id), + prerouting_algorithm: None, modified_at: common_utils::date_time::now(), amount: None, amount_captured: None, @@ -438,16 +455,17 @@ impl From for diesel_models::PaymentIntentUpdateInternal { frm_metadata: None, request_external_three_ds_authentication: None, updated_by, - }, + }), PaymentIntentUpdate::ConfirmIntentPostUpdate { status, updated_by, amount_captured, feature_metadata, - } => Self { + } => Ok(Self { status: Some(status), active_attempt_id: None, + prerouting_algorithm: None, modified_at: common_utils::date_time::now(), amount_captured, amount: None, @@ -480,14 +498,15 @@ impl From for diesel_models::PaymentIntentUpdateInternal { frm_metadata: None, request_external_three_ds_authentication: None, updated_by, - }, + }), PaymentIntentUpdate::SyncUpdate { status, amount_captured, updated_by, - } => Self { + } => Ok(Self { status: Some(status), active_attempt_id: None, + prerouting_algorithm: None, modified_at: common_utils::date_time::now(), amount: None, currency: None, @@ -520,15 +539,16 @@ impl From for diesel_models::PaymentIntentUpdateInternal { frm_metadata: None, request_external_three_ds_authentication: None, updated_by, - }, + }), PaymentIntentUpdate::CaptureUpdate { status, amount_captured, updated_by, - } => Self { + } => Ok(Self { status: Some(status), amount_captured, active_attempt_id: None, + prerouting_algorithm: None, modified_at: common_utils::date_time::now(), amount: None, currency: None, @@ -560,7 +580,51 @@ impl From for diesel_models::PaymentIntentUpdateInternal { frm_metadata: None, request_external_three_ds_authentication: None, updated_by, - }, + }), + PaymentIntentUpdate::SessionIntentUpdate { + prerouting_algorithm, + updated_by, + } => Ok(Self { + status: None, + active_attempt_id: None, + modified_at: common_utils::date_time::now(), + amount_captured: None, + prerouting_algorithm: Some( + prerouting_algorithm + .encode_to_value() + .attach_printable("Failed to Serialize prerouting_algorithm")?, + ), + amount: None, + currency: None, + shipping_cost: None, + tax_details: None, + skip_external_tax_calculation: None, + surcharge_applicable: None, + surcharge_amount: None, + tax_on_surcharge: None, + routing_algorithm_id: None, + capture_method: None, + authentication_type: None, + billing_address: None, + shipping_address: None, + customer_present: None, + description: None, + return_url: None, + setup_future_usage: None, + apply_mit_exemption: None, + statement_descriptor: None, + order_details: None, + allowed_payment_method_types: None, + metadata: None, + connector_metadata: None, + feature_metadata: None, + payment_link_config: None, + request_incremental_authorization: None, + session_expiry: None, + frm_metadata: None, + request_external_three_ds_authentication: None, + updated_by, + }), PaymentIntentUpdate::UpdateIntent(boxed_intent) => { let PaymentIntentUpdateFields { amount, @@ -595,9 +659,10 @@ impl From for diesel_models::PaymentIntentUpdateInternal { active_attempt_id, updated_by, } = *boxed_intent; - Self { + Ok(Self { status: None, active_attempt_id, + prerouting_algorithm: None, modified_at: common_utils::date_time::now(), amount_captured: None, amount, @@ -637,14 +702,14 @@ impl From for diesel_models::PaymentIntentUpdateInternal { request_external_three_ds_authentication.map(|val| val.as_bool()), updated_by, - } + }) } PaymentIntentUpdate::RecordUpdate { status, feature_metadata, active_attempt_id, updated_by, - } => Self { + } => Ok(Self { status: Some(status), amount_captured: None, active_attempt_id: Some(Some(active_attempt_id)), @@ -675,11 +740,12 @@ impl From for diesel_models::PaymentIntentUpdateInternal { feature_metadata: *feature_metadata, payment_link_config: None, request_incremental_authorization: None, + prerouting_algorithm: None, session_expiry: None, frm_metadata: None, request_external_three_ds_authentication: None, updated_by, - }, + }), } } } @@ -1584,7 +1650,15 @@ impl behaviour::Conversion for PaymentIntent { capture_method: Some(capture_method), id, authentication_type, - prerouting_algorithm, + prerouting_algorithm: prerouting_algorithm + .map(|prerouting_algorithm| { + prerouting_algorithm.encode_to_value().change_context( + ValidationError::InvalidValue { + message: "Failed to serialize prerouting_algorithm".to_string(), + }, + ) + }) + .transpose()?, merchant_reference_id, surcharge_amount: amount_details.surcharge_amount, tax_on_surcharge: amount_details.tax_on_surcharge, @@ -1719,7 +1793,14 @@ impl behaviour::Conversion for PaymentIntent { merchant_reference_id: storage_model.merchant_reference_id, organization_id: storage_model.organization_id, authentication_type: storage_model.authentication_type, - prerouting_algorithm: storage_model.prerouting_algorithm, + prerouting_algorithm: storage_model + .prerouting_algorithm + .map(|prerouting_algorithm_value| { + prerouting_algorithm_value + .parse_value("PaymentRoutingInfo") + .change_context(common_utils::errors::CryptoError::DecodingFailed) + }) + .transpose()?, enable_payment_link: storage_model.enable_payment_link.into(), apply_mit_exemption: storage_model.apply_mit_exemption.into(), customer_present: storage_model.customer_present.into(), @@ -1791,7 +1872,16 @@ impl behaviour::Conversion for PaymentIntent { id: self.id, merchant_reference_id: self.merchant_reference_id, authentication_type: self.authentication_type, - prerouting_algorithm: self.prerouting_algorithm, + prerouting_algorithm: self + .prerouting_algorithm + .map(|prerouting_algorithm| { + prerouting_algorithm.encode_to_value().change_context( + ValidationError::InvalidValue { + message: "Failed to serialize prerouting_algorithm".to_string(), + }, + ) + }) + .transpose()?, surcharge_amount: amount_details.surcharge_amount, tax_on_surcharge: amount_details.tax_on_surcharge, organization_id: self.organization_id, diff --git a/crates/hyperswitch_domain_models/src/router_flow_types.rs b/crates/hyperswitch_domain_models/src/router_flow_types.rs index da91162c6f..f5ca9f19b9 100644 --- a/crates/hyperswitch_domain_models/src/router_flow_types.rs +++ b/crates/hyperswitch_domain_models/src/router_flow_types.rs @@ -1,4 +1,5 @@ pub mod access_token_auth; +pub mod authentication; pub mod dispute; pub mod files; pub mod fraud_check; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/authentication.rs b/crates/hyperswitch_domain_models/src/router_flow_types/authentication.rs new file mode 100644 index 0000000000..18a1652e8a --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types/authentication.rs @@ -0,0 +1,11 @@ +#[derive(Debug, Clone)] +pub struct PreAuthentication; + +#[derive(Debug, Clone)] +pub struct PreAuthenticationVersionCall; + +#[derive(Debug, Clone)] +pub struct Authentication; + +#[derive(Debug, Clone)] +pub struct PostAuthentication; diff --git a/crates/hyperswitch_domain_models/src/routing.rs b/crates/hyperswitch_domain_models/src/routing.rs new file mode 100644 index 0000000000..4a80225063 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/routing.rs @@ -0,0 +1,67 @@ +use std::collections::HashMap; + +use api_models::{enums as api_enums, routing}; +use serde; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct RoutingData { + pub routed_through: Option, + + pub merchant_connector_id: Option, + + pub routing_info: PaymentRoutingInfo, + pub algorithm: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)] +#[serde(from = "PaymentRoutingInfoSerde", into = "PaymentRoutingInfoSerde")] +pub struct PaymentRoutingInfo { + pub algorithm: Option, + pub pre_routing_results: + Option>, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)] +#[serde(untagged)] +pub enum PreRoutingConnectorChoice { + Single(routing::RoutableConnectorChoice), + Multiple(Vec), +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct PaymentRoutingInfoInner { + pub algorithm: Option, + pub pre_routing_results: + Option>, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum PaymentRoutingInfoSerde { + OnlyAlgorithm(Box), + WithDetails(Box), +} + +impl From for PaymentRoutingInfo { + fn from(value: PaymentRoutingInfoSerde) -> Self { + match value { + PaymentRoutingInfoSerde::OnlyAlgorithm(algo) => Self { + algorithm: Some(*algo), + pre_routing_results: None, + }, + PaymentRoutingInfoSerde::WithDetails(details) => Self { + algorithm: details.algorithm, + pre_routing_results: details.pre_routing_results, + }, + } + } +} + +impl From for PaymentRoutingInfoSerde { + fn from(value: PaymentRoutingInfo) -> Self { + Self::WithDetails(Box::new(PaymentRoutingInfoInner { + algorithm: value.algorithm, + pre_routing_results: value.pre_routing_results, + })) + } +} diff --git a/crates/hyperswitch_interfaces/src/api.rs b/crates/hyperswitch_interfaces/src/api.rs index 6304b10d85..e9cb8e8a12 100644 --- a/crates/hyperswitch_interfaces/src/api.rs +++ b/crates/hyperswitch_interfaces/src/api.rs @@ -1,5 +1,9 @@ //! API interface +/// authentication module +pub mod authentication; +/// authentication_v2 module +pub mod authentication_v2; pub mod disputes; pub mod disputes_v2; pub mod files; @@ -19,6 +23,8 @@ pub mod refunds_v2; pub mod revenue_recovery; pub mod revenue_recovery_v2; +use std::fmt::Debug; + use common_enums::{ enums::{CallConnectorAction, CaptureMethod, EventClass, PaymentAction, PaymentMethodType}, PaymentMethod, @@ -29,6 +35,8 @@ use common_utils::{ }; use error_stack::ResultExt; use hyperswitch_domain_models::{ + configs::Connectors, + errors::api_error_response::ApiErrorResponse, payment_method_data::PaymentMethodData, router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, router_data_v2::{ @@ -55,14 +63,67 @@ use hyperswitch_domain_models::{ use masking::Maskable; use serde_json::json; +#[cfg(feature = "frm")] +pub use self::fraud_check::*; +#[cfg(feature = "frm")] +pub use self::fraud_check_v2::*; #[cfg(feature = "payouts")] pub use self::payouts::*; +#[cfg(feature = "payouts")] +pub use self::payouts_v2::*; pub use self::{payments::*, refunds::*}; use crate::{ - configs::Connectors, connector_integration_v2::ConnectorIntegrationV2, consts, errors, - events::connector_api_logs::ConnectorEvent, metrics, types, + api::revenue_recovery::RevenueRecovery, connector_integration_v2::ConnectorIntegrationV2, + consts, errors, events::connector_api_logs::ConnectorEvent, metrics, types, webhooks, }; +/// Connector trait +pub trait Connector: + Send + + Refund + + Payment + + ConnectorRedirectResponse + + webhooks::IncomingWebhook + + ConnectorAccessToken + + disputes::Dispute + + files::FileUpload + + ConnectorTransactionId + + Payouts + + ConnectorVerifyWebhookSource + + FraudCheck + + ConnectorMandateRevoke + + authentication::ExternalAuthentication + + TaxCalculation + + UnifiedAuthenticationService + + RevenueRecovery +{ +} + +impl< + T: Refund + + Payment + + ConnectorRedirectResponse + + Send + + webhooks::IncomingWebhook + + ConnectorAccessToken + + disputes::Dispute + + files::FileUpload + + ConnectorTransactionId + + Payouts + + ConnectorVerifyWebhookSource + + FraudCheck + + ConnectorMandateRevoke + + authentication::ExternalAuthentication + + TaxCalculation + + UnifiedAuthenticationService + + RevenueRecovery, + > Connector for T +{ +} + +/// Alias for Box<&'static (dyn Connector + Sync)> +pub type BoxedConnector = Box<&'static (dyn Connector + Sync)>; + /// type BoxedConnectorIntegration pub type BoxedConnectorIntegration<'a, T, Req, Resp> = Box<&'a (dyn ConnectorIntegration + Send + Sync)>; @@ -582,6 +643,16 @@ pub trait ConnectorRedirectResponse { /// Empty trait for when payouts feature is disabled #[cfg(not(feature = "payouts"))] pub trait Payouts {} +/// Empty trait for when payouts feature is disabled +#[cfg(not(feature = "payouts"))] +pub trait PayoutsV2 {} + +/// Empty trait for when frm feature is disabled +#[cfg(not(feature = "frm"))] +pub trait FraudCheck {} +/// Empty trait for when frm feature is disabled +#[cfg(not(feature = "frm"))] +pub trait FraudCheckV2 {} fn get_connector_payment_method_type_info( supported_payment_method: &SupportedPaymentMethods, @@ -609,3 +680,16 @@ fn get_connector_payment_method_type_info( }) .transpose() } + +/// ConnectorTransactionId trait +pub trait ConnectorTransactionId: ConnectorCommon + Sync { + /// fn connector_transaction_id + fn connector_transaction_id( + &self, + payment_attempt: hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, + ) -> Result, ApiErrorResponse> { + Ok(payment_attempt + .get_connector_payment_id() + .map(ToString::to_string)) + } +} diff --git a/crates/hyperswitch_interfaces/src/api/authentication.rs b/crates/hyperswitch_interfaces/src/api/authentication.rs new file mode 100644 index 0000000000..5cb5a5e567 --- /dev/null +++ b/crates/hyperswitch_interfaces/src/api/authentication.rs @@ -0,0 +1,50 @@ +use hyperswitch_domain_models::{ + router_flow_types::authentication::{ + Authentication, PostAuthentication, PreAuthentication, PreAuthenticationVersionCall, + }, + router_request_types::authentication::{ + ConnectorAuthenticationRequestData, ConnectorPostAuthenticationRequestData, + PreAuthNRequestData, + }, + router_response_types::AuthenticationResponseData, +}; + +use crate::api::ConnectorIntegration; + +/// trait ConnectorAuthentication +pub trait ConnectorAuthentication: + ConnectorIntegration +{ +} + +/// trait ConnectorPreAuthentication +pub trait ConnectorPreAuthentication: + ConnectorIntegration +{ +} + +/// trait ConnectorPreAuthenticationVersionCall +pub trait ConnectorPreAuthenticationVersionCall: + ConnectorIntegration +{ +} + +/// trait ConnectorPostAuthentication +pub trait ConnectorPostAuthentication: + ConnectorIntegration< + PostAuthentication, + ConnectorPostAuthenticationRequestData, + AuthenticationResponseData, +> +{ +} + +/// trait ExternalAuthentication +pub trait ExternalAuthentication: + super::ConnectorCommon + + ConnectorAuthentication + + ConnectorPreAuthentication + + ConnectorPreAuthenticationVersionCall + + ConnectorPostAuthentication +{ +} diff --git a/crates/hyperswitch_interfaces/src/api/authentication_v2.rs b/crates/hyperswitch_interfaces/src/api/authentication_v2.rs new file mode 100644 index 0000000000..74988646f2 --- /dev/null +++ b/crates/hyperswitch_interfaces/src/api/authentication_v2.rs @@ -0,0 +1,67 @@ +use hyperswitch_domain_models::{ + router_data_v2::ExternalAuthenticationFlowData, + router_flow_types::authentication::{ + Authentication, PostAuthentication, PreAuthentication, PreAuthenticationVersionCall, + }, + router_request_types::authentication::{ + ConnectorAuthenticationRequestData, ConnectorPostAuthenticationRequestData, + PreAuthNRequestData, + }, + router_response_types::AuthenticationResponseData, +}; + +use crate::api::ConnectorIntegrationV2; + +/// trait ConnectorAuthenticationV2 +pub trait ConnectorAuthenticationV2: + ConnectorIntegrationV2< + Authentication, + ExternalAuthenticationFlowData, + ConnectorAuthenticationRequestData, + AuthenticationResponseData, +> +{ +} + +/// trait ConnectorPreAuthenticationV2 +pub trait ConnectorPreAuthenticationV2: + ConnectorIntegrationV2< + PreAuthentication, + ExternalAuthenticationFlowData, + PreAuthNRequestData, + AuthenticationResponseData, +> +{ +} + +/// trait ConnectorPreAuthenticationVersionCallV2 +pub trait ConnectorPreAuthenticationVersionCallV2: + ConnectorIntegrationV2< + PreAuthenticationVersionCall, + ExternalAuthenticationFlowData, + PreAuthNRequestData, + AuthenticationResponseData, +> +{ +} + +/// trait ConnectorPostAuthenticationV2 +pub trait ConnectorPostAuthenticationV2: + ConnectorIntegrationV2< + PostAuthentication, + ExternalAuthenticationFlowData, + ConnectorPostAuthenticationRequestData, + AuthenticationResponseData, +> +{ +} + +/// trait ExternalAuthenticationV2 +pub trait ExternalAuthenticationV2: + super::ConnectorCommon + + ConnectorAuthenticationV2 + + ConnectorPreAuthenticationV2 + + ConnectorPreAuthenticationVersionCallV2 + + ConnectorPostAuthenticationV2 +{ +} diff --git a/crates/hyperswitch_interfaces/src/api/fraud_check.rs b/crates/hyperswitch_interfaces/src/api/fraud_check.rs index 5bee65c8d9..d95a05b108 100644 --- a/crates/hyperswitch_interfaces/src/api/fraud_check.rs +++ b/crates/hyperswitch_interfaces/src/api/fraud_check.rs @@ -39,3 +39,14 @@ pub trait FraudCheckRecordReturn: ConnectorIntegration { } + +/// trait FraudCheck +pub trait FraudCheck: + super::ConnectorCommon + + FraudCheckSale + + FraudCheckTransaction + + FraudCheckCheckout + + FraudCheckFulfillment + + FraudCheckRecordReturn +{ +} diff --git a/crates/hyperswitch_interfaces/src/api/fraud_check_v2.rs b/crates/hyperswitch_interfaces/src/api/fraud_check_v2.rs index f549290950..1baf725212 100644 --- a/crates/hyperswitch_interfaces/src/api/fraud_check_v2.rs +++ b/crates/hyperswitch_interfaces/src/api/fraud_check_v2.rs @@ -45,3 +45,14 @@ pub trait FraudCheckRecordReturnV2: > { } + +/// trait FraudCheckV2 +pub trait FraudCheckV2: + super::ConnectorCommon + + FraudCheckSaleV2 + + FraudCheckTransactionV2 + + FraudCheckCheckoutV2 + + FraudCheckFulfillmentV2 + + FraudCheckRecordReturnV2 +{ +} diff --git a/crates/hyperswitch_interfaces/src/configs.rs b/crates/hyperswitch_interfaces/src/configs.rs index 811db2cbec..c01a5a5ebb 100644 --- a/crates/hyperswitch_interfaces/src/configs.rs +++ b/crates/hyperswitch_interfaces/src/configs.rs @@ -1,208 +1 @@ -//! Configs interface -use common_enums::ApplicationError; -use masking::Secret; -use router_derive; -use serde::Deserialize; -// struct Connectors -#[allow(missing_docs, missing_debug_implementations)] -#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] -#[serde(default)] -pub struct Connectors { - pub aci: ConnectorParams, - pub adyen: AdyenParamsWithThreeBaseUrls, - pub adyenplatform: ConnectorParams, - pub airwallex: ConnectorParams, - pub amazonpay: ConnectorParams, - pub applepay: ConnectorParams, - pub authorizedotnet: ConnectorParams, - pub bambora: ConnectorParams, - pub bamboraapac: ConnectorParams, - pub bankofamerica: ConnectorParams, - pub billwerk: ConnectorParams, - pub bitpay: ConnectorParams, - pub bluesnap: ConnectorParamsWithSecondaryBaseUrl, - pub boku: ConnectorParams, - pub braintree: ConnectorParams, - pub cashtocode: ConnectorParams, - pub chargebee: ConnectorParams, - pub checkout: ConnectorParams, - pub coinbase: ConnectorParams, - pub coingate: ConnectorParams, - pub cryptopay: ConnectorParams, - pub ctp_mastercard: NoParams, - pub cybersource: ConnectorParams, - pub datatrans: ConnectorParamsWithSecondaryBaseUrl, - pub deutschebank: ConnectorParams, - pub digitalvirgo: ConnectorParams, - pub dlocal: ConnectorParams, - #[cfg(feature = "dummy_connector")] - pub dummyconnector: ConnectorParams, - pub ebanx: ConnectorParams, - pub elavon: ConnectorParams, - pub fiserv: ConnectorParams, - pub fiservemea: ConnectorParams, - pub fiuu: ConnectorParamsWithThreeUrls, - pub forte: ConnectorParams, - pub getnet: ConnectorParams, - pub globalpay: ConnectorParams, - pub globepay: ConnectorParams, - pub gocardless: ConnectorParams, - pub gpayments: ConnectorParams, - pub helcim: ConnectorParams, - pub hipay: ConnectorParamsWithThreeUrls, - pub iatapay: ConnectorParams, - pub inespay: ConnectorParams, - pub itaubank: ConnectorParams, - pub jpmorgan: ConnectorParams, - pub juspaythreedsserver: ConnectorParams, - pub klarna: ConnectorParams, - pub mifinity: ConnectorParams, - pub mollie: ConnectorParams, - pub moneris: ConnectorParams, - pub multisafepay: ConnectorParams, - pub netcetera: ConnectorParams, - pub nexinets: ConnectorParams, - pub nexixpay: ConnectorParams, - pub nmi: ConnectorParams, - pub nomupay: ConnectorParams, - pub noon: ConnectorParamsWithModeType, - pub novalnet: ConnectorParams, - pub nuvei: ConnectorParams, - pub opayo: ConnectorParams, - pub opennode: ConnectorParams, - pub paybox: ConnectorParamsWithSecondaryBaseUrl, - pub payeezy: ConnectorParams, - pub payme: ConnectorParams, - pub payone: ConnectorParams, - pub paypal: ConnectorParams, - pub paystack: ConnectorParams, - pub payu: ConnectorParams, - pub placetopay: ConnectorParams, - pub plaid: ConnectorParams, - pub powertranz: ConnectorParams, - pub prophetpay: ConnectorParams, - pub rapyd: ConnectorParams, - pub razorpay: ConnectorParamsWithKeys, - pub recurly: ConnectorParams, - pub redsys: ConnectorParams, - pub riskified: ConnectorParams, - pub shift4: ConnectorParams, - pub signifyd: ConnectorParams, - pub square: ConnectorParams, - pub stax: ConnectorParams, - pub stripe: ConnectorParamsWithFileUploadUrl, - pub stripebilling: ConnectorParams, - pub taxjar: ConnectorParams, - pub threedsecureio: ConnectorParams, - pub thunes: ConnectorParams, - pub trustpay: ConnectorParamsWithMoreUrls, - pub tsys: ConnectorParams, - pub unified_authentication_service: ConnectorParams, - pub volt: ConnectorParams, - pub wellsfargo: ConnectorParams, - pub wellsfargopayout: ConnectorParams, - pub wise: ConnectorParams, - pub worldline: ConnectorParams, - pub worldpay: ConnectorParams, - pub xendit: ConnectorParams, - pub zen: ConnectorParams, - pub zsl: ConnectorParams, -} - -/// struct ConnectorParams -#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] -#[serde(default)] -pub struct ConnectorParams { - /// base url - pub base_url: String, - /// secondary base url - pub secondary_base_url: Option, -} - -///struct No Param for connectors with no params -#[derive(Debug, Deserialize, Clone, Default)] -pub struct NoParams; - -impl NoParams { - /// function to satisfy connector param validation macro - pub fn validate(&self, _parent_field: &str) -> Result<(), ApplicationError> { - Ok(()) - } -} - -/// struct ConnectorParamsWithKeys -#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] -#[serde(default)] -pub struct ConnectorParamsWithKeys { - /// base url - pub base_url: String, - /// api key - pub api_key: Secret, - /// merchant ID - pub merchant_id: Secret, -} - -/// struct ConnectorParamsWithModeType -#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] -#[serde(default)] -pub struct ConnectorParamsWithModeType { - /// base url - pub base_url: String, - /// secondary base url - pub secondary_base_url: Option, - /// Can take values like Test or Live for Noon - pub key_mode: String, -} - -/// struct ConnectorParamsWithMoreUrls -#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] -#[serde(default)] -pub struct ConnectorParamsWithMoreUrls { - /// base url - pub base_url: String, - /// base url for bank redirects - pub base_url_bank_redirects: String, -} - -/// struct ConnectorParamsWithFileUploadUrl -#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] -#[serde(default)] -pub struct ConnectorParamsWithFileUploadUrl { - /// base url - pub base_url: String, - /// base url for file upload - pub base_url_file_upload: String, -} - -/// struct ConnectorParamsWithThreeBaseUrls -#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] -#[serde(default)] -pub struct AdyenParamsWithThreeBaseUrls { - /// base url - pub base_url: String, - /// secondary base url - #[cfg(feature = "payouts")] - pub payout_base_url: String, - /// third base url - pub dispute_base_url: String, -} -/// struct ConnectorParamsWithSecondaryBaseUrl -#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] -#[serde(default)] -pub struct ConnectorParamsWithSecondaryBaseUrl { - /// base url - pub base_url: String, - /// secondary base url - pub secondary_base_url: String, -} -/// struct ConnectorParamsWithThreeUrls -#[derive(Debug, Deserialize, Clone, Default, router_derive::ConfigValidate)] -#[serde(default)] -pub struct ConnectorParamsWithThreeUrls { - /// base url - pub base_url: String, - /// secondary base url - pub secondary_base_url: String, - /// third base url - pub third_base_url: String, -} +pub use hyperswitch_domain_models::configs::Connectors; diff --git a/crates/hyperswitch_interfaces/src/connector_integration_interface.rs b/crates/hyperswitch_interfaces/src/connector_integration_interface.rs new file mode 100644 index 0000000000..b49869519e --- /dev/null +++ b/crates/hyperswitch_interfaces/src/connector_integration_interface.rs @@ -0,0 +1,737 @@ +use api_models::webhooks::{IncomingWebhookEvent, ObjectReferenceId}; +use common_enums::PaymentAction; +use common_utils::{crypto, errors::CustomResult, request::Request}; +use hyperswitch_domain_models::{ + api::ApplicationResponse, + configs::Connectors, + errors::api_error_response::ApiErrorResponse, + payment_method_data::PaymentMethodData, + router_data::{ConnectorAuthType, ErrorResponse, RouterData}, + router_data_v2::RouterDataV2, + router_response_types::{ConnectorInfo, SupportedPaymentMethods}, +}; + +use crate::{ + api, + api::{ + BoxedConnectorIntegration, CaptureSyncMethod, Connector, ConnectorCommon, + ConnectorIntegration, ConnectorRedirectResponse, ConnectorSpecifications, + ConnectorValidation, CurrencyUnit, + }, + authentication::ExternalAuthenticationPayload, + connector_integration_v2::{BoxedConnectorIntegrationV2, ConnectorIntegrationV2, ConnectorV2}, + disputes, errors, + events::connector_api_logs::ConnectorEvent, + types, + webhooks::{IncomingWebhook, IncomingWebhookFlowError, IncomingWebhookRequestDetails}, +}; + +/// RouterDataConversion trait +/// +/// This trait must be implemented for conversion between Router data and RouterDataV2 +pub trait RouterDataConversion { + /// Convert RouterData to RouterDataV2 + /// + /// # Arguments + /// + /// * `old_router_data` - A reference to the old RouterData + /// + /// # Returns + /// + /// A `CustomResult` containing the new RouterDataV2 or a ConnectorError + fn from_old_router_data( + old_router_data: &RouterData, + ) -> CustomResult, errors::ConnectorError> + where + Self: Sized; + /// Convert RouterDataV2 back to RouterData + /// + /// # Arguments + /// + /// * `new_router_data` - The new RouterDataV2 + /// + /// # Returns + /// + /// A `CustomResult` containing the old RouterData or a ConnectorError + fn to_old_router_data( + new_router_data: RouterDataV2, + ) -> CustomResult, errors::ConnectorError> + where + Self: Sized; +} +/// Alias for Box<&'static (dyn Connector + Sync)> +pub type BoxedConnector = Box<&'static (dyn Connector + Sync)>; +/// Alias for Box<&'static (dyn ConnectorV2 + Sync)> +pub type BoxedConnectorV2 = Box<&'static (dyn ConnectorV2 + Sync)>; + +/// Enum representing the Connector +#[derive(Clone)] +pub enum ConnectorEnum { + /// Old connector type + Old(BoxedConnector), + /// New connector type + New(BoxedConnectorV2), +} + +impl std::fmt::Debug for ConnectorEnum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Old(_) => f + .debug_tuple("Old") + .field(&std::any::type_name::().to_string()) + .finish(), + Self::New(_) => f + .debug_tuple("New") + .field(&std::any::type_name::().to_string()) + .finish(), + } + } +} + +#[allow(missing_debug_implementations)] +/// Enum representing the Connector Integration +#[derive(Clone)] +pub enum ConnectorIntegrationEnum<'a, F, ResourceCommonData, Req, Resp> { + /// Old connector integration type + Old(BoxedConnectorIntegration<'a, F, Req, Resp>), + /// New connector integration type + New(BoxedConnectorIntegrationV2<'a, F, ResourceCommonData, Req, Resp>), +} + +/// Alias for Box +pub type BoxedConnectorIntegrationInterface = + Box + Send + Sync>; + +impl ConnectorEnum { + /// Get the connector integration + /// + /// # Returns + /// + /// A `BoxedConnectorIntegrationInterface` containing the connector integration + pub fn get_connector_integration( + &self, + ) -> BoxedConnectorIntegrationInterface + where + dyn Connector + Sync: ConnectorIntegration, + dyn ConnectorV2 + Sync: ConnectorIntegrationV2, + ResourceCommonData: RouterDataConversion + Clone + 'static, + F: Clone + 'static, + Req: Clone + 'static, + Resp: Clone + 'static, + { + match self { + Self::Old(old_integration) => Box::new(ConnectorIntegrationEnum::Old( + old_integration.get_connector_integration(), + )), + Self::New(new_integration) => Box::new(ConnectorIntegrationEnum::New( + new_integration.get_connector_integration_v2(), + )), + } + } + /// validates the file upload + pub fn validate_file_upload( + &self, + purpose: api::files::FilePurpose, + file_size: i32, + file_type: mime::Mime, + ) -> CustomResult<(), errors::ConnectorError> { + match self { + Self::Old(connector) => connector.validate_file_upload(purpose, file_size, file_type), + Self::New(connector) => { + connector.validate_file_upload_v2(purpose, file_size, file_type) + } + } + } +} + +#[async_trait::async_trait] +impl IncomingWebhook for ConnectorEnum { + fn get_webhook_body_decoding_algorithm( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + match self { + Self::Old(connector) => connector.get_webhook_body_decoding_algorithm(request), + Self::New(connector) => connector.get_webhook_body_decoding_algorithm(request), + } + } + + fn get_webhook_body_decoding_message( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + match self { + Self::Old(connector) => connector.get_webhook_body_decoding_message(request), + Self::New(connector) => connector.get_webhook_body_decoding_message(request), + } + } + + async fn decode_webhook_body( + &self, + request: &IncomingWebhookRequestDetails<'_>, + merchant_id: &common_utils::id_type::MerchantId, + connector_webhook_details: Option, + connector_name: &str, + ) -> CustomResult, errors::ConnectorError> { + match self { + Self::Old(connector) => { + connector + .decode_webhook_body( + request, + merchant_id, + connector_webhook_details, + connector_name, + ) + .await + } + Self::New(connector) => { + connector + .decode_webhook_body( + request, + merchant_id, + connector_webhook_details, + connector_name, + ) + .await + } + } + } + + fn get_webhook_source_verification_algorithm( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + match self { + Self::Old(connector) => connector.get_webhook_source_verification_algorithm(request), + Self::New(connector) => connector.get_webhook_source_verification_algorithm(request), + } + } + + async fn get_webhook_source_verification_merchant_secret( + &self, + merchant_id: &common_utils::id_type::MerchantId, + connector_name: &str, + connector_webhook_details: Option, + ) -> CustomResult { + match self { + Self::Old(connector) => { + connector + .get_webhook_source_verification_merchant_secret( + merchant_id, + connector_name, + connector_webhook_details, + ) + .await + } + Self::New(connector) => { + connector + .get_webhook_source_verification_merchant_secret( + merchant_id, + connector_name, + connector_webhook_details, + ) + .await + } + } + } + + fn get_webhook_source_verification_signature( + &self, + request: &IncomingWebhookRequestDetails<'_>, + connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, + ) -> CustomResult, errors::ConnectorError> { + match self { + Self::Old(connector) => connector + .get_webhook_source_verification_signature(request, connector_webhook_secrets), + Self::New(connector) => connector + .get_webhook_source_verification_signature(request, connector_webhook_secrets), + } + } + + fn get_webhook_source_verification_message( + &self, + request: &IncomingWebhookRequestDetails<'_>, + merchant_id: &common_utils::id_type::MerchantId, + connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, + ) -> CustomResult, errors::ConnectorError> { + match self { + Self::Old(connector) => connector.get_webhook_source_verification_message( + request, + merchant_id, + connector_webhook_secrets, + ), + Self::New(connector) => connector.get_webhook_source_verification_message( + request, + merchant_id, + connector_webhook_secrets, + ), + } + } + + async fn verify_webhook_source( + &self, + request: &IncomingWebhookRequestDetails<'_>, + merchant_id: &common_utils::id_type::MerchantId, + connector_webhook_details: Option, + connector_account_details: crypto::Encryptable>, + connector_name: &str, + ) -> CustomResult { + match self { + Self::Old(connector) => { + connector + .verify_webhook_source( + request, + merchant_id, + connector_webhook_details, + connector_account_details, + connector_name, + ) + .await + } + Self::New(connector) => { + connector + .verify_webhook_source( + request, + merchant_id, + connector_webhook_details, + connector_account_details, + connector_name, + ) + .await + } + } + } + + fn get_webhook_object_reference_id( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + match self { + Self::Old(connector) => connector.get_webhook_object_reference_id(request), + Self::New(connector) => connector.get_webhook_object_reference_id(request), + } + } + + fn get_webhook_event_type( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + match self { + Self::Old(connector) => connector.get_webhook_event_type(request), + Self::New(connector) => connector.get_webhook_event_type(request), + } + } + + fn get_webhook_resource_object( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + match self { + Self::Old(connector) => connector.get_webhook_resource_object(request), + Self::New(connector) => connector.get_webhook_resource_object(request), + } + } + + fn get_webhook_api_response( + &self, + request: &IncomingWebhookRequestDetails<'_>, + error_kind: Option, + ) -> CustomResult, errors::ConnectorError> { + match self { + Self::Old(connector) => connector.get_webhook_api_response(request, error_kind), + Self::New(connector) => connector.get_webhook_api_response(request, error_kind), + } + } + + fn get_dispute_details( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + match self { + Self::Old(connector) => connector.get_dispute_details(request), + Self::New(connector) => connector.get_dispute_details(request), + } + } + + fn get_external_authentication_details( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + match self { + Self::Old(connector) => connector.get_external_authentication_details(request), + Self::New(connector) => connector.get_external_authentication_details(request), + } + } + + fn get_mandate_details( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult< + Option, + errors::ConnectorError, + > { + match self { + Self::Old(connector) => connector.get_mandate_details(request), + Self::New(connector) => connector.get_mandate_details(request), + } + } + + fn get_network_txn_id( + &self, + request: &IncomingWebhookRequestDetails<'_>, + ) -> CustomResult< + Option, + errors::ConnectorError, + > { + match self { + Self::Old(connector) => connector.get_network_txn_id(request), + Self::New(connector) => connector.get_network_txn_id(request), + } + } +} + +impl ConnectorRedirectResponse for ConnectorEnum { + fn get_flow_type( + &self, + query_params: &str, + json_payload: Option, + action: PaymentAction, + ) -> CustomResult { + match self { + Self::Old(connector) => connector.get_flow_type(query_params, json_payload, action), + Self::New(connector) => connector.get_flow_type(query_params, json_payload, action), + } + } +} + +impl ConnectorValidation for ConnectorEnum { + fn validate_connector_against_payment_request( + &self, + capture_method: Option, + payment_method: common_enums::PaymentMethod, + pmt: Option, + ) -> CustomResult<(), errors::ConnectorError> { + match self { + Self::Old(connector) => connector.validate_connector_against_payment_request( + capture_method, + payment_method, + pmt, + ), + Self::New(connector) => connector.validate_connector_against_payment_request( + capture_method, + payment_method, + pmt, + ), + } + } + + fn validate_mandate_payment( + &self, + pm_type: Option, + pm_data: PaymentMethodData, + ) -> CustomResult<(), errors::ConnectorError> { + match self { + Self::Old(connector) => connector.validate_mandate_payment(pm_type, pm_data), + Self::New(connector) => connector.validate_mandate_payment(pm_type, pm_data), + } + } + + fn validate_psync_reference_id( + &self, + data: &hyperswitch_domain_models::router_request_types::PaymentsSyncData, + is_three_ds: bool, + status: common_enums::enums::AttemptStatus, + connector_meta_data: Option, + ) -> CustomResult<(), errors::ConnectorError> { + match self { + Self::Old(connector) => connector.validate_psync_reference_id( + data, + is_three_ds, + status, + connector_meta_data, + ), + Self::New(connector) => connector.validate_psync_reference_id( + data, + is_three_ds, + status, + connector_meta_data, + ), + } + } + + fn is_webhook_source_verification_mandatory(&self) -> bool { + match self { + Self::Old(connector) => connector.is_webhook_source_verification_mandatory(), + Self::New(connector) => connector.is_webhook_source_verification_mandatory(), + } + } +} + +impl ConnectorSpecifications for ConnectorEnum { + fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> { + match self { + Self::Old(connector) => connector.get_supported_payment_methods(), + Self::New(connector) => connector.get_supported_payment_methods(), + } + } + + /// Supported webhooks flows + fn get_supported_webhook_flows(&self) -> Option<&'static [common_enums::EventClass]> { + match self { + Self::Old(connector) => connector.get_supported_webhook_flows(), + Self::New(connector) => connector.get_supported_webhook_flows(), + } + } + + /// Details related to connector + fn get_connector_about(&self) -> Option<&'static ConnectorInfo> { + match self { + Self::Old(connector) => connector.get_connector_about(), + Self::New(connector) => connector.get_connector_about(), + } + } +} + +impl ConnectorCommon for ConnectorEnum { + fn id(&self) -> &'static str { + match self { + Self::Old(connector) => connector.id(), + Self::New(connector) => connector.id(), + } + } + + fn get_currency_unit(&self) -> CurrencyUnit { + match self { + Self::Old(connector) => connector.get_currency_unit(), + Self::New(connector) => connector.get_currency_unit(), + } + } + + fn get_auth_header( + &self, + auth_type: &ConnectorAuthType, + ) -> CustomResult)>, errors::ConnectorError> { + match self { + Self::Old(connector) => connector.get_auth_header(auth_type), + Self::New(connector) => connector.get_auth_header(auth_type), + } + } + + fn common_get_content_type(&self) -> &'static str { + match self { + Self::Old(connector) => connector.common_get_content_type(), + Self::New(connector) => connector.common_get_content_type(), + } + } + + fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str { + match self { + Self::Old(connector) => connector.base_url(connectors), + Self::New(connector) => connector.base_url(connectors), + } + } + + fn build_error_response( + &self, + res: types::Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + match self { + Self::Old(connector) => connector.build_error_response(res, event_builder), + Self::New(connector) => connector.build_error_response(res, event_builder), + } + } +} + +/// Trait representing the connector integration interface +/// +/// This trait defines the methods required for a connector integration interface. +pub trait ConnectorIntegrationInterface: Send + Sync { + /// Clone the connector integration interface + /// + /// # Returns + /// + /// A `Box` containing the cloned connector integration interface + fn clone_box( + &self, + ) -> Box + Send + Sync>; + /// Get the multiple capture sync method + /// + /// # Returns + /// + /// A `CustomResult` containing the `CaptureSyncMethod` or a `ConnectorError` + fn get_multiple_capture_sync_method( + &self, + ) -> CustomResult; + /// Build a request for the connector integration + /// + /// # Arguments + /// + /// * `req` - A reference to the RouterData + /// # Returns + /// + /// A `CustomResult` containing an optional Request or a ConnectorError + fn build_request( + &self, + req: &RouterData, + _connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError>; + /// handles response from the connector + fn handle_response( + &self, + data: &RouterData, + event_builder: Option<&mut ConnectorEvent>, + _res: types::Response, + ) -> CustomResult, errors::ConnectorError> + where + F: Clone, + Req: Clone, + Resp: Clone; + /// Get the error response + /// + /// # Arguments + /// + /// * `res` - The response + /// * `event_builder` - An optional event builder + /// + /// # Returns + /// + /// A `CustomResult` containing the `ErrorResponse` or a `ConnectorError` + fn get_error_response( + &self, + res: types::Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult; + /// Get the 5xx error response + /// + /// # Arguments + /// + /// * `res` - The response + /// * `event_builder` - An optional event builder + /// + /// # Returns + /// + /// A `CustomResult` containing the `ErrorResponse` or a `ConnectorError` + fn get_5xx_error_response( + &self, + res: types::Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult; +} + +impl + ConnectorIntegrationInterface + for ConnectorIntegrationEnum<'static, T, ResourceCommonData, Req, Resp> +where + ResourceCommonData: RouterDataConversion + Clone, + T: Clone, + Req: Clone, + Resp: Clone, +{ + fn get_multiple_capture_sync_method( + &self, + ) -> CustomResult { + match self { + ConnectorIntegrationEnum::Old(old_integration) => { + old_integration.get_multiple_capture_sync_method() + } + ConnectorIntegrationEnum::New(new_integration) => { + new_integration.get_multiple_capture_sync_method() + } + } + } + fn build_request( + &self, + req: &RouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + match self { + ConnectorIntegrationEnum::Old(old_integration) => { + old_integration.build_request(req, connectors) + } + ConnectorIntegrationEnum::New(new_integration) => { + let new_router_data = ResourceCommonData::from_old_router_data(req)?; + new_integration.build_request_v2(&new_router_data) + } + } + } + fn handle_response( + &self, + data: &RouterData, + event_builder: Option<&mut ConnectorEvent>, + res: types::Response, + ) -> CustomResult, errors::ConnectorError> + where + T: Clone, + Req: Clone, + Resp: Clone, + { + match self { + ConnectorIntegrationEnum::Old(old_integration) => { + old_integration.handle_response(data, event_builder, res) + } + ConnectorIntegrationEnum::New(new_integration) => { + let new_router_data = ResourceCommonData::from_old_router_data(data)?; + new_integration + .handle_response_v2(&new_router_data, event_builder, res) + .map(ResourceCommonData::to_old_router_data)? + } + } + } + fn get_error_response( + &self, + res: types::Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + match self { + ConnectorIntegrationEnum::Old(old_integration) => { + old_integration.get_error_response(res, event_builder) + } + ConnectorIntegrationEnum::New(new_integration) => { + new_integration.get_error_response_v2(res, event_builder) + } + } + } + fn get_5xx_error_response( + &self, + res: types::Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + match self { + ConnectorIntegrationEnum::Old(old_integration) => { + old_integration.get_5xx_error_response(res, event_builder) + } + ConnectorIntegrationEnum::New(new_integration) => { + new_integration.get_5xx_error_response(res, event_builder) + } + } + } + + fn clone_box( + &self, + ) -> Box + Send + Sync> + { + Box::new(self.clone()) + } +} + +impl api::ConnectorTransactionId for ConnectorEnum { + /// Get the connector transaction ID + /// + /// # Arguments + /// + /// * `payment_attempt` - The payment attempt + /// + /// # Returns + /// + /// A `Result` containing an optional transaction ID or an ApiErrorResponse + fn connector_transaction_id( + &self, + payment_attempt: hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, + ) -> Result, ApiErrorResponse> { + match self { + Self::Old(connector) => connector.connector_transaction_id(payment_attempt), + Self::New(connector) => connector.connector_transaction_id(payment_attempt), + } + } +} diff --git a/crates/hyperswitch_interfaces/src/connector_integration_v2.rs b/crates/hyperswitch_interfaces/src/connector_integration_v2.rs index e0974f5d04..47a6588a37 100644 --- a/crates/hyperswitch_interfaces/src/connector_integration_v2.rs +++ b/crates/hyperswitch_interfaces/src/connector_integration_v2.rs @@ -1,4 +1,5 @@ //! definition of the new connector integration trait + use common_utils::{ errors::CustomResult, request::{Method, Request, RequestBuilder, RequestContent}, @@ -8,9 +9,54 @@ use masking::Maskable; use serde_json::json; use crate::{ - api::CaptureSyncMethod, errors, events::connector_api_logs::ConnectorEvent, metrics, types, + api::{self, CaptureSyncMethod}, + errors, + events::connector_api_logs::ConnectorEvent, + metrics, types, webhooks, }; +/// ConnectorV2 trait +pub trait ConnectorV2: + Send + + api::refunds_v2::RefundV2 + + api::payments_v2::PaymentV2 + + api::ConnectorRedirectResponse + + webhooks::IncomingWebhook + + api::ConnectorAccessTokenV2 + + api::disputes_v2::DisputeV2 + + api::files_v2::FileUploadV2 + + api::ConnectorTransactionId + + api::PayoutsV2 + + api::ConnectorVerifyWebhookSourceV2 + + api::FraudCheckV2 + + api::ConnectorMandateRevokeV2 + + api::authentication_v2::ExternalAuthenticationV2 + + api::UnifiedAuthenticationServiceV2 +{ +} +impl< + T: api::refunds_v2::RefundV2 + + api::payments_v2::PaymentV2 + + api::ConnectorRedirectResponse + + Send + + webhooks::IncomingWebhook + + api::ConnectorAccessTokenV2 + + api::disputes_v2::DisputeV2 + + api::files_v2::FileUploadV2 + + api::ConnectorTransactionId + + api::PayoutsV2 + + api::ConnectorVerifyWebhookSourceV2 + + api::FraudCheckV2 + + api::ConnectorMandateRevokeV2 + + api::authentication_v2::ExternalAuthenticationV2 + + api::UnifiedAuthenticationServiceV2, + > ConnectorV2 for T +{ +} + +/// Alias for Box<&'static (dyn ConnectorV2 + Sync)> +pub type BoxedConnectorV2 = Box<&'static (dyn ConnectorV2 + Sync)>; + /// alias for Box of a type that implements trait ConnectorIntegrationV2 pub type BoxedConnectorIntegrationV2<'a, Flow, ResourceCommonData, Req, Resp> = Box<&'a (dyn ConnectorIntegrationV2 + Send + Sync)>; @@ -39,7 +85,7 @@ where /// The new connector integration trait with an additional ResourceCommonData generic parameter pub trait ConnectorIntegrationV2: - ConnectorIntegrationAnyV2 + Sync + super::api::ConnectorCommon + ConnectorIntegrationAnyV2 + Sync + api::ConnectorCommon { /// returns a vec of tuple of header key and value fn get_headers( diff --git a/crates/router/src/services/conversion_impls.rs b/crates/hyperswitch_interfaces/src/conversion_impls.rs similarity index 92% rename from crates/router/src/services/conversion_impls.rs rename to crates/hyperswitch_interfaces/src/conversion_impls.rs index e3c00d82d5..3094a0e622 100644 --- a/crates/router/src/services/conversion_impls.rs +++ b/crates/hyperswitch_interfaces/src/conversion_impls.rs @@ -1,4 +1,4 @@ -use common_utils::id_type; +use common_utils::{errors::CustomResult, id_type}; use error_stack::ResultExt; #[cfg(feature = "frm")] use hyperswitch_domain_models::router_data_v2::flow_common_types::FrmFlowData; @@ -17,8 +17,7 @@ use hyperswitch_domain_models::{ }, }; -use super::connector_integration_interface::RouterDataConversion; -use crate::errors; +use crate::{connector_integration_interface::RouterDataConversion, errors::ConnectorError}; fn get_irrelevant_id_string(id_name: &str, flow_name: &str) -> String { format!("irrelevant {id_name} in {flow_name} flow") @@ -89,7 +88,7 @@ fn get_default_router_data( impl RouterDataConversion for AccessTokenFlowData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -106,7 +105,7 @@ impl RouterDataConversion for AccessTo fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -126,7 +125,7 @@ impl RouterDataConversion for AccessTo impl RouterDataConversion for PaymentFlowData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -172,7 +171,7 @@ impl RouterDataConversion for PaymentF fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -247,7 +246,7 @@ impl RouterDataConversion for PaymentF impl RouterDataConversion for RefundFlowData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -263,7 +262,7 @@ impl RouterDataConversion for RefundFl minor_amount_captured: old_router_data.minor_amount_captured, connector_request_reference_id: old_router_data.connector_request_reference_id.clone(), refund_id: old_router_data.refund_id.clone().ok_or( - errors::ConnectorError::MissingRequiredField { + ConnectorError::MissingRequiredField { field_name: "refund_id", }, )?, @@ -280,7 +279,7 @@ impl RouterDataConversion for RefundFl fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -321,7 +320,7 @@ impl RouterDataConversion for RefundFl impl RouterDataConversion for DisputesFlowData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -335,7 +334,7 @@ impl RouterDataConversion for Disputes minor_amount_captured: old_router_data.minor_amount_captured, connector_request_reference_id: old_router_data.connector_request_reference_id.clone(), dispute_id: old_router_data.dispute_id.clone().ok_or( - errors::ConnectorError::MissingRequiredField { + ConnectorError::MissingRequiredField { field_name: "dispute_id", }, )?, @@ -352,7 +351,7 @@ impl RouterDataConversion for Disputes fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -390,7 +389,7 @@ impl RouterDataConversion for Disputes impl RouterDataConversion for FrmFlowData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -418,7 +417,7 @@ impl RouterDataConversion for FrmFlowD fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -459,7 +458,7 @@ impl RouterDataConversion for FrmFlowD impl RouterDataConversion for FilesFlowData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -482,7 +481,7 @@ impl RouterDataConversion for FilesFlo fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -512,7 +511,7 @@ impl RouterDataConversion for FilesFlo impl RouterDataConversion for WebhookSourceVerifyData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -531,7 +530,7 @@ impl RouterDataConversion for WebhookS fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -550,14 +549,14 @@ impl RouterDataConversion for WebhookS impl RouterDataConversion for MandateRevokeFlowData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { let resource_common_data = Self { merchant_id: old_router_data.merchant_id.clone(), customer_id: old_router_data.customer_id.clone().ok_or( - errors::ConnectorError::MissingRequiredField { + ConnectorError::MissingRequiredField { field_name: "customer_id", }, )?, @@ -575,7 +574,7 @@ impl RouterDataConversion for MandateR fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -607,7 +606,7 @@ impl RouterDataConversion for MandateR impl RouterDataConversion for PayoutFlowData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -634,7 +633,7 @@ impl RouterDataConversion for PayoutFl fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -672,7 +671,7 @@ impl RouterDataConversion { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -693,7 +692,7 @@ impl RouterDataConversion fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -720,7 +719,7 @@ impl RouterDataConversion { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -737,7 +736,7 @@ impl RouterDataConversion fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -754,7 +753,7 @@ impl RouterDataConversion impl RouterDataConversion for UasFlowData { fn from_old_router_data( old_router_data: &RouterData, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { @@ -763,7 +762,7 @@ impl RouterDataConversion for UasFlowD source_authentication_id: old_router_data .authentication_id .clone() - .ok_or(errors::ConnectorError::MissingRequiredField { + .ok_or(ConnectorError::MissingRequiredField { field_name: "source_authentication_id", }) .attach_printable("missing authentication id for uas")?, @@ -780,7 +779,7 @@ impl RouterDataConversion for UasFlowD fn to_old_router_data( new_router_data: RouterDataV2, - ) -> errors::CustomResult, errors::ConnectorError> + ) -> CustomResult, ConnectorError> where Self: Sized, { diff --git a/crates/hyperswitch_interfaces/src/lib.rs b/crates/hyperswitch_interfaces/src/lib.rs index 2acbe5c7f9..a9eb2997c8 100644 --- a/crates/hyperswitch_interfaces/src/lib.rs +++ b/crates/hyperswitch_interfaces/src/lib.rs @@ -3,10 +3,16 @@ pub mod api; pub mod authentication; +/// Configuration related functionalities pub mod configs; +/// Connector integration interface module +pub mod connector_integration_interface; /// definition of the new connector integration trait pub mod connector_integration_v2; +/// Constants used throughout the application pub mod consts; +/// Conversion implementations +pub mod conversion_impls; pub mod disputes; pub mod encryption_interface; pub mod errors; diff --git a/crates/kgraph_utils/Cargo.toml b/crates/kgraph_utils/Cargo.toml index 0883e14869..ac14087c08 100644 --- a/crates/kgraph_utils/Cargo.toml +++ b/crates/kgraph_utils/Cargo.toml @@ -8,13 +8,14 @@ license.workspace = true [features] dummy_connector = ["api_models/dummy_connector", "euclid/dummy_connector"] -v1 = ["api_models/v1", "common_utils/v1"] -v2 = ["api_models/v2", "common_utils/v2"] +v1 = ["api_models/v1", "common_utils/v1", "common_types/v1"] +v2 = ["api_models/v2", "common_utils/v2", "common_types/v2"] [dependencies] api_models = { version = "0.1.0", path = "../api_models", package = "api_models" } common_enums = { version = "0.1.0", path = "../common_enums" } common_utils = { version = "0.1.0", path = "../common_utils" } +common_types = {version = "0.1.0", path = "../common_types" } euclid = { version = "0.1.0", path = "../euclid" } hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph", features = ["viz"] } masking = { version = "0.1.0", path = "../masking/" } diff --git a/crates/kgraph_utils/src/error.rs b/crates/kgraph_utils/src/error.rs index ef9e3fd506..017b53ec0c 100644 --- a/crates/kgraph_utils/src/error.rs +++ b/crates/kgraph_utils/src/error.rs @@ -1,10 +1,14 @@ +#[cfg(feature = "v2")] +use common_enums::connector_enums; use euclid::{dssa::types::AnalysisErrorType, frontend::dir}; - #[derive(Debug, thiserror::Error, serde::Serialize)] #[serde(tag = "type", content = "info", rename_all = "snake_case")] pub enum KgraphError { #[error("Invalid connector name encountered: '{0}'")] - InvalidConnectorName(String), + InvalidConnectorName( + #[cfg(feature = "v1")] String, + #[cfg(feature = "v2")] connector_enums::Connector, + ), #[error("Error in domain creation")] DomainCreationError, #[error("There was an error constructing the graph: {0}")] diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index 68ab620b63..abaa9df563 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -16,7 +16,7 @@ use crate::{error::KgraphError, transformers::IntoDirValue, types as kgraph_type pub const DOMAIN_IDENTIFIER: &str = "payment_methods_enabled_for_merchantconnectoraccount"; -#[cfg(feature = "v1")] +// #[cfg(feature = "v1")] fn get_dir_value_payment_method( from: api_enums::PaymentMethodType, ) -> Result { @@ -162,6 +162,200 @@ fn get_dir_value_payment_method( } } +#[cfg(feature = "v2")] +fn compile_request_pm_types( + builder: &mut cgraph::ConstraintGraphBuilder, + pm_types: common_types::payment_methods::RequestPaymentMethodTypes, + pm: api_enums::PaymentMethod, +) -> Result { + let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); + + let pmt_info = "PaymentMethodType"; + let pmt_id = builder.make_value_node( + (pm_types.payment_method_subtype, pm) + .into_dir_value() + .map(Into::into)?, + Some(pmt_info), + None::<()>, + ); + agg_nodes.push(( + pmt_id, + cgraph::Relation::Positive, + match pm_types.payment_method_subtype { + api_enums::PaymentMethodType::Credit | api_enums::PaymentMethodType::Debit => { + cgraph::Strength::Weak + } + + _ => cgraph::Strength::Strong, + }, + )); + + if let Some(card_networks) = pm_types.card_networks { + if !card_networks.is_empty() { + let dir_vals: Vec = card_networks + .into_iter() + .map(IntoDirValue::into_dir_value) + .collect::>()?; + + let card_network_info = "Card Networks"; + let card_network_id = builder + .make_in_aggregator(dir_vals, Some(card_network_info), None::<()>) + .map_err(KgraphError::GraphConstructionError)?; + + agg_nodes.push(( + card_network_id, + cgraph::Relation::Positive, + cgraph::Strength::Weak, + )); + } + } + + let currencies_data = pm_types + .accepted_currencies + .and_then(|accepted_currencies| match accepted_currencies { + common_types::payment_methods::AcceptedCurrencies::EnableOnly(curr) + if !curr.is_empty() => + { + Some(( + curr.into_iter() + .map(IntoDirValue::into_dir_value) + .collect::>() + .ok()?, + cgraph::Relation::Positive, + )) + } + + common_types::payment_methods::AcceptedCurrencies::DisableOnly(curr) + if !curr.is_empty() => + { + Some(( + curr.into_iter() + .map(IntoDirValue::into_dir_value) + .collect::>() + .ok()?, + cgraph::Relation::Negative, + )) + } + + _ => None, + }); + + if let Some((currencies, relation)) = currencies_data { + let accepted_currencies_info = "Accepted Currencies"; + let accepted_currencies_id = builder + .make_in_aggregator(currencies, Some(accepted_currencies_info), None::<()>) + .map_err(KgraphError::GraphConstructionError)?; + + agg_nodes.push((accepted_currencies_id, relation, cgraph::Strength::Strong)); + } + + let mut amount_nodes = Vec::with_capacity(2); + + if let Some(min_amt) = pm_types.minimum_amount { + let num_val = NumValue { + number: min_amt, + refinement: Some(NumValueRefinement::GreaterThanEqual), + }; + + let min_amt_info = "Minimum Amount"; + let min_amt_id = builder.make_value_node( + dir::DirValue::PaymentAmount(num_val).into(), + Some(min_amt_info), + None::<()>, + ); + + amount_nodes.push(min_amt_id); + } + + if let Some(max_amt) = pm_types.maximum_amount { + let num_val = NumValue { + number: max_amt, + refinement: Some(NumValueRefinement::LessThanEqual), + }; + + let max_amt_info = "Maximum Amount"; + let max_amt_id = builder.make_value_node( + dir::DirValue::PaymentAmount(num_val).into(), + Some(max_amt_info), + None::<()>, + ); + + amount_nodes.push(max_amt_id); + } + + if !amount_nodes.is_empty() { + let zero_num_val = NumValue { + number: MinorUnit::zero(), + refinement: None, + }; + + let zero_amt_id = builder.make_value_node( + dir::DirValue::PaymentAmount(zero_num_val).into(), + Some("zero_amount"), + None::<()>, + ); + + let or_node_neighbor_id = if amount_nodes.len() == 1 { + amount_nodes + .first() + .copied() + .ok_or(KgraphError::IndexingError)? + } else { + let nodes = amount_nodes + .iter() + .copied() + .map(|node_id| { + ( + node_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + ) + }) + .collect::>(); + + builder + .make_all_aggregator( + &nodes, + Some("amount_constraint_aggregator"), + None::<()>, + None, + ) + .map_err(KgraphError::GraphConstructionError)? + }; + + let any_aggregator = builder + .make_any_aggregator( + &[ + ( + zero_amt_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + ), + ( + or_node_neighbor_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + ), + ], + Some("zero_plus_limits_amount_aggregator"), + None::<()>, + None, + ) + .map_err(KgraphError::GraphConstructionError)?; + + agg_nodes.push(( + any_aggregator, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + )); + } + + let pmt_all_aggregator_info = "All Aggregator for PaymentMethodType"; + builder + .make_all_aggregator(&agg_nodes, Some(pmt_all_aggregator_info), None::<()>, None) + .map_err(KgraphError::GraphConstructionError) +} + #[cfg(feature = "v1")] fn compile_request_pm_types( builder: &mut cgraph::ConstraintGraphBuilder, @@ -348,6 +542,71 @@ fn compile_request_pm_types( .map_err(KgraphError::GraphConstructionError) } +#[cfg(feature = "v2")] +fn compile_payment_method_enabled( + builder: &mut cgraph::ConstraintGraphBuilder, + enabled: common_types::payment_methods::PaymentMethodsEnabled, +) -> Result, KgraphError> { + let agg_id = if !enabled + .payment_method_subtypes + .as_ref() + .map(|v| v.is_empty()) + .unwrap_or(true) + { + let pm_info = "PaymentMethod"; + let pm_id = builder.make_value_node( + enabled + .payment_method_type + .into_dir_value() + .map(Into::into)?, + Some(pm_info), + None::<()>, + ); + + let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); + + if let Some(pm_types) = enabled.payment_method_subtypes { + for pm_type in pm_types { + let node_id = + compile_request_pm_types(builder, pm_type, enabled.payment_method_type)?; + agg_nodes.push(( + node_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + )); + } + } + + let any_aggregator_info = "Any aggregation for PaymentMethodsType"; + let pm_type_agg_id = builder + .make_any_aggregator(&agg_nodes, Some(any_aggregator_info), None::<()>, None) + .map_err(KgraphError::GraphConstructionError)?; + + let all_aggregator_info = "All aggregation for PaymentMethod"; + let enabled_pm_agg_id = builder + .make_all_aggregator( + &[ + (pm_id, cgraph::Relation::Positive, cgraph::Strength::Strong), + ( + pm_type_agg_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + ), + ], + Some(all_aggregator_info), + None::<()>, + None, + ) + .map_err(KgraphError::GraphConstructionError)?; + + Some(enabled_pm_agg_id) + } else { + None + }; + + Ok(agg_id) +} + #[cfg(feature = "v1")] fn compile_payment_method_enabled( builder: &mut cgraph::ConstraintGraphBuilder, @@ -417,7 +676,7 @@ macro_rules! collect_global_variants { }; } -#[cfg(feature = "v1")] +// #[cfg(feature = "v1")] fn global_vec_pmt( enabled_pmt: Vec, builder: &mut cgraph::ConstraintGraphBuilder, @@ -525,7 +784,7 @@ fn compile_graph_for_countries_and_currencies( .map_err(KgraphError::GraphConstructionError) } -#[cfg(feature = "v1")] +// #[cfg(feature = "v1")] fn compile_config_graph( builder: &mut cgraph::ConstraintGraphBuilder, config: &kgraph_types::CountryCurrencyFilter, @@ -619,6 +878,77 @@ fn compile_config_graph( .map_err(KgraphError::GraphConstructionError) } +#[cfg(feature = "v2")] +fn compile_merchant_connector_graph( + builder: &mut cgraph::ConstraintGraphBuilder, + mca: admin_api::MerchantConnectorResponse, + config: &kgraph_types::CountryCurrencyFilter, +) -> Result<(), KgraphError> { + let connector = common_enums::RoutableConnectors::try_from(mca.connector_name) + .map_err(|_| KgraphError::InvalidConnectorName(mca.connector_name))?; + + let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); + + if let Some(pms_enabled) = mca.payment_methods_enabled.clone() { + for pm_enabled in pms_enabled { + let maybe_pm_enabled_id = compile_payment_method_enabled(builder, pm_enabled)?; + if let Some(pm_enabled_id) = maybe_pm_enabled_id { + agg_nodes.push(( + pm_enabled_id, + cgraph::Relation::Positive, + cgraph::Strength::Strong, + )); + } + } + } + + let aggregator_info = "Available Payment methods for connector"; + let pms_enabled_agg_id = builder + .make_any_aggregator(&agg_nodes, Some(aggregator_info), None::<()>, None) + .map_err(KgraphError::GraphConstructionError)?; + + let config_info = "Config for respective PaymentMethodType for the connector"; + + let config_enabled_agg_id = compile_config_graph(builder, config, connector)?; + + let domain_level_node_id = builder + .make_all_aggregator( + &[ + ( + config_enabled_agg_id, + cgraph::Relation::Positive, + cgraph::Strength::Normal, + ), + ( + pms_enabled_agg_id, + cgraph::Relation::Positive, + cgraph::Strength::Normal, + ), + ], + Some(config_info), + None::<()>, + None, + ) + .map_err(KgraphError::GraphConstructionError)?; + let connector_dir_val = dir::DirValue::Connector(Box::new(ast::ConnectorChoice { connector })); + + let connector_info = "Connector"; + let connector_node_id = + builder.make_value_node(connector_dir_val.into(), Some(connector_info), None::<()>); + + builder + .make_edge( + domain_level_node_id, + connector_node_id, + cgraph::Strength::Normal, + cgraph::Relation::Positive, + None::, + ) + .map_err(KgraphError::GraphConstructionError)?; + + Ok(()) +} + #[cfg(feature = "v1")] fn compile_merchant_connector_graph( builder: &mut cgraph::ConstraintGraphBuilder, @@ -690,7 +1020,7 @@ fn compile_merchant_connector_graph( Ok(()) } -#[cfg(feature = "v1")] +// #[cfg(feature = "v1")] pub fn make_mca_graph( accts: Vec, config: &kgraph_types::CountryCurrencyFilter, diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index ac1a3c94e8..9d326780ac 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -318,6 +318,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::enums::ElementPosition, api_models::enums::ElementSize, api_models::enums::SizeVariants, + api_models::enums::MerchantProductType, api_models::enums::PaymentLinkDetailsLayout, api_models::enums::PaymentMethodStatus, api_models::enums::UIWidgetFormLayout, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index ff4459ac56..5b01911881 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -280,6 +280,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::enums::ElementPosition, api_models::enums::ElementSize, api_models::enums::SizeVariants, + api_models::enums::MerchantProductType, api_models::enums::PaymentLinkDetailsLayout, api_models::enums::PaymentMethodStatus, api_models::enums::PaymentConnectorCategory, diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 780d4509db..548a24b8e3 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -26,7 +26,7 @@ oltp = ["storage_impl/oltp"] kv_store = ["scheduler/kv_store"] accounts_cache = [] vergen = ["router_env/vergen"] -dummy_connector = ["api_models/dummy_connector", "euclid/dummy_connector", "hyperswitch_interfaces/dummy_connector", "kgraph_utils/dummy_connector"] +dummy_connector = ["api_models/dummy_connector", "euclid/dummy_connector", "hyperswitch_interfaces/dummy_connector", "kgraph_utils/dummy_connector", "hyperswitch_domain_models/dummy_connector"] external_access_dc = ["dummy_connector"] detailed_errors = ["api_models/detailed_errors", "error-stack/serde"] payouts = ["api_models/payouts", "common_enums/payouts", "hyperswitch_connectors/payouts", "hyperswitch_domain_models/payouts", "storage_impl/payouts"] diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index d6872bcf06..e368a94b48 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -6,7 +6,10 @@ pub mod user_role; use std::collections::HashSet; use common_utils::consts; -pub use hyperswitch_domain_models::consts::CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH; +pub use hyperswitch_domain_models::consts::{ + CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH, ROUTING_ENABLED_PAYMENT_METHODS, + ROUTING_ENABLED_PAYMENT_METHOD_TYPES, +}; pub use hyperswitch_interfaces::consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE}; // ID generation diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index f8325c426f..5056660b6b 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1824,11 +1824,9 @@ impl PMAuthConfigValidation<'_> { .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { id: self.merchant_id.get_string_repr().to_owned(), })?; - for conn_choice in config.enabled_payment_methods { let pm_auth_mca = all_mcas - .clone() - .into_iter() + .iter() .find(|mca| mca.get_id() == conn_choice.mca_id) .ok_or(errors::ApiErrorResponse::GenericNotFoundError { message: "payment method auth connector account not found".to_string(), @@ -3023,7 +3021,7 @@ async fn validate_pm_auth( }) .attach_printable("Failed to deserialize Payment Method Auth config")?; - let all_mcas = &*state + let all_mcas = state .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( &state.into(), diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 40f8f40200..12e9c6e16e 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -94,6 +94,7 @@ use crate::{ defaults::{get_billing_required_fields, get_shipping_required_fields}, settings, }, + consts as router_consts, core::{ errors::{self, StorageErrorExt}, payment_methods::{network_tokenization, transformers as payment_methods, vault}, @@ -118,8 +119,8 @@ use crate::{ }; #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] use crate::{ - consts as router_consts, core::payment_methods as pm_core, headers, - types::payment_methods as pm_types, utils::ConnectorResponseExt, + core::payment_methods as pm_core, headers, types::payment_methods as pm_types, + utils::ConnectorResponseExt, }; #[cfg(all( @@ -3346,37 +3347,22 @@ pub async fn list_payment_methods( let profile_id = payment_intent .as_ref() - .map(|payment_intent| { - payment_intent - .profile_id - .clone() - .get_required_value("profile_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("profile_id is not set in payment_intent") - }) - .transpose()?; - let business_profile = core_utils::validate_and_get_business_profile( - db, - key_manager_state, - &key_store, - profile_id.as_ref(), - merchant_account.get_id(), - ) - .await?; - - let profile_id = profile_id - .clone() + .and_then(|payment_intent| payment_intent.profile_id.as_ref()) .get_required_value("profile_id") .change_context(errors::ApiErrorResponse::GenericNotFoundError { message: "Profile id not found".to_string(), })?; + let business_profile = db + .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id) + .await + .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound { + id: profile_id.get_string_repr().to_owned(), + })?; // filter out payment connectors based on profile_id - let filtered_mcas = helpers::filter_mca_based_on_profile_and_connector_type( - all_mcas.clone(), - &profile_id, - ConnectorType::PaymentProcessor, - ); + let filtered_mcas = all_mcas + .clone() + .filter_based_on_profile_and_connector_type(profile_id, ConnectorType::PaymentProcessor); logger::debug!(mca_before_filtering=?filtered_mcas); @@ -3535,21 +3521,11 @@ pub async fn list_payment_methods( if let Some((payment_attempt, payment_intent)) = payment_attempt.as_ref().zip(payment_intent.as_ref()) { - let routing_enabled_pms = HashSet::from([ - api_enums::PaymentMethod::BankTransfer, - api_enums::PaymentMethod::BankDebit, - api_enums::PaymentMethod::BankRedirect, - ]); + let routing_enabled_pms = &router_consts::ROUTING_ENABLED_PAYMENT_METHODS; - let routing_enabled_pm_types = HashSet::from([ - api_enums::PaymentMethodType::GooglePay, - api_enums::PaymentMethodType::ApplePay, - api_enums::PaymentMethodType::Klarna, - api_enums::PaymentMethodType::Paypal, - api_enums::PaymentMethodType::SamsungPay, - ]); + let routing_enabled_pm_types = &router_consts::ROUTING_ENABLED_PAYMENT_METHOD_TYPES; - let mut chosen = Vec::::new(); + let mut chosen = api::SessionConnectorDatas::new(Vec::new()); for intermediate in &response { if routing_enabled_pm_types.contains(&intermediate.payment_method_type) || routing_enabled_pms.contains(&intermediate.payment_method) @@ -3564,7 +3540,8 @@ pub async fn list_payment_methods( .attach_printable("invalid connector name received")?; chosen.push(api::SessionConnectorData { - payment_method_type: intermediate.payment_method_type, + payment_method_sub_type: intermediate.payment_method_type, + payment_method_type: intermediate.payment_method, connector: connector_data, business_sub_label: None, }); @@ -3579,10 +3556,14 @@ pub async fn list_payment_methods( payment_intent, chosen, }; - let result = routing::perform_session_flow_routing(sfr, &enums::TransactionType::Payment) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("error performing session flow routing")?; + let result = routing::perform_session_flow_routing( + sfr, + &business_profile, + &enums::TransactionType::Payment, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("error performing session flow routing")?; response.retain(|intermediate| { if !routing_enabled_pm_types.contains(&intermediate.payment_method_type) @@ -3770,8 +3751,7 @@ pub async fn list_payment_methods( // Check for `use_billing_as_payment_method_billing` config under business_profile // If this is disabled, then the billing details in required fields will be empty and have to be collected by the customer let billing_address_for_calculating_required_fields = business_profile - .as_ref() - .and_then(|business_profile| business_profile.use_billing_as_payment_method_billing) + .use_billing_as_payment_method_billing .unwrap_or(true) .then_some(billing_address.as_ref()) .flatten(); @@ -3845,7 +3825,7 @@ pub async fn list_payment_methods( required_fields_hs = should_collect_shipping_or_billing_details_from_wallet_connector( payment_method, element.payment_experience.as_ref(), - business_profile.as_ref(), + &business_profile, required_fields_hs.clone(), ); @@ -4178,61 +4158,47 @@ pub async fn list_payment_methods( .as_ref() .and_then(|intent| intent.request_external_three_ds_authentication) .unwrap_or(false); - let merchant_surcharge_configs = - if let Some((payment_attempt, payment_intent, business_profile)) = payment_attempt - .as_ref() - .zip(payment_intent) - .zip(business_profile.as_ref()) - .map(|((pa, pi), bp)| (pa, pi, bp)) - { - Box::pin(call_surcharge_decision_management( - state, - &merchant_account, - &key_store, - business_profile, - payment_attempt, - payment_intent, - billing_address, - &mut payment_method_responses, - )) - .await? - } else { - api_surcharge_decision_configs::MerchantSurchargeConfigs::default() - }; + let merchant_surcharge_configs = if let Some((payment_attempt, payment_intent)) = + payment_attempt.as_ref().zip(payment_intent) + { + Box::pin(call_surcharge_decision_management( + state, + &merchant_account, + &key_store, + &business_profile, + payment_attempt, + payment_intent, + billing_address, + &mut payment_method_responses, + )) + .await? + } else { + api_surcharge_decision_configs::MerchantSurchargeConfigs::default() + }; - let collect_shipping_details_from_wallets = - business_profile.as_ref().and_then(|business_profile| { - if business_profile - .always_collect_shipping_details_from_wallet_connector - .unwrap_or(false) - { - business_profile.always_collect_shipping_details_from_wallet_connector - } else { - business_profile.collect_shipping_details_from_wallet_connector - } - }); + let collect_shipping_details_from_wallets = if business_profile + .always_collect_shipping_details_from_wallet_connector + .unwrap_or(false) + { + business_profile.always_collect_shipping_details_from_wallet_connector + } else { + business_profile.collect_shipping_details_from_wallet_connector + }; - let collect_billing_details_from_wallets = - business_profile.as_ref().and_then(|business_profile| { - if business_profile - .always_collect_billing_details_from_wallet_connector - .unwrap_or(false) - { - business_profile.always_collect_billing_details_from_wallet_connector - } else { - business_profile.collect_billing_details_from_wallet_connector - } - }); + let collect_billing_details_from_wallets = if business_profile + .always_collect_billing_details_from_wallet_connector + .unwrap_or(false) + { + business_profile.always_collect_billing_details_from_wallet_connector + } else { + business_profile.collect_billing_details_from_wallet_connector + }; - let is_tax_connector_enabled = business_profile - .as_ref() - .is_some_and(|business_profile| business_profile.get_is_tax_connector_enabled()); + let is_tax_connector_enabled = business_profile.get_is_tax_connector_enabled(); Ok(services::ApplicationResponse::Json( api::PaymentMethodListResponse { - redirect_url: business_profile - .as_ref() - .and_then(|business_profile| business_profile.return_url.clone()), + redirect_url: business_profile.return_url.clone(), merchant_name: merchant_account.merchant_name, payment_type, payment_methods: payment_method_responses, @@ -4273,10 +4239,15 @@ pub async fn list_payment_methods( )) } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "customer_v2"), + not(feature = "payment_methods_v2") +))] fn should_collect_shipping_or_billing_details_from_wallet_connector( payment_method: api_enums::PaymentMethod, payment_experience_optional: Option<&api_enums::PaymentExperience>, - business_profile: Option<&Profile>, + business_profile: &Profile, mut required_fields_hs: HashMap, ) -> HashMap { match (payment_method, payment_experience_optional) { @@ -4285,13 +4256,11 @@ fn should_collect_shipping_or_billing_details_from_wallet_connector( api_enums::PaymentMethod::PayLater, Some(api_enums::PaymentExperience::InvokeSdkClient), ) => { - let always_send_billing_details = business_profile.and_then(|business_profile| { - business_profile.always_collect_billing_details_from_wallet_connector - }); + let always_send_billing_details = + business_profile.always_collect_billing_details_from_wallet_connector; - let always_send_shipping_details = business_profile.and_then(|business_profile| { - business_profile.always_collect_shipping_details_from_wallet_connector - }); + let always_send_shipping_details = + business_profile.always_collect_shipping_details_from_wallet_connector; if always_send_billing_details == Some(true) { let billing_details = get_billing_required_fields(); @@ -5243,13 +5212,13 @@ pub async fn get_mca_status( .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { id: merchant_id.get_string_repr().to_owned(), })?; - let merchant_connector_accounts = domain::MerchantConnectorAccounts::new(mcas); - return Ok(merchant_connector_accounts - .is_merchant_connector_account_id_in_connector_mandate_details( + return Ok( + mcas.is_merchant_connector_account_id_in_connector_mandate_details( profile_id.as_ref(), &connector_mandate_details, - )); + ), + ); } Ok(false) } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index f36ce639af..0d113fcc6b 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1,6 +1,5 @@ pub mod access_token; pub mod conditional_configs; -pub mod connector_integration_v2_impls; pub mod customers; pub mod flows; pub mod helpers; @@ -62,6 +61,7 @@ use redis_interface::errors::RedisError; use router_env::{instrument, tracing}; #[cfg(feature = "olap")] use router_types::transformers::ForeignFrom; +use rustc_hash::FxHashMap; use scheduler::utils as pt_utils; #[cfg(feature = "v2")] pub use session_operation::payments_session_core; @@ -93,6 +93,7 @@ use crate::types::api::convert_connector_data_to_routable_connectors; use crate::{ configs::settings::{ApplePayPreDecryptFlow, PaymentMethodTypeTokenFilter}, connector::utils::missing_field_err, + consts, core::{ errors::{self, CustomResult, RouterResponse, RouterResult}, payment_methods::{cards, network_tokenization}, @@ -1457,7 +1458,7 @@ pub async fn call_surcharge_decision_management_for_session_flow( payment_attempt: &storage::PaymentAttempt, payment_intent: &storage::PaymentIntent, billing_address: Option, - session_connector_data: &[api::SessionConnectorData], + session_connector_data: &api::SessionConnectorDatas, ) -> RouterResult> { if let Some(surcharge_amount) = payment_attempt.net_amount.get_surcharge_amount() { Ok(Some(api::SessionSurchargeDetails::PreDetermined( @@ -1475,7 +1476,7 @@ pub async fn call_surcharge_decision_management_for_session_flow( } else { let payment_method_type_list = session_connector_data .iter() - .map(|session_connector_data| session_connector_data.payment_method_type) + .map(|session_connector_data| session_connector_data.payment_method_sub_type) .collect(); #[cfg(feature = "v1")] @@ -2890,7 +2891,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { .set_key_with_expiry( &poll_id.into(), api_models::poll::PollStatus::Pending.to_string(), - crate::consts::POLL_ID_TTL, + consts::POLL_ID_TTL, ) .await .change_context(errors::StorageError::KVError) @@ -3918,7 +3919,7 @@ pub async fn call_multiple_connectors_service( state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - connectors: Vec, + connectors: api::SessionConnectorDatas, _operation: &Op, mut payment_data: D, customer: &Option, @@ -4031,7 +4032,7 @@ pub async fn call_multiple_connectors_service( state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - connectors: Vec, + connectors: api::SessionConnectorDatas, _operation: &Op, mut payment_data: D, customer: &Option, @@ -4074,8 +4075,8 @@ where payment_data.set_surcharge_details(session_surcharge_details.as_ref().and_then( |session_surcharge_details| { session_surcharge_details.fetch_surcharge_details( - session_connector_data.payment_method_type.into(), - session_connector_data.payment_method_type, + session_connector_data.payment_method_sub_type.into(), + session_connector_data.payment_method_sub_type, None, ) }, @@ -6079,6 +6080,7 @@ where let routing_output = perform_session_token_routing( state.clone(), merchant_account, + business_profile, key_store, payment_data, connectors, @@ -7095,13 +7097,15 @@ pub fn should_add_task_to_process_tracker( state: SessionState, merchant_account: &domain::MerchantAccount, + business_profile: &domain::Profile, key_store: &domain::MerchantKeyStore, payment_data: &D, - connectors: Vec, -) -> RouterResult> + connectors: api::SessionConnectorDatas, +) -> RouterResult where F: Clone, D: OperationSessionGetters, @@ -7137,7 +7141,7 @@ where // ) // })); - // let mut final_list: Vec = Vec::new(); + // let mut final_list: api::SessionConnectorDatas = Vec::new(); // for (routed_pm_type, pre_routing_choice) in pre_routing_results.into_iter() { // let routable_connector_list = match pre_routing_choice { // storage::PreRoutingConnectorChoice::Single(routable_connector) => { @@ -7161,20 +7165,7 @@ where // return Ok(final_list); // } // } - - let routing_enabled_pms = HashSet::from([ - enums::PaymentMethodType::GooglePay, - enums::PaymentMethodType::ApplePay, - enums::PaymentMethodType::Klarna, - enums::PaymentMethodType::Paypal, - ]); - - let mut chosen = Vec::::new(); - for connector_data in &connectors { - if routing_enabled_pms.contains(&connector_data.payment_method_type) { - chosen.push(connector_data.clone()); - } - } + let chosen = connectors.apply_filter_for_session_routing(); let sfr = SessionFlowRoutingInput { state: &state, country: payment_data @@ -7186,33 +7177,67 @@ where merchant_account, payment_attempt: payment_data.get_payment_attempt(), payment_intent: payment_data.get_payment_intent(), + chosen, + }; + let result = self_routing::perform_session_flow_routing( + sfr, + business_profile, + &enums::TransactionType::Payment, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("error performing session flow routing")?; + + let final_list = connectors.filter_and_validate_for_session_flow(&result)?; + + Ok(final_list) +} + +pub struct SessionTokenRoutingResult { + pub final_result: api::SessionConnectorDatas, + pub routing_result: + FxHashMap>, +} +#[cfg(feature = "v2")] +pub async fn perform_session_token_routing( + state: SessionState, + business_profile: &domain::Profile, + key_store: &domain::MerchantKeyStore, + payment_data: &D, + connectors: api::SessionConnectorDatas, +) -> RouterResult +where + F: Clone, + D: OperationSessionGetters, +{ + let chosen = connectors.apply_filter_for_session_routing(); + let sfr = SessionFlowRoutingInput { + country: payment_data + .get_payment_intent() + .billing_address + .as_ref() + .and_then(|address| address.get_inner().address.as_ref()) + .and_then(|details| details.country), + payment_intent: payment_data.get_payment_intent(), chosen, }; - let result = self_routing::perform_session_flow_routing(sfr, &enums::TransactionType::Payment) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("error performing session flow routing")?; + let result = self_routing::perform_session_flow_routing( + &state, + key_store, + sfr, + business_profile, + &enums::TransactionType::Payment, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("error performing session flow routing")?; - let mut final_list: Vec = Vec::new(); - - for connector_data in connectors { - if !routing_enabled_pms.contains(&connector_data.payment_method_type) { - final_list.push(connector_data); - } else if let Some(choice) = result.get(&connector_data.payment_method_type) { - let routing_choice = choice - .first() - .ok_or(errors::ApiErrorResponse::InternalServerError)?; - if connector_data.connector.connector_name == routing_choice.connector.connector_name - && connector_data.connector.merchant_connector_id - == routing_choice.connector.merchant_connector_id - { - final_list.push(connector_data); - } - } - } - - Ok(final_list) + let final_list = connectors.filter_and_validate_for_session_flow(&result)?; + Ok(SessionTokenRoutingResult { + final_result: final_list, + routing_result: result, + }) } #[cfg(feature = "v1")] @@ -7283,7 +7308,7 @@ where let static_split: api_models::routing::RoutingVolumeSplit = api_models::routing::RoutingVolumeSplit { routing_type: api_models::routing::RoutingType::Static, - split: crate::consts::DYNAMIC_ROUTING_MAX_VOLUME + split: consts::DYNAMIC_ROUTING_MAX_VOLUME - dynamic_routing_config .dynamic_routing_volume_split .unwrap_or_default(), diff --git a/crates/router/src/core/payments/connector_integration_v2_impls.rs b/crates/router/src/core/payments/connector_integration_v2_impls.rs deleted file mode 100644 index 1346cf1575..0000000000 --- a/crates/router/src/core/payments/connector_integration_v2_impls.rs +++ /dev/null @@ -1,1777 +0,0 @@ -use hyperswitch_domain_models::router_flow_types::{ - Authenticate, AuthenticationConfirmation, PostAuthenticate, PreAuthenticate, -}; -use hyperswitch_interfaces::api::{ - UasAuthenticationConfirmationV2, UasAuthenticationV2, UasPostAuthenticationV2, - UasPreAuthenticationV2, UnifiedAuthenticationServiceV2, -}; - -#[cfg(feature = "frm")] -use crate::types::fraud_check as frm_types; -use crate::{ - connector, services, - types::{self, api}, -}; - -#[cfg(feature = "dummy_connector")] -mod dummy_connector_default_impl { - #[cfg(feature = "frm")] - use super::frm_types; - use super::{api, connector, services, types}; - impl api::PaymentV2 for connector::DummyConnector {} - - impl api::PaymentAuthorizeV2 for connector::DummyConnector {} - - impl api::PaymentAuthorizeSessionTokenV2 for connector::DummyConnector {} - - impl api::PaymentSyncV2 for connector::DummyConnector {} - - impl api::PaymentVoidV2 for connector::DummyConnector {} - - impl api::PaymentApproveV2 for connector::DummyConnector {} - - impl api::PaymentRejectV2 for connector::DummyConnector {} - - impl api::PaymentCaptureV2 for connector::DummyConnector {} - - impl api::PaymentSessionV2 for connector::DummyConnector {} - - impl api::MandateSetupV2 for connector::DummyConnector {} - - impl api::PaymentIncrementalAuthorizationV2 for connector::DummyConnector {} - - impl api::PaymentsCompleteAuthorizeV2 for connector::DummyConnector {} - - impl api::PaymentTokenV2 for connector::DummyConnector {} - - impl api::ConnectorCustomerV2 for connector::DummyConnector {} - - impl api::PaymentsPreProcessingV2 for connector::DummyConnector {} - - impl api::PaymentsPostProcessingV2 for connector::DummyConnector {} - - impl api::TaxCalculationV2 for connector::DummyConnector {} - - impl api::PaymentSessionUpdateV2 for connector::DummyConnector {} - - impl api::PaymentPostSessionTokensV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::Authorize, - types::PaymentFlowData, - types::PaymentsAuthorizeData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::PSync, - types::PaymentFlowData, - types::PaymentsSyncData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::Void, - types::PaymentFlowData, - types::PaymentsCancelData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::Approve, - types::PaymentFlowData, - types::PaymentsApproveData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::Reject, - types::PaymentFlowData, - types::PaymentsRejectData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::Capture, - types::PaymentFlowData, - types::PaymentsCaptureData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::Session, - types::PaymentFlowData, - types::PaymentsSessionData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::SetupMandate, - types::PaymentFlowData, - types::SetupMandateRequestData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::IncrementalAuthorization, - types::PaymentFlowData, - types::PaymentsIncrementalAuthorizationData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::CompleteAuthorize, - types::PaymentFlowData, - types::CompleteAuthorizeData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::PaymentMethodToken, - types::PaymentFlowData, - types::PaymentMethodTokenizationData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::CreateConnectorCustomer, - types::PaymentFlowData, - types::ConnectorCustomerData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::PreProcessing, - types::PaymentFlowData, - types::PaymentsPreProcessingData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::PostProcessing, - types::PaymentFlowData, - types::PaymentsPostProcessingData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - impl - services::ConnectorIntegrationV2< - api::CalculateTax, - types::PaymentFlowData, - types::PaymentsTaxCalculationData, - types::TaxCalculationResponseData, - > for connector::DummyConnector - { - } - impl - services::ConnectorIntegrationV2< - api::SdkSessionUpdate, - types::PaymentFlowData, - types::SdkPaymentsSessionUpdateData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - impl - services::ConnectorIntegrationV2< - api::PostSessionTokens, - types::PaymentFlowData, - types::PaymentsPostSessionTokensData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::AuthorizeSessionToken, - types::PaymentFlowData, - types::AuthorizeSessionTokenData, - types::PaymentsResponseData, - > for connector::DummyConnector - { - } - impl api::RefundV2 for connector::DummyConnector {} - impl api::RefundExecuteV2 for connector::DummyConnector {} - impl api::RefundSyncV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::Execute, - types::RefundFlowData, - types::RefundsData, - types::RefundsResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::RSync, - types::RefundFlowData, - types::RefundsData, - types::RefundsResponseData, - > for connector::DummyConnector - { - } - impl api::ConnectorAccessTokenV2 for connector::DummyConnector {} - impl - services::ConnectorIntegrationV2< - api::AccessTokenAuth, - types::AccessTokenFlowData, - types::AccessTokenRequestData, - types::AccessToken, - > for connector::DummyConnector - { - } - - impl api::DisputeV2 for connector::DummyConnector {} - - impl api::AcceptDisputeV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::Accept, - types::DisputesFlowData, - types::AcceptDisputeRequestData, - types::AcceptDisputeResponse, - > for connector::DummyConnector - { - } - - impl api::DefendDisputeV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::Defend, - types::DisputesFlowData, - types::DefendDisputeRequestData, - types::DefendDisputeResponse, - > for connector::DummyConnector - { - } - - impl api::SubmitEvidenceV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::Evidence, - types::DisputesFlowData, - types::SubmitEvidenceRequestData, - types::SubmitEvidenceResponse, - > for connector::DummyConnector - { - } - - impl api::FileUploadV2 for connector::DummyConnector {} - - impl api::UploadFileV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::Upload, - types::FilesFlowData, - types::UploadFileRequestData, - types::UploadFileResponse, - > for connector::DummyConnector - { - } - - impl api::RetrieveFileV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::Retrieve, - types::FilesFlowData, - types::RetrieveFileRequestData, - types::RetrieveFileResponse, - > for connector::DummyConnector - { - } - - impl api::PayoutsV2 for connector::DummyConnector {} - - #[cfg(feature = "payouts")] - impl api::PayoutCreateV2 for connector::DummyConnector {} - - #[cfg(feature = "payouts")] - impl - services::ConnectorIntegrationV2< - api::PoCreate, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "payouts")] - impl api::PayoutEligibilityV2 for connector::DummyConnector {} - #[cfg(feature = "payouts")] - impl - services::ConnectorIntegrationV2< - api::PoEligibility, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "payouts")] - impl api::PayoutFulfillV2 for connector::DummyConnector {} - #[cfg(feature = "payouts")] - impl - services::ConnectorIntegrationV2< - api::PoFulfill, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "payouts")] - impl api::PayoutCancelV2 for connector::DummyConnector {} - #[cfg(feature = "payouts")] - impl - services::ConnectorIntegrationV2< - api::PoCancel, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "payouts")] - impl api::PayoutQuoteV2 for connector::DummyConnector {} - #[cfg(feature = "payouts")] - impl - services::ConnectorIntegrationV2< - api::PoQuote, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "payouts")] - impl api::PayoutRecipientV2 for connector::DummyConnector {} - #[cfg(feature = "payouts")] - impl - services::ConnectorIntegrationV2< - api::PoRecipient, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "payouts")] - impl api::PayoutSyncV2 for connector::DummyConnector {} - #[cfg(feature = "payouts")] - impl - services::ConnectorIntegrationV2< - api::PoSync, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "payouts")] - impl api::PayoutRecipientAccountV2 for connector::DummyConnector {} - #[cfg(feature = "payouts")] - impl - services::ConnectorIntegrationV2< - api::PoRecipientAccount, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for connector::DummyConnector - { - } - - impl api::ConnectorVerifyWebhookSourceV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::VerifyWebhookSource, - types::WebhookSourceVerifyData, - types::VerifyWebhookSourceRequestData, - types::VerifyWebhookSourceResponseData, - > for connector::DummyConnector - { - } - - impl api::FraudCheckV2 for connector::DummyConnector {} - - #[cfg(feature = "frm")] - impl api::FraudCheckSaleV2 for connector::DummyConnector {} - #[cfg(feature = "frm")] - impl - services::ConnectorIntegrationV2< - api::Sale, - types::FrmFlowData, - frm_types::FraudCheckSaleData, - frm_types::FraudCheckResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "frm")] - impl api::FraudCheckCheckoutV2 for connector::DummyConnector {} - #[cfg(feature = "frm")] - impl - services::ConnectorIntegrationV2< - api::Checkout, - types::FrmFlowData, - frm_types::FraudCheckCheckoutData, - frm_types::FraudCheckResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "frm")] - impl api::FraudCheckTransactionV2 for connector::DummyConnector {} - #[cfg(feature = "frm")] - impl - services::ConnectorIntegrationV2< - api::Transaction, - types::FrmFlowData, - frm_types::FraudCheckTransactionData, - frm_types::FraudCheckResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "frm")] - impl api::FraudCheckFulfillmentV2 for connector::DummyConnector {} - #[cfg(feature = "frm")] - impl - services::ConnectorIntegrationV2< - api::Fulfillment, - types::FrmFlowData, - frm_types::FraudCheckFulfillmentData, - frm_types::FraudCheckResponseData, - > for connector::DummyConnector - { - } - - #[cfg(feature = "frm")] - impl api::FraudCheckRecordReturnV2 for connector::DummyConnector {} - #[cfg(feature = "frm")] - impl - services::ConnectorIntegrationV2< - api::RecordReturn, - types::FrmFlowData, - frm_types::FraudCheckRecordReturnData, - frm_types::FraudCheckResponseData, - > for connector::DummyConnector - { - } - - impl api::ConnectorMandateRevokeV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::MandateRevoke, - types::MandateRevokeFlowData, - types::MandateRevokeRequestData, - types::MandateRevokeResponseData, - > for connector::DummyConnector - { - } - - impl api::ExternalAuthenticationV2 for connector::DummyConnector {} - - impl api::ConnectorPreAuthenticationV2 for connector::DummyConnector {} - - impl api::ConnectorPreAuthenticationVersionCallV2 for connector::DummyConnector {} - - impl api::ConnectorAuthenticationV2 for connector::DummyConnector {} - - impl api::ConnectorPostAuthenticationV2 for connector::DummyConnector {} - - impl - services::ConnectorIntegrationV2< - api::Authentication, - types::ExternalAuthenticationFlowData, - types::authentication::ConnectorAuthenticationRequestData, - types::authentication::AuthenticationResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::PreAuthentication, - types::ExternalAuthenticationFlowData, - types::authentication::PreAuthNRequestData, - types::authentication::AuthenticationResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::PreAuthenticationVersionCall, - types::ExternalAuthenticationFlowData, - types::authentication::PreAuthNRequestData, - types::authentication::AuthenticationResponseData, - > for connector::DummyConnector - { - } - - impl - services::ConnectorIntegrationV2< - api::PostAuthentication, - types::ExternalAuthenticationFlowData, - types::authentication::ConnectorPostAuthenticationRequestData, - types::authentication::AuthenticationResponseData, - > for connector::DummyConnector - { - } -} - -macro_rules! default_imp_for_new_connector_integration_payment { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PaymentV2 for $path::$connector{} - impl api::PaymentAuthorizeV2 for $path::$connector{} - impl api::PaymentAuthorizeSessionTokenV2 for $path::$connector{} - impl api::PaymentSyncV2 for $path::$connector{} - impl api::PaymentVoidV2 for $path::$connector{} - impl api::PaymentApproveV2 for $path::$connector{} - impl api::PaymentRejectV2 for $path::$connector{} - impl api::PaymentCaptureV2 for $path::$connector{} - impl api::PaymentSessionV2 for $path::$connector{} - impl api::MandateSetupV2 for $path::$connector{} - impl api::PaymentIncrementalAuthorizationV2 for $path::$connector{} - impl api::PaymentsCompleteAuthorizeV2 for $path::$connector{} - impl api::PaymentTokenV2 for $path::$connector{} - impl api::ConnectorCustomerV2 for $path::$connector{} - impl api::PaymentsPreProcessingV2 for $path::$connector{} - impl api::PaymentsPostProcessingV2 for $path::$connector{} - impl api::TaxCalculationV2 for $path::$connector{} - impl api::PaymentSessionUpdateV2 for $path::$connector{} - impl api::PaymentPostSessionTokensV2 for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - impl - services::ConnectorIntegrationV2< - api::IncrementalAuthorization, - types::PaymentFlowData, - types::PaymentsIncrementalAuthorizationData, - types::PaymentsResponseData, - > - for $path::$connector{} - impl - services::ConnectorIntegrationV2< - api::CompleteAuthorize, - types::PaymentFlowData, - types::CompleteAuthorizeData, - types::PaymentsResponseData, - > for $path::$connector{} - impl - services::ConnectorIntegrationV2< - api::PaymentMethodToken, - types::PaymentFlowData, - types::PaymentMethodTokenizationData, - types::PaymentsResponseData, - > for $path::$connector{} - impl - services::ConnectorIntegrationV2< - api::CreateConnectorCustomer, - types::PaymentFlowData, - types::ConnectorCustomerData, - types::PaymentsResponseData, - > for $path::$connector{} - impl services::ConnectorIntegrationV2< - api::PreProcessing, - types::PaymentFlowData, - types::PaymentsPreProcessingData, - types::PaymentsResponseData, - > for $path::$connector{} - impl services::ConnectorIntegrationV2< - api::PostProcessing, - types::PaymentFlowData, - types::PaymentsPostProcessingData, - types::PaymentsResponseData, - > for $path::$connector{} - impl - services::ConnectorIntegrationV2< - api::AuthorizeSessionToken, - types::PaymentFlowData, - types::AuthorizeSessionTokenData, - types::PaymentsResponseData - > for $path::$connector{} - impl services::ConnectorIntegrationV2< - api::CalculateTax, - types::PaymentFlowData, - types::PaymentsTaxCalculationData, - types::TaxCalculationResponseData, - > for $path::$connector{} - - impl services::ConnectorIntegrationV2< - api::SdkSessionUpdate, - types::PaymentFlowData, - types::SdkPaymentsSessionUpdateData, - types::PaymentsResponseData, - > for $path::$connector{} - - impl services::ConnectorIntegrationV2< - api::PostSessionTokens, - types::PaymentFlowData, - types::PaymentsPostSessionTokensData, - types::PaymentsResponseData, - > for $path::$connector{} - )* - }; -} - -default_imp_for_new_connector_integration_payment!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wellsfargopayout, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_refund { - ($($path:ident::$connector:ident),*) => { - $( - impl api::RefundV2 for $path::$connector{} - impl api::RefundExecuteV2 for $path::$connector{} - impl api::RefundSyncV2 for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - )* - }; -} - -default_imp_for_new_connector_integration_refund!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_connector_access_token { - ($($path:ident::$connector:ident),*) => { - $( - impl api::ConnectorAccessTokenV2 for $path::$connector{} - impl - services::ConnectorIntegrationV2 - for $path::$connector{} - )* - }; -} - -default_imp_for_new_connector_integration_connector_access_token!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_accept_dispute { - ($($path:ident::$connector:ident),*) => { - $( - impl api::DisputeV2 for $path::$connector {} - impl api::AcceptDisputeV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Accept, - types::DisputesFlowData, - types::AcceptDisputeRequestData, - types::AcceptDisputeResponse, - > for $path::$connector - {} - )* - }; -} - -macro_rules! default_imp_for_new_connector_integration_submit_evidence { - ($($path:ident::$connector:ident),*) => { - $( - impl api::SubmitEvidenceV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Evidence, - types::DisputesFlowData, - types::SubmitEvidenceRequestData, - types::SubmitEvidenceResponse, - > for $path::$connector - {} - )* - }; -} - -default_imp_for_new_connector_integration_accept_dispute!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_defend_dispute { - ($($path:ident::$connector:ident),*) => { - $( - impl api::DefendDisputeV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Defend, - types::DisputesFlowData, - types::DefendDisputeRequestData, - types::DefendDisputeResponse, - > for $path::$connector - {} - )* - }; -} -default_imp_for_new_connector_integration_defend_dispute!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); -default_imp_for_new_connector_integration_submit_evidence!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_file_upload { - ($($path:ident::$connector:ident),*) => { - $( - impl api::FileUploadV2 for $path::$connector {} - impl api::UploadFileV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Upload, - types::FilesFlowData, - types::UploadFileRequestData, - types::UploadFileResponse, - > for $path::$connector - {} - impl api::RetrieveFileV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Retrieve, - types::FilesFlowData, - types::RetrieveFileRequestData, - types::RetrieveFileResponse, - > for $path::$connector - {} - )* - }; -} - -default_imp_for_new_connector_integration_file_upload!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_payouts { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PayoutsV2 for $path::$connector {} - )* - }; -} - -default_imp_for_new_connector_integration_payouts!( - connector::Aci, - connector::Adyen, - connector::Adyenplatform, - connector::Airwallex, - connector::Amazonpay, - connector::Authorizedotnet, - connector::Bankofamerica, - connector::Billwerk, - connector::Bitpay, - connector::Bluesnap, - connector::Boku, - connector::Braintree, - connector::Cashtocode, - connector::Chargebee, - connector::Checkout, - connector::Cryptopay, - connector::Coinbase, - connector::Cybersource, - connector::Deutschebank, - connector::Digitalvirgo, - connector::Dlocal, - connector::Ebanx, - connector::Elavon, - connector::Fiserv, - connector::Fiservemea, - connector::Fiuu, - connector::Forte, - connector::Getnet, - connector::Globalpay, - connector::Globepay, - connector::Gpayments, - connector::Hipay, - connector::Helcim, - connector::Iatapay, - connector::Inespay, - connector::Itaubank, - connector::Jpmorgan, - connector::Juspaythreedsserver, - connector::Klarna, - connector::Mifinity, - connector::Mollie, - connector::Moneris, - connector::Multisafepay, - connector::Netcetera, - connector::Nexinets, - connector::Nexixpay, - connector::Nmi, - connector::Nomupay, - connector::Noon, - connector::Novalnet, - connector::Opennode, - connector::Paybox, - connector::Payeezy, - connector::Payme, - connector::Payone, - connector::Paypal, - connector::Paystack, - connector::Payu, - connector::Powertranz, - connector::Rapyd, - connector::Razorpay, - connector::Recurly, - connector::Redsys, - connector::Riskified, - connector::Signifyd, - connector::Square, - connector::Stax, - connector::Stripe, - connector::Stripebilling, - connector::Shift4, - connector::Taxjar, - connector::Trustpay, - connector::Threedsecureio, - connector::Thunes, - connector::Tsys, - connector::UnifiedAuthenticationService, - connector::Volt, - connector::Wise, - connector::Worldline, - connector::Worldpay, - connector::Zen, - connector::Plaid, - connector::CtpMastercard -); - -#[cfg(feature = "payouts")] -macro_rules! default_imp_for_new_connector_integration_payouts_create { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PayoutCreateV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::PoCreate, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "payouts")] -default_imp_for_new_connector_integration_payouts_create!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "payouts")] -macro_rules! default_imp_for_new_connector_integration_payouts_eligibility { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PayoutEligibilityV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::PoEligibility, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "payouts")] -default_imp_for_new_connector_integration_payouts_eligibility!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "payouts")] -macro_rules! default_imp_for_new_connector_integration_payouts_fulfill { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PayoutFulfillV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::PoFulfill, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "payouts")] -default_imp_for_new_connector_integration_payouts_fulfill!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "payouts")] -macro_rules! default_imp_for_new_connector_integration_payouts_cancel { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PayoutCancelV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::PoCancel, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "payouts")] -default_imp_for_new_connector_integration_payouts_cancel!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "payouts")] -macro_rules! default_imp_for_new_connector_integration_payouts_quote { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PayoutQuoteV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::PoQuote, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "payouts")] -default_imp_for_new_connector_integration_payouts_quote!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "payouts")] -macro_rules! default_imp_for_new_connector_integration_payouts_recipient { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PayoutRecipientV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::PoRecipient, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "payouts")] -default_imp_for_new_connector_integration_payouts_recipient!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "payouts")] -macro_rules! default_imp_for_new_connector_integration_payouts_sync { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PayoutSyncV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::PoSync, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "payouts")] -default_imp_for_new_connector_integration_payouts_sync!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "payouts")] -macro_rules! default_imp_for_new_connector_integration_payouts_recipient_account { - ($($path:ident::$connector:ident),*) => { - $( - impl api::PayoutRecipientAccountV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::PoRecipientAccount, - types::PayoutFlowData, - types::PayoutsData, - types::PayoutsResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "payouts")] -default_imp_for_new_connector_integration_payouts_recipient_account!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_webhook_source_verification { - ($($path:ident::$connector:ident),*) => { - $( - impl api::ConnectorVerifyWebhookSourceV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::VerifyWebhookSource, - types::WebhookSourceVerifyData, - types::VerifyWebhookSourceRequestData, - types::VerifyWebhookSourceResponseData, - > for $path::$connector - {} - )* - }; -} - -default_imp_for_new_connector_integration_webhook_source_verification!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_frm { - ($($path:ident::$connector:ident),*) => { - $( - impl api::FraudCheckV2 for $path::$connector {} - )* - }; -} - -default_imp_for_new_connector_integration_frm!( - connector::Aci, - connector::Adyen, - connector::Adyenplatform, - connector::Airwallex, - connector::Amazonpay, - connector::Authorizedotnet, - connector::Bambora, - connector::Bamboraapac, - connector::Billwerk, - connector::Bitpay, - connector::Bluesnap, - connector::Boku, - connector::Braintree, - connector::Cashtocode, - connector::Chargebee, - connector::Checkout, - connector::Cryptopay, - connector::Coinbase, - connector::Deutschebank, - connector::Digitalvirgo, - connector::Dlocal, - connector::Ebanx, - connector::Elavon, - connector::Fiserv, - connector::Fiservemea, - connector::Forte, - connector::Fiuu, - connector::Getnet, - connector::Globalpay, - connector::Globepay, - connector::Gpayments, - connector::Hipay, - connector::Helcim, - connector::Iatapay, - connector::Inespay, - connector::Itaubank, - connector::Jpmorgan, - connector::Juspaythreedsserver, - connector::Klarna, - connector::Mifinity, - connector::Mollie, - connector::Moneris, - connector::Multisafepay, - connector::Netcetera, - connector::Nexinets, - connector::Nexixpay, - connector::Nmi, - connector::Nomupay, - connector::Noon, - connector::Novalnet, - connector::Opayo, - connector::Opennode, - connector::Paybox, - connector::Payeezy, - connector::Payme, - connector::Payone, - connector::Paypal, - connector::Paystack, - connector::Payu, - connector::Powertranz, - connector::Rapyd, - connector::Razorpay, - connector::Recurly, - connector::Redsys, - connector::Riskified, - connector::Signifyd, - connector::Square, - connector::Stax, - connector::Stripe, - connector::Stripebilling, - connector::Shift4, - connector::Taxjar, - connector::Trustpay, - connector::Threedsecureio, - connector::Thunes, - connector::Tsys, - connector::UnifiedAuthenticationService, - connector::Volt, - connector::Wise, - connector::Worldline, - connector::Worldpay, - connector::Zen, - connector::Plaid, - connector::CtpMastercard -); - -#[cfg(feature = "frm")] -macro_rules! default_imp_for_new_connector_integration_frm_sale { - ($($path:ident::$connector:ident),*) => { - $( - impl api::FraudCheckSaleV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Sale, - types::FrmFlowData, - frm_types::FraudCheckSaleData, - frm_types::FraudCheckResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "frm")] -default_imp_for_new_connector_integration_frm_sale!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "frm")] -macro_rules! default_imp_for_new_connector_integration_frm_checkout { - ($($path:ident::$connector:ident),*) => { - $( - impl api::FraudCheckCheckoutV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Checkout, - types::FrmFlowData, - frm_types::FraudCheckCheckoutData, - frm_types::FraudCheckResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "frm")] -default_imp_for_new_connector_integration_frm_checkout!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "frm")] -macro_rules! default_imp_for_new_connector_integration_frm_transaction { - ($($path:ident::$connector:ident),*) => { - $( - impl api::FraudCheckTransactionV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Transaction, - types::FrmFlowData, - frm_types::FraudCheckTransactionData, - frm_types::FraudCheckResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "frm")] -default_imp_for_new_connector_integration_frm_transaction!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "frm")] -macro_rules! default_imp_for_new_connector_integration_frm_fulfillment { - ($($path:ident::$connector:ident),*) => { - $( - impl api::FraudCheckFulfillmentV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Fulfillment, - types::FrmFlowData, - frm_types::FraudCheckFulfillmentData, - frm_types::FraudCheckResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "frm")] -default_imp_for_new_connector_integration_frm_fulfillment!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -#[cfg(feature = "frm")] -macro_rules! default_imp_for_new_connector_integration_frm_record_return { - ($($path:ident::$connector:ident),*) => { - $( - impl api::FraudCheckRecordReturnV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::RecordReturn, - types::FrmFlowData, - frm_types::FraudCheckRecordReturnData, - frm_types::FraudCheckResponseData, - > for $path::$connector - {} - )* - }; -} - -#[cfg(feature = "frm")] -default_imp_for_new_connector_integration_frm_record_return!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_revoking_mandates { - ($($path:ident::$connector:ident),*) => { - $( impl api::ConnectorMandateRevokeV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::MandateRevoke, - types::MandateRevokeFlowData, - types::MandateRevokeRequestData, - types::MandateRevokeResponseData, - > for $path::$connector - {} - )* - }; -} - -default_imp_for_new_connector_integration_revoking_mandates!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wise, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_connector_authentication { - ($($path:ident::$connector:ident),*) => { - $( impl api::ExternalAuthenticationV2 for $path::$connector {} - impl api::ConnectorAuthenticationV2 for $path::$connector {} - impl api::ConnectorPreAuthenticationV2 for $path::$connector {} - impl api::ConnectorPreAuthenticationVersionCallV2 for $path::$connector {} - impl api::ConnectorPostAuthenticationV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - api::Authentication, - types::ExternalAuthenticationFlowData, - types::authentication::ConnectorAuthenticationRequestData, - types::authentication::AuthenticationResponseData, - > for $path::$connector - {} - impl - services::ConnectorIntegrationV2< - api::PreAuthentication, - types::ExternalAuthenticationFlowData, - types::authentication::PreAuthNRequestData, - types::authentication::AuthenticationResponseData, - > for $path::$connector - {} - impl - services::ConnectorIntegrationV2< - api::PreAuthenticationVersionCall, - types::ExternalAuthenticationFlowData, - types::authentication::PreAuthNRequestData, - types::authentication::AuthenticationResponseData, - > for $path::$connector - {} - impl - services::ConnectorIntegrationV2< - api::PostAuthentication, - types::ExternalAuthenticationFlowData, - types::authentication::ConnectorPostAuthenticationRequestData, - types::authentication::AuthenticationResponseData, - > for $path::$connector - {} - )* - }; -} - -default_imp_for_new_connector_integration_connector_authentication!( - connector::Aci, - connector::Adyen, - connector::Adyenplatform, - connector::Authorizedotnet, - connector::Bambora, - connector::Bamboraapac, - connector::Bankofamerica, - connector::Billwerk, - connector::Bitpay, - connector::Bluesnap, - connector::Boku, - connector::Braintree, - connector::Cashtocode, - connector::Chargebee, - connector::Checkout, - connector::Cryptopay, - connector::Coinbase, - connector::Cybersource, - connector::Datatrans, - connector::Deutschebank, - connector::Digitalvirgo, - connector::Dlocal, - connector::Ebanx, - connector::Elavon, - connector::Fiserv, - connector::Fiservemea, - connector::Forte, - connector::Fiuu, - connector::Getnet, - connector::Globalpay, - connector::Globepay, - connector::Gocardless, - connector::Gpayments, - connector::Hipay, - connector::Helcim, - connector::Iatapay, - connector::Inespay, - connector::Itaubank, - connector::Jpmorgan, - connector::Juspaythreedsserver, - connector::Klarna, - connector::Mifinity, - connector::Mollie, - connector::Moneris, - connector::Multisafepay, - connector::Netcetera, - connector::Nexinets, - connector::Nexixpay, - connector::Nmi, - connector::Nomupay, - connector::Noon, - connector::Novalnet, - connector::Nuvei, - connector::Opayo, - connector::Opennode, - connector::Paybox, - connector::Payeezy, - connector::Payme, - connector::Payone, - connector::Paypal, - connector::Paystack, - connector::Payu, - connector::Placetopay, - connector::Powertranz, - connector::Prophetpay, - connector::Rapyd, - connector::Razorpay, - connector::Recurly, - connector::Redsys, - connector::Riskified, - connector::Signifyd, - connector::Square, - connector::Stax, - connector::Stripe, - connector::Stripebilling, - connector::Shift4, - connector::Taxjar, - connector::Trustpay, - connector::Threedsecureio, - connector::Thunes, - connector::Tsys, - connector::UnifiedAuthenticationService, - connector::Volt, - connector::Wise, - connector::Worldline, - connector::Worldpay, - connector::Xendit, - connector::Zen, - connector::Zsl, - connector::Plaid -); - -macro_rules! default_imp_for_new_connector_integration_uas { - ($($path:ident::$connector:ident),*) => { - $( impl UnifiedAuthenticationServiceV2 for $path::$connector {} - impl UasPreAuthenticationV2 for $path::$connector {} - impl UasPostAuthenticationV2 for $path::$connector {} - impl UasAuthenticationConfirmationV2 for $path::$connector {} - impl UasAuthenticationV2 for $path::$connector {} - impl - services::ConnectorIntegrationV2< - PreAuthenticate, - types::UasFlowData, - types::UasPreAuthenticationRequestData, - types::UasAuthenticationResponseData, - > for $path::$connector - {} - impl - services::ConnectorIntegrationV2< - PostAuthenticate, - types::UasFlowData, - types::UasPostAuthenticationRequestData, - types::UasAuthenticationResponseData, - > for $path::$connector - {} - impl - services::ConnectorIntegrationV2< - AuthenticationConfirmation, - types::UasFlowData, - types::UasConfirmationRequestData, - types::UasAuthenticationResponseData, - > for $path::$connector - {} - impl services::ConnectorIntegrationV2< - Authenticate, - types::UasFlowData, - types::UasAuthenticationRequestData, - types::UasAuthenticationResponseData, - > for $path::$connector - {} - )* - }; -} - -default_imp_for_new_connector_integration_uas!( - connector::Adyenplatform, - connector::Ebanx, - connector::Gpayments, - connector::Netcetera, - connector::Nmi, - connector::Payone, - connector::Plaid, - connector::Riskified, - connector::Signifyd, - connector::Stripe, - connector::Threedsecureio, - connector::Wellsfargopayout, - connector::Wise -); diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index abb6247762..3801240864 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -372,98 +372,18 @@ impl api::ConnectorTransactionId for connector::DummyConnector { default_imp_for_connector_request_id!( connector::Adyenplatform, - connector::Aci, - connector::Adyen, - connector::Airwallex, - connector::Amazonpay, - connector::Authorizedotnet, - connector::Bambora, - connector::Bamboraapac, - connector::Bankofamerica, - connector::Billwerk, - connector::Bitpay, - connector::Bluesnap, - connector::Boku, - connector::Braintree, - connector::Cashtocode, - connector::Chargebee, - connector::Checkout, - connector::Coinbase, - connector::Coingate, - connector::Cryptopay, - connector::Cybersource, - connector::Datatrans, - connector::Deutschebank, - connector::Digitalvirgo, - connector::Dlocal, connector::Ebanx, - connector::Elavon, - connector::Fiserv, - connector::Fiservemea, - connector::Fiuu, - connector::Forte, - connector::Getnet, - connector::Globalpay, - connector::Globepay, - connector::Gocardless, connector::Gpayments, - connector::Hipay, - connector::Iatapay, - connector::Inespay, - connector::Itaubank, - connector::Jpmorgan, - connector::Juspaythreedsserver, - connector::Klarna, - connector::Mifinity, - connector::Mollie, - connector::Moneris, - connector::Multisafepay, connector::Netcetera, - connector::Nexixpay, connector::Nmi, - connector::Nomupay, - connector::Noon, - connector::Novalnet, - connector::Nuvei, - connector::Opayo, - connector::Opennode, - connector::Paybox, - connector::Payeezy, - connector::Payme, connector::Payone, - connector::Paypal, - connector::Paystack, - connector::Payu, - connector::Placetopay, connector::Plaid, - connector::Powertranz, - connector::Prophetpay, - connector::Rapyd, - connector::Razorpay, - connector::Recurly, - connector::Redsys, connector::Riskified, - connector::Shift4, connector::Signifyd, - connector::Square, - connector::Stax, connector::Stripe, - connector::Stripebilling, - connector::Taxjar, connector::Threedsecureio, - connector::Trustpay, - connector::Tsys, - connector::UnifiedAuthenticationService, - connector::Volt, - connector::Wellsfargo, connector::Wellsfargopayout, - connector::Wise, - connector::Worldline, - connector::Worldpay, - connector::Xendit, - connector::Zen, - connector::Zsl, - connector::CtpMastercard + connector::Wise ); macro_rules! default_imp_for_accept_dispute { @@ -1191,6 +1111,7 @@ default_imp_for_reject!( connector::Wise ); +#[cfg(feature = "frm")] macro_rules! default_imp_for_fraud_check { ($($path:ident::$connector:ident),*) => { $( @@ -1202,100 +1123,19 @@ macro_rules! default_imp_for_fraud_check { #[cfg(feature = "dummy_connector")] impl api::FraudCheck for connector::DummyConnector {} +#[cfg(feature = "frm")] default_imp_for_fraud_check!( connector::Adyenplatform, - connector::Aci, - connector::Adyen, - connector::Airwallex, - connector::Amazonpay, - connector::Authorizedotnet, - connector::Bambora, - connector::Bamboraapac, - connector::Bankofamerica, - connector::Billwerk, - connector::Bitpay, - connector::Bluesnap, - connector::Boku, - connector::Braintree, - connector::Cashtocode, - connector::Chargebee, - connector::Checkout, - connector::Cryptopay, - connector::Cybersource, - connector::Coinbase, - connector::Coingate, - connector::Datatrans, - connector::Deutschebank, - connector::Digitalvirgo, - connector::Dlocal, connector::Ebanx, - connector::Elavon, - connector::Fiserv, - connector::Fiservemea, - connector::Fiuu, - connector::Forte, - connector::Getnet, - connector::Globalpay, - connector::Globepay, - connector::Gocardless, connector::Gpayments, - connector::Hipay, - connector::Helcim, - connector::Iatapay, - connector::Inespay, - connector::Itaubank, - connector::Jpmorgan, - connector::Juspaythreedsserver, - connector::Klarna, - connector::Mifinity, - connector::Mollie, - connector::Moneris, - connector::Multisafepay, - connector::Netcetera, - connector::Nexinets, - connector::Nexixpay, connector::Nmi, - connector::Nomupay, - connector::Noon, - connector::Novalnet, - connector::Nuvei, - connector::Opayo, - connector::Opennode, - connector::Paybox, - connector::Payeezy, - connector::Payme, + connector::Netcetera, connector::Payone, - connector::Paypal, - connector::Paystack, - connector::Payu, - connector::Placetopay, connector::Plaid, - connector::Powertranz, - connector::Prophetpay, - connector::Rapyd, - connector::Razorpay, - connector::Recurly, - connector::Redsys, - connector::Shift4, - connector::Square, - connector::Stax, connector::Stripe, - connector::Stripebilling, - connector::Taxjar, connector::Threedsecureio, - connector::Trustpay, - connector::Tsys, - connector::UnifiedAuthenticationService, - connector::Volt, - connector::Wellsfargo, connector::Wellsfargopayout, - connector::Wise, - connector::Worldline, - connector::Worldpay, - connector::Xendit, - connector::Zen, - connector::Zsl, - connector::CtpMastercard + connector::Wise ); #[cfg(feature = "frm")] @@ -1684,97 +1524,15 @@ impl } default_imp_for_connector_authentication!( connector::Adyenplatform, - connector::Aci, - connector::Adyen, - connector::Airwallex, - connector::Amazonpay, - connector::Authorizedotnet, - connector::Bambora, - connector::Bamboraapac, - connector::Bankofamerica, - connector::Billwerk, - connector::Bitpay, - connector::Bluesnap, - connector::Boku, - connector::Braintree, - connector::Cashtocode, - connector::Chargebee, - connector::Checkout, - connector::Cryptopay, - connector::Coinbase, - connector::Coingate, - connector::Cybersource, - connector::Datatrans, - connector::Deutschebank, - connector::Digitalvirgo, - connector::Dlocal, connector::Ebanx, - connector::Elavon, - connector::Fiserv, - connector::Fiservemea, - connector::Fiuu, - connector::Forte, - connector::Getnet, - connector::Globalpay, - connector::Globepay, - connector::Gocardless, - connector::Hipay, - connector::Helcim, - connector::Iatapay, - connector::Inespay, - connector::Itaubank, - connector::Jpmorgan, - connector::Juspaythreedsserver, - connector::Klarna, - connector::Mifinity, - connector::Mollie, - connector::Moneris, - connector::Multisafepay, - connector::Nexinets, - connector::Nexixpay, connector::Nmi, - connector::Nomupay, - connector::Noon, - connector::Novalnet, - connector::Nuvei, - connector::Opayo, - connector::Opennode, - connector::Paybox, - connector::Payeezy, - connector::Payme, connector::Payone, - connector::Paypal, - connector::Paystack, - connector::Payu, - connector::Placetopay, connector::Plaid, - connector::Powertranz, - connector::Prophetpay, - connector::Rapyd, - connector::Razorpay, - connector::Recurly, - connector::Redsys, connector::Riskified, - connector::Shift4, connector::Signifyd, - connector::Square, - connector::Stax, connector::Stripe, - connector::Stripebilling, - connector::Taxjar, - connector::Trustpay, - connector::Tsys, - connector::UnifiedAuthenticationService, - connector::Volt, - connector::Wellsfargo, connector::Wellsfargopayout, - connector::Wise, - connector::Worldline, - connector::Worldpay, - connector::Xendit, - connector::Zen, - connector::Zsl, - connector::CtpMastercard + connector::Wise ); macro_rules! default_imp_for_authorize_session_token { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index a0a66359e8..5b7ead3f4f 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -97,17 +97,6 @@ use crate::{ core::payment_methods::cards::create_encrypted_data, types::storage::CustomerUpdate::Update, }; -pub fn filter_mca_based_on_profile_and_connector_type( - merchant_connector_accounts: Vec, - profile_id: &id_type::ProfileId, - connector_type: ConnectorType, -) -> Vec { - merchant_connector_accounts - .into_iter() - .filter(|mca| &mca.profile_id == profile_id && mca.connector_type == connector_type) - .collect() -} - #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn create_or_update_address_for_payment_by_request( @@ -5431,9 +5420,8 @@ where .await .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?; - let profile_specific_merchant_connector_account_list = - filter_mca_based_on_profile_and_connector_type( - merchant_connector_account_list, + let profile_specific_merchant_connector_account_list = merchant_connector_account_list + .filter_based_on_profile_and_connector_type( profile_id, ConnectorType::PaymentProcessor, ); @@ -7349,11 +7337,11 @@ pub async fn validate_allowed_payment_method_types_request( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to fetch merchant connector account for given merchant id")?; - let filtered_connector_accounts = filter_mca_based_on_profile_and_connector_type( - all_connector_accounts, - profile_id, - ConnectorType::PaymentProcessor, - ); + let filtered_connector_accounts = all_connector_accounts + .filter_based_on_profile_and_connector_type( + profile_id, + ConnectorType::PaymentProcessor, + ); let supporting_payment_method_types: HashSet<_> = filtered_connector_accounts .iter() diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 59a4f47065..83a1b11918 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -351,7 +351,7 @@ where Ok((Box::new(self), None, None)) } - /// Returns `Vec` + /// Returns `SessionConnectorDatas` /// Steps carried out in this function /// Get all the `merchant_connector_accounts` which are not disabled /// Filter out connectors which have `invoke_sdk_client` enabled in `payment_method_types` @@ -388,11 +388,11 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("profile_id is not set in payment_intent")?; - let filtered_connector_accounts = helpers::filter_mca_based_on_profile_and_connector_type( - all_connector_accounts, - &profile_id, - common_enums::ConnectorType::PaymentProcessor, - ); + let filtered_connector_accounts = all_connector_accounts + .filter_based_on_profile_and_connector_type( + &profile_id, + common_enums::ConnectorType::PaymentProcessor, + ); let requested_payment_method_types = request.wallets.clone(); let mut connector_and_supporting_payment_method_type = Vec::new(); @@ -437,7 +437,11 @@ where is_invoke_sdk_client && is_sent_in_request }) .map(|payment_method_type| { - (connector_account, payment_method_type.payment_method_type) + ( + connector_account, + payment_method_type.payment_method_type, + parsed_payment_methods_enabled.payment_method, + ) }) .collect::>() }) @@ -445,10 +449,11 @@ where connector_and_supporting_payment_method_type.extend(res); }); - let mut session_connector_data = - Vec::with_capacity(connector_and_supporting_payment_method_type.len()); + let mut session_connector_data = api::SessionConnectorDatas::with_capacity( + connector_and_supporting_payment_method_type.len(), + ); - for (merchant_connector_account, payment_method_type) in + for (merchant_connector_account, payment_method_type, payment_method) in connector_and_supporting_payment_method_type { let connector_type = api::GetToken::from(payment_method_type); @@ -467,6 +472,7 @@ where payment_method_type, connector_data, merchant_connector_account.business_sub_label.clone(), + payment_method, ); session_connector_data.push(new_session_connector_data) } diff --git a/crates/router/src/core/payments/operations/payment_session_intent.rs b/crates/router/src/core/payments/operations/payment_session_intent.rs index b2ab23223d..79348e6e23 100644 --- a/crates/router/src/core/payments/operations/payment_session_intent.rs +++ b/crates/router/src/core/payments/operations/payment_session_intent.rs @@ -1,25 +1,29 @@ -use std::marker::PhantomData; +use std::{collections::HashMap, marker::PhantomData}; use api_models::payments::PaymentsSessionRequest; use async_trait::async_trait; -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, ext_traits::Encode}; use error_stack::ResultExt; +use hyperswitch_domain_models::customer; use router_env::{instrument, logger, tracing}; -use super::{BoxedOperation, Domain, GetTracker, Operation, ValidateRequest}; +use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; use crate::{ core::{ errors::{self, RouterResult, StorageErrorExt}, payments::{self, helpers, operations, operations::ValidateStatusForOperation}, }, - routes::SessionState, - types::{api, domain, storage::enums}, + routes::{app::ReqState, SessionState}, + types::{api, domain, storage, storage::enums}, utils::ext_traits::OptionExt, }; #[derive(Debug, Clone, Copy)] pub struct PaymentSessionIntent; +type PaymentSessionOperation<'b, F> = + BoxedOperation<'b, F, PaymentsSessionRequest, payments::PaymentIntentData>; + impl ValidateStatusForOperation for PaymentSessionIntent { /// Validate if the current operation can be performed on the current status of the payment intent fn validate_status_for_operation( @@ -64,6 +68,15 @@ impl Operation for &PaymentSe fn to_domain(&self) -> RouterResult<&(dyn Domain)> { Ok(*self) } + fn to_update_tracker( + &self, + ) -> RouterResult< + &(dyn UpdateTracker, PaymentsSessionRequest> + + Send + + Sync), + > { + Ok(*self) + } } impl Operation for PaymentSessionIntent { @@ -82,6 +95,15 @@ impl Operation for PaymentSes fn to_domain(&self) -> RouterResult<&dyn Domain> { Ok(self) } + fn to_update_tracker( + &self, + ) -> RouterResult< + &(dyn UpdateTracker, PaymentsSessionRequest> + + Send + + Sync), + > { + Ok(self) + } } type PaymentsCreateIntentOperation<'b, F> = @@ -134,8 +156,53 @@ impl GetTracker, Payme } } -impl - ValidateRequest> +#[async_trait] +impl UpdateTracker, PaymentsSessionRequest> + for PaymentSessionIntent +{ + #[instrument(skip_all)] + async fn update_trackers<'b>( + &'b self, + state: &'b SessionState, + _req_state: ReqState, + mut payment_data: payments::PaymentIntentData, + _customer: Option, + storage_scheme: enums::MerchantStorageScheme, + _updated_customer: Option, + key_store: &domain::MerchantKeyStore, + _frm_suggestion: Option, + _header_payload: hyperswitch_domain_models::payments::HeaderPayload, + ) -> RouterResult<( + PaymentSessionOperation<'b, F>, + payments::PaymentIntentData, + )> + where + F: 'b + Send, + { + let prerouting_algorithm = payment_data.payment_intent.prerouting_algorithm.clone(); + payment_data.payment_intent = match prerouting_algorithm { + Some(prerouting_algorithm) => state + .store + .update_payment_intent( + &state.into(), + payment_data.payment_intent, + storage::PaymentIntentUpdate::SessionIntentUpdate { + prerouting_algorithm, + updated_by: storage_scheme.to_string(), + }, + key_store, + storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?, + None => payment_data.payment_intent, + }; + + Ok((Box::new(self), payment_data)) + } +} + +impl ValidateRequest> for PaymentSessionIntent { #[instrument(skip_all)] @@ -209,7 +276,7 @@ impl Domain( &'a self, merchant_account: &domain::MerchantAccount, - _business_profile: &domain::Profile, + business_profile: &domain::Profile, state: &SessionState, payment_data: &mut payments::PaymentIntentData, merchant_key_store: &domain::MerchantKeyStore, @@ -225,8 +292,7 @@ impl Domain Domain Some(api::SessionConnectorData::new( + payment_method_type, + connector_data, + None, + payment_method, + )), + Err(err) => { + logger::error!(session_token_error=?err); + None + } + } + }, + ) + .collect(); + let session_token_routing_result = payments::perform_session_token_routing( + state.clone(), + business_profile, + merchant_key_store, + payment_data, + session_connector_data, + ) + .await?; + + let pre_routing = storage::PaymentRoutingInfo { + algorithm: None, + pre_routing_results: Some((|| { + let mut pre_routing_results: HashMap< + common_enums::PaymentMethodType, + storage::PreRoutingConnectorChoice, + > = HashMap::new(); + for (pm_type, routing_choice) in session_token_routing_result.routing_result { + let mut routable_choice_list = vec![]; + for choice in routing_choice { + let routable_choice = api::routing::RoutableConnectorChoice { + choice_kind: api::routing::RoutableChoiceKind::FullStruct, + connector: choice + .connector + .connector_name + .to_string() + .parse::() + .change_context(errors::ApiErrorResponse::InternalServerError)?, + merchant_connector_id: choice.connector.merchant_connector_id.clone(), + }; + routable_choice_list.push(routable_choice); + } + pre_routing_results.insert( + pm_type, + storage::PreRoutingConnectorChoice::Multiple(routable_choice_list), + ); + } + Ok::<_, error_stack::Report>(pre_routing_results) + })()?), + }; + + // Store the routing results in payment intent + payment_data.payment_intent.prerouting_algorithm = Some(pre_routing); Ok(api::ConnectorCallType::SessionMultiple( - session_connector_data, + session_token_routing_result.final_result, )) } diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index e566b69534..0173d36cc4 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -1,11 +1,10 @@ mod transformers; -use std::{ - collections::{hash_map, HashMap}, - hash::{Hash, Hasher}, - str::FromStr, - sync::Arc, -}; +#[cfg(all(feature = "v1", feature = "dynamic_routing"))] +use std::collections::hash_map; +#[cfg(all(feature = "v1", feature = "dynamic_routing"))] +use std::hash::{Hash, Hasher}; +use std::{collections::HashMap, str::FromStr, sync::Arc}; #[cfg(all(feature = "v1", feature = "dynamic_routing"))] use api_models::routing as api_routing; @@ -37,10 +36,9 @@ use kgraph_utils::{ types::CountryCurrencyFilter, }; use masking::PeekInterface; -use rand::{ - distributions::{self, Distribution}, - SeedableRng, -}; +use rand::distributions::{self, Distribution}; +#[cfg(all(feature = "v1", feature = "dynamic_routing"))] +use rand::SeedableRng; #[cfg(all(feature = "v1", feature = "dynamic_routing"))] use router_env::{instrument, tracing}; use rustc_hash::FxHashMap; @@ -52,7 +50,7 @@ use crate::core::admin; use crate::core::payouts; use crate::{ core::{ - errors, errors as oss_errors, payments as payments_oss, + errors, errors as oss_errors, routing::{self}, }, logger, @@ -72,6 +70,7 @@ pub enum CachedAlgorithm { Advanced(backend::VirInterpreterBackend), } +#[cfg(feature = "v1")] pub struct SessionFlowRoutingInput<'a> { pub state: &'a SessionState, pub country: Option, @@ -79,9 +78,18 @@ pub struct SessionFlowRoutingInput<'a> { pub merchant_account: &'a domain::MerchantAccount, pub payment_attempt: &'a oss_storage::PaymentAttempt, pub payment_intent: &'a oss_storage::PaymentIntent, - pub chosen: Vec, + pub chosen: api::SessionConnectorDatas, } +#[cfg(feature = "v2")] +pub struct SessionFlowRoutingInput<'a> { + pub country: Option, + pub payment_intent: &'a oss_storage::PaymentIntent, + pub chosen: api::SessionConnectorDatas, +} + +#[allow(dead_code)] +#[cfg(feature = "v1")] pub struct SessionRoutingPmTypeInput<'a> { state: &'a SessionState, key_store: &'a domain::MerchantKeyStore, @@ -92,6 +100,14 @@ pub struct SessionRoutingPmTypeInput<'a> { profile_id: &'a common_utils::id_type::ProfileId, } +#[cfg(feature = "v2")] +pub struct SessionRoutingPmTypeInput<'a> { + routing_algorithm: &'a MerchantAccountRoutingAlgorithm, + backend_input: dsl_inputs::BackendInput, + allowed_connectors: FxHashMap, + profile_id: &'a common_utils::id_type::ProfileId, +} + type RoutingResult = oss_errors::CustomResult; #[cfg(feature = "v1")] @@ -380,10 +396,7 @@ pub fn make_dsl_input( let metadata = payments_dsl_input .payment_intent - .metadata - .clone() - .map(|val| val.parse_value("routing_parameters")) - .transpose() + .parse_and_get_metadata("routing_parameters") .change_context(errors::RoutingError::MetadataParsingError) .attach_printable("Unable to parse routing_parameters from metadata of payment_intent") .unwrap_or(None); @@ -435,7 +448,7 @@ pub async fn perform_static_routing_v1( CachedAlgorithm::Priority(plist) => plist.clone(), - CachedAlgorithm::VolumeSplit(splits) => perform_volume_split(splits.to_vec(), None) + CachedAlgorithm::VolumeSplit(splits) => perform_volume_split(splits.to_vec()) .change_context(errors::RoutingError::ConnectorSelectionFailed)?, CachedAlgorithm::Advanced(interpreter) => { @@ -507,7 +520,7 @@ pub fn perform_straight_through_routing( routing_types::StraightThroughAlgorithm::Priority(conns) => (conns.clone(), true), routing_types::StraightThroughAlgorithm::VolumeSplit(splits) => ( - perform_volume_split(splits.to_vec(), None) + perform_volume_split(splits.to_vec()) .change_context(errors::RoutingError::ConnectorSelectionFailed) .attach_printable( "Volume Split connector selection error in straight through routing", @@ -543,7 +556,7 @@ fn execute_dsl_and_get_connector_v1( Ok(match routing_output { routing_types::RoutingAlgorithm::Priority(plist) => plist, - routing_types::RoutingAlgorithm::VolumeSplit(splits) => perform_volume_split(splits, None) + routing_types::RoutingAlgorithm::VolumeSplit(splits) => perform_volume_split(splits) .change_context(errors::RoutingError::DslFinalConnectorSelectionFailed)?, _ => Err(errors::RoutingError::DslIncorrectSelectionAlgorithm) @@ -632,24 +645,14 @@ pub fn perform_dynamic_routing_volume_split( pub fn perform_volume_split( mut splits: Vec, - rng_seed: Option<&str>, ) -> RoutingResult> { let weights: Vec = splits.iter().map(|sp| sp.split).collect(); let weighted_index = distributions::WeightedIndex::new(weights) .change_context(errors::RoutingError::VolumeSplitFailed) .attach_printable("Error creating weighted distribution for volume split")?; - let idx = if let Some(seed) = rng_seed { - let mut hasher = hash_map::DefaultHasher::new(); - seed.hash(&mut hasher); - let hash = hasher.finish(); - - let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(hash); - weighted_index.sample(&mut rng) - } else { - let mut rng = rand::thread_rng(); - weighted_index.sample(&mut rng) - }; + let mut rng = rand::thread_rng(); + let idx = weighted_index.sample(&mut rng); splits .get(idx) @@ -664,7 +667,7 @@ pub fn perform_volume_split( Ok(splits.into_iter().map(|sp| sp.connector).collect()) } -#[cfg(feature = "v1")] +// #[cfg(feature = "v1")] pub async fn get_merchant_cgraph( state: &SessionState, key_store: &domain::MerchantKeyStore, @@ -711,7 +714,7 @@ pub async fn get_merchant_cgraph( Ok(cgraph) } -#[cfg(feature = "v1")] +// #[cfg(feature = "v1")] pub async fn refresh_cgraph_cache( state: &SessionState, key_store: &domain::MerchantKeyStore, @@ -752,12 +755,8 @@ pub async fn refresh_cgraph_cache( api_enums::TransactionType::Payout => common_enums::ConnectorType::PayoutProcessor, }; - let merchant_connector_accounts = - payments_oss::helpers::filter_mca_based_on_profile_and_connector_type( - merchant_connector_accounts, - profile_id, - connector_type, - ); + let merchant_connector_accounts = merchant_connector_accounts + .filter_based_on_profile_and_connector_type(profile_id, connector_type); let api_mcas = merchant_connector_accounts .into_iter() @@ -808,21 +807,6 @@ pub async fn refresh_cgraph_cache( Ok(cgraph) } -#[cfg(feature = "v2")] -#[allow(clippy::too_many_arguments)] -pub async fn perform_cgraph_filtering( - state: &SessionState, - key_store: &domain::MerchantKeyStore, - chosen: Vec, - backend_input: dsl_inputs::BackendInput, - eligible_connectors: Option<&Vec>, - profile_id: &common_utils::id_type::ProfileId, - transaction_type: &api_enums::TransactionType, -) -> RoutingResult> { - todo!() -} - -#[cfg(feature = "v1")] #[allow(clippy::too_many_arguments)] pub async fn perform_cgraph_filtering( state: &SessionState, @@ -991,101 +975,52 @@ pub async fn perform_eligibility_analysis_with_fallback( } #[cfg(feature = "v2")] -pub async fn perform_session_flow_routing( - session_input: SessionFlowRoutingInput<'_>, - transaction_type: &api_enums::TransactionType, -) -> RoutingResult>> -{ - todo!() -} - -#[cfg(feature = "v1")] -pub async fn perform_session_flow_routing( +pub async fn perform_session_flow_routing<'a>( + state: &'a SessionState, + key_store: &'a domain::MerchantKeyStore, session_input: SessionFlowRoutingInput<'_>, + business_profile: &domain::Profile, transaction_type: &api_enums::TransactionType, ) -> RoutingResult>> { let mut pm_type_map: FxHashMap> = FxHashMap::default(); - #[cfg(feature = "v1")] - let profile_id = session_input - .payment_intent - .profile_id - .clone() - .get_required_value("profile_id") - .change_context(errors::RoutingError::ProfileIdMissing)?; + let profile_id = business_profile.get_id().clone(); - #[cfg(feature = "v2")] - let profile_id = session_input.payment_intent.profile_id.clone(); - - let business_profile = session_input - .state - .store - .find_business_profile_by_profile_id( - &session_input.state.into(), - session_input.key_store, - &profile_id, - ) - .await - .change_context(errors::RoutingError::ProfileNotFound)?; - #[cfg(feature = "v2")] let routing_algorithm = MerchantAccountRoutingAlgorithm::V1(business_profile.routing_algorithm_id.clone()); - #[cfg(feature = "v1")] - let routing_algorithm: MerchantAccountRoutingAlgorithm = { - business_profile - .routing_algorithm - .clone() - .map(|val| val.parse_value("MerchantAccountRoutingAlgorithm")) - .transpose() - .change_context(errors::RoutingError::InvalidRoutingAlgorithmStructure)? - .unwrap_or_default() - }; - let payment_method_input = dsl_inputs::PaymentMethodInput { payment_method: None, payment_method_type: None, card_network: None, }; - #[cfg(feature = "v1")] let payment_input = dsl_inputs::PaymentInput { - amount: session_input.payment_attempt.get_total_amount(), - currency: session_input + amount: session_input .payment_intent - .currency - .get_required_value("Currency") - .change_context(errors::RoutingError::DslMissingRequiredField { - field_name: "currency".to_string(), - })?, - authentication_type: session_input.payment_attempt.authentication_type, + .amount_details + .calculate_net_amount(), + currency: session_input.payment_intent.amount_details.currency, + authentication_type: session_input.payment_intent.authentication_type, card_bin: None, - capture_method: session_input - .payment_attempt - .capture_method - .and_then(|cm| cm.foreign_into()), - business_country: session_input - .payment_intent - .business_country - .map(api_enums::Country::from_alpha2), + capture_method: Option::::foreign_from( + session_input.payment_intent.capture_method, + ), + // business_country not available in payment_intent anymore + business_country: None, billing_country: session_input .country .map(storage_enums::Country::from_alpha2), - business_label: session_input.payment_intent.business_label.clone(), - setup_future_usage: session_input.payment_intent.setup_future_usage, + // business_label not available in payment_intent anymore + business_label: None, + setup_future_usage: Some(session_input.payment_intent.setup_future_usage), }; - #[cfg(feature = "v2")] - let payment_input = todo!(); - let metadata = session_input .payment_intent - .metadata - .clone() - .map(|val| val.parse_value("routing_parameters")) - .transpose() + .parse_and_get_metadata("routing_parameters") .change_context(errors::RoutingError::MetadataParsingError) .attach_printable("Unable to parse routing_parameters from metadata of payment_intent") .unwrap_or(None); @@ -1103,7 +1038,151 @@ pub async fn perform_session_flow_routing( for connector_data in session_input.chosen.iter() { pm_type_map - .entry(connector_data.payment_method_type) + .entry(connector_data.payment_method_sub_type) + .or_default() + .insert( + connector_data.connector.connector_name.to_string(), + connector_data.connector.get_token.clone(), + ); + } + + let mut result: FxHashMap< + api_enums::PaymentMethodType, + Vec, + > = FxHashMap::default(); + + for (pm_type, allowed_connectors) in pm_type_map { + let euclid_pmt: euclid_enums::PaymentMethodType = pm_type; + let euclid_pm: euclid_enums::PaymentMethod = euclid_pmt.into(); + + backend_input.payment_method.payment_method = Some(euclid_pm); + backend_input.payment_method.payment_method_type = Some(euclid_pmt); + + let session_pm_input = SessionRoutingPmTypeInput { + routing_algorithm: &routing_algorithm, + backend_input: backend_input.clone(), + allowed_connectors, + profile_id: &profile_id, + }; + + let routable_connector_choice_option = perform_session_routing_for_pm_type( + state, + key_store, + &session_pm_input, + transaction_type, + business_profile, + ) + .await?; + + if let Some(routable_connector_choice) = routable_connector_choice_option { + let mut session_routing_choice: Vec = Vec::new(); + + for selection in routable_connector_choice { + let connector_name = selection.connector.to_string(); + if let Some(get_token) = session_pm_input.allowed_connectors.get(&connector_name) { + let connector_data = api::ConnectorData::get_connector_by_name( + &state.clone().conf.connectors, + &connector_name, + get_token.clone(), + selection.merchant_connector_id, + ) + .change_context(errors::RoutingError::InvalidConnectorName(connector_name))?; + + session_routing_choice.push(routing_types::SessionRoutingChoice { + connector: connector_data, + payment_method_type: pm_type, + }); + } + } + if !session_routing_choice.is_empty() { + result.insert(pm_type, session_routing_choice); + } + } + } + + Ok(result) +} + +#[cfg(feature = "v1")] +pub async fn perform_session_flow_routing( + session_input: SessionFlowRoutingInput<'_>, + business_profile: &domain::Profile, + transaction_type: &api_enums::TransactionType, +) -> RoutingResult>> +{ + let mut pm_type_map: FxHashMap> = + FxHashMap::default(); + + let profile_id = session_input + .payment_intent + .profile_id + .clone() + .get_required_value("profile_id") + .change_context(errors::RoutingError::ProfileIdMissing)?; + + let routing_algorithm: MerchantAccountRoutingAlgorithm = { + business_profile + .routing_algorithm + .clone() + .map(|val| val.parse_value("MerchantAccountRoutingAlgorithm")) + .transpose() + .change_context(errors::RoutingError::InvalidRoutingAlgorithmStructure)? + .unwrap_or_default() + }; + + let payment_method_input = dsl_inputs::PaymentMethodInput { + payment_method: None, + payment_method_type: None, + card_network: None, + }; + + let payment_input = dsl_inputs::PaymentInput { + amount: session_input.payment_attempt.get_total_amount(), + currency: session_input + .payment_intent + .currency + .get_required_value("Currency") + .change_context(errors::RoutingError::DslMissingRequiredField { + field_name: "currency".to_string(), + })?, + authentication_type: session_input.payment_attempt.authentication_type, + card_bin: None, + capture_method: session_input + .payment_attempt + .capture_method + .and_then(Option::::foreign_from), + business_country: session_input + .payment_intent + .business_country + .map(api_enums::Country::from_alpha2), + billing_country: session_input + .country + .map(storage_enums::Country::from_alpha2), + business_label: session_input.payment_intent.business_label.clone(), + setup_future_usage: session_input.payment_intent.setup_future_usage, + }; + + let metadata = session_input + .payment_intent + .parse_and_get_metadata("routing_parameters") + .change_context(errors::RoutingError::MetadataParsingError) + .attach_printable("Unable to parse routing_parameters from metadata of payment_intent") + .unwrap_or(None); + + let mut backend_input = dsl_inputs::BackendInput { + metadata, + payment: payment_input, + payment_method: payment_method_input, + mandate: dsl_inputs::MandateData { + mandate_acceptance_type: None, + mandate_type: None, + payment_type: None, + }, + }; + + for connector_data in session_input.chosen.iter() { + pm_type_map + .entry(connector_data.payment_method_sub_type) .or_default() .insert( connector_data.connector.connector_name.to_string(), @@ -1136,7 +1215,7 @@ pub async fn perform_session_flow_routing( let routable_connector_choice_option = perform_session_routing_for_pm_type( &session_pm_input, transaction_type, - &business_profile, + business_profile, ) .await?; @@ -1194,10 +1273,8 @@ async fn perform_session_routing_for_pm_type( match cached_algorithm.as_ref() { CachedAlgorithm::Single(conn) => vec![(**conn).clone()], CachedAlgorithm::Priority(plist) => plist.clone(), - CachedAlgorithm::VolumeSplit(splits) => { - perform_volume_split(splits.to_vec(), Some(session_pm_input.attempt_id)) - .change_context(errors::RoutingError::ConnectorSelectionFailed)? - } + CachedAlgorithm::VolumeSplit(splits) => perform_volume_split(splits.to_vec()) + .change_context(errors::RoutingError::ConnectorSelectionFailed)?, CachedAlgorithm::Advanced(interpreter) => execute_dsl_and_get_connector_v1( session_pm_input.backend_input.clone(), interpreter, @@ -1252,78 +1329,98 @@ async fn perform_session_routing_for_pm_type( } } -// async fn perform_session_routing_for_pm_type( -// session_pm_input: &SessionRoutingPmTypeInput<'_>, -// transaction_type: &api_enums::TransactionType, -// business_profile: &domain::Profile, -// ) -> RoutingResult>> { -// let merchant_id = &session_pm_input.key_store.merchant_id; +#[cfg(feature = "v2")] +async fn get_chosen_connectors<'a>( + state: &'a SessionState, + key_store: &'a domain::MerchantKeyStore, + session_pm_input: &SessionRoutingPmTypeInput<'_>, + transaction_type: &api_enums::TransactionType, + profile_wrapper: &admin::ProfileWrapper, +) -> RoutingResult> { + let merchant_id = &key_store.merchant_id; -// let MerchantAccountRoutingAlgorithm::V1(algorithm_id) = session_pm_input.routing_algorithm; + let MerchantAccountRoutingAlgorithm::V1(algorithm_id) = session_pm_input.routing_algorithm; -// let profile_wrapper = admin::ProfileWrapper::new(business_profile.clone()); -// let chosen_connectors = if let Some(ref algorithm_id) = algorithm_id { -// let cached_algorithm = ensure_algorithm_cached_v1( -// &session_pm_input.state.clone(), -// merchant_id, -// algorithm_id, -// session_pm_input.profile_id, -// transaction_type, -// ) -// .await?; + let chosen_connectors = if let Some(ref algorithm_id) = algorithm_id { + let cached_algorithm = ensure_algorithm_cached_v1( + state, + merchant_id, + algorithm_id, + session_pm_input.profile_id, + transaction_type, + ) + .await?; -// match cached_algorithm.as_ref() { -// CachedAlgorithm::Single(conn) => vec![(**conn).clone()], -// CachedAlgorithm::Priority(plist) => plist.clone(), -// CachedAlgorithm::VolumeSplit(splits) => { -// perform_volume_split(splits.to_vec(), Some(session_pm_input.attempt_id)) -// .change_context(errors::RoutingError::ConnectorSelectionFailed)? -// } -// CachedAlgorithm::Advanced(interpreter) => execute_dsl_and_get_connector_v1( -// session_pm_input.backend_input.clone(), -// interpreter, -// )?, -// } -// } else { -// profile_wrapper -// .get_default_fallback_list_of_connector_under_profile() -// .change_context(errors::RoutingError::FallbackConfigFetchFailed)? -// }; + match cached_algorithm.as_ref() { + CachedAlgorithm::Single(conn) => vec![(**conn).clone()], + CachedAlgorithm::Priority(plist) => plist.clone(), + CachedAlgorithm::VolumeSplit(splits) => perform_volume_split(splits.to_vec()) + .change_context(errors::RoutingError::ConnectorSelectionFailed)?, + CachedAlgorithm::Advanced(interpreter) => execute_dsl_and_get_connector_v1( + session_pm_input.backend_input.clone(), + interpreter, + )?, + } + } else { + profile_wrapper + .get_default_fallback_list_of_connector_under_profile() + .change_context(errors::RoutingError::FallbackConfigFetchFailed)? + }; + Ok(chosen_connectors) +} -// let mut final_selection = perform_cgraph_filtering( -// &session_pm_input.state.clone(), -// session_pm_input.key_store, -// chosen_connectors, -// session_pm_input.backend_input.clone(), -// None, -// session_pm_input.profile_id, -// transaction_type, -// ) -// .await?; +#[cfg(feature = "v2")] +async fn perform_session_routing_for_pm_type<'a>( + state: &'a SessionState, + key_store: &'a domain::MerchantKeyStore, + session_pm_input: &SessionRoutingPmTypeInput<'_>, + transaction_type: &api_enums::TransactionType, + business_profile: &domain::Profile, +) -> RoutingResult>> { + let profile_wrapper = admin::ProfileWrapper::new(business_profile.clone()); + let chosen_connectors = get_chosen_connectors( + state, + key_store, + session_pm_input, + transaction_type, + &profile_wrapper, + ) + .await?; -// if final_selection.is_empty() { -// let fallback = profile_wrapper -// .get_default_fallback_list_of_connector_under_profile() -// .change_context(errors::RoutingError::FallbackConfigFetchFailed)?; + let mut final_selection = perform_cgraph_filtering( + state, + key_store, + chosen_connectors, + session_pm_input.backend_input.clone(), + None, + session_pm_input.profile_id, + transaction_type, + ) + .await?; -// final_selection = perform_cgraph_filtering( -// &session_pm_input.state.clone(), -// session_pm_input.key_store, -// fallback, -// session_pm_input.backend_input.clone(), -// None, -// session_pm_input.profile_id, -// transaction_type, -// ) -// .await?; -// } + if final_selection.is_empty() { + let fallback = profile_wrapper + .get_default_fallback_list_of_connector_under_profile() + .change_context(errors::RoutingError::FallbackConfigFetchFailed)?; -// if final_selection.is_empty() { -// Ok(None) -// } else { -// Ok(Some(final_selection)) -// } -// } + final_selection = perform_cgraph_filtering( + state, + key_store, + fallback, + session_pm_input.backend_input.clone(), + None, + session_pm_input.profile_id, + transaction_type, + ) + .await?; + } + + if final_selection.is_empty() { + Ok(None) + } else { + Ok(Some(final_selection)) + } +} #[cfg(feature = "v2")] pub fn make_dsl_input_for_surcharge( _payment_attempt: &oss_storage::PaymentAttempt, @@ -1369,10 +1466,7 @@ pub fn make_dsl_input_for_surcharge( }; let metadata = payment_intent - .metadata - .clone() - .map(|val| val.parse_value("routing_parameters")) - .transpose() + .parse_and_get_metadata("routing_parameters") .change_context(errors::RoutingError::MetadataParsingError) .attach_printable("Unable to parse routing_parameters from metadata of payment_intent") .unwrap_or(None); diff --git a/crates/router/src/core/payments/session_operation.rs b/crates/router/src/core/payments/session_operation.rs index 40a2717fea..8974f24c00 100644 --- a/crates/router/src/core/payments/session_operation.rs +++ b/crates/router/src/core/payments/session_operation.rs @@ -93,7 +93,7 @@ where #[instrument(skip_all, fields(payment_id, merchant_id))] pub async fn payments_session_operation_core( state: &SessionState, - _req_state: ReqState, + req_state: ReqState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, profile: domain::Profile, @@ -169,6 +169,20 @@ where api::ConnectorCallType::Retryable(_connectors) => todo!(), api::ConnectorCallType::Skip => todo!(), api::ConnectorCallType::SessionMultiple(connectors) => { + operation + .to_update_tracker()? + .update_trackers( + state, + req_state, + payment_data.clone(), + customer.clone(), + merchant_account.storage_scheme, + None, + &key_store, + None, + header_payload.clone(), + ) + .await?; // todo: call surcharge manager for session token call. Box::pin(call_multiple_connectors_service( state, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 5e8b1bc105..ca01ed2238 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -29,7 +29,6 @@ use router_env::{instrument, tracing}; use super::{flows::Feature, types::AuthenticationData, OperationSessionGetters, PaymentData}; use crate::{ configs::settings::ConnectorRequestReferenceIdConfig, - connector::{Helcim, Nexinets}, core::{ errors::{self, RouterResponse, RouterResult}, payments::{self, helpers}, @@ -3378,72 +3377,6 @@ impl TryFrom> } } -impl ConnectorTransactionId for Helcim { - #[cfg(feature = "v1")] - fn connector_transaction_id( - &self, - payment_attempt: storage::PaymentAttempt, - ) -> Result, errors::ApiErrorResponse> { - if payment_attempt.get_connector_payment_id().is_none() { - let metadata = - Self::connector_transaction_id(self, payment_attempt.connector_metadata.as_ref()); - metadata.map_err(|_| errors::ApiErrorResponse::ResourceIdNotFound) - } else { - Ok(payment_attempt - .get_connector_payment_id() - .map(ToString::to_string)) - } - } - - #[cfg(feature = "v2")] - fn connector_transaction_id( - &self, - payment_attempt: storage::PaymentAttempt, - ) -> Result, errors::ApiErrorResponse> { - if payment_attempt.get_connector_payment_id().is_none() { - let metadata = Self::connector_transaction_id( - self, - payment_attempt - .connector_metadata - .as_ref() - .map(|connector_metadata| connector_metadata.peek()), - ); - metadata.map_err(|_| errors::ApiErrorResponse::ResourceIdNotFound) - } else { - Ok(payment_attempt - .get_connector_payment_id() - .map(ToString::to_string)) - } - } -} - -impl ConnectorTransactionId for Nexinets { - #[cfg(feature = "v1")] - fn connector_transaction_id( - &self, - payment_attempt: storage::PaymentAttempt, - ) -> Result, errors::ApiErrorResponse> { - let metadata = - Self::connector_transaction_id(self, payment_attempt.connector_metadata.as_ref()); - metadata.map_err(|_| errors::ApiErrorResponse::ResourceIdNotFound) - } - - #[cfg(feature = "v2")] - fn connector_transaction_id( - &self, - payment_attempt: storage::PaymentAttempt, - ) -> Result, errors::ApiErrorResponse> { - let metadata = Self::connector_transaction_id( - self, - payment_attempt - .connector_metadata - .as_ref() - .map(|connector_metadata| connector_metadata.peek()), - ); - metadata.map_err(|_| errors::ApiErrorResponse::ResourceIdNotFound) - } -} - #[cfg(feature = "v2")] impl TryFrom> for types::PaymentsCaptureData { type Error = error_stack::Report; diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index e45b4fcead..f0ebb44ecd 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -17,10 +17,7 @@ use hyperswitch_domain_models::api::{GenericLinks, GenericLinksData}; use super::errors::{RouterResponse, StorageErrorExt}; use crate::{ configs::settings::{PaymentMethodFilterKey, PaymentMethodFilters}, - core::{ - payments::helpers as payment_helpers, - payouts::{helpers as payout_helpers, validator}, - }, + core::payouts::{helpers as payout_helpers, validator}, errors, routes::{app::StorageInterface, SessionState}, services, @@ -351,8 +348,7 @@ pub async fn filter_payout_methods( .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; // Filter MCAs based on profile_id and connector_type - let filtered_mcas = payment_helpers::filter_mca_based_on_profile_and_connector_type( - all_mcas, + let filtered_mcas = all_mcas.filter_based_on_profile_and_connector_type( &payout.profile_id, common_enums::ConnectorType::PayoutProcessor, ); diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 1a595cc733..34ab9829e0 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -188,15 +188,20 @@ pub async fn create_routing_algorithm_under_profile( ) .await? .get_required_value("Profile")?; - + let merchant_id = merchant_account.get_id(); core_utils::validate_profile_id_from_auth_layer(authentication_profile_id, &business_profile)?; - - let all_mcas = helpers::MerchantConnectorAccounts::get_all_mcas( - merchant_account.get_id(), - &key_store, - &state, - ) - .await?; + let all_mcas = state + .store + .find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, + merchant_id, + true, + &key_store, + ) + .await + .change_context(errors::ApiErrorResponse::MerchantConnectorAccountNotFound { + id: merchant_id.get_string_repr().to_owned(), + })?; let name_mca_id_set = helpers::ConnectNameAndMCAIdForProfile( all_mcas.filter_by_profile(business_profile.get_id(), |mca| { diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index e80ddbead4..ec86102eba 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -305,62 +305,6 @@ pub struct ConnectNameAndMCAIdForProfile<'a>( #[derive(Clone, Debug)] pub struct ConnectNameForProfile<'a>(pub FxHashSet<&'a common_enums::connector_enums::Connector>); -#[cfg(feature = "v2")] -#[derive(Clone, Debug)] -pub struct MerchantConnectorAccounts(pub Vec); - -#[cfg(feature = "v2")] -impl MerchantConnectorAccounts { - pub async fn get_all_mcas( - merchant_id: &id_type::MerchantId, - key_store: &domain::MerchantKeyStore, - state: &SessionState, - ) -> RouterResult { - let db = &*state.store; - let key_manager_state = &state.into(); - Ok(Self( - db.find_merchant_connector_account_by_merchant_id_and_disabled_list( - key_manager_state, - merchant_id, - true, - key_store, - ) - .await - .change_context( - errors::ApiErrorResponse::MerchantConnectorAccountNotFound { - id: merchant_id.get_string_repr().to_owned(), - }, - )?, - )) - } - - fn filter_and_map<'a, T>( - &'a self, - filter: impl Fn(&'a MerchantConnectorAccount) -> bool, - func: impl Fn(&'a MerchantConnectorAccount) -> T, - ) -> FxHashSet - where - T: std::hash::Hash + Eq, - { - self.0 - .iter() - .filter(|mca| filter(mca)) - .map(func) - .collect::>() - } - - pub fn filter_by_profile<'a, T>( - &'a self, - profile_id: &'a id_type::ProfileId, - func: impl Fn(&'a MerchantConnectorAccount) -> T, - ) -> FxHashSet - where - T: std::hash::Hash + Eq, - { - self.filter_and_map(|mca| mca.profile_id == *profile_id, func) - } -} - #[cfg(feature = "v2")] impl RoutingAlgorithmHelpers<'_> { fn connector_choice( @@ -452,7 +396,7 @@ pub async fn validate_connectors_in_routing_config( profile_id: &id_type::ProfileId, routing_algorithm: &routing_types::RoutingAlgorithm, ) -> RouterResult<()> { - let all_mcas = &*state + let all_mcas = state .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( &state.into(), diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 3a7dceb21c..87dce4ade0 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -1786,10 +1786,13 @@ impl GetProfileId for (storage::Payouts, T, F, R) { } /// Filter Objects based on profile ids -pub(super) fn filter_objects_based_on_profile_id_list( +pub(super) fn filter_objects_based_on_profile_id_list< + T: GetProfileId, + U: IntoIterator + FromIterator, +>( profile_id_list_auth_layer: Option>, - object_list: Vec, -) -> Vec { + object_list: U, +) -> U { if let Some(profile_id_list) = profile_id_list_auth_layer { let profile_ids_to_filter: HashSet<_> = profile_id_list.iter().collect(); object_list diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index c9744b131c..60859c6f08 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1332,7 +1332,7 @@ impl MerchantConnectorAccountInterface for KafkaStore { merchant_id: &id_type::MerchantId, get_disabled: bool, key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult { self.diesel_store .find_merchant_connector_account_by_merchant_id_and_disabled_list( state, diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index 1175aa43c5..92683beb28 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -179,7 +179,7 @@ where merchant_id: &common_utils::id_type::MerchantId, get_disabled: bool, key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError>; + ) -> CustomResult; #[cfg(all(feature = "olap", feature = "v2"))] async fn list_connector_account_by_profile_id( @@ -544,9 +544,14 @@ impl MerchantConnectorAccountInterface for Store { merchant_id: &common_utils::id_type::MerchantId, get_disabled: bool, key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; - storage::MerchantConnectorAccount::find_by_merchant_id(&conn, merchant_id, get_disabled) + let merchant_connector_account_vec = + storage::MerchantConnectorAccount::find_by_merchant_id( + &conn, + merchant_id, + get_disabled, + ) .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|items| async { @@ -564,7 +569,10 @@ impl MerchantConnectorAccountInterface for Store { } Ok(output) }) - .await + .await?; + Ok(domain::MerchantConnectorAccounts::new( + merchant_connector_account_vec, + )) } #[instrument(skip_all)] @@ -1314,7 +1322,7 @@ impl MerchantConnectorAccountInterface for MockDb { merchant_id: &common_utils::id_type::MerchantId, get_disabled: bool, key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult { let accounts = self .merchant_connector_accounts .lock() @@ -1343,7 +1351,7 @@ impl MerchantConnectorAccountInterface for MockDb { .change_context(errors::StorageError::DecryptionError)?, ) } - Ok(output) + Ok(domain::MerchantConnectorAccounts::new(output)) } #[cfg(all(feature = "olap", feature = "v2"))] diff --git a/crates/router/src/services.rs b/crates/router/src/services.rs index 7b363d6346..1383768ed8 100644 --- a/crates/router/src/services.rs +++ b/crates/router/src/services.rs @@ -2,7 +2,6 @@ pub mod api; pub mod authentication; pub mod authorization; pub mod connector_integration_interface; -pub mod conversion_impls; #[cfg(feature = "email")] pub mod email; pub mod encryption; diff --git a/crates/router/src/services/connector_integration_interface.rs b/crates/router/src/services/connector_integration_interface.rs index d9b04f5734..025cbae0c3 100644 --- a/crates/router/src/services/connector_integration_interface.rs +++ b/crates/router/src/services/connector_integration_interface.rs @@ -1,664 +1,4 @@ -use common_utils::{crypto, errors::CustomResult, request::Request}; -use hyperswitch_domain_models::{ - router_data::RouterData, - router_data_v2::RouterDataV2, - router_response_types::{ConnectorInfo, SupportedPaymentMethods}, -}; -use hyperswitch_interfaces::{ - authentication::ExternalAuthenticationPayload, +pub use hyperswitch_interfaces::{ + authentication::ExternalAuthenticationPayload, connector_integration_interface::*, connector_integration_v2::ConnectorIntegrationV2, webhooks::IncomingWebhookFlowError, }; - -use super::{BoxedConnectorIntegrationV2, ConnectorSpecifications, ConnectorValidation}; -use crate::{ - core::payments, - errors, - events::connector_api_logs::ConnectorEvent, - services::{ - api as services_api, BoxedConnectorIntegration, CaptureSyncMethod, ConnectorIntegration, - ConnectorRedirectResponse, PaymentAction, - }, - settings::Connectors, - types::{ - self, - api::{ - self, disputes, Connector, ConnectorV2, CurrencyUnit, IncomingWebhookEvent, - IncomingWebhookRequestDetails, ObjectReferenceId, - }, - domain, - }, -}; -pub trait RouterDataConversion { - fn from_old_router_data( - old_router_data: &RouterData, - ) -> CustomResult, errors::ConnectorError> - where - Self: Sized; - fn to_old_router_data( - new_router_data: RouterDataV2, - ) -> CustomResult, errors::ConnectorError> - where - Self: Sized; -} - -#[derive(Clone)] -pub enum ConnectorEnum { - Old(api::BoxedConnector), - New(api::BoxedConnectorV2), -} - -#[derive(Clone)] -pub enum ConnectorIntegrationEnum<'a, F, ResourceCommonData, Req, Resp> { - Old(BoxedConnectorIntegration<'a, F, Req, Resp>), - New(BoxedConnectorIntegrationV2<'a, F, ResourceCommonData, Req, Resp>), -} - -pub type BoxedConnectorIntegrationInterface = - Box + Send + Sync>; - -impl ConnectorEnum { - pub fn get_connector_integration( - &self, - ) -> BoxedConnectorIntegrationInterface - where - dyn Connector + Sync: ConnectorIntegration, - dyn ConnectorV2 + Sync: ConnectorIntegrationV2, - ResourceCommonData: RouterDataConversion + Clone + 'static, - F: Clone + 'static, - Req: Clone + 'static, - Resp: Clone + 'static, - { - match self { - Self::Old(old_integration) => Box::new(ConnectorIntegrationEnum::Old( - old_integration.get_connector_integration(), - )), - Self::New(new_integration) => Box::new(ConnectorIntegrationEnum::New( - new_integration.get_connector_integration_v2(), - )), - } - } - - pub fn validate_file_upload( - &self, - purpose: api::FilePurpose, - file_size: i32, - file_type: mime::Mime, - ) -> CustomResult<(), errors::ConnectorError> { - match self { - Self::Old(connector) => connector.validate_file_upload(purpose, file_size, file_type), - Self::New(connector) => { - connector.validate_file_upload_v2(purpose, file_size, file_type) - } - } - } -} - -#[async_trait::async_trait] -impl api::IncomingWebhook for ConnectorEnum { - fn get_webhook_body_decoding_algorithm( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult, errors::ConnectorError> { - match self { - Self::Old(connector) => connector.get_webhook_body_decoding_algorithm(request), - Self::New(connector) => connector.get_webhook_body_decoding_algorithm(request), - } - } - - fn get_webhook_body_decoding_message( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult, errors::ConnectorError> { - match self { - Self::Old(connector) => connector.get_webhook_body_decoding_message(request), - Self::New(connector) => connector.get_webhook_body_decoding_message(request), - } - } - - async fn decode_webhook_body( - &self, - request: &IncomingWebhookRequestDetails<'_>, - merchant_id: &common_utils::id_type::MerchantId, - connector_webhook_details: Option, - connector_name: &str, - ) -> CustomResult, errors::ConnectorError> { - match self { - Self::Old(connector) => { - connector - .decode_webhook_body( - request, - merchant_id, - connector_webhook_details, - connector_name, - ) - .await - } - Self::New(connector) => { - connector - .decode_webhook_body( - request, - merchant_id, - connector_webhook_details, - connector_name, - ) - .await - } - } - } - - fn get_webhook_source_verification_algorithm( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult, errors::ConnectorError> { - match self { - Self::Old(connector) => connector.get_webhook_source_verification_algorithm(request), - Self::New(connector) => connector.get_webhook_source_verification_algorithm(request), - } - } - - async fn get_webhook_source_verification_merchant_secret( - &self, - merchant_id: &common_utils::id_type::MerchantId, - connector_name: &str, - connector_webhook_details: Option, - ) -> CustomResult { - match self { - Self::Old(connector) => { - connector - .get_webhook_source_verification_merchant_secret( - merchant_id, - connector_name, - connector_webhook_details, - ) - .await - } - Self::New(connector) => { - connector - .get_webhook_source_verification_merchant_secret( - merchant_id, - connector_name, - connector_webhook_details, - ) - .await - } - } - } - - fn get_webhook_source_verification_signature( - &self, - request: &IncomingWebhookRequestDetails<'_>, - connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, - ) -> CustomResult, errors::ConnectorError> { - match self { - Self::Old(connector) => connector - .get_webhook_source_verification_signature(request, connector_webhook_secrets), - Self::New(connector) => connector - .get_webhook_source_verification_signature(request, connector_webhook_secrets), - } - } - - fn get_webhook_source_verification_message( - &self, - request: &IncomingWebhookRequestDetails<'_>, - merchant_id: &common_utils::id_type::MerchantId, - connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, - ) -> CustomResult, errors::ConnectorError> { - match self { - Self::Old(connector) => connector.get_webhook_source_verification_message( - request, - merchant_id, - connector_webhook_secrets, - ), - Self::New(connector) => connector.get_webhook_source_verification_message( - request, - merchant_id, - connector_webhook_secrets, - ), - } - } - - async fn verify_webhook_source( - &self, - request: &IncomingWebhookRequestDetails<'_>, - merchant_id: &common_utils::id_type::MerchantId, - connector_webhook_details: Option, - connector_account_details: crypto::Encryptable>, - connector_name: &str, - ) -> CustomResult { - match self { - Self::Old(connector) => { - connector - .verify_webhook_source( - request, - merchant_id, - connector_webhook_details, - connector_account_details, - connector_name, - ) - .await - } - Self::New(connector) => { - connector - .verify_webhook_source( - request, - merchant_id, - connector_webhook_details, - connector_account_details, - connector_name, - ) - .await - } - } - } - - fn get_webhook_object_reference_id( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult { - match self { - Self::Old(connector) => connector.get_webhook_object_reference_id(request), - Self::New(connector) => connector.get_webhook_object_reference_id(request), - } - } - - fn get_webhook_event_type( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult { - match self { - Self::Old(connector) => connector.get_webhook_event_type(request), - Self::New(connector) => connector.get_webhook_event_type(request), - } - } - - fn get_webhook_resource_object( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult, errors::ConnectorError> { - match self { - Self::Old(connector) => connector.get_webhook_resource_object(request), - Self::New(connector) => connector.get_webhook_resource_object(request), - } - } - - fn get_webhook_api_response( - &self, - request: &IncomingWebhookRequestDetails<'_>, - error_kind: Option, - ) -> CustomResult, errors::ConnectorError> - { - match self { - Self::Old(connector) => connector.get_webhook_api_response(request, error_kind), - Self::New(connector) => connector.get_webhook_api_response(request, error_kind), - } - } - - fn get_dispute_details( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult { - match self { - Self::Old(connector) => connector.get_dispute_details(request), - Self::New(connector) => connector.get_dispute_details(request), - } - } - - fn get_external_authentication_details( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult { - match self { - Self::Old(connector) => connector.get_external_authentication_details(request), - Self::New(connector) => connector.get_external_authentication_details(request), - } - } - - fn get_mandate_details( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult< - Option, - errors::ConnectorError, - > { - match self { - Self::Old(connector) => connector.get_mandate_details(request), - Self::New(connector) => connector.get_mandate_details(request), - } - } - - fn get_network_txn_id( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult< - Option, - errors::ConnectorError, - > { - match self { - Self::Old(connector) => connector.get_network_txn_id(request), - Self::New(connector) => connector.get_network_txn_id(request), - } - } - - #[cfg(all(feature = "revenue_recovery", feature = "v2"))] - fn get_revenue_recovery_attempt_details( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult< - hyperswitch_domain_models::revenue_recovery::RevenueRecoveryAttemptData, - errors::ConnectorError, - > { - match self { - Self::Old(connector) => connector.get_revenue_recovery_attempt_details(request), - Self::New(connector) => connector.get_revenue_recovery_attempt_details(request), - } - } - - #[cfg(all(feature = "revenue_recovery", feature = "v2"))] - fn get_revenue_recovery_invoice_details( - &self, - request: &IncomingWebhookRequestDetails<'_>, - ) -> CustomResult< - hyperswitch_domain_models::revenue_recovery::RevenueRecoveryInvoiceData, - errors::ConnectorError, - > { - match self { - Self::Old(connector) => connector.get_revenue_recovery_invoice_details(request), - Self::New(connector) => connector.get_revenue_recovery_invoice_details(request), - } - } -} - -impl api::ConnectorTransactionId for ConnectorEnum { - fn connector_transaction_id( - &self, - payment_attempt: hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, - ) -> Result, errors::ApiErrorResponse> { - match self { - Self::Old(connector) => connector.connector_transaction_id(payment_attempt), - Self::New(connector) => connector.connector_transaction_id(payment_attempt), - } - } -} - -impl ConnectorRedirectResponse for ConnectorEnum { - fn get_flow_type( - &self, - query_params: &str, - json_payload: Option, - action: PaymentAction, - ) -> CustomResult { - match self { - Self::Old(connector) => connector.get_flow_type(query_params, json_payload, action), - Self::New(connector) => connector.get_flow_type(query_params, json_payload, action), - } - } -} - -impl ConnectorValidation for ConnectorEnum { - fn validate_connector_against_payment_request( - &self, - capture_method: Option, - payment_method: common_enums::PaymentMethod, - pmt: Option, - ) -> CustomResult<(), errors::ConnectorError> { - match self { - Self::Old(connector) => connector.validate_connector_against_payment_request( - capture_method, - payment_method, - pmt, - ), - Self::New(connector) => connector.validate_connector_against_payment_request( - capture_method, - payment_method, - pmt, - ), - } - } - - fn validate_mandate_payment( - &self, - pm_type: Option, - pm_data: domain::payments::PaymentMethodData, - ) -> CustomResult<(), errors::ConnectorError> { - match self { - Self::Old(connector) => connector.validate_mandate_payment(pm_type, pm_data), - Self::New(connector) => connector.validate_mandate_payment(pm_type, pm_data), - } - } - - fn validate_psync_reference_id( - &self, - data: &hyperswitch_domain_models::router_request_types::PaymentsSyncData, - is_three_ds: bool, - status: common_enums::enums::AttemptStatus, - connector_meta_data: Option, - ) -> CustomResult<(), errors::ConnectorError> { - match self { - Self::Old(connector) => connector.validate_psync_reference_id( - data, - is_three_ds, - status, - connector_meta_data, - ), - Self::New(connector) => connector.validate_psync_reference_id( - data, - is_three_ds, - status, - connector_meta_data, - ), - } - } - - fn is_webhook_source_verification_mandatory(&self) -> bool { - match self { - Self::Old(connector) => connector.is_webhook_source_verification_mandatory(), - Self::New(connector) => connector.is_webhook_source_verification_mandatory(), - } - } -} - -impl ConnectorSpecifications for ConnectorEnum { - fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> { - match self { - Self::Old(connector) => connector.get_supported_payment_methods(), - Self::New(connector) => connector.get_supported_payment_methods(), - } - } - - /// Supported webhooks flows - fn get_supported_webhook_flows(&self) -> Option<&'static [common_enums::EventClass]> { - match self { - Self::Old(connector) => connector.get_supported_webhook_flows(), - Self::New(connector) => connector.get_supported_webhook_flows(), - } - } - - /// Details related to connector - fn get_connector_about(&self) -> Option<&'static ConnectorInfo> { - match self { - Self::Old(connector) => connector.get_connector_about(), - Self::New(connector) => connector.get_connector_about(), - } - } -} - -impl api::ConnectorCommon for ConnectorEnum { - fn id(&self) -> &'static str { - match self { - Self::Old(connector) => connector.id(), - Self::New(connector) => connector.id(), - } - } - - fn get_currency_unit(&self) -> CurrencyUnit { - match self { - Self::Old(connector) => connector.get_currency_unit(), - Self::New(connector) => connector.get_currency_unit(), - } - } - - fn get_auth_header( - &self, - auth_type: &types::ConnectorAuthType, - ) -> CustomResult)>, errors::ConnectorError> { - match self { - Self::Old(connector) => connector.get_auth_header(auth_type), - Self::New(connector) => connector.get_auth_header(auth_type), - } - } - - fn common_get_content_type(&self) -> &'static str { - match self { - Self::Old(connector) => connector.common_get_content_type(), - Self::New(connector) => connector.common_get_content_type(), - } - } - - fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str { - match self { - Self::Old(connector) => connector.base_url(connectors), - Self::New(connector) => connector.base_url(connectors), - } - } - - fn build_error_response( - &self, - res: types::Response, - event_builder: Option<&mut ConnectorEvent>, - ) -> CustomResult { - match self { - Self::Old(connector) => connector.build_error_response(res, event_builder), - Self::New(connector) => connector.build_error_response(res, event_builder), - } - } -} - -pub trait ConnectorIntegrationInterface: Send + Sync { - fn clone_box( - &self, - ) -> Box + Send + Sync>; - fn get_multiple_capture_sync_method( - &self, - ) -> CustomResult; - fn build_request( - &self, - req: &RouterData, - _connectors: &Connectors, - ) -> CustomResult, errors::ConnectorError>; - fn handle_response( - &self, - data: &RouterData, - event_builder: Option<&mut ConnectorEvent>, - _res: types::Response, - ) -> CustomResult, errors::ConnectorError> - where - F: Clone, - Req: Clone, - Resp: Clone; - fn get_error_response( - &self, - res: types::Response, - event_builder: Option<&mut ConnectorEvent>, - ) -> CustomResult; - fn get_5xx_error_response( - &self, - res: types::Response, - event_builder: Option<&mut ConnectorEvent>, - ) -> CustomResult; -} - -impl - ConnectorIntegrationInterface - for ConnectorIntegrationEnum<'static, T, ResourceCommonData, Req, Resp> -where - ResourceCommonData: RouterDataConversion + Clone, - T: Clone, - Req: Clone, - Resp: Clone, -{ - fn get_multiple_capture_sync_method( - &self, - ) -> CustomResult { - match self { - ConnectorIntegrationEnum::Old(old_integration) => { - old_integration.get_multiple_capture_sync_method() - } - ConnectorIntegrationEnum::New(new_integration) => { - new_integration.get_multiple_capture_sync_method() - } - } - } - fn build_request( - &self, - req: &RouterData, - connectors: &Connectors, - ) -> CustomResult, errors::ConnectorError> { - match self { - ConnectorIntegrationEnum::Old(old_integration) => { - old_integration.build_request(req, connectors) - } - ConnectorIntegrationEnum::New(new_integration) => { - let new_router_data = ResourceCommonData::from_old_router_data(req)?; - new_integration.build_request_v2(&new_router_data) - } - } - } - fn handle_response( - &self, - data: &RouterData, - event_builder: Option<&mut ConnectorEvent>, - res: types::Response, - ) -> CustomResult, errors::ConnectorError> - where - T: Clone, - Req: Clone, - Resp: Clone, - { - match self { - ConnectorIntegrationEnum::Old(old_integration) => { - old_integration.handle_response(data, event_builder, res) - } - ConnectorIntegrationEnum::New(new_integration) => { - let new_router_data = ResourceCommonData::from_old_router_data(data)?; - new_integration - .handle_response_v2(&new_router_data, event_builder, res) - .map(ResourceCommonData::to_old_router_data)? - } - } - } - fn get_error_response( - &self, - res: types::Response, - event_builder: Option<&mut ConnectorEvent>, - ) -> CustomResult { - match self { - ConnectorIntegrationEnum::Old(old_integration) => { - old_integration.get_error_response(res, event_builder) - } - ConnectorIntegrationEnum::New(new_integration) => { - new_integration.get_error_response_v2(res, event_builder) - } - } - } - fn get_5xx_error_response( - &self, - res: types::Response, - event_builder: Option<&mut ConnectorEvent>, - ) -> CustomResult { - match self { - ConnectorIntegrationEnum::Old(old_integration) => { - old_integration.get_5xx_error_response(res, event_builder) - } - ConnectorIntegrationEnum::New(new_integration) => { - new_integration.get_5xx_error_response(res, event_builder) - } - } - } - - fn clone_box( - &self, - ) -> Box + Send + Sync> - { - Box::new(self.clone()) - } -} diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index ee04f0a7bf..0a7a1f01b4 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -45,14 +45,27 @@ pub use hyperswitch_domain_models::router_flow_types::{ access_token_auth::AccessTokenAuth, mandate_revoke::MandateRevoke, webhooks::VerifyWebhookSource, }; -pub use hyperswitch_interfaces::api::{ - revenue_recovery::{AdditionalRevenueRecovery, RevenueRecovery, RevenueRecoveryRecordBack}, - revenue_recovery_v2::RevenueRecoveryV2, - ConnectorAccessToken, ConnectorAccessTokenV2, ConnectorCommon, ConnectorCommonExt, - ConnectorMandateRevoke, ConnectorMandateRevokeV2, ConnectorVerifyWebhookSource, - ConnectorVerifyWebhookSourceV2, CurrencyUnit, +pub use hyperswitch_interfaces::{ + api::{ + authentication::{ + ConnectorAuthentication, ConnectorPostAuthentication, ConnectorPreAuthentication, + ConnectorPreAuthenticationVersionCall, ExternalAuthentication, + }, + authentication_v2::{ + ConnectorAuthenticationV2, ConnectorPostAuthenticationV2, ConnectorPreAuthenticationV2, + ConnectorPreAuthenticationVersionCallV2, ExternalAuthenticationV2, + }, + fraud_check::FraudCheck, + revenue_recovery::{AdditionalRevenueRecovery, RevenueRecovery, RevenueRecoveryRecordBack}, + revenue_recovery_v2::RevenueRecoveryV2, + BoxedConnector, Connector, ConnectorAccessToken, ConnectorAccessTokenV2, ConnectorCommon, + ConnectorCommonExt, ConnectorMandateRevoke, ConnectorMandateRevokeV2, + ConnectorTransactionId, ConnectorVerifyWebhookSource, ConnectorVerifyWebhookSourceV2, + CurrencyUnit, + }, + connector_integration_v2::{BoxedConnectorV2, ConnectorV2}, }; -use hyperswitch_interfaces::api::{UnifiedAuthenticationService, UnifiedAuthenticationServiceV2}; +use rustc_hash::FxHashMap; #[cfg(feature = "frm")] pub use self::fraud_check::*; @@ -66,119 +79,23 @@ pub use self::{ use super::transformers::ForeignTryFrom; use crate::{ configs::settings::Connectors, - connector, + connector, consts, core::{ errors::{self, CustomResult}, payments::types as payments_types, }, - services::{connector_integration_interface::ConnectorEnum, ConnectorRedirectResponse}, + services::connector_integration_interface::ConnectorEnum, types::{self, api::enums as api_enums}, }; #[derive(Clone)] pub enum ConnectorCallType { PreDetermined(ConnectorData), Retryable(Vec), - SessionMultiple(Vec), + SessionMultiple(SessionConnectorDatas), #[cfg(feature = "v2")] Skip, } -pub trait ConnectorTransactionId: ConnectorCommon + Sync { - fn connector_transaction_id( - &self, - payment_attempt: hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt, - ) -> Result, errors::ApiErrorResponse> { - Ok(payment_attempt - .get_connector_payment_id() - .map(ToString::to_string)) - } -} -pub trait Connector: - Send - + Refund - + Payment - + ConnectorRedirectResponse - + IncomingWebhook - + ConnectorAccessToken - + Dispute - + FileUpload - + ConnectorTransactionId - + Payouts - + ConnectorVerifyWebhookSource - + FraudCheck - + ConnectorMandateRevoke - + ExternalAuthentication - + TaxCalculation - + UnifiedAuthenticationService - + RevenueRecovery -{ -} - -impl< - T: Refund - + Payment - + ConnectorRedirectResponse - + Send - + IncomingWebhook - + ConnectorAccessToken - + Dispute - + FileUpload - + ConnectorTransactionId - + Payouts - + ConnectorVerifyWebhookSource - + FraudCheck - + ConnectorMandateRevoke - + ExternalAuthentication - + TaxCalculation - + UnifiedAuthenticationService - + RevenueRecovery, - > Connector for T -{ -} - -pub trait ConnectorV2: - Send - + RefundV2 - + PaymentV2 - + ConnectorRedirectResponse - + IncomingWebhook - + ConnectorAccessTokenV2 - + DisputeV2 - + FileUploadV2 - + ConnectorTransactionId - + PayoutsV2 - + ConnectorVerifyWebhookSourceV2 - + FraudCheckV2 - + ConnectorMandateRevokeV2 - + ExternalAuthenticationV2 - + UnifiedAuthenticationServiceV2 - + RevenueRecoveryV2 -{ -} -impl< - T: RefundV2 - + PaymentV2 - + ConnectorRedirectResponse - + Send - + IncomingWebhook - + ConnectorAccessTokenV2 - + DisputeV2 - + FileUploadV2 - + ConnectorTransactionId - + PayoutsV2 - + ConnectorVerifyWebhookSourceV2 - + FraudCheckV2 - + ConnectorMandateRevokeV2 - + ExternalAuthenticationV2 - + UnifiedAuthenticationServiceV2 - + RevenueRecoveryV2, - > ConnectorV2 for T -{ -} - -pub type BoxedConnector = Box<&'static (dyn Connector + Sync)>; -pub type BoxedConnectorV2 = Box<&'static (dyn ConnectorV2 + Sync)>; - // Normal flow will call the connector and follow the flow specific operations (capture, authorize) // SessionTokenFromMetadata will avoid calling the connector instead create the session token ( for sdk ) #[derive(Clone, Eq, PartialEq, Debug)] @@ -194,7 +111,7 @@ pub enum GetToken { /// Routing algorithm will output merchant connector identifier instead of connector name /// In order to support backwards compatibility for older routing algorithms and merchant accounts /// the support for connector name is retained -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ConnectorData { pub connector: ConnectorEnum, pub connector_name: types::Connector, @@ -202,27 +119,69 @@ pub struct ConnectorData { pub merchant_connector_id: Option, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct SessionConnectorData { - pub payment_method_type: api_enums::PaymentMethodType, + pub payment_method_sub_type: api_enums::PaymentMethodType, + pub payment_method_type: api_enums::PaymentMethod, pub connector: ConnectorData, pub business_sub_label: Option, } impl SessionConnectorData { pub fn new( - payment_method_type: api_enums::PaymentMethodType, + payment_method_sub_type: api_enums::PaymentMethodType, connector: ConnectorData, business_sub_label: Option, + payment_method_type: api_enums::PaymentMethod, ) -> Self { Self { - payment_method_type, + payment_method_sub_type, connector, business_sub_label, + payment_method_type, } } } +common_utils::create_list_wrapper!( + SessionConnectorDatas, + SessionConnectorData, + impl_functions: { + pub fn apply_filter_for_session_routing(&self) -> Self { + let routing_enabled_pmts = &consts::ROUTING_ENABLED_PAYMENT_METHOD_TYPES; + let routing_enabled_pms = &consts::ROUTING_ENABLED_PAYMENT_METHODS; + self + .iter() + .filter(|connector_data| { + routing_enabled_pmts.contains(&connector_data.payment_method_sub_type) + || routing_enabled_pms.contains(&connector_data.payment_method_type) + }) + .cloned() + .collect() + } + pub fn filter_and_validate_for_session_flow(self, routing_results: &FxHashMap>) -> Result { + let mut final_list = Self::new(Vec::new()); + let routing_enabled_pmts = &consts::ROUTING_ENABLED_PAYMENT_METHOD_TYPES; + for connector_data in self { + if !routing_enabled_pmts.contains(&connector_data.payment_method_sub_type) { + final_list.push(connector_data); + } else if let Some(choice) = routing_results.get(&connector_data.payment_method_sub_type) { + let routing_choice = choice + .first() + .ok_or(errors::ApiErrorResponse::InternalServerError)?; + if connector_data.connector.connector_name == routing_choice.connector.connector_name + && connector_data.connector.merchant_connector_id + == routing_choice.connector.merchant_connector_id + { + final_list.push(connector_data); + } + } + } + Ok(final_list) + } + } +); + pub fn convert_connector_data_to_routable_connectors( connectors: &[ConnectorData], ) -> CustomResult, common_utils::errors::ValidationError> { @@ -277,7 +236,7 @@ impl SessionSurchargeDetails { } pub enum ConnectorChoice { - SessionMultiple(Vec), + SessionMultiple(SessionConnectorDatas), StraightThrough(serde_json::Value), Decide, } @@ -612,23 +571,6 @@ impl ConnectorData { } } -#[cfg(feature = "frm")] -pub trait FraudCheck: - ConnectorCommon - + FraudCheckSale - + FraudCheckTransaction - + FraudCheckCheckout - + FraudCheckFulfillment - + FraudCheckRecordReturn -{ -} - -#[cfg(not(feature = "frm"))] -pub trait FraudCheck {} - -#[cfg(not(feature = "frm"))] -pub trait FraudCheckV2 {} - #[cfg(test)] mod test { #![allow(clippy::unwrap_used)] diff --git a/crates/router/src/types/api/authentication.rs b/crates/router/src/types/api/authentication.rs index e5f835a100..5234013f64 100644 --- a/crates/router/src/types/api/authentication.rs +++ b/crates/router/src/types/api/authentication.rs @@ -3,27 +3,15 @@ use std::str::FromStr; use api_models::enums; use common_utils::errors::CustomResult; use error_stack::ResultExt; -pub use hyperswitch_domain_models::router_request_types::authentication::MessageCategory; - -pub use super::authentication_v2::{ - ConnectorAuthenticationV2, ConnectorPostAuthenticationV2, ConnectorPreAuthenticationV2, - ConnectorPreAuthenticationVersionCallV2, ExternalAuthenticationV2, +pub use hyperswitch_domain_models::{ + router_flow_types::authentication::{ + Authentication, PostAuthentication, PreAuthentication, PreAuthenticationVersionCall, + }, + router_request_types::authentication::MessageCategory, }; -use crate::core::errors; -#[derive(Debug, Clone)] -pub struct PreAuthentication; - -#[derive(Debug, Clone)] -pub struct PreAuthenticationVersionCall; - -#[derive(Debug, Clone)] -pub struct Authentication; - -#[derive(Debug, Clone)] -pub struct PostAuthentication; use crate::{ - connector, services, services::connector_integration_interface::ConnectorEnum, types, + connector, core::errors, services::connector_integration_interface::ConnectorEnum, types::storage, }; @@ -74,51 +62,6 @@ pub struct PostAuthenticationResponse { pub eci: Option, } -pub trait ConnectorAuthentication: - services::ConnectorIntegration< - Authentication, - types::authentication::ConnectorAuthenticationRequestData, - types::authentication::AuthenticationResponseData, -> -{ -} - -pub trait ConnectorPreAuthentication: - services::ConnectorIntegration< - PreAuthentication, - types::authentication::PreAuthNRequestData, - types::authentication::AuthenticationResponseData, -> -{ -} - -pub trait ConnectorPreAuthenticationVersionCall: - services::ConnectorIntegration< - PreAuthenticationVersionCall, - types::authentication::PreAuthNRequestData, - types::authentication::AuthenticationResponseData, -> -{ -} - -pub trait ConnectorPostAuthentication: - services::ConnectorIntegration< - PostAuthentication, - types::authentication::ConnectorPostAuthenticationRequestData, - types::authentication::AuthenticationResponseData, -> -{ -} - -pub trait ExternalAuthentication: - super::ConnectorCommon - + ConnectorAuthentication - + ConnectorPreAuthentication - + ConnectorPreAuthenticationVersionCall - + ConnectorPostAuthentication -{ -} - #[derive(Clone)] pub struct AuthenticationConnectorData { pub connector: ConnectorEnum, diff --git a/crates/router/src/types/api/authentication_v2.rs b/crates/router/src/types/api/authentication_v2.rs index f7469dc867..20f82f4b84 100644 --- a/crates/router/src/types/api/authentication_v2.rs +++ b/crates/router/src/types/api/authentication_v2.rs @@ -1,55 +1,2 @@ pub use hyperswitch_domain_models::router_request_types::authentication::MessageCategory; - -use super::authentication::{ - Authentication, PostAuthentication, PreAuthentication, PreAuthenticationVersionCall, -}; -use crate::{services, types}; - -pub trait ConnectorAuthenticationV2: - services::ConnectorIntegrationV2< - Authentication, - types::ExternalAuthenticationFlowData, - types::authentication::ConnectorAuthenticationRequestData, - types::authentication::AuthenticationResponseData, -> -{ -} - -pub trait ConnectorPreAuthenticationV2: - services::ConnectorIntegrationV2< - PreAuthentication, - types::ExternalAuthenticationFlowData, - types::authentication::PreAuthNRequestData, - types::authentication::AuthenticationResponseData, -> -{ -} - -pub trait ConnectorPreAuthenticationVersionCallV2: - services::ConnectorIntegrationV2< - PreAuthenticationVersionCall, - types::ExternalAuthenticationFlowData, - types::authentication::PreAuthNRequestData, - types::authentication::AuthenticationResponseData, -> -{ -} - -pub trait ConnectorPostAuthenticationV2: - services::ConnectorIntegrationV2< - PostAuthentication, - types::ExternalAuthenticationFlowData, - types::authentication::ConnectorPostAuthenticationRequestData, - types::authentication::AuthenticationResponseData, -> -{ -} - -pub trait ExternalAuthenticationV2: - super::ConnectorCommon - + ConnectorAuthenticationV2 - + ConnectorPreAuthenticationV2 - + ConnectorPreAuthenticationVersionCallV2 - + ConnectorPostAuthenticationV2 -{ -} +pub use hyperswitch_interfaces::api::authentication_v2::ExternalAuthenticationV2; diff --git a/crates/router/src/types/api/fraud_check.rs b/crates/router/src/types/api/fraud_check.rs index 213aef9cf0..44dc351466 100644 --- a/crates/router/src/types/api/fraud_check.rs +++ b/crates/router/src/types/api/fraud_check.rs @@ -15,7 +15,7 @@ pub use super::fraud_check_v2::{ FraudCheckCheckoutV2, FraudCheckFulfillmentV2, FraudCheckRecordReturnV2, FraudCheckSaleV2, FraudCheckTransactionV2, FraudCheckV2, }; -use super::{ConnectorData, SessionConnectorData}; +use super::{ConnectorData, SessionConnectorDatas}; use crate::{connector, core::errors, services::connector_integration_interface::ConnectorEnum}; #[derive(Clone)] @@ -26,7 +26,7 @@ pub struct FraudCheckConnectorData { pub enum ConnectorCallType { PreDetermined(ConnectorData), Retryable(Vec), - SessionMultiple(Vec), + SessionMultiple(SessionConnectorDatas), } impl FraudCheckConnectorData { diff --git a/crates/router/src/types/api/fraud_check_v2.rs b/crates/router/src/types/api/fraud_check_v2.rs index c6f2b4a8ff..dec25085ba 100644 --- a/crates/router/src/types/api/fraud_check_v2.rs +++ b/crates/router/src/types/api/fraud_check_v2.rs @@ -3,18 +3,5 @@ pub use hyperswitch_domain_models::router_flow_types::fraud_check::{ }; pub use hyperswitch_interfaces::api::fraud_check_v2::{ FraudCheckCheckoutV2, FraudCheckFulfillmentV2, FraudCheckRecordReturnV2, FraudCheckSaleV2, - FraudCheckTransactionV2, + FraudCheckTransactionV2, FraudCheckV2, }; - -use crate::types; - -#[cfg(feature = "frm")] -pub trait FraudCheckV2: - types::api::ConnectorCommon - + FraudCheckSaleV2 - + FraudCheckTransactionV2 - + FraudCheckCheckoutV2 - + FraudCheckFulfillmentV2 - + FraudCheckRecordReturnV2 -{ -} diff --git a/crates/router/src/types/storage.rs b/crates/router/src/types/storage.rs index bd033ff4c2..f14d69a098 100644 --- a/crates/router/src/types/storage.rs +++ b/crates/router/src/types/storage.rs @@ -44,24 +44,27 @@ pub mod user; pub mod user_authentication_method; pub mod user_role; -use std::collections::HashMap; - pub use diesel_models::{ process_tracker::business_status, ProcessTracker, ProcessTrackerNew, ProcessTrackerRunner, ProcessTrackerUpdate, }; #[cfg(feature = "v1")] pub use hyperswitch_domain_models::payments::payment_attempt::PaymentAttemptNew; -pub use hyperswitch_domain_models::payments::{ - payment_attempt::{PaymentAttempt, PaymentAttemptUpdate}, - payment_intent::{PaymentIntentUpdate, PaymentIntentUpdateFields}, - PaymentIntent, -}; #[cfg(feature = "payouts")] pub use hyperswitch_domain_models::payouts::{ payout_attempt::{PayoutAttempt, PayoutAttemptNew, PayoutAttemptUpdate}, payouts::{Payouts, PayoutsNew, PayoutsUpdate}, }; +pub use hyperswitch_domain_models::{ + payments::{ + payment_attempt::{PaymentAttempt, PaymentAttemptUpdate}, + payment_intent::{PaymentIntentUpdate, PaymentIntentUpdateFields}, + PaymentIntent, + }, + routing::{ + PaymentRoutingInfo, PaymentRoutingInfoInner, PreRoutingConnectorChoice, RoutingData, + }, +}; pub use scheduler::db::process_tracker; pub use self::{ @@ -74,67 +77,3 @@ pub use self::{ process_tracker::*, refund::*, reverse_lookup::*, role::*, routing_algorithm::*, unified_translations::*, user::*, user_authentication_method::*, user_role::*, }; -use crate::types::api::routing; - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct RoutingData { - pub routed_through: Option, - - pub merchant_connector_id: Option, - - pub routing_info: PaymentRoutingInfo, - pub algorithm: Option, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(from = "PaymentRoutingInfoSerde", into = "PaymentRoutingInfoSerde")] -pub struct PaymentRoutingInfo { - pub algorithm: Option, - pub pre_routing_results: - Option>, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct PaymentRoutingInfoInner { - pub algorithm: Option, - pub pre_routing_results: - Option>, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(untagged)] -pub enum PreRoutingConnectorChoice { - Single(routing::RoutableConnectorChoice), - Multiple(Vec), -} - -#[derive(Debug, serde::Serialize, serde::Deserialize)] -#[serde(untagged)] -pub enum PaymentRoutingInfoSerde { - OnlyAlgorithm(Box), - WithDetails(Box), -} - -impl From for PaymentRoutingInfo { - fn from(value: PaymentRoutingInfoSerde) -> Self { - match value { - PaymentRoutingInfoSerde::OnlyAlgorithm(algo) => Self { - algorithm: Some(*algo), - pre_routing_results: None, - }, - PaymentRoutingInfoSerde::WithDetails(details) => Self { - algorithm: details.algorithm, - pre_routing_results: details.pre_routing_results, - }, - } - } -} - -impl From for PaymentRoutingInfoSerde { - fn from(value: PaymentRoutingInfo) -> Self { - Self::WithDetails(Box::new(PaymentRoutingInfoInner { - algorithm: value.algorithm, - pre_routing_results: value.pre_routing_results, - })) - } -} diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index 166e455e6f..e78d6244d5 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -603,8 +603,8 @@ impl PaymentIntentInterface for crate::RouterStore { ) -> error_stack::Result { let conn = pg_connection_write(self).await?; let diesel_payment_intent_update = - diesel_models::PaymentIntentUpdateInternal::from(payment_intent); - + diesel_models::PaymentIntentUpdateInternal::try_from(payment_intent) + .change_context(StorageError::DeserializationFailed)?; let diesel_payment_intent = this .convert() .await