Bug: [FEATURE] Add support for external 3ds authentication in cybersource.



### Feature Description

Currenctly hyperswitch supports 3ds authenticated payments through cybersource. But we cannot complete 3ds authentication through a separate authentication provider(eg: netcetera). and then do payment authorization through cybersource.

### Possible Implementation

This feature is already supported by NMI and Checkout. Similar solution can be used here.

### 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/enums.rs b/crates/api_models/src/enums.rs
index 59a85841212..4d5b709bba3 100644
--- a/crates/api_models/src/enums.rs
+++ b/crates/api_models/src/enums.rs
@@ -244,10 +244,9 @@ impl Connector {
             | Self::Riskified
             | Self::Threedsecureio
             | Self::Netcetera
-            | Self::Cybersource
             | Self::Noon
             | Self::Stripe => false,
-            Self::Checkout | Self::Nmi => true,
+            Self::Checkout | Self::Nmi| Self::Cybersource => true,
         }
     }
 }
diff --git a/crates/diesel_models/src/authentication.rs b/crates/diesel_models/src/authentication.rs
index 9b7c4c96ee0..71a16dcb863 100644
--- a/crates/diesel_models/src/authentication.rs
+++ b/crates/diesel_models/src/authentication.rs
@@ -42,6 +42,7 @@ pub struct Authentication {
     pub profile_id: String,
     pub payment_id: Option<String>,
     pub merchant_connector_id: String,
+    pub ds_trans_id: Option<String>,
     pub directory_server_id: Option<String>,
 }
 
@@ -87,6 +88,7 @@ pub struct AuthenticationNew {
     pub profile_id: String,
     pub payment_id: Option<String>,
     pub merchant_connector_id: String,
+    pub ds_trans_id: Option<String>,
     pub directory_server_id: Option<String>,
 }
 
@@ -115,6 +117,7 @@ pub enum AuthenticationUpdate {
         acs_trans_id: Option<String>,
         acs_signed_content: Option<String>,
         authentication_status: common_enums::AuthenticationStatus,
+        ds_trans_id: Option<String>,
     },
     PostAuthenticationUpdate {
         trans_status: common_enums::TransactionStatus,
@@ -162,6 +165,7 @@ pub struct AuthenticationUpdateInternal {
     pub acs_reference_number: Option<String>,
     pub acs_trans_id: Option<String>,
     pub acs_signed_content: Option<String>,
+    pub ds_trans_id: Option<String>,
     pub directory_server_id: Option<String>,
 }
 
@@ -193,6 +197,7 @@ impl Default for AuthenticationUpdateInternal {
             acs_reference_number: Default::default(),
             acs_trans_id: Default::default(),
             acs_signed_content: Default::default(),
+            ds_trans_id: Default::default(),
             directory_server_id: Default::default(),
         }
     }
@@ -226,6 +231,7 @@ impl AuthenticationUpdateInternal {
             acs_reference_number,
             acs_trans_id,
             acs_signed_content,
+            ds_trans_id,
             directory_server_id,
         } = self;
         Authentication {
@@ -258,6 +264,7 @@ impl AuthenticationUpdateInternal {
             acs_reference_number: acs_reference_number.or(source.acs_reference_number),
             acs_trans_id: acs_trans_id.or(source.acs_trans_id),
             acs_signed_content: acs_signed_content.or(source.acs_signed_content),
+            ds_trans_id: ds_trans_id.or(source.ds_trans_id),
             directory_server_id: directory_server_id.or(source.directory_server_id),
             ..source
         }
@@ -336,6 +343,7 @@ impl From<AuthenticationUpdate> for AuthenticationUpdateInternal {
                 acs_trans_id,
                 acs_signed_content,
                 authentication_status,
+                ds_trans_id,
             } => Self {
                 cavv: authentication_value,
                 trans_status: Some(trans_status),
@@ -346,6 +354,7 @@ impl From<AuthenticationUpdate> for AuthenticationUpdateInternal {
                 acs_trans_id,
                 acs_signed_content,
                 authentication_status: Some(authentication_status),
+                ds_trans_id,
                 ..Default::default()
             },
             AuthenticationUpdate::PostAuthenticationUpdate {
diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs
index de5b536dcda..6074fdc10b7 100644
--- a/crates/diesel_models/src/schema.rs
+++ b/crates/diesel_models/src/schema.rs
@@ -115,6 +115,8 @@ diesel::table! {
         payment_id -> Nullable<Varchar>,
         #[max_length = 128]
         merchant_connector_id -> Varchar,
+        #[max_length = 64]
+        ds_trans_id -> Nullable<Varchar>,
         #[max_length = 128]
         directory_server_id -> Nullable<Varchar>,
     }
diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs
index 56a4ba739c0..c8e68586dad 100644
--- a/crates/hyperswitch_domain_models/src/router_request_types.rs
+++ b/crates/hyperswitch_domain_models/src/router_request_types.rs
@@ -1,7 +1,7 @@
 pub mod authentication;
 pub mod fraud_check;
 use api_models::payments::RequestSurchargeDetails;
-use common_utils::{consts, errors, ext_traits::OptionExt, pii};
+use common_utils::{consts, errors, ext_traits::OptionExt, pii, types as common_types};
 use diesel_models::enums as storage_enums;
 use error_stack::ResultExt;
 use masking::Secret;
@@ -447,7 +447,8 @@ pub struct AuthenticationData {
     pub eci: Option<String>,
     pub cavv: String,
     pub threeds_server_transaction_id: String,
-    pub message_version: String,
+    pub message_version: common_types::SemanticVersion,
+    pub ds_trans_id: Option<String>,
 }
 
 #[derive(Debug, Clone)]
diff --git a/crates/hyperswitch_domain_models/src/router_response_types.rs b/crates/hyperswitch_domain_models/src/router_response_types.rs
index 3d89163b3da..57d579e1a0a 100644
--- a/crates/hyperswitch_domain_models/src/router_response_types.rs
+++ b/crates/hyperswitch_domain_models/src/router_response_types.rs
@@ -219,6 +219,7 @@ pub enum AuthenticationResponseData {
         authn_flow_type: AuthNFlowType,
         authentication_value: Option<String>,
         trans_status: common_enums::TransactionStatus,
+        ds_trans_id: Option<String>,
     },
     PostAuthNResponse {
         trans_status: common_enums::TransactionStatus,
diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs
index c92d75f1de8..10918b7bc6e 100644
--- a/crates/router/src/connector/checkout/transformers.rs
+++ b/crates/router/src/connector/checkout/transformers.rs
@@ -383,7 +383,7 @@ impl TryFrom<&CheckoutRouterData<&types::PaymentsAuthorizeRouterData>> for Payme
                 eci: authentication_data.and_then(|auth| auth.eci.clone()),
                 cryptogram: authentication_data.map(|auth| auth.cavv.clone()),
                 xid: authentication_data.map(|auth| auth.threeds_server_transaction_id.clone()),
-                version: authentication_data.map(|auth| auth.message_version.clone()),
+                version: authentication_data.map(|auth| auth.message_version.to_string()),
             },
             enums::AuthenticationType::NoThreeDs => CheckoutThreeDS {
                 enabled: false,
diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs
index 43e5660d2b9..3cccb8dcf5c 100644
--- a/crates/router/src/connector/cybersource.rs
+++ b/crates/router/src/connector/cybersource.rs
@@ -848,6 +848,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
         if req.is_three_ds()
             && req.request.is_card()
             && req.request.connector_mandate_id().is_none()
+            && req.request.authentication_data.is_none()
         {
             Ok(format!(
                 "{}risk/v1/authentication-setups",
@@ -875,6 +876,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
         if req.is_three_ds()
             && req.request.is_card()
             && req.request.connector_mandate_id().is_none()
+            && req.request.authentication_data.is_none()
         {
             let connector_req =
                 cybersource::CybersourceAuthSetupRequest::try_from(&connector_router_data)?;
@@ -915,6 +917,7 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
         if data.is_three_ds()
             && data.request.is_card()
             && data.request.connector_mandate_id().is_none()
+            && data.request.authentication_data.is_none()
         {
             let response: cybersource::CybersourceAuthSetupResponse = res
                 .response
diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs
index f1774a0de39..4527ea3950a 100644
--- a/crates/router/src/connector/cybersource/transformers.rs
+++ b/crates/router/src/connector/cybersource/transformers.rs
@@ -6,7 +6,7 @@ use api_models::{
 };
 use base64::Engine;
 use common_enums::FutureUsage;
-use common_utils::{ext_traits::ValueExt, pii};
+use common_utils::{ext_traits::ValueExt, pii, types::SemanticVersion};
 use error_stack::ResultExt;
 use masking::{ExposeInterface, PeekInterface, Secret};
 use serde::{Deserialize, Serialize};
@@ -261,7 +261,16 @@ pub struct CybersourceConsumerAuthInformation {
     xid: Option<String>,
     directory_server_transaction_id: Option<Secret<String>>,
     specification_version: Option<String>,
+    /// This field specifies the 3ds version
+    pa_specification_version: Option<SemanticVersion>,
+    /// Verification response enrollment status.
+    ///
+    /// This field is supported only on Asia, Middle East, and Africa Gateway.
+    ///
+    /// For external authentication, this field will always be "Y"
+    veres_enrolled: Option<String>,
 }
+
 #[derive(Debug, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct MerchantDefinedInformation {
@@ -655,6 +664,19 @@ impl
         } else {
             (None, None, None)
         };
+        // this logic is for external authenticated card
+        let commerce_indicator_for_external_authentication = item
+            .router_data
+            .request
+            .authentication_data
+            .as_ref()
+            .and_then(|authn_data| {
+                authn_data
+                    .eci
+                    .clone()
+                    .map(|eci| get_commerce_indicator_for_external_authentication(network, eci))
+            });
+
         Ok(Self {
             capture: Some(matches!(
                 item.router_data.request.capture_method,
@@ -665,11 +687,62 @@ impl
             action_token_types,
             authorization_options,
             capture_options: None,
-            commerce_indicator,
+            commerce_indicator: commerce_indicator_for_external_authentication
+                .unwrap_or(commerce_indicator),
         })
     }
 }
 
+fn get_commerce_indicator_for_external_authentication(
+    card_network: Option<String>,
+    eci: String,
+) -> String {
+    let card_network_lower_case = card_network
+        .as_ref()
+        .map(|card_network| card_network.to_lowercase());
+    match eci.as_str() {
+        "00" | "01" | "02" => {
+            if matches!(
+                card_network_lower_case.as_deref(),
+                Some("mastercard") | Some("maestro")
+            ) {
+                "spa"
+            } else {
+                "internet"
+            }
+        }
+        "05" => match card_network_lower_case.as_deref() {
+            Some("amex") => "aesk",
+            Some("discover") => "dipb",
+            Some("mastercard") => "spa",
+            Some("visa") => "vbv",
+            Some("diners") => "pb",
+            Some("upi") => "up3ds",
+            _ => "internet",
+        },
+        "06" => match card_network_lower_case.as_deref() {
+            Some("amex") => "aesk_attempted",
+            Some("discover") => "dipb_attempted",
+            Some("mastercard") => "spa",
+            Some("visa") => "vbv_attempted",
+            Some("diners") => "pb_attempted",
+            Some("upi") => "up3ds_attempted",
+            _ => "internet",
+        },
+        "07" => match card_network_lower_case.as_deref() {
+            Some("amex") => "internet",
+            Some("discover") => "internet",
+            Some("mastercard") => "spa",
+            Some("visa") => "vbv_failure",
+            Some("diners") => "internet",
+            Some("upi") => "up3ds_failure",
+            _ => "internet",
+        },
+        _ => "vbv_failure",
+    }
+    .to_string()
+}
+
 impl
     From<(
         &CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>,
@@ -852,12 +925,39 @@ impl
                 Vec::<MerchantDefinedInformation>::foreign_from(metadata.peek().to_owned())
             });
 
+        let consumer_authentication_information = item
+            .router_data
+            .request
+            .authentication_data
+            .as_ref()
+            .map(|authn_data| {
+                let (ucaf_authentication_data, cavv) =
+                    if ccard.card_network == Some(common_enums::CardNetwork::Mastercard) {
+                        (Some(Secret::new(authn_data.cavv.clone())), None)
+                    } else {
+                        (None, Some(authn_data.cavv.clone()))
+                    };
+                CybersourceConsumerAuthInformation {
+                    ucaf_collection_indicator: None,
+                    cavv,
+                    ucaf_authentication_data,
+                    xid: Some(authn_data.threeds_server_transaction_id.clone()),
+                    directory_server_transaction_id: authn_data
+                        .ds_trans_id
+                        .clone()
+                        .map(Secret::new),
+                    specification_version: None,
+                    pa_specification_version: Some(authn_data.message_version.clone()),
+                    veres_enrolled: Some("Y".to_string()),
+                }
+            });
+
         Ok(Self {
             processing_information,
             payment_information,
             order_information,
             client_reference_information,
-            consumer_authentication_information: None,
+            consumer_authentication_information,
             merchant_defined_information,
         })
     }
@@ -922,6 +1022,8 @@ impl
                 .three_ds_data
                 .directory_server_transaction_id,
             specification_version: three_ds_info.three_ds_data.specification_version,
+            pa_specification_version: None,
+            veres_enrolled: None,
         });
 
         let merchant_defined_information =
@@ -1000,6 +1102,8 @@ impl
                 xid: None,
                 directory_server_transaction_id: None,
                 specification_version: None,
+                pa_specification_version: None,
+                veres_enrolled: None,
             }),
             merchant_defined_information,
         })
@@ -1131,6 +1235,8 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>>
                                                 xid: None,
                                                 directory_server_transaction_id: None,
                                                 specification_version: None,
+                                                pa_specification_version: None,
+                                                veres_enrolled: None,
                                             },
                                         ),
                                     })
diff --git a/crates/router/src/connector/netcetera/transformers.rs b/crates/router/src/connector/netcetera/transformers.rs
index fffacc3ea4d..25ed7c5271e 100644
--- a/crates/router/src/connector/netcetera/transformers.rs
+++ b/crates/router/src/connector/netcetera/transformers.rs
@@ -167,6 +167,7 @@ impl
                         authn_flow_type,
                         authentication_value: response.authentication_value,
                         trans_status: response.trans_status,
+                        ds_trans_id: response.authentication_response.ds_trans_id,
                     },
                 )
             }
@@ -646,6 +647,8 @@ pub struct AuthenticationResponse {
     pub acs_reference_number: Option<String>,
     #[serde(rename = "acsTransID")]
     pub acs_trans_id: Option<String>,
+    #[serde(rename = "dsTransID")]
+    pub ds_trans_id: Option<String>,
     pub acs_signed_content: Option<String>,
 }
 
diff --git a/crates/router/src/connector/nmi/transformers.rs b/crates/router/src/connector/nmi/transformers.rs
index 2a8498bfbad..44836002024 100644
--- a/crates/router/src/connector/nmi/transformers.rs
+++ b/crates/router/src/connector/nmi/transformers.rs
@@ -625,7 +625,7 @@ impl TryFrom<(&domain::payments::Card, &types::PaymentsAuthorizeData)> for Payme
             cavv: Some(auth_data.cavv.clone()),
             eci: auth_data.eci.clone(),
             cardholder_auth: None,
-            three_ds_version: Some(auth_data.message_version.clone()),
+            three_ds_version: Some(auth_data.message_version.to_string()),
             directory_server_id: Some(auth_data.threeds_server_transaction_id.clone().into()),
         };
 
diff --git a/crates/router/src/connector/threedsecureio/transformers.rs b/crates/router/src/connector/threedsecureio/transformers.rs
index fe26e509c12..242d0dee946 100644
--- a/crates/router/src/connector/threedsecureio/transformers.rs
+++ b/crates/router/src/connector/threedsecureio/transformers.rs
@@ -187,6 +187,7 @@ impl
                             types::authentication::AuthNFlowType::Frictionless
                         },
                         authentication_value: response.authentication_value,
+                        ds_trans_id: Some(response.ds_trans_id),
                     },
                 )
             }
diff --git a/crates/router/src/core/authentication/utils.rs b/crates/router/src/core/authentication/utils.rs
index 0152fb4321d..f67f66bfffb 100644
--- a/crates/router/src/core/authentication/utils.rs
+++ b/crates/router/src/core/authentication/utils.rs
@@ -84,6 +84,7 @@ pub async fn update_trackers<F: Clone, Req>(
                 authn_flow_type,
                 authentication_value,
                 trans_status,
+                ds_trans_id,
             } => {
                 let authentication_status =
                     common_enums::AuthenticationStatus::foreign_from(trans_status.clone());
@@ -97,6 +98,7 @@ pub async fn update_trackers<F: Clone, Req>(
                     acs_signed_content: authn_flow_type.get_acs_signed_content(),
                     authentication_type: authn_flow_type.get_decoupled_authentication_type(),
                     authentication_status,
+                    ds_trans_id,
                 }
             }
             AuthenticationResponseData::PostAuthNResponse {
@@ -183,6 +185,7 @@ pub async fn create_new_authentication(
         profile_id,
         payment_id,
         merchant_connector_id,
+        ds_trans_id: None,
         directory_server_id: None,
     };
     state
diff --git a/crates/router/src/core/payments/types.rs b/crates/router/src/core/payments/types.rs
index d39527b4229..92c9eec5ff8 100644
--- a/crates/router/src/core/payments/types.rs
+++ b/crates/router/src/core/payments/types.rs
@@ -369,7 +369,8 @@ impl ForeignTryFrom<&storage::Authentication> for AuthenticationData {
                 eci: authentication.eci.clone(),
                 cavv,
                 threeds_server_transaction_id,
-                message_version: message_version.to_string(),
+                message_version,
+                ds_trans_id: authentication.ds_trans_id.clone(),
             })
         } else {
             Err(errors::ApiErrorResponse::PaymentAuthenticationFailed { data: None }.into())
diff --git a/crates/router/src/db/authentication.rs b/crates/router/src/db/authentication.rs
index 4eff3dfdbfd..cd3bb663ec9 100644
--- a/crates/router/src/db/authentication.rs
+++ b/crates/router/src/db/authentication.rs
@@ -147,6 +147,7 @@ impl AuthenticationInterface for MockDb {
             profile_id: authentication.profile_id,
             payment_id: authentication.payment_id,
             merchant_connector_id: authentication.merchant_connector_id,
+            ds_trans_id: authentication.ds_trans_id,
             directory_server_id: authentication.directory_server_id,
         };
         authentications.push(authentication.clone());
diff --git a/migrations/2024-05-21-065403_add_ds_trans_id_to_authentication_table/down.sql b/migrations/2024-05-21-065403_add_ds_trans_id_to_authentication_table/down.sql
new file mode 100644
index 00000000000..146b91185c2
--- /dev/null
+++ b/migrations/2024-05-21-065403_add_ds_trans_id_to_authentication_table/down.sql
@@ -0,0 +1,2 @@
+-- This file should undo anything in `up.sql`
+ALTER TABLE authentication DROP COLUMN If EXISTS ds_trans_id;
\ No newline at end of file
diff --git a/migrations/2024-05-21-065403_add_ds_trans_id_to_authentication_table/up.sql b/migrations/2024-05-21-065403_add_ds_trans_id_to_authentication_table/up.sql
new file mode 100644
index 00000000000..b02613777c8
--- /dev/null
+++ b/migrations/2024-05-21-065403_add_ds_trans_id_to_authentication_table/up.sql
@@ -0,0 +1,2 @@
+-- Your SQL goes here
+ALTER TABLE authentication ADD COLUMN IF NOT EXISTS ds_trans_id VARCHAR(64);
\ No newline at end of file

## 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 -->
 add support for external authentication for cybersource.
 After this change is merged, we'll be able to authenticate a payment through an external 3ds authentication(threedsecureio or netcetera) and authorise the same payment through cybersource.
 
Other changes:
Added `ds_trans_id` field to `authentication` table since it is required for authorization through cybersource.

### Additional Changes

- [ ] This PR modifies the API contract
- [x] 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)?
-->
Ran cybersource postman collection to make sure existing 3ds flow is not affected.
<img width="785" alt="Screenshot 2024-05-23 at 1 00 07 PM" src="https://github.com/juspay/hyperswitch/assets/61539176/c210e8ce-1864-444d-8f81-2ef3a7133160">

curls:
1. Create a cybersource connector and netcetera authentication connector.
```
curl --location 'http://localhost:8080/account/postman_merchant_GHAction_7360288d-b77e-4624-8a48-74c11c31d3a0/connectors' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin' \
--data '{
    "connector_type": "fiz_operations",
    "connector_name": "cybersource",
    "connector_account_details": {
        "auth_type": "SignatureKey",
        "api_key": "",
        "key1": "",
        "api_secret": ""
    },
    "test_mode": false,
    "disabled": false,
    "business_country": "US",
    "business_label": "default",
    "payment_methods_enabled": [
        {
            "payment_method": "card",
            "payment_method_types": [
                {
                    "payment_method_type": "credit",
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        },
        {
            "payment_method": "card",
            "payment_method_types": [
                {
                    "payment_method_type": "debit",
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        },
        {
            "payment_method": "wallet",
            "payment_method_types": [
                {
                    "payment_method_type": "apple_pay",
                    "payment_experience": "invoke_sdk_client",
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        },
        {
            "payment_method": "wallet",
            "payment_method_types": [
                {
                    "payment_method_type": "google_pay",
                    "payment_experience": "invoke_sdk_client",
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        }
    ],
    "metadata": {
        "acquirer_bin": "438309",
        "acquirer_merchant_id": "00002000000"
    }
}'
```
```
curl --location 'http://localhost:8080/account/postman_merchant_GHAction_7360288d-b77e-4624-8a48-74c11c31d3a0/connectors' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: test_admin' \
--data '{
    "connector_type": "authentication_processor",
    "business_country": "US",
    "business_label": "default",
    "connector_name": "netcetera",
    "connector_account_details": {
        "auth_type": "CertificateAuth",
        "certificate": "",
        "private_key": ""
    },
    "test_mode": true,
    "disabled": false,
    "metadata": {
        "mcc": "5411",
        "merchant_country_code": "840",
        "merchant_name": "Dummy Merchant",
        "endpoint_prefix": "flowbird",
        "three_ds_requestor_name": "juspay-prev",
        "three_ds_requestor_id": "juspay-prev",
        "pull_mechanism_for_external_3ds_enabled": false
    }
}'
```
2. Create a payment with "request_external_three_ds_authentication": true, and "authentication_type": "three_ds",
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_Mv6wkxquIeO6WDvY49Mpd4hullQ4CW05hBRT7tlIX4EOS5NaG5fmDzuY5NqacvXT' \
--data-raw '{
    "amount": 6540,
    "currency": "USD",
    "confirm": false,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
    "customer_id": "StripeCustomer",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "Its my first payment request",
    "authentication_type": "three_ds",
    "return_url": "https://duck.com",
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        },
        "phone": {
            "number": "123456789",
            "country_code": "12"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        },
        "phone": {
            "number": "123456789",
            "country_code": "12"
        }
    },
    "request_external_three_ds_authentication": true,
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
```

3. Confirm the payment
```
curl --location 'http://localhost:8080/payments/pay_XPc0gwoIZLYpMBKI8Yng/confirm' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_d777ba4dfb764db2a6f851870a00a7e5' \
--data '{
    "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": "115.99.183.2"
    },
    "client_secret": "pay_XPc0gwoIZLYpMBKI8Yng_secret_vRjk6g87hvftbO9z18BO",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "4929251897047956", 
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "joseph Doe",
            "card_cvc": "123"
        }
    }
}'
```

4. Authenticate the payment.
```
curl --location 'http://localhost:8080/payments/pay_XPc0gwoIZLYpMBKI8Yng/3ds/authentication' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: pk_dev_d777ba4dfb764db2a6f851870a00a7e5' \
--data '{
    "client_secret": "pay_XPc0gwoIZLYpMBKI8Yng_secret_vRjk6g87hvftbO9z18BO",
    "device_channel": "BRW",
    "threeds_method_comp_ind": "N"
}'
```

5. Authorize the payment.
```
curl --location 'http://localhost:8080/payments/pay_XPc0gwoIZLYpMBKI8Yng/postman_merchant_GHAction_7360288d-b77e-4624-8a48-74c11c31d3a0/authorize/checkout' \
--header 'Content-Type: application/json' \
--data '{
    
}'
```

6. Retrive the payment.
```
curl --location 'http://localhost:8080/payments/pay_XPc0gwoIZLYpMBKI8Yng?force_sync=true' \
--header 'Accept: application/json' \
--header 'api-key: dev_Mv6wkxquIeO6WDvY49Mpd4hullQ4CW05hBRT7tlIX4EOS5NaG5fmDzuY5NqacvXT'
```

Payment should `succeed` and below object should be returned in the payment response body.
```
"external_authentication_details": {
        "authentication_flow": "frictionless",
        "electronic_commerce_indicator": null,
        "status": "success",
        "ds_transaction_id": "3961a5b1-593f-4f94-851e-2793cd7f9e77",
        "version": "2.2.0",
        "error_code": null,
        "error_message": 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
