From 93d61d1053a834ac1e7bf6d5dd70053d28f3e7d5 Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Thu, 30 May 2024 16:19:10 +0530 Subject: [PATCH] feat: add a domain type for `customer_id` (#4705) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/api_models/src/customers.rs | 27 +- crates/api_models/src/ephemeral_key.rs | 4 +- crates/api_models/src/events/customer.rs | 6 +- crates/api_models/src/payment_methods.rs | 29 +- crates/api_models/src/payments.rs | 64 +++- crates/api_models/src/payouts.rs | 18 +- crates/cards/tests/basic.rs | 2 +- crates/common_utils/src/consts.rs | 6 + crates/common_utils/src/events.rs | 4 +- crates/common_utils/src/id_type.rs | 299 ++++++++++++++++++ crates/common_utils/src/id_type/customer.rs | 111 +++++++ crates/common_utils/src/lib.rs | 48 ++- crates/diesel_models/src/address.rs | 5 +- crates/diesel_models/src/customers.rs | 9 +- crates/diesel_models/src/ephemeral_key.rs | 6 +- crates/diesel_models/src/locker_mock_up.rs | 5 +- crates/diesel_models/src/mandate.rs | 6 +- crates/diesel_models/src/payment_intent.rs | 12 +- crates/diesel_models/src/payment_method.rs | 6 +- crates/diesel_models/src/payout_attempt.rs | 6 +- crates/diesel_models/src/payouts.rs | 7 +- crates/diesel_models/src/query/address.rs | 3 +- crates/diesel_models/src/query/customers.rs | 9 +- crates/diesel_models/src/query/mandate.rs | 3 +- .../diesel_models/src/query/payment_method.rs | 7 +- .../hyperswitch_domain_models/src/payments.rs | 4 +- .../src/payments/payment_intent.rs | 12 +- .../hyperswitch_domain_models/src/payouts.rs | 4 +- .../src/payouts/payout_attempt.rs | 7 +- .../src/payouts/payouts.rs | 8 +- .../src/router_data.rs | 3 +- .../src/router_request_types.rs | 4 +- crates/openapi/src/openapi.rs | 1 + .../src/connector/plaid/transformers.rs | 3 +- crates/pm_auth/src/types.rs | 3 +- .../src/compatibility/stripe/customers.rs | 13 +- .../compatibility/stripe/customers/types.rs | 12 +- .../stripe/payment_intents/types.rs | 7 +- .../stripe/setup_intents/types.rs | 8 +- .../router/src/connector/aci/transformers.rs | 4 +- .../src/connector/adyen/transformers.rs | 18 +- .../connector/authorizedotnet/transformers.rs | 6 +- .../src/connector/billwerk/transformers.rs | 7 +- .../src/connector/cashtocode/transformers.rs | 6 +- .../src/connector/gocardless/transformers.rs | 7 +- .../connector/riskified/transformers/api.rs | 4 +- crates/router/src/connector/utils.rs | 9 +- .../router/src/connector/volt/transformers.rs | 4 +- .../router/src/connector/zsl/transformers.rs | 2 +- crates/router/src/core/customers.rs | 29 +- crates/router/src/core/locker_migration.rs | 10 +- crates/router/src/core/mandate.rs | 6 +- .../router/src/core/payment_methods/cards.rs | 64 ++-- .../src/core/payment_methods/transformers.rs | 54 +--- .../router/src/core/payment_methods/vault.rs | 112 +++++-- crates/router/src/core/payments.rs | 2 +- crates/router/src/core/payments/helpers.rs | 33 +- .../operations/payment_complete_authorize.rs | 6 +- .../payments/operations/payment_confirm.rs | 6 +- .../payments/operations/payment_create.rs | 8 +- .../payments/operations/payment_update.rs | 6 +- .../router/src/core/payments/tokenization.rs | 14 +- crates/router/src/core/payouts.rs | 13 +- crates/router/src/core/payouts/helpers.rs | 11 +- crates/router/src/core/payouts/validator.rs | 5 +- crates/router/src/db/address.rs | 15 +- crates/router/src/db/customers.rs | 58 ++-- crates/router/src/db/kafka_store.rs | 20 +- crates/router/src/db/locker_mock_up.rs | 12 +- crates/router/src/db/mandate.rs | 14 +- crates/router/src/db/payment_method.rs | 41 +-- crates/router/src/routes/customers.rs | 12 +- crates/router/src/routes/payment_methods.rs | 4 +- crates/router/src/services/authentication.rs | 4 +- .../src/services/kafka/payment_intent.rs | 4 +- crates/router/src/services/kafka/payout.rs | 4 +- crates/router/src/types/domain/address.rs | 5 +- crates/router/src/types/domain/customer.rs | 4 +- crates/router/src/types/transformers.rs | 2 +- crates/router/src/utils.rs | 7 +- crates/router/src/utils/user/sample_data.rs | 7 +- crates/router/tests/connectors/aci.rs | 5 +- crates/router/tests/connectors/utils.rs | 4 +- crates/router/tests/payments.rs | 4 +- crates/router/tests/payments2.rs | 4 +- crates/storage_impl/src/lib.rs | 3 +- openapi/openapi_spec.json | 120 +++++-- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 10 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../Payments - Create/request.json | 2 +- .../stripe.postman_collection.json | 6 +- 106 files changed, 1150 insertions(+), 490 deletions(-) create mode 100644 crates/common_utils/src/id_type.rs create mode 100644 crates/common_utils/src/id_type/customer.rs diff --git a/crates/api_models/src/customers.rs b/crates/api_models/src/customers.rs index eeb10e622860..bdc1f894d928 100644 --- a/crates/api_models/src/customers.rs +++ b/crates/api_models/src/customers.rs @@ -1,4 +1,4 @@ -use common_utils::{consts, crypto, custom_serde, pii}; +use common_utils::{crypto, custom_serde, id_type, pii}; use masking::Secret; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -9,9 +9,8 @@ use crate::payments; #[derive(Debug, Default, Clone, Deserialize, Serialize, ToSchema)] pub struct CustomerRequest { /// The identifier for the customer object. If not provided the customer ID will be autogenerated. - #[schema(max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - #[serde(default = "generate_customer_id")] - pub customer_id: String, + #[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(default = "unknown_merchant", skip)] @@ -43,9 +42,9 @@ pub struct CustomerRequest { #[derive(Debug, Clone, Serialize, ToSchema)] pub struct CustomerResponse { - /// The identifier for the customer object. If not provided the customer ID will be autogenerated. - #[schema(max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: String, + /// The identifier for the customer object + #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: id_type::CustomerId, /// The customer's name #[schema(max_length = 255, value_type = Option, example = "Jon Test")] pub name: crypto::OptionalEncryptableName, @@ -78,16 +77,16 @@ pub struct CustomerResponse { pub default_payment_method_id: Option, } -#[derive(Default, Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct CustomerId { - pub customer_id: String, + pub customer_id: id_type::CustomerId, } -#[derive(Default, Debug, Deserialize, Serialize, ToSchema)] +#[derive(Debug, Deserialize, Serialize, ToSchema)] pub struct CustomerDeleteResponse { /// The identifier for the customer object - #[schema(max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: String, + #[schema(value_type = String, max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: id_type::CustomerId, /// Whether customer was deleted or not #[schema(example = false)] pub customer_deleted: bool, @@ -99,10 +98,6 @@ pub struct CustomerDeleteResponse { pub payment_methods_deleted: bool, } -pub fn generate_customer_id() -> String { - common_utils::generate_id(consts::ID_LENGTH, "cus") -} - fn unknown_merchant() -> String { String::from("merchant_unknown") } diff --git a/crates/api_models/src/ephemeral_key.rs b/crates/api_models/src/ephemeral_key.rs index 4df55db05872..42f5a0877673 100644 --- a/crates/api_models/src/ephemeral_key.rs +++ b/crates/api_models/src/ephemeral_key.rs @@ -1,10 +1,12 @@ +use common_utils::id_type; use serde; use utoipa::ToSchema; #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Eq, PartialEq, ToSchema)] pub struct EphemeralKeyCreateResponse { /// customer_id to which this ephemeral key belongs to - pub customer_id: String, + #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: id_type::CustomerId, /// time at which this ephemeral key was created pub created_at: i64, /// time at which this ephemeral key would expire diff --git a/crates/api_models/src/events/customer.rs b/crates/api_models/src/events/customer.rs index 29f565042181..f98061a2d8d5 100644 --- a/crates/api_models/src/events/customer.rs +++ b/crates/api_models/src/events/customer.rs @@ -12,9 +12,9 @@ impl ApiEventMetric for CustomerDeleteResponse { impl ApiEventMetric for CustomerRequest { fn get_api_event_type(&self) -> Option { - Some(ApiEventsType::Customer { - customer_id: self.customer_id.clone(), - }) + self.customer_id + .clone() + .map(|customer_id| ApiEventsType::Customer { customer_id }) } } diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index d63d2b085b69..3d996ec05b01 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -4,7 +4,7 @@ use cards::CardNumber; use common_utils::{ consts::SURCHARGE_PERCENTAGE_PRECISION_LENGTH, crypto::OptionalEncryptableName, - pii, + id_type, pii, types::{MinorUnit, Percentage, Surcharge}, }; use serde::de; @@ -49,8 +49,8 @@ pub struct PaymentMethodCreate { pub metadata: Option, /// The unique identifier of the customer. - #[schema(example = "cus_meowerunwiuwiwqw")] - pub customer_id: Option, + #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: Option, /// The card network #[schema(example = "Visa")] @@ -194,8 +194,8 @@ pub struct PaymentMethodResponse { pub merchant_id: String, /// The unique identifier of the customer. - #[schema(example = "cus_meowerunwiuwiwqw")] - pub customer_id: Option, + #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: Option, /// The unique identifier of the Payment method #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")] @@ -846,8 +846,8 @@ pub struct CustomerDefaultPaymentMethodResponse { #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")] pub default_payment_method_id: Option, /// The unique identifier of the customer. - #[schema(example = "cus_meowerunwiuwiwqw")] - pub customer_id: String, + #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: id_type::CustomerId, /// The type of payment method use for the payment. #[schema(value_type = PaymentMethod,example = "card")] pub payment_method: api_enums::PaymentMethod, @@ -866,8 +866,8 @@ pub struct CustomerPaymentMethod { pub payment_method_id: String, /// The unique identifier of the customer. - #[schema(example = "cus_meowerunwiuwiwqw")] - pub customer_id: String, + #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: id_type::CustomerId, /// The type of payment method use for the payment. #[schema(value_type = PaymentMethod,example = "card")] @@ -952,7 +952,8 @@ pub struct PaymentMethodId { #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, ToSchema)] pub struct DefaultPaymentMethod { - pub customer_id: String, + #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: id_type::CustomerId, pub payment_method_id: String, } //------------------------------------------------TokenizeService------------------------------------------------ @@ -1034,7 +1035,7 @@ pub struct TokenizedCardValue2 { pub card_security_code: Option, pub card_fingerprint: Option, pub external_id: Option, - pub customer_id: Option, + pub customer_id: Option, pub payment_method_id: Option, } @@ -1045,7 +1046,7 @@ pub struct TokenizedWalletValue1 { #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TokenizedWalletValue2 { - pub customer_id: Option, + pub customer_id: Option, } #[derive(Debug, serde::Serialize, serde::Deserialize)] @@ -1055,7 +1056,7 @@ pub struct TokenizedBankTransferValue1 { #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TokenizedBankTransferValue2 { - pub customer_id: Option, + pub customer_id: Option, } #[derive(Debug, serde::Serialize, serde::Deserialize)] @@ -1065,5 +1066,5 @@ pub struct TokenizedBankRedirectValue1 { #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TokenizedBankRedirectValue2 { - pub customer_id: Option, + pub customer_id: Option, } diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index c24193ef38b2..30329e132b83 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -9,6 +9,7 @@ use common_utils::{ consts::default_payments_list_limit, crypto, ext_traits::{ConfigExt, Encode}, + id_type, pii::{self, Email}, types::MinorUnit, }; @@ -177,10 +178,34 @@ mod client_secret_tests { } } -#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema, PartialEq)] +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema, PartialEq)] pub struct CustomerDetails { /// The identifier for the customer. - pub id: String, + #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub id: id_type::CustomerId, + + /// The customer's name + #[schema(max_length = 255, value_type = Option, example = "John Doe")] + pub name: Option>, + + /// The customer's email address + #[schema(max_length = 255, value_type = Option, example = "johntest@test.com")] + pub email: Option, + + /// The customer's phone number + #[schema(value_type = Option, max_length = 10, example = "3141592653")] + pub phone: Option>, + + /// The country code for the customer's phone number + #[schema(max_length = 2, example = "+1")] + pub phone_country_code: Option, +} + +#[derive(Debug, serde::Serialize, Clone, ToSchema, PartialEq)] +pub struct CustomerDetailsResponse { + /// The identifier for the customer. + #[schema(value_type = String, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub id: id_type::CustomerId, /// The customer's name #[schema(max_length = 255, value_type = Option, example = "John Doe")] @@ -277,9 +302,9 @@ pub struct PaymentsRequest { /// Passing this object creates a new customer or attaches an existing customer to the payment pub customer: Option, - /// The identifier for the customer object. - #[schema(max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: Option, + /// The identifier for the customer + #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: Option, /// The customer's email address. /// This field will be deprecated soon, use the customer object instead @@ -762,7 +787,7 @@ pub struct VerifyRequest { // The merchant_id is generated through api key // and is later passed in the struct pub merchant_id: Option, - pub customer_id: Option, + pub customer_id: Option, pub email: Option, pub name: Option>, pub phone: Option>, @@ -3239,14 +3264,16 @@ pub struct PaymentsResponse { /// The identifier for the customer object. If not provided the customer ID will be autogenerated. /// This field will be deprecated soon. Please refer to `customer.id` #[schema( - max_length = 255, + max_length = 64, + min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44", - deprecated + deprecated, + value_type = Option, )] - pub customer_id: Option, + pub customer_id: Option, /// Details of customer attached to this payment - pub customer: Option, + pub customer: Option, /// A description of the payment #[schema(example = "It's my first payment request")] @@ -3534,8 +3561,13 @@ pub struct ExternalAuthenticationDetailsResponse { #[serde(deny_unknown_fields)] pub struct PaymentListConstraints { /// The identifier for customer - #[schema(example = "cus_meowuwunwiuwiwqw")] - pub customer_id: Option, + #[schema( + max_length = 64, + min_length = 1, + example = "cus_y3oqhf46pyzuxjbcn2giaqnb44", + value_type = Option, + )] + pub customer_id: Option, /// A cursor for use in pagination, fetch the next list after some object #[schema(example = "pay_fafa124123")] @@ -3632,7 +3664,7 @@ pub struct PaymentListFilterConstraints { /// The identifier for business profile pub profile_id: Option, /// The identifier for customer - pub customer_id: Option, + pub customer_id: Option, /// The limit on the number of objects. The default limit is 10 and max limit is 20 #[serde(default = "default_payments_list_limit")] pub limit: u32, @@ -3716,7 +3748,7 @@ pub struct VerifyResponse { pub merchant_id: Option, // pub status: enums::VerifyStatus, pub client_secret: Option>, - pub customer_id: Option, + pub customer_id: Option, pub email: crypto::OptionalEncryptableEmail, pub name: crypto::OptionalEncryptableName, pub phone: crypto::OptionalEncryptablePhone, @@ -3738,7 +3770,7 @@ pub struct PaymentsRedirectionResponse { pub struct MandateValidationFields { pub recurring_details: Option, pub confirm: Option, - pub customer_id: Option, + pub customer_id: Option, pub mandate_data: Option, pub setup_future_usage: Option, pub off_session: Option, @@ -3847,7 +3879,7 @@ pub struct PgRedirectResponse { pub payment_id: String, pub status: api_enums::IntentStatus, pub gateway_id: String, - pub customer_id: Option, + pub customer_id: Option, pub amount: Option, } diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs index 815234d57ed0..69c50da79c23 100644 --- a/crates/api_models/src/payouts.rs +++ b/crates/api_models/src/payouts.rs @@ -1,7 +1,7 @@ use cards::CardNumber; use common_utils::{ consts::default_payouts_list_limit, - crypto, + crypto, id_type, pii::{self, Email}, }; use masking::Secret; @@ -86,8 +86,8 @@ pub struct PayoutCreateRequest { pub billing: Option, /// The identifier for the customer object. If not provided the customer ID will be autogenerated. - #[schema(value_type = String, max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: Option, + #[schema(value_type = Option, max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: Option, /// Set to true to confirm the payout without review, no further action required #[schema(value_type = bool, example = true, default = false)] @@ -314,7 +314,7 @@ pub struct Venmo { pub telephone_number: Option>, } -#[derive(Debug, Default, ToSchema, Clone, Serialize)] +#[derive(Debug, ToSchema, Clone, Serialize)] #[serde(deny_unknown_fields)] pub struct PayoutCreateResponse { /// Unique identifier for the payout. This ensures idempotency for multiple payouts @@ -367,7 +367,7 @@ pub struct PayoutCreateResponse { /// The identifier for the customer object. If not provided the customer ID will be autogenerated. #[schema(value_type = String, max_length = 255, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: String, + pub customer_id: id_type::CustomerId, /// Set to true to confirm the payout without review, no further action required #[schema(value_type = bool, example = true, default = false)] @@ -564,8 +564,8 @@ pub struct PayoutIndividualDetails { #[serde(deny_unknown_fields)] pub struct PayoutListConstraints { /// The identifier for customer - #[schema(example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: Option, + #[schema(value_type = Option, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: Option, /// A cursor for use in pagination, fetch the next list after some object #[schema(example = "pay_fafa124123")] @@ -605,8 +605,8 @@ pub struct PayoutListFilterConstraints { /// The identifier for business profile pub profile_id: Option, /// The identifier for customer - #[schema(example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] - pub customer_id: Option, + #[schema(value_type = Option,example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: Option, /// The limit on the number of objects. The default limit is 10 and max limit is 20 #[serde(default = "default_payouts_list_limit")] pub limit: u32, diff --git a/crates/cards/tests/basic.rs b/crates/cards/tests/basic.rs index 709da760c1ba..0f27c9e02884 100644 --- a/crates/cards/tests/basic.rs +++ b/crates/cards/tests/basic.rs @@ -78,7 +78,7 @@ fn test_card_expiration() { // will panic on unwrap let invalid_card_exp = CardExpiration::try_from((13, curr_year)); - assert_eq!(*card_exp.get_month().peek(), 3); + assert_eq!(*card_exp.get_month().peek(), curr_month); assert_eq!(*card_exp.get_year().peek(), curr_year); assert!(!card_exp.is_expired().unwrap()); diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 94b53766db00..0ea826d15cdd 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -83,3 +83,9 @@ pub const DEFAULT_TTL_FOR_EXTENDED_CARD_INFO: u16 = 15 * 60; /// Max ttl for Extended card info in redis (in seconds) pub const MAX_TTL_FOR_EXTENDED_CARD_INFO: u16 = 60 * 60 * 2; + +/// Max Length for MerchantReferenceId +pub const MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 64; + +/// Minimum allowed length for MerchantReferenceId +pub const MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 1; diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index c51749cf2022..8939e07a76c7 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -1,6 +1,8 @@ use common_enums::{PaymentMethod, PaymentMethodType}; use serde::Serialize; +use crate::id_type; + pub trait ApiEventMetric { fn get_api_event_type(&self) -> Option { None @@ -24,7 +26,7 @@ pub enum ApiEventsType { payment_method_type: Option, }, Customer { - customer_id: String, + customer_id: id_type::CustomerId, }, User { //specified merchant_id will overridden on global defined diff --git a/crates/common_utils/src/id_type.rs b/crates/common_utils/src/id_type.rs new file mode 100644 index 000000000000..5edbed799ff7 --- /dev/null +++ b/crates/common_utils/src/id_type.rs @@ -0,0 +1,299 @@ +//! Common ID types + +use std::{ + borrow::Cow, + fmt::{Debug, Display}, +}; + +mod customer; + +pub use customer::CustomerId; +use diesel::{ + backend::Backend, + deserialize::FromSql, + expression::AsExpression, + serialize::{Output, ToSql}, + sql_types, +}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use crate::{fp_utils::when, generate_id_with_default_len}; + +/// This functions checks for the input string to contain valid characters +/// Returns Some(char) if there are any invalid characters, else None +fn get_invalid_input_character(input_string: Cow<'static, str>) -> Option { + input_string + .trim() + .chars() + .find(|char| !char.is_ascii_alphanumeric() && !matches!(char, '_' | '-')) +} + +#[derive(Debug, PartialEq, Serialize, Clone, Eq)] +/// A type for alphanumeric ids +pub(crate) struct AlphaNumericId(String); + +#[derive(Debug, Deserialize, Serialize, Error, Eq, PartialEq)] +#[error("value `{0}` contains invalid character `{1}`")] +/// The error type for alphanumeric id +pub(crate) struct AlphaNumericIdError(String, char); + +impl<'de> Deserialize<'de> for AlphaNumericId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let deserialized_string = String::deserialize(deserializer)?; + Self::from(deserialized_string.into()).map_err(serde::de::Error::custom) + } +} + +impl AlphaNumericId { + /// Creates a new alphanumeric id from string by applying validation checks + pub fn from(input_string: Cow<'static, str>) -> Result { + let invalid_character = get_invalid_input_character(input_string.clone()); + + if let Some(invalid_character) = invalid_character { + Err(AlphaNumericIdError( + input_string.to_string(), + invalid_character, + ))? + } + + Ok(Self(input_string.to_string())) + } + + /// Create a new alphanumeric id without any validations + pub(crate) fn new_unchecked(input_string: String) -> Self { + Self(input_string) + } + + /// Generate a new alphanumeric id of default length + pub(crate) fn new(prefix: &str) -> Self { + Self(generate_id_with_default_len(prefix)) + } +} + +/// A common type of id that can be used for merchant reference ids +#[derive(Debug, Clone, Serialize, PartialEq, Eq, AsExpression)] +#[diesel(sql_type = sql_types::Text)] +pub(crate) struct MerchantReferenceId(AlphaNumericId); + +/// Error generated from violation of constraints for MerchantReferenceId +#[derive(Debug, Deserialize, Serialize, Error, PartialEq, Eq)] +pub(crate) enum MerchantReferenceIdError { + #[error("the maximum allowed length for this field is {MAX_LENGTH}")] + /// Maximum length of string violated + MaxLengthViolated, + + #[error("the minimum required length for this field is {MIN_LENGTH}")] + /// Minimum length of string violated + MinLengthViolated, + + #[error("{0}")] + /// Input contains invalid characters + AlphanumericIdError(AlphaNumericIdError), +} + +impl From for MerchantReferenceIdError<0, 0> { + fn from(alphanumeric_id_error: AlphaNumericIdError) -> Self { + Self::AlphanumericIdError(alphanumeric_id_error) + } +} + +impl MerchantReferenceId { + /// Generates new [MerchantReferenceId] from the given input string + pub fn from( + input_string: Cow<'static, str>, + ) -> Result> { + let trimmed_input_string = input_string.trim().to_string(); + let length_of_input_string = u8::try_from(trimmed_input_string.len()) + .map_err(|_| MerchantReferenceIdError::MaxLengthViolated)?; + + when(length_of_input_string > MAX_LENGTH, || { + Err(MerchantReferenceIdError::MaxLengthViolated) + })?; + + when(length_of_input_string < MIN_LENGTH, || { + Err(MerchantReferenceIdError::MinLengthViolated) + })?; + + let alphanumeric_id = match AlphaNumericId::from(trimmed_input_string.into()) { + Ok(valid_alphanumeric_id) => valid_alphanumeric_id, + Err(error) => Err(MerchantReferenceIdError::AlphanumericIdError(error))?, + }; + + Ok(Self(alphanumeric_id)) + } + + /// Generate a new MerchantRefId of default length with the given prefix + pub fn new(prefix: &str) -> Self { + Self(AlphaNumericId::new(prefix)) + } +} + +impl<'de, const MAX_LENGTH: u8, const MIN_LENGTH: u8> Deserialize<'de> + for MerchantReferenceId +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let deserialized_string = String::deserialize(deserializer)?; + Self::from(deserialized_string.into()).map_err(serde::de::Error::custom) + } +} + +impl ToSql + for MerchantReferenceId +where + DB: Backend, + String: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + self.0 .0.to_sql(out) + } +} + +impl Display + for MerchantReferenceId +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0 .0) + } +} + +impl FromSql + for MerchantReferenceId +where + DB: Backend, + String: FromSql, +{ + fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result { + let string_val = String::from_sql(value)?; + Ok(Self(AlphaNumericId::new_unchecked(string_val))) + } +} + +#[cfg(test)] +mod alphanumeric_id_tests { + #![allow(clippy::unwrap_used)] + use super::*; + + const VALID_UNDERSCORE_ID_JSON: &str = r#""cus_abcdefghijklmnopqrstuv""#; + const EXPECTED_VALID_UNDERSCORE_ID: &str = "cus_abcdefghijklmnopqrstuv"; + + const VALID_HYPHEN_ID_JSON: &str = r#""cus-abcdefghijklmnopqrstuv""#; + const VALID_HYPHEN_ID_STRING: &str = "cus-abcdefghijklmnopqrstuv"; + + const INVALID_ID_WITH_SPACES: &str = r#""cus abcdefghijklmnopqrstuv""#; + const INVALID_ID_WITH_EMOJIS: &str = r#""cus_abc🦀""#; + + #[test] + fn test_id_deserialize_underscore() { + let parsed_alphanumeric_id = + serde_json::from_str::(VALID_UNDERSCORE_ID_JSON); + let alphanumeric_id = AlphaNumericId::from(EXPECTED_VALID_UNDERSCORE_ID.into()).unwrap(); + + assert_eq!(parsed_alphanumeric_id.unwrap(), alphanumeric_id); + } + + #[test] + fn test_id_deserialize_hyphen() { + let parsed_alphanumeric_id = serde_json::from_str::(VALID_HYPHEN_ID_JSON); + let alphanumeric_id = AlphaNumericId::from(VALID_HYPHEN_ID_STRING.into()).unwrap(); + + assert_eq!(parsed_alphanumeric_id.unwrap(), alphanumeric_id); + } + + #[test] + fn test_id_deserialize_with_spaces() { + let parsed_alphanumeric_id = serde_json::from_str::(INVALID_ID_WITH_SPACES); + + assert!(parsed_alphanumeric_id.is_err()); + } + + #[test] + fn test_id_deserialize_with_emojis() { + let parsed_alphanumeric_id = serde_json::from_str::(INVALID_ID_WITH_EMOJIS); + + assert!(parsed_alphanumeric_id.is_err()); + } +} + +#[cfg(test)] +mod merchant_reference_id_tests { + use super::*; + + const VALID_REF_ID_JSON: &str = r#""cus_abcdefghijklmnopqrstuv""#; + const MAX_LENGTH: u8 = 36; + const MIN_LENGTH: u8 = 6; + + const INVALID_REF_ID_JSON: &str = r#""cus abcdefghijklmnopqrstuv""#; + const INVALID_REF_ID_LENGTH: &str = r#""cus_abcdefghijklmnopqrstuvwxyzabcdefghij""#; + + #[test] + fn test_valid_reference_id() { + let parsed_merchant_reference_id = + serde_json::from_str::>(VALID_REF_ID_JSON); + + dbg!(&parsed_merchant_reference_id); + + assert!(parsed_merchant_reference_id.is_ok()); + } + + #[test] + fn test_invalid_ref_id() { + let parsed_merchant_reference_id = serde_json::from_str::< + MerchantReferenceId, + >(INVALID_REF_ID_JSON); + + assert!(parsed_merchant_reference_id.is_err()); + } + + #[test] + fn test_invalid_ref_id_error_message() { + let parsed_merchant_reference_id = serde_json::from_str::< + MerchantReferenceId, + >(INVALID_REF_ID_JSON); + + let expected_error_message = + r#"value `cus abcdefghijklmnopqrstuv` contains invalid character ` `"#.to_string(); + + let error_message = parsed_merchant_reference_id + .err() + .map(|error| error.to_string()); + + assert_eq!(error_message, Some(expected_error_message)); + } + + #[test] + fn test_invalid_ref_id_length() { + let parsed_merchant_reference_id = serde_json::from_str::< + MerchantReferenceId, + >(INVALID_REF_ID_LENGTH); + + dbg!(&parsed_merchant_reference_id); + + let expected_error_message = + format!("the maximum allowed length for this field is {MAX_LENGTH}"); + + assert!(parsed_merchant_reference_id + .is_err_and(|error_string| error_string.to_string().eq(&expected_error_message))); + } + + #[test] + fn test_invalid_ref_id_length_error_type() { + let parsed_merchant_reference_id = + MerchantReferenceId::::from(INVALID_REF_ID_LENGTH.into()); + + dbg!(&parsed_merchant_reference_id); + + assert!( + parsed_merchant_reference_id.is_err_and(|error_type| matches!( + error_type, + MerchantReferenceIdError::MaxLengthViolated + )) + ); + } +} diff --git a/crates/common_utils/src/id_type/customer.rs b/crates/common_utils/src/id_type/customer.rs new file mode 100644 index 000000000000..4e1256504ffe --- /dev/null +++ b/crates/common_utils/src/id_type/customer.rs @@ -0,0 +1,111 @@ +use std::{borrow::Cow, fmt::Debug}; + +use diesel::{ + backend::Backend, + deserialize::FromSql, + expression::AsExpression, + serialize::{Output, ToSql}, + sql_types, Queryable, +}; +use error_stack::{Result, ResultExt}; +use serde::{Deserialize, Serialize}; + +use crate::{ + consts::{MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH}, + errors, generate_customer_id_of_default_length, + id_type::MerchantReferenceId, +}; + +/// A type for customer_id that can be used for customer ids +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, AsExpression)] +#[diesel(sql_type = sql_types::Text)] +pub struct CustomerId( + MerchantReferenceId< + MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, + MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, + >, +); + +impl Default for CustomerId { + fn default() -> Self { + generate_customer_id_of_default_length() + } +} + +/// This is to display the `CustomerId` as CustomerId(abcd) +impl Debug for CustomerId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("CustomerId").field(&self.0 .0 .0).finish() + } +} + +impl Queryable for CustomerId +where + DB: Backend, + Self: FromSql, +{ + type Row = Self; + + fn build(row: Self::Row) -> diesel::deserialize::Result { + Ok(row) + } +} + +impl CustomerId { + pub(crate) fn new( + merchant_ref_id: MerchantReferenceId< + MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, + MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, + >, + ) -> Self { + Self(merchant_ref_id) + } + + /// Get the string representation of customer id + pub fn get_string_repr(&self) -> &str { + &self.0 .0 .0 + } + + /// Create a Customer id from string + pub fn from(input_string: Cow<'static, str>) -> Result { + let merchant_ref_id = MerchantReferenceId::from(input_string).change_context( + errors::ValidationError::IncorrectValueProvided { + field_name: "customer_id", + }, + )?; + + Ok(Self(merchant_ref_id)) + } +} + +impl masking::SerializableSecret for CustomerId {} + +impl ToSql for CustomerId +where + DB: Backend, + MerchantReferenceId< + MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, + MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, + >: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> diesel::serialize::Result { + self.0.to_sql(out) + } +} + +impl FromSql for CustomerId +where + DB: Backend, + MerchantReferenceId< + MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, + MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, + >: FromSql, +{ + fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result { + MerchantReferenceId::< + MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, + MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, + >::from_sql(value) + .map(Self) + } +} diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index f24b67fde1b3..d77442ad40f1 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -2,6 +2,11 @@ #![warn(missing_docs, missing_debug_implementations)] #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR" ), "/", "README.md"))] +use crate::{ + consts::ID_LENGTH, + id_type::{CustomerId, MerchantReferenceId}, +}; + pub mod access_token; pub mod consts; pub mod crypto; @@ -11,6 +16,7 @@ pub mod errors; pub mod events; pub mod ext_traits; pub mod fp_utils; +pub mod id_type; pub mod macros; pub mod pii; #[allow(missing_docs)] // Todo: add docs @@ -193,10 +199,22 @@ pub fn generate_id(length: usize, prefix: &str) -> String { format!("{}_{}", prefix, nanoid::nanoid!(length, &consts::ALPHABETS)) } +/// Generate a MerchantRefId with the default length +fn generate_merchant_ref_id_with_default_length( + prefix: &str, +) -> MerchantReferenceId { + MerchantReferenceId::::new(prefix) +} + +/// Generate a customer id with default length +pub fn generate_customer_id_of_default_length() -> CustomerId { + CustomerId::new(generate_merchant_ref_id_with_default_length("cus")) +} + /// Generate a nanoid with the given prefix and a default length #[inline] pub fn generate_id_with_default_len(prefix: &str) -> String { - let len = consts::ID_LENGTH; + let len = ID_LENGTH; format!("{}_{}", prefix, nanoid::nanoid!(len, &consts::ALPHABETS)) } @@ -205,3 +223,31 @@ pub fn generate_id_with_default_len(prefix: &str) -> String { pub fn generate_time_ordered_id(prefix: &str) -> String { format!("{prefix}_{}", uuid::Uuid::now_v7().as_simple()) } + +#[cfg(test)] +mod nanoid_tests { + #![allow(clippy::unwrap_used)] + use super::*; + use crate::{ + consts::{ + MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, + }, + id_type::AlphaNumericId, + }; + + #[test] + fn test_generate_id_with_alphanumeric_id() { + let alphanumeric_id = AlphaNumericId::from(generate_id(10, "def").into()); + assert!(alphanumeric_id.is_ok()) + } + + #[test] + fn test_generate_merchant_ref_id_with_default_length() { + let ref_id = MerchantReferenceId::< + MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH, + MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH, + >::from(generate_id_with_default_len("def").into()); + + assert!(ref_id.is_ok()) + } +} diff --git a/crates/diesel_models/src/address.rs b/crates/diesel_models/src/address.rs index e3d5ed73992a..aa1d397797d6 100644 --- a/crates/diesel_models/src/address.rs +++ b/crates/diesel_models/src/address.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -19,7 +20,7 @@ pub struct AddressNew { pub last_name: Option, pub phone_number: Option, pub country_code: Option, - pub customer_id: Option, + pub customer_id: Option, pub merchant_id: String, pub payment_id: Option, pub created_at: PrimitiveDateTime, @@ -46,7 +47,7 @@ pub struct Address { pub country_code: Option, pub created_at: PrimitiveDateTime, pub modified_at: PrimitiveDateTime, - pub customer_id: Option, + pub customer_id: Option, pub merchant_id: String, pub payment_id: Option, pub updated_by: String, diff --git a/crates/diesel_models/src/customers.rs b/crates/diesel_models/src/customers.rs index 0d1657136e02..63be0351a0c2 100644 --- a/crates/diesel_models/src/customers.rs +++ b/crates/diesel_models/src/customers.rs @@ -1,5 +1,4 @@ -use common_enums::MerchantStorageScheme; -use common_utils::pii; +use common_utils::{id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use time::PrimitiveDateTime; @@ -10,7 +9,7 @@ use crate::{encryption::Encryption, schema::customers}; )] #[diesel(table_name = customers)] pub struct CustomerNew { - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub name: Option, pub email: Option, @@ -26,7 +25,7 @@ pub struct CustomerNew { } impl CustomerNew { - pub fn update_storage_scheme(&mut self, storage_scheme: MerchantStorageScheme) { + pub fn update_storage_scheme(&mut self, storage_scheme: common_enums::MerchantStorageScheme) { self.updated_by = Some(storage_scheme.to_string()); } } @@ -57,7 +56,7 @@ impl From for Customer { #[diesel(table_name = customers)] pub struct Customer { pub id: i32, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub name: Option, pub email: Option, diff --git a/crates/diesel_models/src/ephemeral_key.rs b/crates/diesel_models/src/ephemeral_key.rs index 77b9c647e43b..1cf15e9bfdae 100644 --- a/crates/diesel_models/src/ephemeral_key.rs +++ b/crates/diesel_models/src/ephemeral_key.rs @@ -1,7 +1,9 @@ +use common_utils::id_type; + pub struct EphemeralKeyNew { pub id: String, pub merchant_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub secret: String, } @@ -9,7 +11,7 @@ pub struct EphemeralKeyNew { pub struct EphemeralKey { pub id: String, pub merchant_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub created_at: i64, pub expires: i64, pub secret: String, diff --git a/crates/diesel_models/src/locker_mock_up.rs b/crates/diesel_models/src/locker_mock_up.rs index eefaea86abd0..e078b272cbab 100644 --- a/crates/diesel_models/src/locker_mock_up.rs +++ b/crates/diesel_models/src/locker_mock_up.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{Identifiable, Insertable, Queryable}; use crate::schema::locker_mock_up; @@ -16,7 +17,7 @@ pub struct LockerMockUp { pub card_exp_month: String, pub name_on_card: Option, pub nickname: Option, - pub customer_id: Option, + pub customer_id: Option, pub duplicate: Option, pub card_cvc: Option, pub payment_method_id: Option, @@ -37,7 +38,7 @@ pub struct LockerMockUpNew { pub name_on_card: Option, pub card_cvc: Option, pub payment_method_id: Option, - pub customer_id: Option, + pub customer_id: Option, pub nickname: Option, pub enc_card_data: Option, } diff --git a/crates/diesel_models/src/mandate.rs b/crates/diesel_models/src/mandate.rs index 65576b08f237..9a32e1049d5b 100644 --- a/crates/diesel_models/src/mandate.rs +++ b/crates/diesel_models/src/mandate.rs @@ -1,5 +1,5 @@ use common_enums::MerchantStorageScheme; -use common_utils::pii; +use common_utils::{id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use masking::Secret; use time::PrimitiveDateTime; @@ -11,7 +11,7 @@ use crate::{enums as storage_enums, schema::mandate}; pub struct Mandate { pub id: i32, pub mandate_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub payment_method_id: String, pub mandate_status: storage_enums::MandateStatus, @@ -49,7 +49,7 @@ pub struct Mandate { #[diesel(table_name = mandate)] pub struct MandateNew { pub mandate_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub payment_method_id: String, pub mandate_status: storage_enums::MandateStatus, diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 2c992554671e..c54ff2a429e1 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -1,5 +1,5 @@ use common_enums::RequestIncrementalAuthorization; -use common_utils::{pii, types::MinorUnit}; +use common_utils::{id_type, pii, types::MinorUnit}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -16,7 +16,7 @@ pub struct PaymentIntent { pub amount: MinorUnit, pub currency: Option, pub amount_captured: Option, - pub customer_id: Option, + pub customer_id: Option, pub description: Option, pub return_url: Option, pub metadata: Option, @@ -73,7 +73,7 @@ pub struct PaymentIntentNew { pub amount: MinorUnit, pub currency: Option, pub amount_captured: Option, - pub customer_id: Option, + pub customer_id: Option, pub description: Option, pub return_url: Option, pub metadata: Option, @@ -134,7 +134,7 @@ pub enum PaymentIntentUpdate { ReturnUrlUpdate { return_url: Option, status: Option, - customer_id: Option, + customer_id: Option, shipping_address_id: Option, billing_address_id: Option, updated_by: String, @@ -155,7 +155,7 @@ pub enum PaymentIntentUpdate { currency: storage_enums::Currency, setup_future_usage: Option, status: storage_enums::IntentStatus, - customer_id: Option, + customer_id: Option, shipping_address_id: Option, billing_address_id: Option, return_url: Option, @@ -216,7 +216,7 @@ pub struct PaymentIntentUpdateInternal { pub currency: Option, pub status: Option, pub amount_captured: Option, - pub customer_id: Option, + pub customer_id: Option, pub return_url: Option, pub setup_future_usage: Option, pub off_session: Option, diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index f4d46b1d28f2..d9c7a66a6b2d 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -1,5 +1,5 @@ use common_enums::MerchantStorageScheme; -use common_utils::pii; +use common_utils::{id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -11,7 +11,7 @@ use crate::{encryption::Encryption, enums as storage_enums, schema::payment_meth #[diesel(table_name = payment_methods)] pub struct PaymentMethod { pub id: i32, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub payment_method_id: String, #[diesel(deserialize_as = super::OptionalDieselArray)] @@ -50,7 +50,7 @@ pub struct PaymentMethod { )] #[diesel(table_name = payment_methods)] pub struct PaymentMethodNew { - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub payment_method_id: String, pub payment_method: Option, diff --git a/crates/diesel_models/src/payout_attempt.rs b/crates/diesel_models/src/payout_attempt.rs index 765cde503e09..8544d2d273fb 100644 --- a/crates/diesel_models/src/payout_attempt.rs +++ b/crates/diesel_models/src/payout_attempt.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{self, Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -10,7 +11,7 @@ use crate::{enums as storage_enums, schema::payout_attempt}; pub struct PayoutAttempt { pub payout_attempt_id: String, pub payout_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub address_id: String, pub connector: Option, @@ -34,7 +35,6 @@ pub struct PayoutAttempt { #[derive( Clone, Debug, - Default, Eq, PartialEq, Insertable, @@ -47,7 +47,7 @@ pub struct PayoutAttempt { pub struct PayoutAttemptNew { pub payout_attempt_id: String, pub payout_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub address_id: String, pub connector: Option, diff --git a/crates/diesel_models/src/payouts.rs b/crates/diesel_models/src/payouts.rs index 4c6fe40c3999..1549610d4e45 100644 --- a/crates/diesel_models/src/payouts.rs +++ b/crates/diesel_models/src/payouts.rs @@ -1,4 +1,4 @@ -use common_utils::pii; +use common_utils::{id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{self, Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -12,7 +12,7 @@ use crate::{enums as storage_enums, schema::payouts}; pub struct Payouts { pub payout_id: String, pub merchant_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub address_id: String, pub payout_type: storage_enums::PayoutType, pub payout_method_id: Option, @@ -38,7 +38,6 @@ pub struct Payouts { #[derive( Clone, Debug, - Default, Eq, PartialEq, Insertable, @@ -51,7 +50,7 @@ pub struct Payouts { pub struct PayoutsNew { pub payout_id: String, pub merchant_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub address_id: String, pub payout_type: storage_enums::PayoutType, pub payout_method_id: Option, diff --git a/crates/diesel_models/src/query/address.rs b/crates/diesel_models/src/query/address.rs index ffcd740f0760..cf3cd41c405e 100644 --- a/crates/diesel_models/src/query/address.rs +++ b/crates/diesel_models/src/query/address.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods}; use super::generics; @@ -90,7 +91,7 @@ impl Address { pub async fn update_by_merchant_id_customer_id( conn: &PgPooledConn, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, address: AddressUpdateInternal, ) -> StorageResult> { diff --git a/crates/diesel_models/src/query/customers.rs b/crates/diesel_models/src/query/customers.rs index e5ec96c8fc57..f1cc7a734811 100644 --- a/crates/diesel_models/src/query/customers.rs +++ b/crates/diesel_models/src/query/customers.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods}; use super::generics; @@ -17,7 +18,7 @@ impl CustomerNew { impl Customer { pub async fn update_by_customer_id_merchant_id( conn: &PgPooledConn, - customer_id: String, + customer_id: id_type::CustomerId, merchant_id: String, customer: CustomerUpdateInternal, ) -> StorageResult { @@ -44,7 +45,7 @@ impl Customer { pub async fn delete_by_customer_id_merchant_id( conn: &PgPooledConn, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, ) -> StorageResult { generics::generic_delete::<::Table, _>( @@ -58,7 +59,7 @@ impl Customer { pub async fn find_by_customer_id_merchant_id( conn: &PgPooledConn, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, ) -> StorageResult { generics::generic_find_by_id::<::Table, _, _>( @@ -84,7 +85,7 @@ impl Customer { pub async fn find_optional_by_customer_id_merchant_id( conn: &PgPooledConn, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, ) -> StorageResult> { generics::generic_find_by_id_optional::<::Table, _, _>( diff --git a/crates/diesel_models/src/query/mandate.rs b/crates/diesel_models/src/query/mandate.rs index 6925cd12b6a6..584721ed8f39 100644 --- a/crates/diesel_models/src/query/mandate.rs +++ b/crates/diesel_models/src/query/mandate.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods, Table}; use error_stack::report; @@ -42,7 +43,7 @@ impl Mandate { pub async fn find_by_merchant_id_customer_id( conn: &PgPooledConn, merchant_id: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, ) -> StorageResult> { generics::generic_filter::< ::Table, diff --git a/crates/diesel_models/src/query/payment_method.rs b/crates/diesel_models/src/query/payment_method.rs index d3ae4409ee84..8008fec0206a 100644 --- a/crates/diesel_models/src/query/payment_method.rs +++ b/crates/diesel_models/src/query/payment_method.rs @@ -1,4 +1,5 @@ use async_bb8_diesel::AsyncRunQueryDsl; +use common_utils::id_type; use diesel::{ associations::HasTable, debug_query, pg::Pg, BoolExpressionMethods, ExpressionMethods, QueryDsl, Table, @@ -85,7 +86,7 @@ impl PaymentMethod { pub async fn find_by_customer_id_merchant_id( conn: &PgPooledConn, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, limit: Option, ) -> StorageResult> { @@ -103,7 +104,7 @@ impl PaymentMethod { pub async fn get_count_by_customer_id_merchant_id_status( conn: &PgPooledConn, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, ) -> StorageResult { @@ -130,7 +131,7 @@ impl PaymentMethod { pub async fn find_by_customer_id_merchant_id_status( conn: &PgPooledConn, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: storage_enums::PaymentMethodStatus, limit: Option, diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index b9adc162c471..089cb29aec5e 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -1,4 +1,4 @@ -use common_utils::{self, pii, types::MinorUnit}; +use common_utils::{self, id_type, pii, types::MinorUnit}; use time::PrimitiveDateTime; pub mod payment_attempt; @@ -18,7 +18,7 @@ pub struct PaymentIntent { pub amount: MinorUnit, pub currency: Option, 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/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 5cf6f1219f5b..d413e97289af 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -1,7 +1,7 @@ use common_enums as storage_enums; use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, - pii, + id_type, pii, types::MinorUnit, }; use serde::{Deserialize, Serialize}; @@ -78,7 +78,7 @@ pub struct PaymentIntentNew { pub amount: MinorUnit, pub currency: Option, pub amount_captured: Option, - pub customer_id: Option, + pub customer_id: Option, pub description: Option, pub return_url: Option, pub metadata: Option, @@ -135,7 +135,7 @@ pub enum PaymentIntentUpdate { ReturnUrlUpdate { return_url: Option, status: Option, - customer_id: Option, + customer_id: Option, shipping_address_id: Option, billing_address_id: Option, updated_by: String, @@ -156,7 +156,7 @@ pub enum PaymentIntentUpdate { currency: storage_enums::Currency, setup_future_usage: Option, status: storage_enums::IntentStatus, - customer_id: Option, + customer_id: Option, shipping_address_id: Option, billing_address_id: Option, return_url: Option, @@ -216,7 +216,7 @@ pub struct PaymentIntentUpdateInternal { pub currency: Option, pub status: Option, pub amount_captured: Option, - pub customer_id: Option, + pub customer_id: Option, pub return_url: Option, pub setup_future_usage: Option, pub off_session: Option, @@ -458,7 +458,7 @@ pub struct PaymentIntentListParams { pub authentication_type: Option>, pub merchant_connector_id: Option>, pub profile_id: Option, - pub customer_id: Option, + pub customer_id: Option, pub starting_after_id: Option, pub ending_before_id: Option, pub limit: Option, diff --git a/crates/hyperswitch_domain_models/src/payouts.rs b/crates/hyperswitch_domain_models/src/payouts.rs index 75a5a21fb7ac..bb636d2dc488 100644 --- a/crates/hyperswitch_domain_models/src/payouts.rs +++ b/crates/hyperswitch_domain_models/src/payouts.rs @@ -3,7 +3,7 @@ pub mod payout_attempt; pub mod payouts; use common_enums as storage_enums; -use common_utils::consts; +use common_utils::{consts, id_type}; use time::PrimitiveDateTime; pub enum PayoutFetchConstraints { @@ -20,7 +20,7 @@ pub struct PayoutListParams { pub status: Option>, pub payout_method: Option>, pub profile_id: Option, - pub customer_id: Option, + pub customer_id: Option, pub starting_after_id: Option, pub ending_before_id: Option, pub entity_type: Option, diff --git a/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs b/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs index afe45b0a6a2a..a19e694ff275 100644 --- a/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs @@ -1,5 +1,6 @@ use api_models::enums::PayoutConnectors; use common_enums as storage_enums; +use common_utils::{generate_customer_id_of_default_length, id_type}; use serde::{Deserialize, Serialize}; use storage_enums::MerchantStorageScheme; use time::PrimitiveDateTime; @@ -51,7 +52,7 @@ pub struct PayoutListFilters { pub struct PayoutAttempt { pub payout_attempt_id: String, pub payout_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub address_id: String, pub connector: Option, @@ -76,7 +77,7 @@ pub struct PayoutAttempt { pub struct PayoutAttemptNew { pub payout_attempt_id: String, pub payout_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub address_id: String, pub connector: Option, @@ -102,7 +103,7 @@ impl Default for PayoutAttemptNew { Self { payout_attempt_id: String::default(), payout_id: String::default(), - customer_id: String::default(), + customer_id: generate_customer_id_of_default_length(), merchant_id: String::default(), address_id: String::default(), connector: None, diff --git a/crates/hyperswitch_domain_models/src/payouts/payouts.rs b/crates/hyperswitch_domain_models/src/payouts/payouts.rs index 679e4488f7b6..c9017fff5b92 100644 --- a/crates/hyperswitch_domain_models/src/payouts/payouts.rs +++ b/crates/hyperswitch_domain_models/src/payouts/payouts.rs @@ -1,5 +1,5 @@ use common_enums as storage_enums; -use common_utils::pii; +use common_utils::{id_type, pii}; use serde::{Deserialize, Serialize}; use storage_enums::MerchantStorageScheme; use time::PrimitiveDateTime; @@ -71,7 +71,7 @@ pub trait PayoutsInterface { pub struct Payouts { pub payout_id: String, pub merchant_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub address_id: String, pub payout_type: storage_enums::PayoutType, pub payout_method_id: Option, @@ -96,7 +96,7 @@ pub struct Payouts { pub struct PayoutsNew { pub payout_id: String, pub merchant_id: String, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub address_id: String, pub payout_type: storage_enums::PayoutType, pub payout_method_id: Option, @@ -124,7 +124,7 @@ impl Default for PayoutsNew { Self { payout_id: String::default(), merchant_id: String::default(), - customer_id: String::default(), + customer_id: common_utils::generate_customer_id_of_default_length(), address_id: String::default(), payout_type: storage_enums::PayoutType::default(), payout_method_id: Option::default(), diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index d0481cecc539..b5d96a5c53f6 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, marker::PhantomData}; +use common_utils::id_type; use masking::Secret; use crate::payment_address::PaymentAddress; @@ -8,7 +9,7 @@ use crate::payment_address::PaymentAddress; pub struct RouterData { pub flow: PhantomData, pub merchant_id: String, - pub customer_id: Option, + pub customer_id: Option, pub connector_customer: Option, pub connector: String, pub payment_id: String, diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index c8e68586dade..5a4838f9b79f 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -1,7 +1,7 @@ pub mod authentication; pub mod fraud_check; use api_models::payments::RequestSurchargeDetails; -use common_utils::{consts, errors, ext_traits::OptionExt, pii, types as common_types}; +use common_utils::{consts, errors, ext_traits::OptionExt, id_type, pii, types as common_types}; use diesel_models::enums as storage_enums; use error_stack::ResultExt; use masking::Secret; @@ -617,7 +617,7 @@ pub struct PayoutsData { #[derive(Debug, Default, Clone)] pub struct CustomerDetails { - pub customer_id: Option, + pub customer_id: Option, pub name: Option>, pub email: Option, pub phone: Option>, diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index fd0688ed2930..237cddcf94e7 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -529,6 +529,7 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::PaymentChargeRequest, api_models::payments::PaymentChargeResponse, api_models::refunds::ChargeRefunds, + api_models::payments::CustomerDetailsResponse, )), modifiers(&SecurityAddon) )] diff --git a/crates/pm_auth/src/connector/plaid/transformers.rs b/crates/pm_auth/src/connector/plaid/transformers.rs index f163875958e4..9d9039144832 100644 --- a/crates/pm_auth/src/connector/plaid/transformers.rs +++ b/crates/pm_auth/src/connector/plaid/transformers.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use common_enums::{PaymentMethod, PaymentMethodType}; +use common_utils::id_type; use masking::{PeekInterface, Secret}; use serde::{Deserialize, Serialize}; @@ -19,7 +20,7 @@ pub struct PlaidLinkTokenRequest { #[derive(Debug, Serialize, Eq, PartialEq)] pub struct User { - pub client_user_id: String, + pub client_user_id: id_type::CustomerId, } impl TryFrom<&types::LinkTokenRouterData> for PlaidLinkTokenRequest { diff --git a/crates/pm_auth/src/types.rs b/crates/pm_auth/src/types.rs index d5c66b9c64a4..f2a848c2f608 100644 --- a/crates/pm_auth/src/types.rs +++ b/crates/pm_auth/src/types.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use api::auth_service::{BankAccountCredentials, ExchangeToken, LinkToken}; use common_enums::{PaymentMethod, PaymentMethodType}; +use common_utils::id_type; use masking::Secret; #[derive(Debug, Clone)] pub struct PaymentAuthRouterData { @@ -21,7 +22,7 @@ pub struct LinkTokenRequest { pub client_name: String, pub country_codes: Option>, pub language: Option, - pub user_info: Option, + pub user_info: Option, } #[derive(Debug, Clone)] diff --git a/crates/router/src/compatibility/stripe/customers.rs b/crates/router/src/compatibility/stripe/customers.rs index 3628c72a31c9..67c0f973b969 100644 --- a/crates/router/src/compatibility/stripe/customers.rs +++ b/crates/router/src/compatibility/stripe/customers.rs @@ -1,5 +1,6 @@ pub mod types; use actix_web::{web, HttpRequest, HttpResponse}; +use common_utils::id_type; use error_stack::report; use router_env::{instrument, tracing, Flow}; @@ -55,7 +56,7 @@ pub async fn customer_create( pub async fn customer_retrieve( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> HttpResponse { let payload = customer_types::CustomerId { customer_id: path.into_inner(), @@ -90,7 +91,7 @@ pub async fn customer_update( state: web::Data, qs_config: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, form_payload: web::Bytes, ) -> HttpResponse { let payload: types::CustomerUpdateRequest = match qs_config.deserialize_bytes(&form_payload) { @@ -102,7 +103,7 @@ pub async fn customer_update( let customer_id = path.into_inner(); let mut cust_update_req: customer_types::CustomerRequest = payload.into(); - cust_update_req.customer_id = customer_id; + cust_update_req.customer_id = Some(customer_id); let flow = Flow::CustomersUpdate; @@ -132,7 +133,7 @@ pub async fn customer_update( pub async fn customer_delete( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> HttpResponse { let payload = customer_types::CustomerId { customer_id: path.into_inner(), @@ -166,7 +167,7 @@ pub async fn customer_delete( pub async fn list_customer_payment_method_api( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, json_payload: web::Query, ) -> HttpResponse { let payload = json_payload.into_inner(); @@ -193,7 +194,7 @@ pub async fn list_customer_payment_method_api( auth.merchant_account, auth.key_store, Some(req), - Some(customer_id.as_str()), + Some(&customer_id), ) }, &auth::ApiKeyAuth, diff --git a/crates/router/src/compatibility/stripe/customers/types.rs b/crates/router/src/compatibility/stripe/customers/types.rs index 8298595bbc51..0e1a5d1a7932 100644 --- a/crates/router/src/compatibility/stripe/customers/types.rs +++ b/crates/router/src/compatibility/stripe/customers/types.rs @@ -3,7 +3,7 @@ use std::{convert::From, default::Default}; use api_models::{payment_methods as api_types, payments}; use common_utils::{ crypto::Encryptable, - date_time, + date_time, id_type, pii::{self, Email}, }; use serde::{Deserialize, Serialize}; @@ -80,9 +80,9 @@ pub struct CustomerUpdateRequest { pub tax_exempt: Option, // not used } -#[derive(Default, Serialize, PartialEq, Eq)] +#[derive(Serialize, PartialEq, Eq)] pub struct CreateCustomerResponse { - pub id: String, + pub id: id_type::CustomerId, pub object: String, pub created: u64, pub description: Option, @@ -95,9 +95,9 @@ pub struct CreateCustomerResponse { pub type CustomerRetrieveResponse = CreateCustomerResponse; pub type CustomerUpdateResponse = CreateCustomerResponse; -#[derive(Default, Serialize, PartialEq, Eq)] +#[derive(Serialize, PartialEq, Eq)] pub struct CustomerDeleteResponse { - pub id: String, + pub id: id_type::CustomerId, pub deleted: bool, } @@ -120,7 +120,7 @@ impl From for payments::AddressDetails { impl From for api::CustomerRequest { fn from(req: CreateCustomerRequest) -> Self { Self { - customer_id: api_models::customers::generate_customer_id(), + customer_id: Some(common_utils::generate_customer_id_of_default_length()), name: req.name, phone: req.phone, email: req.email, diff --git a/crates/router/src/compatibility/stripe/payment_intents/types.rs b/crates/router/src/compatibility/stripe/payment_intents/types.rs index 0526eacb2e80..5df2ba148743 100644 --- a/crates/router/src/compatibility/stripe/payment_intents/types.rs +++ b/crates/router/src/compatibility/stripe/payment_intents/types.rs @@ -5,6 +5,7 @@ use common_utils::{ crypto::Encryptable, date_time, ext_traits::StringExt, + id_type, pii::{IpAddress, SecretSerdeValue, UpiVpaMaskingStrategy}, types::MinorUnit, }; @@ -246,7 +247,7 @@ pub struct StripePaymentIntentRequest { pub amount_capturable: Option, pub confirm: Option, pub capture_method: Option, - pub customer: Option, + pub customer: Option, pub description: Option, pub payment_method_data: Option, pub receipt_email: Option, @@ -466,7 +467,7 @@ pub struct StripePaymentIntentResponse { pub status: StripePaymentStatus, pub client_secret: Option>, pub created: Option, - pub customer: Option, + pub customer: Option, pub refunds: Option>, pub mandate: Option, pub metadata: Option, @@ -609,7 +610,7 @@ impl Charges { #[derive(Clone, Debug, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct StripePaymentListConstraints { - pub customer: Option, + pub customer: Option, pub starting_after: Option, pub ending_before: Option, #[serde(default = "default_limit")] diff --git a/crates/router/src/compatibility/stripe/setup_intents/types.rs b/crates/router/src/compatibility/stripe/setup_intents/types.rs index 9a1cf58f11b6..2f01da298328 100644 --- a/crates/router/src/compatibility/stripe/setup_intents/types.rs +++ b/crates/router/src/compatibility/stripe/setup_intents/types.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use api_models::payments; -use common_utils::{date_time, ext_traits::StringExt, pii as secret}; +use common_utils::{date_time, ext_traits::StringExt, id_type, pii as secret}; use error_stack::ResultExt; use router_env::logger; use serde::{Deserialize, Serialize}; @@ -152,7 +152,7 @@ impl From for payments::Address { #[derive(Default, Deserialize, Clone)] pub struct StripeSetupIntentRequest { pub confirm: Option, - pub customer: Option, + pub customer: Option, pub connector: Option>, pub description: Option, pub currency: Option, @@ -461,7 +461,7 @@ pub struct StripeSetupIntentResponse { pub metadata: Option, #[serde(with = "common_utils::custom_serde::iso8601::option")] pub created: Option, - pub customer: Option, + pub customer: Option, pub refunds: Option>, pub mandate_id: Option, pub next_action: Option, @@ -538,7 +538,7 @@ impl From for StripeSetupIntentResponse { #[derive(Clone, Debug, serde::Deserialize)] #[serde(deny_unknown_fields)] pub struct StripePaymentListConstraints { - pub customer: Option, + pub customer: Option, pub starting_after: Option, pub ending_before: Option, #[serde(default = "default_limit")] diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index 8415453b6ae5..2fefe996e88e 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use common_utils::pii::Email; +use common_utils::{id_type, pii::Email}; use error_stack::report; use masking::{ExposeInterface, Secret}; use reqwest::Url; @@ -311,7 +311,7 @@ pub struct BankRedirectionPMData { #[serde(rename = "customer.email")] customer_email: Option, #[serde(rename = "customer.merchantCustomerId")] - merchant_customer_id: Option>, + merchant_customer_id: Option>, merchant_transaction_id: Option>, } diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 1c24009c54b4..bed99a9fb663 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -2,7 +2,7 @@ use api_models::payouts::PayoutMethodData; use api_models::{enums, payments, webhooks}; use cards::CardNumber; -use common_utils::{ext_traits::Encode, pii}; +use common_utils::{ext_traits::Encode, id_type, pii}; use error_stack::{report, ResultExt}; use masking::{ExposeInterface, PeekInterface}; use reqwest::Url; @@ -1640,7 +1640,8 @@ fn get_recurring_processing_model( match (item.request.setup_future_usage, item.request.off_session) { (Some(storage_enums::FutureUsage::OffSession), _) => { let customer_id = item.get_customer_id()?; - let shopper_reference = format!("{}_{}", item.merchant_id, customer_id); + let shopper_reference = + format!("{}_{}", item.merchant_id, customer_id.get_string_repr()); let store_payment_method = item.request.is_mandate_payment(); Ok(( Some(AdyenRecurringModel::UnscheduledCardOnFile), @@ -1651,7 +1652,11 @@ fn get_recurring_processing_model( (_, Some(true)) => Ok(( Some(AdyenRecurringModel::UnscheduledCardOnFile), None, - Some(format!("{}_{}", item.merchant_id, item.get_customer_id()?)), + Some(format!( + "{}_{}", + item.merchant_id, + item.get_customer_id()?.get_string_repr() + )), )), _ => Ok((None, None, None)), } @@ -1814,10 +1819,13 @@ fn get_social_security_number(voucher_data: &domain::VoucherData) -> Option, merchant_id: String) -> Option { +fn build_shopper_reference( + customer_id: &Option, + merchant_id: String, +) -> Option { customer_id .clone() - .map(|c_id| format!("{}_{}", merchant_id, c_id)) + .map(|c_id| format!("{}_{}", merchant_id, c_id.get_string_repr())) } impl<'a> TryFrom<(&domain::BankDebitData, &types::PaymentsAuthorizeRouterData)> diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index de68438edc46..b281d0a0a773 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -1,7 +1,7 @@ use common_utils::{ errors::CustomResult, ext_traits::{Encode, ValueExt}, - pii, + id_type, pii, }; use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface, Secret, StrongSecret}; @@ -179,7 +179,7 @@ struct PaymentProfileDetails { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CustomerDetails { - id: String, + id: id_type::CustomerId, } #[derive(Debug, Serialize)] @@ -273,7 +273,7 @@ pub struct AuthorizedotnetZeroMandateRequest { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct Profile { - merchant_customer_id: String, + merchant_customer_id: id_type::CustomerId, #[serde(skip_serializing_if = "Option::is_none")] description: Option, email: Option, diff --git a/crates/router/src/connector/billwerk/transformers.rs b/crates/router/src/connector/billwerk/transformers.rs index 811076a11da6..113f26b55f43 100644 --- a/crates/router/src/connector/billwerk/transformers.rs +++ b/crates/router/src/connector/billwerk/transformers.rs @@ -1,4 +1,7 @@ -use common_utils::pii::{Email, SecretSerdeValue}; +use common_utils::{ + id_type, + pii::{Email, SecretSerdeValue}, +}; use masking::{ExposeInterface, Secret}; use serde::{Deserialize, Serialize}; @@ -145,7 +148,7 @@ impl #[derive(Debug, Serialize)] pub struct BillwerkCustomerObject { - handle: Option, + handle: Option, email: Option, address: Option>, address2: Option>, diff --git a/crates/router/src/connector/cashtocode/transformers.rs b/crates/router/src/connector/cashtocode/transformers.rs index ba55fca9b809..93922449b86e 100644 --- a/crates/router/src/connector/cashtocode/transformers.rs +++ b/crates/router/src/connector/cashtocode/transformers.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; pub use common_utils::request::Method; -use common_utils::{errors::CustomResult, ext_traits::ValueExt, pii::Email}; +use common_utils::{errors::CustomResult, ext_traits::ValueExt, id_type, pii::Email}; use error_stack::ResultExt; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -18,11 +18,11 @@ use crate::{ pub struct CashtocodePaymentsRequest { amount: f64, transaction_id: String, - user_id: Secret, + user_id: Secret, currency: enums::Currency, first_name: Option>, last_name: Option>, - user_alias: Secret, + user_alias: Secret, requested_url: String, cancel_url: String, email: Option, diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index ee1ff91cd478..465086f60eb2 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -2,7 +2,10 @@ use api_models::{ enums::{CountryAlpha2, UsStatesAbbreviation}, payments::AddressDetails, }; -use common_utils::pii::{self, IpAddress}; +use common_utils::{ + id_type, + pii::{self, IpAddress}, +}; use masking::{ExposeInterface, Secret}; use serde::{Deserialize, Serialize}; @@ -59,7 +62,7 @@ pub struct GocardlessCustomer { #[derive(Default, Debug, Serialize)] pub struct CustomerMetaData { - crm_id: Option>, + crm_id: Option>, } impl TryFrom<&types::ConnectorCustomerRouterData> for GocardlessCustomerRequest { diff --git a/crates/router/src/connector/riskified/transformers/api.rs b/crates/router/src/connector/riskified/transformers/api.rs index f0d47274d138..2e0ac3b00473 100644 --- a/crates/router/src/connector/riskified/transformers/api.rs +++ b/crates/router/src/connector/riskified/transformers/api.rs @@ -1,5 +1,5 @@ use api_models::payments::AdditionalPaymentData; -use common_utils::{ext_traits::ValueExt, pii::Email}; +use common_utils::{ext_traits::ValueExt, id_type, pii::Email}; use error_stack::{self, ResultExt}; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -84,7 +84,7 @@ pub struct RiskifiedCustomer { #[serde(with = "common_utils::custom_serde::iso8601")] created_at: PrimitiveDateTime, verified_email: bool, - id: String, + id: id_type::CustomerId, account_type: CustomerAccountType, orders_count: i32, phone: Option>, diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 8ff7d350f24a..a9c78cb4bf18 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -11,6 +11,7 @@ use common_utils::{ date_time, errors::ReportSwitchExt, ext_traits::StringExt, + id_type, pii::{self, Email, IpAddress}, types::MinorUnit, }; @@ -87,7 +88,7 @@ pub trait RouterData { T: serde::de::DeserializeOwned; fn is_three_ds(&self) -> bool; fn get_payment_method_token(&self) -> Result; - fn get_customer_id(&self) -> Result; + fn get_customer_id(&self) -> Result; fn get_connector_customer_id(&self) -> Result; fn get_preprocessing_id(&self) -> Result; fn get_recurring_mandate_payment_data( @@ -402,7 +403,7 @@ impl RouterData for types::RouterData Result { + fn get_customer_id(&self) -> Result { self.customer_id .to_owned() .ok_or_else(missing_field_err("customer_id")) @@ -877,7 +878,7 @@ impl PaymentsSyncRequestData for types::PaymentsSyncData { #[cfg(feature = "payouts")] pub trait CustomerDetails { - fn get_customer_id(&self) -> Result; + fn get_customer_id(&self) -> Result; fn get_customer_name( &self, ) -> Result, errors::ConnectorError>; @@ -890,7 +891,7 @@ pub trait CustomerDetails { #[cfg(feature = "payouts")] impl CustomerDetails for types::CustomerDetails { - fn get_customer_id(&self) -> Result { + fn get_customer_id(&self) -> Result { self.customer_id .clone() .ok_or(errors::ConnectorError::MissingRequiredField { diff --git a/crates/router/src/connector/volt/transformers.rs b/crates/router/src/connector/volt/transformers.rs index a78ecc94443b..19e3b06fae04 100644 --- a/crates/router/src/connector/volt/transformers.rs +++ b/crates/router/src/connector/volt/transformers.rs @@ -1,4 +1,4 @@ -use common_utils::pii::Email; +use common_utils::{id_type, pii::Email}; use diesel_models::enums; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -70,7 +70,7 @@ pub enum TransactionType { #[derive(Debug, Serialize)] pub struct ShopperDetails { - reference: String, + reference: id_type::CustomerId, email: Option, first_name: Secret, last_name: Secret, diff --git a/crates/router/src/connector/zsl/transformers.rs b/crates/router/src/connector/zsl/transformers.rs index a9c80d39b771..e6483c6f7d1a 100644 --- a/crates/router/src/connector/zsl/transformers.rs +++ b/crates/router/src/connector/zsl/transformers.rs @@ -237,7 +237,7 @@ impl TryFrom<&ZslRouterData<&types::PaymentsAuthorizeRouterData>> for ZslPayment .customer_id .clone() .and_then(|customer_id| { - let cust_id = customer_id.replace(['_', '-'], ""); + let cust_id = customer_id.get_string_repr().replace(['_', '-'], ""); let id_len = cust_id.len(); if id_len > 10 { cust_id.get(id_len - 10..id_len).map(|id| id.to_string()) diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 0f3d60cccd79..1c983dcde950 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -1,6 +1,7 @@ use common_utils::{ crypto::{Encryptable, GcmAes256}, errors::ReportSwitchExt, + ext_traits::OptionExt, }; use error_stack::{report, ResultExt}; use masking::ExposeInterface; @@ -35,7 +36,11 @@ pub async fn create_customer( mut customer_data: customers::CustomerRequest, ) -> errors::CustomerResponse { let db = state.store.as_ref(); - let customer_id = &customer_data.customer_id; + let customer_id = customer_data + .customer_id + .clone() + .unwrap_or_else(common_utils::generate_customer_id_of_default_length); + let merchant_id = &merchant_account.merchant_id; merchant_id.clone_into(&mut customer_data.merchant_id); @@ -46,7 +51,7 @@ pub async fn create_customer( // it errors out, now the address that was inserted is not deleted match db .find_customer_by_customer_id_merchant_id( - customer_id, + &customer_id, merchant_id, &key_store, merchant_account.storage_scheme, @@ -73,7 +78,7 @@ pub async fn create_customer( .get_domain_address( customer_address, merchant_id, - customer_id, + &customer_id, key, merchant_account.storage_scheme, ) @@ -93,7 +98,7 @@ pub async fn create_customer( let new_customer = async { Ok(domain::Customer { - customer_id: customer_id.to_string(), + customer_id: customer_id.to_owned(), merchant_id: merchant_id.to_string(), name: customer_data .name @@ -350,9 +355,17 @@ pub async fn update_customer( ) -> errors::CustomerResponse { let db = state.store.as_ref(); //Add this in update call if customer can be updated anywhere else + + let customer_id = update_customer + .customer_id + .as_ref() + .get_required_value("customer_id") + .change_context(errors::CustomersErrorResponse::InternalServerError) + .attach("Missing required field `customer_id`")?; + let customer = db .find_customer_by_customer_id_merchant_id( - &update_customer.customer_id, + customer_id, &merchant_account.merchant_id, &key_store, merchant_account.storage_scheme, @@ -376,8 +389,8 @@ pub async fn update_customer( .await .switch() .attach_printable(format!( - "Failed while updating address: merchant_id: {}, customer_id: {}", - merchant_account.merchant_id, update_customer.customer_id + "Failed while updating address: merchant_id: {}, customer_id: {:?}", + merchant_account.merchant_id, customer_id ))?, ) } @@ -416,7 +429,7 @@ pub async fn update_customer( let response = db .update_customer_by_customer_id_merchant_id( - update_customer.customer_id.to_owned(), + customer_id.to_owned(), merchant_account.merchant_id.to_owned(), customer, async { diff --git a/crates/router/src/core/locker_migration.rs b/crates/router/src/core/locker_migration.rs index bad3b2184b56..9dbc54287484 100644 --- a/crates/router/src/core/locker_migration.rs +++ b/crates/router/src/core/locker_migration.rs @@ -1,5 +1,5 @@ use api_models::{enums as api_enums, locker_migration::MigrateCardResponse}; -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, id_type}; use diesel_models::{enums as storage_enums, PaymentMethod}; use error_stack::{FutureExt, ResultExt}; use futures::TryFutureExt; @@ -77,7 +77,7 @@ pub async fn rust_locker_migration( pub async fn call_to_locker( state: &AppState, payment_methods: Vec, - customer_id: &String, + customer_id: &id_type::CustomerId, merchant_id: &str, merchant_account: &domain::MerchantAccount, ) -> CustomResult { @@ -136,7 +136,7 @@ pub async fn call_to_locker( state, pm_create, &card_details, - customer_id.to_string(), + customer_id, merchant_account, api_enums::LockerChoice::HyperswitchCardVault, Some(pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id)), @@ -144,7 +144,7 @@ pub async fn call_to_locker( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable(format!( - "Card migration failed for merchant_id: {merchant_id}, customer_id: {customer_id}, payment_method_id: {} ", + "Card migration failed for merchant_id: {merchant_id}, customer_id: {customer_id:?}, payment_method_id: {} ", pm.payment_method_id )); @@ -159,7 +159,7 @@ pub async fn call_to_locker( cards_moved += 1; logger::info!( - "Card migrated for merchant_id: {merchant_id}, customer_id: {customer_id}, payment_method_id: {} ", + "Card migrated for merchant_id: {merchant_id}, customer_id: {customer_id:?}, payment_method_id: {} ", pm.payment_method_id ); } diff --git a/crates/router/src/core/mandate.rs b/crates/router/src/core/mandate.rs index 7a1e294f9dc6..839f55dea2db 100644 --- a/crates/router/src/core/mandate.rs +++ b/crates/router/src/core/mandate.rs @@ -1,7 +1,7 @@ pub mod helpers; pub mod utils; use api_models::payments; -use common_utils::ext_traits::Encode; +use common_utils::{ext_traits::Encode, id_type}; use diesel_models::{enums as storage_enums, Mandate}; use error_stack::{report, ResultExt}; use futures::future; @@ -235,7 +235,7 @@ pub async fn get_customer_mandates( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { format!( - "Failed while finding mandate: merchant_id: {}, customer_id: {}", + "Failed while finding mandate: merchant_id: {}, customer_id: {:?}", merchant_account.merchant_id, req.customer_id ) })?; @@ -344,7 +344,7 @@ where pub async fn mandate_procedure( state: &AppState, resp: &types::RouterData, - customer_id: &Option, + customer_id: &Option, pm_id: Option, merchant_connector_id: Option, storage_scheme: MerchantStorageScheme, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index cd51face62db..33103b7a901c 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -24,7 +24,7 @@ use common_enums::enums::MerchantStorageScheme; use common_utils::{ consts, ext_traits::{AsyncExt, Encode, StringExt, ValueExt}, - generate_id, + generate_id, id_type, }; use diesel_models::{business_profile::BusinessProfile, encryption::Encryption, payment_method}; use domain::CustomerUpdate; @@ -85,7 +85,7 @@ use crate::{ pub async fn create_payment_method( db: &dyn db::StorageInterface, req: &api::PaymentMethodCreate, - customer_id: &str, + customer_id: &id_type::CustomerId, payment_method_id: &str, locker_id: Option, merchant_id: &str, @@ -119,7 +119,7 @@ pub async fn create_payment_method( let response = db .insert_payment_method( storage::PaymentMethodNew { - customer_id: customer_id.to_string(), + customer_id: customer_id.to_owned(), merchant_id: merchant_id.to_string(), payment_method_id: payment_method_id.to_string(), locker_id, @@ -173,7 +173,7 @@ pub async fn create_payment_method( pub fn store_default_payment_method( req: &api::PaymentMethodCreate, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &String, ) -> ( api::PaymentMethodResponse, @@ -206,7 +206,7 @@ pub async fn get_or_insert_payment_method( req: api::PaymentMethodCreate, resp: &mut api::PaymentMethodResponse, merchant_account: &domain::MerchantAccount, - customer_id: &str, + customer_id: &id_type::CustomerId, key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult { let mut payment_method_id = resp.payment_method_id.clone(); @@ -293,7 +293,7 @@ pub async fn get_client_secret_or_add_payment_method( let res = create_payment_method( db, &req, - customer_id.as_str(), + &customer_id, payment_method_id.as_str(), None, merchant_id.as_str(), @@ -375,7 +375,7 @@ pub async fn add_payment_method_data( let customer_id = payment_method.customer_id.clone(); let customer = db .find_customer_by_customer_id_merchant_id( - customer_id.as_str(), + &customer_id, &merchant_account.merchant_id, &key_store, merchant_account.storage_scheme, @@ -487,7 +487,7 @@ pub async fn add_payment_method_data( db, merchant_account.merchant_id.clone(), key_store.clone(), - customer_id.as_str(), + &customer_id, pm_id, merchant_account.storage_scheme, ) @@ -626,7 +626,7 @@ pub async fn add_payment_method( &state, req.clone(), &card, - customer_id.clone(), + &customer_id, merchant_account, api::enums::LockerChoice::HyperswitchCardVault, Some( @@ -733,7 +733,7 @@ pub async fn insert_payment_method( req: api::PaymentMethodCreate, key_store: &domain::MerchantKeyStore, merchant_id: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, pm_metadata: Option, customer_acceptance: Option, locker_id: Option, @@ -1030,7 +1030,7 @@ pub async fn add_bank_to_locker( merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, bank: &api::BankPayout, - customer_id: &String, + customer_id: &id_type::CustomerId, ) -> errors::CustomResult< ( api::PaymentMethodResponse, @@ -1093,7 +1093,7 @@ pub async fn add_card_to_locker( state: &routes::AppState, req: api::PaymentMethodCreate, card: &api::CardDetail, - customer_id: &String, + customer_id: &id_type::CustomerId, merchant_account: &domain::MerchantAccount, card_reference: Option<&str>, ) -> errors::CustomResult< @@ -1110,7 +1110,7 @@ pub async fn add_card_to_locker( state, req.clone(), card, - customer_id.to_string(), + customer_id, merchant_account, api_enums::LockerChoice::HyperswitchCardVault, card_reference, @@ -1139,7 +1139,7 @@ pub async fn add_card_to_locker( pub async fn get_card_from_locker( state: &routes::AppState, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &str, ) -> errors::RouterResult { @@ -1180,7 +1180,7 @@ pub async fn get_card_from_locker( pub async fn delete_card_from_locker( state: &routes::AppState, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &str, ) -> errors::RouterResult { @@ -1206,7 +1206,7 @@ pub async fn add_card_hs( state: &routes::AppState, req: api::PaymentMethodCreate, card: &api::CardDetail, - customer_id: String, + customer_id: &id_type::CustomerId, merchant_account: &domain::MerchantAccount, locker_choice: api_enums::LockerChoice, card_reference: Option<&str>, @@ -1233,8 +1233,7 @@ pub async fn add_card_hs( ttl: state.conf.locker.ttl_for_storage_in_secs, }); - let store_card_payload = - call_to_locker_hs(state, &payload, &customer_id, locker_choice).await?; + let store_card_payload = call_to_locker_hs(state, &payload, customer_id, locker_choice).await?; let payment_method_resp = payment_methods::mk_add_card_response_hs( card.clone(), @@ -1270,7 +1269,7 @@ pub async fn decode_and_decrypt_locker_data( pub async fn get_payment_method_from_hs_locker<'a>( state: &'a routes::AppState, key_store: &domain::MerchantKeyStore, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, payment_method_reference: &'a str, locker_choice: Option, @@ -1332,7 +1331,7 @@ pub async fn get_payment_method_from_hs_locker<'a>( pub async fn call_to_locker_hs<'a>( state: &routes::AppState, payload: &payment_methods::StoreLockerReq<'a>, - customer_id: &str, + customer_id: &id_type::CustomerId, locker_choice: api_enums::LockerChoice, ) -> errors::CustomResult { let locker = &state.conf.locker; @@ -1404,7 +1403,7 @@ pub async fn update_payment_method_connector_mandate_details( #[instrument(skip_all)] pub async fn get_card_from_hs_locker<'a>( state: &'a routes::AppState, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &'a str, locker_choice: api_enums::LockerChoice, @@ -1457,7 +1456,7 @@ pub async fn get_card_from_hs_locker<'a>( #[instrument(skip_all)] pub async fn delete_card_from_hs_locker<'a>( state: &routes::AppState, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &'a str, ) -> errors::RouterResult { @@ -1508,7 +1507,7 @@ pub async fn mock_call_to_locker_hs<'a>( payload: &payment_methods::StoreLockerReq<'a>, card_cvc: Option, payment_method_id: Option, - customer_id: Option<&str>, + customer_id: Option<&id_type::CustomerId>, ) -> errors::CustomResult { let mut locker_mock_up = storage::LockerMockUpNew { card_id: card_id.to_string(), @@ -1521,7 +1520,7 @@ pub async fn mock_call_to_locker_hs<'a>( card_exp_month: "12".to_string(), card_cvc, payment_method_id, - customer_id: customer_id.map(str::to_string), + customer_id: customer_id.map(ToOwned::to_owned), name_on_card: None, nickname: None, enc_card_data: None, @@ -1808,7 +1807,7 @@ pub async fn list_payment_methods( .as_ref() .async_and_then(|cust| async { db.find_customer_by_customer_id_merchant_id( - cust.as_str(), + cust, &pi.merchant_id, &key_store, merchant_account.storage_scheme, @@ -3088,7 +3087,7 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: Option, - customer_id: Option<&str>, + customer_id: Option<&id_type::CustomerId>, ) -> errors::RouterResponse { let db = state.store.as_ref(); let limit = req.clone().and_then(|pml_req| pml_req.limit); @@ -3144,7 +3143,7 @@ pub async fn list_customer_payment_method( merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, payment_intent: Option, - customer_id: &str, + customer_id: &id_type::CustomerId, limit: Option, ) -> errors::RouterResponse { let db = &*state.store; @@ -3701,11 +3700,12 @@ pub async fn set_default_payment_method( db: &dyn db::StorageInterface, merchant_id: String, key_store: domain::MerchantKeyStore, - customer_id: &str, + customer_id: &id_type::CustomerId, payment_method_id: String, storage_scheme: MerchantStorageScheme, ) -> errors::RouterResponse { - //check for the customer + // check for the customer + // TODO: customer need not be checked again here, this function can take an optional customer and check for existence of customer based on the optional value let customer = db .find_customer_by_customer_id_merchant_id( customer_id, @@ -3725,7 +3725,7 @@ pub async fn set_default_payment_method( .get_required_value("payment_method")?; utils::when( - payment_method.customer_id != customer_id || payment_method.merchant_id != merchant_id, + &payment_method.customer_id != customer_id || payment_method.merchant_id != merchant_id, || { Err(errors::ApiErrorResponse::PreconditionFailed { message: "The payment_method_id is not valid".to_string(), @@ -3799,7 +3799,7 @@ pub async fn get_bank_from_hs_locker( state: &routes::AppState, key_store: &domain::MerchantKeyStore, temp_token: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, token_ref: &str, ) -> errors::RouterResult { @@ -3886,7 +3886,7 @@ impl TempLockerCardSupport { None, None, None, - Some(pm.customer_id.to_string()), + Some(pm.customer_id.clone()), Some(pm.payment_method_id.to_string()), ) .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 86b41d1d0fe8..21aa51b46a33 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -1,8 +1,7 @@ -use std::str::FromStr; - use api_models::{enums as api_enums, payment_methods::Card}; use common_utils::{ ext_traits::{Encode, StringExt}, + id_type, pii::Email, request::RequestContent, }; @@ -39,7 +38,7 @@ impl StoreLockerReq<'_> { #[derive(Debug, Deserialize, Serialize)] pub struct StoreCardReq<'a> { pub merchant_id: &'a str, - pub merchant_customer_id: String, + pub merchant_customer_id: id_type::CustomerId, #[serde(skip_serializing_if = "Option::is_none")] pub requestor_card_reference: Option, pub card: Card, @@ -49,7 +48,7 @@ pub struct StoreCardReq<'a> { #[derive(Debug, Deserialize, Serialize)] pub struct StoreGenericReq<'a> { pub merchant_id: &'a str, - pub merchant_customer_id: String, + pub merchant_customer_id: id_type::CustomerId, #[serde(rename = "enc_card_data")] pub enc_data: String, pub ttl: i64, @@ -79,7 +78,7 @@ pub enum DataDuplicationCheck { #[derive(Debug, Deserialize, Serialize)] pub struct CardReqBody<'a> { pub merchant_id: &'a str, - pub merchant_customer_id: String, + pub merchant_customer_id: id_type::CustomerId, pub card_reference: String, } @@ -108,7 +107,7 @@ pub struct DeleteCardResp { #[serde(rename_all = "camelCase")] pub struct AddCardRequest<'a> { pub card_number: cards::CardNumber, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub card_exp_month: Secret, pub card_exp_year: Secret, pub merchant_id: &'a str, @@ -131,7 +130,7 @@ pub struct AddCardResponse { pub card_exp_month: Option>, pub name_on_card: Option>, pub nickname: Option, - pub customer_id: Option, + pub customer_id: Option, pub duplicate: Option, } @@ -143,7 +142,7 @@ pub struct AddPaymentMethodResponse { #[serde(rename = "merchant_id")] pub merchant_id: Option, pub nickname: Option, - pub customer_id: Option, + pub customer_id: Option, pub duplicate: Option, pub payment_method_data: Secret, } @@ -380,43 +379,10 @@ pub fn mk_add_card_response_hs( } } -pub fn mk_add_card_request( - locker: &settings::Locker, - card: &api::CardDetail, - customer_id: &str, - _req: &api::PaymentMethodCreate, - locker_id: &'static str, - merchant_id: &str, -) -> CustomResult { - let customer_id = if cfg!(feature = "release") { - format!("{customer_id}::{merchant_id}") - } else { - customer_id.to_owned() - }; - let add_card_req = AddCardRequest { - card_number: card.card_number.clone(), - customer_id, - card_exp_month: card.card_exp_month.clone(), - card_exp_year: card.card_exp_year.clone(), - merchant_id: locker_id, - email_address: match Email::from_str("dummy@gmail.com") { - Ok(email) => Some(email), - Err(_) => None, - }, // - name_on_card: Some("John Doe".to_string().into()), // [#256] - nickname: Some("router".to_string()), // - }; - let mut url = locker.host.to_owned(); - url.push_str("/card/addCard"); - let mut request = services::Request::new(services::Method::Post, &url); - request.set_body(RequestContent::FormUrlEncoded(Box::new(add_card_req))); - Ok(request) -} - pub async fn mk_get_card_request_hs( jwekey: &settings::Jwekey, locker: &settings::Locker, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &str, locker_choice: Option, @@ -488,7 +454,7 @@ pub fn mk_get_card_response(card: GetCardResponse) -> errors::RouterResult pub async fn mk_delete_card_request_hs( jwekey: &settings::Jwekey, locker: &settings::Locker, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &str, ) -> CustomResult { @@ -618,7 +584,7 @@ pub fn mk_card_value2( card_security_code: Option, card_fingerprint: Option, external_id: Option, - customer_id: Option, + customer_id: Option, payment_method_id: Option, ) -> CustomResult { let value2 = api::TokenizedCardValue2 { diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs index 6d4f2322a8d8..e0d8bada65ef 100644 --- a/crates/router/src/core/payment_methods/vault.rs +++ b/crates/router/src/core/payment_methods/vault.rs @@ -2,7 +2,7 @@ use common_enums::PaymentMethodType; use common_utils::{ crypto::{DecodeMessage, EncodeMessage, GcmAes256}, ext_traits::{BytesExt, Encode}, - generate_id_with_default_len, + generate_id_with_default_len, id_type, pii::Email, }; use error_stack::{report, ResultExt}; @@ -26,13 +26,19 @@ use crate::{ const VAULT_SERVICE_NAME: &str = "CARD"; pub struct SupplementaryVaultData { - pub customer_id: Option, + pub customer_id: Option, pub payment_method_id: Option, } pub trait Vaultable: Sized { - fn get_value1(&self, customer_id: Option) -> CustomResult; - fn get_value2(&self, _customer_id: Option) -> CustomResult { + fn get_value1( + &self, + customer_id: Option, + ) -> CustomResult; + fn get_value2( + &self, + _customer_id: Option, + ) -> CustomResult { Ok(String::new()) } fn from_values( @@ -42,7 +48,10 @@ pub trait Vaultable: Sized { } impl Vaultable for api::Card { - fn get_value1(&self, _customer_id: Option) -> CustomResult { + fn get_value1( + &self, + _customer_id: Option, + ) -> CustomResult { let value1 = api::TokenizedCardValue1 { card_number: self.card_number.peek().clone(), exp_year: self.card_exp_year.peek().clone(), @@ -62,7 +71,10 @@ impl Vaultable for api::Card { .attach_printable("Failed to encode card value1") } - fn get_value2(&self, customer_id: Option) -> CustomResult { + fn get_value2( + &self, + customer_id: Option, + ) -> CustomResult { let value2 = api::TokenizedCardValue2 { card_security_code: Some(self.card_cvc.peek().clone()), card_fingerprint: None, @@ -117,7 +129,10 @@ impl Vaultable for api::Card { } impl Vaultable for api_models::payments::BankTransferData { - fn get_value1(&self, _customer_id: Option) -> CustomResult { + fn get_value1( + &self, + _customer_id: Option, + ) -> CustomResult { let value1 = api_models::payment_methods::TokenizedBankTransferValue1 { data: self.to_owned(), }; @@ -128,7 +143,10 @@ impl Vaultable for api_models::payments::BankTransferData { .attach_printable("Failed to encode bank transfer data") } - fn get_value2(&self, customer_id: Option) -> CustomResult { + fn get_value2( + &self, + customer_id: Option, + ) -> CustomResult { let value2 = api_models::payment_methods::TokenizedBankTransferValue2 { customer_id }; value2 @@ -163,7 +181,10 @@ impl Vaultable for api_models::payments::BankTransferData { } impl Vaultable for api::WalletData { - fn get_value1(&self, _customer_id: Option) -> CustomResult { + fn get_value1( + &self, + _customer_id: Option, + ) -> CustomResult { let value1 = api::TokenizedWalletValue1 { data: self.to_owned(), }; @@ -174,7 +195,10 @@ impl Vaultable for api::WalletData { .attach_printable("Failed to encode wallet data value1") } - fn get_value2(&self, customer_id: Option) -> CustomResult { + fn get_value2( + &self, + customer_id: Option, + ) -> CustomResult { let value2 = api::TokenizedWalletValue2 { customer_id }; value2 @@ -209,7 +233,10 @@ impl Vaultable for api::WalletData { } impl Vaultable for api_models::payments::BankRedirectData { - fn get_value1(&self, _customer_id: Option) -> CustomResult { + fn get_value1( + &self, + _customer_id: Option, + ) -> CustomResult { let value1 = api_models::payment_methods::TokenizedBankRedirectValue1 { data: self.to_owned(), }; @@ -220,7 +247,10 @@ impl Vaultable for api_models::payments::BankRedirectData { .attach_printable("Failed to encode bank redirect data") } - fn get_value2(&self, customer_id: Option) -> CustomResult { + fn get_value2( + &self, + customer_id: Option, + ) -> CustomResult { let value2 = api_models::payment_methods::TokenizedBankRedirectValue2 { customer_id }; value2 @@ -264,7 +294,10 @@ pub enum VaultPaymentMethod { } impl Vaultable for api::PaymentMethodData { - fn get_value1(&self, customer_id: Option) -> CustomResult { + fn get_value1( + &self, + customer_id: Option, + ) -> CustomResult { let value1 = match self { Self::Card(card) => VaultPaymentMethod::Card(card.get_value1(customer_id)?), Self::Wallet(wallet) => VaultPaymentMethod::Wallet(wallet.get_value1(customer_id)?), @@ -284,7 +317,10 @@ impl Vaultable for api::PaymentMethodData { .attach_printable("Failed to encode payment method value1") } - fn get_value2(&self, customer_id: Option) -> CustomResult { + fn get_value2( + &self, + customer_id: Option, + ) -> CustomResult { let value2 = match self { Self::Card(card) => VaultPaymentMethod::Card(card.get_value2(customer_id)?), Self::Wallet(wallet) => VaultPaymentMethod::Wallet(wallet.get_value2(customer_id)?), @@ -352,7 +388,10 @@ impl Vaultable for api::PaymentMethodData { #[cfg(feature = "payouts")] impl Vaultable for api::CardPayout { - fn get_value1(&self, _customer_id: Option) -> CustomResult { + fn get_value1( + &self, + _customer_id: Option, + ) -> CustomResult { let value1 = api::TokenizedCardValue1 { card_number: self.card_number.peek().clone(), exp_year: self.expiry_year.peek().clone(), @@ -369,7 +408,10 @@ impl Vaultable for api::CardPayout { .attach_printable("Failed to encode card value1") } - fn get_value2(&self, customer_id: Option) -> CustomResult { + fn get_value2( + &self, + customer_id: Option, + ) -> CustomResult { let value2 = api::TokenizedCardValue2 { card_security_code: None, card_fingerprint: None, @@ -427,12 +469,15 @@ pub struct TokenizedWalletSensitiveValues { #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TokenizedWalletInsensitiveValues { - pub customer_id: Option, + pub customer_id: Option, } #[cfg(feature = "payouts")] impl Vaultable for api::WalletPayout { - fn get_value1(&self, _customer_id: Option) -> CustomResult { + fn get_value1( + &self, + _customer_id: Option, + ) -> CustomResult { let value1 = match self { Self::Paypal(paypal_data) => TokenizedWalletSensitiveValues { email: paypal_data.email.clone(), @@ -454,7 +499,10 @@ impl Vaultable for api::WalletPayout { .attach_printable("Failed to encode wallet data - TokenizedWalletSensitiveValues") } - fn get_value2(&self, customer_id: Option) -> CustomResult { + fn get_value2( + &self, + customer_id: Option, + ) -> CustomResult { let value2 = TokenizedWalletInsensitiveValues { customer_id }; value2 @@ -510,7 +558,7 @@ pub struct TokenizedBankSensitiveValues { #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TokenizedBankInsensitiveValues { - pub customer_id: Option, + pub customer_id: Option, pub bank_name: Option, pub bank_country_code: Option, pub bank_city: Option, @@ -519,7 +567,10 @@ pub struct TokenizedBankInsensitiveValues { #[cfg(feature = "payouts")] impl Vaultable for api::BankPayout { - fn get_value1(&self, _customer_id: Option) -> CustomResult { + fn get_value1( + &self, + _customer_id: Option, + ) -> CustomResult { let bank_sensitive_data = match self { Self::Ach(b) => TokenizedBankSensitiveValues { bank_account_number: Some(b.bank_account_number.clone()), @@ -565,7 +616,10 @@ impl Vaultable for api::BankPayout { .attach_printable("Failed to encode data - bank_sensitive_data") } - fn get_value2(&self, customer_id: Option) -> CustomResult { + fn get_value2( + &self, + customer_id: Option, + ) -> CustomResult { let bank_insensitive_data = match self { Self::Ach(b) => TokenizedBankInsensitiveValues { customer_id, @@ -688,7 +742,10 @@ pub enum VaultPayoutMethod { #[cfg(feature = "payouts")] impl Vaultable for api::PayoutMethodData { - fn get_value1(&self, customer_id: Option) -> CustomResult { + fn get_value1( + &self, + customer_id: Option, + ) -> CustomResult { let value1 = match self { Self::Card(card) => VaultPayoutMethod::Card(card.get_value1(customer_id)?), Self::Bank(bank) => VaultPayoutMethod::Bank(bank.get_value1(customer_id)?), @@ -701,7 +758,10 @@ impl Vaultable for api::PayoutMethodData { .attach_printable("Failed to encode payout method value1") } - fn get_value2(&self, customer_id: Option) -> CustomResult { + fn get_value2( + &self, + customer_id: Option, + ) -> CustomResult { let value2 = match self { Self::Card(card) => VaultPayoutMethod::Card(card.get_value2(customer_id)?), Self::Bank(bank) => VaultPayoutMethod::Bank(bank.get_value2(customer_id)?), @@ -777,7 +837,7 @@ impl Vault { state: &routes::AppState, token_id: Option, payment_method: &api::PaymentMethodData, - customer_id: Option, + customer_id: Option, pm: enums::PaymentMethod, merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult { @@ -829,7 +889,7 @@ impl Vault { state: &routes::AppState, token_id: Option, payout_method: &api::PayoutMethodData, - customer_id: Option, + customer_id: Option, merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult { let value1 = payout_method diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index db99ff590cd9..7bc0b532239e 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3814,7 +3814,7 @@ pub async fn payment_external_authentication( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { - format!("error while finding customer with customer_id {customer_id}") + format!("error while finding customer with customer_id {customer_id:?}") })?, ), None => None, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index e55f5a277879..8f7ed4da2565 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -7,10 +7,10 @@ use api_models::{ use base64::Engine; use common_utils::{ ext_traits::{AsyncExt, ByteSliceExt, Encode, ValueExt}, - fp_utils, generate_id, pii, + fp_utils, generate_id, id_type, pii, types::MinorUnit, }; -use diesel_models::enums; +use diesel_models::enums::{self}; // TODO : Evaluate all the helper functions () use error_stack::{report, ResultExt}; use futures::future::Either; @@ -124,7 +124,7 @@ pub async fn create_or_update_address_for_payment_by_request( req_address: Option<&api::Address>, address_id: Option<&str>, merchant_id: &str, - customer_id: Option<&String>, + customer_id: Option<&id_type::CustomerId>, merchant_key_store: &domain::MerchantKeyStore, payment_id: &str, storage_scheme: storage_enums::MerchantStorageScheme, @@ -286,7 +286,7 @@ pub async fn create_or_find_address_for_payment_by_request( req_address: Option<&api::Address>, address_id: Option<&str>, merchant_id: &str, - customer_id: Option<&String>, + customer_id: Option<&id_type::CustomerId>, merchant_key_store: &domain::MerchantKeyStore, payment_id: &str, storage_scheme: storage_enums::MerchantStorageScheme, @@ -1011,7 +1011,7 @@ fn validate_new_mandate_request( pub fn validate_customer_id_mandatory_cases( has_setup_future_usage: bool, - customer_id: &Option, + customer_id: Option<&id_type::CustomerId>, ) -> RouterResult<()> { match (has_setup_future_usage, customer_id) { (true, None) => Err(errors::ApiErrorResponse::PreconditionFailed { @@ -1162,8 +1162,8 @@ pub fn verify_mandate_details( pub fn verify_mandate_details_for_recurring_payments( mandate_merchant_id: &str, merchant_id: &str, - mandate_customer_id: &str, - customer_id: &str, + mandate_customer_id: &id_type::CustomerId, + customer_id: &id_type::CustomerId, ) -> RouterResult<()> { if mandate_merchant_id != merchant_id { Err(report!(errors::ApiErrorResponse::MandateNotFound))? @@ -1266,7 +1266,7 @@ pub(crate) async fn get_payment_method_create_request( payment_method_data: Option<&domain::PaymentMethodData>, payment_method: Option, payment_method_type: Option, - customer_id: &Option, + customer_id: &Option, billing_name: Option>, ) -> RouterResult { match payment_method_data { @@ -1340,7 +1340,7 @@ pub(crate) async fn get_payment_method_create_request( pub async fn get_customer_from_details( db: &dyn StorageInterface, - customer_id: Option, + customer_id: Option, merchant_id: &str, payment_data: &mut PaymentData, merchant_key_store: &domain::MerchantKeyStore, @@ -1348,10 +1348,10 @@ pub async fn get_customer_from_details( ) -> CustomResult, errors::StorageError> { match customer_id { None => Ok(None), - Some(c_id) => { + Some(customer_id) => { let customer = db .find_customer_optional_by_customer_id_merchant_id( - &c_id, + &customer_id, merchant_id, merchant_key_store, storage_scheme, @@ -1456,8 +1456,9 @@ pub fn get_customer_details_from_request( let customer_id = request .customer .as_ref() - .map(|customer_details| customer_details.id.clone()) - .or(request.customer_id.clone()); + .map(|customer_details| &customer_details.id) + .or(request.customer_id.as_ref()) + .map(ToOwned::to_owned); let customer_name = request .customer @@ -1595,7 +1596,7 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( let key = key_store.key.get_inner().peek(); Ok::<_, error_stack::Report>( domain::Customer { - customer_id: customer_id.to_string(), + customer_id, merchant_id: merchant_id.to_string(), name: request_customer_details .name @@ -2541,7 +2542,7 @@ pub fn make_merchant_url_with_response( pub async fn make_ephemeral_key( state: AppState, - customer_id: String, + customer_id: id_type::CustomerId, merchant_id: String, ) -> errors::RouterResponse { let store = &state.store; @@ -2669,7 +2670,7 @@ pub fn generate_mandate( payment_id: String, connector: String, setup_mandate_details: Option, - customer_id: &Option, + customer_id: &Option, payment_method_id: String, connector_mandate_id: Option, network_txn_id: Option, diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 3f57ba2830fb..49fe8ce20114 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -169,10 +169,10 @@ impl GetTracker, api::PaymentsRequest> for Co helpers::validate_customer_id_mandatory_cases( request.setup_future_usage.is_some(), - &payment_intent + payment_intent .customer_id - .clone() - .or_else(|| request.customer_id.clone()), + .as_ref() + .or(request.customer_id.as_ref()), )?; let shipping_address = helpers::create_or_update_address_for_payment_by_request( diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index a3ed16def0c7..aa2cf09cb6af 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -360,10 +360,10 @@ impl GetTracker, api::PaymentsRequest> for Pa helpers::validate_customer_id_mandatory_cases( request.setup_future_usage.is_some(), - &payment_intent + payment_intent .customer_id - .clone() - .or_else(|| customer_details.customer_id.clone()), + .as_ref() + .or(customer_details.customer_id.as_ref()), )?; let creds_identifier = request diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 68f8b7056c0f..4d4444a28024 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -727,11 +727,11 @@ impl ValidateRequest for PaymentCreate helpers::validate_customer_id_mandatory_cases( request.setup_future_usage.is_some(), - &request + request .customer - .clone() - .map(|customer| customer.id) - .or(request.customer_id.clone()), + .as_ref() + .map(|customer| &customer.id) + .or(request.customer_id.as_ref()), )?; } diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index d7351b3b5ba0..494ab0f2eec8 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -172,10 +172,10 @@ impl GetTracker, api::PaymentsRequest> for Pa if request.confirm.unwrap_or(false) { helpers::validate_customer_id_mandatory_cases( request.setup_future_usage.is_some(), - &payment_intent + payment_intent .customer_id - .clone() - .or_else(|| customer_details.customer_id.clone()), + .as_ref() + .or(customer_details.customer_id.as_ref()), )?; } diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index f5a4265a9fb3..3341011bb5d6 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -4,7 +4,7 @@ use api_models::payment_methods::PaymentMethodsData; use common_enums::PaymentMethod; use common_utils::{ ext_traits::{Encode, ValueExt}, - pii, + id_type, pii, }; use error_stack::{report, ResultExt}; use masking::ExposeInterface; @@ -58,7 +58,7 @@ pub async fn save_payment_method( connector_name: String, merchant_connector_id: Option, save_payment_method_data: SavePaymentMethodData, - customer_id: Option, + customer_id: Option, merchant_account: &domain::MerchantAccount, payment_method_type: Option, key_store: &domain::MerchantKeyStore, @@ -307,7 +307,7 @@ where payment_methods::cards::create_payment_method( db, &payment_method_create_request, - customer_id.as_str(), + &customer_id, &resp.payment_method_id, locker_id, merchant_id, @@ -402,7 +402,7 @@ where payment_method_create_request.clone(), key_store, &merchant_account.merchant_id, - customer_id.as_str(), + &customer_id, resp.metadata.clone().map(|val| val.expose()), customer_acceptance, locker_id, @@ -426,7 +426,7 @@ where payment_methods::cards::delete_card_from_locker( state, - customer_id.as_str(), + &customer_id, merchant_id, existing_pm .locker_id @@ -439,7 +439,7 @@ where state, payment_method_create_request, &card, - customer_id.clone(), + &customer_id, merchant_account, api::enums::LockerChoice::HyperswitchCardVault, Some( @@ -529,7 +529,7 @@ where payment_methods::cards::create_payment_method( db, &payment_method_create_request, - customer_id.as_str(), + &customer_id, &resp.payment_method_id, locker_id, merchant_id, diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 1170d2a25cba..7aaf215ab51c 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -714,13 +714,16 @@ pub async fn payouts_list_core( ) { logger::warn!( ?error, - "customer missing for customer_id : {}", + "customer missing for customer_id : {:?}", payouts.customer_id, ); return None; } Some(Err(error.change_context(StorageError::ValueNotFound( - format!("customer missing for customer_id : {}", payouts.customer_id), + format!( + "customer missing for customer_id : {:?}", + payouts.customer_id + ), )))) } } @@ -789,7 +792,11 @@ pub async fn payouts_filtered_list_core( match domain::Customer::convert_back(c, &key_store.key).await { Ok(domain_cust) => Some((p, pa, domain_cust)), Err(err) => { - logger::warn!(?err, "failed to convert customer for id: {}", p.customer_id); + logger::warn!( + ?err, + "failed to convert customer for id: {:?}", + p.customer_id + ); None } } diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 9c6cdbd88818..3059d3a4164c 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -2,6 +2,7 @@ use api_models::{enums, payment_methods::Card, payouts}; use common_utils::{ errors::CustomResult, ext_traits::{AsyncExt, StringExt}, + generate_customer_id_of_default_length, id_type, }; use diesel_models::encryption::Encryption; use error_stack::ResultExt; @@ -22,7 +23,6 @@ use crate::{ CustomerDetails, }, routing::TransactionData, - utils as core_utils, }, db::StorageInterface, routes::{metrics, AppState}, @@ -44,7 +44,7 @@ pub async fn make_payout_method_data<'a>( state: &'a AppState, payout_method_data: Option<&api::PayoutMethodData>, payout_token: Option<&str>, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, payout_type: Option<&api_enums::PayoutType>, merchant_key_store: &domain::MerchantKeyStore, @@ -581,8 +581,11 @@ pub async fn get_or_create_customer_details( ) -> RouterResult> { let db: &dyn StorageInterface = &*state.store; // Create customer_id if not passed in request - let customer_id = - core_utils::get_or_generate_id("customer_id", &customer_details.customer_id, "cust")?; + let customer_id = customer_details + .customer_id + .clone() + .unwrap_or_else(generate_customer_id_of_default_length); + let merchant_id = &merchant_account.merchant_id; let key = key_store.key.get_inner().peek(); diff --git a/crates/router/src/core/payouts/validator.rs b/crates/router/src/core/payouts/validator.rs index 496aab13b13d..1819c5152e0b 100644 --- a/crates/router/src/core/payouts/validator.rs +++ b/crates/router/src/core/payouts/validator.rs @@ -88,7 +88,10 @@ pub async fn validate_create_request( // Payout token let payout_method_data = match req.payout_token.to_owned() { Some(payout_token) => { - let customer_id = req.customer_id.to_owned().map_or("".to_string(), |c| c); + let customer_id = req + .customer_id + .to_owned() + .unwrap_or_else(common_utils::generate_customer_id_of_default_length); helpers::make_payout_method_data( state, req.payout_method_data.as_ref(), diff --git a/crates/router/src/db/address.rs b/crates/router/src/db/address.rs index 5a6b2606d0c1..f210019b4dac 100644 --- a/crates/router/src/db/address.rs +++ b/crates/router/src/db/address.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel_models::{address::AddressUpdateInternal, enums::MerchantStorageScheme}; use error_stack::ResultExt; @@ -66,7 +67,7 @@ where async fn update_address_by_merchant_id_customer_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, address: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -75,7 +76,7 @@ where #[cfg(not(feature = "kv_store"))] mod storage { - use common_utils::ext_traits::AsyncExt; + use common_utils::{ext_traits::AsyncExt, id_type}; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -237,7 +238,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_by_merchant_id_customer_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, address: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -270,7 +271,7 @@ mod storage { #[cfg(feature = "kv_store")] mod storage { - use common_utils::ext_traits::AsyncExt; + use common_utils::{ext_traits::AsyncExt, id_type}; use diesel_models::{enums::MerchantStorageScheme, AddressUpdateInternal}; use error_stack::{report, ResultExt}; use redis_interface::HsetnxReply; @@ -587,7 +588,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_by_merchant_id_customer_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, address: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -777,7 +778,7 @@ impl AddressInterface for MockDb { async fn update_address_by_merchant_id_customer_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, address_update: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -788,7 +789,7 @@ impl AddressInterface for MockDb { .await .iter_mut() .find(|address| { - address.customer_id == Some(customer_id.to_string()) + address.customer_id.as_ref() == Some(customer_id) && address.merchant_id == merchant_id }) .map(|a| { diff --git a/crates/router/src/db/customers.rs b/crates/router/src/db/customers.rs index 5f8dbe69f803..cad3d2969fe7 100644 --- a/crates/router/src/db/customers.rs +++ b/crates/router/src/db/customers.rs @@ -1,4 +1,4 @@ -use common_utils::ext_traits::AsyncExt; +use common_utils::{ext_traits::AsyncExt, id_type}; use error_stack::ResultExt; use futures::future::try_join_all; use router_env::{instrument, tracing}; @@ -23,13 +23,13 @@ where { async fn delete_customer_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, ) -> CustomResult; async fn find_customer_optional_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -37,7 +37,7 @@ where async fn update_customer_by_customer_id_merchant_id( &self, - customer_id: String, + customer_id: id_type::CustomerId, merchant_id: String, customer: domain::Customer, customer_update: storage_types::CustomerUpdate, @@ -47,7 +47,7 @@ where async fn find_customer_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -69,7 +69,7 @@ where #[cfg(feature = "kv_store")] mod storage { - use common_utils::ext_traits::AsyncExt; + use common_utils::{ext_traits::AsyncExt, id_type}; use diesel_models::kv; use error_stack::{report, ResultExt}; use futures::future::try_join_all; @@ -103,7 +103,7 @@ mod storage { // check customer not found in kv and fallback to db async fn find_customer_optional_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -126,9 +126,9 @@ mod storage { MerchantStorageScheme::RedisKv => { let key = PartitionKey::MerchantIdCustomerId { merchant_id, - customer_id, + customer_id: customer_id.get_string_repr(), }; - let field = format!("cust_{}", customer_id); + let field = format!("cust_{}", customer_id.get_string_repr()); Box::pin(db_utils::try_redis_get_else_try_database_get( // check for ValueNotFound async { @@ -167,7 +167,7 @@ mod storage { #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, - customer_id: String, + customer_id: id_type::CustomerId, merchant_id: String, customer: domain::Customer, customer_update: storage_types::CustomerUpdate, @@ -190,9 +190,9 @@ mod storage { }; let key = PartitionKey::MerchantIdCustomerId { merchant_id: merchant_id.as_str(), - customer_id: customer_id.as_str(), + customer_id: customer_id.get_string_repr(), }; - let field = format!("cust_{}", customer_id); + let field = format!("cust_{}", customer_id.get_string_repr()); let storage_scheme = decide_storage_scheme::<_, diesel_models::Customer>( self, storage_scheme, @@ -244,7 +244,7 @@ mod storage { #[instrument(skip_all)] async fn find_customer_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -267,9 +267,9 @@ mod storage { MerchantStorageScheme::RedisKv => { let key = PartitionKey::MerchantIdCustomerId { merchant_id, - customer_id, + customer_id: customer_id.get_string_repr(), }; - let field = format!("cust_{}", customer_id); + let field = format!("cust_{}", customer_id.get_string_repr()); Box::pin(db_utils::try_redis_get_else_try_database_get( async { kv_wrapper( @@ -357,9 +357,9 @@ mod storage { MerchantStorageScheme::RedisKv => { let key = PartitionKey::MerchantIdCustomerId { merchant_id: merchant_id.as_str(), - customer_id: customer_id.as_str(), + customer_id: customer_id.get_string_repr(), }; - let field = format!("cust_{}", customer_id.clone()); + let field = format!("cust_{}", customer_id.get_string_repr()); let redis_entry = kv::TypedSql { op: kv::DBOperation::Insert { @@ -384,7 +384,7 @@ mod storage { Ok(redis_interface::HsetnxReply::KeyNotSet) => { Err(report!(errors::StorageError::DuplicateValue { entity: "customer", - key: Some(customer_id), + key: Some(customer_id.get_string_repr().to_string()), })) } Ok(redis_interface::HsetnxReply::KeySet) => Ok(storage_customer), @@ -402,7 +402,7 @@ mod storage { #[instrument(skip_all)] async fn delete_customer_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; @@ -419,7 +419,7 @@ mod storage { #[cfg(not(feature = "kv_store"))] mod storage { - use common_utils::ext_traits::AsyncExt; + use common_utils::{ext_traits::AsyncExt, id_type}; use error_stack::{report, ResultExt}; use futures::future::try_join_all; use masking::PeekInterface; @@ -447,7 +447,7 @@ mod storage { #[instrument(skip_all)] async fn find_customer_optional_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -483,7 +483,7 @@ mod storage { #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, - customer_id: String, + customer_id: id_type::CustomerId, merchant_id: String, _customer: domain::Customer, customer_update: storage_types::CustomerUpdate, @@ -510,7 +510,7 @@ mod storage { #[instrument(skip_all)] async fn find_customer_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -590,7 +590,7 @@ mod storage { #[instrument(skip_all)] async fn delete_customer_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; @@ -610,7 +610,7 @@ impl CustomerInterface for MockDb { #[allow(clippy::panic)] async fn find_customer_optional_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -619,7 +619,7 @@ impl CustomerInterface for MockDb { let customer = customers .iter() .find(|customer| { - customer.customer_id == customer_id && customer.merchant_id == merchant_id + customer.customer_id == *customer_id && customer.merchant_id == merchant_id }) .cloned(); customer @@ -659,7 +659,7 @@ impl CustomerInterface for MockDb { #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, - _customer_id: String, + _customer_id: id_type::CustomerId, _merchant_id: String, _customer: domain::Customer, _customer_update: storage_types::CustomerUpdate, @@ -672,7 +672,7 @@ impl CustomerInterface for MockDb { async fn find_customer_by_customer_id_merchant_id( &self, - _customer_id: &str, + _customer_id: &id_type::CustomerId, _merchant_id: &str, _key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -704,7 +704,7 @@ impl CustomerInterface for MockDb { async fn delete_customer_by_customer_id_merchant_id( &self, - _customer_id: &str, + _customer_id: &id_type::CustomerId, _merchant_id: &str, ) -> CustomResult { // [#172]: Implement function for `MockDb` diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index a2aed2923647..0e3b365ae075 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use common_enums::enums::MerchantStorageScheme; -use common_utils::{errors::CustomResult, pii}; +use common_utils::{errors::CustomResult, id_type, pii}; use diesel_models::{ enums, enums::ProcessTrackerStatus, @@ -175,7 +175,7 @@ impl AddressInterface for KafkaStore { async fn update_address_by_merchant_id_customer_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, address: storage::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -320,7 +320,7 @@ impl ConfigInterface for KafkaStore { impl CustomerInterface for KafkaStore { async fn delete_customer_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, ) -> CustomResult { self.diesel_store @@ -330,7 +330,7 @@ impl CustomerInterface for KafkaStore { async fn find_customer_optional_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -347,7 +347,7 @@ impl CustomerInterface for KafkaStore { async fn update_customer_by_customer_id_merchant_id( &self, - customer_id: String, + customer_id: id_type::CustomerId, merchant_id: String, customer: domain::Customer, customer_update: storage::CustomerUpdate, @@ -378,7 +378,7 @@ impl CustomerInterface for KafkaStore { async fn find_customer_by_customer_id_merchant_id( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -706,7 +706,7 @@ impl MandateInterface for KafkaStore { async fn find_mandate_by_merchant_id_customer_id( &self, merchant_id: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, ) -> CustomResult, errors::StorageError> { self.diesel_store .find_mandate_by_merchant_id_customer_id(merchant_id, customer_id) @@ -1453,7 +1453,7 @@ impl PaymentMethodInterface for KafkaStore { async fn find_payment_method_by_customer_id_merchant_id_list( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, limit: Option, ) -> CustomResult, errors::StorageError> { @@ -1464,7 +1464,7 @@ impl PaymentMethodInterface for KafkaStore { async fn find_payment_method_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, limit: Option, @@ -1483,7 +1483,7 @@ impl PaymentMethodInterface for KafkaStore { async fn get_payment_method_count_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, ) -> CustomResult { diff --git a/crates/router/src/db/locker_mock_up.rs b/crates/router/src/db/locker_mock_up.rs index ea0e70c5bb90..0fb675025c9a 100644 --- a/crates/router/src/db/locker_mock_up.rs +++ b/crates/router/src/db/locker_mock_up.rs @@ -131,6 +131,8 @@ impl LockerMockUpInterface for MockDb { mod tests { #[allow(clippy::unwrap_used)] mod mockdb_locker_mock_up_interface { + use common_utils::{generate_customer_id_of_default_length, id_type}; + use crate::{ db::{locker_mock_up::LockerMockUpInterface, MockDb}, types::storage, @@ -140,7 +142,7 @@ mod tests { card_id: String, external_id: String, merchant_id: String, - customer_id: String, + customer_id: id_type::CustomerId, } fn create_locker_mock_up_new(locker_ids: LockerMockUpIds) -> storage::LockerMockUpNew { @@ -174,7 +176,7 @@ mod tests { card_id: "card_1".into(), external_id: "external_1".into(), merchant_id: "merchant_1".into(), - customer_id: "customer_1".into(), + customer_id: generate_customer_id_of_default_length(), })) .await .unwrap(); @@ -184,7 +186,7 @@ mod tests { card_id: "card_2".into(), external_id: "external_1".into(), merchant_id: "merchant_1".into(), - customer_id: "customer_1".into(), + customer_id: generate_customer_id_of_default_length(), })) .await; @@ -205,7 +207,7 @@ mod tests { card_id: "card_1".into(), external_id: "external_1".into(), merchant_id: "merchant_1".into(), - customer_id: "customer_1".into(), + customer_id: generate_customer_id_of_default_length(), })) .await .unwrap(); @@ -235,7 +237,7 @@ mod tests { card_id: "card_1".into(), external_id: "external_1".into(), merchant_id: "merchant_1".into(), - customer_id: "customer_1".into(), + customer_id: generate_customer_id_of_default_length(), })) .await .unwrap(); diff --git a/crates/router/src/db/mandate.rs b/crates/router/src/db/mandate.rs index c9995d502c0d..e69e3318ae61 100644 --- a/crates/router/src/db/mandate.rs +++ b/crates/router/src/db/mandate.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use error_stack::ResultExt; use super::MockDb; @@ -25,7 +26,7 @@ pub trait MandateInterface { async fn find_mandate_by_merchant_id_customer_id( &self, merchant_id: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, ) -> CustomResult, errors::StorageError>; async fn update_mandate_by_merchant_id_mandate_id( @@ -52,7 +53,7 @@ pub trait MandateInterface { #[cfg(feature = "kv_store")] mod storage { - use common_utils::fallback_reverse_lookup_not_found; + use common_utils::{fallback_reverse_lookup_not_found, id_type}; use diesel_models::kv; use error_stack::{report, ResultExt}; use redis_interface::HsetnxReply; @@ -175,7 +176,7 @@ mod storage { async fn find_mandate_by_merchant_id_customer_id( &self, merchant_id: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; storage_types::Mandate::find_by_merchant_id_customer_id(&conn, merchant_id, customer_id) @@ -363,6 +364,7 @@ mod storage { #[cfg(not(feature = "kv_store"))] mod storage { + use common_utils::id_type; use error_stack::report; use router_env::{instrument, tracing}; @@ -410,7 +412,7 @@ mod storage { async fn find_mandate_by_merchant_id_customer_id( &self, merchant_id: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; storage_types::Mandate::find_by_merchant_id_customer_id(&conn, merchant_id, customer_id) @@ -505,7 +507,7 @@ impl MandateInterface for MockDb { async fn find_mandate_by_merchant_id_customer_id( &self, merchant_id: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, ) -> CustomResult, errors::StorageError> { return Ok(self .mandates @@ -513,7 +515,7 @@ impl MandateInterface for MockDb { .await .iter() .filter(|mandate| { - mandate.merchant_id == merchant_id && mandate.customer_id == customer_id + mandate.merchant_id == merchant_id && &mandate.customer_id == customer_id }) .cloned() .collect()); diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index 29b3379a2919..9ec25b1d1b2e 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -1,3 +1,4 @@ +use common_utils::id_type; use diesel_models::payment_method::PaymentMethodUpdateInternal; use error_stack::ResultExt; @@ -23,14 +24,14 @@ pub trait PaymentMethodInterface { async fn find_payment_method_by_customer_id_merchant_id_list( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, limit: Option, ) -> CustomResult, errors::StorageError>; async fn find_payment_method_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, limit: Option, @@ -39,7 +40,7 @@ pub trait PaymentMethodInterface { async fn get_payment_method_count_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, ) -> CustomResult; @@ -66,7 +67,7 @@ pub trait PaymentMethodInterface { #[cfg(feature = "kv_store")] mod storage { - use common_utils::fallback_reverse_lookup_not_found; + use common_utils::{fallback_reverse_lookup_not_found, id_type}; use diesel_models::{kv, PaymentMethodUpdateInternal}; use error_stack::{report, ResultExt}; use redis_interface::HsetnxReply; @@ -188,7 +189,7 @@ mod storage { #[instrument(skip_all)] async fn get_payment_method_count_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, ) -> CustomResult { @@ -230,7 +231,7 @@ mod storage { let key = PartitionKey::MerchantIdCustomerId { merchant_id: &merchant_id, - customer_id: &customer_id, + customer_id: customer_id.get_string_repr(), }; let key_str = key.to_string(); let field = @@ -301,7 +302,7 @@ mod storage { let customer_id = payment_method.customer_id.clone(); let key = PartitionKey::MerchantIdCustomerId { merchant_id: &merchant_id, - customer_id: &customer_id, + customer_id: customer_id.get_string_repr(), }; let field = format!("payment_method_id_{}", payment_method.payment_method_id); let storage_scheme = decide_storage_scheme::<_, storage_types::PaymentMethod>( @@ -364,7 +365,7 @@ mod storage { #[instrument(skip_all)] async fn find_payment_method_by_customer_id_merchant_id_list( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, limit: Option, ) -> CustomResult, errors::StorageError> { @@ -382,7 +383,7 @@ mod storage { #[instrument(skip_all)] async fn find_payment_method_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, limit: Option, @@ -406,7 +407,7 @@ mod storage { MerchantStorageScheme::RedisKv => { let key = PartitionKey::MerchantIdCustomerId { merchant_id, - customer_id, + customer_id: customer_id.get_string_repr(), }; let pattern = "payment_method_id_*"; @@ -456,6 +457,7 @@ mod storage { #[cfg(not(feature = "kv_store"))] mod storage { + use common_utils::id_type; use error_stack::report; use router_env::{instrument, tracing}; @@ -466,6 +468,7 @@ mod storage { services::Store, types::storage::{self as storage_types, enums::MerchantStorageScheme}, }; + #[async_trait::async_trait] impl PaymentMethodInterface for Store { #[instrument(skip_all)] @@ -495,7 +498,7 @@ mod storage { #[instrument(skip_all)] async fn get_payment_method_count_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, ) -> CustomResult { @@ -540,7 +543,7 @@ mod storage { #[instrument(skip_all)] async fn find_payment_method_by_customer_id_merchant_id_list( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, limit: Option, ) -> CustomResult, errors::StorageError> { @@ -558,7 +561,7 @@ mod storage { #[instrument(skip_all)] async fn find_payment_method_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, limit: Option, @@ -637,7 +640,7 @@ impl PaymentMethodInterface for MockDb { async fn get_payment_method_count_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, ) -> CustomResult { @@ -645,7 +648,7 @@ impl PaymentMethodInterface for MockDb { let count = payment_methods .iter() .filter(|pm| { - pm.customer_id == customer_id + pm.customer_id == *customer_id && pm.merchant_id == merchant_id && pm.status == status }) @@ -700,14 +703,14 @@ impl PaymentMethodInterface for MockDb { async fn find_payment_method_by_customer_id_merchant_id_list( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, _limit: Option, ) -> 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) + .filter(|pm| pm.customer_id == *customer_id && pm.merchant_id == merchant_id) .cloned() .collect(); @@ -723,7 +726,7 @@ impl PaymentMethodInterface for MockDb { async fn find_payment_method_by_customer_id_merchant_id_status( &self, - customer_id: &str, + customer_id: &id_type::CustomerId, merchant_id: &str, status: common_enums::PaymentMethodStatus, _limit: Option, @@ -733,7 +736,7 @@ impl PaymentMethodInterface for MockDb { let payment_methods_found: Vec = payment_methods .iter() .filter(|pm| { - pm.customer_id == customer_id + pm.customer_id == *customer_id && pm.merchant_id == merchant_id && pm.status == status }) diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs index 64fa8e83d498..b97cfc3fa0d8 100644 --- a/crates/router/src/routes/customers.rs +++ b/crates/router/src/routes/customers.rs @@ -1,4 +1,5 @@ use actix_web::{web, HttpRequest, HttpResponse, Responder}; +use common_utils::id_type; use router_env::{instrument, tracing, Flow}; use super::app::AppState; @@ -35,7 +36,7 @@ pub async fn customers_create( 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::CustomerId { @@ -90,12 +91,12 @@ pub async fn customers_list(state: web::Data, req: HttpRequest) -> Htt pub async fn customers_update( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, mut json_payload: web::Json, ) -> HttpResponse { let flow = Flow::CustomersUpdate; let customer_id = path.into_inner(); - json_payload.customer_id = customer_id; + json_payload.customer_id = Some(customer_id); Box::pin(api::server_wrap( flow, state, @@ -116,13 +117,14 @@ 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::CustomersCreate; let payload = web::Json(customers::CustomerId { customer_id: path.into_inner(), }) .into_inner(); + Box::pin(api::server_wrap( flow, state, @@ -142,7 +144,7 @@ pub async fn customers_delete( pub async fn get_customer_mandates( state: web::Data, req: HttpRequest, - path: web::Path, + path: web::Path, ) -> impl Responder { let flow = Flow::CustomersGetMandates; let customer_id = customers::CustomerId { diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index e9dacd9f84f9..96daf95d1156 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -1,5 +1,5 @@ use actix_web::{web, HttpRequest, HttpResponse}; -use common_utils::{consts::TOKEN_TTL, errors::CustomResult}; +use common_utils::{consts::TOKEN_TTL, errors::CustomResult, id_type}; use diesel_models::enums::IntentStatus; use error_stack::ResultExt; use router_env::{instrument, logger, tracing, Flow}; @@ -131,7 +131,7 @@ pub async fn list_payment_method_api( #[instrument(skip_all, fields(flow = ?Flow::CustomerPaymentMethodsList))] pub async fn list_customer_payment_method_api( state: web::Data, - customer_id: web::Path<(String,)>, + customer_id: web::Path<(id_type::CustomerId,)>, req: HttpRequest, query_payload: web::Query, ) -> HttpResponse { diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index c7280261c2a5..64a7c8069f78 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -5,7 +5,7 @@ use api_models::{ }; use async_trait::async_trait; use common_enums::TokenPurpose; -use common_utils::date_time; +use common_utils::{date_time, id_type}; use error_stack::{report, ResultExt}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use masking::PeekInterface; @@ -1037,7 +1037,7 @@ where pub async fn is_ephemeral_auth( headers: &HeaderMap, db: &dyn StorageInterface, - customer_id: &str, + customer_id: &id_type::CustomerId, ) -> RouterResult>> { let api_key = get_api_key(headers)?; diff --git a/crates/router/src/services/kafka/payment_intent.rs b/crates/router/src/services/kafka/payment_intent.rs index 81ac6454c91f..7a74f07e3b2e 100644 --- a/crates/router/src/services/kafka/payment_intent.rs +++ b/crates/router/src/services/kafka/payment_intent.rs @@ -1,4 +1,4 @@ -use common_utils::types::MinorUnit; +use common_utils::{id_type, types::MinorUnit}; use diesel_models::enums as storage_enums; use hyperswitch_domain_models::payments::PaymentIntent; use time::OffsetDateTime; @@ -11,7 +11,7 @@ pub struct KafkaPaymentIntent<'a> { pub amount: MinorUnit, pub currency: Option, pub amount_captured: Option, - pub customer_id: Option<&'a String>, + pub customer_id: Option<&'a id_type::CustomerId>, pub description: Option<&'a String>, pub return_url: Option<&'a String>, pub connector_id: Option<&'a String>, diff --git a/crates/router/src/services/kafka/payout.rs b/crates/router/src/services/kafka/payout.rs index a07b90ee288b..fd781c7df9c2 100644 --- a/crates/router/src/services/kafka/payout.rs +++ b/crates/router/src/services/kafka/payout.rs @@ -1,4 +1,4 @@ -use common_utils::pii; +use common_utils::{id_type, pii}; use diesel_models::enums as storage_enums; use hyperswitch_domain_models::payouts::{payout_attempt::PayoutAttempt, payouts::Payouts}; use time::OffsetDateTime; @@ -8,7 +8,7 @@ pub struct KafkaPayout<'a> { pub payout_id: &'a String, pub payout_attempt_id: &'a String, pub merchant_id: &'a String, - pub customer_id: &'a String, + pub customer_id: &'a id_type::CustomerId, pub address_id: &'a String, pub profile_id: &'a String, pub payout_method_id: Option<&'a String>, diff --git a/crates/router/src/types/domain/address.rs b/crates/router/src/types/domain/address.rs index f2b110deb191..0ed09a35013e 100644 --- a/crates/router/src/types/domain/address.rs +++ b/crates/router/src/types/domain/address.rs @@ -2,6 +2,7 @@ use async_trait::async_trait; use common_utils::{ crypto, date_time, errors::{CustomResult, ValidationError}, + id_type, }; use diesel_models::{address::AddressUpdateInternal, encryption::Encryption, enums}; use error_stack::ResultExt; @@ -48,13 +49,13 @@ pub struct PaymentAddress { pub address: Address, pub payment_id: String, // This is present in `PaymentAddress` because even `payouts` uses `PaymentAddress` - pub customer_id: Option, + pub customer_id: Option, } #[derive(Debug, Clone)] pub struct CustomerAddress { pub address: Address, - pub customer_id: String, + pub customer_id: id_type::CustomerId, } #[async_trait] diff --git a/crates/router/src/types/domain/customer.rs b/crates/router/src/types/domain/customer.rs index 0e8ac32b4af3..c5b0b3701cb8 100644 --- a/crates/router/src/types/domain/customer.rs +++ b/crates/router/src/types/domain/customer.rs @@ -1,4 +1,4 @@ -use common_utils::{crypto, date_time, pii}; +use common_utils::{crypto, date_time, id_type, pii}; use diesel_models::{customers::CustomerUpdateInternal, encryption::Encryption}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -10,7 +10,7 @@ use crate::errors::{CustomResult, ValidationError}; #[derive(Clone, Debug)] pub struct Customer { pub id: Option, - pub customer_id: String, + pub customer_id: id_type::CustomerId, pub merchant_id: String, pub name: crypto::OptionalEncryptableName, pub email: crypto::OptionalEncryptableEmail, diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 82182d117029..3391b52a22c3 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -1184,7 +1184,7 @@ impl ForeignFrom for gsm_api_types::GsmResponse { } } -impl ForeignFrom<&domain::Customer> for payments::CustomerDetails { +impl ForeignFrom<&domain::Customer> for payments::CustomerDetailsResponse { fn foreign_from(customer: &domain::Customer) -> Self { Self { id: customer.customer_id.clone(), diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index fdbf5087919d..da6cac195ca2 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -16,6 +16,7 @@ use std::fmt::Debug; use api_models::{enums, payments, webhooks}; use base64::Engine; +use common_utils::id_type; pub use common_utils::{ crypto, ext_traits::{ByteSliceExt, BytesExt, Encode, StringExt, ValueExt}, @@ -572,7 +573,7 @@ pub trait CustomerAddress { &self, address_details: payments::AddressDetails, merchant_id: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, ) -> CustomResult; @@ -640,7 +641,7 @@ impl CustomerAddress for api_models::customers::CustomerRequest { &self, address_details: payments::AddressDetails, merchant_id: &str, - customer_id: &str, + customer_id: &id_type::CustomerId, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, ) -> CustomResult { @@ -698,7 +699,7 @@ impl CustomerAddress for api_models::customers::CustomerRequest { Ok(domain::CustomerAddress { address, - customer_id: customer_id.to_string(), + customer_id: customer_id.to_owned(), }) } .await diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index 8936b2af67fe..256b115dbbad 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -2,6 +2,7 @@ use api_models::{ enums::Connector::{DummyConnector4, DummyConnector7}, user::sample_data::SampleDataRequest, }; +use common_utils::id_type; use diesel_models::{user::sample_data::PaymentAttemptBatchNew, RefundNew}; use error_stack::ResultExt; use hyperswitch_domain_models::payments::payment_intent::PaymentIntentNew; @@ -143,6 +144,10 @@ pub async fn generate_sample_data( return Err(SampleDataError::InvalidParameters.into()); } + // This has to be an internal server error because, this function failing means that the intended functionality is not working as expected + let dashboard_customer_id = id_type::CustomerId::from("hs-dashboard-user".into()) + .change_context(SampleDataError::InternalServerError)?; + for num in 1..=sample_data_size { let payment_id = common_utils::generate_id_with_default_len("test"); let attempt_id = crate::utils::get_payment_attempt_id(&payment_id, 1); @@ -191,7 +196,7 @@ pub async fn generate_sample_data( attempt_id.clone(), ), attempt_count: 1, - customer_id: Some("hs-dashboard-user".to_string()), + customer_id: Some(dashboard_customer_id.clone()), amount_captured: Some(common_utils::types::MinorUnit::new(amount * 100)), profile_id: Some(profile_id.clone()), return_url: Default::default(), diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index b4eafee4c832..414ab8cd1d5f 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -1,6 +1,7 @@ use std::{marker::PhantomData, str::FromStr}; use api_models::payments::{Address, AddressDetails, PhoneDetails}; +use common_utils::id_type; use masking::Secret; use router::{ configs::settings::Settings, @@ -22,7 +23,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { types::RouterData { flow: PhantomData, merchant_id: String::from("aci"), - customer_id: Some(String::from("aci")), + customer_id: Some(id_type::CustomerId::from("aci".into()).unwrap()), connector: "aci".to_string(), payment_id: uuid::Uuid::new_v4().to_string(), attempt_id: uuid::Uuid::new_v4().to_string(), @@ -130,7 +131,7 @@ fn construct_refund_router_data() -> types::RefundsRouterData { types::RouterData { flow: PhantomData, merchant_id: String::from("aci"), - customer_id: Some(String::from("aci")), + customer_id: Some(id_type::CustomerId::from("aci".into()).unwrap()), connector: "aci".to_string(), payment_id: uuid::Uuid::new_v4().to_string(), attempt_id: uuid::Uuid::new_v4().to_string(), diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index 9955cb77df73..ae93ca326bac 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -480,7 +480,7 @@ pub trait ConnectorActions: Connector { entity_type: enums::PayoutEntityType::Individual, payout_type, customer_details: Some(payments::CustomerDetails { - customer_id: core_utils::get_or_generate_id("customer_id", &None, "cust_").ok(), + customer_id: Some(common_utils::generate_customer_id_of_default_length()), name: Some(Secret::new("John Doe".to_string())), email: Email::from_str("john.doe@example").ok(), phone: Some(Secret::new("620874518".to_string())), @@ -500,7 +500,7 @@ pub trait ConnectorActions: Connector { RouterData { flow: PhantomData, merchant_id: self.get_name(), - customer_id: Some(self.get_name()), + customer_id: Some(common_utils::generate_customer_id_of_default_length()), connector: self.get_name(), payment_id: uuid::Uuid::new_v4().to_string(), attempt_id: uuid::Uuid::new_v4().to_string(), diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index 6566004c4cf6..e76eee4c1792 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -2,7 +2,7 @@ mod utils; -use common_utils::types::MinorUnit; +use common_utils::{id_type, types::MinorUnit}; use router::{ configs, core::payments, @@ -493,7 +493,7 @@ async fn payments_create_core_adyen_no_redirect() { amount_to_capture: Some(MinorUnit::new(6540)), capture_on: Some(datetime!(2022-09-10 10:11:12)), confirm: Some(true), - customer_id: Some(customer_id), + customer_id: Some(id_type::CustomerId::from(customer_id.into()).unwrap()), description: Some("Its my first payment request".to_string()), return_url: Some(url::Url::parse("http://example.com/payments").unwrap()), setup_future_usage: Some(api_enums::FutureUsage::OnSession), diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 288a1af3006c..42cfe3b74c1d 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -2,7 +2,7 @@ mod utils; -use common_utils::types::MinorUnit; +use common_utils::{id_type, types::MinorUnit}; use router::{ core::payments, db::StorageImpl, @@ -260,7 +260,7 @@ async fn payments_create_core_adyen_no_redirect() { amount_to_capture: Some(MinorUnit::new(6540)), capture_on: Some(datetime!(2022-09-10 10:11:12)), confirm: Some(true), - customer_id: Some(customer_id), + customer_id: Some(id_type::CustomerId::from(customer_id.into()).unwrap()), description: Some("Its my first payment request".to_string()), return_url: Some(url::Url::parse("http://example.com/payments").unwrap()), setup_future_usage: Some(api_enums::FutureUsage::OffSession), diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs index 4ee66826b208..b1582bb6e9af 100644 --- a/crates/storage_impl/src/lib.rs +++ b/crates/storage_impl/src/lib.rs @@ -392,7 +392,8 @@ impl UniqueConstraints for diesel_models::Customer { fn unique_constraints(&self) -> Vec { vec![format!( "customer_{}_{}", - self.customer_id, self.merchant_id + self.customer_id.get_string_repr(), + self.merchant_id )] } fn table_name(&self) -> &str { diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 5a21fe0d2497..9defe94f94c5 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -8118,7 +8118,9 @@ "customer_id": { "type": "string", "description": "The unique identifier of the customer.", - "example": "cus_meowerunwiuwiwqw" + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "maxLength": 64, + "minLength": 1 }, "payment_method": { "$ref": "#/components/schemas/PaymentMethod" @@ -8173,7 +8175,53 @@ "properties": { "id": { "type": "string", - "description": "The identifier for the customer." + "description": "The identifier for the customer.", + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "maxLength": 64, + "minLength": 1 + }, + "name": { + "type": "string", + "description": "The customer's name", + "example": "John Doe", + "nullable": true, + "maxLength": 255 + }, + "email": { + "type": "string", + "description": "The customer's email address", + "example": "johntest@test.com", + "nullable": true, + "maxLength": 255 + }, + "phone": { + "type": "string", + "description": "The customer's phone number", + "example": "3141592653", + "nullable": true, + "maxLength": 10 + }, + "phone_country_code": { + "type": "string", + "description": "The country code for the customer's phone number", + "example": "+1", + "nullable": true, + "maxLength": 2 + } + } + }, + "CustomerDetailsResponse": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "description": "The identifier for the customer.", + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "maxLength": 64, + "minLength": 1 }, "name": { "type": "string", @@ -8231,7 +8279,9 @@ "customer_id": { "type": "string", "description": "The unique identifier of the customer.", - "example": "cus_meowerunwiuwiwqw" + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "maxLength": 64, + "minLength": 1 }, "payment_method": { "$ref": "#/components/schemas/PaymentMethod" @@ -8378,7 +8428,9 @@ "type": "string", "description": "The identifier for the customer object. If not provided the customer ID will be autogenerated.", "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", - "maxLength": 255 + "nullable": true, + "maxLength": 64, + "minLength": 1 }, "name": { "type": "string", @@ -8439,9 +8491,10 @@ "properties": { "customer_id": { "type": "string", - "description": "The identifier for the customer object. If not provided the customer ID will be autogenerated.", + "description": "The identifier for the customer object", "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", - "maxLength": 255 + "maxLength": 64, + "minLength": 1 }, "name": { "type": "string", @@ -8521,7 +8574,10 @@ ], "properties": { "customer_id": { - "type": "string" + "type": "string", + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "maxLength": 64, + "minLength": 1 }, "payment_method_id": { "type": "string" @@ -8773,7 +8829,10 @@ "properties": { "customer_id": { "type": "string", - "description": "customer_id to which this ephemeral key belongs to" + "description": "customer_id to which this ephemeral key belongs to", + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "maxLength": 64, + "minLength": 1 }, "created_at": { "type": "integer", @@ -12885,8 +12944,10 @@ "customer_id": { "type": "string", "description": "The identifier for customer", - "example": "cus_meowuwunwiuwiwqw", - "nullable": true + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "nullable": true, + "maxLength": 64, + "minLength": 1 }, "starting_after": { "type": "string", @@ -13031,8 +13092,10 @@ "customer_id": { "type": "string", "description": "The unique identifier of the customer.", - "example": "cus_meowerunwiuwiwqw", - "nullable": true + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "nullable": true, + "maxLength": 64, + "minLength": 1 }, "card_network": { "type": "string", @@ -13404,8 +13467,10 @@ "customer_id": { "type": "string", "description": "The unique identifier of the customer.", - "example": "cus_meowerunwiuwiwqw", - "nullable": true + "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "nullable": true, + "maxLength": 64, + "minLength": 1 }, "payment_method_id": { "type": "string", @@ -13862,10 +13927,11 @@ }, "customer_id": { "type": "string", - "description": "The identifier for the customer object.", + "description": "The identifier for the customer", "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", "nullable": true, - "maxLength": 255 + "maxLength": 64, + "minLength": 1 }, "off_session": { "type": "boolean", @@ -14222,10 +14288,11 @@ }, "customer_id": { "type": "string", - "description": "The identifier for the customer object.", + "description": "The identifier for the customer", "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", "nullable": true, - "maxLength": 255 + "maxLength": 64, + "minLength": 1 }, "off_session": { "type": "boolean", @@ -14694,10 +14761,11 @@ }, "customer_id": { "type": "string", - "description": "The identifier for the customer object.", + "description": "The identifier for the customer", "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", "nullable": true, - "maxLength": 255 + "maxLength": 64, + "minLength": 1 }, "email": { "type": "string", @@ -15101,12 +15169,13 @@ "deprecated": true, "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", "nullable": true, - "maxLength": 255 + "maxLength": 64, + "minLength": 1 }, "customer": { "allOf": [ { - "$ref": "#/components/schemas/CustomerDetails" + "$ref": "#/components/schemas/CustomerDetailsResponse" } ], "nullable": true @@ -15792,10 +15861,11 @@ }, "customer_id": { "type": "string", - "description": "The identifier for the customer object.", + "description": "The identifier for the customer", "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", "nullable": true, - "maxLength": 255 + "maxLength": 64, + "minLength": 1 }, "off_session": { "type": "boolean", @@ -16155,7 +16225,6 @@ "currency", "confirm", "payout_type", - "customer_id", "auto_fulfill", "client_secret", "return_url", @@ -16233,6 +16302,7 @@ "type": "string", "description": "The identifier for the customer object. If not provided the customer ID will be autogenerated.", "example": "cus_y3oqhf46pyzuxjbcn2giaqnb44", + "nullable": true, "maxLength": 255 }, "auto_fulfill": { diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/request.json index 397f164511f7..01ca82e7a6da 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario19-Add card flow/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json index 397f164511f7..01ca82e7a6da 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario20-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json index 397f164511f7..01ca82e7a6da 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario21-Don't Pass CVV for save card flow and verify failed payment Copy/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario11-Pass Invalid CVV for save card flow and verify failed payment Copy/Payments - Create/request.json b/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario11-Pass Invalid CVV for save card flow and verify failed payment Copy/Payments - Create/request.json index 2b6e0287d48c..d84bc378e9fe 100644 --- a/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario11-Pass Invalid CVV for save card flow and verify failed payment Copy/Payments - Create/request.json +++ b/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario11-Pass Invalid CVV for save card flow and verify failed payment Copy/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario12-Save card payment with manual capture Copy/Payments - Create/request.json b/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario12-Save card payment with manual capture Copy/Payments - Create/request.json index 2b6e0287d48c..d84bc378e9fe 100644 --- a/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario12-Save card payment with manual capture Copy/Payments - Create/request.json +++ b/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario12-Save card payment with manual capture Copy/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario13-Don't Pass CVV for save card flow and verify success payment Copy/Payments - Create/request.json b/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario13-Don't Pass CVV for save card flow and verify success payment Copy/Payments - Create/request.json index 2b6e0287d48c..d84bc378e9fe 100644 --- a/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario13-Don't Pass CVV for save card flow and verify success payment Copy/Payments - Create/request.json +++ b/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario13-Don't Pass CVV for save card flow and verify success payment Copy/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario11-Save card flow/Payments - Create/request.json b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario11-Save card flow/Payments - Create/request.json index 2b6e0287d48c..d84bc378e9fe 100644 --- a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario11-Save card flow/Payments - Create/request.json +++ b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario11-Save card flow/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario12-Don't Pass CVV for save card flow and verify success payment/Payments - Create/request.json b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario12-Don't Pass CVV for save card flow and verify success payment/Payments - Create/request.json index 2b6e0287d48c..d84bc378e9fe 100644 --- a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario12-Don't Pass CVV for save card flow and verify success payment/Payments - Create/request.json +++ b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario12-Don't Pass CVV for save card flow and verify success payment/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario13-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario13-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json index 2b6e0287d48c..d84bc378e9fe 100644 --- a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario13-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json +++ b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario13-Pass Invalid CVV for save card flow and verify failed payment/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario14-Save card payment with manual capture/Payments - Create/request.json b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario14-Save card payment with manual capture/Payments - Create/request.json index 2b6e0287d48c..d84bc378e9fe 100644 --- a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario14-Save card payment with manual capture/Payments - Create/request.json +++ b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario14-Save card payment with manual capture/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario10-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json b/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario10-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json index 188c0b386762..4df766dd9ae1 100644 --- a/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario10-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json +++ b/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario10-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "stripesavecard_{{random_number}}", + "customer_id": "stripesavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario11-Save card payment with manual capture/Payments - Create/request.json b/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario11-Save card payment with manual capture/Payments - Create/request.json index f40ab1192656..e2e3487835b0 100644 --- a/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario11-Save card payment with manual capture/Payments - Create/request.json +++ b/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario11-Save card payment with manual capture/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario9-Add card flow/Payments - Create/request.json b/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario9-Add card flow/Payments - Create/request.json index 750011ab1775..5210f97558b3 100644 --- a/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario9-Add card flow/Payments - Create/request.json +++ b/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario9-Add card flow/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "stripesavecard_{{random_number}}", + "customer_id": "stripesavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", @@ -76,12 +76,8 @@ }, "url": { "raw": "{{baseUrl}}/payments", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments" - ] + "host": ["{{baseUrl}}"], + "path": ["payments"] }, "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" } diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Create/request.json b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Create/request.json index d0320b099752..191d28b57229 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Create/request.json +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario10-Add card flow/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": "{{random_number}}", - "customer_id": "stripesavecard_{{random_number}}", + "customer_id": "stripesavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Create/request.json b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Create/request.json index fe4adccda47f..e9a8d716625f 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Create/request.json +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario11-Don't Pass CVV for save card flow and verify success payment/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": "{{random_number}}", - "customer_id": "stripesavecard_{{random_number}}", + "customer_id": "stripesavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Add card flow/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Add card flow/Payments - Create/request.json index 2ac0cacfb992..4e2266d7a8e7 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Add card flow/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Add card flow/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "stripesavecard_{{random_number}}", + "customer_id": "stripesavecard_1234", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json index 150106cfdedb..2fc42dd451df 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "stripesavecard_{{random_number}}", + "customer_id": "stripesavecard_1234", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario26-Save card payment with manual capture/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario26-Save card payment with manual capture/Payments - Create/request.json index d73166871cdd..875cca26aad9 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario26-Save card payment with manual capture/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario26-Save card payment with manual capture/Payments - Create/request.json @@ -24,7 +24,7 @@ "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, - "customer_id": "adyensavecard_{{random_number}}", + "customer_id": "adyensavecard", "email": "guest@example.com", "name": "John Doe", "phone": "999999999", diff --git a/postman/collection-json/stripe.postman_collection.json b/postman/collection-json/stripe.postman_collection.json index 397864885cb6..4fdc32050a18 100644 --- a/postman/collection-json/stripe.postman_collection.json +++ b/postman/collection-json/stripe.postman_collection.json @@ -18389,7 +18389,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard_{{random_number}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard_1234\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -19103,7 +19103,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard_{{random_number}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard_1234\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -19569,7 +19569,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard_{{random_number}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" }, "url": { "raw": "{{baseUrl}}/payments",