Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(pm): create new crate for payment methods #7355

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions crates/hyperswitch_domain_models/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,13 @@ pub enum StorageError {
#[error("RedisError: {0:?}")]
RedisError(String),
}

impl StorageError {
pub fn is_db_not_found(&self) -> bool {
match self {
Self::DatabaseError(err) => matches!(err.current_context(), DatabaseError::NotFound),
Self::ValueNotFound(_) => true,
_ => false,
}
}
}
176 changes: 169 additions & 7 deletions crates/hyperswitch_domain_models/src/payment_methods.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
#[cfg(feature = "v2")]
use api_models::payment_methods::PaymentMethodsData;
// specific imports because of using the macro
use common_enums::enums::MerchantStorageScheme;
#[cfg(feature = "v2")]
use common_utils::{
crypto::Encryptable, encryption::Encryption, ext_traits::ValueExt,
types::keymanager::ToEncryptable,
};
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
use common_utils::{crypto::Encryptable, encryption::Encryption, types::keymanager::ToEncryptable};
use common_utils::{
crypto::OptionalEncryptableValue,
errors::{CustomResult, ParsingError, ValidationError},
pii, type_name,
types::keymanager,
id_type, pii, type_name,
types::{keymanager, keymanager::KeyManagerState},
};
use diesel_models::enums as storage_enums;
use diesel_models::{enums as storage_enums, PaymentMethodUpdate};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
// specific imports because of using the macro
#[cfg(feature = "v2")]
use rustc_hash::FxHashMap;
#[cfg(feature = "v2")]
use rustc_hash::FxHashMap;
#[cfg(feature = "v2")]
use serde_json::Value;
#[cfg(feature = "v2")]
use serde_json::Value;
use time::PrimitiveDateTime;

#[cfg(feature = "v2")]
use crate::{
address::Address,
consts, router_response_types,
type_encryption::{crypto_operation, CryptoOperation},
};
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
use crate::{address::Address, type_encryption::OptionalEncryptableJsonType};
use crate::{
errors,
mandates::{CommonMandateReference, PaymentsMandateReference},
merchant_key_store::MerchantKeyStore,
type_encryption::{crypto_operation, AsyncLift, CryptoOperation},
};

Expand All @@ -45,8 +63,8 @@ impl VaultId {
))]
#[derive(Clone, Debug)]
pub struct PaymentMethod {
pub customer_id: common_utils::id_type::CustomerId,
pub merchant_id: common_utils::id_type::MerchantId,
pub customer_id: id_type::CustomerId,
pub merchant_id: id_type::MerchantId,
pub payment_method_id: String,
pub accepted_currency: Option<Vec<storage_enums::Currency>>,
pub scheme: Option<String>,
Expand Down Expand Up @@ -272,7 +290,7 @@ impl super::behaviour::Conversion for PaymentMethod {
}

async fn convert_back(
state: &keymanager::KeyManagerState,
state: &KeyManagerState,
item: Self::DstType,
key: &Secret<Vec<u8>>,
key_manager_identifier: keymanager::Identifier,
Expand Down Expand Up @@ -661,6 +679,150 @@ impl super::behaviour::Conversion for PaymentMethodsSession {
}
}

#[async_trait::async_trait]
pub trait PaymentMethodInterface {
#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
async fn find_payment_method(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
payment_method_id: &str,
storage_scheme: MerchantStorageScheme,
) -> CustomResult<PaymentMethod, errors::StorageError>;

#[cfg(all(feature = "v2", feature = "customer_v2"))]
async fn find_payment_method(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
payment_method_id: &id_type::GlobalPaymentMethodId,
storage_scheme: MerchantStorageScheme,
) -> CustomResult<PaymentMethod, errors::StorageError>;

#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
async fn find_payment_method_by_locker_id(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
locker_id: &str,
storage_scheme: MerchantStorageScheme,
) -> CustomResult<PaymentMethod, errors::StorageError>;

#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
async fn find_payment_method_by_customer_id_merchant_id_list(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
customer_id: &id_type::CustomerId,
merchant_id: &id_type::MerchantId,
limit: Option<i64>,
) -> CustomResult<Vec<PaymentMethod>, errors::StorageError>;

// Need to fix this once we start moving to v2 for payment method
#[cfg(all(feature = "v2", feature = "customer_v2"))]
async fn find_payment_method_list_by_global_customer_id(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
id: &id_type::GlobalCustomerId,
limit: Option<i64>,
) -> CustomResult<Vec<PaymentMethod>, errors::StorageError>;

#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
#[allow(clippy::too_many_arguments)]
async fn find_payment_method_by_customer_id_merchant_id_status(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
customer_id: &id_type::CustomerId,
merchant_id: &id_type::MerchantId,
status: common_enums::PaymentMethodStatus,
limit: Option<i64>,
storage_scheme: MerchantStorageScheme,
) -> CustomResult<Vec<PaymentMethod>, errors::StorageError>;

#[cfg(all(feature = "v2", feature = "customer_v2"))]
#[allow(clippy::too_many_arguments)]
async fn find_payment_method_by_global_customer_id_merchant_id_status(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
customer_id: &id_type::GlobalCustomerId,
merchant_id: &id_type::MerchantId,
status: common_enums::PaymentMethodStatus,
limit: Option<i64>,
storage_scheme: MerchantStorageScheme,
) -> CustomResult<Vec<PaymentMethod>, errors::StorageError>;

#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
async fn get_payment_method_count_by_customer_id_merchant_id_status(
&self,
customer_id: &id_type::CustomerId,
merchant_id: &id_type::MerchantId,
status: common_enums::PaymentMethodStatus,
) -> CustomResult<i64, errors::StorageError>;

async fn insert_payment_method(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
payment_method: PaymentMethod,
storage_scheme: MerchantStorageScheme,
) -> CustomResult<PaymentMethod, errors::StorageError>;

async fn update_payment_method(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
payment_method: PaymentMethod,
payment_method_update: PaymentMethodUpdate,
storage_scheme: MerchantStorageScheme,
) -> CustomResult<PaymentMethod, errors::StorageError>;

#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
async fn delete_payment_method(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
payment_method: PaymentMethod,
) -> CustomResult<PaymentMethod, errors::StorageError>;

#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
async fn find_payment_method_by_fingerprint_id(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
fingerprint_id: &str,
) -> CustomResult<PaymentMethod, errors::StorageError>;

#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
))]
async fn delete_payment_method_by_merchant_id_payment_method_id(
&self,
state: &KeyManagerState,
key_store: &MerchantKeyStore,
merchant_id: &id_type::MerchantId,
payment_method_id: &str,
) -> CustomResult<PaymentMethod, errors::StorageError>;
}

#[cfg(all(
any(feature = "v1", feature = "v2"),
not(feature = "payment_methods_v2")
Expand Down
24 changes: 24 additions & 0 deletions crates/payment_methods/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "payment_methods"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
license.workspace = true

[dependencies]
async-trait = "0.1.79"
dyn-clone = "1.0.17"

common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs", "metrics", "keymanager", "encryption_service"] }
hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false }
storage_impl = { version = "0.1.0", path = "../storage_impl", default-features = false }

[lints]
workspace = true

[features]
default = ["v1"]
v1 = ["hyperswitch_domain_models/v1", "storage_impl/v1", "common_utils/v1"]
v2 = [ "customer_v2", "payment_methods_v2"]
customer_v2 = [ "hyperswitch_domain_models/customer_v2", "storage_impl/customer_v2"]
payment_methods_v2 = [ "hyperswitch_domain_models/payment_methods_v2", "storage_impl/payment_methods_v2", "common_utils/payment_methods_v2"]
72 changes: 72 additions & 0 deletions crates/payment_methods/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use common_utils::{errors::CustomResult, id_type, types::keymanager::KeyManagerState};
use hyperswitch_domain_models::{
errors,
merchant_account::MerchantAccount,
merchant_key_store::MerchantKeyStore,
payment_methods::{PaymentMethod, PaymentMethodInterface},
};
use storage_impl::{kv_router_store::KVRouterStore, DatabaseStore, MockDb, RouterStore};

#[async_trait::async_trait]
pub trait PaymentMethodsStorageInterface:
Send + Sync + dyn_clone::DynClone + PaymentMethodInterface + 'static
{
}
dyn_clone::clone_trait_object!(PaymentMethodsStorageInterface);

#[async_trait::async_trait]
impl PaymentMethodsStorageInterface for MockDb {}

#[async_trait::async_trait]
impl<T: DatabaseStore + 'static> PaymentMethodsStorageInterface for RouterStore<T> {}

#[async_trait::async_trait]
impl<T: DatabaseStore + 'static> PaymentMethodsStorageInterface for KVRouterStore<T> {}

#[derive(Clone)]
pub struct PaymentMethodsClient {
pub state: Box<dyn PaymentMethodsStorageInterface>,
pub key_store: Option<MerchantKeyStore>,
pub customer_id: Option<id_type::CustomerId>,
pub merchant_id: Option<id_type::MerchantId>,
pub limit: Option<i64>,
pub key_manager_state: KeyManagerState,
}
impl From<&PaymentMethodsClient> for KeyManagerState {
fn from(state: &PaymentMethodsClient) -> Self {
state.key_manager_state.clone()
}
}
impl PaymentMethodsClient {
pub async fn find_payment_method(
&self,
key_store: &MerchantKeyStore,
merchant_account: &MerchantAccount,
payment_method_id: String,
) -> CustomResult<PaymentMethod, errors::StorageError> {
let db = &*self.state;
let key_manager_state = &(self.key_manager_state).clone();

match db
.find_payment_method(
key_manager_state,
key_store,
&payment_method_id,
merchant_account.storage_scheme,
)
.await
{
Err(err) if err.current_context().is_db_not_found() => {
db.find_payment_method_by_locker_id(
key_manager_state,
key_store,
&payment_method_id,
merchant_account.storage_scheme,
)
.await
}
Ok(pm) => Ok(pm),
Err(err) => Err(err),
}
}
}
1 change: 1 addition & 0 deletions crates/payment_methods/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod client;
1 change: 1 addition & 0 deletions crates/router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ router_derive = { version = "0.1.0", path = "../router_derive" }
router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
scheduler = { version = "0.1.0", path = "../scheduler", default-features = false }
storage_impl = { version = "0.1.0", path = "../storage_impl", default-features = false }
payment_methods = { version = "0.1.0", path = "../payment_methods", default-features = false }

[build-dependencies]
router_env = { version = "0.1.0", path = "../router_env", default-features = false }
Expand Down
Loading
Loading