Skip to content

Fix KiloClaw early bird checkout to link payments to existing Stripe customer#773

Merged
pandemicsyn merged 10 commits intomainfrom
session/agent_1aceb2ba-6240-4b30-8f20-76f4267cadf7
Mar 3, 2026
Merged

Fix KiloClaw early bird checkout to link payments to existing Stripe customer#773
pandemicsyn merged 10 commits intomainfrom
session/agent_1aceb2ba-6240-4b30-8f20-76f4267cadf7

Conversation

@kilo-code-bot
Copy link
Contributor

@kilo-code-bot kilo-code-bot bot commented Mar 3, 2026

Summary

  • Replace the static Stripe Payment Link (buy.stripe.com/...) with a server-side Stripe Checkout Session that passes the user's existing stripe_customer_id, preventing duplicate/guest Stripe accounts
  • Add createEarlybirdCheckoutSession tRPC mutation to the kiloclawRouter, following the same patterns used by Kilo Pass and other checkout flows
  • Update the early bird page to call the new mutation instead of opening an external Stripe link
  • Add kiloclaw_earlybird_purchases table to record completed earlybird payments, enabling duplicate purchase prevention and traceability
  • Add getEarlybirdStatus tRPC query to check if the current user has already purchased
  • Hide the earlybird banner and block re-purchase for users who have already completed checkout
  • Update GDPR soft-delete to clean up earlybird purchase records

Problem

The early bird checkout used a static Stripe Payment Link with only prefilled_email, which is cosmetic and does not link the payment to the user's existing Stripe customer record. This caused:

  • Payments appearing under a guest/duplicate Stripe customer
  • The Stripe link from the KiloCode admin panel not working
  • Users ending up with 2 Stripe accounts
    Additionally, there was no server-side record of earlybird purchases, no duplicate purchase prevention, and no webhook fulfillment handler.

Changes

packages/db/src/schema.ts
Added kiloclaw_earlybird_purchases table with:

  • user_id (unique) — enforces one purchase per user at the DB level
  • stripe_charge_id (unique) — provides webhook idempotency
  • amount_cents and created_at
    packages/db/src/migrations/0039_mushy_darkhawk.sql
    Migration for the new table.
    src/routers/kiloclaw-router.ts
  • Added createEarlybirdCheckoutSession mutation that:
    • Checks for an existing completed purchase before creating a session
    • Validates the user has a stripe_customer_id
    • Creates a Stripe Checkout Session with customer: stripeCustomerId
    • Sets metadata on both payment_intent_data and the session (matches patterns used by all other checkout flows)
  • Added getEarlybirdStatus query returning { purchased: boolean }
    src/lib/stripe.ts
  • Replaced the no-op skip in charge.succeeded with recordKiloclawEarlybirdPurchase, which inserts into kiloclaw_earlybird_purchases with onConflictDoNothing for idempotency
  • Uses .returning() to distinguish "recorded" vs "duplicate skipped" in logs
  • Errors propagate (no try/catch) so Stripe retries on transient DB failures
    src/app/(app)/claw/earlybird/page.tsx
  • Replaced static buy.stripe.com link with tRPC mutation call
  • Queries getEarlybirdStatus and shows "already purchased" message if applicable
  • Button disabled while status query is loading (prevents premature clicks)
    src/app/(app)/claw/components/ClawDashboard.tsx
  • EarlybirdBanner only renders after getEarlybirdStatus resolves and confirms no purchase exists (prevents flash during loading)
    src/lib/user.ts / src/lib/user.test.ts
  • Added kiloclaw_earlybird_purchases to GDPR soft-delete flow
  • Added corresponding test
    .env.test
  • Added STRIPE_KILOCLAW_EARLYBIRD_PRICE_ID and STRIPE_KILOCLAW_EARLYBIRD_COUPON_ID test env vars

Setup required

The following environment variables must be set in Vercel/production:

  • STRIPE_KILOCLAW_EARLYBIRD_PRICE_ID — Stripe Price ID for the early bird product
  • STRIPE_KILOCLAW_EARLYBIRD_COUPON_ID (optional) — if set, applies a coupon; otherwise falls back to allow_promotion_codes: true

Built for Brendan by Kilo for Slack and FlorianBot

@kilo-code-bot
Copy link
Contributor Author

kilo-code-bot bot commented Mar 3, 2026

Code Review Summary

Status: No New Issues Found | Recommendation: Address existing comments before merge

Overview

This PR adds a KiloClaw Early Bird checkout flow with server-side Stripe Checkout Session creation, webhook-based purchase recording, and proper GDPR compliance. The implementation is well-structured.

Positive observations:

  • ✅ DB schema uses UNIQUE(user_id) and UNIQUE(stripe_charge_id) for idempotency
  • onConflictDoNothing in webhook handler prevents duplicate purchase records
  • ✅ GDPR soft-delete updated with corresponding test
  • ✅ Server-side duplicate check before creating checkout session
  • payment_intent_data.metadata correctly propagates metadata to the PaymentIntent for webhook matching
  • ✅ Banner correctly hidden during loading state (no flash)
  • ✅ Button disabled while earlybird status is loading
  • ✅ Env var validation at mutation time with proper error response

Previously flagged issues (from existing comments) still worth addressing:

Severity File Issue
WARNING src/routers/kiloclaw-router.ts Static idempotency key can prevent checkout retry
SUGGESTION src/lib/stripe.ts Log message is misleading after onConflictDoNothing — "Duplicate" implies a problem but it's expected idempotent behavior
SUGGESTION src/routers/kiloclaw-router.ts Env vars should be validated at server startup time rather than at request time
Other Observations (not in diff)
File Issue
src/lib/stripe.ts No test coverage for recordKiloclawEarlybirdPurchase — the GDPR deletion test exists but there's no integration test verifying the webhook handler correctly records a purchase or handles duplicates
src/routers/kiloclaw-router.ts No test coverage for createEarlybirdCheckoutSession or getEarlybirdStatus tRPC procedures
Files Reviewed (9 files)
  • .env.test - 0 issues
  • packages/db/src/schema.ts - 0 issues
  • packages/db/src/migrations/0040_chief_vampiro.sql - 0 issues (generated)
  • packages/db/src/migrations/meta/_journal.json - 0 issues (generated)
  • src/app/(app)/claw/components/ClawDashboard.tsx - 0 issues
  • src/app/(app)/claw/earlybird/page.tsx - 0 issues
  • src/lib/config.server.ts - 0 issues
  • src/lib/stripe.ts - 0 issues (new)
  • src/lib/user.ts - 0 issues
  • src/lib/user.test.ts - 0 issues
  • src/routers/kiloclaw-router.ts - 0 issues (new)

Fix these issues in Kilo Cloud

Comment on lines +404 to +412
const priceId = getEnvVariable('STRIPE_KILOCLAW_EARLYBIRD_PRICE_ID');
if (!priceId) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Early bird pricing is not configured.',
});
}

const couponId = getEnvVariable('STRIPE_KILOCLAW_EARLYBIRD_COUPON_ID');
Copy link
Contributor

Choose a reason for hiding this comment

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

These env vars should be validated at server startup time - can put them in src/lib/config.server.ts

kilo-code-bot bot and others added 10 commits March 3, 2026 14:41
…ession

Replace the static Stripe Payment Link with a server-side Stripe
Checkout Session that passes the user's existing stripe_customer_id.
This prevents duplicate/guest Stripe customer accounts from being
created during early bird purchases.
…lfillment

- Add kiloclaw_earlybird_purchases table to record earlybird payments
- Record purchases in charge.succeeded webhook (replaces no-op skip)
- Add duplicate purchase check and Stripe idempotency key to mutation
- Add payment_intent_data.metadata for reliable webhook traceability
- Add getEarlybirdStatus query; hide banner/block checkout after purchase
- Update GDPR soft-delete to clean up earlybird purchases
- Let webhook DB errors propagate so Stripe retries on failure
@pandemicsyn pandemicsyn force-pushed the session/agent_1aceb2ba-6240-4b30-8f20-76f4267cadf7 branch from 85134eb to 5145a0f Compare March 3, 2026 20:48
@pandemicsyn pandemicsyn enabled auto-merge March 3, 2026 20:50
@pandemicsyn pandemicsyn merged commit 6899b98 into main Mar 3, 2026
13 checks passed
@pandemicsyn pandemicsyn deleted the session/agent_1aceb2ba-6240-4b30-8f20-76f4267cadf7 branch March 3, 2026 20:53
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.

3 participants