From 7c408aff1e7a664d38216fadb49730f378622ed2 Mon Sep 17 00:00:00 2001 From: DEEPANSHU BANSAL <41580413+deepanshu-iiitu@users.noreply.github.com> Date: Thu, 11 Jul 2024 18:04:25 +0530 Subject: [PATCH] fix(connector): [BANKOFAMERICA] Remove cards 3ds flow (#5294) --- crates/router/src/connector/bankofamerica.rs | 338 +------- .../connector/bankofamerica/transformers.rs | 783 +----------------- crates/router/src/core/payments.rs | 3 +- crates/router/src/core/payments/flows.rs | 2 + 4 files changed, 29 insertions(+), 1097 deletions(-) diff --git a/crates/router/src/connector/bankofamerica.rs b/crates/router/src/connector/bankofamerica.rs index 19bc3f9875..daec033f6f 100644 --- a/crates/router/src/connector/bankofamerica.rs +++ b/crates/router/src/connector/bankofamerica.rs @@ -12,7 +12,6 @@ use time::OffsetDateTime; use transformers as bankofamerica; use url::Url; -use super::utils::{PaymentsAuthorizeRequestData, PaymentsSetupMandateRequestData, RouterData}; use crate::{ configs::settings, connector::{ @@ -53,8 +52,6 @@ impl api::Refund for Bankofamerica {} impl api::RefundExecute for Bankofamerica {} impl api::RefundSync for Bankofamerica {} impl api::PaymentToken for Bankofamerica {} -impl api::PaymentsPreProcessing for Bankofamerica {} -impl api::PaymentsCompleteAuthorize for Bankofamerica {} impl Bankofamerica { pub fn generate_digest(&self, payload: &[u8]) -> String { @@ -339,33 +336,18 @@ impl } fn get_url( &self, - req: &types::SetupMandateRouterData, + _req: &types::SetupMandateRouterData, connectors: &settings::Connectors, ) -> CustomResult { - if req.is_three_ds() && req.request.is_card() { - Ok(format!( - "{}risk/v1/authentication-setups", - self.base_url(connectors) - )) - } else { - Ok(format!("{}pts/v2/payments/", self.base_url(connectors))) - } + Ok(format!("{}pts/v2/payments/", self.base_url(connectors))) } fn get_request_body( &self, req: &types::SetupMandateRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - if req.is_three_ds() && req.request.is_card() { - let connector_req = bankofamerica::BankOfAmericaAuthSetupRequest::try_from(( - &req.request.payment_method_data, - req.connector_request_reference_id.clone(), - ))?; - Ok(RequestContent::Json(Box::new(connector_req))) - } else { - let connector_req = bankofamerica::BankOfAmericaPaymentsRequest::try_from(req)?; - Ok(RequestContent::Json(Box::new(connector_req))) - } + let connector_req = bankofamerica::BankOfAmericaPaymentsRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(connector_req))) } fn build_request( @@ -395,33 +377,19 @@ impl event_builder: Option<&mut ConnectorEvent>, res: Response, ) -> CustomResult { - if data.is_three_ds() && data.request.is_card() { - let response: bankofamerica::BankOfAmericaAuthSetupResponse = res - .response - .parse_struct("Bankofamerica AuthSetupResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - event_builder.map(|i| i.set_response_body(&response)); - router_env::logger::info!(connector_response=?response); - types::RouterData::try_from(types::ResponseRouterData { - response, - data: data.clone(), - http_code: res.status_code, - }) - } else { - let response: bankofamerica::BankOfAmericaSetupMandatesResponse = res - .response - .parse_struct("BankOfAmericaSetupMandatesResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: bankofamerica::BankOfAmericaSetupMandatesResponse = res + .response + .parse_struct("BankOfAmericaSetupMandatesResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - event_builder.map(|i| i.set_response_body(&response)); - router_env::logger::info!(connector_response=?response); + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); - types::RouterData::try_from(types::ResponseRouterData { - response, - data: data.clone(), - http_code: res.status_code, - }) - } + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) } fn get_error_response( @@ -465,117 +433,6 @@ impl } } -impl - ConnectorIntegration< - api::PreProcessing, - types::PaymentsPreProcessingData, - types::PaymentsResponseData, - > for Bankofamerica -{ - fn get_headers( - &self, - req: &types::PaymentsPreProcessingRouterData, - 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::PaymentsPreProcessingRouterData, - connectors: &settings::Connectors, - ) -> CustomResult { - let redirect_response = req.request.redirect_response.clone().ok_or( - errors::ConnectorError::MissingRequiredField { - field_name: "redirect_response", - }, - )?; - match redirect_response.params { - Some(param) if !param.clone().peek().is_empty() => Ok(format!( - "{}risk/v1/authentications", - self.base_url(connectors) - )), - Some(_) | None => Ok(format!( - "{}risk/v1/authentication-results", - self.base_url(connectors) - )), - } - } - fn get_request_body( - &self, - req: &types::PaymentsPreProcessingRouterData, - _connectors: &settings::Connectors, - ) -> CustomResult { - let connector_router_data = bankofamerica::BankOfAmericaRouterData::try_from(( - &self.get_currency_unit(), - req.request - .currency - .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "currency", - })?, - req.request - .amount - .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "amount", - })?, - req, - ))?; - let connector_req = - bankofamerica::BankOfAmericaPreProcessingRequest::try_from(&connector_router_data)?; - Ok(RequestContent::Json(Box::new(connector_req))) - } - fn build_request( - &self, - req: &types::PaymentsPreProcessingRouterData, - connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { - Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Post) - .url(&types::PaymentsPreProcessingType::get_url( - self, req, connectors, - )?) - .attach_default_headers() - .headers(types::PaymentsPreProcessingType::get_headers( - self, req, connectors, - )?) - .set_body(types::PaymentsPreProcessingType::get_request_body( - self, req, connectors, - )?) - .build(), - )) - } - - fn handle_response( - &self, - data: &types::PaymentsPreProcessingRouterData, - event_builder: Option<&mut ConnectorEvent>, - res: Response, - ) -> CustomResult { - let response: bankofamerica::BankOfAmericaPreProcessingResponse = res - .response - .parse_struct("BankOfAmerica AuthEnrollmentResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - event_builder.map(|i| i.set_response_body(&response)); - router_env::logger::info!(connector_response=?response); - types::RouterData::try_from(types::ResponseRouterData { - response, - data: data.clone(), - http_code: res.status_code, - }) - } - - fn get_error_response( - &self, - res: Response, - event_builder: Option<&mut ConnectorEvent>, - ) -> CustomResult { - self.build_error_response(res, event_builder) - } -} - impl ConnectorIntegration for Bankofamerica { @@ -593,17 +450,13 @@ impl ConnectorIntegration CustomResult { - if req.is_three_ds() && req.request.is_card() { - Ok(format!( - "{}risk/v1/authentication-setups", - self.base_url(connectors) - )) - } else { - Ok(format!("{}pts/v2/payments/", self.base_url(connectors))) - } + Ok(format!( + "{}pts/v2/payments/", + ConnectorCommon::base_url(self, connectors) + )) } fn get_request_body( @@ -617,17 +470,9 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult { - if data.is_three_ds() && data.request.is_card() { - let response: bankofamerica::BankOfAmericaAuthSetupResponse = res - .response - .parse_struct("Bankofamerica AuthSetupResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - event_builder.map(|i| i.set_response_body(&response)); - router_env::logger::info!(connector_response=?response); - types::RouterData::try_from(types::ResponseRouterData { - response, - data: data.clone(), - http_code: res.status_code, - }) - } else { - let response: bankofamerica::BankOfAmericaPaymentsResponse = res - .response - .parse_struct("Bankofamerica PaymentResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - event_builder.map(|i| i.set_response_body(&response)); - router_env::logger::info!(connector_response=?response); - types::RouterData::try_from(types::ResponseRouterData { - response, - data: data.clone(), - http_code: res.status_code, - }) - } - } - - fn get_error_response( - &self, - res: Response, - event_builder: Option<&mut ConnectorEvent>, - ) -> CustomResult { - self.build_error_response(res, event_builder) - } - - fn get_5xx_error_response( - &self, - res: Response, - event_builder: Option<&mut ConnectorEvent>, - ) -> CustomResult { - let response: bankofamerica::BankOfAmericaServerErrorResponse = res - .response - .parse_struct("BankOfAmericaServerErrorResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - - event_builder.map(|event| event.set_response_body(&response)); - router_env::logger::info!(error_response=?response); - - let attempt_status = match response.reason { - Some(reason) => match reason { - transformers::Reason::SystemError => Some(enums::AttemptStatus::Failure), - transformers::Reason::ServerTimeout | transformers::Reason::ServiceTimeout => None, - }, - None => None, - }; - Ok(ErrorResponse { - status_code: res.status_code, - reason: response.status.clone(), - code: response.status.unwrap_or(consts::NO_ERROR_CODE.to_string()), - message: response - .message - .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), - attempt_status, - connector_transaction_id: None, - }) - } -} - -impl - ConnectorIntegration< - api::CompleteAuthorize, - types::CompleteAuthorizeData, - types::PaymentsResponseData, - > for Bankofamerica -{ - fn get_headers( - &self, - req: &types::PaymentsCompleteAuthorizeRouterData, - 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::PaymentsCompleteAuthorizeRouterData, - connectors: &settings::Connectors, - ) -> CustomResult { - Ok(format!("{}pts/v2/payments/", self.base_url(connectors))) - } - fn get_request_body( - &self, - req: &types::PaymentsCompleteAuthorizeRouterData, - _connectors: &settings::Connectors, - ) -> CustomResult { - let connector_router_data = bankofamerica::BankOfAmericaRouterData::try_from(( - &self.get_currency_unit(), - req.request.currency, - req.request.amount, - req, - ))?; - let connector_req = - bankofamerica::BankOfAmericaPaymentsRequest::try_from(&connector_router_data)?; - Ok(RequestContent::Json(Box::new(connector_req))) - } - fn build_request( - &self, - req: &types::PaymentsCompleteAuthorizeRouterData, - connectors: &settings::Connectors, - ) -> CustomResult, errors::ConnectorError> { - Ok(Some( - services::RequestBuilder::new() - .method(services::Method::Post) - .url(&types::PaymentsCompleteAuthorizeType::get_url( - self, req, connectors, - )?) - .attach_default_headers() - .headers(types::PaymentsCompleteAuthorizeType::get_headers( - self, req, connectors, - )?) - .set_body(types::PaymentsCompleteAuthorizeType::get_request_body( - self, req, connectors, - )?) - .build(), - )) - } - - fn handle_response( - &self, - data: &types::PaymentsCompleteAuthorizeRouterData, - event_builder: Option<&mut ConnectorEvent>, - res: Response, - ) -> CustomResult { let response: bankofamerica::BankOfAmericaPaymentsResponse = res .response - .parse_struct("BankOfAmerica PaymentResponse") + .parse_struct("Bankofamerica PaymentResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index a82b2cc480..c0434572ce 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -1,7 +1,6 @@ use api_models::payments; use base64::Engine; -use common_utils::{ext_traits::ValueExt, pii}; -use error_stack::ResultExt; +use common_utils::pii; use masking::{ExposeInterface, PeekInterface, Secret}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -9,13 +8,11 @@ use serde_json::Value; use crate::{ connector::utils::{ self, AddressDetailsData, ApplePayDecrypt, CardData, CardIssuer, - PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, - PaymentsPreProcessingData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData, + PaymentsAuthorizeRequestData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData, }, consts, core::errors, - services, types::{ self, api::{self, enums as api_enums}, @@ -578,28 +575,6 @@ impl } } -impl - From<( - &BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>, - BillTo, - )> for OrderInformationWithBill -{ - fn from( - (item, bill_to): ( - &BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>, - BillTo, - ), - ) -> Self { - Self { - amount_details: Amount { - total_amount: item.amount.to_owned(), - currency: item.router_data.request.currency, - }, - bill_to: Some(bill_to), - } - } -} - impl TryFrom<( &BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>, @@ -678,67 +653,6 @@ impl } } -impl - From<( - &BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>, - Option, - &BankOfAmericaConsumerAuthValidateResponse, - )> for ProcessingInformation -{ - fn from( - (item, solution, three_ds_data): ( - &BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>, - Option, - &BankOfAmericaConsumerAuthValidateResponse, - ), - ) -> Self { - let (action_list, action_token_types, authorization_options) = - if is_customer_initiated_mandate_payment(&item.router_data.request) { - ( - Some(vec![BankOfAmericaActionsList::TokenCreate]), - Some(vec![ - BankOfAmericaActionsTokenType::PaymentInstrument, - BankOfAmericaActionsTokenType::Customer, - ]), - Some(BankOfAmericaAuthorizationOptions { - initiator: Some(BankOfAmericaPaymentInitiator { - initiator_type: Some(BankOfAmericaPaymentInitiatorTypes::Customer), - credential_stored_on_file: Some(true), - stored_credential_used: None, - }), - merchant_intitiated_transaction: None, - }), - ) - } else { - (None, None, None) - }; - - let is_setup_mandate_payment = is_setup_mandate_payment(&item.router_data.request); - - let capture = if is_setup_mandate_payment { - Some(false) - } else { - Some(matches!( - item.router_data.request.capture_method, - Some(enums::CaptureMethod::Automatic) | None - )) - }; - - Self { - capture, - payment_solution: solution.map(String::from), - action_list, - action_token_types, - authorization_options, - capture_options: None, - commerce_indicator: three_ds_data - .indicator - .to_owned() - .unwrap_or(String::from("internet")), - } - } -} - impl From<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>> for ClientReferenceInformation { @@ -749,16 +663,6 @@ impl From<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>> } } -impl From<&BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>> - for ClientReferenceInformation -{ - fn from(item: &BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>) -> Self { - Self { - code: Some(item.router_data.connector_request_reference_id.clone()), - } - } -} - impl From<&types::SetupMandateRouterData> for ClientReferenceInformation { fn from(item: &types::SetupMandateRouterData) -> Self { Self { @@ -894,71 +798,6 @@ pub struct Avs { code_raw: Option, } -impl - TryFrom<( - &BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>, - domain::Card, - )> for BankOfAmericaPaymentsRequest -{ - type Error = error_stack::Report; - fn try_from( - (item, ccard): ( - &BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>, - domain::Card, - ), - ) -> Result { - let email = item.router_data.request.get_email()?; - let bill_to = build_bill_to(item.router_data.get_optional_billing(), email)?; - let order_information = OrderInformationWithBill::from((item, bill_to)); - - let payment_information = PaymentInformation::try_from(&ccard)?; - let client_reference_information = ClientReferenceInformation::from(item); - - let three_ds_info: BankOfAmericaThreeDSMetadata = item - .router_data - .request - .connector_meta - .clone() - .ok_or(errors::ConnectorError::MissingRequiredField { - field_name: "connector_meta", - })? - .parse_value("BankOfAmericaThreeDSMetadata") - .change_context(errors::ConnectorError::InvalidConnectorConfig { - config: "metadata", - })?; - - let processing_information = - ProcessingInformation::from((item, None, &three_ds_info.three_ds_data)); - - let consumer_authentication_information = Some(BankOfAmericaConsumerAuthInformation { - ucaf_collection_indicator: three_ds_info.three_ds_data.ucaf_collection_indicator, - cavv: three_ds_info.three_ds_data.cavv, - ucaf_authentication_data: three_ds_info.three_ds_data.ucaf_authentication_data, - xid: three_ds_info.three_ds_data.xid, - directory_server_transaction_id: three_ds_info - .three_ds_data - .directory_server_transaction_id, - specification_version: three_ds_info.three_ds_data.specification_version, - }); - - let merchant_defined_information = item - .router_data - .request - .metadata - .clone() - .map(Vec::::foreign_from); - - Ok(Self { - processing_information, - payment_information, - order_information, - client_reference_information, - consumer_authentication_information, - merchant_defined_information, - }) - } -} - impl TryFrom<( &BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>, @@ -1245,53 +1084,6 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>> } } -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct BankOfAmericaAuthSetupRequest { - payment_information: PaymentInformation, - client_reference_information: ClientReferenceInformation, -} - -impl TryFrom<(&domain::PaymentMethodData, String)> for BankOfAmericaAuthSetupRequest { - type Error = error_stack::Report; - fn try_from( - (payment_method_data, connector_request_reference_id): (&domain::PaymentMethodData, String), - ) -> Result { - match payment_method_data.clone() { - domain::PaymentMethodData::Card(ccard) => { - let payment_information = PaymentInformation::try_from(&ccard)?; - let client_reference_information = ClientReferenceInformation { - code: Some(connector_request_reference_id), - }; - - Ok(Self { - payment_information, - client_reference_information, - }) - } - domain::PaymentMethodData::Wallet(_) - | domain::PaymentMethodData::CardRedirect(_) - | domain::PaymentMethodData::PayLater(_) - | domain::PaymentMethodData::BankRedirect(_) - | domain::PaymentMethodData::BankDebit(_) - | domain::PaymentMethodData::BankTransfer(_) - | domain::PaymentMethodData::Crypto(_) - | domain::PaymentMethodData::MandatePayment - | domain::PaymentMethodData::Reward - | domain::PaymentMethodData::RealTimePayment(_) - | domain::PaymentMethodData::Upi(_) - | domain::PaymentMethodData::Voucher(_) - | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { - Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("Bank Of America"), - ) - .into()) - } - } - } -} - impl TryFrom<( &BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>, @@ -1399,39 +1191,6 @@ impl ForeignFrom<(BankofamericaPaymentStatus, bool)> for enums::AttemptStatus { } } -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct BankOfAmericaConsumerAuthInformationResponse { - access_token: Secret, - device_data_collection_url: Secret, - reference_id: String, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ClientAuthSetupInfoResponse { - id: String, - client_reference_information: ClientReferenceInformation, - consumer_authentication_information: BankOfAmericaConsumerAuthInformationResponse, - processor_information: Option, - processing_information: Option, - payment_account_information: Option, - payment_information: Option, - payment_insights_information: Option, - risk_information: Option, - token_information: Option, - error_information: Option, - issuer_information: Option, - reconciliation_id: Option, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(untagged)] -pub enum BankOfAmericaAuthSetupResponse { - ClientAuthSetupInfo(Box), - ErrorInformation(Box), -} - #[derive(Debug, Deserialize, Serialize)] #[serde(untagged)] pub enum BankOfAmericaPaymentsResponse { @@ -1745,533 +1504,6 @@ fn get_payment_response( } } -impl - TryFrom< - types::ResponseRouterData< - F, - BankOfAmericaAuthSetupResponse, - T, - types::PaymentsResponseData, - >, - > for types::RouterData -{ - type Error = error_stack::Report; - fn try_from( - item: types::ResponseRouterData< - F, - BankOfAmericaAuthSetupResponse, - T, - types::PaymentsResponseData, - >, - ) -> Result { - match item.response { - BankOfAmericaAuthSetupResponse::ClientAuthSetupInfo(info_response) => Ok(Self { - status: enums::AttemptStatus::AuthenticationPending, - response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::NoResponseId, - redirection_data: Some(services::RedirectForm::CybersourceAuthSetup { - access_token: info_response - .consumer_authentication_information - .access_token - .expose(), - ddc_url: info_response - .consumer_authentication_information - .device_data_collection_url - .expose(), - reference_id: info_response - .consumer_authentication_information - .reference_id, - }), - mandate_reference: None, - connector_metadata: None, - network_txn_id: None, - connector_response_reference_id: Some( - info_response - .client_reference_information - .code - .unwrap_or(info_response.id.clone()), - ), - incremental_authorization_allowed: None, - charge_id: None, - }), - ..item.data - }), - BankOfAmericaAuthSetupResponse::ErrorInformation(error_response) => { - let detailed_error_info = - error_response - .error_information - .to_owned() - .details - .map(|error_details| { - error_details - .iter() - .map(|details| format!("{} : {}", details.field, details.reason)) - .collect::>() - .join(", ") - }); - - let reason = get_error_reason( - error_response.error_information.message, - detailed_error_info, - None, - ); - - Ok(Self { - response: Err(types::ErrorResponse { - code: error_response - .error_information - .reason - .clone() - .unwrap_or(consts::NO_ERROR_CODE.to_string()), - message: error_response - .error_information - .reason - .unwrap_or(consts::NO_ERROR_MESSAGE.to_string()), - reason, - status_code: item.http_code, - attempt_status: None, - connector_transaction_id: Some(error_response.id.clone()), - }), - status: enums::AttemptStatus::AuthenticationFailed, - ..item.data - }) - } - } - } -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct BankOfAmericaConsumerAuthInformationRequest { - return_url: String, - reference_id: String, -} -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct BankOfAmericaAuthEnrollmentRequest { - payment_information: PaymentInformation, - client_reference_information: ClientReferenceInformation, - consumer_authentication_information: BankOfAmericaConsumerAuthInformationRequest, - order_information: OrderInformationWithBill, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "PascalCase")] -pub struct BankOfAmericaRedirectionAuthResponse { - pub transaction_id: String, -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct BankOfAmericaConsumerAuthInformationValidateRequest { - authentication_transaction_id: String, -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct BankOfAmericaAuthValidateRequest { - payment_information: PaymentInformation, - client_reference_information: ClientReferenceInformation, - consumer_authentication_information: BankOfAmericaConsumerAuthInformationValidateRequest, - order_information: OrderInformation, -} - -#[derive(Debug, Serialize)] -#[serde(untagged)] -pub enum BankOfAmericaPreProcessingRequest { - AuthEnrollment(Box), - AuthValidate(Box), -} - -impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsPreProcessingRouterData>> - for BankOfAmericaPreProcessingRequest -{ - type Error = error_stack::Report; - fn try_from( - item: &BankOfAmericaRouterData<&types::PaymentsPreProcessingRouterData>, - ) -> Result { - let client_reference_information = ClientReferenceInformation { - code: Some(item.router_data.connector_request_reference_id.clone()), - }; - let payment_method_data = item.router_data.request.payment_method_data.clone().ok_or( - errors::ConnectorError::MissingConnectorRedirectionPayload { - field_name: "payment_method_data", - }, - )?; - let payment_information = match payment_method_data { - domain::PaymentMethodData::Card(ccard) => PaymentInformation::try_from(&ccard), - domain::PaymentMethodData::Wallet(_) - | domain::PaymentMethodData::CardRedirect(_) - | domain::PaymentMethodData::PayLater(_) - | domain::PaymentMethodData::BankRedirect(_) - | domain::PaymentMethodData::BankDebit(_) - | domain::PaymentMethodData::BankTransfer(_) - | domain::PaymentMethodData::Crypto(_) - | domain::PaymentMethodData::MandatePayment - | domain::PaymentMethodData::Reward - | domain::PaymentMethodData::RealTimePayment(_) - | domain::PaymentMethodData::Upi(_) - | domain::PaymentMethodData::Voucher(_) - | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { - Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("BankOfAmerica"), - ) - .into()) - } - }?; - - let redirect_response = item.router_data.request.redirect_response.clone().ok_or( - errors::ConnectorError::MissingRequiredField { - field_name: "redirect_response", - }, - )?; - - let amount_details = Amount { - total_amount: item.amount.clone(), - currency: item.router_data.request.currency.ok_or( - errors::ConnectorError::MissingRequiredField { - field_name: "currency", - }, - )?, - }; - - match redirect_response.params { - Some(param) if !param.clone().peek().is_empty() => { - let reference_id = param - .clone() - .peek() - .split_once('=') - .ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload { - field_name: "request.redirect_response.params.reference_id", - })? - .1 - .to_string(); - let email = item.router_data.request.get_email()?; - let bill_to = build_bill_to(item.router_data.get_optional_billing(), email)?; - let order_information = OrderInformationWithBill { - amount_details, - bill_to: Some(bill_to), - }; - Ok(Self::AuthEnrollment(Box::new( - BankOfAmericaAuthEnrollmentRequest { - payment_information, - client_reference_information, - consumer_authentication_information: - BankOfAmericaConsumerAuthInformationRequest { - return_url: item - .router_data - .request - .get_complete_authorize_url()?, - reference_id, - }, - order_information, - }, - ))) - } - Some(_) | None => { - let redirect_payload: BankOfAmericaRedirectionAuthResponse = redirect_response - .payload - .ok_or(errors::ConnectorError::MissingConnectorRedirectionPayload { - field_name: "request.redirect_response.payload", - })? - .peek() - .clone() - .parse_value("BankOfAmericaRedirectionAuthResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; - let order_information = OrderInformation { amount_details }; - Ok(Self::AuthValidate(Box::new( - BankOfAmericaAuthValidateRequest { - payment_information, - client_reference_information, - consumer_authentication_information: - BankOfAmericaConsumerAuthInformationValidateRequest { - authentication_transaction_id: redirect_payload.transaction_id, - }, - order_information, - }, - ))) - } - } - } -} - -impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>> - for BankOfAmericaPaymentsRequest -{ - type Error = error_stack::Report; - fn try_from( - item: &BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>, - ) -> Result { - let payment_method_data = item.router_data.request.payment_method_data.clone().ok_or( - errors::ConnectorError::MissingRequiredField { - field_name: "payment_method_data", - }, - )?; - match payment_method_data { - domain::PaymentMethodData::Card(ccard) => Self::try_from((item, ccard)), - domain::PaymentMethodData::Wallet(_) - | domain::PaymentMethodData::CardRedirect(_) - | domain::PaymentMethodData::PayLater(_) - | domain::PaymentMethodData::BankRedirect(_) - | domain::PaymentMethodData::BankDebit(_) - | domain::PaymentMethodData::BankTransfer(_) - | domain::PaymentMethodData::Crypto(_) - | domain::PaymentMethodData::MandatePayment - | domain::PaymentMethodData::Reward - | domain::PaymentMethodData::RealTimePayment(_) - | domain::PaymentMethodData::Upi(_) - | domain::PaymentMethodData::Voucher(_) - | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { - Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("BankOfAmerica"), - ) - .into()) - } - } - } -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum BankOfAmericaAuthEnrollmentStatus { - PendingAuthentication, - AuthenticationSuccessful, - AuthenticationFailed, -} -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct BankOfAmericaConsumerAuthValidateResponse { - ucaf_collection_indicator: Option, - cavv: Option, - ucaf_authentication_data: Option>, - xid: Option, - specification_version: Option, - directory_server_transaction_id: Option>, - indicator: Option, -} - -#[derive(Debug, Deserialize, Serialize)] -pub struct BankOfAmericaThreeDSMetadata { - three_ds_data: BankOfAmericaConsumerAuthValidateResponse, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct BankOfAmericaConsumerAuthInformationEnrollmentResponse { - access_token: Option>, - step_up_url: Option, - //Added to segregate the three_ds_data in a separate struct - #[serde(flatten)] - validate_response: BankOfAmericaConsumerAuthValidateResponse, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ClientAuthCheckInfoResponse { - id: String, - client_reference_information: ClientReferenceInformation, - consumer_authentication_information: BankOfAmericaConsumerAuthInformationEnrollmentResponse, - status: BankOfAmericaAuthEnrollmentStatus, - error_information: Option, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(untagged)] -pub enum BankOfAmericaPreProcessingResponse { - ClientAuthCheckInfo(Box), - ErrorInformation(Box), -} - -impl From for enums::AttemptStatus { - fn from(item: BankOfAmericaAuthEnrollmentStatus) -> Self { - match item { - BankOfAmericaAuthEnrollmentStatus::PendingAuthentication => Self::AuthenticationPending, - BankOfAmericaAuthEnrollmentStatus::AuthenticationSuccessful => { - Self::AuthenticationSuccessful - } - BankOfAmericaAuthEnrollmentStatus::AuthenticationFailed => Self::AuthenticationFailed, - } - } -} - -impl - TryFrom< - types::ResponseRouterData< - F, - BankOfAmericaPreProcessingResponse, - types::PaymentsPreProcessingData, - types::PaymentsResponseData, - >, - > for types::RouterData -{ - type Error = error_stack::Report; - fn try_from( - item: types::ResponseRouterData< - F, - BankOfAmericaPreProcessingResponse, - types::PaymentsPreProcessingData, - types::PaymentsResponseData, - >, - ) -> Result { - match item.response { - BankOfAmericaPreProcessingResponse::ClientAuthCheckInfo(info_response) => { - let status = enums::AttemptStatus::from(info_response.status); - let risk_info: Option = None; - if utils::is_payment_failure(status) { - let response = Err(types::ErrorResponse::foreign_from(( - &info_response.error_information, - &risk_info, - Some(status), - item.http_code, - info_response.id.clone(), - ))); - - Ok(Self { - status, - response, - ..item.data - }) - } else { - let connector_response_reference_id = Some( - info_response - .client_reference_information - .code - .unwrap_or(info_response.id.clone()), - ); - - let redirection_data = match ( - info_response - .consumer_authentication_information - .access_token - .map(|access_token| access_token.expose()), - info_response - .consumer_authentication_information - .step_up_url, - ) { - (Some(access_token), Some(step_up_url)) => { - Some(services::RedirectForm::CybersourceConsumerAuth { - access_token, - step_up_url, - }) - } - _ => None, - }; - let three_ds_data = serde_json::to_value( - info_response - .consumer_authentication_information - .validate_response, - ) - .change_context(errors::ConnectorError::ResponseHandlingFailed)?; - Ok(Self { - status, - response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::NoResponseId, - redirection_data, - mandate_reference: None, - connector_metadata: Some(serde_json::json!({ - "three_ds_data": three_ds_data - })), - network_txn_id: None, - connector_response_reference_id, - incremental_authorization_allowed: None, - charge_id: None, - }), - ..item.data - }) - } - } - BankOfAmericaPreProcessingResponse::ErrorInformation(error_response) => { - let response = Err(types::ErrorResponse::foreign_from(( - &*error_response, - item.http_code, - ))); - Ok(Self { - response, - status: enums::AttemptStatus::AuthenticationFailed, - ..item.data - }) - } - } - } -} - -impl - TryFrom< - types::ResponseRouterData< - F, - BankOfAmericaPaymentsResponse, - types::CompleteAuthorizeData, - types::PaymentsResponseData, - >, - > for types::RouterData -{ - type Error = error_stack::Report; - fn try_from( - item: types::ResponseRouterData< - F, - BankOfAmericaPaymentsResponse, - types::CompleteAuthorizeData, - types::PaymentsResponseData, - >, - ) -> Result { - match item.response { - BankOfAmericaPaymentsResponse::ClientReferenceInformation(info_response) => { - let status = enums::AttemptStatus::foreign_from(( - info_response.status.clone(), - item.data.request.is_auto_capture()? - || is_setup_mandate_payment(&item.data.request), - )); - let response = get_payment_response((&info_response, status, item.http_code)); - let connector_response = match item.data.payment_method { - common_enums::PaymentMethod::Card => info_response - .processor_information - .as_ref() - .and_then(|processor_information| { - info_response - .consumer_authentication_information - .as_ref() - .map(|consumer_auth_information| { - types::AdditionalPaymentMethodConnectorResponse::foreign_from(( - processor_information, - consumer_auth_information, - )) - }) - }) - .map(types::ConnectorResponseData::with_additional_payment_method_data), - common_enums::PaymentMethod::CardRedirect - | common_enums::PaymentMethod::PayLater - | common_enums::PaymentMethod::Wallet - | common_enums::PaymentMethod::BankRedirect - | common_enums::PaymentMethod::BankTransfer - | common_enums::PaymentMethod::Crypto - | common_enums::PaymentMethod::BankDebit - | common_enums::PaymentMethod::Reward - | common_enums::PaymentMethod::RealTimePayment - | common_enums::PaymentMethod::Upi - | common_enums::PaymentMethod::Voucher - | common_enums::PaymentMethod::GiftCard => None, - }; - - Ok(Self { - status, - response, - connector_response, - ..item.data - }) - } - BankOfAmericaPaymentsResponse::ErrorInformation(ref error_response) => { - Ok(Self::foreign_from(( - &*error_response.clone(), - item, - Some(enums::AttemptStatus::Failure), - ))) - } - } - } -} - impl TryFrom< types::ResponseRouterData< @@ -3318,17 +2550,6 @@ fn get_commerce_indicator(network: Option) -> String { .to_string() } -fn is_setup_mandate_payment(item: &types::CompleteAuthorizeData) -> bool { - matches!(item.amount, 0) && is_customer_initiated_mandate_payment(item) -} - -fn is_customer_initiated_mandate_payment(item: &types::CompleteAuthorizeData) -> bool { - item.setup_future_usage.map_or(false, |future_usage| { - matches!(future_usage, common_enums::FutureUsage::OffSession) - }) - // add check for customer_acceptance -} - pub fn get_error_reason( error_info: Option, detailed_error_info: Option, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 18e62b9c1c..55edce1b85 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -1975,8 +1975,7 @@ where router_data = router_data.preprocessing_steps(state, connector).await?; (router_data, false) - } else if (connector.connector_name == router_types::Connector::Cybersource - || connector.connector_name == router_types::Connector::Bankofamerica) + } else if connector.connector_name == router_types::Connector::Cybersource && is_operation_complete_authorize(&operation) && router_data.auth_type == storage_enums::AuthenticationType::ThreeDs { diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index fec9471ea9..60f14dd399 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -166,6 +166,7 @@ default_imp_for_complete_authorize!( connector::Aci, connector::Adyen, connector::Bamboraapac, + connector::Bankofamerica, connector::Billwerk, connector::Bitpay, connector::Boku, @@ -987,6 +988,7 @@ default_imp_for_pre_processing_steps!( connector::Authorizedotnet, connector::Bambora, connector::Bamboraapac, + connector::Bankofamerica, connector::Billwerk, connector::Bitpay, connector::Bluesnap,