refactor: Clean up subscription admin override and Stripe integration (#205) #218

Merged
egullickson merged 3 commits from issue-205-clean-subscription-admin-override into main 2026-02-16 15:44:12 +00:00
Owner

Summary

Refactors the subscription system to cleanly separate admin-managed tier overrides from Stripe-managed billing.

  • Make stripe_customer_id NULLABLE (replaces admin_override_* sentinel values with NULL)
  • Replace resolveStripeCustomerId() with ensureStripeCustomer() (includes orphaned customer cleanup)
  • Make syncTierToUserProfile() blocking (prevents tier drift in all code paths)
  • Add null guards to cancel/reactivate for admin-set subscriptions
  • Simplify getInvoices() null check

Changes

Migration

  • New 002_nullable_stripe_customer_id.sql - DROP NOT NULL, cleanup sentinel values

Backend Types

  • Subscription.stripeCustomerId: string | null
  • SubscriptionResponse.stripeCustomerId: string | null
  • UpdateSubscriptionData.stripeCustomerId?: string | null

Repository

  • createForAdminOverride() inserts NULL instead of admin_override_* placeholder
  • mapSubscriptionRow() uses ?? null (nullish coalescing)
  • create() accepts optional stripeCustomerId

Service

  • Deleted resolveStripeCustomerId() (30 lines removed)
  • Added ensureStripeCustomer() with orphaned Stripe customer cleanup on DB failure
  • syncTierToUserProfile() made blocking (error swallowing removed)
  • cancelSubscription() / reactivateSubscription() - null guard for stripeCustomerId
  • getInvoices() simplified from prefix check to null check

Stripe Client

  • Added deleteCustomer() for cleanup logic

Controller

  • Updated stale comment referencing admin_override_

Frontend Types

  • Subscription.stripeCustomerId: string | null

Test Plan

  • Backend TypeScript: 0 errors
  • Frontend TypeScript: 0 errors
  • Backend lint: 0 errors
  • Frontend lint: 0 errors
  • Tests: 141 passed (27 failed - pre-existing, unrelated to #205)
  • QR post-implementation: PASS_WITH_CONCERNS (pre-existing issues only)

Fixes #205
Fixes #207
Fixes #208
Fixes #209
Fixes #210

## Summary Refactors the subscription system to cleanly separate admin-managed tier overrides from Stripe-managed billing. - Make `stripe_customer_id` NULLABLE (replaces `admin_override_*` sentinel values with NULL) - Replace `resolveStripeCustomerId()` with `ensureStripeCustomer()` (includes orphaned customer cleanup) - Make `syncTierToUserProfile()` blocking (prevents tier drift in all code paths) - Add null guards to cancel/reactivate for admin-set subscriptions - Simplify `getInvoices()` null check ## Changes ### Migration - New `002_nullable_stripe_customer_id.sql` - DROP NOT NULL, cleanup sentinel values ### Backend Types - `Subscription.stripeCustomerId: string | null` - `SubscriptionResponse.stripeCustomerId: string | null` - `UpdateSubscriptionData.stripeCustomerId?: string | null` ### Repository - `createForAdminOverride()` inserts NULL instead of `admin_override_*` placeholder - `mapSubscriptionRow()` uses `?? null` (nullish coalescing) - `create()` accepts optional `stripeCustomerId` ### Service - Deleted `resolveStripeCustomerId()` (30 lines removed) - Added `ensureStripeCustomer()` with orphaned Stripe customer cleanup on DB failure - `syncTierToUserProfile()` made blocking (error swallowing removed) - `cancelSubscription()` / `reactivateSubscription()` - null guard for stripeCustomerId - `getInvoices()` simplified from prefix check to null check ### Stripe Client - Added `deleteCustomer()` for cleanup logic ### Controller - Updated stale comment referencing `admin_override_` ### Frontend Types - `Subscription.stripeCustomerId: string | null` ## Test Plan - [x] Backend TypeScript: 0 errors - [x] Frontend TypeScript: 0 errors - [x] Backend lint: 0 errors - [x] Frontend lint: 0 errors - [x] Tests: 141 passed (27 failed - pre-existing, unrelated to #205) - [x] QR post-implementation: PASS_WITH_CONCERNS (pre-existing issues only) Fixes #205 Fixes #207 Fixes #208 Fixes #209 Fixes #210
egullickson added 3 commits 2026-02-16 15:31:32 +00:00
Make stripe_customer_id NULLABLE via migration, clean up admin_override_*
values to NULL, and update Subscription/SubscriptionResponse/UpdateSubscriptionData
types in both backend and frontend.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove admin_override_ placeholder from createForAdminOverride(), use NULL.
Update mapSubscriptionRow() with ?? null. Make stripeCustomerId optional
in create() method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: replace resolveStripeCustomerId with ensureStripeCustomer, harden sync (refs #209, refs #210)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 6m33s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 52s
Deploy to Staging / Verify Staging (pull_request) Successful in 9s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 9s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
93e79d1170
Delete resolveStripeCustomerId() and replace with ensureStripeCustomer()
that includes orphaned Stripe customer cleanup on DB failure. Make
syncTierToUserProfile() blocking (errors propagate). Add null guards to
cancel/reactivate for admin-set subscriptions. Fix getInvoices() null
check. Clean controller comment. Add deleteCustomer() to StripeClient.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
egullickson merged commit 5f0da87110 into main 2026-02-16 15:44:12 +00:00
egullickson deleted branch issue-205-clean-subscription-admin-override 2026-02-16 15:44:12 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: egullickson/motovaultpro#218