use common_enums as storage_enums; use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, crypto::Encryptable, encryption::Encryption, id_type, pii::{self, Email}, types::{keymanager::KeyManagerState, MinorUnit}, }; use masking::{Deserialize, Secret}; use serde::Serialize; use time::PrimitiveDateTime; use super::{payment_attempt::PaymentAttempt, PaymentIntent}; use crate::{errors, merchant_key_store::MerchantKeyStore, RemoteStorageObject}; #[async_trait::async_trait] pub trait PaymentIntentInterface { async fn update_payment_intent( &self, state: &KeyManagerState, this: PaymentIntent, payment_intent: PaymentIntentUpdate, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; async fn insert_payment_intent( &self, state: &KeyManagerState, new: PaymentIntent, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; async fn find_payment_intent_by_payment_id_merchant_id( &self, state: &KeyManagerState, payment_id: &str, merchant_id: &id_type::MerchantId, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; async fn get_active_payment_attempt( &self, payment: &mut PaymentIntent, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result; #[cfg(feature = "olap")] async fn filter_payment_intent_by_constraints( &self, state: &KeyManagerState, merchant_id: &id_type::MerchantId, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; #[cfg(feature = "olap")] async fn filter_payment_intents_by_time_range_constraints( &self, state: &KeyManagerState, merchant_id: &id_type::MerchantId, time_range: &api_models::payments::TimeRange, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; #[cfg(feature = "olap")] async fn get_intent_status_with_count( &self, merchant_id: &id_type::MerchantId, constraints: &api_models::payments::TimeRange, ) -> error_stack::Result, errors::StorageError>; #[cfg(feature = "olap")] async fn get_filtered_payment_intents_attempt( &self, state: &KeyManagerState, merchant_id: &id_type::MerchantId, constraints: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; #[cfg(feature = "olap")] async fn get_filtered_active_attempt_ids_for_total_count( &self, merchant_id: &id_type::MerchantId, constraints: &PaymentIntentFetchConstraints, storage_scheme: storage_enums::MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; } #[derive(Clone, Debug, PartialEq, router_derive::DebugAsDisplay, Serialize, Deserialize)] pub struct CustomerData { pub name: Option>, pub email: Option, pub phone: Option>, pub phone_country_code: Option, } #[derive(Clone, Debug, PartialEq)] pub struct PaymentIntentNew { pub payment_id: String, pub merchant_id: id_type::MerchantId, pub status: storage_enums::IntentStatus, pub amount: MinorUnit, pub currency: Option, pub amount_captured: Option, pub customer_id: Option, pub description: Option, pub return_url: Option, pub metadata: Option, pub frm_metadata: Option, pub connector_id: Option, pub shipping_address_id: Option, pub billing_address_id: Option, pub statement_descriptor_name: Option, pub statement_descriptor_suffix: Option, pub created_at: Option, pub modified_at: Option, pub last_synced: Option, pub setup_future_usage: Option, pub off_session: Option, pub client_secret: Option, pub active_attempt: RemoteStorageObject, pub business_country: Option, pub business_label: Option, pub order_details: Option>, pub allowed_payment_method_types: Option, pub connector_metadata: Option, pub feature_metadata: Option, pub attempt_count: i16, pub profile_id: Option, pub merchant_decision: Option, pub payment_link_id: Option, pub payment_confirm_source: Option, pub updated_by: String, pub surcharge_applicable: Option, pub request_incremental_authorization: Option, pub incremental_authorization_allowed: Option, pub authorization_count: Option, pub fingerprint_id: Option, pub session_expiry: Option, pub request_external_three_ds_authentication: Option, pub charges: Option, pub customer_details: Option>>, pub billing_details: Option>>, pub shipping_details: Option>>, pub is_payment_processor_token_flow: Option, } #[derive(Debug, Clone, Serialize)] pub struct PaymentIntentUpdateFields { pub amount: MinorUnit, pub currency: storage_enums::Currency, pub setup_future_usage: Option, pub status: storage_enums::IntentStatus, pub customer_id: Option, pub shipping_address_id: Option, pub billing_address_id: Option, pub return_url: Option, pub business_country: Option, pub business_label: Option, pub description: Option, pub statement_descriptor_name: Option, pub statement_descriptor_suffix: Option, pub order_details: Option>, pub metadata: Option, pub frm_metadata: Option, pub payment_confirm_source: Option, pub updated_by: String, pub fingerprint_id: Option, pub session_expiry: Option, pub request_external_three_ds_authentication: Option, pub customer_details: Option>>, pub billing_details: Option>>, pub merchant_order_reference_id: Option, pub shipping_details: Option>>, pub is_payment_processor_token_flow: Option, } #[derive(Debug, Clone, Serialize)] pub enum PaymentIntentUpdate { ResponseUpdate { status: storage_enums::IntentStatus, amount_captured: Option, return_url: Option, updated_by: String, fingerprint_id: Option, incremental_authorization_allowed: Option, }, MetadataUpdate { metadata: serde_json::Value, updated_by: String, }, Update(Box), PaymentCreateUpdate { return_url: Option, status: Option, customer_id: Option, shipping_address_id: Option, billing_address_id: Option, customer_details: Option>>, updated_by: String, }, MerchantStatusUpdate { status: storage_enums::IntentStatus, shipping_address_id: Option, billing_address_id: Option, updated_by: String, }, PGStatusUpdate { status: storage_enums::IntentStatus, incremental_authorization_allowed: Option, updated_by: String, }, PaymentAttemptAndAttemptCountUpdate { active_attempt_id: String, attempt_count: i16, updated_by: String, }, StatusAndAttemptUpdate { status: storage_enums::IntentStatus, active_attempt_id: String, attempt_count: i16, updated_by: String, }, ApproveUpdate { status: storage_enums::IntentStatus, merchant_decision: Option, updated_by: String, }, RejectUpdate { status: storage_enums::IntentStatus, merchant_decision: Option, updated_by: String, }, SurchargeApplicableUpdate { surcharge_applicable: bool, updated_by: String, }, IncrementalAuthorizationAmountUpdate { amount: MinorUnit, }, AuthorizationCountUpdate { authorization_count: i32, }, CompleteAuthorizeUpdate { shipping_address_id: Option, }, ManualUpdate { status: Option, updated_by: String, }, } #[derive(Clone, Debug, Default)] pub struct PaymentIntentUpdateInternal { pub amount: Option, pub currency: Option, pub status: Option, pub amount_captured: Option, pub customer_id: Option, pub return_url: Option, pub setup_future_usage: Option, pub off_session: Option, pub metadata: Option, pub billing_address_id: Option, pub shipping_address_id: Option, pub modified_at: Option, pub active_attempt_id: Option, pub business_country: Option, pub business_label: Option, pub description: Option, pub statement_descriptor_name: Option, pub statement_descriptor_suffix: Option, pub order_details: Option>, pub attempt_count: Option, // Denotes the action(approve or reject) taken by merchant in case of manual review. // Manual review can occur when the transaction is marked as risky by the frm_processor, payment processor or when there is underpayment/over payment incase of crypto payment pub merchant_decision: Option, pub payment_confirm_source: Option, pub updated_by: String, pub surcharge_applicable: Option, pub incremental_authorization_allowed: Option, pub authorization_count: Option, pub fingerprint_id: Option, pub session_expiry: Option, pub request_external_three_ds_authentication: Option, pub frm_metadata: Option, pub customer_details: Option>>, pub billing_details: Option>>, pub merchant_order_reference_id: Option, pub shipping_details: Option>>, pub is_payment_processor_token_flow: Option, } impl From for PaymentIntentUpdateInternal { fn from(payment_intent_update: PaymentIntentUpdate) -> Self { match payment_intent_update { PaymentIntentUpdate::MetadataUpdate { metadata, updated_by, } => Self { metadata: Some(metadata), modified_at: Some(common_utils::date_time::now()), updated_by, ..Default::default() }, PaymentIntentUpdate::Update(value) => Self { amount: Some(value.amount), currency: Some(value.currency), setup_future_usage: value.setup_future_usage, status: Some(value.status), customer_id: value.customer_id, shipping_address_id: value.shipping_address_id, billing_address_id: value.billing_address_id, return_url: value.return_url, business_country: value.business_country, business_label: value.business_label, description: value.description, statement_descriptor_name: value.statement_descriptor_name, statement_descriptor_suffix: value.statement_descriptor_suffix, order_details: value.order_details, metadata: value.metadata, payment_confirm_source: value.payment_confirm_source, updated_by: value.updated_by, session_expiry: value.session_expiry, fingerprint_id: value.fingerprint_id, request_external_three_ds_authentication: value .request_external_three_ds_authentication, frm_metadata: value.frm_metadata, customer_details: value.customer_details, billing_details: value.billing_details, merchant_order_reference_id: value.merchant_order_reference_id, shipping_details: value.shipping_details, is_payment_processor_token_flow: value.is_payment_processor_token_flow, ..Default::default() }, PaymentIntentUpdate::PaymentCreateUpdate { return_url, status, customer_id, shipping_address_id, billing_address_id, customer_details, updated_by, } => Self { return_url, status, customer_id, shipping_address_id, billing_address_id, customer_details, modified_at: Some(common_utils::date_time::now()), updated_by, ..Default::default() }, PaymentIntentUpdate::PGStatusUpdate { status, updated_by, incremental_authorization_allowed, } => Self { status: Some(status), modified_at: Some(common_utils::date_time::now()), updated_by, incremental_authorization_allowed, ..Default::default() }, PaymentIntentUpdate::MerchantStatusUpdate { status, shipping_address_id, billing_address_id, updated_by, } => Self { status: Some(status), shipping_address_id, billing_address_id, modified_at: Some(common_utils::date_time::now()), updated_by, ..Default::default() }, PaymentIntentUpdate::ResponseUpdate { // amount, // currency, status, amount_captured, fingerprint_id, // customer_id, return_url, updated_by, incremental_authorization_allowed, } => Self { // amount, // currency: Some(currency), status: Some(status), amount_captured, fingerprint_id, // customer_id, return_url, modified_at: Some(common_utils::date_time::now()), updated_by, incremental_authorization_allowed, ..Default::default() }, PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { active_attempt_id, attempt_count, updated_by, } => Self { active_attempt_id: Some(active_attempt_id), attempt_count: Some(attempt_count), updated_by, ..Default::default() }, PaymentIntentUpdate::StatusAndAttemptUpdate { status, active_attempt_id, attempt_count, updated_by, } => Self { status: Some(status), active_attempt_id: Some(active_attempt_id), attempt_count: Some(attempt_count), updated_by, ..Default::default() }, PaymentIntentUpdate::ApproveUpdate { status, merchant_decision, updated_by, } => Self { status: Some(status), merchant_decision, updated_by, ..Default::default() }, PaymentIntentUpdate::RejectUpdate { status, merchant_decision, updated_by, } => Self { status: Some(status), merchant_decision, updated_by, ..Default::default() }, PaymentIntentUpdate::SurchargeApplicableUpdate { surcharge_applicable, updated_by, } => Self { surcharge_applicable: Some(surcharge_applicable), updated_by, ..Default::default() }, PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => Self { amount: Some(amount), ..Default::default() }, PaymentIntentUpdate::AuthorizationCountUpdate { authorization_count, } => Self { authorization_count: Some(authorization_count), ..Default::default() }, PaymentIntentUpdate::CompleteAuthorizeUpdate { shipping_address_id, } => Self { shipping_address_id, ..Default::default() }, PaymentIntentUpdate::ManualUpdate { status, updated_by } => Self { status, modified_at: Some(common_utils::date_time::now()), updated_by, ..Default::default() }, } } } use diesel_models::{ PaymentIntentUpdate as DieselPaymentIntentUpdate, PaymentIntentUpdateFields as DieselPaymentIntentUpdateFields, }; impl From for DieselPaymentIntentUpdate { fn from(value: PaymentIntentUpdate) -> Self { match value { PaymentIntentUpdate::ResponseUpdate { status, amount_captured, fingerprint_id, return_url, updated_by, incremental_authorization_allowed, } => Self::ResponseUpdate { status, amount_captured, fingerprint_id, return_url, updated_by, incremental_authorization_allowed, }, PaymentIntentUpdate::MetadataUpdate { metadata, updated_by, } => Self::MetadataUpdate { metadata, updated_by, }, PaymentIntentUpdate::Update(value) => { Self::Update(Box::new(DieselPaymentIntentUpdateFields { amount: value.amount, currency: value.currency, setup_future_usage: value.setup_future_usage, status: value.status, customer_id: value.customer_id, shipping_address_id: value.shipping_address_id, billing_address_id: value.billing_address_id, return_url: value.return_url, business_country: value.business_country, business_label: value.business_label, description: value.description, statement_descriptor_name: value.statement_descriptor_name, statement_descriptor_suffix: value.statement_descriptor_suffix, order_details: value.order_details, metadata: value.metadata, payment_confirm_source: value.payment_confirm_source, updated_by: value.updated_by, session_expiry: value.session_expiry, fingerprint_id: value.fingerprint_id, request_external_three_ds_authentication: value .request_external_three_ds_authentication, frm_metadata: value.frm_metadata, customer_details: value.customer_details.map(Encryption::from), billing_details: value.billing_details.map(Encryption::from), merchant_order_reference_id: value.merchant_order_reference_id, shipping_details: value.shipping_details.map(Encryption::from), is_payment_processor_token_flow: value.is_payment_processor_token_flow, })) } PaymentIntentUpdate::PaymentCreateUpdate { return_url, status, customer_id, shipping_address_id, billing_address_id, customer_details, updated_by, } => Self::PaymentCreateUpdate { return_url, status, customer_id, shipping_address_id, billing_address_id, customer_details: customer_details.map(Encryption::from), updated_by, }, PaymentIntentUpdate::MerchantStatusUpdate { status, shipping_address_id, billing_address_id, updated_by, } => Self::MerchantStatusUpdate { status, shipping_address_id, billing_address_id, updated_by, }, PaymentIntentUpdate::PGStatusUpdate { status, updated_by, incremental_authorization_allowed, } => Self::PGStatusUpdate { status, updated_by, incremental_authorization_allowed, }, PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { active_attempt_id, attempt_count, updated_by, } => Self::PaymentAttemptAndAttemptCountUpdate { active_attempt_id, attempt_count, updated_by, }, PaymentIntentUpdate::StatusAndAttemptUpdate { status, active_attempt_id, attempt_count, updated_by, } => Self::StatusAndAttemptUpdate { status, active_attempt_id, attempt_count, updated_by, }, PaymentIntentUpdate::ApproveUpdate { status, merchant_decision, updated_by, } => Self::ApproveUpdate { status, merchant_decision, updated_by, }, PaymentIntentUpdate::RejectUpdate { status, merchant_decision, updated_by, } => Self::RejectUpdate { status, merchant_decision, updated_by, }, PaymentIntentUpdate::SurchargeApplicableUpdate { surcharge_applicable, updated_by, } => Self::SurchargeApplicableUpdate { surcharge_applicable: Some(surcharge_applicable), updated_by, }, PaymentIntentUpdate::IncrementalAuthorizationAmountUpdate { amount } => { Self::IncrementalAuthorizationAmountUpdate { amount } } PaymentIntentUpdate::AuthorizationCountUpdate { authorization_count, } => Self::AuthorizationCountUpdate { authorization_count, }, PaymentIntentUpdate::CompleteAuthorizeUpdate { shipping_address_id, } => Self::CompleteAuthorizeUpdate { shipping_address_id, }, PaymentIntentUpdate::ManualUpdate { status, updated_by } => { Self::ManualUpdate { status, updated_by } } } } } impl From for diesel_models::PaymentIntentUpdateInternal { fn from(value: PaymentIntentUpdateInternal) -> Self { let modified_at = common_utils::date_time::now(); let PaymentIntentUpdateInternal { amount, currency, status, amount_captured, customer_id, return_url, setup_future_usage, off_session, metadata, billing_address_id, shipping_address_id, modified_at: _, active_attempt_id, business_country, business_label, description, statement_descriptor_name, statement_descriptor_suffix, order_details, attempt_count, merchant_decision, payment_confirm_source, updated_by, surcharge_applicable, incremental_authorization_allowed, authorization_count, session_expiry, fingerprint_id, request_external_three_ds_authentication, frm_metadata, customer_details, billing_details, merchant_order_reference_id, shipping_details, is_payment_processor_token_flow, } = value; Self { amount, currency, status, amount_captured, customer_id, return_url, setup_future_usage, off_session, metadata, billing_address_id, shipping_address_id, modified_at, active_attempt_id, business_country, business_label, description, statement_descriptor_name, statement_descriptor_suffix, order_details, attempt_count, merchant_decision, payment_confirm_source, updated_by, surcharge_applicable, incremental_authorization_allowed, authorization_count, session_expiry, fingerprint_id, request_external_three_ds_authentication, frm_metadata, customer_details: customer_details.map(Encryption::from), billing_details: billing_details.map(Encryption::from), merchant_order_reference_id, shipping_details: shipping_details.map(Encryption::from), is_payment_processor_token_flow, } } } pub enum PaymentIntentFetchConstraints { Single { payment_intent_id: String }, List(Box), } pub struct PaymentIntentListParams { pub offset: u32, pub starting_at: Option, pub ending_at: Option, pub amount_filter: Option, pub connector: Option>, pub currency: Option>, pub status: Option>, pub payment_method: Option>, pub payment_method_type: Option>, pub authentication_type: Option>, pub merchant_connector_id: Option>, pub profile_id: Option, pub customer_id: Option, pub starting_after_id: Option, pub ending_before_id: Option, pub limit: Option, pub order: api_models::payments::Order, } impl From for PaymentIntentFetchConstraints { fn from(value: api_models::payments::PaymentListConstraints) -> Self { Self::List(Box::new(PaymentIntentListParams { offset: 0, starting_at: value.created_gte.or(value.created_gt).or(value.created), ending_at: value.created_lte.or(value.created_lt).or(value.created), amount_filter: None, connector: None, currency: None, status: None, payment_method: None, payment_method_type: None, authentication_type: None, merchant_connector_id: None, profile_id: None, customer_id: value.customer_id, starting_after_id: value.starting_after, ending_before_id: value.ending_before, limit: Some(std::cmp::min(value.limit, PAYMENTS_LIST_MAX_LIMIT_V1)), order: Default::default(), })) } } impl From for PaymentIntentFetchConstraints { fn from(value: api_models::payments::TimeRange) -> Self { Self::List(Box::new(PaymentIntentListParams { offset: 0, starting_at: Some(value.start_time), ending_at: value.end_time, amount_filter: None, connector: None, currency: None, status: None, payment_method: None, payment_method_type: None, authentication_type: None, merchant_connector_id: None, profile_id: None, customer_id: None, starting_after_id: None, ending_before_id: None, limit: None, order: Default::default(), })) } } impl From for PaymentIntentFetchConstraints { fn from(value: api_models::payments::PaymentListFilterConstraints) -> Self { if let Some(payment_intent_id) = value.payment_id { Self::Single { payment_intent_id } } else { Self::List(Box::new(PaymentIntentListParams { offset: value.offset.unwrap_or_default(), starting_at: value.time_range.map(|t| t.start_time), ending_at: value.time_range.and_then(|t| t.end_time), amount_filter: value.amount_filter, connector: value.connector, currency: value.currency, status: value.status, payment_method: value.payment_method, payment_method_type: value.payment_method_type, authentication_type: value.authentication_type, merchant_connector_id: value.merchant_connector_id, profile_id: value.profile_id, customer_id: value.customer_id, starting_after_id: None, ending_before_id: None, limit: Some(std::cmp::min(value.limit, PAYMENTS_LIST_MAX_LIMIT_V2)), order: value.order, })) } } }