Docs

Create Checkout Session

Create a Stripe checkout session for initiating a subscription.

Create Checkout Session

Initiates a Stripe checkout session for subscribing to a plan.

Endpoint

POST /api/billing/checkout

Authentication

Required. Bearer token in Authorization header.

Request Body

FieldTypeRequiredDescription
priceIdstringYesStripe price ID for the plan
successUrlstringYesURL to redirect after successful checkout
cancelUrlstringYesURL to redirect if checkout is cancelled
metadataobjectNoAdditional metadata to attach to the session

Example Request

curl -X POST https://api.example.com/api/billing/checkout \
  -H "Authorization: Bearer <session_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "priceId": "price_1ABC123XYZ",
    "successUrl": "https://app.example.com/dashboard?checkout=success",
    "cancelUrl": "https://app.example.com/pricing?checkout=cancelled",
    "metadata": {
      "plan": "pro",
      "referralCode": "FRIEND20"
    }
  }'

TypeScript Example

const response = await fetch('/api/billing/checkout', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${sessionToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    priceId: 'price_1ABC123XYZ',
    successUrl: `${window.location.origin}/dashboard?checkout=success`,
    cancelUrl: `${window.location.origin}/pricing?checkout=cancelled`
  })
});

const { data } = await response.json();
// Redirect to Stripe checkout
window.location.href = data.url;

Response

Success Response (200)

{
  "success": true,
  "data": {
    "sessionId": "cs_test_1234567890abcdef",
    "url": "https://checkout.stripe.com/pay/cs_test_1234567890abcdef"
  }
}
FieldTypeDescription
sessionIdstringStripe checkout session ID
urlstringURL to redirect the customer to

Error Responses

Missing Price ID (400)

{
  "success": false,
  "error": {
    "code": "BAD_REQUEST",
    "message": "priceId is required"
  }
}

Invalid Price (400)

{
  "success": false,
  "error": {
    "code": "BAD_REQUEST",
    "message": "Invalid price ID"
  }
}

Unauthorized (401)

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication required"
  }
}

Flow Diagram

┌─────────────┐     POST /checkout      ┌─────────────┐
│   Client    │ ───────────────────────>│   Server    │
│             │                         │             │
│             │ <───────────────────────│             │
│             │    { sessionId, url }   │             │
└─────────────┘                         └─────────────┘

       │ Redirect to Stripe

┌─────────────┐
│   Stripe    │
│  Checkout   │
└─────────────┘

       │ Return to successUrl/cancelUrl

┌─────────────┐
│   Client    │
│  Dashboard  │
└─────────────┘

Notes

  • The checkout session is valid for 24 hours
  • Customers without a Stripe customer ID will have one created automatically
  • Metadata is passed through to the subscription for tracking
  • Webhook events handle the actual subscription creation after checkout completes

On this page