mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(Connector): [VOLT] Add support for Payments Webhooks (#3155)
This commit is contained in:
		| @ -2,11 +2,13 @@ pub mod transformers; | |||||||
|  |  | ||||||
| use std::fmt::Debug; | use std::fmt::Debug; | ||||||
|  |  | ||||||
| use common_utils::request::RequestContent; | use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; | ||||||
| use error_stack::{IntoReport, ResultExt}; | use error_stack::{IntoReport, ResultExt}; | ||||||
| use masking::{ExposeInterface, PeekInterface}; | use masking::{ExposeInterface, PeekInterface}; | ||||||
| use transformers as volt; | use transformers as volt; | ||||||
|  |  | ||||||
|  | use self::transformers::webhook_headers; | ||||||
|  | use super::utils; | ||||||
| use crate::{ | use crate::{ | ||||||
|     configs::settings, |     configs::settings, | ||||||
|     core::errors::{self, CustomResult}, |     core::errors::{self, CustomResult}, | ||||||
| @ -398,7 +400,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe | |||||||
|         data: &types::PaymentsSyncRouterData, |         data: &types::PaymentsSyncRouterData, | ||||||
|         res: Response, |         res: Response, | ||||||
|     ) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> { |     ) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> { | ||||||
|         let response: volt::VoltPsyncResponse = res |         let response: volt::VoltPaymentsResponseData = res | ||||||
|             .response |             .response | ||||||
|             .parse_struct("volt PaymentsSyncResponse") |             .parse_struct("volt PaymentsSyncResponse") | ||||||
|             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; |             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; | ||||||
| @ -586,24 +588,93 @@ impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponse | |||||||
|  |  | ||||||
| #[async_trait::async_trait] | #[async_trait::async_trait] | ||||||
| impl api::IncomingWebhook for Volt { | impl api::IncomingWebhook for Volt { | ||||||
|     fn get_webhook_object_reference_id( |     fn get_webhook_source_verification_algorithm( | ||||||
|         &self, |         &self, | ||||||
|         _request: &api::IncomingWebhookRequestDetails<'_>, |         _request: &api::IncomingWebhookRequestDetails<'_>, | ||||||
|  |     ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, errors::ConnectorError> { | ||||||
|  |         Ok(Box::new(crypto::HmacSha256)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn get_webhook_source_verification_signature( | ||||||
|  |         &self, | ||||||
|  |         request: &api::IncomingWebhookRequestDetails<'_>, | ||||||
|  |         _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, | ||||||
|  |     ) -> CustomResult<Vec<u8>, errors::ConnectorError> { | ||||||
|  |         let signature = | ||||||
|  |             utils::get_header_key_value(webhook_headers::X_VOLT_SIGNED, request.headers) | ||||||
|  |                 .change_context(errors::ConnectorError::WebhookSignatureNotFound)?; | ||||||
|  |  | ||||||
|  |         hex::decode(signature) | ||||||
|  |             .into_report() | ||||||
|  |             .change_context(errors::ConnectorError::WebhookVerificationSecretInvalid) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn get_webhook_source_verification_message( | ||||||
|  |         &self, | ||||||
|  |         request: &api::IncomingWebhookRequestDetails<'_>, | ||||||
|  |         _merchant_id: &str, | ||||||
|  |         _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets, | ||||||
|  |     ) -> CustomResult<Vec<u8>, errors::ConnectorError> { | ||||||
|  |         let x_volt_timed = | ||||||
|  |             utils::get_header_key_value(webhook_headers::X_VOLT_TIMED, request.headers)?; | ||||||
|  |         let user_agent = utils::get_header_key_value(webhook_headers::USER_AGENT, request.headers)?; | ||||||
|  |         let version = user_agent | ||||||
|  |             .split('/') | ||||||
|  |             .last() | ||||||
|  |             .ok_or(errors::ConnectorError::WebhookSourceVerificationFailed)?; | ||||||
|  |         Ok(format!( | ||||||
|  |             "{}|{}|{}", | ||||||
|  |             String::from_utf8_lossy(request.body), | ||||||
|  |             x_volt_timed, | ||||||
|  |             version | ||||||
|  |         ) | ||||||
|  |         .into_bytes()) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn get_webhook_object_reference_id( | ||||||
|  |         &self, | ||||||
|  |         request: &api::IncomingWebhookRequestDetails<'_>, | ||||||
|     ) -> CustomResult<api::webhooks::ObjectReferenceId, errors::ConnectorError> { |     ) -> CustomResult<api::webhooks::ObjectReferenceId, errors::ConnectorError> { | ||||||
|         Err(errors::ConnectorError::WebhooksNotImplemented).into_report() |         let webhook_body: volt::VoltWebhookBodyReference = request | ||||||
|  |             .body | ||||||
|  |             .parse_struct("VoltWebhookBodyReference") | ||||||
|  |             .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?; | ||||||
|  |         let reference = match webhook_body.merchant_internal_reference { | ||||||
|  |             Some(merchant_internal_reference) => { | ||||||
|  |                 api_models::payments::PaymentIdType::PaymentAttemptId(merchant_internal_reference) | ||||||
|  |             } | ||||||
|  |             None => { | ||||||
|  |                 api_models::payments::PaymentIdType::ConnectorTransactionId(webhook_body.payment) | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         Ok(api_models::webhooks::ObjectReferenceId::PaymentId( | ||||||
|  |             reference, | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn get_webhook_event_type( |     fn get_webhook_event_type( | ||||||
|         &self, |         &self, | ||||||
|         _request: &api::IncomingWebhookRequestDetails<'_>, |         request: &api::IncomingWebhookRequestDetails<'_>, | ||||||
|     ) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> { |     ) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> { | ||||||
|         Err(errors::ConnectorError::WebhooksNotImplemented).into_report() |         if request.body.is_empty() { | ||||||
|  |             Ok(api::IncomingWebhookEvent::EndpointVerification) | ||||||
|  |         } else { | ||||||
|  |             let payload: volt::VoltWebhookBodyEventType = request | ||||||
|  |                 .body | ||||||
|  |                 .parse_struct("VoltWebhookBodyEventType") | ||||||
|  |                 .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?; | ||||||
|  |             Ok(api::IncomingWebhookEvent::from(payload.status)) | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn get_webhook_resource_object( |     fn get_webhook_resource_object( | ||||||
|         &self, |         &self, | ||||||
|         _request: &api::IncomingWebhookRequestDetails<'_>, |         request: &api::IncomingWebhookRequestDetails<'_>, | ||||||
|     ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> { |     ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> { | ||||||
|         Err(errors::ConnectorError::WebhooksNotImplemented).into_report() |         let details: volt::VoltWebhookObjectResource = request | ||||||
|  |             .body | ||||||
|  |             .parse_struct("VoltWebhookObjectResource") | ||||||
|  |             .change_context(errors::ConnectorError::WebhookResourceObjectNotFound)?; | ||||||
|  |         Ok(Box::new(details)) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; | |||||||
|  |  | ||||||
| use crate::{ | use crate::{ | ||||||
|     connector::utils::{self, AddressDetailsData, RouterData}, |     connector::utils::{self, AddressDetailsData, RouterData}, | ||||||
|  |     consts, | ||||||
|     core::errors, |     core::errors, | ||||||
|     services, |     services, | ||||||
|     types::{self, api, storage::enums as storage_enums}, |     types::{self, api, storage::enums as storage_enums}, | ||||||
| @ -41,6 +42,12 @@ impl<T> | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | pub mod webhook_headers { | ||||||
|  |     pub const X_VOLT_SIGNED: &str = "X-Volt-Signed"; | ||||||
|  |     pub const X_VOLT_TIMED: &str = "X-Volt-Timed"; | ||||||
|  |     pub const USER_AGENT: &str = "User-Agent"; | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Debug, Serialize)] | #[derive(Debug, Serialize)] | ||||||
| #[serde(rename_all = "camelCase")] | #[serde(rename_all = "camelCase")] | ||||||
| pub struct VoltPaymentsRequest { | pub struct VoltPaymentsRequest { | ||||||
| @ -50,7 +57,6 @@ pub struct VoltPaymentsRequest { | |||||||
|     transaction_type: TransactionType, |     transaction_type: TransactionType, | ||||||
|     merchant_internal_reference: String, |     merchant_internal_reference: String, | ||||||
|     shopper: ShopperDetails, |     shopper: ShopperDetails, | ||||||
|     notification_url: Option<String>, |  | ||||||
|     payment_success_url: Option<String>, |     payment_success_url: Option<String>, | ||||||
|     payment_failure_url: Option<String>, |     payment_failure_url: Option<String>, | ||||||
|     payment_pending_url: Option<String>, |     payment_pending_url: Option<String>, | ||||||
| @ -91,7 +97,6 @@ impl TryFrom<&VoltRouterData<&types::PaymentsAuthorizeRouterData>> for VoltPayme | |||||||
|                     let payment_failure_url = item.router_data.request.router_return_url.clone(); |                     let payment_failure_url = item.router_data.request.router_return_url.clone(); | ||||||
|                     let payment_pending_url = item.router_data.request.router_return_url.clone(); |                     let payment_pending_url = item.router_data.request.router_return_url.clone(); | ||||||
|                     let payment_cancel_url = item.router_data.request.router_return_url.clone(); |                     let payment_cancel_url = item.router_data.request.router_return_url.clone(); | ||||||
|                     let notification_url = item.router_data.request.webhook_url.clone(); |  | ||||||
|                     let address = item.router_data.get_billing_address()?; |                     let address = item.router_data.get_billing_address()?; | ||||||
|                     let shopper = ShopperDetails { |                     let shopper = ShopperDetails { | ||||||
|                         email: item.router_data.request.email.clone(), |                         email: item.router_data.request.email.clone(), | ||||||
| @ -109,7 +114,6 @@ impl TryFrom<&VoltRouterData<&types::PaymentsAuthorizeRouterData>> for VoltPayme | |||||||
|                         payment_failure_url, |                         payment_failure_url, | ||||||
|                         payment_pending_url, |                         payment_pending_url, | ||||||
|                         payment_cancel_url, |                         payment_cancel_url, | ||||||
|                         notification_url, |  | ||||||
|                         shopper, |                         shopper, | ||||||
|                         transaction_type, |                         transaction_type, | ||||||
|                     }) |                     }) | ||||||
| @ -291,8 +295,9 @@ impl<F, T> | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Deserialize)] | #[derive(Debug, Serialize, Clone, Deserialize)] | ||||||
| #[serde(rename_all = "SCREAMING_SNAKE_CASE")] | #[serde(rename_all = "SCREAMING_SNAKE_CASE")] | ||||||
|  | #[derive(strum::Display)] | ||||||
| pub enum VoltPaymentStatus { | pub enum VoltPaymentStatus { | ||||||
|     NewPayment, |     NewPayment, | ||||||
|     Completed, |     Completed, | ||||||
| @ -309,7 +314,15 @@ pub enum VoltPaymentStatus { | |||||||
|     Failed, |     Failed, | ||||||
|     Settled, |     Settled, | ||||||
| } | } | ||||||
| #[derive(Debug, Deserialize)] |  | ||||||
|  | #[derive(Debug, Serialize, Deserialize)] | ||||||
|  | #[serde(untagged)] | ||||||
|  | pub enum VoltPaymentsResponseData { | ||||||
|  |     WebhookResponse(VoltWebhookObjectResource), | ||||||
|  |     PsyncResponse(VoltPsyncResponse), | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Serialize, Clone, Deserialize)] | ||||||
| #[serde(rename_all = "camelCase")] | #[serde(rename_all = "camelCase")] | ||||||
| pub struct VoltPsyncResponse { | pub struct VoltPsyncResponse { | ||||||
|     status: VoltPaymentStatus, |     status: VoltPaymentStatus, | ||||||
| @ -317,30 +330,103 @@ pub struct VoltPsyncResponse { | |||||||
|     merchant_internal_reference: Option<String>, |     merchant_internal_reference: Option<String>, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<F, T> TryFrom<types::ResponseRouterData<F, VoltPsyncResponse, T, types::PaymentsResponseData>> | impl<F, T> | ||||||
|  |     TryFrom<types::ResponseRouterData<F, VoltPaymentsResponseData, T, types::PaymentsResponseData>> | ||||||
|     for types::RouterData<F, T, types::PaymentsResponseData> |     for types::RouterData<F, T, types::PaymentsResponseData> | ||||||
| { | { | ||||||
|     type Error = error_stack::Report<errors::ConnectorError>; |     type Error = error_stack::Report<errors::ConnectorError>; | ||||||
|     fn try_from( |     fn try_from( | ||||||
|         item: types::ResponseRouterData<F, VoltPsyncResponse, T, types::PaymentsResponseData>, |         item: types::ResponseRouterData< | ||||||
|  |             F, | ||||||
|  |             VoltPaymentsResponseData, | ||||||
|  |             T, | ||||||
|  |             types::PaymentsResponseData, | ||||||
|  |         >, | ||||||
|     ) -> Result<Self, Self::Error> { |     ) -> Result<Self, Self::Error> { | ||||||
|  |         match item.response { | ||||||
|  |             VoltPaymentsResponseData::PsyncResponse(payment_response) => { | ||||||
|  |                 let status = enums::AttemptStatus::from(payment_response.status.clone()); | ||||||
|                 Ok(Self { |                 Ok(Self { | ||||||
|             status: enums::AttemptStatus::from(item.response.status), |                     status, | ||||||
|             response: Ok(types::PaymentsResponseData::TransactionResponse { |                     response: if is_payment_failure(status) { | ||||||
|                 resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()), |                         Err(types::ErrorResponse { | ||||||
|  |                             code: payment_response.status.clone().to_string(), | ||||||
|  |                             message: payment_response.status.clone().to_string(), | ||||||
|  |                             reason: Some(payment_response.status.to_string()), | ||||||
|  |                             status_code: item.http_code, | ||||||
|  |                             attempt_status: None, | ||||||
|  |                             connector_transaction_id: Some(payment_response.id), | ||||||
|  |                         }) | ||||||
|  |                     } else { | ||||||
|  |                         Ok(types::PaymentsResponseData::TransactionResponse { | ||||||
|  |                             resource_id: types::ResponseId::ConnectorTransactionId( | ||||||
|  |                                 payment_response.id.clone(), | ||||||
|  |                             ), | ||||||
|                             redirection_data: None, |                             redirection_data: None, | ||||||
|                             mandate_reference: None, |                             mandate_reference: None, | ||||||
|                             connector_metadata: None, |                             connector_metadata: None, | ||||||
|                             network_txn_id: None, |                             network_txn_id: None, | ||||||
|                 connector_response_reference_id: item |                             connector_response_reference_id: payment_response | ||||||
|                     .response |  | ||||||
|                                 .merchant_internal_reference |                                 .merchant_internal_reference | ||||||
|                     .or(Some(item.response.id)), |                                 .or(Some(payment_response.id)), | ||||||
|                             incremental_authorization_allowed: None, |                             incremental_authorization_allowed: None, | ||||||
|             }), |                         }) | ||||||
|  |                     }, | ||||||
|                     ..item.data |                     ..item.data | ||||||
|                 }) |                 }) | ||||||
|             } |             } | ||||||
|  |             VoltPaymentsResponseData::WebhookResponse(webhook_response) => { | ||||||
|  |                 let detailed_status = webhook_response.detailed_status.clone(); | ||||||
|  |                 let status = enums::AttemptStatus::from(webhook_response.status); | ||||||
|  |                 Ok(Self { | ||||||
|  |                     status, | ||||||
|  |                     response: if is_payment_failure(status) { | ||||||
|  |                         Err(types::ErrorResponse { | ||||||
|  |                             code: detailed_status | ||||||
|  |                                 .clone() | ||||||
|  |                                 .map(|volt_status| volt_status.to_string()) | ||||||
|  |                                 .unwrap_or_else(|| consts::NO_ERROR_CODE.to_owned()), | ||||||
|  |                             message: detailed_status | ||||||
|  |                                 .clone() | ||||||
|  |                                 .map(|volt_status| volt_status.to_string()) | ||||||
|  |                                 .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_owned()), | ||||||
|  |                             reason: detailed_status | ||||||
|  |                                 .clone() | ||||||
|  |                                 .map(|volt_status| volt_status.to_string()), | ||||||
|  |                             status_code: item.http_code, | ||||||
|  |                             attempt_status: None, | ||||||
|  |                             connector_transaction_id: Some(webhook_response.payment.clone()), | ||||||
|  |                         }) | ||||||
|  |                     } else { | ||||||
|  |                         Ok(types::PaymentsResponseData::TransactionResponse { | ||||||
|  |                             resource_id: types::ResponseId::ConnectorTransactionId( | ||||||
|  |                                 webhook_response.payment.clone(), | ||||||
|  |                             ), | ||||||
|  |                             redirection_data: None, | ||||||
|  |                             mandate_reference: None, | ||||||
|  |                             connector_metadata: None, | ||||||
|  |                             network_txn_id: None, | ||||||
|  |                             connector_response_reference_id: webhook_response | ||||||
|  |                                 .merchant_internal_reference | ||||||
|  |                                 .or(Some(webhook_response.payment)), | ||||||
|  |                             incremental_authorization_allowed: None, | ||||||
|  |                         }) | ||||||
|  |                     }, | ||||||
|  |                     ..item.data | ||||||
|  |                 }) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<VoltWebhookStatus> for enums::AttemptStatus { | ||||||
|  |     fn from(status: VoltWebhookStatus) -> Self { | ||||||
|  |         match status { | ||||||
|  |             VoltWebhookStatus::Completed | VoltWebhookStatus::Received => Self::Charged, | ||||||
|  |             VoltWebhookStatus::Failed | VoltWebhookStatus::NotReceived => Self::Failure, | ||||||
|  |             VoltWebhookStatus::Pending => Self::Pending, | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // REFUND : | // REFUND : | ||||||
| @ -405,6 +491,68 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>> | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Deserialize, Clone, Serialize)] | ||||||
|  | pub struct VoltWebhookBodyReference { | ||||||
|  |     pub payment: String, | ||||||
|  |     pub merchant_internal_reference: Option<String>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Deserialize, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct VoltWebhookBodyEventType { | ||||||
|  |     pub status: VoltWebhookStatus, | ||||||
|  |     pub detailed_status: Option<VoltDetailedStatus>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Deserialize, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct VoltWebhookObjectResource { | ||||||
|  |     pub payment: String, | ||||||
|  |     pub merchant_internal_reference: Option<String>, | ||||||
|  |     pub status: VoltWebhookStatus, | ||||||
|  |     pub detailed_status: Option<VoltDetailedStatus>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Deserialize, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "SCREAMING_SNAKE_CASE")] | ||||||
|  | pub enum VoltWebhookStatus { | ||||||
|  |     Completed, | ||||||
|  |     Failed, | ||||||
|  |     Pending, | ||||||
|  |     Received, | ||||||
|  |     NotReceived, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Deserialize, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "SCREAMING_SNAKE_CASE")] | ||||||
|  | #[derive(strum::Display)] | ||||||
|  | pub enum VoltDetailedStatus { | ||||||
|  |     RefusedByRisk, | ||||||
|  |     RefusedByBank, | ||||||
|  |     ErrorAtBank, | ||||||
|  |     CancelledByUser, | ||||||
|  |     AbandonedByUser, | ||||||
|  |     Failed, | ||||||
|  |     Completed, | ||||||
|  |     BankRedirect, | ||||||
|  |     DelayedAtBank, | ||||||
|  |     AwaitingCheckoutAuthorisation, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<VoltWebhookStatus> for api::IncomingWebhookEvent { | ||||||
|  |     fn from(status: VoltWebhookStatus) -> Self { | ||||||
|  |         match status { | ||||||
|  |             VoltWebhookStatus::Completed | VoltWebhookStatus::Received => { | ||||||
|  |                 Self::PaymentIntentSuccess | ||||||
|  |             } | ||||||
|  |             VoltWebhookStatus::Failed | VoltWebhookStatus::NotReceived => { | ||||||
|  |                 Self::PaymentIntentFailure | ||||||
|  |             } | ||||||
|  |             VoltWebhookStatus::Pending => Self::PaymentIntentProcessing, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Default, Debug, Serialize, Deserialize, PartialEq)] | #[derive(Default, Debug, Serialize, Deserialize, PartialEq)] | ||||||
| pub struct VoltErrorResponse { | pub struct VoltErrorResponse { | ||||||
|     pub exception: VoltErrorException, |     pub exception: VoltErrorException, | ||||||
| @ -429,3 +577,32 @@ pub struct VoltErrorList { | |||||||
|     pub property: String, |     pub property: String, | ||||||
|     pub message: String, |     pub message: String, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | fn is_payment_failure(status: enums::AttemptStatus) -> bool { | ||||||
|  |     match status { | ||||||
|  |         common_enums::AttemptStatus::AuthenticationFailed | ||||||
|  |         | common_enums::AttemptStatus::AuthorizationFailed | ||||||
|  |         | common_enums::AttemptStatus::CaptureFailed | ||||||
|  |         | common_enums::AttemptStatus::VoidFailed | ||||||
|  |         | common_enums::AttemptStatus::Failure => true, | ||||||
|  |         common_enums::AttemptStatus::Started | ||||||
|  |         | common_enums::AttemptStatus::RouterDeclined | ||||||
|  |         | common_enums::AttemptStatus::AuthenticationPending | ||||||
|  |         | common_enums::AttemptStatus::AuthenticationSuccessful | ||||||
|  |         | common_enums::AttemptStatus::Authorized | ||||||
|  |         | common_enums::AttemptStatus::Charged | ||||||
|  |         | common_enums::AttemptStatus::Authorizing | ||||||
|  |         | common_enums::AttemptStatus::CodInitiated | ||||||
|  |         | common_enums::AttemptStatus::Voided | ||||||
|  |         | common_enums::AttemptStatus::VoidInitiated | ||||||
|  |         | common_enums::AttemptStatus::CaptureInitiated | ||||||
|  |         | common_enums::AttemptStatus::AutoRefunded | ||||||
|  |         | common_enums::AttemptStatus::PartialCharged | ||||||
|  |         | common_enums::AttemptStatus::PartialChargedAndChargeable | ||||||
|  |         | common_enums::AttemptStatus::Unresolved | ||||||
|  |         | common_enums::AttemptStatus::Pending | ||||||
|  |         | common_enums::AttemptStatus::PaymentMethodAwaited | ||||||
|  |         | common_enums::AttemptStatus::ConfirmationAwaited | ||||||
|  |         | common_enums::AttemptStatus::DeviceDataCollectionPending => false, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Swangi Kumari
					Swangi Kumari