diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index b0866cfb26..535a5abffc 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1042,6 +1042,7 @@ pub struct ConnectorMandateReferenceId { pub connector_mandate_id: Option, pub payment_method_id: Option, pub update_history: Option>, + pub mandate_metadata: Option, } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)] diff --git a/crates/hyperswitch_connectors/src/connectors/deutschebank.rs b/crates/hyperswitch_connectors/src/connectors/deutschebank.rs index 30e67babe8..a861155afb 100644 --- a/crates/hyperswitch_connectors/src/connectors/deutschebank.rs +++ b/crates/hyperswitch_connectors/src/connectors/deutschebank.rs @@ -50,7 +50,10 @@ use transformers as deutschebank; use crate::{ constants::headers, types::ResponseRouterData, - utils::{self, PaymentsCompleteAuthorizeRequestData, RefundsRequestData}, + utils::{ + self, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, + RefundsRequestData, + }, }; #[derive(Clone)] @@ -182,6 +185,16 @@ impl ConnectorValidation for Deutschebank { ), } } + + fn validate_mandate_payment( + &self, + pm_type: Option, + pm_data: hyperswitch_domain_models::payment_method_data::PaymentMethodData, + ) -> CustomResult<(), errors::ConnectorError> { + let mandate_supported_pmd = + std::collections::HashSet::from([utils::PaymentMethodDataType::SepaBankDebit]); + utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id()) + } } impl ConnectorIntegration for Deutschebank { @@ -302,13 +315,26 @@ impl ConnectorIntegration CustomResult { - Ok(format!( - "{}/services/v2.1/managedmandate", - self.base_url(connectors) - )) + if req.request.connector_mandate_id().is_none() { + Ok(format!( + "{}/services/v2.1/managedmandate", + self.base_url(connectors) + )) + } else { + let event_id = req.connector_request_reference_id.clone(); + let tx_action = if req.request.is_auto_capture()? { + "authorization" + } else { + "preauthorization" + }; + Ok(format!( + "{}/services/v2.1/payment/event/{event_id}/directdebit/{tx_action}", + self.base_url(connectors) + )) + } } fn get_request_body( @@ -356,17 +382,31 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult { - let response: deutschebank::DeutschebankMandatePostResponse = res - .response - .parse_struct("Deutschebank PaymentsAuthorizeResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - event_builder.map(|i| i.set_response_body(&response)); - router_env::logger::info!(connector_response=?response); - RouterData::try_from(ResponseRouterData { - response, - data: data.clone(), - http_code: res.status_code, - }) + if data.request.connector_mandate_id().is_none() { + let response: deutschebank::DeutschebankMandatePostResponse = res + .response + .parse_struct("DeutschebankMandatePostResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } else { + let response: deutschebank::DeutschebankPaymentsResponse = res + .response + .parse_struct("DeutschebankPaymentsAuthorizeResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } } fn get_error_response( diff --git a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs index dc146dc38d..8b3837edd6 100644 --- a/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/deutschebank/transformers.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; use common_enums::enums; -use common_utils::{pii::Email, types::MinorUnit}; +use common_utils::{ext_traits::ValueExt, pii::Email, types::MinorUnit}; +use error_stack::ResultExt; use hyperswitch_domain_models::{ payment_method_data::{BankDebitData, PaymentMethodData}, router_data::{AccessToken, ConnectorAuthType, RouterData}, @@ -13,7 +14,9 @@ use hyperswitch_domain_models::{ CompleteAuthorizeData, PaymentsAuthorizeData, PaymentsCaptureData, PaymentsSyncData, ResponseId, }, - router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData}, + router_response_types::{ + MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData, + }, types::{ PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData, PaymentsCompleteAuthorizeRouterData, RefundsRouterData, @@ -26,8 +29,8 @@ use serde::{Deserialize, Serialize}; use crate::{ types::{PaymentsCancelResponseRouterData, RefundsResponseRouterData, ResponseRouterData}, utils::{ - AddressDetailsData, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, - RefundsRequestData, RouterData as OtherRouterData, + self, AddressDetailsData, PaymentsAuthorizeRequestData, + PaymentsCompleteAuthorizeRequestData, RefundsRequestData, RouterData as OtherRouterData, }, }; @@ -113,7 +116,7 @@ pub enum DeutschebankSEPAApproval { } #[derive(Debug, Serialize, PartialEq)] -pub struct DeutschebankPaymentsRequest { +pub struct DeutschebankMandatePostRequest { approval_by: DeutschebankSEPAApproval, email_address: Email, iban: Secret, @@ -121,6 +124,13 @@ pub struct DeutschebankPaymentsRequest { last_name: Secret, } +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum DeutschebankPaymentsRequest { + MandatePost(DeutschebankMandatePostRequest), + DirectDebit(DeutschebankDirectDebitRequest), +} + impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>> for DeutschebankPaymentsRequest { @@ -128,16 +138,71 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsAuthorizeRouterData>> fn try_from( item: &DeutschebankRouterData<&PaymentsAuthorizeRouterData>, ) -> Result { - let billing_address = item.router_data.get_billing_address()?; - match item.router_data.request.payment_method_data.clone() { - PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { iban, .. }) => Ok(Self { - approval_by: DeutschebankSEPAApproval::Click, - email_address: item.router_data.request.get_email()?, - iban, - first_name: billing_address.get_first_name()?.clone(), - last_name: billing_address.get_last_name()?.clone(), - }), - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + match item + .router_data + .request + .mandate_id + .clone() + .and_then(|mandate_id| mandate_id.mandate_reference_id) + { + None => { + if item.router_data.request.is_mandate_payment() { + match item.router_data.request.payment_method_data.clone() { + PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { + iban, .. + }) => { + let billing_address = item.router_data.get_billing_address()?; + Ok(Self::MandatePost(DeutschebankMandatePostRequest { + approval_by: DeutschebankSEPAApproval::Click, + email_address: item.router_data.request.get_email()?, + iban, + first_name: billing_address.get_first_name()?.clone(), + last_name: billing_address.get_last_name()?.clone(), + })) + } + _ => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("deutschebank"), + ) + .into()), + } + } else { + Err(errors::ConnectorError::MissingRequiredField { + field_name: "setup_future_usage or customer_acceptance.acceptance_type", + } + .into()) + } + } + Some(api_models::payments::MandateReferenceId::ConnectorMandateId(mandate_data)) => { + let mandate_metadata: DeutschebankMandateMetadata = mandate_data + .mandate_metadata + .ok_or(errors::ConnectorError::MissingConnectorMandateMetadata)? + .clone() + .parse_value("DeutschebankMandateMetadata") + .change_context(errors::ConnectorError::ParsingFailed)?; + Ok(Self::DirectDebit(DeutschebankDirectDebitRequest { + amount_total: DeutschebankAmount { + amount: item.amount, + currency: item.router_data.request.currency, + }, + means_of_payment: DeutschebankMeansOfPayment { + bank_account: DeutschebankBankAccount { + account_holder: mandate_metadata.account_holder, + iban: mandate_metadata.iban, + }, + }, + mandate: DeutschebankMandate { + reference: mandate_metadata.reference, + signed_on: mandate_metadata.signed_on, + }, + })) + } + Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_)) + | Some(api_models::payments::MandateReferenceId::NetworkMandateId(_)) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("deutschebank"), + ) + .into()) + } } } } @@ -176,6 +241,14 @@ impl From for common_enums::AttemptStatus { } } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct DeutschebankMandateMetadata { + account_holder: Secret, + iban: Secret, + reference: Secret, + signed_on: String, +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct DeutschebankMandatePostResponse { rc: String, @@ -207,14 +280,14 @@ impl PaymentsResponseData, >, ) -> Result { - let signed_on = match item.response.approval_date { + let signed_on = match item.response.approval_date.clone() { Some(date) => date.chars().take(10).collect(), None => time::OffsetDateTime::now_utc().date().to_string(), }; - match item.response.reference { + match item.response.reference.clone() { Some(reference) => Ok(Self { status: if item.response.rc == "0" { - match item.response.state { + match item.response.state.clone() { Some(state) => common_enums::AttemptStatus::from(state), None => common_enums::AttemptStatus::Failure, } @@ -227,11 +300,29 @@ impl endpoint: item.data.request.get_complete_authorize_url()?, method: common_utils::request::Method::Get, form_fields: HashMap::from([ - ("reference".to_string(), reference), - ("signed_on".to_string(), signed_on), + ("reference".to_string(), reference.clone()), + ("signed_on".to_string(), signed_on.clone()), ]), }), - mandate_reference: None, + mandate_reference: Some(MandateReference { + connector_mandate_id: item.response.mandate_id, + payment_method_id: None, + mandate_metadata: Some(serde_json::json!(DeutschebankMandateMetadata { + account_holder: item.data.get_billing_address()?.get_full_name()?, + iban: match item.data.request.payment_method_data.clone() { + PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { + iban, + .. + }) => Ok(iban), + _ => Err(errors::ConnectorError::MissingRequiredField { + field_name: + "payment_method_data.bank_debit.sepa_bank_debit.iban" + }), + }?, + reference: Secret::from(reference), + signed_on, + })), + }), connector_metadata: None, network_txn_id: None, connector_response_reference_id: None, @@ -248,6 +339,49 @@ impl } } +impl + TryFrom< + ResponseRouterData< + Authorize, + DeutschebankPaymentsResponse, + PaymentsAuthorizeData, + PaymentsResponseData, + >, + > for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData< + Authorize, + DeutschebankPaymentsResponse, + PaymentsAuthorizeData, + PaymentsResponseData, + >, + ) -> Result { + Ok(Self { + status: if item.response.rc == "0" { + match item.data.request.is_auto_capture()? { + true => common_enums::AttemptStatus::Charged, + false => common_enums::AttemptStatus::Authorized, + } + } else { + common_enums::AttemptStatus::Failure + }, + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(item.response.tx_id), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } +} + #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct DeutschebankAmount { amount: MinorUnit, @@ -268,7 +402,7 @@ pub struct DeutschebankBankAccount { #[derive(Debug, Serialize, PartialEq)] pub struct DeutschebankMandate { reference: Secret, - signed_on: Secret, + signed_on: String, } #[derive(Debug, Serialize, PartialEq)] @@ -319,14 +453,12 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsCompleteAuthorizeRouterData>> })? .to_owned(), ); - let signed_on = Secret::from( - queries_params - .get("signed_on") - .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "signed_on", - })? - .to_owned(), - ); + let signed_on = queries_params + .get("signed_on") + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "signed_on", + })? + .to_owned(); match item.router_data.request.payment_method_data.clone() { Some(PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { iban, .. })) => { @@ -349,7 +481,10 @@ impl TryFrom<&DeutschebankRouterData<&PaymentsCompleteAuthorizeRouterData>> }, }) } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + _ => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("deutschebank"), + ) + .into()), } } } diff --git a/crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs index fdec3d0e1e..7f88903e86 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiservemea/transformers.rs @@ -170,13 +170,6 @@ pub enum ResponseType { UnsupportedMediaType, } -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum FiservemeaResponseType { - TransactionResponse, - ErrorResponse, -} - #[derive(Debug, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum FiservemeaTransactionType { @@ -329,7 +322,7 @@ fn map_status( pub struct FiservemeaPaymentsResponse { response_type: Option, #[serde(rename = "type")] - fiservemea_type: Option, + fiservemea_type: Option, client_request_id: Option, api_trace_id: Option, ipg_transaction_id: String, @@ -526,7 +519,7 @@ pub struct FiservemeaError { #[derive(Debug, Serialize, Deserialize)] pub struct FiservemeaErrorResponse { #[serde(rename = "type")] - fiservemea_type: Option, + fiservemea_type: Option, client_request_id: Option, api_trace_id: Option, pub response_type: Option, diff --git a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs index b400555305..56da53c287 100644 --- a/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/novalnet/transformers.rs @@ -787,6 +787,7 @@ impl MandateReference { connector_mandate_id: Some(id.clone()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index df0b63833d..a9451f6156 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -242,6 +242,7 @@ pub struct RecurringMandatePaymentData { pub payment_method_type: Option, //required for making recurring payment using saved payment method through stripe pub original_payment_authorized_amount: Option, pub original_payment_authorized_currency: Option, + pub mandate_metadata: 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 cb682514c8..79a78efb53 100644 --- a/crates/hyperswitch_domain_models/src/router_response_types.rs +++ b/crates/hyperswitch_domain_models/src/router_response_types.rs @@ -82,6 +82,7 @@ pub struct TaxCalculationResponseData { pub struct MandateReference { pub connector_mandate_id: Option, pub payment_method_id: Option, + pub mandate_metadata: Option, } #[derive(Debug, Clone)] diff --git a/crates/hyperswitch_interfaces/src/errors.rs b/crates/hyperswitch_interfaces/src/errors.rs index e36707af6b..06f197ebf0 100644 --- a/crates/hyperswitch_interfaces/src/errors.rs +++ b/crates/hyperswitch_interfaces/src/errors.rs @@ -56,6 +56,8 @@ pub enum ConnectorError { CaptureMethodNotSupported, #[error("Missing connector mandate ID")] MissingConnectorMandateID, + #[error("Missing connector mandate metadata")] + MissingConnectorMandateMetadata, #[error("Missing connector transaction ID")] MissingConnectorTransactionID, #[error("Missing connector refund ID")] diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index 5842143ad4..46f312b38f 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -746,6 +746,7 @@ impl .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(Self { diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index e915d05c35..361ebece29 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -3294,6 +3294,7 @@ pub fn get_adyen_response( .map(|mandate_id| types::MandateReference { connector_mandate_id: Some(mandate_id.expose()), payment_method_id: None, + mandate_metadata: 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 57563c276f..868409dafc 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -398,6 +398,7 @@ impl format!("{customer_profile_id}-{payment_profile_id}") }), payment_method_id: None, + mandate_metadata: None, }, ), connector_metadata: None, @@ -1109,6 +1110,7 @@ impl }, ), payment_method_id: None, + mandate_metadata: None, } }); diff --git a/crates/router/src/connector/bamboraapac/transformers.rs b/crates/router/src/connector/bamboraapac/transformers.rs index 3c19769930..819c918338 100644 --- a/crates/router/src/connector/bamboraapac/transformers.rs +++ b/crates/router/src/connector/bamboraapac/transformers.rs @@ -280,6 +280,7 @@ impl Some(types::MandateReference { connector_mandate_id, payment_method_id: None, + mandate_metadata: None, }) } else { None @@ -463,6 +464,7 @@ impl mandate_reference: Some(types::MandateReference { connector_mandate_id: Some(connector_mandate_id), payment_method_id: None, + mandate_metadata: 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 46a522c737..f47ab4139e 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -359,6 +359,7 @@ impl .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, } }); let mut mandate_status = @@ -1487,6 +1488,7 @@ fn get_payment_response( .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(types::PaymentsResponseData::TransactionResponse { diff --git a/crates/router/src/connector/braintree/transformers.rs b/crates/router/src/connector/braintree/transformers.rs index 22982ceb21..05d8c1c559 100644 --- a/crates/router/src/connector/braintree/transformers.rs +++ b/crates/router/src/connector/braintree/transformers.rs @@ -443,6 +443,7 @@ impl MandateReference { connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, @@ -617,6 +618,7 @@ impl MandateReference { connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, @@ -698,6 +700,7 @@ impl MandateReference { connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, @@ -761,6 +764,7 @@ impl MandateReference { connector_mandate_id: Some(pm.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, } }), connector_metadata: None, diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index fc35cab773..9ad4f0604a 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -2227,6 +2227,7 @@ fn get_payment_response( .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(types::PaymentsResponseData::TransactionResponse { @@ -2947,6 +2948,7 @@ impl .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: 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 efc6661b44..19568ae467 100644 --- a/crates/router/src/connector/globalpay/transformers.rs +++ b/crates/router/src/connector/globalpay/transformers.rs @@ -261,6 +261,7 @@ fn get_payment_response( .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }) }); match status { diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index f3250602db..ff2f9d9502 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -512,6 +512,7 @@ impl let mandate_reference = Some(MandateReference { connector_mandate_id: Some(item.response.mandates.id.clone().expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(Self { response: Ok(types::PaymentsResponseData::TransactionResponse { @@ -664,6 +665,7 @@ impl let mandate_reference = MandateReference { connector_mandate_id: Some(item.data.request.get_connector_mandate_id()?), payment_method_id: None, + mandate_metadata: 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 c44cf14ab6..547a38ad84 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -982,6 +982,7 @@ impl .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }), connector_metadata: None, network_txn_id: None, diff --git a/crates/router/src/connector/nexinets/transformers.rs b/crates/router/src/connector/nexinets/transformers.rs index 43cb4cecec..55b2fbe417 100644 --- a/crates/router/src/connector/nexinets/transformers.rs +++ b/crates/router/src/connector/nexinets/transformers.rs @@ -358,6 +358,7 @@ impl .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(Self { status: enums::AttemptStatus::foreign_from(( diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs index b7df6829da..5d98717427 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -574,6 +574,7 @@ impl .map(|subscription_data| types::MandateReference { connector_mandate_id: Some(subscription_data.identifier.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(Self { status, diff --git a/crates/router/src/connector/nuvei/transformers.rs b/crates/router/src/connector/nuvei/transformers.rs index 2547443484..f280d3a42a 100644 --- a/crates/router/src/connector/nuvei/transformers.rs +++ b/crates/router/src/connector/nuvei/transformers.rs @@ -1602,6 +1602,7 @@ where .map(|id| types::MandateReference { connector_mandate_id: Some(id), payment_method_id: None, + mandate_metadata: None, }), // we don't need to save session token for capture, void flow so ignoring if it is not present connector_metadata: if let Some(token) = response.session_token { diff --git a/crates/router/src/connector/payeezy/transformers.rs b/crates/router/src/connector/payeezy/transformers.rs index 0bb10c9845..8c2ad2ef97 100644 --- a/crates/router/src/connector/payeezy/transformers.rs +++ b/crates/router/src/connector/payeezy/transformers.rs @@ -418,6 +418,7 @@ impl .map(|id| types::MandateReference { connector_mandate_id: Some(id.expose()), payment_method_id: None, + mandate_metadata: None, }); let status = enums::AttemptStatus::foreign_from(( item.response.transaction_status, diff --git a/crates/router/src/connector/payme/transformers.rs b/crates/router/src/connector/payme/transformers.rs index c12e478b08..e0ab598d76 100644 --- a/crates/router/src/connector/payme/transformers.rs +++ b/crates/router/src/connector/payme/transformers.rs @@ -250,6 +250,7 @@ impl TryFrom<&PaymePaySaleResponse> for types::PaymentsResponseData { mandate_reference: value.buyer_key.clone().map(|buyer_key| MandateReference { connector_mandate_id: Some(buyer_key.expose()), payment_method_id: None, + mandate_metadata: None, }), connector_metadata: None, network_txn_id: None, diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 910cb94e92..2129ad327c 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -2389,6 +2389,7 @@ impl types::MandateReference { connector_mandate_id, payment_method_id, + mandate_metadata: None, } }); @@ -2583,6 +2584,7 @@ impl types::MandateReference { connector_mandate_id, payment_method_id: Some(payment_method_id), + mandate_metadata: None, } }); @@ -2673,6 +2675,7 @@ impl types::MandateReference { connector_mandate_id, payment_method_id, + mandate_metadata: None, } }); let status = enums::AttemptStatus::from(item.response.status); diff --git a/crates/router/src/connector/wellsfargo/transformers.rs b/crates/router/src/connector/wellsfargo/transformers.rs index 761fc43982..be3cf4a1ce 100644 --- a/crates/router/src/connector/wellsfargo/transformers.rs +++ b/crates/router/src/connector/wellsfargo/transformers.rs @@ -1778,6 +1778,7 @@ fn get_payment_response( .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, }); Ok(types::PaymentsResponseData::TransactionResponse { @@ -1963,6 +1964,7 @@ impl .payment_instrument .map(|payment_instrument| payment_instrument.id.expose()), payment_method_id: None, + mandate_metadata: None, }); let mut mandate_status = enums::AttemptStatus::foreign_from(( item.response diff --git a/crates/router/src/core/errors/utils.rs b/crates/router/src/core/errors/utils.rs index d2812447ed..549857494f 100644 --- a/crates/router/src/core/errors/utils.rs +++ b/crates/router/src/core/errors/utils.rs @@ -189,6 +189,7 @@ impl ConnectorErrorExt for error_stack::Result | errors::ConnectorError::FlowNotSupported { .. } | errors::ConnectorError::CaptureMethodNotSupported | errors::ConnectorError::MissingConnectorMandateID + | errors::ConnectorError::MissingConnectorMandateMetadata | errors::ConnectorError::MissingConnectorTransactionID | errors::ConnectorError::MissingConnectorRefundID | errors::ConnectorError::MissingApplePayTokenData @@ -288,6 +289,7 @@ impl ConnectorErrorExt for error_stack::Result errors::ConnectorError::FailedToObtainCertificateKey | errors::ConnectorError::CaptureMethodNotSupported | errors::ConnectorError::MissingConnectorMandateID | + errors::ConnectorError::MissingConnectorMandateMetadata | errors::ConnectorError::MissingConnectorTransactionID | errors::ConnectorError::MissingConnectorRefundID | errors::ConnectorError::MissingApplePayTokenData | @@ -376,6 +378,7 @@ impl ConnectorErrorExt for error_stack::Result | errors::ConnectorError::FlowNotSupported { .. } | errors::ConnectorError::CaptureMethodNotSupported | errors::ConnectorError::MissingConnectorMandateID + | errors::ConnectorError::MissingConnectorMandateMetadata | errors::ConnectorError::MissingConnectorTransactionID | errors::ConnectorError::MissingConnectorRefundID | errors::ConnectorError::MissingApplePayTokenData diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 85ae864137..3a55bf7777 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -4093,6 +4093,9 @@ where payment_method_info.get_id().clone(), ), update_history: None, + mandate_metadata: mandate_reference_record + .mandate_metadata + .clone(), }, )); payment_data.set_recurring_mandate_payment_data( @@ -4103,6 +4106,8 @@ where .original_payment_authorized_amount, original_payment_authorized_currency: mandate_reference_record .original_payment_authorized_currency, + mandate_metadata: mandate_reference_record + .mandate_metadata.clone(), }); connector_choice = Some((connector_data, mandate_reference_id.clone())); diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index e9dca13dc8..ac6c236f53 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -802,6 +802,7 @@ pub async fn get_token_for_recurring_mandate( payment_method_type, original_payment_authorized_amount, original_payment_authorized_currency, + mandate_metadata: None, }), payment_method_type: payment_method.payment_method_type, mandate_connector: Some(mandate_connector_details), @@ -816,6 +817,7 @@ pub async fn get_token_for_recurring_mandate( payment_method_type, original_payment_authorized_amount, original_payment_authorized_currency, + mandate_metadata: None, }), payment_method_type: payment_method.payment_method_type, mandate_connector: Some(mandate_connector_details), diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index c75cc29ce5..658a02f7d0 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -681,6 +681,7 @@ impl GetTracker, api::PaymentsRequest> for Pa ), payment_method_id: None, update_history: None, + mandate_metadata: None, }, ), ), diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 0731def945..4b931d68f1 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -398,7 +398,8 @@ impl GetTracker, api::PaymentsRequest> for Pa api_models::payments::ConnectorMandateReferenceId{ connector_mandate_id: connector_id.connector_mandate_id, payment_method_id: connector_id.payment_method_id, - update_history: None + update_history: None, + mandate_metadata: None, } )) } @@ -437,6 +438,7 @@ impl GetTracker, api::PaymentsRequest> for Pa ), payment_method_id: None, update_history: None, + mandate_metadata: None, }, ), ), diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 90ca49c974..09292224ca 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -1504,21 +1504,24 @@ async fn payment_response_update_tracker( let flow_name = core_utils::get_flow_name::()?; if flow_name == "PSync" || flow_name == "CompleteAuthorize" { - let connector_mandate_id = match router_data.response.clone() { + let (connector_mandate_id, mandate_metadata) = match router_data.response.clone() { Ok(resp) => match resp { types::PaymentsResponseData::TransactionResponse { ref mandate_reference, .. } => { if let Some(mandate_ref) = mandate_reference { - mandate_ref.connector_mandate_id.clone() + ( + mandate_ref.connector_mandate_id.clone(), + mandate_ref.mandate_metadata.clone(), + ) } else { - None + (None, None) } } - _ => None, + _ => (None, None), }, - Err(_) => None, + Err(_) => (None, None), }; if let Some(payment_method) = payment_data.payment_method_info.clone() { let connector_mandate_details = @@ -1529,6 +1532,7 @@ async fn payment_response_update_tracker( payment_data.payment_attempt.currency, payment_data.payment_attempt.merchant_connector_id.clone(), connector_mandate_id, + mandate_metadata, )?; payment_methods::cards::update_payment_method_connector_mandate_details( state, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index c5e7cebac3..c69595d189 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -329,7 +329,7 @@ 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 }, + 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, }, )) } }), diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 2d81aecb30..ca8c6718aa 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -148,18 +148,21 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to serialize customer acceptance to value")?; - let connector_mandate_id = match responses { + let (connector_mandate_id, mandate_metadata) = match responses { types::PaymentsResponseData::TransactionResponse { ref mandate_reference, .. } => { if let Some(mandate_ref) = mandate_reference { - mandate_ref.connector_mandate_id.clone() + ( + mandate_ref.connector_mandate_id.clone(), + mandate_ref.mandate_metadata.clone(), + ) } else { - None + (None, None) } } - _ => None, + _ => (None, None), }; let check_for_mit_mandates = save_payment_method_data .request @@ -178,6 +181,7 @@ where currency, merchant_connector_id.clone(), connector_mandate_id.clone(), + mandate_metadata.clone(), ) } else { None @@ -373,6 +377,7 @@ where currency, merchant_connector_id.clone(), connector_mandate_id.clone(), + mandate_metadata.clone(), )?; payment_methods::cards::update_payment_method_connector_mandate_details(state, @@ -479,6 +484,7 @@ where currency, merchant_connector_id.clone(), connector_mandate_id.clone(), + mandate_metadata.clone(), )?; payment_methods::cards::update_payment_method_connector_mandate_details( state, @@ -1160,6 +1166,7 @@ pub fn add_connector_mandate_details_in_payment_method( authorized_currency: Option, merchant_connector_id: Option, connector_mandate_id: Option, + mandate_metadata: Option, ) -> Option { let mut mandate_details = HashMap::new(); @@ -1173,6 +1180,7 @@ pub fn add_connector_mandate_details_in_payment_method( payment_method_type, original_payment_authorized_amount: authorized_amount, original_payment_authorized_currency: authorized_currency, + mandate_metadata, }, ); Some(storage::PaymentsMandateReference(mandate_details)) @@ -1188,6 +1196,7 @@ pub fn update_connector_mandate_details_in_payment_method( authorized_currency: Option, merchant_connector_id: Option, connector_mandate_id: Option, + mandate_metadata: Option, ) -> RouterResult> { let mandate_reference = match payment_method.connector_mandate_details { Some(_) => { @@ -1208,6 +1217,7 @@ pub fn update_connector_mandate_details_in_payment_method( payment_method_type, original_payment_authorized_amount: authorized_amount, original_payment_authorized_currency: authorized_currency, + mandate_metadata: mandate_metadata.clone(), }; mandate_details.map(|mut payment_mandate_reference| { payment_mandate_reference @@ -1218,6 +1228,7 @@ pub fn update_connector_mandate_details_in_payment_method( payment_method_type, original_payment_authorized_amount: authorized_amount, original_payment_authorized_currency: authorized_currency, + mandate_metadata: mandate_metadata.clone(), }); payment_mandate_reference }) @@ -1231,6 +1242,7 @@ pub fn update_connector_mandate_details_in_payment_method( authorized_currency, merchant_connector_id, connector_mandate_id, + mandate_metadata, ), }; let connector_mandate_details = mandate_reference diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs index 34c5714e12..bb1b801edb 100644 --- a/crates/router/src/types/storage/payment_method.rs +++ b/crates/router/src/types/storage/payment_method.rs @@ -124,6 +124,7 @@ pub struct PaymentsMandateReferenceRecord { pub payment_method_type: Option, pub original_payment_authorized_amount: Option, pub original_payment_authorized_currency: Option, + pub mandate_metadata: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]