diff --git a/backend/services/billing/index.ts b/backend/services/billing/index.ts index 92f0f988..ce9183e8 100644 --- a/backend/services/billing/index.ts +++ b/backend/services/billing/index.ts @@ -131,3 +131,18 @@ export { PricingStrategyFactory, PlanType } from './strategyFactory'; export { BillingEngine, BillingEngineConfig } from './billingEngine'; export { PricingAnalyticsService, RevenueMetrics } from './billingAnalytics'; +// Plan Templates and Dynamic Pricing Tiers +export { + PlanTemplateService, + validateTiers, + validateTemplateDraft, + quoteTemplate, + resolvePlan +} from './planTemplateService'; +export type { + PricingTier, + PlanTemplate, + TemplateOverrides, + ResolvedPlan, + TemplateAnalytics +} from './planTemplateService'; diff --git a/contracts/subscription/src/gas_storage.rs b/contracts/subscription/src/gas_storage.rs index 8c7fca5c..9f99bf61 100644 --- a/contracts/subscription/src/gas_storage.rs +++ b/contracts/subscription/src/gas_storage.rs @@ -6,7 +6,7 @@ use crate::gas_profiler::GasProfile; #[derive(Clone)] pub enum GasStorageKey { /// Function gas profile: StorageKey::GasProfile(function_name) - GasProfile(SorobanString), + GasProfile(String), /// Daily gas usage: StorageKey::DailyGasUsage(timestamp) DailyGasUsage(u64), /// Weekly gas usage: StorageKey::WeeklyGasUsage(timestamp) @@ -18,9 +18,9 @@ pub enum GasStorageKey { /// Total number of contract calls TotalCallCount, /// Gas alert count by type - AlertCount(SorobanString), + AlertCount(String), /// Last recorded gas usage for a function - LastGasUsage(SorobanString), + LastGasUsage(String), } /// Gas metrics storage handler @@ -38,7 +38,7 @@ impl GasMetricsStorage { pub fn get_profile( env: &Env, storage: &Address, - function_name: &SorobanString, + function_name: &String, ) -> Option { // Retrieve and deserialize profile None @@ -111,26 +111,26 @@ impl GasMetricsStorage { /// Record gas alert pub fn record_alert(env: &Env, storage: &Address, alert_type: &str) { - let alert_key = SorobanString::from_str(env, alert_type); + let alert_key = String::from_str(env, alert_type); // Increment alert count } /// Get gas alert count by type pub fn get_alert_count(env: &Env, storage: &Address, alert_type: &str) -> u64 { - let alert_key = SorobanString::from_str(env, alert_type); + let alert_key = String::from_str(env, alert_type); // Retrieve alert count 0 } /// Update last recorded gas usage for a function pub fn update_last_usage(env: &Env, storage: &Address, function_name: &str, gas_used: u64) { - let fname = SorobanString::from_str(env, function_name); + let fname = String::from_str(env, function_name); // Update last usage } /// Get last recorded gas usage pub fn get_last_usage(env: &Env, storage: &Address, function_name: &str) -> Option { - let fname = SorobanString::from_str(env, function_name); + let fname = String::from_str(env, function_name); // Retrieve last usage None } @@ -151,7 +151,7 @@ impl GasMetricsStorage { } /// Helper function to format gas profile storage key -fn format_gas_profile_key(env: &Env, function_name: &SorobanString) -> SorobanString { +fn format_gas_profile_key(env: &Env, function_name: &String) -> String { // Format: "gas_profile_{function_name}" function_name.clone() } diff --git a/contracts/subscription/src/lib.rs b/contracts/subscription/src/lib.rs index 7a15210b..cd41e44f 100644 --- a/contracts/subscription/src/lib.rs +++ b/contracts/subscription/src/lib.rs @@ -5,6 +5,7 @@ mod gas_storage; mod quota; mod revenue; mod usage; +mod plan_templates; use soroban_sdk::{token, Address, Bytes, BytesN, Env, IntoVal, String, TryFromVal, Val, Vec}; use subtrackr_types::{ ChargeCommitment, Interval, Invoice, MevAlert, MevProtectionConfig, Plan, StorageKey, @@ -1405,4 +1406,143 @@ impl SubTrackrSubscription { .expect("Subscription not found"); usage::check_quota(&env, &storage, subscription_id, sub.plan_id, metric) } + + // ── Plan Templates ── + + pub fn create_template( + env: Env, + proxy: Address, + storage: Address, + owner: Address, + name: String, + description: String, + base_price: i128, + token: Address, + interval: Interval, + tiers: Vec, + features: Vec, + ) -> u64 { + proxy.require_auth(); + owner.require_auth(); + plan_templates::create_template( + &env, + &storage, + &owner, + name, + description, + base_price, + token, + interval, + tiers, + features, + ) + } + + pub fn publish_template_version( + env: Env, + proxy: Address, + storage: Address, + caller: Address, + template_id: u64, + name: String, + description: String, + base_price: i128, + tiers: Vec, + features: Vec, + ) -> u64 { + proxy.require_auth(); + caller.require_auth(); + plan_templates::publish_version( + &env, + &storage, + &caller, + template_id, + name, + description, + base_price, + tiers, + features, + ) + } + + pub fn set_template_shared( + env: Env, + proxy: Address, + storage: Address, + caller: Address, + template_id: u64, + shared: bool, + ) { + proxy.require_auth(); + caller.require_auth(); + plan_templates::set_shared(&env, &storage, &caller, template_id, shared) + } + + pub fn instantiate_template( + env: Env, + proxy: Address, + storage: Address, + caller: Address, + template_id: u64, + overrides: plan_templates::TemplateOverrides, + ) -> plan_templates::ResolvedPlan { + proxy.require_auth(); + caller.require_auth(); + plan_templates::instantiate(&env, &storage, &caller, template_id, overrides) + } + + pub fn get_template( + env: Env, + proxy: Address, + storage: Address, + template_id: u64, + ) -> Option { + proxy.require_auth(); + plan_templates::get_template(&env, &storage, template_id) + } + + pub fn get_owner_templates( + env: Env, + proxy: Address, + storage: Address, + owner: Address, + ) -> Vec { + proxy.require_auth(); + plan_templates::get_owner_templates(&env, &storage, &owner) + } + + pub fn get_shared_templates(env: Env, proxy: Address, storage: Address) -> Vec { + proxy.require_auth(); + plan_templates::get_shared_templates(&env, &storage) + } + + pub fn get_template_versions( + env: Env, + proxy: Address, + storage: Address, + root_id: u64, + ) -> Vec { + proxy.require_auth(); + plan_templates::get_template_versions(&env, &storage, root_id) + } + + pub fn get_latest_template_version( + env: Env, + proxy: Address, + storage: Address, + root_id: u64, + ) -> Option { + proxy.require_auth(); + plan_templates::get_latest_version(&env, &storage, root_id) + } + + pub fn get_template_analytics( + env: Env, + proxy: Address, + storage: Address, + template_id: u64, + ) -> plan_templates::TemplateAnalytics { + proxy.require_auth(); + plan_templates::get_analytics(&env, &storage, template_id) + } } diff --git a/contracts/subscription/src/quota.rs b/contracts/subscription/src/quota.rs index 76709bd5..fef4b520 100644 --- a/contracts/subscription/src/quota.rs +++ b/contracts/subscription/src/quota.rs @@ -1,6 +1,6 @@ use crate::{storage_persistent_get, storage_persistent_set}; use soroban_sdk::{Address, Env, Vec}; -use subtrackr_types::{Quota, StorageKeyExt}; +use subtrackr_types::Quota; pub fn set_plan_quotas(env: &Env, storage: &Address, plan_id: u64, quotas: Vec) { storage_persistent_set(env, storage, StorageKeyExt::PlanQuotas(plan_id), quotas); diff --git a/contracts/subscription/src/revenue.rs b/contracts/subscription/src/revenue.rs index ce7a8057..982b2a6e 100644 --- a/contracts/subscription/src/revenue.rs +++ b/contracts/subscription/src/revenue.rs @@ -8,7 +8,7 @@ /// All storage is delegated to the shared storage contract via the /// `storage_persistent_*` helpers defined in the parent module. use soroban_sdk::{contracttype, Address, Env, Vec}; -use subtrackr_types::StorageKeyExt; +use subtrackr_types::StorageKey; use crate::{storage_persistent_get, storage_persistent_set}; diff --git a/contracts/types/src/lib.rs b/contracts/types/src/lib.rs index a6c8233d..8f5c2ecd 100644 --- a/contracts/types/src/lib.rs +++ b/contracts/types/src/lib.rs @@ -407,4 +407,18 @@ pub enum StorageKey { ChargeCommitment(u64), MevAlertCount, MevAlert(u64), + + // ── Plan Templates ── + PlanTemplate(TemplateKey), +} + +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub enum TemplateKey { + Template(u64), + ByOwner(Address), + Shared, + Versions(u64), + Analytics(u64), + Count, }