Repository navigation
feat(devpass): add weekly premium Reset Passes - #3093
Conversation
Reset Passes let DevPass subscribers instantly restore their weekly premium-model allowance instead of waiting for the rolling 7-day window. - Tier pricing at ~82-86% of the weekly cap each pass unlocks: $9 Lite / $29 Pro / $79 Max (DEV_PLAN_RESET_PASS_PRICES). - Included passes per cycle: 0 Lite / 1 Pro / 2 Max, non-rollover; consumed before purchased passes. Purchased passes survive plan changes and plan end. - One-time Stripe Checkout purchase + webhook fulfillment with payment-intent dedup, invoice email, and PostHog event. - Explicit redeem endpoint zeroes premium usage and clears the window; atomic counter guards prevent double-redeem races. - Dashboard: Reset Pass card styled as a stamped passport visa extension (stamp slots, redeem animation, MRZ footer), wired into the premium meter; billing history shows pass purchases. - Gateway premium-cap 402 now points at Reset Passes; pricing page matrix row + FAQ entry added. Claude-Session: https://claude.ai/code/session_01DiqhTuu8Gg6nUXFuaRVUH3
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughChangesReset Passes add tier-specific pricing and included allowances, persistent purchased inventory, Stripe-backed purchase and atomic redemption APIs, billing-cycle resets, dashboard interactions, gateway guidance, transaction classification, and documentation. DevPass Reset Passes
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Dashboard
participant DevPlansAPI
participant Stripe
participant Database
Dashboard->>DevPlansAPI: Request Reset Pass status
DevPlansAPI->>Database: Read inventory and premium usage
Database-->>DevPlansAPI: Return pass counts and allowance state
Dashboard->>DevPlansAPI: Purchase or redeem a Reset Pass
DevPlansAPI->>Stripe: Charge saved payment method
DevPlansAPI->>Database: Record purchase or atomically reset usage
DevPlansAPI-->>Dashboard: Return updated pass inventory
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Changelog entry with gpt-image-2 OG image - Knowledge base page learn/reset-passes with light/dark screenshots of the visa-extension card and billing history - Reset Passes section + cross-link in model-categories fair-use doc Claude-Session: https://claude.ai/code/session_01DiqhTuu8Gg6nUXFuaRVUH3
|
Images automagically compressed by Calibre's image-actions ✨ Compression reduced images by 72%, saving 1.8 MB.
|
|
Images automagically compressed by Calibre's image-actions ✨ Compression reduced images by 8.8%, saving 4.4 KB.
2 images did not require optimisation. |
|
Images automagically compressed by Calibre's image-actions ✨ Compression reduced images by 6.8%, saving 1.2 KB.
4 images did not require optimisation. |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.claude/skills/verify/SKILL.md:
- Around line 16-25: Update the service startup instructions in SKILL.md to make
the API, DevPass dashboard, and gateway commands run in separate terminal
sessions, explicitly labeling each session. Keep the existing commands and
configuration unchanged while removing the sequential single-shell execution
ambiguity.
In `@apps/api/src/routes/dev-plans.ts`:
- Around line 2887-2908: Update the redeemResetPass handler to independently
require a verified email after the existing authentication check and before
entitlement validation. Reuse the established email-verification guard or user
verification field and return the standard unauthorized/forbidden response when
the email is unverified, while preserving the existing personal-organization and
dev-plan checks.
- Around line 2813-2838: Update the checkout session creation in
handleResetPassCheckout to restrict accepted payment methods to cards,
preventing delayed payment methods from creating sessions that cannot be
fulfilled. Preserve the existing checkout metadata, URLs, pricing, and
fulfillment flow.
- Around line 2910-2975: Update the redeem flow around the organization update
to make the reset conditional on the originally observed premium allowance
state, not only pass inventory. Add compare-and-swap predicates for
personalOrg.devPlanPremiumCreditsUsed and personalOrg.devPlanPremiumWeekStart
alongside the existing pass guards, so only one concurrent request can reset the
same allowance; preserve the existing successful update and error behavior when
no row matches.
In `@packages/db/src/schema.ts`:
- Around line 262-265: Bind purchased Reset Pass inventory to the paid tier
instead of allowing cross-tier redemption: update
packages/db/src/schema.ts:262-265 to represent tier-specific inventory,
regenerate packages/db/migrations/1784223442_graceful_lady_bullseye.sql:1-1
accordingly, retain tier pricing in packages/shared/src/dev-plans.ts:45-49 only
with tier-bound entitlements, encode the entitlement tier in the checkout
contract in apps/api/src/routes/dev-plans.ts:2805-2837, and have the
apps/api/src/stripe.ts:1960-1984 flow validate the charged tier and amount
before incrementing only that tier’s inventory.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: e37259e4-db29-428d-aeba-8934230993e8
⛔ Files ignored due to path filters (5)
apps/docs/public/learn/reset-passes-billing-dark.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-billing-light.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-dark.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-light.pngis excluded by!**/*.pngapps/ui/public/changelog/devpass-reset-passes.pngis excluded by!**/*.png
📒 Files selected for processing (25)
.claude/skills/verify/SKILL.mdapps/api/src/routes/dev-plans.tsapps/api/src/routes/organization.tsapps/api/src/stripe.tsapps/api/src/utils/devpass-filter.tsapps/code/src/app/dashboard/(main)/page.tsxapps/code/src/app/dashboard/components/DevPassInvoices.tsxapps/code/src/app/dashboard/components/ResetPassCard.tsxapps/code/src/app/dashboard/components/UsageOverview.tsxapps/code/src/app/pricing/page.tsxapps/code/src/components/Faq.tsxapps/docs/content/learn/index.mdxapps/docs/content/learn/meta.jsonapps/docs/content/learn/model-categories.mdxapps/docs/content/learn/reset-passes.mdxapps/gateway/src/chat/tools/resolve-provider-context.tsapps/gateway/src/lib/rate-limit.spec.tsapps/ui/src/content/changelog/2026-07-16-devpass-reset-passes.mdpackages/db/migrations/1784223442_graceful_lady_bullseye.sqlpackages/db/migrations/meta/1784223442_snapshot.jsonpackages/db/migrations/meta/_journal.jsonpackages/db/src/schema.tspackages/shared/src/dev-plans.spec.tspackages/shared/src/dev-plans.tspackages/shared/src/index.ts
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.claude/skills/verify/SKILL.md:
- Around line 12-25: Update the service startup commands in the verification
instructions so the API, DevPass dashboard, and gateway all launch during one
documented verification flow. Since the API and gateway commands are foreground
processes, separate each service into its own terminal block or explicitly
background the processes while preserving their existing ports and environment
variables.
In `@apps/api/src/routes/dev-plans.ts`:
- Around line 2904-2975: Update the atomic UPDATE in the Reset Pass redemption
handler to add a live-row guard that requires the premium allowance to still be
non-full before consuming inventory, using the current database values rather
than the stale personalOrg snapshot. Preserve the existing source-specific
inventory guards and ensure a concurrent redemption that already reset the
window matches zero rows instead of consuming another pass.
In `@apps/api/src/stripe.ts`:
- Line 3792: Update the renewal flow around the organization update and renewal
transaction insert so both database writes execute within a single atomic DB
transaction, ensuring retries cannot observe only the transaction record. Commit
this combined transaction before sending the invoice, and preserve the existing
allowance reset values and invoice behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 7df2ce8b-9711-4cb5-9999-ab41f6583dd1
📒 Files selected for processing (20)
.claude/skills/verify/SKILL.mdapps/api/src/routes/dev-plans.tsapps/api/src/routes/organization.tsapps/api/src/stripe.tsapps/api/src/utils/devpass-filter.tsapps/code/src/app/dashboard/(main)/page.tsxapps/code/src/app/dashboard/components/DevPassInvoices.tsxapps/code/src/app/dashboard/components/ResetPassCard.tsxapps/code/src/app/dashboard/components/UsageOverview.tsxapps/code/src/app/pricing/page.tsxapps/code/src/components/Faq.tsxapps/gateway/src/chat/tools/resolve-provider-context.tsapps/gateway/src/lib/rate-limit.spec.tspackages/db/migrations/1784223442_graceful_lady_bullseye.sqlpackages/db/migrations/meta/1784223442_snapshot.jsonpackages/db/migrations/meta/_journal.jsonpackages/db/src/schema.tspackages/shared/src/dev-plans.spec.tspackages/shared/src/dev-plans.tspackages/shared/src/index.ts
Review fixes for PR #3093: - Redeem: compare-and-swap on the observed premium usage and week start so two concurrent redeems can't each burn a pass to reset the same allowance (verified live: 2 passes held, 2 concurrent requests, only 1 consumed) - Redeem: require a verified email, matching the checkout route - Checkout: cards only — fulfillment has no async-payment handler, so delayed methods would charge without granting the pass - verify skill: label the three servers as separate terminal sessions Skipped: tier-bound pass inventory — purchased passes surviving tier changes is deliberate, documented behavior (changelog/docs/KB); the cross-tier arbitrage is bounded and gated behind paying the full higher-tier price, and both the price and tier metadata originate server-side. Claude-Session: https://claude.ai/code/session_01DiqhTuu8Gg6nUXFuaRVUH3
Review follow-ups and UX changes for PR #3093: - Purchased Reset Pass inventory is now tier-bound (per-tier columns, regenerated migration): a pass is redeemable only on the tier it was bought for, closing cheap-tier arbitrage. Verified live: pro passes invisible on lite, redeemable again back on pro. - Buying a pass now shows a confirmation dialog and charges the saved payment method via an off-session PaymentIntent with synchronous, atomic fulfilment — no Stripe Checkout redirect. Declines return 402. - Weekly premium allowance meter redesigned: '$X spent / Resets MMM d' on the left, slim track, 'N% used' on the right. - Copy across the card, dialog, changelog, KB, fair-use doc and FAQ now states a pass lifts the weekly limit only — it grants no credits. - apps/ui: register Geist Mono as --font-geist-mono so the font-mono token resolves and changelog code blocks render monospace. - Fresh KB screenshots (reset-passes with the new meter, dashboard with populated demo data) and a new /enterprise dashboard showcase pair. Claude-Session: https://claude.ai/code/session_01DiqhTuu8Gg6nUXFuaRVUH3
|
Images automagically compressed by Calibre's image-actions ✨ Compression reduced images by 66.8%, saving 781.6 KB.
1 image did not require optimisation. |
|
Images automagically compressed by Calibre's image-actions ✨ Compression reduced images by 6.4%, saving 13.5 KB.
5 images did not require optimisation. |
|
Images automagically compressed by Calibre's image-actions ✨ Compression reduced images by 5.9%, saving 3.6 KB.
10 images did not require optimisation. |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/api/src/routes/dev-plans.ts`:
- Around line 2889-2896: Update the catch block handling “Reset Pass charge
failed” to return HTTP 402 only for recognized Stripe payment-decline error
codes, following the tier-change handler’s existing pattern; rethrow all other
errors so configuration, outage, and programming failures reach the global
handler unchanged.
- Around line 2868-2949: The Reset Pass purchase flow around paymentIntent
creation and the db.transaction must be made retry-safe: persist a unique
purchase token before charging, pass that stable token as Stripe’s idempotency
key, and associate it with the transaction/pass fulfillment. Add recovery for
failures after a successful charge by reconciling the persisted purchase state
on retry or refunding orphaned PaymentIntents, ensuring retries cannot create
duplicate charges or grants.
In `@apps/code/src/app/dashboard/components/UsageOverview.tsx`:
- Around line 95-108: Update the allowance meter’s inner progress element in
UsageOverview to expose progressbar semantics, including an accessible label,
the current clamped value, and explicit minimum and maximum range values.
Preserve the existing visual classes and width styling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 64088700-9989-493c-ba4a-37f442842b6e
⛔ Files ignored due to path filters (12)
apps/docs/public/learn/dashboard-cost-dark.pngis excluded by!**/*.pngapps/docs/public/learn/dashboard-cost-light.pngis excluded by!**/*.pngapps/docs/public/learn/dashboard-dark.pngis excluded by!**/*.pngapps/docs/public/learn/dashboard-light.pngis excluded by!**/*.pngapps/docs/public/learn/dashboard-usage-dark.pngis excluded by!**/*.pngapps/docs/public/learn/dashboard-usage-light.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-billing-dark.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-billing-light.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-dark.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-light.pngis excluded by!**/*.pngapps/ui/public/screenshots/dashboard-dark.pngis excluded by!**/*.pngapps/ui/public/screenshots/dashboard-light.pngis excluded by!**/*.png
📒 Files selected for processing (15)
apps/api/src/routes/dev-plans.tsapps/api/src/routes/organization.tsapps/api/src/stripe.tsapps/code/src/app/dashboard/components/ResetPassCard.tsxapps/code/src/app/dashboard/components/UsageOverview.tsxapps/code/src/components/Faq.tsxapps/docs/content/learn/model-categories.mdxapps/docs/content/learn/reset-passes.mdxapps/gateway/src/lib/rate-limit.spec.tsapps/ui/src/app/layout.tsxapps/ui/src/content/changelog/2026-07-16-devpass-reset-passes.mdpackages/db/migrations/1784231994_friendly_phil_sheldon.sqlpackages/db/migrations/meta/1784231994_snapshot.jsonpackages/db/migrations/meta/_journal.jsonpackages/db/src/schema.ts
💤 Files with no reviewable changes (1)
- apps/api/src/stripe.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/code/src/components/Faq.tsx
- apps/docs/content/learn/model-categories.mdx
- apps/api/src/routes/organization.ts
- apps/ui/src/content/changelog/2026-07-16-devpass-reset-passes.md
Review fixes for PR #3093: - Purchase fulfilment extracted to fulfillResetPassPurchase and re-run from the payment_intent.succeeded webhook as the recovery path, so a successful charge can never be lost if the API dies before granting the pass. A pg advisory xact lock + payment-intent dedup make the synchronous and webhook paths race-safe (exactly one grant per charge). Verified live: happy path grants once with the PI recorded. - Purchase catch now returns 402 only for StripeCardError/card_declined (mirroring the tier-change handler) and rethrows everything else. Verified live: always-fail test card -> 402 decline message; corrupted customer id -> 500 via the global handler, not a fake decline. - Weekly allowance meter track exposes progressbar semantics (role, label, aria-valuenow/min/max); visuals unchanged. Skipped: persisted purchase-token idempotency keys + orphaned-PI auto-refunds — webhook redelivery is the repo's established reconciliation mechanism for every payment flow (top-ups, upgrades, end-user wallets), and with the recovery branch no succeeded charge can remain undelivered, so there is no orphan class left to refund. Claude-Session: https://claude.ai/code/session_01DiqhTuu8Gg6nUXFuaRVUH3
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api/src/routes/dev-plans.ts (1)
2830-2869: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winRequire a live subscription before charging.
devPlan !== "none"can lag behind Stripe, so this route can still bill the fallback card after the subscription has already ended. Mirror the canceled/incomplete_expired self-heal used elsewhere here, and fail closed instead of swallowing the subscription read error.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/routes/dev-plans.ts` around lines 2830 - 2869, Update the payment-method lookup around getStripe().subscriptions.retrieve to require a live DevPass subscription before charging: detect canceled or incomplete_expired status, apply the existing canceled/incomplete_expired self-heal used elsewhere, and stop the request without falling back to the customer card when the subscription is not active. Do not swallow subscription retrieval errors; propagate an appropriate failure instead of continuing to charge.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@apps/api/src/routes/dev-plans.ts`:
- Around line 2830-2869: Update the payment-method lookup around
getStripe().subscriptions.retrieve to require a live DevPass subscription before
charging: detect canceled or incomplete_expired status, apply the existing
canceled/incomplete_expired self-heal used elsewhere, and stop the request
without falling back to the customer card when the subscription is not active.
Do not swallow subscription retrieval errors; propagate an appropriate failure
instead of continuing to charge.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 05d9a9b9-7a66-4bea-91a5-1c2580c2bf60
📒 Files selected for processing (3)
apps/api/src/routes/dev-plans.tsapps/api/src/stripe.tsapps/code/src/app/dashboard/components/UsageOverview.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/code/src/app/dashboard/components/UsageOverview.tsx
charge.refunded previously ignored dev_plan_reset_pass rows, so a support refund left the purchased pass usable. Full refunds now remove one tier-bound pass (clamped at zero when already redeemed) and are excluded from the full-refund-cancels-subscription branch, which would otherwise have cancelled the whole DevPass over a pass refund. Adds 27 edge-case tests covering redeem guards, included-before-purchased ordering, tier-bound inventory, the CAS redeem race, purchase charging, declined cards, fulfilment dedup/validation, plan-end lifecycle, refunds, and the status surface. Claude-Session: https://claude.ai/code/session_01ALMqWviMXjb4ZA6dXjRcgE
Replaces the visa-stamp passport card with the same slim design language as the weekly allowance meter: fixed-width stat block, discrete slot strip at track height, right-aligned actions, tabular numerals, and an inline 'Allowance restored' confirmation instead of the full-card stamp overlay. Drops the now-unused organizationId prop. Claude-Session: https://claude.ai/code/session_01ALMqWviMXjb4ZA6dXjRcgE
Retakes the KB and dashboard screenshots for the redesigned Reset Passes row (light + dark), replaces the broken 40px billing screenshots with real invoice-history captures, and rewrites the visa-stamp copy in the KB page and changelog to describe the slot-strip design. Claude-Session: https://claude.ai/code/session_01ALMqWviMXjb4ZA6dXjRcgE
The stamp animation and visa-extension styling are deliberate DevPass brand identity, not decoration — restore the stamped card, docs copy, and changelog wording, and retake the KB screenshots with the stamp design (the pre-redesign images predated tier-bound stamps and the a11y meter). Keeps the fixed billing-history screenshots. Claude-Session: https://claude.ai/code/session_01ALMqWviMXjb4ZA6dXjRcgE
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api/src/stripe.ts (1)
3244-3310: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick winCommit the refund transaction and inventory clawback atomically.
The refund
transactionis inserted before theorganization's inventory update. If the process crashes between these two writes, a webhook retry will see the existing refund transaction, log "Refund already processed", and return early—permanently failing to claw back the refunded Reset Pass (and similarly failing to deduct credits for top-ups).Combine the transaction insert and organization update into a single
db.transaction(). Additionally, fetch thepaymentIntentsmetadata beforehand to avoid holding the database connection open during the external API call.🔒️ Proposed fix to ensure atomic refund and clawback execution
- // Create refund transaction - await db.insert(tables.transaction).values({ - organizationId: originalTransaction.organizationId, - type: "credit_refund", - amount: refundAmountInDollars.toString(), - creditAmount: (-creditRefundAmount).toString(), - currency: originalTransaction.currency, - status: "completed", - stripePaymentIntentId: payment_intent as string, - stripeRefundId: latestRefund.id, - relatedTransactionId: originalTransaction.id, - refundReason: latestRefund.reason ?? null, - description: `Credit refund: $${refundAmountInDollars.toFixed(2)} (${(refundRatio * 100).toFixed(1)}% of original purchase)`, - }); - - // Deduct credits from organization (allow negative) — only for credit_topup - // refunds, since dev plan / subscription purchases don't add to credits. - if (isCreditTopup && creditRefundAmount !== 0) { - await db - .update(tables.organization) - .set({ - credits: sql`${tables.organization.credits} - ${creditRefundAmount}`, - }) - .where(eq(tables.organization.id, originalTransaction.organizationId)); - } - - // A full refund of a Reset Pass claws back one unredeemed pass from the - // tier-bound inventory the purchase granted, clamped at zero when the pass - // was already redeemed — a refunded purchase must not leave a free pass - // behind. The tier comes from the PaymentIntent metadata stamped by the - // purchase route. - if (originalTransaction.type === "dev_plan_reset_pass" && charge.refunded) { - const refundedIntent = await getStripe().paymentIntents.retrieve( - payment_intent as string, - ); - const tierValue = refundedIntent.metadata?.devPlan; - const tier = - tierValue && tierValue in DEV_PLAN_RESET_PASS_PRICES - ? (tierValue as DevPlanTier) - : null; - if (!tier) { - logger.error( - "Refunded Reset Pass has no valid tier in its payment intent metadata", - { paymentIntentId: refundedIntent.id }, - ); - } else { - const clawback = - tier === "lite" - ? { - devPlanResetPassesLite: sql`GREATEST(${tables.organization.devPlanResetPassesLite} - 1, 0)`, - } - : tier === "pro" - ? { - devPlanResetPassesPro: sql`GREATEST(${tables.organization.devPlanResetPassesPro} - 1, 0)`, - } - : { - devPlanResetPassesMax: sql`GREATEST(${tables.organization.devPlanResetPassesMax} - 1, 0)`, - }; - await db - .update(tables.organization) - .set(clawback) - .where(eq(tables.organization.id, organization.id)); - logger.info( - `Clawed back one ${tier} Reset Pass after full refund for organization ${organization.id}`, - ); - } - } + // Fetch PaymentIntent metadata for reset pass clawback before the DB transaction + // to avoid holding the connection open during Stripe API calls. + let clawbackTier: DevPlanTier | null = null; + if (originalTransaction.type === "dev_plan_reset_pass" && charge.refunded) { + const refundedIntent = await getStripe().paymentIntents.retrieve( + payment_intent as string, + ); + const tierValue = refundedIntent.metadata?.devPlan; + const tier = + tierValue && tierValue in DEV_PLAN_RESET_PASS_PRICES + ? (tierValue as DevPlanTier) + : null; + if (!tier) { + logger.error( + "Refunded Reset Pass has no valid tier in its payment intent metadata", + { paymentIntentId: refundedIntent.id }, + ); + } else { + clawbackTier = tier; + } + } + + await db.transaction(async (tx) => { + // Create refund transaction + await tx.insert(tables.transaction).values({ + organizationId: originalTransaction.organizationId, + type: "credit_refund", + amount: refundAmountInDollars.toString(), + creditAmount: (-creditRefundAmount).toString(), + currency: originalTransaction.currency, + status: "completed", + stripePaymentIntentId: payment_intent as string, + stripeRefundId: latestRefund.id, + relatedTransactionId: originalTransaction.id, + refundReason: latestRefund.reason ?? null, + description: `Credit refund: $${refundAmountInDollars.toFixed(2)} (${(refundRatio * 100).toFixed(1)}% of original purchase)`, + }); + + // Deduct credits from organization (allow negative) — only for credit_topup + // refunds, since dev plan / subscription purchases don't add to credits. + if (isCreditTopup && creditRefundAmount !== 0) { + await tx + .update(tables.organization) + .set({ + credits: sql`${tables.organization.credits} - ${creditRefundAmount}`, + }) + .where(eq(tables.organization.id, originalTransaction.organizationId)); + } + + // A full refund of a Reset Pass claws back one unredeemed pass from the + // tier-bound inventory the purchase granted, clamped at zero when the pass + // was already redeemed — a refunded purchase must not leave a free pass + // behind. + if (clawbackTier) { + const clawback = + clawbackTier === "lite" + ? { devPlanResetPassesLite: sql`GREATEST(${tables.organization.devPlanResetPassesLite} - 1, 0)` } + : clawbackTier === "pro" + ? { devPlanResetPassesPro: sql`GREATEST(${tables.organization.devPlanResetPassesPro} - 1, 0)` } + : { devPlanResetPassesMax: sql`GREATEST(${tables.organization.devPlanResetPassesMax} - 1, 0)` }; + await tx + .update(tables.organization) + .set(clawback) + .where(eq(tables.organization.id, organization.id)); + } + }); + + if (clawbackTier) { + logger.info( + `Clawed back one ${clawbackTier} Reset Pass after full refund for organization ${organization.id}`, + ); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/stripe.ts` around lines 3244 - 3310, Wrap the refund transaction insert and the related organization credit or Reset Pass inventory update in one db.transaction() so either all writes commit or none do, preserving webhook retry safety. In the refund handler, retrieve the payment intent metadata before starting the database transaction, then reuse it in the dev_plan_reset_pass clawback logic instead of calling getStripe().paymentIntents.retrieve inside the transaction. Keep the existing tier validation and update behavior unchanged.
🧹 Nitpick comments (2)
apps/api/src/routes/dev-plans-reset-passes.spec.ts (2)
606-633: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winVerify full-refund webhook idempotency.
Redeliver the same refund event in this test. Otherwise, a regression could decrement another purchased pass when Stripe retries the webhook.
Proposed test extension
await handleChargeRefunded(chargeRefundedEvent("pi_refund_full")); + await handleChargeRefunded(chargeRefundedEvent("pi_refund_full")); const org = await getOrg(); expect(org.devPlanResetPassesPro).toBe(1);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/routes/dev-plans-reset-passes.spec.ts` around lines 606 - 633, The full-refund test around handleChargeRefunded lacks a webhook retry assertion. Invoke handleChargeRefunded again with the same chargeRefundedEvent after the initial handling, then verify the organization still has one remaining dev plan reset pass and only one credit_refund row, confirming duplicate delivery does not reclaim another pass.
337-379: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winAdd purchase-specific authentication tests.
The redeem tests do not protect the separate purchase endpoint from regressions. Add unauthenticated and unverified-email cases asserting that Stripe is never called.
Proposed tests
describe("reset pass purchase", () => { let token: string; + it("rejects unauthenticated requests", async () => { + await insertOrg(); + const res = await purchaseRequest(); + expect(res.status).toBe(401); + expect(stripeMock.paymentIntents.create).not.toHaveBeenCalled(); + }); + + it("rejects users without a verified email", async () => { + await insertOrg(); + await db + .update(tables.user) + .set({ emailVerified: false }) + .where(eq(tables.user.id, "test-user-id")); + + const res = await purchaseRequest(token); + expect(res.status).toBe(403); + expect(stripeMock.paymentIntents.create).not.toHaveBeenCalled(); + }); + it("rejects purchase without an active dev plan", async () => {As per coding guidelines, “API endpoints must independently verify authentication, email verification, and permissions rather than relying on UI-only gates or request-body identity fields.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/routes/dev-plans-reset-passes.spec.ts` around lines 337 - 379, Add purchase-specific authentication coverage within the “reset pass purchase” suite: add unauthenticated and unverified-email requests to the purchase endpoint, assert each is rejected, and verify stripeMock.paymentIntents.create is never called. Reuse the existing purchaseRequest and test-user setup helpers while keeping the current active-plan permission test unchanged.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@apps/api/src/stripe.ts`:
- Around line 3244-3310: Wrap the refund transaction insert and the related
organization credit or Reset Pass inventory update in one db.transaction() so
either all writes commit or none do, preserving webhook retry safety. In the
refund handler, retrieve the payment intent metadata before starting the
database transaction, then reuse it in the dev_plan_reset_pass clawback logic
instead of calling getStripe().paymentIntents.retrieve inside the transaction.
Keep the existing tier validation and update behavior unchanged.
---
Nitpick comments:
In `@apps/api/src/routes/dev-plans-reset-passes.spec.ts`:
- Around line 606-633: The full-refund test around handleChargeRefunded lacks a
webhook retry assertion. Invoke handleChargeRefunded again with the same
chargeRefundedEvent after the initial handling, then verify the organization
still has one remaining dev plan reset pass and only one credit_refund row,
confirming duplicate delivery does not reclaim another pass.
- Around line 337-379: Add purchase-specific authentication coverage within the
“reset pass purchase” suite: add unauthenticated and unverified-email requests
to the purchase endpoint, assert each is rejected, and verify
stripeMock.paymentIntents.create is never called. Reuse the existing
purchaseRequest and test-user setup helpers while keeping the current
active-plan permission test unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: e9978789-1922-4cfa-b8ff-9969e5a9ad5c
⛔ Files ignored due to path filters (6)
apps/docs/public/learn/dashboard-dark.pngis excluded by!**/*.pngapps/docs/public/learn/dashboard-light.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-billing-dark.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-billing-light.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-dark.pngis excluded by!**/*.pngapps/docs/public/learn/reset-passes-light.pngis excluded by!**/*.png
📒 Files selected for processing (2)
apps/api/src/routes/dev-plans-reset-passes.spec.tsapps/api/src/stripe.ts
…sses # Conflicts: # apps/api/src/routes/dev-plans.ts # packages/db/migrations/meta/_journal.json
|
Images automagically compressed by Calibre's image-actions ✨ Compression reduced images by 68.1%, saving 573.6 KB.
6 images did not require optimisation. |
|
Images automagically compressed by Calibre's image-actions ✨ Compression reduced images by 6.4%, saving 2.9 KB.
9 images did not require optimisation. |
|
Images automagically compressed by Calibre's image-actions ✨ Compression reduced images by 5.6%, saving 914 B.
11 images did not require optimisation. |
Swap the monthly credits bar to show spent-of-limit as the primary figure with remaining as the secondary, matching the weekly premium meter which leads with amount spent.
## Summary Reset Pass purchases (theopenco#3093) were the only paid flow with no internal Discord notification — credits, DevPass/chat plan subscribe/renew/cancel, and refunds all post to the billing channel, and a *refunded* Reset Pass already notified via the generic refund handler. This closes that gap. - Add `notifyResetPassPurchased` to `apps/api/src/utils/discord.ts` (email, name, tier, amount — same embed pattern as the other purchase notifiers, posts to `DISCORD_NOTIFICATION_URL`) - Call it from `fulfillResetPassPurchase` after the payment-intent dedupe guard, so the synchronous purchase route and the `payment_intent.succeeded` recovery webhook can't double-notify - Resolves the recipient from `organization.billingEmail` + user lookup, mirroring the refund notification path ## Test plan - `pnpm exec turbo run build --filter=api` passes - Notification is best-effort: `sendDiscordNotification` already swallows webhook failures and skips when `DISCORD_NOTIFICATION_URL` is unset, so fulfilment is unaffected either way <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Improved handling of reset pass purchases to ensure completed transactions are recorded reliably. * Added more detailed purchase information to internal notifications, including the purchaser’s email, name, selected tier, and payment amount. * Duplicate purchase fulfillment remains prevented, and notification failures do not interrupt payment processing. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem The July traffic report showed revenue concentrating in two places we under-serve: 1. **The DevPass dashboard is the #1 converting surface** (354 payers last month), and Reset Passes sold 42 units in month one with zero targeting — but the agreed follow-up from theopenco#3093 (a real cap-hit analytics event) never shipped, and the dashboard shows the same ResetPassCard at 0% and 100% usage. No contextual offer exists at the moment of highest intent. 2. **Compliance content converts payers at ~5%** (soc2-type-ii: 158 readers → 8 payers) but enterprise leads are flat at 4/month — the post never presents the enterprise path. Meanwhile the weekly report shows organic momentum fading, with the proven "[X] alternatives" and Kimi K3 playbooks sitting unshipped. ## What this ships ### DevPass: cap-hit funnel + contextual offer - **`devpass_premium_cap_rejected`** captured server-side in the gateway when the weekly premium-cap 402 fires (`assertDevPlanPremiumCapNotExceeded`), with `devPlan`, `model`, `msUntilReset`, and org group. This is the follow-up agreed in theopenco#3093: `devpass_weekly_cap_hit_viewed` only counts users who open the dashboard, but most cap hits happen inside coding agents that swallow the 402 — demand was undercounted. Adds a `posthog-node` client to `apps/gateway` (mirrors `apps/api/src/posthog.ts`; disabled without env, no hot-path cost). - **`CapHitResetOfferDialog`** on the DevPass dashboard: a visa-stamp dialog (border-control stamp, MRZ strip — house DevPass brand) that appears the moment the weekly premium cap is hit, driven by the existing 5s status poll. It mirrors the server's purchase/redeem gates (never offers an action the API would 400), stays quiet when the monthly pool is exhausted (that state belongs to `AllowanceExhaustedCard`), snoozes per cap-window via cookie, and emits `devpass_cap_hit_offer_shown/dismissed/clicked`. The CTA scrolls to the ResetPassCard rather than duplicating the purchase surface. - **`/ingest` PostHog proxy for `apps/code`** (mirroring apps/ui and apps/playground) — DevPass dashboard events were ad-blocker-droppable until now, so July's 500 cap-hit views were an undercount. Expect an event step-up after deploy. <img width="1080" alt="Cap-hit Reset Pass offer demo: dialog appears at 100% weekly usage, CTA scrolls to the ResetPassCard, redeem restores the allowance" src="https://raw.githubusercontent.com/theopenco/llmgateway/cc22b375431e6148e4889e403c4635327095fc0c/cap-hit-reset-pass-demo.gif" /> ([MP4 version](https://raw.githubusercontent.com/theopenco/llmgateway/cc22b375431e6148e4889e403c4635327095fc0c/cap-hit-reset-pass-demo.mp4)) ### Compliance/enterprise content cluster - `soc2-type-ii` gains a **provider compliance policies** section (the theopenco#3339 policy-aware picker, fail-closed requirements, 403-before-egress), a proper enterprise CTA block, and links into the new cluster. - Three sibling posts feeding the same funnel: **`llm-data-retention`**, **`gdpr-compliant-llm-routing`**, and **`llm-compliance-checklist`** — all fact-checked against `apps/docs/content/features/{data-retention,compliance}.mdx` and the routing docs (region example uses a real catalogue mapping, `aws-bedrock/claude-sonnet-4-6:eu-west-2`). - New **`BlogCta variant="enterprise"`** (→ `/enterprise#contact` + `/enterprise/compliance`) used by all three. ### Organic pipeline refill - **`portkey-alternatives`** and **`helicone-alternatives`** listicles — the two SERP gaps left open after the litellm/openrouter/copilot listicles proved the pattern. Facts per the verified June-2026 competitor landscape (Portkey→Palo Alto/Prisma AIRS; Helicone→Mintlify maintenance mode; Langfuse/LangSmith claims re-verified this week). Internal links added from `/compare/portkey`, the vs-Portkey post, best-ai-gateways, and both existing listicles' "skip" sections. - **Kimi K3 spokes**: `kimi-k3-open-weights` (weights shipped Jul 26 on HF under a custom **"Kimi K3 License"** — not the Modified MIT press predicted, so the post and pillar deliberately point at the LICENSE file instead of summarizing terms) and `kimi-k3-api` (the "kimi k3 api" query; reasoning_effort semantics, cached-input economics, sticky sessions). Pillar updated: weights-release facts corrected, spokes interlinked. - **`/rankings` interlinks** from the `/models` SEO copy and `llms.txt` (it had no entry there). - 7 OG images generated in the house circuit-board style with the composited wordmark. ## Verification - `pnpm format`, full `pnpm build`, and `dev-plans-reset-passes.spec.ts` (32/32) pass. - Demo recorded against the local stack with the real seed org: staged cap-hit via SQL (millisecond-truncated `dev_plan_premium_week_start` for the CAS), dialog fired, CTA scrolled to the card, redeem zeroed `devPlanPremiumCreditsUsed` and consumed the included pass server-side. ## Notes for review / follow-ups - The census dialog and the new cap-hit dialog can theoretically stack (both Radix dialogs; census mounts globally, cap-hit on the main dashboard page). In the wild both firing together should be rare; if we care, a simple priority gate in `DashboardShell` would fix it. - The premium-cap 402 still has no machine-readable `code` distinguishing it from out-of-credits — agents can't react programmatically. Left out deliberately (error-shape compatibility); worth its own PR. - Blog listicles ship FAQ sections but blog posts emit no FAQPage JSON-LD (model pages do). Separate SEO follow-up. - dev.to syndication for the new listicles is intentionally not part of this PR (staggering per the syndication policy). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018NeUff4XEsqAVuJRZ8KuvS <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added a dashboard offer for eligible users who exhaust weekly premium usage, including reset-pass redemption and purchase options. - Added links to live model rankings and expanded enterprise compliance calls to action. - Added guidance for Kimi K3, GDPR-compliant routing, LLM compliance, data retention, and gateway alternatives. - **Documentation** - Updated model, compliance, licensing, and comparison content with new articles, refreshed links, and recommendations. - **Bug Fixes** - Improved analytics loading, routing reliability, and shutdown handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Luca Steeb <contact@luca-steeb.com>
What
Reset Passes — a consumable add-on that instantly restores a DevPass subscriber's weekly premium-model allowance (fresh 7-day window) instead of making them wait out the rolling window or upgrade. A pass lifts the weekly limit only — it grants no extra credits; usage still draws from the monthly allowance.
Pricing rationale: ~82–86% of the weekly cap each pass unlocks — cheaper than the equivalent PAYG credits, while the unlocked spend still draws from the already-granted monthly pool, keeping the pool as the hard cost ceiling. Included passes are non-rollover and consumed before purchased ones. Purchased inventory is tier-bound (per-tier columns): a pass is redeemable only on the tier it was bought for, closing cheap-tier arbitrage; unused passes persist and apply again when back on that tier.
How
DEV_PLAN_RESET_PASS_PRICES,DEV_PLAN_INCLUDED_RESET_PASSES,getIncludedResetPassesRemaining+ unit tests.devPlanResetPassesLite/Pro/Max) +devPlanIncludedResetPassesUsed(per-cycle counter, cleared on subscribe/upgrade/renewal/plan-end);dev_plan_reset_passtransaction type; generated migration.POST /dev-plans/reset-pass/purchase— confirmation-dialog flow: charges the saved payment method via an off-session PaymentIntent (no Stripe Checkout redirect); synchronous atomic fulfilment (transaction row + tier inventory in one DB tx), invoice email, PostHogreset_pass_purchased; card declines return 402.POST /dev-plans/reset-pass/redeem— zeroes premium usage and clears the week; included-first consumption; requires verified email; compare-and-swap on observed premium usage/week-start + inventory guards blocks double-redeem races (verified live with concurrent requests).dev_plan_reset_passin DevPass revenue/paid filters.apps/code): weekly allowance meter redesigned ("$X spent / Resets MMM d / N% used" slim-track layout); Reset Pass card styled as a stamped passport visa extension — stamp slots, redeem animation, MRZ footer, buy-confirmation dialog. Pricing page matrix row + FAQ entry.apps/ui): Geist Mono registered under--font-geist-monoso thefont-monotoken resolves — changelog code blocks now render monospace.learn/reset-passesKB page + Reset Passes section in the fair-use doc (all state limit-only semantics), fresh light/dark KB screenshots (reset-passes, dashboard with populated demo data) and an updated /enterprise dashboard showcase pair.Verification (all driven live)
pnpm buildall apps; affected unit suites green.Follow-ups
https://claude.ai/code/session_01DiqhTuu8Gg6nUXFuaRVUH3
Summary by CodeRabbit