diff --git a/Cargo.lock b/Cargo.lock index 886a8b50ac..ac7fde55d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1503,7 +1503,6 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" name = "common_enums" version = "0.1.0" dependencies = [ - "common_utils", "diesel", "router_derive", "serde", @@ -1519,6 +1518,7 @@ version = "0.1.0" dependencies = [ "async-trait", "bytes", + "common_enums", "diesel", "error-stack", "fake", diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 037d223754..e844d1900a 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -893,6 +893,8 @@ pub struct ToggleKVResponse { #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct ToggleKVRequest { + #[serde(skip_deserializing)] + pub merchant_id: String, /// Status of KV for the specific merchant #[schema(example = true)] pub kv_enabled: bool, diff --git a/crates/api_models/src/api_keys.rs b/crates/api_models/src/api_keys.rs index f0ab403d9c..805c5616c2 100644 --- a/crates/api_models/src/api_keys.rs +++ b/crates/api_models/src/api_keys.rs @@ -129,6 +129,12 @@ pub struct UpdateApiKeyRequest { /// rotating your keys once every 6 months. #[schema(example = "2022-09-10T10:11:12Z")] pub expiration: Option, + + #[serde(skip_deserializing)] + pub key_id: String, + + #[serde(skip_deserializing)] + pub merchant_id: String, } /// The response body for revoking an API Key. diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs new file mode 100644 index 0000000000..78f34b4b87 --- /dev/null +++ b/crates/api_models/src/events.rs @@ -0,0 +1,74 @@ +pub mod customer; +pub mod payment; +#[cfg(feature = "payouts")] +pub mod payouts; +pub mod refund; +pub mod routing; + +use common_utils::{ + events::{ApiEventMetric, ApiEventsType}, + impl_misc_api_event_type, +}; + +use crate::{ + admin::*, api_keys::*, cards_info::*, disputes::*, files::*, mandates::*, payment_methods::*, + payments::*, verifications::*, +}; + +impl ApiEventMetric for TimeRange {} + +impl_misc_api_event_type!( + PaymentMethodId, + PaymentsSessionResponse, + PaymentMethodListResponse, + PaymentMethodCreate, + PaymentLinkInitiateRequest, + RetrievePaymentLinkResponse, + MandateListConstraints, + CreateFileResponse, + DisputeResponse, + SubmitEvidenceRequest, + MerchantConnectorResponse, + MerchantConnectorId, + MandateResponse, + MandateRevokedResponse, + RetrievePaymentLinkRequest, + MandateId, + DisputeListConstraints, + RetrieveApiKeyResponse, + BusinessProfileResponse, + BusinessProfileUpdate, + BusinessProfileCreate, + RevokeApiKeyResponse, + ToggleKVResponse, + ToggleKVRequest, + MerchantAccountDeleteResponse, + MerchantAccountUpdate, + CardInfoResponse, + CreateApiKeyResponse, + CreateApiKeyRequest, + MerchantConnectorDeleteResponse, + MerchantConnectorUpdate, + MerchantConnectorCreate, + MerchantId, + CardsInfoRequest, + MerchantAccountResponse, + MerchantAccountListRequest, + MerchantAccountCreate, + PaymentsSessionRequest, + ApplepayMerchantVerificationRequest, + ApplepayMerchantResponse, + ApplepayVerifiedDomainsResponse, + UpdateApiKeyRequest +); + +#[cfg(feature = "stripe")] +impl_misc_api_event_type!( + StripeSetupIntentResponse, + StripeRefundResponse, + StripePaymentIntentListResponse, + StripePaymentIntentResponse, + CustomerDeleteResponse, + CustomerPaymentMethodListResponse, + CreateCustomerResponse +); diff --git a/crates/api_models/src/events/customer.rs b/crates/api_models/src/events/customer.rs new file mode 100644 index 0000000000..29f5650421 --- /dev/null +++ b/crates/api_models/src/events/customer.rs @@ -0,0 +1,35 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::customers::{CustomerDeleteResponse, CustomerId, CustomerRequest, CustomerResponse}; + +impl ApiEventMetric for CustomerDeleteResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Customer { + customer_id: self.customer_id.clone(), + }) + } +} + +impl ApiEventMetric for CustomerRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Customer { + customer_id: self.customer_id.clone(), + }) + } +} + +impl ApiEventMetric for CustomerResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Customer { + customer_id: self.customer_id.clone(), + }) + } +} + +impl ApiEventMetric for CustomerId { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Customer { + customer_id: self.customer_id.clone(), + }) + } +} diff --git a/crates/api_models/src/events/payment.rs b/crates/api_models/src/events/payment.rs new file mode 100644 index 0000000000..2f3336fc27 --- /dev/null +++ b/crates/api_models/src/events/payment.rs @@ -0,0 +1,151 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::{ + payment_methods::{ + CustomerPaymentMethodsListResponse, PaymentMethodDeleteResponse, PaymentMethodListRequest, + PaymentMethodResponse, PaymentMethodUpdate, + }, + payments::{ + PaymentIdType, PaymentListConstraints, PaymentListFilterConstraints, PaymentListFilters, + PaymentListResponse, PaymentListResponseV2, PaymentsApproveRequest, PaymentsCancelRequest, + PaymentsCaptureRequest, PaymentsRejectRequest, PaymentsRequest, PaymentsResponse, + PaymentsRetrieveRequest, PaymentsStartRequest, RedirectionResponse, + }, +}; +impl ApiEventMetric for PaymentsRetrieveRequest { + fn get_api_event_type(&self) -> Option { + match self.resource_id { + PaymentIdType::PaymentIntentId(ref id) => Some(ApiEventsType::Payment { + payment_id: id.clone(), + }), + _ => None, + } + } +} + +impl ApiEventMetric for PaymentsStartRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.clone(), + }) + } +} + +impl ApiEventMetric for PaymentsCaptureRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.to_owned(), + }) + } +} + +impl ApiEventMetric for PaymentsCancelRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.clone(), + }) + } +} + +impl ApiEventMetric for PaymentsApproveRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.clone(), + }) + } +} + +impl ApiEventMetric for PaymentsRejectRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payment { + payment_id: self.payment_id.clone(), + }) + } +} + +impl ApiEventMetric for PaymentsRequest { + fn get_api_event_type(&self) -> Option { + match self.payment_id { + Some(PaymentIdType::PaymentIntentId(ref id)) => Some(ApiEventsType::Payment { + payment_id: id.clone(), + }), + _ => None, + } + } +} + +impl ApiEventMetric for PaymentsResponse { + fn get_api_event_type(&self) -> Option { + self.payment_id + .clone() + .map(|payment_id| ApiEventsType::Payment { payment_id }) + } +} + +impl ApiEventMetric for PaymentMethodResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::PaymentMethod { + payment_method_id: self.payment_method_id.clone(), + payment_method: Some(self.payment_method), + payment_method_type: self.payment_method_type, + }) + } +} + +impl ApiEventMetric for PaymentMethodUpdate {} + +impl ApiEventMetric for PaymentMethodDeleteResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::PaymentMethod { + payment_method_id: self.payment_method_id.clone(), + payment_method: None, + payment_method_type: None, + }) + } +} + +impl ApiEventMetric for CustomerPaymentMethodsListResponse {} + +impl ApiEventMetric for PaymentMethodListRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::PaymentMethodList { + payment_id: self + .client_secret + .as_ref() + .and_then(|cs| cs.rsplit_once("_secret_")) + .map(|(pid, _)| pid.to_string()), + }) + } +} + +impl ApiEventMetric for PaymentListFilterConstraints { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for PaymentListFilters { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for PaymentListConstraints { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for PaymentListResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for PaymentListResponseV2 { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for RedirectionResponse {} diff --git a/crates/api_models/src/events/payouts.rs b/crates/api_models/src/events/payouts.rs new file mode 100644 index 0000000000..303709acc4 --- /dev/null +++ b/crates/api_models/src/events/payouts.rs @@ -0,0 +1,29 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::payouts::{ + PayoutActionRequest, PayoutCreateRequest, PayoutCreateResponse, PayoutRetrieveRequest, +}; + +impl ApiEventMetric for PayoutRetrieveRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payout) + } +} + +impl ApiEventMetric for PayoutCreateRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payout) + } +} + +impl ApiEventMetric for PayoutCreateResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payout) + } +} + +impl ApiEventMetric for PayoutActionRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Payout) + } +} diff --git a/crates/api_models/src/events/refund.rs b/crates/api_models/src/events/refund.rs new file mode 100644 index 0000000000..424a3191db --- /dev/null +++ b/crates/api_models/src/events/refund.rs @@ -0,0 +1,63 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::refunds::{ + RefundListMetaData, RefundListRequest, RefundListResponse, RefundRequest, RefundResponse, + RefundUpdateRequest, RefundsRetrieveRequest, +}; + +impl ApiEventMetric for RefundRequest { + fn get_api_event_type(&self) -> Option { + let payment_id = self.payment_id.clone(); + self.refund_id + .clone() + .map(|refund_id| ApiEventsType::Refund { + payment_id: Some(payment_id), + refund_id, + }) + } +} + +impl ApiEventMetric for RefundResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Refund { + payment_id: Some(self.payment_id.clone()), + refund_id: self.refund_id.clone(), + }) + } +} + +impl ApiEventMetric for RefundsRetrieveRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Refund { + payment_id: None, + refund_id: self.refund_id.clone(), + }) + } +} + +impl ApiEventMetric for RefundUpdateRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Refund { + payment_id: None, + refund_id: self.refund_id.clone(), + }) + } +} + +impl ApiEventMetric for RefundListRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for RefundListResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} + +impl ApiEventMetric for RefundListMetaData { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::ResourceListAPI) + } +} diff --git a/crates/api_models/src/events/routing.rs b/crates/api_models/src/events/routing.rs new file mode 100644 index 0000000000..5eca01acc6 --- /dev/null +++ b/crates/api_models/src/events/routing.rs @@ -0,0 +1,58 @@ +use common_utils::events::{ApiEventMetric, ApiEventsType}; + +use crate::routing::{ + LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, RoutingAlgorithmId, + RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind, +}; +#[cfg(feature = "business_profile_routing")] +use crate::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery}; + +impl ApiEventMetric for RoutingKind { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for MerchantRoutingAlgorithm { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for RoutingAlgorithmId { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for RoutingDictionaryRecord { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for LinkedRoutingConfigRetrieveResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +#[cfg(feature = "business_profile_routing")] +impl ApiEventMetric for RoutingRetrieveQuery { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +impl ApiEventMetric for RoutingConfigRequest { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} + +#[cfg(feature = "business_profile_routing")] +impl ApiEventMetric for RoutingRetrieveLinkQuery { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Routing) + } +} diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs index ec272514e3..9fff344b9f 100644 --- a/crates/api_models/src/lib.rs +++ b/crates/api_models/src/lib.rs @@ -9,6 +9,7 @@ pub mod enums; pub mod ephemeral_key; #[cfg(feature = "errors")] pub mod errors; +pub mod events; pub mod files; pub mod mandates; pub mod organization; diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index dcbdb56bf7..289f652981 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -12,7 +12,9 @@ use utoipa::ToSchema; #[cfg(feature = "payouts")] use crate::payouts; use crate::{ - admin, enums as api_enums, + admin, + customers::CustomerId, + enums as api_enums, payments::{self, BankCodeResponse}, }; @@ -476,6 +478,8 @@ pub struct RequestPaymentMethodTypes { #[derive(Debug, Clone, serde::Serialize, Default, ToSchema)] #[serde(deny_unknown_fields)] pub struct PaymentMethodListRequest { + #[serde(skip_deserializing)] + pub customer_id: Option, /// 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_k2uj3he2893ein2d")] pub client_secret: Option, diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index f9cb21dae5..c1880d58ad 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -2232,7 +2232,9 @@ pub struct PaymentListFilters { pub authentication_type: Vec, } -#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)] +#[derive( + Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, ToSchema, +)] pub struct TimeRange { /// The start time to filter payments list or to get list of filters. To get list of filters start time is needed to be passed #[serde(with = "common_utils::custom_serde::iso8601")] diff --git a/crates/api_models/src/refunds.rs b/crates/api_models/src/refunds.rs index 7b4eae4238..6fe8be8b52 100644 --- a/crates/api_models/src/refunds.rs +++ b/crates/api_models/src/refunds.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; use utoipa::ToSchema; +use super::payments::TimeRange; use crate::{admin, enums}; #[derive(Default, Debug, ToSchema, Clone, Deserialize, Serialize)] @@ -75,6 +76,8 @@ pub struct RefundsRetrieveRequest { #[derive(Default, Debug, ToSchema, Clone, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub struct RefundUpdateRequest { + #[serde(skip)] + pub refund_id: String, /// An arbitrary string attached to the object. Often useful for displaying to users and your customer support executive #[schema(max_length = 255, example = "Customer returned the product")] pub reason: Option, @@ -152,16 +155,6 @@ pub struct RefundListRequest { pub refund_status: Option>, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, ToSchema)] -pub struct TimeRange { - /// The start time to filter refunds list or to get list of filters. To get list of filters start time is needed to be passed - #[serde(with = "common_utils::custom_serde::iso8601")] - pub start_time: PrimitiveDateTime, - /// The end time to filter refunds list or to get list of filters. If not passed the default time is now - #[serde(default, with = "common_utils::custom_serde::iso8601::option")] - pub end_time: Option, -} - #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ToSchema)] pub struct RefundListResponse { /// The number of refunds included in the list diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index 95d4c5e10e..425ca36419 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -592,3 +592,8 @@ pub enum RoutingKind { Config(RoutingDictionary), RoutingAlgorithm(Vec), } + +#[repr(transparent)] +#[derive(serde::Serialize, serde::Deserialize, Debug)] +#[serde(transparent)] +pub struct RoutingAlgorithmId(pub String); diff --git a/crates/common_enums/Cargo.toml b/crates/common_enums/Cargo.toml index 10b4fb509e..e9f2dffcc0 100644 --- a/crates/common_enums/Cargo.toml +++ b/crates/common_enums/Cargo.toml @@ -19,7 +19,6 @@ time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] } utoipa = { version = "3.3.0", features = ["preserve_order"] } # First party crates -common_utils = { version = "0.1.0", path = "../common_utils" } router_derive = { version = "0.1.0", path = "../router_derive" } [dev-dependencies] diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index c1fd91a351..62bd747da1 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -42,6 +42,7 @@ phonenumber = "0.3.3" # First party crates masking = { version = "0.1.0", path = "../masking" } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"], optional = true } +common_enums = { version = "0.1.0", path = "../common_enums" } [target.'cfg(not(target_os = "windows"))'.dependencies] signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"], optional = true } diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs new file mode 100644 index 0000000000..1d48736403 --- /dev/null +++ b/crates/common_utils/src/events.rs @@ -0,0 +1,91 @@ +use common_enums::{PaymentMethod, PaymentMethodType}; +use serde::Serialize; + +pub trait ApiEventMetric { + fn get_api_event_type(&self) -> Option { + None + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(tag = "flow_type")] +pub enum ApiEventsType { + Payout, + Payment { + payment_id: String, + }, + Refund { + payment_id: Option, + refund_id: String, + }, + PaymentMethod { + payment_method_id: String, + payment_method: Option, + payment_method_type: Option, + }, + Customer { + customer_id: String, + }, + User { + //specified merchant_id will overridden on global defined + merchant_id: String, + user_id: String, + }, + PaymentMethodList { + payment_id: Option, + }, + Webhooks { + connector: String, + payment_id: Option, + }, + Routing, + ResourceListAPI, + PaymentRedirectionResponse, + // TODO: This has to be removed once the corresponding apiEventTypes are created + Miscellaneous, +} + +impl ApiEventMetric for serde_json::Value {} +impl ApiEventMetric for () {} + +impl ApiEventMetric for Result { + fn get_api_event_type(&self) -> Option { + match self { + Ok(q) => q.get_api_event_type(), + Err(_) => None, + } + } +} + +// TODO: Ideally all these types should be replaced by newtype responses +impl ApiEventMetric for Vec { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Miscellaneous) + } +} + +#[macro_export] +macro_rules! impl_misc_api_event_type { + ($($type:ty),+) => { + $( + impl ApiEventMetric for $type { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Miscellaneous) + } + } + )+ + }; +} + +impl_misc_api_event_type!( + String, + (&String, &String), + (Option, Option, String), + bool +); + +impl ApiEventMetric for &T { + fn get_api_event_type(&self) -> Option { + T::get_api_event_type(self) + } +} diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index 724c3bca0a..62428dccfb 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -6,6 +6,8 @@ pub mod consts; pub mod crypto; pub mod custom_serde; pub mod errors; +#[allow(missing_docs)] // Todo: add docs +pub mod events; pub mod ext_traits; pub mod fp_utils; pub mod pii; diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index 96bd6e497c..77b9c647e4 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -14,3 +14,9 @@ pub struct EphemeralKey { pub expires: i64, pub secret: String, } + +impl common_utils::events::ApiEventMetric for EphemeralKey { + fn get_api_event_type(&self) -> Option { + Some(common_utils::events::ApiEventsType::Miscellaneous) + } +} diff --git a/crates/diesel_models/src/refund.rs b/crates/diesel_models/src/refund.rs index 73ff34030f..62aec3fb27 100644 --- a/crates/diesel_models/src/refund.rs +++ b/crates/diesel_models/src/refund.rs @@ -227,3 +227,12 @@ pub struct RefundCoreWorkflow { pub merchant_id: String, pub payment_id: String, } + +impl common_utils::events::ApiEventMetric for Refund { + fn get_api_event_type(&self) -> Option { + Some(common_utils::events::ApiEventsType::Refund { + payment_id: Some(self.payment_id.clone()), + refund_id: self.refund_id.clone(), + }) + } +} diff --git a/crates/router/src/compatibility/stripe/refunds.rs b/crates/router/src/compatibility/stripe/refunds.rs index dc14744382..ad4accf6ca 100644 --- a/crates/router/src/compatibility/stripe/refunds.rs +++ b/crates/router/src/compatibility/stripe/refunds.rs @@ -149,8 +149,8 @@ pub async fn refund_update( path: web::Path, form_payload: web::Form, ) -> HttpResponse { - let refund_id = path.into_inner(); - let payload = form_payload.into_inner(); + let mut payload = form_payload.into_inner(); + payload.refund_id = path.into_inner(); let create_refund_update_req: refund_types::RefundUpdateRequest = payload.into(); let flow = Flow::RefundsUpdate; @@ -169,9 +169,7 @@ pub async fn refund_update( state.into_inner(), &req, create_refund_update_req, - |state, auth, req| { - refunds::refund_update_core(state, auth.merchant_account, &refund_id, req) - }, + |state, auth, req| refunds::refund_update_core(state, auth.merchant_account, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, )) diff --git a/crates/router/src/compatibility/stripe/refunds/types.rs b/crates/router/src/compatibility/stripe/refunds/types.rs index e148618649..8d65a09187 100644 --- a/crates/router/src/compatibility/stripe/refunds/types.rs +++ b/crates/router/src/compatibility/stripe/refunds/types.rs @@ -17,6 +17,8 @@ pub struct StripeCreateRefundRequest { #[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct StripeUpdateRefundRequest { + #[serde(skip)] + pub refund_id: String, pub metadata: Option, } @@ -58,6 +60,7 @@ impl From for refunds::RefundRequest { impl From for refunds::RefundUpdateRequest { fn from(req: StripeUpdateRefundRequest) -> Self { Self { + refund_id: req.refund_id, metadata: req.metadata, reason: None, } diff --git a/crates/router/src/compatibility/wrap.rs b/crates/router/src/compatibility/wrap.rs index 75cb07de02..1ab156d32a 100644 --- a/crates/router/src/compatibility/wrap.rs +++ b/crates/router/src/compatibility/wrap.rs @@ -7,6 +7,7 @@ use serde::Serialize; use crate::{ core::{api_locking, errors}, + events::api_logs::ApiEventMetric, routes::{app::AppStateInfo, metrics}, services::{self, api, authentication as auth, logger}, }; @@ -25,12 +26,12 @@ where F: Fn(A, U, T) -> Fut, Fut: Future, E2>>, E2: ErrorSwitch + std::error::Error + Send + Sync + 'static, - Q: Serialize + std::fmt::Debug + 'a, + Q: Serialize + std::fmt::Debug + 'a + ApiEventMetric, S: TryFrom + Serialize, E: Serialize + error_stack::Context + actix_web::ResponseError + Clone, error_stack::Report: services::EmbedError, errors::ApiErrorResponse: ErrorSwitch, - T: std::fmt::Debug + Serialize, + T: std::fmt::Debug + Serialize + ApiEventMetric, A: AppStateInfo + Clone, { let request_method = request.method().as_str(); diff --git a/crates/router/src/core/api_keys.rs b/crates/router/src/core/api_keys.rs index 7bda894826..c1ddc43cd6 100644 --- a/crates/router/src/core/api_keys.rs +++ b/crates/router/src/core/api_keys.rs @@ -294,10 +294,10 @@ pub async fn retrieve_api_key( #[instrument(skip_all)] pub async fn update_api_key( state: AppState, - merchant_id: &str, - key_id: &str, api_key: api::UpdateApiKeyRequest, ) -> RouterResponse { + let merchant_id = api_key.merchant_id.clone(); + let key_id = api_key.key_id.clone(); let store = state.store.as_ref(); let api_key = store @@ -313,7 +313,7 @@ pub async fn update_api_key( { let expiry_reminder_days = state.conf.api_keys.expiry_reminder_days.clone(); - let task_id = generate_task_id_for_api_key_expiry_workflow(key_id); + let task_id = generate_task_id_for_api_key_expiry_workflow(&key_id); // In order to determine how to update the existing process in the process_tracker table, // we need access to the current entry in the table. let existing_process_tracker_task = store @@ -339,7 +339,7 @@ pub async fn update_api_key( // If an expiry is set to 'never' else { // Process exist in process, revoke it - revoke_api_key_expiry_task(store, key_id) + revoke_api_key_expiry_task(store, &key_id) .await .into_report() .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index fcda3c8daf..a42e46ca62 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -476,14 +476,13 @@ pub async fn sync_refund_with_gateway( pub async fn refund_update_core( state: AppState, merchant_account: domain::MerchantAccount, - refund_id: &str, req: refunds::RefundUpdateRequest, ) -> RouterResponse { let db = state.store.as_ref(); let refund = db .find_refund_by_merchant_id_refund_id( &merchant_account.merchant_id, - refund_id, + &req.refund_id, merchant_account.storage_scheme, ) .await @@ -501,7 +500,9 @@ pub async fn refund_update_core( ) .await .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| format!("Unable to update refund with refund_id: {refund_id}"))?; + .attach_printable_lazy(|| { + format!("Unable to update refund with refund_id: {}", req.refund_id) + })?; Ok(services::ApplicationResponse::Json(response.foreign_into())) } @@ -698,7 +699,7 @@ pub async fn refund_list( pub async fn refund_filter_list( state: AppState, merchant_account: domain::MerchantAccount, - req: api_models::refunds::TimeRange, + req: api_models::payments::TimeRange, ) -> RouterResponse { let db = state.store; let filter_list = db diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 8033cc792b..723611ed50 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -1,7 +1,7 @@ pub mod helpers; pub mod transformers; -use api_models::routing as routing_types; +use api_models::routing::{self as routing_types, RoutingAlgorithmId}; #[cfg(feature = "business_profile_routing")] use api_models::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery}; #[cfg(not(feature = "business_profile_routing"))] @@ -319,14 +319,14 @@ pub async fn link_routing_config( pub async fn retrieve_routing_config( state: AppState, merchant_account: domain::MerchantAccount, - algorithm_id: String, + algorithm_id: RoutingAlgorithmId, ) -> RouterResponse { let db = state.store.as_ref(); #[cfg(feature = "business_profile_routing")] { let routing_algorithm = db .find_routing_algorithm_by_algorithm_id_merchant_id( - &algorithm_id, + &algorithm_id.0, &merchant_account.merchant_id, ) .await @@ -356,13 +356,13 @@ pub async fn retrieve_routing_config( let record = merchant_dictionary .records .into_iter() - .find(|rec| rec.id == algorithm_id) + .find(|rec| rec.id == algorithm_id.0) .ok_or(errors::ApiErrorResponse::ResourceIdNotFound) .into_report() .attach_printable("Algorithm with the given ID not found in the merchant dictionary")?; let algorithm_config = db - .find_config_by_key(&algorithm_id) + .find_config_by_key(&algorithm_id.0) .await .change_context(errors::ApiErrorResponse::ResourceIdNotFound) .attach_printable("Routing config not found in DB")?; diff --git a/crates/router/src/core/verification.rs b/crates/router/src/core/verification.rs index fa700b4cd6..e643e0455b 100644 --- a/crates/router/src/core/verification.rs +++ b/crates/router/src/core/verification.rs @@ -1,5 +1,4 @@ pub mod utils; -use actix_web::web; use api_models::verifications::{self, ApplepayMerchantResponse}; use common_utils::{errors::CustomResult, ext_traits::Encode}; use error_stack::ResultExt; @@ -18,7 +17,7 @@ const APPLEPAY_INTERNAL_MERCHANT_NAME: &str = "Applepay_merchant"; pub async fn verify_merchant_creds_for_applepay( state: AppState, _req: &actix_web::HttpRequest, - body: web::Json, + body: verifications::ApplepayMerchantVerificationRequest, kms_config: &kms::KmsConfig, merchant_id: String, ) -> CustomResult< diff --git a/crates/router/src/db/refund.rs b/crates/router/src/db/refund.rs index a6133edad6..c9b9f8ac55 100644 --- a/crates/router/src/db/refund.rs +++ b/crates/router/src/db/refund.rs @@ -78,7 +78,7 @@ pub trait RefundInterface { async fn filter_refund_by_meta_constraints( &self, merchant_id: &str, - refund_details: &api_models::refunds::TimeRange, + refund_details: &api_models::payments::TimeRange, storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult; @@ -232,7 +232,7 @@ mod storage { async fn filter_refund_by_meta_constraints( &self, merchant_id: &str, - refund_details: &api_models::refunds::TimeRange, + refund_details: &api_models::payments::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; @@ -707,7 +707,7 @@ mod storage { async fn filter_refund_by_meta_constraints( &self, merchant_id: &str, - refund_details: &api_models::refunds::TimeRange, + refund_details: &api_models::payments::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; @@ -979,7 +979,7 @@ impl RefundInterface for MockDb { async fn filter_refund_by_meta_constraints( &self, _merchant_id: &str, - refund_details: &api_models::refunds::TimeRange, + refund_details: &api_models::payments::TimeRange, _storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { let refunds = self.refunds.lock().await; diff --git a/crates/router/src/events/api_logs.rs b/crates/router/src/events/api_logs.rs index 5a66ba3e0b..1a47568e7a 100644 --- a/crates/router/src/events/api_logs.rs +++ b/crates/router/src/events/api_logs.rs @@ -1,10 +1,25 @@ use actix_web::HttpRequest; +pub use common_utils::events::{ApiEventMetric, ApiEventsType}; +use common_utils::impl_misc_api_event_type; use router_env::{tracing_actix_web::RequestId, types::FlowMetric}; use serde::Serialize; use time::OffsetDateTime; use super::{EventType, RawEvent}; -use crate::services::authentication::AuthenticationType; +#[cfg(feature = "dummy_connector")] +use crate::routes::dummy_connector::types::{ + DummyConnectorPaymentCompleteRequest, DummyConnectorPaymentConfirmRequest, + DummyConnectorPaymentRequest, DummyConnectorPaymentResponse, + DummyConnectorPaymentRetrieveRequest, DummyConnectorRefundRequest, + DummyConnectorRefundResponse, DummyConnectorRefundRetrieveRequest, +}; +use crate::{ + core::payments::PaymentsRedirectResponseData, + services::{authentication::AuthenticationType, ApplicationResponse, PaymentLinkFormData}, + types::api::{ + AttachEvidenceRequest, Config, ConfigUpdate, CreateFileRequest, DisputeId, FileId, + }, +}; #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ApiEvent { @@ -20,6 +35,8 @@ pub struct ApiEvent { ip_addr: Option, url_path: String, response: Option, + #[serde(flatten)] + event_type: ApiEventsType, } impl ApiEvent { @@ -32,6 +49,7 @@ impl ApiEvent { request: serde_json::Value, response: Option, auth_type: AuthenticationType, + event_type: ApiEventsType, http_req: &HttpRequest, ) -> Self { Self { @@ -52,6 +70,7 @@ impl ApiEvent { .get("user-agent") .and_then(|user_agent_value| user_agent_value.to_str().ok().map(ToOwned::to_owned)), url_path: http_req.path().to_string(), + event_type, } } } @@ -67,3 +86,35 @@ impl TryFrom for RawEvent { }) } } + +impl ApiEventMetric for ApplicationResponse { + fn get_api_event_type(&self) -> Option { + match self { + Self::Json(r) => r.get_api_event_type(), + Self::JsonWithHeaders((r, _)) => r.get_api_event_type(), + _ => None, + } + } +} +impl_misc_api_event_type!( + Config, + CreateFileRequest, + FileId, + AttachEvidenceRequest, + DisputeId, + PaymentLinkFormData, + PaymentsRedirectResponseData, + ConfigUpdate +); + +#[cfg(feature = "dummy_connector")] +impl_misc_api_event_type!( + DummyConnectorPaymentCompleteRequest, + DummyConnectorPaymentRequest, + DummyConnectorPaymentResponse, + DummyConnectorPaymentRetrieveRequest, + DummyConnectorPaymentConfirmRequest, + DummyConnectorRefundRetrieveRequest, + DummyConnectorRefundResponse, + DummyConnectorRefundRequest +); diff --git a/crates/router/src/openapi.rs b/crates/router/src/openapi.rs index a5bce20088..dbcd8cbe4c 100644 --- a/crates/router/src/openapi.rs +++ b/crates/router/src/openapi.rs @@ -305,7 +305,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payment_methods::RequiredFieldInfo, api_models::refunds::RefundListRequest, api_models::refunds::RefundListResponse, - api_models::refunds::TimeRange, + api_models::payments::TimeRange, api_models::mandates::MandateRevokedResponse, api_models::mandates::MandateResponse, api_models::mandates::MandateCardDetails, diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index a93556202a..9153e9e747 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -388,15 +388,15 @@ pub async fn merchant_account_toggle_kv( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::ConfigKeyUpdate; - let payload = json_payload.into_inner(); - let merchant_id = path.into_inner(); + let mut payload = json_payload.into_inner(); + payload.merchant_id = path.into_inner(); api::server_wrap( flow, state, &req, - (merchant_id, payload), - |state, _, (merchant_id, payload)| kv_for_merchant(state, merchant_id, payload.kv_enabled), + payload, + |state, _, payload| kv_for_merchant(state, payload.merchant_id, payload.kv_enabled), &auth::AdminApiAuth, api_locking::LockAction::NotApplicable, ) diff --git a/crates/router/src/routes/api_keys.rs b/crates/router/src/routes/api_keys.rs index 6057b4c5db..c2e289cd0f 100644 --- a/crates/router/src/routes/api_keys.rs +++ b/crates/router/src/routes/api_keys.rs @@ -124,16 +124,16 @@ pub async fn api_key_update( ) -> impl Responder { let flow = Flow::ApiKeyUpdate; let (merchant_id, key_id) = path.into_inner(); - let payload = json_payload.into_inner(); + let mut payload = json_payload.into_inner(); + payload.key_id = key_id; + payload.merchant_id = merchant_id; api::server_wrap( flow, state, &req, - (&merchant_id, &key_id, payload), - |state, _, (merchant_id, key_id, payload)| { - api_keys::update_api_key(state, merchant_id, key_id, payload) - }, + payload, + |state, _, payload| api_keys::update_api_key(state, payload), &auth::AdminApiAuth, api_locking::LockAction::NotApplicable, ) diff --git a/crates/router/src/routes/dummy_connector.rs b/crates/router/src/routes/dummy_connector.rs index 52a7f7f77c..7d2aad7e34 100644 --- a/crates/router/src/routes/dummy_connector.rs +++ b/crates/router/src/routes/dummy_connector.rs @@ -10,7 +10,7 @@ use crate::{ mod consts; mod core; mod errors; -mod types; +pub mod types; mod utils; #[instrument(skip_all, fields(flow = ?types::Flow::DummyPaymentCreate))] diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs index 4c4121b5d5..c20f3fbf97 100644 --- a/crates/router/src/routes/refunds.rs +++ b/crates/router/src/routes/refunds.rs @@ -161,13 +161,14 @@ pub async fn refunds_update( path: web::Path, ) -> HttpResponse { let flow = Flow::RefundsUpdate; - let refund_id = path.into_inner(); + let mut refund_update_req = json_payload.into_inner(); + refund_update_req.refund_id = path.into_inner(); api::server_wrap( flow, state, &req, - json_payload.into_inner(), - |state, auth, req| refund_update_core(state, auth.merchant_account, &refund_id, req), + refund_update_req, + |state, auth, req| refund_update_core(state, auth.merchant_account, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, ) @@ -225,7 +226,7 @@ pub async fn refunds_list( pub async fn refunds_filter_list( state: web::Data, req: HttpRequest, - payload: web::Json, + payload: web::Json, ) -> HttpResponse { let flow = Flow::RefundsList; api::server_wrap( diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 1d5ccdf502..9252c360a9 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -47,7 +47,7 @@ pub async fn routing_create_config( pub async fn routing_link_config( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> impl Responder { let flow = Flow::RoutingLinkConfig; Box::pin(oss_api::server_wrap( @@ -61,7 +61,7 @@ pub async fn routing_link_config( auth.merchant_account, #[cfg(not(feature = "business_profile_routing"))] auth.key_store, - algorithm_id, + algorithm_id.0, ) }, #[cfg(not(feature = "release"))] @@ -78,7 +78,7 @@ pub async fn routing_link_config( pub async fn routing_retrieve_config( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> impl Responder { let algorithm_id = path.into_inner(); let flow = Flow::RoutingRetrieveConfig; diff --git a/crates/router/src/routes/verification.rs b/crates/router/src/routes/verification.rs index a0861f2b14..2ad061848c 100644 --- a/crates/router/src/routes/verification.rs +++ b/crates/router/src/routes/verification.rs @@ -22,7 +22,7 @@ pub async fn apple_pay_merchant_registration( flow, state, &req, - json_payload, + json_payload.into_inner(), |state, _, body| { verification::verify_merchant_creds_for_applepay( state.clone(), diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 3626449069..bb0e70b4b2 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -34,7 +34,7 @@ use crate::{ errors::{self, CustomResult}, payments, }, - events::api_logs::ApiEvent, + events::api_logs::{ApiEvent, ApiEventMetric, ApiEventsType}, logger, routes::{ app::AppStateInfo, @@ -769,8 +769,8 @@ where F: Fn(A, U, T) -> Fut, 'b: 'a, Fut: Future, E>>, - Q: Serialize + Debug + 'a, - T: Debug + Serialize, + Q: Serialize + Debug + 'a + ApiEventMetric, + T: Debug + Serialize + ApiEventMetric, A: AppStateInfo + Clone, E: ErrorSwitch + error_stack::Context, OErr: ResponseError + error_stack::Context, @@ -791,6 +791,8 @@ where .attach_printable("Failed to serialize json request") .change_context(errors::ApiErrorResponse::InternalServerError.switch())?; + let mut event_type = payload.get_api_event_type(); + // Currently auth failures are not recorded as API events let (auth_out, auth_type) = api_auth .authenticate_and_fetch(request.headers(), &request_state) @@ -838,6 +840,7 @@ where .change_context(errors::ApiErrorResponse::InternalServerError.switch())?, ); } + event_type = res.get_api_event_type().or(event_type); metrics::request::track_response_status_code(res) } @@ -852,6 +855,7 @@ where serialized_request, serialized_response, auth_type, + event_type.unwrap_or(ApiEventsType::Miscellaneous), request, ); match api_event.clone().try_into() { @@ -884,8 +888,8 @@ pub async fn server_wrap<'a, A, T, U, Q, F, Fut, E>( where F: Fn(A, U, T) -> Fut, Fut: Future, E>>, - Q: Serialize + Debug + 'a, - T: Debug + Serialize, + Q: Serialize + Debug + ApiEventMetric + 'a, + T: Debug + Serialize + ApiEventMetric, A: AppStateInfo + Clone, ApplicationResponse: Debug, E: ErrorSwitch + error_stack::Context, diff --git a/crates/router/src/types/api/customers.rs b/crates/router/src/types/api/customers.rs index 2050b4149e..32430c0918 100644 --- a/crates/router/src/types/api/customers.rs +++ b/crates/router/src/types/api/customers.rs @@ -10,6 +10,12 @@ newtype!( derives = (Debug, Clone, Serialize) ); +impl common_utils::events::ApiEventMetric for CustomerResponse { + fn get_api_event_type(&self) -> Option { + self.0.get_api_event_type() + } +} + pub(crate) trait CustomerRequestExt: Sized { fn validate(self) -> RouterResult; } diff --git a/crates/router/src/types/storage/refund.rs b/crates/router/src/types/storage/refund.rs index bdfa8dc5b5..4d56677001 100644 --- a/crates/router/src/types/storage/refund.rs +++ b/crates/router/src/types/storage/refund.rs @@ -27,7 +27,7 @@ pub trait RefundDbExt: Sized { async fn filter_by_meta_constraints( conn: &PgPooledConn, merchant_id: &str, - refund_list_details: &api_models::refunds::TimeRange, + refund_list_details: &api_models::payments::TimeRange, ) -> CustomResult; async fn get_refunds_count( @@ -114,7 +114,7 @@ impl RefundDbExt for Refund { async fn filter_by_meta_constraints( conn: &PgPooledConn, merchant_id: &str, - refund_list_details: &api_models::refunds::TimeRange, + refund_list_details: &api_models::payments::TimeRange, ) -> CustomResult { let start_time = refund_list_details.start_time; diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 822b1aacee..5af67e4992 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -11218,12 +11218,12 @@ "start_time": { "type": "string", "format": "date-time", - "description": "The start time to filter refunds list or to get list of filters. To get list of filters start time is needed to be passed" + "description": "The start time to filter payments list or to get list of filters. To get list of filters start time is needed to be passed" }, "end_time": { "type": "string", "format": "date-time", - "description": "The end time to filter refunds list or to get list of filters. If not passed the default time is now", + "description": "The end time to filter payments list or to get list of filters. If not passed the default time is now", "nullable": true } }