From 7f74ae98a1d48eed98341e4505d3801a61e69fc7 Mon Sep 17 00:00:00 2001 From: SamraatBansal <55536657+SamraatBansal@users.noreply.github.com> Date: Wed, 22 Nov 2023 00:35:40 +0530 Subject: [PATCH] fix: cybersource mandates and fiserv exp year (#2920) Co-authored-by: Arjun Karthik --- crates/router/src/connector/cybersource.rs | 94 ++++- .../src/connector/cybersource/transformers.rs | 388 +++++++++++++----- .../src/connector/fiserv/transformers.rs | 37 +- crates/router/src/connector/utils.rs | 10 + crates/router/src/core/payments.rs | 24 +- 5 files changed, 427 insertions(+), 126 deletions(-) diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs index f69701f73958..ce283b12b798 100644 --- a/crates/router/src/connector/cybersource.rs +++ b/crates/router/src/connector/cybersource.rs @@ -94,7 +94,7 @@ impl ConnectorCommon for Cybersource { } fn get_currency_unit(&self) -> api::CurrencyUnit { - api::CurrencyUnit::Minor + api::CurrencyUnit::Base } fn build_error_response( @@ -252,6 +252,80 @@ impl types::PaymentsResponseData, > for Cybersource { + fn get_headers( + &self, + req: &types::SetupMandateRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + fn get_url( + &self, + _req: &types::SetupMandateRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!("{}pts/v2/payments/", self.base_url(connectors))) + } + fn get_request_body( + &self, + req: &types::SetupMandateRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let req_obj = cybersource::CybersourceZeroMandateRequest::try_from(req)?; + let cybersource_req = types::RequestBody::log_and_get_request_body( + &req_obj, + utils::Encode::::encode_to_string_of_json, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(cybersource_req)) + } + + fn build_request( + &self, + req: &types::SetupMandateRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::SetupMandateType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::SetupMandateType::get_headers(self, req, connectors)?) + .body(types::SetupMandateType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::SetupMandateRouterData, + res: types::Response, + ) -> CustomResult { + let response: cybersource::CybersourcePaymentsResponse = res + .response + .parse_struct("CybersourceMandateResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + types::RouterData::try_from(( + types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }, + false, + )) + } + + fn get_error_response( + &self, + res: types::Response, + ) -> CustomResult { + self.build_error_response(res) + } } impl ConnectorIntegration @@ -300,7 +374,14 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { - let connector_request = cybersource::CybersourcePaymentsRequest::try_from(req)?; + let connector_router_data = cybersource::CybersourceRouterData::try_from(( + &self.get_currency_unit(), + req.request.currency, + req.request.amount_to_capture, + req, + ))?; + let connector_request = + cybersource::CybersourcePaymentsCaptureRequest::try_from(&connector_router_data)?; let cybersource_payments_request = types::RequestBody::log_and_get_request_body( &connector_request, utils::Encode::::encode_to_string_of_json, @@ -665,7 +746,14 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { - let connector_request = cybersource::CybersourceRefundRequest::try_from(req)?; + let connector_router_data = cybersource::CybersourceRouterData::try_from(( + &self.get_currency_unit(), + req.request.currency, + req.request.refund_amount, + req, + ))?; + let connector_request = + cybersource::CybersourceRefundRequest::try_from(&connector_router_data)?; let cybersource_refund_request = types::RequestBody::log_and_get_request_body( &connector_request, utils::Encode::::encode_to_string_of_json, diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 9233a95d7dd7..0e81b6b59dff 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -4,10 +4,12 @@ use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::{self, AddressDetailsData, PhoneDetailsData, RouterData}, + connector::utils::{ + self, AddressDetailsData, PaymentsAuthorizeRequestData, PaymentsSetupMandateRequestData, + PhoneDetailsData, RouterData, + }, consts, core::errors, - pii::PeekInterface, types::{ self, api::{self, enums as api_enums}, @@ -46,7 +48,81 @@ impl } } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceZeroMandateRequest { + processing_information: ProcessingInformation, + payment_information: PaymentInformation, + order_information: OrderInformationWithBill, + client_reference_information: ClientReferenceInformation, +} + +impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest { + type Error = error_stack::Report; + fn try_from(item: &types::SetupMandateRouterData) -> Result { + let phone = item.get_billing_phone()?; + let number_with_code = phone.get_number_with_country_code()?; + let email = item.request.get_email()?; + let bill_to = build_bill_to(item.get_billing()?, email, number_with_code)?; + + let order_information = OrderInformationWithBill { + amount_details: Amount { + total_amount: "0".to_string(), + currency: item.request.currency.to_string(), + }, + bill_to: Some(bill_to), + }; + let (action_list, action_token_types, authorization_options) = ( + Some(vec![CybersourceActionsList::TokenCreate]), + Some(vec![CybersourceActionsTokenType::InstrumentIdentifier]), + Some(CybersourceAuthorizationOptions { + initiator: CybersourcePaymentInitiator { + initiator_type: CybersourcePaymentInitiatorTypes::Customer, + credential_stored_on_file: true, + }, + }), + ); + + let processing_information = ProcessingInformation { + capture: Some(false), + capture_options: None, + action_list, + action_token_types, + authorization_options, + commerce_indicator: CybersourceCommerceIndicator::Internet, + }; + + let client_reference_information = ClientReferenceInformation { + code: Some(item.connector_request_reference_id.clone()), + }; + + let payment_information = match item.request.payment_method_data.clone() { + api::PaymentMethodData::Card(ccard) => { + let card = CardDetails::PaymentCard(Card { + number: ccard.card_number, + expiration_month: ccard.card_exp_month, + expiration_year: ccard.card_exp_year, + security_code: ccard.card_cvc, + }); + PaymentInformation { + card, + instrument_identifier: None, + } + } + _ => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Cybersource"), + ))?, + }; + Ok(Self { + processing_information, + payment_information, + order_information, + client_reference_information, + }) + } +} + +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CybersourcePaymentsRequest { processing_information: ProcessingInformation, @@ -55,26 +131,82 @@ pub struct CybersourcePaymentsRequest { client_reference_information: ClientReferenceInformation, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct ProcessingInformation { - capture: bool, + action_list: Option>, + action_token_types: Option>, + authorization_options: Option, + commerce_indicator: CybersourceCommerceIndicator, + capture: Option, capture_options: Option, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum CybersourceActionsList { + TokenCreate, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum CybersourceActionsTokenType { + InstrumentIdentifier, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceAuthorizationOptions { + initiator: CybersourcePaymentInitiator, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourcePaymentInitiator { + #[serde(rename = "type")] + initiator_type: CybersourcePaymentInitiatorTypes, + credential_stored_on_file: bool, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum CybersourcePaymentInitiatorTypes { + Customer, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum CybersourceCommerceIndicator { + Internet, +} + +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CaptureOptions { capture_sequence_number: u32, total_capture_count: u32, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub struct PaymentInformation { - card: Card, + card: CardDetails, + instrument_identifier: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CybersoucreInstrumentIdentifier { + id: String, +} + +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum CardDetails { + PaymentCard(Card), + MandateCard(MandateCardDetails), } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Card { number: cards::CardNumber, @@ -83,27 +215,34 @@ pub struct Card { security_code: Secret, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct MandateCardDetails { + expiration_month: Secret, + expiration_year: Secret, +} + +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct OrderInformationWithBill { amount_details: Amount, - bill_to: BillTo, + bill_to: Option, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct OrderInformation { amount_details: Amount, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Amount { total_amount: String, currency: String, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct BillTo { first_name: Secret, @@ -147,104 +286,135 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> fn try_from( item: &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>, ) -> Result { - match item.router_data.request.payment_method_data.clone() { + let phone = item.router_data.get_billing_phone()?; + let number_with_code = phone.get_number_with_country_code()?; + let email = item.router_data.request.get_email()?; + let bill_to = build_bill_to(item.router_data.get_billing()?, email, number_with_code)?; + + let order_information = OrderInformationWithBill { + amount_details: Amount { + total_amount: item.amount.to_owned(), + currency: item.router_data.request.currency.to_string(), + }, + bill_to: Some(bill_to), + }; + let (action_list, action_token_types, authorization_options) = + if item.router_data.request.setup_future_usage.is_some() { + ( + Some(vec![CybersourceActionsList::TokenCreate]), + Some(vec![CybersourceActionsTokenType::InstrumentIdentifier]), + Some(CybersourceAuthorizationOptions { + initiator: CybersourcePaymentInitiator { + initiator_type: CybersourcePaymentInitiatorTypes::Customer, + credential_stored_on_file: true, + }, + }), + ) + } else { + (None, None, None) + }; + + let processing_information = ProcessingInformation { + capture: Some(matches!( + item.router_data.request.capture_method, + Some(enums::CaptureMethod::Automatic) | None + )), + capture_options: None, + action_list, + action_token_types, + authorization_options, + commerce_indicator: CybersourceCommerceIndicator::Internet, + }; + + let client_reference_information = ClientReferenceInformation { + code: Some(item.router_data.connector_request_reference_id.clone()), + }; + let payment_information = match item.router_data.request.payment_method_data.clone() { api::PaymentMethodData::Card(ccard) => { - let phone = item.router_data.get_billing_phone()?; - let phone_number = phone.get_number()?; - let country_code = phone.get_country_code()?; - let number_with_code = - Secret::new(format!("{}{}", country_code, phone_number.peek())); - let email = item - .router_data - .request - .email - .clone() - .ok_or_else(utils::missing_field_err("email"))?; - let bill_to = - build_bill_to(item.router_data.get_billing()?, email, number_with_code)?; - - let order_information = OrderInformationWithBill { - amount_details: Amount { - total_amount: item.amount.to_owned(), - currency: item.router_data.request.currency.to_string().to_uppercase(), - }, - bill_to, - }; - - let payment_information = PaymentInformation { - card: Card { + let instrument_identifier = + item.router_data + .request + .connector_mandate_id() + .map(|mandate_token_id| CybersoucreInstrumentIdentifier { + id: mandate_token_id, + }); + let card = if instrument_identifier.is_some() { + CardDetails::MandateCard(MandateCardDetails { + expiration_month: ccard.card_exp_month, + expiration_year: ccard.card_exp_year, + }) + } else { + CardDetails::PaymentCard(Card { number: ccard.card_number, expiration_month: ccard.card_exp_month, expiration_year: ccard.card_exp_year, security_code: ccard.card_cvc, - }, - }; - - let processing_information = ProcessingInformation { - capture: matches!( - item.router_data.request.capture_method, - Some(enums::CaptureMethod::Automatic) | None - ), - capture_options: None, - }; - - let client_reference_information = ClientReferenceInformation { - code: Some(item.router_data.connector_request_reference_id.clone()), + }) }; - - Ok(Self { - processing_information, - payment_information, - order_information, - client_reference_information, - }) + PaymentInformation { + card, + instrument_identifier, + } } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), - } + payments::PaymentMethodData::CardRedirect(_) + | payments::PaymentMethodData::Wallet(_) + | payments::PaymentMethodData::PayLater(_) + | payments::PaymentMethodData::BankRedirect(_) + | payments::PaymentMethodData::BankDebit(_) + | payments::PaymentMethodData::BankTransfer(_) + | payments::PaymentMethodData::Crypto(_) + | payments::PaymentMethodData::MandatePayment + | payments::PaymentMethodData::Reward + | payments::PaymentMethodData::Upi(_) + | payments::PaymentMethodData::Voucher(_) + | payments::PaymentMethodData::GiftCard(_) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Cybersource"), + ))? + } + }; + Ok(Self { + processing_information, + payment_information, + order_information, + client_reference_information, + }) } } -impl TryFrom<&types::PaymentsCaptureRouterData> for CybersourcePaymentsRequest { +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourcePaymentsCaptureRequest { + processing_information: ProcessingInformation, + order_information: OrderInformationWithBill, +} + +impl TryFrom<&CybersourceRouterData<&types::PaymentsCaptureRouterData>> + for CybersourcePaymentsCaptureRequest +{ type Error = error_stack::Report; - fn try_from(value: &types::PaymentsCaptureRouterData) -> Result { + fn try_from( + item: &CybersourceRouterData<&types::PaymentsCaptureRouterData>, + ) -> Result { Ok(Self { processing_information: ProcessingInformation { capture_options: Some(CaptureOptions { capture_sequence_number: 1, total_capture_count: 1, }), - ..Default::default() + action_list: None, + action_token_types: None, + authorization_options: None, + capture: None, + commerce_indicator: CybersourceCommerceIndicator::Internet, }, order_information: OrderInformationWithBill { amount_details: Amount { - total_amount: value.request.amount_to_capture.to_string(), - ..Default::default() + total_amount: item.amount.clone(), + currency: item.router_data.request.currency.to_string(), }, - ..Default::default() - }, - client_reference_information: ClientReferenceInformation { - code: Some(value.connector_request_reference_id.clone()), + bill_to: None, }, - ..Default::default() - }) - } -} - -impl TryFrom<&types::RefundExecuteRouterData> for CybersourcePaymentsRequest { - type Error = error_stack::Report; - fn try_from(value: &types::RefundExecuteRouterData) -> Result { - Ok(Self { - order_information: OrderInformationWithBill { - amount_details: Amount { - total_amount: value.request.refund_amount.to_string(), - currency: value.request.currency.to_string(), - }, - ..Default::default() - }, - client_reference_information: ClientReferenceInformation { - code: Some(value.connector_request_reference_id.clone()), - }, - ..Default::default() }) } } @@ -274,7 +444,7 @@ impl TryFrom<&types::ConnectorAuthType> for CybersourceAuthType { } } } -#[derive(Debug, Default, Clone, Deserialize, Eq, PartialEq)] +#[derive(Debug, Default, Clone, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum CybersourcePaymentStatus { Authorized, @@ -318,22 +488,29 @@ impl From for enums::RefundStatus { } } -#[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CybersourcePaymentsResponse { id: String, status: CybersourcePaymentStatus, error_information: Option, client_reference_information: Option, + token_information: Option, } -#[derive(Default, Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ClientReferenceInformation { code: Option, } -#[derive(Default, Debug, Clone, Deserialize, Eq, PartialEq)] +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CybersourceTokenInformation { + instrument_identifier: CybersoucreInstrumentIdentifier, +} + +#[derive(Debug, Clone, Deserialize)] pub struct CybersourceErrorInformation { reason: String, message: String, @@ -359,6 +536,13 @@ impl ) -> Result { let item = data.0; let is_capture = data.1; + let mandate_reference = + item.response + .token_information + .map(|token_info| types::MandateReference { + connector_mandate_id: Some(token_info.instrument_identifier.id), + payment_method_id: None, + }); Ok(Self { status: get_payment_status(is_capture, item.response.status.into()), response: match item.response.error_information { @@ -374,7 +558,7 @@ impl item.response.id.clone(), ), redirection_data: None, - mandate_reference: None, + mandate_reference, connector_metadata: None, network_txn_id: None, connector_response_reference_id: item @@ -495,26 +679,28 @@ pub struct Details { pub reason: String, } -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Deserialize)] pub struct ErrorInformation { pub message: String, pub reason: String, } -#[derive(Default, Debug, Serialize)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CybersourceRefundRequest { order_information: OrderInformation, } -impl TryFrom<&types::RefundsRouterData> for CybersourceRefundRequest { +impl TryFrom<&CybersourceRouterData<&types::RefundsRouterData>> for CybersourceRefundRequest { type Error = error_stack::Report; - fn try_from(item: &types::RefundsRouterData) -> Result { + fn try_from( + item: &CybersourceRouterData<&types::RefundsRouterData>, + ) -> Result { Ok(Self { order_information: OrderInformation { amount_details: Amount { - total_amount: item.request.refund_amount.to_string(), - currency: item.request.currency.to_string(), + total_amount: item.amount.clone(), + currency: item.router_data.request.currency.to_string(), }, }, }) diff --git a/crates/router/src/connector/fiserv/transformers.rs b/crates/router/src/connector/fiserv/transformers.rs index f8d88d08c6ba..5add9b79e3f9 100644 --- a/crates/router/src/connector/fiserv/transformers.rs +++ b/crates/router/src/connector/fiserv/transformers.rs @@ -3,7 +3,10 @@ use error_stack::ResultExt; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::{self, PaymentsCancelRequestData, PaymentsSyncRequestData, RouterData}, + connector::utils::{ + self, CardData as CardDataUtil, PaymentsCancelRequestData, PaymentsSyncRequestData, + RouterData, + }, core::errors, pii::Secret, types::{self, api, storage::enums}, @@ -41,7 +44,7 @@ impl } } -#[derive(Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct FiservPaymentsRequest { amount: Amount, @@ -51,7 +54,7 @@ pub struct FiservPaymentsRequest { transaction_interaction: TransactionInteraction, } -#[derive(Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(tag = "sourceType")] pub enum Source { PaymentCard { @@ -65,7 +68,7 @@ pub enum Source { }, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CardData { card_data: cards::CardNumber, @@ -74,7 +77,7 @@ pub struct CardData { security_code: Secret, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct GooglePayToken { signature: String, @@ -82,14 +85,14 @@ pub struct GooglePayToken { protocol_version: String, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] pub struct Amount { #[serde(serialize_with = "utils::str_to_f32")] total: String, currency: String, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct TransactionDetails { capture_flag: Option, @@ -97,14 +100,14 @@ pub struct TransactionDetails { merchant_transaction_id: String, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct MerchantDetails { merchant_id: Secret, terminal_id: Option, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct TransactionInteraction { origin: TransactionInteractionOrigin, @@ -112,19 +115,19 @@ pub struct TransactionInteraction { pos_condition_code: TransactionInteractionPosConditionCode, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] #[serde(rename_all = "UPPERCASE")] pub enum TransactionInteractionOrigin { #[default] Ecom, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum TransactionInteractionEciIndicator { #[default] ChannelEncrypted, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum TransactionInteractionPosConditionCode { #[default] @@ -174,7 +177,7 @@ impl TryFrom<&FiservRouterData<&types::PaymentsAuthorizeRouterData>> for FiservP let card = CardData { card_data: ccard.card_number.clone(), expiration_month: ccard.card_exp_month.clone(), - expiration_year: ccard.card_exp_year.clone(), + expiration_year: ccard.get_expiry_year_4_digit(), security_code: ccard.card_cvc.clone(), }; Source::PaymentCard { card } @@ -219,7 +222,7 @@ impl TryFrom<&types::ConnectorAuthType> for FiservAuthType { } } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct FiservCancelRequest { transaction_details: TransactionDetails, @@ -406,7 +409,7 @@ impl TryFrom> for FiservCap } } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] +#[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct FiservSyncRequest { merchant_details: MerchantDetails, diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 8b20332ce5ed..a098cef5b778 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -322,6 +322,7 @@ impl PaymentsCaptureRequestData for types::PaymentsCaptureData { pub trait PaymentsSetupMandateRequestData { fn get_browser_info(&self) -> Result; + fn get_email(&self) -> Result; } impl PaymentsSetupMandateRequestData for types::SetupMandateRequestData { @@ -330,6 +331,9 @@ impl PaymentsSetupMandateRequestData for types::SetupMandateRequestData { .clone() .ok_or_else(missing_field_err("browser_info")) } + fn get_email(&self) -> Result { + self.email.clone().ok_or_else(missing_field_err("email")) + } } pub trait PaymentsAuthorizeRequestData { fn is_auto_capture(&self) -> Result; @@ -869,6 +873,7 @@ impl CryptoData for api::CryptoData { pub trait PhoneDetailsData { fn get_number(&self) -> Result, Error>; fn get_country_code(&self) -> Result; + fn get_number_with_country_code(&self) -> Result, Error>; } impl PhoneDetailsData for api::PhoneDetails { @@ -882,6 +887,11 @@ impl PhoneDetailsData for api::PhoneDetails { .clone() .ok_or_else(missing_field_err("billing.phone.number")) } + fn get_number_with_country_code(&self) -> Result, Error> { + let number = self.get_number()?; + let country_code = self.get_country_code()?; + Ok(Secret::new(format!("{}{}", country_code, number.peek()))) + } } pub trait AddressDetailsData { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 8c13b05836f1..1c40ef81f497 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1663,10 +1663,24 @@ where .unwrap_or(false); let payment_data_and_tokenization_action = match connector { - Some(_) if is_mandate => ( - payment_data.to_owned(), - TokenizationAction::SkipConnectorTokenization, - ), + Some(connector_name) if is_mandate => { + if connector_name == *"cybersource" { + let (_operation, payment_method_data) = operation + .to_domain()? + .make_pm_data( + state, + payment_data, + validate_result.storage_scheme, + merchant_key_store, + ) + .await?; + payment_data.payment_method_data = payment_method_data; + } + ( + payment_data.to_owned(), + TokenizationAction::SkipConnectorTokenization, + ) + } Some(connector) if is_operation_confirm(&operation) => { let payment_method = &payment_data .payment_attempt @@ -1749,7 +1763,7 @@ where }; (payment_data.to_owned(), connector_tokenization_action) } - _ => ( + Some(_) | None => ( payment_data.to_owned(), TokenizationAction::SkipConnectorTokenization, ),