mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 10:06:32 +08:00 
			
		
		
		
	fix(connector): Trustpay zen error mapping (#3255)
Co-authored-by: Prasunna Soppa <prasunna.soppa@juspay.in> Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
		| @ -813,7 +813,7 @@ fn handle_bank_redirects_sync_response( | |||||||
|     errors::ConnectorError, |     errors::ConnectorError, | ||||||
| > { | > { | ||||||
|     let status = enums::AttemptStatus::from(response.payment_information.status); |     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 |         let reason_info = response | ||||||
|             .payment_information |             .payment_information | ||||||
|             .status_reason_information |             .status_reason_information | ||||||
| @ -856,6 +856,7 @@ fn handle_bank_redirects_sync_response( | |||||||
|  |  | ||||||
| pub fn handle_webhook_response( | pub fn handle_webhook_response( | ||||||
|     payment_information: WebhookPaymentInformation, |     payment_information: WebhookPaymentInformation, | ||||||
|  |     status_code: u16, | ||||||
| ) -> CustomResult< | ) -> CustomResult< | ||||||
|     ( |     ( | ||||||
|         enums::AttemptStatus, |         enums::AttemptStatus, | ||||||
| @ -865,6 +866,22 @@ pub fn handle_webhook_response( | |||||||
|     errors::ConnectorError, |     errors::ConnectorError, | ||||||
| > { | > { | ||||||
|     let status = enums::AttemptStatus::try_from(payment_information.status)?; |     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 { |     let payment_response_data = types::PaymentsResponseData::TransactionResponse { | ||||||
|         resource_id: types::ResponseId::NoResponseId, |         resource_id: types::ResponseId::NoResponseId, | ||||||
|         redirection_data: None, |         redirection_data: None, | ||||||
| @ -874,7 +891,7 @@ pub fn handle_webhook_response( | |||||||
|         connector_response_reference_id: None, |         connector_response_reference_id: None, | ||||||
|         incremental_authorization_allowed: None, |         incremental_authorization_allowed: None, | ||||||
|     }; |     }; | ||||||
|     Ok((status, None, payment_response_data)) |     Ok((status, error, payment_response_data)) | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn get_trustpay_response( | pub fn get_trustpay_response( | ||||||
| @ -901,7 +918,9 @@ pub fn get_trustpay_response( | |||||||
|         TrustpayPaymentsResponse::BankRedirectError(response) => { |         TrustpayPaymentsResponse::BankRedirectError(response) => { | ||||||
|             handle_bank_redirects_error_response(*response, status_code) |             handle_bank_redirects_error_response(*response, status_code) | ||||||
|         } |         } | ||||||
|         TrustpayPaymentsResponse::WebhookResponse(response) => handle_webhook_response(*response), |         TrustpayPaymentsResponse::WebhookResponse(response) => { | ||||||
|  |             handle_webhook_response(*response, status_code) | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -1452,9 +1471,24 @@ fn handle_cards_refund_response( | |||||||
|  |  | ||||||
| fn handle_webhooks_refund_response( | fn handle_webhooks_refund_response( | ||||||
|     response: WebhookPaymentInformation, |     response: WebhookPaymentInformation, | ||||||
|  |     status_code: u16, | ||||||
| ) -> CustomResult<(Option<types::ErrorResponse>, types::RefundsResponseData), errors::ConnectorError> | ) -> CustomResult<(Option<types::ErrorResponse>, types::RefundsResponseData), errors::ConnectorError> | ||||||
| { | { | ||||||
|     let refund_status = diesel_models::enums::RefundStatus::try_from(response.status)?; |     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 { |     let refund_response_data = types::RefundsResponseData { | ||||||
|         connector_refund_id: response |         connector_refund_id: response | ||||||
|             .references |             .references | ||||||
| @ -1462,7 +1496,7 @@ fn handle_webhooks_refund_response( | |||||||
|             .ok_or(errors::ConnectorError::MissingConnectorRefundID)?, |             .ok_or(errors::ConnectorError::MissingConnectorRefundID)?, | ||||||
|         refund_status, |         refund_status, | ||||||
|     }; |     }; | ||||||
|     Ok((None, refund_response_data)) |     Ok((error, refund_response_data)) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn handle_bank_redirects_refund_response( | fn handle_bank_redirects_refund_response( | ||||||
| @ -1495,7 +1529,7 @@ fn handle_bank_redirects_refund_sync_response( | |||||||
|     status_code: u16, |     status_code: u16, | ||||||
| ) -> (Option<types::ErrorResponse>, types::RefundsResponseData) { | ) -> (Option<types::ErrorResponse>, types::RefundsResponseData) { | ||||||
|     let refund_status = enums::RefundStatus::from(response.payment_information.status); |     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 |         let reason_info = response | ||||||
|             .payment_information |             .payment_information | ||||||
|             .status_reason_information |             .status_reason_information | ||||||
| @ -1551,7 +1585,9 @@ impl<F> TryFrom<types::RefundsResponseRouterData<F, RefundResponse>> | |||||||
|             RefundResponse::CardsRefund(response) => { |             RefundResponse::CardsRefund(response) => { | ||||||
|                 handle_cards_refund_response(*response, item.http_code)? |                 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) => { |             RefundResponse::BankRedirectRefund(response) => { | ||||||
|                 handle_bank_redirects_refund_response(*response, item.http_code) |                 handle_bank_redirects_refund_response(*response, item.http_code) | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ use crate::{ | |||||||
|     connector::utils::{ |     connector::utils::{ | ||||||
|         self, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, RouterData, |         self, BrowserInformationData, CardData, PaymentsAuthorizeRequestData, RouterData, | ||||||
|     }, |     }, | ||||||
|  |     consts, | ||||||
|     core::errors::{self, CustomResult}, |     core::errors::{self, CustomResult}, | ||||||
|     services::{self, Method}, |     services::{self, Method}, | ||||||
|     types::{self, api, storage::enums, transformers::ForeignTryFrom}, |     types::{self, api, storage::enums, transformers::ForeignTryFrom}, | ||||||
| @ -848,12 +849,15 @@ impl ForeignTryFrom<(ZenPaymentStatus, Option<ZenActions>)> for enums::AttemptSt | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Deserialize)] | #[derive(Debug, Clone, Deserialize)] | ||||||
| #[serde(rename_all = "camelCase")] | #[serde(rename_all = "camelCase")] | ||||||
| pub struct ApiResponse { | pub struct ApiResponse { | ||||||
|     status: ZenPaymentStatus, |     status: ZenPaymentStatus, | ||||||
|     id: String, |     id: String, | ||||||
|  |     // merchant_transaction_id: Option<String>, | ||||||
|     merchant_action: Option<ZenMerchantAction>, |     merchant_action: Option<ZenMerchantAction>, | ||||||
|  |     reject_code: Option<String>, | ||||||
|  |     reject_reason: Option<String>, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Deserialize)] | #[derive(Debug, Deserialize)] | ||||||
| @ -869,18 +873,18 @@ pub struct CheckoutResponse { | |||||||
|     redirect_url: url::Url, |     redirect_url: url::Url, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Serialize, Deserialize)] | #[derive(Debug, Clone, Serialize, Deserialize)] | ||||||
| #[serde(rename_all = "camelCase")] | #[serde(rename_all = "camelCase")] | ||||||
| pub struct ZenMerchantAction { | pub struct ZenMerchantAction { | ||||||
|     action: ZenActions, |     action: ZenActions, | ||||||
|     data: ZenMerchantActionData, |     data: ZenMerchantActionData, | ||||||
| } | } | ||||||
| #[derive(Debug, Serialize, Deserialize)] | #[derive(Debug, Clone, Serialize, Deserialize)] | ||||||
| #[serde(rename_all = "UPPERCASE")] | #[serde(rename_all = "UPPERCASE")] | ||||||
| pub enum ZenActions { | pub enum ZenActions { | ||||||
|     Redirect, |     Redirect, | ||||||
| } | } | ||||||
| #[derive(Debug, Serialize, Deserialize)] | #[derive(Debug, Clone, Serialize, Deserialize)] | ||||||
| #[serde(rename_all = "camelCase")] | #[serde(rename_all = "camelCase")] | ||||||
| pub struct ZenMerchantActionData { | pub struct ZenMerchantActionData { | ||||||
|     redirect_url: url::Url, |     redirect_url: url::Url, | ||||||
| @ -913,6 +917,57 @@ 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>> | impl<F, T> TryFrom<types::ResponseRouterData<F, ApiResponse, T, types::PaymentsResponseData>> | ||||||
|     for types::RouterData<F, T, types::PaymentsResponseData> |     for types::RouterData<F, T, types::PaymentsResponseData> | ||||||
| { | { | ||||||
| @ -920,28 +975,12 @@ impl<F, T> TryFrom<types::ResponseRouterData<F, ApiResponse, T, types::PaymentsR | |||||||
|     fn try_from( |     fn try_from( | ||||||
|         value: types::ResponseRouterData<F, ApiResponse, T, types::PaymentsResponseData>, |         value: types::ResponseRouterData<F, ApiResponse, T, types::PaymentsResponseData>, | ||||||
|     ) -> Result<Self, Self::Error> { |     ) -> Result<Self, Self::Error> { | ||||||
|         let redirection_data_action = value.response.merchant_action.map(|merchant_action| { |         let (status, error, payment_response_data) = | ||||||
|             ( |             get_zen_response(value.response.clone(), value.http_code)?; | ||||||
|                 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), |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         Ok(Self { |         Ok(Self { | ||||||
|             status: enums::AttemptStatus::foreign_try_from((value.response.status, action))?, |             status, | ||||||
|             response: Ok(types::PaymentsResponseData::TransactionResponse { |             response: error.map_or_else(|| Ok(payment_response_data), Err), | ||||||
|                 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, |  | ||||||
|             }), |  | ||||||
|             ..value.data |             ..value.data | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| @ -1016,9 +1055,12 @@ impl From<RefundStatus> for enums::RefundStatus { | |||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Default, Debug, Deserialize)] | #[derive(Default, Debug, Deserialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
| pub struct RefundResponse { | pub struct RefundResponse { | ||||||
|     id: String, |     id: String, | ||||||
|     status: RefundStatus, |     status: RefundStatus, | ||||||
|  |     reject_code: Option<String>, | ||||||
|  |     reject_reason: Option<String>, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>> | impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>> | ||||||
| @ -1028,17 +1070,44 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>> | |||||||
|     fn try_from( |     fn try_from( | ||||||
|         item: types::RefundsResponseRouterData<api::Execute, RefundResponse>, |         item: types::RefundsResponseRouterData<api::Execute, RefundResponse>, | ||||||
|     ) -> Result<Self, Self::Error> { |     ) -> 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 { |         Ok(Self { | ||||||
|             response: Ok(types::RefundsResponseData { |             response: error.map_or_else(|| Ok(refund_response_data), Err), | ||||||
|                 connector_refund_id: item.response.id, |  | ||||||
|                 refund_status, |  | ||||||
|             }), |  | ||||||
|             ..item.data |             ..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>> | impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>> | ||||||
|     for types::RefundsRouterData<api::RSync> |     for types::RefundsRouterData<api::RSync> | ||||||
| { | { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Sahkal Poddar
					Sahkal Poddar