diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index ceb2b654871..777cabe3002 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1316,12 +1316,59 @@ pub struct NetworkTokenWithNTIRef { #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] pub struct ConnectorMandateReferenceId { - pub connector_mandate_id: Option, - pub payment_method_id: Option, - pub update_history: Option>, - pub mandate_metadata: Option, -} + connector_mandate_id: Option, + payment_method_id: Option, + update_history: Option>, + mandate_metadata: Option, + connector_mandate_request_reference_id: Option, +} + +impl ConnectorMandateReferenceId { + pub fn new( + connector_mandate_id: Option, + payment_method_id: Option, + update_history: Option>, + mandate_metadata: Option, + connector_mandate_request_reference_id: Option, + ) -> Self { + Self { + connector_mandate_id, + payment_method_id, + update_history, + mandate_metadata, + connector_mandate_request_reference_id, + } + } + pub fn get_connector_mandate_id(&self) -> Option { + self.connector_mandate_id.clone() + } + pub fn get_payment_method_id(&self) -> Option { + self.payment_method_id.clone() + } + pub fn get_mandate_metadata(&self) -> Option { + self.mandate_metadata.clone() + } + pub fn get_connector_mandate_request_reference_id(&self) -> Option { + self.connector_mandate_request_reference_id.clone() + } + + pub fn update( + &mut self, + connector_mandate_id: Option, + payment_method_id: Option, + update_history: Option>, + mandate_metadata: Option, + connector_mandate_request_reference_id: Option, + ) { + self.connector_mandate_id = connector_mandate_id.or(self.connector_mandate_id.clone()); + self.payment_method_id = payment_method_id.or(self.payment_method_id.clone()); + self.update_history = update_history.or(self.update_history.clone()); + self.mandate_metadata = mandate_metadata.or(self.mandate_metadata.clone()); + self.connector_mandate_request_reference_id = connector_mandate_request_reference_id + .or(self.connector_mandate_request_reference_id.clone()); + } +} #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)] pub struct UpdateHistory { pub connector_mandate_id: Option, diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index 923bdf89c02..463ec3ee1b6 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -270,6 +270,11 @@ pub fn generate_time_ordered_id_without_prefix() -> String { uuid::Uuid::now_v7().as_simple().to_string() } +/// Generate a nanoid with the specified length +#[inline] +pub fn generate_id_with_len(length: usize) -> String { + nanoid::nanoid!(length, &consts::ALPHABETS) +} #[allow(missing_docs)] pub trait DbConnectionParams { fn get_username(&self) -> &str; diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs index ae4722a6f37..f8654d3cd96 100644 --- a/crates/diesel_models/src/payment_attempt.rs +++ b/crates/diesel_models/src/payment_attempt.rs @@ -21,7 +21,15 @@ pub struct ConnectorMandateReferenceId { pub connector_mandate_id: Option, pub payment_method_id: Option, pub mandate_metadata: Option, + pub connector_mandate_request_reference_id: Option, } + +impl ConnectorMandateReferenceId { + pub fn get_connector_mandate_request_reference_id(&self) -> Option { + self.connector_mandate_request_reference_id.clone() + } +} + #[cfg(feature = "v2")] #[derive( Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Serialize, Deserialize, Selectable, @@ -414,6 +422,7 @@ pub enum PaymentAttemptUpdate { customer_acceptance: Option, shipping_cost: Option, order_tax_amount: Option, + connector_mandate_detail: Option, }, VoidUpdate { status: storage_enums::AttemptStatus, @@ -2158,6 +2167,7 @@ impl From for PaymentAttemptUpdateInternal { customer_acceptance, shipping_cost, order_tax_amount, + connector_mandate_detail, } => Self { amount: Some(amount), currency: Some(currency), @@ -2209,7 +2219,7 @@ impl From for PaymentAttemptUpdateInternal { shipping_cost, order_tax_amount, connector_transaction_data: None, - connector_mandate_detail: None, + connector_mandate_detail, }, PaymentAttemptUpdate::VoidUpdate { status, diff --git a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs index ccecdaa3d41..9dd842768f1 100644 --- a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs @@ -176,7 +176,7 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>> } Some(api_models::payments::MandateReferenceId::ConnectorMandateId(mandate_data)) => { let mandate_metadata: DeutschebankMandateMetadata = mandate_data - .mandate_metadata + .get_mandate_metadata() .ok_or(errors::ConnectorError::MissingConnectorMandateMetadata)? .clone() .parse_value("DeutschebankMandateMetadata") @@ -325,6 +325,7 @@ impl reference: Secret::from(reference.clone()), signed_on, })), + connector_mandate_request_reference_id: None, })) } else { Box::new(None) diff --git a/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs index 7c346bd86cc..3639a7db4c8 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiuu/transformers.rs @@ -809,6 +809,7 @@ impl connector_mandate_id: Some(token.clone().expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }) }); let status = match non_threeds_data.status.as_str() { @@ -1184,6 +1185,7 @@ impl TryFrom> for PaymentsSy connector_mandate_id: Some(token.clone().expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id:None }) } Err(err) => { diff --git a/crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs b/crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs index 4761581401f..4149f740e76 100644 --- a/crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/nexinets/transformers.rs @@ -360,6 +360,7 @@ impl TryFrom> for NovalnetPaym .into()), }, Some(api_models::payments::MandateReferenceId::ConnectorMandateId(mandate_data)) => { - let connector_mandate_id = mandate_data.connector_mandate_id.ok_or( + let connector_mandate_id = mandate_data.get_connector_mandate_id().ok_or( errors::ConnectorError::MissingRequiredField { field_name: "connector_mandate_id", }, @@ -597,6 +597,7 @@ impl TryFrom connector_mandate_id: Some(id.clone()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, } })), connector_metadata: None, diff --git a/crates/hyperswitch_connectors/src/connectors/payeezy/transformers.rs b/crates/hyperswitch_connectors/src/connectors/payeezy/transformers.rs index af29d54117a..707e34a158c 100644 --- a/crates/hyperswitch_connectors/src/connectors/payeezy/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/payeezy/transformers.rs @@ -185,7 +185,7 @@ fn get_transaction_type_and_stored_creds( match mandate_ids.mandate_reference_id.clone() { Some(api_models::payments::MandateReferenceId::ConnectorMandateId( connector_mandate_ids, - )) => connector_mandate_ids.connector_mandate_id, + )) => connector_mandate_ids.get_connector_mandate_id(), _ => None, } }); @@ -420,6 +420,7 @@ impl TryFrom { - connector_mandate_ids.connector_mandate_id.clone() + connector_mandate_ids.get_connector_mandate_id() } Some(payments::MandateReferenceId::NetworkMandateId(_)) | None diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index 4b3fd1f8cf9..831e97b54a1 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -787,6 +787,7 @@ pub enum PaymentAttemptUpdate { client_source: Option, client_version: Option, customer_acceptance: Option, + connector_mandate_detail: Option, }, RejectUpdate { status: storage_enums::AttemptStatus, @@ -1027,6 +1028,7 @@ impl PaymentAttemptUpdate { client_source, client_version, customer_acceptance, + connector_mandate_detail, } => DieselPaymentAttemptUpdate::ConfirmUpdate { amount: net_amount.get_order_amount(), currency, @@ -1060,6 +1062,7 @@ impl PaymentAttemptUpdate { customer_acceptance, shipping_cost: net_amount.get_shipping_cost(), order_tax_amount: net_amount.get_order_tax_amount(), + connector_mandate_detail, }, Self::VoidUpdate { status, diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index ba76cc533ca..186a4f01262 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -84,6 +84,8 @@ pub struct RouterData { pub additional_merchant_data: Option, pub header_payload: Option, + + pub connector_mandate_request_reference_id: Option, } // Different patterns of authentication. diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index 5878a28f40d..d20ac148b70 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -1,6 +1,6 @@ pub mod authentication; pub mod fraud_check; -use api_models::payments::{Address, RequestSurchargeDetails}; +use api_models::payments::{AdditionalPaymentData, Address, RequestSurchargeDetails}; use common_utils::{ consts, errors, ext_traits::OptionExt, @@ -72,6 +72,7 @@ pub struct PaymentsAuthorizeData { pub merchant_order_reference_id: Option, pub integrity_object: Option, pub shipping_cost: Option, + pub additional_payment_method_data: Option, } #[derive(Debug, Clone)] diff --git a/crates/hyperswitch_domain_models/src/router_response_types.rs b/crates/hyperswitch_domain_models/src/router_response_types.rs index 2ed77c8072d..6356301c726 100644 --- a/crates/hyperswitch_domain_models/src/router_response_types.rs +++ b/crates/hyperswitch_domain_models/src/router_response_types.rs @@ -83,6 +83,7 @@ pub struct MandateReference { pub connector_mandate_id: Option, pub payment_method_id: Option, pub mandate_metadata: Option, + pub connector_mandate_request_reference_id: Option, } #[derive(Debug, Clone)] diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index 7355617b53a..77598f7be38 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -749,6 +749,7 @@ impl connector_mandate_id: Some(id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }); Ok(Self { diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index bc75fd29c02..95ac2a67a53 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -14,7 +14,7 @@ use time::{Duration, OffsetDateTime, PrimitiveDateTime}; use crate::{connector::utils::PayoutsData, types::api::payouts, utils::OptionExt}; use crate::{ connector::utils::{ - self, AddressDetailsData, BrowserInformationData, CardData, MandateReferenceData, + self, missing_field_err, AddressDetailsData, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, PhoneDetailsData, RouterData, }, consts, @@ -2573,7 +2573,9 @@ impl<'a> None => PaymentType::Scheme, }, stored_payment_method_id: Secret::new( - connector_mandate_ids.get_connector_mandate_id()?, + connector_mandate_ids + .get_connector_mandate_id() + .ok_or_else(missing_field_err("mandate_id"))?, ), }; Ok::, Self::Error>(AdyenPaymentMethod::Mandate(Box::new( @@ -3365,6 +3367,7 @@ pub fn get_adyen_response( connector_mandate_id: Some(mandate_id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }); let network_txn_id = response.additional_data.and_then(|additional_data| { additional_data diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 6961a87326b..c9427683d88 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -399,6 +399,7 @@ impl ), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }, )), connector_metadata: None, @@ -647,7 +648,7 @@ impl ), ) -> Result { let mandate_id = connector_mandate_id - .connector_mandate_id + .get_connector_mandate_id() .ok_or(errors::ConnectorError::MissingConnectorMandateID)?; Ok(Self { transaction_type: TransactionType::try_from(item.router_data.request.capture_method)?, @@ -1113,6 +1114,7 @@ impl ), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, } }); diff --git a/crates/router/src/connector/bamboraapac/transformers.rs b/crates/router/src/connector/bamboraapac/transformers.rs index 8fcbcd98f09..1a254c052a4 100644 --- a/crates/router/src/connector/bamboraapac/transformers.rs +++ b/crates/router/src/connector/bamboraapac/transformers.rs @@ -281,6 +281,7 @@ impl connector_mandate_id, payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }) } else { None @@ -465,6 +466,7 @@ impl connector_mandate_id: Some(connector_mandate_id), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, })), connector_metadata: None, network_txn_id: None, diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index b5669decd86..c6490cd6139 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -362,6 +362,7 @@ impl .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, } }); let mut mandate_status = @@ -1512,6 +1513,7 @@ fn get_payment_response( .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }); Ok(types::PaymentsResponseData::TransactionResponse { diff --git a/crates/router/src/connector/braintree/transformers.rs b/crates/router/src/connector/braintree/transformers.rs index 948d6f2cfd3..f606e671a8d 100644 --- a/crates/router/src/connector/braintree/transformers.rs +++ b/crates/router/src/connector/braintree/transformers.rs @@ -445,6 +445,7 @@ impl connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }, )), connector_metadata: None, @@ -620,6 +621,7 @@ impl connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }, )), connector_metadata: None, @@ -702,6 +704,7 @@ impl connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }, )), connector_metadata: None, @@ -766,6 +769,7 @@ impl connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }, )), connector_metadata: None, diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 4331b4d7983..c187dcb6dfc 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -2555,6 +2555,7 @@ fn get_payment_response( .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }); Ok(types::PaymentsResponseData::TransactionResponse { @@ -3283,6 +3284,7 @@ impl .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }); let mut mandate_status = enums::AttemptStatus::foreign_from(( item.response diff --git a/crates/router/src/connector/globalpay/transformers.rs b/crates/router/src/connector/globalpay/transformers.rs index d17c8a8fdfd..8008cc309c2 100644 --- a/crates/router/src/connector/globalpay/transformers.rs +++ b/crates/router/src/connector/globalpay/transformers.rs @@ -262,6 +262,7 @@ fn get_payment_response( connector_mandate_id: Some(id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }) }); match status { @@ -472,7 +473,7 @@ fn get_mandate_details(item: &types::PaymentsAuthorizeRouterData) -> Result connector_mandate_ids.connector_mandate_id, + )) => connector_mandate_ids.get_connector_mandate_id(), _ => None, } }); diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index dac90b28366..6a8fe01eede 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -516,6 +516,7 @@ impl connector_mandate_id: Some(item.response.mandates.id.clone().expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }); Ok(Self { response: Ok(types::PaymentsResponseData::TransactionResponse { @@ -669,6 +670,7 @@ impl connector_mandate_id: Some(item.data.request.get_connector_mandate_id()?), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }; Ok(Self { status: enums::AttemptStatus::from(item.response.payments.status), diff --git a/crates/router/src/connector/multisafepay/transformers.rs b/crates/router/src/connector/multisafepay/transformers.rs index 2831b63fcbe..6a227cffb37 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -830,7 +830,9 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> .and_then(|mandate_ids| match mandate_ids.mandate_reference_id { Some(api_models::payments::MandateReferenceId::ConnectorMandateId( connector_mandate_ids, - )) => connector_mandate_ids.connector_mandate_id.map(Secret::new), + )) => connector_mandate_ids + .get_connector_mandate_id() + .map(Secret::new), _ => None, }), days_active: Some(30), @@ -989,6 +991,7 @@ impl connector_mandate_id: Some(id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }), ), connector_metadata: None, diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs index 634a9feccf3..4556121db23 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -577,6 +577,7 @@ impl connector_mandate_id: Some(subscription_data.identifier.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }); Ok(Self { status, diff --git a/crates/router/src/connector/nuvei/transformers.rs b/crates/router/src/connector/nuvei/transformers.rs index 68c25f58acb..209d1a7476f 100644 --- a/crates/router/src/connector/nuvei/transformers.rs +++ b/crates/router/src/connector/nuvei/transformers.rs @@ -1607,6 +1607,7 @@ where connector_mandate_id: Some(id), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }), ), // we don't need to save session token for capture, void flow so ignoring if it is not present diff --git a/crates/router/src/connector/paybox.rs b/crates/router/src/connector/paybox.rs index a251679be85..dd54e185dac 100644 --- a/crates/router/src/connector/paybox.rs +++ b/crates/router/src/connector/paybox.rs @@ -11,7 +11,7 @@ use super::utils::{ }; use crate::{ configs::settings, - connector::utils, + connector::{utils, utils::PaymentMethodDataType}, core::{ errors::{self, CustomResult}, payments, @@ -60,6 +60,13 @@ impl api::PaymentsCompleteAuthorize for Paybox {} impl ConnectorIntegration for Paybox { + fn build_request( + &self, + _req: &types::PaymentsCancelRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Err(errors::ConnectorError::NotImplemented("Cancel/Void flow".to_string()).into()) + } } impl @@ -151,6 +158,14 @@ impl ConnectorValidation for Paybox { ), } } + fn validate_mandate_payment( + &self, + pm_type: Option, + pm_data: types::domain::payments::PaymentMethodData, + ) -> CustomResult<(), errors::ConnectorError> { + let mandate_supported_pmd = std::collections::HashSet::from([PaymentMethodDataType::Card]); + connector_utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id()) + } } impl ConnectorIntegration diff --git a/crates/router/src/connector/paybox/transformers.rs b/crates/router/src/connector/paybox/transformers.rs index b52a6e463f6..4b9c25f6dba 100644 --- a/crates/router/src/connector/paybox/transformers.rs +++ b/crates/router/src/connector/paybox/transformers.rs @@ -1,3 +1,4 @@ +use api_models::payments::AdditionalPaymentData; use bytes::Bytes; use common_utils::{ date_time::DateFormat, errors::CustomResult, ext_traits::ValueExt, types::MinorUnit, @@ -8,7 +9,7 @@ use hyperswitch_domain_models::{ router_data::ConnectorAuthType, router_response_types::RedirectForm, }; use hyperswitch_interfaces::consts; -use masking::{PeekInterface, Secret}; +use masking::{ExposeInterface, PeekInterface, Secret}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ @@ -16,7 +17,7 @@ use crate::{ self, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, RouterData, }, core::errors, - types::{self, api, domain, storage::enums}, + types::{self, api, domain, storage::enums, MandateReference}, }; pub struct PayboxRouterData { @@ -42,6 +43,10 @@ const SUCCESS_CODE: &str = "00000"; const VERSION_PAYBOX: &str = "00104"; const PAY_ORIGIN_INTERNET: &str = "024"; const THREE_DS_FAIL_CODE: &str = "00000000"; +const RECURRING_ORIGIN: &str = "027"; +const MANDATE_REQUEST: &str = "00056"; +const MANDATE_AUTH_ONLY: &str = "00051"; +const MANDATE_AUTH_AND_CAPTURE_ONLY: &str = "00053"; type Error = error_stack::Report; @@ -50,6 +55,13 @@ type Error = error_stack::Report; pub enum PayboxPaymentsRequest { Card(PaymentsRequest), CardThreeDs(ThreeDSPaymentsRequest), + Mandate(MandatePaymentRequest), +} + +#[derive(Debug, Serialize)] +pub struct CardMandateInfo { + pub card_exp_month: Secret, + pub card_exp_year: Secret, } #[derive(Debug, Serialize)] @@ -99,6 +111,10 @@ pub struct PaymentsRequest { #[serde(rename = "ID3D")] #[serde(skip_serializing_if = "Option::is_none")] pub three_ds_data: Option>, + + #[serde(rename = "REFABONNE")] + #[serde(skip_serializing_if = "Option::is_none")] + pub customer_id: Option>, } #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] @@ -367,8 +383,10 @@ impl TryFrom<&PayboxRouterData<&types::PaymentsAuthorizeRouterData>> for PayboxP let auth_data: PayboxAuthType = PayboxAuthType::try_from(&item.router_data.connector_auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; - let transaction_type = - get_transaction_type(item.router_data.request.capture_method)?; + let transaction_type = get_transaction_type( + item.router_data.request.capture_method, + item.router_data.request.is_mandate_payment(), + )?; let currency = diesel_models::enums::Currency::iso_4217(&item.router_data.request.currency) .to_string(); @@ -420,18 +438,80 @@ impl TryFrom<&PayboxRouterData<&types::PaymentsAuthorizeRouterData>> for PayboxP rank: auth_data.rang, key: auth_data.cle, three_ds_data: None, + customer_id: match item.router_data.request.is_mandate_payment() { + true => { + let reference_id = item + .router_data + .connector_mandate_request_reference_id + .clone() + .ok_or_else(|| { + errors::ConnectorError::MissingRequiredField { + field_name: "connector_mandate_request_reference_id", + } + })?; + Some(Secret::new(reference_id)) + } + false => None, + }, })) } } + domain::PaymentMethodData::MandatePayment => { + let mandate_data = extract_card_mandate_info( + item.router_data + .request + .additional_payment_method_data + .clone(), + )?; + Ok(Self::Mandate(MandatePaymentRequest::try_from(( + item, + mandate_data, + ))?)) + } _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), } } } -fn get_transaction_type(capture_method: Option) -> Result { - match capture_method { - Some(enums::CaptureMethod::Automatic) | None => Ok(AUTH_AND_CAPTURE_REQUEST.to_string()), - Some(enums::CaptureMethod::Manual) => Ok(AUTH_REQUEST.to_string()), +fn extract_card_mandate_info( + additional_payment_method_data: Option, +) -> Result { + match additional_payment_method_data { + Some(AdditionalPaymentData::Card(card_data)) => Ok(CardMandateInfo { + card_exp_month: card_data.card_exp_month.clone().ok_or_else(|| { + errors::ConnectorError::MissingRequiredField { + field_name: "card_exp_month", + } + })?, + card_exp_year: card_data.card_exp_year.clone().ok_or_else(|| { + errors::ConnectorError::MissingRequiredField { + field_name: "card_exp_year", + } + })?, + }), + _ => Err(errors::ConnectorError::MissingRequiredFields { + field_names: vec!["card_exp_month", "card_exp_year"], + } + .into()), + } +} + +fn get_transaction_type( + capture_method: Option, + is_mandate_request: bool, +) -> Result { + match (capture_method, is_mandate_request) { + (Some(enums::CaptureMethod::Automatic), false) | (None, false) => { + Ok(AUTH_AND_CAPTURE_REQUEST.to_string()) + } + (Some(enums::CaptureMethod::Automatic), true) | (None, true) => { + Err(errors::ConnectorError::NotSupported { + message: "Capture Not allowed in case of Creating the Subscriber".to_string(), + connector: "Paybox", + })? + } + (Some(enums::CaptureMethod::Manual), false) => Ok(AUTH_REQUEST.to_string()), + (Some(enums::CaptureMethod::Manual), true) => Ok(MANDATE_REQUEST.to_string()), _ => Err(errors::ConnectorError::CaptureMethodNotSupported)?, } } @@ -499,6 +579,11 @@ pub struct TransactionResponse { #[serde(rename = "COMMENTAIRE")] pub response_message: String, + #[serde(rename = "PORTEUR")] + pub carrier_id: Option>, + + #[serde(rename = "REFABONNE")] + pub customer_id: Option>, } pub fn parse_url_encoded_to_struct( @@ -658,7 +743,15 @@ impl response.paybox_order_id, ), redirection_data: Box::new(None), - mandate_reference: Box::new(None), + mandate_reference: Box::new(response.carrier_id.as_ref().map( + |pm: &Secret| MandateReference { + connector_mandate_id: Some(pm.clone().expose()), + payment_method_id: None, + mandate_metadata: None, + connector_mandate_request_reference_id: + response.customer_id.map(|secret| secret.expose()), + }, + )), connector_metadata: Some(serde_json::json!(PayboxMeta { connector_request_id: response.transaction_number.clone() })), @@ -917,7 +1010,16 @@ impl response.paybox_order_id, ), redirection_data: Box::new(None), - mandate_reference: Box::new(None), + mandate_reference: Box::new(response.carrier_id.as_ref().map(|pm| { + MandateReference { + connector_mandate_id: Some(pm.clone().expose()), + payment_method_id: None, + mandate_metadata: None, + connector_mandate_request_reference_id: response + .customer_id + .map(|secret| secret.expose()), + } + })), connector_metadata: Some(serde_json::json!(PayboxMeta { connector_request_id: response.transaction_number.clone() })), @@ -973,8 +1075,10 @@ impl TryFrom<&PayboxRouterData<&types::PaymentsCompleteAuthorizeRouterData>> for let auth_data: PayboxAuthType = PayboxAuthType::try_from(&item.router_data.connector_auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; - let transaction_type = - get_transaction_type(item.router_data.request.capture_method)?; + let transaction_type = get_transaction_type( + item.router_data.request.capture_method, + item.router_data.request.is_mandate_payment(), + )?; let currency = diesel_models::enums::Currency::iso_4217(&item.router_data.request.currency) .to_string(); @@ -1004,9 +1108,130 @@ impl TryFrom<&PayboxRouterData<&types::PaymentsCompleteAuthorizeRouterData>> for || Some(Secret::new(THREE_DS_FAIL_CODE.to_string())), |data| Some(data.clone()), ), + customer_id: match item.router_data.request.is_mandate_payment() { + true => Some(Secret::new(item.router_data.payment_id.clone())), + false => None, + }, }) } _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), } } } + +#[derive(Debug, Serialize)] +pub struct MandatePaymentRequest { + #[serde(rename = "DATEQ")] + pub date: String, + + #[serde(rename = "TYPE")] + pub transaction_type: String, + + #[serde(rename = "NUMQUESTION")] + pub paybox_request_number: String, + + #[serde(rename = "MONTANT")] + pub amount: MinorUnit, + + #[serde(rename = "REFERENCE")] + pub description_reference: String, + + #[serde(rename = "VERSION")] + pub version: String, + + #[serde(rename = "DEVISE")] + pub currency: String, + + #[serde(rename = "ACTIVITE")] + pub activity: String, + + #[serde(rename = "SITE")] + pub site: Secret, + + #[serde(rename = "RANG")] + pub rank: Secret, + + #[serde(rename = "CLE")] + pub key: Secret, + + #[serde(rename = "DATEVAL")] + pub cc_exp_date: Secret, + + #[serde(rename = "REFABONNE")] + pub customer_id: Secret, + + #[serde(rename = "PORTEUR")] + pub carrier_id: Secret, +} + +impl + TryFrom<( + &PayboxRouterData<&types::PaymentsAuthorizeRouterData>, + CardMandateInfo, + )> for MandatePaymentRequest +{ + type Error = error_stack::Report; + fn try_from( + (item, card_mandate_info): ( + &PayboxRouterData<&types::PaymentsAuthorizeRouterData>, + CardMandateInfo, + ), + ) -> Result { + let auth_data: PayboxAuthType = + PayboxAuthType::try_from(&item.router_data.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + let transaction_type = match item.router_data.request.capture_method { + Some(enums::CaptureMethod::Automatic) | None => { + Ok(MANDATE_AUTH_AND_CAPTURE_ONLY.to_string()) + } + Some(enums::CaptureMethod::Manual) => Ok(MANDATE_AUTH_ONLY.to_string()), + _ => Err(errors::ConnectorError::CaptureMethodNotSupported), + }?; + let currency = diesel_models::enums::Currency::iso_4217(&item.router_data.request.currency) + .to_string(); + let format_time = common_utils::date_time::format_date( + common_utils::date_time::now(), + DateFormat::DDMMYYYYHHmmss, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Self { + date: format_time.clone(), + transaction_type, + paybox_request_number: get_paybox_request_number()?, + amount: item.router_data.request.minor_amount, + description_reference: item.router_data.connector_request_reference_id.clone(), + version: VERSION_PAYBOX.to_string(), + currency, + activity: RECURRING_ORIGIN.to_string(), + site: auth_data.site, + rank: auth_data.rang, + key: auth_data.cle, + customer_id: Secret::new( + item.router_data + .request + .get_connector_mandate_request_reference_id()?, + ), + carrier_id: Secret::new(item.router_data.request.get_connector_mandate_id()?), + cc_exp_date: get_card_expiry_month_year_2_digit( + card_mandate_info.card_exp_month.clone(), + card_mandate_info.card_exp_year.clone(), + )?, + }) + } +} + +fn get_card_expiry_month_year_2_digit( + card_exp_month: Secret, + card_exp_year: Secret, +) -> Result, errors::ConnectorError> { + let year_2_digit = card_exp_year + .peek() + .get(..2) + .ok_or(errors::ConnectorError::RequestEncodingFailed)? + .to_string(); + Ok(Secret::new(format!( + "{}{}", + card_exp_month.peek(), + year_2_digit + ))) +} diff --git a/crates/router/src/connector/payme/transformers.rs b/crates/router/src/connector/payme/transformers.rs index ceeafb50251..2f8b78e281a 100644 --- a/crates/router/src/connector/payme/transformers.rs +++ b/crates/router/src/connector/payme/transformers.rs @@ -252,6 +252,7 @@ impl TryFrom<&PaymePaySaleResponse> for types::PaymentsResponseData { connector_mandate_id: Some(buyer_key.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, } })), connector_metadata: None, diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index fe69f43777e..af82bda3e6a 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -1709,7 +1709,7 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntent connector_mandate_ids, )) => ( None, - connector_mandate_ids.connector_mandate_id, + connector_mandate_ids.get_connector_mandate_id(), StripeBillingAddress::default(), get_payment_method_type_for_saved_payment_method_payment(item)?, ), @@ -2450,6 +2450,7 @@ impl connector_mandate_id, payment_method_id, mandate_metadata: None, + connector_mandate_request_reference_id: None, } }); @@ -2655,6 +2656,7 @@ impl connector_mandate_id, payment_method_id: Some(payment_method_id), mandate_metadata: None, + connector_mandate_request_reference_id: None, } }); @@ -2746,6 +2748,7 @@ impl connector_mandate_id, payment_method_id, mandate_metadata: None, + connector_mandate_request_reference_id: None, } }); let status = enums::AttemptStatus::from(item.response.status); diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 9e9e43734db..76def480cff 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -713,7 +713,7 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData { .as_ref() .and_then(|mandate_ids| match &mandate_ids.mandate_reference_id { Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => { - connector_mandate_ids.connector_mandate_id.clone() + connector_mandate_ids.get_connector_mandate_id() } Some(payments::MandateReferenceId::NetworkMandateId(_)) | None @@ -799,6 +799,7 @@ pub trait PaymentsAuthorizeRequestData { fn get_total_surcharge_amount(&self) -> Option; fn get_metadata_as_object(&self) -> Option; fn get_authentication_data(&self) -> Result; + fn get_connector_mandate_request_reference_id(&self) -> Result; } pub trait PaymentMethodTokenizationRequestData { @@ -861,7 +862,7 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { .as_ref() .and_then(|mandate_ids| match &mandate_ids.mandate_reference_id { Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => { - connector_mandate_ids.connector_mandate_id.clone() + connector_mandate_ids.get_connector_mandate_id() } Some(payments::MandateReferenceId::NetworkMandateId(_)) | None @@ -979,6 +980,21 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { .clone() .ok_or_else(missing_field_err("authentication_data")) } + + /// Attempts to retrieve the connector mandate reference ID as a `Result`. + fn get_connector_mandate_request_reference_id(&self) -> Result { + self.mandate_id + .as_ref() + .and_then(|mandate_ids| match &mandate_ids.mandate_reference_id { + Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => { + connector_mandate_ids.get_connector_mandate_request_reference_id() + } + Some(payments::MandateReferenceId::NetworkMandateId(_)) + | None + | Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) => None, + }) + .ok_or_else(missing_field_err("connector_mandate_request_reference_id")) + } } pub trait ConnectorCustomerData { @@ -1058,6 +1074,7 @@ pub trait PaymentsCompleteAuthorizeRequestData { fn get_redirect_response_payload(&self) -> Result; fn get_complete_authorize_url(&self) -> Result; fn is_mandate_payment(&self) -> bool; + fn get_connector_mandate_request_reference_id(&self) -> Result; } impl PaymentsCompleteAuthorizeRequestData for types::CompleteAuthorizeData { @@ -1098,6 +1115,20 @@ impl PaymentsCompleteAuthorizeRequestData for types::CompleteAuthorizeData { .and_then(|mandate_ids| mandate_ids.mandate_reference_id.as_ref()) .is_some() } + /// Attempts to retrieve the connector mandate reference ID as a `Result`. + fn get_connector_mandate_request_reference_id(&self) -> Result { + self.mandate_id + .as_ref() + .and_then(|mandate_ids| match &mandate_ids.mandate_reference_id { + Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => { + connector_mandate_ids.get_connector_mandate_request_reference_id() + } + Some(payments::MandateReferenceId::NetworkMandateId(_)) + | None + | Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) => None, + }) + .ok_or_else(missing_field_err("connector_mandate_request_reference_id")) + } } pub trait PaymentsSyncRequestData { @@ -1924,8 +1955,7 @@ pub trait MandateReferenceData { impl MandateReferenceData for payments::ConnectorMandateReferenceId { fn get_connector_mandate_id(&self) -> Result { - self.connector_mandate_id - .clone() + self.get_connector_mandate_id() .ok_or_else(missing_field_err("mandate_id")) } } diff --git a/crates/router/src/connector/wellsfargo/transformers.rs b/crates/router/src/connector/wellsfargo/transformers.rs index 4dd455d0da3..1ca6c7bd968 100644 --- a/crates/router/src/connector/wellsfargo/transformers.rs +++ b/crates/router/src/connector/wellsfargo/transformers.rs @@ -1787,6 +1787,7 @@ fn get_payment_response( .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }); Ok(types::PaymentsResponseData::TransactionResponse { @@ -1973,6 +1974,7 @@ impl .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, mandate_metadata: None, + connector_mandate_request_reference_id: None, }); let mut mandate_status = enums::AttemptStatus::foreign_from(( item.response diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index 6a5f5b88f45..090ddca961b 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -142,6 +142,9 @@ pub const DEFAULT_UNIFIED_ERROR_MESSAGE: &str = "Something went wrong"; // Recon's feature tag pub const RECON_FEATURE_TAG: &str = "RECONCILIATION AND SETTLEMENT"; +// Length of the unique reference ID generated for connector mandate requests +pub const CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH: usize = 18; + /// Vault Add request url #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub const ADD_VAULT_REQUEST_URL: &str = "/vault/add"; diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index 6013cdbe97f..6b8a378a070 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -184,6 +184,7 @@ pub fn construct_router_data( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }) } diff --git a/crates/router/src/core/fraud_check/flows/checkout_flow.rs b/crates/router/src/core/fraud_check/flows/checkout_flow.rs index 87e76b2c5fb..4249559aab6 100644 --- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs +++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs @@ -160,6 +160,7 @@ impl ConstructFlowSpecificData( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) } diff --git a/crates/router/src/core/fraud_check/flows/record_return.rs b/crates/router/src/core/fraud_check/flows/record_return.rs index f80f604daa4..3ef2b923208 100644 --- a/crates/router/src/core/fraud_check/flows/record_return.rs +++ b/crates/router/src/core/fraud_check/flows/record_return.rs @@ -128,6 +128,7 @@ impl ConstructFlowSpecificData( connector_wallets_details: router_data.connector_wallets_details, additional_merchant_data: router_data.additional_merchant_data, header_payload: router_data.header_payload, + connector_mandate_request_reference_id: router_data.connector_mandate_request_reference_id, } } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 3a66094081e..1bd3ddce735 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1,16 +1,17 @@ use std::marker::PhantomData; +// use api_models::{admin::ExtendedCardInfoConfig, enums::FrmSuggestion, payments::ExtendedCardInfo}; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use api_models::payment_methods::PaymentMethodsData; use api_models::{ admin::ExtendedCardInfoConfig, enums::FrmSuggestion, // payment_methods::PaymentMethodsData, - payments::{ExtendedCardInfo, GetAddressFromPaymentMethodData}, + payments::{ConnectorMandateReferenceId, ExtendedCardInfo, GetAddressFromPaymentMethodData}, }; -// use api_models::{admin::ExtendedCardInfoConfig, enums::FrmSuggestion, payments::ExtendedCardInfo}; -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -use api_models::{payment_methods::PaymentMethodsData, payments::AdditionalPaymentData}; use async_trait::async_trait; use common_utils::ext_traits::{AsyncExt, Encode, StringExt, ValueExt}; +use diesel_models::payment_attempt::ConnectorMandateReferenceId as DieselConnectorMandateReferenceId; use error_stack::{report, ResultExt}; use futures::FutureExt; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] @@ -23,6 +24,7 @@ use tracing_futures::Instrument; use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use crate::{ + consts, core::payment_methods::cards::create_encrypted_data, events::audit_events::{AuditEvent, AuditEventType}, }; @@ -45,6 +47,7 @@ use crate::{ api::{self, ConnectorCallType, PaymentIdTypeExt}, domain::{self}, storage::{self, enums as storage_enums}, + transformers::ForeignFrom, }, utils::{self, OptionExt}, }; @@ -573,6 +576,39 @@ impl GetTracker, api::PaymentsRequest> for Pa payment_method_info, } = mandate_details; + let additional_pm_data_from_locker = if let Some(ref pm) = payment_method_info { + let card_detail_from_locker: Option = pm + .payment_method_data + .clone() + .map(|x| x.into_inner().expose()) + .and_then(|v| { + v.parse_value("PaymentMethodsData") + .map_err(|err| { + router_env::logger::info!( + "PaymentMethodsData deserialization failed: {:?}", + err + ) + }) + .ok() + }) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); + card_detail_from_locker.map(|card_details| { + let additional_data = card_details.into(); + api_models::payments::AdditionalPaymentData::Card(Box::new(additional_data)) + }) + } else { + None + }; + payment_attempt.payment_method_data = additional_pm_data_from_locker + .as_ref() + .map(Encode::encode_to_value) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to encode additional pm data")?; + payment_attempt.payment_method = payment_method.or(payment_attempt.payment_method); payment_attempt.payment_method_type = payment_method_type @@ -679,14 +715,13 @@ impl GetTracker, api::PaymentsRequest> for Pa mandate_id: None, mandate_reference_id: Some( api_models::payments::MandateReferenceId::ConnectorMandateId( - api_models::payments::ConnectorMandateReferenceId { - connector_mandate_id: Some( - token.processor_payment_token.clone(), - ), - payment_method_id: None, - update_history: None, - mandate_metadata: None, - }, + ConnectorMandateReferenceId::new( + Some(token.processor_payment_token.clone()), // connector_mandate_id + None, // payment_method_id + None, // update_history + None, // mandate_metadata + None, // connector_mandate_request_reference_id + ), ), ), }) @@ -714,6 +749,17 @@ impl GetTracker, api::PaymentsRequest> for Pa .net_amount .set_order_tax_amount(order_tax_amount); + payment_attempt.connector_mandate_detail = Some( + DieselConnectorMandateReferenceId::foreign_from(ConnectorMandateReferenceId::new( + None, + None, + None, // update_history + None, // mandate_metadata + Some(common_utils::generate_id_with_len( + consts::CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH.to_owned(), + )), // connector_mandate_request_reference_id + )), + ); let payment_data = PaymentData { flow: PhantomData, payment_intent, @@ -1198,31 +1244,6 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to encode additional pm data")?; - let encode_additional_pm_to_value = if let Some(ref pm) = payment_data.payment_method_info { - let card_detail_from_locker: Option = pm - .payment_method_data - .clone() - .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) - .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), - _ => None, - }); - - card_detail_from_locker.and_then(|card_details| { - let additional_data = card_details.into(); - let additional_data_payment = - AdditionalPaymentData::Card(Box::new(additional_data)); - additional_data_payment - .encode_to_value() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to encode additional pm data") - .ok() - }) - } else { - None - }; - let customer_details = payment_data.payment_intent.customer_details.clone(); let business_sub_label = payment_data.payment_attempt.business_sub_label.clone(); let authentication_type = payment_data.payment_attempt.authentication_type; @@ -1278,7 +1299,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let m_payment_token = payment_token.clone(); let m_additional_pm_data = encoded_additional_pm_data .clone() - .or(encode_additional_pm_to_value); + .or(payment_data.payment_attempt.payment_method_data); let m_business_sub_label = business_sub_label.clone(); let m_straight_through_algorithm = straight_through_algorithm.clone(); let m_error_code = error_code.clone(); @@ -1350,6 +1371,9 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen surcharge_amount, tax_amount, ), + connector_mandate_detail: payment_data + .payment_attempt + .connector_mandate_detail, }, storage_scheme, ) diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 696bd1468e4..a9b5b571c5e 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -13,7 +13,10 @@ use common_utils::{ MinorUnit, }, }; -use diesel_models::ephemeral_key; +use diesel_models::{ + ephemeral_key, + payment_attempt::ConnectorMandateReferenceId as DieselConnectorMandateReferenceId, +}; use error_stack::{self, ResultExt}; use hyperswitch_domain_models::{ mandates::{MandateData, MandateDetails}, @@ -49,7 +52,7 @@ use crate::{ self, enums::{self, IntentStatus}, }, - transformers::ForeignTryFrom, + transformers::{ForeignFrom, ForeignTryFrom}, }, utils::{self, OptionExt}, }; @@ -344,7 +347,7 @@ impl GetTracker, api::PaymentsRequest> for Pa } #[cfg(feature = "v1")] - let payment_attempt = db + let mut payment_attempt = db .insert_payment_attempt(payment_attempt_new, storage_scheme) .await .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { @@ -399,12 +402,13 @@ impl GetTracker, api::PaymentsRequest> for Pa api_models::payments::MandateIds { mandate_id: Some(mandate_obj.mandate_id), mandate_reference_id: Some(api_models::payments::MandateReferenceId::ConnectorMandateId( - api_models::payments::ConnectorMandateReferenceId{ - connector_mandate_id: connector_id.connector_mandate_id, - payment_method_id: connector_id.payment_method_id, - update_history: None, - mandate_metadata: None, - } + api_models::payments::ConnectorMandateReferenceId::new( + connector_id.get_connector_mandate_id(), + connector_id.get_payment_method_id(), + None, + None, + connector_id.get_connector_mandate_request_reference_id(), + ) )) } }), @@ -436,14 +440,13 @@ impl GetTracker, api::PaymentsRequest> for Pa mandate_id: None, mandate_reference_id: Some( api_models::payments::MandateReferenceId::ConnectorMandateId( - api_models::payments::ConnectorMandateReferenceId { - connector_mandate_id: Some( - token.processor_payment_token.clone(), - ), - payment_method_id: None, - update_history: None, - mandate_metadata: None, - }, + api_models::payments::ConnectorMandateReferenceId::new( + Some(token.processor_payment_token.clone()), + None, + None, + None, + None, + ), ), ), }) @@ -500,8 +503,55 @@ impl GetTracker, api::PaymentsRequest> for Pa .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Card cobadge check failed due to an invalid card network regex")?; + let additional_pm_data_from_locker = if let Some(ref pm) = payment_method_info { + let card_detail_from_locker: Option = pm + .payment_method_data + .clone() + .map(|x| x.into_inner().expose()) + .and_then(|v| { + v.parse_value("PaymentMethodsData") + .map_err(|err| { + router_env::logger::info!( + "PaymentMethodsData deserialization failed: {:?}", + err + ) + }) + .ok() + }) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); + + card_detail_from_locker.map(|card_details| { + let additional_data = card_details.into(); + api_models::payments::AdditionalPaymentData::Card(Box::new(additional_data)) + }) + } else { + None + }; + + payment_attempt.payment_method_data = additional_pm_data_from_locker + .as_ref() + .map(Encode::encode_to_value) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to encode additional pm data")?; let amount = payment_attempt.get_total_amount().into(); + payment_attempt.connector_mandate_detail = + Some(DieselConnectorMandateReferenceId::foreign_from( + api_models::payments::ConnectorMandateReferenceId::new( + None, + None, + None, // update_history + None, // mandate_metadata + Some(common_utils::generate_id_with_len( + consts::CONNECTOR_MANDATE_REQUEST_REFERENCE_ID_LENGTH.to_owned(), + )), // connector_mandate_request_reference_id + ), + )); + let address = PaymentAddress::new( shipping_address.as_ref().map(From::from), billing_address.as_ref().map(From::from), diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 2c09ffd8ff2..0234b97032c 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -181,7 +181,11 @@ impl PostUpdateTracker, types::PaymentsAuthor .ok(); } }; - + let connector_mandate_reference_id = payment_data + .payment_attempt + .connector_mandate_detail + .as_ref() + .map(|detail| ConnectorMandateReferenceId::foreign_from(detail.clone())); let save_payment_call_future = Box::pin(tokenization::save_payment_method( state, connector_name.clone(), @@ -193,6 +197,7 @@ impl PostUpdateTracker, types::PaymentsAuthor billing_name.clone(), payment_method_billing_address, business_profile, + connector_mandate_reference_id.clone(), )); let is_connector_mandate = resp.request.customer_acceptance.is_some() @@ -307,6 +312,7 @@ impl PostUpdateTracker, types::PaymentsAuthor billing_name, payment_method_billing_address.as_ref(), &business_profile, + connector_mandate_reference_id, )) .await; @@ -571,7 +577,7 @@ impl PostUpdateTracker, types::PaymentsSyncData> for where F: 'b + Clone + Send + Sync, { - let (connector_mandate_id, mandate_metadata) = resp + let (connector_mandate_id, mandate_metadata, connector_mandate_request_reference_id) = resp .response .clone() .ok() @@ -584,17 +590,19 @@ impl PostUpdateTracker, types::PaymentsSyncData> for ( mandate_ref.connector_mandate_id.clone(), mandate_ref.mandate_metadata.clone(), + mandate_ref.connector_mandate_request_reference_id.clone(), ) }) } else { None } }) - .unwrap_or((None, None)); + .unwrap_or((None, None, None)); update_connector_mandate_details_for_the_flow( connector_mandate_id, mandate_metadata, + connector_mandate_request_reference_id, payment_data, )?; @@ -1066,6 +1074,12 @@ impl PostUpdateTracker, types::SetupMandateRequestDa field_name: "connector_name", } })?; + let connector_mandate_reference_id = payment_data + .payment_attempt + .connector_mandate_detail + .as_ref() + .map(|detail| ConnectorMandateReferenceId::foreign_from(detail.clone())); + let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone(); let tokenization::SavePaymentMethodDataResponse { payment_method_id, @@ -1082,6 +1096,7 @@ impl PostUpdateTracker, types::SetupMandateRequestDa billing_name, payment_method_billing_address, business_profile, + connector_mandate_reference_id, )) .await?; @@ -1186,7 +1201,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData where F: 'b + Clone + Send + Sync, { - let (connector_mandate_id, mandate_metadata) = resp + let (connector_mandate_id, mandate_metadata, connector_mandate_request_reference_id) = resp .response .clone() .ok() @@ -1199,16 +1214,18 @@ impl PostUpdateTracker, types::CompleteAuthorizeData ( mandate_ref.connector_mandate_id.clone(), mandate_ref.mandate_metadata.clone(), + mandate_ref.connector_mandate_request_reference_id.clone(), ) }) } else { None } }) - .unwrap_or((None, None)); + .unwrap_or((None, None, None)); update_connector_mandate_details_for_the_flow( connector_mandate_id, mandate_metadata, + connector_mandate_request_reference_id, payment_data, )?; @@ -1595,10 +1612,10 @@ async fn payment_response_update_tracker( }) .unwrap_or(false) { - let (connector_mandate_id, mandate_metadata) = payment_data.payment_attempt.connector_mandate_detail.clone() - .map(|cmr| (cmr.connector_mandate_id, cmr.mandate_metadata)) - .unwrap_or((None, None)); + let (connector_mandate_id, mandate_metadata,connector_mandate_request_reference_id) = payment_data.payment_attempt.connector_mandate_detail.clone() + .map(|cmr| (cmr.connector_mandate_id, cmr.mandate_metadata,cmr.connector_mandate_request_reference_id)) + .unwrap_or((None, None,None)); // Update the connector mandate details with the payment attempt connector mandate id let connector_mandate_details = tokenization::update_connector_mandate_details( @@ -1615,6 +1632,7 @@ async fn payment_response_update_tracker( payment_data.payment_attempt.merchant_connector_id.clone(), connector_mandate_id, mandate_metadata, + connector_mandate_request_reference_id )?; // Update the payment method table with the active mandate record payment_methods::cards::update_payment_method_connector_mandate_details( @@ -2318,15 +2336,33 @@ impl PostUpdateTracker, types::PaymentsAuthor fn update_connector_mandate_details_for_the_flow( connector_mandate_id: Option, mandate_metadata: Option, + connector_mandate_request_reference_id: Option, payment_data: &mut PaymentData, ) -> RouterResult<()> { + let mut original_connector_mandate_reference_id = payment_data + .payment_attempt + .connector_mandate_detail + .as_ref() + .map(|detail| ConnectorMandateReferenceId::foreign_from(detail.clone())); let connector_mandate_reference_id = if connector_mandate_id.is_some() { - Some(ConnectorMandateReferenceId { - connector_mandate_id: connector_mandate_id.clone(), - payment_method_id: None, - update_history: None, - mandate_metadata: mandate_metadata.clone(), - }) + if let Some(ref mut record) = original_connector_mandate_reference_id { + record.update( + connector_mandate_id, + None, + None, + mandate_metadata, + connector_mandate_request_reference_id, + ); + Some(record.clone()) + } else { + Some(ConnectorMandateReferenceId::new( + connector_mandate_id, + None, + None, + mandate_metadata, + connector_mandate_request_reference_id, + )) + } } else { None }; diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 0b530d4d054..9005e94c962 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -329,7 +329,13 @@ impl GetTracker, api::PaymentsRequest> for Pa api_models::payments::MandateIds { mandate_id: Some(mandate_obj.mandate_id), mandate_reference_id: Some(api_models::payments::MandateReferenceId::ConnectorMandateId( - api_models::payments::ConnectorMandateReferenceId {connector_mandate_id:connector_id.connector_mandate_id,payment_method_id:connector_id.payment_method_id, update_history: None, mandate_metadata:connector_id.mandate_metadata, }, + api_models::payments::ConnectorMandateReferenceId::new( + connector_id.get_connector_mandate_id(), // connector_mandate_id + connector_id.get_payment_method_id(), // payment_method_id + None, // update_history + connector_id.get_mandate_metadata(), // mandate_metadata + connector_id.get_connector_mandate_request_reference_id() // connector_mandate_request_reference_id + ) )) } }), diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index c14542de547..553eb672486 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -80,6 +80,7 @@ pub async fn save_payment_method( billing_name: Option>, payment_method_billing_address: Option<&api::Address>, business_profile: &domain::Profile, + mut original_connector_mandate_reference_id: Option, ) -> RouterResult where FData: mandate::MandateBehaviour + Clone, @@ -155,21 +156,23 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to serialize customer acceptance to value")?; - let (connector_mandate_id, mandate_metadata) = match responses { - types::PaymentsResponseData::TransactionResponse { - mandate_reference, .. - } => { - if let Some(ref mandate_ref) = *mandate_reference { - ( - mandate_ref.connector_mandate_id.clone(), - mandate_ref.mandate_metadata.clone(), - ) - } else { - (None, None) + let (connector_mandate_id, mandate_metadata, connector_mandate_request_reference_id) = + match responses { + types::PaymentsResponseData::TransactionResponse { + mandate_reference, .. + } => { + if let Some(ref mandate_ref) = *mandate_reference { + ( + mandate_ref.connector_mandate_id.clone(), + mandate_ref.mandate_metadata.clone(), + mandate_ref.connector_mandate_request_reference_id.clone(), + ) + } else { + (None, None, None) + } } - } - _ => (None, None), - }; + _ => (None, None, None), + }; let pm_id = if customer_acceptance.is_some() { let payment_method_create_request = @@ -689,12 +692,24 @@ where }; // check if there needs to be a config if yes then remove it to a different place let connector_mandate_reference_id = if connector_mandate_id.is_some() { - Some(ConnectorMandateReferenceId { - connector_mandate_id: connector_mandate_id.clone(), - payment_method_id: None, - update_history: None, - mandate_metadata: mandate_metadata.clone(), - }) + if let Some(ref mut record) = original_connector_mandate_reference_id { + record.update( + connector_mandate_id, + None, + None, + mandate_metadata, + connector_mandate_request_reference_id, + ); + Some(record.clone()) + } else { + Some(ConnectorMandateReferenceId::new( + connector_mandate_id, + None, + None, + mandate_metadata, + connector_mandate_request_reference_id, + )) + } } else { None }; @@ -728,6 +743,7 @@ pub async fn save_payment_method( _billing_name: Option>, _payment_method_billing_address: Option<&api::Address>, _business_profile: &domain::Profile, + _connector_mandate_request_reference_id: Option, ) -> RouterResult where FData: mandate::MandateBehaviour + Clone, @@ -1134,6 +1150,7 @@ pub fn add_connector_mandate_details_in_payment_method( merchant_connector_id: Option, connector_mandate_id: Option, mandate_metadata: Option, + connector_mandate_request_reference_id: Option, ) -> Option { let mut mandate_details = HashMap::new(); @@ -1149,6 +1166,7 @@ pub fn add_connector_mandate_details_in_payment_method( original_payment_authorized_currency: authorized_currency, mandate_metadata, connector_mandate_status: Some(ConnectorMandateStatus::Active), + connector_mandate_request_reference_id, }, ); Some(storage::PaymentsMandateReference(mandate_details)) @@ -1156,7 +1174,7 @@ pub fn add_connector_mandate_details_in_payment_method( None } } - +#[allow(clippy::too_many_arguments)] pub fn update_connector_mandate_details( mandate_details: Option, payment_method_type: Option, @@ -1165,6 +1183,7 @@ pub fn update_connector_mandate_details( merchant_connector_id: Option, connector_mandate_id: Option, mandate_metadata: Option, + connector_mandate_request_reference_id: Option, ) -> RouterResult> { let mandate_reference = match mandate_details { Some(mut payment_mandate_reference) => { @@ -1178,6 +1197,8 @@ pub fn update_connector_mandate_details( original_payment_authorized_currency: authorized_currency, mandate_metadata: mandate_metadata.clone(), connector_mandate_status: Some(ConnectorMandateStatus::Active), + connector_mandate_request_reference_id: connector_mandate_request_reference_id + .clone(), }; payment_mandate_reference @@ -1190,6 +1211,7 @@ pub fn update_connector_mandate_details( original_payment_authorized_currency: authorized_currency, mandate_metadata: mandate_metadata.clone(), connector_mandate_status: Some(ConnectorMandateStatus::Active), + connector_mandate_request_reference_id, }); Some(payment_mandate_reference) } else { @@ -1203,6 +1225,7 @@ pub fn update_connector_mandate_details( merchant_connector_id, connector_mandate_id, mandate_metadata, + connector_mandate_request_reference_id, ), }; let connector_mandate_details = mandate_reference diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index e059ebdb8b5..95d114d3d68 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -102,6 +102,12 @@ where customer_data: customer, }; + let connector_mandate_request_reference_id = payment_data + .payment_attempt + .connector_mandate_detail + .as_ref() + .and_then(|detail| detail.get_connector_mandate_request_reference_id()); + let router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_account.get_id().clone(), @@ -159,6 +165,7 @@ where integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id, }; Ok(router_data) } @@ -279,7 +286,13 @@ pub async fn construct_payment_router_data_for_authorize<'a>( merchant_order_reference_id: None, integrity_object: None, shipping_cost: payment_data.payment_intent.amount_details.shipping_cost, + additional_payment_method_data: None, }; + let connector_mandate_request_reference_id = payment_data + .payment_attempt + .connector_mandate_detail + .as_ref() + .and_then(|detail| detail.get_connector_mandate_request_reference_id()); // TODO: evaluate the fields in router data, if they are required or not let router_data = types::RouterData { @@ -355,6 +368,7 @@ pub async fn construct_payment_router_data_for_authorize<'a>( integrity_check: Ok(()), additional_merchant_data: None, header_payload, + connector_mandate_request_reference_id, }; Ok(router_data) @@ -501,6 +515,11 @@ where } else { payment_data.address }; + let connector_mandate_request_reference_id = payment_data + .payment_attempt + .connector_mandate_detail + .as_ref() + .and_then(|detail| detail.get_connector_mandate_request_reference_id()); crate::logger::debug!("unified address details {:?}", unified_address); @@ -570,6 +589,7 @@ where ) }), header_payload, + connector_mandate_request_reference_id, }; Ok(router_data) @@ -1538,7 +1558,7 @@ where .and_then(|mandate_ref| match mandate_ref { api_models::payments::MandateReferenceId::ConnectorMandateId( connector_mandate_reference_id, - ) => connector_mandate_reference_id.connector_mandate_id.clone(), + ) => connector_mandate_reference_id.get_connector_mandate_id(), _ => None, }) }); @@ -2112,7 +2132,18 @@ impl TryFrom> for types::PaymentsAuthoriz payment_data.creds_identifier.as_deref(), )); - // payment_method_data is not required during recurring mandate payment, in such case keep default PaymentMethodData as MandatePayment + let additional_payment_method_data = if payment_data.mandate_id.is_some() { + let parsed_additional_payment_data: Option = + payment_data.payment_attempt + .payment_method_data + .as_ref().map(|data| data.clone().parse_value("AdditionalPaymentData")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to parse AdditionalPaymentData from payment_data.payment_attempt.payment_method_data")?; + parsed_additional_payment_data + } else { + None + }; let payment_method_data = payment_data.payment_method_data.or_else(|| { if payment_data.mandate_id.is_some() { Some(domain::PaymentMethodData::MandatePayment) @@ -2198,6 +2229,7 @@ impl TryFrom> for types::PaymentsAuthoriz charges, merchant_order_reference_id, integrity_object: None, + additional_payment_method_data, shipping_cost, }) } @@ -3277,20 +3309,23 @@ impl ForeignFrom impl ForeignFrom for ConnectorMandateReferenceId { fn foreign_from(value: DieselConnectorMandateReferenceId) -> Self { - Self { - connector_mandate_id: value.connector_mandate_id, - payment_method_id: value.payment_method_id, - update_history: None, - mandate_metadata: value.mandate_metadata, - } + Self::new( + value.connector_mandate_id, + value.payment_method_id, + None, + value.mandate_metadata, + value.connector_mandate_request_reference_id, + ) } } impl ForeignFrom for DieselConnectorMandateReferenceId { fn foreign_from(value: ConnectorMandateReferenceId) -> Self { Self { - connector_mandate_id: value.connector_mandate_id, - payment_method_id: value.payment_method_id, - mandate_metadata: value.mandate_metadata, + connector_mandate_id: value.get_connector_mandate_id(), + payment_method_id: value.get_payment_method_id(), + mandate_metadata: value.get_mandate_metadata(), + connector_mandate_request_reference_id: value + .get_connector_mandate_request_reference_id(), } } } diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index def86907911..abd602760fa 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -217,6 +217,7 @@ pub async fn construct_payout_router_data<'a, F>( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) @@ -395,6 +396,7 @@ pub async fn construct_refund_router_data<'a, F>( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) @@ -705,6 +707,7 @@ pub async fn construct_accept_dispute_router_data<'a>( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) } @@ -800,6 +803,7 @@ pub async fn construct_submit_evidence_router_data<'a>( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) } @@ -901,6 +905,7 @@ pub async fn construct_upload_file_router_data<'a>( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) } @@ -1022,6 +1027,7 @@ pub async fn construct_payments_dynamic_tax_calculation_router_data<'a, F: Clone integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) } @@ -1120,6 +1126,7 @@ pub async fn construct_defend_dispute_router_data<'a>( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) } @@ -1212,6 +1219,7 @@ pub async fn construct_retrieve_file_router_data<'a>( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) } diff --git a/crates/router/src/core/webhooks/utils.rs b/crates/router/src/core/webhooks/utils.rs index 3d71f5f5553..fff675503ad 100644 --- a/crates/router/src/core/webhooks/utils.rs +++ b/crates/router/src/core/webhooks/utils.rs @@ -122,6 +122,7 @@ pub async fn construct_webhook_router_data<'a>( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, }; Ok(router_data) } diff --git a/crates/router/src/services/conversion_impls.rs b/crates/router/src/services/conversion_impls.rs index 22930916093..33c655eed78 100644 --- a/crates/router/src/services/conversion_impls.rs +++ b/crates/router/src/services/conversion_impls.rs @@ -76,6 +76,7 @@ fn get_default_router_data( integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, } } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 1f7c635da00..230ac753110 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -879,6 +879,7 @@ impl ForeignFrom<&SetupMandateRouterData> for PaymentsAuthorizeData { charges: None, // TODO: allow charges on mandates? merchant_order_reference_id: None, integrity_object: None, + additional_payment_method_data: None, shipping_cost: data.request.shipping_cost, } } @@ -936,6 +937,9 @@ impl ForeignFrom<(&RouterData, T2) integrity_check: Ok(()), additional_merchant_data: data.additional_merchant_data.clone(), header_payload: data.header_payload.clone(), + connector_mandate_request_reference_id: data + .connector_mandate_request_reference_id + .clone(), } } } @@ -1000,6 +1004,7 @@ impl integrity_check: Ok(()), additional_merchant_data: data.additional_merchant_data.clone(), header_payload: data.header_payload.clone(), + connector_mandate_request_reference_id: None, } } } diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index 052bd582317..a472296d2a7 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -57,6 +57,7 @@ impl VerifyConnectorData { charges: None, merchant_order_reference_id: None, integrity_object: None, + additional_payment_method_data: None, shipping_cost: None, } } @@ -116,6 +117,7 @@ impl VerifyConnectorData { integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, } } } diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs index 20d0ee74b01..7739c0f0ba8 100644 --- a/crates/router/src/types/storage/payment_method.rs +++ b/crates/router/src/types/storage/payment_method.rs @@ -126,6 +126,7 @@ pub struct PaymentsMandateReferenceRecord { pub original_payment_authorized_currency: Option, pub mandate_metadata: Option, pub connector_mandate_status: Option, + pub connector_mandate_request_reference_id: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index 5d48a0188ff..6f4855d1e22 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -128,6 +128,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, } } @@ -197,6 +198,7 @@ fn construct_refund_router_data() -> types::RefundsRouterData { integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, } } diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index eccade536c4..52218b211ac 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -546,6 +546,7 @@ pub trait ConnectorActions: Connector { integrity_check: Ok(()), additional_merchant_data: None, header_payload: None, + connector_mandate_request_reference_id: None, } } @@ -947,6 +948,7 @@ impl Default for PaymentAuthorizeType { charges: None, integrity_object: None, merchant_order_reference_id: None, + additional_payment_method_data: None, shipping_cost: None, }; Self(data)