94b8c357b4
- add workspace-scoped billing storage, usage tracking, and add-on catalog - enforce plan entitlements for search and deep research routes - expand pricing and account UI around billing state, usage, and upgrades
133 lines
6.6 KiB
SQL
133 lines
6.6 KiB
SQL
create table if not exists public.workspace_billing_accounts (
|
|
id uuid primary key default gen_random_uuid(),
|
|
workspace_id uuid not null references public.workspaces (id) on delete cascade,
|
|
plan_code text,
|
|
billing_interval text check (billing_interval in ('monthly', 'annual', 'custom')),
|
|
status text not null check (status in ('not_configured', 'inactive', 'active', 'past_due', 'canceled')),
|
|
current_period_starts_at timestamptz,
|
|
current_period_ends_at timestamptz,
|
|
cancel_at_period_end boolean not null default false,
|
|
canceled_at timestamptz,
|
|
trial_ends_at timestamptz,
|
|
external_customer_ref text,
|
|
external_subscription_ref text,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
constraint workspace_billing_accounts_workspace_id_key unique (workspace_id),
|
|
constraint workspace_billing_accounts_period_bounds_check check (
|
|
(current_period_starts_at is null and current_period_ends_at is null)
|
|
or (current_period_starts_at is not null and current_period_ends_at is not null)
|
|
)
|
|
);
|
|
|
|
create table if not exists public.workspace_usage_periods (
|
|
id uuid primary key default gen_random_uuid(),
|
|
workspace_id uuid not null references public.workspaces (id) on delete cascade,
|
|
billing_account_id uuid not null references public.workspace_billing_accounts (id) on delete cascade,
|
|
period_starts_at timestamptz not null,
|
|
period_ends_at timestamptz not null,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
constraint workspace_usage_periods_workspace_period_key unique (workspace_id, period_starts_at, period_ends_at),
|
|
constraint workspace_usage_periods_bounds_check check (period_starts_at < period_ends_at)
|
|
);
|
|
|
|
create table if not exists public.workspace_usage_counters (
|
|
id uuid primary key default gen_random_uuid(),
|
|
usage_period_id uuid not null references public.workspace_usage_periods (id) on delete cascade,
|
|
workspace_id uuid not null references public.workspaces (id) on delete cascade,
|
|
resource text not null check (resource in ('research_credits', 'exports', 'enrichments', 'api_requests')),
|
|
consumed_quantity integer not null default 0 check (consumed_quantity >= 0),
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
constraint workspace_usage_counters_period_resource_key unique (usage_period_id, resource)
|
|
);
|
|
|
|
create table if not exists public.workspace_addon_purchases (
|
|
id uuid primary key default gen_random_uuid(),
|
|
workspace_id uuid not null references public.workspaces (id) on delete cascade,
|
|
addon_code text not null check (addon_code in ('export_pack_10k', 'export_pack_50k', 'enrichment_pack_1k', 'ai_assistant_monthly', 'white_label_monthly')),
|
|
resource text not null check (resource in ('research_credits', 'exports', 'enrichments', 'api_requests')),
|
|
purchased_quantity integer not null check (purchased_quantity >= 0),
|
|
remaining_quantity integer not null check (remaining_quantity >= 0),
|
|
purchased_at timestamptz not null default now(),
|
|
expires_at timestamptz,
|
|
external_purchase_ref text,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
create table if not exists public.workspace_addon_balances (
|
|
id uuid primary key default gen_random_uuid(),
|
|
workspace_id uuid not null references public.workspaces (id) on delete cascade,
|
|
addon_code text not null check (addon_code in ('export_pack_10k', 'export_pack_50k', 'enrichment_pack_1k', 'ai_assistant_monthly', 'white_label_monthly')),
|
|
resource text not null check (resource in ('research_credits', 'exports', 'enrichments', 'api_requests')),
|
|
remaining_quantity integer not null default 0 check (remaining_quantity >= 0),
|
|
expires_at timestamptz,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
constraint workspace_addon_balances_workspace_addon_resource_key unique (workspace_id, addon_code, resource)
|
|
);
|
|
|
|
create index if not exists workspace_billing_accounts_status_idx on public.workspace_billing_accounts (status);
|
|
create index if not exists workspace_billing_accounts_plan_code_idx on public.workspace_billing_accounts (plan_code);
|
|
create index if not exists workspace_usage_periods_workspace_id_idx on public.workspace_usage_periods (workspace_id);
|
|
create index if not exists workspace_usage_counters_workspace_id_idx on public.workspace_usage_counters (workspace_id);
|
|
create index if not exists workspace_addon_purchases_workspace_id_idx on public.workspace_addon_purchases (workspace_id);
|
|
create index if not exists workspace_addon_balances_workspace_id_idx on public.workspace_addon_balances (workspace_id);
|
|
|
|
drop trigger if exists set_workspace_billing_accounts_updated_at on public.workspace_billing_accounts;
|
|
create trigger set_workspace_billing_accounts_updated_at
|
|
before update on public.workspace_billing_accounts
|
|
for each row
|
|
execute function public.set_updated_at();
|
|
|
|
drop trigger if exists set_workspace_usage_periods_updated_at on public.workspace_usage_periods;
|
|
create trigger set_workspace_usage_periods_updated_at
|
|
before update on public.workspace_usage_periods
|
|
for each row
|
|
execute function public.set_updated_at();
|
|
|
|
drop trigger if exists set_workspace_usage_counters_updated_at on public.workspace_usage_counters;
|
|
create trigger set_workspace_usage_counters_updated_at
|
|
before update on public.workspace_usage_counters
|
|
for each row
|
|
execute function public.set_updated_at();
|
|
|
|
drop trigger if exists set_workspace_addon_purchases_updated_at on public.workspace_addon_purchases;
|
|
create trigger set_workspace_addon_purchases_updated_at
|
|
before update on public.workspace_addon_purchases
|
|
for each row
|
|
execute function public.set_updated_at();
|
|
|
|
drop trigger if exists set_workspace_addon_balances_updated_at on public.workspace_addon_balances;
|
|
create trigger set_workspace_addon_balances_updated_at
|
|
before update on public.workspace_addon_balances
|
|
for each row
|
|
execute function public.set_updated_at();
|
|
|
|
insert into public.workspace_billing_accounts (
|
|
workspace_id,
|
|
plan_code,
|
|
billing_interval,
|
|
status,
|
|
current_period_starts_at,
|
|
current_period_ends_at
|
|
)
|
|
select w.id, 'starter_monthly', 'monthly', 'active', now(), now() + interval '1 month'
|
|
from public.workspaces w
|
|
where not exists (
|
|
select 1
|
|
from public.workspace_billing_accounts billing
|
|
where billing.workspace_id = w.id
|
|
);
|
|
|
|
update public.workspace_billing_accounts
|
|
set
|
|
plan_code = 'starter_monthly',
|
|
billing_interval = 'monthly',
|
|
status = 'active',
|
|
current_period_starts_at = coalesce(current_period_starts_at, now()),
|
|
current_period_ends_at = coalesce(current_period_ends_at, now() + interval '1 month')
|
|
where plan_code is null and status = 'not_configured';
|