Bug: add Samsung pay mandate support for Cybersource



add Samsung pay mandate support for Cybersource
diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml
index b7b57690474..f9b9fb1b414 100644
--- a/crates/connector_configs/toml/sandbox.toml
+++ b/crates/connector_configs/toml/sandbox.toml
@@ -1215,6 +1215,8 @@ merchant_secret="Source verification key"
   payment_method_type = "google_pay"
 [[cybersource.wallet]]
   payment_method_type = "paze"
+[[cybersource.wallet]]
+  payment_method_type = "samsung_pay"
 [cybersource.connector_auth.SignatureKey]
 api_key="Key"
 key1="Merchant ID"
diff --git a/crates/hyperswitch_connectors/src/connectors/cybersource.rs b/crates/hyperswitch_connectors/src/connectors/cybersource.rs
index 5a60f6d705a..812d0391dec 100644
--- a/crates/hyperswitch_connectors/src/connectors/cybersource.rs
+++ b/crates/hyperswitch_connectors/src/connectors/cybersource.rs
@@ -312,6 +312,7 @@ impl ConnectorValidation for Cybersource {
             PaymentMethodDataType::Card,
             PaymentMethodDataType::ApplePay,
             PaymentMethodDataType::GooglePay,
+            PaymentMethodDataType::SamsungPay,
         ]);
         utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id())
     }
diff --git a/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs b/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
index b953fa73754..4652080397a 100644
--- a/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
@@ -251,6 +251,11 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
                     )),
                     Some(PaymentSolution::GooglePay),
                 ),
+                WalletData::SamsungPay(samsung_pay_data) => (
+                    (get_samsung_pay_payment_information(&samsung_pay_data)
+                        .attach_printable("Failed to get samsung pay payment information")?),
+                    Some(PaymentSolution::SamsungPay),
+                ),
                 WalletData::AliPayQr(_)
                 | WalletData::AliPayRedirect(_)
                 | WalletData::AliPayHkRedirect(_)
@@ -269,7 +274,6 @@ impl TryFrom<&SetupMandateRouterData> for CybersourceZeroMandateRequest {
                 | WalletData::PaypalRedirect(_)
                 | WalletData::PaypalSdk(_)
                 | WalletData::Paze(_)
-                | WalletData::SamsungPay(_)
                 | WalletData::TwintRedirect {}
                 | WalletData::VippsRedirect {}
                 | WalletData::TouchNGoRedirect(_)
@@ -1859,25 +1863,8 @@ impl
         let bill_to = build_bill_to(item.router_data.get_optional_billing(), email)?;
         let order_information = OrderInformationWithBill::from((item, Some(bill_to)));
 
-        let samsung_pay_fluid_data_value =
-            get_samsung_pay_fluid_data_value(&samsung_pay_data.payment_credential.token_data)?;
-
-        let samsung_pay_fluid_data_str = serde_json::to_string(&samsung_pay_fluid_data_value)
-            .change_context(errors::ConnectorError::RequestEncodingFailed)
-            .attach_printable("Failed to serialize samsung pay fluid data")?;
-
-        let payment_information =
-            PaymentInformation::SamsungPay(Box::new(SamsungPayPaymentInformation {
-                fluid_data: FluidData {
-                    value: Secret::new(consts::BASE64_ENGINE.encode(samsung_pay_fluid_data_str)),
-                    descriptor: Some(
-                        consts::BASE64_ENGINE.encode(FLUID_DATA_DESCRIPTOR_FOR_SAMSUNG_PAY),
-                    ),
-                },
-                tokenized_card: SamsungPayTokenizedCard {
-                    transaction_type: TransactionType::SamsungPay,
-                },
-            }));
+        let payment_information = get_samsung_pay_payment_information(&samsung_pay_data)
+            .attach_printable("Failed to get samsung pay payment information")?;
 
         let processing_information = ProcessingInformation::try_from((
             item,
@@ -1903,6 +1890,32 @@ impl
     }
 }
 
+fn get_samsung_pay_payment_information(
+    samsung_pay_data: &SamsungPayWalletData,
+) -> Result<PaymentInformation, error_stack::Report<errors::ConnectorError>> {
+    let samsung_pay_fluid_data_value =
+        get_samsung_pay_fluid_data_value(&samsung_pay_data.payment_credential.token_data)?;
+
+    let samsung_pay_fluid_data_str = serde_json::to_string(&samsung_pay_fluid_data_value)
+        .change_context(errors::ConnectorError::RequestEncodingFailed)
+        .attach_printable("Failed to serialize samsung pay fluid data")?;
+
+    let payment_information =
+        PaymentInformation::SamsungPay(Box::new(SamsungPayPaymentInformation {
+            fluid_data: FluidData {
+                value: Secret::new(consts::BASE64_ENGINE.encode(samsung_pay_fluid_data_str)),
+                descriptor: Some(
+                    consts::BASE64_ENGINE.encode(FLUID_DATA_DESCRIPTOR_FOR_SAMSUNG_PAY),
+                ),
+            },
+            tokenized_card: SamsungPayTokenizedCard {
+                transaction_type: TransactionType::SamsungPay,
+            },
+        }));
+
+    Ok(payment_information)
+}
+
 fn get_samsung_pay_fluid_data_value(
     samsung_pay_token_data: &hyperswitch_domain_models::payment_method_data::SamsungPayTokenData,
 ) -> Result<SamsungPayFluidDataValue, error_stack::Report<errors::ConnectorError>> {
diff --git a/crates/router/src/configs/defaults/payment_connector_required_fields.rs b/crates/router/src/configs/defaults/payment_connector_required_fields.rs
index 27cc485188b..3849054045c 100644
--- a/crates/router/src/configs/defaults/payment_connector_required_fields.rs
+++ b/crates/router/src/configs/defaults/payment_connector_required_fields.rs
@@ -9128,7 +9128,86 @@ impl Default for settings::RequiredFields {
                                     RequiredFieldFinal {
                                         mandate: HashMap::new(),
                                         non_mandate: HashMap::new(),
-                                        common: HashMap::new(),
+                                        common: HashMap::from(
+                                            [
+                                                (
+                                                    "billing.email".to_string(),
+                                                    RequiredFieldInfo {
+                                                        required_field: "payment_method_data.billing.email".to_string(),
+                                                        display_name: "email".to_string(),
+                                                        field_type: enums::FieldType::UserEmailAddress,
+                                                        value: None,
+                                                    }
+                                                ),
+                                                (
+                                                    "billing.address.first_name".to_string(),
+                                                    RequiredFieldInfo {
+                                                        required_field: "payment_method_data.billing.address.first_name".to_string(),
+                                                        display_name: "billing_first_name".to_string(),
+                                                        field_type: enums::FieldType::UserBillingName,
+                                                        value: None,
+                                                    }
+                                                ),
+                                                (
+                                                    "billing.address.last_name".to_string(),
+                                                    RequiredFieldInfo {
+                                                        required_field: "payment_method_data.billing.address.last_name".to_string(),
+                                                        display_name: "billing_last_name".to_string(),
+                                                        field_type: enums::FieldType::UserBillingName,
+                                                        value: None,
+                                                    }
+                                                ),
+                                                (
+                                                    "billing.address.city".to_string(),
+                                                    RequiredFieldInfo {
+                                                        required_field: "payment_method_data.billing.address.city".to_string(),
+                                                        display_name: "city".to_string(),
+                                                        field_type: enums::FieldType::UserAddressCity,
+                                                        value: None,
+                                                    }
+                                                ),
+                                                (
+                                                    "billing.address.state".to_string(),
+                                                    RequiredFieldInfo {
+                                                        required_field: "payment_method_data.billing.address.state".to_string(),
+                                                        display_name: "state".to_string(),
+                                                        field_type: enums::FieldType::UserAddressState,
+                                                        value: None,
+                                                    }
+                                                ),
+                                                (
+                                                    "billing.address.zip".to_string(),
+                                                    RequiredFieldInfo {
+                                                        required_field: "payment_method_data.billing.address.zip".to_string(),
+                                                        display_name: "zip".to_string(),
+                                                        field_type: enums::FieldType::UserAddressPincode,
+                                                        value: None,
+                                                    }
+                                                ),
+                                                (
+                                                    "billing.address.country".to_string(),
+                                                    RequiredFieldInfo {
+                                                        required_field: "payment_method_data.billing.address.country".to_string(),
+                                                        display_name: "country".to_string(),
+                                                        field_type: enums::FieldType::UserAddressCountry{
+                                                            options: vec![
+                                                                "ALL".to_string(),
+                                                            ]
+                                                        },
+                                                        value: None,
+                                                    }
+                                                ),
+                                                (
+                                                    "billing.address.line1".to_string(),
+                                                    RequiredFieldInfo {
+                                                        required_field: "payment_method_data.billing.address.line1".to_string(),
+                                                        display_name: "line1".to_string(),
+                                                        field_type: enums::FieldType::UserAddressLine1,
+                                                        value: None,
+                                                    }
+                                                )
+                                            ]
+                                        ),
                                     }
                                 ),
                             ]),

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

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

## Description
<!-- Describe your changes in detail -->
This pr adds Samsung pay mandate support for Cybersource and enable samsung pay for cybersource on sandbox.

### 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 Cybersource connector with Samsung pay enabled for it
-> Make a samsung pay off_session payment with amount set to zero. Payment will be in failed status with     `"error_message": "SERVER_ERROR"`. This is expected, we need to get off_session payments enabled for our cybersource account.
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: api-key' \
--data-raw '{
    "amount": 0,
    "currency": "USD",
    "confirm": true,
    "business_country": "US",
    "business_label": "default",
    "customer_id": "cu_1739882499",
    "setup_future_usage": "off_session",
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "return_url": "https://google.com",
    "email": "samsungpay@gmail.com",
    "name": "Joseph Doe",
    "phone": "999999999",
    "phone_country_code": "+65",
    "payment_type": "setup_mandate",
    "description": "Its my first payment request",
    "statement_descriptor_name": "Juspay",
    "statement_descriptor_suffix": "Router",
    "payment_method": "wallet",
    "payment_method_type": "samsung_pay",
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        }
    },
    "payment_method_data": {
        "wallet": {
            "samsung_pay": {
                "payment_credential": {
                    "3_d_s": {
                        "type": "S",
                        "version": "100",
                        "data": "samsung-pay-token"
                    },
                    "payment_card_brand": "VI",
                    "payment_currency_type": "USD",
                    "payment_last4_fpan": "1661",
                    "method": "3DS",
                    "recurring_payment": false
                }
            }
        }
    }
}
'
```
```
{
    "payment_id": "pay_WXbt7aYJDjO0DvyZRpsf",
    "merchant_id": "merchant_1739867293",
    "status": "failed",
    "amount": 0,
    "net_amount": 0,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "cybersource",
    "client_secret": "pay_WXbt7aYJDjO0DvyZRpsf_secret_qZUj82yqxmREZX3MBnTR",
    "created": "2025-02-18T08:29:27.643Z",
    "currency": "USD",
    "customer_id": "cu_1739867368",
    "customer": {
        "id": "cu_1739867368",
        "name": "Joseph Doe",
        "email": "samsungpay@gmail.com",
        "phone": "999999999",
        "phone_country_code": "+65"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "off_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "wallet",
    "payment_method_data": {
        "wallet": {
            "samsung_pay": {
                "last4": "1661",
                "card_network": "Visa",
                "type": 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": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": null,
    "email": "samsungpay@gmail.com",
    "name": "Joseph Doe",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "Juspay",
    "statement_descriptor_suffix": "Router",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": "SERVER_ERROR",
    "error_message": "SERVER_ERROR",
    "unified_code": "UE_9000",
    "unified_message": "Something went wrong",
    "payment_experience": null,
    "payment_method_type": "samsung_pay",
    "connector_label": "cybersource_US_default_default",
    "business_country": "US",
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "cu_1739867368",
        "created_at": 1739867367,
        "expires": 1739870967,
        "secret": "epk_7c1492ae7ec84198a64f11a089f48508"
    },
    "manual_retry_allowed": true,
    "connector_transaction_id": null,
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_lUspT7TZ8NWvqh6aWiEy",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_oGt66qf0Ct6bRlh8P9BN",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-02-18T08:44:27.643Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-02-18T08:29:28.698Z",
    "split_payments": null,
    "frm_metadata": null,
    "merchant_order_reference_id": null,
    "order_tax_amount": null,
    "connector_mandate_id": null
}
```


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
