From dfbfce4e4247166e43f1a805e65331b21eab4e09 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:54:27 +0530 Subject: [PATCH] refactor(customers_v2): address panics and some bugs in customers v2 endpoints (#6836) --- .../customers/customers--create.mdx | 2 +- .../customers/customers--delete.mdx | 2 +- .../customers/customers--list.mdx | 2 +- .../customers/customers--retrieve.mdx | 2 +- .../customers/customers--update.mdx | 4 +- api-reference-v2/openapi_spec.json | 145 ++++++++--- api-reference/openapi_spec.json | 56 ++++- crates/api_models/src/customers.rs | 104 +++----- crates/api_models/src/ephemeral_key.rs | 19 ++ crates/api_models/src/events/customer.rs | 43 +--- crates/api_models/src/payment_methods.rs | 66 +++-- crates/api_models/src/payments.rs | 18 +- crates/common_utils/src/events.rs | 2 +- crates/common_utils/src/id_type.rs | 38 +-- crates/common_utils/src/id_type/customer.rs | 9 + crates/common_utils/src/id_type/global_id.rs | 7 +- .../src/id_type/global_id/customer.rs | 45 ++++ .../src/id_type/global_id/payment.rs | 8 +- crates/diesel_models/src/customers.rs | 4 +- crates/diesel_models/src/kv.rs | 2 +- crates/diesel_models/src/payment_intent.rs | 4 +- crates/diesel_models/src/payment_method.rs | 4 +- crates/diesel_models/src/query/customers.rs | 7 +- crates/diesel_models/src/query/mandate.rs | 19 +- .../diesel_models/src/query/payment_method.rs | 29 ++- .../hyperswitch_domain_models/src/customer.rs | 16 +- .../src/payment_methods.rs | 2 +- .../hyperswitch_domain_models/src/payments.rs | 2 +- crates/openapi/src/openapi.rs | 1 + crates/openapi/src/openapi_v2.rs | 1 + crates/openapi/src/routes/customers.rs | 4 +- .../src/compatibility/stripe/customers.rs | 39 +-- crates/router/src/core/customers.rs | 125 ++++----- crates/router/src/core/mandate.rs | 12 +- crates/router/src/core/payment_methods.rs | 26 +- .../router/src/core/payment_methods/cards.rs | 32 +-- .../payment_methods/network_tokenization.rs | 3 + crates/router/src/core/payments.rs | 1 + crates/router/src/core/payments/helpers.rs | 1 + .../operations/payment_confirm_intent.rs | 2 +- .../operations/payment_create_intent.rs | 2 +- .../core/payments/operations/payment_get.rs | 2 +- .../operations/payment_session_intent.rs | 2 +- .../router/src/core/payments/transformers.rs | 8 +- crates/router/src/core/payouts.rs | 4 +- crates/router/src/db/customers.rs | 48 ++-- crates/router/src/db/kafka_store.rs | 48 +++- crates/router/src/db/mandate.rs | 18 +- crates/router/src/db/payment_method.rs | 237 ++++++++++++++++-- crates/router/src/routes/app.rs | 4 +- crates/router/src/routes/customers.rs | 83 +++--- crates/router/src/routes/ephemeral_key.rs | 7 +- crates/router/src/routes/payment_methods.rs | 4 +- crates/router/src/routes/payments.rs | 2 +- crates/router/src/types/api/customers.rs | 8 +- crates/storage_impl/src/lib.rs | 2 +- 56 files changed, 909 insertions(+), 478 deletions(-) create mode 100644 crates/common_utils/src/id_type/global_id/customer.rs diff --git a/api-reference-v2/api-reference/customers/customers--create.mdx b/api-reference-v2/api-reference/customers/customers--create.mdx index c53a4cd620..1d517ca26f 100644 --- a/api-reference-v2/api-reference/customers/customers--create.mdx +++ b/api-reference-v2/api-reference/customers/customers--create.mdx @@ -1,3 +1,3 @@ --- openapi: post /v2/customers ---- \ No newline at end of file +--- diff --git a/api-reference-v2/api-reference/customers/customers--delete.mdx b/api-reference-v2/api-reference/customers/customers--delete.mdx index eee5cffd59..5d642f125f 100644 --- a/api-reference-v2/api-reference/customers/customers--delete.mdx +++ b/api-reference-v2/api-reference/customers/customers--delete.mdx @@ -1,3 +1,3 @@ --- openapi: delete /v2/customers/{id} ---- \ No newline at end of file +--- diff --git a/api-reference-v2/api-reference/customers/customers--list.mdx b/api-reference-v2/api-reference/customers/customers--list.mdx index 432370e119..ae0d884e30 100644 --- a/api-reference-v2/api-reference/customers/customers--list.mdx +++ b/api-reference-v2/api-reference/customers/customers--list.mdx @@ -1,3 +1,3 @@ --- openapi: get /v2/customers/list ---- \ No newline at end of file +--- diff --git a/api-reference-v2/api-reference/customers/customers--retrieve.mdx b/api-reference-v2/api-reference/customers/customers--retrieve.mdx index e89fe53d42..16f8a06255 100644 --- a/api-reference-v2/api-reference/customers/customers--retrieve.mdx +++ b/api-reference-v2/api-reference/customers/customers--retrieve.mdx @@ -1,3 +1,3 @@ --- openapi: get /v2/customers/{id} ---- \ No newline at end of file +--- diff --git a/api-reference-v2/api-reference/customers/customers--update.mdx b/api-reference-v2/api-reference/customers/customers--update.mdx index 65433d8fad..8d4bed86c9 100644 --- a/api-reference-v2/api-reference/customers/customers--update.mdx +++ b/api-reference-v2/api-reference/customers/customers--update.mdx @@ -1,3 +1,3 @@ --- -openapi: post /v2/customers/{id} ---- \ No newline at end of file +openapi: put /v2/customers/{id} +--- diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 252174d11f..58a81caeed 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -1712,7 +1712,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CustomerRequest" + "$ref": "#/components/schemas/CustomerUpdateRequest" }, "examples": { "Update name and email of a customer": { @@ -7253,13 +7253,20 @@ "CustomerDeleteResponse": { "type": "object", "required": [ + "id", "merchant_reference_id", "customer_deleted", "address_deleted", - "payment_methods_deleted", - "id" + "payment_methods_deleted" ], "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the customer", + "example": "12345_cus_01926c58bc6e77c09e809964e72af8c8", + "maxLength": 64, + "minLength": 32 + }, "merchant_reference_id": { "type": "string", "description": "The identifier for the customer object", @@ -7280,10 +7287,6 @@ "type": "boolean", "description": "Whether payment methods deleted or not", "example": false - }, - "id": { - "type": "string", - "description": "Global id" } } }, @@ -7399,9 +7402,9 @@ "customer_id": { "type": "string", "description": "The unique identifier of the customer.", - "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "example": "12345_cus_01926c58bc6e77c09e809964e72af8c8", "maxLength": 64, - "minLength": 1 + "minLength": 32 }, "payment_method_type": { "$ref": "#/components/schemas/PaymentMethod" @@ -7566,16 +7569,24 @@ "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500\ncharacters long. Metadata is useful for storing additional, structured information on an\nobject.", "nullable": true } - } + }, + "additionalProperties": false }, "CustomerResponse": { "type": "object", "required": [ + "id", "merchant_reference_id", - "created_at", - "id" + "created_at" ], "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the customer", + "example": "12345_cus_01926c58bc6e77c09e809964e72af8c8", + "maxLength": 64, + "minLength": 32 + }, "merchant_reference_id": { "type": "string", "description": "The identifier for the customer object", @@ -7651,13 +7662,87 @@ "example": "pm_djh2837dwduh890123", "nullable": true, "maxLength": 64 - }, - "id": { - "type": "string", - "description": "Global id" } } }, + "CustomerUpdateRequest": { + "type": "object", + "required": [ + "name", + "email" + ], + "properties": { + "merchant_reference_id": { + "type": "string", + "description": "The merchant identifier for the customer object.", + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "nullable": true, + "maxLength": 64, + "minLength": 1 + }, + "name": { + "type": "string", + "description": "The customer's name", + "example": "Jon Test", + "maxLength": 255 + }, + "email": { + "type": "string", + "description": "The customer's email address", + "example": "JonTest@test.com", + "maxLength": 255 + }, + "phone": { + "type": "string", + "description": "The customer's phone number", + "example": "9123456789", + "nullable": true, + "maxLength": 255 + }, + "description": { + "type": "string", + "description": "An arbitrary string that you can attach to a customer object.", + "example": "First Customer", + "nullable": true, + "maxLength": 255 + }, + "phone_country_code": { + "type": "string", + "description": "The country code for the customer phone number", + "example": "+65", + "nullable": true, + "maxLength": 255 + }, + "default_billing_address": { + "allOf": [ + { + "$ref": "#/components/schemas/AddressDetails" + } + ], + "nullable": true + }, + "default_shipping_address": { + "allOf": [ + { + "$ref": "#/components/schemas/AddressDetails" + } + ], + "nullable": true + }, + "metadata": { + "type": "object", + "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500\ncharacters long. Metadata is useful for storing additional, structured information on an\nobject.", + "nullable": true + }, + "default_payment_method_id": { + "type": "string", + "description": "The unique identifier of the payment method", + "example": "card_rGK4Vi5iSW70MY7J2mIg", + "nullable": true + } + }, + "additionalProperties": false + }, "DecoupledAuthenticationType": { "type": "string", "enum": [ @@ -13122,9 +13207,9 @@ "customer_id": { "type": "string", "description": "The unique identifier of the customer.", - "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "example": "12345_cus_01926c58bc6e77c09e809964e72af8c8", "maxLength": 64, - "minLength": 1 + "minLength": 32 }, "payment_method_data": { "$ref": "#/components/schemas/PaymentMethodCreateData" @@ -13663,9 +13748,9 @@ "customer_id": { "type": "string", "description": "The unique identifier of the customer.", - "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "example": "12345_cus_01926c58bc6e77c09e809964e72af8c8", "maxLength": 64, - "minLength": 1 + "minLength": 32 } }, "additionalProperties": false @@ -13854,6 +13939,7 @@ "type": "object", "required": [ "merchant_id", + "customer_id", "payment_method_id", "payment_method_type", "recurring_enabled" @@ -13867,10 +13953,9 @@ "customer_id": { "type": "string", "description": "The unique identifier of the customer.", - "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", - "nullable": true, + "example": "12345_cus_01926c58bc6e77c09e809964e72af8c8", "maxLength": 64, - "minLength": 1 + "minLength": 32 }, "payment_method_id": { "type": "string", @@ -14436,7 +14521,8 @@ "PaymentsCreateIntentRequest": { "type": "object", "required": [ - "amount_details" + "amount_details", + "customer_id" ], "properties": { "amount_details": { @@ -14491,10 +14577,9 @@ "customer_id": { "type": "string", "description": "The identifier for the customer", - "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", - "nullable": true, + "example": "12345_cus_01926c58bc6e77c09e809964e72af8c8", "maxLength": 64, - "minLength": 1 + "minLength": 32 }, "customer_present": { "allOf": [ @@ -15328,6 +15413,7 @@ "profile_id", "capture_method", "authentication_type", + "customer_id", "customer_present", "setup_future_usage", "apply_mit_exemption", @@ -15399,10 +15485,9 @@ "customer_id": { "type": "string", "description": "The identifier for the customer", - "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", - "nullable": true, + "example": "12345_cus_01926c58bc6e77c09e809964e72af8c8", "maxLength": 64, - "minLength": 1 + "minLength": 32 }, "customer_present": { "$ref": "#/components/schemas/PresenceOfCustomerDuringPayment" diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 6b6147aa90..c31a8bd11e 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -2266,7 +2266,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CustomerRequest" + "$ref": "#/components/schemas/CustomerUpdateRequest" }, "examples": { "Update name and email of a customer": { @@ -10036,6 +10036,60 @@ } } }, + "CustomerUpdateRequest": { + "type": "object", + "description": "The identifier for the customer object. If not provided the customer ID will be autogenerated.", + "properties": { + "name": { + "type": "string", + "description": "The customer's name", + "example": "Jon Test", + "nullable": true, + "maxLength": 255 + }, + "email": { + "type": "string", + "description": "The customer's email address", + "example": "JonTest@test.com", + "nullable": true, + "maxLength": 255 + }, + "phone": { + "type": "string", + "description": "The customer's phone number", + "example": "9123456789", + "nullable": true, + "maxLength": 255 + }, + "description": { + "type": "string", + "description": "An arbitrary string that you can attach to a customer object.", + "example": "First Customer", + "nullable": true, + "maxLength": 255 + }, + "phone_country_code": { + "type": "string", + "description": "The country code for the customer phone number", + "example": "+65", + "nullable": true, + "maxLength": 255 + }, + "address": { + "allOf": [ + { + "$ref": "#/components/schemas/AddressDetails" + } + ], + "nullable": true + }, + "metadata": { + "type": "object", + "description": "You can specify up to 50 keys, with key names up to 40 characters long and values up to 500\ncharacters long. Metadata is useful for storing additional, structured information on an\nobject.", + "nullable": true + } + } + }, "DecoupledAuthenticationType": { "type": "string", "enum": [ diff --git a/crates/api_models/src/customers.rs b/crates/api_models/src/customers.rs index 4f6411a42c..fe4ba7d968 100644 --- a/crates/api_models/src/customers.rs +++ b/crates/api_models/src/customers.rs @@ -71,6 +71,7 @@ impl CustomerRequest { /// The customer details #[cfg(all(feature = "v2", feature = "customer_v2"))] #[derive(Debug, Default, Clone, Deserialize, Serialize, ToSchema)] +#[serde(deny_unknown_fields)] pub struct CustomerRequest { /// The merchant identifier for the customer object. #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] @@ -170,6 +171,14 @@ impl CustomerResponse { #[cfg(all(feature = "v2", feature = "customer_v2"))] #[derive(Debug, Clone, Serialize, ToSchema)] pub struct CustomerResponse { + /// Unique identifier for the customer + #[schema( + min_length = 32, + max_length = 64, + example = "12345_cus_01926c58bc6e77c09e809964e72af8c8", + value_type = String + )] + pub id: id_type::GlobalCustomerId, /// The identifier for the customer object #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] pub merchant_reference_id: Option, @@ -206,8 +215,6 @@ pub struct CustomerResponse { /// The identifier for the default payment method. #[schema(max_length = 64, example = "pm_djh2837dwduh890123")] pub default_payment_method_id: Option, - /// Global id - pub id: String, } #[cfg(all(feature = "v2", feature = "customer_v2"))] @@ -217,55 +224,6 @@ impl CustomerResponse { } } -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct CustomerId { - pub customer_id: id_type::CustomerId, -} - -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -impl CustomerId { - pub fn get_merchant_reference_id(&self) -> id_type::CustomerId { - self.customer_id.clone() - } - - pub fn new_customer_id_struct(cust: id_type::CustomerId) -> Self { - Self { customer_id: cust } - } -} - -#[cfg(all(feature = "v2", feature = "customer_v2"))] -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct GlobalId { - pub id: String, -} - -#[cfg(all(feature = "v2", feature = "customer_v2"))] -impl GlobalId { - pub fn new(id: String) -> Self { - Self { id } - } -} - -#[cfg(all(feature = "v2", feature = "customer_v2"))] -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct CustomerId { - pub merchant_reference_id: id_type::CustomerId, -} - -#[cfg(all(feature = "v2", feature = "customer_v2"))] -impl CustomerId { - pub fn get_merchant_reference_id(&self) -> id_type::CustomerId { - self.merchant_reference_id.clone() - } - - pub fn new_customer_id_struct(cust: id_type::CustomerId) -> Self { - Self { - merchant_reference_id: cust, - } - } -} - #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[derive(Debug, Deserialize, Serialize, ToSchema)] pub struct CustomerDeleteResponse { @@ -286,6 +244,14 @@ pub struct CustomerDeleteResponse { #[cfg(all(feature = "v2", feature = "customer_v2"))] #[derive(Debug, Deserialize, Serialize, ToSchema)] pub struct CustomerDeleteResponse { + /// Unique identifier for the customer + #[schema( + min_length = 32, + max_length = 64, + example = "12345_cus_01926c58bc6e77c09e809964e72af8c8", + value_type = String + )] + pub id: id_type::GlobalCustomerId, /// The identifier for the customer object #[schema(value_type = String, max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] pub merchant_reference_id: Option, @@ -298,17 +264,12 @@ pub struct CustomerDeleteResponse { /// Whether payment methods deleted or not #[schema(example = false)] pub payment_methods_deleted: bool, - /// Global id - pub id: String, } /// The identifier for the customer object. If not provided the customer ID will be autogenerated. #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[derive(Debug, Default, Clone, Deserialize, Serialize, ToSchema)] pub struct CustomerUpdateRequest { - /// The identifier for the customer object - #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: Option, /// The identifier for the Merchant Account #[schema(max_length = 255, example = "y3oqhf46pyzuxjbcn2giaqnb44")] #[serde(skip)] @@ -340,13 +301,6 @@ pub struct CustomerUpdateRequest { #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] impl CustomerUpdateRequest { - pub fn get_merchant_reference_id(&self) -> Option { - Some( - self.customer_id - .to_owned() - .unwrap_or_else(common_utils::generate_customer_id_of_default_length), - ) - } pub fn get_address(&self) -> Option { self.address.clone() } @@ -354,6 +308,7 @@ impl CustomerUpdateRequest { #[cfg(all(feature = "v2", feature = "customer_v2"))] #[derive(Debug, Default, Clone, Deserialize, Serialize, ToSchema)] +#[serde(deny_unknown_fields)] pub struct CustomerUpdateRequest { /// The merchant identifier for the customer object. #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] @@ -404,15 +359,16 @@ impl CustomerUpdateRequest { } } -#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone)] -pub struct UpdateCustomerId(String); - -impl UpdateCustomerId { - pub fn get_global_id(&self) -> String { - self.0.clone() - } - - pub fn new(id: String) -> Self { - Self(id) - } +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +#[derive(Debug, Serialize)] +pub struct CustomerUpdateRequestInternal { + pub customer_id: id_type::CustomerId, + pub request: CustomerUpdateRequest, +} + +#[cfg(all(feature = "v2", feature = "customer_v2"))] +#[derive(Debug, Serialize)] +pub struct CustomerUpdateRequestInternal { + pub id: id_type::GlobalCustomerId, + pub request: CustomerUpdateRequest, } diff --git a/crates/api_models/src/ephemeral_key.rs b/crates/api_models/src/ephemeral_key.rs index d7ee7bd251..d06490d6ba 100644 --- a/crates/api_models/src/ephemeral_key.rs +++ b/crates/api_models/src/ephemeral_key.rs @@ -2,6 +2,25 @@ use common_utils::id_type; use serde; use utoipa::ToSchema; +/// 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 = 1, + max_length = 64, + value_type = String, + example = "cus_y3oqhf46pyzuxjbcn2giaqnb44" + )] + pub customer_id: id_type::CustomerId, +} + +impl common_utils::events::ApiEventMetric for EphemeralKeyCreateRequest { + 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/api_models/src/events/customer.rs b/crates/api_models/src/events/customer.rs index d2b30bcf01..891fb0e9d3 100644 --- a/crates/api_models/src/events/customer.rs +++ b/crates/api_models/src/events/customer.rs @@ -1,12 +1,6 @@ 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; -use crate::customers::{ - CustomerDeleteResponse, CustomerRequest, CustomerResponse, CustomerUpdateRequest, -}; +use crate::customers::{CustomerDeleteResponse, CustomerRequest, CustomerResponse}; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] impl ApiEventMetric for CustomerDeleteResponse { @@ -21,7 +15,7 @@ impl ApiEventMetric for CustomerDeleteResponse { impl ApiEventMetric for CustomerDeleteResponse { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Customer { - id: self.id.clone(), + customer_id: Some(self.id.clone()), }) } } @@ -38,9 +32,7 @@ impl ApiEventMetric for CustomerRequest { #[cfg(all(feature = "v2", feature = "customer_v2"))] impl ApiEventMetric for CustomerRequest { fn get_api_event_type(&self) -> Option { - Some(ApiEventsType::Customer { - id: "temp_id".to_string(), - }) + Some(ApiEventsType::Customer { customer_id: None }) } } @@ -57,44 +49,25 @@ impl ApiEventMetric for CustomerResponse { impl ApiEventMetric for CustomerResponse { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Customer { - id: self.id.clone(), + customer_id: Some(self.id.clone()), }) } } #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -impl ApiEventMetric for CustomerId { +impl ApiEventMetric for crate::customers::CustomerUpdateRequestInternal { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Customer { - customer_id: self.get_merchant_reference_id().clone(), + customer_id: self.customer_id.clone(), }) } } #[cfg(all(feature = "v2", feature = "customer_v2"))] -impl ApiEventMetric for GlobalId { +impl ApiEventMetric for crate::customers::CustomerUpdateRequestInternal { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Customer { - id: self.id.clone(), + customer_id: Some(self.id.clone()), }) } } - -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -impl ApiEventMetric for CustomerUpdateRequest { - fn get_api_event_type(&self) -> Option { - self.get_merchant_reference_id() - .clone() - .map(|cid| ApiEventsType::Customer { customer_id: cid }) - } -} - -#[cfg(all(feature = "v2", feature = "customer_v2"))] -impl ApiEventMetric for CustomerUpdateRequest { - fn get_api_event_type(&self) -> Option { - Some(ApiEventsType::Customer { - id: "temo_id".to_string(), - }) - } -} -// These needs to be fixed for v2 diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 283a0d662d..c821feaff5 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -121,8 +121,13 @@ pub struct PaymentMethodCreate { pub metadata: Option, /// The unique identifier of the customer. - #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: id_type::CustomerId, + #[schema( + min_length = 32, + max_length = 64, + example = "12345_cus_01926c58bc6e77c09e809964e72af8c8", + value_type = String + )] + pub customer_id: id_type::GlobalCustomerId, /// Payment method data to be passed pub payment_method_data: PaymentMethodCreateData, @@ -145,8 +150,13 @@ pub struct PaymentMethodIntentCreate { pub billing: Option, /// The unique identifier of the customer. - #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: id_type::CustomerId, + #[schema( + min_length = 32, + max_length = 64, + example = "12345_cus_01926c58bc6e77c09e809964e72af8c8", + value_type = String + )] + pub customer_id: id_type::GlobalCustomerId, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] @@ -782,8 +792,13 @@ pub struct PaymentMethodResponse { pub merchant_id: id_type::MerchantId, /// The unique identifier of the customer. - #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: id_type::CustomerId, + #[schema( + min_length = 32, + max_length = 64, + example = "12345_cus_01926c58bc6e77c09e809964e72af8c8", + value_type = String + )] + pub customer_id: id_type::GlobalCustomerId, /// The unique identifier of the Payment method #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")] @@ -1780,8 +1795,13 @@ pub struct CustomerPaymentMethod { pub payment_method_id: String, /// The unique identifier of the customer. - #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: id_type::CustomerId, + #[schema( + min_length = 32, + max_length = 64, + example = "12345_cus_01926c58bc6e77c09e809964e72af8c8", + value_type = String + )] + pub customer_id: id_type::GlobalCustomerId, /// The type of payment method use for the payment. #[schema(value_type = PaymentMethod,example = "card")] @@ -2253,36 +2273,6 @@ impl From for PaymentMethodMigrationResponse } } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -impl From for PaymentMethodMigrationResponse { - fn from((response, record): PaymentMethodMigrationResponseType) -> Self { - match response { - Ok(res) => Self { - payment_method_id: Some(res.payment_method_response.payment_method_id), - payment_method: res.payment_method_response.payment_method_type, - payment_method_type: res.payment_method_response.payment_method_subtype, - customer_id: Some(res.payment_method_response.customer_id), - migration_status: MigrationStatus::Success, - migration_error: None, - card_number_masked: Some(record.card_number_masked), - line_number: record.line_number, - card_migrated: res.card_migrated, - network_token_migrated: res.network_token_migrated, - connector_mandate_details_migrated: res.connector_mandate_details_migrated, - network_transaction_id_migrated: res.network_transaction_id_migrated, - }, - Err(e) => Self { - customer_id: Some(record.customer_id), - migration_status: MigrationStatus::Failed, - migration_error: Some(e), - card_number_masked: Some(record.card_number_masked), - line_number: record.line_number, - ..Self::default() - }, - } - } -} - impl TryFrom<( PaymentMethodRecord, diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 2f08ba830f..0381e347f4 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -153,8 +153,13 @@ pub struct PaymentsCreateIntentRequest { pub shipping: Option
, /// The identifier for the customer - #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: Option, + #[schema( + min_length = 32, + max_length = 64, + example = "12345_cus_01926c58bc6e77c09e809964e72af8c8", + value_type = String + )] + pub customer_id: Option, /// Set to `present` to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be `absent` when merchant's doing merchant initiated payments and customer is not present while doing the payment. #[schema(example = "present", value_type = Option)] @@ -442,8 +447,13 @@ pub struct PaymentsIntentResponse { pub shipping: Option
, /// The identifier for the customer - #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: Option, + #[schema( + min_length = 32, + max_length = 64, + example = "12345_cus_01926c58bc6e77c09e809964e72af8c8", + value_type = String + )] + pub customer_id: Option, /// Set to `present` to indicate that the customer is in your checkout flow during this payment, and therefore is able to authenticate. This parameter should be `absent` when merchant's doing merchant initiated payments and customer is not present while doing the payment. #[schema(example = "present", value_type = PresenceOfCustomerDuringPayment)] diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index 3b86ea8e88..b9af92786f 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -42,7 +42,7 @@ pub enum ApiEventsType { PaymentMethodCreate, #[cfg(all(feature = "v2", feature = "customer_v2"))] Customer { - id: String, + customer_id: Option, }, #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] Customer { diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs index e0aa77ce70..594078bb40 100644 --- a/crates/common_utils/src/id_type.rs +++ b/crates/common_utils/src/id_type.rs @@ -1,10 +1,10 @@ //! Common ID types //! The id type can be used to create specific id types with custom behaviour -use std::{borrow::Cow, fmt::Debug}; - mod api_key; mod customer; +#[cfg(feature = "v2")] +mod global_id; mod merchant; mod merchant_connector_account; mod organization; @@ -14,11 +14,8 @@ mod refunds; mod routing; mod tenant; -#[cfg(feature = "v2")] -mod global_id; +use std::{borrow::Cow, fmt::Debug}; -pub use api_key::ApiKeyId; -pub use customer::CustomerId; use diesel::{ backend::Backend, deserialize::FromSql, @@ -26,24 +23,29 @@ use diesel::{ serialize::{Output, ToSql}, sql_types, }; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + #[cfg(feature = "v2")] -pub use global_id::{ +pub use self::global_id::{ + customer::GlobalCustomerId, payment::{GlobalAttemptId, GlobalPaymentId}, payment_methods::GlobalPaymentMethodId, refunds::GlobalRefundId, CellId, }; -pub use merchant::MerchantId; -pub use merchant_connector_account::MerchantConnectorAccountId; -pub use organization::OrganizationId; -pub use payment::{PaymentId, PaymentReferenceId}; -pub use profile::ProfileId; -pub use refunds::RefundReferenceId; -pub use routing::RoutingId; -use serde::{Deserialize, Serialize}; -pub use tenant::TenantId; -use thiserror::Error; - +pub use self::{ + api_key::ApiKeyId, + customer::CustomerId, + merchant::MerchantId, + merchant_connector_account::MerchantConnectorAccountId, + organization::OrganizationId, + payment::{PaymentId, PaymentReferenceId}, + profile::ProfileId, + refunds::RefundReferenceId, + routing::RoutingId, + tenant::TenantId, +}; use crate::{fp_utils::when, generate_id_with_default_len}; #[inline] diff --git a/crates/common_utils/src/id_type/customer.rs b/crates/common_utils/src/id_type/customer.rs index 9d02f20138..54c44020b8 100644 --- a/crates/common_utils/src/id_type/customer.rs +++ b/crates/common_utils/src/id_type/customer.rs @@ -13,3 +13,12 @@ crate::impl_generate_id_id_type!(CustomerId, "cus"); crate::impl_serializable_secret_id_type!(CustomerId); crate::impl_queryable_id_type!(CustomerId); crate::impl_to_sql_from_sql_id_type!(CustomerId); + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +impl crate::events::ApiEventMetric for CustomerId { + fn get_api_event_type(&self) -> Option { + Some(crate::events::ApiEventsType::Customer { + customer_id: self.clone(), + }) + } +} diff --git a/crates/common_utils/src/id_type/global_id.rs b/crates/common_utils/src/id_type/global_id.rs index d783912459..1ad1bd9608 100644 --- a/crates/common_utils/src/id_type/global_id.rs +++ b/crates/common_utils/src/id_type/global_id.rs @@ -1,6 +1,7 @@ -pub mod payment; -pub mod payment_methods; -pub mod refunds; +pub(super) mod customer; +pub(super) mod payment; +pub(super) mod payment_methods; +pub(super) mod refunds; use diesel::{backend::Backend, deserialize::FromSql, serialize::ToSql, sql_types}; use error_stack::ResultExt; diff --git a/crates/common_utils/src/id_type/global_id/customer.rs b/crates/common_utils/src/id_type/global_id/customer.rs new file mode 100644 index 0000000000..e0de91d8ae --- /dev/null +++ b/crates/common_utils/src/id_type/global_id/customer.rs @@ -0,0 +1,45 @@ +use error_stack::ResultExt; + +use crate::{errors, generate_id_with_default_len, generate_time_ordered_id_without_prefix, types}; + +crate::global_id_type!( + GlobalCustomerId, + "A global id that can be used to identify a customer. + +The format will be `__`. + +Example: `cell1_cus_uu1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p`" +); + +// Database related implementations so that this field can be used directly in the database tables +crate::impl_queryable_id_type!(GlobalCustomerId); +crate::impl_to_sql_from_sql_global_id_type!(GlobalCustomerId); + +impl GlobalCustomerId { + /// Get string representation of the id + pub fn get_string_repr(&self) -> &str { + self.0.get_string_repr() + } + + /// Generate a new GlobalCustomerId from a cell id + pub fn generate(cell_id: &crate::id_type::CellId) -> Self { + let global_id = super::GlobalId::generate(cell_id, super::GlobalEntity::Customer); + Self(global_id) + } +} + +impl TryFrom for crate::id_type::CustomerId { + type Error = error_stack::Report; + + fn try_from(value: GlobalCustomerId) -> Result { + Self::try_from(std::borrow::Cow::from(value.get_string_repr().to_owned())) + } +} + +impl crate::events::ApiEventMetric for GlobalCustomerId { + fn get_api_event_type(&self) -> Option { + Some(crate::events::ApiEventsType::Customer { + customer_id: Some(self.clone()), + }) + } +} diff --git a/crates/common_utils/src/id_type/global_id/payment.rs b/crates/common_utils/src/id_type/global_id/payment.rs index 5a2da3998b..c65fd06f06 100644 --- a/crates/common_utils/src/id_type/global_id/payment.rs +++ b/crates/common_utils/src/id_type/global_id/payment.rs @@ -4,9 +4,11 @@ use crate::{errors, generate_id_with_default_len, generate_time_ordered_id_witho crate::global_id_type!( GlobalPaymentId, - "A global id that can be used to identify a payment - The format will be `__` - example - cell1_pay_uu1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p" + "A global id that can be used to identify a payment. + +The format will be `__`. + +Example: `cell1_pay_uu1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p`" ); // Database related implementations so that this field can be used directly in the database tables diff --git a/crates/diesel_models/src/customers.rs b/crates/diesel_models/src/customers.rs index 46b5059b42..7bd7d8368a 100644 --- a/crates/diesel_models/src/customers.rs +++ b/crates/diesel_models/src/customers.rs @@ -91,7 +91,7 @@ pub struct CustomerNew { pub default_billing_address: Option, pub default_shipping_address: Option, pub status: DeleteStatus, - pub id: String, + pub id: common_utils::id_type::GlobalCustomerId, } #[cfg(all(feature = "v2", feature = "customer_v2"))] @@ -173,7 +173,7 @@ pub struct Customer { pub default_billing_address: Option, pub default_shipping_address: Option, pub status: DeleteStatus, - pub id: String, + pub id: common_utils::id_type::GlobalCustomerId, } #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] diff --git a/crates/diesel_models/src/kv.rs b/crates/diesel_models/src/kv.rs index 8015927129..94dcf00f75 100644 --- a/crates/diesel_models/src/kv.rs +++ b/crates/diesel_models/src/kv.rs @@ -179,7 +179,7 @@ impl DBOperation { )), #[cfg(all(feature = "v2", feature = "customer_v2"))] Updateable::CustomerUpdate(cust) => DBResult::Customer(Box::new( - Customer::update_by_id(conn, cust.orig.id.clone(), cust.update_data).await?, + Customer::update_by_id(conn, cust.orig.id, cust.update_data).await?, )), }, }) diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 69743dbf1c..892e8867ab 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -21,7 +21,7 @@ pub struct PaymentIntent { pub amount: MinorUnit, pub currency: storage_enums::Currency, pub amount_captured: Option, - pub customer_id: Option, + pub customer_id: Option, pub description: Option, pub return_url: Option, pub metadata: Option, @@ -251,7 +251,7 @@ pub struct PaymentIntentNew { pub amount: MinorUnit, pub currency: storage_enums::Currency, pub amount_captured: Option, - pub customer_id: Option, + pub customer_id: Option, pub description: Option, pub return_url: Option, pub metadata: Option, diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 206ba63e17..46af78d6d9 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -70,7 +70,7 @@ pub struct PaymentMethod { #[derive(Clone, Debug, Identifiable, Queryable, Selectable, Serialize, Deserialize)] #[diesel(table_name = payment_methods, primary_key(id), check_for_backend(diesel::pg::Pg))] pub struct PaymentMethod { - pub customer_id: common_utils::id_type::CustomerId, + pub customer_id: common_utils::id_type::GlobalCustomerId, pub merchant_id: common_utils::id_type::MerchantId, pub created_at: PrimitiveDateTime, pub last_modified: PrimitiveDateTime, @@ -158,7 +158,7 @@ pub struct PaymentMethodNew { #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay, Serialize, Deserialize)] #[diesel(table_name = payment_methods)] pub struct PaymentMethodNew { - pub customer_id: common_utils::id_type::CustomerId, + pub customer_id: common_utils::id_type::GlobalCustomerId, pub merchant_id: common_utils::id_type::MerchantId, pub created_at: PrimitiveDateTime, pub last_modified: PrimitiveDateTime, diff --git a/crates/diesel_models/src/query/customers.rs b/crates/diesel_models/src/query/customers.rs index cf6e8fa645..5caad88c8d 100644 --- a/crates/diesel_models/src/query/customers.rs +++ b/crates/diesel_models/src/query/customers.rs @@ -33,7 +33,7 @@ impl Customer { #[cfg(all(feature = "v2", feature = "customer_v2"))] pub async fn update_by_id( conn: &PgPooledConn, - id: String, + id: id_type::GlobalCustomerId, customer: CustomerUpdateInternal, ) -> StorageResult { match generics::generic_update_by_id::<::Table, _, _, _>( @@ -54,7 +54,10 @@ impl Customer { } #[cfg(all(feature = "v2", feature = "customer_v2"))] - pub async fn find_by_global_id(conn: &PgPooledConn, id: &str) -> StorageResult { + pub async fn find_by_global_id( + conn: &PgPooledConn, + id: &id_type::GlobalCustomerId, + ) -> StorageResult { generics::generic_find_by_id::<::Table, _, _>(conn, id.to_owned()).await } diff --git a/crates/diesel_models/src/query/mandate.rs b/crates/diesel_models/src/query/mandate.rs index 9f5a402c17..96e6850f0f 100644 --- a/crates/diesel_models/src/query/mandate.rs +++ b/crates/diesel_models/src/query/mandate.rs @@ -63,8 +63,23 @@ impl Mandate { //Fix this function once V2 mandate is schema is being built #[cfg(all(feature = "v2", feature = "customer_v2"))] - pub async fn find_by_global_id(_conn: &PgPooledConn, _id: &str) -> StorageResult> { - todo!() + pub async fn find_by_global_customer_id( + conn: &PgPooledConn, + customer_id: &common_utils::id_type::GlobalCustomerId, + ) -> StorageResult> { + generics::generic_filter::< + ::Table, + _, + <::Table as Table>::PrimaryKey, + _, + >( + conn, + dsl::customer_id.eq(customer_id.to_owned()), + None, + None, + None, + ) + .await } pub async fn update_by_merchant_id_mandate_id( diff --git a/crates/diesel_models/src/query/payment_method.rs b/crates/diesel_models/src/query/payment_method.rs index 321c7a6829..d4ad52ae14 100644 --- a/crates/diesel_models/src/query/payment_method.rs +++ b/crates/diesel_models/src/query/payment_method.rs @@ -126,16 +126,6 @@ impl PaymentMethod { .await } - // Need to fix this function once we start moving to v2 for payment method - #[cfg(all(feature = "v2", feature = "customer_v2"))] - pub async fn find_by_global_id( - _conn: &PgPooledConn, - _id: &str, - _limit: Option, - ) -> StorageResult> { - todo!() - } - pub async fn get_count_by_customer_id_merchant_id_status( conn: &PgPooledConn, customer_id: &common_utils::id_type::CustomerId, @@ -219,9 +209,9 @@ impl PaymentMethod { .await } - pub async fn find_by_customer_id_merchant_id_status( + pub async fn find_by_global_customer_id_merchant_id_status( conn: &PgPooledConn, - customer_id: &common_utils::id_type::CustomerId, + customer_id: &common_utils::id_type::GlobalCustomerId, merchant_id: &common_utils::id_type::MerchantId, status: storage_enums::PaymentMethodStatus, limit: Option, @@ -239,6 +229,21 @@ impl PaymentMethod { .await } + pub async fn find_by_global_customer_id( + conn: &PgPooledConn, + customer_id: &common_utils::id_type::GlobalCustomerId, + limit: Option, + ) -> StorageResult> { + generics::generic_filter::<::Table, _, _, _>( + conn, + dsl::customer_id.eq(customer_id.to_owned()), + limit, + None, + Some(dsl::last_used_at.desc()), + ) + .await + } + pub async fn update_with_id( self, conn: &PgPooledConn, diff --git a/crates/hyperswitch_domain_models/src/customer.rs b/crates/hyperswitch_domain_models/src/customer.rs index 71cb4ebc2f..b503326f84 100644 --- a/crates/hyperswitch_domain_models/src/customer.rs +++ b/crates/hyperswitch_domain_models/src/customer.rs @@ -63,11 +63,25 @@ pub struct Customer { pub merchant_reference_id: Option, pub default_billing_address: Option, pub default_shipping_address: Option, - pub id: String, + pub id: id_type::GlobalCustomerId, pub version: common_enums::ApiVersion, pub status: DeleteStatus, } +impl Customer { + /// Get the unique identifier of Customer + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] + pub fn get_id(&self) -> &id_type::CustomerId { + &self.customer_id + } + + /// Get the global identifier of Customer + #[cfg(all(feature = "v2", feature = "customer_v2"))] + pub fn get_id(&self) -> &id_type::GlobalCustomerId { + &self.id + } +} + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[async_trait::async_trait] impl super::behaviour::Conversion for Customer { diff --git a/crates/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs index 4e7d727839..d03762dd64 100644 --- a/crates/hyperswitch_domain_models/src/payment_methods.rs +++ b/crates/hyperswitch_domain_models/src/payment_methods.rs @@ -74,7 +74,7 @@ pub struct PaymentMethod { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Clone, Debug)] pub struct PaymentMethod { - pub customer_id: common_utils::id_type::CustomerId, + pub customer_id: common_utils::id_type::GlobalCustomerId, pub merchant_id: common_utils::id_type::MerchantId, pub created_at: PrimitiveDateTime, pub last_modified: PrimitiveDateTime, diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index 9488a762a6..0243c25492 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -281,7 +281,7 @@ pub struct PaymentIntent { /// The total amount captured for the order. This is the sum of all the captured amounts for the order. pub amount_captured: Option, /// The identifier for the customer. This is the identifier for the customer in the merchant's system. - pub customer_id: Option, + pub customer_id: Option, /// The description of the order. This will be passed to connectors which support description. pub description: Option, /// The return url for the payment. This is the url to which the user will be redirected after the payment is completed. diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index 7ad5645e75..db0db93109 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -235,6 +235,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::admin::BusinessCollectLinkConfig, api_models::admin::BusinessPayoutLinkConfig, api_models::customers::CustomerRequest, + api_models::customers::CustomerUpdateRequest, api_models::customers::CustomerDeleteResponse, api_models::payment_methods::PaymentMethodCreate, api_models::payment_methods::PaymentMethodResponse, diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs index 5a5454e250..b5de979b3a 100644 --- a/crates/openapi/src/openapi_v2.rs +++ b/crates/openapi/src/openapi_v2.rs @@ -187,6 +187,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::admin::BusinessCollectLinkConfig, api_models::admin::BusinessPayoutLinkConfig, api_models::customers::CustomerRequest, + api_models::customers::CustomerUpdateRequest, api_models::customers::CustomerDeleteResponse, api_models::payment_methods::PaymentMethodCreate, api_models::payment_methods::PaymentMethodIntentCreate, diff --git a/crates/openapi/src/routes/customers.rs b/crates/openapi/src/routes/customers.rs index b4c6123075..f23b8b3495 100644 --- a/crates/openapi/src/routes/customers.rs +++ b/crates/openapi/src/routes/customers.rs @@ -51,7 +51,7 @@ pub async fn customers_retrieve() {} post, path = "/customers/{customer_id}", request_body ( - content = CustomerRequest, + content = CustomerUpdateRequest, examples (( "Update name and email of a customer" =( value =json!( { "email": "guest@example.com", @@ -159,7 +159,7 @@ pub async fn customers_retrieve() {} post, path = "/v2/customers/{id}", request_body ( - content = CustomerRequest, + content = CustomerUpdateRequest, examples (( "Update name and email of a customer" =( value =json!( { "email": "guest@example.com", diff --git a/crates/router/src/compatibility/stripe/customers.rs b/crates/router/src/compatibility/stripe/customers.rs index 190e25d71c..56cf25434c 100644 --- a/crates/router/src/compatibility/stripe/customers.rs +++ b/crates/router/src/compatibility/stripe/customers.rs @@ -74,7 +74,7 @@ pub async fn customer_retrieve( req: HttpRequest, path: web::Path, ) -> HttpResponse { - let payload = customer_types::CustomerId::new_customer_id_struct(path.into_inner()); + let customer_id = path.into_inner(); let flow = Flow::CustomersRetrieve; @@ -91,9 +91,15 @@ pub async fn customer_retrieve( flow, state.into_inner(), &req, - payload, - |state, auth: auth::AuthenticationData, req, _| { - customers::retrieve_customer(state, auth.merchant_account, None, auth.key_store, req) + customer_id, + |state, auth: auth::AuthenticationData, customer_id, _| { + customers::retrieve_customer( + state, + auth.merchant_account, + None, + auth.key_store, + customer_id, + ) }, &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, @@ -121,10 +127,12 @@ pub async fn customer_update( } }; - let customer_id = path.into_inner(); - let mut cust_update_req: customer_types::CustomerUpdateRequest = payload.into(); - cust_update_req.customer_id = Some(customer_id); - let customer_update_id = customer_types::UpdateCustomerId::new("temp_global_id".to_string()); + let customer_id = path.into_inner().clone(); + let request = customer_types::CustomerUpdateRequest::from(payload); + let request_internal = customer_types::CustomerUpdateRequestInternal { + customer_id, + request, + }; let flow = Flow::CustomersUpdate; @@ -141,14 +149,13 @@ pub async fn customer_update( flow, state.into_inner(), &req, - cust_update_req, - |state, auth: auth::AuthenticationData, req, _| { + request_internal, + |state, auth: auth::AuthenticationData, request_internal, _| { customers::update_customer( state, auth.merchant_account, - req, + request_internal, auth.key_store, - customer_update_id.clone(), ) }, &auth::HeaderAuth(auth::ApiKeyAuth), @@ -168,7 +175,7 @@ pub async fn customer_delete( req: HttpRequest, path: web::Path, ) -> HttpResponse { - let payload = customer_types::CustomerId::new_customer_id_struct(path.into_inner()); + let customer_id = path.into_inner(); let flow = Flow::CustomersDelete; @@ -185,9 +192,9 @@ pub async fn customer_delete( flow, state.into_inner(), &req, - payload, - |state, auth: auth::AuthenticationData, req, _| { - customers::delete_customer(state, auth.merchant_account, req, auth.key_store) + customer_id, + |state, auth: auth::AuthenticationData, customer_id, _| { + customers::delete_customer(state, auth.merchant_account, customer_id, auth.key_store) }, &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 3081617f3e..059ac4c2f3 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -1,7 +1,7 @@ use common_utils::{ crypto::Encryptable, errors::ReportSwitchExt, - ext_traits::{AsyncExt, OptionExt}, + ext_traits::AsyncExt, id_type, pii, type_name, types::{ keymanager::{Identifier, KeyManagerState, ToEncryptable}, @@ -264,7 +264,7 @@ impl CustomerCreateBridge for customers::CustomerRequest { .change_context(errors::CustomersErrorResponse::InternalServerError)?; Ok(domain::Customer { - id: common_utils::generate_time_ordered_id("cus"), + id: id_type::GlobalCustomerId::generate(&state.conf.cell_information.id), merchant_reference_id: merchant_reference_id.to_owned(), merchant_id, name: encryptable_customer.name, @@ -457,7 +457,7 @@ pub async fn retrieve_customer( merchant_account: domain::MerchantAccount, _profile_id: Option, key_store: domain::MerchantKeyStore, - req: customers::CustomerId, + customer_id: id_type::CustomerId, ) -> errors::CustomerResponse { let db = state.store.as_ref(); let key_manager_state = &(&state).into(); @@ -465,7 +465,7 @@ pub async fn retrieve_customer( let response = db .find_customer_by_customer_id_merchant_id( key_manager_state, - &req.customer_id, + &customer_id, merchant_account.get_id(), &key_store, merchant_account.storage_scheme, @@ -491,7 +491,7 @@ pub async fn retrieve_customer( state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, - req: customers::GlobalId, + id: id_type::GlobalCustomerId, ) -> errors::CustomerResponse { let db = state.store.as_ref(); let key_manager_state = &(&state).into(); @@ -499,7 +499,7 @@ pub async fn retrieve_customer( let response = db .find_customer_by_global_id( key_manager_state, - &req.id, + &id, merchant_account.get_id(), &key_store, merchant_account.storage_scheme, @@ -563,12 +563,12 @@ pub async fn list_customers( pub async fn delete_customer( state: SessionState, merchant_account: domain::MerchantAccount, - req: customers::GlobalId, + id: id_type::GlobalCustomerId, key_store: domain::MerchantKeyStore, ) -> errors::CustomerResponse { let db = &*state.store; let key_manager_state = &(&state).into(); - req.fetch_domain_model_and_update_and_generate_delete_customer_response( + id.fetch_domain_model_and_update_and_generate_delete_customer_response( db, &key_store, &merchant_account, @@ -584,7 +584,7 @@ pub async fn delete_customer( feature = "payment_methods_v2" ))] #[async_trait::async_trait] -impl CustomerDeleteBridge for customers::GlobalId { +impl CustomerDeleteBridge for id_type::GlobalCustomerId { async fn fetch_domain_model_and_update_and_generate_delete_customer_response<'a>( &'a self, db: &'a dyn StorageInterface, @@ -596,7 +596,7 @@ impl CustomerDeleteBridge for customers::GlobalId { let customer_orig = db .find_customer_by_global_id( key_manager_state, - &self.id, + self, merchant_account.get_id(), key_store, merchant_account.storage_scheme, @@ -606,7 +606,7 @@ impl CustomerDeleteBridge for customers::GlobalId { let merchant_reference_id = customer_orig.merchant_reference_id.clone(); - let customer_mandates = db.find_mandate_by_global_id(&self.id).await.switch()?; + let customer_mandates = db.find_mandate_by_global_customer_id(self).await.switch()?; for mandate in customer_mandates.into_iter() { if mandate.mandate_status == enums::MandateStatus::Active { @@ -615,14 +615,19 @@ impl CustomerDeleteBridge for customers::GlobalId { } match db - .find_payment_method_list_by_global_id(key_manager_state, key_store, &self.id, None) + .find_payment_method_list_by_global_customer_id( + key_manager_state, + key_store, + self, + None, + ) .await { // check this in review Ok(customer_payment_methods) => { for pm in customer_payment_methods.into_iter() { - if pm.payment_method_type_v2 == Some(enums::PaymentMethod::Card) { - cards::delete_card_by_locker_id(state, &self.id, merchant_account.get_id()) + if pm.get_payment_method_type() == Some(enums::PaymentMethod::Card) { + cards::delete_card_by_locker_id(state, self, merchant_account.get_id()) .await .switch()?; } @@ -688,7 +693,7 @@ impl CustomerDeleteBridge for customers::GlobalId { db.update_customer_by_global_id( key_manager_state, - self.id.clone(), + self, customer_orig, merchant_account.get_id(), updated_customer, @@ -699,11 +704,11 @@ impl CustomerDeleteBridge for customers::GlobalId { .switch()?; let response = customers::CustomerDeleteResponse { + id: self.clone(), merchant_reference_id, customer_deleted: true, address_deleted: true, payment_methods_deleted: true, - id: self.id.clone(), }; metrics::CUSTOMER_REDACTED.add(1, &[]); Ok(services::ApplicationResponse::Json(response)) @@ -731,19 +736,20 @@ trait CustomerDeleteBridge { pub async fn delete_customer( state: SessionState, merchant_account: domain::MerchantAccount, - req: customers::CustomerId, + customer_id: id_type::CustomerId, key_store: domain::MerchantKeyStore, ) -> errors::CustomerResponse { let db = &*state.store; let key_manager_state = &(&state).into(); - req.fetch_domain_model_and_update_and_generate_delete_customer_response( - db, - &key_store, - &merchant_account, - key_manager_state, - &state, - ) - .await + customer_id + .fetch_domain_model_and_update_and_generate_delete_customer_response( + db, + &key_store, + &merchant_account, + key_manager_state, + &state, + ) + .await } #[cfg(all( @@ -752,7 +758,7 @@ pub async fn delete_customer( not(feature = "payment_methods_v2") ))] #[async_trait::async_trait] -impl CustomerDeleteBridge for customers::CustomerId { +impl CustomerDeleteBridge for id_type::CustomerId { async fn fetch_domain_model_and_update_and_generate_delete_customer_response<'a>( &'a self, db: &'a dyn StorageInterface, @@ -764,7 +770,7 @@ impl CustomerDeleteBridge for customers::CustomerId { let customer_orig = db .find_customer_by_customer_id_merchant_id( key_manager_state, - &self.customer_id, + self, merchant_account.get_id(), key_store, merchant_account.storage_scheme, @@ -773,7 +779,7 @@ impl CustomerDeleteBridge for customers::CustomerId { .switch()?; let customer_mandates = db - .find_mandate_by_merchant_id_customer_id(merchant_account.get_id(), &self.customer_id) + .find_mandate_by_merchant_id_customer_id(merchant_account.get_id(), self) .await .switch()?; @@ -787,7 +793,7 @@ impl CustomerDeleteBridge for customers::CustomerId { .find_payment_method_by_customer_id_merchant_id_list( key_manager_state, key_store, - &self.customer_id, + self, merchant_account.get_id(), None, ) @@ -799,7 +805,7 @@ impl CustomerDeleteBridge for customers::CustomerId { if pm.get_payment_method_type() == Some(enums::PaymentMethod::Card) { cards::delete_card_from_locker( state, - &self.customer_id, + self, merchant_account.get_id(), pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id), ) @@ -810,7 +816,7 @@ impl CustomerDeleteBridge for customers::CustomerId { { network_tokenization::delete_network_token_from_locker_and_token_service( state, - &self.customer_id, + self, merchant_account.get_id(), pm.payment_method_id.clone(), pm.network_token_locker_id, @@ -884,7 +890,7 @@ impl CustomerDeleteBridge for customers::CustomerId { match db .update_address_by_merchant_id_customer_id( key_manager_state, - &self.customer_id, + self, merchant_account.get_id(), update_address, key_store, @@ -927,7 +933,7 @@ impl CustomerDeleteBridge for customers::CustomerId { db.update_customer_by_customer_id_merchant_id( key_manager_state, - self.customer_id.clone(), + self.clone(), merchant_account.get_id().to_owned(), customer_orig, updated_customer, @@ -938,7 +944,7 @@ impl CustomerDeleteBridge for customers::CustomerId { .switch()?; let response = customers::CustomerDeleteResponse { - customer_id: self.customer_id.clone(), + customer_id: self.clone(), customer_deleted: true, address_deleted: true, payment_methods_deleted: true, @@ -952,19 +958,24 @@ impl CustomerDeleteBridge for customers::CustomerId { pub async fn update_customer( state: SessionState, merchant_account: domain::MerchantAccount, - update_customer: customers::CustomerUpdateRequest, + update_customer: customers::CustomerUpdateRequestInternal, key_store: domain::MerchantKeyStore, - id: customers::UpdateCustomerId, ) -> errors::CustomerResponse { let db = state.store.as_ref(); let key_manager_state = &(&state).into(); //Add this in update call if customer can be updated anywhere else - let merchant_reference_id = update_customer.get_merchant_reference_id(); - + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] let verify_id_for_update_customer = VerifyIdForUpdateCustomer { - merchant_reference_id: merchant_reference_id.as_ref(), - id: &id, + merchant_reference_id: &update_customer.customer_id, + merchant_account: &merchant_account, + key_store: &key_store, + key_manager_state, + }; + + #[cfg(all(feature = "v2", feature = "customer_v2"))] + let verify_id_for_update_customer = VerifyIdForUpdateCustomer { + id: &update_customer.id, merchant_account: &merchant_account, key_store: &key_store, key_manager_state, @@ -975,6 +986,7 @@ pub async fn update_customer( .await?; let updated_customer = update_customer + .request .create_domain_model_from_request( db, &key_store, @@ -985,7 +997,7 @@ pub async fn update_customer( ) .await?; - update_customer.generate_response(&updated_customer) + update_customer.request.generate_response(&updated_customer) } #[async_trait::async_trait] @@ -1100,9 +1112,19 @@ impl<'a> AddressStructForDbUpdate<'a> { } } +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +#[derive(Debug)] struct VerifyIdForUpdateCustomer<'a> { - merchant_reference_id: Option<&'a id_type::CustomerId>, - id: &'a customers::UpdateCustomerId, + merchant_reference_id: &'a id_type::CustomerId, + merchant_account: &'a domain::MerchantAccount, + key_store: &'a domain::MerchantKeyStore, + key_manager_state: &'a KeyManagerState, +} + +#[cfg(all(feature = "v2", feature = "customer_v2"))] +#[derive(Debug)] +struct VerifyIdForUpdateCustomer<'a> { + id: &'a id_type::GlobalCustomerId, merchant_account: &'a domain::MerchantAccount, key_store: &'a domain::MerchantKeyStore, key_manager_state: &'a KeyManagerState, @@ -1114,18 +1136,10 @@ impl<'a> VerifyIdForUpdateCustomer<'a> { &self, db: &dyn StorageInterface, ) -> Result> { - let customer_id = self - .merchant_reference_id - .get_required_value("customer_id") - .change_context(errors::CustomersErrorResponse::InternalServerError) - .attach("Missing required field `customer_id`")?; - - let _id = self.id; - let customer = db .find_customer_by_customer_id_merchant_id( self.key_manager_state, - customer_id, + self.merchant_reference_id, self.merchant_account.get_id(), self.key_store, self.merchant_account.storage_scheme, @@ -1143,13 +1157,10 @@ impl<'a> VerifyIdForUpdateCustomer<'a> { &self, db: &dyn StorageInterface, ) -> Result> { - let id = self.id.get_global_id(); - - let _merchant_reference_id = self.merchant_reference_id; let customer = db .find_customer_by_global_id( self.key_manager_state, - &id, + self.id, self.merchant_account.get_id(), self.key_store, self.merchant_account.storage_scheme, @@ -1322,7 +1333,7 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest { let response = db .update_customer_by_global_id( key_manager_state, - domain_customer.id.to_owned(), + &domain_customer.id, domain_customer.to_owned(), merchant_account.get_id(), storage::CustomerUpdate::Update { diff --git a/crates/router/src/core/mandate.rs b/crates/router/src/core/mandate.rs index da8015662a..15ceb9b1da 100644 --- a/crates/router/src/core/mandate.rs +++ b/crates/router/src/core/mandate.rs @@ -19,7 +19,6 @@ use crate::{ types::{ self, api::{ - customers, mandates::{self, MandateResponseExt}, ConnectorData, GetToken, }, @@ -224,27 +223,24 @@ pub async fn update_connector_mandate_id( } Ok(services::ApplicationResponse::StatusOk) } - +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip(state))] pub async fn get_customer_mandates( state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, - req: customers::CustomerId, + customer_id: id_type::CustomerId, ) -> RouterResponse> { let mandates = state .store - .find_mandate_by_merchant_id_customer_id( - merchant_account.get_id(), - &req.get_merchant_reference_id(), - ) + .find_mandate_by_merchant_id_customer_id(merchant_account.get_id(), &customer_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { format!( "Failed while finding mandate: merchant_id: {:?}, customer_id: {:?}", merchant_account.get_id(), - req.get_merchant_reference_id() + customer_id, ) })?; diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 2d9a71a258..61dc70ac47 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1,4 +1,8 @@ pub mod cards; +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] pub mod migration; pub mod network_tokenization; pub mod surcharge_decision_configs; @@ -677,7 +681,7 @@ pub(crate) async fn get_payment_method_create_request( payment_method_data: Option<&domain::PaymentMethodData>, payment_method_type: Option, payment_method_subtype: Option, - customer_id: &Option, + customer_id: &Option, billing_name: Option>, ) -> RouterResult { match payment_method_data { @@ -848,7 +852,7 @@ pub async fn create_payment_method( let merchant_id = merchant_account.get_id(); let customer_id = req.customer_id.to_owned(); - db.find_customer_by_merchant_reference_id_merchant_id( + db.find_customer_by_global_id( &(state.into()), &customer_id, merchant_account.get_id(), @@ -961,7 +965,7 @@ pub async fn payment_method_intent_create( let merchant_id = merchant_account.get_id(); let customer_id = req.customer_id.to_owned(); - db.find_customer_by_merchant_reference_id_merchant_id( + db.find_customer_by_global_id( &(state.into()), &customer_id, merchant_account.get_id(), @@ -1049,7 +1053,7 @@ 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( + db.find_customer_by_global_id( &(state.into()), &customer_id, merchant_account.get_id(), @@ -1132,7 +1136,7 @@ pub async fn payment_method_intent_confirm( pub async fn create_payment_method_in_db( state: &SessionState, req: &api::PaymentMethodCreate, - customer_id: &id_type::CustomerId, + customer_id: &id_type::GlobalCustomerId, payment_method_id: id_type::GlobalPaymentMethodId, locker_id: Option, merchant_id: &id_type::MerchantId, @@ -1195,7 +1199,7 @@ pub async fn create_payment_method_in_db( pub async fn create_payment_method_for_intent( state: &SessionState, metadata: Option, - customer_id: &id_type::CustomerId, + customer_id: &id_type::GlobalCustomerId, payment_method_id: id_type::GlobalPaymentMethodId, merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, @@ -1421,7 +1425,7 @@ pub async fn list_customer_payment_method_util( profile: domain::Profile, key_store: domain::MerchantKeyStore, req: Option, - customer_id: Option, + customer_id: Option, is_payment_associated: bool, ) -> RouterResponse { let limit = req.as_ref().and_then(|pml_req| pml_req.limit); @@ -1481,7 +1485,7 @@ pub async fn list_customer_payment_method( profile: domain::Profile, key_store: domain::MerchantKeyStore, payment_intent: Option, - customer_id: &id_type::CustomerId, + customer_id: &id_type::GlobalCustomerId, limit: Option, is_payment_associated: bool, ) -> RouterResponse { @@ -1491,7 +1495,7 @@ pub async fn list_customer_payment_method( let customer = 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, @@ -1514,7 +1518,7 @@ pub async fn list_customer_payment_method( .transpose()?; let saved_payment_methods = db - .find_payment_method_by_customer_id_merchant_id_status( + .find_payment_method_by_global_customer_id_merchant_id_status( key_manager_state, &key_store, customer_id, @@ -1901,7 +1905,7 @@ pub async fn delete_payment_method( let _customer = db .find_customer_by_global_id( key_manager_state, - payment_method.customer_id.get_string_repr(), + &payment_method.customer_id, merchant_account.get_id(), &key_store, merchant_account.storage_scheme, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 1357596110..2dba211e24 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -58,12 +58,14 @@ use router_env::{instrument, tracing}; use serde_json::json; use strum::IntoEnumIterator; -use super::{ - migration::RecordMigrationStatusBuilder, - surcharge_decision_configs::{ - perform_surcharge_decision_management_for_payment_method_list, - perform_surcharge_decision_management_for_saved_cards, - }, +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] +use super::migration; +use super::surcharge_decision_configs::{ + perform_surcharge_decision_management_for_payment_method_list, + perform_surcharge_decision_management_for_saved_cards, }; #[cfg(all( any(feature = "v2", feature = "v1"), @@ -80,9 +82,7 @@ use crate::{ }, core::{ errors::{self, StorageErrorExt}, - payment_methods::{ - migration, network_tokenization, transformers as payment_methods, vault, - }, + payment_methods::{network_tokenization, transformers as payment_methods, vault}, payments::{ helpers, routing::{self, SessionFlowRoutingInput}, @@ -411,7 +411,7 @@ pub async fn migrate_payment_method( let should_require_connector_mandate_details = req.network_token.is_none(); - let mut migration_status = RecordMigrationStatusBuilder::new(); + let mut migration_status = migration::RecordMigrationStatusBuilder::new(); let resp = match card_number_validation_result { Ok(card_number) => { @@ -508,6 +508,10 @@ pub async fn migrate_payment_method( todo!() } +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] pub async fn populate_bin_details_for_masked_card( card_details: &api_models::payment_methods::MigrateCardDetail, db: &dyn db::StorageInterface, @@ -716,7 +720,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( merchant_account: &domain::MerchantAccount, card: api_models::payment_methods::CardDetailFromLocker, should_require_connector_mandate_details: bool, - migration_status: &mut RecordMigrationStatusBuilder, + migration_status: &mut migration::RecordMigrationStatusBuilder, ) -> errors::RouterResponse { let db = &*state.store; let customer_id = req.customer_id.clone().get_required_value("customer_id")?; @@ -1107,7 +1111,7 @@ pub async fn get_client_secret_or_add_payment_method_for_migration( req: api::PaymentMethodCreate, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - migration_status: &mut RecordMigrationStatusBuilder, + migration_status: &mut migration::RecordMigrationStatusBuilder, ) -> errors::RouterResponse { let merchant_id = merchant_account.get_id(); let customer_id = req.customer_id.clone().get_required_value("customer_id")?; @@ -1705,7 +1709,7 @@ pub async fn save_migration_payment_method( req: api::PaymentMethodCreate, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - migration_status: &mut RecordMigrationStatusBuilder, + migration_status: &mut migration::RecordMigrationStatusBuilder, ) -> errors::RouterResponse { req.validate()?; let db = &*state.store; @@ -2533,7 +2537,7 @@ pub async fn delete_card_from_locker( #[cfg(all(feature = "v2", feature = "customer_v2"))] pub async fn delete_card_by_locker_id( state: &routes::SessionState, - id: &str, + id: &id_type::GlobalCustomerId, merchant_id: &id_type::MerchantId, ) -> errors::RouterResult { todo!() diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index cab515d9ba..bb730caad2 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -308,6 +308,7 @@ pub async fn make_card_network_tokenization_request( } } +#[cfg(feature = "v1")] pub async fn get_network_token( state: &routes::SessionState, customer_id: id_type::CustomerId, @@ -375,6 +376,7 @@ pub async fn get_network_token( Ok(token_response) } +#[cfg(feature = "v1")] pub async fn get_token_from_tokenization_service( state: &routes::SessionState, network_token_requestor_ref_id: String, @@ -441,6 +443,7 @@ pub async fn get_token_from_tokenization_service( Ok(network_token_data) } +#[cfg(feature = "v1")] pub async fn do_status_check_for_network_token( state: &routes::SessionState, payment_method_info: &domain::PaymentMethod, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 5849027107..e942288078 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -5850,6 +5850,7 @@ pub fn filter_network_tokenization_supported_connectors( .collect() } +#[cfg(feature = "v1")] pub async fn decide_action_type( state: &SessionState, is_connector_agnostic_mit_enabled: Option, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 84256f1b29..78cfdda33b 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2447,6 +2447,7 @@ pub async fn make_pm_data<'a, F: Clone, R, D>( Ok((operation, payment_method, pm_id)) } +#[cfg(feature = "v1")] pub async fn store_in_vault_and_generate_ppmt( state: &SessionState, payment_method_data: &domain::PaymentMethodData, diff --git a/crates/router/src/core/payments/operations/payment_confirm_intent.rs b/crates/router/src/core/payments/operations/payment_confirm_intent.rs index f6c47de376..374a33026d 100644 --- a/crates/router/src/core/payments/operations/payment_confirm_intent.rs +++ b/crates/router/src/core/payments/operations/payment_confirm_intent.rs @@ -276,7 +276,7 @@ impl Domain Domain Domain Domain( // TODO: Take Globalid and convert to connector reference id let customer_id = customer .to_owned() - .map(|customer| customer.id.clone()) - .map(std::borrow::Cow::Owned) - .map(common_utils::id_type::CustomerId::try_from) + .map(|customer| common_utils::id_type::CustomerId::try_from(customer.id.clone())) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable( @@ -540,9 +538,7 @@ pub async fn construct_payment_router_data_for_sdk_session<'a>( // TODO: Take Globalid and convert to connector reference id let customer_id = customer .to_owned() - .map(|customer| customer.id.clone()) - .map(std::borrow::Cow::Owned) - .map(common_utils::id_type::CustomerId::try_from) + .map(|customer| common_utils::id_type::CustomerId::try_from(customer.id.clone())) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable( diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 94946b7e0e..609414c292 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -1269,11 +1269,11 @@ pub async fn create_recipient( #[cfg(all(feature = "v2", feature = "customer_v2"))] { - let global_id = "temp_id".to_string(); + let customer_id = customer.get_id().clone(); payout_data.customer_details = Some( db.update_customer_by_global_id( &state.into(), - global_id, + &customer_id, customer, merchant_account.get_id(), updated_customer, diff --git a/crates/router/src/db/customers.rs b/crates/router/src/db/customers.rs index ae23f81e2e..f1845be537 100644 --- a/crates/router/src/db/customers.rs +++ b/crates/router/src/db/customers.rs @@ -129,7 +129,7 @@ where async fn update_customer_by_global_id( &self, state: &KeyManagerState, - id: String, + id: &id_type::GlobalCustomerId, customer: customer::Customer, merchant_id: &id_type::MerchantId, customer_update: storage_types::CustomerUpdate, @@ -141,7 +141,7 @@ where async fn find_customer_by_global_id( &self, state: &KeyManagerState, - id: &str, + id: &id_type::GlobalCustomerId, merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -540,7 +540,6 @@ mod storage { ) .await .change_context(errors::StorageError::DecryptionError)?; - //.await match result.name { Some(ref name) if name.peek() == REDACTED => { @@ -608,7 +607,6 @@ mod storage { ) .await .change_context(errors::StorageError::DecryptionError)?; - //.await match result.name { Some(ref name) if name.peek() == REDACTED => { @@ -686,8 +684,10 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) } MerchantStorageScheme::RedisKv => { - let key = PartitionKey::GlobalId { id: &id }; - let field = format!("cust_{}", id); + let key = PartitionKey::GlobalId { + id: id.get_string_repr(), + }; + let field = format!("cust_{}", id.get_string_repr()); let redis_entry = kv::TypedSql { op: kv::DBOperation::Insert { @@ -712,7 +712,7 @@ mod storage { Ok(redis_interface::HsetnxReply::KeyNotSet) => { Err(report!(errors::StorageError::DuplicateValue { entity: "customer", - key: Some(id.to_string()), + key: Some(id.get_string_repr().to_owned()), })) } Ok(redis_interface::HsetnxReply::KeySet) => Ok(storage_customer), @@ -832,7 +832,7 @@ mod storage { async fn find_customer_by_global_id( &self, state: &KeyManagerState, - id: &str, + id: &id_type::GlobalCustomerId, _merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -852,8 +852,10 @@ mod storage { let customer = match storage_scheme { MerchantStorageScheme::PostgresOnly => database_call().await, MerchantStorageScheme::RedisKv => { - let key = PartitionKey::GlobalId { id }; - let field = format!("cust_{}", id); + let key = PartitionKey::GlobalId { + id: id.get_string_repr(), + }; + let field = format!("cust_{}", id.get_string_repr()); Box::pin(db_utils::try_redis_get_else_try_database_get( async { kv_wrapper( @@ -878,13 +880,11 @@ mod storage { ) .await .change_context(errors::StorageError::DecryptionError)?; - //.await - match result.name { - Some(ref name) if name.peek() == REDACTED => { - Err(errors::StorageError::CustomerRedacted)? - } - _ => Ok(result), + if result.status == common_enums::DeleteStatus::Redacted { + Err(report!(errors::StorageError::CustomerRedacted)) + } else { + Ok(result) } } @@ -893,7 +893,7 @@ mod storage { async fn update_customer_by_global_id( &self, state: &KeyManagerState, - id: String, + id: &id_type::GlobalCustomerId, customer: customer::Customer, _merchant_id: &id_type::MerchantId, customer_update: storage_types::CustomerUpdate, @@ -913,8 +913,10 @@ mod storage { .await .map_err(|error| report!(errors::StorageError::from(error))) }; - let key = PartitionKey::GlobalId { id: &id }; - let field = format!("cust_{}", id); + let key = PartitionKey::GlobalId { + id: id.get_string_repr(), + }; + let field = format!("cust_{}", id.get_string_repr()); let storage_scheme = Box::pin(decide_storage_scheme::<_, diesel_models::Customer>( self, storage_scheme, @@ -1284,7 +1286,7 @@ mod storage { async fn update_customer_by_global_id( &self, state: &KeyManagerState, - id: String, + id: &id_type::GlobalCustomerId, customer: customer::Customer, merchant_id: &id_type::MerchantId, customer_update: storage_types::CustomerUpdate, @@ -1308,7 +1310,7 @@ mod storage { async fn find_customer_by_global_id( &self, state: &KeyManagerState, - id: &str, + id: &id_type::GlobalCustomerId, merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -1526,7 +1528,7 @@ impl CustomerInterface for MockDb { async fn update_customer_by_global_id( &self, _state: &KeyManagerState, - _id: String, + _id: &id_type::GlobalCustomerId, _customer: customer::Customer, _merchant_id: &id_type::MerchantId, _customer_update: storage_types::CustomerUpdate, @@ -1541,7 +1543,7 @@ impl CustomerInterface for MockDb { async fn find_customer_by_global_id( &self, _state: &KeyManagerState, - _id: &str, + _id: &id_type::GlobalCustomerId, _merchant_id: &id_type::MerchantId, _key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index a7e71284aa..f49173b354 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -449,7 +449,7 @@ impl CustomerInterface for KafkaStore { async fn update_customer_by_global_id( &self, state: &KeyManagerState, - id: String, + id: &id_type::GlobalCustomerId, customer: domain::Customer, merchant_id: &id_type::MerchantId, customer_update: storage::CustomerUpdate, @@ -525,7 +525,7 @@ impl CustomerInterface for KafkaStore { async fn find_customer_by_global_id( &self, state: &KeyManagerState, - id: &str, + id: &id_type::GlobalCustomerId, merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -862,11 +862,13 @@ impl MandateInterface for KafkaStore { } #[cfg(all(feature = "v2", feature = "customer_v2"))] - async fn find_mandate_by_global_id( + async fn find_mandate_by_global_customer_id( &self, - id: &str, + id: &id_type::GlobalCustomerId, ) -> CustomResult, errors::StorageError> { - self.diesel_store.find_mandate_by_global_id(id).await + self.diesel_store + .find_mandate_by_global_customer_id(id) + .await } async fn find_mandate_by_merchant_id_customer_id( @@ -1909,18 +1911,22 @@ impl PaymentMethodInterface for KafkaStore { } #[cfg(all(feature = "v2", feature = "customer_v2"))] - async fn find_payment_method_list_by_global_id( + async fn find_payment_method_list_by_global_customer_id( &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - id: &str, + id: &id_type::GlobalCustomerId, limit: Option, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult, errors::StorageError> { self.diesel_store - .find_payment_method_list_by_global_id(state, key_store, id, limit) + .find_payment_method_list_by_global_customer_id(state, key_store, id, limit) .await } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] async fn find_payment_method_by_customer_id_merchant_id_status( &self, state: &KeyManagerState, @@ -1944,6 +1950,30 @@ impl PaymentMethodInterface for KafkaStore { .await } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_payment_method_by_global_customer_id_merchant_id_status( + &self, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + customer_id: &id_type::GlobalCustomerId, + merchant_id: &id_type::MerchantId, + status: common_enums::PaymentMethodStatus, + limit: Option, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError> { + self.diesel_store + .find_payment_method_by_global_customer_id_merchant_id_status( + state, + key_store, + customer_id, + merchant_id, + status, + limit, + storage_scheme, + ) + .await + } + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") diff --git a/crates/router/src/db/mandate.rs b/crates/router/src/db/mandate.rs index 076cb768e6..236c1d526a 100644 --- a/crates/router/src/db/mandate.rs +++ b/crates/router/src/db/mandate.rs @@ -30,9 +30,9 @@ pub trait MandateInterface { // Fix this function once we move to mandate v2 #[cfg(all(feature = "v2", feature = "customer_v2"))] - async fn find_mandate_by_global_id( + async fn find_mandate_by_global_customer_id( &self, - id: &str, + id: &id_type::GlobalCustomerId, ) -> CustomResult, errors::StorageError>; async fn update_mandate_by_merchant_id_mandate_id( @@ -201,12 +201,12 @@ mod storage { #[cfg(all(feature = "v2", feature = "customer_v2"))] #[instrument(skip_all)] - async fn find_mandate_by_global_id( + async fn find_mandate_by_global_customer_id( &self, - id: &str, + id: &id_type::GlobalCustomerId, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; - storage_types::Mandate::find_by_global_id(&conn, id) + storage_types::Mandate::find_by_global_customer_id(&conn, id) .await .map_err(|error| report!(errors::StorageError::from(error))) } @@ -459,9 +459,9 @@ mod storage { // Need to fix this once we start moving to mandate v2 #[cfg(all(feature = "v2", feature = "customer_v2"))] #[instrument(skip_all)] - async fn find_mandate_by_global_id( + async fn find_mandate_by_global_customer_id( &self, - customer_id: &str, + customer_id: &id_type::GlobalCustomerId, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; storage_types::Mandate::find_by_global_id(&conn, customer_id) @@ -572,9 +572,9 @@ impl MandateInterface for MockDb { // Need to fix this once we move to v2 mandate #[cfg(all(feature = "v2", feature = "customer_v2"))] - async fn find_mandate_by_global_id( + async fn find_mandate_by_global_customer_id( &self, - id: &str, + id: &id_type::GlobalCustomerId, ) -> CustomResult, errors::StorageError> { todo!() } diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index 28a3ae7ec0..cc5af40b30 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -62,14 +62,18 @@ pub trait PaymentMethodInterface { // Need to fix this once we start moving to v2 for payment method #[cfg(all(feature = "v2", feature = "customer_v2"))] - async fn find_payment_method_list_by_global_id( + async fn find_payment_method_list_by_global_customer_id( &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - id: &str, + id: &id_type::GlobalCustomerId, limit: Option, - ) -> CustomResult, errors::StorageError>; + ) -> CustomResult, errors::StorageError>; + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] #[allow(clippy::too_many_arguments)] async fn find_payment_method_by_customer_id_merchant_id_status( &self, @@ -82,6 +86,19 @@ pub trait PaymentMethodInterface { storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::StorageError>; + #[cfg(all(feature = "v2", feature = "customer_v2"))] + #[allow(clippy::too_many_arguments)] + async fn find_payment_method_by_global_customer_id_merchant_id_status( + &self, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + customer_id: &id_type::GlobalCustomerId, + merchant_id: &id_type::MerchantId, + status: common_enums::PaymentMethodStatus, + limit: Option, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError>; + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") @@ -698,16 +715,41 @@ mod storage { feature = "customer_v2", feature = "payment_methods_v2" ))] - async fn find_payment_method_list_by_global_id( + async fn find_payment_method_list_by_global_customer_id( &self, - _state: &KeyManagerState, - _key_store: &domain::MerchantKeyStore, - _id: &str, - _limit: Option, - ) -> CustomResult, errors::StorageError> { - todo!() + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + customer_id: &id_type::GlobalCustomerId, + limit: Option, + ) -> CustomResult, errors::StorageError> { + let conn = connection::pg_connection_read(self).await?; + let payment_methods = + storage_types::PaymentMethod::find_by_global_customer_id(&conn, customer_id, limit) + .await + .map_err(|error| report!(errors::StorageError::from(error)))?; + + let pm_futures = payment_methods + .into_iter() + .map(|pm| async { + pm.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .collect::>(); + + let domain_payment_methods = futures::future::try_join_all(pm_futures).await?; + + Ok(domain_payment_methods) } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] #[instrument(skip_all)] async fn find_payment_method_by_customer_id_merchant_id_status( &self, @@ -785,6 +827,48 @@ mod storage { Ok(domain_payment_methods) } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + #[instrument(skip_all)] + async fn find_payment_method_by_global_customer_id_merchant_id_status( + &self, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + customer_id: &id_type::GlobalCustomerId, + merchant_id: &id_type::MerchantId, + status: common_enums::PaymentMethodStatus, + limit: Option, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError> { + let conn = connection::pg_connection_read(self).await?; + let payment_methods = + storage_types::PaymentMethod::find_by_global_customer_id_merchant_id_status( + &conn, + customer_id, + merchant_id, + status, + limit, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error)))?; + + let pm_futures = payment_methods + .into_iter() + .map(|pm| async { + pm.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .collect::>(); + + let domain_payment_methods = futures::future::try_join_all(pm_futures).await?; + + Ok(domain_payment_methods) + } + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") @@ -1112,19 +1196,41 @@ mod storage { // Need to fix this once we move to payment method for customer #[cfg(all(feature = "v2", feature = "customer_v2"))] #[instrument(skip_all)] - async fn find_payment_method_list_by_global_id( + async fn find_payment_method_list_by_global_customer_id( &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - id: &str, + id: &id_type::GlobalCustomerId, limit: Option, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; - storage_types::PaymentMethod::find_by_global_id(&conn, id, limit) - .await - .map_err(|error| report!(errors::StorageError::from(error))) + let payment_methods = + storage_types::PaymentMethod::find_by_global_customer_id(&conn, customer_id, limit) + .await + .map_err(|error| report!(errors::StorageError::from(error)))?; + + let pm_futures = payment_methods + .into_iter() + .map(|pm| async { + pm.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .collect::>(); + + let domain_payment_methods = futures::future::try_join_all(pm_futures).await?; + + Ok(domain_payment_methods) } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] #[instrument(skip_all)] async fn find_payment_method_by_customer_id_merchant_id_status( &self, @@ -1166,6 +1272,48 @@ mod storage { Ok(domain_payment_methods) } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + #[instrument(skip_all)] + async fn find_payment_method_by_global_customer_id_merchant_id_status( + &self, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + customer_id: &id_type::GlobalCustomerId, + merchant_id: &id_type::MerchantId, + status: common_enums::PaymentMethodStatus, + limit: Option, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError> { + let conn = connection::pg_connection_read(self).await?; + let payment_methods = + storage_types::PaymentMethod::find_by_global_customer_id_merchant_id_status( + &conn, + customer_id, + merchant_id, + status, + limit, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error)))?; + + let pm_futures = payment_methods + .into_iter() + .map(|pm| async { + pm.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .collect::>(); + + let domain_payment_methods = futures::future::try_join_all(pm_futures).await?; + + Ok(domain_payment_methods) + } + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") @@ -1426,16 +1574,20 @@ impl PaymentMethodInterface for MockDb { // Need to fix this once we complete v2 payment method #[cfg(all(feature = "v2", feature = "customer_v2"))] - async fn find_payment_method_list_by_global_id( + async fn find_payment_method_list_by_global_customer_id( &self, state: &KeyManagerState, key_store: &domain::MerchantKeyStore, - _id: &str, + _id: &id_type::GlobalCustomerId, _limit: Option, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult, errors::StorageError> { todo!() } + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") + ))] async fn find_payment_method_by_customer_id_merchant_id_status( &self, state: &KeyManagerState, @@ -1482,6 +1634,53 @@ impl PaymentMethodInterface for MockDb { } } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_payment_method_by_global_customer_id_merchant_id_status( + &self, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + customer_id: &id_type::GlobalCustomerId, + merchant_id: &id_type::MerchantId, + status: common_enums::PaymentMethodStatus, + _limit: Option, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError> { + let payment_methods = self.payment_methods.lock().await; + let payment_methods_found: Vec = payment_methods + .iter() + .filter(|pm| { + pm.customer_id == *customer_id + && pm.merchant_id == *merchant_id + && pm.status == status + }) + .cloned() + .collect(); + + if payment_methods_found.is_empty() { + Err( + errors::StorageError::ValueNotFound("cannot find payment methods".to_string()) + .into(), + ) + } else { + let pm_futures = payment_methods_found + .into_iter() + .map(|pm| async { + pm.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .collect::>(); + + let domain_payment_methods = futures::future::try_join_all(pm_futures).await?; + + Ok(domain_payment_methods) + } + } + #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 5f04581fbb..766679491c 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -973,8 +973,8 @@ impl Customers { .service(web::resource("").route(web::post().to(customers_create))) .service( web::resource("/{id}") - .route(web::post().to(customers_update)) - .route(web::post().to(customers_retrieve)) + .route(web::put().to(customers_update)) + .route(web::get().to(customers_retrieve)) .route(web::delete().to(customers_delete)), ) } diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs index 5ff155966a..09b6983d2a 100644 --- a/crates/router/src/routes/customers.rs +++ b/crates/router/src/routes/customers.rs @@ -1,5 +1,4 @@ use actix_web::{web, HttpRequest, HttpResponse, Responder}; -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use common_utils::id_type; use router_env::{instrument, tracing, Flow}; @@ -46,10 +45,7 @@ pub async fn customers_retrieve( ) -> HttpResponse { let flow = Flow::CustomersRetrieve; - let payload = web::Json(customers::CustomerId::new_customer_id_struct( - path.into_inner(), - )) - .into_inner(); + let customer_id = path.into_inner(); let auth = if auth::is_jwt_auth(req.headers()) { Box::new(auth::JWTAuth { @@ -66,14 +62,14 @@ pub async fn customers_retrieve( flow, state, &req, - payload, - |state, auth: auth::AuthenticationData, req, _| { + customer_id, + |state, auth: auth::AuthenticationData, customer_id, _| { retrieve_customer( state, auth.merchant_account, auth.profile_id, auth.key_store, - req, + customer_id, ) }, &*auth, @@ -87,11 +83,11 @@ pub async fn customers_retrieve( pub async fn customers_retrieve( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> HttpResponse { let flow = Flow::CustomersRetrieve; - let payload = web::Json(customers::GlobalId::new(path.into_inner())).into_inner(); + let id = path.into_inner(); let auth = if auth::is_jwt_auth(req.headers()) { Box::new(auth::JWTAuth { @@ -108,9 +104,9 @@ pub async fn customers_retrieve( flow, state, &req, - payload, - |state, auth: auth::AuthenticationData, req, _| { - retrieve_customer(state, auth.merchant_account, auth.key_store, req) + id, + |state, auth: auth::AuthenticationData, id, _| { + retrieve_customer(state, auth.merchant_account, auth.key_store, id) }, &*auth, api_locking::LockAction::NotApplicable, @@ -159,24 +155,27 @@ pub async fn customers_update( state: web::Data, req: HttpRequest, path: web::Path, - mut json_payload: web::Json, + json_payload: web::Json, ) -> HttpResponse { let flow = Flow::CustomersUpdate; let customer_id = path.into_inner(); - json_payload.customer_id = Some(customer_id); - let customer_update_id = customers::UpdateCustomerId::new("temp_global_id".to_string()); + let request = json_payload.into_inner(); + let request_internal = customers::CustomerUpdateRequestInternal { + customer_id, + request, + }; + Box::pin(api::server_wrap( flow, state, &req, - json_payload.into_inner(), - |state, auth: auth::AuthenticationData, req, _| { + request_internal, + |state, auth: auth::AuthenticationData, request_internal, _| { update_customer( state, auth.merchant_account, - req, + request_internal, auth.key_store, - customer_update_id.clone(), ) }, auth::auth_type( @@ -196,24 +195,25 @@ pub async fn customers_update( pub async fn customers_update( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, json_payload: web::Json, ) -> HttpResponse { let flow = Flow::CustomersUpdate; - let id = path.into_inner().clone(); - let customer_update_id = customers::UpdateCustomerId::new(id); + let id = path.into_inner(); + let request = json_payload.into_inner(); + let request_internal = customers::CustomerUpdateRequestInternal { id, request }; + Box::pin(api::server_wrap( flow, state, &req, - json_payload.into_inner(), - |state, auth: auth::AuthenticationData, req, _| { + request_internal, + |state, auth: auth::AuthenticationData, request_internal, _| { update_customer( state, auth.merchant_account, - req, + request_internal, auth.key_store, - customer_update_id.clone(), ) }, auth::auth_type( @@ -233,18 +233,18 @@ pub async fn customers_update( pub async fn customers_delete( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> impl Responder { let flow = Flow::CustomersDelete; - let payload = web::Json(customers::GlobalId::new(path.into_inner())).into_inner(); + let id = path.into_inner(); Box::pin(api::server_wrap( flow, state, &req, - payload, - |state, auth: auth::AuthenticationData, req, _| { - delete_customer(state, auth.merchant_account, req, auth.key_store) + id, + |state, auth: auth::AuthenticationData, id, _| { + delete_customer(state, auth.merchant_account, id, auth.key_store) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -266,18 +266,15 @@ pub async fn customers_delete( path: web::Path, ) -> impl Responder { let flow = Flow::CustomersDelete; - let payload = web::Json(customers::CustomerId { - customer_id: path.into_inner(), - }) - .into_inner(); + let customer_id = path.into_inner(); Box::pin(api::server_wrap( flow, state, &req, - payload, - |state, auth: auth::AuthenticationData, req, _| { - delete_customer(state, auth.merchant_account, req, auth.key_store) + customer_id, + |state, auth: auth::AuthenticationData, customer_id, _| { + delete_customer(state, auth.merchant_account, customer_id, auth.key_store) }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), @@ -299,21 +296,19 @@ pub async fn get_customer_mandates( path: web::Path, ) -> impl Responder { let flow = Flow::CustomersGetMandates; - let customer_id = customers::CustomerId { - customer_id: path.into_inner(), - }; + let customer_id = path.into_inner(); Box::pin(api::server_wrap( flow, state, &req, customer_id, - |state, auth: auth::AuthenticationData, req, _| { + |state, auth: auth::AuthenticationData, customer_id, _| { crate::core::mandate::get_customer_mandates( state, auth.merchant_account, auth.key_store, - req, + customer_id, ) }, auth::auth_type( diff --git a/crates/router/src/routes/ephemeral_key.rs b/crates/router/src/routes/ephemeral_key.rs index dd11bee24b..e6a33c5919 100644 --- a/crates/router/src/routes/ephemeral_key.rs +++ b/crates/router/src/routes/ephemeral_key.rs @@ -9,7 +9,6 @@ use super::AppState; use crate::{ core::{api_locking, payments::helpers}, services::{api, authentication as auth}, - types::api::customers, }; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] @@ -17,7 +16,7 @@ use crate::{ 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(); @@ -26,10 +25,10 @@ pub async fn ephemeral_key_create( state, &req, payload, - |state, auth: auth::AuthenticationData, req, _| { + |state, auth: auth::AuthenticationData, payload, _| { helpers::make_ephemeral_key( state, - req.get_merchant_reference_id(), + payload.customer_id, auth.merchant_account.get_id().to_owned(), ) }, diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 8ee31ecf94..c27a25677a 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -554,13 +554,13 @@ pub async fn list_customer_payment_method_for_payment( #[instrument(skip_all, fields(flow = ?Flow::CustomerPaymentMethodsList))] pub async fn list_customer_payment_method_api( state: web::Data, - customer_id: web::Path<(id_type::CustomerId,)>, + customer_id: web::Path, req: HttpRequest, query_payload: web::Query, ) -> HttpResponse { let flow = Flow::CustomerPaymentMethodsList; let payload = query_payload.into_inner(); - let customer_id = customer_id.into_inner().0.clone(); + let customer_id = customer_id.into_inner(); let ephemeral_or_api_auth = match auth::is_ephemeral_auth(req.headers()) { Ok(auth) => auth, diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index 7a8e65e060..2893cb8fa0 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -119,7 +119,7 @@ pub async fn payments_create_intent( } }; let global_payment_id = - common_utils::id_type::GlobalPaymentId::generate(&state.conf.cell_information.id.clone()); + common_utils::id_type::GlobalPaymentId::generate(&state.conf.cell_information.id); Box::pin(api::server_wrap( flow, diff --git a/crates/router/src/types/api/customers.rs b/crates/router/src/types/api/customers.rs index 9cfec6f7b5..5b37fb2add 100644 --- a/crates/router/src/types/api/customers.rs +++ b/crates/router/src/types/api/customers.rs @@ -1,9 +1,7 @@ use api_models::customers; -#[cfg(all(feature = "v2", feature = "customer_v2"))] -pub use api_models::customers::GlobalId; pub use api_models::customers::{ - CustomerDeleteResponse, CustomerId, CustomerListRequest, CustomerRequest, - CustomerUpdateRequest, UpdateCustomerId, + CustomerDeleteResponse, CustomerListRequest, CustomerRequest, CustomerUpdateRequest, + CustomerUpdateRequestInternal, }; #[cfg(all(feature = "v2", feature = "customer_v2"))] use hyperswitch_domain_models::customer; @@ -50,6 +48,7 @@ impl ForeignFrom<(domain::Customer, Option)> for Custo impl ForeignFrom for CustomerResponse { fn foreign_from(cust: domain::Customer) -> Self { customers::CustomerResponse { + id: cust.id, merchant_reference_id: cust.merchant_reference_id, name: cust.name, email: cust.email, @@ -61,7 +60,6 @@ impl ForeignFrom for CustomerResponse { default_billing_address: None, default_shipping_address: None, default_payment_method_id: cust.default_payment_method_id, - id: cust.id, } .into() } diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs index 1d48606bf0..09bb42567d 100644 --- a/crates/storage_impl/src/lib.rs +++ b/crates/storage_impl/src/lib.rs @@ -479,7 +479,7 @@ impl UniqueConstraints for diesel_models::Customer { #[cfg(all(feature = "v2", feature = "customer_v2"))] impl UniqueConstraints for diesel_models::Customer { fn unique_constraints(&self) -> Vec { - vec![format!("customer_{}", self.id.clone())] + vec![format!("customer_{}", self.id.get_string_repr())] } fn table_name(&self) -> &str { "Customer"