Skip to content

Guard late invoice payments for ended subscriptions#956

Merged
ross0x01 merged 1 commit into
mainfrom
codex/guard-late-paid-subscriptions
Jul 24, 2026
Merged

Guard late invoice payments for ended subscriptions#956
ross0x01 merged 1 commit into
mainfrom
codex/guard-late-paid-subscriptions

Conversation

@ross0x01

@ross0x01 ross0x01 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • skip invoice.paid entitlement side effects when Stripe reports a terminal canceled or incomplete_expired subscription
  • emit a structured reconciliation warning with invoice, subscription, customer, user, and organization identifiers
  • cover both terminal states and verify the webhook remains acknowledged and idempotently marked without referral, revenue/MRR, proration, or usage-credit mutations

Why

Stripe can leave a renewal invoice collectible after its subscription has been canceled. Paying that invoice later succeeds financially but does not reactivate the subscription. The existing webhook treated the late payment like an active renewal, which could create split state between Stripe/WorkOS entitlements and HackerAI billing side effects.

Validation

  • corepack pnpm test --runInBand — 296 suites, 2,922 tests passed
  • corepack pnpm typecheck
  • corepack pnpm lint — passes with 5 existing unrelated warnings
  • corepack pnpm exec eslint app/api/subscription/webhook/route.ts app/api/subscription/webhook/__tests__/route.test.ts
  • corepack pnpm exec prettier --check app/api/subscription/webhook/route.ts app/api/subscription/webhook/__tests__/route.test.ts
  • git diff --check

Manual verification

  • In Stripe Billing recovery settings, change the production subscription cancellation window from 1 day to at least the end of the configured 1/2/3-day authentication reminder sequence; 7 days is recommended to match the current invoice recovery window.
  • In Stripe test mode, deliver an invoice.paid event whose referenced subscription is canceled or incomplete_expired. Confirm the webhook returns 200, emits billing_invoice_paid_terminal_subscription_skipped, and does not restore paid entitlements or usage credits.
  • Deliver a normal paid renewal for an active subscription and confirm its included usage still resets normally.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of paid invoices tied to canceled or expired subscriptions.
    • Prevented ineligible usage credits, tier updates, referral rewards, and related analytics from being applied in these cases.
    • Added warning reporting for invoices requiring manual reconciliation.

@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hackerai Ready Ready Preview, Comment Jul 24, 2026 2:06am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c37bd145-414f-40ab-9bb4-5ab7cc42b612

📥 Commits

Reviewing files that changed from the base of the PR and between 3d075c5 and 99fa3cf.

📒 Files selected for processing (2)
  • app/api/subscription/webhook/__tests__/route.test.ts
  • app/api/subscription/webhook/route.ts

📝 Walkthrough

Walkthrough

The invoice-paid webhook now recognizes canceled and incomplete-expired subscriptions, logs a reconciliation warning, and skips restoration and billing side effects. Parameterized tests verify the response, skipped operations, warning event, and idempotency checks.

Changes

Terminal invoice handling

Layer / File(s) Summary
Terminal status guard and coverage
app/api/subscription/webhook/route.ts, app/api/subscription/webhook/__tests__/route.test.ts
Terminal subscription statuses trigger an early return from invoice.paid processing, while tests verify warning emission, skipped mutations and bucket operations, successful handling, and idempotency.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Stripe
  participant WebhookHandler
  participant PostHog
  Stripe->>WebhookHandler: invoice.paid with terminal subscription
  WebhookHandler->>PostHog: emit terminal subscription skip warning
  WebhookHandler-->>Stripe: return success without restoration side effects
Loading

Possibly related PRs

Suggested labels: codex

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: guarding invoice.paid handling for ended subscriptions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/guard-late-paid-subscriptions

Comment @coderabbitai help to get the list of available commands.

@ross0x01
ross0x01 merged commit 2fd0b8a into main Jul 24, 2026
5 checks passed
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.

1 participant