feat(connector): [Nuvei] add support for bank redirect Eps, Sofort, Giropay, Ideal (#870)

Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
Jagan
2023-04-21 12:51:06 +05:30
committed by GitHub
parent ccc0c3f960
commit c1a25b30bd
12 changed files with 1162 additions and 152 deletions

View File

@ -10,7 +10,7 @@ use ::common_utils::{
use error_stack::{IntoReport, ResultExt};
use transformers as nuvei;
use super::utils::{self, to_boolean, RouterData};
use super::utils::{self, RouterData};
use crate::{
configs::settings,
core::{
@ -486,35 +486,42 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
)
.await?;
router_data.session_token = resp.session_token;
let (enrolled_for_3ds, related_transaction_id) = match router_data.auth_type {
storage_models::enums::AuthenticationType::ThreeDs => {
let integ: Box<
&(dyn ConnectorIntegration<
InitPayment,
types::PaymentsAuthorizeData,
types::PaymentsResponseData,
> + Send
+ Sync
+ 'static),
> = Box::new(&Self);
let init_data = &types::PaymentsInitRouterData::from((
&router_data,
router_data.request.clone(),
));
let init_resp = services::execute_connector_processing_step(
app_state,
integ,
init_data,
payments::CallConnectorAction::Trigger,
)
.await?;
let (enrolled_for_3ds, related_transaction_id) =
match (router_data.auth_type, router_data.payment_method) {
(
init_resp.request.enrolled_for_3ds,
init_resp.request.related_transaction_id,
)
}
storage_models::enums::AuthenticationType::NoThreeDs => (false, None),
};
storage_models::enums::AuthenticationType::ThreeDs,
storage_models::enums::PaymentMethod::Card,
) => {
let integ: Box<
&(dyn ConnectorIntegration<
InitPayment,
types::PaymentsAuthorizeData,
types::PaymentsResponseData,
> + Send
+ Sync
+ 'static),
> = Box::new(&Self);
let init_data = &types::PaymentsInitRouterData::from((
&router_data,
router_data.request.clone(),
));
let init_resp = services::execute_connector_processing_step(
app_state,
integ,
init_data,
payments::CallConnectorAction::Trigger,
)
.await?;
match init_resp.response {
Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse {
enrolled_v2,
related_transaction_id,
}) => (enrolled_v2, related_transaction_id),
_ => (false, None),
}
}
_ => (false, None),
};
router_data.request.enrolled_for_3ds = enrolled_for_3ds;
router_data.request.related_transaction_id = related_transaction_id;
@ -725,28 +732,12 @@ impl ConnectorIntegration<InitPayment, types::PaymentsAuthorizeData, types::Paym
.response
.parse_struct("NuveiPaymentsResponse")
.switch()?;
let response_data = types::RouterData::try_from(types::ResponseRouterData {
response: response.clone(),
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
.change_context(errors::ConnectorError::ResponseHandlingFailed)?;
let is_enrolled_for_3ds = response
.clone()
.payment_option
.and_then(|po| po.card)
.and_then(|c| c.three_d)
.and_then(|t| t.v2supported)
.map(to_boolean)
.unwrap_or_default();
Ok(types::RouterData {
request: types::PaymentsAuthorizeData {
enrolled_for_3ds: is_enrolled_for_3ds,
related_transaction_id: response.transaction_id,
..response_data.request
},
..response_data
})
.change_context(errors::ConnectorError::ResponseHandlingFailed)
}
fn get_error_response(

View File

@ -1,3 +1,4 @@
use api_models::payments;
use common_utils::{
crypto::{self, GenerateDigest},
date_time, fp_utils,
@ -10,12 +11,13 @@ use serde::{Deserialize, Serialize};
use crate::{
connector::utils::{
self, MandateData, PaymentsAuthorizeRequestData, PaymentsCancelRequestData, RouterData,
self, AddressDetailsData, MandateData, PaymentsAuthorizeRequestData,
PaymentsCancelRequestData, RouterData,
},
consts,
core::errors,
services,
types::{self, api, storage::enums},
types::{self, api, storage::enums, transformers::ForeignTryFrom},
};
#[derive(Debug, Serialize, Default, Deserialize)]
@ -62,7 +64,7 @@ pub struct NuveiPaymentsRequest {
pub merchant_site_id: String,
pub client_request_id: String,
pub amount: String,
pub currency: String,
pub currency: storage_models::enums::Currency,
/// This ID uniquely identifies your consumer/user in your system.
pub user_token_id: Option<Secret<String, Email>>,
pub client_unique_id: String,
@ -95,7 +97,7 @@ pub struct NuveiPaymentFlowRequest {
pub merchant_site_id: String,
pub client_request_id: String,
pub amount: String,
pub currency: String,
pub currency: storage_models::enums::Currency,
pub related_transaction_id: Option<String>,
pub checksum: String,
}
@ -125,22 +127,65 @@ pub struct PaymentOption {
pub billing_address: Option<BillingAddress>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NuveiBIC {
#[serde(rename = "ABNANL2A")]
Abnamro,
#[serde(rename = "ASNBNL21")]
ASNBank,
#[serde(rename = "BUNQNL2A")]
Bunq,
#[serde(rename = "INGBNL2A")]
Ing,
#[serde(rename = "KNABNL2H")]
Knab,
#[serde(rename = "RABONL2U")]
Rabobank,
#[serde(rename = "RBRBNL21")]
RegioBank,
#[serde(rename = "SNSBNL2A")]
SNSBank,
#[serde(rename = "TRIONL2U")]
TriodosBank,
#[serde(rename = "FVLBNL22")]
VanLanschotBankiers,
#[serde(rename = "MOYONL21")]
Moneyou,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AlternativePaymentMethod {
pub payment_method: AlternativePaymentMethodType,
#[serde(rename = "BIC")]
pub bank_id: Option<NuveiBIC>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AlternativePaymentMethodType {
#[default]
ApmgwExpresscheckout,
#[serde(rename = "apmgw_expresscheckout")]
Expresscheckout,
#[serde(rename = "apmgw_Giropay")]
Giropay,
#[serde(rename = "apmgw_Sofort")]
Sofort,
#[serde(rename = "apmgw_iDeal")]
Ideal,
#[serde(rename = "apmgw_EPS")]
Eps,
}
#[serde_with::skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BillingAddress {
pub email: Secret<String, Email>,
pub email: Option<Secret<String, Email>>,
pub first_name: Option<Secret<String>>,
pub last_name: Option<Secret<String>>,
pub country: api_models::enums::CountryCode,
}
@ -366,28 +411,34 @@ impl<F, T>
#[derive(Debug, Default)]
pub struct NuveiCardDetails {
card: api_models::payments::Card,
card: payments::Card,
three_d: Option<ThreeD>,
}
impl From<api_models::payments::GooglePayWalletData> for NuveiPaymentsRequest {
fn from(gpay_data: api_models::payments::GooglePayWalletData) -> Self {
Self {
impl TryFrom<payments::GooglePayWalletData> for NuveiPaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(gpay_data: payments::GooglePayWalletData) -> Result<Self, Self::Error> {
Ok(Self {
payment_option: PaymentOption {
card: Some(Card {
external_token: Some(ExternalToken {
external_token_provider: ExternalTokenProvider::GooglePay,
mobile_token: gpay_data.tokenization_data.token,
mobile_token: common_utils::ext_traits::Encode::<
payments::GooglePayWalletData,
>::encode_to_string_of_json(
&gpay_data
)
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
}),
..Default::default()
}),
..Default::default()
},
..Default::default()
}
})
}
}
impl From<api_models::payments::ApplePayWalletData> for NuveiPaymentsRequest {
fn from(apple_pay_data: api_models::payments::ApplePayWalletData) -> Self {
impl From<payments::ApplePayWalletData> for NuveiPaymentsRequest {
fn from(apple_pay_data: payments::ApplePayWalletData) -> Self {
Self {
payment_option: PaymentOption {
card: Some(Card {
@ -404,6 +455,109 @@ impl From<api_models::payments::ApplePayWalletData> for NuveiPaymentsRequest {
}
}
impl TryFrom<api_models::enums::BankNames> for NuveiBIC {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(bank: api_models::enums::BankNames) -> Result<Self, Self::Error> {
match bank {
api_models::enums::BankNames::AbnAmro => Ok(Self::Abnamro),
api_models::enums::BankNames::AsnBank => Ok(Self::ASNBank),
api_models::enums::BankNames::Bunq => Ok(Self::Bunq),
api_models::enums::BankNames::Ing => Ok(Self::Ing),
api_models::enums::BankNames::Knab => Ok(Self::Knab),
api_models::enums::BankNames::Rabobank => Ok(Self::Rabobank),
api_models::enums::BankNames::SnsBank => Ok(Self::SNSBank),
api_models::enums::BankNames::TriodosBank => Ok(Self::TriodosBank),
api_models::enums::BankNames::VanLanschot => Ok(Self::VanLanschotBankiers),
api_models::enums::BankNames::Moneyou => Ok(Self::Moneyou),
_ => Err(errors::ConnectorError::FlowNotSupported {
flow: bank.to_string(),
connector: "Nuvei".to_string(),
}
.into()),
}
}
}
impl<F>
ForeignTryFrom<(
AlternativePaymentMethodType,
Option<payments::BankRedirectData>,
&types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
)> for NuveiPaymentsRequest
{
type Error = error_stack::Report<errors::ConnectorError>;
fn foreign_try_from(
data: (
AlternativePaymentMethodType,
Option<payments::BankRedirectData>,
&types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
),
) -> Result<Self, Self::Error> {
let (payment_method, redirect, item) = data;
let (billing_address, bank_id) = match (&payment_method, redirect) {
(AlternativePaymentMethodType::Expresscheckout, _) => (
Some(BillingAddress {
email: Some(item.request.get_email()?),
country: item.get_billing_country()?,
..Default::default()
}),
None,
),
(AlternativePaymentMethodType::Giropay, _) => (
Some(BillingAddress {
email: Some(item.request.get_email()?),
country: item.get_billing_country()?,
..Default::default()
}),
None,
),
(AlternativePaymentMethodType::Sofort, _) | (AlternativePaymentMethodType::Eps, _) => {
let address = item.get_billing_address()?;
(
Some(BillingAddress {
first_name: Some(address.get_first_name()?.clone()),
last_name: Some(address.get_last_name()?.clone()),
email: Some(item.request.get_email()?),
country: item.get_billing_country()?,
}),
None,
)
}
(
AlternativePaymentMethodType::Ideal,
Some(payments::BankRedirectData::Ideal { bank_name, .. }),
) => {
let address = item.get_billing_address()?;
(
Some(BillingAddress {
first_name: Some(address.get_first_name()?.clone()),
last_name: Some(address.get_last_name()?.clone()),
email: Some(item.request.get_email()?),
country: item.get_billing_country()?,
}),
Some(NuveiBIC::try_from(bank_name)?),
)
}
_ => Err(errors::ConnectorError::NotSupported {
payment_method: "Bank Redirect".to_string(),
connector: "Nuvei",
payment_experience: "Redirection".to_string(),
})?,
};
Ok(Self {
payment_option: PaymentOption {
alternative_payment_method: Some(AlternativePaymentMethod {
payment_method,
bank_id,
}),
..Default::default()
},
billing_address,
..Default::default()
})
}
}
impl<F>
TryFrom<(
&types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
@ -421,23 +575,13 @@ impl<F>
let request_data = match item.request.payment_method_data.clone() {
api::PaymentMethodData::Card(card) => get_card_info(item, &card),
api::PaymentMethodData::Wallet(wallet) => match wallet {
api_models::payments::WalletData::GooglePay(gpay_data) => Ok(Self::from(gpay_data)),
api_models::payments::WalletData::ApplePay(apple_pay_data) => {
Ok(Self::from(apple_pay_data))
}
api_models::payments::WalletData::PaypalRedirect(_) => Ok(Self {
payment_option: PaymentOption {
alternative_payment_method: Some(AlternativePaymentMethod {
payment_method: AlternativePaymentMethodType::ApmgwExpresscheckout,
}),
..Default::default()
},
billing_address: Some(BillingAddress {
email: item.request.get_email()?,
country: item.get_billing_country()?,
}),
..Default::default()
}),
payments::WalletData::GooglePay(gpay_data) => Self::try_from(gpay_data),
payments::WalletData::ApplePay(apple_pay_data) => Ok(Self::from(apple_pay_data)),
payments::WalletData::PaypalRedirect(_) => Self::foreign_try_from((
AlternativePaymentMethodType::Expresscheckout,
None,
item,
)),
_ => Err(errors::ConnectorError::NotSupported {
payment_method: "Wallet".to_string(),
connector: "Nuvei",
@ -445,11 +589,39 @@ impl<F>
}
.into()),
},
api::PaymentMethodData::BankRedirect(redirect) => match redirect {
payments::BankRedirectData::Eps { .. } => Self::foreign_try_from((
AlternativePaymentMethodType::Eps,
Some(redirect),
item,
)),
payments::BankRedirectData::Giropay { .. } => Self::foreign_try_from((
AlternativePaymentMethodType::Giropay,
Some(redirect),
item,
)),
payments::BankRedirectData::Ideal { .. } => Self::foreign_try_from((
AlternativePaymentMethodType::Ideal,
Some(redirect),
item,
)),
payments::BankRedirectData::Sofort { .. } => Self::foreign_try_from((
AlternativePaymentMethodType::Sofort,
Some(redirect),
item,
)),
_ => Err(errors::ConnectorError::NotSupported {
payment_method: "Bank Redirect".to_string(),
connector: "Nuvei",
payment_experience: "RedirectToUrl".to_string(),
}
.into()),
},
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
}?;
let request = Self::try_from(NuveiPaymentRequestData {
amount: item.request.amount.clone().to_string(),
currency: item.request.currency.clone().to_string(),
amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
currency: item.request.currency,
connector_auth_type: item.connector_auth_type.clone(),
client_request_id: item.attempt_id.clone(),
session_token: data.1,
@ -461,6 +633,7 @@ impl<F>
user_token_id: request_data.user_token_id,
related_transaction_id: request_data.related_transaction_id,
payment_option: request_data.payment_option,
billing_address: request_data.billing_address,
..request
})
}
@ -468,7 +641,7 @@ impl<F>
fn get_card_info<F>(
item: &types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
card_details: &api_models::payments::Card,
card_details: &payments::Card,
) -> Result<NuveiPaymentsRequest, error_stack::Report<errors::ConnectorError>> {
let browser_info = item.request.get_browser_info()?;
let related_transaction_id = if item.is_three_ds() {
@ -493,8 +666,8 @@ fn get_card_info<F>(
match item.request.setup_mandate_details.clone() {
Some(mandate_data) => {
let details = match mandate_data.mandate_type {
api_models::payments::MandateType::SingleUse(details) => details,
api_models::payments::MandateType::MultiUse(details) => {
payments::MandateType::SingleUse(details) => details,
payments::MandateType::MultiUse(details) => {
details.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "mandate_data.mandate_type.multi_use",
})?
@ -593,8 +766,8 @@ impl TryFrom<(&types::PaymentsCompleteAuthorizeRouterData, String)> for NuveiPay
)),
}?;
let request = Self::try_from(NuveiPaymentRequestData {
amount: item.request.amount.clone().to_string(),
currency: item.request.currency.clone().to_string(),
amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
currency: item.request.currency,
connector_auth_type: item.connector_auth_type.clone(),
client_request_id: item.attempt_id.clone(),
session_token: data.1,
@ -640,7 +813,7 @@ impl TryFrom<NuveiPaymentRequestData> for NuveiPaymentsRequest {
merchant_site_id,
client_request_id,
request.amount.clone(),
request.currency.clone(),
request.currency.clone().to_string(),
time_stamp,
merchant_secret,
])?,
@ -673,7 +846,7 @@ impl TryFrom<NuveiPaymentRequestData> for NuveiPaymentFlowRequest {
merchant_site_id,
client_request_id,
request.amount.clone(),
request.currency.clone(),
request.currency.clone().to_string(),
request.related_transaction_id.clone().unwrap_or_default(),
time_stamp,
merchant_secret,
@ -685,11 +858,10 @@ impl TryFrom<NuveiPaymentRequestData> for NuveiPaymentFlowRequest {
}
}
/// Common request handler for all the flows that has below fields in common
#[derive(Debug, Clone, Default)]
pub struct NuveiPaymentRequestData {
pub amount: String,
pub currency: String,
pub currency: storage_models::enums::Currency,
pub related_transaction_id: Option<String>,
pub client_request_id: String,
pub connector_auth_type: types::ConnectorAuthType,
@ -704,7 +876,7 @@ impl TryFrom<&types::PaymentsCaptureRouterData> for NuveiPaymentFlowRequest {
client_request_id: item.attempt_id.clone(),
connector_auth_type: item.connector_auth_type.clone(),
amount: item.request.amount_to_capture.to_string(),
currency: item.request.currency.to_string(),
currency: item.request.currency,
related_transaction_id: Some(item.request.connector_transaction_id.clone()),
..Default::default()
})
@ -717,7 +889,7 @@ impl TryFrom<&types::RefundExecuteRouterData> for NuveiPaymentFlowRequest {
client_request_id: item.attempt_id.clone(),
connector_auth_type: item.connector_auth_type.clone(),
amount: item.request.amount.to_string(),
currency: item.request.currency.to_string(),
currency: item.request.currency,
related_transaction_id: Some(item.request.connector_transaction_id.clone()),
..Default::default()
})
@ -741,7 +913,7 @@ impl TryFrom<&types::PaymentsCancelRouterData> for NuveiPaymentFlowRequest {
client_request_id: item.attempt_id.clone(),
connector_auth_type: item.connector_auth_type.clone(),
amount: item.request.get_amount()?.to_string(),
currency: item.request.get_currency()?.to_string(),
currency: item.request.get_currency()?,
related_transaction_id: Some(item.request.connector_transaction_id.clone()),
..Default::default()
})
@ -868,14 +1040,11 @@ fn get_payment_status(response: &NuveiPaymentsResponse) -> enums::AttemptStatus
NuveiTransactionStatus::Declined | NuveiTransactionStatus::Error => {
match response.transaction_type {
Some(NuveiTransactionType::Auth) => enums::AttemptStatus::AuthorizationFailed,
Some(NuveiTransactionType::Sale) | Some(NuveiTransactionType::Settle) => {
enums::AttemptStatus::Failure
}
Some(NuveiTransactionType::Void) => enums::AttemptStatus::VoidFailed,
Some(NuveiTransactionType::Auth3D) => {
enums::AttemptStatus::AuthenticationFailed
}
_ => enums::AttemptStatus::Pending,
_ => enums::AttemptStatus::Failure,
}
}
NuveiTransactionStatus::Processing => enums::AttemptStatus::Pending,
@ -888,16 +1057,58 @@ fn get_payment_status(response: &NuveiPaymentsResponse) -> enums::AttemptStatus
}
}
fn build_error_response<T>(
response: &NuveiPaymentsResponse,
http_code: u16,
) -> Option<Result<T, types::ErrorResponse>> {
match response.status {
NuveiPaymentStatus::Error => Some(get_error_response(
response.err_code,
&response.reason,
http_code,
)),
_ => {
let err = Some(get_error_response(
response.gw_error_code,
&response.gw_error_reason,
http_code,
));
match response.transaction_status {
Some(NuveiTransactionStatus::Error) => err,
_ => match response
.gw_error_reason
.as_ref()
.map(|r| r.eq("Missing argument"))
{
Some(true) => err,
_ => None,
},
}
}
}
}
pub trait NuveiPaymentsGenericResponse {}
impl NuveiPaymentsGenericResponse for api::Authorize {}
impl NuveiPaymentsGenericResponse for api::CompleteAuthorize {}
impl NuveiPaymentsGenericResponse for api::Void {}
impl NuveiPaymentsGenericResponse for api::PSync {}
impl NuveiPaymentsGenericResponse for api::Capture {}
impl<F, T>
TryFrom<types::ResponseRouterData<F, NuveiPaymentsResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
where
F: NuveiPaymentsGenericResponse,
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, NuveiPaymentsResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_data = match item.data.payment_method {
storage_models::enums::PaymentMethod::Wallet => item
storage_models::enums::PaymentMethod::Wallet
| storage_models::enums::PaymentMethod::BankRedirect => item
.response
.payment_option
.as_ref()
@ -920,46 +1131,65 @@ impl<F, T>
let response = item.response;
Ok(Self {
status: get_payment_status(&response),
response: match response.status {
NuveiPaymentStatus::Error => {
get_error_response(response.err_code, response.reason, item.http_code)
}
_ => match response.transaction_status {
Some(NuveiTransactionStatus::Error) => get_error_response(
response.gw_error_code,
response.gw_error_reason,
item.http_code,
),
_ => Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: response
.transaction_id
.map_or(response.order_id, Some) // For paypal there will be no transaction_id, only order_id will be present
.map(types::ResponseId::ConnectorTransactionId)
.ok_or(errors::ConnectorError::MissingConnectorTransactionID)?,
redirection_data,
mandate_reference: response
.payment_option
.and_then(|po| po.user_payment_option_id),
// we don't need to save session token for capture, void flow so ignoring if it is not present
connector_metadata: if let Some(token) = response.session_token {
Some(
serde_json::to_value(NuveiMeta {
session_token: token,
})
.into_report()
.change_context(errors::ConnectorError::ResponseHandlingFailed)?,
)
} else {
None
},
}),
},
response: if let Some(err) = build_error_response(&response, item.http_code) {
err
} else {
Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: response
.transaction_id
.map_or(response.order_id, Some) // For paypal there will be no transaction_id, only order_id will be present
.map(types::ResponseId::ConnectorTransactionId)
.ok_or(errors::ConnectorError::MissingConnectorTransactionID)?,
redirection_data,
mandate_reference: response
.payment_option
.and_then(|po| po.user_payment_option_id),
// we don't need to save session token for capture, void flow so ignoring if it is not present
connector_metadata: if let Some(token) = response.session_token {
Some(
serde_json::to_value(NuveiMeta {
session_token: token,
})
.into_report()
.change_context(errors::ConnectorError::ResponseHandlingFailed)?,
)
} else {
None
},
})
},
..item.data
})
}
}
impl TryFrom<types::PaymentsInitResponseRouterData<NuveiPaymentsResponse>>
for types::PaymentsInitRouterData
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::PaymentsInitResponseRouterData<NuveiPaymentsResponse>,
) -> Result<Self, Self::Error> {
let response = item.response;
let is_enrolled_for_3ds = response
.clone()
.payment_option
.and_then(|po| po.card)
.and_then(|c| c.three_d)
.and_then(|t| t.v2supported)
.map(utils::to_boolean)
.unwrap_or_default();
Ok(Self {
status: get_payment_status(&response),
response: Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse {
enrolled_v2: is_enrolled_for_3ds,
related_transaction_id: response.transaction_id,
}),
..item.data
})
}
}
impl From<NuveiTransactionStatus> for enums::RefundStatus {
fn from(item: NuveiTransactionStatus) -> Self {
match item {
@ -1022,11 +1252,11 @@ fn get_refund_response(
.unwrap_or(enums::RefundStatus::Failure);
match response.status {
NuveiPaymentStatus::Error => {
get_error_response(response.err_code, response.reason, http_code)
get_error_response(response.err_code, &response.reason, http_code)
}
_ => match response.transaction_status {
Some(NuveiTransactionStatus::Error) => {
get_error_response(response.gw_error_code, response.gw_error_reason, http_code)
get_error_response(response.gw_error_code, &response.gw_error_reason, http_code)
}
_ => Ok(types::RefundsResponseData {
connector_refund_id: txn_id,
@ -1038,14 +1268,16 @@ fn get_refund_response(
fn get_error_response<T>(
error_code: Option<i64>,
error_msg: Option<String>,
error_msg: &Option<String>,
http_code: u16,
) -> Result<T, types::ErrorResponse> {
Err(types::ErrorResponse {
code: error_code
.map(|c| c.to_string())
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: error_msg.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
message: error_msg
.clone()
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: None,
status_code: http_code,
})

View File

@ -117,8 +117,10 @@ impl types::PaymentsAuthorizeRouterData {
.execute_pretasks(self, state)
.await
.map_err(|error| error.to_payment_failed_response())?;
logger::debug!(completed_pre_tasks=?true);
if self.should_proceed_with_authorize() {
self.decide_authentication_type();
logger::debug!(auth_type=?self.auth_type);
let resp = services::execute_connector_processing_step(
state,
connector_integration,

View File

@ -387,6 +387,7 @@ async fn payment_response_update_tracker<F: Clone, T>(
types::PaymentsResponseData::SessionResponse { .. } => (None, None),
types::PaymentsResponseData::SessionTokenResponse { .. } => (None, None),
types::PaymentsResponseData::TokenizationResponse { .. } => (None, None),
types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => (None, None),
},
};

View File

@ -51,6 +51,8 @@ pub type PaymentsSyncResponseRouterData<R> =
ResponseRouterData<api::PSync, R, PaymentsSyncData, PaymentsResponseData>;
pub type PaymentsSessionResponseRouterData<R> =
ResponseRouterData<api::Session, R, PaymentsSessionData, PaymentsResponseData>;
pub type PaymentsInitResponseRouterData<R> =
ResponseRouterData<api::InitPayment, R, PaymentsAuthorizeData, PaymentsResponseData>;
pub type PaymentsCaptureResponseRouterData<R> =
ResponseRouterData<api::Capture, R, PaymentsCaptureData, PaymentsResponseData>;
pub type TokenizationResponseRouterData<R> = ResponseRouterData<
@ -283,6 +285,10 @@ pub enum PaymentsResponseData {
TokenizationResponse {
token: String,
},
ThreeDSEnrollmentResponse {
enrolled_v2: bool,
related_transaction_id: Option<String>,
},
}
#[derive(Debug, Clone, Default)]