Skip to content
arminrad edited this page Mar 16, 2026 · 2 revisions

Subscription Plans

Flexible subscription tiers with Stripe integration


Overview

Multi-tier subscription system offering:

  • Free Tier - Basic access with limited credits
  • Starter - $20/month with 2000 credits
  • Pro - $50/month with 5000 credits
  • Enterprise - Custom pricing and features

Payment Provider: Stripe Database Tables: subscription_products, users


Quick Setup

1. Configure Stripe

# Add to .env
STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key
STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret

2. Create Products in Stripe

  1. Log in to Stripe Dashboard

  2. Navigate to Products

  3. Create subscription products:

    • Starter: $20/month
    • Pro: $50/month
    • Enterprise: Custom
  4. Copy Price IDs and add to database

3. Seed Database

INSERT INTO subscription_products (
  stripe_price_id,
  name,
  price,
  credits,
  billing_cycle
) VALUES
('price_starter_monthly', 'Starter', 20.00, 2000, 'monthly'),
('price_pro_monthly', 'Pro', 50.00, 5000, 'monthly'),
('price_enterprise_monthly', 'Enterprise', 200.00, 25000, 'monthly');

Plan Tiers

Free Tier

Price: $0/month

Features:

  • $10 free trial credits
  • Basic API access
  • Community support
  • 1000 requests per month
  • Standard rate limits

Best For: Testing and small projects

Starter Plan

Price: $20/month

Features:

  • 2000 credits per month ($20 value)
  • All AI models
  • Email support
  • 10,000 requests per month
  • Higher rate limits

Best For: Individual developers and small teams

Pro Plan

Price: $50/month

Features:

  • 5000 credits per month ($50 value)
  • Priority support
  • Advanced analytics
  • 50,000 requests per month
  • Highest rate limits
  • Custom rate limits

Best For: Production applications and growing teams

Enterprise Plan

Price: Custom

Features:

  • Custom credit allocation
  • Dedicated support
  • SLA guarantees
  • Custom integrations
  • Unlimited requests
  • White-label options
  • Custom contracts

Best For: Large organizations with specific needs


API Endpoints

Get Available Plans

GET /plans

Response:

{
  "plans": [
    {
      "id": 1,
      "name": "Starter",
      "price": 20.00,
      "credits": 2000,
      "billing_cycle": "monthly",
      "features": [
        "2000 credits per month",
        "All AI models",
        "Email support"
      ]
    },
    {
      "id": 2,
      "name": "Pro",
      "price": 50.00,
      "credits": 5000,
      "billing_cycle": "monthly",
      "features": [
        "5000 credits per month",
        "Priority support",
        "Advanced analytics"
      ]
    }
  ]
}

Subscribe to Plan

POST /subscribe

Request:

{
  "plan_id": 1,
  "billing_cycle": "monthly"
}

Response:

{
  "checkout_url": "https://checkout.stripe.com/session_abc123",
  "session_id": "cs_test_abc123"
}

Get Current Subscription

GET /user/subscription

Response:

{
  "plan": "Pro",
  "status": "active",
  "credits_remaining": 3500,
  "credits_total": 5000,
  "renews_at": "2025-01-15T00:00:00Z",
  "billing_cycle": "monthly"
}

Cancel Subscription

POST /user/subscription/cancel

Response:

{
  "status": "canceled",
  "canceled_at": "2024-12-15T10:30:00Z",
  "effective_until": "2025-01-15T00:00:00Z"
}

Usage Examples

Subscribe to Plan (Python)

import requests

url = "https://api.gatewayz.com/subscribe"
headers = {"Authorization": "Bearer YOUR_API_KEY"}

payload = {
    "plan_id": 1,  # Starter plan
    "billing_cycle": "monthly"
}

response = requests.post(url, headers=headers, json=payload)
checkout_url = response.json()["checkout_url"]

# Redirect user to checkout_url
print(f"Complete payment at: {checkout_url}")

Check Subscription Status

url = "https://api.gatewayz.com/user/subscription"
headers = {"Authorization": "Bearer YOUR_API_KEY"}

response = requests.get(url, headers=headers)
subscription = response.json()

print(f"Plan: {subscription['plan']}")
print(f"Credits remaining: {subscription['credits_remaining']}")

Database Schema

subscription_products Table

CREATE TABLE subscription_products (
  id SERIAL PRIMARY KEY,
  stripe_price_id TEXT UNIQUE NOT NULL,
  name TEXT NOT NULL,
  price DECIMAL(10,2) NOT NULL,
  credits INTEGER NOT NULL,
  billing_cycle TEXT NOT NULL,  -- monthly, yearly
  features JSONB,
  active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW()
);

users Table (Subscription Fields)

-- Subscription-related fields in users table
subscription_status TEXT,  -- trial, active, canceled, expired
subscription_plan_id INTEGER REFERENCES subscription_products(id),
subscription_started_at TIMESTAMP,
subscription_renews_at TIMESTAMP,
stripe_customer_id TEXT,
stripe_subscription_id TEXT

Stripe Integration

Create Checkout Session

import stripe

stripe.api_key = os.getenv("STRIPE_SECRET_KEY")

session = stripe.checkout.Session.create(
    customer=stripe_customer_id,
    payment_method_types=["card"],
    line_items=[{
        "price": stripe_price_id,
        "quantity": 1
    }],
    mode="subscription",
    success_url="https://yourapp.com/success?session_id={CHECKOUT_SESSION_ID}",
    cancel_url="https://yourapp.com/canceled",
    metadata={
        "user_id": user.id,
        "plan_id": plan.id
    }
)

Handle Webhook Events

@app.post("/webhooks/stripe")
async def stripe_webhook(request: Request):
    payload = await request.body()
    sig_header = request.headers.get("stripe-signature")

    event = stripe.Webhook.construct_event(
        payload, sig_header, webhook_secret
    )

    if event.type == "checkout.session.completed":
        session = event.data.object
        await handle_subscription_created(session)

    elif event.type == "invoice.payment_succeeded":
        invoice = event.data.object
        await handle_subscription_renewed(invoice)

    elif event.type == "customer.subscription.deleted":
        subscription = event.data.object
        await handle_subscription_canceled(subscription)

    return {"status": "success"}

Subscription Lifecycle

1. Trial Period

  • User signs up
  • Automatically gets free tier
  • $10 trial credits added
  • 3-day trial period

2. Subscription Activation

  • User subscribes to paid plan
  • Trial status changes to "active"
  • Credits allocated based on plan
  • Billing cycle starts

3. Monthly Renewal

  • Stripe charges card automatically
  • Credits reset to plan allocation
  • Usage resets
  • Subscription extends for another month

4. Subscription Cancellation

  • User cancels subscription
  • Access continues until end of billing period
  • No automatic renewal
  • Status changes to "canceled"

5. Subscription Expiry

  • Billing period ends
  • Credits set to 0
  • Status changes to "expired"
  • User moves to free tier

Credit Management

Credit Allocation

def allocate_credits(user_id, plan_id):
    plan = db.table("subscription_products").select("credits").eq("id", plan_id).single()
    db.table("users").update({"credits": plan["credits"]}).eq("id", user_id).execute()

Credit Reset on Renewal

def reset_monthly_credits(user_id):
    user = get_user(user_id)
    plan = get_plan(user.subscription_plan_id)

    db.table("users").update({
        "credits": plan.credits,
        "subscription_renews_at": next_month()
    }).eq("id", user_id).execute()

Overage Handling

def handle_credit_overage(user_id):
    user = get_user(user_id)

    if user.credits < 0:
        # Option 1: Block API access
        block_api_access(user_id)

        # Option 2: Allow overage with notification
        send_overage_notification(user_id, abs(user.credits))

Pricing Comparison

Feature Free Starter Pro Enterprise
Price $0 $20/mo $50/mo Custom
Credits $10 trial 2000 5000 Custom
Requests/mo 1,000 10,000 50,000 Unlimited
Support Community Email Priority Dedicated
Analytics Basic Standard Advanced Custom
Rate Limits Standard Higher Highest Custom
SLA None None 99.9% Custom

Upgrade/Downgrade

Upgrade to Higher Tier

def upgrade_subscription(user_id, new_plan_id):
    # Calculate prorated amount
    prorated_amount = calculate_proration(user_id, new_plan_id)

    # Update Stripe subscription
    stripe.Subscription.modify(
        subscription_id,
        items=[{"price": new_price_id}],
        proration_behavior="always_invoice"
    )

    # Update database
    db.table("users").update({
        "subscription_plan_id": new_plan_id,
        "credits": new_plan.credits  # Immediate credit boost
    }).eq("id", user_id).execute()

Downgrade to Lower Tier

def downgrade_subscription(user_id, new_plan_id):
    # Schedule downgrade at period end
    stripe.Subscription.modify(
        subscription_id,
        items=[{"price": new_price_id}],
        proration_behavior="none",
        prorate=False
    )

    # Database updated on next renewal

Best Practices

  1. Clear Pricing - Make pricing transparent and easy to understand
  2. Flexible Billing - Offer monthly and yearly options
  3. Easy Upgrades - Allow seamless plan changes
  4. Grace Period - Give users time after payment failure
  5. Usage Tracking - Help users monitor credit usage
  6. Notifications - Alert before subscription renewal
  7. Cancellation - Make it easy to cancel (build trust)

Troubleshooting

Payment Failed

Symptom: Subscription payment fails

Solution:

  1. Send notification to user
  2. Retry payment (Stripe automatic retry)
  3. Provide grace period (7 days)
  4. Downgrade to free tier if unresolved

Credits Not Allocated

Symptom: User subscribed but no credits

Solution:

  1. Check webhook delivery in Stripe dashboard
  2. Verify webhook handler processed event
  3. Manually allocate credits if needed
  4. Review logs for errors

Duplicate Subscriptions

Symptom: User has multiple active subscriptions

Solution:

  1. Cancel older subscriptions in Stripe
  2. Consolidate to single active subscription
  3. Refund if necessary
  4. Update database to reflect single subscription

Related Documentation


Last Updated: December 2024 Status: Production Ready


Related

Clone this wiki locally