Skip to content

feat(claw): prompt users to purchase credits when selecting a paid model#1324

Open
RSO wants to merge 7 commits intomainfrom
purchase-credits-k
Open

feat(claw): prompt users to purchase credits when selecting a paid model#1324
RSO wants to merge 7 commits intomainfrom
purchase-credits-k

Conversation

@RSO
Copy link
Contributor

@RSO RSO commented Mar 20, 2026

Summary

  • When a user without credits selects a paid model during KiloClaw onboarding, an inline credit purchase nudge now appears below the model picker. It offers preset amounts ($10, $20, $50), a Stripe checkout CTA, and a "Use the free model instead" fallback.
  • After completing payment, the user is automatically redirected back to /claw with their model selection preserved via the existing payment-return-url cookie mechanism, a success toast is shown, and provisioning starts automatically.
  • Free model detection uses the shared isFreeModel() helper, so selecting any free model (not just Kilo Auto: Free) from the 500+ dropdown also hides the nudge.

Verification

  • pnpm typecheck — passes across all packages
  • pnpm format:check (oxfmt) — passes
  • pnpm lint — passes (oxlint + eslint)

Visual Changes

credit-purchase.mp4

Reviewer Notes

  • The Stripe cancel URL still redirects to /profile (hardcoded in getStripeTopUpCheckoutUrl). A follow-up could pass a custom cancel URL so users return to /claw on cancellation too.
  • The auto-provision effect uses a useRef guard (hasAutoProvisioned) to prevent double-firing in React strict mode or on re-renders.

RSO added 5 commits March 20, 2026 12:25
…del during onboarding

Users without credits can select paid models during KiloClaw onboarding,
but the bot won't work without a balance. This adds an inline credit
purchase nudge (amount selector + Stripe checkout CTA + free model
fallback) and preserves model selection across the checkout redirect via
the existing payment-return-url cookie.
Replace useTransition with a plain useState boolean that stays true
once set, since the form submission navigates away and never needs
to reset.
Show a success toast and immediately start provisioning when the user
returns from Stripe checkout, so they don't have to manually click
Get Started again.
Replace the hand-rolled Set of free model IDs with a direct call to the
existing isFreeModel() helper, which already covers all free model
detection cases (kilo free models, :free suffix, stealth models, etc.).
);
}

const needsCredits = !hasCredits && selectedModel !== '' && !isFreeModel(selectedModel);
Copy link
Contributor

Choose a reason for hiding this comment

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

WARNING: Trial-eligible users are blocked from paid-model onboarding

ensureProvisionAccess() already auto-creates a trial for first-time users, but this condition only checks hasCredits. A brand-new trialEligible user who picks Frontier/Balanced will now see the credit nudge instead of the Get Started CTA, so they can no longer start the 7-day trial from this flow.

Suggested change
const needsCredits = !hasCredits && selectedModel !== '' && !isFreeModel(selectedModel);
const needsCredits = !canStartTrial && !hasCredits && selectedModel !== '' && !isFreeModel(selectedModel);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jeanduplessis is this true? Am I missing something in the logic?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm thinking the bot is confused, but let me triple check this.

Copy link
Contributor

Choose a reason for hiding this comment

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

@RSO confirmed, this is a false positive:

The bot's review comment conflates two separate concerns:

  1. Trial = KiloClaw instance access (the kiloclaw_subscriptions row with status: 'trialing')
  2. Credits = payment for model inference (total_microdollars_acquired)

These are orthogonal. A trial-eligible user who picks a frontier model genuinely needs credits because every inference call goes through OpenRouter and costs money. An instance provisioned with a paid model but zero credits is useless.

The flow the current code produces is completely correct:

  1. Trial-eligible user picks Frontier → sees CreditsNudge → buys $10 via Stripe
  2. Stripe redirects back to /claw?model=...&payment=success
  3. hasCredits is now true → needsCredits is false → the auto-provision effect fires (lines 102–131)
  4. ensureProvisionAccess() runs server-side, sees no subscription row, auto-creates a trial
  5. User ends up with both a trial subscription and credits for inference

Buying credits doesn't bypass or prevent the trial — ensureProvisionAccess doesn't check credits at all, it only

@kilo-code-bot
Copy link
Contributor

kilo-code-bot bot commented Mar 20, 2026

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
src/app/(app)/claw/components/CreateInstanceCard.tsx 168 Trial-eligible users are blocked from paid-model onboarding because the credit gate ignores billingStatus.trialEligible.
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
src/app/payments/topup/success/page.tsx 79 The redirect effect does not wait for getPaymentReturnUrl() to finish, so the new /claw?model=...&payment=success cookie can be ignored and users may fall back to /payments/topup/thank-you.
Files Reviewed (4 files)
  • src/app/(app)/claw/components/AutoModelPicker.tsx - 0 issues
  • src/app/(app)/claw/components/CreditsNudge.tsx - 0 issues
  • src/app/payments/topup/route.ts - 0 issues
  • src/lib/stripe.ts - 0 issues

Reviewed by gpt-5.4-20260305 · 217,692 tokens

RSO added 2 commits March 20, 2026 12:56
Add an optional cancel-path query param to the /payments/topup route,
validated with isValidReturnUrl to prevent open redirects. The claw
CreditsNudge now passes cancel-path=/claw so users return to the
onboarding page instead of /profile when they cancel checkout.
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.

2 participants