feat: add subscriptions feature capsule - M1 database schema and Stripe client (refs #55)

- Create 4 new tables: subscriptions, subscription_events, donations, tier_vehicle_selections
- Add StripeClient wrapper with createCustomer, createSubscription, cancelSubscription,
  updatePaymentMethod, createPaymentIntent, constructWebhookEvent methods
- Implement SubscriptionsRepository with full CRUD and mapRow case conversion
- Add domain types for all subscription entities
- Install stripe npm package v20.2.0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-18 16:04:11 -06:00
parent 411a569788
commit 88b820b1c3
9 changed files with 1404 additions and 41 deletions

View File

@@ -0,0 +1,133 @@
/**
* @ai-summary Type definitions for subscriptions feature
* @ai-context Core business types for Stripe subscription management
*/
// Subscription tier types (matches DB enum)
export type SubscriptionTier = 'free' | 'pro' | 'enterprise';
// Subscription status types (matches DB enum)
export type SubscriptionStatus = 'active' | 'past_due' | 'canceled' | 'unpaid';
// Billing cycle types (matches DB enum)
export type BillingCycle = 'monthly' | 'yearly';
// Donation status types (matches DB enum)
export type DonationStatus = 'pending' | 'succeeded' | 'failed' | 'canceled';
// Main subscription entity
export interface Subscription {
id: string;
userId: string;
stripeCustomerId: string;
stripeSubscriptionId?: string;
tier: SubscriptionTier;
billingCycle?: BillingCycle;
status: SubscriptionStatus;
currentPeriodStart?: Date;
currentPeriodEnd?: Date;
gracePeriodEnd?: Date;
cancelAtPeriodEnd: boolean;
createdAt: Date;
updatedAt: Date;
}
// Subscription event entity (webhook event logging)
export interface SubscriptionEvent {
id: string;
subscriptionId: string;
stripeEventId: string;
eventType: string;
payload: Record<string, any>;
createdAt: Date;
}
// Donation entity (one-time payments)
export interface Donation {
id: string;
userId: string;
stripePaymentIntentId: string;
amountCents: number;
currency: string;
status: DonationStatus;
createdAt: Date;
updatedAt: Date;
}
// Tier vehicle selection entity (tracks which vehicles user selected to keep during downgrade)
export interface TierVehicleSelection {
id: string;
userId: string;
vehicleId: string;
selectedAt: Date;
}
// Request/Response types
export interface CreateSubscriptionRequest {
userId: string;
tier: SubscriptionTier;
billingCycle: BillingCycle;
paymentMethodId?: string;
}
export interface SubscriptionResponse {
id: string;
userId: string;
stripeCustomerId: string;
stripeSubscriptionId?: string;
tier: SubscriptionTier;
billingCycle?: BillingCycle;
status: SubscriptionStatus;
currentPeriodStart?: string;
currentPeriodEnd?: string;
gracePeriodEnd?: string;
cancelAtPeriodEnd: boolean;
createdAt: string;
updatedAt: string;
}
export interface DonationResponse {
id: string;
userId: string;
stripePaymentIntentId: string;
amountCents: number;
currency: string;
status: DonationStatus;
createdAt: string;
updatedAt: string;
}
export interface CreateDonationRequest {
userId: string;
amountCents: number;
currency?: string;
}
export interface CreateSubscriptionEventRequest {
subscriptionId: string;
stripeEventId: string;
eventType: string;
payload: Record<string, any>;
}
export interface CreateTierVehicleSelectionRequest {
userId: string;
vehicleId: string;
}
// Service layer types
export interface UpdateSubscriptionData {
stripeSubscriptionId?: string;
tier?: SubscriptionTier;
billingCycle?: BillingCycle;
status?: SubscriptionStatus;
currentPeriodStart?: Date;
currentPeriodEnd?: Date;
gracePeriodEnd?: Date;
cancelAtPeriodEnd?: boolean;
}
export interface UpdateDonationData {
status?: DonationStatus;
}