Subscription & Billing API

API for managing subscriptions and Stripe checkout sessions, enabling users to subscribe to paid plans.

Updated Nov 16, 2025
Edit on GitHub
subscriptions billing stripe checkout

Subscription & Billing

API for managing subscriptions and Stripe checkout sessions, enabling users to subscribe to paid plans.

Overview

The subscription API enables monetization of your services through Stripe integration. Key features include:

  • Stripe Checkout Sessions: Redirect users to Stripe-hosted checkout pages
  • Plan-based Subscriptions: Users can subscribe to plans with configured Stripe prices
  • Automatic Webhook Handling: Subscription lifecycle events are handled automatically via Stripe webhooks
  • Organization-based Billing: Stripe customers are created per organization

Prerequisites

Before using the billing API:

  1. Configure Stripe: Set STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET environment variables
  2. Create Stripe Products/Prices: Create products and prices in your Stripe dashboard
  3. Link Plans to Stripe: When creating or updating plans, provide the stripe_price_id
  4. Configure Webhook: Set up Stripe webhook to send events to /webhooks/stripe

Data Models

Checkout Request

{
  "plan_id": "uuid",
  "success_url": "https://app.example.com/billing/success?session_id={CHECKOUT_SESSION_ID}",
  "cancel_url": "https://app.example.com/billing/cancel"
}

Checkout Response

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_...",
  "session_id": "cs_test_a1b2c3d4..."
}

Plan with Stripe Integration

{
  "id": "uuid",
  "service_id": "uuid",
  "name": "Pro Plan",
  "price_cents": 2999,
  "currency": "usd",
  "features": ["feature1", "feature2"],
  "stripe_price_id": "price_1234567890",
  "created_at": "datetime"
}

Endpoints Summary

Method Path Description Permissions
POST /api/organizations/:org_slug/services/:service_slug/checkout Create Stripe checkout session Member

Create Checkout Session

POST /api/organizations/:org_slug/services/:service_slug/checkout

Creates a Stripe checkout session for the authenticated user to subscribe to a plan.

Permissions: Any organization member

Headers:

Header Value
Authorization Bearer {jwt}
Content-Type application/json

Path Parameters:

Parameter Type Description
org_slug string Organization slug
service_slug string Service slug

Request Body:

Field Type Required Description
plan_id string Yes The plan ID to subscribe to
success_url string Yes URL to redirect after successful checkout. Use {CHECKOUT_SESSION_ID} placeholder to include session ID
cancel_url string Yes URL to redirect if checkout is cancelled

Example Request:

curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/checkout \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "plan_id": "550e8400-e29b-41d4-a716-446655440000",
    "success_url": "https://app.acme.com/billing/success?session_id={CHECKOUT_SESSION_ID}",
    "cancel_url": "https://app.acme.com/billing/cancel"
  }'

Success Response (200 OK):

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4e5f6g7h8i9j0...",
  "session_id": "cs_test_a1b2c3d4e5f6g7h8i9j0"
}

Error Responses:

Status Description
400 Bad Request Plan not found, plan has no Stripe price, or plan doesn’t belong to service
401 Unauthorized Missing or invalid JWT token
403 Forbidden User is not a member of the organization
404 Not Found Organization or service not found
500 Internal Server Error Stripe API error or missing checkout URL

Example Error Response (400 Bad Request):

{
  "error": "This plan is not available for purchase (no Stripe price configured)"
}

SDK Usage

import { SSOClient } from '@drmhse/sso-sdk';

const sso = new SSOClient({ baseURL: 'https://sso.example.com' });

// Create a checkout session
const checkout = await sso.services.billing.createCheckout('acme-corp', 'main-app', {
  plan_id: 'plan_pro_id',
  success_url: 'https://app.acme.com/billing/success?session_id={CHECKOUT_SESSION_ID}',
  cancel_url: 'https://app.acme.com/billing/cancel'
});

// Redirect user to Stripe checkout
window.location.href = checkout.checkout_url;

Setting Up Plans with Stripe

Creating a Plan with Stripe Integration

When creating or updating a plan, provide the stripe_price_id from your Stripe dashboard:

curl -X POST https://sso.example.com/api/organizations/acme-corp/services/main-app/plans \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pro",
    "price_cents": 2999,
    "currency": "usd",
    "features": ["api-access", "analytics", "support"],
    "stripe_price_id": "price_1234567890abcdef"
  }'

Updating an Existing Plan with Stripe

curl -X PATCH https://sso.example.com/api/organizations/acme-corp/services/main-app/plans/plan_id \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "stripe_price_id": "price_1234567890abcdef"
  }'

Webhook Integration

Stripe webhooks are automatically processed to:

  1. Handle checkout completion: When a checkout session is completed, the subscription is tracked
  2. Create/update subscriptions: When a subscription is created or updated in Stripe
  3. Cancel subscriptions: When a subscription is deleted in Stripe

Configure your Stripe webhook to send events to:

POST /webhooks/stripe

Required events:

  • checkout.session.completed
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted

Flow Diagram

1. User selects plan in your app
2. Frontend calls POST /checkout with plan_id
3. Backend creates Stripe customer (if needed)
4. Backend creates Stripe checkout session
5. Backend returns checkout_url
6. Frontend redirects user to checkout_url
7. User completes payment on Stripe
8. Stripe redirects to success_url
9. Stripe sends webhook event
10. Backend updates subscription status

Important Notes

  • Organization Membership Required: User must be a member of the organization
  • Active Organization: Organization must be in active status (not suspended or pending)
  • Stripe Price Required: Plans must have a stripe_price_id configured to be purchasable
  • Automatic Customer Creation: Stripe customers are automatically created per organization
  • Metadata Tracking: Subscription metadata includes user_id, service_id, plan_id, and org_id

Security Considerations

  • All checkout requests require valid JWT authentication
  • Users can only create checkouts for organizations they belong to
  • Stripe webhook signatures are verified using STRIPE_WEBHOOK_SECRET
  • Plan ownership is verified to ensure plan belongs to the requested service