mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
fix(nuvei): nuvei 3ds fix + psync fix (#9279)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -53,6 +53,7 @@ use masking::ExposeInterface;
|
||||
use transformers as nuvei;
|
||||
|
||||
use crate::{
|
||||
connectors::nuvei::transformers::{NuveiPaymentsResponse, NuveiTransactionSyncResponse},
|
||||
constants::headers,
|
||||
types::ResponseRouterData,
|
||||
utils::{self, is_mandate_supported, PaymentMethodDataType, RouterData as _},
|
||||
@ -211,7 +212,7 @@ impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsRespons
|
||||
RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
|
||||
errors::ConnectorError,
|
||||
> {
|
||||
let response: nuvei::NuveiPaymentsResponse = res
|
||||
let response: NuveiPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.switch()?;
|
||||
@ -234,6 +235,85 @@ impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsRespons
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Nuvei {
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &PaymentsCancelRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<Vec<(String, masking::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: &PaymentsCancelRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}ppp/api/v1/voidTransaction.do",
|
||||
ConnectorCommon::base_url(self, connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &PaymentsCancelRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = nuvei::NuveiPaymentFlowRequest::try_from(req)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &PaymentsCancelRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<Option<Request>, errors::ConnectorError> {
|
||||
let request = RequestBuilder::new()
|
||||
.method(Method::Post)
|
||||
.url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsVoidType::get_headers(self, req, connectors)?)
|
||||
.set_body(types::PaymentsVoidType::get_request_body(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.build();
|
||||
Ok(Some(request))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &PaymentsCancelRouterData,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsCancelRouterData, errors::ConnectorError> {
|
||||
let response: NuveiPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.switch()?;
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
RouterData::try_from(ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res, event_builder)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<CompleteAuthorize, CompleteAuthorizeData, PaymentsResponseData>
|
||||
for Nuvei
|
||||
{
|
||||
@ -293,87 +373,7 @@ impl ConnectorIntegration<CompleteAuthorize, CompleteAuthorizeData, PaymentsResp
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsCompleteAuthorizeRouterData, errors::ConnectorError> {
|
||||
let response: nuvei::NuveiPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.switch()?;
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
|
||||
RouterData::try_from(ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
http_code: res.status_code,
|
||||
})
|
||||
.change_context(errors::ConnectorError::ResponseHandlingFailed)
|
||||
}
|
||||
|
||||
fn get_error_response(
|
||||
&self,
|
||||
res: Response,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
|
||||
self.build_error_response(res, event_builder)
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Nuvei {
|
||||
fn get_headers(
|
||||
&self,
|
||||
req: &PaymentsCancelRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<Vec<(String, masking::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: &PaymentsCancelRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}ppp/api/v1/voidTransaction.do",
|
||||
ConnectorCommon::base_url(self, connectors)
|
||||
))
|
||||
}
|
||||
|
||||
fn get_request_body(
|
||||
&self,
|
||||
req: &PaymentsCancelRouterData,
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = nuvei::NuveiPaymentFlowRequest::try_from(req)?;
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
|
||||
fn build_request(
|
||||
&self,
|
||||
req: &PaymentsCancelRouterData,
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<Option<Request>, errors::ConnectorError> {
|
||||
let request = RequestBuilder::new()
|
||||
.method(Method::Post)
|
||||
.url(&types::PaymentsVoidType::get_url(self, req, connectors)?)
|
||||
.attach_default_headers()
|
||||
.headers(types::PaymentsVoidType::get_headers(self, req, connectors)?)
|
||||
.set_body(types::PaymentsVoidType::get_request_body(
|
||||
self, req, connectors,
|
||||
)?)
|
||||
.build();
|
||||
Ok(Some(request))
|
||||
}
|
||||
|
||||
fn handle_response(
|
||||
&self,
|
||||
data: &PaymentsCancelRouterData,
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsCancelRouterData, errors::ConnectorError> {
|
||||
let response: nuvei::NuveiPaymentsResponse = res
|
||||
let response: NuveiPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.switch()?;
|
||||
@ -458,7 +458,7 @@ impl ConnectorIntegration<PostCaptureVoid, PaymentsCancelPostCaptureData, Paymen
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsCancelPostCaptureRouterData, errors::ConnectorError> {
|
||||
let response: nuvei::NuveiPaymentsResponse = res
|
||||
let response: NuveiPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.switch()?;
|
||||
@ -501,7 +501,7 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Nuv
|
||||
connectors: &Connectors,
|
||||
) -> CustomResult<String, errors::ConnectorError> {
|
||||
Ok(format!(
|
||||
"{}ppp/api/v1/getPaymentStatus.do",
|
||||
"{}ppp/api/v1/getTransactionDetails.do",
|
||||
ConnectorCommon::base_url(self, connectors)
|
||||
))
|
||||
}
|
||||
@ -546,9 +546,9 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Nuv
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
|
||||
let response: nuvei::NuveiPaymentsResponse = res
|
||||
let response: NuveiTransactionSyncResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.parse_struct("NuveiTransactionSyncResponse")
|
||||
.switch()?;
|
||||
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
@ -622,7 +622,7 @@ impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> fo
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
|
||||
let response: nuvei::NuveiPaymentsResponse = res
|
||||
let response: NuveiPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.switch()?;
|
||||
@ -678,7 +678,6 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
|
||||
_connectors: &Connectors,
|
||||
) -> CustomResult<RequestContent, errors::ConnectorError> {
|
||||
let connector_req = nuvei::NuveiPaymentsRequest::try_from((req, req.get_session_token()?))?;
|
||||
|
||||
Ok(RequestContent::Json(Box::new(connector_req)))
|
||||
}
|
||||
|
||||
@ -710,7 +709,7 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
|
||||
let response: nuvei::NuveiPaymentsResponse = res
|
||||
let response: NuveiPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.switch()?;
|
||||
@ -883,13 +882,12 @@ impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResp
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<PaymentsPreProcessingRouterData, errors::ConnectorError> {
|
||||
let response: nuvei::NuveiPaymentsResponse = res
|
||||
let response: NuveiPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.switch()?;
|
||||
event_builder.map(|i| i.set_response_body(&response));
|
||||
router_env::logger::info!(connector_response=?response);
|
||||
|
||||
RouterData::try_from(ResponseRouterData {
|
||||
response,
|
||||
data: data.clone(),
|
||||
@ -965,7 +963,7 @@ impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Nuvei {
|
||||
event_builder: Option<&mut ConnectorEvent>,
|
||||
res: Response,
|
||||
) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
|
||||
let response: nuvei::NuveiPaymentsResponse = res
|
||||
let response: NuveiPaymentsResponse = res
|
||||
.response
|
||||
.parse_struct("NuveiPaymentsResponse")
|
||||
.switch()?;
|
||||
@ -1124,10 +1122,8 @@ impl IncomingWebhook for Nuvei {
|
||||
// Parse the webhook payload
|
||||
let webhook = serde_urlencoded::from_str::<nuvei::NuveiWebhook>(&request.query_params)
|
||||
.change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
|
||||
|
||||
// Convert webhook to payments response
|
||||
let payment_response = nuvei::NuveiPaymentsResponse::from(webhook);
|
||||
|
||||
let payment_response = NuveiPaymentsResponse::from(webhook);
|
||||
Ok(Box::new(payment_response))
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +49,8 @@ use crate::{
|
||||
utils::{
|
||||
self, convert_amount, missing_field_err, AddressData, AddressDetailsData,
|
||||
BrowserInformationData, ForeignTryFrom, PaymentsAuthorizeRequestData,
|
||||
PaymentsCancelRequestData, PaymentsPreProcessingRequestData, RouterData as _,
|
||||
PaymentsCancelRequestData, PaymentsPreProcessingRequestData,
|
||||
PaymentsSetupMandateRequestData, RouterData as _,
|
||||
},
|
||||
};
|
||||
|
||||
@ -136,9 +137,7 @@ impl NuveiAuthorizePreprocessingCommon for SetupMandateRequestData {
|
||||
fn get_return_url_required(
|
||||
&self,
|
||||
) -> Result<String, error_stack::Report<errors::ConnectorError>> {
|
||||
self.router_return_url
|
||||
.clone()
|
||||
.ok_or_else(missing_field_err("return_url"))
|
||||
self.get_router_return_url()
|
||||
}
|
||||
|
||||
fn get_capture_method(&self) -> Option<CaptureMethod> {
|
||||
@ -201,10 +200,6 @@ impl NuveiAuthorizePreprocessingCommon for PaymentsAuthorizeData {
|
||||
self.customer_id.clone()
|
||||
}
|
||||
|
||||
fn get_complete_authorize_url(&self) -> Option<String> {
|
||||
self.complete_authorize_url.clone()
|
||||
}
|
||||
|
||||
fn get_connector_mandate_id(&self) -> Option<String> {
|
||||
self.connector_mandate_id().clone()
|
||||
}
|
||||
@ -219,6 +214,10 @@ impl NuveiAuthorizePreprocessingCommon for PaymentsAuthorizeData {
|
||||
self.capture_method
|
||||
}
|
||||
|
||||
fn get_complete_authorize_url(&self) -> Option<String> {
|
||||
self.complete_authorize_url.clone()
|
||||
}
|
||||
|
||||
fn get_minor_amount_required(
|
||||
&self,
|
||||
) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>> {
|
||||
@ -273,10 +272,6 @@ impl NuveiAuthorizePreprocessingCommon for PaymentsPreProcessingData {
|
||||
&& self.setup_future_usage == Some(FutureUsage::OffSession)
|
||||
}
|
||||
|
||||
fn get_complete_authorize_url(&self) -> Option<String> {
|
||||
self.complete_authorize_url.clone()
|
||||
}
|
||||
|
||||
fn get_connector_mandate_id(&self) -> Option<String> {
|
||||
self.connector_mandate_id()
|
||||
}
|
||||
@ -291,6 +286,10 @@ impl NuveiAuthorizePreprocessingCommon for PaymentsPreProcessingData {
|
||||
self.capture_method
|
||||
}
|
||||
|
||||
fn get_complete_authorize_url(&self) -> Option<String> {
|
||||
self.complete_authorize_url.clone()
|
||||
}
|
||||
|
||||
fn get_minor_amount_required(
|
||||
&self,
|
||||
) -> Result<MinorUnit, error_stack::Report<errors::ConnectorError>> {
|
||||
@ -502,7 +501,11 @@ pub struct NuveiPaymentFlowRequest {
|
||||
#[derive(Debug, Serialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NuveiPaymentSyncRequest {
|
||||
pub session_token: Secret<String>,
|
||||
pub merchant_id: Secret<String>,
|
||||
pub merchant_site_id: Secret<String>,
|
||||
pub time_stamp: String,
|
||||
pub checksum: Secret<String>,
|
||||
pub transaction_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
@ -848,9 +851,9 @@ pub struct NuveiACSResponse {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum LiabilityShift {
|
||||
#[serde(rename = "Y", alias = "1")]
|
||||
#[serde(rename = "Y", alias = "1", alias = "y")]
|
||||
Success,
|
||||
#[serde(rename = "N", alias = "0")]
|
||||
#[serde(rename = "N", alias = "0", alias = "n")]
|
||||
Failed,
|
||||
}
|
||||
|
||||
@ -1724,8 +1727,8 @@ where
|
||||
let shipping_address: Option<ShippingAddress> =
|
||||
item.get_optional_shipping().map(|address| address.into());
|
||||
|
||||
let billing_address: Option<BillingAddress> = address.map(|ref address| address.into());
|
||||
|
||||
let billing_address: Option<BillingAddress> =
|
||||
address.clone().map(|ref address| address.into());
|
||||
let device_details = if request_data
|
||||
.device_details
|
||||
.ip_address
|
||||
@ -1902,25 +1905,7 @@ impl TryFrom<(&types::PaymentsCompleteAuthorizeRouterData, Secret<String>)>
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
Some(PaymentMethodData::Wallet(..))
|
||||
| Some(PaymentMethodData::PayLater(..))
|
||||
| Some(PaymentMethodData::BankDebit(..))
|
||||
| Some(PaymentMethodData::BankRedirect(..))
|
||||
| Some(PaymentMethodData::BankTransfer(..))
|
||||
| Some(PaymentMethodData::Crypto(..))
|
||||
| Some(PaymentMethodData::MandatePayment)
|
||||
| Some(PaymentMethodData::GiftCard(..))
|
||||
| Some(PaymentMethodData::Voucher(..))
|
||||
| Some(PaymentMethodData::CardRedirect(..))
|
||||
| Some(PaymentMethodData::Reward)
|
||||
| Some(PaymentMethodData::RealTimePayment(..))
|
||||
| Some(PaymentMethodData::MobilePayment(..))
|
||||
| Some(PaymentMethodData::Upi(..))
|
||||
| Some(PaymentMethodData::OpenBanking(_))
|
||||
| Some(PaymentMethodData::CardToken(..))
|
||||
| Some(PaymentMethodData::NetworkToken(..))
|
||||
| Some(PaymentMethodData::CardDetailsForNetworkTransactionId(_))
|
||||
| None => Err(errors::ConnectorError::NotImplemented(
|
||||
_ => Err(errors::ConnectorError::NotImplemented(
|
||||
utils::get_unimplemented_payment_method_error_message("nuvei"),
|
||||
)),
|
||||
}?;
|
||||
@ -1938,7 +1923,7 @@ impl TryFrom<(&types::PaymentsCompleteAuthorizeRouterData, Secret<String>)>
|
||||
..Default::default()
|
||||
})?;
|
||||
Ok(Self {
|
||||
related_transaction_id: request_data.related_transaction_id,
|
||||
related_transaction_id: item.request.connector_transaction_id.clone(),
|
||||
payment_option: request_data.payment_option,
|
||||
device_details: request_data.device_details,
|
||||
..request
|
||||
@ -2071,9 +2056,33 @@ impl TryFrom<&types::RefundExecuteRouterData> for NuveiPaymentFlowRequest {
|
||||
impl TryFrom<&types::PaymentsSyncRouterData> for NuveiPaymentSyncRequest {
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(value: &types::PaymentsSyncRouterData) -> Result<Self, Self::Error> {
|
||||
let meta: NuveiMeta = utils::to_connector_meta(value.request.connector_meta.clone())?;
|
||||
let connector_meta: NuveiAuthType = NuveiAuthType::try_from(&value.connector_auth_type)?;
|
||||
let merchant_id = connector_meta.merchant_id.clone();
|
||||
let merchant_site_id = connector_meta.merchant_site_id.clone();
|
||||
let merchant_secret = connector_meta.merchant_secret.clone();
|
||||
let time_stamp =
|
||||
date_time::format_date(date_time::now(), date_time::DateFormat::YYYYMMDDHHmmss)
|
||||
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
|
||||
let transaction_id = value
|
||||
.request
|
||||
.connector_transaction_id
|
||||
.clone()
|
||||
.get_connector_transaction_id()
|
||||
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
|
||||
let checksum = Secret::new(encode_payload(&[
|
||||
merchant_id.peek(),
|
||||
merchant_site_id.peek(),
|
||||
&transaction_id,
|
||||
&time_stamp,
|
||||
merchant_secret.peek(),
|
||||
])?);
|
||||
|
||||
Ok(Self {
|
||||
session_token: meta.session_token,
|
||||
merchant_id,
|
||||
merchant_site_id,
|
||||
time_stamp,
|
||||
checksum,
|
||||
transaction_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2189,11 +2198,17 @@ pub enum NuveiPaymentStatus {
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum NuveiTransactionStatus {
|
||||
#[serde(alias = "Approved", alias = "APPROVED")]
|
||||
Approved,
|
||||
#[serde(alias = "Declined", alias = "DECLINED")]
|
||||
Declined,
|
||||
#[serde(alias = "Filter Error", alias = "ERROR", alias = "Error")]
|
||||
Error,
|
||||
#[serde(alias = "Redirect", alias = "REDIRECT")]
|
||||
Redirect,
|
||||
#[serde(alias = "Pending", alias = "PENDING")]
|
||||
Pending,
|
||||
#[serde(alias = "Processing", alias = "PROCESSING")]
|
||||
#[default]
|
||||
Processing,
|
||||
}
|
||||
@ -2251,38 +2266,108 @@ pub struct NuveiPaymentsResponse {
|
||||
pub client_request_id: Option<String>,
|
||||
pub merchant_advice_code: Option<String>,
|
||||
}
|
||||
impl NuveiPaymentsResponse {
|
||||
/// returns amount_captured and minor_amount_capturable
|
||||
pub fn get_amount_captured(
|
||||
&self,
|
||||
) -> Result<(Option<i64>, Option<MinorUnit>), error_stack::Report<errors::ConnectorError>> {
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NuveiTxnPartialApproval {
|
||||
requested_amount: Option<StringMajorUnit>,
|
||||
requested_currency: Option<enums::Currency>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NuveiTransactionSyncResponseDetails {
|
||||
gw_error_code: Option<i64>,
|
||||
gw_error_reason: Option<String>,
|
||||
gw_extended_error_code: Option<i64>,
|
||||
transaction_id: Option<String>,
|
||||
transaction_status: Option<NuveiTransactionStatus>,
|
||||
transaction_type: Option<NuveiTransactionType>,
|
||||
auth_code: Option<String>,
|
||||
processed_amount: Option<StringMajorUnit>,
|
||||
processed_currency: Option<enums::Currency>,
|
||||
acquiring_bank_name: Option<String>,
|
||||
}
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NuveiTransactionSyncResponse {
|
||||
pub payment_option: Option<PaymentOption>,
|
||||
pub partial_approval: Option<NuveiTxnPartialApproval>,
|
||||
pub is_currency_converted: Option<bool>,
|
||||
pub transaction_details: Option<NuveiTransactionSyncResponseDetails>,
|
||||
pub fraud_details: Option<FraudDetails>,
|
||||
pub client_unique_id: Option<String>,
|
||||
pub internal_request_id: Option<i64>,
|
||||
pub status: NuveiPaymentStatus,
|
||||
pub err_code: Option<i64>,
|
||||
pub reason: Option<String>,
|
||||
pub merchant_id: Option<Secret<String>>,
|
||||
pub merchant_site_id: Option<Secret<String>>,
|
||||
pub version: Option<String>,
|
||||
pub client_request_id: Option<String>,
|
||||
pub merchant_advice_code: Option<String>,
|
||||
}
|
||||
impl NuveiTransactionSyncResponse {
|
||||
pub fn get_partial_approval(&self) -> Option<NuveiPartialApproval> {
|
||||
match &self.partial_approval {
|
||||
Some(partial_approval) => {
|
||||
let amount = utils::convert_back_amount_to_minor_units(
|
||||
NUVEI_AMOUNT_CONVERTOR,
|
||||
partial_approval.processed_amount.clone(),
|
||||
partial_approval.processed_currency,
|
||||
)?;
|
||||
match self.transaction_type {
|
||||
None => Ok((None, None)),
|
||||
Some(NuveiTransactionType::Sale) => {
|
||||
Ok((Some(MinorUnit::get_amount_as_i64(amount)), None))
|
||||
}
|
||||
Some(NuveiTransactionType::Auth) => Ok((None, Some(amount))),
|
||||
Some(NuveiTransactionType::Auth3D) => {
|
||||
Ok((Some(MinorUnit::get_amount_as_i64(amount)), None))
|
||||
}
|
||||
Some(NuveiTransactionType::InitAuth3D) => Ok((None, Some(amount))),
|
||||
Some(NuveiTransactionType::Credit) => Ok((None, None)),
|
||||
Some(NuveiTransactionType::Void) => Ok((None, None)),
|
||||
Some(NuveiTransactionType::Settle) => Ok((None, None)),
|
||||
}
|
||||
}
|
||||
None => Ok((None, None)),
|
||||
Some(partial_approval) => match (
|
||||
partial_approval.requested_amount.clone(),
|
||||
partial_approval.requested_currency,
|
||||
self.transaction_details
|
||||
.as_ref()
|
||||
.and_then(|txn| txn.processed_amount.clone()),
|
||||
self.transaction_details
|
||||
.as_ref()
|
||||
.and_then(|txn| txn.processed_currency),
|
||||
) {
|
||||
(
|
||||
Some(requested_amount),
|
||||
Some(requested_currency),
|
||||
Some(processed_amount),
|
||||
Some(processed_currency),
|
||||
) => Some(NuveiPartialApproval {
|
||||
requested_amount,
|
||||
requested_currency,
|
||||
processed_amount,
|
||||
processed_currency,
|
||||
}),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_amount_captured(
|
||||
partial_approval_data: Option<NuveiPartialApproval>,
|
||||
transaction_type: Option<NuveiTransactionType>,
|
||||
) -> Result<(Option<i64>, Option<MinorUnit>), error_stack::Report<errors::ConnectorError>> {
|
||||
match partial_approval_data {
|
||||
Some(partial_approval) => {
|
||||
let amount = utils::convert_back_amount_to_minor_units(
|
||||
NUVEI_AMOUNT_CONVERTOR,
|
||||
partial_approval.processed_amount.clone(),
|
||||
partial_approval.processed_currency,
|
||||
)?;
|
||||
match transaction_type {
|
||||
None => Ok((None, None)),
|
||||
Some(NuveiTransactionType::Sale) => {
|
||||
Ok((Some(MinorUnit::get_amount_as_i64(amount)), None))
|
||||
}
|
||||
Some(NuveiTransactionType::Auth) => Ok((None, Some(amount))),
|
||||
Some(NuveiTransactionType::Auth3D) => {
|
||||
Ok((Some(MinorUnit::get_amount_as_i64(amount)), None))
|
||||
}
|
||||
Some(NuveiTransactionType::InitAuth3D) => Ok((None, Some(amount))),
|
||||
Some(NuveiTransactionType::Credit) => Ok((None, None)),
|
||||
Some(NuveiTransactionType::Void) => Ok((None, None)),
|
||||
Some(NuveiTransactionType::Settle) => Ok((None, None)),
|
||||
}
|
||||
}
|
||||
None => Ok((None, None)),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum NuveiTransactionType {
|
||||
Auth,
|
||||
@ -2301,13 +2386,15 @@ pub struct FraudDetails {
|
||||
}
|
||||
|
||||
fn get_payment_status(
|
||||
response: &NuveiPaymentsResponse,
|
||||
amount: Option<i64>,
|
||||
is_post_capture_void: bool,
|
||||
transaction_type: Option<NuveiTransactionType>,
|
||||
transaction_status: Option<NuveiTransactionStatus>,
|
||||
status: NuveiPaymentStatus,
|
||||
) -> enums::AttemptStatus {
|
||||
// ZERO dollar authorization
|
||||
if amount == Some(0) && response.transaction_type.clone() == Some(NuveiTransactionType::Auth) {
|
||||
return match response.transaction_status.clone() {
|
||||
if amount == Some(0) && transaction_type == Some(NuveiTransactionType::Auth) {
|
||||
return match transaction_status {
|
||||
Some(NuveiTransactionStatus::Approved) => enums::AttemptStatus::Charged,
|
||||
Some(NuveiTransactionStatus::Declined) | Some(NuveiTransactionStatus::Error) => {
|
||||
enums::AttemptStatus::AuthorizationFailed
|
||||
@ -2316,7 +2403,7 @@ fn get_payment_status(
|
||||
enums::AttemptStatus::Pending
|
||||
}
|
||||
Some(NuveiTransactionStatus::Redirect) => enums::AttemptStatus::AuthenticationPending,
|
||||
None => match response.status {
|
||||
None => match status {
|
||||
NuveiPaymentStatus::Failed | NuveiPaymentStatus::Error => {
|
||||
enums::AttemptStatus::Failure
|
||||
}
|
||||
@ -2325,10 +2412,12 @@ fn get_payment_status(
|
||||
};
|
||||
}
|
||||
|
||||
match response.transaction_status.clone() {
|
||||
match transaction_status {
|
||||
Some(status) => match status {
|
||||
NuveiTransactionStatus::Approved => match response.transaction_type {
|
||||
Some(NuveiTransactionType::Auth) => enums::AttemptStatus::Authorized,
|
||||
NuveiTransactionStatus::Approved => match transaction_type {
|
||||
Some(NuveiTransactionType::InitAuth3D) | Some(NuveiTransactionType::Auth) => {
|
||||
enums::AttemptStatus::Authorized
|
||||
}
|
||||
Some(NuveiTransactionType::Sale) | Some(NuveiTransactionType::Settle) => {
|
||||
enums::AttemptStatus::Charged
|
||||
}
|
||||
@ -2336,14 +2425,14 @@ fn get_payment_status(
|
||||
enums::AttemptStatus::VoidedPostCharge
|
||||
}
|
||||
Some(NuveiTransactionType::Void) => enums::AttemptStatus::Voided,
|
||||
|
||||
Some(NuveiTransactionType::Auth3D) => enums::AttemptStatus::AuthenticationPending,
|
||||
_ => enums::AttemptStatus::Pending,
|
||||
},
|
||||
NuveiTransactionStatus::Declined | NuveiTransactionStatus::Error => {
|
||||
match response.transaction_type {
|
||||
match transaction_type {
|
||||
Some(NuveiTransactionType::Auth) => enums::AttemptStatus::AuthorizationFailed,
|
||||
Some(NuveiTransactionType::Void) => enums::AttemptStatus::VoidFailed,
|
||||
Some(NuveiTransactionType::Auth3D) => {
|
||||
Some(NuveiTransactionType::Auth3D) | Some(NuveiTransactionType::InitAuth3D) => {
|
||||
enums::AttemptStatus::AuthenticationFailed
|
||||
}
|
||||
_ => enums::AttemptStatus::Failure,
|
||||
@ -2354,37 +2443,49 @@ fn get_payment_status(
|
||||
}
|
||||
NuveiTransactionStatus::Redirect => enums::AttemptStatus::AuthenticationPending,
|
||||
},
|
||||
None => match response.status {
|
||||
None => match status {
|
||||
NuveiPaymentStatus::Failed | NuveiPaymentStatus::Error => enums::AttemptStatus::Failure,
|
||||
_ => enums::AttemptStatus::Pending,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn build_error_response(response: &NuveiPaymentsResponse, http_code: u16) -> Option<ErrorResponse> {
|
||||
match response.status {
|
||||
#[derive(Debug)]
|
||||
struct ErrorResponseParams {
|
||||
http_code: u16,
|
||||
status: NuveiPaymentStatus,
|
||||
err_code: Option<i64>,
|
||||
err_msg: Option<String>,
|
||||
merchant_advice_code: Option<String>,
|
||||
gw_error_code: Option<i64>,
|
||||
gw_error_reason: Option<String>,
|
||||
transaction_status: Option<NuveiTransactionStatus>,
|
||||
}
|
||||
|
||||
fn build_error_response(params: ErrorResponseParams) -> Option<ErrorResponse> {
|
||||
match params.status {
|
||||
NuveiPaymentStatus::Error => Some(get_error_response(
|
||||
response.err_code,
|
||||
&response.reason,
|
||||
http_code,
|
||||
&response.merchant_advice_code,
|
||||
&response.gw_error_code.map(|e| e.to_string()),
|
||||
&response.gw_error_reason,
|
||||
params.err_code,
|
||||
params.err_msg.clone(),
|
||||
params.http_code,
|
||||
params.merchant_advice_code.clone(),
|
||||
params.gw_error_code.map(|code| code.to_string()),
|
||||
params.gw_error_reason.clone(),
|
||||
)),
|
||||
|
||||
_ => {
|
||||
let err = Some(get_error_response(
|
||||
response.gw_error_code,
|
||||
&response.gw_error_reason,
|
||||
http_code,
|
||||
&response.merchant_advice_code,
|
||||
&response.gw_error_code.map(|e| e.to_string()),
|
||||
&response.gw_error_reason,
|
||||
params.gw_error_code,
|
||||
params.gw_error_reason.clone(),
|
||||
params.http_code,
|
||||
params.merchant_advice_code,
|
||||
params.gw_error_code.map(|e| e.to_string()),
|
||||
params.gw_error_reason.clone(),
|
||||
));
|
||||
|
||||
match response.transaction_status {
|
||||
match params.transaction_status {
|
||||
Some(NuveiTransactionStatus::Error) | Some(NuveiTransactionStatus::Declined) => err,
|
||||
_ => match response
|
||||
_ => match params
|
||||
.gw_error_reason
|
||||
.as_ref()
|
||||
.map(|r| r.eq("Missing argument"))
|
||||
@ -2433,11 +2534,15 @@ impl
|
||||
>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let amount = item.data.request.amount;
|
||||
let response = &item.response;
|
||||
let (status, redirection_data, connector_response_data) = process_nuvei_payment_response(
|
||||
NuveiPaymentResponseData::new(amount, false, item.data.payment_method, response),
|
||||
)?;
|
||||
|
||||
let (status, redirection_data, connector_response_data) =
|
||||
process_nuvei_payment_response(&item, amount, false)?;
|
||||
|
||||
let (amount_captured, minor_amount_capturable) = item.response.get_amount_captured()?;
|
||||
let (amount_captured, minor_amount_capturable) = get_amount_captured(
|
||||
response.partial_approval.clone(),
|
||||
response.transaction_type.clone(),
|
||||
)?;
|
||||
|
||||
let ip_address = item
|
||||
.data
|
||||
@ -2453,16 +2558,31 @@ impl
|
||||
field_name: "browser_info.ip_address",
|
||||
})?
|
||||
.to_string();
|
||||
let response = &item.response;
|
||||
|
||||
Ok(Self {
|
||||
status,
|
||||
response: if let Some(err) = build_error_response(&item.response, item.http_code) {
|
||||
response: if let Some(err) = build_error_response(ErrorResponseParams {
|
||||
http_code: item.http_code,
|
||||
status: response.status.clone(),
|
||||
err_code: response.err_code,
|
||||
err_msg: response.reason.clone(),
|
||||
merchant_advice_code: response.merchant_advice_code.clone(),
|
||||
gw_error_code: response.gw_error_code,
|
||||
gw_error_reason: response.gw_error_reason.clone(),
|
||||
transaction_status: response.transaction_status.clone(),
|
||||
}) {
|
||||
Err(err)
|
||||
} else {
|
||||
let response = &item.response;
|
||||
Ok(create_transaction_response(
|
||||
&item.response,
|
||||
redirection_data,
|
||||
Some(ip_address),
|
||||
response.transaction_id.clone(),
|
||||
response.order_id.clone(),
|
||||
response.session_token.clone(),
|
||||
response.external_scheme_transaction_id.clone(),
|
||||
response.payment_option.clone(),
|
||||
)?)
|
||||
},
|
||||
amount_captured,
|
||||
@ -2475,10 +2595,64 @@ impl
|
||||
|
||||
// Helper function to process Nuvei payment response
|
||||
|
||||
fn process_nuvei_payment_response<F, T>(
|
||||
item: &ResponseRouterData<F, NuveiPaymentsResponse, T, PaymentsResponseData>,
|
||||
amount: Option<i64>,
|
||||
is_post_capture_void: bool,
|
||||
/// Struct to encapsulate parameters for processing Nuvei payment responses
|
||||
#[derive(Debug)]
|
||||
pub struct NuveiPaymentResponseData {
|
||||
pub amount: Option<i64>,
|
||||
pub is_post_capture_void: bool,
|
||||
pub payment_method: enums::PaymentMethod,
|
||||
pub payment_option: Option<PaymentOption>,
|
||||
pub transaction_type: Option<NuveiTransactionType>,
|
||||
pub transaction_status: Option<NuveiTransactionStatus>,
|
||||
pub status: NuveiPaymentStatus,
|
||||
pub merchant_advice_code: Option<String>,
|
||||
}
|
||||
|
||||
impl NuveiPaymentResponseData {
|
||||
pub fn new(
|
||||
amount: Option<i64>,
|
||||
is_post_capture_void: bool,
|
||||
payment_method: enums::PaymentMethod,
|
||||
response: &NuveiPaymentsResponse,
|
||||
) -> Self {
|
||||
Self {
|
||||
amount,
|
||||
is_post_capture_void,
|
||||
payment_method,
|
||||
payment_option: response.payment_option.clone(),
|
||||
transaction_type: response.transaction_type.clone(),
|
||||
transaction_status: response.transaction_status.clone(),
|
||||
status: response.status.clone(),
|
||||
merchant_advice_code: response.merchant_advice_code.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_sync_response(
|
||||
amount: Option<i64>,
|
||||
is_post_capture_void: bool,
|
||||
payment_method: enums::PaymentMethod,
|
||||
response: &NuveiTransactionSyncResponse,
|
||||
) -> Self {
|
||||
let transaction_details = &response.transaction_details;
|
||||
Self {
|
||||
amount,
|
||||
is_post_capture_void,
|
||||
payment_method,
|
||||
payment_option: response.payment_option.clone(),
|
||||
transaction_type: transaction_details
|
||||
.as_ref()
|
||||
.and_then(|details| details.transaction_type.clone()),
|
||||
transaction_status: transaction_details
|
||||
.as_ref()
|
||||
.and_then(|details| details.transaction_status.clone()),
|
||||
status: response.status.clone(),
|
||||
merchant_advice_code: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_nuvei_payment_response(
|
||||
data: NuveiPaymentResponseData,
|
||||
) -> Result<
|
||||
(
|
||||
enums::AttemptStatus,
|
||||
@ -2486,20 +2660,14 @@ fn process_nuvei_payment_response<F, T>(
|
||||
Option<ConnectorResponseData>,
|
||||
),
|
||||
error_stack::Report<errors::ConnectorError>,
|
||||
>
|
||||
where
|
||||
F: std::fmt::Debug,
|
||||
T: std::fmt::Debug,
|
||||
{
|
||||
let redirection_data = match item.data.payment_method {
|
||||
enums::PaymentMethod::Wallet | enums::PaymentMethod::BankRedirect => item
|
||||
.response
|
||||
> {
|
||||
let redirection_data = match data.payment_method {
|
||||
enums::PaymentMethod::Wallet | enums::PaymentMethod::BankRedirect => data
|
||||
.payment_option
|
||||
.as_ref()
|
||||
.and_then(|po| po.redirect_url.clone())
|
||||
.map(|base_url| RedirectForm::from((base_url, Method::Get))),
|
||||
_ => item
|
||||
.response
|
||||
_ => data
|
||||
.payment_option
|
||||
.as_ref()
|
||||
.and_then(|o| o.card.clone())
|
||||
@ -2511,32 +2679,42 @@ where
|
||||
form_fields: std::collections::HashMap::from([("creq".to_string(), creq.expose())]),
|
||||
}),
|
||||
};
|
||||
let connector_response_data =
|
||||
convert_to_additional_payment_method_connector_response(&item.response)
|
||||
.map(ConnectorResponseData::with_additional_payment_method_data);
|
||||
|
||||
let status = get_payment_status(&item.response, amount, is_post_capture_void);
|
||||
let connector_response_data = convert_to_additional_payment_method_connector_response(
|
||||
data.payment_option.clone(),
|
||||
data.merchant_advice_code,
|
||||
)
|
||||
.map(ConnectorResponseData::with_additional_payment_method_data);
|
||||
let status = get_payment_status(
|
||||
data.amount,
|
||||
data.is_post_capture_void,
|
||||
data.transaction_type,
|
||||
data.transaction_status,
|
||||
data.status,
|
||||
);
|
||||
|
||||
Ok((status, redirection_data, connector_response_data))
|
||||
}
|
||||
|
||||
// Helper function to create transaction response
|
||||
fn create_transaction_response(
|
||||
response: &NuveiPaymentsResponse,
|
||||
redirection_data: Option<RedirectForm>,
|
||||
ip_address: Option<String>,
|
||||
transaction_id: Option<String>,
|
||||
order_id: Option<String>,
|
||||
session_token: Option<Secret<String>>,
|
||||
external_scheme_transaction_id: Option<Secret<String>>,
|
||||
payment_option: Option<PaymentOption>,
|
||||
) -> Result<PaymentsResponseData, error_stack::Report<errors::ConnectorError>> {
|
||||
Ok(PaymentsResponseData::TransactionResponse {
|
||||
resource_id: response
|
||||
.transaction_id
|
||||
resource_id: transaction_id
|
||||
.clone()
|
||||
.map_or(response.order_id.clone(), Some) // For paypal there will be no transaction_id, only order_id will be present
|
||||
.map_or(order_id.clone(), Some) // For paypal there will be no transaction_id, only order_id will be present
|
||||
.map(ResponseId::ConnectorTransactionId)
|
||||
.ok_or(errors::ConnectorError::MissingConnectorTransactionID)?,
|
||||
redirection_data: Box::new(redirection_data),
|
||||
mandate_reference: Box::new(
|
||||
response
|
||||
.payment_option
|
||||
payment_option
|
||||
.as_ref()
|
||||
.and_then(|po| po.user_payment_option_id.clone())
|
||||
.map(|id| MandateReference {
|
||||
@ -2548,7 +2726,7 @@ fn create_transaction_response(
|
||||
}),
|
||||
),
|
||||
// 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.clone() {
|
||||
connector_metadata: if let Some(token) = session_token {
|
||||
Some(
|
||||
serde_json::to_value(NuveiMeta {
|
||||
session_token: token,
|
||||
@ -2558,11 +2736,10 @@ fn create_transaction_response(
|
||||
} else {
|
||||
None
|
||||
},
|
||||
network_txn_id: response
|
||||
.external_scheme_transaction_id
|
||||
network_txn_id: external_scheme_transaction_id
|
||||
.as_ref()
|
||||
.map(|ntid| ntid.clone().expose()),
|
||||
connector_response_reference_id: response.order_id.clone(),
|
||||
connector_response_reference_id: order_id.clone(),
|
||||
incremental_authorization_allowed: None,
|
||||
charges: None,
|
||||
})
|
||||
@ -2590,11 +2767,15 @@ impl
|
||||
) -> Result<Self, Self::Error> {
|
||||
// Get amount directly from the authorize data
|
||||
let amount = Some(item.data.request.amount);
|
||||
let response = &item.response;
|
||||
let (status, redirection_data, connector_response_data) = process_nuvei_payment_response(
|
||||
NuveiPaymentResponseData::new(amount, false, item.data.payment_method, response),
|
||||
)?;
|
||||
|
||||
let (status, redirection_data, connector_response_data) =
|
||||
process_nuvei_payment_response(&item, amount, false)?;
|
||||
|
||||
let (amount_captured, minor_amount_capturable) = item.response.get_amount_captured()?;
|
||||
let (amount_captured, minor_amount_capturable) = get_amount_captured(
|
||||
response.partial_approval.clone(),
|
||||
response.transaction_type.clone(),
|
||||
)?;
|
||||
|
||||
let ip_address = item
|
||||
.data
|
||||
@ -2605,13 +2786,27 @@ impl
|
||||
|
||||
Ok(Self {
|
||||
status,
|
||||
response: if let Some(err) = build_error_response(&item.response, item.http_code) {
|
||||
response: if let Some(err) = build_error_response(ErrorResponseParams {
|
||||
http_code: item.http_code,
|
||||
status: response.status.clone(),
|
||||
err_code: response.err_code,
|
||||
err_msg: response.reason.clone(),
|
||||
merchant_advice_code: response.merchant_advice_code.clone(),
|
||||
gw_error_code: response.gw_error_code,
|
||||
gw_error_reason: response.gw_error_reason.clone(),
|
||||
transaction_status: response.transaction_status.clone(),
|
||||
}) {
|
||||
Err(err)
|
||||
} else {
|
||||
let response = &item.response;
|
||||
Ok(create_transaction_response(
|
||||
&item.response,
|
||||
redirection_data,
|
||||
ip_address,
|
||||
response.transaction_id.clone(),
|
||||
response.order_id.clone(),
|
||||
response.session_token.clone(),
|
||||
response.external_scheme_transaction_id.clone(),
|
||||
response.payment_option.clone(),
|
||||
)?)
|
||||
},
|
||||
amount_captured,
|
||||
@ -2638,19 +2833,113 @@ where
|
||||
.data
|
||||
.minor_amount_capturable
|
||||
.map(|amount| amount.get_amount_as_i64());
|
||||
let response = &item.response;
|
||||
let (status, redirection_data, connector_response_data) =
|
||||
process_nuvei_payment_response(&item, amount, F::is_post_capture_void())?;
|
||||
process_nuvei_payment_response(NuveiPaymentResponseData::new(
|
||||
amount,
|
||||
F::is_post_capture_void(),
|
||||
item.data.payment_method,
|
||||
response,
|
||||
))?;
|
||||
|
||||
let (amount_captured, minor_amount_capturable) = item.response.get_amount_captured()?;
|
||||
let (amount_captured, minor_amount_capturable) = get_amount_captured(
|
||||
response.partial_approval.clone(),
|
||||
response.transaction_type.clone(),
|
||||
)?;
|
||||
Ok(Self {
|
||||
status,
|
||||
response: if let Some(err) = build_error_response(&item.response, item.http_code) {
|
||||
response: if let Some(err) = build_error_response(ErrorResponseParams {
|
||||
http_code: item.http_code,
|
||||
status: response.status.clone(),
|
||||
err_code: response.err_code,
|
||||
err_msg: response.reason.clone(),
|
||||
merchant_advice_code: response.merchant_advice_code.clone(),
|
||||
gw_error_code: response.gw_error_code,
|
||||
gw_error_reason: response.gw_error_reason.clone(),
|
||||
transaction_status: response.transaction_status.clone(),
|
||||
}) {
|
||||
Err(err)
|
||||
} else {
|
||||
let response = &item.response;
|
||||
Ok(create_transaction_response(
|
||||
redirection_data,
|
||||
None,
|
||||
response.transaction_id.clone(),
|
||||
response.order_id.clone(),
|
||||
response.session_token.clone(),
|
||||
response.external_scheme_transaction_id.clone(),
|
||||
response.payment_option.clone(),
|
||||
)?)
|
||||
},
|
||||
amount_captured,
|
||||
minor_amount_capturable,
|
||||
connector_response: connector_response_data,
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Generic implementation for other flow types
|
||||
impl<F, T> TryFrom<ResponseRouterData<F, NuveiTransactionSyncResponse, T, PaymentsResponseData>>
|
||||
for RouterData<F, T, PaymentsResponseData>
|
||||
where
|
||||
F: NuveiPaymentsGenericResponse + std::fmt::Debug,
|
||||
T: std::fmt::Debug,
|
||||
F: std::any::Any,
|
||||
{
|
||||
type Error = error_stack::Report<errors::ConnectorError>;
|
||||
fn try_from(
|
||||
item: ResponseRouterData<F, NuveiTransactionSyncResponse, T, PaymentsResponseData>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let amount = item
|
||||
.data
|
||||
.minor_amount_capturable
|
||||
.map(|amount| amount.get_amount_as_i64());
|
||||
let response = &item.response;
|
||||
let transaction_details = &response.transaction_details;
|
||||
let transaction_type = transaction_details
|
||||
.as_ref()
|
||||
.and_then(|details| details.transaction_type.clone());
|
||||
let (status, redirection_data, connector_response_data) =
|
||||
process_nuvei_payment_response(NuveiPaymentResponseData::new_from_sync_response(
|
||||
amount,
|
||||
F::is_post_capture_void(),
|
||||
item.data.payment_method,
|
||||
response,
|
||||
))?;
|
||||
|
||||
let (amount_captured, minor_amount_capturable) =
|
||||
get_amount_captured(response.get_partial_approval(), transaction_type.clone())?;
|
||||
Ok(Self {
|
||||
status,
|
||||
response: if let Some(err) = build_error_response(ErrorResponseParams {
|
||||
http_code: item.http_code,
|
||||
status: response.status.clone(),
|
||||
err_code: response.err_code,
|
||||
err_msg: response.reason.clone(),
|
||||
merchant_advice_code: None,
|
||||
gw_error_code: transaction_details
|
||||
.as_ref()
|
||||
.and_then(|details| details.gw_error_code),
|
||||
gw_error_reason: transaction_details
|
||||
.as_ref()
|
||||
.and_then(|details| details.gw_error_reason.clone()),
|
||||
transaction_status: transaction_details
|
||||
.as_ref()
|
||||
.and_then(|details| details.transaction_status.clone()),
|
||||
}) {
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(create_transaction_response(
|
||||
&item.response,
|
||||
redirection_data,
|
||||
None,
|
||||
transaction_details
|
||||
.as_ref()
|
||||
.and_then(|data| data.transaction_id.clone()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
response.payment_option.clone(),
|
||||
)?)
|
||||
},
|
||||
amount_captured,
|
||||
@ -2678,12 +2967,17 @@ impl TryFrom<PaymentsPreprocessingResponseRouterData<NuveiPaymentsResponse>>
|
||||
.map(to_boolean)
|
||||
.unwrap_or_default();
|
||||
Ok(Self {
|
||||
status: get_payment_status(&response, item.data.request.amount, false),
|
||||
status: get_payment_status(
|
||||
item.data.request.amount,
|
||||
false,
|
||||
response.transaction_type,
|
||||
response.transaction_status,
|
||||
response.status,
|
||||
),
|
||||
response: Ok(PaymentsResponseData::ThreeDSEnrollmentResponse {
|
||||
enrolled_v2: is_enrolled_for_3ds,
|
||||
related_transaction_id: response.transaction_id,
|
||||
}),
|
||||
|
||||
..item.data
|
||||
})
|
||||
}
|
||||
@ -2760,11 +3054,7 @@ where
|
||||
.request
|
||||
.get_customer_id_required()
|
||||
.ok_or(missing_field_err("customer_id")())?;
|
||||
let related_transaction_id = if item.is_three_ds() {
|
||||
item.request.get_related_transaction_id().clone()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let related_transaction_id = item.request.get_related_transaction_id().clone();
|
||||
|
||||
let ip_address = data
|
||||
.recurring_mandate_payment_data
|
||||
@ -2823,20 +3113,20 @@ fn get_refund_response(
|
||||
match response.status {
|
||||
NuveiPaymentStatus::Error => Err(Box::new(get_error_response(
|
||||
response.err_code,
|
||||
&response.reason,
|
||||
response.reason.clone(),
|
||||
http_code,
|
||||
&response.merchant_advice_code,
|
||||
&response.gw_error_code.map(|e| e.to_string()),
|
||||
&response.gw_error_reason,
|
||||
response.merchant_advice_code,
|
||||
response.gw_error_code.map(|e| e.to_string()),
|
||||
response.gw_error_reason,
|
||||
))),
|
||||
_ => match response.transaction_status {
|
||||
Some(NuveiTransactionStatus::Error) => Err(Box::new(get_error_response(
|
||||
response.err_code,
|
||||
&response.reason,
|
||||
response.reason,
|
||||
http_code,
|
||||
&response.merchant_advice_code,
|
||||
&response.gw_error_code.map(|e| e.to_string()),
|
||||
&response.gw_error_reason,
|
||||
response.merchant_advice_code,
|
||||
response.gw_error_code.map(|e| e.to_string()),
|
||||
response.gw_error_reason,
|
||||
))),
|
||||
_ => Ok(RefundsResponseData {
|
||||
connector_refund_id: txn_id,
|
||||
@ -2848,11 +3138,11 @@ fn get_refund_response(
|
||||
|
||||
fn get_error_response(
|
||||
error_code: Option<i64>,
|
||||
error_msg: &Option<String>,
|
||||
error_msg: Option<String>,
|
||||
http_code: u16,
|
||||
network_advice_code: &Option<String>,
|
||||
network_decline_code: &Option<String>,
|
||||
network_error_message: &Option<String>,
|
||||
network_advice_code: Option<String>,
|
||||
network_decline_code: Option<String>,
|
||||
network_error_message: Option<String>,
|
||||
) -> ErrorResponse {
|
||||
ErrorResponse {
|
||||
code: error_code
|
||||
@ -3049,6 +3339,7 @@ impl From<NuveiWebhook> for NuveiPaymentsResponse {
|
||||
TransactionStatus::Declined => NuveiTransactionStatus::Declined,
|
||||
TransactionStatus::Error => NuveiTransactionStatus::Error,
|
||||
TransactionStatus::Settled => NuveiTransactionStatus::Approved,
|
||||
|
||||
_ => NuveiTransactionStatus::Processing,
|
||||
}),
|
||||
transaction_id: notification.transaction_id,
|
||||
@ -3116,21 +3407,17 @@ pub fn concat_strings(strings: &[String]) -> String {
|
||||
}
|
||||
|
||||
fn convert_to_additional_payment_method_connector_response(
|
||||
transaction_response: &NuveiPaymentsResponse,
|
||||
payment_option: Option<PaymentOption>,
|
||||
merchant_advice_code: Option<String>,
|
||||
) -> Option<AdditionalPaymentMethodConnectorResponse> {
|
||||
let card = transaction_response
|
||||
.payment_option
|
||||
.as_ref()?
|
||||
.card
|
||||
.as_ref()?;
|
||||
let card = payment_option.as_ref()?.card.as_ref()?;
|
||||
let avs_code = card.avs_code.as_ref();
|
||||
let cvv2_code = card.cvv2_reply.as_ref();
|
||||
let merchant_advice_code = transaction_response.merchant_advice_code.as_ref();
|
||||
|
||||
let avs_description = avs_code.and_then(|code| get_avs_response_description(code));
|
||||
let cvv_description = cvv2_code.and_then(|code| get_cvv2_response_description(code));
|
||||
let merchant_advice_description =
|
||||
merchant_advice_code.and_then(|code| get_merchant_advice_code_description(code));
|
||||
let merchant_advice_description = merchant_advice_code
|
||||
.as_ref()
|
||||
.and_then(|code| get_merchant_advice_code_description(code));
|
||||
|
||||
let payment_checks = serde_json::json!({
|
||||
"avs_result": avs_code,
|
||||
|
||||
@ -38,7 +38,7 @@ const multiUseMandateData = {
|
||||
// Payment method data objects for responses
|
||||
const payment_method_data_no3ds = {
|
||||
card: {
|
||||
authentication_data: {},
|
||||
authentication_data: { challengePreferenceReason: "12" },
|
||||
last4: "1111",
|
||||
card_type: "CREDIT",
|
||||
card_network: "Visa",
|
||||
|
||||
Reference in New Issue
Block a user