From 77b6c06df2eb9aaf5137a434c3368f56aa1a08de Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Wed, 11 Dec 2024 18:19:32 +0530 Subject: [PATCH 01/29] feat(v2): Add ephemeral auth for v2 --- crates/api_models/src/events/customer.rs | 13 +++- crates/api_models/src/payment_methods.rs | 2 +- crates/diesel_models/src/ephemeral_key.rs | 39 ++++++++++++ .../hyperswitch_domain_models/src/payments.rs | 6 +- crates/router/src/core/payment_methods.rs | 43 ++++++++------ .../src/core/payment_methods/transformers.rs | 3 +- crates/router/src/core/payments/helpers.rs | 59 +++++++++++++++++++ crates/router/src/lib.rs | 1 + crates/router/src/routes/ephemeral_key.rs | 34 +++++++++-- crates/router/src/routes/payment_methods.rs | 25 ++++++-- crates/router/src/services/authentication.rs | 41 ++++++++++++- .../router/src/types/storage/ephemeral_key.rs | 2 +- 12 files changed, 235 insertions(+), 33 deletions(-) diff --git a/crates/api_models/src/events/customer.rs b/crates/api_models/src/events/customer.rs index d2b30bcf0100..7f0f11727749 100644 --- a/crates/api_models/src/events/customer.rs +++ b/crates/api_models/src/events/customer.rs @@ -1,6 +1,5 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use crate::customers::CustomerId; #[cfg(all(feature = "v2", feature = "customer_v2"))] use crate::customers::GlobalId; @@ -80,6 +79,18 @@ impl ApiEventMetric for GlobalId { } } +#[cfg(all(feature = "v2", feature = "customer_v2"))] +impl ApiEventMetric for CustomerId { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Customer { + id: self + .get_merchant_reference_id() + .get_string_repr() + .to_string(), + }) + } +} + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] impl ApiEventMetric for CustomerUpdateRequest { fn get_api_event_type(&self) -> Option { diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 283a0d662d71..19c90beda5a2 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -811,7 +811,7 @@ pub struct PaymentMethodResponse { pub last_used_at: Option, /// For Client based calls - pub client_secret: Option, + pub ephemeral_key: Option, pub payment_method_data: Option, } diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index d398ecdf784a..d65de4285d23 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -1,10 +1,33 @@ +#[cfg(feature = "v2")] pub struct EphemeralKeyNew { pub id: String, pub merchant_id: common_utils::id_type::MerchantId, pub customer_id: common_utils::id_type::CustomerId, pub secret: String, + pub resource_type: ResourceType, } +#[cfg(feature = "v2")] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct EphemeralKey { + pub id: String, + pub merchant_id: common_utils::id_type::MerchantId, + pub customer_id: common_utils::id_type::CustomerId, + pub resource_type: ResourceType, + pub created_at: i64, + pub expires: i64, + pub secret: String, +} + +#[cfg(feature = "v1")] +pub struct EphemeralKeyNew { + pub id: String, + pub merchant_id: common_utils::id_type::MerchantId, + pub customer_id: common_utils::id_type::CustomerId, + pub secret: String, +} + +#[cfg(feature = "v1")] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct EphemeralKey { pub id: String, @@ -20,3 +43,19 @@ impl common_utils::events::ApiEventMetric for EphemeralKey { Some(common_utils::events::ApiEventsType::Miscellaneous) } } + +#[derive( + Clone, + Copy, + Debug, + serde::Serialize, + serde::Deserialize, + strum::Display, + strum::EnumString, + PartialEq, + Eq, +)] +pub enum ResourceType { + Payment, + PaymentMethod, +} diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 9e6477fa26ba..a80db1d6bb7f 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -29,7 +29,10 @@ pub mod payment_intent; use common_enums as storage_enums; #[cfg(feature = "v2")] -use diesel_models::types::{FeatureMetadata, OrderDetailsWithAmount}; +use diesel_models::{ + ephemeral_key, + types::{FeatureMetadata, OrderDetailsWithAmount}, +}; use self::payment_attempt::PaymentAttempt; #[cfg(feature = "v1")] @@ -536,6 +539,7 @@ pub struct HeaderPayload { pub x_app_id: Option, pub x_redirect_uri: Option, pub client_secret: Option, + pub resource_type: Option, } impl HeaderPayload { diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 53ec01298967..1a4ec40b0dae 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -58,7 +58,7 @@ use crate::{ types::{ api::{self, payment_methods::PaymentMethodCreateExt}, payment_methods as pm_types, - storage::PaymentMethodListContext, + storage::{ephemeral_key, PaymentMethodListContext}, }, utils::ext_traits::OptionExt, }; @@ -871,7 +871,7 @@ pub async fn create_payment_method( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to generate GlobalPaymentMethodId")?; - let payment_method = create_payment_method_for_intent( + let (payment_method, ephemeral_key) = create_payment_method_for_intent( state, req.metadata.clone(), &customer_id, @@ -920,7 +920,10 @@ pub async fn create_payment_method( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update payment method in db")?; - let resp = pm_transforms::generate_payment_method_response(&payment_method)?; + let resp = pm_transforms::generate_payment_method_response( + &payment_method, + Some(ephemeral_key), + )?; Ok(resp) } @@ -985,7 +988,7 @@ pub async fn payment_method_intent_create( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to generate GlobalPaymentMethodId")?; - let payment_method = create_payment_method_for_intent( + let (payment_method, ephemeral_key) = create_payment_method_for_intent( state, req.metadata.clone(), &customer_id, @@ -998,7 +1001,8 @@ pub async fn payment_method_intent_create( .await .attach_printable("Failed to add Payment method to DB")?; - let resp = pm_transforms::generate_payment_method_response(&payment_method)?; + let resp = + pm_transforms::generate_payment_method_response(&payment_method, Some(ephemeral_key))?; Ok(services::ApplicationResponse::Json(resp)) } @@ -1015,7 +1019,6 @@ pub async fn payment_method_intent_confirm( req.validate()?; let db = &*state.store; - let client_secret = req.client_secret.clone(); let pm_id = id_type::GlobalPaymentMethodId::generate_from_string(pm_id) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to generate GlobalPaymentMethodId")?; @@ -1031,11 +1034,6 @@ pub async fn payment_method_intent_confirm( .change_context(errors::ApiErrorResponse::PaymentMethodNotFound) .attach_printable("Unable to find payment method")?; - when( - cards::authenticate_pm_client_secret_and_check_expiry(&client_secret, &payment_method)?, - || Err(errors::ApiErrorResponse::ClientSecretExpired), - )?; - when( payment_method.status != enums::PaymentMethodStatus::AwaitingData, || { @@ -1093,7 +1091,7 @@ pub async fn payment_method_intent_confirm( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update payment method in db")?; - let resp = pm_transforms::generate_payment_method_response(&payment_method)?; + let resp = pm_transforms::generate_payment_method_response(&payment_method, None)?; Ok(resp) } @@ -1199,9 +1197,18 @@ pub async fn create_payment_method_for_intent( key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, payment_method_billing_address: crypto::OptionalEncryptableValue, -) -> errors::CustomResult { +) -> errors::CustomResult<(domain::PaymentMethod, String), errors::ApiErrorResponse> { let db = &*state.store; - let client_secret = pm_types::PaymentMethodClientSecret::generate(&payment_method_id); + let ephemeral_key = payment_helpers::create_ephemeral_key( + state, + customer_id, + merchant_id, + ephemeral_key::ResourceType::PaymentMethod, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to create ephemeral_key")?; + let current_time = common_utils::date_time::now(); let response = db @@ -1218,7 +1225,7 @@ pub async fn create_payment_method_for_intent( payment_method_data: None, connector_mandate_details: None, customer_acceptance: None, - client_secret: Some(client_secret), + client_secret: None, status: enums::PaymentMethodStatus::AwaitingData, network_transaction_id: None, created_at: current_time, @@ -1238,7 +1245,7 @@ pub async fn create_payment_method_for_intent( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to add payment method in db")?; - Ok(response) + Ok((response, ephemeral_key.secret)) } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] @@ -1768,7 +1775,7 @@ pub async fn retrieve_payment_method( created: Some(payment_method.created_at), recurring_enabled: false, last_used_at: Some(payment_method.last_used_at), - client_secret: payment_method.client_secret.clone(), + ephemeral_key: None, payment_method_data: pmd, }; @@ -1858,7 +1865,7 @@ pub async fn update_payment_method( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update payment method in db")?; - let response = pm_transforms::generate_payment_method_response(&payment_method)?; + let response = pm_transforms::generate_payment_method_response(&payment_method, None)?; // Add a PT task to handle payment_method delete from vault diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index c3fbfd8afbff..c590d00c490c 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -551,6 +551,7 @@ pub fn generate_pm_vaulting_req_from_update_request( #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub fn generate_payment_method_response( pm: &domain::PaymentMethod, + ephemeral_key: Option, ) -> errors::RouterResult { let pmd = pm .payment_method_data @@ -572,7 +573,7 @@ pub fn generate_payment_method_response( created: Some(pm.created_at), recurring_enabled: false, last_used_at: Some(pm.last_used_at), - client_secret: pm.client_secret.clone(), + ephemeral_key, payment_method_data: pmd, }; diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 6d40593060dd..afbfc949155c 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2995,6 +2995,7 @@ pub fn make_merchant_url_with_response( Ok(merchant_url_with_response.to_string()) } +#[cfg(feature = "v1")] pub async fn make_ephemeral_key( state: SessionState, customer_id: id_type::CustomerId, @@ -3017,6 +3018,64 @@ pub async fn make_ephemeral_key( Ok(services::ApplicationResponse::Json(ek)) } +#[cfg(feature = "v2")] +pub async fn make_ephemeral_key( + state: SessionState, + customer_id: id_type::CustomerId, + merchant_id: id_type::MerchantId, + headers: &actix_web::http::header::HeaderMap, +) -> errors::RouterResponse { + let store = &state.store; + let id = utils::generate_id(consts::ID_LENGTH, "eki"); + let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); + let resource_type = services::authentication::get_header_value_by_key( + crate::headers::X_RESOURCE_TYPE.to_string(), + headers, + )? + .map(ephemeral_key::ResourceType::from_str) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError)? + .get_required_value("ResourceType")?; + let ek = ephemeral_key::EphemeralKeyNew { + id, + customer_id: customer_id.to_owned(), + merchant_id: merchant_id.to_owned(), + secret, + resource_type, + }; + let ek = store + .create_ephemeral_key(ek, state.conf.eph_key.validity) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to create ephemeral key")?; + Ok(services::ApplicationResponse::Json(ek)) +} + +#[cfg(feature = "v2")] +pub async fn create_ephemeral_key( + state: &SessionState, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + resource_type: ephemeral_key::ResourceType, +) -> RouterResult { + let store = &state.store; + let id = utils::generate_id(consts::ID_LENGTH, "eki"); + let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); + let ek = ephemeral_key::EphemeralKeyNew { + id, + customer_id: customer_id.to_owned(), + merchant_id: merchant_id.to_owned(), + secret, + resource_type, + }; + let ek = store + .create_ephemeral_key(ek, state.conf.eph_key.validity) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to create ephemeral key")?; + Ok(ek) +} + pub async fn delete_ephemeral_key( state: SessionState, ek_id: String, diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 839dd472423a..27b987c0a9d4 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -89,6 +89,7 @@ pub mod headers { pub const X_REDIRECT_URI: &str = "x-redirect-uri"; pub const X_TENANT_ID: &str = "x-tenant-id"; pub const X_CLIENT_SECRET: &str = "X-Client-Secret"; + pub const X_RESOURCE_TYPE: &str = "X-Resource-Type"; } pub mod pii { diff --git a/crates/router/src/routes/ephemeral_key.rs b/crates/router/src/routes/ephemeral_key.rs index dd11bee24bdf..19595b218915 100644 --- a/crates/router/src/routes/ephemeral_key.rs +++ b/crates/router/src/routes/ephemeral_key.rs @@ -1,11 +1,9 @@ -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use actix_web::{web, HttpRequest, HttpResponse}; -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] + use router_env::{instrument, tracing, Flow}; -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use super::AppState; -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] + use crate::{ core::{api_locking, payments::helpers}, services::{api, authentication as auth}, @@ -39,6 +37,34 @@ pub async fn ephemeral_key_create( .await } +#[cfg(feature = "v2")] +#[instrument(skip_all, fields(flow = ?Flow::EphemeralKeyCreate))] +pub async fn ephemeral_key_create( + state: web::Data, + req: HttpRequest, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::EphemeralKeyCreate; + let payload = json_payload.into_inner(); + api::server_wrap( + flow, + state, + &req, + payload, + |state, auth: auth::AuthenticationData, payload, _| { + helpers::make_ephemeral_key( + state, + payload.get_merchant_reference_id().clone(), + auth.merchant_account.get_id().to_owned(), + req.headers(), + ) + }, + &auth::HeaderAuth(auth::ApiKeyAuth), + api_locking::LockAction::NotApplicable, + ) + .await +} + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all, fields(flow = ?Flow::EphemeralKeyDelete))] pub async fn ephemeral_key_delete( diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 8ee31ecf943a..00ef7a1acec5 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -141,8 +141,8 @@ pub async fn confirm_payment_method_intent_api( let pm_id = path.into_inner(); let payload = json_payload.into_inner(); - let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) { - Ok((auth, _auth_flow)) => (auth, _auth_flow), + let auth = match auth::is_ephemeral_auth(req.headers()) { + Ok(auth) => auth, Err(e) => return api::log_and_return_error_response(e), }; @@ -191,6 +191,11 @@ pub async fn payment_method_update_api( let payment_method_id = path.into_inner(); let payload = json_payload.into_inner(); + let auth = match auth::is_ephemeral_auth(req.headers()) { + Ok(auth) => auth, + Err(e) => return api::log_and_return_error_response(e), + }; + Box::pin(api::server_wrap( flow, state, @@ -205,7 +210,7 @@ pub async fn payment_method_update_api( auth.key_store, ) }, - &auth::HeaderAuth(auth::ApiKeyAuth), + &*auth, api_locking::LockAction::NotApplicable, )) .await @@ -224,6 +229,11 @@ pub async fn payment_method_retrieve_api( }) .into_inner(); + let auth = match auth::is_ephemeral_auth(req.headers()) { + Ok(auth) => auth, + Err(e) => return api::log_and_return_error_response(e), + }; + Box::pin(api::server_wrap( flow, state, @@ -232,7 +242,7 @@ pub async fn payment_method_retrieve_api( |state, auth: auth::AuthenticationData, pm, _| { retrieve_payment_method(state, pm, auth.key_store, auth.merchant_account) }, - &auth::HeaderAuth(auth::ApiKeyAuth), + &*auth, api_locking::LockAction::NotApplicable, )) .await @@ -251,6 +261,11 @@ pub async fn payment_method_delete_api( }) .into_inner(); + let auth = match auth::is_ephemeral_auth(req.headers()) { + Ok(auth) => auth, + Err(e) => return api::log_and_return_error_response(e), + }; + Box::pin(api::server_wrap( flow, state, @@ -259,7 +274,7 @@ pub async fn payment_method_delete_api( |state, auth: auth::AuthenticationData, pm, _| { delete_payment_method(state, pm, auth.key_store, auth.merchant_account) }, - &auth::HeaderAuth(auth::ApiKeyAuth), + &*auth, api_locking::LockAction::NotApplicable, )) .await diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index f465719949aa..2417dc7f9fd5 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -11,12 +11,14 @@ use api_models::payouts; use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; use common_enums::TokenPurpose; -use common_utils::{date_time, id_type}; +use common_utils::{date_time, fp_utils, id_type}; +use diesel_models::ephemeral_key; use error_stack::{report, ResultExt}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::PeekInterface; use router_env::logger; use serde::Serialize; +use std::str::FromStr; use self::blacklist::BlackList; #[cfg(all(feature = "partial-auth", feature = "v1"))] @@ -1329,6 +1331,32 @@ where #[derive(Debug)] pub struct EphemeralKeyAuth; +#[cfg(feature = "v1")] +#[async_trait] +impl AuthenticateAndFetch for EphemeralKeyAuth +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + let api_key = + get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?; + let ephemeral_key = state + .store() + .get_ephemeral_key(api_key) + .await + .change_context(errors::ApiErrorResponse::Unauthorized)?; + + MerchantIdAuth(ephemeral_key.merchant_id) + .authenticate_and_fetch(request_headers, state) + .await + } +} + +#[cfg(feature = "v2")] #[async_trait] impl AuthenticateAndFetch for EphemeralKeyAuth where @@ -1347,6 +1375,17 @@ where .await .change_context(errors::ApiErrorResponse::Unauthorized)?; + let resource_type = + get_header_value_by_key(headers::X_RESOURCE_TYPE.to_string(), request_headers)? + .map(ephemeral_key::ResourceType::from_str) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError)? + .get_required_value("ResourceType")?; + + fp_utils::when(resource_type != ephemeral_key.resource_type, || { + Err(errors::ApiErrorResponse::Unauthorized) + })?; + MerchantIdAuth(ephemeral_key.merchant_id) .authenticate_and_fetch(request_headers, state) .await diff --git a/crates/router/src/types/storage/ephemeral_key.rs b/crates/router/src/types/storage/ephemeral_key.rs index 9927e16b3fad..b609a535de88 100644 --- a/crates/router/src/types/storage/ephemeral_key.rs +++ b/crates/router/src/types/storage/ephemeral_key.rs @@ -1 +1 @@ -pub use diesel_models::ephemeral_key::{EphemeralKey, EphemeralKeyNew}; +pub use diesel_models::ephemeral_key::{EphemeralKey, EphemeralKeyNew, ResourceType}; From 4927db38d8eafcdcb581166d68f25ed83bad1943 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Wed, 11 Dec 2024 22:44:08 +0530 Subject: [PATCH 02/29] refactor: Refactored pm v2 code --- crates/diesel_models/src/ephemeral_key.rs | 2 + crates/diesel_models/src/payment_method.rs | 11 ++ .../hyperswitch_domain_models/src/payments.rs | 1 - crates/router/src/consts.rs | 8 +- crates/router/src/core/payment_methods.rs | 124 ++++++++++-------- .../router/src/core/payment_methods/vault.rs | 9 +- crates/router/src/db/ephemeral_key.rs | 84 ++++++++++++ crates/router/src/lib.rs | 8 +- crates/router/src/routes/app.rs | 23 +++- crates/router/src/routes/ephemeral_key.rs | 1 - crates/router/src/services/authentication.rs | 4 +- crates/router/src/types/payment_methods.rs | 12 +- 12 files changed, 204 insertions(+), 83 deletions(-) diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index d65de4285d23..ac95533993fe 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -55,6 +55,8 @@ impl common_utils::events::ApiEventMetric for EphemeralKey { PartialEq, Eq, )] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] pub enum ResourceType { Payment, PaymentMethod, diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 206ba63e17fc..ffd282c5aa6a 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -288,6 +288,7 @@ pub enum PaymentMethodUpdate { network_token_requestor_reference_id: Option, network_token_locker_id: Option, network_token_payment_method_data: Option, + locker_fingerprint_id: Option, }, ConnectorMandateDetailsUpdate { connector_mandate_details: Option, @@ -322,6 +323,7 @@ pub struct PaymentMethodUpdateInternal { network_token_requestor_reference_id: Option, network_token_locker_id: Option, network_token_payment_method_data: Option, + locker_fingerprint_id: Option, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] @@ -341,6 +343,7 @@ impl PaymentMethodUpdateInternal { network_token_requestor_reference_id, network_token_locker_id, network_token_payment_method_data, + locker_fingerprint_id, } = self; PaymentMethod { @@ -695,6 +698,7 @@ impl From for PaymentMethodUpdateInternal { network_token_locker_id: None, network_token_requestor_reference_id: None, network_token_payment_method_data: None, + locker_fingerprint_id: None, }, PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self { payment_method_data: None, @@ -710,6 +714,7 @@ impl From for PaymentMethodUpdateInternal { network_token_locker_id: None, network_token_requestor_reference_id: None, network_token_payment_method_data: None, + locker_fingerprint_id: None, }, PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed { payment_method_data, @@ -728,6 +733,7 @@ impl From for PaymentMethodUpdateInternal { network_token_locker_id: None, network_token_requestor_reference_id: None, network_token_payment_method_data: None, + locker_fingerprint_id: None, }, PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate { network_transaction_id, @@ -746,6 +752,7 @@ impl From for PaymentMethodUpdateInternal { network_token_locker_id: None, network_token_requestor_reference_id: None, network_token_payment_method_data: None, + locker_fingerprint_id: None, }, PaymentMethodUpdate::StatusUpdate { status } => Self { payment_method_data: None, @@ -761,6 +768,7 @@ impl From for PaymentMethodUpdateInternal { network_token_locker_id: None, network_token_requestor_reference_id: None, network_token_payment_method_data: None, + locker_fingerprint_id: None, }, PaymentMethodUpdate::AdditionalDataUpdate { payment_method_data, @@ -771,6 +779,7 @@ impl From for PaymentMethodUpdateInternal { network_token_requestor_reference_id, network_token_locker_id, network_token_payment_method_data, + locker_fingerprint_id, } => Self { payment_method_data, last_used_at: None, @@ -785,6 +794,7 @@ impl From for PaymentMethodUpdateInternal { network_token_requestor_reference_id, network_token_locker_id, network_token_payment_method_data, + locker_fingerprint_id, }, PaymentMethodUpdate::ConnectorMandateDetailsUpdate { connector_mandate_details, @@ -802,6 +812,7 @@ impl From for PaymentMethodUpdateInternal { network_token_locker_id: None, network_token_requestor_reference_id: None, network_token_payment_method_data: None, + locker_fingerprint_id: None, }, } } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index a80db1d6bb7f..4d9320f0d30c 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -539,7 +539,6 @@ pub struct HeaderPayload { pub x_app_id: Option, pub x_redirect_uri: Option, pub client_secret: Option, - pub resource_type: Option, } impl HeaderPayload { diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index bf78424c9d24..a10fb4b30bf5 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -175,19 +175,19 @@ pub const DEFAULT_SDK_LAYOUT: &str = "tabs"; /// Vault Add request url #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -pub const ADD_VAULT_REQUEST_URL: &str = "/vault/add"; +pub const ADD_VAULT_REQUEST_URL: &str = "/api/v2/vault/add"; /// Vault Get Fingerprint request url #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -pub const VAULT_FINGERPRINT_REQUEST_URL: &str = "/fingerprint"; +pub const VAULT_FINGERPRINT_REQUEST_URL: &str = "/api/v2/vault/fingerprint"; /// Vault Retrieve request url #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -pub const VAULT_RETRIEVE_REQUEST_URL: &str = "/vault/retrieve"; +pub const VAULT_RETRIEVE_REQUEST_URL: &str = "/api/v2/vault/retrieve"; /// Vault Delete request url #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -pub const VAULT_DELETE_REQUEST_URL: &str = "/vault/delete"; +pub const VAULT_DELETE_REQUEST_URL: &str = "/api/v2/vault/delete"; /// Vault Header content type #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 1a4ec40b0dae..72f865889a56 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -845,17 +845,20 @@ pub async fn create_payment_method( let db = &*state.store; let merchant_id = merchant_account.get_id(); let customer_id = req.customer_id.to_owned(); + let key_manager_state = &(state).into(); + + let _customer = db + .find_customer_by_global_id( + key_manager_state, + customer_id.get_string_repr(), + merchant_account.get_id(), + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Customer not found for the payment method")?; - db.find_customer_by_merchant_reference_id_merchant_id( - &(state.into()), - &customer_id, - merchant_account.get_id(), - key_store, - merchant_account.storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; - let key_manager_state = state.into(); let payment_method_billing_address: Option>> = req .billing .clone() @@ -896,14 +899,15 @@ pub async fn create_payment_method( .await; let response = match vaulting_result { - Ok(resp) => { + Ok((vaulting_resp, fingerprint_id)) => { let pm_update = create_pm_additional_data_update( &payment_method_data, state, key_store, - Some(resp.vault_id.get_string_repr().clone()), + Some(vaulting_resp.vault_id.get_string_repr().clone()), Some(req.payment_method_type), Some(req.payment_method_subtype), + Some(fingerprint_id), ) .await .attach_printable("Unable to create Payment method data")?; @@ -961,17 +965,20 @@ pub async fn payment_method_intent_create( let db = &*state.store; let merchant_id = merchant_account.get_id(); let customer_id = req.customer_id.to_owned(); + let key_manager_state = &(state).into(); + + let _customer = db + .find_customer_by_global_id( + key_manager_state, + customer_id.get_string_repr(), + merchant_account.get_id(), + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Customer not found for the payment method")?; - db.find_customer_by_merchant_reference_id_merchant_id( - &(state.into()), - &customer_id, - merchant_account.get_id(), - key_store, - merchant_account.storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; - let key_manager_state = state.into(); let payment_method_billing_address: Option>> = req .billing .clone() @@ -1016,6 +1023,7 @@ pub async fn payment_method_intent_confirm( key_store: &domain::MerchantKeyStore, pm_id: String, ) -> RouterResponse { + let key_manager_state = &(state).into(); req.validate()?; let db = &*state.store; @@ -1045,15 +1053,17 @@ pub async fn payment_method_intent_confirm( )?; let customer_id = payment_method.customer_id.to_owned(); - db.find_customer_by_merchant_reference_id_merchant_id( - &(state.into()), - &customer_id, - merchant_account.get_id(), - key_store, - merchant_account.storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; + let customer = db + .find_customer_by_global_id( + key_manager_state, + customer_id.get_string_repr(), + merchant_account.get_id(), + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Customer not found for the payment method")?; let payment_method_data = pm_types::PaymentMethodVaultingData::from(req.payment_method_data); @@ -1067,14 +1077,15 @@ pub async fn payment_method_intent_confirm( .await; let response = match vaulting_result { - Ok(resp) => { + Ok((vaulting_resp, fingerprint_id)) => { let pm_update = create_pm_additional_data_update( &payment_method_data, state, key_store, - Some(resp.vault_id.get_string_repr().clone()), + Some(vaulting_resp.vault_id.get_string_repr().clone()), Some(req.payment_method_type), Some(req.payment_method_subtype), + Some(fingerprint_id), ) .await .attach_printable("Unable to create Payment method data")?; @@ -1145,7 +1156,6 @@ pub async fn create_payment_method_in_db( card_scheme: Option, ) -> errors::CustomResult { let db = &*state.store; - let client_secret = pm_types::PaymentMethodClientSecret::generate(&payment_method_id); let current_time = common_utils::date_time::now(); let response = db @@ -1162,7 +1172,7 @@ pub async fn create_payment_method_in_db( payment_method_data, connector_mandate_details, customer_acceptance, - client_secret: Some(client_secret), + client_secret: None, status: status.unwrap_or(enums::PaymentMethodStatus::Active), network_transaction_id: network_transaction_id.to_owned(), created_at: current_time, @@ -1256,13 +1266,14 @@ pub async fn create_pm_additional_data_update( vault_id: Option, payment_method_type: Option, payment_method_subtype: Option, + vault_fingerprint_id: Option, ) -> RouterResult { let card = match pmd { pm_types::PaymentMethodVaultingData::Card(card) => { api::PaymentMethodsData::Card(card.clone().into()) } }; - let key_manager_state = state.into(); + let key_manager_state = &(state).into(); let pmd: Encryptable> = cards::create_encrypted_data(&key_manager_state, key_store, card) .await @@ -1278,6 +1289,7 @@ pub async fn create_pm_additional_data_update( network_token_requestor_reference_id: None, network_token_locker_id: None, network_token_payment_method_data: None, + locker_fingerprint_id: vault_fingerprint_id, }; Ok(pm_update) @@ -1291,7 +1303,7 @@ pub async fn vault_payment_method( merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, existing_vault_id: Option, -) -> RouterResult { +) -> RouterResult<(pm_types::AddVaultResponse, String)> { let db = &*state.store; // get fingerprint_id from vault @@ -1302,16 +1314,15 @@ pub async fn vault_payment_method( // throw back error if payment method is duplicated when( - Some( - db.find_payment_method_by_fingerprint_id( - &(state.into()), - key_store, - &fingerprint_id_from_vault, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to find payment method by fingerprint_id")?, + db.find_payment_method_by_fingerprint_id( + &(state.into()), + key_store, + &fingerprint_id_from_vault, ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to find payment method by fingerprint_id") + .ok() .is_some(), || { Err(report!(errors::ApiErrorResponse::DuplicatePaymentMethod) @@ -1325,7 +1336,7 @@ pub async fn vault_payment_method( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to add payment method in vault")?; - Ok(resp_from_vault) + Ok((resp_from_vault, fingerprint_id_from_vault)) } #[cfg(all( @@ -1823,16 +1834,12 @@ pub async fn update_payment_method( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to retrieve payment method from vault")? - .data - .expose() - .parse_struct("PaymentMethodVaultingData") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to parse PaymentMethodVaultingData")?; + .data; let vault_request_data = pm_transforms::generate_pm_vaulting_req_from_update_request(pmd, req.payment_method_data); - let vaulting_response = vault_payment_method( + let (vaulting_response, fingerprint_id) = vault_payment_method( &state, &vault_request_data, &merchant_account, @@ -1849,6 +1856,7 @@ pub async fn update_payment_method( Some(vaulting_response.vault_id.get_string_repr().clone()), payment_method.get_payment_method_type(), payment_method.get_payment_method_subtype(), + Some(fingerprint_id), ) .await .attach_printable("Unable to create Payment method data")?; @@ -1897,6 +1905,16 @@ pub async fn delete_payment_method( .await .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?; + when( + payment_method.status == enums::PaymentMethodStatus::Inactive, + || { + Err(errors::ApiErrorResponse::PreconditionFailed { + message: "This Payment method is already inactive and hence cannot be deleted" + .to_string(), + }) + }, + )?; + let vault_id = payment_method .locker_id .clone() diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs index e4f72c717f6c..8dfa54496158 100644 --- a/crates/router/src/core/payment_methods/vault.rs +++ b/crates/router/src/core/payment_methods/vault.rs @@ -1184,6 +1184,7 @@ async fn create_vault_request( jwekey: &settings::Jwekey, locker: &settings::Locker, payload: Vec, + tenant_id: id_type::TenantId, ) -> CustomResult { let private_key = jwekey.vault_private_key.peek().as_bytes(); @@ -1204,6 +1205,10 @@ async fn create_vault_request( headers::CONTENT_TYPE, consts::VAULT_HEADER_CONTENT_TYPE.into(), ); + request.add_header( + headers::X_TENANT_ID, + tenant_id.get_string_repr().to_owned().into(), + ); request.set_body(request::RequestContent::Json(Box::new(jwe_payload))); Ok(request) } @@ -1217,7 +1222,9 @@ pub async fn call_to_vault( let locker = &state.conf.locker; let jwekey = state.conf.jwekey.get_inner(); - let request = create_vault_request::(jwekey, locker, payload).await?; + let request = + create_vault_request::(jwekey, locker, payload, state.tenant.tenant_id.to_owned()) + .await?; let response = services::call_connector_api(state, request, V::get_vaulting_flow_name()) .await .change_context(errors::VaultError::VaultAPIError); diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index a1d43e83b871..455c0cc6f518 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -32,6 +32,8 @@ mod storage { use time::ext::NumericalDuration; use super::EphemeralKeyInterface; + #[cfg(feature = "v2")] + use crate::types::storage::ephemeral_key::ResourceType; use crate::{ core::errors::{self, CustomResult}, services::Store, @@ -40,6 +42,7 @@ mod storage { #[async_trait::async_trait] impl EphemeralKeyInterface for Store { + #[cfg(feature = "v1")] #[instrument(skip_all)] async fn create_ephemeral_key( &self, @@ -94,6 +97,63 @@ mod storage { Err(er) => Err(er).change_context(errors::StorageError::KVError), } } + + #[cfg(feature = "v2")] + #[instrument(skip_all)] + async fn create_ephemeral_key( + &self, + new: EphemeralKeyNew, + validity: i64, + ) -> CustomResult { + let secret_key = format!("epkey_{}", &new.secret); + let id_key = format!("epkey_{}", &new.id); + + let created_at = date_time::now(); + let expires = created_at.saturating_add(validity.hours()); + let created_ek = EphemeralKey { + id: new.id, + created_at: created_at.assume_utc().unix_timestamp(), + expires: expires.assume_utc().unix_timestamp(), + customer_id: new.customer_id, + merchant_id: new.merchant_id, + secret: new.secret, + resource_type: new.resource_type, + }; + + match self + .get_redis_conn() + .map_err(Into::::into)? + .serialize_and_set_multiple_hash_field_if_not_exist( + &[(&secret_key, &created_ek), (&id_key, &created_ek)], + "ephkey", + None, + ) + .await + { + Ok(v) if v.contains(&HsetnxReply::KeyNotSet) => { + Err(errors::StorageError::DuplicateValue { + entity: "ephemeral key", + key: None, + } + .into()) + } + Ok(_) => { + let expire_at = expires.assume_utc().unix_timestamp(); + self.get_redis_conn() + .map_err(Into::::into)? + .set_expire_at(&secret_key, expire_at) + .await + .change_context(errors::StorageError::KVError)?; + self.get_redis_conn() + .map_err(Into::::into)? + .set_expire_at(&id_key, expire_at) + .await + .change_context(errors::StorageError::KVError)?; + Ok(created_ek) + } + Err(er) => Err(er).change_context(errors::StorageError::KVError), + } + } #[instrument(skip_all)] async fn get_ephemeral_key( &self, @@ -130,6 +190,29 @@ mod storage { #[async_trait::async_trait] impl EphemeralKeyInterface for MockDb { + #[cfg(feature = "v1")] + async fn create_ephemeral_key( + &self, + ek: EphemeralKeyNew, + validity: i64, + ) -> CustomResult { + let mut ephemeral_keys = self.ephemeral_keys.lock().await; + let created_at = common_utils::date_time::now(); + let expires = created_at.saturating_add(validity.hours()); + + let ephemeral_key = EphemeralKey { + id: ek.id, + merchant_id: ek.merchant_id, + customer_id: ek.customer_id, + created_at: created_at.assume_utc().unix_timestamp(), + expires: expires.assume_utc().unix_timestamp(), + secret: ek.secret, + }; + ephemeral_keys.push(ephemeral_key.clone()); + Ok(ephemeral_key) + } + + #[cfg(feature = "v2")] async fn create_ephemeral_key( &self, ek: EphemeralKeyNew, @@ -146,6 +229,7 @@ impl EphemeralKeyInterface for MockDb { created_at: created_at.assume_utc().unix_timestamp(), expires: expires.assume_utc().unix_timestamp(), secret: ek.secret, + resource_type: ek.resource_type, }; ephemeral_keys.push(ephemeral_key.clone()); Ok(ephemeral_key) diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 27b987c0a9d4..924803319818 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -155,15 +155,17 @@ pub fn mk_app( } } + #[cfg(all(feature = "oltp", any(feature = "v1", feature = "v2"),))] + { + server_app = server_app.service(routes::EphemeralKey::server(state.clone())) + } #[cfg(all( feature = "oltp", any(feature = "v1", feature = "v2"), not(feature = "customer_v2") ))] { - server_app = server_app - .service(routes::EphemeralKey::server(state.clone())) - .service(routes::Poll::server(state.clone())) + server_app = server_app.service(routes::Poll::server(state.clone())) } #[cfg(feature = "olap")] diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index ca7c4847ad4a..1501ad4a6781 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -27,11 +27,7 @@ use self::settings::Tenant; use super::currency; #[cfg(feature = "dummy_connector")] use super::dummy_connector::*; -#[cfg(all( - any(feature = "v1", feature = "v2"), - not(feature = "customer_v2"), - feature = "oltp" -))] +#[cfg(all(any(feature = "v1", feature = "v2"), feature = "oltp"))] use super::ephemeral_key::*; #[cfg(any(feature = "olap", feature = "oltp"))] use super::payment_methods::*; @@ -1141,8 +1137,11 @@ impl PaymentMethods { web::resource("/{id}/update-saved-payment-method") .route(web::patch().to(payment_method_update_api)), ) - .service(web::resource("/{id}").route(web::get().to(payment_method_retrieve_api))) - .service(web::resource("/{id}").route(web::delete().to(payment_method_delete_api))); + .service( + web::resource("/{id}") + .route(web::get().to(payment_method_retrieve_api)) + .route(web::delete().to(payment_method_delete_api)), + ); route } @@ -1416,6 +1415,16 @@ impl EphemeralKey { } } +#[cfg(feature = "v2")] +impl EphemeralKey { + pub fn server(config: AppState) -> Scope { + web::scope("/v2/ephemeral_keys") + .app_data(web::Data::new(config)) + .service(web::resource("").route(web::post().to(ephemeral_key_create))) + .service(web::resource("/{id}").route(web::delete().to(ephemeral_key_delete))) + } +} + pub struct Mandates; #[cfg(all(any(feature = "olap", feature = "oltp"), feature = "v1"))] diff --git a/crates/router/src/routes/ephemeral_key.rs b/crates/router/src/routes/ephemeral_key.rs index 19595b218915..1e5fea5441c1 100644 --- a/crates/router/src/routes/ephemeral_key.rs +++ b/crates/router/src/routes/ephemeral_key.rs @@ -65,7 +65,6 @@ pub async fn ephemeral_key_create( .await } -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all, fields(flow = ?Flow::EphemeralKeyDelete))] pub async fn ephemeral_key_delete( state: web::Data, diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 2417dc7f9fd5..13273361e1d3 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1379,7 +1379,7 @@ where get_header_value_by_key(headers::X_RESOURCE_TYPE.to_string(), request_headers)? .map(ephemeral_key::ResourceType::from_str) .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError)? + .change_context(errors::ApiErrorResponse::Unauthorized)? // Should this be a 5xx? .get_required_value("ResourceType")?; fp_utils::when(resource_type != ephemeral_key.resource_type, || { @@ -3148,7 +3148,7 @@ pub fn get_header_value_by_key(key: String, headers: &HeaderMap) -> RouterResult }) .transpose() } -pub fn get_id_type_by_key_from_headers( +pub fn get_id_type_by_key_from_headers( key: String, headers: &HeaderMap, ) -> RouterResult> { diff --git a/crates/router/src/types/payment_methods.rs b/crates/router/src/types/payment_methods.rs index 1a6b9053dcb9..c40d6fedfe73 100644 --- a/crates/router/src/types/payment_methods.rs +++ b/crates/router/src/types/payment_methods.rs @@ -126,16 +126,6 @@ impl VaultingDataInterface for PaymentMethodVaultingData { } } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -pub struct PaymentMethodClientSecret; - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -impl PaymentMethodClientSecret { - pub fn generate(payment_method_id: &common_utils::id_type::GlobalPaymentMethodId) -> String { - todo!() - } -} - #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub struct SavedPMLPaymentsInfo { pub payment_intent: storage::PaymentIntent, @@ -155,7 +145,7 @@ pub struct VaultRetrieveRequest { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct VaultRetrieveResponse { - pub data: Secret, + pub data: PaymentMethodVaultingData, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] From 80783559b1427cee4679eb7fbb75346ab8b7344c Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 18:52:57 +0000 Subject: [PATCH 03/29] chore: run formatter --- crates/api_models/src/events/customer.rs | 3 +-- crates/router/src/routes/ephemeral_key.rs | 2 -- crates/router/src/services/authentication.rs | 3 ++- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/api_models/src/events/customer.rs b/crates/api_models/src/events/customer.rs index 7f0f11727749..2c1b75aeb0c4 100644 --- a/crates/api_models/src/events/customer.rs +++ b/crates/api_models/src/events/customer.rs @@ -1,10 +1,9 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; -use crate::customers::CustomerId; #[cfg(all(feature = "v2", feature = "customer_v2"))] use crate::customers::GlobalId; use crate::customers::{ - CustomerDeleteResponse, CustomerRequest, CustomerResponse, CustomerUpdateRequest, + CustomerDeleteResponse, CustomerId, CustomerRequest, CustomerResponse, CustomerUpdateRequest, }; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] diff --git a/crates/router/src/routes/ephemeral_key.rs b/crates/router/src/routes/ephemeral_key.rs index 1e5fea5441c1..59788ce32dd3 100644 --- a/crates/router/src/routes/ephemeral_key.rs +++ b/crates/router/src/routes/ephemeral_key.rs @@ -1,9 +1,7 @@ use actix_web::{web, HttpRequest, HttpResponse}; - use router_env::{instrument, tracing, Flow}; use super::AppState; - use crate::{ core::{api_locking, payments::helpers}, services::{api, authentication as auth}, diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 13273361e1d3..af1ea5a10541 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use actix_web::http::header::HeaderMap; #[cfg(all( any(feature = "v2", feature = "v1"), @@ -18,7 +20,6 @@ use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::PeekInterface; use router_env::logger; use serde::Serialize; -use std::str::FromStr; use self::blacklist::BlackList; #[cfg(all(feature = "partial-auth", feature = "v1"))] From 7d0a4db0598369e088ca2aa9f30cc66f4a1d10c6 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 11 Dec 2024 18:55:53 +0000 Subject: [PATCH 04/29] docs(openapi): re-generate OpenAPI specification --- api-reference-v2/openapi_spec.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 64949c3812b6..b055cc9c8aef 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -13729,7 +13729,7 @@ "example": "2024-02-24T11:04:09.922Z", "nullable": true }, - "client_secret": { + "ephemeral_key": { "type": "string", "description": "For Client based calls", "nullable": true From 423c71312467b931307ed8162fa08568a89cbdfc Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 12 Dec 2024 00:54:30 +0530 Subject: [PATCH 05/29] fix: Fixed error responses and eph endpoint --- crates/router/src/routes/app.rs | 2 +- crates/router/src/services/authentication.rs | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 1501ad4a6781..aca0069fe70e 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1418,7 +1418,7 @@ impl EphemeralKey { #[cfg(feature = "v2")] impl EphemeralKey { pub fn server(config: AppState) -> Scope { - web::scope("/v2/ephemeral_keys") + web::scope("/v2/ephemeral-keys") .app_data(web::Data::new(config)) .service(web::resource("").route(web::post().to(ephemeral_key_create))) .service(web::resource("/{id}").route(web::delete().to(ephemeral_key_delete))) diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index af1ea5a10541..983558d4526b 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1376,12 +1376,15 @@ where .await .change_context(errors::ApiErrorResponse::Unauthorized)?; - let resource_type = - get_header_value_by_key(headers::X_RESOURCE_TYPE.to_string(), request_headers)? - .map(ephemeral_key::ResourceType::from_str) - .transpose() - .change_context(errors::ApiErrorResponse::Unauthorized)? // Should this be a 5xx? - .get_required_value("ResourceType")?; + let resource_type = HeaderMapStruct::new(request_headers) + .get_mandatory_header_value_by_key(headers::X_RESOURCE_TYPE) + .and_then(|val| { + ephemeral_key::ResourceType::from_str(val).change_context( + errors::ApiErrorResponse::InvalidRequestData { + message: format!("`{}` header is invalid", headers::X_RESOURCE_TYPE), + }, + ) + })?; fp_utils::when(resource_type != ephemeral_key.resource_type, || { Err(errors::ApiErrorResponse::Unauthorized) From 241921c7d31c67e681f3f94a33662354dc747ff7 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 12 Dec 2024 01:16:54 +0530 Subject: [PATCH 06/29] fix: Added checks on pm retrieve --- crates/router/src/core/payment_methods.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 72f865889a56..a98b71f7f4e1 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1766,6 +1766,11 @@ pub async fn retrieve_payment_method( .await .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?; + when( + payment_method.status == enums::PaymentMethodStatus::Inactive, + || Err(errors::ApiErrorResponse::PaymentMethodNotFound), + )?; + let pmd = payment_method .payment_method_data .clone() @@ -1907,12 +1912,7 @@ pub async fn delete_payment_method( when( payment_method.status == enums::PaymentMethodStatus::Inactive, - || { - Err(errors::ApiErrorResponse::PreconditionFailed { - message: "This Payment method is already inactive and hence cannot be deleted" - .to_string(), - }) - }, + || Err(errors::ApiErrorResponse::PaymentMethodNotFound), )?; let vault_id = payment_method From f8d6dd91e5eda5ecc516f08efc89030a90f1ac79 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 12 Dec 2024 14:51:19 +0530 Subject: [PATCH 07/29] fix: Fixed errors --- crates/router/src/core/payment_methods.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index a98b71f7f4e1..6f57ea09225a 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -852,7 +852,7 @@ pub async fn create_payment_method( key_manager_state, customer_id.get_string_repr(), merchant_account.get_id(), - &key_store, + key_store, merchant_account.storage_scheme, ) .await @@ -862,7 +862,7 @@ pub async fn create_payment_method( let payment_method_billing_address: Option>> = req .billing .clone() - .async_map(|billing| cards::create_encrypted_data(&key_manager_state, key_store, billing)) + .async_map(|billing| cards::create_encrypted_data(key_manager_state, key_store, billing)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -972,7 +972,7 @@ pub async fn payment_method_intent_create( key_manager_state, customer_id.get_string_repr(), merchant_account.get_id(), - &key_store, + key_store, merchant_account.storage_scheme, ) .await @@ -982,7 +982,7 @@ pub async fn payment_method_intent_create( let payment_method_billing_address: Option>> = req .billing .clone() - .async_map(|billing| cards::create_encrypted_data(&key_manager_state, key_store, billing)) + .async_map(|billing| cards::create_encrypted_data(key_manager_state, key_store, billing)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1058,7 +1058,7 @@ pub async fn payment_method_intent_confirm( key_manager_state, customer_id.get_string_repr(), merchant_account.get_id(), - &key_store, + key_store, merchant_account.storage_scheme, ) .await @@ -1275,7 +1275,7 @@ pub async fn create_pm_additional_data_update( }; let key_manager_state = &(state).into(); let pmd: Encryptable> = - cards::create_encrypted_data(&key_manager_state, key_store, card) + cards::create_encrypted_data(key_manager_state, key_store, card) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt Payment method data")?; From b0e13658cb0e30b982dae4a2634a21b868c753c7 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 12 Dec 2024 14:54:28 +0530 Subject: [PATCH 08/29] chore: Resolved comments --- crates/router/src/core/payments/helpers.rs | 9 +++++---- crates/router/src/services/authentication.rs | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index afbfc949155c..b3e6252442e1 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -50,8 +50,6 @@ use super::{ operations::{BoxedOperation, Operation, PaymentResponse}, CustomerDetails, PaymentData, }; -#[cfg(feature = "v2")] -use crate::core::admin as core_admin; use crate::{ configs::settings::{ConnectorRequestReferenceIdConfig, TempLockerEnableConfig}, connector, @@ -86,6 +84,8 @@ use crate::{ OptionExt, StringExt, }, }; +#[cfg(feature = "v2")] +use crate::{core::admin as core_admin, headers}; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use crate::{ core::payment_methods::cards::create_encrypted_data, types::storage::CustomerUpdate::Update, @@ -3029,13 +3029,14 @@ pub async fn make_ephemeral_key( let id = utils::generate_id(consts::ID_LENGTH, "eki"); let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); let resource_type = services::authentication::get_header_value_by_key( - crate::headers::X_RESOURCE_TYPE.to_string(), + headers::X_RESOURCE_TYPE.to_string(), headers, )? .map(ephemeral_key::ResourceType::from_str) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError)? - .get_required_value("ResourceType")?; + .get_required_value("ResourceType") + .attach_printable("Falied to convert ResourceType from string")?; let ek = ephemeral_key::EphemeralKeyNew { id, customer_id: customer_id.to_owned(), diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 983558d4526b..f0536c946e7a 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -13,7 +13,10 @@ use api_models::payouts; use api_models::{payment_methods::PaymentMethodListRequest, payments}; use async_trait::async_trait; use common_enums::TokenPurpose; -use common_utils::{date_time, fp_utils, id_type}; +#[cfg(feature = "v2")] +use common_utils::fp_utils; +use common_utils::{date_time, id_type}; +#[cfg(feature = "v2")] use diesel_models::ephemeral_key; use error_stack::{report, ResultExt}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; From cf0c5f86b2cac3d2ad8bc450c78e6521a4532f9e Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Mon, 16 Dec 2024 15:04:32 +0530 Subject: [PATCH 09/29] chore: Resolved comments --- crates/api_models/src/payment_methods.rs | 11 ----------- crates/diesel_models/src/payment_method.rs | 2 +- crates/router/src/routes/payment_methods.rs | 15 ++------------- crates/router/src/services/authentication.rs | 8 +------- 4 files changed, 4 insertions(+), 32 deletions(-) diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 19c90beda5a2..8272e4e612e4 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -153,9 +153,6 @@ pub struct PaymentMethodIntentCreate { #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] #[serde(deny_unknown_fields)] pub struct PaymentMethodIntentConfirm { - /// For SDK based calls, client_secret would be required - pub client_secret: String, - /// The unique identifier of the customer. #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] pub customer_id: Option, @@ -201,9 +198,6 @@ pub struct PaymentMethodIntentConfirmInternal { #[schema(value_type = PaymentMethodType,example = "credit")] pub payment_method_subtype: api_enums::PaymentMethodType, - /// For SDK based calls, client_secret would be required - pub client_secret: String, - /// The unique identifier of the customer. #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] pub customer_id: Option, @@ -216,7 +210,6 @@ pub struct PaymentMethodIntentConfirmInternal { impl From for PaymentMethodIntentConfirm { fn from(item: PaymentMethodIntentConfirmInternal) -> Self { Self { - client_secret: item.client_secret, payment_method_type: item.payment_method_type, payment_method_subtype: item.payment_method_subtype, customer_id: item.customer_id, @@ -398,10 +391,6 @@ pub struct PaymentMethodUpdate { pub struct PaymentMethodUpdate { /// payment method data to be passed pub payment_method_data: PaymentMethodUpdateData, - - /// This is a 15 minute expiry token which shall be used from the client to authenticate and perform sessions from the SDK - #[schema(max_length = 30, min_length = 30, example = "secret_k2uj3he2893eiu2d")] - pub client_secret: Option, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index ffd282c5aa6a..a3d371943a79 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -362,7 +362,7 @@ impl PaymentMethodUpdateInternal { client_secret: source.client_secret, payment_method_billing_address: source.payment_method_billing_address, updated_by: updated_by.or(source.updated_by), - locker_fingerprint_id: source.locker_fingerprint_id, + locker_fingerprint_id: locker_fingerprint_id.or(source.locker_fingerprint_id), payment_method_type_v2: payment_method_type_v2.or(source.payment_method_type_v2), payment_method_subtype: payment_method_subtype.or(source.payment_method_subtype), id: source.id, diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 00ef7a1acec5..7c9f0cd205ea 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -150,7 +150,6 @@ pub async fn confirm_payment_method_intent_api( id: pm_id.clone(), payment_method_type: payload.payment_method_type, payment_method_subtype: payload.payment_method_subtype, - client_secret: payload.client_secret.clone(), customer_id: payload.customer_id.to_owned(), payment_method_data: payload.payment_method_data.clone(), }; @@ -229,11 +228,6 @@ pub async fn payment_method_retrieve_api( }) .into_inner(); - let auth = match auth::is_ephemeral_auth(req.headers()) { - Ok(auth) => auth, - Err(e) => return api::log_and_return_error_response(e), - }; - Box::pin(api::server_wrap( flow, state, @@ -242,7 +236,7 @@ pub async fn payment_method_retrieve_api( |state, auth: auth::AuthenticationData, pm, _| { retrieve_payment_method(state, pm, auth.key_store, auth.merchant_account) }, - &*auth, + &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, )) .await @@ -261,11 +255,6 @@ pub async fn payment_method_delete_api( }) .into_inner(); - let auth = match auth::is_ephemeral_auth(req.headers()) { - Ok(auth) => auth, - Err(e) => return api::log_and_return_error_response(e), - }; - Box::pin(api::server_wrap( flow, state, @@ -274,7 +263,7 @@ pub async fn payment_method_delete_api( |state, auth: auth::AuthenticationData, pm, _| { delete_payment_method(state, pm, auth.key_store, auth.merchant_account) }, - &*auth, + &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, )) .await diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index f0536c946e7a..f190db5a7820 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -2984,13 +2984,6 @@ impl ClientSecretFetch for PaymentMethodCreate { } } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -impl ClientSecretFetch for PaymentMethodIntentConfirm { - fn get_client_secret(&self) -> Option<&String> { - Some(&self.client_secret) - } -} - impl ClientSecretFetch for api_models::cards_info::CardsInfoRequest { fn get_client_secret(&self) -> Option<&String> { self.client_secret.as_ref() @@ -3015,6 +3008,7 @@ impl ClientSecretFetch for api_models::pm_auth::ExchangeTokenCreateRequest { } } +#[cfg(feature = "v1")] impl ClientSecretFetch for api_models::payment_methods::PaymentMethodUpdate { fn get_client_secret(&self) -> Option<&String> { self.client_secret.as_ref() From 631a628948e89e85341ae67b0bc9b4e2a2059721 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:47:34 +0000 Subject: [PATCH 10/29] docs(openapi): re-generate OpenAPI specification --- api-reference-v2/openapi_spec.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index b055cc9c8aef..b4adeffe60af 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -13434,16 +13434,11 @@ "PaymentMethodIntentConfirm": { "type": "object", "required": [ - "client_secret", "payment_method_data", "payment_method_type", "payment_method_subtype" ], "properties": { - "client_secret": { - "type": "string", - "description": "For SDK based calls, client_secret would be required" - }, "customer_id": { "type": "string", "description": "The unique identifier of the customer.", @@ -13878,14 +13873,6 @@ "properties": { "payment_method_data": { "$ref": "#/components/schemas/PaymentMethodUpdateData" - }, - "client_secret": { - "type": "string", - "description": "This is a 15 minute expiry token which shall be used from the client to authenticate and perform sessions from the SDK", - "example": "secret_k2uj3he2893eiu2d", - "nullable": true, - "maxLength": 30, - "minLength": 30 } }, "additionalProperties": false From 8206b9c72514068f42970366771d44ee94b62740 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Mon, 16 Dec 2024 18:45:12 +0530 Subject: [PATCH 11/29] refactor: Refcatored to use publishible_key auth as well --- crates/router/src/routes/payment_methods.rs | 4 ++-- crates/router/src/services/authentication.rs | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 7c9f0cd205ea..b7e2f3cbe0de 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -141,7 +141,7 @@ pub async fn confirm_payment_method_intent_api( let pm_id = path.into_inner(); let payload = json_payload.into_inner(); - let auth = match auth::is_ephemeral_auth(req.headers()) { + let auth = match auth::is_ephemeral_or_publishible_auth(req.headers()) { Ok(auth) => auth, Err(e) => return api::log_and_return_error_response(e), }; @@ -190,7 +190,7 @@ pub async fn payment_method_update_api( let payment_method_id = path.into_inner(); let payload = json_payload.into_inner(); - let auth = match auth::is_ephemeral_auth(req.headers()) { + let auth = match auth::is_ephemeral_or_publishible_auth(req.headers()) { Ok(auth) => auth, Err(e) => return api::log_and_return_error_response(e), }; diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index f190db5a7820..2fad157fadda 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -3099,6 +3099,20 @@ where } } +pub fn is_ephemeral_or_publishible_auth( + headers: &HeaderMap, +) -> RouterResult>> { + let api_key = get_api_key(headers)?; + + if api_key.starts_with("epk") { + Ok(Box::new(EphemeralKeyAuth)) + } else if api_key.starts_with("pk_") { + Ok(Box::new(HeaderAuth(PublishableKeyAuth))) + } else { + Ok(Box::new(HeaderAuth(ApiKeyAuth))) + } +} + pub fn is_ephemeral_auth( headers: &HeaderMap, ) -> RouterResult>> { From 07fd9d81353d43907df22b2555ef818b85987a6b Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:33:23 +0000 Subject: [PATCH 12/29] chore: run formatter --- crates/api_models/src/events/customer.rs | 2 +- crates/router/src/core/payment_methods.rs | 42 +++++++++++------------ 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/crates/api_models/src/events/customer.rs b/crates/api_models/src/events/customer.rs index 1ebb0c74e1b5..993dad2af709 100644 --- a/crates/api_models/src/events/customer.rs +++ b/crates/api_models/src/events/customer.rs @@ -82,4 +82,4 @@ impl ApiEventMetric for CustomerId { .to_string(), }) } -} \ No newline at end of file +} diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 42709f5fb817..f3dbcf7a1418 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -853,17 +853,16 @@ pub async fn create_payment_method( let customer_id = req.customer_id.to_owned(); let key_manager_state = &(state).into(); - db - .find_customer_by_global_id( - key_manager_state, - customer_id.get_string_repr(), - merchant_account.get_id(), - key_store, - merchant_account.storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound) - .attach_printable("Customer not found for the payment method")?; + db.find_customer_by_global_id( + key_manager_state, + customer_id.get_string_repr(), + merchant_account.get_id(), + key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound) + .attach_printable("Customer not found for the payment method")?; let payment_method_billing_address: Option>> = req .billing @@ -973,17 +972,16 @@ pub async fn payment_method_intent_create( let customer_id = req.customer_id.to_owned(); let key_manager_state = &(state).into(); - db - .find_customer_by_global_id( - key_manager_state, - customer_id.get_string_repr(), - merchant_account.get_id(), - key_store, - merchant_account.storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound) - .attach_printable("Customer not found for the payment method")?; + db.find_customer_by_global_id( + key_manager_state, + customer_id.get_string_repr(), + merchant_account.get_id(), + key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound) + .attach_printable("Customer not found for the payment method")?; let payment_method_billing_address: Option>> = req .billing From dfe8d540df8e3d50ac45ae5b7795b983baf62c3f Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Tue, 17 Dec 2024 22:26:33 +0530 Subject: [PATCH 13/29] fix: Fixed errors --- crates/router/src/core/payment_methods.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index f3dbcf7a1418..da73ce836efd 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -855,7 +855,7 @@ pub async fn create_payment_method( db.find_customer_by_global_id( key_manager_state, - customer_id.get_string_repr(), + &customer_id, merchant_account.get_id(), key_store, merchant_account.storage_scheme, @@ -974,7 +974,7 @@ pub async fn payment_method_intent_create( db.find_customer_by_global_id( key_manager_state, - customer_id.get_string_repr(), + &customer_id, merchant_account.get_id(), key_store, merchant_account.storage_scheme, @@ -1058,7 +1058,7 @@ pub async fn payment_method_intent_confirm( let customer_id = payment_method.customer_id.to_owned(); db.find_customer_by_global_id( - &(state.into()), + key_manager_state, &customer_id, merchant_account.get_id(), key_store, From d4bd82cb887541a50ee95c5a8c9b600b8a7ae789 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Wed, 18 Dec 2024 15:50:46 +0530 Subject: [PATCH 14/29] refactor: Refactored to use GlobalCustomerId --- crates/api_models/src/ephemeral_key.rs | 15 +++ crates/api_models/src/events/customer.rs | 12 -- crates/diesel_models/src/ephemeral_key.rs | 17 ++- crates/router/src/core/payments/helpers.rs | 31 +++-- crates/router/src/db/ephemeral_key.rs | 119 ++++++++++++++---- crates/router/src/db/kafka_store.rs | 32 +++++ crates/router/src/routes/ephemeral_key.rs | 4 +- .../router/src/types/storage/ephemeral_key.rs | 4 +- 8 files changed, 185 insertions(+), 49 deletions(-) diff --git a/crates/api_models/src/ephemeral_key.rs b/crates/api_models/src/ephemeral_key.rs index d06490d6bac2..66b4fbab0e3a 100644 --- a/crates/api_models/src/ephemeral_key.rs +++ b/crates/api_models/src/ephemeral_key.rs @@ -2,6 +2,7 @@ use common_utils::id_type; use serde; use utoipa::ToSchema; +#[cfg(feature = "v1")] /// Information required to create an ephemeral key. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] pub struct EphemeralKeyCreateRequest { @@ -15,6 +16,20 @@ pub struct EphemeralKeyCreateRequest { pub customer_id: id_type::CustomerId, } +#[cfg(feature = "v2")] +/// Information required to create an ephemeral key. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] +pub struct EphemeralKeyCreateRequest { + /// Customer ID for which an ephemeral key must be created + #[schema( + min_length = 32, + max_length = 64, + value_type = String, + example = "12345_cus_01926c58bc6e77c09e809964e72af8c8" + )] + pub customer_id: id_type::GlobalCustomerId, +} + impl common_utils::events::ApiEventMetric for EphemeralKeyCreateRequest { fn get_api_event_type(&self) -> Option { Some(common_utils::events::ApiEventsType::Miscellaneous) diff --git a/crates/api_models/src/events/customer.rs b/crates/api_models/src/events/customer.rs index 993dad2af709..891fb0e9d3c6 100644 --- a/crates/api_models/src/events/customer.rs +++ b/crates/api_models/src/events/customer.rs @@ -71,15 +71,3 @@ impl ApiEventMetric for crate::customers::CustomerUpdateRequestInternal { }) } } - -#[cfg(all(feature = "v2", feature = "customer_v2"))] -impl ApiEventMetric for CustomerId { - fn get_api_event_type(&self) -> Option { - Some(ApiEventsType::Customer { - id: self - .get_merchant_reference_id() - .get_string_repr() - .to_string(), - }) - } -} diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index ac95533993fe..ae342efa8750 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -1,25 +1,24 @@ #[cfg(feature = "v2")] -pub struct EphemeralKeyNew { +pub struct EphemeralKeyTypeNew { pub id: String, pub merchant_id: common_utils::id_type::MerchantId, - pub customer_id: common_utils::id_type::CustomerId, + pub customer_id: common_utils::id_type::GlobalCustomerId, pub secret: String, pub resource_type: ResourceType, } #[cfg(feature = "v2")] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -pub struct EphemeralKey { +pub struct EphemeralKeyType { pub id: String, pub merchant_id: common_utils::id_type::MerchantId, - pub customer_id: common_utils::id_type::CustomerId, + pub customer_id: common_utils::id_type::GlobalCustomerId, pub resource_type: ResourceType, pub created_at: i64, pub expires: i64, pub secret: String, } -#[cfg(feature = "v1")] pub struct EphemeralKeyNew { pub id: String, pub merchant_id: common_utils::id_type::MerchantId, @@ -27,7 +26,6 @@ pub struct EphemeralKeyNew { pub secret: String, } -#[cfg(feature = "v1")] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct EphemeralKey { pub id: String, @@ -44,6 +42,13 @@ impl common_utils::events::ApiEventMetric for EphemeralKey { } } +#[cfg(feature = "v2")] +impl common_utils::events::ApiEventMetric for EphemeralKeyType { + fn get_api_event_type(&self) -> Option { + Some(common_utils::events::ApiEventsType::Miscellaneous) + } +} + #[derive( Clone, Copy, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index e323134deeb0..1431d27405ad 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3047,10 +3047,10 @@ pub async fn make_ephemeral_key( #[cfg(feature = "v2")] pub async fn make_ephemeral_key( state: SessionState, - customer_id: id_type::CustomerId, + customer_id: id_type::GlobalCustomerId, merchant_id: id_type::MerchantId, headers: &actix_web::http::header::HeaderMap, -) -> errors::RouterResponse { +) -> errors::RouterResponse { let store = &state.store; let id = utils::generate_id(consts::ID_LENGTH, "eki"); let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); @@ -3060,10 +3060,12 @@ pub async fn make_ephemeral_key( )? .map(ephemeral_key::ResourceType::from_str) .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError)? + .change_context(errors::ApiErrorResponse::InvalidRequestData { + message: format!("`{}` header is invalid", headers::X_RESOURCE_TYPE), + })? .get_required_value("ResourceType") .attach_printable("Falied to convert ResourceType from string")?; - let ek = ephemeral_key::EphemeralKeyNew { + let ek = ephemeral_key::EphemeralKeyTypeNew { id, customer_id: customer_id.to_owned(), merchant_id: merchant_id.to_owned(), @@ -3081,14 +3083,14 @@ pub async fn make_ephemeral_key( #[cfg(feature = "v2")] pub async fn create_ephemeral_key( state: &SessionState, - customer_id: &id_type::CustomerId, + customer_id: &id_type::GlobalCustomerId, merchant_id: &id_type::MerchantId, resource_type: ephemeral_key::ResourceType, -) -> RouterResult { +) -> RouterResult { let store = &state.store; let id = utils::generate_id(consts::ID_LENGTH, "eki"); let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); - let ek = ephemeral_key::EphemeralKeyNew { + let ek = ephemeral_key::EphemeralKeyTypeNew { id, customer_id: customer_id.to_owned(), merchant_id: merchant_id.to_owned(), @@ -3103,6 +3105,7 @@ pub async fn create_ephemeral_key( Ok(ek) } +#[cfg(feature = "v1")] pub async fn delete_ephemeral_key( state: SessionState, ek_id: String, @@ -3116,6 +3119,20 @@ pub async fn delete_ephemeral_key( Ok(services::ApplicationResponse::Json(ek)) } +#[cfg(feature = "v2")] +pub async fn delete_ephemeral_key( + state: SessionState, + ek_id: String, +) -> errors::RouterResponse { + let db = state.store.as_ref(); + let ek = db + .delete_ephemeral_key(&ek_id) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to delete ephemeral key")?; + Ok(services::ApplicationResponse::Json(ek)) +} + pub fn make_pg_redirect_response( payment_id: id_type::PaymentId, response: &api::PaymentsResponse, diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index 455c0cc6f518..be935a6a972e 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -1,26 +1,54 @@ use time::ext::NumericalDuration; +#[cfg(feature = "v2")] +use crate::types::storage::ephemeral_key::{EphemeralKeyType, EphemeralKeyTypeNew, ResourceType}; use crate::{ core::errors::{self, CustomResult}, db::MockDb, types::storage::ephemeral_key::{EphemeralKey, EphemeralKeyNew}, }; +#[cfg(feature = "v2")] +use common_utils::id_type; #[async_trait::async_trait] pub trait EphemeralKeyInterface { + #[cfg(feature = "v1")] async fn create_ephemeral_key( &self, _ek: EphemeralKeyNew, _validity: i64, ) -> CustomResult; + + #[cfg(feature = "v2")] + async fn create_ephemeral_key( + &self, + _ek: EphemeralKeyTypeNew, + _validity: i64, + ) -> CustomResult; + + #[cfg(feature = "v1")] async fn get_ephemeral_key( &self, _key: &str, ) -> CustomResult; + + #[cfg(feature = "v2")] + async fn get_ephemeral_key( + &self, + _key: &str, + ) -> CustomResult; + + #[cfg(feature = "v1")] async fn delete_ephemeral_key( &self, _id: &str, ) -> CustomResult; + + #[cfg(feature = "v2")] + async fn delete_ephemeral_key( + &self, + _id: &str, + ) -> CustomResult; } mod storage { @@ -33,12 +61,16 @@ mod storage { use super::EphemeralKeyInterface; #[cfg(feature = "v2")] - use crate::types::storage::ephemeral_key::ResourceType; + use crate::types::storage::ephemeral_key::{ + EphemeralKeyType, EphemeralKeyTypeNew, ResourceType, + }; use crate::{ core::errors::{self, CustomResult}, services::Store, types::storage::ephemeral_key::{EphemeralKey, EphemeralKeyNew}, }; + #[cfg(feature = "v2")] + use common_utils::id_type; #[async_trait::async_trait] impl EphemeralKeyInterface for Store { @@ -102,15 +134,15 @@ mod storage { #[instrument(skip_all)] async fn create_ephemeral_key( &self, - new: EphemeralKeyNew, + new: EphemeralKeyTypeNew, validity: i64, - ) -> CustomResult { + ) -> CustomResult { let secret_key = format!("epkey_{}", &new.secret); let id_key = format!("epkey_{}", &new.id); let created_at = date_time::now(); let expires = created_at.saturating_add(validity.hours()); - let created_ek = EphemeralKey { + let created_ek = EphemeralKeyType { id: new.id, created_at: created_at.assume_utc().unix_timestamp(), expires: expires.assume_utc().unix_timestamp(), @@ -154,6 +186,8 @@ mod storage { Err(er) => Err(er).change_context(errors::StorageError::KVError), } } + + #[cfg(feature = "v1")] #[instrument(skip_all)] async fn get_ephemeral_key( &self, @@ -166,6 +200,22 @@ mod storage { .await .change_context(errors::StorageError::KVError) } + + #[cfg(feature = "v2")] + #[instrument(skip_all)] + async fn get_ephemeral_key( + &self, + key: &str, + ) -> CustomResult { + let key = format!("epkey_{key}"); + self.get_redis_conn() + .map_err(Into::::into)? + .get_hash_field_and_deserialize(&key, "ephkey", "EphemeralKey") + .await + .change_context(errors::StorageError::KVError) + } + + #[cfg(feature = "v1")] async fn delete_ephemeral_key( &self, id: &str, @@ -185,6 +235,27 @@ mod storage { .change_context(errors::StorageError::KVError)?; Ok(ek) } + + #[cfg(feature = "v2")] + async fn delete_ephemeral_key( + &self, + id: &str, + ) -> CustomResult { + let ek = self.get_ephemeral_key(id).await?; + + self.get_redis_conn() + .map_err(Into::::into)? + .delete_key(&format!("epkey_{}", &ek.id)) + .await + .change_context(errors::StorageError::KVError)?; + + self.get_redis_conn() + .map_err(Into::::into)? + .delete_key(&format!("epkey_{}", &ek.secret)) + .await + .change_context(errors::StorageError::KVError)?; + Ok(ek) + } } } @@ -215,25 +286,13 @@ impl EphemeralKeyInterface for MockDb { #[cfg(feature = "v2")] async fn create_ephemeral_key( &self, - ek: EphemeralKeyNew, + ek: EphemeralKeyTypeNew, validity: i64, - ) -> CustomResult { - let mut ephemeral_keys = self.ephemeral_keys.lock().await; - let created_at = common_utils::date_time::now(); - let expires = created_at.saturating_add(validity.hours()); - - let ephemeral_key = EphemeralKey { - id: ek.id, - merchant_id: ek.merchant_id, - customer_id: ek.customer_id, - created_at: created_at.assume_utc().unix_timestamp(), - expires: expires.assume_utc().unix_timestamp(), - secret: ek.secret, - resource_type: ek.resource_type, - }; - ephemeral_keys.push(ephemeral_key.clone()); - Ok(ephemeral_key) + ) -> CustomResult { + todo!() } + + #[cfg(feature = "v1")] async fn get_ephemeral_key( &self, key: &str, @@ -251,6 +310,16 @@ impl EphemeralKeyInterface for MockDb { ), } } + + #[cfg(feature = "v2")] + async fn get_ephemeral_key( + &self, + key: &str, + ) -> CustomResult { + todo!() + } + + #[cfg(feature = "v1")] async fn delete_ephemeral_key( &self, id: &str, @@ -265,4 +334,12 @@ impl EphemeralKeyInterface for MockDb { ); } } + + #[cfg(feature = "v2")] + async fn delete_ephemeral_key( + &self, + id: &str, + ) -> CustomResult { + todo!() + } } diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index f49173b35496..4155bbf3ff9a 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -6,6 +6,8 @@ use common_utils::{ id_type, types::{keymanager::KeyManagerState, theme::ThemeLineage}, }; +#[cfg(feature = "v2")] +use diesel_models::ephemeral_key::{EphemeralKeyType, EphemeralKeyTypeNew}; use diesel_models::{ enums, enums::ProcessTrackerStatus, @@ -646,6 +648,7 @@ impl DisputeInterface for KafkaStore { #[async_trait::async_trait] impl EphemeralKeyInterface for KafkaStore { + #[cfg(feature = "v1")] async fn create_ephemeral_key( &self, ek: EphemeralKeyNew, @@ -653,18 +656,47 @@ impl EphemeralKeyInterface for KafkaStore { ) -> CustomResult { self.diesel_store.create_ephemeral_key(ek, validity).await } + + #[cfg(feature = "v2")] + async fn create_ephemeral_key( + &self, + ek: EphemeralKeyTypeNew, + validity: i64, + ) -> CustomResult { + self.diesel_store.create_ephemeral_key(ek, validity).await + } + + #[cfg(feature = "v1")] async fn get_ephemeral_key( &self, key: &str, ) -> CustomResult { self.diesel_store.get_ephemeral_key(key).await } + + #[cfg(feature = "v2")] + async fn get_ephemeral_key( + &self, + key: &str, + ) -> CustomResult { + self.diesel_store.get_ephemeral_key(key).await + } + + #[cfg(feature = "v1")] async fn delete_ephemeral_key( &self, id: &str, ) -> CustomResult { self.diesel_store.delete_ephemeral_key(id).await } + + #[cfg(feature = "v2")] + async fn delete_ephemeral_key( + &self, + id: &str, + ) -> CustomResult { + self.diesel_store.delete_ephemeral_key(id).await + } } #[async_trait::async_trait] diff --git a/crates/router/src/routes/ephemeral_key.rs b/crates/router/src/routes/ephemeral_key.rs index 436e92196e90..7fada34f39df 100644 --- a/crates/router/src/routes/ephemeral_key.rs +++ b/crates/router/src/routes/ephemeral_key.rs @@ -39,7 +39,7 @@ pub async fn ephemeral_key_create( pub async fn ephemeral_key_create( state: web::Data, req: HttpRequest, - json_payload: web::Json, + json_payload: web::Json, ) -> HttpResponse { let flow = Flow::EphemeralKeyCreate; let payload = json_payload.into_inner(); @@ -51,7 +51,7 @@ pub async fn ephemeral_key_create( |state, auth: auth::AuthenticationData, payload, _| { helpers::make_ephemeral_key( state, - payload.get_merchant_reference_id().clone(), + payload.customer_id.to_owned(), auth.merchant_account.get_id().to_owned(), req.headers(), ) diff --git a/crates/router/src/types/storage/ephemeral_key.rs b/crates/router/src/types/storage/ephemeral_key.rs index b609a535de88..fb548a107467 100644 --- a/crates/router/src/types/storage/ephemeral_key.rs +++ b/crates/router/src/types/storage/ephemeral_key.rs @@ -1 +1,3 @@ -pub use diesel_models::ephemeral_key::{EphemeralKey, EphemeralKeyNew, ResourceType}; +pub use diesel_models::ephemeral_key::{ + EphemeralKey, EphemeralKeyNew, EphemeralKeyType, EphemeralKeyTypeNew, ResourceType, +}; From 455b89f03ff2e0e7c67695adb2a278df172770de Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:22:59 +0000 Subject: [PATCH 15/29] chore: run formatter --- crates/router/src/db/ephemeral_key.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index be935a6a972e..d9172d1df8a4 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "v2")] +use common_utils::id_type; use time::ext::NumericalDuration; #[cfg(feature = "v2")] @@ -7,8 +9,6 @@ use crate::{ db::MockDb, types::storage::ephemeral_key::{EphemeralKey, EphemeralKeyNew}, }; -#[cfg(feature = "v2")] -use common_utils::id_type; #[async_trait::async_trait] pub trait EphemeralKeyInterface { @@ -53,6 +53,8 @@ pub trait EphemeralKeyInterface { mod storage { use common_utils::date_time; + #[cfg(feature = "v2")] + use common_utils::id_type; use error_stack::ResultExt; use redis_interface::HsetnxReply; use router_env::{instrument, tracing}; @@ -69,8 +71,6 @@ mod storage { services::Store, types::storage::ephemeral_key::{EphemeralKey, EphemeralKeyNew}, }; - #[cfg(feature = "v2")] - use common_utils::id_type; #[async_trait::async_trait] impl EphemeralKeyInterface for Store { From 3cbce21939cf5c148439bee3690c978165e0bbc5 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Wed, 18 Dec 2024 16:33:55 +0530 Subject: [PATCH 16/29] fix: Fixed clippy errors --- crates/router/src/core/payments/helpers.rs | 2 +- crates/router/src/types/storage/ephemeral_key.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 1431d27405ad..0e5113e3b6ce 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3064,7 +3064,7 @@ pub async fn make_ephemeral_key( message: format!("`{}` header is invalid", headers::X_RESOURCE_TYPE), })? .get_required_value("ResourceType") - .attach_printable("Falied to convert ResourceType from string")?; + .attach_printable("Failed to convert ResourceType from string")?; let ek = ephemeral_key::EphemeralKeyTypeNew { id, customer_id: customer_id.to_owned(), diff --git a/crates/router/src/types/storage/ephemeral_key.rs b/crates/router/src/types/storage/ephemeral_key.rs index fb548a107467..7536e22a5402 100644 --- a/crates/router/src/types/storage/ephemeral_key.rs +++ b/crates/router/src/types/storage/ephemeral_key.rs @@ -1,3 +1,4 @@ -pub use diesel_models::ephemeral_key::{ - EphemeralKey, EphemeralKeyNew, EphemeralKeyType, EphemeralKeyTypeNew, ResourceType, -}; +pub use diesel_models::ephemeral_key::{EphemeralKey, EphemeralKeyNew}; + +#[cfg(feature = "v2")] +pub use diesel_model::ephemeral_key::{EphemeralKeyType, EphemeralKeyTypeNew, ResourceType}; From 295753d439f183a5f1b18ce435149032e88836a6 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:04:56 +0000 Subject: [PATCH 17/29] chore: run formatter --- crates/router/src/types/storage/ephemeral_key.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/router/src/types/storage/ephemeral_key.rs b/crates/router/src/types/storage/ephemeral_key.rs index 7536e22a5402..ea2241198385 100644 --- a/crates/router/src/types/storage/ephemeral_key.rs +++ b/crates/router/src/types/storage/ephemeral_key.rs @@ -1,4 +1,3 @@ -pub use diesel_models::ephemeral_key::{EphemeralKey, EphemeralKeyNew}; - #[cfg(feature = "v2")] pub use diesel_model::ephemeral_key::{EphemeralKeyType, EphemeralKeyTypeNew, ResourceType}; +pub use diesel_models::ephemeral_key::{EphemeralKey, EphemeralKeyNew}; From bd232c7d9b9f7c90798eebe495f5d30e30f88c82 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Wed, 18 Dec 2024 19:02:22 +0530 Subject: [PATCH 18/29] chore: Resolved comments --- crates/common_utils/src/events.rs | 3 ++ crates/common_utils/src/id_type.rs | 2 ++ .../common_utils/src/id_type/ephemeral_key.rs | 30 +++++++++++++++++++ crates/diesel_models/src/ephemeral_key.rs | 8 ++--- crates/router/src/core/payments/helpers.rs | 4 +-- crates/router/src/db/ephemeral_key.rs | 10 +++---- .../router/src/types/storage/ephemeral_key.rs | 4 +-- 7 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 crates/common_utils/src/id_type/ephemeral_key.rs diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index b9af92786f58..9494b8209e75 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -102,6 +102,9 @@ pub enum ApiEventsType { poll_id: String, }, Analytics, + EphemeralKey { + key_id: id_type::EphemeralKeyId, + }, } impl ApiEventMetric for serde_json::Value {} diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs index 594078bb4057..da013b3489ee 100644 --- a/crates/common_utils/src/id_type.rs +++ b/crates/common_utils/src/id_type.rs @@ -3,6 +3,7 @@ mod api_key; mod customer; +mod ephemeral_key; #[cfg(feature = "v2")] mod global_id; mod merchant; @@ -37,6 +38,7 @@ pub use self::global_id::{ pub use self::{ api_key::ApiKeyId, customer::CustomerId, + ephemeral_key::EphemeralKeyId, merchant::MerchantId, merchant_connector_account::MerchantConnectorAccountId, organization::OrganizationId, diff --git a/crates/common_utils/src/id_type/ephemeral_key.rs b/crates/common_utils/src/id_type/ephemeral_key.rs new file mode 100644 index 000000000000..86b54213b02a --- /dev/null +++ b/crates/common_utils/src/id_type/ephemeral_key.rs @@ -0,0 +1,30 @@ +crate::id_type!( + EphemeralKeyId, + "A type for key_id that can be used for Ephemeral key IDs" +); +crate::impl_id_type_methods!(EphemeralKeyId, "key_id"); + +// This is to display the `EphemeralKeyId` as EphemeralKeyId(abcd) +crate::impl_debug_id_type!(EphemeralKeyId); +crate::impl_try_from_cow_str_id_type!(EphemeralKeyId, "key_id"); + +crate::impl_serializable_secret_id_type!(EphemeralKeyId); +crate::impl_queryable_id_type!(EphemeralKeyId); +crate::impl_to_sql_from_sql_id_type!(EphemeralKeyId); + +impl EphemeralKeyId { + /// Generate Ephemeral Key Id from prefix + pub fn generate_key_id(prefix: &'static str) -> Self { + Self(crate::generate_ref_id_with_default_length(prefix)) + } +} + +impl crate::events::ApiEventMetric for EphemeralKeyId { + fn get_api_event_type(&self) -> Option { + Some(crate::events::ApiEventsType::EphemeralKey { + key_id: self.clone(), + }) + } +} + +crate::impl_default_id_type!(EphemeralKeyId, "key"); diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index ae342efa8750..6ed3f12ddce8 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -1,6 +1,6 @@ #[cfg(feature = "v2")] pub struct EphemeralKeyTypeNew { - pub id: String, + pub id: common_utils::id_type::EphemeralKeyId, pub merchant_id: common_utils::id_type::MerchantId, pub customer_id: common_utils::id_type::GlobalCustomerId, pub secret: String, @@ -10,12 +10,12 @@ pub struct EphemeralKeyTypeNew { #[cfg(feature = "v2")] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct EphemeralKeyType { - pub id: String, + pub id: common_utils::id_type::EphemeralKeyId, pub merchant_id: common_utils::id_type::MerchantId, pub customer_id: common_utils::id_type::GlobalCustomerId, pub resource_type: ResourceType, - pub created_at: i64, - pub expires: i64, + pub created_at: time::PrimitiveDateTime, + pub expires: time::PrimitiveDateTime, pub secret: String, } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 0e5113e3b6ce..023f42274cc3 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3052,7 +3052,7 @@ pub async fn make_ephemeral_key( headers: &actix_web::http::header::HeaderMap, ) -> errors::RouterResponse { let store = &state.store; - let id = utils::generate_id(consts::ID_LENGTH, "eki"); + let id = id_type::EphemeralKeyId::generate_key_id("eki"); let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); let resource_type = services::authentication::get_header_value_by_key( headers::X_RESOURCE_TYPE.to_string(), @@ -3088,7 +3088,7 @@ pub async fn create_ephemeral_key( resource_type: ephemeral_key::ResourceType, ) -> RouterResult { let store = &state.store; - let id = utils::generate_id(consts::ID_LENGTH, "eki"); + let id = id_type::EphemeralKeyId::generate_key_id("eki"); let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); let ek = ephemeral_key::EphemeralKeyTypeNew { id, diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index d9172d1df8a4..57b63f29e28d 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -138,14 +138,14 @@ mod storage { validity: i64, ) -> CustomResult { let secret_key = format!("epkey_{}", &new.secret); - let id_key = format!("epkey_{}", &new.id); + let id_key = format!("epkey_{}", new.id.get_string_repr()); let created_at = date_time::now(); let expires = created_at.saturating_add(validity.hours()); let created_ek = EphemeralKeyType { id: new.id, - created_at: created_at.assume_utc().unix_timestamp(), - expires: expires.assume_utc().unix_timestamp(), + created_at, + expires, customer_id: new.customer_id, merchant_id: new.merchant_id, secret: new.secret, @@ -224,7 +224,7 @@ mod storage { self.get_redis_conn() .map_err(Into::::into)? - .delete_key(&format!("epkey_{}", &ek.id)) + .delete_key(&format!("epkey_{}", ek.id.get_string_repr())) .await .change_context(errors::StorageError::KVError)?; @@ -245,7 +245,7 @@ mod storage { self.get_redis_conn() .map_err(Into::::into)? - .delete_key(&format!("epkey_{}", &ek.id)) + .delete_key(&format!("epkey_{}", &ek.id.get_string_repr())) .await .change_context(errors::StorageError::KVError)?; diff --git a/crates/router/src/types/storage/ephemeral_key.rs b/crates/router/src/types/storage/ephemeral_key.rs index ea2241198385..6e7184df4fc2 100644 --- a/crates/router/src/types/storage/ephemeral_key.rs +++ b/crates/router/src/types/storage/ephemeral_key.rs @@ -1,3 +1,3 @@ -#[cfg(feature = "v2")] -pub use diesel_model::ephemeral_key::{EphemeralKeyType, EphemeralKeyTypeNew, ResourceType}; pub use diesel_models::ephemeral_key::{EphemeralKey, EphemeralKeyNew}; +#[cfg(feature = "v2")] +pub use diesel_models::ephemeral_key::{EphemeralKeyType, EphemeralKeyTypeNew, ResourceType}; From a809655f07f3a4945d84c13e30812baefc823c50 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Wed, 18 Dec 2024 19:15:35 +0530 Subject: [PATCH 19/29] fix: Fixed errors --- crates/router/src/db/ephemeral_key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index 57b63f29e28d..af219fb7fad7 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -224,7 +224,7 @@ mod storage { self.get_redis_conn() .map_err(Into::::into)? - .delete_key(&format!("epkey_{}", ek.id.get_string_repr())) + .delete_key(&format!("epkey_{}", &ek.id)) .await .change_context(errors::StorageError::KVError)?; From 7aa4143141cae9db3d453c0b6d6e747b10e078a8 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 19 Dec 2024 00:41:35 +0530 Subject: [PATCH 20/29] chore: Resolved comments --- crates/api_models/src/ephemeral_key.rs | 22 +++++++++++++++++++ crates/diesel_models/src/ephemeral_key.rs | 7 ------ crates/router/src/core/payments/helpers.rs | 12 ++++++---- .../router/src/types/storage/ephemeral_key.rs | 14 ++++++++++++ 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/crates/api_models/src/ephemeral_key.rs b/crates/api_models/src/ephemeral_key.rs index 66b4fbab0e3a..c4d1323b36c0 100644 --- a/crates/api_models/src/ephemeral_key.rs +++ b/crates/api_models/src/ephemeral_key.rs @@ -30,12 +30,34 @@ pub struct EphemeralKeyCreateRequest { pub customer_id: id_type::GlobalCustomerId, } +#[cfg(feature = "v2")] +/// ephemeral_key for the customer_id mentioned +#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq, ToSchema)] +pub struct EphemeralKeyResponse { + /// customer_id to which this ephemeral key belongs to + #[schema(value_type = String, max_length = 64, min_length = 32, example = "12345_cus_01926c58bc6e77c09e809964e72af8c8")] + pub customer_id: id_type::GlobalCustomerId, + /// time at which this ephemeral key was created + pub created_at: time::PrimitiveDateTime, + /// time at which this ephemeral key would expire + pub expires: time::PrimitiveDateTime, + /// ephemeral key + pub secret: String, +} + impl common_utils::events::ApiEventMetric for EphemeralKeyCreateRequest { fn get_api_event_type(&self) -> Option { Some(common_utils::events::ApiEventsType::Miscellaneous) } } +#[cfg(feature = "v2")] +impl common_utils::events::ApiEventMetric for EphemeralKeyResponse { + fn get_api_event_type(&self) -> Option { + Some(common_utils::events::ApiEventsType::Miscellaneous) + } +} + /// ephemeral_key for the customer_id mentioned #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq, ToSchema)] pub struct EphemeralKeyCreateResponse { diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index 6ed3f12ddce8..d5eaba93f175 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -42,13 +42,6 @@ impl common_utils::events::ApiEventMetric for EphemeralKey { } } -#[cfg(feature = "v2")] -impl common_utils::events::ApiEventMetric for EphemeralKeyType { - fn get_api_event_type(&self) -> Option { - Some(common_utils::events::ApiEventsType::Miscellaneous) - } -} - #[derive( Clone, Copy, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 023f42274cc3..1daa13ba2c29 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1,5 +1,7 @@ use std::{borrow::Cow, str::FromStr}; +#[cfg(feature = "v2")] +use api_models::ephemeral_key::EphemeralKeyResponse; use api_models::{ mandates::RecurringDetails, payments::{additional_info as payment_additional_types, RequestSurchargeDetails}, @@ -3050,7 +3052,7 @@ pub async fn make_ephemeral_key( customer_id: id_type::GlobalCustomerId, merchant_id: id_type::MerchantId, headers: &actix_web::http::header::HeaderMap, -) -> errors::RouterResponse { +) -> errors::RouterResponse { let store = &state.store; let id = id_type::EphemeralKeyId::generate_key_id("eki"); let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); @@ -3077,7 +3079,8 @@ pub async fn make_ephemeral_key( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to create ephemeral key")?; - Ok(services::ApplicationResponse::Json(ek)) + let response = EphemeralKeyResponse::foreign_from(ek); + Ok(services::ApplicationResponse::Json(response)) } #[cfg(feature = "v2")] @@ -3123,14 +3126,15 @@ pub async fn delete_ephemeral_key( pub async fn delete_ephemeral_key( state: SessionState, ek_id: String, -) -> errors::RouterResponse { +) -> errors::RouterResponse { let db = state.store.as_ref(); let ek = db .delete_ephemeral_key(&ek_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to delete ephemeral key")?; - Ok(services::ApplicationResponse::Json(ek)) + let response = EphemeralKeyResponse::foreign_from(ek); + Ok(services::ApplicationResponse::Json(response)) } pub fn make_pg_redirect_response( diff --git a/crates/router/src/types/storage/ephemeral_key.rs b/crates/router/src/types/storage/ephemeral_key.rs index 6e7184df4fc2..bdda69dc1a4d 100644 --- a/crates/router/src/types/storage/ephemeral_key.rs +++ b/crates/router/src/types/storage/ephemeral_key.rs @@ -1,3 +1,17 @@ pub use diesel_models::ephemeral_key::{EphemeralKey, EphemeralKeyNew}; #[cfg(feature = "v2")] pub use diesel_models::ephemeral_key::{EphemeralKeyType, EphemeralKeyTypeNew, ResourceType}; + +#[cfg(feature = "v2")] +use crate::types::transformers::ForeignFrom; +#[cfg(feature = "v2")] +impl ForeignFrom for api_models::ephemeral_key::EphemeralKeyResponse { + fn foreign_from(from: EphemeralKeyType) -> Self { + Self { + customer_id: from.customer_id, + created_at: from.created_at, + expires: from.expires, + secret: from.secret, + } + } +} From 35cd0b49c287090ecbf3d0e085a2f3b6ebabcada Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 19 Dec 2024 16:10:49 +0530 Subject: [PATCH 21/29] chore: Resolved comments --- crates/api_models/src/ephemeral_key.rs | 5 ++- crates/api_models/src/payment_methods.rs | 2 +- .../common_utils/src/id_type/ephemeral_key.rs | 8 +---- crates/diesel_models/src/ephemeral_key.rs | 6 ++-- crates/router/src/core/payment_methods.rs | 6 ++-- .../src/core/payment_methods/transformers.rs | 2 +- crates/router/src/core/payments/helpers.rs | 33 ++++++++++--------- crates/router/src/db/ephemeral_key.rs | 28 ++++++++++++---- 8 files changed, 54 insertions(+), 36 deletions(-) diff --git a/crates/api_models/src/ephemeral_key.rs b/crates/api_models/src/ephemeral_key.rs index c4d1323b36c0..50b1d457c4df 100644 --- a/crates/api_models/src/ephemeral_key.rs +++ b/crates/api_models/src/ephemeral_key.rs @@ -1,6 +1,8 @@ use common_utils::id_type; use serde; use utoipa::ToSchema; +#[cfg(feature = "v2")] +use masking::Secret; #[cfg(feature = "v1")] /// Information required to create an ephemeral key. @@ -41,8 +43,9 @@ pub struct EphemeralKeyResponse { pub created_at: time::PrimitiveDateTime, /// time at which this ephemeral key would expire pub expires: time::PrimitiveDateTime, + #[schema(value_type=String)] /// ephemeral key - pub secret: String, + pub secret: Secret, } impl common_utils::events::ApiEventMetric for EphemeralKeyCreateRequest { diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 0821ae1a4d6b..8a13da7a7969 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -815,7 +815,7 @@ pub struct PaymentMethodResponse { pub last_used_at: Option, /// For Client based calls - pub ephemeral_key: Option, + pub ephemeral_key: Option>, pub payment_method_data: Option, } diff --git a/crates/common_utils/src/id_type/ephemeral_key.rs b/crates/common_utils/src/id_type/ephemeral_key.rs index 86b54213b02a..f457b9d44437 100644 --- a/crates/common_utils/src/id_type/ephemeral_key.rs +++ b/crates/common_utils/src/id_type/ephemeral_key.rs @@ -8,17 +8,11 @@ crate::impl_id_type_methods!(EphemeralKeyId, "key_id"); crate::impl_debug_id_type!(EphemeralKeyId); crate::impl_try_from_cow_str_id_type!(EphemeralKeyId, "key_id"); +crate::impl_generate_id_id_type!(EphemeralKeyId, "eki"); crate::impl_serializable_secret_id_type!(EphemeralKeyId); crate::impl_queryable_id_type!(EphemeralKeyId); crate::impl_to_sql_from_sql_id_type!(EphemeralKeyId); -impl EphemeralKeyId { - /// Generate Ephemeral Key Id from prefix - pub fn generate_key_id(prefix: &'static str) -> Self { - Self(crate::generate_ref_id_with_default_length(prefix)) - } -} - impl crate::events::ApiEventMetric for EphemeralKeyId { fn get_api_event_type(&self) -> Option { Some(crate::events::ApiEventsType::EphemeralKey { diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index d5eaba93f175..290bd697e657 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -1,9 +1,11 @@ #[cfg(feature = "v2")] +use masking::Secret; +#[cfg(feature = "v2")] pub struct EphemeralKeyTypeNew { pub id: common_utils::id_type::EphemeralKeyId, pub merchant_id: common_utils::id_type::MerchantId, pub customer_id: common_utils::id_type::GlobalCustomerId, - pub secret: String, + pub secret: Secret, pub resource_type: ResourceType, } @@ -16,7 +18,7 @@ pub struct EphemeralKeyType { pub resource_type: ResourceType, pub created_at: time::PrimitiveDateTime, pub expires: time::PrimitiveDateTime, - pub secret: String, + pub secret: Secret, } pub struct EphemeralKeyNew { diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index da73ce836efd..6cdadf07320c 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1209,7 +1209,7 @@ pub async fn create_payment_method_for_intent( key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, payment_method_billing_address: crypto::OptionalEncryptableValue, -) -> errors::CustomResult<(domain::PaymentMethod, String), errors::ApiErrorResponse> { +) -> errors::CustomResult<(domain::PaymentMethod, Secret), errors::ApiErrorResponse> { let db = &*state.store; let ephemeral_key = payment_helpers::create_ephemeral_key( state, @@ -1324,8 +1324,8 @@ pub async fn vault_payment_method( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to find payment method by fingerprint_id") - .ok() - .is_some(), + .inspect_err(|e| logger::error!("Vault Fingerprint_id error: {:?}", e)) + .is_ok(), || { Err(report!(errors::ApiErrorResponse::DuplicatePaymentMethod) .attach_printable("Cannot vault duplicate payment method")) diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index c590d00c490c..ce95096a0fb3 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -551,7 +551,7 @@ pub fn generate_pm_vaulting_req_from_update_request( #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub fn generate_payment_method_response( pm: &domain::PaymentMethod, - ephemeral_key: Option, + ephemeral_key: Option>, ) -> errors::RouterResult { let pmd = pm .payment_method_data diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 1daa13ba2c29..e9703885feb4 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -8,6 +8,8 @@ use api_models::{ }; use base64::Engine; use common_enums::ConnectorType; +#[cfg(feature = "v2")] +use common_utils::id_type::GenerateId; use common_utils::{ crypto::Encryptable, ext_traits::{AsyncExt, ByteSliceExt, Encode, ValueExt}, @@ -20,6 +22,8 @@ use common_utils::{ }, }; use diesel_models::enums; +#[cfg(feature = "v2")] +use redis_interface::errors::RedisError; // TODO : Evaluate all the helper functions () use error_stack::{report, ResultExt}; use futures::future::Either; @@ -3053,9 +3057,6 @@ pub async fn make_ephemeral_key( merchant_id: id_type::MerchantId, headers: &actix_web::http::header::HeaderMap, ) -> errors::RouterResponse { - let store = &state.store; - let id = id_type::EphemeralKeyId::generate_key_id("eki"); - let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); let resource_type = services::authentication::get_header_value_by_key( headers::X_RESOURCE_TYPE.to_string(), headers, @@ -3067,18 +3068,12 @@ pub async fn make_ephemeral_key( })? .get_required_value("ResourceType") .attach_printable("Failed to convert ResourceType from string")?; - let ek = ephemeral_key::EphemeralKeyTypeNew { - id, - customer_id: customer_id.to_owned(), - merchant_id: merchant_id.to_owned(), - secret, - resource_type, - }; - let ek = store - .create_ephemeral_key(ek, state.conf.eph_key.validity) + + let ek = create_ephemeral_key(&state, &customer_id, &merchant_id, resource_type) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to create ephemeral key")?; + let response = EphemeralKeyResponse::foreign_from(ek); Ok(services::ApplicationResponse::Json(response)) } @@ -3091,8 +3086,8 @@ pub async fn create_ephemeral_key( resource_type: ephemeral_key::ResourceType, ) -> RouterResult { let store = &state.store; - let id = id_type::EphemeralKeyId::generate_key_id("eki"); - let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string()); + let id = id_type::EphemeralKeyId::generate(); + let secret = masking::Secret::new(format!("epk_{}", &Uuid::now_v7().simple().to_string())); let ek = ephemeral_key::EphemeralKeyTypeNew { id, customer_id: customer_id.to_owned(), @@ -3131,8 +3126,16 @@ pub async fn delete_ephemeral_key( let ek = db .delete_ephemeral_key(&ek_id) .await - .change_context(errors::ApiErrorResponse::InternalServerError) + .map_err(|err| match err.current_context() { + errors::StorageError::ValueNotFound(_) => { + err.change_context(errors::ApiErrorResponse::GenericNotFoundError { + message: "Ephemeral Key not found".to_string(), + }) + } + _ => err.change_context(errors::ApiErrorResponse::InternalServerError), + }) .attach_printable("Unable to delete ephemeral key")?; + let response = EphemeralKeyResponse::foreign_from(ek); Ok(services::ApplicationResponse::Json(response)) } diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index af219fb7fad7..fa8eef6e76b1 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -56,6 +56,10 @@ mod storage { #[cfg(feature = "v2")] use common_utils::id_type; use error_stack::ResultExt; + #[cfg(feature = "v2")] + use masking::PeekInterface; + #[cfg(feature = "v2")] + use redis_interface::errors::RedisError; use redis_interface::HsetnxReply; use router_env::{instrument, tracing}; use storage_impl::redis::kv_store::RedisConnInterface; @@ -137,7 +141,7 @@ mod storage { new: EphemeralKeyTypeNew, validity: i64, ) -> CustomResult { - let secret_key = format!("epkey_{}", &new.secret); + let secret_key = format!("epkey_{}", &new.secret.peek()); let id_key = format!("epkey_{}", new.id.get_string_repr()); let created_at = date_time::now(); @@ -210,7 +214,7 @@ mod storage { let key = format!("epkey_{key}"); self.get_redis_conn() .map_err(Into::::into)? - .get_hash_field_and_deserialize(&key, "ephkey", "EphemeralKey") + .get_hash_field_and_deserialize(&key, "ephkey", "EphemeralKeyType") .await .change_context(errors::StorageError::KVError) } @@ -242,18 +246,30 @@ mod storage { id: &str, ) -> CustomResult { let ek = self.get_ephemeral_key(id).await?; + let key_id = format!("epkey_{}", &ek.id.get_string_repr()); + let secret_key = format!("epkey_{}", &ek.secret.peek()); self.get_redis_conn() .map_err(Into::::into)? - .delete_key(&format!("epkey_{}", &ek.id.get_string_repr())) + .delete_key(&key_id) .await - .change_context(errors::StorageError::KVError)?; + .map_err(|err| match err.current_context() { + RedisError::NotFound => { + err.change_context(errors::StorageError::ValueNotFound(key_id)) + } + _ => err.change_context(errors::StorageError::KVError), + })?; self.get_redis_conn() .map_err(Into::::into)? - .delete_key(&format!("epkey_{}", &ek.secret)) + .delete_key(&secret_key) .await - .change_context(errors::StorageError::KVError)?; + .map_err(|err| match err.current_context() { + RedisError::NotFound => { + err.change_context(errors::StorageError::ValueNotFound(secret_key)) + } + _ => err.change_context(errors::StorageError::KVError), + })?; Ok(ek) } } From c7057330e5aaf77da704253a0d60c1f05115bf74 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:48:43 +0000 Subject: [PATCH 22/29] chore: run formatter --- crates/api_models/src/ephemeral_key.rs | 4 ++-- crates/router/src/core/payments/helpers.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/api_models/src/ephemeral_key.rs b/crates/api_models/src/ephemeral_key.rs index 50b1d457c4df..15ee073d5814 100644 --- a/crates/api_models/src/ephemeral_key.rs +++ b/crates/api_models/src/ephemeral_key.rs @@ -1,8 +1,8 @@ use common_utils::id_type; -use serde; -use utoipa::ToSchema; #[cfg(feature = "v2")] use masking::Secret; +use serde; +use utoipa::ToSchema; #[cfg(feature = "v1")] /// Information required to create an ephemeral key. diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index e9703885feb4..50cc5f6dc9a3 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -22,8 +22,6 @@ use common_utils::{ }, }; use diesel_models::enums; -#[cfg(feature = "v2")] -use redis_interface::errors::RedisError; // TODO : Evaluate all the helper functions () use error_stack::{report, ResultExt}; use futures::future::Either; @@ -46,6 +44,8 @@ use openssl::{ pkey::PKey, symm::{decrypt_aead, Cipher}, }; +#[cfg(feature = "v2")] +use redis_interface::errors::RedisError; use router_env::{instrument, logger, tracing}; use uuid::Uuid; use x509_parser::parse_x509_certificate; From 81f6888503130b5876b8efba7ba867375e215b47 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 19 Dec 2024 16:27:48 +0530 Subject: [PATCH 23/29] chore: Resolved comments --- crates/common_utils/src/id_type/ephemeral_key.rs | 7 +++++++ crates/router/src/db/ephemeral_key.rs | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/common_utils/src/id_type/ephemeral_key.rs b/crates/common_utils/src/id_type/ephemeral_key.rs index f457b9d44437..b18c3ed10be1 100644 --- a/crates/common_utils/src/id_type/ephemeral_key.rs +++ b/crates/common_utils/src/id_type/ephemeral_key.rs @@ -22,3 +22,10 @@ impl crate::events::ApiEventMetric for EphemeralKeyId { } crate::impl_default_id_type!(EphemeralKeyId, "key"); + +impl EphemeralKeyId { + /// Generate a key for redis + pub fn generate_redis_key(&self) -> String { + format!("epkey_{}", &self.get_string_repr()) + } +} diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index fa8eef6e76b1..4cccfb06bfeb 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -142,7 +142,7 @@ mod storage { validity: i64, ) -> CustomResult { let secret_key = format!("epkey_{}", &new.secret.peek()); - let id_key = format!("epkey_{}", new.id.get_string_repr()); + let id_key = new.id.generate_redis_key(); let created_at = date_time::now(); let expires = created_at.saturating_add(validity.hours()); @@ -225,10 +225,11 @@ mod storage { id: &str, ) -> CustomResult { let ek = self.get_ephemeral_key(id).await?; + let redis_id_key = ek.id.generate_redis_key(); self.get_redis_conn() .map_err(Into::::into)? - .delete_key(&format!("epkey_{}", &ek.id)) + .delete_key(&redis_id_key) .await .change_context(errors::StorageError::KVError)?; From a7b3b6e179cb871d69edca08fe14906970335bb0 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 19 Dec 2024 16:31:32 +0530 Subject: [PATCH 24/29] fix: Fixed errors --- crates/api_models/src/payment_methods.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 8a13da7a7969..fe5cb2933f9e 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -815,6 +815,7 @@ pub struct PaymentMethodResponse { pub last_used_at: Option, /// For Client based calls + #[schema(value_type=Option)] pub ephemeral_key: Option>, pub payment_method_data: Option, From d1c4b9b41cbb26f8b24932a509e8e429ebde8f3b Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 19 Dec 2024 17:48:32 +0530 Subject: [PATCH 25/29] fix: Fixed errors --- crates/api_models/src/ephemeral_key.rs | 3 ++ crates/router/src/core/payments/helpers.rs | 28 +++++++++++++++---- crates/router/src/db/ephemeral_key.rs | 7 ++--- crates/router/src/routes/ephemeral_key.rs | 3 +- .../router/src/types/storage/ephemeral_key.rs | 1 + 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/crates/api_models/src/ephemeral_key.rs b/crates/api_models/src/ephemeral_key.rs index 15ee073d5814..fa61642e353a 100644 --- a/crates/api_models/src/ephemeral_key.rs +++ b/crates/api_models/src/ephemeral_key.rs @@ -36,6 +36,9 @@ pub struct EphemeralKeyCreateRequest { /// ephemeral_key for the customer_id mentioned #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq, ToSchema)] pub struct EphemeralKeyResponse { + /// Ephemeral key id + #[schema(value_type = String, max_length = 32, min_length = 1)] + pub id: id_type::EphemeralKeyId, /// customer_id to which this ephemeral key belongs to #[schema(value_type = String, max_length = 64, min_length = 32, example = "12345_cus_01926c58bc6e77c09e809964e72af8c8")] pub customer_id: id_type::GlobalCustomerId, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 50cc5f6dc9a3..820c810907c3 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3054,9 +3054,22 @@ pub async fn make_ephemeral_key( pub async fn make_ephemeral_key( state: SessionState, customer_id: id_type::GlobalCustomerId, - merchant_id: id_type::MerchantId, + merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, headers: &actix_web::http::header::HeaderMap, ) -> errors::RouterResponse { + let db = &state.store; + let key_manager_state = &((&state).into()); + db.find_customer_by_global_id( + &key_manager_state, + &customer_id, + merchant_account.get_id(), + &key_store, + merchant_account.storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; + let resource_type = services::authentication::get_header_value_by_key( headers::X_RESOURCE_TYPE.to_string(), headers, @@ -3069,10 +3082,15 @@ pub async fn make_ephemeral_key( .get_required_value("ResourceType") .attach_printable("Failed to convert ResourceType from string")?; - let ek = create_ephemeral_key(&state, &customer_id, &merchant_id, resource_type) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to create ephemeral key")?; + let ek = create_ephemeral_key( + &state, + &customer_id, + merchant_account.get_id(), + resource_type, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to create ephemeral key")?; let response = EphemeralKeyResponse::foreign_from(ek); Ok(services::ApplicationResponse::Json(response)) diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index 4cccfb06bfeb..80f126235f4e 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -225,11 +225,10 @@ mod storage { id: &str, ) -> CustomResult { let ek = self.get_ephemeral_key(id).await?; - let redis_id_key = ek.id.generate_redis_key(); self.get_redis_conn() .map_err(Into::::into)? - .delete_key(&redis_id_key) + .delete_key(&format!("epkey_{}", &ek.id)) .await .change_context(errors::StorageError::KVError)?; @@ -247,12 +246,12 @@ mod storage { id: &str, ) -> CustomResult { let ek = self.get_ephemeral_key(id).await?; - let key_id = format!("epkey_{}", &ek.id.get_string_repr()); + let redis_id_key = ek.id.generate_redis_key(); let secret_key = format!("epkey_{}", &ek.secret.peek()); self.get_redis_conn() .map_err(Into::::into)? - .delete_key(&key_id) + .delete_key(&redis_id_key) .await .map_err(|err| match err.current_context() { RedisError::NotFound => { diff --git a/crates/router/src/routes/ephemeral_key.rs b/crates/router/src/routes/ephemeral_key.rs index 7fada34f39df..0330482e81a9 100644 --- a/crates/router/src/routes/ephemeral_key.rs +++ b/crates/router/src/routes/ephemeral_key.rs @@ -52,7 +52,8 @@ pub async fn ephemeral_key_create( helpers::make_ephemeral_key( state, payload.customer_id.to_owned(), - auth.merchant_account.get_id().to_owned(), + auth.merchant_account, + auth.key_store, req.headers(), ) }, diff --git a/crates/router/src/types/storage/ephemeral_key.rs b/crates/router/src/types/storage/ephemeral_key.rs index bdda69dc1a4d..c4b8e2ba701a 100644 --- a/crates/router/src/types/storage/ephemeral_key.rs +++ b/crates/router/src/types/storage/ephemeral_key.rs @@ -12,6 +12,7 @@ impl ForeignFrom for api_models::ephemeral_key::EphemeralKeyRe created_at: from.created_at, expires: from.expires, secret: from.secret, + id: from.id, } } } From cc2c594e33a4e5282f2742127b0c1aa63b0a5fe9 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 19 Dec 2024 18:56:27 +0530 Subject: [PATCH 26/29] chore: Resolved comments --- crates/common_utils/src/id_type/ephemeral_key.rs | 2 +- crates/diesel_models/src/ephemeral_key.rs | 16 +++++++++++++++- crates/router/src/core/payments/helpers.rs | 2 +- crates/router/src/db/ephemeral_key.rs | 6 +++--- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/crates/common_utils/src/id_type/ephemeral_key.rs b/crates/common_utils/src/id_type/ephemeral_key.rs index b18c3ed10be1..071980fc6a46 100644 --- a/crates/common_utils/src/id_type/ephemeral_key.rs +++ b/crates/common_utils/src/id_type/ephemeral_key.rs @@ -26,6 +26,6 @@ crate::impl_default_id_type!(EphemeralKeyId, "key"); impl EphemeralKeyId { /// Generate a key for redis pub fn generate_redis_key(&self) -> String { - format!("epkey_{}", &self.get_string_repr()) + format!("epkey_{}", self.get_string_repr()) } } diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index 290bd697e657..b933706d13fd 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -1,5 +1,5 @@ #[cfg(feature = "v2")] -use masking::Secret; +use masking::{PeekInterface, Secret}; #[cfg(feature = "v2")] pub struct EphemeralKeyTypeNew { pub id: common_utils::id_type::EphemeralKeyId, @@ -9,6 +9,13 @@ pub struct EphemeralKeyTypeNew { pub resource_type: ResourceType, } +#[cfg(feature = "v2")] +impl EphemeralKeyTypeNew { + pub fn generate_secret_key(&self) -> String { + format!("epkey_{}", &self.secret.peek()) + } +} + #[cfg(feature = "v2")] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct EphemeralKeyType { @@ -21,6 +28,13 @@ pub struct EphemeralKeyType { pub secret: Secret, } +#[cfg(feature = "v2")] +impl EphemeralKeyType { + pub fn generate_secret_key(&self) -> String { + format!("epkey_{}", &self.secret.peek()) + } +} + pub struct EphemeralKeyNew { pub id: String, pub merchant_id: common_utils::id_type::MerchantId, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 820c810907c3..d8ef021aa8bc 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3061,7 +3061,7 @@ pub async fn make_ephemeral_key( let db = &state.store; let key_manager_state = &((&state).into()); db.find_customer_by_global_id( - &key_manager_state, + key_manager_state, &customer_id, merchant_account.get_id(), &key_store, diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index 80f126235f4e..f576e0b1a59a 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -141,7 +141,7 @@ mod storage { new: EphemeralKeyTypeNew, validity: i64, ) -> CustomResult { - let secret_key = format!("epkey_{}", &new.secret.peek()); + let secret_key = new.generate_secret_key(); let id_key = new.id.generate_redis_key(); let created_at = date_time::now(); @@ -247,7 +247,7 @@ mod storage { ) -> CustomResult { let ek = self.get_ephemeral_key(id).await?; let redis_id_key = ek.id.generate_redis_key(); - let secret_key = format!("epkey_{}", &ek.secret.peek()); + let secret_key = ek.generate_secret_key(); self.get_redis_conn() .map_err(Into::::into)? @@ -255,7 +255,7 @@ mod storage { .await .map_err(|err| match err.current_context() { RedisError::NotFound => { - err.change_context(errors::StorageError::ValueNotFound(key_id)) + err.change_context(errors::StorageError::ValueNotFound(redis_id_key)) } _ => err.change_context(errors::StorageError::KVError), })?; From d4cbc53943576146816c890a4c1e1dc4e21f0b33 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 19 Dec 2024 19:01:02 +0530 Subject: [PATCH 27/29] chore: Resolved comments --- crates/diesel_models/src/ephemeral_key.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index b933706d13fd..4a1378ae7b4e 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -12,7 +12,7 @@ pub struct EphemeralKeyTypeNew { #[cfg(feature = "v2")] impl EphemeralKeyTypeNew { pub fn generate_secret_key(&self) -> String { - format!("epkey_{}", &self.secret.peek()) + format!("epkey_{}", self.secret.peek()) } } @@ -31,7 +31,7 @@ pub struct EphemeralKeyType { #[cfg(feature = "v2")] impl EphemeralKeyType { pub fn generate_secret_key(&self) -> String { - format!("epkey_{}", &self.secret.peek()) + format!("epkey_{}", self.secret.peek()) } } From e980125436afaa03773e7826b71087bc78df0363 Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 19 Dec 2024 19:05:44 +0530 Subject: [PATCH 28/29] chore: Resolved comments --- crates/diesel_models/src/ephemeral_key.rs | 7 ------- crates/router/src/db/ephemeral_key.rs | 10 +++++----- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index 4a1378ae7b4e..c7fc103ed09b 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -9,13 +9,6 @@ pub struct EphemeralKeyTypeNew { pub resource_type: ResourceType, } -#[cfg(feature = "v2")] -impl EphemeralKeyTypeNew { - pub fn generate_secret_key(&self) -> String { - format!("epkey_{}", self.secret.peek()) - } -} - #[cfg(feature = "v2")] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct EphemeralKeyType { diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index f576e0b1a59a..7cce452e7d88 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -141,11 +141,6 @@ mod storage { new: EphemeralKeyTypeNew, validity: i64, ) -> CustomResult { - let secret_key = new.generate_secret_key(); - let id_key = new.id.generate_redis_key(); - - let created_at = date_time::now(); - let expires = created_at.saturating_add(validity.hours()); let created_ek = EphemeralKeyType { id: new.id, created_at, @@ -155,6 +150,11 @@ mod storage { secret: new.secret, resource_type: new.resource_type, }; + let secret_key = created_ek.generate_secret_key(); + let id_key = new.id.generate_redis_key(); + + let created_at = date_time::now(); + let expires = created_at.saturating_add(validity.hours()); match self .get_redis_conn() From 967d9f9ed7047205379f2e33b3be291e01199b0c Mon Sep 17 00:00:00 2001 From: Sarthak Soni Date: Thu, 19 Dec 2024 19:38:22 +0530 Subject: [PATCH 29/29] fix: Fixed errors --- crates/router/src/core/payments/helpers.rs | 24 ++++++++++--------- crates/router/src/db/ephemeral_key.rs | 27 ++++++++++++---------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index d8ef021aa8bc..c243686a76cd 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3082,7 +3082,7 @@ pub async fn make_ephemeral_key( .get_required_value("ResourceType") .attach_printable("Failed to convert ResourceType from string")?; - let ek = create_ephemeral_key( + let ephemeral_key = create_ephemeral_key( &state, &customer_id, merchant_account.get_id(), @@ -3092,7 +3092,7 @@ pub async fn make_ephemeral_key( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to create ephemeral key")?; - let response = EphemeralKeyResponse::foreign_from(ek); + let response = EphemeralKeyResponse::foreign_from(ephemeral_key); Ok(services::ApplicationResponse::Json(response)) } @@ -3103,22 +3103,24 @@ pub async fn create_ephemeral_key( merchant_id: &id_type::MerchantId, resource_type: ephemeral_key::ResourceType, ) -> RouterResult { + use common_utils::generate_time_ordered_id; + let store = &state.store; let id = id_type::EphemeralKeyId::generate(); - let secret = masking::Secret::new(format!("epk_{}", &Uuid::now_v7().simple().to_string())); - let ek = ephemeral_key::EphemeralKeyTypeNew { + let secret = masking::Secret::new(generate_time_ordered_id("epk")); + let ephemeral_key = ephemeral_key::EphemeralKeyTypeNew { id, customer_id: customer_id.to_owned(), merchant_id: merchant_id.to_owned(), secret, resource_type, }; - let ek = store - .create_ephemeral_key(ek, state.conf.eph_key.validity) + let ephemeral_key = store + .create_ephemeral_key(ephemeral_key, state.conf.eph_key.validity) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to create ephemeral key")?; - Ok(ek) + Ok(ephemeral_key) } #[cfg(feature = "v1")] @@ -3138,11 +3140,11 @@ pub async fn delete_ephemeral_key( #[cfg(feature = "v2")] pub async fn delete_ephemeral_key( state: SessionState, - ek_id: String, + ephemeral_key_id: String, ) -> errors::RouterResponse { let db = state.store.as_ref(); - let ek = db - .delete_ephemeral_key(&ek_id) + let ephemeral_key = db + .delete_ephemeral_key(&ephemeral_key_id) .await .map_err(|err| match err.current_context() { errors::StorageError::ValueNotFound(_) => { @@ -3154,7 +3156,7 @@ pub async fn delete_ephemeral_key( }) .attach_printable("Unable to delete ephemeral key")?; - let response = EphemeralKeyResponse::foreign_from(ek); + let response = EphemeralKeyResponse::foreign_from(ephemeral_key); Ok(services::ApplicationResponse::Json(response)) } diff --git a/crates/router/src/db/ephemeral_key.rs b/crates/router/src/db/ephemeral_key.rs index 7cce452e7d88..a77995e4e58e 100644 --- a/crates/router/src/db/ephemeral_key.rs +++ b/crates/router/src/db/ephemeral_key.rs @@ -141,7 +141,11 @@ mod storage { new: EphemeralKeyTypeNew, validity: i64, ) -> CustomResult { - let created_ek = EphemeralKeyType { + let created_at = date_time::now(); + let expires = created_at.saturating_add(validity.hours()); + let id_key = new.id.generate_redis_key(); + + let created_ephemeral_key = EphemeralKeyType { id: new.id, created_at, expires, @@ -150,17 +154,16 @@ mod storage { secret: new.secret, resource_type: new.resource_type, }; - let secret_key = created_ek.generate_secret_key(); - let id_key = new.id.generate_redis_key(); - - let created_at = date_time::now(); - let expires = created_at.saturating_add(validity.hours()); + let secret_key = created_ephemeral_key.generate_secret_key(); match self .get_redis_conn() .map_err(Into::::into)? .serialize_and_set_multiple_hash_field_if_not_exist( - &[(&secret_key, &created_ek), (&id_key, &created_ek)], + &[ + (&secret_key, &created_ephemeral_key), + (&id_key, &created_ephemeral_key), + ], "ephkey", None, ) @@ -185,7 +188,7 @@ mod storage { .set_expire_at(&id_key, expire_at) .await .change_context(errors::StorageError::KVError)?; - Ok(created_ek) + Ok(created_ephemeral_key) } Err(er) => Err(er).change_context(errors::StorageError::KVError), } @@ -245,9 +248,9 @@ mod storage { &self, id: &str, ) -> CustomResult { - let ek = self.get_ephemeral_key(id).await?; - let redis_id_key = ek.id.generate_redis_key(); - let secret_key = ek.generate_secret_key(); + let ephemeral_key = self.get_ephemeral_key(id).await?; + let redis_id_key = ephemeral_key.id.generate_redis_key(); + let secret_key = ephemeral_key.generate_secret_key(); self.get_redis_conn() .map_err(Into::::into)? @@ -270,7 +273,7 @@ mod storage { } _ => err.change_context(errors::StorageError::KVError), })?; - Ok(ek) + Ok(ephemeral_key) } } }