Bug: [REFACTOR] allow option for skipping billing details during batched data migration



**Problem Statement**
Currently, `/migrate-batch` API has below mandatory parameters

```rust
{
    pub customer_id: id_type::CustomerId,
    pub nick_name: masking::Secret<String>,
    pub card_number_masked: masking::Secret<String>,
    pub card_expiry_month: masking::Secret<String>,
    pub card_expiry_year: masking::Secret<String>,
    pub billing_address_zip: masking::Secret<String>,
    pub billing_address_state: masking::Secret<String>,
    pub billing_address_first_name: masking::Secret<String>,
    pub billing_address_last_name: masking::Secret<String>,
    pub billing_address_city: String,
    pub billing_address_line1: masking::Secret<String>,
}
```

Billing details are mandatory but there can be use cases when these details are not be required by the underlying connector during payments. For such cases, these details become a blocker for using the migrate API. These can be made optional.


**Proposed Solution**
Fields related to billing details can be made `Optional` 
diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs
index 5ecd2f71ef7..25b583f5dc4 100644
--- a/crates/api_models/src/payment_methods.rs
+++ b/crates/api_models/src/payment_methods.rs
@@ -2481,7 +2481,7 @@ pub struct PaymentMethodRecord {
     pub merchant_id: Option<id_type::MerchantId>,
     pub payment_method: Option<api_enums::PaymentMethod>,
     pub payment_method_type: Option<api_enums::PaymentMethodType>,
-    pub nick_name: masking::Secret<String>,
+    pub nick_name: Option<masking::Secret<String>>,
     pub payment_instrument_id: Option<masking::Secret<String>>,
     pub connector_customer_id: Option<String>,
     pub card_number_masked: masking::Secret<String>,
@@ -2489,13 +2489,13 @@ pub struct PaymentMethodRecord {
     pub card_expiry_year: masking::Secret<String>,
     pub card_scheme: Option<String>,
     pub original_transaction_id: Option<String>,
-    pub billing_address_zip: masking::Secret<String>,
-    pub billing_address_state: masking::Secret<String>,
-    pub billing_address_first_name: masking::Secret<String>,
-    pub billing_address_last_name: masking::Secret<String>,
-    pub billing_address_city: String,
+    pub billing_address_zip: Option<masking::Secret<String>>,
+    pub billing_address_state: Option<masking::Secret<String>>,
+    pub billing_address_first_name: Option<masking::Secret<String>>,
+    pub billing_address_last_name: Option<masking::Secret<String>>,
+    pub billing_address_city: Option<String>,
     pub billing_address_country: Option<api_enums::CountryAlpha2>,
-    pub billing_address_line1: masking::Secret<String>,
+    pub billing_address_line1: Option<masking::Secret<String>>,
     pub billing_address_line2: Option<masking::Secret<String>>,
     pub billing_address_line3: Option<masking::Secret<String>>,
     pub raw_card_number: Option<masking::Secret<String>>,
@@ -2548,6 +2548,57 @@ pub enum MigrationStatus {
     Failed,
 }
 
+impl PaymentMethodRecord {
+    fn create_address(&self) -> Option<payments::AddressDetails> {
+        if self.billing_address_first_name.is_some()
+            && self.billing_address_line1.is_some()
+            && self.billing_address_zip.is_some()
+            && self.billing_address_city.is_some()
+            && self.billing_address_country.is_some()
+        {
+            Some(payments::AddressDetails {
+                city: self.billing_address_city.clone(),
+                country: self.billing_address_country,
+                line1: self.billing_address_line1.clone(),
+                line2: self.billing_address_line2.clone(),
+                state: self.billing_address_state.clone(),
+                line3: self.billing_address_line3.clone(),
+                zip: self.billing_address_zip.clone(),
+                first_name: self.billing_address_first_name.clone(),
+                last_name: self.billing_address_last_name.clone(),
+            })
+        } else {
+            None
+        }
+    }
+
+    fn create_phone(&self) -> Option<payments::PhoneDetails> {
+        if self.phone.is_some() || self.phone_country_code.is_some() {
+            Some(payments::PhoneDetails {
+                number: self.phone.clone(),
+                country_code: self.phone_country_code.clone(),
+            })
+        } else {
+            None
+        }
+    }
+
+    fn create_billing(&self) -> Option<payments::Address> {
+        let address = self.create_address();
+        let phone = self.create_phone();
+
+        if address.is_some() || phone.is_some() || self.email.is_some() {
+            Some(payments::Address {
+                address,
+                phone,
+                email: self.email.clone(),
+            })
+        } else {
+            None
+        }
+    }
+}
+
 #[cfg(feature = "v1")]
 type PaymentMethodMigrationResponseType = (
     Result<PaymentMethodMigrateResponse, String>,
@@ -2600,6 +2651,7 @@ impl
         ),
     ) -> Result<Self, Self::Error> {
         let (record, merchant_id, mca_id) = item;
+        let billing = record.create_billing();
 
         //  if payment instrument id is present then only construct this
         let connector_mandate_details = if record.payment_instrument_id.is_some() {
@@ -2631,7 +2683,7 @@ impl
                 card_type: None,
                 card_issuer: None,
                 card_issuing_country: None,
-                nick_name: Some(record.nick_name.clone()),
+                nick_name: record.nick_name.clone(),
             }),
             network_token: Some(MigrateNetworkTokenDetail {
                 network_token_data: MigrateNetworkTokenData {
@@ -2639,7 +2691,7 @@ impl
                     network_token_exp_month: record.network_token_expiry_month.unwrap_or_default(),
                     network_token_exp_year: record.network_token_expiry_year.unwrap_or_default(),
                     card_holder_name: record.name,
-                    nick_name: Some(record.nick_name.clone()),
+                    nick_name: record.nick_name.clone(),
                     card_issuing_country: None,
                     card_network: None,
                     card_issuer: None,
@@ -2652,24 +2704,7 @@ impl
             payment_method: record.payment_method,
             payment_method_type: record.payment_method_type,
             payment_method_issuer: None,
-            billing: Some(payments::Address {
-                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),
-                }),
-                phone: Some(payments::PhoneDetails {
-                    number: record.phone,
-                    country_code: record.phone_country_code,
-                }),
-                email: record.email,
-            }),
+            billing,
             connector_mandate_details: connector_mandate_details.map(
                 |payments_mandate_reference| {
                     CommonMandateReference::from(payments_mandate_reference)
@@ -2683,7 +2718,7 @@ impl
             #[cfg(feature = "payouts")]
             wallet: None,
             payment_method_data: None,
-            network_transaction_id: record.original_transaction_id,
+            network_transaction_id: record.original_transaction_id.clone(),
         })
     }
 }
@@ -2702,15 +2737,15 @@ impl From<(PaymentMethodRecord, id_type::MerchantId)> for PaymentMethodCustomerM
                 description: None,
                 phone_country_code: record.phone_country_code,
                 address: Some(payments::AddressDetails {
-                    city: Some(record.billing_address_city),
+                    city: record.billing_address_city,
                     country: record.billing_address_country,
-                    line1: Some(record.billing_address_line1),
+                    line1: record.billing_address_line1,
                     line2: record.billing_address_line2,
-                    state: Some(record.billing_address_state),
+                    state: 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),
+                    zip: record.billing_address_zip,
+                    first_name: record.billing_address_first_name,
+                    last_name: record.billing_address_last_name,
                 }),
                 metadata: None,
             },
diff --git a/crates/payment_methods/src/core/migration/payment_methods.rs b/crates/payment_methods/src/core/migration/payment_methods.rs
index 10808f08a25..5c30698f615 100644
--- a/crates/payment_methods/src/core/migration/payment_methods.rs
+++ b/crates/payment_methods/src/core/migration/payment_methods.rs
@@ -184,7 +184,7 @@ pub async fn populate_bin_details_for_masked_card(
         card_number.peek(),
     )
     .change_context(errors::ApiErrorResponse::InvalidRequestData {
-        message: "Invalid card number".to_string(),
+        message: "Invalid masked card number".to_string(),
     })?;
 
     let card_bin_details = if card_details.card_issuer.is_some()
@@ -223,7 +223,7 @@ impl
         let (card_isin, last4_digits) =
             get_card_bin_and_last4_digits_for_masked_card(card_details.card_number.peek())
                 .change_context(errors::ApiErrorResponse::InvalidRequestData {
-                    message: "Invalid card number".to_string(),
+                    message: "Invalid masked card number".to_string(),
                 })?;
         if let Some(card_bin_info) = card_info {
             Ok(Self {
@@ -298,7 +298,7 @@ impl
         let (card_isin, last4_digits) =
             get_card_bin_and_last4_digits_for_masked_card(card_details.card_number.peek())
                 .change_context(errors::ApiErrorResponse::InvalidRequestData {
-                    message: "Invalid card number".to_string(),
+                    message: "Invalid masked card number".to_string(),
                 })?;
         if let Some(card_bin_info) = card_info {
             Ok(Self {

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

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

## Description
This PR includes changes to make billing details related fields optional during batched operation of data 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 who use connectors which does not mandate address fields to be present during a payment txn to be able to migrate their data. Billing details will not be mandated during batched operation of data migration.

## How did you test it?
1. Create MCA

<details>
    <summary>2. Run batch migration</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payment_methods/migrate-batch' \
        --header 'api-key: test_admin' \
        --form 'merchant_id="merchant_1750764063"' \
        --form 'merchant_connector_id="mca_F0qqizsBSwbZ77gQYLRs"' \
        --form 'file=@"sample.csv"'

Response

    [{"line_number":1,"payment_method_id":"pm_wY7TUBMPZZISgu2Zf74M","payment_method":"card","payment_method_type":"debit","customer_id":"45CC60D1F2283097BB1DE41A2636414445908518D50B8B4CDC57959CD7FB4539","migration_status":"Success","card_number_masked":"4360000 xxxx 0005","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":2,"payment_method_id":"pm_gNTfSTNVhvlURJPnsiYQ","payment_method":"card","payment_method_type":"credit","customer_id":"782BC898951D910C1A99DA69A4E4FAD6CEE53AB9311FA2C552028DA89428DB7F","migration_status":"Success","card_number_masked":"4111111 xxxx 1111","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":3,"payment_method_id":"pm_EeJ2hgF3OmHgTFEfe58X","payment_method":"card","payment_method_type":"credit","customer_id":"C81CC958611A803D9CC3E2E7818A1691943E3143E6DF603A7F45E55BA7629938","migration_status":"Success","card_number_masked":"222300 xxxx 0010","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":4,"payment_method_id":"pm_p1ckEakoWB12EtHYtjtm","payment_method":"card","payment_method_type":"credit","customer_id":"9F4BB23B3ED615753F5A27CF6D75274713B30A67ACFEA7CE44B058F6AC044E33","migration_status":"Success","card_number_masked":"5555555 xxxx 4444","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":5,"payment_method_id":"pm_0HrpkOfnLuIPSJiU3fdZ","payment_method":"card","payment_method_type":"debit","customer_id":"B22BFDD8BB5840B5F6EE0026A430B72359C50E631C7A2D9D2BC014BDB1EB212C","migration_status":"Success","card_number_masked":"222300 xxxx 0010","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":6,"payment_method_id":"pm_iOtdYdcOErhUz4bXmZgX","payment_method":"card","payment_method_type":"credit","customer_id":"EECB88827D346EDCFB9AB7302B0C387C42FB5EBAD87EC0E4FA4301F6148BB22B","migration_status":"Success","card_number_masked":"557700 xxxx 0004","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":7,"payment_method_id":"pm_JnzwASYJStJUQ02LEcqf","payment_method":"card","payment_method_type":"credit","customer_id":"C81CC958611A803D9CC3E2E7818A1691943E3143E6DF603A7F45E55BA7629938","migration_status":"Success","card_number_masked":"222241 xxxx 0002","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":8,"payment_method_id":"pm_CJsKWoBsOqkRlikFMO2t","payment_method":"card","payment_method_type":"credit","customer_id":"66C96C852E423B701EF06C5109C40D72E77B4C7F4CDD108AD4FCC3DCCC38AB2F","migration_status":"Success","card_number_masked":"513029 xxxx 0009","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":9,"payment_method_id":"pm_z0jFnOfXn1andQp9qbXd","payment_method":"card","payment_method_type":"credit","customer_id":"C81CC958611A803D9CC3E2E7818A1691943E3143E6DF603A7F45E55BA7629938","migration_status":"Success","card_number_masked":"510322 xxxx 9245","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":10,"payment_method_id":"pm_4lJAPwuf8iepQCWKDub1","payment_method":"card","payment_method_type":"credit","customer_id":"C81CC958611A803D9CC3E2E7818A1691943E3143E6DF603A7F45E55BA7629938","migration_status":"Success","card_number_masked":"222241 xxxx 0002","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":11,"payment_method_id":"pm_J1ZMlj5lzHyf2P5Lid6S","payment_method":"card","payment_method_type":"credit","customer_id":"ADE2795AC3C88525528F0DB95677BE9DB27E792850B1A67CA654BFFAEB31AFE3","migration_status":"Success","card_number_masked":"222241 xxxx 0018","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":12,"payment_method_id":"pm_zySRb0JCNxcCMH2Ubocc","payment_method":"card","payment_method_type":"credit","customer_id":"ADE2795AC3C88525528F0DB95677BE9DB27E792850B1A67CA654BFFAEB31AFE3","migration_status":"Success","card_number_masked":"411111 xxxx 1111","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":13,"payment_method_id":"pm_M9wKyu4kEYNcHIjyrVfI","payment_method":"card","payment_method_type":"credit","customer_id":"D83D0437F8883C26C75C04756D95E21B90877020743EA1C1CC49883A90E8DF97","migration_status":"Success","card_number_masked":"411111 xxxx 1111","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":14,"payment_method_id":"pm_RDiPAalAZkCj1uOqKYSb","payment_method":"card","payment_method_type":"credit","customer_id":"ADE2795AC3C88525528F0DB95677BE9DB27E792850B1A67CA654BFFAEB31AFE3","migration_status":"Success","card_number_masked":"624303 xxxx 0001","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":15,"payment_method_id":"pm_0XTr9lrG6LGNl9d4iFPR","payment_method":"card","payment_method_type":"debit","customer_id":"ADE2795AC3C88525528F0DB95677BE9DB27E792850B1A67CA654BFFAEB31AFE3","migration_status":"Success","card_number_masked":"222241 xxxx 0002","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":16,"payment_method_id":"pm_aUtJIRDcCH7CIwJ2hQEk","payment_method":"card","payment_method_type":"credit","customer_id":"D83D0437F8883C26C75C04756D95E21B90877020743EA1C1CC49883A90E8DF97","migration_status":"Success","card_number_masked":"817199 xxxx 0000","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":17,"payment_method_id":"pm_THz5Ds8VHAgyJPPOA4GY","payment_method":"card","payment_method_type":"credit","customer_id":"D83D0437F8883C26C75C04756D95E21B90877020743EA1C1CC49883A90E8DF97","migration_status":"Success","card_number_masked":"464646 xxxx 4644","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":18,"payment_method_id":"pm_l82mYW4tMsRkJF4hq5fn","payment_method":"card","payment_method_type":"credit","customer_id":"ADE2795AC3C88525528F0DB95677BE9DB27E792850B1A67CA654BFFAEB31AFE3","migration_status":"Success","card_number_masked":"222240 xxxx 0007","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":19,"payment_method_id":"pm_E8fkAc9RHknoPmnYFBOa","payment_method":"card","payment_method_type":"debit","customer_id":"ADE2795AC3C88525528F0DB95677BE9DB27E792850B1A67CA654BFFAEB31AFE3","migration_status":"Success","card_number_masked":"400006 xxxx 0006","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":20,"payment_method_id":"pm_TEEJO7DdZTkl55PH8P1T","payment_method":"card","payment_method_type":"credit","customer_id":"D83D0437F8883C26C75C04756D95E21B90877020743EA1C1CC49883A90E8DF97","migration_status":"Success","card_number_masked":"497794 xxxx 9497","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":21,"payment_method_id":"pm_3gTcA4MJsTjasj2XlJs2","payment_method":"card","payment_method_type":"credit","customer_id":"C81CC958611A803D9CC3E2E7818A1691943E3143E6DF603A7F45E55BA7629938","migration_status":"Success","card_number_masked":"436000 xxxx 0005","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null},{"line_number":22,"payment_method_id":"pm_GzUYGU3GpOXOIkWFRkM0","payment_method":"card","payment_method_type":"credit","customer_id":"C80162F403CCA7AF1CB6732291D6D53B88636BB6A9B82D2CD43AC30775925B4F","migration_status":"Success","card_number_masked":"222241 xxxx 0002","card_migrated":null,"network_token_migrated":true,"connector_mandate_details_migrated":true,"network_transaction_id_migrated":null}]

</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
