pub mod payment_cancel; pub mod payment_capture; pub mod payment_confirm; pub mod payment_create; pub mod payment_method_validate; pub mod payment_response; pub mod payment_session; pub mod payment_start; pub mod payment_status; pub mod payment_update; use async_trait::async_trait; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; pub use self::{ payment_cancel::PaymentCancel, payment_capture::PaymentCapture, payment_confirm::PaymentConfirm, payment_create::PaymentCreate, payment_method_validate::PaymentMethodValidate, payment_response::PaymentResponse, payment_session::PaymentSession, payment_start::PaymentStart, payment_status::PaymentStatus, payment_update::PaymentUpdate, }; use super::{helpers, CustomerDetails, PaymentData}; use crate::{ core::errors::{self, CustomResult, RouterResult}, db::StorageInterface, routes::AppState, types::{ self, api, storage::{self, enums}, PaymentsResponseData, }, }; pub type BoxedOperation<'a, F, T> = Box + Send + Sync + 'a>; pub trait Operation: Send + std::fmt::Debug { fn to_validate_request(&self) -> RouterResult<&(dyn ValidateRequest + Send + Sync)> { Err(report!(errors::ApiErrorResponse::InternalServerError)) .attach_printable_lazy(|| format!("validate request interface not found for {self:?}")) } fn to_get_tracker( &self, ) -> RouterResult<&(dyn GetTracker, T> + Send + Sync)> { Err(report!(errors::ApiErrorResponse::InternalServerError)) .attach_printable_lazy(|| format!("get tracker interface not found for {self:?}")) } fn to_domain(&self) -> RouterResult<&dyn Domain> { Err(report!(errors::ApiErrorResponse::InternalServerError)) .attach_printable_lazy(|| format!("domain interface not found for {self:?}")) } fn to_update_tracker( &self, ) -> RouterResult<&(dyn UpdateTracker, T> + Send + Sync)> { Err(report!(errors::ApiErrorResponse::InternalServerError)) .attach_printable_lazy(|| format!("update tracker interface not found for {self:?}")) } fn to_post_update_tracker( &self, ) -> RouterResult<&(dyn PostUpdateTracker, T> + Send + Sync)> { Err(report!(errors::ApiErrorResponse::InternalServerError)).attach_printable_lazy(|| { format!("post connector update tracker not found for {self:?}") }) } } pub struct ValidateResult<'a> { pub merchant_id: &'a str, pub payment_id: api::PaymentIdType, pub mandate_type: Option, pub storage_scheme: enums::MerchantStorageScheme, } #[allow(clippy::type_complexity)] pub trait ValidateRequest { fn validate_request<'a, 'b>( &'b self, request: &R, merchant_account: &'a storage::MerchantAccount, ) -> RouterResult<(BoxedOperation<'b, F, R>, ValidateResult<'a>)>; } #[async_trait] pub trait GetTracker: Send { #[allow(clippy::too_many_arguments)] async fn get_trackers<'a>( &'a self, state: &'a AppState, payment_id: &api::PaymentIdType, request: &R, mandate_type: Option, merchant_account: &storage::MerchantAccount, ) -> RouterResult<(BoxedOperation<'a, F, R>, D, Option)>; } #[async_trait] pub trait Domain: Send + Sync { /// This will fetch customer details, (this operation is flow specific) async fn get_or_create_customer_details<'a>( &'a self, db: &dyn StorageInterface, payment_data: &mut PaymentData, request: Option, merchant_id: &str, ) -> CustomResult<(BoxedOperation<'a, F, R>, Option), errors::StorageError>; #[allow(clippy::too_many_arguments)] async fn make_pm_data<'a>( &'a self, state: &'a AppState, payment_data: &mut PaymentData, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult<(BoxedOperation<'a, F, R>, Option)>; async fn add_task_to_process_tracker<'a>( &'a self, _db: &'a AppState, _payment_attempt: &storage::PaymentAttempt, ) -> CustomResult<(), errors::ApiErrorResponse> { Ok(()) } async fn get_connector<'a>( &'a self, merchant_account: &storage::MerchantAccount, state: &AppState, request: &R, previously_used_connector: Option<&String>, ) -> CustomResult; } #[async_trait] pub trait UpdateTracker: Send { async fn update_trackers<'b>( &'b self, db: &dyn StorageInterface, payment_id: &api::PaymentIdType, payment_data: D, customer: Option, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult<(BoxedOperation<'b, F, Req>, D)> where F: 'b + Send; } #[async_trait] pub trait PostUpdateTracker: Send { async fn update_tracker<'b>( &'b self, db: &dyn StorageInterface, payment_id: &api::PaymentIdType, payment_data: D, response: types::RouterData, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult where F: 'b + Send; } #[async_trait] impl> Domain for Op where for<'a> &'a Op: Operation, { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, db: &dyn StorageInterface, payment_data: &mut PaymentData, _request: Option, merchant_id: &str, ) -> CustomResult< ( BoxedOperation<'a, F, api::PaymentsRetrieveRequest>, Option, ), errors::StorageError, > { Ok(( Box::new(self), helpers::get_customer_from_details( db, payment_data.payment_intent.customer_id.clone(), merchant_id, ) .await?, )) } async fn get_connector<'a>( &'a self, _merchant_account: &storage::MerchantAccount, state: &AppState, _request: &api::PaymentsRetrieveRequest, previously_used_connector: Option<&String>, ) -> CustomResult { helpers::get_connector_default(state, previously_used_connector).await } #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, state: &'a AppState, payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRetrieveRequest>, Option, )> { helpers::make_pm_data(Box::new(self), state, payment_data).await } } #[async_trait] impl> Domain for Op where for<'a> &'a Op: Operation, { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, db: &dyn StorageInterface, payment_data: &mut PaymentData, _request: Option, merchant_id: &str, ) -> CustomResult< ( BoxedOperation<'a, F, api::PaymentsCaptureRequest>, Option, ), errors::StorageError, > { Ok(( Box::new(self), helpers::get_customer_from_details( db, payment_data.payment_intent.customer_id.clone(), merchant_id, ) .await?, )) } #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, _state: &'a AppState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCaptureRequest>, Option, )> { Ok((Box::new(self), None)) } async fn get_connector<'a>( &'a self, _merchant_account: &storage::MerchantAccount, state: &AppState, _request: &api::PaymentsCaptureRequest, previously_used_connector: Option<&String>, ) -> CustomResult { helpers::get_connector_default(state, previously_used_connector).await } } #[async_trait] impl> Domain for Op where for<'a> &'a Op: Operation, { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, db: &dyn StorageInterface, payment_data: &mut PaymentData, _request: Option, merchant_id: &str, ) -> CustomResult< ( BoxedOperation<'a, F, api::PaymentsCancelRequest>, Option, ), errors::StorageError, > { Ok(( Box::new(self), helpers::get_customer_from_details( db, payment_data.payment_intent.customer_id.clone(), merchant_id, ) .await?, )) } #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, _state: &'a AppState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCancelRequest>, Option, )> { Ok((Box::new(self), None)) } async fn get_connector<'a>( &'a self, _merchant_account: &storage::MerchantAccount, state: &AppState, _request: &api::PaymentsCancelRequest, previously_used_connector: Option<&String>, ) -> CustomResult { helpers::get_connector_default(state, previously_used_connector).await } }