Bug: [FEATURE] L2-l3 data for checkout



### Feature Description

Add l2-l3 data for checkout

### Possible Implementation

https://www.checkout.com/docs/payments/manage-payments/submit-level-2-or-level-3-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?

None
diff --git a/crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs b/crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs
index 912ea1f5d3e..8ebe17a2acc 100644
--- a/crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs
@@ -1,7 +1,11 @@
-use common_enums::enums::{self, AttemptStatus, PaymentChannel};
+use common_enums::{
+    enums::{self, AttemptStatus, PaymentChannel},
+    CountryAlpha2,
+};
 use common_utils::{
     errors::{CustomResult, ParsingError},
     ext_traits::ByteSliceExt,
+    id_type::CustomerId,
     request::Method,
     types::MinorUnit,
 };
@@ -25,6 +29,7 @@ use hyperswitch_domain_models::{
 use hyperswitch_interfaces::{consts, errors, webhooks};
 use masking::{ExposeInterface, Secret};
 use serde::{Deserialize, Serialize};
+use serde_with::skip_serializing_none;
 use time::PrimitiveDateTime;
 use url::Url;
 
@@ -269,6 +274,54 @@ pub struct ReturnUrl {
     pub failure_url: Option<String>,
 }
 
+#[skip_serializing_none]
+#[derive(Debug, Default, Serialize)]
+pub struct CheckoutCustomer {
+    pub name: Option<CustomerId>,
+    pub tax_number: Option<Secret<String>>,
+}
+
+#[skip_serializing_none]
+#[derive(Debug, Default, Serialize)]
+pub struct CheckoutProcessing {
+    pub order_id: Option<String>,
+    pub tax_amount: Option<MinorUnit>,
+    pub discount_amount: Option<MinorUnit>,
+    pub duty_amount: Option<MinorUnit>,
+    pub shipping_amount: Option<MinorUnit>,
+    pub shipping_tax_amount: Option<MinorUnit>,
+}
+
+#[skip_serializing_none]
+#[derive(Debug, Default, Serialize)]
+pub struct CheckoutShippingAddress {
+    pub country: Option<CountryAlpha2>,
+    pub zip: Option<Secret<String>>,
+}
+
+#[skip_serializing_none]
+#[derive(Debug, Default, Serialize)]
+pub struct CheckoutShipping {
+    pub address: Option<CheckoutShippingAddress>,
+    pub from_address_zip: Option<String>,
+}
+
+#[skip_serializing_none]
+#[derive(Debug, Default, Serialize)]
+pub struct CheckoutLineItem {
+    pub commodity_code: Option<String>,
+    pub discount_amount: Option<MinorUnit>,
+    pub name: Option<String>,
+    pub quantity: Option<u16>,
+    pub reference: Option<String>,
+    pub tax_exempt: Option<bool>,
+    pub tax_amount: Option<MinorUnit>,
+    pub total_amount: Option<MinorUnit>,
+    pub unit_of_measure: Option<String>,
+    pub unit_price: Option<MinorUnit>,
+}
+
+#[skip_serializing_none]
 #[derive(Debug, Serialize)]
 pub struct PaymentsRequest {
     pub source: PaymentSource,
@@ -285,6 +338,12 @@ pub struct PaymentsRequest {
     pub payment_type: CheckoutPaymentType,
     pub merchant_initiated: Option<bool>,
     pub previous_payment_id: Option<String>,
+
+    // Level 2/3 data fields
+    pub customer: Option<CheckoutCustomer>,
+    pub processing: Option<CheckoutProcessing>,
+    pub shipping: Option<CheckoutShipping>,
+    pub items: Option<Vec<CheckoutLineItem>>,
 }
 
 #[derive(Debug, Serialize, Deserialize)]
@@ -512,8 +571,51 @@ impl TryFrom<&CheckoutRouterData<&PaymentsAuthorizeRouterData>> for PaymentsRequ
         let auth_type: CheckoutAuthType = connector_auth.try_into()?;
         let processing_channel_id = auth_type.processing_channel_id;
         let metadata = item.router_data.request.metadata.clone().map(Into::into);
+        let (customer, processing, shipping, items) =
+            if let Some(l2l3_data) = &item.router_data.l2_l3_data {
+                (
+                    Some(CheckoutCustomer {
+                        name: l2l3_data.customer_id.clone(),
+                        tax_number: l2l3_data.customer_tax_registration_id.clone(),
+                    }),
+                    Some(CheckoutProcessing {
+                        order_id: l2l3_data.merchant_order_reference_id.clone(),
+                        tax_amount: l2l3_data.order_tax_amount,
+                        discount_amount: l2l3_data.discount_amount,
+                        duty_amount: l2l3_data.duty_amount,
+                        shipping_amount: l2l3_data.shipping_cost,
+                        shipping_tax_amount: l2l3_data.shipping_amount_tax,
+                    }),
+                    Some(CheckoutShipping {
+                        address: Some(CheckoutShippingAddress {
+                            country: l2l3_data.shipping_country,
+                            zip: l2l3_data.shipping_destination_zip.clone(),
+                        }),
+                        from_address_zip: l2l3_data.shipping_origin_zip.clone().map(|z| z.expose()),
+                    }),
+                    l2l3_data.order_details.as_ref().map(|details| {
+                        details
+                            .iter()
+                            .map(|item| CheckoutLineItem {
+                                commodity_code: item.commodity_code.clone(),
+                                discount_amount: item.unit_discount_amount,
+                                name: Some(item.product_name.clone()),
+                                quantity: Some(item.quantity),
+                                reference: item.product_id.clone(),
+                                tax_exempt: None,
+                                tax_amount: item.total_tax_amount,
+                                total_amount: item.total_amount,
+                                unit_of_measure: item.unit_of_measure.clone(),
+                                unit_price: Some(item.amount),
+                            })
+                            .collect()
+                    }),
+                )
+            } else {
+                (None, None, None, None)
+            };
 
-        Ok(Self {
+        let request = Self {
             source: source_var,
             amount: item.amount.to_owned(),
             currency: item.router_data.request.currency.to_string(),
@@ -526,7 +628,13 @@ impl TryFrom<&CheckoutRouterData<&PaymentsAuthorizeRouterData>> for PaymentsRequ
             payment_type,
             merchant_initiated,
             previous_payment_id,
-        })
+            customer,
+            processing,
+            shipping,
+            items,
+        };
+
+        Ok(request)
     }
 }
 

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

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

## Description
- Add l2l3 data for checkout
- Note : checkout recommends some checks like line_items should sum up proper total and discount amount. But it doesn't throw any errors for this. Hence in this pr we are not adding any such validations .

## Request

```json
{
    "amount": 30000,
    "capture_method": "automatic",
    "currency": "USD",
    "confirm": true,
    "payment_method": "card",
    "payment_method_type": "credit",
    "billing": {
        "address": {
            "zip": "560095",
            "country": "AT",
            "first_name": "Sakil",
            "last_name": "Mostak",
            "line1": "Fasdf",
            "line2": "Fasdf",
            "city": "Fasdf"
        }
    },
    "payment_method_data": {
        "card": {
            "card_number": "4000000000009995",
            "card_exp_month": "01",
            "card_exp_year": "2026",
            "card_holder_name": "John Smith",
            "card_cvc": "100"
        }
    },

    //l2l3 data
    "merchant_order_reference_id":"fasdfasfasf",
    "customer_id": "nithxxinn",
    "order_tax_amount": 10000,
    "shipping_cost": 21,
    "discount_amount": 1,
    "duty_amount": 2,
    "shipping_amount_tax":22,
    "order_details": [ //l2 l3data
        {
            "commodity_code": "8471",
            "unit_discount_amount": 1200,
            "product_name": "Laptop",
            "quantity": 1,
            "product_id":"fafasdfasdfdasdfsadfsadfrewfdscrwefdscerwfdasewfdsacxzfdsasdf",
            "total_tax_amount": 5000,
            "amount": 8000,
            "unit_of_measure": "EA",
            "unit_price": 8000
        },
            {
            "commodity_code": "471",
            "unit_discount_amount": 34,
            "product_name": "Laptop",
            "quantity": 1,
            "product_id":"fas22df",
            "total_tax_amount": 3000,
            "amount": 4000,
            "unit_of_measure": "EA",
            "unit_price": 8500
        }
        
    ]
}
```

## Response

```json
{
    "payment_id": "pay_v01RncbYqwx0ZtB6x7yV",
    "merchant_id": "merchant_1758258316",
    "status": "succeeded",
    "amount": 30000,
    "net_amount": 40021,
    "shipping_cost": 21,
    "amount_capturable": 0,
    "amount_received": 40021,
    "connector": "checkout",
    "client_secret": "pay_v01RncbYqwx0ZtB6x7yV_secret_iolJ5SxVFNsQljPaO42C",
    "created": "2025-09-19T06:18:02.547Z",
    "currency": "USD",
    "customer_id": "nithxxinn",
    "customer": {
        "id": "nithxxinn",
        "name": null,
        "email": null,
        "phone": null,
        "phone_country_code": null
    },
    "description": null,
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "9995",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "400000",
            "card_extended_bin": null,
            "card_exp_month": "01",
            "card_exp_year": "2026",
            "card_holder_name": "John Smith",
            "payment_checks": {
                "avs_result": "G",
                "card_validation_result": "Y"
            },
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": null,
    "billing": {
        "address": {
            "city": "Fasdf",
            "country": "AT",
            "line1": "Fasdf",
            "line2": "Fasdf",
            "line3": null,
            "zip": "560095",
            "state": null,
            "first_name": "Sakil",
            "last_name": "Mostak",
            "origin_zip": null
        },
        "phone": null,
        "email": null
    },
    "order_details": [
        {
            "sku": null,
            "upc": null,
            "brand": null,
            "amount": 8000,
            "category": null,
            "quantity": 1,
            "tax_rate": null,
            "product_id": "fafasdfasdfdasdfsadfsadfrewfdscrwefdscerwfdasewfdsacxzfdsasdf",
            "description": null,
            "product_name": "Laptop",
            "product_type": null,
            "sub_category": null,
            "total_amount": null,
            "commodity_code": "8471",
            "unit_of_measure": "EA",
            "product_img_link": null,
            "product_tax_code": null,
            "total_tax_amount": 5000,
            "requires_shipping": null,
            "unit_discount_amount": 1200
        },
        {
            "sku": null,
            "upc": null,
            "brand": null,
            "amount": 4000,
            "category": null,
            "quantity": 1,
            "tax_rate": null,
            "product_id": "fas22df",
            "description": null,
            "product_name": "Laptop",
            "product_type": null,
            "sub_category": null,
            "total_amount": null,
            "commodity_code": "471",
            "unit_of_measure": "EA",
            "product_img_link": null,
            "product_tax_code": null,
            "total_tax_amount": 3000,
            "requires_shipping": null,
            "unit_discount_amount": 34
        }
    ],
    "email": null,
    "name": null,
    "phone": null,
    "return_url": null,
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "credit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "nithxxinn",
        "created_at": 1758262682,
        "expires": 1758266282,
        "secret": "epk_e62d911ac98c4ccea72189e40ad83af4"
    },
    "manual_retry_allowed": null,
    "connector_transaction_id": "pay_2667htrzd3qu7bx4fk36nkiva4",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": {
        "redirect_response": null,
        "search_tags": null,
        "apple_pay_recurring_details": null,
        "gateway_system": "direct"
    },
    "reference_id": "pay_v01RncbYqwx0ZtB6x7yV_1",
    "payment_link": null,
    "profile_id": "pro_tqppxjNXH3TckLuEmhDJ",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_z4uydERX9Y3VqfGMXeuh",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-09-19T06:33:02.547Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_channel": null,
    "payment_method_id": null,
    "network_transaction_id": "716806996896398",
    "payment_method_status": null,
    "updated": "2025-09-19T06:18:04.255Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "capture_before": null,
    "merchant_order_reference_id": "fasdfasfasf",
    "order_tax_amount": 10000,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null,
    "whole_connector_response": null,
    "enable_partial_authorization": null,
    "enable_overcapture": null,
    "is_overcapture_enabled": null,
    "network_details": null
}
```

## connector mapping

```json
{
  "source": {
    "type": "card",
    "number": "4000000000009995",
    "expiry_month": "01",
    "expiry_year": "2026",
    "cvv": "100"
  },
  "amount": 40021,
  "currency": "USD",
  "processing_channel_id": "pc_jx5lvimg4obe7nhoqnhptm6xoq",
  "3ds": {
    "enabled": false,
    "force_3ds": false,
    "eci": null,
    "cryptogram": null,
    "xid": null,
    "version": null,
    "challenge_indicator": "no_preference"
  },
  "success_url": "http://localhost:8080/payments/pay_v01RncbYqwx0ZtB6x7yV/merchant_1758258316/redirect/response/checkout?status=success",
  "failure_url": "http://localhost:8080/payments/pay_v01RncbYqwx0ZtB6x7yV/merchant_1758258316/redirect/response/checkout?status=failure",
  "capture": true,
  "reference": "pay_v01RncbYqwx0ZtB6x7yV_1",
  "payment_type": "Regular",
  "merchant_initiated": false,
  "customer": {
    "name": "nithxxinn"
  },
  "processing": {
    "order_id": "fasdfasfasf",
    "tax_amount": 10000,
    "discount_amount": 1,
    "duty_amount": 2,
    "shipping_amount": 21,
    "shipping_tax_amount": 22
  },
  "shipping": {
    "address": {}
  },
  "items": [
    {
      "commodity_code": "8471",
      "discount_amount": 1200,
      "name": "Laptop",
      "quantity": 1,
      "reference": "fafasdfasdfdasdfsadfsadfrewfdscrwefdscerwfdasewfdsacxzfdsasdf",
      "tax_amount": 5000,
      "unit_of_measure": "EA",
      "unit_price": 8000
    },
    {
      "commodity_code": "471",
      "discount_amount": 34,
      "name": "Laptop",
      "quantity": 1,
      "reference": "fas22df",
      "tax_amount": 3000,
      "unit_of_measure": "EA",
      "unit_price": 4000
    }
  ]
}
```

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


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

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