From af11b49e266cc8be98269a2e83fdb027781478b1 Mon Sep 17 00:00:00 2001 From: Eric Gullickson <16152721+ericgullickson@users.noreply.github.com> Date: Mon, 16 Feb 2026 09:28:46 -0600 Subject: [PATCH] refactor: add migration and nullable types for stripe_customer_id (refs #207) 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 --- .../subscriptions/domain/subscriptions.types.ts | 6 +++--- .../migrations/002_nullable_stripe_customer_id.sql | 11 +++++++++++ .../features/subscription/types/subscription.types.ts | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 backend/src/features/subscriptions/migrations/002_nullable_stripe_customer_id.sql diff --git a/backend/src/features/subscriptions/domain/subscriptions.types.ts b/backend/src/features/subscriptions/domain/subscriptions.types.ts index 7c34020..298fe9b 100644 --- a/backend/src/features/subscriptions/domain/subscriptions.types.ts +++ b/backend/src/features/subscriptions/domain/subscriptions.types.ts @@ -19,7 +19,7 @@ export type DonationStatus = 'pending' | 'succeeded' | 'failed' | 'canceled'; export interface Subscription { id: string; userId: string; - stripeCustomerId: string; + stripeCustomerId: string | null; stripeSubscriptionId?: string; tier: SubscriptionTier; billingCycle?: BillingCycle; @@ -74,7 +74,7 @@ export interface CreateSubscriptionRequest { export interface SubscriptionResponse { id: string; userId: string; - stripeCustomerId: string; + stripeCustomerId: string | null; stripeSubscriptionId?: string; tier: SubscriptionTier; billingCycle?: BillingCycle; @@ -118,7 +118,7 @@ export interface CreateTierVehicleSelectionRequest { // Service layer types export interface UpdateSubscriptionData { - stripeCustomerId?: string; + stripeCustomerId?: string | null; stripeSubscriptionId?: string; tier?: SubscriptionTier; billingCycle?: BillingCycle; diff --git a/backend/src/features/subscriptions/migrations/002_nullable_stripe_customer_id.sql b/backend/src/features/subscriptions/migrations/002_nullable_stripe_customer_id.sql new file mode 100644 index 0000000..86dd540 --- /dev/null +++ b/backend/src/features/subscriptions/migrations/002_nullable_stripe_customer_id.sql @@ -0,0 +1,11 @@ +-- Migration: Make stripe_customer_id NULLABLE +-- Removes the NOT NULL constraint that forced admin_override_ placeholder values. +-- Admin-set subscriptions (no Stripe billing) use NULL instead of sentinel strings. +-- PostgreSQL UNIQUE constraint allows multiple NULLs (SQL standard). + +-- Drop NOT NULL constraint on stripe_customer_id +ALTER TABLE subscriptions ALTER COLUMN stripe_customer_id DROP NOT NULL; + +-- Clean up existing admin_override_ placeholder values to NULL +UPDATE subscriptions SET stripe_customer_id = NULL +WHERE stripe_customer_id LIKE 'admin_override_%'; diff --git a/frontend/src/features/subscription/types/subscription.types.ts b/frontend/src/features/subscription/types/subscription.types.ts index f8a6ac4..045063e 100644 --- a/frontend/src/features/subscription/types/subscription.types.ts +++ b/frontend/src/features/subscription/types/subscription.types.ts @@ -5,7 +5,7 @@ export type SubscriptionStatus = 'active' | 'past_due' | 'canceled' | 'unpaid'; export interface Subscription { id: string; userId: string; - stripeCustomerId: string; + stripeCustomerId: string | null; stripeSubscriptionId?: string; tier: SubscriptionTier; billingCycle?: BillingCycle;