mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
feat(connector): [BANKOFAMERICA] Implement 3DS flow for cards (#3343)
This commit is contained in:
@ -12,6 +12,7 @@ use time::OffsetDateTime;
|
||||
use transformers as bankofamerica;
|
||||
use url::Url;
|
||||
|
||||
use super::utils::{PaymentsAuthorizeRequestData, RouterData};
|
||||
use crate::{
|
||||
configs::settings,
|
||||
connector::{utils as connector_utils, utils::RefundsRequestData},
|
||||
@ -48,6 +49,8 @@ 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 {
|
||||
@ -299,6 +302,113 @@ impl
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
ConnectorIntegration<
|
||||
api::PreProcessing,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
> for Bankofamerica
|
||||
{
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &types::PaymentsPreProcessingRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<Vec<(String, request::Maskable<String>)>, 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<String, errors::ConnectorError> {
|
||||
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<RequestContent, errors::ConnectorError> {
|
||||
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<Option<services::Request>, 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,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsPreProcessingRouterData, errors::ConnectorError> {
|
||||
let response: bankofamerica::BankOfAmericaPreProcessingResponse = res
|
||||
.response
|
||||
.parse_struct("BankOfAmerica AuthEnrollmentResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
for Bankofamerica
|
||||
{
|
||||
@ -316,13 +426,17 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
|
||||
fn get_url(
|
||||
&self,
|
||||
_req: &types::PaymentsAuthorizeRouterData,
|
||||
req: &types::PaymentsAuthorizeRouterData,
|
||||
connectors: &settings::Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}pts/v2/payments/",
|
||||
api::ConnectorCommon::base_url(self, connectors)
|
||||
))
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
@ -336,9 +450,15 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
req.request.amount,
|
||||
req,
|
||||
))?;
|
||||
let connector_req =
|
||||
bankofamerica::BankOfAmericaPaymentsRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
if req.is_three_ds() && req.request.is_card() {
|
||||
let connector_req =
|
||||
bankofamerica::BankOfAmericaAuthSetupRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
} else {
|
||||
let connector_req =
|
||||
bankofamerica::BankOfAmericaPaymentsRequest::try_from(&connector_router_data)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
@ -368,6 +488,130 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
data: &types::PaymentsAuthorizeRouterData,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||
if data.is_three_ds() && data.request.is_card() {
|
||||
let response: bankofamerica::BankOfAmericaAuthSetupResponse = res
|
||||
.response
|
||||
.parse_struct("Bankofamerica AuthSetupResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
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)?;
|
||||
types::RouterData::try_from(types::ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
fn get_5xx_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
let response: bankofamerica::BankOfAmericaServerErrorResponse = res
|
||||
.response
|
||||
.parse_struct("BankOfAmericaServerErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
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<Vec<(String, request::Maskable<String>)>, 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<String, errors::ConnectorError> {
|
||||
Ok(format!("{}pts/v2/payments/", self.base_url(connectors)))
|
||||
}
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &types::PaymentsCompleteAuthorizeRouterData,
|
||||
_connectors: &settings::Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
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<Option<services::Request>, 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,
|
||||
res: Response,
|
||||
) -> CustomResult<types::PaymentsCompleteAuthorizeRouterData, errors::ConnectorError> {
|
||||
let response: bankofamerica::BankOfAmericaPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("BankOfAmerica PaymentResponse")
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use api_models::payments;
|
||||
use base64::Engine;
|
||||
use common_utils::pii;
|
||||
use common_utils::{ext_traits::ValueExt, pii};
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
use masking::{PeekInterface, Secret};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@ -8,10 +9,12 @@ use serde_json::Value;
|
||||
use crate::{
|
||||
connector::utils::{
|
||||
self, AddressDetailsData, ApplePayDecrypt, CardData, CardIssuer,
|
||||
PaymentsAuthorizeRequestData, PaymentsSyncRequestData, RouterData,
|
||||
PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData,
|
||||
PaymentsPreProcessingData, PaymentsSyncRequestData, RouterData,
|
||||
},
|
||||
consts,
|
||||
core::errors,
|
||||
services,
|
||||
types::{
|
||||
self,
|
||||
api::{self, enums as api_enums},
|
||||
@ -85,14 +88,17 @@ pub struct BankOfAmericaPaymentsRequest {
|
||||
order_information: OrderInformationWithBill,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
consumer_authentication_information: Option<BankOfAmericaConsumerAuthInformation>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
merchant_defined_information: Option<Vec<MerchantDefinedInformation>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProcessingInformation {
|
||||
capture: bool,
|
||||
capture: Option<bool>,
|
||||
payment_solution: Option<String>,
|
||||
commerce_indicator: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@ -102,6 +108,17 @@ pub struct MerchantDefinedInformation {
|
||||
value: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BankOfAmericaConsumerAuthInformation {
|
||||
ucaf_collection_indicator: Option<String>,
|
||||
cavv: Option<String>,
|
||||
ucaf_authentication_data: Option<String>,
|
||||
xid: Option<String>,
|
||||
directory_server_transaction_id: Option<String>,
|
||||
specification_version: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CaptureOptions {
|
||||
@ -287,6 +304,28 @@ 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
From<(
|
||||
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||
@ -300,11 +339,40 @@ impl
|
||||
),
|
||||
) -> Self {
|
||||
Self {
|
||||
capture: matches!(
|
||||
capture: Some(matches!(
|
||||
item.router_data.request.capture_method,
|
||||
Some(enums::CaptureMethod::Automatic) | None
|
||||
),
|
||||
)),
|
||||
payment_solution: solution.map(String::from),
|
||||
commerce_indicator: String::from("internet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
From<(
|
||||
&BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
Option<PaymentSolution>,
|
||||
&BankOfAmericaConsumerAuthValidateResponse,
|
||||
)> for ProcessingInformation
|
||||
{
|
||||
fn from(
|
||||
(item, solution, three_ds_data): (
|
||||
&BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
Option<PaymentSolution>,
|
||||
&BankOfAmericaConsumerAuthValidateResponse,
|
||||
),
|
||||
) -> Self {
|
||||
Self {
|
||||
capture: Some(matches!(
|
||||
item.router_data.request.capture_method,
|
||||
Some(enums::CaptureMethod::Automatic) | None
|
||||
)),
|
||||
payment_solution: solution.map(String::from),
|
||||
commerce_indicator: three_ds_data
|
||||
.indicator
|
||||
.to_owned()
|
||||
.unwrap_or(String::from("internet")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -319,6 +387,16 @@ 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 ForeignFrom<Value> for Vec<MerchantDefinedInformation> {
|
||||
fn foreign_from(metadata: Value) -> Self {
|
||||
let hashmap: std::collections::BTreeMap<String, Value> =
|
||||
@ -367,6 +445,83 @@ pub struct Avs {
|
||||
code_raw: String,
|
||||
}
|
||||
|
||||
impl
|
||||
TryFrom<(
|
||||
&BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
payments::Card,
|
||||
)> for BankOfAmericaPaymentsRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
(item, ccard): (
|
||||
&BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
payments::Card,
|
||||
),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let email = item.router_data.request.get_email()?;
|
||||
let bill_to = build_bill_to(item.router_data.get_billing()?, email)?;
|
||||
let order_information = OrderInformationWithBill::from((item, bill_to));
|
||||
|
||||
let card_issuer = ccard.get_card_issuer();
|
||||
let card_type = match card_issuer {
|
||||
Ok(issuer) => Some(String::from(issuer)),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let payment_information = PaymentInformation::Cards(CardPaymentInformation {
|
||||
card: Card {
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
card_type,
|
||||
},
|
||||
});
|
||||
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: "Merchant connector account 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(|metadata| {
|
||||
Vec::<MerchantDefinedInformation>::foreign_from(metadata.peek().to_owned())
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
processing_information,
|
||||
payment_information,
|
||||
order_information,
|
||||
client_reference_information,
|
||||
consumer_authentication_information,
|
||||
merchant_defined_information,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
TryFrom<(
|
||||
&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||
@ -410,6 +565,7 @@ impl
|
||||
order_information,
|
||||
client_reference_information,
|
||||
merchant_defined_information,
|
||||
consumer_authentication_information: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -455,6 +611,7 @@ impl
|
||||
order_information,
|
||||
client_reference_information,
|
||||
merchant_defined_information,
|
||||
consumer_authentication_information: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -496,6 +653,7 @@ impl
|
||||
order_information,
|
||||
client_reference_information,
|
||||
merchant_defined_information,
|
||||
consumer_authentication_information: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -552,6 +710,7 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
order_information,
|
||||
merchant_defined_information,
|
||||
client_reference_information,
|
||||
consumer_authentication_information: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -608,6 +767,64 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BankOfAmericaAuthSetupRequest {
|
||||
payment_information: PaymentInformation,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
}
|
||||
|
||||
impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>>
|
||||
for BankOfAmericaAuthSetupRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.router_data.request.payment_method_data.clone() {
|
||||
payments::PaymentMethodData::Card(ccard) => {
|
||||
let card_issuer = ccard.get_card_issuer();
|
||||
let card_type = match card_issuer {
|
||||
Ok(issuer) => Some(String::from(issuer)),
|
||||
Err(_) => None,
|
||||
};
|
||||
let payment_information = PaymentInformation::Cards(CardPaymentInformation {
|
||||
card: Card {
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
card_type,
|
||||
},
|
||||
});
|
||||
let client_reference_information = ClientReferenceInformation::from(item);
|
||||
Ok(Self {
|
||||
payment_information,
|
||||
client_reference_information,
|
||||
})
|
||||
}
|
||||
payments::PaymentMethodData::Wallet(_)
|
||||
| payments::PaymentMethodData::CardRedirect(_)
|
||||
| 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(_)
|
||||
| payments::PaymentMethodData::CardToken(_) => {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("BankOfAmerica"),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum BankofamericaPaymentStatus {
|
||||
@ -669,6 +886,29 @@ impl ForeignFrom<(BankofamericaPaymentStatus, bool)> for enums::AttemptStatus {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BankOfAmericaConsumerAuthInformationResponse {
|
||||
access_token: String,
|
||||
device_data_collection_url: String,
|
||||
reference_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClientAuthSetupInfoResponse {
|
||||
id: String,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
consumer_authentication_information: BankOfAmericaConsumerAuthInformationResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum BankOfAmericaAuthSetupResponse {
|
||||
ClientAuthSetupInfo(ClientAuthSetupInfoResponse),
|
||||
ErrorInformation(BankOfAmericaErrorInformationResponse),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum BankOfAmericaPaymentsResponse {
|
||||
@ -799,6 +1039,494 @@ fn get_payment_response(
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
BankOfAmericaAuthSetupResponse,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
BankOfAmericaAuthSetupResponse,
|
||||
types::PaymentsAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
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,
|
||||
ddc_url: info_response
|
||||
.consumer_authentication_information
|
||||
.device_data_collection_url,
|
||||
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,
|
||||
}),
|
||||
..item.data
|
||||
}),
|
||||
BankOfAmericaAuthSetupResponse::ErrorInformation(error_response) => {
|
||||
let error_reason = error_response
|
||||
.error_information
|
||||
.message
|
||||
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string());
|
||||
let error_message = error_response.error_information.reason;
|
||||
Ok(Self {
|
||||
response: Err(types::ErrorResponse {
|
||||
code: error_message
|
||||
.clone()
|
||||
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
|
||||
message: error_message.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
|
||||
reason: Some(error_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(BankOfAmericaAuthEnrollmentRequest),
|
||||
AuthValidate(BankOfAmericaAuthValidateRequest),
|
||||
}
|
||||
|
||||
impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsPreProcessingRouterData>>
|
||||
for BankOfAmericaPreProcessingRequest
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &BankOfAmericaRouterData<&types::PaymentsPreProcessingRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
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 {
|
||||
payments::PaymentMethodData::Card(ccard) => {
|
||||
let card_issuer = ccard.get_card_issuer();
|
||||
let card_type = match card_issuer {
|
||||
Ok(issuer) => Some(String::from(issuer)),
|
||||
Err(_) => None,
|
||||
};
|
||||
Ok(PaymentInformation::Cards(CardPaymentInformation {
|
||||
card: Card {
|
||||
number: ccard.card_number,
|
||||
expiration_month: ccard.card_exp_month,
|
||||
expiration_year: ccard.card_exp_year,
|
||||
security_code: ccard.card_cvc,
|
||||
card_type,
|
||||
},
|
||||
}))
|
||||
}
|
||||
payments::PaymentMethodData::Wallet(_)
|
||||
| payments::PaymentMethodData::CardRedirect(_)
|
||||
| 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(_)
|
||||
| payments::PaymentMethodData::CardToken(_) => {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("BankOfAmerica"),
|
||||
))
|
||||
}
|
||||
}?;
|
||||
|
||||
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_billing()?, email)?;
|
||||
let order_information = OrderInformationWithBill {
|
||||
amount_details,
|
||||
bill_to,
|
||||
};
|
||||
Ok(Self::AuthEnrollment(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(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<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: &BankOfAmericaRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
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 {
|
||||
payments::PaymentMethodData::Card(ccard) => Self::try_from((item, ccard)),
|
||||
payments::PaymentMethodData::Wallet(_)
|
||||
| payments::PaymentMethodData::CardRedirect(_)
|
||||
| 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(_)
|
||||
| payments::PaymentMethodData::CardToken(_) => {
|
||||
Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("BankOfAmerica"),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[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<String>,
|
||||
cavv: Option<String>,
|
||||
ucaf_authentication_data: Option<String>,
|
||||
xid: Option<String>,
|
||||
specification_version: Option<String>,
|
||||
directory_server_transaction_id: Option<String>,
|
||||
indicator: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct BankOfAmericaThreeDSMetadata {
|
||||
three_ds_data: BankOfAmericaConsumerAuthValidateResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BankOfAmericaConsumerAuthInformationEnrollmentResponse {
|
||||
access_token: Option<String>,
|
||||
step_up_url: Option<String>,
|
||||
//Added to segregate the three_ds_data in a separate struct
|
||||
#[serde(flatten)]
|
||||
validate_response: BankOfAmericaConsumerAuthValidateResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ClientAuthCheckInfoResponse {
|
||||
id: String,
|
||||
client_reference_information: ClientReferenceInformation,
|
||||
consumer_authentication_information: BankOfAmericaConsumerAuthInformationEnrollmentResponse,
|
||||
status: BankOfAmericaAuthEnrollmentStatus,
|
||||
error_information: Option<BankOfAmericaErrorInformation>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum BankOfAmericaPreProcessingResponse {
|
||||
ClientAuthCheckInfo(Box<ClientAuthCheckInfoResponse>),
|
||||
ErrorInformation(BankOfAmericaErrorInformationResponse),
|
||||
}
|
||||
|
||||
impl From<BankOfAmericaAuthEnrollmentStatus> for enums::AttemptStatus {
|
||||
fn from(item: BankOfAmericaAuthEnrollmentStatus) -> Self {
|
||||
match item {
|
||||
BankOfAmericaAuthEnrollmentStatus::PendingAuthentication => Self::AuthenticationPending,
|
||||
BankOfAmericaAuthEnrollmentStatus::AuthenticationSuccessful => {
|
||||
Self::AuthenticationSuccessful
|
||||
}
|
||||
BankOfAmericaAuthEnrollmentStatus::AuthenticationFailed => Self::AuthenticationFailed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
BankOfAmericaPreProcessingResponse,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::PaymentsPreProcessingData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
BankOfAmericaPreProcessingResponse,
|
||||
types::PaymentsPreProcessingData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.response {
|
||||
BankOfAmericaPreProcessingResponse::ClientAuthCheckInfo(info_response) => {
|
||||
let status = enums::AttemptStatus::from(info_response.status);
|
||||
let risk_info: Option<ClientRiskInformation> = None;
|
||||
if utils::is_payment_failure(status) {
|
||||
let response = Err(types::ErrorResponse::from((
|
||||
&info_response.error_information,
|
||||
&risk_info,
|
||||
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,
|
||||
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,
|
||||
)
|
||||
.into_report()
|
||||
.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,
|
||||
}),
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
BankOfAmericaPreProcessingResponse::ErrorInformation(ref error_response) => {
|
||||
let error_reason = error_response
|
||||
.error_information
|
||||
.message
|
||||
.to_owned()
|
||||
.unwrap_or(consts::NO_ERROR_MESSAGE.to_string());
|
||||
let error_message = error_response.error_information.reason.to_owned();
|
||||
let response = Err(types::ErrorResponse {
|
||||
code: error_message
|
||||
.clone()
|
||||
.unwrap_or(consts::NO_ERROR_CODE.to_string()),
|
||||
message: error_message.unwrap_or(consts::NO_ERROR_MESSAGE.to_string()),
|
||||
reason: Some(error_reason),
|
||||
status_code: item.http_code,
|
||||
attempt_status: None,
|
||||
connector_transaction_id: Some(error_response.id.clone()),
|
||||
});
|
||||
Ok(Self {
|
||||
response,
|
||||
status: enums::AttemptStatus::AuthenticationFailed,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
F,
|
||||
BankOfAmericaPaymentsResponse,
|
||||
types::CompleteAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
> for types::RouterData<F, types::CompleteAuthorizeData, types::PaymentsResponseData>
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: types::ResponseRouterData<
|
||||
F,
|
||||
BankOfAmericaPaymentsResponse,
|
||||
types::CompleteAuthorizeData,
|
||||
types::PaymentsResponseData,
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
match item.response {
|
||||
BankOfAmericaPaymentsResponse::ClientReferenceInformation(info_response) => {
|
||||
let status = enums::AttemptStatus::foreign_from((
|
||||
info_response.status.clone(),
|
||||
item.data.request.is_auto_capture()?,
|
||||
));
|
||||
let response = get_payment_response((&info_response, status, item.http_code));
|
||||
Ok(Self {
|
||||
status,
|
||||
response,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
BankOfAmericaPaymentsResponse::ErrorInformation(ref error_response) => {
|
||||
Ok(Self::from((
|
||||
&error_response.clone(),
|
||||
item,
|
||||
Some(enums::AttemptStatus::Failure),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F>
|
||||
TryFrom<
|
||||
types::ResponseRouterData<
|
||||
|
||||
@ -874,6 +874,33 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res)
|
||||
}
|
||||
|
||||
fn get_5xx_error_response(
|
||||
&self,
|
||||
res: types::Response,
|
||||
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||
let response: cybersource::CybersourceServerErrorResponse = res
|
||||
.response
|
||||
.parse_struct("CybersourceServerErrorResponse")
|
||||
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||
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(types::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
|
||||
|
||||
@ -1489,7 +1489,8 @@ where
|
||||
router_data = router_data.preprocessing_steps(state, connector).await?;
|
||||
|
||||
(router_data, false)
|
||||
} else if connector.connector_name == router_types::Connector::Cybersource
|
||||
} else if (connector.connector_name == router_types::Connector::Cybersource
|
||||
|| connector.connector_name == router_types::Connector::Bankofamerica)
|
||||
&& is_operation_complete_authorize(&operation)
|
||||
&& router_data.auth_type == storage_enums::AuthenticationType::ThreeDs
|
||||
{
|
||||
|
||||
@ -147,7 +147,6 @@ impl<const T: u8>
|
||||
default_imp_for_complete_authorize!(
|
||||
connector::Aci,
|
||||
connector::Adyen,
|
||||
connector::Bankofamerica,
|
||||
connector::Bitpay,
|
||||
connector::Boku,
|
||||
connector::Cashtocode,
|
||||
@ -863,7 +862,6 @@ default_imp_for_pre_processing_steps!(
|
||||
connector::Airwallex,
|
||||
connector::Authorizedotnet,
|
||||
connector::Bambora,
|
||||
connector::Bankofamerica,
|
||||
connector::Bitpay,
|
||||
connector::Bluesnap,
|
||||
connector::Boku,
|
||||
|
||||
Reference in New Issue
Block a user