Skip to main content

User Endpoints

The user API provides endpoints for managing authenticated user preferences, subscription details, payment history, and profile location settings. All endpoints require session-based authentication.

Overview

EndpointMethodAuthDescription
/api/user/currencyGETPublicDetect user currency from headers
/api/user/currencyPUTUserUpdate currency preference
/api/user/paymentsGETUserGet payment history from Stripe
/api/user/plan-statusGETUserGet plan status with expiration info
/api/user/subscriptionGETUserGet subscription details
/api/user/profile/locationGETUserGet saved location settings
/api/user/profile/locationPATCHUserUpdate location settings

Currency Detection and Preferences

Detect Currency

GET /api/user/currency

Detects the user's currency based on HTTP headers from CDN/proxy providers. This endpoint uses graceful degradation -- it always returns 200 OK with a valid currency code, falling back to USD if detection fails. No authentication is required.

Query Parameters:

ParameterTypeDefaultDescription
providerstring"smart"Detection provider: "cloudflare", "vercel", "cloudfront", "fastly", "generic", "auto", "smart"

Success Response (200):

{
"currency": "EUR",
"country": "FR",
"detected": true
}
FieldTypeDescription
currencystringISO 4217 currency code (3 characters), defaults to "USD"
countrystring or nullISO 3166-1 alpha-2 country code, null if detection failed
detectedbooleanWhether detection succeeded or value is a fallback

When detection fails, the response still returns 200 with "USD" and detected: false.

Source: template/app/api/user/currency/route.ts

Update Currency Preference

PUT /api/user/currency

Updates the authenticated user's preferred currency and country. Validated using Zod with the SUPPORTED_CURRENCIES list from lib/config/billing.

Authentication: Required

Request Body:

{
"currency": "EUR",
"country": "FR"
}
FieldTypeRequiredDescription
currencystringYesISO 4217 currency code (exactly 3 characters, uppercase)
countrystring or nullNoISO 3166-1 alpha-2 country code (exactly 2 characters)

Success Response (200):

{
"currency": "EUR",
"country": "FR"
}
StatusCondition
400Invalid JSON, unsupported currency code, or invalid country format
401User not authenticated
500Failed to persist the update

Source: template/app/api/user/currency/route.ts

Payment History

Get Payment History

GET /api/user/payments

Retrieves the authenticated user's complete payment history from Stripe. Fetches invoices and subscriptions, enriches them with plan metadata, and returns a sorted list of payment records.

Authentication: Required

Success Response (200):

[
{
"id": "in_1234567890abcdef",
"date": "2024-01-15T10:30:00.000Z",
"amount": 29.99,
"currency": "USD",
"plan": "Premium Plan",
"planId": "pro",
"status": "Paid",
"billingInterval": "monthly",
"paymentProvider": "stripe",
"subscriptionId": "sub_1234567890abcdef",
"description": "Premium Plan - monthly billing",
"invoiceUrl": "https://invoice.stripe.com/i/acct_123/test_abc",
"invoicePdf": "https://pay.stripe.com/invoice/acct_123/test_abc/pdf",
"invoiceNumber": "INV-2024-001",
"period_end": "2024-02-15T10:30:00.000Z",
"period_start": "2024-01-15T10:30:00.000Z"
}
]

Key processing details:

  • Filters to only "paid" and "open" invoices
  • Converts amounts from cents to major currency units (divides by 100)
  • Sorts by date, newest first
  • Maps status to human-readable values: "Paid", "Pending", "Draft", "Unknown"
  • Returns an empty array [] if no Stripe customer exists

Source: template/app/api/user/payments/route.ts

Plan Status

Get Plan Status

GET /api/user/plan-status

Returns comprehensive plan status information including expiration details. Used by the frontend to display plan warnings and gate features behind plan checks.

Authentication: Required

Success Response (200):

{
"success": true,
"data": {
"planId": "premium",
"effectivePlan": "premium",
"isExpired": false,
"expiresAt": "2024-12-31T23:59:59.000Z",
"daysUntilExpiration": 45,
"isInWarningPeriod": false,
"canAccessPlanFeatures": true,
"warningMessage": null,
"status": "active"
}
}
FieldTypeDescription
planIdstringThe user's subscribed plan: "free", "standard", "premium"
effectivePlanstringThe plan the user can actually access (may differ if expired)
isExpiredbooleanWhether the subscription has expired
expiresAtstring or nullExpiration date in ISO format
daysUntilExpirationinteger or nullDays until expiration (negative if already expired)
isInWarningPeriodbooleanTrue if subscription expires within 7 days
canAccessPlanFeaturesbooleanWhether the user can access their plan's features
warningMessagestring or nullUser-facing warning message if applicable
statusstring or nullRaw subscription status

Uses subscriptionService.getUserPlanWithExpiration() from lib/services/subscription.service.

Source: template/app/api/user/plan-status/route.ts

Subscription Details

Get Subscription Status

GET /api/user/subscription

Retrieves detailed subscription information from Stripe including the current active subscription and complete subscription history.

Authentication: Required

Success Response (200) -- Active Subscription:

{
"hasActiveSubscription": true,
"currentSubscription": {
"id": "sub_1234567890abcdef",
"planId": "price_1234567890abcdef",
"planName": "Premium Plan",
"status": "active",
"startDate": "2024-01-15T10:30:00.000Z",
"endDate": "2024-02-15T10:30:00.000Z",
"nextBillingDate": "2024-02-15T10:30:00.000Z",
"paymentProvider": "stripe",
"subscriptionId": "sub_1234567890abcdef",
"amount": 29.99,
"currency": "USD",
"billingInterval": "monthly"
},
"subscriptionHistory": [
{
"id": "sub_1234567890abcdef",
"planId": "price_1234567890abcdef",
"planName": "Premium Plan",
"status": "active",
"startDate": "2024-01-15T10:30:00.000Z",
"endDate": "2024-02-15T10:30:00.000Z",
"amount": 29.99,
"currency": "USD",
"billingInterval": "monthly"
}
]
}

Active subscriptions are identified by status === "active" or status === "trialing". History entries may include cancelledAt and cancelReason for cancelled subscriptions.

Source: template/app/api/user/subscription/route.ts

Profile Location

Get Location Settings

GET /api/user/profile/location

Returns the authenticated user's saved default location and privacy preference.

Authentication: Required (client profile)

Success Response (200):

{
"defaultLatitude": 48.8566,
"defaultLongitude": 2.3522,
"defaultCity": "Paris",
"defaultCountry": "FR",
"locationPrivacy": "city"
}

Source: template/app/api/user/profile/location/route.ts

Update Location Settings

PATCH /api/user/profile/location

Updates the authenticated user's default location and privacy preference. Validated using the updateLocationSchema from lib/validations/user-location.

Request Body:

{
"defaultLatitude": 48.8566,
"defaultLongitude": 2.3522,
"defaultCity": "Paris",
"defaultCountry": "FR",
"locationPrivacy": "city"
}
FieldTypeRequiredDescription
defaultLatitudenumber or nullNoLatitude coordinate
defaultLongitudenumber or nullNoLongitude coordinate
defaultCitystring or nullNoCity name
defaultCountrystring or nullNoCountry code
locationPrivacystringNoPrivacy level: "private", "city", "exact"

Both latitude and longitude must be provided together.

Source: template/app/api/user/profile/location/route.ts