Skip to content

Commit 17f9e6e

Browse files
refactor(payments_v2): create customer at connector end and populate connector customer ID (#7246)
1 parent 3c7cb9e commit 17f9e6e

File tree

11 files changed

+356
-114
lines changed

11 files changed

+356
-114
lines changed

crates/diesel_models/src/customers.rs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,7 @@ impl From<CustomerNew> for Customer {
6464

6565
#[cfg(all(feature = "v2", feature = "customer_v2"))]
6666
#[derive(
67-
Clone,
68-
Debug,
69-
PartialEq,
70-
Insertable,
71-
router_derive::DebugAsDisplay,
72-
serde::Deserialize,
73-
serde::Serialize,
67+
Clone, Debug, Insertable, router_derive::DebugAsDisplay, serde::Deserialize, serde::Serialize,
7468
)]
7569
#[diesel(table_name = customers, primary_key(id))]
7670
pub struct CustomerNew {
@@ -82,7 +76,7 @@ pub struct CustomerNew {
8276
pub description: Option<Description>,
8377
pub created_at: PrimitiveDateTime,
8478
pub metadata: Option<pii::SecretSerdeValue>,
85-
pub connector_customer: Option<pii::SecretSerdeValue>,
79+
pub connector_customer: Option<ConnectorCustomerMap>,
8680
pub modified_at: PrimitiveDateTime,
8781
pub default_payment_method_id: Option<common_utils::id_type::GlobalPaymentMethodId>,
8882
pub updated_by: Option<String>,
@@ -164,7 +158,7 @@ pub struct Customer {
164158
pub description: Option<Description>,
165159
pub created_at: PrimitiveDateTime,
166160
pub metadata: Option<pii::SecretSerdeValue>,
167-
pub connector_customer: Option<pii::SecretSerdeValue>,
161+
pub connector_customer: Option<ConnectorCustomerMap>,
168162
pub modified_at: PrimitiveDateTime,
169163
pub default_payment_method_id: Option<common_utils::id_type::GlobalPaymentMethodId>,
170164
pub updated_by: Option<String>,
@@ -242,7 +236,7 @@ pub struct CustomerUpdateInternal {
242236
pub phone_country_code: Option<String>,
243237
pub metadata: Option<pii::SecretSerdeValue>,
244238
pub modified_at: PrimitiveDateTime,
245-
pub connector_customer: Option<pii::SecretSerdeValue>,
239+
pub connector_customer: Option<ConnectorCustomerMap>,
246240
pub default_payment_method_id: Option<Option<common_utils::id_type::GlobalPaymentMethodId>>,
247241
pub updated_by: Option<String>,
248242
pub default_billing_address: Option<Encryption>,
@@ -289,3 +283,31 @@ impl CustomerUpdateInternal {
289283
}
290284
}
291285
}
286+
287+
#[cfg(all(feature = "v2", feature = "customer_v2"))]
288+
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, diesel::AsExpression)]
289+
#[diesel(sql_type = diesel::sql_types::Jsonb)]
290+
#[serde(transparent)]
291+
pub struct ConnectorCustomerMap(
292+
std::collections::HashMap<common_utils::id_type::MerchantConnectorAccountId, String>,
293+
);
294+
295+
#[cfg(all(feature = "v2", feature = "customer_v2"))]
296+
common_utils::impl_to_sql_from_sql_json!(ConnectorCustomerMap);
297+
298+
#[cfg(all(feature = "v2", feature = "customer_v2"))]
299+
impl std::ops::Deref for ConnectorCustomerMap {
300+
type Target =
301+
std::collections::HashMap<common_utils::id_type::MerchantConnectorAccountId, String>;
302+
303+
fn deref(&self) -> &Self::Target {
304+
&self.0
305+
}
306+
}
307+
308+
#[cfg(all(feature = "v2", feature = "customer_v2"))]
309+
impl std::ops::DerefMut for ConnectorCustomerMap {
310+
fn deref_mut(&mut self) -> &mut Self::Target {
311+
&mut self.0
312+
}
313+
}

crates/hyperswitch_domain_models/src/business_profile.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ impl From<ProfileUpdate> for ProfileUpdateInternal {
985985
is_network_tokenization_enabled,
986986
is_auto_retries_enabled: None,
987987
max_auto_retries_enabled: None,
988-
is_click_to_pay_enabled: None,
988+
is_click_to_pay_enabled,
989989
authentication_product_ids,
990990
three_ds_decision_manager_config,
991991
}

crates/hyperswitch_domain_models/src/customer.rs

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub struct Customer {
5656
pub description: Option<Description>,
5757
pub created_at: PrimitiveDateTime,
5858
pub metadata: Option<pii::SecretSerdeValue>,
59-
pub connector_customer: Option<pii::SecretSerdeValue>,
59+
pub connector_customer: Option<diesel_models::ConnectorCustomerMap>,
6060
pub modified_at: PrimitiveDateTime,
6161
pub default_payment_method_id: Option<id_type::GlobalPaymentMethodId>,
6262
pub updated_by: Option<String>,
@@ -80,6 +80,31 @@ impl Customer {
8080
pub fn get_id(&self) -> &id_type::GlobalCustomerId {
8181
&self.id
8282
}
83+
84+
/// Get the connector customer ID for the specified connector label, if present
85+
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
86+
pub fn get_connector_customer_id(&self, connector_label: &str) -> Option<&str> {
87+
use masking::PeekInterface;
88+
89+
self.connector_customer
90+
.as_ref()
91+
.and_then(|connector_customer_value| {
92+
connector_customer_value.peek().get(connector_label)
93+
})
94+
.and_then(|connector_customer| connector_customer.as_str())
95+
}
96+
97+
/// Get the connector customer ID for the specified merchant connector account ID, if present
98+
#[cfg(all(feature = "v2", feature = "customer_v2"))]
99+
pub fn get_connector_customer_id(
100+
&self,
101+
merchant_connector_id: &id_type::MerchantConnectorAccountId,
102+
) -> Option<&str> {
103+
self.connector_customer
104+
.as_ref()
105+
.and_then(|connector_customer_map| connector_customer_map.get(merchant_connector_id))
106+
.map(|connector_customer_id| connector_customer_id.as_str())
107+
}
83108
}
84109

85110
#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))]
@@ -300,24 +325,28 @@ impl super::behaviour::Conversion for Customer {
300325
}
301326
}
302327

328+
#[cfg(all(feature = "v2", feature = "customer_v2"))]
329+
#[derive(Clone, Debug)]
330+
pub struct CustomerGeneralUpdate {
331+
pub name: crypto::OptionalEncryptableName,
332+
pub email: Box<crypto::OptionalEncryptableEmail>,
333+
pub phone: Box<crypto::OptionalEncryptablePhone>,
334+
pub description: Option<Description>,
335+
pub phone_country_code: Option<String>,
336+
pub metadata: Option<pii::SecretSerdeValue>,
337+
pub connector_customer: Box<Option<diesel_models::ConnectorCustomerMap>>,
338+
pub default_billing_address: Option<Encryption>,
339+
pub default_shipping_address: Option<Encryption>,
340+
pub default_payment_method_id: Option<Option<id_type::GlobalPaymentMethodId>>,
341+
pub status: Option<DeleteStatus>,
342+
}
343+
303344
#[cfg(all(feature = "v2", feature = "customer_v2"))]
304345
#[derive(Clone, Debug)]
305346
pub enum CustomerUpdate {
306-
Update {
307-
name: crypto::OptionalEncryptableName,
308-
email: Box<crypto::OptionalEncryptableEmail>,
309-
phone: Box<crypto::OptionalEncryptablePhone>,
310-
description: Option<Description>,
311-
phone_country_code: Option<String>,
312-
metadata: Option<pii::SecretSerdeValue>,
313-
connector_customer: Box<Option<pii::SecretSerdeValue>>,
314-
default_billing_address: Option<Encryption>,
315-
default_shipping_address: Option<Encryption>,
316-
default_payment_method_id: Option<Option<id_type::GlobalPaymentMethodId>>,
317-
status: Option<DeleteStatus>,
318-
},
347+
Update(Box<CustomerGeneralUpdate>),
319348
ConnectorCustomer {
320-
connector_customer: Option<pii::SecretSerdeValue>,
349+
connector_customer: Option<diesel_models::ConnectorCustomerMap>,
321350
},
322351
UpdateDefaultPaymentMethod {
323352
default_payment_method_id: Option<Option<id_type::GlobalPaymentMethodId>>,
@@ -328,33 +357,36 @@ pub enum CustomerUpdate {
328357
impl From<CustomerUpdate> for CustomerUpdateInternal {
329358
fn from(customer_update: CustomerUpdate) -> Self {
330359
match customer_update {
331-
CustomerUpdate::Update {
332-
name,
333-
email,
334-
phone,
335-
description,
336-
phone_country_code,
337-
metadata,
338-
connector_customer,
339-
default_billing_address,
340-
default_shipping_address,
341-
default_payment_method_id,
342-
status,
343-
} => Self {
344-
name: name.map(Encryption::from),
345-
email: email.map(Encryption::from),
346-
phone: phone.map(Encryption::from),
347-
description,
348-
phone_country_code,
349-
metadata,
350-
connector_customer: *connector_customer,
351-
modified_at: date_time::now(),
352-
default_billing_address,
353-
default_shipping_address,
354-
default_payment_method_id,
355-
updated_by: None,
356-
status,
357-
},
360+
CustomerUpdate::Update(update) => {
361+
let CustomerGeneralUpdate {
362+
name,
363+
email,
364+
phone,
365+
description,
366+
phone_country_code,
367+
metadata,
368+
connector_customer,
369+
default_billing_address,
370+
default_shipping_address,
371+
default_payment_method_id,
372+
status,
373+
} = *update;
374+
Self {
375+
name: name.map(Encryption::from),
376+
email: email.map(Encryption::from),
377+
phone: phone.map(Encryption::from),
378+
description,
379+
phone_country_code,
380+
metadata,
381+
connector_customer: *connector_customer,
382+
modified_at: date_time::now(),
383+
default_billing_address,
384+
default_shipping_address,
385+
default_payment_method_id,
386+
updated_by: None,
387+
status,
388+
}
389+
}
358390
CustomerUpdate::ConnectorCustomer { connector_customer } => Self {
359391
connector_customer,
360392
name: None,

crates/router/src/core/customers.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -679,19 +679,20 @@ impl CustomerDeleteBridge for id_type::GlobalCustomerId {
679679
redacted_encrypted_value.clone().into_encrypted(),
680680
);
681681

682-
let updated_customer = storage::CustomerUpdate::Update {
683-
name: Some(redacted_encrypted_value.clone()),
684-
email: Box::new(Some(redacted_encrypted_email)),
685-
phone: Box::new(Some(redacted_encrypted_value.clone())),
686-
description: Some(Description::from_str_unchecked(REDACTED)),
687-
phone_country_code: Some(REDACTED.to_string()),
688-
metadata: None,
689-
connector_customer: Box::new(None),
690-
default_billing_address: None,
691-
default_shipping_address: None,
692-
default_payment_method_id: None,
693-
status: Some(common_enums::DeleteStatus::Redacted),
694-
};
682+
let updated_customer =
683+
storage::CustomerUpdate::Update(Box::new(storage::CustomerGeneralUpdate {
684+
name: Some(redacted_encrypted_value.clone()),
685+
email: Box::new(Some(redacted_encrypted_email)),
686+
phone: Box::new(Some(redacted_encrypted_value.clone())),
687+
description: Some(Description::from_str_unchecked(REDACTED)),
688+
phone_country_code: Some(REDACTED.to_string()),
689+
metadata: None,
690+
connector_customer: Box::new(None),
691+
default_billing_address: None,
692+
default_shipping_address: None,
693+
default_payment_method_id: None,
694+
status: Some(common_enums::DeleteStatus::Redacted),
695+
}));
695696

696697
db.update_customer_by_global_id(
697698
key_manager_state,
@@ -1338,7 +1339,7 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
13381339
&domain_customer.id,
13391340
domain_customer.to_owned(),
13401341
merchant_account.get_id(),
1341-
storage::CustomerUpdate::Update {
1342+
storage::CustomerUpdate::Update(Box::new(storage::CustomerGeneralUpdate {
13421343
name: encryptable_customer.name,
13431344
email: Box::new(encryptable_customer.email.map(|email| {
13441345
let encryptable: Encryptable<Secret<String, pii::EmailStrategy>> =
@@ -1357,7 +1358,7 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
13571358
default_shipping_address: encrypted_customer_shipping_address.map(Into::into),
13581359
default_payment_method_id: Some(self.default_payment_method_id.clone()),
13591360
status: None,
1360-
},
1361+
})),
13611362
key_store,
13621363
merchant_account.storage_scheme,
13631364
)

0 commit comments

Comments
 (0)