Bug: Add support for passing the domain dynamically in the session call



Now the merchant domain is being collected while enabling apple pay for a particular merchant connector account.
Instead this pr is to add support to accept the merchant domain dynamically in the session call.

As this is a test feature we have a fallback in which if the session call fails with the dynamic domain then it will be retried with the domain configured for the mca.

diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs
index c04af204493..619f345a333 100644
--- a/crates/api_models/src/payments.rs
+++ b/crates/api_models/src/payments.rs
@@ -745,6 +745,7 @@ pub struct HeaderPayload {
     pub x_hs_latency: Option<bool>,
     pub browser_name: Option<api_enums::BrowserName>,
     pub x_client_platform: Option<api_enums::ClientPlatform>,
+    pub x_merchant_domain: Option<String>,
 }
 
 impl HeaderPayload {
diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs
index 883203ad99e..4441e349d9f 100644
--- a/crates/router/src/core/payments/flows/session_flow.rs
+++ b/crates/router/src/core/payments/flows/session_flow.rs
@@ -210,6 +210,7 @@ async fn create_applepay_session_token(
             apple_pay_merchant_cert_key,
             apple_pay_merchant_identifier,
             merchant_business_country,
+            merchant_configured_domain_optional,
         ) = match apple_pay_metadata {
             payment_types::ApplepaySessionTokenMetadata::ApplePayCombined(
                 apple_pay_combined_metadata,
@@ -232,7 +233,7 @@ async fn create_applepay_session_token(
 
                     let apple_pay_session_request = get_session_request_for_simplified_apple_pay(
                         merchant_identifier.clone(),
-                        session_token_data,
+                        session_token_data.clone(),
                     );
 
                     let apple_pay_merchant_cert = state
@@ -256,6 +257,7 @@ async fn create_applepay_session_token(
                         apple_pay_merchant_cert_key,
                         merchant_identifier,
                         merchant_business_country,
+                        Some(session_token_data.initiative_context),
                     )
                 }
                 payment_types::ApplePayCombinedMetadata::Manual {
@@ -264,8 +266,10 @@ async fn create_applepay_session_token(
                 } => {
                     logger::info!("Apple pay manual flow");
 
-                    let apple_pay_session_request =
-                        get_session_request_for_manual_apple_pay(session_token_data.clone());
+                    let apple_pay_session_request = get_session_request_for_manual_apple_pay(
+                        session_token_data.clone(),
+                        header_payload.x_merchant_domain.clone(),
+                    );
 
                     let merchant_business_country = session_token_data.merchant_business_country;
 
@@ -276,6 +280,7 @@ async fn create_applepay_session_token(
                         session_token_data.certificate_keys,
                         session_token_data.merchant_identifier,
                         merchant_business_country,
+                        session_token_data.initiative_context,
                     )
                 }
             },
@@ -284,6 +289,7 @@ async fn create_applepay_session_token(
 
                 let apple_pay_session_request = get_session_request_for_manual_apple_pay(
                     apple_pay_metadata.session_token_data.clone(),
+                    header_payload.x_merchant_domain.clone(),
                 );
 
                 let merchant_business_country = apple_pay_metadata
@@ -299,6 +305,7 @@ async fn create_applepay_session_token(
                         .clone(),
                     apple_pay_metadata.session_token_data.merchant_identifier,
                     merchant_business_country,
+                    apple_pay_metadata.session_token_data.initiative_context,
                 )
             }
         };
@@ -383,9 +390,9 @@ async fn create_applepay_session_token(
                     .attach_printable("Failed to obtain apple pay session request")?;
                 let applepay_session_request = build_apple_pay_session_request(
                     state,
-                    apple_pay_session_request,
-                    apple_pay_merchant_cert,
-                    apple_pay_merchant_cert_key,
+                    apple_pay_session_request.clone(),
+                    apple_pay_merchant_cert.clone(),
+                    apple_pay_merchant_cert_key.clone(),
                 )?;
 
                 let response = services::call_connector_api(
@@ -395,10 +402,43 @@ async fn create_applepay_session_token(
                 )
                 .await;
 
-                // logging the error if present in session call response
-                log_session_response_if_error(&response);
+                let updated_response = match (
+                    response.as_ref().ok(),
+                    header_payload.x_merchant_domain.clone(),
+                ) {
+                    (Some(Err(error)), Some(_)) => {
+                        logger::error!(
+                            "Retry apple pay session call with the merchant configured domain {error:?}"
+                        );
+                        let merchant_configured_domain = merchant_configured_domain_optional
+                            .ok_or(errors::ApiErrorResponse::InternalServerError)
+                            .attach_printable(
+                                "Failed to get initiative_context for apple pay session call retry",
+                            )?;
+                        let apple_pay_retry_session_request =
+                            payment_types::ApplepaySessionRequest {
+                                initiative_context: merchant_configured_domain,
+                                ..apple_pay_session_request
+                            };
+                        let applepay_retry_session_request = build_apple_pay_session_request(
+                            state,
+                            apple_pay_retry_session_request,
+                            apple_pay_merchant_cert,
+                            apple_pay_merchant_cert_key,
+                        )?;
+                        services::call_connector_api(
+                            state,
+                            applepay_retry_session_request,
+                            "create_apple_pay_session_token",
+                        )
+                        .await
+                    }
+                    _ => response,
+                };
 
-                response
+                // logging the error if present in session call response
+                log_session_response_if_error(&updated_response);
+                updated_response
                     .ok()
                     .and_then(|apple_pay_res| {
                         apple_pay_res
@@ -454,6 +494,7 @@ fn get_session_request_for_simplified_apple_pay(
 
 fn get_session_request_for_manual_apple_pay(
     session_token_data: payment_types::SessionTokenInfo,
+    merchant_domain: Option<String>,
 ) -> RouterResult<payment_types::ApplepaySessionRequest> {
     let initiative_context = session_token_data
         .initiative_context
@@ -463,7 +504,7 @@ fn get_session_request_for_manual_apple_pay(
         merchant_identifier: session_token_data.merchant_identifier.clone(),
         display_name: session_token_data.display_name.clone(),
         initiative: session_token_data.initiative.to_string(),
-        initiative_context,
+        initiative_context: merchant_domain.unwrap_or(initiative_context),
     })
 }
 
diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs
index 69a76be5bc0..38000f7b664 100644
--- a/crates/router/src/lib.rs
+++ b/crates/router/src/lib.rs
@@ -79,6 +79,7 @@ pub mod headers {
     pub const CONTENT_LENGTH: &str = "Content-Length";
     pub const BROWSER_NAME: &str = "x-browser-name";
     pub const X_CLIENT_PLATFORM: &str = "x-client-platform";
+    pub const X_MERCHANT_DOMAIN: &str = "x-merchant-domain";
 }
 
 pub mod pii {
diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs
index 9a42916f68d..d32f62ed0be 100644
--- a/crates/router/src/types/transformers.rs
+++ b/crates/router/src/types/transformers.rs
@@ -21,7 +21,7 @@ use super::domain;
 use crate::{
     core::errors,
     headers::{
-        BROWSER_NAME, X_CLIENT_PLATFORM, X_CLIENT_SOURCE, X_CLIENT_VERSION,
+        BROWSER_NAME, X_CLIENT_PLATFORM, X_CLIENT_SOURCE, X_CLIENT_VERSION, X_MERCHANT_DOMAIN,
         X_PAYMENT_CONFIRM_SOURCE,
     },
     services::authentication::get_header_value_by_key,
@@ -1157,6 +1157,9 @@ impl ForeignTryFrom<&HeaderMap> for payments::HeaderPayload {
                     .unwrap_or(api_enums::ClientPlatform::Unknown)
             });
 
+        let x_merchant_domain =
+            get_header_value_by_key(X_MERCHANT_DOMAIN.into(), headers)?.map(|val| val.to_string());
+
         Ok(Self {
             payment_confirm_source,
             client_source,
@@ -1164,6 +1167,7 @@ impl ForeignTryFrom<&HeaderMap> for payments::HeaderPayload {
             x_hs_latency: Some(x_hs_latency),
             browser_name,
             x_client_platform,
+            x_merchant_domain,
         })
     }
 }

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

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

## Description
<!-- Describe your changes in detail -->
Now the merchant domain is being collected while enabling apple pay for a particular merchant connector account.
Instead this pr is to add support to accept the merchant domain dynamically in the session call.

As this is a test feature we have a fallback in which if the session call fails with the dynamic domain then it will be retried with the domain configured for the mca.

### 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 merchant connector account with apple pay enabled
-> Create a payment with confirm false
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_b7ym5n82JbU8NDduwRHW8QlBYHMR6rXEOCa7m6vEmPImmKbAvZWbfDkVffKIGkv3' \
--data '{
  "amount": 10000,
  "currency": "USD",
  "capture_method": "automatic",
  "authentication_type": "no_three_ds",
  "confirm": false,
  "customer_id": "test_1"
}'
```
-> Make a session call with the domain passed in header
```
curl --location 'http://localhost:8080/payments/session_tokens' \
--header 'Content-Type: application/json' \
--header 'x-merchant-domain: sdk-test-app.netlify.app' \
--header 'api-key:' \
--data '{
    "payment_id": "pay_W9jZdVDlhrpkZdl56Woq",
    "wallets": [],
    "client_secret": "pay_W9jZdVDlhrpkZdl56Woq_secret_Jf9bWFRNA1ebDcWz2IwN"
}'
```
<img width="1127" alt="image" src="https://github.com/user-attachments/assets/6cb3d4ee-e9b6-40b6-bf0a-e59238cecb37">

-> Provide a wrong domain in the header. The session call will still get succeeded with retry
```
curl --location 'http://localhost:8080/payments/session_tokens' \
--header 'Content-Type: application/json' \
--header 'x-merchant-domain: hyperswitch-demo-store.netlify.app' \
--header 'api-key: pk_dev_97c8390e78a54e178d30b1f28bf87743' \
--data '{
    "payment_id": "pay_VNOvTIkIdl4pA2OWCCjV",
    "wallets": [],
    "client_secret": "pay_VNOvTIkIdl4pA2OWCCjV_secret_WeQ8F31Mjvddn91oYZuG"
}'
```
<img width="1158" alt="image" src="https://github.com/user-attachments/assets/fe0d7067-dd5c-4525-82f5-3f0d31c20476">




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