Bug: FEATURE: [Paysafe] Implement card 3ds flow



- Implement card 3ds flow
diff --git a/crates/hyperswitch_connectors/src/connectors/paysafe.rs b/crates/hyperswitch_connectors/src/connectors/paysafe.rs
index 64296c2ebe6..8d463966748 100644
--- a/crates/hyperswitch_connectors/src/connectors/paysafe.rs
+++ b/crates/hyperswitch_connectors/src/connectors/paysafe.rs
@@ -17,15 +17,15 @@ use hyperswitch_domain_models::{
     router_flow_types::{
         access_token_auth::AccessTokenAuth,
         payments::{
-            Authorize, Capture, PSync, PaymentMethodToken, PreProcessing, Session, SetupMandate,
-            Void,
+            Authorize, Capture, CompleteAuthorize, PSync, PaymentMethodToken, PreProcessing,
+            Session, SetupMandate, Void,
         },
         refunds::{Execute, RSync},
     },
     router_request_types::{
-        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
-        PaymentsCancelData, PaymentsCaptureData, PaymentsPreProcessingData, PaymentsSessionData,
-        PaymentsSyncData, RefundsData, SetupMandateRequestData,
+        AccessTokenRequestData, CompleteAuthorizeData, PaymentMethodTokenizationData,
+        PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsPreProcessingData,
+        PaymentsSessionData, PaymentsSyncData, RefundsData, SetupMandateRequestData,
     },
     router_response_types::{
         ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
@@ -33,8 +33,8 @@ use hyperswitch_domain_models::{
     },
     types::{
         PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
-        PaymentsPreProcessingRouterData, PaymentsSyncRouterData, RefundSyncRouterData,
-        RefundsRouterData,
+        PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData,
+        PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData,
     },
 };
 use hyperswitch_interfaces::{
@@ -54,7 +54,10 @@ use transformers as paysafe;
 use crate::{
     constants::headers,
     types::ResponseRouterData,
-    utils::{self, RefundsRequestData as OtherRefundsRequestData},
+    utils::{
+        self, PaymentsSyncRequestData, RefundsRequestData as OtherRefundsRequestData,
+        RouterData as _,
+    },
 };
 
 #[derive(Clone)]
@@ -262,7 +265,6 @@ impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResp
 
         let connector_router_data = paysafe::PaysafeRouterData::from((amount, req));
         let connector_req = paysafe::PaysafePaymentHandleRequest::try_from(&connector_router_data)?;
-
         Ok(RequestContent::Json(Box::new(connector_req)))
     }
 
@@ -333,10 +335,15 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
 
     fn get_url(
         &self,
-        _req: &PaymentsAuthorizeRouterData,
+        req: &PaymentsAuthorizeRouterData,
         connectors: &Connectors,
     ) -> CustomResult<String, errors::ConnectorError> {
-        Ok(format!("{}v1/payments", self.base_url(connectors),))
+        match req.payment_method {
+            enums::PaymentMethod::Card if !req.is_three_ds() => {
+                Ok(format!("{}v1/payments", self.base_url(connectors)))
+            }
+            _ => Ok(format!("{}v1/paymenthandles", self.base_url(connectors),)),
+        }
     }
 
     fn get_request_body(
@@ -351,8 +358,19 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
         )?;
 
         let connector_router_data = paysafe::PaysafeRouterData::from((amount, req));
-        let connector_req = paysafe::PaysafePaymentsRequest::try_from(&connector_router_data)?;
-        Ok(RequestContent::Json(Box::new(connector_req)))
+        match req.payment_method {
+            //Card No 3DS
+            enums::PaymentMethod::Card if !req.is_three_ds() => {
+                let connector_req =
+                    paysafe::PaysafePaymentsRequest::try_from(&connector_router_data)?;
+                Ok(RequestContent::Json(Box::new(connector_req)))
+            }
+            _ => {
+                let connector_req =
+                    paysafe::PaysafePaymentHandleRequest::try_from(&connector_router_data)?;
+                Ok(RequestContent::Json(Box::new(connector_req)))
+            }
+        }
     }
 
     fn build_request(
@@ -383,17 +401,122 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
         event_builder: Option<&mut ConnectorEvent>,
         res: Response,
     ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
+        match data.payment_method {
+            enums::PaymentMethod::Card if !data.is_three_ds() => {
+                let response: paysafe::PaysafePaymentsResponse = res
+                    .response
+                    .parse_struct("Paysafe PaymentsAuthorizeResponse")
+                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
+                event_builder.map(|i| i.set_response_body(&response));
+                router_env::logger::info!(connector_response=?response);
+                RouterData::try_from(ResponseRouterData {
+                    response,
+                    data: data.clone(),
+                    http_code: res.status_code,
+                })
+            }
+            _ => {
+                let response: paysafe::PaysafePaymentHandleResponse = res
+                    .response
+                    .parse_struct("Paysafe PaymentHandleResponse")
+                    .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
+                event_builder.map(|i| i.set_response_body(&response));
+                router_env::logger::info!(connector_response=?response);
+                RouterData::try_from(ResponseRouterData {
+                    response,
+                    data: data.clone(),
+                    http_code: res.status_code,
+                })
+            }
+        }
+    }
+
+    fn get_error_response(
+        &self,
+        res: Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
+}
+
+impl api::PaymentsCompleteAuthorize for Paysafe {}
+
+impl ConnectorIntegration<CompleteAuthorize, CompleteAuthorizeData, PaymentsResponseData>
+    for Paysafe
+{
+    fn get_headers(
+        &self,
+        req: &PaymentsCompleteAuthorizeRouterData,
+        connectors: &Connectors,
+    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
+        self.build_headers(req, connectors)
+    }
+    fn get_content_type(&self) -> &'static str {
+        self.common_get_content_type()
+    }
+    fn get_url(
+        &self,
+        _req: &PaymentsCompleteAuthorizeRouterData,
+        connectors: &Connectors,
+    ) -> CustomResult<String, errors::ConnectorError> {
+        Ok(format!("{}v1/payments", self.base_url(connectors),))
+    }
+    fn get_request_body(
+        &self,
+        req: &PaymentsCompleteAuthorizeRouterData,
+        _connectors: &Connectors,
+    ) -> CustomResult<RequestContent, errors::ConnectorError> {
+        let amount = utils::convert_amount(
+            self.amount_converter,
+            req.request.minor_amount,
+            req.request.currency,
+        )?;
+
+        let connector_router_data = paysafe::PaysafeRouterData::from((amount, req));
+        let connector_req = paysafe::PaysafePaymentsRequest::try_from(&connector_router_data)?;
+        Ok(RequestContent::Json(Box::new(connector_req)))
+    }
+    fn build_request(
+        &self,
+        req: &PaymentsCompleteAuthorizeRouterData,
+        connectors: &Connectors,
+    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
+        Ok(Some(
+            RequestBuilder::new()
+                .method(Method::Post)
+                .url(&types::PaymentsCompleteAuthorizeType::get_url(
+                    self, req, connectors,
+                )?)
+                .attach_default_headers()
+                .headers(types::PaymentsCompleteAuthorizeType::get_headers(
+                    self, req, connectors,
+                )?)
+                .set_body(types::PaymentsCompleteAuthorizeType::get_request_body(
+                    self, req, connectors,
+                )?)
+                .build(),
+        ))
+    }
+    fn handle_response(
+        &self,
+        data: &PaymentsCompleteAuthorizeRouterData,
+        event_builder: Option<&mut ConnectorEvent>,
+        res: Response,
+    ) -> CustomResult<PaymentsCompleteAuthorizeRouterData, errors::ConnectorError> {
         let response: paysafe::PaysafePaymentsResponse = res
             .response
             .parse_struct("Paysafe PaymentsAuthorizeResponse")
             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
         event_builder.map(|i| i.set_response_body(&response));
         router_env::logger::info!(connector_response=?response);
+
         RouterData::try_from(ResponseRouterData {
             response,
             data: data.clone(),
             http_code: res.status_code,
         })
+        .change_context(errors::ConnectorError::ResponseHandlingFailed)
     }
 
     fn get_error_response(
@@ -424,11 +547,17 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Pay
         connectors: &Connectors,
     ) -> CustomResult<String, errors::ConnectorError> {
         let connector_payment_id = req.connector_request_reference_id.clone();
-        Ok(format!(
-            "{}v1/payments?merchantRefNum={}",
-            self.base_url(connectors),
-            connector_payment_id
-        ))
+        let connector_transaction_id = req.request.get_optional_connector_transaction_id();
+
+        let base_url = self.base_url(connectors);
+
+        let url = if connector_transaction_id.is_some() {
+            format!("{base_url}v1/payments?merchantRefNum={connector_payment_id}")
+        } else {
+            format!("{base_url}v1/paymenthandles?merchantRefNum={connector_payment_id}")
+        };
+
+        Ok(url)
     }
 
     fn build_request(
@@ -452,9 +581,9 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Pay
         event_builder: Option<&mut ConnectorEvent>,
         res: Response,
     ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
-        let response: paysafe::PaysafePaymentsSyncResponse = res
+        let response: paysafe::PaysafeSyncResponse = res
             .response
-            .parse_struct("paysafe PaysafePaymentsSyncResponse")
+            .parse_struct("paysafe PaysafeSyncResponse")
             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
         event_builder.map(|i| i.set_response_body(&response));
         router_env::logger::info!(connector_response=?response);
diff --git a/crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs b/crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
index 50c665a65bd..26e9d8de48d 100644
--- a/crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
@@ -1,5 +1,7 @@
+use std::collections::HashMap;
+
 use cards::CardNumber;
-use common_enums::enums;
+use common_enums::{enums, Currency};
 use common_utils::{
     pii::{IpAddress, SecretSerdeValue},
     request::Method,
@@ -11,12 +13,13 @@ use hyperswitch_domain_models::{
     router_data::{ConnectorAuthType, RouterData},
     router_flow_types::refunds::{Execute, RSync},
     router_request_types::{
-        PaymentsAuthorizeData, PaymentsPreProcessingData, PaymentsSyncData, ResponseId,
+        CompleteAuthorizeData, PaymentsAuthorizeData, PaymentsPreProcessingData, PaymentsSyncData,
+        ResponseId,
     },
-    router_response_types::{PaymentsResponseData, RefundsResponseData},
+    router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
     types::{
         PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
-        PaymentsPreProcessingRouterData, RefundsRouterData,
+        PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData, RefundsRouterData,
     },
 };
 use hyperswitch_interfaces::errors;
@@ -26,8 +29,8 @@ use serde::{Deserialize, Serialize};
 use crate::{
     types::{RefundsResponseRouterData, ResponseRouterData},
     utils::{
-        self, BrowserInformationData, CardData, PaymentsAuthorizeRequestData,
-        PaymentsPreProcessingRequestData, RouterData as _,
+        self, to_connector_meta, BrowserInformationData, CardData, PaymentsAuthorizeRequestData,
+        PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingRequestData, RouterData as _,
     },
 };
 
@@ -47,7 +50,18 @@ impl<T> From<(MinorUnit, T)> for PaysafeRouterData<T> {
 
 #[derive(Debug, Default, Serialize, Deserialize)]
 pub struct PaysafeConnectorMetadataObject {
-    pub account_id: Secret<String>,
+    pub account_id: PaysafePaymentMethodDetails,
+}
+
+#[derive(Debug, Default, Serialize, Deserialize)]
+pub struct PaysafePaymentMethodDetails {
+    pub card: Option<HashMap<Currency, CardAccountId>>,
+}
+
+#[derive(Debug, Default, Serialize, Deserialize)]
+pub struct CardAccountId {
+    no_three_ds: Option<Secret<String>>,
+    three_ds: Option<Secret<String>>,
 }
 
 impl TryFrom<&Option<SecretSerdeValue>> for PaysafeConnectorMetadataObject {
@@ -61,18 +75,65 @@ impl TryFrom<&Option<SecretSerdeValue>> for PaysafeConnectorMetadataObject {
     }
 }
 
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ThreeDs {
+    pub merchant_url: String,
+    pub device_channel: DeviceChannel,
+    pub message_category: ThreeDsMessageCategory,
+    pub authentication_purpose: ThreeDsAuthenticationPurpose,
+    pub requestor_challenge_preference: ThreeDsChallengePreference,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "UPPERCASE")]
+pub enum DeviceChannel {
+    Browser,
+    Sdk,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+pub enum ThreeDsMessageCategory {
+    Payment,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+pub enum ThreeDsAuthenticationPurpose {
+    PaymentTransaction,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+pub enum ThreeDsChallengePreference {
+    ChallengeMandated,
+    NoPreference,
+    NoChallengeRequested,
+    ChallengeRequested,
+}
+
 #[derive(Debug, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct PaysafePaymentHandleRequest {
     pub merchant_ref_num: String,
     pub amount: MinorUnit,
     pub settle_with_auth: bool,
-    pub card: PaysafeCard,
-    pub currency_code: enums::Currency,
+    #[serde(flatten)]
+    pub payment_method: PaysafePaymentMethod,
+    pub currency_code: Currency,
     pub payment_type: PaysafePaymentType,
     pub transaction_type: TransactionType,
     pub return_links: Vec<ReturnLink>,
     pub account_id: Secret<String>,
+    pub three_ds: Option<ThreeDs>,
+}
+
+#[derive(Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+#[serde(untagged)]
+pub enum PaysafePaymentMethod {
+    Card { card: PaysafeCard },
 }
 
 #[derive(Debug, Serialize)]
@@ -103,6 +164,34 @@ pub enum TransactionType {
     Payment,
 }
 
+impl PaysafePaymentMethodDetails {
+    pub fn get_no_three_ds_account_id(
+        &self,
+        currency: Currency,
+    ) -> Result<Secret<String>, errors::ConnectorError> {
+        self.card
+            .as_ref()
+            .and_then(|cards| cards.get(&currency))
+            .and_then(|card| card.no_three_ds.clone())
+            .ok_or_else(|| errors::ConnectorError::InvalidConnectorConfig {
+                config: "Missing no_3ds account_id",
+            })
+    }
+
+    pub fn get_three_ds_account_id(
+        &self,
+        currency: Currency,
+    ) -> Result<Secret<String>, errors::ConnectorError> {
+        self.card
+            .as_ref()
+            .and_then(|cards| cards.get(&currency))
+            .and_then(|card| card.three_ds.clone())
+            .ok_or_else(|| errors::ConnectorError::InvalidConnectorConfig {
+                config: "Missing 3ds account_id",
+            })
+    }
+}
+
 impl TryFrom<&PaysafeRouterData<&PaymentsPreProcessingRouterData>> for PaysafePaymentHandleRequest {
     type Error = error_stack::Report<errors::ConnectorError>;
     fn try_from(
@@ -119,6 +208,7 @@ impl TryFrom<&PaysafeRouterData<&PaymentsPreProcessingRouterData>> for PaysafePa
                 .change_context(errors::ConnectorError::InvalidConnectorConfig {
                     config: "merchant_connector_account.metadata",
                 })?;
+        let currency = item.router_data.request.get_currency()?;
         match item.router_data.request.get_payment_method_data()?.clone() {
             PaymentMethodData::Card(req_card) => {
                 let card = PaysafeCard {
@@ -134,8 +224,9 @@ impl TryFrom<&PaysafeRouterData<&PaymentsPreProcessingRouterData>> for PaysafePa
                     },
                     holder_name: item.router_data.get_optional_billing_full_name(),
                 };
-                let account_id = metadata.account_id;
 
+                let payment_method = PaysafePaymentMethod::Card { card: card.clone() };
+                let account_id = metadata.account_id.get_no_three_ds_account_id(currency)?;
                 let amount = item.amount;
                 let payment_type = PaysafePaymentType::Card;
                 let transaction_type = TransactionType::Payment;
@@ -170,12 +261,13 @@ impl TryFrom<&PaysafeRouterData<&PaymentsPreProcessingRouterData>> for PaysafePa
                         item.router_data.request.capture_method,
                         Some(enums::CaptureMethod::Automatic) | None
                     ),
-                    card,
-                    currency_code: item.router_data.request.get_currency()?,
+                    payment_method,
+                    currency_code: currency,
                     payment_type,
                     transaction_type,
                     return_links,
                     account_id,
+                    three_ds: None,
                 })
             }
             _ => Err(errors::ConnectorError::NotImplemented(
@@ -192,6 +284,13 @@ pub struct PaysafePaymentHandleResponse {
     pub merchant_ref_num: String,
     pub payment_handle_token: Secret<String>,
     pub status: PaysafePaymentHandleStatus,
+    pub links: Option<Vec<PaymentLink>>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct PaymentLink {
+    pub rel: String,
+    pub href: String,
 }
 
 #[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]
@@ -206,6 +305,23 @@ pub enum PaysafePaymentHandleStatus {
     Completed,
 }
 
+impl TryFrom<PaysafePaymentHandleStatus> for common_enums::AttemptStatus {
+    type Error = error_stack::Report<errors::ConnectorError>;
+    fn try_from(item: PaysafePaymentHandleStatus) -> Result<Self, Self::Error> {
+        match item {
+            PaysafePaymentHandleStatus::Completed => Ok(Self::Authorized),
+            PaysafePaymentHandleStatus::Failed | PaysafePaymentHandleStatus::Expired => {
+                Ok(Self::Failure)
+            }
+            // We get an `Initiated` status, with a redirection link from the connector, which indicates that further action is required by the customer,
+            PaysafePaymentHandleStatus::Initiated => Ok(Self::AuthenticationPending),
+            PaysafePaymentHandleStatus::Payable | PaysafePaymentHandleStatus::Processing => {
+                Ok(Self::Pending)
+            }
+        }
+    }
+}
+
 #[derive(Debug, Serialize, Deserialize)]
 pub struct PaysafeMeta {
     pub payment_handle_token: Secret<String>,
@@ -287,6 +403,54 @@ impl<F>
     }
 }
 
+impl<F>
+    TryFrom<
+        ResponseRouterData<
+            F,
+            PaysafePaymentHandleResponse,
+            PaymentsAuthorizeData,
+            PaymentsResponseData,
+        >,
+    > for RouterData<F, PaymentsAuthorizeData, PaymentsResponseData>
+{
+    type Error = error_stack::Report<errors::ConnectorError>;
+    fn try_from(
+        item: ResponseRouterData<
+            F,
+            PaysafePaymentHandleResponse,
+            PaymentsAuthorizeData,
+            PaymentsResponseData,
+        >,
+    ) -> Result<Self, Self::Error> {
+        let url = match item.response.links.as_ref().and_then(|links| links.first()) {
+            Some(link) => link.href.clone(),
+            None => return Err(errors::ConnectorError::ResponseDeserializationFailed)?,
+        };
+        let redirection_data = Some(RedirectForm::Form {
+            endpoint: url,
+            method: Method::Get,
+            form_fields: Default::default(),
+        });
+        let connector_metadata = serde_json::json!(PaysafeMeta {
+            payment_handle_token: item.response.payment_handle_token.clone(),
+        });
+        Ok(Self {
+            status: common_enums::AttemptStatus::try_from(item.response.status)?,
+            response: Ok(PaymentsResponseData::TransactionResponse {
+                resource_id: ResponseId::NoResponseId,
+                redirection_data: Box::new(redirection_data),
+                mandate_reference: Box::new(None),
+                connector_metadata: Some(connector_metadata),
+                network_txn_id: None,
+                connector_response_reference_id: None,
+                incremental_authorization_allowed: None,
+                charges: None,
+            }),
+            ..item.data
+        })
+    }
+}
+
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct PaysafePaymentsRequest {
@@ -294,11 +458,11 @@ pub struct PaysafePaymentsRequest {
     pub amount: MinorUnit,
     pub settle_with_auth: bool,
     pub payment_handle_token: Secret<String>,
-    pub currency_code: enums::Currency,
+    pub currency_code: Currency,
     pub customer_ip: Option<Secret<String, IpAddress>>,
 }
 
-#[derive(Debug, Serialize, PartialEq)]
+#[derive(Debug, Serialize, Clone, PartialEq)]
 #[serde(rename_all = "camelCase")]
 pub struct PaysafeCard {
     pub card_num: CardNumber,
@@ -319,12 +483,6 @@ impl TryFrom<&PaysafeRouterData<&PaymentsAuthorizeRouterData>> for PaysafePaymen
     fn try_from(
         item: &PaysafeRouterData<&PaymentsAuthorizeRouterData>,
     ) -> Result<Self, Self::Error> {
-        if item.router_data.is_three_ds() {
-            Err(errors::ConnectorError::NotSupported {
-                message: "Card 3DS".to_string(),
-                connector: "Paysafe",
-            })?
-        };
         let payment_handle_token = Secret::new(item.router_data.get_preprocessing_id()?);
         let amount = item.amount;
         let customer_ip = Some(
@@ -345,6 +503,169 @@ impl TryFrom<&PaysafeRouterData<&PaymentsAuthorizeRouterData>> for PaysafePaymen
     }
 }
 
+impl TryFrom<&PaysafeRouterData<&PaymentsAuthorizeRouterData>> for PaysafePaymentHandleRequest {
+    type Error = error_stack::Report<errors::ConnectorError>;
+    fn try_from(
+        item: &PaysafeRouterData<&PaymentsAuthorizeRouterData>,
+    ) -> Result<Self, Self::Error> {
+        let metadata: PaysafeConnectorMetadataObject =
+            utils::to_connector_meta_from_secret(item.router_data.connector_meta_data.clone())
+                .change_context(errors::ConnectorError::InvalidConnectorConfig {
+                    config: "merchant_connector_account.metadata",
+                })?;
+        let redirect_url_success = item.router_data.request.get_complete_authorize_url()?;
+        let redirect_url = item.router_data.request.get_router_return_url()?;
+        let return_links = vec![
+            ReturnLink {
+                rel: LinkType::Default,
+                href: redirect_url.clone(),
+                method: Method::Get.to_string(),
+            },
+            ReturnLink {
+                rel: LinkType::OnCompleted,
+                href: redirect_url_success.clone(),
+                method: Method::Get.to_string(),
+            },
+            ReturnLink {
+                rel: LinkType::OnFailed,
+                href: redirect_url.clone(),
+                method: Method::Get.to_string(),
+            },
+            ReturnLink {
+                rel: LinkType::OnCancelled,
+                href: redirect_url.clone(),
+                method: Method::Get.to_string(),
+            },
+        ];
+        let amount = item.amount;
+        let currency_code = item.router_data.request.currency;
+        let settle_with_auth = matches!(
+            item.router_data.request.capture_method,
+            Some(enums::CaptureMethod::Automatic) | None
+        );
+        let transaction_type = TransactionType::Payment;
+        match item.router_data.request.payment_method_data.clone() {
+            PaymentMethodData::Card(req_card) => {
+                let card = PaysafeCard {
+                    card_num: req_card.card_number.clone(),
+                    card_expiry: PaysafeCardExpiry {
+                        month: req_card.card_exp_month.clone(),
+                        year: req_card.get_expiry_year_4_digit(),
+                    },
+                    cvv: if req_card.card_cvc.clone().expose().is_empty() {
+                        None
+                    } else {
+                        Some(req_card.card_cvc.clone())
+                    },
+                    holder_name: item.router_data.get_optional_billing_full_name(),
+                };
+                let payment_method = PaysafePaymentMethod::Card { card: card.clone() };
+                let payment_type = PaysafePaymentType::Card;
+                let headers = item.router_data.header_payload.clone();
+                let platform = headers
+                    .as_ref()
+                    .and_then(|headers| headers.x_client_platform.clone());
+                let device_channel = match platform {
+                    Some(common_enums::ClientPlatform::Web)
+                    | Some(common_enums::ClientPlatform::Unknown)
+                    | None => DeviceChannel::Browser,
+                    Some(common_enums::ClientPlatform::Ios)
+                    | Some(common_enums::ClientPlatform::Android) => DeviceChannel::Sdk,
+                };
+                let account_id = metadata.account_id.get_three_ds_account_id(currency_code)?;
+                let three_ds = Some(ThreeDs {
+                    merchant_url: item.router_data.request.get_router_return_url()?,
+                    device_channel,
+                    message_category: ThreeDsMessageCategory::Payment,
+                    authentication_purpose: ThreeDsAuthenticationPurpose::PaymentTransaction,
+                    requestor_challenge_preference: ThreeDsChallengePreference::ChallengeMandated,
+                });
+
+                Ok(Self {
+                    merchant_ref_num: item.router_data.connector_request_reference_id.clone(),
+                    amount,
+                    settle_with_auth,
+                    payment_method,
+                    currency_code,
+                    payment_type,
+                    transaction_type,
+                    return_links,
+                    account_id,
+                    three_ds,
+                })
+            }
+            _ => Err(errors::ConnectorError::NotImplemented(
+                "Payment Method".to_string(),
+            ))?,
+        }
+    }
+}
+
+impl TryFrom<&PaysafeRouterData<&PaymentsCompleteAuthorizeRouterData>> for PaysafePaymentsRequest {
+    type Error = error_stack::Report<errors::ConnectorError>;
+    fn try_from(
+        item: &PaysafeRouterData<&PaymentsCompleteAuthorizeRouterData>,
+    ) -> Result<Self, Self::Error> {
+        let paysafe_meta: PaysafeMeta = to_connector_meta(
+            item.router_data.request.connector_meta.clone(),
+        )
+        .change_context(errors::ConnectorError::InvalidConnectorConfig {
+            config: "connector_metadata",
+        })?;
+        let payment_handle_token = paysafe_meta.payment_handle_token;
+        let amount = item.amount;
+        let customer_ip = Some(
+            item.router_data
+                .request
+                .get_browser_info()?
+                .get_ip_address()?,
+        );
+
+        Ok(Self {
+            merchant_ref_num: item.router_data.connector_request_reference_id.clone(),
+            payment_handle_token,
+            amount,
+            settle_with_auth: item.router_data.request.is_auto_capture()?,
+            currency_code: item.router_data.request.currency,
+            customer_ip,
+        })
+    }
+}
+
+impl<F>
+    TryFrom<
+        ResponseRouterData<F, PaysafePaymentsResponse, CompleteAuthorizeData, PaymentsResponseData>,
+    > for RouterData<F, CompleteAuthorizeData, PaymentsResponseData>
+{
+    type Error = error_stack::Report<errors::ConnectorError>;
+    fn try_from(
+        item: ResponseRouterData<
+            F,
+            PaysafePaymentsResponse,
+            CompleteAuthorizeData,
+            PaymentsResponseData,
+        >,
+    ) -> Result<Self, Self::Error> {
+        Ok(Self {
+            status: get_paysafe_payment_status(
+                item.response.status,
+                item.data.request.capture_method,
+            ),
+            response: Ok(PaymentsResponseData::TransactionResponse {
+                resource_id: ResponseId::ConnectorTransactionId(item.response.id),
+                redirection_data: Box::new(None),
+                mandate_reference: Box::new(None),
+                connector_metadata: None,
+                network_txn_id: None,
+                connector_response_reference_id: None,
+                incremental_authorization_allowed: None,
+                charges: None,
+            }),
+            ..item.data
+        })
+    }
+}
+
 pub struct PaysafeAuthType {
     pub(super) username: Secret<String>,
     pub(super) password: Secret<String>,
@@ -402,6 +723,13 @@ pub fn get_paysafe_payment_status(
     }
 }
 
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(untagged)]
+pub enum PaysafeSyncResponse {
+    Payments(PaysafePaymentsSyncResponse),
+    PaymentHandles(PaysafePaymentHandlesSyncResponse),
+}
+
 // Paysafe Payments Response Structure
 #[derive(Debug, Clone, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
@@ -409,6 +737,12 @@ pub struct PaysafePaymentsSyncResponse {
     pub payments: Vec<PaysafePaymentsResponse>,
 }
 
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct PaysafePaymentHandlesSyncResponse {
+    pub payment_handles: Vec<PaysafePaymentHandleResponse>,
+}
+
 // Paysafe Payments Response Structure
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 #[serde(rename_all = "camelCase")]
@@ -427,30 +761,34 @@ pub struct PaysafeSettlementResponse {
     pub status: PaysafeSettlementStatus,
 }
 
-impl<F>
-    TryFrom<
-        ResponseRouterData<F, PaysafePaymentsSyncResponse, PaymentsSyncData, PaymentsResponseData>,
-    > for RouterData<F, PaymentsSyncData, PaymentsResponseData>
+impl<F> TryFrom<ResponseRouterData<F, PaysafeSyncResponse, PaymentsSyncData, PaymentsResponseData>>
+    for RouterData<F, PaymentsSyncData, PaymentsResponseData>
 {
     type Error = error_stack::Report<errors::ConnectorError>;
     fn try_from(
-        item: ResponseRouterData<
-            F,
-            PaysafePaymentsSyncResponse,
-            PaymentsSyncData,
-            PaymentsResponseData,
-        >,
+        item: ResponseRouterData<F, PaysafeSyncResponse, PaymentsSyncData, PaymentsResponseData>,
     ) -> Result<Self, Self::Error> {
-        let payment_handle = item
-            .response
-            .payments
-            .first()
-            .ok_or(errors::ConnectorError::ResponseDeserializationFailed)?;
+        let status = match &item.response {
+            PaysafeSyncResponse::Payments(sync_response) => {
+                let payment_response = sync_response
+                    .payments
+                    .first()
+                    .ok_or(errors::ConnectorError::ResponseDeserializationFailed)?;
+                get_paysafe_payment_status(
+                    payment_response.status,
+                    item.data.request.capture_method,
+                )
+            }
+            PaysafeSyncResponse::PaymentHandles(sync_response) => {
+                let payment_handle_response = sync_response
+                    .payment_handles
+                    .first()
+                    .ok_or(errors::ConnectorError::ResponseDeserializationFailed)?;
+                common_enums::AttemptStatus::try_from(payment_handle_response.status)?
+            }
+        };
         Ok(Self {
-            status: get_paysafe_payment_status(
-                payment_handle.status,
-                item.data.request.capture_method,
-            ),
+            status,
             response: Ok(PaymentsResponseData::TransactionResponse {
                 resource_id: ResponseId::NoResponseId,
                 redirection_data: Box::new(None),
@@ -700,6 +1038,6 @@ pub struct Error {
 
 #[derive(Debug, Serialize, Deserialize)]
 pub struct FieldError {
-    pub field: String,
+    pub field: Option<String>,
     pub error: String,
 }
diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs
index 0535088c884..ce4f694b7c5 100644
--- a/crates/hyperswitch_connectors/src/default_implementations.rs
+++ b/crates/hyperswitch_connectors/src/default_implementations.rs
@@ -1145,7 +1145,6 @@ macro_rules! default_imp_for_complete_authorize {
 }
 
 default_imp_for_complete_authorize!(
-    connectors::Paysafe,
     connectors::Silverflow,
     connectors::Vgs,
     connectors::Aci,
diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs
index c6bb9f10f0d..4b7d75ed772 100644
--- a/crates/hyperswitch_connectors/src/utils.rs
+++ b/crates/hyperswitch_connectors/src/utils.rs
@@ -2078,6 +2078,7 @@ pub trait PaymentsSyncRequestData {
     fn is_auto_capture(&self) -> Result<bool, Error>;
     fn get_connector_transaction_id(&self) -> CustomResult<String, errors::ConnectorError>;
     fn is_mandate_payment(&self) -> bool;
+    fn get_optional_connector_transaction_id(&self) -> Option<String>;
 }
 
 impl PaymentsSyncRequestData for PaymentsSyncData {
@@ -2105,6 +2106,13 @@ impl PaymentsSyncRequestData for PaymentsSyncData {
     fn is_mandate_payment(&self) -> bool {
         matches!(self.setup_future_usage, Some(FutureUsage::OffSession))
     }
+
+    fn get_optional_connector_transaction_id(&self) -> Option<String> {
+        match self.connector_transaction_id.clone() {
+            ResponseId::ConnectorTransactionId(txn_id) => Some(txn_id),
+            _ => None,
+        }
+    }
 }
 
 pub trait PaymentsPostSessionTokensRequestData {

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

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

## Description
Implement card 3ds flow for Paysafe

### 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?
Create a card 3ds payment for paysafe
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: _' \
--data '{
    "amount": 1500,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "customer_id": "aaaa",
    "payment_method": "card",
    "payment_method_type": "credit",
    "profile_id": "pro_ndJetHbOnmgPs4ed6FLr",
    "authentication_type" : "three_ds",
    "payment_method_data": {
        "card": {
            "card_number": "4000000000001091", 
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "Joseph",
            "card_cvc": "111"
            
        }

    },
     "billing": {
        "address": {
            "line1": "Singapore Changi Airport. 2nd flr",
            "line2": "",
            "line3": "",
            "city": "Downtown Core",
            "state": "Central Indiana America",
            "zip": "039393",
            "country": "SG",
            "first_name": "박성준",
            "last_name": "박성준"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
      "shipping": {
        "address": {
            "line1": "Singapore Changi Airport. 2nd flr",
            "line2": "",
            "line3": "",
            "city": "Downtown Core",
            "state": "Central Indiana America",
            "zip": "039393",
            "country": "SG",
            "first_name": "박성준",
            "last_name": "박성준"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
        "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": "127.0.0.1"
    }
}'
```

Response
```
{
    "payment_id": "pay_UZEf5nPy4rjBtygB8Wm1",
    "merchant_id": "merchant_1757407144",
    "status": "requires_customer_action",
    "amount": 1500,
    "net_amount": 1500,
    "shipping_cost": null,
    "amount_capturable": 1500,
    "amount_received": null,
    "connector": "paysafe",
    "client_secret": "pay_UZEf5nPy4rjBtygB8Wm1_secret_mMQoKXN8QCHYDAl0fYKQ",
    "created": "2025-09-09T09:23:44.962Z",
    "currency": "USD",
    "customer_id": "aaaa",
    "customer": {
        "id": "aaaa",
        "name": null,
        "email": null,
        "phone": null,
        "phone_country_code": null
    },
    "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": "card",
    "payment_method_data": {
        "card": {
            "last4": "1091",
            "card_type": "CREDIT",
            "card_network": "Visa",
            "card_issuer": "INTL HDQTRS-CENTER OWNED",
            "card_issuing_country": "UNITEDSTATES",
            "card_isin": "400000",
            "card_extended_bin": null,
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "Joseph",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "Downtown Core",
            "country": "SG",
            "line1": "Singapore Changi Airport. 2nd flr",
            "line2": "",
            "line3": "",
            "zip": "039393",
            "state": "Central Indiana America",
            "first_name": "박성준",
            "last_name": "박성준",
            "origin_zip": null
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "Downtown Core",
            "country": "SG",
            "line1": "Singapore Changi Airport. 2nd flr",
            "line2": "",
            "line3": "",
            "zip": "039393",
            "state": "Central Indiana America",
            "first_name": "박성준",
            "last_name": "박성준",
            "origin_zip": null
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": null,
    "email": null,
    "name": null,
    "phone": null,
    "return_url": null,
    "authentication_type": "three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": {
        "type": "redirect_to_url",
        "redirect_to_url": "http://localhost:8080/payments/redirect/pay_UZEf5nPy4rjBtygB8Wm1/merchant_1757407144/pay_UZEf5nPy4rjBtygB8Wm1_1"
    },
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "credit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "aaaa",
        "created_at": 1757409824,
        "expires": 1757413424,
        "secret": "epk_e1f53385eb104247b6d2a830b38696de"
    },
    "manual_retry_allowed": null,
    "connector_transaction_id": null,
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": {
        "redirect_response": null,
        "search_tags": null,
        "apple_pay_recurring_details": null,
        "gateway_system": "direct"
    },
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_ndJetHbOnmgPs4ed6FLr",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_uXktUCjj5V9CBf3x48Pz",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-09-09T09:38:44.962Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "127.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_channel": null,
    "payment_method_id": null,
    "network_transaction_id": null,
    "payment_method_status": null,
    "updated": "2025-09-09T09:23:46.684Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "capture_before": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null,
    "whole_connector_response": null,
    "enable_partial_authorization": null
}
```
complete the payment using redirection

2. sync payment 
```
curl --location 'http://localhost:8080/payments/pay_UZEf5nPy4rjBtygB8Wm1?force_sync=true' \
--header 'Accept: application/json' \
--header 'api-key: dev_tFbrAdIUEDW40STTsNQNk5dFxYgRoZPtPW7GtKCP7tbSapty2u1UuDBxl4zkLCxf'
```

Response
```
{
    "payment_id": "pay_UZEf5nPy4rjBtygB8Wm1",
    "merchant_id": "merchant_1757407144",
    "status": "succeeded",
    "amount": 1500,
    "net_amount": 1500,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 1500,
    "connector": "paysafe",
    "client_secret": "pay_UZEf5nPy4rjBtygB8Wm1_secret_mMQoKXN8QCHYDAl0fYKQ",
    "created": "2025-09-09T09:23:44.962Z",
    "currency": "USD",
    "customer_id": "aaaa",
    "customer": {
        "id": "aaaa",
        "name": null,
        "email": null,
        "phone": null,
        "phone_country_code": null
    },
    "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": "card",
    "payment_method_data": {
        "card": {
            "last4": "1091",
            "card_type": "CREDIT",
            "card_network": "Visa",
            "card_issuer": "INTL HDQTRS-CENTER OWNED",
            "card_issuing_country": "UNITEDSTATES",
            "card_isin": "400000",
            "card_extended_bin": null,
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "Joseph",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "Downtown Core",
            "country": "SG",
            "line1": "Singapore Changi Airport. 2nd flr",
            "line2": "",
            "line3": "",
            "zip": "039393",
            "state": "Central Indiana America",
            "first_name": "박성준",
            "last_name": "박성준",
            "origin_zip": null
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "Downtown Core",
            "country": "SG",
            "line1": "Singapore Changi Airport. 2nd flr",
            "line2": "",
            "line3": "",
            "zip": "039393",
            "state": "Central Indiana America",
            "first_name": "박성준",
            "last_name": "박성준",
            "origin_zip": null
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": null,
    "email": null,
    "name": null,
    "phone": null,
    "return_url": null,
    "authentication_type": "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": "credit",
    "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": "3dbc45d1-878b-446c-8b97-3d299aa3effc",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": {
        "redirect_response": null,
        "search_tags": null,
        "apple_pay_recurring_details": null,
        "gateway_system": "direct"
    },
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_ndJetHbOnmgPs4ed6FLr",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_uXktUCjj5V9CBf3x48Pz",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-09-09T09:38:44.962Z",
    "fingerprint": null,
    "browser_info": {
        "language": "nl-NL",
        "time_zone": 0,
        "ip_address": "127.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_channel": null,
    "payment_method_id": null,
    "network_transaction_id": null,
    "payment_method_status": null,
    "updated": "2025-09-09T09:25:27.509Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "capture_before": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null,
    "whole_connector_response": null,
    "enable_partial_authorization": null
}
```

cypress
<img width="1131" height="846" alt="Screenshot 2025-09-11 at 6 33 31 PM" src="https://github.com/user-attachments/assets/6e7669a6-6af3-4362-9afe-8cc8f832fd15" />

<img width="1201" height="813" alt="Screenshot 2025-09-11 at 6 33 59 PM" src="https://github.com/user-attachments/assets/3429e33b-6fbf-41a0-bb70-3de0add2f46c" />

After redirection i m using ngrok as , connector only allows https redirection. 

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