Bug: [FEATURE] integrate 3DS for Worldpay connector



### Feature Description

WorldPay offers 3DS capabilities for a card txn. Same needs to be integrated in HyperSwitch.

Flow - https://developer.worldpay.com/products/access/3ds/web

### Possible Implementation

Implementing 3DS for WorldPay requires one additional step of running DDC flows. Flow can be looked at in WorldPay's dev docs

1. Redirect to Worldpay's DDC
2. Redirect to Wordlpay's 3DS redirection form
3. Capture details in HyperSwitch's `/redirect/complete` flow
4. Perform a PSync (optional)

Reference
https://developer.worldpay.com/products/access/3ds/web

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

None
diff --git a/crates/diesel_models/src/query/payment_attempt.rs b/crates/diesel_models/src/query/payment_attempt.rs
index 85c69824892..0627fa0048a 100644
--- a/crates/diesel_models/src/query/payment_attempt.rs
+++ b/crates/diesel_models/src/query/payment_attempt.rs
@@ -171,11 +171,23 @@ impl PaymentAttempt {
         merchant_id: &common_utils::id_type::MerchantId,
         connector_txn_id: &str,
     ) -> StorageResult<Self> {
+        let (txn_id, txn_data) = common_utils::types::ConnectorTransactionId::form_id_and_data(
+            connector_txn_id.to_string(),
+        );
+        let connector_transaction_id = txn_id
+            .get_txn_id(txn_data.as_ref())
+            .change_context(DatabaseError::Others)
+            .attach_printable_lazy(|| {
+                format!(
+                    "Failed to retrieve txn_id for ({:?}, {:?})",
+                    txn_id, txn_data
+                )
+            })?;
         generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
             conn,
             dsl::merchant_id
                 .eq(merchant_id.to_owned())
-                .and(dsl::connector_transaction_id.eq(connector_txn_id.to_owned())),
+                .and(dsl::connector_transaction_id.eq(connector_transaction_id.to_owned())),
         )
         .await
     }
diff --git a/crates/hyperswitch_domain_models/src/router_response_types.rs b/crates/hyperswitch_domain_models/src/router_response_types.rs
index eca56b8c866..6682ac1ad44 100644
--- a/crates/hyperswitch_domain_models/src/router_response_types.rs
+++ b/crates/hyperswitch_domain_models/src/router_response_types.rs
@@ -163,6 +163,12 @@ pub enum RedirectForm {
     Mifinity {
         initialization_token: String,
     },
+    WorldpayDDCForm {
+        endpoint: url::Url,
+        method: Method,
+        form_fields: HashMap<String, String>,
+        collection_id: Option<String>,
+    },
 }
 
 impl From<(url::Url, Method)> for RedirectForm {
diff --git a/crates/router/src/connector/worldpay.rs b/crates/router/src/connector/worldpay.rs
index 7a4c4c5da97..97da36f39b3 100644
--- a/crates/router/src/connector/worldpay.rs
+++ b/crates/router/src/connector/worldpay.rs
@@ -624,6 +624,113 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
     }
 }
 
+impl api::PaymentsCompleteAuthorize for Worldpay {}
+impl
+    ConnectorIntegration<
+        api::CompleteAuthorize,
+        types::CompleteAuthorizeData,
+        types::PaymentsResponseData,
+    > for Worldpay
+{
+    fn get_headers(
+        &self,
+        req: &types::PaymentsCompleteAuthorizeRouterData,
+        connectors: &settings::Connectors,
+    ) -> CustomResult<Vec<(String, request::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: &types::PaymentsCompleteAuthorizeRouterData,
+        connectors: &settings::Connectors,
+    ) -> CustomResult<String, errors::ConnectorError> {
+        let connector_payment_id = req
+            .request
+            .connector_transaction_id
+            .clone()
+            .ok_or(errors::ConnectorError::MissingConnectorTransactionID)?;
+        let stage = match req.status {
+            enums::AttemptStatus::DeviceDataCollectionPending => "3dsDeviceData".to_string(),
+            _ => "3dsChallenges".to_string(),
+        };
+        Ok(format!(
+            "{}api/payments/{connector_payment_id}/{stage}",
+            self.base_url(connectors),
+        ))
+    }
+
+    fn get_request_body(
+        &self,
+        req: &types::PaymentsCompleteAuthorizeRouterData,
+        _connectors: &settings::Connectors,
+    ) -> CustomResult<RequestContent, errors::ConnectorError> {
+        let req_obj = WorldpayCompleteAuthorizationRequest::try_from(req)?;
+        Ok(RequestContent::Json(Box::new(req_obj)))
+    }
+
+    fn build_request(
+        &self,
+        req: &types::PaymentsCompleteAuthorizeRouterData,
+        connectors: &settings::Connectors,
+    ) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
+        let request = services::RequestBuilder::new()
+            .method(services::Method::Post)
+            .url(&types::PaymentsCompleteAuthorizeType::get_url(
+                self, req, connectors,
+            )?)
+            .headers(types::PaymentsCompleteAuthorizeType::get_headers(
+                self, req, connectors,
+            )?)
+            .set_body(types::PaymentsCompleteAuthorizeType::get_request_body(
+                self, req, connectors,
+            )?)
+            .build();
+        Ok(Some(request))
+    }
+
+    fn handle_response(
+        &self,
+        data: &types::PaymentsCompleteAuthorizeRouterData,
+        event_builder: Option<&mut ConnectorEvent>,
+        res: Response,
+    ) -> CustomResult<types::PaymentsCompleteAuthorizeRouterData, errors::ConnectorError> {
+        let response: WorldpayPaymentsResponse = res
+            .response
+            .parse_struct("WorldpayPaymentsResponse")
+            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
+        event_builder.map(|i| i.set_response_body(&response));
+        router_env::logger::info!(connector_response=?response);
+        let optional_correlation_id = res.headers.and_then(|headers| {
+            headers
+                .get("WP-CorrelationId")
+                .and_then(|header_value| header_value.to_str().ok())
+                .map(|id| id.to_string())
+        });
+        types::RouterData::foreign_try_from((
+            types::ResponseRouterData {
+                response,
+                data: data.clone(),
+                http_code: res.status_code,
+            },
+            optional_correlation_id,
+        ))
+        .change_context(errors::ConnectorError::ResponseHandlingFailed)
+    }
+
+    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::Refund for Worldpay {}
 impl api::RefundExecute for Worldpay {}
 impl api::RefundSync for Worldpay {}
@@ -900,3 +1007,20 @@ impl api::IncomingWebhook for Worldpay {
         Ok(Box::new(psync_body))
     }
 }
+
+impl services::ConnectorRedirectResponse for Worldpay {
+    fn get_flow_type(
+        &self,
+        _query_params: &str,
+        _json_payload: Option<serde_json::Value>,
+        action: services::PaymentAction,
+    ) -> CustomResult<enums::CallConnectorAction, errors::ConnectorError> {
+        match action {
+            services::PaymentAction::CompleteAuthorize => Ok(enums::CallConnectorAction::Trigger),
+            services::PaymentAction::PSync
+            | services::PaymentAction::PaymentAuthenticateCompleteAuthorize => {
+                Ok(enums::CallConnectorAction::Avoid)
+            }
+        }
+    }
+}
diff --git a/crates/router/src/connector/worldpay/requests.rs b/crates/router/src/connector/worldpay/requests.rs
index 3d0be891ebb..b0fa85a64c3 100644
--- a/crates/router/src/connector/worldpay/requests.rs
+++ b/crates/router/src/connector/worldpay/requests.rs
@@ -31,6 +31,8 @@ pub struct Instruction {
     pub value: PaymentValue,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub debt_repayment: Option<bool>,
+    #[serde(rename = "threeDS")]
+    pub three_ds: Option<ThreeDSRequest>,
 }
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -187,6 +189,44 @@ pub struct AutoSettlement {
     pub auto: bool,
 }
 
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ThreeDSRequest {
+    #[serde(rename = "type")]
+    pub three_ds_type: String,
+    pub mode: String,
+    pub device_data: ThreeDSRequestDeviceData,
+    pub challenge: ThreeDSRequestChallenge,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ThreeDSRequestDeviceData {
+    pub accept_header: String,
+    pub user_agent_header: String,
+    pub browser_language: Option<String>,
+    pub browser_screen_width: Option<u32>,
+    pub browser_screen_height: Option<u32>,
+    pub browser_color_depth: Option<String>,
+    pub time_zone: Option<String>,
+    pub browser_java_enabled: Option<bool>,
+    pub browser_javascript_enabled: Option<bool>,
+    pub channel: Option<ThreeDSRequestChannel>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "lowercase")]
+pub enum ThreeDSRequestChannel {
+    Browser,
+    Native,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ThreeDSRequestChallenge {
+    pub return_url: String,
+}
+
 #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
 #[serde(rename_all = "lowercase")]
 pub enum PaymentMethod {
@@ -237,3 +277,10 @@ pub struct WorldpayPartialRequest {
     pub value: PaymentValue,
     pub reference: String,
 }
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct WorldpayCompleteAuthorizationRequest {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub collection_reference: Option<String>,
+}
diff --git a/crates/router/src/connector/worldpay/response.rs b/crates/router/src/connector/worldpay/response.rs
index 0a7f690c3aa..edc3c26948f 100644
--- a/crates/router/src/connector/worldpay/response.rs
+++ b/crates/router/src/connector/worldpay/response.rs
@@ -1,6 +1,7 @@
 use error_stack::ResultExt;
 use masking::Secret;
 use serde::{Deserialize, Serialize};
+use url::Url;
 
 use super::requests::*;
 use crate::core::errors;
@@ -10,7 +11,7 @@ pub struct WorldpayPaymentsResponse {
     pub outcome: PaymentOutcome,
     pub transaction_reference: Option<String>,
     #[serde(flatten)]
-    pub other_fields: WorldpayPaymentResponseFields,
+    pub other_fields: Option<WorldpayPaymentResponseFields>,
 }
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -20,13 +21,13 @@ pub enum WorldpayPaymentResponseFields {
     DDCResponse(DDCResponse),
     FraudHighRisk(FraudHighRiskResponse),
     RefusedResponse(RefusedResponse),
+    ThreeDsChallenged(ThreeDsChallengedResponse),
 }
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct AuthorizedResponse {
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub payment_instrument: Option<PaymentsResPaymentInstrument>,
+    pub payment_instrument: PaymentsResPaymentInstrument,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub issuer: Option<Issuer>,
     #[serde(skip_serializing_if = "Option::is_none")]
@@ -67,6 +68,34 @@ pub struct ThreeDsResponse {
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
+pub struct ThreeDsChallengedResponse {
+    pub authentication: AuthenticationResponse,
+    pub challenge: ThreeDsChallenge,
+    #[serde(rename = "_actions")]
+    pub actions: CompleteThreeDsActionLink,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct AuthenticationResponse {
+    pub version: String,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct ThreeDsChallenge {
+    pub reference: String,
+    pub url: Url,
+    pub jwt: Secret<String>,
+    pub payload: Secret<String>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct CompleteThreeDsActionLink {
+    #[serde(rename = "complete3dsChallenge")]
+    pub complete_three_ds_challenge: ActionLink,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "lowercase")]
 pub enum IssuerResponse {
     Challenged,
     Frictionless,
@@ -82,16 +111,15 @@ pub struct DDCResponse {
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 pub struct DDCToken {
-    pub jwt: String,
-    pub url: String,
-    pub bin: String,
+    pub jwt: Secret<String>,
+    pub url: Url,
+    pub bin: Secret<String>,
 }
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 pub struct DDCActionLink {
     #[serde(rename = "supply3dsDeviceData")]
     supply_ddc_data: ActionLink,
-    method: String,
 }
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -105,11 +133,32 @@ pub enum PaymentOutcome {
     FraudHighRisk,
     #[serde(alias = "3dsDeviceDataRequired")]
     ThreeDsDeviceDataRequired,
-    ThreeDsChallenged,
     SentForCancellation,
     #[serde(alias = "3dsAuthenticationFailed")]
     ThreeDsAuthenticationFailed,
     SentForPartialRefund,
+    #[serde(alias = "3dsChallenged")]
+    ThreeDsChallenged,
+    #[serde(alias = "3dsUnavailable")]
+    ThreeDsUnavailable,
+}
+
+impl std::fmt::Display for PaymentOutcome {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Authorized => write!(f, "authorized"),
+            Self::Refused => write!(f, "refused"),
+            Self::SentForSettlement => write!(f, "sentForSettlement"),
+            Self::SentForRefund => write!(f, "sentForRefund"),
+            Self::FraudHighRisk => write!(f, "fraudHighRisk"),
+            Self::ThreeDsDeviceDataRequired => write!(f, "3dsDeviceDataRequired"),
+            Self::SentForCancellation => write!(f, "sentForCancellation"),
+            Self::ThreeDsAuthenticationFailed => write!(f, "3dsAuthenticationFailed"),
+            Self::SentForPartialRefund => write!(f, "sentForPartialRefund"),
+            Self::ThreeDsChallenged => write!(f, "3dsChallenged"),
+            Self::ThreeDsUnavailable => write!(f, "3dsUnavailable"),
+        }
+    }
 }
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -202,30 +251,33 @@ pub fn get_resource_id<T, F>(
 where
     F: Fn(String) -> T,
 {
-    let reference_id = match response.other_fields {
-        WorldpayPaymentResponseFields::AuthorizedResponse(res) => res
-            .links
-            .as_ref()
-            .and_then(|link| link.self_link.href.rsplit_once('/'))
-            .map(|(_, h)| urlencoding::decode(h))
-            .transpose()
-            .change_context(errors::ConnectorError::ResponseHandlingFailed)?
-            .map(|s| transform_fn(s.into_owned())),
-        WorldpayPaymentResponseFields::DDCResponse(res) => res
-            .actions
-            .supply_ddc_data
-            .href
-            .split('/')
-            .rev()
-            .nth(1)
-            .map(urlencoding::decode)
-            .transpose()
-            .change_context(errors::ConnectorError::ResponseHandlingFailed)?
-            .map(|s| transform_fn(s.into_owned())),
-        WorldpayPaymentResponseFields::FraudHighRisk(_) => None,
-        WorldpayPaymentResponseFields::RefusedResponse(_) => None,
-    };
-    reference_id
+    let optional_reference_id = response
+        .other_fields
+        .as_ref()
+        .and_then(|other_fields| match other_fields {
+            WorldpayPaymentResponseFields::AuthorizedResponse(res) => res
+                .links
+                .as_ref()
+                .and_then(|link| link.self_link.href.rsplit_once('/').map(|(_, h)| h)),
+            WorldpayPaymentResponseFields::DDCResponse(res) => {
+                res.actions.supply_ddc_data.href.split('/').nth_back(1)
+            }
+            WorldpayPaymentResponseFields::ThreeDsChallenged(res) => res
+                .actions
+                .complete_three_ds_challenge
+                .href
+                .split('/')
+                .nth_back(1),
+            WorldpayPaymentResponseFields::FraudHighRisk(_)
+            | WorldpayPaymentResponseFields::RefusedResponse(_) => None,
+        })
+        .map(|href| {
+            urlencoding::decode(href)
+                .map(|s| transform_fn(s.into_owned()))
+                .change_context(errors::ConnectorError::ResponseHandlingFailed)
+        })
+        .transpose()?;
+    optional_reference_id
         .or_else(|| connector_transaction_id.map(transform_fn))
         .ok_or_else(|| {
             errors::ConnectorError::MissingRequiredField {
@@ -256,8 +308,8 @@ impl Issuer {
 #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct PaymentsResPaymentInstrument {
-    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
-    pub payment_instrument_type: Option<String>,
+    #[serde(rename = "type")]
+    pub payment_instrument_type: String,
     pub card_bin: Option<String>,
     pub last_four: Option<String>,
     pub expiry_date: Option<ExpiryDate>,
@@ -268,22 +320,6 @@ pub struct PaymentsResPaymentInstrument {
     pub payment_account_reference: Option<String>,
 }
 
-impl PaymentsResPaymentInstrument {
-    pub fn new() -> Self {
-        Self {
-            payment_instrument_type: None,
-            card_bin: None,
-            last_four: None,
-            category: None,
-            expiry_date: None,
-            card_brand: None,
-            funding_type: None,
-            issuer_name: None,
-            payment_account_reference: None,
-        }
-    }
-}
-
 #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct RiskFactorsInner {
diff --git a/crates/router/src/connector/worldpay/transformers.rs b/crates/router/src/connector/worldpay/transformers.rs
index a0f2bfd2508..a28d3bff7ed 100644
--- a/crates/router/src/connector/worldpay/transformers.rs
+++ b/crates/router/src/connector/worldpay/transformers.rs
@@ -1,17 +1,20 @@
+use std::collections::HashMap;
+
 use api_models::payments::Address;
 use base64::Engine;
 use common_utils::{errors::CustomResult, ext_traits::OptionExt, pii, types::MinorUnit};
 use diesel_models::enums;
 use error_stack::ResultExt;
-use hyperswitch_connectors::utils::RouterData;
+use hyperswitch_connectors::utils::{PaymentsAuthorizeRequestData, RouterData};
 use masking::{ExposeInterface, PeekInterface, Secret};
 use serde::{Deserialize, Serialize};
 
 use super::{requests::*, response::*};
 use crate::{
-    connector::utils,
+    connector::utils::{self, AddressData},
     consts,
     core::errors,
+    services,
     types::{
         self, domain, transformers::ForeignTryFrom, PaymentsAuthorizeData, PaymentsResponseData,
     },
@@ -65,49 +68,40 @@ impl TryFrom<&Option<pii::SecretSerdeValue>> for WorldpayConnectorMetadataObject
 fn fetch_payment_instrument(
     payment_method: domain::PaymentMethodData,
     billing_address: Option<&Address>,
-    auth_type: enums::AuthenticationType,
 ) -> CustomResult<PaymentInstrument, errors::ConnectorError> {
     match payment_method {
-        domain::PaymentMethodData::Card(card) => {
-            if auth_type == enums::AuthenticationType::ThreeDs {
-                return Err(errors::ConnectorError::NotImplemented(
-                    "ThreeDS flow through worldpay".to_string(),
-                )
-                .into());
-            }
-            Ok(PaymentInstrument::Card(CardPayment {
-                payment_type: PaymentType::Plain,
-                expiry_date: ExpiryDate {
-                    month: utils::CardData::get_expiry_month_as_i8(&card)?,
-                    year: utils::CardData::get_expiry_year_as_i32(&card)?,
-                },
-                card_number: card.card_number,
-                cvc: card.card_cvc,
-                card_holder_name: card.nick_name,
-                billing_address: if let Some(address) =
-                    billing_address.and_then(|addr| addr.address.clone())
-                {
-                    Some(BillingAddress {
-                        address1: address.line1,
-                        address2: address.line2,
-                        address3: address.line3,
-                        city: address.city,
-                        state: address.state,
-                        postal_code: address.zip.get_required_value("zip").change_context(
-                            errors::ConnectorError::MissingRequiredField { field_name: "zip" },
-                        )?,
-                        country_code: address
-                            .country
-                            .get_required_value("country_code")
-                            .change_context(errors::ConnectorError::MissingRequiredField {
-                                field_name: "country_code",
-                            })?,
-                    })
-                } else {
-                    None
-                },
-            }))
-        }
+        domain::PaymentMethodData::Card(card) => Ok(PaymentInstrument::Card(CardPayment {
+            payment_type: PaymentType::Plain,
+            expiry_date: ExpiryDate {
+                month: utils::CardData::get_expiry_month_as_i8(&card)?,
+                year: utils::CardData::get_expiry_year_as_i32(&card)?,
+            },
+            card_number: card.card_number,
+            cvc: card.card_cvc,
+            card_holder_name: billing_address.and_then(|address| address.get_optional_full_name()),
+            billing_address: if let Some(address) =
+                billing_address.and_then(|addr| addr.address.clone())
+            {
+                Some(BillingAddress {
+                    address1: address.line1,
+                    address2: address.line2,
+                    address3: address.line3,
+                    city: address.city,
+                    state: address.state,
+                    postal_code: address.zip.get_required_value("zip").change_context(
+                        errors::ConnectorError::MissingRequiredField { field_name: "zip" },
+                    )?,
+                    country_code: address
+                        .country
+                        .get_required_value("country_code")
+                        .change_context(errors::ConnectorError::MissingRequiredField {
+                            field_name: "country_code",
+                        })?,
+                })
+            } else {
+                None
+            },
+        })),
         domain::PaymentMethodData::Wallet(wallet) => match wallet {
             domain::WalletData::GooglePay(data) => {
                 Ok(PaymentInstrument::Googlepay(WalletPayment {
@@ -230,6 +224,53 @@ impl
                 config: "metadata.merchant_name",
             },
         )?;
+        let three_ds = match item.router_data.auth_type {
+            enums::AuthenticationType::ThreeDs => {
+                let browser_info = item
+                    .router_data
+                    .request
+                    .browser_info
+                    .clone()
+                    .get_required_value("browser_info")
+                    .change_context(errors::ConnectorError::MissingRequiredField {
+                        field_name: "browser_info",
+                    })?;
+                let accept_header = browser_info
+                    .accept_header
+                    .get_required_value("accept_header")
+                    .change_context(errors::ConnectorError::MissingRequiredField {
+                        field_name: "accept_header",
+                    })?;
+                let user_agent_header = browser_info
+                    .user_agent
+                    .get_required_value("user_agent")
+                    .change_context(errors::ConnectorError::MissingRequiredField {
+                        field_name: "user_agent",
+                    })?;
+                Some(ThreeDSRequest {
+                    three_ds_type: "integrated".to_string(),
+                    mode: "always".to_string(),
+                    device_data: ThreeDSRequestDeviceData {
+                        accept_header,
+                        user_agent_header,
+                        browser_language: browser_info.language.clone(),
+                        browser_screen_width: browser_info.screen_width,
+                        browser_screen_height: browser_info.screen_height,
+                        browser_color_depth: browser_info
+                            .color_depth
+                            .map(|depth| depth.to_string()),
+                        time_zone: browser_info.time_zone.map(|tz| tz.to_string()),
+                        browser_java_enabled: browser_info.java_enabled,
+                        browser_javascript_enabled: browser_info.java_script_enabled,
+                        channel: Some(ThreeDSRequestChannel::Browser),
+                    },
+                    challenge: ThreeDSRequestChallenge {
+                        return_url: item.router_data.request.get_complete_authorize_url()?,
+                    },
+                })
+            }
+            _ => None,
+        };
         Ok(Self {
             instruction: Instruction {
                 settlement: item
@@ -252,7 +293,6 @@ impl
                 payment_instrument: fetch_payment_instrument(
                     item.router_data.request.payment_method_data.clone(),
                     item.router_data.get_optional_billing(),
-                    item.router_data.auth_type,
                 )?,
                 narrative: InstructionNarrative {
                     line1: merchant_name.expose(),
@@ -262,6 +302,7 @@ impl
                     currency: item.router_data.request.currency,
                 },
                 debt_repayment: None,
+                three_ds,
             },
             merchant: Merchant {
                 entity: entity_id.clone(),
@@ -321,6 +362,7 @@ impl From<PaymentOutcome> for enums::AttemptStatus {
                 Self::AutoRefunded
             }
             PaymentOutcome::Refused | PaymentOutcome::FraudHighRisk => Self::Failure,
+            PaymentOutcome::ThreeDsUnavailable => Self::AuthenticationFailed,
         }
     }
 }
@@ -363,42 +405,105 @@ impl From<EventType> for enums::RefundStatus {
     }
 }
 
-impl
+impl<F, T>
     ForeignTryFrom<(
-        types::PaymentsResponseRouterData<WorldpayPaymentsResponse>,
+        types::ResponseRouterData<F, WorldpayPaymentsResponse, T, PaymentsResponseData>,
         Option<String>,
-    )> for types::PaymentsAuthorizeRouterData
+    )> for types::RouterData<F, T, PaymentsResponseData>
 {
     type Error = error_stack::Report<errors::ConnectorError>;
     fn foreign_try_from(
         item: (
-            types::PaymentsResponseRouterData<WorldpayPaymentsResponse>,
+            types::ResponseRouterData<F, WorldpayPaymentsResponse, T, PaymentsResponseData>,
             Option<String>,
         ),
     ) -> Result<Self, Self::Error> {
         let (router_data, optional_correlation_id) = item;
-        let description = match router_data.response.other_fields {
-            WorldpayPaymentResponseFields::AuthorizedResponse(ref res) => res.description.clone(),
-            WorldpayPaymentResponseFields::DDCResponse(_)
-            | WorldpayPaymentResponseFields::FraudHighRisk(_)
-            | WorldpayPaymentResponseFields::RefusedResponse(_) => None,
+        let (description, redirection_data) = router_data
+            .response
+            .other_fields
+            .as_ref()
+            .map(|other_fields| match other_fields {
+                WorldpayPaymentResponseFields::AuthorizedResponse(res) => {
+                    (res.description.clone(), None)
+                }
+                WorldpayPaymentResponseFields::DDCResponse(res) => (
+                    None,
+                    Some(services::RedirectForm::WorldpayDDCForm {
+                        endpoint: res.device_data_collection.url.clone(),
+                        method: common_utils::request::Method::Post,
+                        collection_id: Some("SessionId".to_string()),
+                        form_fields: HashMap::from([
+                            (
+                                "Bin".to_string(),
+                                res.device_data_collection.bin.clone().expose(),
+                            ),
+                            (
+                                "JWT".to_string(),
+                                res.device_data_collection.jwt.clone().expose(),
+                            ),
+                        ]),
+                    }),
+                ),
+                WorldpayPaymentResponseFields::ThreeDsChallenged(res) => (
+                    None,
+                    Some(services::RedirectForm::Form {
+                        endpoint: res.challenge.url.to_string(),
+                        method: common_utils::request::Method::Post,
+                        form_fields: HashMap::from([(
+                            "JWT".to_string(),
+                            res.challenge.jwt.clone().expose(),
+                        )]),
+                    }),
+                ),
+                WorldpayPaymentResponseFields::FraudHighRisk(_)
+                | WorldpayPaymentResponseFields::RefusedResponse(_) => (None, None),
+            })
+            .unwrap_or((None, None));
+        let worldpay_status = router_data.response.outcome.clone();
+        let optional_reason = match worldpay_status {
+            PaymentOutcome::ThreeDsAuthenticationFailed => {
+                Some("3DS authentication failed from issuer".to_string())
+            }
+            PaymentOutcome::ThreeDsUnavailable => {
+                Some("3DS authentication unavailable from issuer".to_string())
+            }
+            PaymentOutcome::FraudHighRisk => {
+                Some("Transaction marked as high risk by Worldpay".to_string())
+            }
+            PaymentOutcome::Refused => Some("Transaction refused by issuer".to_string()),
+            _ => None,
         };
-        Ok(Self {
-            status: enums::AttemptStatus::from(router_data.response.outcome.clone()),
-            description,
-            response: Ok(PaymentsResponseData::TransactionResponse {
+        let status = enums::AttemptStatus::from(worldpay_status.clone());
+        let response = optional_reason.map_or(
+            Ok(PaymentsResponseData::TransactionResponse {
                 resource_id: types::ResponseId::foreign_try_from((
                     router_data.response,
                     optional_correlation_id.clone(),
                 ))?,
-                redirection_data: None,
+                redirection_data,
                 mandate_reference: None,
                 connector_metadata: None,
                 network_txn_id: None,
-                connector_response_reference_id: optional_correlation_id,
+                connector_response_reference_id: optional_correlation_id.clone(),
                 incremental_authorization_allowed: None,
                 charge_id: None,
             }),
+            |reason| {
+                Err(types::ErrorResponse {
+                    code: worldpay_status.to_string(),
+                    message: reason.clone(),
+                    reason: Some(reason),
+                    status_code: router_data.http_code,
+                    attempt_status: Some(status),
+                    connector_transaction_id: optional_correlation_id,
+                })
+            },
+        );
+        Ok(Self {
+            status,
+            description,
+            response,
             ..router_data.data
         })
     }
@@ -459,3 +564,17 @@ impl ForeignTryFrom<(WorldpayPaymentsResponse, Option<String>)> for types::Respo
         get_resource_id(item.0, item.1, Self::ConnectorTransactionId)
     }
 }
+
+impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for WorldpayCompleteAuthorizationRequest {
+    type Error = error_stack::Report<errors::ConnectorError>;
+    fn try_from(item: &types::PaymentsCompleteAuthorizeRouterData) -> Result<Self, Self::Error> {
+        let params = item
+            .request
+            .redirect_response
+            .as_ref()
+            .and_then(|redirect_response| redirect_response.params.as_ref())
+            .ok_or(errors::ConnectorError::ResponseDeserializationFailed)?;
+        serde_urlencoded::from_str::<Self>(params.peek())
+            .change_context(errors::ConnectorError::ResponseDeserializationFailed)
+    }
+}
diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs
index babf393bb3a..700fff16cf5 100644
--- a/crates/router/src/core/payments/flows.rs
+++ b/crates/router/src/core/payments/flows.rs
@@ -240,7 +240,6 @@ default_imp_for_complete_authorize!(
     connector::Wise,
     connector::Wellsfargo,
     connector::Wellsfargopayout,
-    connector::Worldpay,
     connector::Zen,
     connector::Zsl
 );
@@ -472,7 +471,6 @@ default_imp_for_connector_redirect_response!(
     connector::Wellsfargo,
     connector::Wellsfargopayout,
     connector::Wise,
-    connector::Worldpay,
     connector::Zsl
 );
 
diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs
index 579a4ac4ffd..0d84ba9c30f 100644
--- a/crates/router/src/services/api.rs
+++ b/crates/router/src/services/api.rs
@@ -1809,6 +1809,135 @@ pub fn build_redirection_form(
 
             }
         }
+        RedirectForm::WorldpayDDCForm {
+            endpoint,
+            method,
+            form_fields,
+            collection_id,
+        } => maud::html! {
+            (maud::DOCTYPE)
+            html {
+                meta name="viewport" content="width=device-width, initial-scale=1";
+                head {
+                    (PreEscaped(r##"
+                            <style>
+                                #loader1 {
+                                    width: 500px;
+                                }
+                                @media max-width: 600px {
+                                    #loader1 {
+                                        width: 200px;
+                                    }
+                                }
+                            </style>
+                        "##))
+                }
+
+                body style="background-color: #ffffff; padding: 20px; font-family: Arial, Helvetica, Sans-Serif;" {
+                    div id="loader1" class="lottie" style="height: 150px; display: block; position: relative; margin-left: auto; margin-right: auto;" { "" }
+                    (PreEscaped(r#"<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.4/lottie.min.js"></script>"#))
+                    (PreEscaped(r#"
+                        <script>
+                            var anime = bodymovin.loadAnimation({
+                                container: document.getElementById('loader1'),
+                                renderer: 'svg',
+                                loop: true,
+                                autoplay: true,
+                                name: 'hyperswitch loader',
+                                animationData: {"v":"4.8.0","meta":{"g":"LottieFiles AE 3.1.1","a":"","k":"","d":"","tc":""},"fr":29.9700012207031,"ip":0,"op":31.0000012626559,"w":400,"h":250,"nm":"loader_shape","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"circle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[278.25,202.671,0],"ix":2},"a":{"a":0,"k":[23.72,23.72,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[12.935,0],[0,-12.936],[-12.935,0],[0,12.935]],"o":[[-12.952,0],[0,12.935],[12.935,0],[0,-12.936]],"v":[[0,-23.471],[-23.47,0.001],[0,23.471],[23.47,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":19.99,"s":[100]},{"t":29.9800012211104,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[23.72,23.721],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"square 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[196.25,201.271,0],"ix":2},"a":{"a":0,"k":[22.028,22.03,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.914,0],[0,0],[0,-1.914],[0,0],[-1.914,0],[0,0],[0,1.914],[0,0]],"o":[[0,0],[-1.914,0],[0,0],[0,1.914],[0,0],[1.914,0],[0,0],[0,-1.914]],"v":[[18.313,-21.779],[-18.312,-21.779],[-21.779,-18.313],[-21.779,18.314],[-18.312,21.779],[18.313,21.779],[21.779,18.314],[21.779,-18.313]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14.99,"s":[100]},{"t":24.9800010174563,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[22.028,22.029],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":47.0000019143492,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Triangle 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[116.25,200.703,0],"ix":2},"a":{"a":0,"k":[27.11,21.243,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.558,-0.879],[0,0],[-1.133,0],[0,0],[0.609,0.947],[0,0]],"o":[[-0.558,-0.879],[0,0],[-0.609,0.947],[0,0],[1.133,0],[0,0],[0,0]],"v":[[1.209,-20.114],[-1.192,-20.114],[-26.251,18.795],[-25.051,20.993],[25.051,20.993],[26.251,18.795],[1.192,-20.114]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.427451010311,0.976470648074,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[10]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9.99,"s":[100]},{"t":19.9800008138021,"s":[10]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.11,21.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":48.0000019550801,"st":0,"bm":0}],"markers":[]}
+                            })
+                        </script>
+                    "#))
+                    h3 style="text-align: center;" { "Please wait while we process your payment..." }
+
+                    script {
+                        (PreEscaped(format!(
+                            r#"
+                                function submitCollectionReference(collectionReference) {{
+                                    var redirectPathname = window.location.pathname.replace(/payments\/redirect\/(\w+)\/(\w+)\/\w+/, "payments/$1/$2/redirect/complete/worldpay");
+                                    var redirectUrl = window.location.origin + redirectPathname;
+                                    try {{
+                                        if (typeof collectionReference === "string" && collectionReference.length > 0) {{
+                                            var form = document.createElement("form");
+                                            form.action = redirectPathname;
+                                            form.method = "GET";
+                                            var input = document.createElement("input");
+                                            input.type = "hidden";
+                                            input.name = "collectionReference";
+                                            input.value = collectionReference;
+                                            form.appendChild(input);
+                                            document.body.appendChild(form);
+                                            form.submit();;
+                                        }} else {{
+                                            window.location.replace(redirectUrl);
+                                        }}
+                                    }} catch (error) {{
+                                        window.location.replace(redirectUrl);
+                                    }}
+                                }}
+                                var allowedHost = "{}";
+                                var collectionField = "{}";
+                                window.addEventListener("message", function(event) {{
+                                    if (event.origin === allowedHost) {{
+                                        try {{
+                                            var data = JSON.parse(event.data);
+                                            if (collectionField.length > 0) {{
+                                                var collectionReference = data[collectionField];
+                                                return submitCollectionReference(collectionReference);
+                                            }} else {{
+                                                console.error("Collection field not found in event data (" + collectionField + ")"); 
+                                            }}
+                                        }} catch (error) {{
+                                            console.error("Error parsing event data: ", error);
+                                        }}
+                                    }} else {{
+                                        console.error("Invalid origin: " + event.origin, "Expected origin: " + allowedHost);
+                                    }}
+
+                                    submitCollectionReference("");
+                                }});
+
+                                // Redirect within 8 seconds if no collection reference is received
+                                window.setTimeout(submitCollectionReference, 8000);
+                            "#,
+                            endpoint.host_str().map_or(endpoint.as_ref().split('/').take(3).collect::<Vec<&str>>().join("/"), |host| format!("{}://{}", endpoint.scheme(), host)),
+                            collection_id.clone().unwrap_or("".to_string())))
+                        )
+                    }
+
+                    iframe
+                        style="display: none;"
+                        srcdoc=(
+                            maud::html! {
+                                (maud::DOCTYPE)
+                                html {
+                                    body {
+                                        form action=(PreEscaped(endpoint.to_string())) method=(method.to_string()) #payment_form {
+                                            @for (field, value) in form_fields {
+                                                input type="hidden" name=(field) value=(value);
+                                            }
+                                        }
+                                        (PreEscaped(format!(r#"
+                                            <script type="text/javascript"> {logging_template}
+                                                var form = document.getElementById("payment_form"); 
+                                                var formFields = form.querySelectorAll("input");
+                                                window.setTimeout(function () {{
+                                                    if (form.method.toUpperCase() === "GET" && formFields.length === 0) {{
+                                                        window.location.href = form.action;
+                                                    }} else {{
+                                                        form.submit();
+                                                    }}
+                                                }}, 300);
+                                            </script>
+                                        "#)))
+                                    }
+                                }
+                            }.into_string()
+                        )
+                        {}
+                }
+            }
+        },
     }
 }
 

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

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

## Description
Described in #6177 

### 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
Integrates Worldpay's 3DS flow

## How did you test it?
Things to test

1. 3DS payments using automatic capture
2. No 3DS payment using manual capture (full amount captures)
3. Payments Sync

<details>
    <summary>1. Create and capture</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_sMl3D9efytjSoXznSOKCv5PHyql50M1BMhU8e7qsa5m6mXxHvyROVg7cER7AWue9' \
        --data-raw '{"authentication_type":"three_ds","capture_method":"automatic","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","profile_id":"pro_OodaMoJ1Y9nlnegU4soR","amount":100,"currency":"USD","confirm":true,"connector":["worldpay"],"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"CA","zip":"94122","country":"US","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"}},"email":"john.doe@example.com","session_expiry":10000,"return_url":"https://example.com","off_session":true,"payment_method":"card","payment_method_type":"credit","payment_method_data":{"card":{"card_number":"4000000000001091","card_exp_month":"12","card_exp_year":"2030","card_holder_name":"John Doe","card_cvc":"123"}},"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":"en-US","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"},"statement_descriptor_name":"John","statement_descriptor_suffix":"JD","metadata":{"udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"}}'

Response

    {"payment_id":"pay_OjJGWEhFHNpycXbZmDNS","merchant_id":"merchant_1728979001","status":"requires_customer_action","amount":100,"net_amount":100,"shipping_cost":null,"amount_capturable":100,"amount_received":null,"connector":"worldpay","client_secret":"pay_OjJGWEhFHNpycXbZmDNS_secret_vMQEnVxtwo0xKYXtDTqh","created":"2024-10-21T10:59:11.767Z","currency":"USD","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","customer":{"id":"cus_sH6s3hf1uukqvYlAGkj5","name":"John Doe","email":"john.doe@example.com","phone":"999999999","phone_country_code":"+65"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":true,"capture_on":null,"capture_method":"automatic","payment_method":"card","payment_method_data":{"card":{"last4":"1091","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"400000","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"order_details":null,"email":"john.doe@example.com","name":"John Doe","phone":"999999999","return_url":"https://example.com/","authentication_type":"three_ds","statement_descriptor_name":"John","statement_descriptor_suffix":"JD","next_action":{"type":"redirect_to_url","redirect_to_url":"http://localhost:8080/payments/redirect/pay_OjJGWEhFHNpycXbZmDNS/merchant_1728979001/pay_OjJGWEhFHNpycXbZmDNS_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":"cus_sH6s3hf1uukqvYlAGkj5","created_at":1729508351,"expires":1729511951,"secret":"epk_dfac7bbac2014a18ae323ba08bfe7e9b"},"manual_retry_allowed":null,"connector_transaction_id":"eyJrIjoxLCJkIjoibVJjNnpNRE45cys3RmIxWjRRbWF2SWd4akc2VC9KdHZiVlgrTTRvTURQdzJJRGpBeDR3ekZaWWVqNDJzOTJkWiJ9","frm_message":null,"metadata":{"udf1":"value1","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"3fbccf2f-0ab5-494c-9909-1c8d2be40049","payment_link":null,"profile_id":"pro_OodaMoJ1Y9nlnegU4soR","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_omabWO9B7CSmaCMuT6eh","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2024-10-21T13:45:51.767Z","fingerprint":null,"browser_info":{"language":"en-US","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_method_id":null,"payment_method_status":null,"updated":"2024-10-21T10:59:12.308Z","charges":null,"frm_metadata":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null}

Open the link and continue

https://github.com/user-attachments/assets/52239cd6-fa40-45e3-88a9-231115ab8b20

</details>

<details>
    <summary>2. Create a payment using manual capture</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_sMl3D9efytjSoXznSOKCv5PHyql50M1BMhU8e7qsa5m6mXxHvyROVg7cER7AWue9' \
        --data-raw '{"authentication_type":"three_ds","capture_method":"manual","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","profile_id":"pro_OodaMoJ1Y9nlnegU4soR","amount":100,"currency":"USD","confirm":true,"connector":["worldpay"],"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"CA","zip":"94122","country":"US","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"}},"email":"john.doe@example.com","session_expiry":10000,"return_url":"https://example.com","off_session":true,"payment_method":"card","payment_method_type":"credit","payment_method_data":{"card":{"card_number":"4000000000001091","card_exp_month":"12","card_exp_year":"2030","card_holder_name":"John Doe","card_cvc":"123"}},"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":"en-US","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"},"statement_descriptor_name":"John","statement_descriptor_suffix":"JD","metadata":{"udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"}}'

Response

    {"payment_id":"pay_ff5lUcN6X1lg5f2PBtyv","merchant_id":"merchant_1728979001","status":"requires_customer_action","amount":100,"net_amount":100,"shipping_cost":null,"amount_capturable":100,"amount_received":null,"connector":"worldpay","client_secret":"pay_ff5lUcN6X1lg5f2PBtyv_secret_6iktmiNQwi8DAsIRNn4i","created":"2024-10-21T11:03:31.227Z","currency":"USD","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","customer":{"id":"cus_sH6s3hf1uukqvYlAGkj5","name":"John Doe","email":"john.doe@example.com","phone":"999999999","phone_country_code":"+65"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":true,"capture_on":null,"capture_method":"manual","payment_method":"card","payment_method_data":{"card":{"last4":"1096","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"520000","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"order_details":null,"email":"john.doe@example.com","name":"John Doe","phone":"999999999","return_url":"https://example.com/","authentication_type":"three_ds","statement_descriptor_name":"John","statement_descriptor_suffix":"JD","next_action":{"type":"redirect_to_url","redirect_to_url":"http://localhost:8080/payments/redirect/pay_ff5lUcN6X1lg5f2PBtyv/merchant_1728979001/pay_ff5lUcN6X1lg5f2PBtyv_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":"cus_sH6s3hf1uukqvYlAGkj5","created_at":1729508611,"expires":1729512211,"secret":"epk_739c58c7f7384fac89c7099e1cdae436"},"manual_retry_allowed":null,"connector_transaction_id":"eyJrIjoxLCJkIjoiQTRKMWZFZXlONnFqdGhHTWlmWDdYY2RzUVlIUzI2dEdLcEFPak9GRzNaczJJRGpBeDR3ekZaWWVqNDJzOTJkWiJ9","frm_message":null,"metadata":{"udf1":"value1","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"5b1cdf1c-26ea-42b1-b6e0-a02453ebf21f","payment_link":null,"profile_id":"pro_OodaMoJ1Y9nlnegU4soR","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_omabWO9B7CSmaCMuT6eh","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2024-10-21T13:50:11.227Z","fingerprint":null,"browser_info":{"language":"en-US","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_method_id":null,"payment_method_status":null,"updated":"2024-10-21T11:03:31.677Z","charges":null,"frm_metadata":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null}

cURL (capture)

    curl --location --request POST 'http://localhost:8080/payments/pay_ff5lUcN6X1lg5f2PBtyv/capture' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_sMl3D9efytjSoXznSOKCv5PHyql50M1BMhU8e7qsa5m6mXxHvyROVg7cER7AWue9' \
        --data '{}'

Response

    {"payment_id":"pay_ff5lUcN6X1lg5f2PBtyv","merchant_id":"merchant_1728979001","status":"processing","amount":100,"net_amount":100,"shipping_cost":null,"amount_capturable":100,"amount_received":null,"connector":"worldpay","client_secret":"pay_ff5lUcN6X1lg5f2PBtyv_secret_6iktmiNQwi8DAsIRNn4i","created":"2024-10-21T11:03:31.227Z","currency":"USD","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","customer":{"id":"cus_sH6s3hf1uukqvYlAGkj5","name":"John Doe","email":"john.doe@example.com","phone":"999999999","phone_country_code":"+65"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":true,"capture_on":null,"capture_method":"manual","payment_method":"card","payment_method_data":{"card":{"last4":"1096","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"520000","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"order_details":null,"email":"john.doe@example.com","name":"John Doe","phone":"999999999","return_url":"https://example.com/","authentication_type":"three_ds","statement_descriptor_name":"John","statement_descriptor_suffix":"JD","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":"eyJrIjoiazNhYjYzMiIsImxpbmtWZXJzaW9uIjoiNS4wLjAifQ==.sN:g8wd64bwkbrp0md+bPxcanBnk2zLdsIqSa1pR99GGg8fCNQpPLoWNslSzWNPFBM5Tpa8tW7EFI5onKINsgChMHeJVoeH2lrBWCRyjZYT6h+lbqfJa+1BSoKFSY8HLZqKn+Z0amZtgDGJNxxTouoBznrTCkCd2i5TQsznCawECpZY8cZeCeMgt1ol:GABqQG6u+T:u3sfHv3ezSOJxioRTixsThPEhzFW4ZZ6mObj3CK0rnFndcM0swvZMqgQwSEj5tBsydfzM4XKX20O6Nme96ha9twqxTSQIfr1rtl9V3q7fw3w5O9UZT4vQi9BMyCcaHkWSD:RbCWCcmiQqa:C6PZmXicLsKiJilUv0dIUwRuaBJaSeFhCq3fDWQC7:n7drI2adm1BXX7w39afnJw==","frm_message":null,"metadata":{"udf1":"value1","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"f7cb203a-aa4d-4fbc-84e7-9f6c1a9273a5","payment_link":null,"profile_id":"pro_OodaMoJ1Y9nlnegU4soR","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_omabWO9B7CSmaCMuT6eh","incremental_authorization_allowed":false,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2024-10-21T13:50:11.227Z","fingerprint":null,"browser_info":{"language":"en-US","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_method_id":null,"payment_method_status":null,"updated":"2024-10-21T11:04:47.760Z","charges":null,"frm_metadata":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null}

</details>

<details>
    <summary>3. Payments Sync</summary>

cURL

    curl --location --request GET 'http://localhost:8080/payments/pay_ff5lUcN6X1lg5f2PBtyv?force_sync=true' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_sMl3D9efytjSoXznSOKCv5PHyql50M1BMhU8e7qsa5m6mXxHvyROVg7cER7AWue9'

Response

    {"payment_id":"pay_ff5lUcN6X1lg5f2PBtyv","merchant_id":"merchant_1728979001","status":"processing","amount":100,"net_amount":100,"shipping_cost":null,"amount_capturable":100,"amount_received":null,"connector":"worldpay","client_secret":"pay_ff5lUcN6X1lg5f2PBtyv_secret_6iktmiNQwi8DAsIRNn4i","created":"2024-10-21T11:03:31.227Z","currency":"USD","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","customer":{"id":"cus_sH6s3hf1uukqvYlAGkj5","name":"John Doe","email":"john.doe@example.com","phone":"999999999","phone_country_code":"+65"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":true,"capture_on":null,"capture_method":"manual","payment_method":"card","payment_method_data":{"card":{"last4":"1096","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"520000","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"order_details":null,"email":"john.doe@example.com","name":"John Doe","phone":"999999999","return_url":"https://example.com/","authentication_type":"three_ds","statement_descriptor_name":"John","statement_descriptor_suffix":"JD","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":"eyJrIjoiazNhYjYzMiIsImxpbmtWZXJzaW9uIjoiNS4wLjAifQ==.sN:g8wd64bwkbrp0md+bPxcanBnk2zLdsIqSa1pR99GGg8fCNQpPLoWNslSzWNPFBM5Tpa8tW7EFI5onKINsgChMHeJVoeH2lrBWCRyjZYT6h+lbqfJa+1BSoKFSY8HLZqKn+Z0amZtgDGJNxxTouoBznrTCkCd2i5TQsznCawECpZY8cZeCeMgt1ol:GABqQG6u+T:u3sfHv3ezSOJxioRTixsThPEhzFW4ZZ6mObj3CK0rnFndcM0swvZMqgQwSEj5tBsydfzM4XKX20O6Nme96ha9twqxTSQIfr1rtl9V3q7fw3w5O9UZT4vQi9BMyCcaHkWSD:RbCWCcmiQqa:C6PZmXicLsKiJilUv0dIUwRuaBJaSeFhCq3fDWQC7:n7drI2adm1BXX7w39afnJw==","frm_message":null,"metadata":{"udf1":"value1","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"eaf9a1d5-031a-4b3b-b70b-ed9b7ec05406","payment_link":null,"profile_id":"pro_OodaMoJ1Y9nlnegU4soR","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_omabWO9B7CSmaCMuT6eh","incremental_authorization_allowed":false,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2024-10-21T13:50:11.227Z","fingerprint":null,"browser_info":{"language":"en-US","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_method_id":null,"payment_method_status":null,"updated":"2024-10-21T11:06:18.222Z","charges":null,"frm_metadata":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null}

</details>



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

Things to test

1. 3DS payments using automatic capture
2. No 3DS payment using manual capture (full amount captures)
3. Payments Sync

<details>
    <summary>1. Create and capture</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_sMl3D9efytjSoXznSOKCv5PHyql50M1BMhU8e7qsa5m6mXxHvyROVg7cER7AWue9' \
        --data-raw '{"authentication_type":"three_ds","capture_method":"automatic","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","profile_id":"pro_OodaMoJ1Y9nlnegU4soR","amount":100,"currency":"USD","confirm":true,"connector":["worldpay"],"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"CA","zip":"94122","country":"US","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"}},"email":"john.doe@example.com","session_expiry":10000,"return_url":"https://example.com","off_session":true,"payment_method":"card","payment_method_type":"credit","payment_method_data":{"card":{"card_number":"4000000000001091","card_exp_month":"12","card_exp_year":"2030","card_holder_name":"John Doe","card_cvc":"123"}},"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":"en-US","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"},"statement_descriptor_name":"John","statement_descriptor_suffix":"JD","metadata":{"udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"}}'

Response

    {"payment_id":"pay_OjJGWEhFHNpycXbZmDNS","merchant_id":"merchant_1728979001","status":"requires_customer_action","amount":100,"net_amount":100,"shipping_cost":null,"amount_capturable":100,"amount_received":null,"connector":"worldpay","client_secret":"pay_OjJGWEhFHNpycXbZmDNS_secret_vMQEnVxtwo0xKYXtDTqh","created":"2024-10-21T10:59:11.767Z","currency":"USD","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","customer":{"id":"cus_sH6s3hf1uukqvYlAGkj5","name":"John Doe","email":"john.doe@example.com","phone":"999999999","phone_country_code":"+65"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":true,"capture_on":null,"capture_method":"automatic","payment_method":"card","payment_method_data":{"card":{"last4":"1091","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"400000","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"order_details":null,"email":"john.doe@example.com","name":"John Doe","phone":"999999999","return_url":"https://example.com/","authentication_type":"three_ds","statement_descriptor_name":"John","statement_descriptor_suffix":"JD","next_action":{"type":"redirect_to_url","redirect_to_url":"http://localhost:8080/payments/redirect/pay_OjJGWEhFHNpycXbZmDNS/merchant_1728979001/pay_OjJGWEhFHNpycXbZmDNS_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":"cus_sH6s3hf1uukqvYlAGkj5","created_at":1729508351,"expires":1729511951,"secret":"epk_dfac7bbac2014a18ae323ba08bfe7e9b"},"manual_retry_allowed":null,"connector_transaction_id":"eyJrIjoxLCJkIjoibVJjNnpNRE45cys3RmIxWjRRbWF2SWd4akc2VC9KdHZiVlgrTTRvTURQdzJJRGpBeDR3ekZaWWVqNDJzOTJkWiJ9","frm_message":null,"metadata":{"udf1":"value1","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"3fbccf2f-0ab5-494c-9909-1c8d2be40049","payment_link":null,"profile_id":"pro_OodaMoJ1Y9nlnegU4soR","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_omabWO9B7CSmaCMuT6eh","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2024-10-21T13:45:51.767Z","fingerprint":null,"browser_info":{"language":"en-US","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_method_id":null,"payment_method_status":null,"updated":"2024-10-21T10:59:12.308Z","charges":null,"frm_metadata":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null}

Open the link and continue

https://github.com/user-attachments/assets/52239cd6-fa40-45e3-88a9-231115ab8b20

</details>

<details>
    <summary>2. Create a payment using manual capture</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_sMl3D9efytjSoXznSOKCv5PHyql50M1BMhU8e7qsa5m6mXxHvyROVg7cER7AWue9' \
        --data-raw '{"authentication_type":"three_ds","capture_method":"manual","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","profile_id":"pro_OodaMoJ1Y9nlnegU4soR","amount":100,"currency":"USD","confirm":true,"connector":["worldpay"],"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"CA","zip":"94122","country":"US","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"}},"email":"john.doe@example.com","session_expiry":10000,"return_url":"https://example.com","off_session":true,"payment_method":"card","payment_method_type":"credit","payment_method_data":{"card":{"card_number":"4000000000001091","card_exp_month":"12","card_exp_year":"2030","card_holder_name":"John Doe","card_cvc":"123"}},"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":"en-US","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"},"statement_descriptor_name":"John","statement_descriptor_suffix":"JD","metadata":{"udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"}}'

Response

    {"payment_id":"pay_ff5lUcN6X1lg5f2PBtyv","merchant_id":"merchant_1728979001","status":"requires_customer_action","amount":100,"net_amount":100,"shipping_cost":null,"amount_capturable":100,"amount_received":null,"connector":"worldpay","client_secret":"pay_ff5lUcN6X1lg5f2PBtyv_secret_6iktmiNQwi8DAsIRNn4i","created":"2024-10-21T11:03:31.227Z","currency":"USD","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","customer":{"id":"cus_sH6s3hf1uukqvYlAGkj5","name":"John Doe","email":"john.doe@example.com","phone":"999999999","phone_country_code":"+65"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":true,"capture_on":null,"capture_method":"manual","payment_method":"card","payment_method_data":{"card":{"last4":"1096","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"520000","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"order_details":null,"email":"john.doe@example.com","name":"John Doe","phone":"999999999","return_url":"https://example.com/","authentication_type":"three_ds","statement_descriptor_name":"John","statement_descriptor_suffix":"JD","next_action":{"type":"redirect_to_url","redirect_to_url":"http://localhost:8080/payments/redirect/pay_ff5lUcN6X1lg5f2PBtyv/merchant_1728979001/pay_ff5lUcN6X1lg5f2PBtyv_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":"cus_sH6s3hf1uukqvYlAGkj5","created_at":1729508611,"expires":1729512211,"secret":"epk_739c58c7f7384fac89c7099e1cdae436"},"manual_retry_allowed":null,"connector_transaction_id":"eyJrIjoxLCJkIjoiQTRKMWZFZXlONnFqdGhHTWlmWDdYY2RzUVlIUzI2dEdLcEFPak9GRzNaczJJRGpBeDR3ekZaWWVqNDJzOTJkWiJ9","frm_message":null,"metadata":{"udf1":"value1","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"5b1cdf1c-26ea-42b1-b6e0-a02453ebf21f","payment_link":null,"profile_id":"pro_OodaMoJ1Y9nlnegU4soR","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_omabWO9B7CSmaCMuT6eh","incremental_authorization_allowed":null,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2024-10-21T13:50:11.227Z","fingerprint":null,"browser_info":{"language":"en-US","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_method_id":null,"payment_method_status":null,"updated":"2024-10-21T11:03:31.677Z","charges":null,"frm_metadata":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null}

cURL (capture)

    curl --location --request POST 'http://localhost:8080/payments/pay_ff5lUcN6X1lg5f2PBtyv/capture' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_sMl3D9efytjSoXznSOKCv5PHyql50M1BMhU8e7qsa5m6mXxHvyROVg7cER7AWue9' \
        --data '{}'

Response

    {"payment_id":"pay_ff5lUcN6X1lg5f2PBtyv","merchant_id":"merchant_1728979001","status":"processing","amount":100,"net_amount":100,"shipping_cost":null,"amount_capturable":100,"amount_received":null,"connector":"worldpay","client_secret":"pay_ff5lUcN6X1lg5f2PBtyv_secret_6iktmiNQwi8DAsIRNn4i","created":"2024-10-21T11:03:31.227Z","currency":"USD","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","customer":{"id":"cus_sH6s3hf1uukqvYlAGkj5","name":"John Doe","email":"john.doe@example.com","phone":"999999999","phone_country_code":"+65"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":true,"capture_on":null,"capture_method":"manual","payment_method":"card","payment_method_data":{"card":{"last4":"1096","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"520000","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"order_details":null,"email":"john.doe@example.com","name":"John Doe","phone":"999999999","return_url":"https://example.com/","authentication_type":"three_ds","statement_descriptor_name":"John","statement_descriptor_suffix":"JD","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":"eyJrIjoiazNhYjYzMiIsImxpbmtWZXJzaW9uIjoiNS4wLjAifQ==.sN:g8wd64bwkbrp0md+bPxcanBnk2zLdsIqSa1pR99GGg8fCNQpPLoWNslSzWNPFBM5Tpa8tW7EFI5onKINsgChMHeJVoeH2lrBWCRyjZYT6h+lbqfJa+1BSoKFSY8HLZqKn+Z0amZtgDGJNxxTouoBznrTCkCd2i5TQsznCawECpZY8cZeCeMgt1ol:GABqQG6u+T:u3sfHv3ezSOJxioRTixsThPEhzFW4ZZ6mObj3CK0rnFndcM0swvZMqgQwSEj5tBsydfzM4XKX20O6Nme96ha9twqxTSQIfr1rtl9V3q7fw3w5O9UZT4vQi9BMyCcaHkWSD:RbCWCcmiQqa:C6PZmXicLsKiJilUv0dIUwRuaBJaSeFhCq3fDWQC7:n7drI2adm1BXX7w39afnJw==","frm_message":null,"metadata":{"udf1":"value1","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"f7cb203a-aa4d-4fbc-84e7-9f6c1a9273a5","payment_link":null,"profile_id":"pro_OodaMoJ1Y9nlnegU4soR","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_omabWO9B7CSmaCMuT6eh","incremental_authorization_allowed":false,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2024-10-21T13:50:11.227Z","fingerprint":null,"browser_info":{"language":"en-US","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_method_id":null,"payment_method_status":null,"updated":"2024-10-21T11:04:47.760Z","charges":null,"frm_metadata":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null}

</details>

<details>
    <summary>3. Payments Sync</summary>

cURL

    curl --location --request GET 'http://localhost:8080/payments/pay_ff5lUcN6X1lg5f2PBtyv?force_sync=true' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_sMl3D9efytjSoXznSOKCv5PHyql50M1BMhU8e7qsa5m6mXxHvyROVg7cER7AWue9'

Response

    {"payment_id":"pay_ff5lUcN6X1lg5f2PBtyv","merchant_id":"merchant_1728979001","status":"processing","amount":100,"net_amount":100,"shipping_cost":null,"amount_capturable":100,"amount_received":null,"connector":"worldpay","client_secret":"pay_ff5lUcN6X1lg5f2PBtyv_secret_6iktmiNQwi8DAsIRNn4i","created":"2024-10-21T11:03:31.227Z","currency":"USD","customer_id":"cus_sH6s3hf1uukqvYlAGkj5","customer":{"id":"cus_sH6s3hf1uukqvYlAGkj5","name":"John Doe","email":"john.doe@example.com","phone":"999999999","phone_country_code":"+65"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":true,"capture_on":null,"capture_method":"manual","payment_method":"card","payment_method_data":{"card":{"last4":"1096","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"520000","card_extended_bin":null,"card_exp_month":"12","card_exp_year":"2030","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":null},"payment_token":null,"shipping":null,"billing":{"address":{"city":"San Fransico","country":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"order_details":null,"email":"john.doe@example.com","name":"John Doe","phone":"999999999","return_url":"https://example.com/","authentication_type":"three_ds","statement_descriptor_name":"John","statement_descriptor_suffix":"JD","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":"eyJrIjoiazNhYjYzMiIsImxpbmtWZXJzaW9uIjoiNS4wLjAifQ==.sN:g8wd64bwkbrp0md+bPxcanBnk2zLdsIqSa1pR99GGg8fCNQpPLoWNslSzWNPFBM5Tpa8tW7EFI5onKINsgChMHeJVoeH2lrBWCRyjZYT6h+lbqfJa+1BSoKFSY8HLZqKn+Z0amZtgDGJNxxTouoBznrTCkCd2i5TQsznCawECpZY8cZeCeMgt1ol:GABqQG6u+T:u3sfHv3ezSOJxioRTixsThPEhzFW4ZZ6mObj3CK0rnFndcM0swvZMqgQwSEj5tBsydfzM4XKX20O6Nme96ha9twqxTSQIfr1rtl9V3q7fw3w5O9UZT4vQi9BMyCcaHkWSD:RbCWCcmiQqa:C6PZmXicLsKiJilUv0dIUwRuaBJaSeFhCq3fDWQC7:n7drI2adm1BXX7w39afnJw==","frm_message":null,"metadata":{"udf1":"value1","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":null,"reference_id":"eaf9a1d5-031a-4b3b-b70b-ed9b7ec05406","payment_link":null,"profile_id":"pro_OodaMoJ1Y9nlnegU4soR","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_omabWO9B7CSmaCMuT6eh","incremental_authorization_allowed":false,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2024-10-21T13:50:11.227Z","fingerprint":null,"browser_info":{"language":"en-US","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_method_id":null,"payment_method_status":null,"updated":"2024-10-21T11:06:18.222Z","charges":null,"frm_metadata":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null}

</details>
