Bug: [FEATURE] include connector_customer creation during payment methods migration



### Feature Description

`connector_customer` is a map of merchant_connector_id (key) and connector_customer_id (value) in `customers` table which stores the customer's ID created at connector's end.

This is usually populated as a part of payments flow when customer creation is required at connector's end.

The requirement here is to populate this during payment methods migration which also handles creation of customers.

### Possible Implementation

1. Read `connector_customer_id` in payment methods migrate API
2. Pass it down to `create_customer` which handles creation of new customers
3. In case customer already exists, do nothing
4. In case customer does not exist, `connector_customer` is populated with the merchant's connector ID and connector's customer ID.

### Have you spent some time checking if this feature request has been raised before?

- [x] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [x] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs
index 6b0e9dcb38d..5ecd2f71ef7 100644
--- a/crates/api_models/src/payment_methods.rs
+++ b/crates/api_models/src/payment_methods.rs
@@ -16,12 +16,10 @@ use masking::PeekInterface;
 use serde::de;
 use utoipa::{schema, ToSchema};
 
-#[cfg(feature = "v1")]
-use crate::customers;
 #[cfg(feature = "payouts")]
 use crate::payouts;
 use crate::{
-    admin, enums as api_enums,
+    admin, customers, enums as api_enums,
     payments::{self, BankCodeResponse},
 };
 
@@ -2485,6 +2483,7 @@ pub struct PaymentMethodRecord {
     pub payment_method_type: Option<api_enums::PaymentMethodType>,
     pub nick_name: masking::Secret<String>,
     pub payment_instrument_id: Option<masking::Secret<String>>,
+    pub connector_customer_id: Option<String>,
     pub card_number_masked: masking::Secret<String>,
     pub card_expiry_month: masking::Secret<String>,
     pub card_expiry_year: masking::Secret<String>,
@@ -2510,6 +2509,18 @@ pub struct PaymentMethodRecord {
     pub network_token_requestor_ref_id: Option<String>,
 }
 
+#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
+pub struct ConnectorCustomerDetails {
+    pub connector_customer_id: String,
+    pub merchant_connector_id: id_type::MerchantConnectorAccountId,
+}
+
+#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
+pub struct PaymentMethodCustomerMigrate {
+    pub customer: customers::CustomerRequest,
+    pub connector_customer_details: Option<ConnectorCustomerDetails>,
+}
+
 #[derive(Debug, Default, serde::Serialize)]
 pub struct PaymentMethodMigrationResponse {
     pub line_number: Option<i64>,
@@ -2678,29 +2689,40 @@ impl
 }
 
 #[cfg(feature = "v1")]
-impl From<(PaymentMethodRecord, id_type::MerchantId)> for customers::CustomerRequest {
+impl From<(PaymentMethodRecord, id_type::MerchantId)> for PaymentMethodCustomerMigrate {
     fn from(value: (PaymentMethodRecord, id_type::MerchantId)) -> Self {
         let (record, merchant_id) = value;
         Self {
-            customer_id: Some(record.customer_id),
-            merchant_id,
-            name: record.name,
-            email: record.email,
-            phone: record.phone,
-            description: None,
-            phone_country_code: record.phone_country_code,
-            address: Some(payments::AddressDetails {
-                city: Some(record.billing_address_city),
-                country: record.billing_address_country,
-                line1: Some(record.billing_address_line1),
-                line2: record.billing_address_line2,
-                state: Some(record.billing_address_state),
-                line3: record.billing_address_line3,
-                zip: Some(record.billing_address_zip),
-                first_name: Some(record.billing_address_first_name),
-                last_name: Some(record.billing_address_last_name),
-            }),
-            metadata: None,
+            customer: customers::CustomerRequest {
+                customer_id: Some(record.customer_id),
+                merchant_id,
+                name: record.name,
+                email: record.email,
+                phone: record.phone,
+                description: None,
+                phone_country_code: record.phone_country_code,
+                address: Some(payments::AddressDetails {
+                    city: Some(record.billing_address_city),
+                    country: record.billing_address_country,
+                    line1: Some(record.billing_address_line1),
+                    line2: record.billing_address_line2,
+                    state: Some(record.billing_address_state),
+                    line3: record.billing_address_line3,
+                    zip: Some(record.billing_address_zip),
+                    first_name: Some(record.billing_address_first_name),
+                    last_name: Some(record.billing_address_last_name),
+                }),
+                metadata: None,
+            },
+            connector_customer_details: record
+                .connector_customer_id
+                .zip(record.merchant_connector_id)
+                .map(
+                    |(connector_customer_id, merchant_connector_id)| ConnectorCustomerDetails {
+                        connector_customer_id,
+                        merchant_connector_id,
+                    },
+                ),
         }
     }
 }
diff --git a/crates/common_types/src/customers.rs b/crates/common_types/src/customers.rs
index 1d53630cade..1610bef511d 100644
--- a/crates/common_types/src/customers.rs
+++ b/crates/common_types/src/customers.rs
@@ -9,6 +9,16 @@ pub struct ConnectorCustomerMap(
     std::collections::HashMap<common_utils::id_type::MerchantConnectorAccountId, String>,
 );
 
+#[cfg(feature = "v2")]
+impl ConnectorCustomerMap {
+    /// Creates a new `ConnectorCustomerMap` from a HashMap
+    pub fn new(
+        map: std::collections::HashMap<common_utils::id_type::MerchantConnectorAccountId, String>,
+    ) -> Self {
+        Self(map)
+    }
+}
+
 #[cfg(feature = "v2")]
 common_utils::impl_to_sql_from_sql_json!(ConnectorCustomerMap);
 
diff --git a/crates/router/src/compatibility/stripe/customers.rs b/crates/router/src/compatibility/stripe/customers.rs
index 8acd4d10d43..9c5ebb22bc0 100644
--- a/crates/router/src/compatibility/stripe/customers.rs
+++ b/crates/router/src/compatibility/stripe/customers.rs
@@ -57,7 +57,7 @@ pub async fn customer_create(
             let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                 domain::Context(auth.merchant_account, auth.key_store),
             ));
-            customers::create_customer(state, merchant_context, req)
+            customers::create_customer(state, merchant_context, req, None)
         },
         &auth::HeaderAuth(auth::ApiKeyAuth {
             is_connected_allowed: false,
diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs
index 5ad3d95405c..4b4c35dac2a 100644
--- a/crates/router/src/core/customers.rs
+++ b/crates/router/src/core/customers.rs
@@ -27,7 +27,7 @@ use crate::{
     routes::{metrics, SessionState},
     services,
     types::{
-        api::customers,
+        api::{customers, payment_methods as payment_methods_api},
         domain::{self, types},
         storage::{self, enums},
         transformers::ForeignFrom,
@@ -41,6 +41,7 @@ pub async fn create_customer(
     state: SessionState,
     merchant_context: domain::MerchantContext,
     customer_data: customers::CustomerRequest,
+    connector_customer_details: Option<payment_methods_api::ConnectorCustomerDetails>,
 ) -> errors::CustomerResponse<customers::CustomerResponse> {
     let db: &dyn StorageInterface = state.store.as_ref();
     let key_manager_state = &(&state).into();
@@ -69,6 +70,7 @@ pub async fn create_customer(
 
     let domain_customer = customer_data
         .create_domain_model_from_request(
+            &connector_customer_details,
             db,
             &merchant_reference_id,
             &merchant_context,
@@ -94,6 +96,7 @@ pub async fn create_customer(
 trait CustomerCreateBridge {
     async fn create_domain_model_from_request<'a>(
         &'a self,
+        connector_customer_details: &'a Option<payment_methods_api::ConnectorCustomerDetails>,
         db: &'a dyn StorageInterface,
         merchant_reference_id: &'a Option<id_type::CustomerId>,
         merchant_context: &'a domain::MerchantContext,
@@ -112,6 +115,7 @@ trait CustomerCreateBridge {
 impl CustomerCreateBridge for customers::CustomerRequest {
     async fn create_domain_model_from_request<'a>(
         &'a self,
+        connector_customer_details: &'a Option<payment_methods_api::ConnectorCustomerDetails>,
         db: &'a dyn StorageInterface,
         merchant_reference_id: &'a Option<id_type::CustomerId>,
         merchant_context: &'a domain::MerchantContext,
@@ -171,6 +175,15 @@ impl CustomerCreateBridge for customers::CustomerRequest {
             domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
                 .change_context(errors::CustomersErrorResponse::InternalServerError)?;
 
+        let connector_customer = connector_customer_details.as_ref().map(|details| {
+            let merchant_connector_id = details.merchant_connector_id.get_string_repr().to_string();
+            let connector_customer_id = details.connector_customer_id.to_string();
+            let object = serde_json::json!({
+                merchant_connector_id: connector_customer_id
+            });
+            pii::SecretSerdeValue::new(object)
+        });
+
         Ok(domain::Customer {
             customer_id: merchant_reference_id
                 .to_owned()
@@ -188,7 +201,7 @@ impl CustomerCreateBridge for customers::CustomerRequest {
             description: self.description.clone(),
             phone_country_code: self.phone_country_code.clone(),
             metadata: self.metadata.clone(),
-            connector_customer: None,
+            connector_customer,
             address_id: address_from_db.clone().map(|addr| addr.address_id),
             created_at: common_utils::date_time::now(),
             modified_at: common_utils::date_time::now(),
@@ -214,6 +227,7 @@ impl CustomerCreateBridge for customers::CustomerRequest {
 impl CustomerCreateBridge for customers::CustomerRequest {
     async fn create_domain_model_from_request<'a>(
         &'a self,
+        connector_customer_details: &'a Option<payment_methods_api::ConnectorCustomerDetails>,
         _db: &'a dyn StorageInterface,
         merchant_reference_id: &'a Option<id_type::CustomerId>,
         merchant_context: &'a domain::MerchantContext,
@@ -283,6 +297,15 @@ impl CustomerCreateBridge for customers::CustomerRequest {
             domain::FromRequestEncryptableCustomer::from_encryptable(encrypted_data)
                 .change_context(errors::CustomersErrorResponse::InternalServerError)?;
 
+        let connector_customer = connector_customer_details.as_ref().map(|details| {
+            let mut map = std::collections::HashMap::new();
+            map.insert(
+                details.merchant_connector_id.clone(),
+                details.connector_customer_id.to_string(),
+            );
+            common_types::customers::ConnectorCustomerMap::new(map)
+        });
+
         Ok(domain::Customer {
             id: id_type::GlobalCustomerId::generate(&state.conf.cell_information.id),
             merchant_reference_id: merchant_reference_id.to_owned(),
@@ -299,7 +322,7 @@ impl CustomerCreateBridge for customers::CustomerRequest {
             description: self.description.clone(),
             phone_country_code: self.phone_country_code.clone(),
             metadata: self.metadata.clone(),
-            connector_customer: None,
+            connector_customer,
             created_at: common_utils::date_time::now(),
             modified_at: common_utils::date_time::now(),
             default_payment_method_id: None,
@@ -1024,6 +1047,7 @@ pub async fn update_customer(
     let updated_customer = update_customer
         .request
         .create_domain_model_from_request(
+            &None,
             db,
             &merchant_context,
             key_manager_state,
@@ -1039,6 +1063,7 @@ pub async fn update_customer(
 trait CustomerUpdateBridge {
     async fn create_domain_model_from_request<'a>(
         &'a self,
+        connector_customer_details: &'a Option<payment_methods_api::ConnectorCustomerDetails>,
         db: &'a dyn StorageInterface,
         merchant_context: &'a domain::MerchantContext,
         key_manager_state: &'a KeyManagerState,
@@ -1211,6 +1236,7 @@ impl VerifyIdForUpdateCustomer<'_> {
 impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
     async fn create_domain_model_from_request<'a>(
         &'a self,
+        _connector_customer_details: &'a Option<payment_methods_api::ConnectorCustomerDetails>,
         db: &'a dyn StorageInterface,
         merchant_context: &'a domain::MerchantContext,
         key_manager_state: &'a KeyManagerState,
@@ -1315,6 +1341,7 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
 impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
     async fn create_domain_model_from_request<'a>(
         &'a self,
+        connector_customer_details: &'a Option<payment_methods_api::ConnectorCustomerDetails>,
         db: &'a dyn StorageInterface,
         merchant_context: &'a domain::MerchantContext,
         key_manager_state: &'a KeyManagerState,
@@ -1432,11 +1459,18 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
 
 pub async fn migrate_customers(
     state: SessionState,
-    customers: Vec<customers::CustomerRequest>,
+    customers_migration: Vec<payment_methods_api::PaymentMethodCustomerMigrate>,
     merchant_context: domain::MerchantContext,
 ) -> errors::CustomerResponse<()> {
-    for customer in customers {
-        match create_customer(state.clone(), merchant_context.clone(), customer).await {
+    for customer_migration in customers_migration {
+        match create_customer(
+            state.clone(),
+            merchant_context.clone(),
+            customer_migration.customer,
+            customer_migration.connector_customer_details,
+        )
+        .await
+        {
             Ok(_) => (),
             Err(e) => match e.current_context() {
                 errors::CustomersErrorResponse::CustomerAlreadyExists => (),
diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs
index ce5d007fcde..96733181600 100644
--- a/crates/router/src/routes/customers.rs
+++ b/crates/router/src/routes/customers.rs
@@ -25,7 +25,7 @@ pub async fn customers_create(
             let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                 domain::Context(auth.merchant_account, auth.key_store),
             ));
-            create_customer(state, merchant_context, req)
+            create_customer(state, merchant_context, req, None)
         },
         auth::auth_type(
             &auth::V2ApiKeyAuth {
@@ -58,7 +58,7 @@ pub async fn customers_create(
             let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                 domain::Context(auth.merchant_account, auth.key_store),
             ));
-            create_customer(state, merchant_context, req)
+            create_customer(state, merchant_context, req, None)
         },
         auth::auth_type(
             &auth::HeaderAuth(auth::ApiKeyAuth {
diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs
index 3288a912ade..f094b89a16c 100644
--- a/crates/router/src/routes/payment_methods.rs
+++ b/crates/router/src/routes/payment_methods.rs
@@ -14,6 +14,8 @@ use hyperswitch_domain_models::{
 use router_env::{instrument, logger, tracing, Flow};
 
 use super::app::{AppState, SessionState};
+#[cfg(all(feature = "v1", any(feature = "olap", feature = "oltp")))]
+use crate::core::{customers, payment_methods::tokenize};
 use crate::{
     core::{
         api_locking,
@@ -27,11 +29,6 @@ use crate::{
         storage::payment_method::PaymentTokenData,
     },
 };
-#[cfg(all(feature = "v1", any(feature = "olap", feature = "oltp")))]
-use crate::{
-    core::{customers, payment_methods::tokenize},
-    types::api::customers::CustomerRequest,
-};
 
 #[cfg(feature = "v1")]
 #[instrument(skip_all, fields(flow = ?Flow::PaymentMethodsCreate))]
@@ -360,7 +357,12 @@ pub async fn migrate_payment_methods(
                 customers::migrate_customers(
                     state.clone(),
                     req.iter()
-                        .map(|e| CustomerRequest::from((e.clone(), merchant_id.clone())))
+                        .map(|e| {
+                            payment_methods::PaymentMethodCustomerMigrate::from((
+                                e.clone(),
+                                merchant_id.clone(),
+                            ))
+                        })
                         .collect(),
                     merchant_context.clone(),
                 )
diff --git a/crates/router/src/types/api/payment_methods.rs b/crates/router/src/types/api/payment_methods.rs
index 4c094af5acc..0c29b58cbb6 100644
--- a/crates/router/src/types/api/payment_methods.rs
+++ b/crates/router/src/types/api/payment_methods.rs
@@ -1,30 +1,32 @@
 #[cfg(feature = "v2")]
 pub use api_models::payment_methods::{
     CardDetail, CardDetailFromLocker, CardDetailsPaymentMethod, CardNetworkTokenizeRequest,
-    CardNetworkTokenizeResponse, CardType, CustomerPaymentMethodResponseItem,
-    DeleteTokenizeByTokenRequest, GetTokenizePayloadRequest, GetTokenizePayloadResponse,
-    ListCountriesCurrenciesRequest, MigrateCardDetail, NetworkTokenDetailsPaymentMethod,
-    NetworkTokenDetailsResponse, NetworkTokenResponse, PaymentMethodCollectLinkRenderRequest,
-    PaymentMethodCollectLinkRequest, PaymentMethodCreate, PaymentMethodCreateData,
-    PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodIntentConfirm,
-    PaymentMethodIntentCreate, PaymentMethodListData, PaymentMethodListRequest,
-    PaymentMethodListResponseForSession, PaymentMethodMigrate, PaymentMethodMigrateResponse,
-    PaymentMethodResponse, PaymentMethodResponseData, PaymentMethodUpdate, PaymentMethodUpdateData,
-    PaymentMethodsData, TokenDataResponse, TokenDetailsResponse, TokenizePayloadEncrypted,
-    TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1,
-    TokenizedWalletValue2, TotalPaymentMethodCountResponse,
+    CardNetworkTokenizeResponse, CardType, ConnectorCustomerDetails,
+    CustomerPaymentMethodResponseItem, DeleteTokenizeByTokenRequest, GetTokenizePayloadRequest,
+    GetTokenizePayloadResponse, ListCountriesCurrenciesRequest, MigrateCardDetail,
+    NetworkTokenDetailsPaymentMethod, NetworkTokenDetailsResponse, NetworkTokenResponse,
+    PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest, PaymentMethodCreate,
+    PaymentMethodCreateData, PaymentMethodCustomerMigrate, PaymentMethodDeleteResponse,
+    PaymentMethodId, PaymentMethodIntentConfirm, PaymentMethodIntentCreate, PaymentMethodListData,
+    PaymentMethodListRequest, PaymentMethodListResponseForSession, PaymentMethodMigrate,
+    PaymentMethodMigrateResponse, PaymentMethodResponse, PaymentMethodResponseData,
+    PaymentMethodUpdate, PaymentMethodUpdateData, PaymentMethodsData, TokenDataResponse,
+    TokenDetailsResponse, TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1,
+    TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2,
+    TotalPaymentMethodCountResponse,
 };
 #[cfg(feature = "v1")]
 pub use api_models::payment_methods::{
     CardDetail, CardDetailFromLocker, CardDetailsPaymentMethod, CardNetworkTokenizeRequest,
-    CardNetworkTokenizeResponse, CustomerPaymentMethod, CustomerPaymentMethodsListResponse,
-    DefaultPaymentMethod, DeleteTokenizeByTokenRequest, GetTokenizePayloadRequest,
-    GetTokenizePayloadResponse, ListCountriesCurrenciesRequest, MigrateCardDetail,
-    PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest, PaymentMethodCreate,
-    PaymentMethodCreateData, PaymentMethodDeleteResponse, PaymentMethodId,
-    PaymentMethodListRequest, PaymentMethodListResponse, PaymentMethodMigrate,
-    PaymentMethodMigrateResponse, PaymentMethodResponse, PaymentMethodUpdate, PaymentMethodsData,
-    TokenizeCardRequest, TokenizeDataRequest, TokenizePayloadEncrypted, TokenizePayloadRequest,
+    CardNetworkTokenizeResponse, ConnectorCustomerDetails, CustomerPaymentMethod,
+    CustomerPaymentMethodsListResponse, DefaultPaymentMethod, DeleteTokenizeByTokenRequest,
+    GetTokenizePayloadRequest, GetTokenizePayloadResponse, ListCountriesCurrenciesRequest,
+    MigrateCardDetail, PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest,
+    PaymentMethodCreate, PaymentMethodCreateData, PaymentMethodCustomerMigrate,
+    PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodListRequest,
+    PaymentMethodListResponse, PaymentMethodMigrate, PaymentMethodMigrateResponse,
+    PaymentMethodResponse, PaymentMethodUpdate, PaymentMethodsData, TokenizeCardRequest,
+    TokenizeDataRequest, TokenizePayloadEncrypted, TokenizePayloadRequest,
     TokenizePaymentMethodRequest, TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1,
     TokenizedWalletValue2,
 };

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [x] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
This PR introduces populating `connector_customer` field in `customers` table during payment method migration.

### Additional Changes

- [x] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
Allows merchants to populate `connector_customer` per `merchant_connector_id` based on `connector_customer_id` passed in the request.

## How did you test it?

Steps for testing -

1. Create a merchant account

2. Create a merchant's connector account

<details>
    <summary>3. Run /migrate-form API</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payment_methods/migrate-batch' \
        --header 'api-key: test_admin' \
        --form 'merchant_id="merchant_1749642145"' \
        --form 'merchant_connector_id="mca_llNSJsE2tTVdpfrb6n3x"' \
        --form 'file=@"/Users/test/Downloads/migrate_sample.csv"'

Response

    [{"line_number":1,"customer_id":"customerReference-43","migration_status":"Failed","migration_error":"{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"IR_16\",\"message\":\"Card Expired\"}}","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":null,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":2,"customer_id":"customerReference-44","migration_status":"Failed","migration_error":"{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"IR_16\",\"message\":\"Card Expired\"}}","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":null,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":3,"customer_id":"customerReference-45","migration_status":"Failed","migration_error":"{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"IR_16\",\"message\":\"Card Expired\"}}","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":null,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":4,"customer_id":"customerReference-46","migration_status":"Failed","migration_error":"{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"IR_16\",\"message\":\"Card Expired\"}}","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":null,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":5,"customer_id":"customerReference-47","migration_status":"Failed","migration_error":"{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"IR_16\",\"message\":\"Card Expired\"}}","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":null,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":6,"payment_method_id":"pm_YQMH2vTIIhT0dDwIz2wu","payment_method":"card","payment_method_type":"credit","customer_id":"customerReference-48","migration_status":"Success","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":true},{"line_number":7,"payment_method_id":"pm_6ir1orxpyF5wy4kkOYwh","payment_method":"card","payment_method_type":"credit","customer_id":"customerReference-49","migration_status":"Success","card_number_masked":"","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":true},{"line_number":8,"customer_id":"customerReference-50","migration_status":"Failed","migration_error":"{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"IR_07\",\"message\":\"Invalid value provided: card_exp_month\"}}","card_number_masked":"","card_migrated":null,"network_token_migrated":null,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":9,"payment_method_id":"pm_uMqDIb7yeJL6cKDXHOm0","payment_method":"card","payment_method_type":"credit","customer_id":"customerReference-51","migration_status":"Success","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":10,"customer_id":"customerReference-69","migration_status":"Failed","migration_error":"{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"IR_16\",\"message\":\"Card Expired\"}}","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":null,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":11,"customer_id":"customerReference-89","migration_status":"Failed","migration_error":"{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"IR_16\",\"message\":\"Invalid Expiry Year\"}}","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":null,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":12,"customer_id":"customerReference-99","migration_status":"Failed","migration_error":"{\"error\":{\"type\":\"invalid_request_error\",\"code\":\"IR_16\",\"message\":\"Invalid Expiry Year\"}}","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":null,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null},{"line_number":13,"payment_method_id":"pm_CuESKjB4r0rosTccLRMV","payment_method":"card","payment_method_type":"credit","customer_id":"customerReference-52","migration_status":"Success","card_number_masked":"420000XXXXXX0000","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":null,"network_transaction_id_migrated":null}]

</details>

<details>
    <summary>Look for connector_customer in customers table</summary>

<img width="587" alt="Screenshot 2025-06-11 at 10 08 27 PM" src="https://github.com/user-attachments/assets/21247f0f-2998-4fd3-8ccb-a77a15a25f95" />


</details>

<details>
    <summary>Find CSV data below</summary>

    name,email,phone,phone_country_code,customer_id,subscription_id,payment_method,payment_method_type,nick_name,payment_instrument_id,raw_card_number,card_number_masked,card_expiry_month,card_expiry_year,card_scheme,network_token_number,network_token_requestor_ref_id,network_token_expiry_month,network_token_expiry_year,original_transaction_id,billing_address_zip,billing_address_state,billing_address_first_name,billing_address_last_name,billing_address_city,billing_address_country,billing_address_line1,billing_address_line2,connector_customer_id,merchant_connector_id
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-43,orderReference-20240930-220107,card,credit,real joe,instructionReference-20240930-220107-S,4200000000000000,420000XXXXXX0000,1,2025,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2025,60720116005060,530004,Oman,joey,joey,Oman,,st mark,oman,1,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-44,orderReference-20240930-220107,card,credit,real joe,instructionReference-20240930-220107-S,4200000000000000,420000XXXXXX0000,1,2025,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2025,60720116005060,530004,Oman,joey,joey,Oman,,st mark,oman,2,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-45,orderReference-20240930-220107,card,credit,real joe,,4200000000000000,420000XXXXXX0000,1,2025,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2025,60720116005060,530004,Oman,joey,joey,Oman,,st mark,oman,3,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-46,orderReference-20240930-220107,card,credit,real joe,,4200000000000000,420000XXXXXX0000,1,2025,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2025,60720116005060,530004,Oman,joey,joey,Oman,,st mark,oman,4,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-47,orderReference-20240930-220107,card,credit,real joe,,4200000000000000,420000XXXXXX0000,1,2025,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2025,60720116005060,530004,Oman,joey,joey,Oman,,st mark,oman,5,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-48,orderReference-20240930-220107,card,credit,real joe,,,420000XXXXXX0000,1,2025,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2025,60720116005060,530004,Oman,joey,joey,Oman,,st mark,oman,7,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-49,orderReference-20240930-220107,card,credit,real joe,,,,1,2025,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2025,60720116005060,530004,Oman,joey,joey,Oman,,st mark,oman,6,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-50,orderReference-20240930-220107,card,credit,real joe,,,,,,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2025,60720116005060,530004,Oman,joey,joey,Oman,,st mark,oman,8,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-51,orderReference-20240930-220107,card,credit,real joe,,,420000XXXXXX0000,1,25,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2025,,530004,Oman,joey,joey,Oman,,st mark,oman,9,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-69,orderReference-20240930-220107,card,credit,real joe,,4200000000000000,420000XXXXXX0000,1,25,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2022,,530004,Oman,joey,joey,Oman,,st mark,oman,10,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-89,orderReference-20240930-220107,card,credit,real joe,,4200000000000000,420000XXXXXX0000,1,22,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2022,,530004,Oman,joey,joey,Oman,,st mark,oman,11,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-99,orderReference-20240930-220107,card,credit,real joe,,4200000000000000,420000XXXXXX0000,1,2022,,5595300359062662,RR16qBRBV-00a00a20uBPVV-18sNRV20uFBTTD22w18s12mRR18sZ00000334051,1,2022,,530004,Oman,joey,joey,Oman,,st mark,oman,12,mca_llNSJsE2tTVdpfrb6n3x
    Max Mustermann,noreply+20240930-220107@gmail.com,,,customerReference-52,orderReference-20240930-220107,card,credit,real joe,,,420000XXXXXX0000,1,25,,,,,,,530004,Oman,joey,joey,Oman,,st mark,oman,13,mca_llNSJsE2tTVdpfrb6n3x

</details>



## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Added support for associating connector-specific customer details with customers during creation and migration.
  - Enhanced customer migration to include connector customer information when available.

- **Bug Fixes**
  - Updated customer creation and migration flows to handle new customer details without disrupting existing functionality.

- **Chores**
  - Improved internal handling of customer data to support future extensibility with connector-specific fields.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->