Repository navigation
feat(code): gate reset passes on usage, add refunds - #3120
Conversation
Block buying a Reset Pass above 95% of the monthly cycle credit allowance and redeeming one above 90%, since a pass restores a weekly cap with nothing left to spend against. Purchases were already tracked in the transaction table; unused passes can now be self-refunded within 7 days of purchase (tier-bound inventory must still hold the pass), leaving the DevPass plan untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E18J9zTY3MiR6SedFgY4aN
When the monthly credit allowance (the hard ceiling, not the weekly premium cap) is exhausted, Reset Passes can't unlock anything: they only reset the weekly premium window and never add credits. Selling one at that point is a no-op purchase. - API: reject reset-pass purchase with 400 when the monthly pool is exhausted (weekly-cap-only exhaustion still allows purchase) - Dashboard: hide the Reset Pass card in that state and show a promo instead: Lite/Pro get an upgrade CTA to the next tier (upgrading starts a fresh cycle instantly with the full new allowance); Max gets a PAYG CTA to llmgateway.io highlighting that it works with the same coding agents via an API key swap + credits - Weekly meter copy no longer points at the hidden pass card - PostHog events for promo views/clicks Claude-Session: https://claude.ai/code/session_011UftFFQDksranqTiWPNMBb
WalkthroughReset Pass purchase and redemption now enforce monthly usage thresholds. The dashboard handles exhausted allowances with upgrade or PAYG messaging, and Reset Pass refunds now support dedicated eligibility rules and user-facing states. ChangesReset Pass cycle-usage controls
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Dashboard
participant UsageOverview
participant ResetPassAPI
participant OrganizationCredits
participant Stripe
Dashboard->>UsageOverview: Display monthly usage
UsageOverview->>ResetPassAPI: Purchase or redeem Reset Pass
ResetPassAPI->>OrganizationCredits: Read credits used and limit
OrganizationCredits-->>ResetPassAPI: Return usage fraction
ResetPassAPI->>Stripe: Create payment intent for allowed purchase
ResetPassAPI-->>Dashboard: Return success or threshold rejection
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 |
…ction-yc2kdk' into devpass-exhausted-allowance-promo # Conflicts: # apps/api/src/routes/dev-plans-reset-passes.spec.ts # apps/api/src/routes/dev-plans.ts # apps/code/src/app/dashboard/components/UsageOverview.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/ui/src/app/dashboard/[orgId]/org/transactions/page.tsx (1)
27-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider adding
"dev_plan_reset_pass"to theTransaction["type"]union.You've added
"pass_already_used"to therefund.reasonunion, but thetypeunion (lines 7-11) does not include"dev_plan_reset_pass". Sincepass_already_usedis specific to reset passes, it might be beneficial to also include"dev_plan_reset_pass"in thetypeunion for TypeScript completeness, in case this transaction type is encountered via URL navigation or an API response.💡 Proposed addition to `type`
type: | "credit_topup" | "subscription_start" | "subscription_cancel" - | "subscription_end"; + | "subscription_end" + | "dev_plan_reset_pass";🤖 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/ui/src/app/dashboard/`[orgId]/org/transactions/page.tsx around lines 27 - 28, Extend the Transaction["type"] union near the existing transaction type literals to include "dev_plan_reset_pass", keeping the new value aligned with the related refund.reason support for reset-pass transactions.
🤖 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/docs/content/learn/reset-passes.mdx`:
- Line 64: Update the refund eligibility wording in the reset-pass documentation
to refer only to an eligible unused pass, rather than implying every unredeemed
pass purchased within seven days qualifies. Mention or link to the refund flow’s
inventory and amount eligibility checks while preserving the existing seven-day,
payment-method, passport, and DevPass plan behavior.
---
Nitpick comments:
In `@apps/ui/src/app/dashboard/`[orgId]/org/transactions/page.tsx:
- Around line 27-28: Extend the Transaction["type"] union near the existing
transaction type literals to include "dev_plan_reset_pass", keeping the new
value aligned with the related refund.reason support for reset-pass
transactions.
🪄 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: 21b597d6-9482-43db-acf1-a35ee479fc3a
📒 Files selected for processing (14)
apps/api/src/lib/self-refund.spec.tsapps/api/src/lib/self-refund.tsapps/api/src/routes/dev-plans-reset-passes.spec.tsapps/api/src/routes/dev-plans.tsapps/api/src/routes/organization.tsapps/code/src/app/dashboard/components/DevPassInvoices.tsxapps/code/src/app/dashboard/components/ResetPassCard.tsxapps/code/src/app/dashboard/components/UsageOverview.tsxapps/docs/content/learn/reset-passes.mdxapps/ui/src/app/dashboard/[orgId]/org/transactions/page.tsxapps/ui/src/components/billing/transactions-client.tsxpackages/shared/src/dev-plans.spec.tspackages/shared/src/dev-plans.tspackages/shared/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/code/src/app/dashboard/components/UsageOverview.tsx
| **Buy a pass** shows a confirmation dialog, then charges the payment method saved on your DevPass — no checkout redirect, no subscription change, no proration. Each purchase emails a PDF invoice and appears in the DevPass billing history alongside plan charges. | ||
| **Buy a pass** shows a confirmation dialog, then charges the payment method saved on your DevPass — no checkout redirect, no subscription change, no proration. Each purchase emails a PDF invoice and appears in the DevPass billing history alongside plan charges. Purchases are paused once you've used more than 95% of your monthly credit allowance — a pass would give you almost nothing to use at that point — and open up again when your credits renew. | ||
|
|
||
| An unused pass can be returned: for 7 days after purchase, a purchased pass that hasn't been redeemed can be self-refunded from the billing history. The refund goes back to your payment method, the pass is removed from your passport, and your DevPass plan itself is unaffected. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Document the additional refund eligibility checks.
This wording implies every unredeemed pass within seven days is refundable, but the refund flow also applies inventory and amount checks. Describe it as an eligible unused pass and mention or link to those conditions.
Based on the refund implementation’s additional inventory and amount eligibility rules.
🤖 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/docs/content/learn/reset-passes.mdx` at line 64, Update the refund
eligibility wording in the reset-pass documentation to refer only to an eligible
unused pass, rather than implying every unredeemed pass purchased within seven
days qualifies. Mention or link to the refund flow’s inventory and amount
eligibility checks while preserving the existing seven-day, payment-method,
passport, and DevPass plan behavior.
When the 90%/95% cycle-usage gates disable redeeming/buying, the Reset Pass card previously offered no way forward — just disabled buttons with hover-only tooltips. Now the gated card shows a visible explanation line and a primary CTA: upgrade to the next tier (Lite/Pro) or PAYG credits on llmgateway.io (Max), matching the promo already shown at 100% exhaustion. Adds a devpass_reset_gate_upgrade_clicked PostHog event. Claude-Session: https://claude.ai/code/session_011UftFFQDksranqTiWPNMBb
Sync with main's Reset Pass refunds (#3120/#3124): the reset-pass refunded-purchase attribution and the new admin devpass refund queries only matched credit_refund rows; match subscription_refund too since plan refunds are now recorded with that type. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sync with main's Reset Pass refunds (#3120/#3124): the reset-pass refunded-purchase attribution and the new admin devpass refund queries only matched credit_refund rows; match subscription_refund too since plan refunds are now recorded with that type. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What
Combines two related Reset Pass safeguards (this PR now includes #3119):
A Reset Pass lifts the weekly premium cap, but the unlocked spend still draws from the plan's monthly credit pool — so near or at the end of the pool a pass delivers little or nothing. This PR gates purchase/redeem on cycle usage, replaces the pass card with an upgrade/PAYG promo once the pool is fully exhausted, and adds a self-refund path for unused passes.
Cycle-usage gates (server + mirrored in UI)
getDevPlanCycleUsageFractionlive in@llmgateway/shared, shared by API and dashboard.devpass_reset_gate_upgrade_clicked.100% exhausted → promo instead of passes
When the monthly allowance is fully used ($87 Lite / $237 Pro / $537 Max), the Reset Pass card is hidden entirely and replaced with a passport-style promo:
/dashboard/billing), highlighting that upgrading starts a fresh billing cycle immediately with the full new allowance.devpass_allowance_exhausted_viewed/..._promo_clicked.Self-refund of unused passes (7 days)
dev_plan_reset_passis now self-refundable with a 7-day window (vs 14 for plan payments), only while the tier-bound inventory still holds a pass; a redeemed pass returnspass_already_used.charge.refundedwebhook bookkeeping (claws back the pass, leaves the DevPass subscription untouched); works after plan cancellation since purchased passes survive it.Testing
dev-plans-reset-passes.spec.ts(32),self-refund.spec.ts(30),packages/shared/dev-plans.spec.ts(8) — 70/70 passing.pnpm build(17 tasks) +pnpm formatpass.https://claude.ai/code/session_011UftFFQDksranqTiWPNMBb
Summary by CodeRabbit