Skip to content

feat(coding-plans): add coding plan subscriptions backend#1887

Open
jeanduplessis wants to merge 5 commits intomainfrom
coding-plans
Open

feat(coding-plans): add coding plan subscriptions backend#1887
jeanduplessis wants to merge 5 commits intomainfrom
coding-plans

Conversation

@jeanduplessis
Copy link
Copy Markdown
Contributor

Summary

  • Add the Coding Plans backend, including the requirements/spec docs, Drizzle schema + migration for subscriptions and pre-purchased key inventory, pricing helpers, and the tRPC endpoints for catalog/list/subscribe/cancel plus admin inventory management.
  • Add the billing lifecycle cron and secured /api/cron/coding-plans-billing route to renew subscriptions from credits, trigger auto-top-up once per period, cancel subscriptions on insufficient balance, and remove BYOK keys when access ends.
  • Extend GDPR soft-delete handling for coding plans and fix same-month reactivation so a canceled subscription can be purchased again without reusing the previous activation idempotency key.

Verification

  • pnpm test -- src/lib/coding-plans/index.test.ts src/lib/user.test.ts
  • pnpm lint
  • pnpm typecheck

Visual Changes

N/A

Reviewer Notes

  • The main behavioral risk area is src/lib/coding-plans/index.ts: initial activation/reactivation and active-plan extension intentionally use different credit transaction idempotency keys.
  • packages/db/src/migrations/0071_majestic_pestilence.sql and the snapshot are generated migration artifacts for the new coding plan tables and indexes.
  • This branch is backend-only; there are no UI changes in scope.

jeanduplessis and others added 5 commits April 2, 2026 06:11
… and user-owns-key principle

Audit identified 17 findings across 12 categories. Adds Definitions
section, clarifies subscription model (recurring, credit-based,
pre-purchased keys), adds boundary/failure/lifecycle rules, and
ensures the spec reflects that assigned API keys belong to the user.
Add subscription lifecycle, pre-purchased key inventory, atomic credit
deduction, billing cron, and GDPR soft-delete support for coding plans.

New tables: coding_plan_subscriptions, coding_plan_key_inventory.
New tRPC router with user (catalog, subscribe, cancel) and admin
(uploadKeys, keyInventory, cancelSubscription) endpoints.
Hourly billing cron handles renewals and cancel-at-period-end.
Pricing is env-configurable per provider via CODING_PLAN_PRICE_* vars.
}

const balance = user.total_microdollars_acquired - user.microdollars_used;
if (costMicrodollars > 0 && balance < costMicrodollars) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Balance check happens outside the transaction

This verifies the user balance before any row is locked, then blindly increments microdollars_used later in the transaction. Two concurrent purchases can both pass this pre-check and both deduct, which lets the account go negative and violates the spec's requirement that credit verification and activation be atomic.

}

let byokId: string;
if (existingByok) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Re-subscribe can overwrite a user-managed BYOK key

existingSub only proves there is a canceled subscription row; it does not prove that existingByok is the old coding-plan key. If the user canceled, added their own BYOK key for the same provider, and then re-subscribed, this branch updates that manual key in place and silently replaces it with the inventory key.


const balance = row.total_microdollars_acquired - row.microdollars_used;

if (balance >= costMicrodollars) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Renewal balance is computed from a stale snapshot

sweepRenewals() joins kilocode_users once up front, so every due subscription for the same user sees the same starting balance. If two coding plans renew in the same sweep and the user only has enough credits for one, both rows still enter this branch and both deductions succeed, which can overdraw the account.

@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot bot commented Apr 2, 2026

Code Review Summary

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
src/lib/coding-plans/index.ts 75 Balance validation runs before the transaction, so concurrent purchases can both pass and overdraw the user.
src/lib/coding-plans/index.ts 174 Re-subscribing can overwrite a user-managed BYOK key because the existing row is updated without verifying ownership.
src/lib/coding-plans/billing-lifecycle-cron.ts 215 Renewal processing uses a stale user balance snapshot, so multiple plans due in one sweep can all renew and overdraw the user.

Fix these issues in Kilo Cloud

Other Observations (not in diff)

None.

Files Reviewed (15 files)
  • .specs/coding-plans.md - 0 issues
  • PLAN.md - 0 issues
  • packages/db/src/migrations/0071_majestic_pestilence.sql - 0 issues
  • packages/db/src/schema-types.ts - 0 issues
  • packages/db/src/schema.ts - 0 issues
  • src/app/api/cron/coding-plans-billing/route.ts - 0 issues
  • src/lib/coding-plans/billing-lifecycle-cron.ts - 1 issue
  • src/lib/coding-plans/index.test.ts - 0 issues
  • src/lib/coding-plans/index.ts - 2 issues
  • src/lib/coding-plans/pricing.ts - 0 issues
  • src/lib/user.test.ts - 0 issues
  • src/lib/user.ts - 0 issues
  • src/routers/coding-plans-router.ts - 0 issues
  • src/routers/root-router.ts - 0 issues
  • vercel.json - 0 issues

Reviewed by gpt-5.4-20260305 · 1,313,263 tokens

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant