Subscription Management Endpoints Index

Stripe Subscription Migration

The authentication system includes automatic Stripe subscription migration to seamlessly transition users from legacy applications while preserving their existing subscriptions, billing cycles, and pricing.

How Migration Works

  1. Automatic Detection: During login or registration, the system checks if the user has a local subscription record
  2. Stripe Search: If no local subscription exists, the system searches Stripe for customers matching the user's email address
  3. Background Processing: Migration runs asynchronously to avoid blocking the authentication response
  4. Data Preservation: Successfully migrated subscriptions maintain their exact:
    • Billing cycle dates (current period start/end)
    • Trial period information (if applicable)
    • Pricing and plan details
    • Subscription status (active, trialing, past_due, etc.)
    • Cancellation settings (cancel_at_period_end flag)
  5. Local Record Creation: The Stripe subscription is imported into the local database for faster access
  6. Ongoing Sync: Future logins automatically sync subscription data with Stripe to ensure accuracy

When Migration Occurs

Automatic migration attempts happen during the following authentication flows:

Migration Behavior

Scenario Behavior User Experience
User has local subscription No migration attempted; existing record used Immediate access with existing subscription
User has Stripe subscription Background migration imports subscription Can log in immediately; subscription appears after migration
User has no subscription Migration finds nothing; no error Can log in and subscribe normally
Migration error occurs Error logged; authentication succeeds Can log in; migration retried on next login

Edge Cases Handled

Non-Blocking Design

Migration is designed to never block user authentication:

Technical Implementation

The migration process follows these steps:

1. Check if user has local subscription (migration_status field)
2. If not, search Stripe for customer by email
3. If customer found, retrieve all subscriptions
4. Filter for active subscriptions (active, trialing, past_due)
5. Select most recent subscription if multiple exist
6. Extract price ID and sync price/plan to database
7. Create local subscription record with all metadata
8. Mark migration as complete (migration_status='migrated')
9. Log success/failure to audit table
10. Continue with authentication flow

Migration Tracking

Subscription records include migration tracking fields:

For Developers

When implementing client applications:

Subscription Endpoints

GET /api/subscriptions

List all subscriptions with filtering and pagination (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Query Parameters:

Example Requests:

GET /api/subscriptions?page=1&perPage=10&sort=["createdAt","DESC"]&filter={"status":"active"}
GET /api/subscriptions?page=2&perPage=20&filter={"q":"pro","status":"active"}
GET /api/subscriptions?filter={"planName":"Premium","interval":"month"}
GET /api/subscriptions?filter={"currency":"usd","amount":999}
GET /api/subscriptions?filter={"email":"gmail.com","isActive":true}

Response (200 OK):

{
    "data": [
        {
            "id": 1,
            "userId": 1,
            "email": "user@example.com",
            "username": "user123",
            "plan": {
                "id": 1,
                "stripePriceId": "price_...",
                "name": "Pro Plan",
                "interval": "month",
                "amount": 999,
                "currency": "usd",
                "trialPeriodDays": 14,
                "isActive": true,
                "createdAt": "2023-01-15T08:30:00Z",
                "updatedAt": null
            },
            "status": "active",
            "currentPeriodStart": "2023-06-01T00:00:00Z",
            "currentPeriodEnd": "2023-07-01T00:00:00Z",
            "trialStart": "2023-06-01T00:00:00Z",
            "trialEnd": "2023-06-15T00:00:00Z",
            "cancelAtPeriodEnd": false,
            "canceledAt": null,
            "createdAt": "2023-06-01T00:00:00Z",
            "updatedAt": null,
            "promotion": {
                "id": 5,
                "name": "Summer Sale 2023",
                "coupon": {
                    "id": 3,
                    "type": "DISC",
                    "name": "Discount Percentage",
                    "percentOff": 20,
                    "trialDays": null
                }
            },
            "discount": {
                "percentOff": 20,
                "amountOff": 200,
                "discountedAmount": 799
            }
        }
        // Additional subscription objects...
    ],
    "total": 42
}

Notes:

GET /api/subscriptions/me

Get the current user's subscription (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Response (200 OK):

{
    "id": 1,
    "userId": 1,
    "email": "user@example.com",
    "username": "user123",
    "plan": {
        "id": 1,
        "stripePriceId": "price_...",
        "name": "Pro Plan",
        "interval": "month",
        "amount": 999,
        "currency": "usd",
        "trialPeriodDays": 14,
        "isActive": true,
        "createdAt": "2023-01-15T08:30:00Z",
        "updatedAt": null
    },
    "status": "active",
    "currentPeriodStart": "2023-06-01T00:00:00Z",
    "currentPeriodEnd": "2023-07-01T00:00:00Z",
    "trialStart": "2023-06-01T00:00:00Z",
    "trialEnd": "2023-06-15T00:00:00Z",
    "cancelAtPeriodEnd": false,
    "canceledAt": null,
    "createdAt": "2023-06-01T00:00:00Z",
    "updatedAt": null,
    "promotion": {
        "id": 5,
        "name": "Summer Sale 2023",
        "coupon": {
            "id": 3,
            "type": "DISC",
            "name": "Discount Percentage",
            "percentOff": 20,
            "trialDays": null
        }
    },
    "discount": {
        "percentOff": 20,
        "amountOff": 200,
        "discountedAmount": 799
    }
}

Notes:

GET /api/subscriptions/plans

List all available subscription plans (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Query Parameters:

Example Requests:

GET /api/subscriptions/plans?page=1&perPage=10&sort=["amount","ASC"]&filter={"isActive":true}&syncWithStripe=true
GET /api/subscriptions/plans?page=2&perPage=20&filter={"q":"pro","interval":"month"}
GET /api/subscriptions/plans?filter={"currency":"usd","amount":999}
GET /api/subscriptions/plans?filter={"trialPeriodDays":14,"isActive":true}

Response (200 OK):

{
    "data": [
        {
            "id": 1,
            "stripePriceId": "price_...",
            "name": "Basic Plan",
            "interval": "month",
            "amount": 499,
            "currency": "usd",
            "trialPeriodDays": 14,
            "isActive": true,
            "createdAt": "2023-01-15T08:30:00Z",
            "updatedAt": null
        },
        {
            "id": 2,
            "stripePriceId": "price_...",
            "name": "Pro Plan",
            "interval": "month",
            "amount": 999,
            "currency": "usd",
            "trialPeriodDays": 14,
            "isActive": true,
            "createdAt": "2023-01-15T08:30:00Z",
            "updatedAt": null
        }
        // Additional plan objects...
    ],
    "total": 5
}

Notes:

GET /api/subscriptions/verify-payment

Verify payment status for a subscription using payment intent ID (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Query Parameters:

Response (200 OK):

{
    "id": 1,
    "userId": 1,
    "email": "user@example.com",
    "username": "user123",
    "plan": {
        "id": 1,
        "stripePriceId": "price_...",
        "name": "Pro Plan",
        "interval": "month",
        "amount": 999,
        "currency": "usd",
        "trialPeriodDays": 14,
        "isActive": true,
        "createdAt": "2023-01-15T08:30:00Z",
        "updatedAt": null
    },
    "status": "active",
    "currentPeriodStart": "2023-06-01T00:00:00Z",
    "currentPeriodEnd": "2023-07-01T00:00:00Z",
    "trialStart": null,
    "trialEnd": null,
    "cancelAtPeriodEnd": false,
    "canceledAt": null,
    "createdAt": "2023-06-01T00:00:00Z",
    "updatedAt": "2023-06-15T00:00:00Z",
    "promotion": null
}

Error Responses:

{
    "message": "Payment intent ID is required"
}

{
    "message": "Subscription not found or could not be updated"
}
GET /api/subscriptions/verify-setup

Verify setup intent status for a subscription (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Query Parameters:

Response (200 OK):

{
    "id": 1,
    "userId": 1,
    "email": "user@example.com",
    "username": "user123",
    "plan": {
        "id": 1,
        "stripePriceId": "price_...",
        "name": "Pro Plan",
        "interval": "month",
        "amount": 999,
        "currency": "usd",
        "trialPeriodDays": 14,
        "isActive": true,
        "createdAt": "2023-01-15T08:30:00Z",
        "updatedAt": null
    },
    "status": "active",
    "currentPeriodStart": "2023-06-01T00:00:00Z",
    "currentPeriodEnd": "2023-07-01T00:00:00Z",
    "trialStart": null,
    "trialEnd": null,
    "cancelAtPeriodEnd": false,
    "canceledAt": null,
    "createdAt": "2023-06-01T00:00:00Z",
    "updatedAt": "2023-06-15T00:00:00Z",
    "promotion": null
}

Error Responses:

{
    "message": "Setup intent ID is required"
}

{
    "message": "Subscription not found or could not be updated"
}
POST /api/subscriptions/change-plan

Change the user's subscription to a different plan (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Request Body:

{
    "planId": 2,
    "prorationBehavior": "create_prorations"  // Optional: "create_prorations", "none", or "always_invoice"
}

Response (200 OK):

{
    "message": "Subscription plan changed successfully",
    "subscription": {
        "id": 1,
        "userId": 1,
        "email": "user@example.com",
        "username": "user123",
        "plan": {
            "id": 2,
            "stripePriceId": "price_...",
            "name": "Premium Plan",
            "interval": "month",
            "amount": 1999,
            "currency": "usd",
            "trialPeriodDays": 0,
            "isActive": true,
            "createdAt": "2023-01-15T08:30:00Z",
            "updatedAt": null
        },
        "status": "active",
        "currentPeriodStart": "2023-06-01T00:00:00Z",
        "currentPeriodEnd": "2023-07-01T00:00:00Z",
        "trialStart": null,
        "trialEnd": null,
        "cancelAtPeriodEnd": false,
        "canceledAt": null,
        "createdAt": "2023-06-01T00:00:00Z",
        "updatedAt": "2023-06-15T00:00:00Z",
        "promotion": null
    }
}

Error Responses:

{
    "message": "Plan ID is required"
}

{
    "message": "You don't have an active subscription"
}

{
    "message": "You are already subscribed to this plan"
}

{
    "message": "The selected plan is not available"
}
POST /api/subscriptions/change-plan-with-payment-method

Change the user's subscription to a different plan while updating payment method (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Request Body:

{
    "planId": 2,
    "paymentMethodId": "pm_1234567890",
    "prorationBehavior": "create_prorations"  // Optional: "create_prorations", "none", or "always_invoice"
}

Response (200 OK):

{
    "requiresAction": true,
    "setupIntentClientSecret": "seti_1RJYc0PFpKHu6YD8quMd7VH8_secret_...",
    "setupIntentId": "seti_1RJYc0PFpKHu6YD8quMd7VH8",
    "planId": 2,
    "prorationBehavior": "create_prorations"
}

Error Responses:

{
    "message": "Plan ID is required"
}

{
    "message": "Payment method ID is required"
}

{
    "message": "You don't have an active subscription"
}

{
    "message": "There was a problem setting up the payment method"
}
POST /api/subscriptions/confirm-change-plan

Confirm plan change after setup intent is completed (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Request Body:

{
    "setupIntentId": "seti_1RJYc0PFpKHu6YD8quMd7VH8",
    "planId": 2,
    "prorationBehavior": "create_prorations"  // Optional: "create_prorations", "none", or "always_invoice"
}

Response (200 OK):

{
    "message": "Subscription plan and payment method updated successfully",
    "subscription": {
        "id": 1,
        "userId": 1,
        "email": "user@example.com",
        "username": "user123",
        "plan": {
            "id": 2,
            "stripePriceId": "price_...",
            "name": "Premium Plan",
            "interval": "month",
            "amount": 1999,
            "currency": "usd",
            "trialPeriodDays": 0,
            "isActive": true,
            "createdAt": "2023-01-15T08:30:00Z",
            "updatedAt": null
        },
        "status": "active",
        "currentPeriodStart": "2023-06-01T00:00:00Z",
        "currentPeriodEnd": "2023-07-01T00:00:00Z",
        "trialStart": null,
        "trialEnd": null,
        "cancelAtPeriodEnd": false,
        "canceledAt": null,
        "createdAt": "2023-06-01T00:00:00Z",
        "updatedAt": "2023-06-15T00:00:00Z",
        "promotion": null
    }
}

Error Responses:

{
    "message": "Setup intent ID is required"
}

{
    "message": "Plan ID is required"
}

{
    "message": "Setup intent is not complete. Current status: requires_payment_method"
}

{
    "message": "Failed to attach payment method"
}
POST /api/subscriptions/create-free-trial

Create a free trial subscription without requiring payment method (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Request Body:

{
    "priceId": "price_H5ggYwtDq4fbrJ",
    "trialPeriodDays": 14  // Optional: Defaults to 14 days if not specified
}

Response (200 OK):

{
    "message": "Free trial subscription created successfully",
    "subscription": {
        "id": 1,
        "userId": 1,
        "email": "user@example.com",
        "username": "user123",
        "plan": {
            "id": 1,
            "stripePriceId": "price_H5ggYwtDq4fbrJ",
            "name": "Pro Plan",
            "interval": "month",
            "amount": 999,
            "currency": "usd",
            "trialPeriodDays": 14,
            "isActive": true,
            "createdAt": "2023-01-15T08:30:00Z",
            "updatedAt": null
        },
        "status": "trialing",
        "currentPeriodStart": "2023-06-01T00:00:00Z",
        "currentPeriodEnd": "2023-07-01T00:00:00Z",
        "trialStart": "2023-06-01T00:00:00Z",
        "trialEnd": "2023-06-15T00:00:00Z",
        "cancelAtPeriodEnd": false,
        "canceledAt": null,
        "createdAt": "2023-06-01T00:00:00Z",
        "updatedAt": null,
        "promotion": null
    },
    "trialEnd": "2023-06-15T00:00:00Z"
}

Error Responses:

{
    "message": "User already has an active subscription"
}

{
    "message": "Price ID is required"
}

{
    "message": "User not found"
}

{
    "message": "Subscription plan not found"
}
POST /api/subscriptions/create-with-payment-method

Create a subscription with payment method ID (deferred payment flow) (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Request Body:

{
    "priceId": "price_H5ggYwtDq4fbrJ",
    "paymentMethodId": "pm_1234567890",
    "trialPeriodDays": 14,  // Optional: Number of trial days
    "promoCode": "SUMMER20",  // Optional: Promotion code to apply (provide either promoCode or promotionId, not both)
    "promotionId": 5  // Optional: Promotion ID (database ID) to apply (provide either promoCode or promotionId, not both)
}

Response (200 OK):

{
    "subscriptionId": "sub_1RJYc0PFpKHu6YD8quMd7VH8",
    "type": "payment",
    "clientSecret": "cs_test_...",
    "promotionApplied": true
}

Alternative Response (Setup Flow):

{
    "subscriptionId": "sub_1RJYc0PFpKHu6YD8quMd7VH8",
    "type": "setup",
    "clientSecret": "seti_1RJYc0PFpKHu6YD8quMd7VH8_secret_...",
    "promotionApplied": true
}

Error Responses:

{
    "message": "Price ID is required"
}

{
    "message": "Payment method ID is required"
}

{
    "message": "Please provide either promoCode or promotionId, not both"
}

{
    "message": "Invalid promotion code"
}

{
    "message": "Promotion with ID 5 not found"
}

{
    "message": "Promotion code \"SUMMER20\" not found in Stripe"
}

{
    "message": "This promotion cannot be applied to subscriptions"
}

{
    "message": "User already has an active subscription"
}

{
    "message": "Subscription plan not found"
}

Notes:

POST /api/subscriptions/migrate-and-sync

Trigger bulk migration and synchronization of Stripe subscriptions for SUBS users (requires authentication and admin privileges). This endpoint searches Stripe for subscriptions associated with user email addresses and imports them into the local database, preserving all billing cycles, pricing, and trial information. It can also re-sync existing subscriptions to ensure data consistency between Stripe and the local database. This is particularly useful for:

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...
x-csrf-token: a8d7f9c6e5b4a3c2d1e0f9a8d7f6c5b4a3

Request Body:

{
    "batchSize": 50,         // Optional: Number of users to process (default: 50)
    "dryRun": false,         // Optional: If true, returns users to migrate without processing (default: false)
    "resync": false,         // Optional: If true, re-syncs ALL SUBS users including those with subscriptions (default: false)
    "forceResync": false,    // Optional: If true, includes users synced within the last hour (default: false)
    "activeUsersOnly": true  // Optional: If true, only migrates users who have logged in at least once (default: true)
}

Response (200 OK) - Dry Run:

{
    "message": "Dry run completed - no migrations performed",
    "usersToMigrate": 42,
    "users": [
        {
            "id": 123,
            "email": "user@example.com",
            "hasSubscription": false
        },
        {
            "id": 456,
            "email": "existing@example.com",
            "hasSubscription": true
        }
        // Additional users...
    ],
    "note": "Excluding users synced within the last hour",
    "activeUsersOnly": true
}

Response (200 OK) - Actual Migration:

{
    "message": "Bulk migration completed",
    "results": {
        "total": 50,
        "successful": 30,    // New migrations
        "failed": 5,
        "skipped": 10,
        "resynced": 5        // Existing subscriptions that were re-synced
    },
    "errors": [
        {
            "userId": 456,
            "email": "failed@example.com",
            "error": "Failed to sync price: Price not found in Stripe"
        }
        // Additional errors if any...
    ]
}

Response (200 OK) - Re-sync Mode:

{
    "message": "Bulk re-sync completed",
    "results": {
        "total": 50,
        "successful": 15,    // New migrations found
        "failed": 2,
        "skipped": 8,        // No Stripe subscriptions found
        "resynced": 25       // Existing subscriptions updated with latest Stripe data
    }
}

Error Responses:

{
    "message": "Access denied. Admin privileges required."
}

Notes:

POST /api/subscriptions/resume

Resume a subscription that was previously scheduled for cancellation (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Response (200 OK):

{
    "id": 1,
    "userId": 1,
    "email": "user@example.com",
    "username": "user123",
    "plan": {
        "id": 1,
        "stripePriceId": "price_...",
        "name": "Pro Plan",
        "interval": "month",
        "amount": 999,
        "currency": "usd",
        "trialPeriodDays": 14,
        "isActive": true,
        "createdAt": "2023-01-15T08:30:00Z",
        "updatedAt": null
    },
    "status": "active",
    "currentPeriodStart": "2023-06-01T00:00:00Z",
    "currentPeriodEnd": "2023-07-01T00:00:00Z",
    "trialStart": null,
    "trialEnd": null,
    "cancelAtPeriodEnd": false,
    "canceledAt": null,
    "createdAt": "2023-06-01T00:00:00Z",
    "updatedAt": "2023-06-15T00:00:00Z",
    "promotion": null
}

Error Responses:

{
    "message": "Subscription not found or could not be resumed"
}

{
    "message": "Failed to resume subscription",
    "error": "Cannot resume subscription because it is already fully canceled"
}
POST /api/subscriptions/sync-plans

Sync subscription plans with Stripe (requires authentication and admin privileges). This endpoint synchronizes subscription plans from Stripe into the local database, ensuring pricing and plan information is up-to-date.

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...
x-csrf-token: a8d7f9c6e5b4a3c2d1e0f9a8d7f6c5b4a3

Request Body:

{
    "dryRun": false  // Optional: If true, returns plans to sync without processing (default: false)
}

Response (200 OK) - Dry Run:

{
    "message": "Dry run completed - no plans were synced",
    "plansToSync": 8,
    "details": {
        "existingPlans": [
            {
                "id": 1,
                "stripePriceId": "price_...",
                "name": "Basic Plan",
                "amount": 499,
                "currency": "usd",
                "interval": "month",
                "isActive": true
            }
            // Additional existing plans...
        ],
        "stripePrices": [
            {
                "id": "price_...",
                "nickname": "Basic Monthly",
                "amount": 499,
                "currency": "usd",
                "interval": "month",
                "active": true
            }
            // Additional Stripe prices...
        ],
        "analysis": {
            "totalExistingPlans": 5,
            "totalStripePrices": 8,
            "plansToUpdate": 2,
            "plansToAdd": 3,
            "plansToDeactivate": 0
        }
    }
}

Response (200 OK) - Actual Sync:

{
    "message": "Successfully synchronized 8 subscription plans with Stripe",
    "syncedPlans": 8,
    "results": {
        "synced": 5,
        "added": 3,
        "deactivated": 0
    }
}

Error Responses:

{
    "message": "Access denied. Admin privileges required."
}

Notes:

POST /api/subscriptions/update-payment-method

Update the payment method for a user's subscription (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Request Body:

{
    "paymentMethodId": "pm_1234567890"
}

Response (200 OK):

{
    "success": true,
    "message": "Payment method updated successfully",
    "subscription": {
        "id": 1,
        "userId": 1,
        "email": "user@example.com",
        "username": "user123",
        "plan": {
            "id": 1,
            "stripePriceId": "price_...",
            "name": "Pro Plan",
            "interval": "month",
            "amount": 999,
            "currency": "usd",
            "trialPeriodDays": 14,
            "isActive": true,
            "createdAt": "2023-01-15T08:30:00Z",
            "updatedAt": null
        },
        "status": "active",
        "currentPeriodStart": "2023-06-01T00:00:00Z",
        "currentPeriodEnd": "2023-07-01T00:00:00Z",
        "trialStart": null,
        "trialEnd": null,
        "cancelAtPeriodEnd": false,
        "canceledAt": null,
        "createdAt": "2023-06-01T00:00:00Z",
        "updatedAt": "2023-06-15T00:00:00Z",
        "promotion": null
    }
}

Error Responses:

{
    "success": false,
    "message": "Payment method ID is required"
}

{
    "success": false,
    "message": "Failed to update payment method",
    "error": {
        "type": "card_error",
        "message": "Your card was declined.",
        "code": "card_declined",
        "decline_code": "generic_decline"
    }
}

Notes:

PUT /api/subscriptions/me

Update the current user's subscription (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...
x-csrf-token: a8d7f9c6e5b4a3c2d1e0f9a8d7f6c5b4a3

Request Body:

{
    "planId": 2,
    "paymentMethodId": "pm_1234567890"  // Optional: New payment method
}

Response (200 OK):

{
    "id": 1,
    "userId": 1,
    "email": "user@example.com",
    "username": "user123",
    "plan": {
        "id": 2,
        "stripePriceId": "price_...",
        "name": "Premium Plan",
        "interval": "month",
        "amount": 1999,
        "currency": "usd",
        "trialPeriodDays": 0,
        "isActive": true,
        "createdAt": "2023-01-15T08:30:00Z",
        "updatedAt": null
    },
    "status": "active",
    "currentPeriodStart": "2023-06-01T00:00:00Z",
    "currentPeriodEnd": "2023-07-01T00:00:00Z",
    "trialStart": null,
    "trialEnd": null,
    "cancelAtPeriodEnd": false,
    "canceledAt": null,
    "createdAt": "2023-06-01T00:00:00Z",
    "updatedAt": "2023-06-15T00:00:00Z",
    "promotion": null
}
DELETE /api/subscriptions/me

Cancel the current user's subscription (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Query Parameters:

Response (200 OK):

{
    "id": 1,
    "userId": 1,
    "email": "user@example.com",
    "username": "user123",
    "plan": {
        "id": 1,
        "stripePriceId": "price_...",
        "name": "Pro Plan",
        "interval": "month",
        "amount": 999,
        "currency": "usd",
        "trialPeriodDays": 14,
        "isActive": true,
        "createdAt": "2023-01-15T08:30:00Z",
        "updatedAt": null
    },
    "status": "active",
    "currentPeriodStart": "2023-06-01T00:00:00Z",
    "currentPeriodEnd": "2023-07-01T00:00:00Z",
    "trialStart": null,
    "trialEnd": null,
    "cancelAtPeriodEnd": true,
    "canceledAt": "2023-06-15T00:00:00Z",
    "createdAt": "2023-06-01T00:00:00Z",
    "updatedAt": "2023-06-15T00:00:00Z",
    "promotion": null
}