From 04a52a965945d01513e2c74096cd21d6555eb543 Mon Sep 17 00:00:00 2001 From: Timmatt112 Date: Thu, 27 Aug 2026 04:01:55 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20expose=20subscription=20plan=20template?= =?UTF-8?q?s=20and=20tiered=20pricing\n\nAdd=20append-only=20template=20st?= =?UTF-8?q?orage=20keys=20and=20authenticated=20contract=20entrypoints=20f?= =?UTF-8?q?or=20reusable=20plans,=20versioning,=20sharing,=20analytics,=20?= =?UTF-8?q?and=20graduated=20pricing=20quotes.\n\nCloses=20#950\n\nGenerat?= =?UTF-8?q?ed=20with=20Codebuff=20=F0=9F=A4=96\nCo-Authored-By:=20Codebuff?= =?UTF-8?q?=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/subscription/src/lib.rs | 122 ++++++++++++++++++++++++++++++ contracts/types/src/lib.rs | 15 ++++ 2 files changed, 137 insertions(+) diff --git a/contracts/subscription/src/lib.rs b/contracts/subscription/src/lib.rs index 7a15210b..c60af7bc 100644 --- a/contracts/subscription/src/lib.rs +++ b/contracts/subscription/src/lib.rs @@ -11,6 +11,8 @@ use subtrackr_types::{ Subscription, SubscriptionStatus, TimeRange, }; +mod plan_templates; + /// Billing interval in seconds. const MAX_PAUSE_DURATION: u64 = 2_592_000; // 30 days const DEFAULT_COMMIT_REVEAL_THRESHOLD: i128 = i128::MAX; @@ -646,6 +648,126 @@ impl SubTrackrSubscription { ); } + // ── Plan Templates ── + + pub fn create_plan_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_plan_template_version( + env: Env, + proxy: Address, + storage: Address, + owner: Address, + template_id: u64, + name: String, + description: String, + base_price: i128, + tiers: Vec, + features: Vec, + ) -> u64 { + proxy.require_auth(); + owner.require_auth(); + plan_templates::publish_version( + &env, &storage, &owner, template_id, name, description, base_price, tiers, features, + ) + } + + pub fn set_plan_template_shared( + env: Env, + proxy: Address, + storage: Address, + owner: Address, + template_id: u64, + shared: bool, + ) { + proxy.require_auth(); + owner.require_auth(); + plan_templates::set_shared(&env, &storage, &owner, template_id, shared); + } + + pub fn instantiate_plan_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 quote_plan_template( + env: Env, + proxy: Address, + storage: Address, + template_id: u64, + units: u64, + ) -> i128 { + proxy.require_auth(); + let template = plan_templates::get_template(&env, &storage, template_id) + .expect("Template not found"); + plan_templates::quote_template(&template, units) + } + + pub fn get_plan_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_plan_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) + } + + pub fn record_plan_template_view( + env: Env, + proxy: Address, + storage: Address, + template_id: u64, + ) { + proxy.require_auth(); + plan_templates::record_view(&env, &storage, template_id); + } + + pub fn record_plan_template_subscription( + env: Env, + proxy: Address, + storage: Address, + template_id: u64, + ) { + proxy.require_auth(); + plan_templates::record_subscription(&env, &storage, template_id); + } + // ── Plan Management ── pub fn create_plan( diff --git a/contracts/types/src/lib.rs b/contracts/types/src/lib.rs index a6c8233d..d5ee3d08 100644 --- a/contracts/types/src/lib.rs +++ b/contracts/types/src/lib.rs @@ -204,6 +204,18 @@ pub struct UpgradeEvent { pub type SubscriptionId = u64; pub type MerchantId = Address; +/// Secondary keys used by the reusable plan-template library. +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub enum TemplateKey { + Template(u64), + ByOwner(Address), + Shared, + Versions(u64), + Analytics(u64), + Count, +} + #[contracttype] #[derive(Clone, Debug, PartialEq)] pub enum FraudAction { @@ -402,6 +414,9 @@ pub enum StorageKey { /// Usage record for a subscription and metric (sub_id, metric -> UsageRecord) SubscriptionUsage(u64, QuotaMetric), + // ── Plan templates (appended for storage compatibility) ── + PlanTemplate(TemplateKey), + // Added for MEV-resistant subscription charging MevProtectionConfig, ChargeCommitment(u64),