Bug: feat(subscription): domain_model for subscription and invoice




diff --git a/crates/hyperswitch_domain_models/src/invoice.rs b/crates/hyperswitch_domain_models/src/invoice.rs
new file mode 100644
index 00000000000..9457eac56d2
--- /dev/null
+++ b/crates/hyperswitch_domain_models/src/invoice.rs
@@ -0,0 +1,250 @@
+use std::str::FromStr;
+
+use common_utils::{
+    errors::{CustomResult, ValidationError},
+    id_type::GenerateId,
+    pii::SecretSerdeValue,
+    types::{
+        keymanager::{Identifier, KeyManagerState},
+        MinorUnit,
+    },
+};
+use error_stack::ResultExt;
+use masking::Secret;
+use utoipa::ToSchema;
+
+use crate::merchant_key_store::MerchantKeyStore;
+
+#[derive(Debug, Clone, serde::Serialize, ToSchema)]
+pub struct Invoice {
+    pub id: common_utils::id_type::InvoiceId,
+    pub subscription_id: common_utils::id_type::SubscriptionId,
+    pub merchant_id: common_utils::id_type::MerchantId,
+    pub profile_id: common_utils::id_type::ProfileId,
+    pub merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId,
+    pub payment_intent_id: Option<common_utils::id_type::PaymentId>,
+    pub payment_method_id: Option<String>,
+    pub customer_id: common_utils::id_type::CustomerId,
+    pub amount: MinorUnit,
+    pub currency: String,
+    pub status: String,
+    pub provider_name: common_enums::connector_enums::Connector,
+    pub metadata: Option<SecretSerdeValue>,
+    pub connector_invoice_id: Option<String>,
+}
+
+#[async_trait::async_trait]
+
+impl super::behaviour::Conversion for Invoice {
+    type DstType = diesel_models::invoice::Invoice;
+    type NewDstType = diesel_models::invoice::InvoiceNew;
+
+    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
+        let now = common_utils::date_time::now();
+        Ok(diesel_models::invoice::Invoice {
+            id: self.id,
+            subscription_id: self.subscription_id,
+            merchant_id: self.merchant_id,
+            profile_id: self.profile_id,
+            merchant_connector_id: self.merchant_connector_id,
+            payment_intent_id: self.payment_intent_id,
+            payment_method_id: self.payment_method_id,
+            customer_id: self.customer_id,
+            amount: self.amount,
+            currency: self.currency.to_string(),
+            status: self.status,
+            provider_name: self.provider_name,
+            metadata: None,
+            created_at: now,
+            modified_at: now,
+            connector_invoice_id: self.connector_invoice_id,
+        })
+    }
+
+    async fn convert_back(
+        _state: &KeyManagerState,
+        item: Self::DstType,
+        _key: &Secret<Vec<u8>>,
+        _key_manager_identifier: Identifier,
+    ) -> CustomResult<Self, ValidationError>
+    where
+        Self: Sized,
+    {
+        Ok(Self {
+            id: item.id,
+            subscription_id: item.subscription_id,
+            merchant_id: item.merchant_id,
+            profile_id: item.profile_id,
+            merchant_connector_id: item.merchant_connector_id,
+            payment_intent_id: item.payment_intent_id,
+            payment_method_id: item.payment_method_id,
+            customer_id: item.customer_id,
+            amount: item.amount,
+            currency: item.currency,
+            status: item.status,
+            provider_name: item.provider_name,
+            metadata: item.metadata,
+            connector_invoice_id: item.connector_invoice_id,
+        })
+    }
+
+    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
+        let invoice_status = common_enums::connector_enums::InvoiceStatus::from_str(&self.status)
+            .change_context(ValidationError::InvalidValue {
+            message: "Invalid invoice status".to_string(),
+        })?;
+
+        Ok(diesel_models::invoice::InvoiceNew::new(
+            self.subscription_id,
+            self.merchant_id,
+            self.profile_id,
+            self.merchant_connector_id,
+            self.payment_intent_id,
+            self.payment_method_id,
+            self.customer_id,
+            self.amount,
+            self.currency.to_string(),
+            invoice_status,
+            self.provider_name,
+            None,
+            self.connector_invoice_id,
+        ))
+    }
+}
+
+impl Invoice {
+    #[allow(clippy::too_many_arguments)]
+    pub fn to_invoice(
+        subscription_id: common_utils::id_type::SubscriptionId,
+        merchant_id: common_utils::id_type::MerchantId,
+        profile_id: common_utils::id_type::ProfileId,
+        merchant_connector_id: common_utils::id_type::MerchantConnectorAccountId,
+        payment_intent_id: Option<common_utils::id_type::PaymentId>,
+        payment_method_id: Option<String>,
+        customer_id: common_utils::id_type::CustomerId,
+        amount: MinorUnit,
+        currency: String,
+        status: common_enums::connector_enums::InvoiceStatus,
+        provider_name: common_enums::connector_enums::Connector,
+        metadata: Option<SecretSerdeValue>,
+        connector_invoice_id: Option<String>,
+    ) -> Self {
+        Self {
+            id: common_utils::id_type::InvoiceId::generate(),
+            subscription_id,
+            merchant_id,
+            profile_id,
+            merchant_connector_id,
+            payment_intent_id,
+            payment_method_id,
+            customer_id,
+            amount,
+            currency: currency.to_string(),
+            status: status.to_string(),
+            provider_name,
+            metadata,
+            connector_invoice_id,
+        }
+    }
+}
+
+#[async_trait::async_trait]
+pub trait InvoiceInterface {
+    type Error;
+    async fn insert_invoice_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        invoice_new: Invoice,
+    ) -> CustomResult<Invoice, Self::Error>;
+
+    async fn find_invoice_by_invoice_id(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        invoice_id: String,
+    ) -> CustomResult<Invoice, Self::Error>;
+
+    async fn update_invoice_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        invoice_id: String,
+        data: InvoiceUpdate,
+    ) -> CustomResult<Invoice, Self::Error>;
+
+    async fn get_latest_invoice_for_subscription(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        subscription_id: String,
+    ) -> CustomResult<Invoice, Self::Error>;
+}
+
+pub struct InvoiceUpdate {
+    pub status: Option<String>,
+    pub payment_method_id: Option<String>,
+    pub connector_invoice_id: Option<String>,
+    pub modified_at: time::PrimitiveDateTime,
+    pub payment_intent_id: Option<common_utils::id_type::PaymentId>,
+}
+#[async_trait::async_trait]
+impl super::behaviour::Conversion for InvoiceUpdate {
+    type DstType = diesel_models::invoice::InvoiceUpdate;
+    type NewDstType = diesel_models::invoice::InvoiceUpdate;
+
+    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
+        Ok(diesel_models::invoice::InvoiceUpdate {
+            status: self.status,
+            payment_method_id: self.payment_method_id,
+            connector_invoice_id: self.connector_invoice_id,
+            modified_at: self.modified_at,
+            payment_intent_id: self.payment_intent_id,
+        })
+    }
+
+    async fn convert_back(
+        _state: &KeyManagerState,
+        item: Self::DstType,
+        _key: &Secret<Vec<u8>>,
+        _key_manager_identifier: Identifier,
+    ) -> CustomResult<Self, ValidationError>
+    where
+        Self: Sized,
+    {
+        Ok(Self {
+            status: item.status,
+            payment_method_id: item.payment_method_id,
+            connector_invoice_id: item.connector_invoice_id,
+            modified_at: item.modified_at,
+            payment_intent_id: item.payment_intent_id,
+        })
+    }
+
+    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
+        Ok(diesel_models::invoice::InvoiceUpdate {
+            status: self.status,
+            payment_method_id: self.payment_method_id,
+            connector_invoice_id: self.connector_invoice_id,
+            modified_at: self.modified_at,
+            payment_intent_id: self.payment_intent_id,
+        })
+    }
+}
+
+impl InvoiceUpdate {
+    pub fn new(
+        payment_method_id: Option<String>,
+        status: Option<common_enums::connector_enums::InvoiceStatus>,
+        connector_invoice_id: Option<String>,
+        payment_intent_id: Option<common_utils::id_type::PaymentId>,
+    ) -> Self {
+        Self {
+            payment_method_id,
+            status: status.map(|status| status.to_string()),
+            connector_invoice_id,
+            modified_at: common_utils::date_time::now(),
+            payment_intent_id,
+        }
+    }
+}
diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs
index 3ed5130e077..1a3da30c975 100644
--- a/crates/hyperswitch_domain_models/src/lib.rs
+++ b/crates/hyperswitch_domain_models/src/lib.rs
@@ -15,6 +15,7 @@ pub mod disputes;
 pub mod errors;
 pub mod ext_traits;
 pub mod gsm;
+pub mod invoice;
 pub mod mandates;
 pub mod merchant_account;
 pub mod merchant_connector_account;
diff --git a/crates/hyperswitch_domain_models/src/subscription.rs b/crates/hyperswitch_domain_models/src/subscription.rs
index af2390de3f8..86db04ddc5c 100644
--- a/crates/hyperswitch_domain_models/src/subscription.rs
+++ b/crates/hyperswitch_domain_models/src/subscription.rs
@@ -1,7 +1,15 @@
-use common_utils::events::ApiEventMetric;
+use common_utils::{
+    errors::{CustomResult, ValidationError},
+    events::ApiEventMetric,
+    generate_id_with_default_len,
+    pii::SecretSerdeValue,
+    types::keymanager::{self, KeyManagerState},
+};
 use error_stack::ResultExt;
+use masking::{ExposeInterface, PeekInterface, Secret};
+use time::PrimitiveDateTime;
 
-use crate::errors::api_error_response::ApiErrorResponse;
+use crate::{errors::api_error_response::ApiErrorResponse, merchant_key_store::MerchantKeyStore};
 
 const SECRET_SPLIT: &str = "_secret";
 
@@ -48,3 +56,222 @@ impl From<ClientSecret> for api_models::subscription::ClientSecret {
         Self::new(domain_secret.to_string())
     }
 }
+
+#[derive(Clone, Debug, serde::Serialize)]
+pub struct Subscription {
+    pub id: common_utils::id_type::SubscriptionId,
+    pub status: String,
+    pub billing_processor: Option<String>,
+    pub payment_method_id: Option<String>,
+    pub merchant_connector_id: Option<common_utils::id_type::MerchantConnectorAccountId>,
+    pub client_secret: Option<String>,
+    pub connector_subscription_id: Option<String>,
+    pub merchant_id: common_utils::id_type::MerchantId,
+    pub customer_id: common_utils::id_type::CustomerId,
+    pub metadata: Option<SecretSerdeValue>,
+    pub created_at: PrimitiveDateTime,
+    pub modified_at: PrimitiveDateTime,
+    pub profile_id: common_utils::id_type::ProfileId,
+    pub merchant_reference_id: Option<String>,
+}
+
+#[derive(Debug, Clone, serde::Serialize)]
+pub enum SubscriptionStatus {
+    Active,
+    Created,
+    InActive,
+    Pending,
+    Trial,
+    Paused,
+    Unpaid,
+    Onetime,
+    Cancelled,
+    Failed,
+}
+
+impl std::fmt::Display for SubscriptionStatus {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Active => write!(f, "Active"),
+            Self::Created => write!(f, "Created"),
+            Self::InActive => write!(f, "InActive"),
+            Self::Pending => write!(f, "Pending"),
+            Self::Trial => write!(f, "Trial"),
+            Self::Paused => write!(f, "Paused"),
+            Self::Unpaid => write!(f, "Unpaid"),
+            Self::Onetime => write!(f, "Onetime"),
+            Self::Cancelled => write!(f, "Cancelled"),
+            Self::Failed => write!(f, "Failed"),
+        }
+    }
+}
+
+impl Subscription {
+    pub fn generate_and_set_client_secret(&mut self) -> Secret<String> {
+        let client_secret =
+            generate_id_with_default_len(&format!("{}_secret", self.id.get_string_repr()));
+        self.client_secret = Some(client_secret.clone());
+        Secret::new(client_secret)
+    }
+}
+
+#[async_trait::async_trait]
+impl super::behaviour::Conversion for Subscription {
+    type DstType = diesel_models::subscription::Subscription;
+    type NewDstType = diesel_models::subscription::SubscriptionNew;
+
+    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
+        let now = common_utils::date_time::now();
+        Ok(diesel_models::subscription::Subscription {
+            id: self.id,
+            status: self.status,
+            billing_processor: self.billing_processor,
+            payment_method_id: self.payment_method_id,
+            merchant_connector_id: self.merchant_connector_id,
+            client_secret: self.client_secret,
+            connector_subscription_id: self.connector_subscription_id,
+            merchant_id: self.merchant_id,
+            customer_id: self.customer_id,
+            metadata: self.metadata.map(|m| m.expose()),
+            created_at: now,
+            modified_at: now,
+            profile_id: self.profile_id,
+            merchant_reference_id: self.merchant_reference_id,
+        })
+    }
+
+    async fn convert_back(
+        _state: &KeyManagerState,
+        item: Self::DstType,
+        _key: &Secret<Vec<u8>>,
+        _key_manager_identifier: keymanager::Identifier,
+    ) -> CustomResult<Self, ValidationError>
+    where
+        Self: Sized,
+    {
+        Ok(Self {
+            id: item.id,
+            status: item.status,
+            billing_processor: item.billing_processor,
+            payment_method_id: item.payment_method_id,
+            merchant_connector_id: item.merchant_connector_id,
+            client_secret: item.client_secret,
+            connector_subscription_id: item.connector_subscription_id,
+            merchant_id: item.merchant_id,
+            customer_id: item.customer_id,
+            metadata: item.metadata.map(SecretSerdeValue::new),
+            created_at: item.created_at,
+            modified_at: item.modified_at,
+            profile_id: item.profile_id,
+            merchant_reference_id: item.merchant_reference_id,
+        })
+    }
+
+    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
+        Ok(diesel_models::subscription::SubscriptionNew::new(
+            self.id,
+            self.status,
+            self.billing_processor,
+            self.payment_method_id,
+            self.merchant_connector_id,
+            self.client_secret,
+            self.connector_subscription_id,
+            self.merchant_id,
+            self.customer_id,
+            self.metadata,
+            self.profile_id,
+            self.merchant_reference_id,
+        ))
+    }
+}
+
+#[async_trait::async_trait]
+pub trait SubscriptionInterface {
+    type Error;
+    async fn insert_subscription_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        subscription_new: Subscription,
+    ) -> CustomResult<Subscription, Self::Error>;
+
+    async fn find_by_merchant_id_subscription_id(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        merchant_id: &common_utils::id_type::MerchantId,
+        subscription_id: String,
+    ) -> CustomResult<Subscription, Self::Error>;
+
+    async fn update_subscription_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        merchant_id: &common_utils::id_type::MerchantId,
+        subscription_id: String,
+        data: SubscriptionUpdate,
+    ) -> CustomResult<Subscription, Self::Error>;
+}
+
+pub struct SubscriptionUpdate {
+    pub connector_subscription_id: Option<String>,
+    pub payment_method_id: Option<String>,
+    pub status: Option<String>,
+    pub modified_at: PrimitiveDateTime,
+}
+
+impl SubscriptionUpdate {
+    pub fn new(
+        payment_method_id: Option<Secret<String>>,
+        status: Option<String>,
+        connector_subscription_id: Option<String>,
+    ) -> Self {
+        Self {
+            payment_method_id: payment_method_id.map(|pmid| pmid.peek().clone()),
+            status,
+            connector_subscription_id,
+            modified_at: common_utils::date_time::now(),
+        }
+    }
+}
+
+#[async_trait::async_trait]
+impl super::behaviour::Conversion for SubscriptionUpdate {
+    type DstType = diesel_models::subscription::SubscriptionUpdate;
+    type NewDstType = diesel_models::subscription::SubscriptionUpdate;
+
+    async fn convert(self) -> CustomResult<Self::DstType, ValidationError> {
+        Ok(diesel_models::subscription::SubscriptionUpdate {
+            connector_subscription_id: self.connector_subscription_id,
+            payment_method_id: self.payment_method_id,
+            status: self.status,
+            modified_at: self.modified_at,
+        })
+    }
+
+    async fn convert_back(
+        _state: &KeyManagerState,
+        item: Self::DstType,
+        _key: &Secret<Vec<u8>>,
+        _key_manager_identifier: keymanager::Identifier,
+    ) -> CustomResult<Self, ValidationError>
+    where
+        Self: Sized,
+    {
+        Ok(Self {
+            connector_subscription_id: item.connector_subscription_id,
+            payment_method_id: item.payment_method_id,
+            status: item.status,
+            modified_at: item.modified_at,
+        })
+    }
+
+    async fn construct_new(self) -> CustomResult<Self::NewDstType, ValidationError> {
+        Ok(diesel_models::subscription::SubscriptionUpdate {
+            connector_subscription_id: self.connector_subscription_id,
+            payment_method_id: self.payment_method_id,
+            status: self.status,
+            modified_at: self.modified_at,
+        })
+    }
+}
diff --git a/crates/router/src/core/subscription.rs b/crates/router/src/core/subscription.rs
index 636b960fed6..05254a9b77d 100644
--- a/crates/router/src/core/subscription.rs
+++ b/crates/router/src/core/subscription.rs
@@ -81,11 +81,13 @@ pub async fn create_subscription(
         .attach_printable("subscriptions: failed to create invoice")?;
 
     subscription
-        .update_subscription(diesel_models::subscription::SubscriptionUpdate::new(
-            payment.payment_method_id.clone(),
-            None,
-            None,
-        ))
+        .update_subscription(
+            hyperswitch_domain_models::subscription::SubscriptionUpdate::new(
+                payment.payment_method_id.clone(),
+                None,
+                None,
+            ),
+        )
         .await
         .attach_printable("subscriptions: failed to update subscription")?;
 
@@ -146,7 +148,6 @@ pub async fn get_subscription_plans(
     }
     Ok(ApplicationResponse::Json(response))
 }
-
 /// Creates and confirms a subscription in one operation.
 pub async fn create_and_confirm_subscription(
     state: SessionState,
@@ -253,16 +254,18 @@ pub async fn create_and_confirm_subscription(
         .await?;
 
     subs_handler
-        .update_subscription(diesel_models::subscription::SubscriptionUpdate::new(
-            payment_response.payment_method_id.clone(),
-            Some(SubscriptionStatus::from(subscription_create_response.status).to_string()),
-            Some(
-                subscription_create_response
-                    .subscription_id
-                    .get_string_repr()
-                    .to_string(),
+        .update_subscription(
+            hyperswitch_domain_models::subscription::SubscriptionUpdate::new(
+                payment_response.payment_method_id.clone(),
+                Some(SubscriptionStatus::from(subscription_create_response.status).to_string()),
+                Some(
+                    subscription_create_response
+                        .subscription_id
+                        .get_string_repr()
+                        .to_string(),
+                ),
             ),
-        ))
+        )
         .await?;
 
     let response = subs_handler.generate_response(
@@ -386,16 +389,18 @@ pub async fn confirm_subscription(
         .await?;
 
     subscription_entry
-        .update_subscription(diesel_models::subscription::SubscriptionUpdate::new(
-            payment_response.payment_method_id.clone(),
-            Some(SubscriptionStatus::from(subscription_create_response.status).to_string()),
-            Some(
-                subscription_create_response
-                    .subscription_id
-                    .get_string_repr()
-                    .to_string(),
+        .update_subscription(
+            hyperswitch_domain_models::subscription::SubscriptionUpdate::new(
+                payment_response.payment_method_id.clone(),
+                Some(SubscriptionStatus::from(subscription_create_response.status).to_string()),
+                Some(
+                    subscription_create_response
+                        .subscription_id
+                        .get_string_repr()
+                        .to_string(),
+                ),
             ),
-        ))
+        )
         .await?;
 
     let response = subscription_entry.generate_response(
diff --git a/crates/router/src/core/subscription/billing_processor_handler.rs b/crates/router/src/core/subscription/billing_processor_handler.rs
index aa361ef8ce9..910596d66c4 100644
--- a/crates/router/src/core/subscription/billing_processor_handler.rs
+++ b/crates/router/src/core/subscription/billing_processor_handler.rs
@@ -176,7 +176,7 @@ impl BillingHandler {
     pub async fn create_subscription_on_connector(
         &self,
         state: &SessionState,
-        subscription: diesel_models::subscription::Subscription,
+        subscription: hyperswitch_domain_models::subscription::Subscription,
         item_price_id: Option<String>,
         billing_address: Option<api_models::payments::Address>,
     ) -> errors::RouterResult<subscription_response_types::SubscriptionCreateResponse> {
diff --git a/crates/router/src/core/subscription/invoice_handler.rs b/crates/router/src/core/subscription/invoice_handler.rs
index b7d5cb328fd..b383f7a4650 100644
--- a/crates/router/src/core/subscription/invoice_handler.rs
+++ b/crates/router/src/core/subscription/invoice_handler.rs
@@ -10,12 +10,14 @@ use masking::{PeekInterface, Secret};
 
 use super::errors;
 use crate::{
-    core::subscription::payments_api_client, routes::SessionState, types::storage as storage_types,
+    core::{errors::utils::StorageErrorExt, subscription::payments_api_client},
+    routes::SessionState,
+    types::storage as storage_types,
     workflows::invoice_sync as invoice_sync_workflow,
 };
 
 pub struct InvoiceHandler {
-    pub subscription: diesel_models::subscription::Subscription,
+    pub subscription: hyperswitch_domain_models::subscription::Subscription,
     pub merchant_account: hyperswitch_domain_models::merchant_account::MerchantAccount,
     pub profile: hyperswitch_domain_models::business_profile::Profile,
 }
@@ -23,7 +25,7 @@ pub struct InvoiceHandler {
 #[allow(clippy::todo)]
 impl InvoiceHandler {
     pub fn new(
-        subscription: diesel_models::subscription::Subscription,
+        subscription: hyperswitch_domain_models::subscription::Subscription,
         merchant_account: hyperswitch_domain_models::merchant_account::MerchantAccount,
         profile: hyperswitch_domain_models::business_profile::Profile,
     ) -> Self {
@@ -45,8 +47,8 @@ impl InvoiceHandler {
         provider_name: connector_enums::Connector,
         metadata: Option<pii::SecretSerdeValue>,
         connector_invoice_id: Option<String>,
-    ) -> errors::RouterResult<diesel_models::invoice::Invoice> {
-        let invoice_new = diesel_models::invoice::InvoiceNew::new(
+    ) -> errors::RouterResult<hyperswitch_domain_models::invoice::Invoice> {
+        let invoice_new = hyperswitch_domain_models::invoice::Invoice::to_invoice(
             self.subscription.id.to_owned(),
             self.subscription.merchant_id.to_owned(),
             self.subscription.profile_id.to_owned(),
@@ -62,9 +64,19 @@ impl InvoiceHandler {
             connector_invoice_id,
         );
 
+        let key_manager_state = &(state).into();
+        let merchant_key_store = state
+            .store
+            .get_merchant_key_store_by_merchant_id(
+                key_manager_state,
+                self.merchant_account.get_id(),
+                &state.store.get_master_key().to_vec().into(),
+            )
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
         let invoice = state
             .store
-            .insert_invoice_entry(invoice_new)
+            .insert_invoice_entry(key_manager_state, &merchant_key_store, invoice_new)
             .await
             .change_context(errors::ApiErrorResponse::SubscriptionError {
                 operation: "Create Invoice".to_string(),
@@ -82,16 +94,31 @@ impl InvoiceHandler {
         payment_intent_id: Option<common_utils::id_type::PaymentId>,
         status: connector_enums::InvoiceStatus,
         connector_invoice_id: Option<String>,
-    ) -> errors::RouterResult<diesel_models::invoice::Invoice> {
-        let update_invoice = diesel_models::invoice::InvoiceUpdate::new(
+    ) -> errors::RouterResult<hyperswitch_domain_models::invoice::Invoice> {
+        let update_invoice = hyperswitch_domain_models::invoice::InvoiceUpdate::new(
             payment_method_id.as_ref().map(|id| id.peek()).cloned(),
             Some(status),
             connector_invoice_id,
             payment_intent_id,
         );
+        let key_manager_state = &(state).into();
+        let merchant_key_store = state
+            .store
+            .get_merchant_key_store_by_merchant_id(
+                key_manager_state,
+                self.merchant_account.get_id(),
+                &state.store.get_master_key().to_vec().into(),
+            )
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
         state
             .store
-            .update_invoice_entry(invoice_id.get_string_repr().to_string(), update_invoice)
+            .update_invoice_entry(
+                key_manager_state,
+                &merchant_key_store,
+                invoice_id.get_string_repr().to_string(),
+                update_invoice,
+            )
             .await
             .change_context(errors::ApiErrorResponse::SubscriptionError {
                 operation: "Invoice Update".to_string(),
@@ -213,10 +240,24 @@ impl InvoiceHandler {
     pub async fn get_latest_invoice(
         &self,
         state: &SessionState,
-    ) -> errors::RouterResult<diesel_models::invoice::Invoice> {
+    ) -> errors::RouterResult<hyperswitch_domain_models::invoice::Invoice> {
+        let key_manager_state = &(state).into();
+        let merchant_key_store = state
+            .store
+            .get_merchant_key_store_by_merchant_id(
+                key_manager_state,
+                self.merchant_account.get_id(),
+                &state.store.get_master_key().to_vec().into(),
+            )
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
         state
             .store
-            .get_latest_invoice_for_subscription(self.subscription.id.get_string_repr().to_string())
+            .get_latest_invoice_for_subscription(
+                key_manager_state,
+                &merchant_key_store,
+                self.subscription.id.get_string_repr().to_string(),
+            )
             .await
             .change_context(errors::ApiErrorResponse::SubscriptionError {
                 operation: "Get Latest Invoice".to_string(),
@@ -228,10 +269,24 @@ impl InvoiceHandler {
         &self,
         state: &SessionState,
         invoice_id: common_utils::id_type::InvoiceId,
-    ) -> errors::RouterResult<diesel_models::invoice::Invoice> {
+    ) -> errors::RouterResult<hyperswitch_domain_models::invoice::Invoice> {
+        let key_manager_state = &(state).into();
+        let merchant_key_store = state
+            .store
+            .get_merchant_key_store_by_merchant_id(
+                key_manager_state,
+                self.merchant_account.get_id(),
+                &state.store.get_master_key().to_vec().into(),
+            )
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
         state
             .store
-            .find_invoice_by_invoice_id(invoice_id.get_string_repr().to_string())
+            .find_invoice_by_invoice_id(
+                key_manager_state,
+                &merchant_key_store,
+                invoice_id.get_string_repr().to_string(),
+            )
             .await
             .change_context(errors::ApiErrorResponse::SubscriptionError {
                 operation: "Get Invoice by ID".to_string(),
@@ -242,7 +297,7 @@ impl InvoiceHandler {
     pub async fn create_invoice_sync_job(
         &self,
         state: &SessionState,
-        invoice: &diesel_models::invoice::Invoice,
+        invoice: &hyperswitch_domain_models::invoice::Invoice,
         connector_invoice_id: String,
         connector_name: connector_enums::Connector,
     ) -> errors::RouterResult<()> {
diff --git a/crates/router/src/core/subscription/subscription_handler.rs b/crates/router/src/core/subscription/subscription_handler.rs
index b608faca5d5..76799c21a74 100644
--- a/crates/router/src/core/subscription/subscription_handler.rs
+++ b/crates/router/src/core/subscription/subscription_handler.rs
@@ -2,15 +2,15 @@ use std::str::FromStr;
 
 use api_models::{
     enums as api_enums,
-    subscription::{self as subscription_types, SubscriptionResponse, SubscriptionStatus},
+    subscription::{self as subscription_types, SubscriptionResponse},
 };
 use common_enums::connector_enums;
 use common_utils::{consts, ext_traits::OptionExt};
-use diesel_models::subscription::SubscriptionNew;
 use error_stack::ResultExt;
 use hyperswitch_domain_models::{
     merchant_context::MerchantContext,
     router_response_types::subscriptions as subscription_response_types,
+    subscription::{Subscription, SubscriptionStatus},
 };
 use masking::Secret;
 
@@ -48,28 +48,33 @@ impl<'a> SubscriptionHandler<'a> {
         let store = self.state.store.clone();
         let db = store.as_ref();
 
-        let mut subscription = SubscriptionNew::new(
-            subscription_id,
-            SubscriptionStatus::Created.to_string(),
-            Some(billing_processor.to_string()),
-            None,
-            Some(merchant_connector_id),
-            None,
-            None,
-            self.merchant_context
+        let mut subscription = Subscription {
+            id: subscription_id,
+            status: SubscriptionStatus::Created.to_string(),
+            billing_processor: Some(billing_processor.to_string()),
+            payment_method_id: None,
+            merchant_connector_id: Some(merchant_connector_id),
+            client_secret: None,
+            connector_subscription_id: None,
+            merchant_id: self
+                .merchant_context
                 .get_merchant_account()
                 .get_id()
                 .clone(),
-            customer_id.clone(),
-            None,
-            profile.get_id().clone(),
+            customer_id: customer_id.clone(),
+            metadata: None,
+            profile_id: profile.get_id().clone(),
             merchant_reference_id,
-        );
+            created_at: common_utils::date_time::now(),
+            modified_at: common_utils::date_time::now(),
+        };
 
         subscription.generate_and_set_client_secret();
 
+        let key_manager_state = &(self.state).into();
+        let merchant_key_store = self.merchant_context.get_merchant_key_store();
         let new_subscription = db
-            .insert_subscription_entry(subscription)
+            .insert_subscription_entry(key_manager_state, merchant_key_store, subscription)
             .await
             .change_context(errors::ApiErrorResponse::InternalServerError)
             .attach_printable("subscriptions: unable to insert subscription entry to database")?;
@@ -129,10 +134,15 @@ impl<'a> SubscriptionHandler<'a> {
     ) -> errors::RouterResult<()> {
         let subscription_id = client_secret.get_subscription_id()?;
 
+        let key_manager_state = &(self.state).into();
+        let key_store = self.merchant_context.get_merchant_key_store();
+
         let subscription = self
             .state
             .store
             .find_by_merchant_id_subscription_id(
+                key_manager_state,
+                key_store,
                 self.merchant_context.get_merchant_account().get_id(),
                 subscription_id.to_string(),
             )
@@ -150,7 +160,7 @@ impl<'a> SubscriptionHandler<'a> {
     pub fn validate_client_secret(
         &self,
         client_secret: &hyperswitch_domain_models::subscription::ClientSecret,
-        subscription: &diesel_models::subscription::Subscription,
+        subscription: &Subscription,
     ) -> errors::RouterResult<()> {
         let stored_client_secret = subscription
             .client_secret
@@ -185,6 +195,8 @@ impl<'a> SubscriptionHandler<'a> {
             .state
             .store
             .find_by_merchant_id_subscription_id(
+                &(self.state).into(),
+                self.merchant_context.get_merchant_key_store(),
                 self.merchant_context.get_merchant_account().get_id(),
                 subscription_id.get_string_repr().to_string().clone(),
             )
@@ -205,21 +217,21 @@ impl<'a> SubscriptionHandler<'a> {
 }
 pub struct SubscriptionWithHandler<'a> {
     pub handler: &'a SubscriptionHandler<'a>,
-    pub subscription: diesel_models::subscription::Subscription,
+    pub subscription: Subscription,
     pub merchant_account: hyperswitch_domain_models::merchant_account::MerchantAccount,
 }
 
 impl SubscriptionWithHandler<'_> {
     pub fn generate_response(
         &self,
-        invoice: &diesel_models::invoice::Invoice,
+        invoice: &hyperswitch_domain_models::invoice::Invoice,
         payment_response: &subscription_types::PaymentResponseData,
         status: subscription_response_types::SubscriptionStatus,
     ) -> errors::RouterResult<subscription_types::ConfirmSubscriptionResponse> {
         Ok(subscription_types::ConfirmSubscriptionResponse {
             id: self.subscription.id.clone(),
             merchant_reference_id: self.subscription.merchant_reference_id.clone(),
-            status: SubscriptionStatus::from(status),
+            status: subscription_types::SubscriptionStatus::from(status),
             plan_id: None,
             profile_id: self.subscription.profile_id.to_owned(),
             payment: Some(payment_response.clone()),
@@ -234,13 +246,13 @@ impl SubscriptionWithHandler<'_> {
     pub fn to_subscription_response(
         &self,
         payment: Option<subscription_types::PaymentResponseData>,
-        invoice: Option<&diesel_models::invoice::Invoice>,
+        invoice: Option<&hyperswitch_domain_models::invoice::Invoice>,
     ) -> errors::RouterResult<SubscriptionResponse> {
         Ok(SubscriptionResponse::new(
             self.subscription.id.clone(),
             self.subscription.merchant_reference_id.clone(),
-            SubscriptionStatus::from_str(&self.subscription.status)
-                .unwrap_or(SubscriptionStatus::Created),
+            subscription_types::SubscriptionStatus::from_str(&self.subscription.status)
+                .unwrap_or(subscription_types::SubscriptionStatus::Created),
             None,
             self.subscription.profile_id.to_owned(),
             self.subscription.merchant_id.to_owned(),
@@ -259,11 +271,13 @@ impl SubscriptionWithHandler<'_> {
 
     pub async fn update_subscription(
         &mut self,
-        subscription_update: diesel_models::subscription::SubscriptionUpdate,
+        subscription_update: hyperswitch_domain_models::subscription::SubscriptionUpdate,
     ) -> errors::RouterResult<()> {
         let db = self.handler.state.store.as_ref();
         let updated_subscription = db
             .update_subscription_entry(
+                &(self.handler.state).into(),
+                self.handler.merchant_context.get_merchant_key_store(),
                 self.handler
                     .merchant_context
                     .get_merchant_account()
@@ -363,10 +377,12 @@ impl SubscriptionWithHandler<'_> {
     }
 }
 
-impl ForeignTryFrom<&diesel_models::invoice::Invoice> for subscription_types::Invoice {
+impl ForeignTryFrom<&hyperswitch_domain_models::invoice::Invoice> for subscription_types::Invoice {
     type Error = error_stack::Report<errors::ApiErrorResponse>;
 
-    fn foreign_try_from(invoice: &diesel_models::invoice::Invoice) -> Result<Self, Self::Error> {
+    fn foreign_try_from(
+        invoice: &hyperswitch_domain_models::invoice::Invoice,
+    ) -> Result<Self, Self::Error> {
         Ok(Self {
             id: invoice.id.clone(),
             subscription_id: invoice.subscription_id.clone(),
diff --git a/crates/router/src/db.rs b/crates/router/src/db.rs
index 2cead7d0509..4aef4efd3e1 100644
--- a/crates/router/src/db.rs
+++ b/crates/router/src/db.rs
@@ -21,7 +21,6 @@ pub mod generic_link;
 pub mod gsm;
 pub mod health_check;
 pub mod hyperswitch_ai_interaction;
-pub mod invoice;
 pub mod kafka_store;
 pub mod locker_mock_up;
 pub mod mandate;
@@ -36,7 +35,6 @@ pub mod relay;
 pub mod reverse_lookup;
 pub mod role;
 pub mod routing_algorithm;
-pub mod subscription;
 pub mod unified_translations;
 pub mod user;
 pub mod user_authentication_method;
@@ -147,8 +145,8 @@ pub trait StorageInterface:
     + payment_method_session::PaymentMethodsSessionInterface
     + tokenization::TokenizationInterface
     + callback_mapper::CallbackMapperInterface
-    + subscription::SubscriptionInterface
-    + invoice::InvoiceInterface
+    + storage_impl::subscription::SubscriptionInterface<Error = StorageError>
+    + storage_impl::invoice::InvoiceInterface<Error = StorageError>
     + 'static
 {
     fn get_scheduler_db(&self) -> Box<dyn scheduler::SchedulerInterface>;
diff --git a/crates/router/src/db/invoice.rs b/crates/router/src/db/invoice.rs
deleted file mode 100644
index b12a806840f..00000000000
--- a/crates/router/src/db/invoice.rs
+++ /dev/null
@@ -1,169 +0,0 @@
-use error_stack::report;
-use router_env::{instrument, tracing};
-use storage_impl::MockDb;
-
-use super::Store;
-use crate::{
-    connection,
-    core::errors::{self, CustomResult},
-    db::kafka_store::KafkaStore,
-    types::storage,
-};
-
-#[async_trait::async_trait]
-pub trait InvoiceInterface {
-    async fn insert_invoice_entry(
-        &self,
-        invoice_new: storage::invoice::InvoiceNew,
-    ) -> CustomResult<storage::Invoice, errors::StorageError>;
-
-    async fn find_invoice_by_invoice_id(
-        &self,
-        invoice_id: String,
-    ) -> CustomResult<storage::Invoice, errors::StorageError>;
-
-    async fn update_invoice_entry(
-        &self,
-        invoice_id: String,
-        data: storage::invoice::InvoiceUpdate,
-    ) -> CustomResult<storage::Invoice, errors::StorageError>;
-
-    async fn get_latest_invoice_for_subscription(
-        &self,
-        subscription_id: String,
-    ) -> CustomResult<storage::Invoice, errors::StorageError>;
-}
-
-#[async_trait::async_trait]
-impl InvoiceInterface for Store {
-    #[instrument(skip_all)]
-    async fn insert_invoice_entry(
-        &self,
-        invoice_new: storage::invoice::InvoiceNew,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        let conn = connection::pg_connection_write(self).await?;
-        invoice_new
-            .insert(&conn)
-            .await
-            .map_err(|error| report!(errors::StorageError::from(error)))
-    }
-
-    #[instrument(skip_all)]
-    async fn find_invoice_by_invoice_id(
-        &self,
-        invoice_id: String,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        let conn = connection::pg_connection_read(self).await?;
-        storage::Invoice::find_invoice_by_id_invoice_id(&conn, invoice_id)
-            .await
-            .map_err(|error| report!(errors::StorageError::from(error)))
-    }
-
-    #[instrument(skip_all)]
-    async fn update_invoice_entry(
-        &self,
-        invoice_id: String,
-        data: storage::invoice::InvoiceUpdate,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        let conn = connection::pg_connection_write(self).await?;
-        storage::Invoice::update_invoice_entry(&conn, invoice_id, data)
-            .await
-            .map_err(|error| report!(errors::StorageError::from(error)))
-    }
-
-    #[instrument(skip_all)]
-    async fn get_latest_invoice_for_subscription(
-        &self,
-        subscription_id: String,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        let conn = connection::pg_connection_write(self).await?;
-        storage::Invoice::list_invoices_by_subscription_id(
-            &conn,
-            subscription_id.clone(),
-            Some(1),
-            None,
-            false,
-        )
-        .await
-        .map_err(|error| report!(errors::StorageError::from(error)))
-        .map(|e| e.last().cloned())?
-        .ok_or(report!(errors::StorageError::ValueNotFound(format!(
-            "Invoice not found for subscription_id: {}",
-            subscription_id
-        ))))
-    }
-}
-
-#[async_trait::async_trait]
-impl InvoiceInterface for MockDb {
-    #[instrument(skip_all)]
-    async fn insert_invoice_entry(
-        &self,
-        _invoice_new: storage::invoice::InvoiceNew,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        Err(errors::StorageError::MockDbError)?
-    }
-
-    async fn find_invoice_by_invoice_id(
-        &self,
-        _invoice_id: String,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        Err(errors::StorageError::MockDbError)?
-    }
-
-    async fn update_invoice_entry(
-        &self,
-        _invoice_id: String,
-        _data: storage::invoice::InvoiceUpdate,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        Err(errors::StorageError::MockDbError)?
-    }
-
-    async fn get_latest_invoice_for_subscription(
-        &self,
-        _subscription_id: String,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        Err(errors::StorageError::MockDbError)?
-    }
-}
-
-#[async_trait::async_trait]
-impl InvoiceInterface for KafkaStore {
-    #[instrument(skip_all)]
-    async fn insert_invoice_entry(
-        &self,
-        invoice_new: storage::invoice::InvoiceNew,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        self.diesel_store.insert_invoice_entry(invoice_new).await
-    }
-
-    #[instrument(skip_all)]
-    async fn find_invoice_by_invoice_id(
-        &self,
-        invoice_id: String,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        self.diesel_store
-            .find_invoice_by_invoice_id(invoice_id)
-            .await
-    }
-
-    #[instrument(skip_all)]
-    async fn update_invoice_entry(
-        &self,
-        invoice_id: String,
-        data: storage::invoice::InvoiceUpdate,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        self.diesel_store
-            .update_invoice_entry(invoice_id, data)
-            .await
-    }
-
-    async fn get_latest_invoice_for_subscription(
-        &self,
-        subscription_id: String,
-    ) -> CustomResult<storage::Invoice, errors::StorageError> {
-        self.diesel_store
-            .get_latest_invoice_for_subscription(subscription_id)
-            .await
-    }
-}
diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs
index 8bbee6ab8cf..84799f91cf5 100644
--- a/crates/router/src/db/kafka_store.rs
+++ b/crates/router/src/db/kafka_store.rs
@@ -23,9 +23,14 @@ use hyperswitch_domain_models::payouts::{
 use hyperswitch_domain_models::{
     cards_info::CardsInfoInterface,
     disputes,
+    invoice::{Invoice as DomainInvoice, InvoiceInterface, InvoiceUpdate as DomainInvoiceUpdate},
     payment_methods::PaymentMethodInterface,
     payments::{payment_attempt::PaymentAttemptInterface, payment_intent::PaymentIntentInterface},
     refunds,
+    subscription::{
+        Subscription as DomainSubscription, SubscriptionInterface,
+        SubscriptionUpdate as DomainSubscriptionUpdate,
+    },
 };
 #[cfg(not(feature = "payouts"))]
 use hyperswitch_domain_models::{PayoutAttemptInterface, PayoutsInterface};
@@ -4335,3 +4340,101 @@ impl TokenizationInterface for KafkaStore {
 
 #[cfg(not(all(feature = "v2", feature = "tokenization_v2")))]
 impl TokenizationInterface for KafkaStore {}
+
+#[async_trait::async_trait]
+impl InvoiceInterface for KafkaStore {
+    type Error = errors::StorageError;
+
+    #[instrument(skip_all)]
+    async fn insert_invoice_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
+        invoice_new: DomainInvoice,
+    ) -> CustomResult<DomainInvoice, errors::StorageError> {
+        self.diesel_store
+            .insert_invoice_entry(state, key_store, invoice_new)
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn find_invoice_by_invoice_id(
+        &self,
+        state: &KeyManagerState,
+        key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
+        invoice_id: String,
+    ) -> CustomResult<DomainInvoice, errors::StorageError> {
+        self.diesel_store
+            .find_invoice_by_invoice_id(state, key_store, invoice_id)
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn update_invoice_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
+        invoice_id: String,
+        data: DomainInvoiceUpdate,
+    ) -> CustomResult<DomainInvoice, errors::StorageError> {
+        self.diesel_store
+            .update_invoice_entry(state, key_store, invoice_id, data)
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn get_latest_invoice_for_subscription(
+        &self,
+        state: &KeyManagerState,
+        key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
+        subscription_id: String,
+    ) -> CustomResult<DomainInvoice, errors::StorageError> {
+        self.diesel_store
+            .get_latest_invoice_for_subscription(state, key_store, subscription_id)
+            .await
+    }
+}
+
+#[async_trait::async_trait]
+impl SubscriptionInterface for KafkaStore {
+    type Error = errors::StorageError;
+
+    #[instrument(skip_all)]
+    async fn insert_subscription_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
+        subscription_new: DomainSubscription,
+    ) -> CustomResult<DomainSubscription, errors::StorageError> {
+        self.diesel_store
+            .insert_subscription_entry(state, key_store, subscription_new)
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn find_by_merchant_id_subscription_id(
+        &self,
+        state: &KeyManagerState,
+        key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
+        merchant_id: &id_type::MerchantId,
+        subscription_id: String,
+    ) -> CustomResult<DomainSubscription, errors::StorageError> {
+        self.diesel_store
+            .find_by_merchant_id_subscription_id(state, key_store, merchant_id, subscription_id)
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn update_subscription_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore,
+        merchant_id: &id_type::MerchantId,
+        subscription_id: String,
+        data: DomainSubscriptionUpdate,
+    ) -> CustomResult<DomainSubscription, errors::StorageError> {
+        self.diesel_store
+            .update_subscription_entry(state, key_store, merchant_id, subscription_id, data)
+            .await
+    }
+}
diff --git a/crates/router/src/db/subscription.rs b/crates/router/src/db/subscription.rs
deleted file mode 100644
index 5242c44299b..00000000000
--- a/crates/router/src/db/subscription.rs
+++ /dev/null
@@ -1,140 +0,0 @@
-use error_stack::report;
-use router_env::{instrument, tracing};
-use storage_impl::MockDb;
-
-use super::Store;
-use crate::{
-    connection,
-    core::errors::{self, CustomResult},
-    db::kafka_store::KafkaStore,
-    types::storage,
-};
-
-#[async_trait::async_trait]
-pub trait SubscriptionInterface {
-    async fn insert_subscription_entry(
-        &self,
-        subscription_new: storage::subscription::SubscriptionNew,
-    ) -> CustomResult<storage::Subscription, errors::StorageError>;
-
-    async fn find_by_merchant_id_subscription_id(
-        &self,
-        merchant_id: &common_utils::id_type::MerchantId,
-        subscription_id: String,
-    ) -> CustomResult<storage::Subscription, errors::StorageError>;
-
-    async fn update_subscription_entry(
-        &self,
-        merchant_id: &common_utils::id_type::MerchantId,
-        subscription_id: String,
-        data: storage::SubscriptionUpdate,
-    ) -> CustomResult<storage::Subscription, errors::StorageError>;
-}
-
-#[async_trait::async_trait]
-impl SubscriptionInterface for Store {
-    #[instrument(skip_all)]
-    async fn insert_subscription_entry(
-        &self,
-        subscription_new: storage::subscription::SubscriptionNew,
-    ) -> CustomResult<storage::Subscription, errors::StorageError> {
-        let conn = connection::pg_connection_write(self).await?;
-        subscription_new
-            .insert(&conn)
-            .await
-            .map_err(|error| report!(errors::StorageError::from(error)))
-    }
-
-    #[instrument(skip_all)]
-    async fn find_by_merchant_id_subscription_id(
-        &self,
-        merchant_id: &common_utils::id_type::MerchantId,
-        subscription_id: String,
-    ) -> CustomResult<storage::Subscription, errors::StorageError> {
-        let conn = connection::pg_connection_write(self).await?;
-        storage::Subscription::find_by_merchant_id_subscription_id(
-            &conn,
-            merchant_id,
-            subscription_id,
-        )
-        .await
-        .map_err(|error| report!(errors::StorageError::from(error)))
-    }
-
-    #[instrument(skip_all)]
-    async fn update_subscription_entry(
-        &self,
-        merchant_id: &common_utils::id_type::MerchantId,
-        subscription_id: String,
-        data: storage::SubscriptionUpdate,
-    ) -> CustomResult<storage::Subscription, errors::StorageError> {
-        let conn = connection::pg_connection_write(self).await?;
-        storage::Subscription::update_subscription_entry(&conn, merchant_id, subscription_id, data)
-            .await
-            .map_err(|error| report!(errors::StorageError::from(error)))
-    }
-}
-
-#[async_trait::async_trait]
-impl SubscriptionInterface for MockDb {
-    #[instrument(skip_all)]
-    async fn insert_subscription_entry(
-        &self,
-        _subscription_new: storage::subscription::SubscriptionNew,
-    ) -> CustomResult<storage::Subscription, errors::StorageError> {
-        Err(errors::StorageError::MockDbError)?
-    }
-
-    async fn find_by_merchant_id_subscription_id(
-        &self,
-        _merchant_id: &common_utils::id_type::MerchantId,
-        _subscription_id: String,
-    ) -> CustomResult<storage::Subscription, errors::StorageError> {
-        Err(errors::StorageError::MockDbError)?
-    }
-
-    async fn update_subscription_entry(
-        &self,
-        _merchant_id: &common_utils::id_type::MerchantId,
-        _subscription_id: String,
-        _data: storage::SubscriptionUpdate,
-    ) -> CustomResult<storage::Subscription, errors::StorageError> {
-        Err(errors::StorageError::MockDbError)?
-    }
-}
-
-#[async_trait::async_trait]
-impl SubscriptionInterface for KafkaStore {
-    #[instrument(skip_all)]
-    async fn insert_subscription_entry(
-        &self,
-        subscription_new: storage::subscription::SubscriptionNew,
-    ) -> CustomResult<storage::Subscription, errors::StorageError> {
-        self.diesel_store
-            .insert_subscription_entry(subscription_new)
-            .await
-    }
-
-    #[instrument(skip_all)]
-    async fn find_by_merchant_id_subscription_id(
-        &self,
-        merchant_id: &common_utils::id_type::MerchantId,
-        subscription_id: String,
-    ) -> CustomResult<storage::Subscription, errors::StorageError> {
-        self.diesel_store
-            .find_by_merchant_id_subscription_id(merchant_id, subscription_id)
-            .await
-    }
-
-    #[instrument(skip_all)]
-    async fn update_subscription_entry(
-        &self,
-        merchant_id: &common_utils::id_type::MerchantId,
-        subscription_id: String,
-        data: storage::SubscriptionUpdate,
-    ) -> CustomResult<storage::Subscription, errors::StorageError> {
-        self.diesel_store
-            .update_subscription_entry(merchant_id, subscription_id, data)
-            .await
-    }
-}
diff --git a/crates/router/src/workflows/invoice_sync.rs b/crates/router/src/workflows/invoice_sync.rs
index ec71af2b708..d72e59904f1 100644
--- a/crates/router/src/workflows/invoice_sync.rs
+++ b/crates/router/src/workflows/invoice_sync.rs
@@ -5,9 +5,7 @@ use common_utils::{
     errors::CustomResult,
     ext_traits::{StringExt, ValueExt},
 };
-use diesel_models::{
-    invoice::Invoice, process_tracker::business_status, subscription::Subscription,
-};
+use diesel_models::process_tracker::business_status;
 use error_stack::ResultExt;
 use router_env::logger;
 use scheduler::{
@@ -39,8 +37,8 @@ pub struct InvoiceSyncHandler<'a> {
     pub merchant_account: domain::MerchantAccount,
     pub customer: domain::Customer,
     pub profile: domain::Profile,
-    pub subscription: Subscription,
-    pub invoice: Invoice,
+    pub subscription: hyperswitch_domain_models::subscription::Subscription,
+    pub invoice: hyperswitch_domain_models::invoice::Invoice,
 }
 
 #[cfg(feature = "v1")]
@@ -95,6 +93,8 @@ impl<'a> InvoiceSyncHandler<'a> {
         let subscription = state
             .store
             .find_by_merchant_id_subscription_id(
+                &state.into(),
+                &key_store,
                 merchant_account.get_id(),
                 tracking_data.subscription_id.get_string_repr().to_string(),
             )
@@ -103,7 +103,11 @@ impl<'a> InvoiceSyncHandler<'a> {
 
         let invoice = state
             .store
-            .find_invoice_by_invoice_id(tracking_data.invoice_id.get_string_repr().to_string())
+            .find_invoice_by_invoice_id(
+                &state.into(),
+                &key_store,
+                tracking_data.invoice_id.get_string_repr().to_string(),
+            )
             .await
             .attach_printable("invoices: unable to get latest invoice from database")?;
 
diff --git a/crates/storage_impl/src/invoice.rs b/crates/storage_impl/src/invoice.rs
new file mode 100644
index 00000000000..95330239167
--- /dev/null
+++ b/crates/storage_impl/src/invoice.rs
@@ -0,0 +1,200 @@
+use common_utils::{errors::CustomResult, types::keymanager::KeyManagerState};
+pub use diesel_models::invoice::Invoice;
+use error_stack::{report, ResultExt};
+pub use hyperswitch_domain_models::{
+    behaviour::Conversion,
+    invoice::{Invoice as DomainInvoice, InvoiceInterface, InvoiceUpdate as DomainInvoiceUpdate},
+    merchant_key_store::MerchantKeyStore,
+};
+use router_env::{instrument, tracing};
+
+use crate::{
+    connection, errors::StorageError, kv_router_store::KVRouterStore, DatabaseStore, MockDb,
+    RouterStore,
+};
+
+#[async_trait::async_trait]
+impl<T: DatabaseStore> InvoiceInterface for RouterStore<T> {
+    type Error = StorageError;
+
+    #[instrument(skip_all)]
+    async fn insert_invoice_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        invoice_new: DomainInvoice,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        let inv_new = invoice_new
+            .construct_new()
+            .await
+            .change_context(StorageError::DecryptionError)?;
+        let conn = connection::pg_connection_write(self).await?;
+        self.call_database(state, key_store, inv_new.insert(&conn))
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn find_invoice_by_invoice_id(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        invoice_id: String,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        let conn = connection::pg_connection_read(self).await?;
+        self.call_database(
+            state,
+            key_store,
+            Invoice::find_invoice_by_id_invoice_id(&conn, invoice_id),
+        )
+        .await
+    }
+
+    #[instrument(skip_all)]
+    async fn update_invoice_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        invoice_id: String,
+        data: DomainInvoiceUpdate,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        let inv_new = data
+            .construct_new()
+            .await
+            .change_context(StorageError::DecryptionError)?;
+        let conn = connection::pg_connection_write(self).await?;
+        self.call_database(
+            state,
+            key_store,
+            Invoice::update_invoice_entry(&conn, invoice_id, inv_new),
+        )
+        .await
+    }
+
+    #[instrument(skip_all)]
+    async fn get_latest_invoice_for_subscription(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        subscription_id: String,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        let conn = connection::pg_connection_write(self).await?;
+        let invoices: Vec<DomainInvoice> = self
+            .find_resources(
+                state,
+                key_store,
+                Invoice::list_invoices_by_subscription_id(
+                    &conn,
+                    subscription_id.clone(),
+                    Some(1),
+                    None,
+                    false,
+                ),
+            )
+            .await?;
+
+        invoices
+            .last()
+            .cloned()
+            .ok_or(report!(StorageError::ValueNotFound(format!(
+                "Invoice not found for subscription_id: {}",
+                subscription_id
+            ))))
+    }
+}
+
+#[async_trait::async_trait]
+impl<T: DatabaseStore> InvoiceInterface for KVRouterStore<T> {
+    type Error = StorageError;
+
+    #[instrument(skip_all)]
+    async fn insert_invoice_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        invoice_new: DomainInvoice,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        self.router_store
+            .insert_invoice_entry(state, key_store, invoice_new)
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn find_invoice_by_invoice_id(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        invoice_id: String,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        self.router_store
+            .find_invoice_by_invoice_id(state, key_store, invoice_id)
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn update_invoice_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        invoice_id: String,
+        data: DomainInvoiceUpdate,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        self.router_store
+            .update_invoice_entry(state, key_store, invoice_id, data)
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn get_latest_invoice_for_subscription(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        subscription_id: String,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        self.router_store
+            .get_latest_invoice_for_subscription(state, key_store, subscription_id)
+            .await
+    }
+}
+
+#[async_trait::async_trait]
+impl InvoiceInterface for MockDb {
+    type Error = StorageError;
+
+    #[instrument(skip_all)]
+    async fn insert_invoice_entry(
+        &self,
+        _state: &KeyManagerState,
+        _key_store: &MerchantKeyStore,
+        _invoice_new: DomainInvoice,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        Err(StorageError::MockDbError)?
+    }
+
+    async fn find_invoice_by_invoice_id(
+        &self,
+        _state: &KeyManagerState,
+        _key_store: &MerchantKeyStore,
+        _invoice_id: String,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        Err(StorageError::MockDbError)?
+    }
+
+    async fn update_invoice_entry(
+        &self,
+        _state: &KeyManagerState,
+        _key_store: &MerchantKeyStore,
+        _invoice_id: String,
+        _data: DomainInvoiceUpdate,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        Err(StorageError::MockDbError)?
+    }
+
+    async fn get_latest_invoice_for_subscription(
+        &self,
+        _state: &KeyManagerState,
+        _key_store: &MerchantKeyStore,
+        _subscription_id: String,
+    ) -> CustomResult<DomainInvoice, StorageError> {
+        Err(StorageError::MockDbError)?
+    }
+}
diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs
index b2a2dba5779..5e6483fd1d3 100644
--- a/crates/storage_impl/src/lib.rs
+++ b/crates/storage_impl/src/lib.rs
@@ -16,6 +16,7 @@ pub mod connection;
 pub mod customers;
 pub mod database;
 pub mod errors;
+pub mod invoice;
 pub mod kv_router_store;
 pub mod lookup;
 pub mod mandate;
@@ -28,6 +29,7 @@ pub mod payouts;
 pub mod redis;
 pub mod refund;
 mod reverse_lookup;
+pub mod subscription;
 pub mod utils;
 
 use common_utils::{errors::CustomResult, types::keymanager::KeyManagerState};
diff --git a/crates/storage_impl/src/subscription.rs b/crates/storage_impl/src/subscription.rs
new file mode 100644
index 00000000000..79e844c9857
--- /dev/null
+++ b/crates/storage_impl/src/subscription.rs
@@ -0,0 +1,155 @@
+use common_utils::{errors::CustomResult, types::keymanager::KeyManagerState};
+pub use diesel_models::subscription::Subscription;
+use error_stack::ResultExt;
+pub use hyperswitch_domain_models::{
+    behaviour::Conversion,
+    merchant_key_store::MerchantKeyStore,
+    subscription::{
+        Subscription as DomainSubscription, SubscriptionInterface,
+        SubscriptionUpdate as DomainSubscriptionUpdate,
+    },
+};
+use router_env::{instrument, tracing};
+
+use crate::{
+    connection, errors::StorageError, kv_router_store::KVRouterStore, DatabaseStore, MockDb,
+    RouterStore,
+};
+
+#[async_trait::async_trait]
+impl<T: DatabaseStore> SubscriptionInterface for RouterStore<T> {
+    type Error = StorageError;
+
+    #[instrument(skip_all)]
+    async fn insert_subscription_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        subscription_new: DomainSubscription,
+    ) -> CustomResult<DomainSubscription, StorageError> {
+        let sub_new = subscription_new
+            .construct_new()
+            .await
+            .change_context(StorageError::DecryptionError)?;
+        let conn = connection::pg_connection_write(self).await?;
+        self.call_database(state, key_store, sub_new.insert(&conn))
+            .await
+    }
+    #[instrument(skip_all)]
+    async fn find_by_merchant_id_subscription_id(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        merchant_id: &common_utils::id_type::MerchantId,
+        subscription_id: String,
+    ) -> CustomResult<DomainSubscription, StorageError> {
+        let conn = connection::pg_connection_write(self).await?;
+        self.call_database(
+            state,
+            key_store,
+            Subscription::find_by_merchant_id_subscription_id(&conn, merchant_id, subscription_id),
+        )
+        .await
+    }
+
+    #[instrument(skip_all)]
+    async fn update_subscription_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        merchant_id: &common_utils::id_type::MerchantId,
+        subscription_id: String,
+        data: DomainSubscriptionUpdate,
+    ) -> CustomResult<DomainSubscription, StorageError> {
+        let sub_new = data
+            .construct_new()
+            .await
+            .change_context(StorageError::DecryptionError)?;
+        let conn = connection::pg_connection_write(self).await?;
+        self.call_database(
+            state,
+            key_store,
+            Subscription::update_subscription_entry(&conn, merchant_id, subscription_id, sub_new),
+        )
+        .await
+    }
+}
+
+#[async_trait::async_trait]
+impl<T: DatabaseStore> SubscriptionInterface for KVRouterStore<T> {
+    type Error = StorageError;
+
+    #[instrument(skip_all)]
+    async fn insert_subscription_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        subscription_new: DomainSubscription,
+    ) -> CustomResult<DomainSubscription, StorageError> {
+        self.router_store
+            .insert_subscription_entry(state, key_store, subscription_new)
+            .await
+    }
+    #[instrument(skip_all)]
+    async fn find_by_merchant_id_subscription_id(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        merchant_id: &common_utils::id_type::MerchantId,
+        subscription_id: String,
+    ) -> CustomResult<DomainSubscription, StorageError> {
+        self.router_store
+            .find_by_merchant_id_subscription_id(state, key_store, merchant_id, subscription_id)
+            .await
+    }
+
+    #[instrument(skip_all)]
+    async fn update_subscription_entry(
+        &self,
+        state: &KeyManagerState,
+        key_store: &MerchantKeyStore,
+        merchant_id: &common_utils::id_type::MerchantId,
+        subscription_id: String,
+        data: DomainSubscriptionUpdate,
+    ) -> CustomResult<DomainSubscription, StorageError> {
+        self.router_store
+            .update_subscription_entry(state, key_store, merchant_id, subscription_id, data)
+            .await
+    }
+}
+
+#[async_trait::async_trait]
+impl SubscriptionInterface for MockDb {
+    type Error = StorageError;
+
+    #[instrument(skip_all)]
+    async fn insert_subscription_entry(
+        &self,
+        _state: &KeyManagerState,
+        _key_store: &MerchantKeyStore,
+        _subscription_new: DomainSubscription,
+    ) -> CustomResult<DomainSubscription, StorageError> {
+        Err(StorageError::MockDbError)?
+    }
+
+    async fn find_by_merchant_id_subscription_id(
+        &self,
+        _state: &KeyManagerState,
+        _key_store: &MerchantKeyStore,
+        _merchant_id: &common_utils::id_type::MerchantId,
+        _subscription_id: String,
+    ) -> CustomResult<DomainSubscription, StorageError> {
+        Err(StorageError::MockDbError)?
+    }
+
+    async fn update_subscription_entry(
+        &self,
+        _state: &KeyManagerState,
+        _key_store: &MerchantKeyStore,
+        _merchant_id: &common_utils::id_type::MerchantId,
+        _subscription_id: String,
+        _data: DomainSubscriptionUpdate,
+    ) -> CustomResult<DomainSubscription, StorageError> {
+        Err(StorageError::MockDbError)?
+    }
+}

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

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

## Description

This pull request introduces new domain models for subscriptions and invoices, and refactors the codebase to use these models instead of the previous Diesel-based models. The changes improve type safety and abstraction for subscription and invoice handling, and update relevant interfaces and function calls throughout the codebase to use the new models.

**Domain Model Additions:**

* Added a new `Subscription` domain model in `crates/hyperswitch_domain_models/src/subscription.rs`, including the `SubscriptionStatus` enum, conversion traits, and the `SubscriptionInterface` trait for standardized operations. Also added the `SubscriptionUpdate` struct with conversion logic.
* Added a new `Invoice` domain model in `crates/hyperswitch_domain_models/src/invoice.rs`, including conversion traits, the `InvoiceInterface` trait for CRUD operations, and the `InvoiceUpdate` struct with conversion logic.

**Refactoring and Integration:**

* Refactored all subscription and invoice-related logic in `crates/router/src/core/subscription.rs` and `crates/router/src/core/subscription/billing_processor_handler.rs` to use the new domain models (`hyperswitch_domain_models::subscription::Subscription` and `hyperswitch_domain_models::invoice::Invoice`) instead of Diesel models. [[1]](diffhunk://#diff-adea1b33060886c62616ea56c46edb8f356525cbacea004da12e8819334cf27cL83-L95) [[2]](diffhunk://#diff-adea1b33060886c62616ea56c46edb8f356525cbacea004da12e8819334cf27cL201-R203) [[3]](diffhunk://#diff-adea1b33060886c62616ea56c46edb8f356525cbacea004da12e8819334cf27cL210-R213) [[4]](diffhunk://#diff-adea1b33060886c62616ea56c46edb8f356525cbacea004da12e8819334cf27cL320-R324) [[5]](diffhunk://#diff-adea1b33060886c62616ea56c46edb8f356525cbacea004da12e8819334cf27cL329-R334) [[6]](diffhunk://#diff-57a5a7402c9b28e022a6839808acf84b88808f76da82ce1c76a80660c8869ec6L168-R168) [[7]](diffhunk://#diff-bacee0b3d81189090ca0d5f72fc58118b681a7dd4aab772679f2dec2a585a073L13-R28) [[8]](diffhunk://#diff-bacee0b3d81189090ca0d5f72fc58118b681a7dd4aab772679f2dec2a585a073L47-R50)
* Updated the invoice creation logic in `invoice_handler.rs` to use the new domain model and retrieve the merchant key store for secure operations.

**Module Organization:**

* Registered the new `invoice` and `subscription` modules in `crates/hyperswitch_domain_models/src/lib.rs` for proper integration with the rest of the domain models. [[1]](diffhunk://#diff-7ea431033335b7e3d2454c104f750be19973ad4f061a8cdb5c50af53ebdff00bR18) [[2]](diffhunk://#diff-7ea431033335b7e3d2454c104f750be19973ad4f061a8cdb5c50af53ebdff00bR41)

These changes lay the groundwork for improved maintainability and extensibility in handling subscriptions and invoices in the codebase.


## How did you test it?

Create Billing Connector

```
curl --location 'http://localhost:8080/account/merchant_1759737987/connectors' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: ****' \
--data '{
    "connector_type": "billing_processor",
    "connector_name": "chargebee",
    "connector_account_details": {
        "auth_type": "HeaderKey",
        "api_key": "****"
    },
    "business_country": "US",
    "business_label": "default",
    "connector_webhook_details": {
        "merchant_secret": "hyperswitch",
        "additional_secret": "hyperswitch"
    },
    "metadata": {
        "site": "hyperswitch-juspay3-test"
    }
}'
```

```
{
    "connector_type": "billing_processor",
    "connector_name": "chargebee",
    "connector_label": "chargebee_US_default",
    "merchant_connector_id": "mca_nyehf3o9gtzGgeVFzsx0",
    "profile_id": "pro_Ba2HYJtnuKdJzv2SBizB",
    "connector_account_details": {
        "auth_type": "HeaderKey",
        "api_key": "te**********************************MK"
    },
    "payment_methods_enabled": null,
    "connector_webhook_details": {
        "merchant_secret": "hyperswitch",
        "additional_secret": "hyperswitch"
    },
    "metadata": {
        "site": "hyperswitch-juspay3-test"
    },
    "test_mode": null,
    "disabled": false,
    "frm_configs": null,
    "business_country": "US",
    "business_label": "default",
    "business_sub_label": null,
    "applepay_verified_domains": null,
    "pm_auth_config": null,
    "status": "active",
    "additional_merchant_data": null,
    "connector_wallets_details": null
}
```

Update profile to set the billing processor

```
curl --location 'http://localhost:8080/account/merchant_1759737987/business_profile/pro_Ba2HYJtnuKdJzv2SBizB' \
--header 'Content-Type: application/json' \
--header 'api-key: ****' \
--data '{
  "billing_processor_id": "mca_nyehf3o9gtzGgeVFzsx0"
}'

```
```
{
    "merchant_id": "merchant_1759737987",
    "profile_id": "pro_Ba2HYJtnuKdJzv2SBizB",
    "profile_name": "US_default",
    "return_url": "https://google.com/success",
    "enable_payment_response_hash": true,
    "payment_response_hash_key": "BPIswjNm5ivN92rBE9J4ShmB2dfLVLeQ8DRnzY7cR7qsaUlfNpBgvnOCBWRfhR5e",
    "redirect_to_merchant_with_http_post": false,
    "webhook_details": {
        "webhook_version": "1.0.1",
        "webhook_username": "ekart_retail",
        "webhook_password": "password_ekart@123",
        "webhook_url": "https://webhook.site",
        "payment_created_enabled": true,
        "payment_succeeded_enabled": true,
        "payment_failed_enabled": true,
        "payment_statuses_enabled": null,
        "refund_statuses_enabled": null,
        "payout_statuses_enabled": null
    },
    "metadata": null,
    "routing_algorithm": null,
    "intent_fulfillment_time": 900,
    "frm_routing_algorithm": null,
    "payout_routing_algorithm": null,
    "applepay_verified_domains": null,
    "session_expiry": 900,
    "payment_link_config": null,
    "authentication_connector_details": null,
    "use_billing_as_payment_method_billing": true,
    "extended_card_info_config": null,
    "collect_shipping_details_from_wallet_connector": false,
    "collect_billing_details_from_wallet_connector": false,
    "always_collect_shipping_details_from_wallet_connector": false,
    "always_collect_billing_details_from_wallet_connector": false,
    "is_connector_agnostic_mit_enabled": false,
    "payout_link_config": null,
    "outgoing_webhook_custom_http_headers": null,
    "tax_connector_id": null,
    "is_tax_connector_enabled": false,
    "is_network_tokenization_enabled": false,
    "is_auto_retries_enabled": false,
    "max_auto_retries_enabled": null,
    "always_request_extended_authorization": null,
    "is_click_to_pay_enabled": false,
    "authentication_product_ids": null,
    "card_testing_guard_config": {
        "card_ip_blocking_status": "disabled",
        "card_ip_blocking_threshold": 3,
        "guest_user_card_blocking_status": "disabled",
        "guest_user_card_blocking_threshold": 10,
        "customer_id_blocking_status": "disabled",
        "customer_id_blocking_threshold": 5,
        "card_testing_guard_expiry": 3600
    },
    "is_clear_pan_retries_enabled": false,
    "force_3ds_challenge": false,
    "is_debit_routing_enabled": false,
    "merchant_business_country": null,
    "is_pre_network_tokenization_enabled": false,
    "acquirer_configs": null,
    "is_iframe_redirection_enabled": null,
    "merchant_category_code": null,
    "merchant_country_code": null,
    "dispute_polling_interval": null,
    "is_manual_retry_enabled": null,
    "always_enable_overcapture": null,
    "is_external_vault_enabled": "skip",
    "external_vault_connector_details": null,
    "billing_processor_id": "mca_nyehf3o9gtzGgeVFzsx0"
}

```
Create Customer

```
curl --location 'http://localhost:8080/customers' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: ****' \
--data-raw '{
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "First customer",
    "address": {
        "line1": "1467",
        "line2": "Harrison Street",
        "line3": "Harrison Street",
        "city": "San Fransico",
        "state": "California",
        "zip": "94122",
        "country": "US",
        "first_name": "joseph",
        "last_name": "Doe"
    },
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'

```
```
{
    "customer_id": "cus_EkIUbktzRFCHooI9Kylq",
    "name": "John Doe",
    "email": "guest@example.com",
    "phone": "999999999",
    "phone_country_code": "+65",
    "description": "First customer",
    "address": {
        "city": "San Fransico",
        "country": "US",
        "line1": "1467",
        "line2": "Harrison Street",
        "line3": "Harrison Street",
        "zip": "94122",
        "state": "California",
        "first_name": "joseph",
        "last_name": "Doe",
        "origin_zip": null
    },
    "created_at": "2025-10-06T08:08:37.562Z",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    },
    "default_payment_method_id": null,
    "tax_registration_id": null
}
```


Create Subscription

```
curl --location 'http://localhost:8080/subscriptions/create' \
--header 'Content-Type: application/json' \
--header 'X-Profile-Id: pro_Ba2HYJtnuKdJzv2SBizB' \
--header 'api-key: ****' \
--data '{
    "customer_id": "cus_EkIUbktzRFCHooI9Kylq",
    "amount": 14100,
    "currency": "USD",
    "payment_details": {
        "authentication_type": "no_three_ds",
        "setup_future_usage": "off_session",
        "capture_method": "automatic",
        "return_url": "https://google.com"
    }
}'
```

```
{
    "id": "sub_Q1ttH0p1b50OtnDgZfrz",
    "merchant_reference_id": null,
    "status": "created",
    "plan_id": null,
    "profile_id": "pro_Ba2HYJtnuKdJzv2SBizB",
    "client_secret": "sub_Q1ttH0p1b50OtnDgZfrz_secret_jXFTn0oMPowj2RqRUcWX",
    "merchant_id": "merchant_1759737987",
    "coupon_code": null,
    "customer_id": "cus_EkIUbktzRFCHooI9Kylq"
}
```

Confirm Subscription

```
curl --location 'http://localhost:8080/subscriptions/sub_Q1ttH0p1b50OtnDgZfrz/confirm' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-Profile-Id: pro_Ba2HYJtnuKdJzv2SBizB' \
--header 'api-key: ****' \
--data '{
    "item_price_id": "cbdemo_enterprise-suite-monthly",
    "customer_id": "cus_EkIUbktzRFCHooI9Kylq",
    "description": "Hello this is description",
    "shipping": {
        "address": {
            "state": "zsaasdas",
            "city": "Banglore",
            "country": "US",
            "line1": "sdsdfsdf",
            "line2": "hsgdbhd",
            "line3": "alsksoe",
            "zip": "571201",
            "first_name": "joseph",
            "last_name": "doe"
        },
        "phone": {
            "number": "123456789",
            "country_code": "+1"
        }
    },
    "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": "123456789",
            "country_code": "+1"
        }
    },
    "payment_details": {
        "payment_method": "card",
        "payment_method_type": "credit",
        "payment_method_data": {
            "card": {
                "card_number": "4111111111111111",
                "card_exp_month": "03",
                "card_exp_year": "2030",
                "card_holder_name": "CLBRW dffdg",
                "card_cvc": "737"
            }
        },
        "customer_acceptance": {
            "acceptance_type": "online",
            "accepted_at": "1963-05-03T04:07:52.723Z",
            "online": {
                "ip_address": "127.0.0.1",
                "user_agent": "amet irure esse"
            }
        }
    }
}'
```

```
{
    "id": "sub_Q1ttH0p1b50OtnDgZfrz",
    "merchant_reference_id": null,
    "status": "active",
    "plan_id": null,
    "price_id": null,
    "coupon": null,
    "profile_id": "pro_Ba2HYJtnuKdJzv2SBizB",
    "payment": {
        "payment_id": "pay_uadWety0AlrsIpLa2Dt6",
        "status": "succeeded",
        "amount": 14100,
        "currency": "USD",
        "connector": "stripe",
        "payment_method_id": "pm_HBp7wdIyHsvnta708kxF",
        "payment_experience": null,
        "error_code": null,
        "error_message": null,
        "payment_method_type": "credit"
    },
    "customer_id": "cus_EkIUbktzRFCHooI9Kylq",
    "invoice": {
        "id": "invoice_UwA8TmKbXgxE1a7s6Nux",
        "subscription_id": "sub_Q1ttH0p1b50OtnDgZfrz",
        "merchant_id": "merchant_1759737987",
        "profile_id": "pro_Ba2HYJtnuKdJzv2SBizB",
        "merchant_connector_id": "mca_nyehf3o9gtzGgeVFzsx0",
        "payment_intent_id": "pay_uadWety0AlrsIpLa2Dt6",
        "payment_method_id": "pm_HBp7wdIyHsvnta708kxF",
        "customer_id": "cus_EkIUbktzRFCHooI9Kylq",
        "amount": 14100,
        "currency": "USD",
        "status": "payment_pending"
    },
    "billing_processor_subscription_id": "sub_Q1ttH0p1b50OtnDgZfrz"
}
```

Screenshots

<img width="617" height="229" alt="Screenshot 2025-10-06 at 1 44 10 PM" src="https://github.com/user-attachments/assets/f11a1fe2-d7e9-4d2b-99db-c68f35212b2b" />

<img width="436" height="252" alt="Screenshot 2025-10-06 at 1 44 18 PM" src="https://github.com/user-attachments/assets/b66abdad-d216-4a12-84f0-653a1654ea7a" />


