diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index 72e3de0bf7..db96ff62f6 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -442,7 +442,7 @@ pub struct ClientRiskInformationRules { #[serde(rename_all = "camelCase")] pub struct Avs { code: String, - code_raw: String, + code_raw: Option, } impl diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 0abe1fff42..659f0733fd 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -10,7 +10,7 @@ use crate::{ connector::utils::{ self, AddressDetailsData, ApplePayDecrypt, CardData, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingData, - PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RouterData, + PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData, }, consts, core::errors, @@ -47,6 +47,7 @@ impl T, ), ) -> Result { + // This conversion function is used at different places in the file, if updating this, keep a check for those let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; Ok(Self { amount, @@ -81,11 +82,11 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest { Some(vec![CybersourceActionsList::TokenCreate]), Some(vec![CybersourceActionsTokenType::PaymentInstrument]), Some(CybersourceAuthorizationOptions { - initiator: CybersourcePaymentInitiator { + initiator: Some(CybersourcePaymentInitiator { initiator_type: Some(CybersourcePaymentInitiatorTypes::Customer), credential_stored_on_file: Some(true), stored_credential_used: None, - }, + }), merchant_intitiated_transaction: None, }), ); @@ -272,14 +273,16 @@ pub enum CybersourceActionsTokenType { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CybersourceAuthorizationOptions { - initiator: CybersourcePaymentInitiator, + initiator: Option, merchant_intitiated_transaction: Option, } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct MerchantInitiatedTransaction { - reason: String, + reason: Option, + //Required for recurring mandates payment + original_authorized_amount: Option, } #[derive(Debug, Serialize)] @@ -470,35 +473,60 @@ impl From<&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>> } impl - From<( + TryFrom<( &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>, Option, )> for ProcessingInformation { - fn from( + type Error = error_stack::Report; + fn try_from( (item, solution): ( &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>, Option, ), - ) -> Self { + ) -> Result { let (action_list, action_token_types, authorization_options) = if item.router_data.request.setup_mandate_details.is_some() { ( Some(vec![CybersourceActionsList::TokenCreate]), Some(vec![CybersourceActionsTokenType::PaymentInstrument]), Some(CybersourceAuthorizationOptions { - initiator: CybersourcePaymentInitiator { + initiator: Some(CybersourcePaymentInitiator { initiator_type: Some(CybersourcePaymentInitiatorTypes::Customer), credential_stored_on_file: Some(true), stored_credential_used: None, - }, + }), merchant_intitiated_transaction: None, }), ) + } else if item.router_data.request.connector_mandate_id().is_some() { + let original_amount = item + .router_data + .get_recurring_mandate_payment_data()? + .get_original_payment_amount()?; + let original_currency = item + .router_data + .get_recurring_mandate_payment_data()? + .get_original_payment_currency()?; + ( + None, + None, + Some(CybersourceAuthorizationOptions { + initiator: None, + merchant_intitiated_transaction: Some(MerchantInitiatedTransaction { + reason: None, + original_authorized_amount: Some(utils::get_amount_as_string( + &types::api::CurrencyUnit::Base, + original_amount, + original_currency, + )?), + }), + }), + ) } else { (None, None, None) }; - Self { + Ok(Self { capture: Some(matches!( item.router_data.request.capture_method, Some(enums::CaptureMethod::Automatic) | None @@ -509,7 +537,7 @@ impl authorization_options, capture_options: None, commerce_indicator: String::from("internet"), - } + }) } } @@ -533,11 +561,11 @@ impl Some(vec![CybersourceActionsList::TokenCreate]), Some(vec![CybersourceActionsTokenType::PaymentInstrument]), Some(CybersourceAuthorizationOptions { - initiator: CybersourcePaymentInitiator { + initiator: Some(CybersourcePaymentInitiator { initiator_type: Some(CybersourcePaymentInitiatorTypes::Customer), credential_stored_on_file: Some(true), stored_credential_used: None, - }, + }), merchant_intitiated_transaction: None, }), ) @@ -680,7 +708,7 @@ impl }, }); - let processing_information = ProcessingInformation::from((item, None)); + let processing_information = ProcessingInformation::try_from((item, None))?; let client_reference_information = ClientReferenceInformation::from(item); let merchant_defined_information = item.router_data.request.metadata.clone().map(|metadata| { @@ -792,7 +820,7 @@ impl let bill_to = build_bill_to(item.router_data.get_billing()?, email)?; let order_information = OrderInformationWithBill::from((item, bill_to)); let processing_information = - ProcessingInformation::from((item, Some(PaymentSolution::ApplePay))); + ProcessingInformation::try_from((item, Some(PaymentSolution::ApplePay)))?; let client_reference_information = ClientReferenceInformation::from(item); let expiration_month = apple_pay_data.get_expiry_month()?; let expiration_year = apple_pay_data.get_four_digit_expiry_year()?; @@ -846,7 +874,7 @@ impl }, }); let processing_information = - ProcessingInformation::from((item, Some(PaymentSolution::GooglePay))); + ProcessingInformation::try_from((item, Some(PaymentSolution::GooglePay)))?; let client_reference_information = ClientReferenceInformation::from(item); let merchant_defined_information = item.router_data.request.metadata.clone().map(|metadata| { @@ -893,10 +921,9 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> build_bill_to(item.router_data.get_billing()?, email)?; let order_information = OrderInformationWithBill::from((item, bill_to)); - let processing_information = ProcessingInformation::from(( - item, - Some(PaymentSolution::ApplePay), - )); + let processing_information = ProcessingInformation::try_from( + (item, Some(PaymentSolution::ApplePay)), + )?; let client_reference_information = ClientReferenceInformation::from(item); let payment_information = PaymentInformation::ApplePayToken( @@ -1008,7 +1035,7 @@ impl String, ), ) -> Result { - let processing_information = ProcessingInformation::from((item, None)); + let processing_information = ProcessingInformation::try_from((item, None))?; let payment_instrument = CybersoucrePaymentInstrument { id: connector_mandate_id, }; @@ -1159,13 +1186,14 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsIncrementalAuthorizationRout action_list: None, action_token_types: None, authorization_options: Some(CybersourceAuthorizationOptions { - initiator: CybersourcePaymentInitiator { + initiator: Some(CybersourcePaymentInitiator { initiator_type: None, credential_stored_on_file: None, stored_credential_used: Some(true), - }, + }), merchant_intitiated_transaction: Some(MerchantInitiatedTransaction { - reason: "5".to_owned(), + reason: Some("5".to_owned()), + original_authorized_amount: None, }), }), commerce_indicator: String::from("internet"), @@ -1339,18 +1367,6 @@ impl From for common_enums::Authoriza } } -impl From for enums::RefundStatus { - fn from(item: CybersourcePaymentStatus) -> Self { - match item { - CybersourcePaymentStatus::Succeeded | CybersourcePaymentStatus::Transmitted => { - Self::Success - } - CybersourcePaymentStatus::Failed => Self::Failure, - _ => Self::Pending, - } - } -} - #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum CybersourcePaymentsResponse { @@ -1430,7 +1446,7 @@ pub struct ClientProcessorInformation { #[serde(rename_all = "camelCase")] pub struct Avs { code: String, - code_raw: String, + code_raw: Option, } #[derive(Debug, Clone, Deserialize)] diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 7226cb7734..3b3c992f0e 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -25,7 +25,7 @@ use crate::{ consts, core::{ errors::{self, ApiErrorResponse, CustomResult}, - payments::PaymentData, + payments::{PaymentData, RecurringMandatePaymentData}, }, pii::PeekInterface, types::{ @@ -81,6 +81,7 @@ pub trait RouterData { fn get_customer_id(&self) -> Result; fn get_connector_customer_id(&self) -> Result; fn get_preprocessing_id(&self) -> Result; + fn get_recurring_mandate_payment_data(&self) -> Result; #[cfg(feature = "payouts")] fn get_payout_method_data(&self) -> Result; #[cfg(feature = "payouts")] @@ -250,6 +251,12 @@ impl RouterData for types::RouterData Result { + self.recurring_mandate_payment_data + .to_owned() + .ok_or_else(missing_field_err("recurring_mandate_payment_data")) + } + #[cfg(feature = "payouts")] fn get_payout_method_data(&self) -> Result { self.payout_method_data @@ -1133,6 +1140,22 @@ impl MandateData for payments::MandateAmountData { } } +pub trait RecurringMandateData { + fn get_original_payment_amount(&self) -> Result; + fn get_original_payment_currency(&self) -> Result; +} + +impl RecurringMandateData for RecurringMandatePaymentData { + fn get_original_payment_amount(&self) -> Result { + self.original_payment_authorized_amount + .ok_or_else(missing_field_err("original_payment_authorized_amount")) + } + fn get_original_payment_currency(&self) -> Result { + self.original_payment_authorized_currency + .ok_or_else(missing_field_err("original_payment_authorized_currency")) + } +} + pub trait MandateReferenceData { fn get_connector_mandate_id(&self) -> Result; } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 21b82f3077..186f760ace 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2039,6 +2039,8 @@ pub struct IncrementalAuthorizationDetails { #[derive(Debug, Default, Clone)] 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, } #[derive(Debug, Default, Clone)] diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 213adc79fb..0cbed25534 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -479,6 +479,27 @@ pub async fn get_token_for_recurring_mandate( .await .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?; + let original_payment_intent = mandate + .original_payment_id + .as_ref() + .async_map(|payment_id| async { + db.find_payment_intent_by_payment_id_merchant_id( + payment_id, + &mandate.merchant_id, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound) + .map_err(|err| logger::error!(mandate_original_payment_not_found=?err)) + .ok() + }) + .await + .flatten(); + + let original_payment_authorized_amount = original_payment_intent.clone().map(|pi| pi.amount); + let original_payment_authorized_currency = + original_payment_intent.clone().and_then(|pi| pi.currency); + let customer = req.customer_id.clone().get_required_value("customer_id")?; let payment_method_id = { @@ -540,6 +561,8 @@ pub async fn get_token_for_recurring_mandate( Some(payment_method.payment_method), Some(payments::RecurringMandatePaymentData { payment_method_type, + original_payment_authorized_amount, + original_payment_authorized_currency, }), payment_method.payment_method_type, Some(mandate_connector_details), @@ -550,6 +573,8 @@ pub async fn get_token_for_recurring_mandate( Some(payment_method.payment_method), Some(payments::RecurringMandatePaymentData { payment_method_type, + original_payment_authorized_amount, + original_payment_authorized_currency, }), payment_method.payment_method_type, Some(mandate_connector_details),