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, | ||||
| > { | ||||
|     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 | ||||
| @ -856,6 +856,7 @@ fn handle_bank_redirects_sync_response( | ||||
|  | ||||
| pub fn handle_webhook_response( | ||||
|     payment_information: WebhookPaymentInformation, | ||||
|     status_code: u16, | ||||
| ) -> CustomResult< | ||||
|     ( | ||||
|         enums::AttemptStatus, | ||||
| @ -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, | ||||
| @ -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( | ||||
| @ -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) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1452,9 +1471,24 @@ 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 | ||||
| @ -1462,7 +1496,7 @@ fn handle_webhooks_refund_response( | ||||
|             .ok_or(errors::ConnectorError::MissingConnectorRefundID)?, | ||||
|         refund_status, | ||||
|     }; | ||||
|     Ok((None, refund_response_data)) | ||||
|     Ok((error, refund_response_data)) | ||||
| } | ||||
|  | ||||
| fn handle_bank_redirects_refund_response( | ||||
| @ -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 | ||||
| @ -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) | ||||
|             } | ||||
|  | ||||
| @ -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}, | ||||
| @ -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)] | ||||
| @ -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, | ||||
| @ -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>> | ||||
|     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( | ||||
|         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 | ||||
|         }) | ||||
|     } | ||||
| @ -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>> | ||||
| @ -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> | ||||
| { | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Sahkal Poddar
					Sahkal Poddar