Skip to content

Commit

Permalink
fix(connector): Trustpay zen error mapping (#3255)
Browse files Browse the repository at this point in the history
Co-authored-by: Prasunna Soppa <[email protected]>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 18, 2024
1 parent 2f693ad commit e816ccf
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 35 deletions.
48 changes: 42 additions & 6 deletions crates/router/src/connector/trustpay/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ fn handle_bank_redirects_sync_response(
errors::ConnectorError,
> {
let status = enums::AttemptStatus::from(response.payment_information.status);
let error = if status == enums::AttemptStatus::AuthorizationFailed {
let error = if utils::is_payment_failure(status) {
let reason_info = response
.payment_information
.status_reason_information
Expand Down Expand Up @@ -856,6 +856,7 @@ fn handle_bank_redirects_sync_response(

pub fn handle_webhook_response(
payment_information: WebhookPaymentInformation,
status_code: u16,
) -> CustomResult<
(
enums::AttemptStatus,
Expand All @@ -865,6 +866,22 @@ pub fn handle_webhook_response(
errors::ConnectorError,
> {
let status = enums::AttemptStatus::try_from(payment_information.status)?;
let error = if utils::is_payment_failure(status) {
let reason_info = payment_information
.status_reason_information
.unwrap_or_default();
Some(types::ErrorResponse {
code: reason_info.reason.code.clone(),
// message vary for the same code, so relying on code alone as it is unique
message: reason_info.reason.code,
reason: reason_info.reason.reject_reason,
status_code,
attempt_status: None,
connector_transaction_id: payment_information.references.payment_request_id.clone(),
})
} else {
None
};
let payment_response_data = types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::NoResponseId,
redirection_data: None,
Expand All @@ -874,7 +891,7 @@ pub fn handle_webhook_response(
connector_response_reference_id: None,
incremental_authorization_allowed: None,
};
Ok((status, None, payment_response_data))
Ok((status, error, payment_response_data))
}

pub fn get_trustpay_response(
Expand All @@ -901,7 +918,9 @@ pub fn get_trustpay_response(
TrustpayPaymentsResponse::BankRedirectError(response) => {
handle_bank_redirects_error_response(*response, status_code)
}
TrustpayPaymentsResponse::WebhookResponse(response) => handle_webhook_response(*response),
TrustpayPaymentsResponse::WebhookResponse(response) => {
handle_webhook_response(*response, status_code)
}
}
}

Expand Down Expand Up @@ -1452,17 +1471,32 @@ fn handle_cards_refund_response(

fn handle_webhooks_refund_response(
response: WebhookPaymentInformation,
status_code: u16,
) -> CustomResult<(Option<types::ErrorResponse>, types::RefundsResponseData), errors::ConnectorError>
{
let refund_status = diesel_models::enums::RefundStatus::try_from(response.status)?;
let error = if utils::is_refund_failure(refund_status) {
let reason_info = response.status_reason_information.unwrap_or_default();
Some(types::ErrorResponse {
code: reason_info.reason.code.clone(),
// message vary for the same code, so relying on code alone as it is unique
message: reason_info.reason.code,
reason: reason_info.reason.reject_reason,
status_code,
attempt_status: None,
connector_transaction_id: response.references.payment_request_id.clone(),
})
} else {
None
};
let refund_response_data = types::RefundsResponseData {
connector_refund_id: response
.references
.payment_request_id
.ok_or(errors::ConnectorError::MissingConnectorRefundID)?,
refund_status,
};
Ok((None, refund_response_data))
Ok((error, refund_response_data))
}

fn handle_bank_redirects_refund_response(
Expand Down Expand Up @@ -1495,7 +1529,7 @@ fn handle_bank_redirects_refund_sync_response(
status_code: u16,
) -> (Option<types::ErrorResponse>, types::RefundsResponseData) {
let refund_status = enums::RefundStatus::from(response.payment_information.status);
let error = if refund_status == enums::RefundStatus::Failure {
let error = if utils::is_refund_failure(refund_status) {
let reason_info = response
.payment_information
.status_reason_information
Expand Down Expand Up @@ -1551,7 +1585,9 @@ impl<F> TryFrom<types::RefundsResponseRouterData<F, RefundResponse>>
RefundResponse::CardsRefund(response) => {
handle_cards_refund_response(*response, item.http_code)?
}
RefundResponse::WebhookRefund(response) => handle_webhooks_refund_response(*response)?,
RefundResponse::WebhookRefund(response) => {
handle_webhooks_refund_response(*response, item.http_code)?
}
RefundResponse::BankRedirectRefund(response) => {
handle_bank_redirects_refund_response(*response, item.http_code)
}
Expand Down
127 changes: 98 additions & 29 deletions crates/router/src/connector/zen/transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
connector::utils::{
self, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, RouterData,
},
consts,
core::errors::{self, CustomResult},
services::{self, Method},
types::{self, api, storage::enums, transformers::ForeignTryFrom},
Expand Down Expand Up @@ -848,12 +849,15 @@ impl ForeignTryFrom<(ZenPaymentStatus, Option<ZenActions>)> for enums::AttemptSt
}
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApiResponse {
status: ZenPaymentStatus,
id: String,
// merchant_transaction_id: Option<String>,
merchant_action: Option<ZenMerchantAction>,
reject_code: Option<String>,
reject_reason: Option<String>,
}

#[derive(Debug, Deserialize)]
Expand All @@ -869,18 +873,18 @@ pub struct CheckoutResponse {
redirect_url: url::Url,
}

#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenMerchantAction {
action: ZenActions,
data: ZenMerchantActionData,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum ZenActions {
Redirect,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenMerchantActionData {
redirect_url: url::Url,
Expand Down Expand Up @@ -913,35 +917,70 @@ impl<F, T>
}
}

fn get_zen_response(
response: ApiResponse,
status_code: u16,
) -> CustomResult<
(
enums::AttemptStatus,
Option<types::ErrorResponse>,
types::PaymentsResponseData,
),
errors::ConnectorError,
> {
let redirection_data_action = response.merchant_action.map(|merchant_action| {
(
services::RedirectForm::from((merchant_action.data.redirect_url, Method::Get)),
merchant_action.action,
)
});
let (redirection_data, action) = match redirection_data_action {
Some((redirect_form, action)) => (Some(redirect_form), Some(action)),
None => (None, None),
};
let status = enums::AttemptStatus::foreign_try_from((response.status, action))?;
let error = if utils::is_payment_failure(status) {
Some(types::ErrorResponse {
code: response
.reject_code
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: response
.reject_reason
.clone()
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: response.reject_reason,
status_code,
attempt_status: Some(status),
connector_transaction_id: Some(response.id.clone()),
})
} else {
None
};
let payment_response_data = types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(response.id.clone()),
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: None,
incremental_authorization_allowed: None,
};
Ok((status, error, payment_response_data))
}

impl<F, T> TryFrom<types::ResponseRouterData<F, ApiResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
value: types::ResponseRouterData<F, ApiResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_data_action = value.response.merchant_action.map(|merchant_action| {
(
services::RedirectForm::from((merchant_action.data.redirect_url, Method::Get)),
merchant_action.action,
)
});
let (redirection_data, action) = match redirection_data_action {
Some((redirect_form, action)) => (Some(redirect_form), Some(action)),
None => (None, None),
};
let (status, error, payment_response_data) =
get_zen_response(value.response.clone(), value.http_code)?;

Ok(Self {
status: enums::AttemptStatus::foreign_try_from((value.response.status, action))?,
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(value.response.id),
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: None,
incremental_authorization_allowed: None,
}),
status,
response: error.map_or_else(|| Ok(payment_response_data), Err),
..value.data
})
}
Expand Down Expand Up @@ -1016,9 +1055,12 @@ impl From<RefundStatus> for enums::RefundStatus {
}

#[derive(Default, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RefundResponse {
id: String,
status: RefundStatus,
reject_code: Option<String>,
reject_reason: Option<String>,
}

impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
Expand All @@ -1028,17 +1070,44 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
fn try_from(
item: types::RefundsResponseRouterData<api::Execute, RefundResponse>,
) -> Result<Self, Self::Error> {
let refund_status = enums::RefundStatus::from(item.response.status);
let (error, refund_response_data) = get_zen_refund_response(item.response, item.http_code)?;
Ok(Self {
response: Ok(types::RefundsResponseData {
connector_refund_id: item.response.id,
refund_status,
}),
response: error.map_or_else(|| Ok(refund_response_data), Err),
..item.data
})
}
}

fn get_zen_refund_response(
response: RefundResponse,
status_code: u16,
) -> CustomResult<(Option<types::ErrorResponse>, types::RefundsResponseData), errors::ConnectorError>
{
let refund_status = enums::RefundStatus::from(response.status);
let error = if utils::is_refund_failure(refund_status) {
Some(types::ErrorResponse {
code: response
.reject_code
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
message: response
.reject_reason
.clone()
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
reason: response.reject_reason,
status_code,
attempt_status: None,
connector_transaction_id: Some(response.id.clone()),
})
} else {
None
};
let refund_response_data = types::RefundsResponseData {
connector_refund_id: response.id,
refund_status,
};
Ok((error, refund_response_data))
}

impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
for types::RefundsRouterData<api::RSync>
{
Expand Down

0 comments on commit e816ccf

Please sign in to comment.