feat(kiloclaw): add subscription billing, trial lifecycle, and Stripe integration#1054
Open
jeanduplessis wants to merge 8 commits intomainfrom
Open
feat(kiloclaw): add subscription billing, trial lifecycle, and Stripe integration#1054jeanduplessis wants to merge 8 commits intomainfrom
jeanduplessis wants to merge 8 commits intomainfrom
Conversation
Contributor
Code Review SummaryStatus: 1 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)CRITICAL
Fix these issues in Kilo Cloud Other Observations (not in diff)Issues found in unchanged code that cannot receive inline comments: N/A Files Reviewed (9 files)
Reviewed by gpt-5.4-20260305 · 880,829 tokens |
192bce9 to
4a294d3
Compare
4e38b88 to
87adb5e
Compare
Contributor
Author
|
In case anyone is interested here's my local verification test plan: |
Contributor
Author
Manual Verification Report51 of 56 scenarios verified. 2 code bugs found and fixed. 0 failures remaining. A. Welcome Page & Trial
B. Stripe Checkout — Standard Plan
C. Stripe Checkout — Commit Plan
D. Delayed Billing (Prelaunch)
E. Plan Switching
F. Commit Renewal
G. Cancellation & Reactivation
H. Billing Lifecycle Cron — Trial Expiry
I. Billing Lifecycle Cron — Subscription Expiry
J. Billing Lifecycle Cron — Earlybird
K. Auto-Resume on Resubscription
L. Access Gate
M. UI Banner States
N. AccessLockedDialog Variants
Bugs Fixed
Both fixes pass Coverage
|
087ed80 to
b6c8745
Compare
… webhook handlers - Stripe integration for commit ($9/mo for 6 months) and standard ($25/mo) plans - Trial flow with 30-day free period and automatic suspension - Subscription lifecycle: create, cancel, reactivate, switch plans, renew commit - Billing lifecycle cron for trial expiry, payment suspension, and instance destruction - Webhook handlers for subscription.created/updated/deleted and invoice events - Promo code support via Stripe promotion codes - Billing UI: welcome page, subscription card, plan selection, cancel dialog - Access gating with BillingWrapper, lock dialog, and billing banner - GDPR soft-delete support for billing data - Idempotent webhook guards against stale/replayed events - Concurrent checkout session guard - Hardened error handling: cancellation rollback, reactivation rollback - Edge-case test coverage for webhook idempotency, concurrent checkouts, stale subscription guards, cancellation ordering, and reactivation rollback
a3513b1 to
d42f330
Compare
…ock dialog Expired earlybird users who never provisioned an instance were routed to WelcomePage instead of AccessLockedDialog. The welcome guard now also checks !billing.earlybird and !billing.trial?.expired so only truly new users see the onboarding flow.
…ionAccess Move earlybird lookup before the trial creation path so that: - Active earlybird users get access without an accidental trial row - Expired earlybird users cannot regain access by provisioning - New non-earlybird users still get auto-trial (unchanged)
…Eligible for returning users - Stale-webhook guard in handleKiloClawSubscriptionCreated now permits subscription.created when the existing row is canceled, so re-subscribing after cancellation correctly upserts the new Stripe subscription. - trialEligible now checks for both instance and subscription rows, preventing a dead-end free-trial CTA for users with a canceled subscription.
…haned schedules on rollback
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the full KiloClaw billing backend and UI, ready for production launch:
kiloclaw_subscriptionsandkiloclaw_email_logtables (single consolidated migration 0049). GDPR soft-delete coverage added tosoftDeleteUser— blocks active (not pending cancellation),past_due, andunpaidsubscriptions.durationAPI for exact 6-month calendar periods. Checkout sessions with plan metadata, promo code support (Standard only), Rewardful referral tracking, and configurable billing delay viaSTRIPE_KILOCLAW_BILLING_STARTenv var. Schedule creation is idempotent on webhook replay — skips ifstripe_schedule_idis already set.clawAccessProceduretRPC middleware enforces subscription/trial/earlybird access on all mutating KiloClaw operations. Three access checks (requireKiloClawAccess,ensureProvisionAccess,getBillingStatus.hasAccess) are kept consistent —past_duegrants access only until the billing lifecycle cron setssuspended_at.startTrialmutation for the welcome page flow. Billing lifecycle cron (hourly, Vercel) suspends expired trials/subscriptions, destroys instances past grace period, handles earlybird expiry.kiloclaw_email_logunique constraint. All email CTAs link to/clawwhereAccessLockedDialoghandles payment recovery via Stripe portal.instance === null). Destroyed instances route toClawDashboardwhereAccessLockedDialoghandles them. Three options: free trial (conditional on eligibility), Standard, and Commit plans.SubscriptionCardcorrectly distinguishes mandatory commit→standard auto-transitions from user-requested plan switches.BillingWrapperrenders billing UI unconditionally (no feature flag).getBillingStatus,startTrial,createSubscriptionCheckout,cancelSubscription,reactivateSubscription,renewCommit,switchPlan,cancelPlanSwitch,createBillingPortalSession.Clawprefix from billing components, removedascasts in favor ofsatisfiesand flow-sensitive typing, lazy-initialized Stripe price ID metadata to avoid build-time crashes.Verification
pnpm typecheck— passes across all workspace packagespnpm test— 167 suites, 2648 tests pass, 6 skippedkiloclaw-billing-router.test.ts— 13 tests:startTrialhappy/reject paths,getBillingStatustrial eligibility,createSubscriptionCheckoutpromo codes/Rewardful/trial_end,handleKiloClawSubscriptionUpdatedstatus mappinguser.test.ts— GDPR soft-delete: blocks active (not pending cancellation), allowscancel_at_period_end: true, blockspast_due/unpaidpnpm build— compiles successfullyVisual Changes
Welcome Page
Trial States
Earlybird States
Subscription States
Dialogs
Emails
Reviewer Notes
Post-Deployment Actions
Environment variables — set in Vercel before deploying:
STRIPE_KILOCLAW_COMMIT_PRICE_ID— Stripe Price ID for the 6-month commit plan (required)STRIPE_KILOCLAW_STANDARD_PRICE_ID— Stripe Price ID for the monthly standard plan (required)STRIPE_KILOCLAW_BILLING_START— ISO 8601 date; checkouts before this date get a delayedtrial_endso billing starts on launch day. Leave empty/unset for immediate billing.Database migration — single consolidated migration
0049creates bothkiloclaw_subscriptionsandkiloclaw_email_logtables. Runs automatically.Stripe Dashboard — create two Products/Prices for the commit and standard plans, and set the env vars above. Optionally create a promo coupon (100% off, repeating, 2 months) restricted to the Standard plan product.
Stripe webhook events — no changes needed. The existing webhook endpoint already handles all required event types (
customer.subscription.created/updated/deleted,subscription_schedule.updated,invoice.paid). This PR adds KiloClaw-specific branching within those existing handlers viametadata.type === 'kiloclaw'.Cron —
/api/cron/kiloclaw-billing-lifecycleis registered invercel.jsonrunning hourly. RequiresCRON_SECRET(likely already set).No feature flag — billing is always active. There is no rollout toggle.
Key design decisions
kiloclaw_subscriptionstable uses auser_id UNIQUEconstraint — one billing row per user.subscription.metadata.type === 'kiloclaw'to distinguish KiloClaw subscriptions from org/pass subscriptions.duration: { interval: 'month', interval_count: 6 }for exact calendar months.commit_ends_atis derived from the schedule's resolved phases, notcurrent_period_end(which may reflect a delayed-billing trial boundary).subscription.createdis idempotent — skips ifstripe_schedule_idis already set, preventing orphaned schedules on webhook replay.renewCommitschedule rebuild finds the currently active phase (notphases[0]), handling delayed-billing prelaunch phases correctly.SubscriptionCarddistinguishes mandatory commit→standard auto-transitions from user-requested plan switches — "Cancel Switch" only appears for genuine user requests.stop()/destroy()calls are wrapped in inner try/catch so DB state transitions proceed even on 404/409 API errors.kiloclaw_email_logINSERT ... ON CONFLICT DO NOTHING. Failed provider calls delete the log row so the next cron retries.past_duesubscriptions retain access only until the billing lifecycle cron setssuspended_at(14-day grace viapast_due_since, notupdated_at).