← all branches

feat/subscription-gating

needs attentionviewing older commit
372882b · incrementalPR #234reviewed 2026-07-21 17:31 UTC0H · 1M · 2L · 1I
The branch
Purpose
Gate CFE bill-download jobs for organizations with an inactive Stripe subscription (past_due → canceled), while keeping reads, billing UI, and payment-status checks open so customers can self-cure.
Goal
One signal (organizations.subscription_status), one decision (decideFeatureAccess), one guard (requireActiveSubscription). Block consumption, not data.
Sub-goals
  • SG-1: Pure decision + SubscriptionInactiveError in domains/core/src/billing
  • SG-2: requireActiveSubscription middleware in packages/api/src/middleware
  • SG-3: CFE handler enforcement + subscriber lambda org filtering
  • SG-4: Public API /v1/jobs gate (402 subscription_inactive)
  • SG-5: SubscriptionBanner (amber past_due / red canceled) in dashboard layout
  • SG-6: Design doc + verification guide
The changes (whole branch)
What
Merge-of-main window: public-v1 jobs contract gains force_refresh/period_count documentation; triggerCfeJobBatch normalizes three validation-rejection shapes into per-record BatchValidationIssue[]; bulkBar i18n keys merged in from main (feat/contratos #281).
Why
ts-rest/zod serialization of ZodErrors in bodyErrors varies by version (issues array vs JSON message string); normalizing all three shapes prevents silent per-RPU attribution loss on a dependency bump.
Areas
apps/platform+1403domains/core/src/billing+2370packages/api/src/middleware+1110services/utility/bills/cfe+1462docs/development+2090
Blast
26 files, +880/-4 cumulative. Enforcement: download-path only. No migration. Fail-open design.
public-api billing
CI· GraphQL access denied — check PR #234 directlyCodeRabbit· no .coderabbit.yaml in repo

Findings · 4

correctness1

low

field: '' is ambiguous for record-level Zod errors

apps/platform/src/app/[locale]/(dashboard)/bills/_lib/cfe-job-client.ts:138

String(i.path[2] ?? '') returns empty string for a record-level error path ['records', n]. Consider undefined/null to signal 'no specific field'.

security1

info

JSON.parse of server error string is safely try-catched

apps/platform/src/app/[locale]/(dashboard)/bills/_lib/cfe-job-client.ts:126

No risk; acknowledged for completeness.

tests1

medium

Three-shape validation normalization has no unit tests

apps/platform/src/app/[locale]/(dashboard)/bills/_lib/cfe-job-client.ts:113

The comment documents three distinct server response shapes, each a separate code path. None are covered by unit tests. A wrong assumption would silently leave e.issues undefined on the most common validation failure path.

improvement1

low

retryCfeJob uses stale inline error type

apps/platform/src/app/[locale]/(dashboard)/bills/_lib/cfe-job-client.ts:58

Uses Error & { status?: number; code?: string } inline while BatchRequestError is now exported from the same file. A shared base type would eliminate duplication.

History · 7 commits

  1. c807731needs attentionincremental0H · 6M · 8L2026-07-30 17:24
  2. 372882bneeds attentionincremental0H · 1M · 2L2026-07-21 17:31current
  3. 1fac5adneeds attentionincremental0H · 5M · 4L2026-07-07 02:50
  4. 99bf169safeincremental0H · 0M · 1L2026-07-04 04:01
  5. 6305b3cneeds attentionincremental0H · 1M · 4L2026-07-04 03:53
  6. f00e3caneeds attentionincremental0H · 7M · 16L2026-07-04 03:15
  7. 1ae74bdneeds attentionfull6H · 9M · 8L2026-07-04 02:36