Bug: [REFACTOR]: [Cybersource] Recurring Mandates Payments Flow 



### Feature Description

In recurring subsequent mandate payment (MIT) the payment was getting processed with card fetched from locker, it should being processed with `connector_mandate_id`

### Possible Implementation

Implement a check over `connector_mandate_id` rather than checking over Payment Method Data

### Have you spent some time checking if this feature request has been raised before?

- [X] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [X] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/router/src/connector/cybersource.rs b/crates/router/src/connector/cybersource.rs
index b300e97b44a..ac2d16c9610 100644
--- a/crates/router/src/connector/cybersource.rs
+++ b/crates/router/src/connector/cybersource.rs
@@ -785,7 +785,10 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
         req: &types::PaymentsAuthorizeRouterData,
         connectors: &settings::Connectors,
     ) -> CustomResult<String, errors::ConnectorError> {
-        if req.is_three_ds() && req.request.is_card() {
+        if req.is_three_ds()
+            && req.request.is_card()
+            && req.request.connector_mandate_id().is_none()
+        {
             Ok(format!(
                 "{}risk/v1/authentication-setups",
                 api::ConnectorCommon::base_url(self, connectors)
@@ -809,7 +812,10 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
             req.request.amount,
             req,
         ))?;
-        if req.is_three_ds() && req.request.is_card() {
+        if req.is_three_ds()
+            && req.request.is_card()
+            && req.request.connector_mandate_id().is_none()
+        {
             let connector_req =
                 cybersource::CybersourceAuthSetupRequest::try_from(&connector_router_data)?;
             Ok(RequestContent::Json(Box::new(connector_req)))
@@ -845,7 +851,10 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
         data: &types::PaymentsAuthorizeRouterData,
         res: types::Response,
     ) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
-        if data.is_three_ds() && data.request.is_card() {
+        if data.is_three_ds()
+            && data.request.is_card()
+            && data.request.connector_mandate_id().is_none()
+        {
             let response: cybersource::CybersourceAuthSetupResponse = res
                 .response
                 .parse_struct("Cybersource AuthSetupResponse")
diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs
index e83b23603e9..8beb81d9236 100644
--- a/crates/router/src/connector/cybersource/transformers.rs
+++ b/crates/router/src/connector/cybersource/transformers.rs
@@ -342,7 +342,7 @@ pub struct ApplePayPaymentInformation {
 #[derive(Debug, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct MandatePaymentInformation {
-    payment_instrument: Option<CybersoucrePaymentInstrument>,
+    payment_instrument: CybersoucrePaymentInstrument,
 }
 
 #[derive(Debug, Serialize)]
@@ -482,7 +482,7 @@ impl
         ),
     ) -> Self {
         let (action_list, action_token_types, authorization_options) =
-            if item.router_data.request.setup_future_usage.is_some() {
+            if item.router_data.request.setup_mandate_details.is_some() {
                 (
                     Some(vec![CybersourceActionsList::TokenCreate]),
                     Some(vec![CybersourceActionsTokenType::PaymentInstrument]),
@@ -871,139 +871,168 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>>
     fn try_from(
         item: &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>,
     ) -> Result<Self, Self::Error> {
-        match item.router_data.request.payment_method_data.clone() {
-            payments::PaymentMethodData::Card(ccard) => Self::try_from((item, ccard)),
-            payments::PaymentMethodData::Wallet(wallet_data) => match wallet_data {
-                payments::WalletData::ApplePay(apple_pay_data) => {
-                    match item.router_data.payment_method_token.clone() {
-                        Some(payment_method_token) => match payment_method_token {
-                            types::PaymentMethodToken::ApplePayDecrypt(decrypt_data) => {
-                                Self::try_from((item, decrypt_data))
-                            }
-                            types::PaymentMethodToken::Token(_) => {
-                                Err(errors::ConnectorError::InvalidWalletToken)?
-                            }
-                        },
-                        None => {
-                            let email = item.router_data.request.get_email()?;
-                            let bill_to = build_bill_to(item.router_data.get_billing()?, email)?;
-                            let order_information = OrderInformationWithBill::from((item, bill_to));
-                            let processing_information = ProcessingInformation::from((
-                                item,
-                                Some(PaymentSolution::ApplePay),
-                            ));
-                            let client_reference_information =
-                                ClientReferenceInformation::from(item);
-                            let payment_information = PaymentInformation::ApplePayToken(
-                                ApplePayTokenPaymentInformation {
-                                    fluid_data: FluidData {
-                                        value: Secret::from(apple_pay_data.payment_data),
-                                    },
-                                    tokenized_card: ApplePayTokenizedCard {
-                                        transaction_type: TransactionType::ApplePay,
-                                    },
+        match item.router_data.request.connector_mandate_id() {
+            Some(connector_mandate_id) => Self::try_from((item, connector_mandate_id)),
+            None => {
+                match item.router_data.request.payment_method_data.clone() {
+                    payments::PaymentMethodData::Card(ccard) => Self::try_from((item, ccard)),
+                    payments::PaymentMethodData::Wallet(wallet_data) => match wallet_data {
+                        payments::WalletData::ApplePay(apple_pay_data) => {
+                            match item.router_data.payment_method_token.clone() {
+                                Some(payment_method_token) => match payment_method_token {
+                                    types::PaymentMethodToken::ApplePayDecrypt(decrypt_data) => {
+                                        Self::try_from((item, decrypt_data))
+                                    }
+                                    types::PaymentMethodToken::Token(_) => {
+                                        Err(errors::ConnectorError::InvalidWalletToken)?
+                                    }
                                 },
-                            );
-                            let merchant_defined_information =
-                                item.router_data.request.metadata.clone().map(|metadata| {
-                                    Vec::<MerchantDefinedInformation>::foreign_from(
-                                        metadata.peek().to_owned(),
-                                    )
-                                });
-
-                            Ok(Self {
-                                processing_information,
-                                payment_information,
-                                order_information,
-                                client_reference_information,
-                                merchant_defined_information,
-                                consumer_authentication_information: None,
-                            })
+                                None => {
+                                    let email = item.router_data.request.get_email()?;
+                                    let bill_to =
+                                        build_bill_to(item.router_data.get_billing()?, email)?;
+                                    let order_information =
+                                        OrderInformationWithBill::from((item, bill_to));
+                                    let processing_information = ProcessingInformation::from((
+                                        item,
+                                        Some(PaymentSolution::ApplePay),
+                                    ));
+                                    let client_reference_information =
+                                        ClientReferenceInformation::from(item);
+                                    let payment_information = PaymentInformation::ApplePayToken(
+                                        ApplePayTokenPaymentInformation {
+                                            fluid_data: FluidData {
+                                                value: Secret::from(apple_pay_data.payment_data),
+                                            },
+                                            tokenized_card: ApplePayTokenizedCard {
+                                                transaction_type: TransactionType::ApplePay,
+                                            },
+                                        },
+                                    );
+                                    let merchant_defined_information =
+                                        item.router_data.request.metadata.clone().map(|metadata| {
+                                            Vec::<MerchantDefinedInformation>::foreign_from(
+                                                metadata.peek().to_owned(),
+                                            )
+                                        });
+
+                                    Ok(Self {
+                                        processing_information,
+                                        payment_information,
+                                        order_information,
+                                        client_reference_information,
+                                        merchant_defined_information,
+                                        consumer_authentication_information: None,
+                                    })
+                                }
+                            }
+                        }
+                        payments::WalletData::GooglePay(google_pay_data) => {
+                            Self::try_from((item, google_pay_data))
                         }
+                        payments::WalletData::AliPayQr(_)
+                        | payments::WalletData::AliPayRedirect(_)
+                        | payments::WalletData::AliPayHkRedirect(_)
+                        | payments::WalletData::MomoRedirect(_)
+                        | payments::WalletData::KakaoPayRedirect(_)
+                        | payments::WalletData::GoPayRedirect(_)
+                        | payments::WalletData::GcashRedirect(_)
+                        | payments::WalletData::ApplePayRedirect(_)
+                        | payments::WalletData::ApplePayThirdPartySdk(_)
+                        | payments::WalletData::DanaRedirect {}
+                        | payments::WalletData::GooglePayRedirect(_)
+                        | payments::WalletData::GooglePayThirdPartySdk(_)
+                        | payments::WalletData::MbWayRedirect(_)
+                        | payments::WalletData::MobilePayRedirect(_)
+                        | payments::WalletData::PaypalRedirect(_)
+                        | payments::WalletData::PaypalSdk(_)
+                        | payments::WalletData::SamsungPay(_)
+                        | payments::WalletData::TwintRedirect {}
+                        | payments::WalletData::VippsRedirect {}
+                        | payments::WalletData::TouchNGoRedirect(_)
+                        | payments::WalletData::WeChatPayRedirect(_)
+                        | payments::WalletData::WeChatPayQr(_)
+                        | payments::WalletData::CashappQr(_)
+                        | payments::WalletData::SwishQr(_) => {
+                            Err(errors::ConnectorError::NotImplemented(
+                                utils::get_unimplemented_payment_method_error_message(
+                                    "Cybersource",
+                                ),
+                            )
+                            .into())
+                        }
+                    },
+                    // If connector_mandate_id is present MandatePayment will be the PMD, the case will be handled in the first `if` clause.
+                    // This is a fallback implementation in the event of catastrophe.
+                    payments::PaymentMethodData::MandatePayment => {
+                        let connector_mandate_id =
+                            item.router_data.request.connector_mandate_id().ok_or(
+                                errors::ConnectorError::MissingRequiredField {
+                                    field_name: "connector_mandate_id",
+                                },
+                            )?;
+                        Self::try_from((item, connector_mandate_id))
+                    }
+                    payments::PaymentMethodData::CardRedirect(_)
+                    | payments::PaymentMethodData::PayLater(_)
+                    | payments::PaymentMethodData::BankRedirect(_)
+                    | payments::PaymentMethodData::BankDebit(_)
+                    | payments::PaymentMethodData::BankTransfer(_)
+                    | payments::PaymentMethodData::Crypto(_)
+                    | payments::PaymentMethodData::Reward
+                    | payments::PaymentMethodData::Upi(_)
+                    | payments::PaymentMethodData::Voucher(_)
+                    | payments::PaymentMethodData::GiftCard(_)
+                    | payments::PaymentMethodData::CardToken(_) => {
+                        Err(errors::ConnectorError::NotImplemented(
+                            utils::get_unimplemented_payment_method_error_message("Cybersource"),
+                        )
+                        .into())
                     }
                 }
-                payments::WalletData::GooglePay(google_pay_data) => {
-                    Self::try_from((item, google_pay_data))
-                }
-                payments::WalletData::AliPayQr(_)
-                | payments::WalletData::AliPayRedirect(_)
-                | payments::WalletData::AliPayHkRedirect(_)
-                | payments::WalletData::MomoRedirect(_)
-                | payments::WalletData::KakaoPayRedirect(_)
-                | payments::WalletData::GoPayRedirect(_)
-                | payments::WalletData::GcashRedirect(_)
-                | payments::WalletData::ApplePayRedirect(_)
-                | payments::WalletData::ApplePayThirdPartySdk(_)
-                | payments::WalletData::DanaRedirect {}
-                | payments::WalletData::GooglePayRedirect(_)
-                | payments::WalletData::GooglePayThirdPartySdk(_)
-                | payments::WalletData::MbWayRedirect(_)
-                | payments::WalletData::MobilePayRedirect(_)
-                | payments::WalletData::PaypalRedirect(_)
-                | payments::WalletData::PaypalSdk(_)
-                | payments::WalletData::SamsungPay(_)
-                | payments::WalletData::TwintRedirect {}
-                | payments::WalletData::VippsRedirect {}
-                | payments::WalletData::TouchNGoRedirect(_)
-                | payments::WalletData::WeChatPayRedirect(_)
-                | payments::WalletData::WeChatPayQr(_)
-                | payments::WalletData::CashappQr(_)
-                | payments::WalletData::SwishQr(_) => Err(errors::ConnectorError::NotImplemented(
-                    utils::get_unimplemented_payment_method_error_message("Cybersource"),
-                )
-                .into()),
-            },
-            payments::PaymentMethodData::MandatePayment => {
-                let processing_information = ProcessingInformation::from((item, None));
-                let payment_instrument =
-                    item.router_data
-                        .request
-                        .connector_mandate_id()
-                        .map(|mandate_token_id| CybersoucrePaymentInstrument {
-                            id: mandate_token_id,
-                        });
-
-                let email = item.router_data.request.get_email()?;
-                let bill_to = build_bill_to(item.router_data.get_billing()?, email)?;
-                let order_information = OrderInformationWithBill::from((item, bill_to));
-                let payment_information =
-                    PaymentInformation::MandatePayment(MandatePaymentInformation {
-                        payment_instrument,
-                    });
-                let client_reference_information = ClientReferenceInformation::from(item);
-                let merchant_defined_information =
-                    item.router_data.request.metadata.clone().map(|metadata| {
-                        Vec::<MerchantDefinedInformation>::foreign_from(metadata.peek().to_owned())
-                    });
-                Ok(Self {
-                    processing_information,
-                    payment_information,
-                    order_information,
-                    client_reference_information,
-                    merchant_defined_information,
-                    consumer_authentication_information: None,
-                })
-            }
-            payments::PaymentMethodData::CardRedirect(_)
-            | payments::PaymentMethodData::PayLater(_)
-            | payments::PaymentMethodData::BankRedirect(_)
-            | payments::PaymentMethodData::BankDebit(_)
-            | payments::PaymentMethodData::BankTransfer(_)
-            | payments::PaymentMethodData::Crypto(_)
-            | payments::PaymentMethodData::Reward
-            | payments::PaymentMethodData::Upi(_)
-            | payments::PaymentMethodData::Voucher(_)
-            | payments::PaymentMethodData::GiftCard(_)
-            | payments::PaymentMethodData::CardToken(_) => {
-                Err(errors::ConnectorError::NotImplemented(
-                    utils::get_unimplemented_payment_method_error_message("Cybersource"),
-                )
-                .into())
             }
         }
     }
 }
 
+impl
+    TryFrom<(
+        &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>,
+        String,
+    )> for CybersourcePaymentsRequest
+{
+    type Error = error_stack::Report<errors::ConnectorError>;
+    fn try_from(
+        (item, connector_mandate_id): (
+            &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>,
+            String,
+        ),
+    ) -> Result<Self, Self::Error> {
+        let processing_information = ProcessingInformation::from((item, None));
+        let payment_instrument = CybersoucrePaymentInstrument {
+            id: connector_mandate_id,
+        };
+        let email = item.router_data.request.get_email()?;
+        let bill_to = build_bill_to(item.router_data.get_billing()?, email)?;
+        let order_information = OrderInformationWithBill::from((item, bill_to));
+        let payment_information =
+            PaymentInformation::MandatePayment(MandatePaymentInformation { payment_instrument });
+        let client_reference_information = ClientReferenceInformation::from(item);
+        let merchant_defined_information =
+            item.router_data.request.metadata.clone().map(|metadata| {
+                Vec::<MerchantDefinedInformation>::foreign_from(metadata.peek().to_owned())
+            });
+        Ok(Self {
+            processing_information,
+            payment_information,
+            order_information,
+            client_reference_information,
+            merchant_defined_information,
+            consumer_authentication_information: None,
+        })
+    }
+}
+
 #[derive(Debug, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct CybersourceAuthSetupRequest {
diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs
index 9eb06d675a0..ad463fcf2b9 100644
--- a/crates/router/src/services/api.rs
+++ b/crates/router/src/services/api.rs
@@ -1524,12 +1524,12 @@ pub fn build_redirection_form(
                 // This is the iframe recommended by cybersource but the redirection happens inside this iframe once otp
                 // is received and we lose control of the redirection on user client browser, so to avoid that we have removed this iframe and directly consumed it.
                 // (PreEscaped(r#"<iframe id="step_up_iframe" style="border: none; margin-left: auto; margin-right: auto; display: block" height="800px" width="400px" name="stepUpIframe"></iframe>"#))
-                (PreEscaped(format!("<form id=\"step_up_form\" method=\"POST\" action=\"{step_up_url}\">
-                <input id=\"step_up_form_jwt_input\" type=\"hidden\" name=\"JWT\" value=\"{access_token}\">
+                (PreEscaped(format!("<form id=\"step-up-form\" method=\"POST\" action=\"{step_up_url}\">
+                <input type=\"hidden\" name=\"JWT\" value=\"{access_token}\">
               </form>")))
               (PreEscaped(r#"<script>
               window.onload = function() {
-              var stepUpForm = document.querySelector('#step_up_form'); if(stepUpForm) stepUpForm.submit();
+              var stepUpForm = document.querySelector('#step-up-form'); if(stepUpForm) stepUpForm.submit();
               }
               </script>"#))
             }}

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

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

## Description
<!-- Describe your changes in detail -->
In recurring subsequent mandate payment (MIT) the payment was getting processed with card fetched from locker, now it is being processed with `connector_mandate_id`

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->

- Create a CIT with zero and non-zero amount
<img width="1296" alt="Screenshot 2024-01-16 at 11 06 18 AM" src="https://github.com/juspay/hyperswitch/assets/55536657/e746049b-257e-4808-9587-583b7b2c78ef">
<img width="1285" alt="Screenshot 2024-01-16 at 11 06 50 AM" src="https://github.com/juspay/hyperswitch/assets/55536657/38888a7c-89ee-4e28-826f-f600a4c2780d">

- Create MIT using Mandate ID
<img width="1278" alt="Screenshot 2024-01-16 at 11 10 41 AM" src="https://github.com/juspay/hyperswitch/assets/55536657/04139f77-8628-4479-8a4d-15cd3f9d9aed">

## 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
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
