Bug: Enable auto-retries for apple pay



Currently for apple pay pre-routing is done before the session call and a connector is decided based on the routing logic. Because of this we are not able retry the failed apple pay payments.

As the certificates use in the simplified flows are same across the connectors the failed apple pay payment can be retired with the other connectors with apple pay simplified flow configured. In order to achieve this we need to pass a list of apple pay simplified flow configured connectors and in pre routing.
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index 175400c54bf..9b2082cf31f 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -3170,6 +3170,28 @@ where
             {
                 routing_data.business_sub_label = choice.sub_label.clone();
             }
+
+            if payment_data.payment_attempt.payment_method_type
+                == Some(storage_enums::PaymentMethodType::ApplePay)
+            {
+                let retryable_connector_data = helpers::get_apple_pay_retryable_connectors(
+                    state,
+                    merchant_account,
+                    payment_data,
+                    key_store,
+                    connector_data.clone(),
+                    #[cfg(feature = "connector_choice_mca_id")]
+                    choice.merchant_connector_id.clone().as_ref(),
+                    #[cfg(not(feature = "connector_choice_mca_id"))]
+                    None,
+                )
+                .await?;
+
+                if let Some(connector_data_list) = retryable_connector_data {
+                    return Ok(ConnectorCallType::Retryable(connector_data_list));
+                }
+            }
+
             return Ok(ConnectorCallType::PreDetermined(connector_data));
         }
     }
diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs
index 2eb0c921bbf..76ff96c7021 100644
--- a/crates/router/src/core/payments/flows/session_flow.rs
+++ b/crates/router/src/core/payments/flows/session_flow.rs
@@ -122,36 +122,6 @@ fn is_dynamic_fields_required(
         .unwrap_or(false)
 }
 
-fn get_applepay_metadata(
-    connector_metadata: Option<common_utils::pii::SecretSerdeValue>,
-) -> RouterResult<payment_types::ApplepaySessionTokenMetadata> {
-    connector_metadata
-        .clone()
-        .parse_value::<api_models::payments::ApplepayCombinedSessionTokenData>(
-            "ApplepayCombinedSessionTokenData",
-        )
-        .map(|combined_metadata| {
-            api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
-                combined_metadata.apple_pay_combined,
-            )
-        })
-        .or_else(|_| {
-            connector_metadata
-                .parse_value::<api_models::payments::ApplepaySessionTokenData>(
-                    "ApplepaySessionTokenData",
-                )
-                .map(|old_metadata| {
-                    api_models::payments::ApplepaySessionTokenMetadata::ApplePay(
-                        old_metadata.apple_pay,
-                    )
-                })
-        })
-        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
-            field_name: "connector_metadata".to_string(),
-            expected_format: "applepay_metadata_format".to_string(),
-        })
-}
-
 fn build_apple_pay_session_request(
     state: &routes::AppState,
     request: payment_types::ApplepaySessionRequest,
@@ -196,7 +166,8 @@ async fn create_applepay_session_token(
         )
     } else {
         // Get the apple pay metadata
-        let apple_pay_metadata = get_applepay_metadata(router_data.connector_meta_data.clone())?;
+        let apple_pay_metadata =
+            helpers::get_applepay_metadata(router_data.connector_meta_data.clone())?;
 
         // Get payment request data , apple pay session request and merchant keys
         let (
@@ -213,6 +184,8 @@ async fn create_applepay_session_token(
                     payment_request_data,
                     session_token_data,
                 } => {
+                    logger::info!("Apple pay simplified flow");
+
                     let merchant_identifier = state
                         .conf
                         .applepay_merchant_configs
@@ -254,6 +227,8 @@ async fn create_applepay_session_token(
                     payment_request_data,
                     session_token_data,
                 } => {
+                    logger::info!("Apple pay manual flow");
+
                     let apple_pay_session_request =
                         get_session_request_for_manual_apple_pay(session_token_data.clone());
 
@@ -269,6 +244,8 @@ async fn create_applepay_session_token(
                 }
             },
             payment_types::ApplepaySessionTokenMetadata::ApplePay(apple_pay_metadata) => {
+                logger::info!("Apple pay manual flow");
+
                 let apple_pay_session_request = get_session_request_for_manual_apple_pay(
                     apple_pay_metadata.session_token_data.clone(),
                 );
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index eb3dca11060..06c8c7f2c94 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -3888,6 +3888,122 @@ pub fn validate_customer_access(
     Ok(())
 }
 
+pub fn is_apple_pay_simplified_flow(
+    connector_metadata: Option<pii::SecretSerdeValue>,
+) -> CustomResult<bool, errors::ApiErrorResponse> {
+    let apple_pay_metadata = get_applepay_metadata(connector_metadata)?;
+
+    Ok(match apple_pay_metadata {
+        api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
+            apple_pay_combined_metadata,
+        ) => match apple_pay_combined_metadata {
+            api_models::payments::ApplePayCombinedMetadata::Simplified { .. } => true,
+            api_models::payments::ApplePayCombinedMetadata::Manual { .. } => false,
+        },
+        api_models::payments::ApplepaySessionTokenMetadata::ApplePay(_) => false,
+    })
+}
+
+pub fn get_applepay_metadata(
+    connector_metadata: Option<pii::SecretSerdeValue>,
+) -> RouterResult<api_models::payments::ApplepaySessionTokenMetadata> {
+    connector_metadata
+        .clone()
+        .parse_value::<api_models::payments::ApplepayCombinedSessionTokenData>(
+            "ApplepayCombinedSessionTokenData",
+        )
+        .map(|combined_metadata| {
+            api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
+                combined_metadata.apple_pay_combined,
+            )
+        })
+        .or_else(|_| {
+            connector_metadata
+                .parse_value::<api_models::payments::ApplepaySessionTokenData>(
+                    "ApplepaySessionTokenData",
+                )
+                .map(|old_metadata| {
+                    api_models::payments::ApplepaySessionTokenMetadata::ApplePay(
+                        old_metadata.apple_pay,
+                    )
+                })
+        })
+        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
+            field_name: "connector_metadata".to_string(),
+            expected_format: "applepay_metadata_format".to_string(),
+        })
+}
+
+pub async fn get_apple_pay_retryable_connectors<F>(
+    state: AppState,
+    merchant_account: &domain::MerchantAccount,
+    payment_data: &mut PaymentData<F>,
+    key_store: &domain::MerchantKeyStore,
+    decided_connector_data: api::ConnectorData,
+    merchant_connector_id: Option<&String>,
+) -> CustomResult<Option<Vec<api::ConnectorData>>, errors::ApiErrorResponse>
+where
+    F: Send + Clone,
+{
+    let profile_id = &payment_data
+        .payment_intent
+        .profile_id
+        .clone()
+        .get_required_value("profile_id")
+        .change_context(errors::ApiErrorResponse::MissingRequiredField {
+            field_name: "profile_id",
+        })?;
+
+    let merchant_connector_account = get_merchant_connector_account(
+        &state,
+        merchant_account.merchant_id.as_str(),
+        payment_data.creds_identifier.to_owned(),
+        key_store,
+        profile_id, // need to fix this
+        &decided_connector_data.connector_name.to_string(),
+        merchant_connector_id,
+    )
+    .await?
+    .get_metadata();
+
+    let connector_data_list = if is_apple_pay_simplified_flow(merchant_connector_account)? {
+        let merchant_connector_account_list = state
+            .store
+            .find_merchant_connector_account_by_merchant_id_and_disabled_list(
+                merchant_account.merchant_id.as_str(),
+                true,
+                key_store,
+            )
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::InternalServerError)?;
+
+        let mut connector_data_list = vec![decided_connector_data.clone()];
+
+        for merchant_connector_account in merchant_connector_account_list {
+            if is_apple_pay_simplified_flow(merchant_connector_account.metadata)? {
+                let connector_data = api::ConnectorData::get_connector_by_name(
+                    &state.conf.connectors,
+                    &merchant_connector_account.connector_name.to_string(),
+                    api::GetToken::Connector,
+                    Some(merchant_connector_account.merchant_connector_id),
+                )
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Invalid connector name received")?;
+
+                if !connector_data_list.iter().any(|connector_details| {
+                    connector_details.merchant_connector_id == connector_data.merchant_connector_id
+                }) {
+                    connector_data_list.push(connector_data)
+                }
+            }
+        }
+        Some(connector_data_list)
+    } else {
+        None
+    };
+    Ok(connector_data_list)
+}
+
 #[derive(Debug, serde::Serialize, serde::Deserialize)]
 pub struct ApplePayData {
     version: masking::Secret<String>,
@@ -4040,6 +4156,8 @@ impl ApplePayData {
         &self,
         symmetric_key: &[u8],
     ) -> CustomResult<String, errors::ApplePayDecryptionError> {
+        logger::info!("Decrypt apple pay token");
+
         let data = BASE64_ENGINE
             .decode(self.data.peek().as_bytes())
             .change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?;

## 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 -->
Currently for apple pay pre-routing is done before the session call and a connector is decided based on the routing logic. Because of this we are not able retry the failed apple pay payments.

As the certificates use in the simplified flows are same across the connectors the failed apple pay payment can be retired with the other connectors with apple pay simplified flow configured. In order to achieve this we need to pass a list of apple pay simplified flow configured connectors and in pre routing.

### 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 a merchant account
-> Enable gcm for the `merchant_id`
```
curl --location 'localhost:8080/configs/' \
--header 'api-key: test_admin' \
--header 'Content-Type: application/json' \
--data '{
    "key": "should_call_gsm_{merchant_id}",
    "value": "true"
}'
```
-> Set the maximum retries count
```
curl --location 'localhost:8080/configs/' \
--header 'api-key: test_admin' \
--header 'Content-Type: application/json' \
--data '{
    "key": "max_auto_retries_enabled_{merchant_id}",
    "value": "1"
}'
```
-> Create MCA for stripe and cybersource with apple pay simplified flow 
metadata for simplified flow 
```
"metadata": {
        "apple_pay_combined": {
            "simplified": {
                "session_token_data": {
                    "initiative_context": "sdk-test-app.netlify.app",
                    "merchant_business_country": "US"
                },
                "payment_request_data": {
                    "label": "applepay",
                    "supported_networks": [
                        "visa",
                        "masterCard",
                        "amex",
                        "discover"
                    ],
                    "merchant_capabilities": [
                        "supports3DS"
                    ]
                }
            }
        },
        "google_pay": {
            "merchant_info": {
                "merchant_name": "Stripe"
            },
            "allowed_payment_methods": [
                {
                    "type": "CARD",
                    "parameters": {
                        "allowed_auth_methods": [
                            "PAN_ONLY",
                            "CRYPTOGRAM_3DS"
                        ],
                        "allowed_card_networks": [
                            "AMEX",
                            "DISCOVER",
                            "INTERAC",
                            "JCB",
                            "MASTERCARD",
                            "VISA"
                        ]
                    },
                    "tokenization_specification": {
                        "type": "PAYMENT_GATEWAY",
                        "parameters": {
                            "gateway": "stripe",
                            "stripe:version": "2018-10-31",
                            "stripe:publishableKey": "pk_test_51Msk2GAGHc77EJXX78h549SX2uaOnEkUYqBfjcoD05PIpAnDkYxMn8nQ4d19im85NQuX4Z6WDyHaUw2fFTPBWsIY00Wa7oNerO"
                        }
                    }
                }
            ]
        }
    }
}
```
-> Do a apple pay payment create with confirm false
```
{
    "amount": 650,
    "currency": "USD",
    "confirm": false,
    "business_country": "US",
    "business_label": "default",
    "amount_to_capture": 650,
    "customer_id": "custhype1232",
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "authentication_type": "three_ds",
    "return_url": "https://google.com",
    "email": "something@gmail.com",
    "name": "Joseph Doe",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "Its my first payment request",
    "statement_descriptor_name": "Juspay",
    "statement_descriptor_suffix": "Router",
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    }
}
```
<img width="1007" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/67deb74e-e0dd-4af3-b5d9-7298d65c3e01">
-> Do payment method list for merchant

```
curl --location 'http://localhost:8080/account/payment_methods?client_secret=pay_V1VBfr3VW6MVXaFw01sT_secret_6bByPi9DrnPk0uv6vYJj' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_cc7a8a8132a840338323ac8f7f6860d0'
```
<img width="1059" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/38f97f9d-ff51-41dc-9a06-20137f2ead1a">

-> Confirm the above create payment with `payment_id` and  payment_data
```
curl --location 'http://localhost:8080/payments/pay_V1VBfr3VW6MVXaFw01sT/confirm' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: api_key' \
--data '{
    "payment_method": "wallet",
    "payment_method_type": "apple_pay",
    "payment_method_data": {
        "wallet": {
            "apple_pay": {
                "payment_data":"eyJkYXRhIjoiSWJKaWp1TTRXT0lpM1BZcTRWVlpIL2wwUld1Qm5JRk5vMHBsWGp1L1E2bW9uQ0JVOE9rVHFDbnkwWDlZVVBZYzVBMXRIckE1eTV0NlpsSFVkZFJyYlY3QXIzMVVEc0NSbVlDVDRweFdEQ0oxTnF5WWYrK3NPQThZRkh5di83aUJiTnJ6K1g0Q0dwelNtelRNL2V6TUxzY2VCdHM4dkozQk05VnRGdVY2bXJsN2lPcGNHUGlNam5wK2ZkaG1OYlNHcGt0Qmt4QVRmaHlpM3VjbWNBejNjYWlMV3RSN2Yrc2owUmlzL0pBcmhuVzVZdmNodE53K2prSFBXZW9HeG1KTEFFUW1HRnZ3dGJCaWFKVFEvVnpmdEJtK3RGYkliWTBYeFdrRXdSQllIajlCVnVmbEVDaVlKK2NYLytBdVhlS2ZHZHZMQWZrNjNSYmZJQm5hTVR6dWlXZ2tEM1N5T3hJbE82MGxveHFPSmkxbkgvQXZSdFlMOUt4NWM0dzEwYnZVcENZZys4aytpdmg0aEdIdTF3PT0iLCJzaWduYXR1cmUiOiJNSUFHQ1NxR1NJYjNEUUVIQXFDQU1JQUNBUUV4RFRBTEJnbGdoa2dCWlFNRUFnRXdnQVlKS29aSWh2Y05BUWNCQUFDZ2dEQ0NBK013Z2dPSW9BTUNBUUlDQ0JaalRJc09NRmNYTUFvR0NDcUdTTTQ5QkFNQ01Ib3hMakFzQmdOVkJBTU1KVUZ3Y0d4bElFRndjR3hwWTJGMGFXOXVJRWx1ZEdWbmNtRjBhVzl1SUVOQklDMGdSek14SmpBa0JnTlZCQXNNSFVGd2NHeGxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1STXdFUVlEVlFRS0RBcEJjSEJzWlNCSmJtTXVNUXN3Q1FZRFZRUUdFd0pWVXpBZUZ3MHlOREEwTWpreE56UTNNamRhRncweU9UQTBNamd4TnpRM01qWmFNRjh4SlRBakJnTlZCQU1NSEdWall5MXpiWEF0WW5KdmEyVnlMWE5wWjI1ZlZVTTBMVkJTVDBReEZEQVNCZ05WQkFzTUMybFBVeUJUZVhOMFpXMXpNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVFzd0NRWURWUVFHRXdKVlV6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJNSVZkKzNyMXNleUlZOW8zWENRb1NHTng3QzlieXdvUFlSZ2xkbEs5S1ZCRzROQ0R0Z1I4MEIrZ3pNZkhGVEQ5K3N5SU5hNjFkVHY5SktKaVQ1OER4T2pnZ0lSTUlJQ0RUQU1CZ05WSFJNQkFmOEVBakFBTUI4R0ExVWRJd1FZTUJhQUZDUHlTY1JQaytUdkorYkU5aWhzUDZLNy9TNUxNRVVHQ0NzR0FRVUZCd0VCQkRrd056QTFCZ2dyQmdFRkJRY3dBWVlwYUhSMGNEb3ZMMjlqYzNBdVlYQndiR1V1WTI5dEwyOWpjM0F3TkMxaGNIQnNaV0ZwWTJFek1ESXdnZ0VkQmdOVkhTQUVnZ0VVTUlJQkVEQ0NBUXdHQ1NxR1NJYjNZMlFGQVRDQi9qQ0J3d1lJS3dZQkJRVUhBZ0l3Z2JZTWdiTlNaV3hwWVc1alpTQnZiaUIwYUdseklHTmxjblJwWm1sallYUmxJR0o1SUdGdWVTQndZWEowZVNCaGMzTjFiV1Z6SUdGalkyVndkR0Z1WTJVZ2IyWWdkR2hsSUhSb1pXNGdZWEJ3YkdsallXSnNaU0J6ZEdGdVpHRnlaQ0IwWlhKdGN5QmhibVFnWTI5dVpHbDBhVzl1Y3lCdlppQjFjMlVzSUdObGNuUnBabWxqWVhSbElIQnZiR2xqZVNCaGJtUWdZMlZ5ZEdsbWFXTmhkR2x2YmlCd2NtRmpkR2xqWlNCemRHRjBaVzFsYm5SekxqQTJCZ2dyQmdFRkJRY0NBUllxYUhSMGNEb3ZMM2QzZHk1aGNIQnNaUzVqYjIwdlkyVnlkR2xtYVdOaGRHVmhkWFJvYjNKcGRIa3ZNRFFHQTFVZEh3UXRNQ3N3S2FBbm9DV0dJMmgwZEhBNkx5OWpjbXd1WVhCd2JHVXVZMjl0TDJGd2NHeGxZV2xqWVRNdVkzSnNNQjBHQTFVZERnUVdCQlNVVjl0djFYU0Job21KZGk5K1Y0VUg1NXRZSkRBT0JnTlZIUThCQWY4RUJBTUNCNEF3RHdZSktvWklodmRqWkFZZEJBSUZBREFLQmdncWhrak9QUVFEQWdOSkFEQkdBaUVBeHZBanl5WVV1ekE0aUtGaW1ENGFrL0VGYjFENmVNMjV1a3lpUWN3VTRsNENJUUMrUE5EZjBXSkg5a2xFZFRnT25VVENLS0VJa0tPaDNISkxpMHk0aUpnWXZEQ0NBdTR3Z2dKMW9BTUNBUUlDQ0VsdEw3ODZtTnFYTUFvR0NDcUdTTTQ5QkFNQ01HY3hHekFaQmdOVkJBTU1Fa0Z3Y0d4bElGSnZiM1FnUTBFZ0xTQkhNekVtTUNRR0ExVUVDd3dkUVhCd2JHVWdRMlZ5ZEdsbWFXTmhkR2x2YmlCQmRYUm9iM0pwZEhreEV6QVJCZ05WQkFvTUNrRndjR3hsSUVsdVl5NHhDekFKQmdOVkJBWVRBbFZUTUI0WERURTBNRFV3TmpJek5EWXpNRm9YRFRJNU1EVXdOakl6TkRZek1Gb3dlakV1TUN3R0ExVUVBd3dsUVhCd2JHVWdRWEJ3YkdsallYUnBiMjRnU1c1MFpXZHlZWFJwYjI0Z1EwRWdMU0JITXpFbU1DUUdBMVVFQ3d3ZFFYQndiR1VnUTJWeWRHbG1hV05oZEdsdmJpQkJkWFJvYjNKcGRIa3hFekFSQmdOVkJBb01Da0Z3Y0d4bElFbHVZeTR4Q3pBSkJnTlZCQVlUQWxWVE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRThCY1JoQm5YWklYVkdsNGxnUWQyNklDaTc5NTdyazNnamZ4TGsrRXpWdFZtV3pXdUl0Q1hkZzBpVG51NkNQMTJGODZJeTNhN1puQyt5T2dwaFA5VVJhT0I5ekNCOURCR0JnZ3JCZ0VGQlFjQkFRUTZNRGd3TmdZSUt3WUJCUVVITUFHR0ttaDBkSEE2THk5dlkzTndMbUZ3Y0d4bExtTnZiUzl2WTNOd01EUXRZWEJ3YkdWeWIyOTBZMkZuTXpBZEJnTlZIUTRFRmdRVUkvSkp4RStUNU84bjVzVDJLR3cvb3J2OUxrc3dEd1lEVlIwVEFRSC9CQVV3QXdFQi96QWZCZ05WSFNNRUdEQVdnQlM3c042aFdET0ltcVNLbWQ2K3ZldXYyc3NrcXpBM0JnTlZIUjhFTURBdU1DeWdLcUFvaGlab2RIUndPaTh2WTNKc0xtRndjR3hsTG1OdmJTOWhjSEJzWlhKdmIzUmpZV2N6TG1OeWJEQU9CZ05WSFE4QkFmOEVCQU1DQVFZd0VBWUtLb1pJaHZkalpBWUNEZ1FDQlFBd0NnWUlLb1pJemowRUF3SURad0F3WkFJd09zOXlnMUVXbWJHRyt6WERWc3Bpdi9RWDdka1BkVTJpanI3eG5JRmVRcmVKK0pqM20xbWZtTlZCRFkrZDZjTCtBakF5TGRWRUliQ2pCWGRzWGZNNE81Qm4vUmQ4TENGdGxrL0djbW1DRW05VStIcDlHNW5MbXdtSklXRUdtUThKa2gwQUFER0NBWWN3Z2dHREFnRUJNSUdHTUhveExqQXNCZ05WQkFNTUpVRndjR3hsSUVGd2NHeHBZMkYwYVc5dUlFbHVkR1ZuY21GMGFXOXVJRU5CSUMwZ1J6TXhKakFrQmdOVkJBc01IVUZ3Y0d4bElFTmxjblJwWm1sallYUnBiMjRnUVhWMGFHOXlhWFI1TVJNd0VRWURWUVFLREFwQmNIQnNaU0JKYm1NdU1Rc3dDUVlEVlFRR0V3SlZVd0lJRm1OTWl3NHdWeGN3Q3dZSllJWklBV1VEQkFJQm9JR1RNQmdHQ1NxR1NJYjNEUUVKQXpFTEJna3Foa2lHOXcwQkJ3RXdIQVlKS29aSWh2Y05BUWtGTVE4WERUSTBNRFV5TVRBM016TXlORm93S0FZSktvWklodmNOQVFrME1Sc3dHVEFMQmdsZ2hrZ0JaUU1FQWdHaENnWUlLb1pJemowRUF3SXdMd1lKS29aSWh2Y05BUWtFTVNJRUlEa29VdTlwZ01JUHR2R0VEZi9tSXk3LzNjSTg2b3U5eTJaZkV6RkhuRFN0TUFvR0NDcUdTTTQ5QkFNQ0JFWXdSQUlnTWdkSG9rZHNWQndya3RYRzd1VmowMm9QVVNsQllaWGVPeXJyd3RsQk5MUUNJRDdzZnZPaThZcjVWNkVyNjFqU05sKzR3ZDhpR050YUxEdFRMZjNBQ0VhNkFBQUFBQUFBIiwiaGVhZGVyIjp7InB1YmxpY0tleUhhc2giOiJ4MnFmMTFaemRXbnFCZnc0U0NyOWxIYzZRc1JQOEp6Z2xrZnU5RTVkWUpnPSIsImVwaGVtZXJhbFB1YmxpY0tleSI6Ik1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTR0bHR1ODRldG5zeWR0ZXh5RnJVeVRhN3pqbXlYcjZ3OFIraU9TUm1qUDV5ZWlWUEs4OWFoZDJYM1JtaUZtUjQxdHN4S1AwOEpBZVYrSXpvUXBnPT0iLCJ0cmFuc2FjdGlvbklkIjoiMmEzZDYyYTUzZDBmMzg1NGUwNTA0Y2RhZDU2MzlmYjA2MjJiZTI0YzY0ZDg2ZGYxMDlkMTNjZjdkNTAxNjA1MSJ9LCJ2ZXJzaW9uIjoiRUNfdjEifQ==",
                "payment_method": {
                    "display_name": "Visa 0326",
                    "network": "Mastercard",
                    "type": "debit"
                },
                "transaction_identifier": "55CC32D7BF7890B9064433F15B9F23F849CF84AFD01E4E65DD8ADE306300E9D8"
            }
        }
    }
}'
```

<img width="1045" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/1b5c8cb4-43bf-42ea-a82f-9c6db940950b">
-> Apple pay retry connectors
<img width="1174" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/f3612f01-ee02-4687-b42c-af70eaf3e521">
-> Payment retrieve have two payment attempts
```
curl --location 'http://localhost:8080/payments/pay_V1VBfr3VW6MVXaFw01sT?expand_attempts=true' \
--header 'Accept: application/json' \
--header 'api-key: api_key'
```
<img width="720" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/705f2a0e-c057-47e1-8edf-e63139975a42">


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