Bug: Create Customer




diff --git a/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs b/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
index 4976c5fc61c..ebc244832b0 100644
--- a/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
@@ -20,7 +20,8 @@ use hyperswitch_domain_models::{
     router_flow_types::RSync,
     router_request_types::ResponseId,
     router_response_types::{
-        MandateReference, PaymentsResponseData, RedirectForm, RefundsResponseData,
+        ConnectorCustomerResponseData, MandateReference, PaymentsResponseData, RedirectForm,
+        RefundsResponseData,
     },
     types::{
         ConnectorCustomerRouterData, PaymentsAuthorizeRouterData, PaymentsCancelRouterData,
@@ -619,9 +620,9 @@ impl<F, T> TryFrom<ResponseRouterData<F, AuthorizedotnetCustomerResponse, T, Pay
         match item.response.messages.result_code {
             ResultCode::Ok => match item.response.customer_profile_id.clone() {
                 Some(connector_customer_id) => Ok(Self {
-                    response: Ok(PaymentsResponseData::ConnectorCustomerResponse {
-                        connector_customer_id,
-                    }),
+                    response: Ok(PaymentsResponseData::ConnectorCustomerResponse(
+                        ConnectorCustomerResponseData::new_with_customer_id(connector_customer_id),
+                    )),
                     ..item.data
                 }),
                 None => Err(
@@ -637,9 +638,11 @@ impl<F, T> TryFrom<ResponseRouterData<F, AuthorizedotnetCustomerResponse, T, Pay
                     error_message.and_then(|error| extract_customer_id(&error.text))
                 {
                     Ok(Self {
-                        response: Ok(PaymentsResponseData::ConnectorCustomerResponse {
-                            connector_customer_id,
-                        }),
+                        response: Ok(PaymentsResponseData::ConnectorCustomerResponse(
+                            ConnectorCustomerResponseData::new_with_customer_id(
+                                connector_customer_id,
+                            ),
+                        )),
                         ..item.data
                     })
                 } else {
diff --git a/crates/hyperswitch_connectors/src/connectors/chargebee.rs b/crates/hyperswitch_connectors/src/connectors/chargebee.rs
index 7f64c2f299d..632064a893d 100644
--- a/crates/hyperswitch_connectors/src/connectors/chargebee.rs
+++ b/crates/hyperswitch_connectors/src/connectors/chargebee.rs
@@ -13,7 +13,7 @@ use common_utils::{
 use error_stack::report;
 use error_stack::ResultExt;
 #[cfg(all(feature = "v2", feature = "revenue_recovery"))]
-use hyperswitch_domain_models::revenue_recovery;
+use hyperswitch_domain_models::{revenue_recovery, router_data_v2::RouterDataV2};
 use hyperswitch_domain_models::{
     router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
     router_flow_types::{
@@ -22,26 +22,29 @@ use hyperswitch_domain_models::{
         refunds::{Execute, RSync},
         revenue_recovery::InvoiceRecordBack,
         subscriptions::GetSubscriptionPlans,
+        CreateConnectorCustomer,
     },
     router_request_types::{
         revenue_recovery::InvoiceRecordBackRequest, subscriptions::GetSubscriptionPlansRequest,
-        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
-        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
-        RefundsData, SetupMandateRequestData,
+        AccessTokenRequestData, ConnectorCustomerData, PaymentMethodTokenizationData,
+        PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData,
+        PaymentsSyncData, RefundsData, SetupMandateRequestData,
     },
     router_response_types::{
         revenue_recovery::InvoiceRecordBackResponse, subscriptions::GetSubscriptionPlansResponse,
         ConnectorInfo, PaymentsResponseData, RefundsResponseData,
     },
     types::{
-        GetSubscriptionPlansRouterData, InvoiceRecordBackRouterData, PaymentsAuthorizeRouterData,
-        PaymentsCaptureRouterData, PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData,
+        ConnectorCustomerRouterData, GetSubscriptionPlansRouterData, InvoiceRecordBackRouterData,
+        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
+        RefundSyncRouterData, RefundsRouterData,
     },
 };
 use hyperswitch_interfaces::{
     api::{
-        self, subscriptions_v2::GetSubscriptionPlansV2, ConnectorCommon, ConnectorCommonExt,
-        ConnectorIntegration, ConnectorSpecifications, ConnectorValidation,
+        self, payments::ConnectorCustomer, subscriptions_v2::GetSubscriptionPlansV2,
+        ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
+        ConnectorValidation,
     },
     configs::Connectors,
     connector_integration_v2::ConnectorIntegrationV2,
@@ -72,7 +75,7 @@ impl Chargebee {
         }
     }
 }
-
+impl ConnectorCustomer for Chargebee {}
 impl api::Payment for Chargebee {}
 impl api::PaymentSession for Chargebee {}
 impl api::ConnectorAccessToken for Chargebee {}
@@ -85,6 +88,7 @@ impl api::Refund for Chargebee {}
 impl api::RefundExecute for Chargebee {}
 impl api::RefundSync for Chargebee {}
 impl api::PaymentToken for Chargebee {}
+
 #[cfg(all(feature = "v2", feature = "revenue_recovery"))]
 impl api::revenue_recovery::RevenueRecoveryRecordBack for Chargebee {}
 
@@ -792,6 +796,112 @@ impl
     // Not implemented (R)
 }
 
+impl ConnectorIntegration<CreateConnectorCustomer, ConnectorCustomerData, PaymentsResponseData>
+    for Chargebee
+{
+    fn get_headers(
+        &self,
+        req: &ConnectorCustomerRouterData,
+        connectors: &Connectors,
+    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
+        self.build_headers(req, connectors)
+    }
+
+    fn get_url(
+        &self,
+        req: &ConnectorCustomerRouterData,
+        connectors: &Connectors,
+    ) -> CustomResult<String, errors::ConnectorError> {
+        let metadata: chargebee::ChargebeeMetadata =
+            utils::to_connector_meta_from_secret(req.connector_meta_data.clone())?;
+
+        let site = metadata.site.peek();
+
+        let mut base = self.base_url(connectors).to_string();
+
+        base = base.replace("{{merchant_endpoint_prefix}}", site);
+        base = base.replace("$", site);
+
+        if base.contains("{{merchant_endpoint_prefix}}") || base.contains('$') {
+            return Err(errors::ConnectorError::InvalidConnectorConfig {
+                config: "Chargebee base_url has an unresolved placeholder (expected `$` or `{{merchant_endpoint_prefix}}`).",
+            }
+            .into());
+        }
+
+        if !base.ends_with('/') {
+            base.push('/');
+        }
+
+        let url = format!("{base}v2/customers");
+        Ok(url)
+    }
+
+    fn get_content_type(&self) -> &'static str {
+        self.common_get_content_type()
+    }
+
+    fn get_request_body(
+        &self,
+        req: &ConnectorCustomerRouterData,
+        _connectors: &Connectors,
+    ) -> CustomResult<RequestContent, errors::ConnectorError> {
+        let connector_router_data = chargebee::ChargebeeRouterData::from((MinorUnit::new(0), req));
+        let connector_req =
+            chargebee::ChargebeeCustomerCreateRequest::try_from(&connector_router_data)?;
+        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
+    }
+
+    fn build_request(
+        &self,
+        req: &ConnectorCustomerRouterData,
+        connectors: &Connectors,
+    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
+        Ok(Some(
+            RequestBuilder::new()
+                .method(Method::Post)
+                .url(&types::ConnectorCustomerType::get_url(
+                    self, req, connectors,
+                )?)
+                .attach_default_headers()
+                .headers(types::ConnectorCustomerType::get_headers(
+                    self, req, connectors,
+                )?)
+                .set_body(types::ConnectorCustomerType::get_request_body(
+                    self, req, connectors,
+                )?)
+                .build(),
+        ))
+    }
+
+    fn handle_response(
+        &self,
+        data: &ConnectorCustomerRouterData,
+        event_builder: Option<&mut ConnectorEvent>,
+        res: Response,
+    ) -> CustomResult<ConnectorCustomerRouterData, errors::ConnectorError> {
+        let response: chargebee::ChargebeeCustomerCreateResponse = res
+            .response
+            .parse_struct("ChargebeeCustomerCreateResponse")
+            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
+        event_builder.map(|i| i.set_response_body(&response));
+        router_env::logger::info!(connector_response=?response);
+        RouterData::try_from(ResponseRouterData {
+            response,
+            data: data.clone(),
+            http_code: res.status_code,
+        })
+    }
+
+    fn get_error_response(
+        &self,
+        res: Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
+}
+
 #[async_trait::async_trait]
 impl webhooks::IncomingWebhook for Chargebee {
     fn get_webhook_source_verification_signature(
diff --git a/crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs b/crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
index 721f50a52fb..f5b950a1b04 100644
--- a/crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/chargebee/transformers.rs
@@ -2,7 +2,13 @@
 use std::str::FromStr;
 
 use common_enums::enums;
-use common_utils::{errors::CustomResult, ext_traits::ByteSliceExt, pii, types::MinorUnit};
+use common_utils::{
+    errors::CustomResult,
+    ext_traits::ByteSliceExt,
+    id_type::CustomerId,
+    pii::{self, Email},
+    types::MinorUnit,
+};
 use error_stack::ResultExt;
 #[cfg(all(feature = "revenue_recovery", feature = "v2"))]
 use hyperswitch_domain_models::revenue_recovery;
@@ -11,17 +17,19 @@ use hyperswitch_domain_models::{
     router_data::{ConnectorAuthType, RouterData},
     router_flow_types::{
         refunds::{Execute, RSync},
-        InvoiceRecordBack,
+        CreateConnectorCustomer, InvoiceRecordBack,
+    },
+    router_request_types::{
+        revenue_recovery::InvoiceRecordBackRequest, ConnectorCustomerData, ResponseId,
     },
-    router_request_types::{revenue_recovery::InvoiceRecordBackRequest, ResponseId},
     router_response_types::{
         revenue_recovery::InvoiceRecordBackResponse, subscriptions::GetSubscriptionPlansResponse,
-        PaymentsResponseData, RefundsResponseData,
+        ConnectorCustomerResponseData, PaymentsResponseData, RefundsResponseData,
     },
     types::{InvoiceRecordBackRouterData, PaymentsAuthorizeRouterData, RefundsRouterData},
 };
 use hyperswitch_interfaces::errors;
-use masking::Secret;
+use masking::{ExposeInterface, Secret};
 use serde::{Deserialize, Serialize};
 use time::PrimitiveDateTime;
 
@@ -821,3 +829,94 @@ impl<F, T>
         })
     }
 }
+
+#[derive(Debug, Serialize)]
+pub struct ChargebeeCustomerCreateRequest {
+    #[serde(rename = "id")]
+    pub customer_id: CustomerId,
+    #[serde(rename = "first_name")]
+    pub name: Option<Secret<String>>,
+    pub email: Option<Email>,
+    pub billing_address: Option<api_models::payments::AddressDetails>,
+}
+
+impl TryFrom<&ChargebeeRouterData<&hyperswitch_domain_models::types::ConnectorCustomerRouterData>>
+    for ChargebeeCustomerCreateRequest
+{
+    type Error = error_stack::Report<errors::ConnectorError>;
+
+    fn try_from(
+        item: &ChargebeeRouterData<&hyperswitch_domain_models::types::ConnectorCustomerRouterData>,
+    ) -> Result<Self, Self::Error> {
+        let req = &item.router_data.request;
+
+        Ok(Self {
+            customer_id: req
+                .customer_id
+                .as_ref()
+                .ok_or_else(|| errors::ConnectorError::MissingRequiredField {
+                    field_name: "customer_id",
+                })?
+                .clone(),
+            name: req.name.clone(),
+            email: req.email.clone(),
+            billing_address: req.billing_address.clone(),
+        })
+    }
+}
+
+#[derive(Debug, Deserialize, Serialize, Clone)]
+pub struct ChargebeeCustomerCreateResponse {
+    pub customer: ChargebeeCustomerDetails,
+}
+
+#[derive(Debug, Deserialize, Serialize, Clone)]
+pub struct ChargebeeCustomerDetails {
+    pub id: String,
+    #[serde(rename = "first_name")]
+    pub name: Option<Secret<String>>,
+    pub email: Option<Email>,
+    pub billing_address: Option<api_models::payments::AddressDetails>,
+}
+
+impl
+    TryFrom<
+        ResponseRouterData<
+            CreateConnectorCustomer,
+            ChargebeeCustomerCreateResponse,
+            ConnectorCustomerData,
+            PaymentsResponseData,
+        >,
+    > for hyperswitch_domain_models::types::ConnectorCustomerRouterData
+{
+    type Error = error_stack::Report<errors::ConnectorError>;
+
+    fn try_from(
+        item: ResponseRouterData<
+            CreateConnectorCustomer,
+            ChargebeeCustomerCreateResponse,
+            ConnectorCustomerData,
+            PaymentsResponseData,
+        >,
+    ) -> Result<Self, Self::Error> {
+        let customer_response = &item.response.customer;
+
+        Ok(Self {
+            response: Ok(PaymentsResponseData::ConnectorCustomerResponse(
+                ConnectorCustomerResponseData::new(
+                    customer_response.id.clone(),
+                    customer_response
+                        .name
+                        .as_ref()
+                        .map(|name| name.clone().expose()),
+                    customer_response
+                        .email
+                        .as_ref()
+                        .map(|email| email.clone().expose().expose()),
+                    customer_response.billing_address.clone(),
+                ),
+            )),
+            ..item.data
+        })
+    }
+}
diff --git a/crates/hyperswitch_connectors/src/connectors/dwolla.rs b/crates/hyperswitch_connectors/src/connectors/dwolla.rs
index a66e05e7e4c..001436b7797 100644
--- a/crates/hyperswitch_connectors/src/connectors/dwolla.rs
+++ b/crates/hyperswitch_connectors/src/connectors/dwolla.rs
@@ -27,8 +27,8 @@ use hyperswitch_domain_models::{
         PaymentsSyncData, RefundsData, ResponseId, SetupMandateRequestData,
     },
     router_response_types::{
-        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
-        SupportedPaymentMethods, SupportedPaymentMethodsExt,
+        ConnectorCustomerResponseData, ConnectorInfo, PaymentMethodDetails, PaymentsResponseData,
+        RefundsResponseData, SupportedPaymentMethods, SupportedPaymentMethodsExt,
     },
     types::{
         ConnectorCustomerRouterData, PaymentsAuthorizeRouterData, PaymentsSyncRouterData,
@@ -346,9 +346,9 @@ impl ConnectorIntegration<CreateConnectorCustomer, ConnectorCustomerData, Paymen
 
         Ok(RouterData {
             connector_customer: Some(connector_customer_id.clone()),
-            response: Ok(PaymentsResponseData::ConnectorCustomerResponse {
-                connector_customer_id,
-            }),
+            response: Ok(PaymentsResponseData::ConnectorCustomerResponse(
+                ConnectorCustomerResponseData::new_with_customer_id(connector_customer_id),
+            )),
             ..data.clone()
         })
     }
diff --git a/crates/hyperswitch_connectors/src/connectors/facilitapay/transformers.rs b/crates/hyperswitch_connectors/src/connectors/facilitapay/transformers.rs
index 3d8c57fb6e0..955691a11f2 100644
--- a/crates/hyperswitch_connectors/src/connectors/facilitapay/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/facilitapay/transformers.rs
@@ -16,7 +16,9 @@ use hyperswitch_domain_models::{
         refunds::{Execute, RSync},
     },
     router_request_types::{PaymentsCancelData, ResponseId},
-    router_response_types::{PaymentsResponseData, RefundsResponseData},
+    router_response_types::{
+        ConnectorCustomerResponseData, PaymentsResponseData, RefundsResponseData,
+    },
     types,
 };
 use hyperswitch_interfaces::{
@@ -372,9 +374,11 @@ impl<F, T> TryFrom<ResponseRouterData<F, FacilitapayCustomerResponse, T, Payment
         item: ResponseRouterData<F, FacilitapayCustomerResponse, T, PaymentsResponseData>,
     ) -> Result<Self, Self::Error> {
         Ok(Self {
-            response: Ok(PaymentsResponseData::ConnectorCustomerResponse {
-                connector_customer_id: item.response.data.customer_id.expose(),
-            }),
+            response: Ok(PaymentsResponseData::ConnectorCustomerResponse(
+                ConnectorCustomerResponseData::new_with_customer_id(
+                    item.response.data.customer_id.expose(),
+                ),
+            )),
             ..item.data
         })
     }
diff --git a/crates/hyperswitch_connectors/src/connectors/gocardless/transformers.rs b/crates/hyperswitch_connectors/src/connectors/gocardless/transformers.rs
index 29f9525b944..b4019d98e1e 100644
--- a/crates/hyperswitch_connectors/src/connectors/gocardless/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/gocardless/transformers.rs
@@ -13,7 +13,9 @@ use hyperswitch_domain_models::{
         ConnectorCustomerData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
         PaymentsSyncData, ResponseId, SetupMandateRequestData,
     },
-    router_response_types::{MandateReference, PaymentsResponseData, RefundsResponseData},
+    router_response_types::{
+        ConnectorCustomerResponseData, MandateReference, PaymentsResponseData, RefundsResponseData,
+    },
     types,
 };
 use hyperswitch_interfaces::errors;
@@ -152,9 +154,11 @@ impl<F>
         >,
     ) -> Result<Self, Self::Error> {
         Ok(Self {
-            response: Ok(PaymentsResponseData::ConnectorCustomerResponse {
-                connector_customer_id: item.response.customers.id.expose(),
-            }),
+            response: Ok(PaymentsResponseData::ConnectorCustomerResponse(
+                ConnectorCustomerResponseData::new_with_customer_id(
+                    item.response.customers.id.expose(),
+                ),
+            )),
             ..item.data
         })
     }
diff --git a/crates/hyperswitch_connectors/src/connectors/hyperswitch_vault/transformers.rs b/crates/hyperswitch_connectors/src/connectors/hyperswitch_vault/transformers.rs
index 220e2e55440..cb2ddd67722 100644
--- a/crates/hyperswitch_connectors/src/connectors/hyperswitch_vault/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/hyperswitch_vault/transformers.rs
@@ -2,7 +2,9 @@ use common_utils::pii::Email;
 use hyperswitch_domain_models::{
     router_data::{ConnectorAuthType, RouterData},
     router_flow_types::vault::ExternalVaultCreateFlow,
-    router_response_types::{PaymentsResponseData, VaultResponseData},
+    router_response_types::{
+        ConnectorCustomerResponseData, PaymentsResponseData, VaultResponseData,
+    },
     types::{ConnectorCustomerRouterData, VaultRouterData},
 };
 use hyperswitch_interfaces::errors;
@@ -108,9 +110,9 @@ impl<F, T>
         >,
     ) -> Result<Self, Self::Error> {
         Ok(Self {
-            response: Ok(PaymentsResponseData::ConnectorCustomerResponse {
-                connector_customer_id: item.response.id,
-            }),
+            response: Ok(PaymentsResponseData::ConnectorCustomerResponse(
+                ConnectorCustomerResponseData::new_with_customer_id(item.response.id),
+            )),
             ..item.data
         })
     }
diff --git a/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs b/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs
index 07515761e72..29f673302cc 100644
--- a/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs
@@ -6,7 +6,9 @@ use hyperswitch_domain_models::{
     router_data::{ConnectorAuthType, PaymentMethodToken, RouterData},
     router_flow_types::refunds::{Execute, RSync},
     router_request_types::ResponseId,
-    router_response_types::{PaymentsResponseData, RefundsResponseData},
+    router_response_types::{
+        ConnectorCustomerResponseData, PaymentsResponseData, RefundsResponseData,
+    },
     types,
 };
 use hyperswitch_interfaces::{api, errors};
@@ -196,9 +198,9 @@ impl<F, T> TryFrom<ResponseRouterData<F, StaxCustomerResponse, T, PaymentsRespon
         item: ResponseRouterData<F, StaxCustomerResponse, T, PaymentsResponseData>,
     ) -> Result<Self, Self::Error> {
         Ok(Self {
-            response: Ok(PaymentsResponseData::ConnectorCustomerResponse {
-                connector_customer_id: item.response.id.expose(),
-            }),
+            response: Ok(PaymentsResponseData::ConnectorCustomerResponse(
+                ConnectorCustomerResponseData::new_with_customer_id(item.response.id.expose()),
+            )),
             ..item.data
         })
     }
diff --git a/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs b/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs
index cbedbd62704..c7b728db21f 100644
--- a/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/stripe/transformers.rs
@@ -31,8 +31,8 @@ use hyperswitch_domain_models::{
         PaymentsIncrementalAuthorizationData, ResponseId, SplitRefundsRequest,
     },
     router_response_types::{
-        MandateReference, PaymentsResponseData, PreprocessingResponseId, RedirectForm,
-        RefundsResponseData,
+        ConnectorCustomerResponseData, MandateReference, PaymentsResponseData,
+        PreprocessingResponseId, RedirectForm, RefundsResponseData,
     },
     types::{
         ConnectorCustomerRouterData, PaymentsAuthorizeRouterData, PaymentsCancelRouterData,
@@ -4136,9 +4136,9 @@ impl<F, T> TryFrom<ResponseRouterData<F, StripeCustomerResponse, T, PaymentsResp
         item: ResponseRouterData<F, StripeCustomerResponse, T, PaymentsResponseData>,
     ) -> Result<Self, Self::Error> {
         Ok(Self {
-            response: Ok(PaymentsResponseData::ConnectorCustomerResponse {
-                connector_customer_id: item.response.id,
-            }),
+            response: Ok(PaymentsResponseData::ConnectorCustomerResponse(
+                ConnectorCustomerResponseData::new_with_customer_id(item.response.id),
+            )),
             ..item.data
         })
     }
diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs
index 1e404df7f97..9d131d006b2 100644
--- a/crates/hyperswitch_connectors/src/default_implementations.rs
+++ b/crates/hyperswitch_connectors/src/default_implementations.rs
@@ -1441,7 +1441,6 @@ default_imp_for_create_customer!(
     connectors::Breadpay,
     connectors::Cashtocode,
     connectors::Celero,
-    connectors::Chargebee,
     connectors::Checkbook,
     connectors::Checkout,
     connectors::Coinbase,
diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs
index b97675006ed..38e44ef88fe 100644
--- a/crates/hyperswitch_domain_models/src/router_request_types.rs
+++ b/crates/hyperswitch_domain_models/src/router_request_types.rs
@@ -3,7 +3,7 @@ pub mod fraud_check;
 pub mod revenue_recovery;
 pub mod subscriptions;
 pub mod unified_authentication_service;
-use api_models::payments::{AdditionalPaymentData, RequestSurchargeDetails};
+use api_models::payments::{AdditionalPaymentData, AddressDetails, RequestSurchargeDetails};
 use common_types::payments as common_payments_types;
 use common_utils::{consts, errors, ext_traits::OptionExt, id_type, pii, types::MinorUnit};
 use diesel_models::{enums as storage_enums, types::OrderDetailsWithAmount};
@@ -183,6 +183,8 @@ impl
             split_payments: data.request.split_payments.clone(),
             setup_future_usage: data.request.setup_future_usage,
             customer_acceptance: data.request.customer_acceptance.clone(),
+            customer_id: None,
+            billing_address: None,
         })
     }
 }
@@ -289,6 +291,8 @@ pub struct ConnectorCustomerData {
     // Mandates
     pub setup_future_usage: Option<storage_enums::FutureUsage>,
     pub customer_acceptance: Option<common_payments_types::CustomerAcceptance>,
+    pub customer_id: Option<id_type::CustomerId>,
+    pub billing_address: Option<AddressDetails>,
 }
 
 impl TryFrom<SetupMandateRequestData> for ConnectorCustomerData {
@@ -304,6 +308,8 @@ impl TryFrom<SetupMandateRequestData> for ConnectorCustomerData {
             split_payments: None,
             setup_future_usage: data.setup_future_usage,
             customer_acceptance: data.customer_acceptance,
+            customer_id: None,
+            billing_address: None,
         })
     }
 }
@@ -363,6 +369,8 @@ impl
             split_payments: data.request.split_payments.clone(),
             setup_future_usage: data.request.setup_future_usage,
             customer_acceptance: data.request.customer_acceptance.clone(),
+            customer_id: None,
+            billing_address: None,
         })
     }
 }
@@ -389,6 +397,8 @@ impl TryFrom<&RouterData<flows::Session, PaymentsSessionData, response_types::Pa
             split_payments: None,
             setup_future_usage: None,
             customer_acceptance: None,
+            customer_id: None,
+            billing_address: None,
         })
     }
 }
diff --git a/crates/hyperswitch_domain_models/src/router_response_types.rs b/crates/hyperswitch_domain_models/src/router_response_types.rs
index cc766a00ba8..197472bf26f 100644
--- a/crates/hyperswitch_domain_models/src/router_response_types.rs
+++ b/crates/hyperswitch_domain_models/src/router_response_types.rs
@@ -4,6 +4,7 @@ pub mod revenue_recovery;
 pub mod subscriptions;
 use std::collections::HashMap;
 
+use api_models::payments::AddressDetails;
 use common_utils::{pii, request::Method, types::MinorUnit};
 pub use disputes::{
     AcceptDisputeResponse, DefendDisputeResponse, DisputeSyncResponse, FetchDisputesResponse,
@@ -23,6 +24,33 @@ pub struct RefundsResponseData {
     // pub amount_received: Option<i32>, // Calculation for amount received not in place yet
 }
 
+#[derive(Debug, Clone)]
+pub struct ConnectorCustomerResponseData {
+    pub connector_customer_id: String,
+    pub name: Option<String>,
+    pub email: Option<String>,
+    pub billing_address: Option<AddressDetails>,
+}
+
+impl ConnectorCustomerResponseData {
+    pub fn new_with_customer_id(connector_customer_id: String) -> Self {
+        Self::new(connector_customer_id, None, None, None)
+    }
+    pub fn new(
+        connector_customer_id: String,
+        name: Option<String>,
+        email: Option<String>,
+        billing_address: Option<AddressDetails>,
+    ) -> Self {
+        Self {
+            connector_customer_id,
+            name,
+            email,
+            billing_address,
+        }
+    }
+}
+
 #[derive(Debug, Clone)]
 pub enum PaymentsResponseData {
     TransactionResponse {
@@ -55,9 +83,7 @@ pub enum PaymentsResponseData {
         token: String,
     },
 
-    ConnectorCustomerResponse {
-        connector_customer_id: String,
-    },
+    ConnectorCustomerResponse(ConnectorCustomerResponseData),
 
     ThreeDSEnrollmentResponse {
         enrolled_v2: bool,
diff --git a/crates/hyperswitch_interfaces/src/api/subscriptions.rs b/crates/hyperswitch_interfaces/src/api/subscriptions.rs
index a9e53ec5603..1f911940d2f 100644
--- a/crates/hyperswitch_interfaces/src/api/subscriptions.rs
+++ b/crates/hyperswitch_interfaces/src/api/subscriptions.rs
@@ -7,7 +7,9 @@ use hyperswitch_domain_models::{
 };
 
 #[cfg(feature = "v1")]
-use super::{ConnectorCommon, ConnectorIntegration};
+use super::{
+    payments::ConnectorCustomer as PaymentsConnectorCustomer, ConnectorCommon, ConnectorIntegration,
+};
 
 #[cfg(feature = "v1")]
 /// trait GetSubscriptionPlans for V1
@@ -22,7 +24,10 @@ pub trait GetSubscriptionPlansFlow:
 
 /// trait Subscriptions
 #[cfg(feature = "v1")]
-pub trait Subscriptions: ConnectorCommon + GetSubscriptionPlansFlow {}
+pub trait Subscriptions:
+    ConnectorCommon + GetSubscriptionPlansFlow + PaymentsConnectorCustomer
+{
+}
 
 /// trait Subscriptions (disabled when not V1)
 #[cfg(not(feature = "v1"))]
@@ -31,3 +36,7 @@ pub trait Subscriptions {}
 /// trait GetSubscriptionPlansFlow (disabled when not V1)
 #[cfg(not(feature = "v1"))]
 pub trait GetSubscriptionPlansFlow {}
+
+#[cfg(not(feature = "v1"))]
+/// trait CreateCustomer (disabled when not V1)
+pub trait ConnectorCustomer {}
diff --git a/crates/hyperswitch_interfaces/src/api/subscriptions_v2.rs b/crates/hyperswitch_interfaces/src/api/subscriptions_v2.rs
index 5a37b013ef4..ba8b54062fa 100644
--- a/crates/hyperswitch_interfaces/src/api/subscriptions_v2.rs
+++ b/crates/hyperswitch_interfaces/src/api/subscriptions_v2.rs
@@ -6,10 +6,11 @@ use hyperswitch_domain_models::{
     router_response_types::subscriptions::GetSubscriptionPlansResponse,
 };
 
+use super::payments_v2::ConnectorCustomerV2;
 use crate::connector_integration_v2::ConnectorIntegrationV2;
 
 /// trait SubscriptionsV2
-pub trait SubscriptionsV2: GetSubscriptionPlansV2 {}
+pub trait SubscriptionsV2: GetSubscriptionPlansV2 + ConnectorCustomerV2 {}
 
 /// trait GetSubscriptionPlans for V1
 pub trait GetSubscriptionPlansV2:
diff --git a/crates/hyperswitch_interfaces/src/types.rs b/crates/hyperswitch_interfaces/src/types.rs
index e263735b8af..7125c9b3b00 100644
--- a/crates/hyperswitch_interfaces/src/types.rs
+++ b/crates/hyperswitch_interfaces/src/types.rs
@@ -373,3 +373,11 @@ impl Default for Proxy {
         }
     }
 }
+
+/// Type alias for `ConnectorIntegrationV2<CreateConnectorCustomer, PaymentFlowData, ConnectorCustomerData, PaymentsResponseData>`
+pub type CreateCustomerTypeV2 = dyn ConnectorIntegrationV2<
+    CreateConnectorCustomer,
+    flow_common_types::PaymentFlowData,
+    ConnectorCustomerData,
+    PaymentsResponseData,
+>;
diff --git a/crates/router/src/core/payments/customers.rs b/crates/router/src/core/payments/customers.rs
index d1fc40aef39..f3926365cbe 100644
--- a/crates/router/src/core/payments/customers.rs
+++ b/crates/router/src/core/payments/customers.rs
@@ -60,9 +60,9 @@ pub async fn create_connector_customer<F: Clone, T: Clone>(
 
     let connector_customer_id = match resp.response {
         Ok(response) => match response {
-            types::PaymentsResponseData::ConnectorCustomerResponse {
-                connector_customer_id,
-            } => Some(connector_customer_id),
+            types::PaymentsResponseData::ConnectorCustomerResponse(customer_data) => {
+                Some(customer_data.connector_customer_id)
+            }
             _ => None,
         },
         Err(err) => {
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index 2b1d1024e76..292a668a07c 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -1997,7 +1997,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                         types::PaymentsResponseData::TokenizationResponse { .. } => {
                             (None, None, None)
                         }
-                        types::PaymentsResponseData::ConnectorCustomerResponse { .. } => {
+                        types::PaymentsResponseData::ConnectorCustomerResponse(..) => {
                             (None, None, None)
                         }
                         types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => {
diff --git a/crates/router/tests/connectors/stax.rs b/crates/router/tests/connectors/stax.rs
index 89b1bc7f4b9..69e241edd3b 100644
--- a/crates/router/tests/connectors/stax.rs
+++ b/crates/router/tests/connectors/stax.rs
@@ -92,9 +92,9 @@ async fn create_customer_and_get_token() -> Option<String> {
         .await
         .expect("Authorize payment response");
     let connector_customer_id = match customer_response.response.unwrap() {
-        PaymentsResponseData::ConnectorCustomerResponse {
-            connector_customer_id,
-        } => Some(connector_customer_id),
+        PaymentsResponseData::ConnectorCustomerResponse(customer_data) => {
+            Some(customer_data.connector_customer_id)
+        }
         _ => None,
     };
 
@@ -468,9 +468,9 @@ async fn should_fail_payment_for_incorrect_cvc() {
         .await
         .expect("Authorize payment response");
     let connector_customer_id = match customer_response.response.unwrap() {
-        PaymentsResponseData::ConnectorCustomerResponse {
-            connector_customer_id,
-        } => Some(connector_customer_id),
+        PaymentsResponseData::ConnectorCustomerResponse(customer_data) => {
+            Some(customer_data.connector_customer_id)
+        }
         _ => None,
     };
 
@@ -511,9 +511,9 @@ async fn should_fail_payment_for_invalid_exp_month() {
         .await
         .expect("Authorize payment response");
     let connector_customer_id = match customer_response.response.unwrap() {
-        PaymentsResponseData::ConnectorCustomerResponse {
-            connector_customer_id,
-        } => Some(connector_customer_id),
+        PaymentsResponseData::ConnectorCustomerResponse(customer_data) => {
+            Some(customer_data.connector_customer_id)
+        }
         _ => None,
     };
 
@@ -554,9 +554,9 @@ async fn should_fail_payment_for_incorrect_expiry_year() {
         .await
         .expect("Authorize payment response");
     let connector_customer_id = match customer_response.response.unwrap() {
-        PaymentsResponseData::ConnectorCustomerResponse {
-            connector_customer_id,
-        } => Some(connector_customer_id),
+        PaymentsResponseData::ConnectorCustomerResponse(customer_data) => {
+            Some(customer_data.connector_customer_id)
+        }
         _ => None,
     };
 
diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs
index c35654719fb..9394752fb90 100644
--- a/crates/router/tests/connectors/utils.rs
+++ b/crates/router/tests/connectors/utils.rs
@@ -573,7 +573,7 @@ pub trait ConnectorActions: Connector {
             Ok(types::PaymentsResponseData::SessionTokenResponse { .. }) => None,
             Ok(types::PaymentsResponseData::TokenizationResponse { .. }) => None,
             Ok(types::PaymentsResponseData::TransactionUnresolvedResponse { .. }) => None,
-            Ok(types::PaymentsResponseData::ConnectorCustomerResponse { .. }) => None,
+            Ok(types::PaymentsResponseData::ConnectorCustomerResponse(..)) => None,
             Ok(types::PaymentsResponseData::PreProcessingResponse { .. }) => None,
             Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. }) => None,
             Ok(types::PaymentsResponseData::MultipleCaptureResponse { .. }) => None,
@@ -1117,6 +1117,8 @@ impl Default for CustomerType {
             split_payments: None,
             customer_acceptance: None,
             setup_future_usage: None,
+            customer_id: None,
+            billing_address: None,
         };
         Self(data)
     }
@@ -1151,7 +1153,7 @@ pub fn get_connector_transaction_id(
         Ok(types::PaymentsResponseData::TokenizationResponse { .. }) => None,
         Ok(types::PaymentsResponseData::TransactionUnresolvedResponse { .. }) => None,
         Ok(types::PaymentsResponseData::PreProcessingResponse { .. }) => None,
-        Ok(types::PaymentsResponseData::ConnectorCustomerResponse { .. }) => None,
+        Ok(types::PaymentsResponseData::ConnectorCustomerResponse(..)) => None,
         Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. }) => None,
         Ok(types::PaymentsResponseData::MultipleCaptureResponse { .. }) => None,
         Ok(types::PaymentsResponseData::IncrementalAuthorizationResponse { .. }) => None,

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

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


### Summary

This PR introduces a **create customer handler for the Chargebee connector**. It enables merchants to create and manage customers in Chargebee directly through Hyperswitch by mapping customer data between our domain models and Chargebee’s API.

### Key Changes

* **New Connector Integration**

  * Added `ConnectorIntegration<CreateConnectorCustomer, ConnectorCustomerData, PaymentsResponseData>` implementation for Chargebee.
  * Added response mapper to convert Chargebee’s response into `PaymentsResponseData::ConnectorCustomerResponse`.

* **Extended Response Model**

  * `ConnectorCustomerResponse` now carries optional fields:

    * `name`
    * `email`
    * `billing_address`


* **Domain Models & Flow Types**

  * Extended `ConnectorCustomerData` to support new fields (`customer_id`, `billing_address`).

### Benefits

* Merchants can now **create customers in Chargebee** via Hyperswitch.
* Unified model for customer creation across connectors.
* Future-proofing: response carries richer customer metadata, making subscription flows easier to support.


## How did you test it?

cargo r
<img width="1723" height="1065" alt="Screenshot 2025-09-10 at 4 11 52 PM" src="https://github.com/user-attachments/assets/10a67220-94f8-4d92-950d-acc933d11de0" />

just clippy
<img width="1699" height="455" alt="Screenshot 2025-09-10 at 4 17 39 PM" src="https://github.com/user-attachments/assets/5ad6b2f9-84d0-4434-ad4e-ed98fbccc8eb" />




just clippy_v2

<img width="1728" height="459" alt="Screenshot 2025-09-10 at 4 22 46 PM" src="https://github.com/user-attachments/assets/65336216-67e5-4900-b638-c6543bb47b83" />




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

- [ ] 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
