Bug: [FEATURE] CELERO CIT_MIT (ALPHACONNECTOR)



### Feature Description

IMPLEMENT CIT & MIT for celero

### Possible Implementation

- DOCS : https://sandbox.gotnpgateway.com/docs/api/transactions#cit-mit

### 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/celero/transformers.rs b/crates/hyperswitch_connectors/src/connectors/celero/transformers.rs
index 2003ba59ae0..4e4c3f113cf 100644
--- a/crates/hyperswitch_connectors/src/connectors/celero/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/celero/transformers.rs
@@ -1,5 +1,5 @@
 use common_enums::{enums, Currency};
-use common_utils::{pii::Email, types::MinorUnit};
+use common_utils::{id_type::CustomerId, pii::Email, types::MinorUnit};
 use hyperswitch_domain_models::{
     address::Address as DomainAddress,
     payment_method_data::PaymentMethodData,
@@ -12,20 +12,24 @@ use hyperswitch_domain_models::{
         refunds::{Execute, RSync},
     },
     router_request_types::{PaymentsCaptureData, ResponseId},
-    router_response_types::{PaymentsResponseData, RefundsResponseData},
+    router_response_types::{MandateReference, PaymentsResponseData, RefundsResponseData},
     types::{
         PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
         RefundSyncRouterData, RefundsRouterData,
     },
 };
-use hyperswitch_interfaces::{consts, errors};
+use hyperswitch_interfaces::{
+    consts,
+    errors::{self},
+};
 use masking::{PeekInterface, Secret};
 use serde::{Deserialize, Serialize};
 
 use crate::{
     types::{RefundsResponseRouterData, ResponseRouterData},
     utils::{
-        AddressDetailsData, PaymentsAuthorizeRequestData, RefundsRequestData, RouterData as _,
+        get_unimplemented_payment_method_error_message, AddressDetailsData,
+        PaymentsAuthorizeRequestData, RefundsRequestData, RouterData as _,
     },
 };
 
@@ -87,6 +91,17 @@ pub struct CeleroPaymentsRequest {
     shipping_address: Option<CeleroAddress>,
     #[serde(skip_serializing_if = "Option::is_none")]
     create_vault_record: Option<bool>,
+    // CIT/MIT fields
+    #[serde(skip_serializing_if = "Option::is_none")]
+    card_on_file_indicator: Option<CardOnFileIndicator>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    initiated_by: Option<InitiatedBy>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    initial_transaction_id: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    stored_credential_indicator: Option<StoredCredentialIndicator>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    billing_method: Option<BillingMethod>,
 }
 
 #[derive(Debug, Serialize, PartialEq)]
@@ -135,8 +150,14 @@ impl TryFrom<&DomainAddress> for CeleroAddress {
 #[serde(rename_all = "lowercase")]
 pub enum CeleroPaymentMethod {
     Card(CeleroCard),
+    Customer(CeleroCustomer),
 }
 
+#[derive(Debug, Serialize, PartialEq)]
+pub struct CeleroCustomer {
+    id: Option<CustomerId>,
+    payment_method_id: Option<String>,
+}
 #[derive(Debug, Serialize, PartialEq, Clone, Copy)]
 #[serde(rename_all = "lowercase")]
 pub enum CeleroEntryType {
@@ -233,27 +254,102 @@ impl TryFrom<&CeleroRouterData<&PaymentsAuthorizeRouterData>> for CeleroPayments
             .get_optional_shipping()
             .and_then(|address| address.try_into().ok());
 
-        // Check if 3DS is requested
-        let is_three_ds = item.router_data.is_three_ds();
+        // Determine CIT/MIT fields based on mandate data
+        let (mandate_fields, payment_method) = determine_cit_mit_fields(item.router_data)?;
 
         let request = Self {
             idempotency_key: item.router_data.connector_request_reference_id.clone(),
             transaction_type,
             amount: item.amount,
             currency: item.router_data.request.currency,
-            payment_method: CeleroPaymentMethod::try_from((
-                &item.router_data.request.payment_method_data,
-                is_three_ds,
-            ))?,
+            payment_method,
             billing_address,
             shipping_address,
             create_vault_record: Some(false),
+            card_on_file_indicator: mandate_fields.card_on_file_indicator,
+            initiated_by: mandate_fields.initiated_by,
+            initial_transaction_id: mandate_fields.initial_transaction_id,
+            stored_credential_indicator: mandate_fields.stored_credential_indicator,
+            billing_method: mandate_fields.billing_method,
         };
 
         Ok(request)
     }
 }
 
+// Define a struct to hold CIT/MIT fields to avoid complex tuple return type
+#[derive(Debug, Default)]
+pub struct CeleroMandateFields {
+    pub card_on_file_indicator: Option<CardOnFileIndicator>,
+    pub initiated_by: Option<InitiatedBy>,
+    pub initial_transaction_id: Option<String>,
+    pub stored_credential_indicator: Option<StoredCredentialIndicator>,
+    pub billing_method: Option<BillingMethod>,
+}
+
+// Helper function to determine CIT/MIT fields based on mandate data
+fn determine_cit_mit_fields(
+    router_data: &PaymentsAuthorizeRouterData,
+) -> Result<(CeleroMandateFields, CeleroPaymentMethod), error_stack::Report<errors::ConnectorError>>
+{
+    // Default null values
+    let mut mandate_fields = CeleroMandateFields::default();
+
+    // First check if there's a mandate_id in the request
+    match router_data
+        .request
+        .mandate_id
+        .clone()
+        .and_then(|mandate_ids| mandate_ids.mandate_reference_id)
+    {
+        // If there's a connector mandate ID, this is a MIT (Merchant Initiated Transaction)
+        Some(api_models::payments::MandateReferenceId::ConnectorMandateId(
+            connector_mandate_id,
+        )) => {
+            mandate_fields.card_on_file_indicator = Some(CardOnFileIndicator::RecurringPayment);
+            mandate_fields.initiated_by = Some(InitiatedBy::Merchant); // This is a MIT
+            mandate_fields.stored_credential_indicator = Some(StoredCredentialIndicator::Used);
+            mandate_fields.billing_method = Some(BillingMethod::Recurring);
+            mandate_fields.initial_transaction_id =
+                connector_mandate_id.get_connector_mandate_request_reference_id();
+            Ok((
+                mandate_fields,
+                CeleroPaymentMethod::Customer(CeleroCustomer {
+                    id: Some(router_data.get_customer_id()?),
+                    payment_method_id: connector_mandate_id.get_payment_method_id(),
+                }),
+            ))
+        }
+        // For other mandate types that might not be supported
+        Some(api_models::payments::MandateReferenceId::NetworkMandateId(_))
+        | Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_)) => {
+            // These might need different handling or return an error
+            Err(errors::ConnectorError::NotImplemented(
+                get_unimplemented_payment_method_error_message("Celero"),
+            )
+            .into())
+        }
+        // If no mandate ID is present, check if it's a mandate payment
+        None => {
+            if router_data.request.is_mandate_payment() {
+                // This is a customer-initiated transaction for a recurring payment
+                mandate_fields.initiated_by = Some(InitiatedBy::Customer);
+                mandate_fields.card_on_file_indicator = Some(CardOnFileIndicator::RecurringPayment);
+                mandate_fields.billing_method = Some(BillingMethod::Recurring);
+                mandate_fields.stored_credential_indicator = Some(StoredCredentialIndicator::Used);
+            }
+            let is_three_ds = router_data.is_three_ds();
+            Ok((
+                mandate_fields,
+                CeleroPaymentMethod::try_from((
+                    &router_data.request.payment_method_data,
+                    is_three_ds,
+                ))?,
+            ))
+        }
+    }
+}
+
 // Auth Struct for CeleroCommerce API key authentication
 pub struct CeleroAuthType {
     pub(super) api_key: Secret<String>,
@@ -326,6 +422,38 @@ pub enum TransactionType {
     Sale,
     Authorize,
 }
+
+// CIT/MIT related enums
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+pub enum CardOnFileIndicator {
+    #[serde(rename = "C")]
+    GeneralPurposeStorage,
+    #[serde(rename = "R")]
+    RecurringPayment,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "lowercase")]
+pub enum InitiatedBy {
+    Customer,
+    Merchant,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "lowercase")]
+pub enum StoredCredentialIndicator {
+    Used,
+    Stored,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "lowercase")]
+pub enum BillingMethod {
+    Straight,
+    #[serde(rename = "initial_recurring")]
+    InitialRecurring,
+    Recurring,
+}
 #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
 #[serde_with::skip_serializing_none]
 pub struct CeleroTransactionResponseData {
@@ -337,6 +465,26 @@ pub struct CeleroTransactionResponseData {
     pub response: CeleroPaymentMethodResponse,
     pub billing_address: Option<CeleroAddressResponse>,
     pub shipping_address: Option<CeleroAddressResponse>,
+    // Additional fields from the sample response
+    pub status: Option<String>,
+    pub response_code: Option<i32>,
+    pub customer_id: Option<String>,
+    pub payment_method_id: Option<String>,
+}
+
+impl CeleroTransactionResponseData {
+    pub fn get_mandate_reference(&self) -> Box<Option<MandateReference>> {
+        if self.payment_method_id.is_some() {
+            Box::new(Some(MandateReference {
+                connector_mandate_id: None,
+                payment_method_id: self.payment_method_id.clone(),
+                mandate_metadata: None,
+                connector_mandate_request_reference_id: Some(self.id.clone()),
+            }))
+        } else {
+            Box::new(None)
+        }
+    }
 }
 
 #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
@@ -411,9 +559,11 @@ impl<F, T> TryFrom<ResponseRouterData<F, CeleroPaymentsResponse, T, PaymentsResp
                             Ok(Self {
                                 status: final_status,
                                 response: Ok(PaymentsResponseData::TransactionResponse {
-                                    resource_id: ResponseId::ConnectorTransactionId(data.id),
+                                    resource_id: ResponseId::ConnectorTransactionId(
+                                        data.id.clone(),
+                                    ),
                                     redirection_data: Box::new(None),
-                                    mandate_reference: Box::new(None),
+                                    mandate_reference: data.get_mandate_reference(),
                                     connector_metadata: None,
                                     network_txn_id: None,
                                     connector_response_reference_id: response.auth_code.clone(),
@@ -427,6 +577,7 @@ impl<F, T> TryFrom<ResponseRouterData<F, CeleroPaymentsResponse, T, PaymentsResp
                     }
                 } else {
                     // No transaction data in successful response
+                    // We don't have a transaction ID in this case
                     Ok(Self {
                         status: common_enums::AttemptStatus::Failure,
                         response: Err(hyperswitch_domain_models::router_data::ErrorResponse {
@@ -450,6 +601,10 @@ impl<F, T> TryFrom<ResponseRouterData<F, CeleroPaymentsResponse, T, PaymentsResp
                 let error_details =
                     CeleroErrorDetails::from_top_level_error(item.response.msg.clone());
 
+                // Extract transaction ID from the top-level data if available
+                let connector_transaction_id =
+                    item.response.data.as_ref().map(|data| data.id.clone());
+
                 Ok(Self {
                     status: common_enums::AttemptStatus::Failure,
                     response: Err(hyperswitch_domain_models::router_data::ErrorResponse {
@@ -460,7 +615,7 @@ impl<F, T> TryFrom<ResponseRouterData<F, CeleroPaymentsResponse, T, PaymentsResp
                         reason: error_details.decline_reason,
                         status_code: item.http_code,
                         attempt_status: None,
-                        connector_transaction_id: None,
+                        connector_transaction_id,
                         network_decline_code: None,
                         network_advice_code: None,
                         network_error_message: 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 -->
CIT-MIT implementation for CELERO (alpha connector)


DOCS
- https://sandbox.gotnpgateway.com/docs/api/transactions#cit-mit

Caviats
- Connector expecting initial_transaction_id which is nothing but prev txn id
- Connector not returning `payment_method_id` or `mandate_id`

### Note this is an alpha connector and we depend on alpha cypress test which 

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