feat/subscription-gating
needs attentionviewing older commit372882b · incrementalPR #234reviewed 2026-07-21 17:31 UTC0H · 1M · 2L · 1I- 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
- 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+140−3domains/core/src/billing+237−0packages/api/src/middleware+111−0services/utility/bills/cfe+146−2docs/development+209−0
- Blast
- 26 files, +880/-4 cumulative. Enforcement: download-path only. No migration. Fail-open design.
Findings · 4
correctness1
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
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
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
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
- c807731needs attentionincremental0H · 6M · 8L2026-07-30 17:24
- 372882bneeds attentionincremental0H · 1M · 2L2026-07-21 17:31current
- 1fac5adneeds attentionincremental0H · 5M · 4L2026-07-07 02:50
- 99bf169safeincremental0H · 0M · 1L2026-07-04 04:01
- 6305b3cneeds attentionincremental0H · 1M · 4L2026-07-04 03:53
- f00e3caneeds attentionincremental0H · 7M · 16L2026-07-04 03:15
- 1ae74bdneeds attentionfull6H · 9M · 8L2026-07-04 02:36