Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/revokslab/shipfree/llms.txt

Use this file to discover all available pages before exploring further.

The subscription endpoint allows authenticated users to retrieve their current subscription information, including plan details, status, and billing cycle.

Endpoint

GET /api/payments/subscription

Authentication

This endpoint requires authentication. The user must have an active session with a valid JWT token.

Request

No request body is required. Simply make a GET request to the endpoint with valid authentication cookies.

Example Request

const response = await fetch('/api/payments/subscription', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
  }
})

const data = await response.json()
console.log(data.subscription)
cURL
curl -X GET https://yourdomain.com/api/payments/subscription \
  -H "Content-Type: application/json" \
  -H "Cookie: better-auth.session_token=..."

Response

subscription
object | null
The user’s subscription details, or null if they don’t have an active subscription.

Success Response (200)

{
  "subscription": {
    "id": "sub_abc123",
    "userId": "user_xyz789",
    "customerId": "cus_def456",
    "provider": "stripe",
    "providerSubscriptionId": "sub_1234567890",
    "status": "active",
    "plan": "pro",
    "interval": "month",
    "amount": "29.90",
    "currency": "usd",
    "currentPeriodStart": "2024-01-01T00:00:00.000Z",
    "currentPeriodEnd": "2024-02-01T00:00:00.000Z",
    "cancelAtPeriodEnd": false,
    "canceledAt": null,
    "trialStart": null,
    "trialEnd": null,
    "createdAt": "2024-01-01T00:00:00.000Z",
    "updatedAt": "2024-01-01T00:00:00.000Z"
  }
}

No Subscription Response (200)

If the user doesn’t have a subscription, the response will be:
{
  "subscription": null
}

Error Responses

Returned when the user is not authenticated.
{
  "error": "Unauthorized"
}
Returned when an unexpected error occurs.
{
  "error": "Internal Server Error"
}

Implementation Details

The endpoint retrieves the most recently updated subscription for the authenticated user:
export async function GET(req: Request) {
  const session = await auth.api.getSession({
    headers: await headers(),
  })

  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const userSubscription = await db.query.subscription.findFirst({
    where: eq(subscription.userId, session.user.id),
    orderBy: (subscriptions, { desc }) => [desc(subscriptions.updatedAt)],
  })

  return NextResponse.json({ subscription: userSubscription || null })
}

Source Code Reference

The implementation can be found in src/app/api/payments/subscription/route.ts:9-32.

Usage Examples

React Hook

'use client'

import { useEffect, useState } from 'react'

interface Subscription {
  id: string
  plan: string
  status: string
  currentPeriodEnd: string
  // ... other fields
}

export function useSubscription() {
  const [subscription, setSubscription] = useState<Subscription | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    const fetchSubscription = async () => {
      try {
        const response = await fetch('/api/payments/subscription')
        const data = await response.json()
        setSubscription(data.subscription)
      } catch (error) {
        console.error('Failed to fetch subscription:', error)
      } finally {
        setLoading(false)
      }
    }

    fetchSubscription()
  }, [])

  return { subscription, loading }
}

Server Component

import { headers } from 'next/headers'
import { auth } from '@/lib/auth'
import { db } from '@/database'
import { eq } from 'drizzle-orm'
import { subscription } from '@/database/schema'

export default async function SubscriptionPage() {
  const session = await auth.api.getSession({
    headers: await headers(),
  })

  if (!session) {
    return <div>Please log in</div>
  }

  const userSubscription = await db.query.subscription.findFirst({
    where: eq(subscription.userId, session.user.id),
    orderBy: (subscriptions, { desc }) => [desc(subscriptions.updatedAt)],
  })

  if (!userSubscription) {
    return <div>No active subscription</div>
  }

  return (
    <div>
      <h1>Your Subscription</h1>
      <p>Plan: {userSubscription.plan}</p>
      <p>Status: {userSubscription.status}</p>
      <p>Renews: {new Date(userSubscription.currentPeriodEnd).toLocaleDateString()}</p>
    </div>
  )
}

Subscription Status Values

active

Subscription is active and in good standing

trialing

Subscription is in trial period

past_due

Payment failed, subscription at risk

canceled

Subscription has been canceled

incomplete

Initial payment failed or pending

Next Steps

Build docs developers (and LLMs) love