Bug: use the saved billing details in the recurring payments



Currently when a payment method is the billing details will also get saved in the payment method table.
But when a recurring payment is performed with the saved payment method, it is expected that the billing details to be passed in the current payment as well instead of using already saved billing details.

This pr contains the changes to use the saved payment method billing details during the recurring payment.
diff --git a/crates/hyperswitch_domain_models/src/payment_address.rs b/crates/hyperswitch_domain_models/src/payment_address.rs
index d4f2cf9f350..548dd0c5416 100644
--- a/crates/hyperswitch_domain_models/src/payment_address.rs
+++ b/crates/hyperswitch_domain_models/src/payment_address.rs
@@ -53,6 +53,7 @@ impl PaymentAddress {
     }
 
     /// Unify the billing details from `payment_method_data.[payment_method_data].billing details`.
+    /// Here the fields passed in payment_method_data_billing takes precedence
     pub fn unify_with_payment_method_data_billing(
         self,
         payment_method_data_billing: Option<Address>,
@@ -72,6 +73,29 @@ impl PaymentAddress {
         }
     }
 
+    /// Unify the billing details from `payment_method_data.[payment_method_data].billing details`.
+    /// Here the `self` takes precedence
+    pub fn unify_with_payment_data_billing(
+        self,
+        other_payment_method_billing: Option<Address>,
+    ) -> Self {
+        let unified_payment_method_billing = self
+            .get_payment_method_billing()
+            .map(|payment_method_billing| {
+                payment_method_billing
+                    .clone()
+                    .unify_address(other_payment_method_billing.as_ref())
+            })
+            .or(other_payment_method_billing);
+
+        Self {
+            shipping: self.shipping,
+            billing: self.billing,
+            unified_payment_method_billing,
+            payment_method_billing: self.payment_method_billing,
+        }
+    }
+
     pub fn get_request_payment_method_billing(&self) -> Option<&Address> {
         self.payment_method_billing.as_ref()
     }
diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs
index 9182bf93606..24f5ffdca9a 100644
--- a/crates/router/src/core/payments/transformers.rs
+++ b/crates/router/src/core/payments/transformers.rs
@@ -42,7 +42,7 @@ pub async fn construct_payment_router_data<'a, F, T>(
     payment_data: PaymentData<F>,
     connector_id: &str,
     merchant_account: &domain::MerchantAccount,
-    _key_store: &domain::MerchantKeyStore,
+    key_store: &domain::MerchantKeyStore,
     customer: &'a Option<domain::Customer>,
     merchant_connector_account: &helpers::MerchantConnectorAccountType,
     merchant_recipient_data: Option<types::MerchantRecipientData>,
@@ -135,6 +135,26 @@ where
         Some(merchant_connector_account),
     );
 
+    let unified_address =
+        if let Some(payment_method_info) = payment_data.payment_method_info.clone() {
+            let payment_method_billing =
+                crate::core::payment_methods::cards::decrypt_generic_data::<Address>(
+                    state,
+                    payment_method_info.payment_method_billing_address,
+                    key_store,
+                )
+                .await
+                .attach_printable("unable to decrypt payment method billing address details")?;
+            payment_data
+                .address
+                .clone()
+                .unify_with_payment_data_billing(payment_method_billing)
+        } else {
+            payment_data.address
+        };
+
+    crate::logger::debug!("unified address details {:?}", unified_address);
+
     router_data = types::RouterData {
         flow: PhantomData,
         merchant_id: merchant_account.get_id().clone(),
@@ -147,7 +167,7 @@ where
         connector_auth_type: auth_type,
         description: payment_data.payment_intent.description.clone(),
         return_url: payment_data.payment_intent.return_url.clone(),
-        address: payment_data.address.clone(),
+        address: unified_address,
         auth_type: payment_data
             .payment_attempt
             .authentication_type

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

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

## Description
<!-- Describe your changes in detail -->
Currently when a payment method is the billing details will also get saved in the payment method table.
But when a recurring payment is performed with the saved payment method, it is expected that the billing details to be passed in the current payment as well instead of using already saved billing details.

This pr contains the changes to use the saved payment method billing details during the recurring payment.

### Additional Changes

- [ ] 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
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
-> Create mca for cybersource
-> Create a payment to save a card
```
{
    "amount": 100,
    "amount_to_capture": 100,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "customer_id": "cu_{{$timestamp}}",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "setup_future_usage": "on_session",
    "return_url": "http://127.0.0.1:4040",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4000000000000119",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "838"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX",
            "last_name": "ss"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "browser_info": {
        "user_agent": "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/70.0.3538.110 Safari\/537.36",
        "accept_header": "text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,image\/apng,*\/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "125.0.0.1"
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
```
```
{
    "payment_id": "pay_UEWLBw1G1wBgvsfuA7WB",
    "merchant_id": "merchant_1723633906",
    "status": "succeeded",
    "amount": 100,
    "net_amount": 100,
    "amount_capturable": 0,
    "amount_received": 100,
    "connector": "cybersource",
    "client_secret": "pay_UEWLBw1G1wBgvsfuA7WB_secret_yZ2lHGDhVFBq7u77qjCJ",
    "created": "2024-08-14T11:11:51.983Z",
    "currency": "USD",
    "customer_id": "cu_1723633912",
    "customer": {
        "id": "cu_1723633912",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "on_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "0119",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "400000",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": null,
            "payment_checks": {
                "avs_response": {
                    "code": "Y",
                    "codeRaw": "Y"
                },
                "card_verification": null
            },
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": "token_8CYTus4XuN7WktyT3X7i",
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null
        },
        "phone": null,
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": "ss"
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "http://127.0.0.1:4040/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "cu_1723633912",
        "created_at": 1723633911,
        "expires": 1723637511,
        "secret": "epk_5ba7fd4213f54f659bf419e626089c5c"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "7236339122516836903955",
    "frm_message": null,
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_UEWLBw1G1wBgvsfuA7WB_1",
    "payment_link": null,
    "profile_id": "pro_DYzvILr3A14CyJsK6xkr",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_FARwzfcWZdCdzslixC5L",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-08-14T11:26:51.983Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "125.0.0.1",
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "color_depth": 24,
        "java_enabled": true,
        "screen_width": 1536,
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "screen_height": 723,
        "java_script_enabled": true
    },
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-08-14T11:11:53.399Z",
    "charges": null,
    "frm_metadata": null,
    "merchant_order_reference_id": null
}
```
-> List payment methods for a customer
```
{
  "amount": 100000,
  "currency": "USD",
  "capture_method": "automatic",
  "authentication_type": "no_three_ds",
  "confirm": false,
  "customer_id": "cu_1723633912"
}
```
```
{
    "payment_id": "pay_7vbNGGq2n3W1S0OCN66m",
    "merchant_id": "merchant_1723633906",
    "status": "requires_payment_method",
    "amount": 100000,
    "net_amount": 100000,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": null,
    "client_secret": "pay_7vbNGGq2n3W1S0OCN66m_secret_Nm8uMOZFEkoRT87UKugm",
    "created": "2024-08-14T11:12:49.616Z",
    "currency": "USD",
    "customer_id": "cu_1723633912",
    "customer": {
        "id": "cu_1723633912",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": null,
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": null,
    "payment_method_data": null,
    "payment_token": null,
    "shipping": null,
    "billing": null,
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": null,
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "cu_1723633912",
        "created_at": 1723633969,
        "expires": 1723637569,
        "secret": "epk_35d19936f46b44229892ff22e1976740"
    },
    "manual_retry_allowed": null,
    "connector_transaction_id": null,
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_DYzvILr3A14CyJsK6xkr",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": null,
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-08-14T11:27:49.616Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-08-14T11:12:49.626Z",
    "charges": null,
    "frm_metadata": null,
    "merchant_order_reference_id": null
}
```
```
curl --location 'http://localhost:8080/customers/payment_methods?client_secret=pay_7vbNGGq2n3W1S0OCN66m_secret_Nm8uMOZFEkoRT87UKugm' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_b099e8ed348440b79d39fdfbb3f393dd'
```
```
{
    "customer_payment_methods": [
        {
            "payment_token": "token_Abm2R8KFhP2N02lWMxWg",
            "payment_method_id": "pm_O4GweV6awC1FW9KvgHCu",
            "customer_id": "cu_1723633912",
            "payment_method": "card",
            "payment_method_type": null,
            "payment_method_issuer": null,
            "payment_method_issuer_code": null,
            "recurring_enabled": false,
            "installment_payment_enabled": false,
            "payment_experience": [
                "redirect_to_url"
            ],
            "card": {
                "scheme": null,
                "issuer_country": null,
                "last4_digits": "0119",
                "expiry_month": "03",
                "expiry_year": "2030",
                "card_token": null,
                "card_holder_name": "joseph Doe",
                "card_fingerprint": null,
                "nick_name": null,
                "card_network": null,
                "card_isin": "400000",
                "card_issuer": null,
                "card_type": null,
                "saved_to_locker": true
            },
            "metadata": null,
            "created": "2024-08-14T11:11:53.436Z",
            "bank": null,
            "surcharge_details": null,
            "requires_cvv": true,
            "last_used_at": "2024-08-14T11:12:35.615Z",
            "default_payment_method_set": true,
            "billing": {
                "address": {
                    "city": "San Fransico",
                    "country": "US",
                    "line1": "1467",
                    "line2": "Harrison Street",
                    "line3": "Harrison Street",
                    "zip": "94122",
                    "state": "California",
                    "first_name": "joseph",
                    "last_name": "Doe"
                },
                "phone": null,
                "email": null
            }
        }
    ],
    "is_guest_customer": false
}
```
-> Confirm the payment with the token by without passing the billing details
```
curl --location 'http://localhost:8080/payments/pay_8Zx3ByOLe2LKJk80ovSO/confirm' \
--header 'api-key: pk_dev_c4ad2f0ffc704baf8c845f9463096d9d' \
--header 'Content-Type: application/json' \
--data '{
    "payment_token": "token_JieHCOogYw7eZxhAkooU",
    "client_secret": "pay_8Zx3ByOLe2LKJk80ovSO_secret_lF641suBegY0vA9BQ3wx",
    "payment_method": "card"
}'
```
```
{
    "payment_id": "pay_8Zx3ByOLe2LKJk80ovSO",
    "merchant_id": "merchant_1723638884",
    "status": "succeeded",
    "amount": 100000,
    "net_amount": 100000,
    "amount_capturable": 0,
    "amount_received": 100000,
    "connector": "cybersource",
    "client_secret": "pay_8Zx3ByOLe2LKJk80ovSO_secret_lF641suBegY0vA9BQ3wx",
    "created": "2024-08-14T12:35:02.511Z",
    "currency": "USD",
    "customer_id": "cu_1723638892",
    "customer": {
        "id": "cu_1723638892",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": null,
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": null,
    "payment_method_data": null,
    "payment_token": "token_JieHCOogYw7eZxhAkooU",
    "shipping": null,
    "billing": null,
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": null,
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": null,
    "manual_retry_allowed": false,
    "connector_transaction_id": "7236389224576123203954",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_8Zx3ByOLe2LKJk80ovSO_1",
    "payment_link": null,
    "profile_id": "pro_JzHXSgyHYbzaxsycvvjC",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_33pcEQPsIl5PumUlPxEb",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-08-14T12:50:02.511Z",
    "fingerprint": null,
    "browser_info": {
        "language": null,
        "time_zone": null,
        "ip_address": "::1",
        "user_agent": null,
        "color_depth": null,
        "java_enabled": null,
        "screen_width": null,
        "accept_header": null,
        "screen_height": null,
        "java_script_enabled": null
    },
    "payment_method_id": "pm_rY1D25c9x9LyyT20xX8x",
    "payment_method_status": "active",
    "updated": "2024-08-14T12:35:23.640Z",
    "charges": null,
    "frm_metadata": null,
    "merchant_order_reference_id": null
}
```


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