Loading IconFastLaunchAPI
API routes

Payments

Complete Stripe-based payment system with subscriptions, one-time payments, and billing management

All payment routes are prefixed with /payments and tagged as payments in the API documentation. Authentication is required for all endpoints.

Features

Stripe Integration

Full Stripe payment processing with secure checkout sessions

Subscription Management

Recurring subscriptions with automatic billing and cancellation

One-time Payments

Single purchase transactions for products and services

Billing Portal

Customer self-service portal for managing subscriptions

Webhook Handling

Automated processing of Stripe events and status updates

Product Management

Get Products

GET /payments/products/

Fetch available products filtered by type.

Query Parameters:

  • product_type (required): Product type filter - one_time or subscription

Response:

[
  {
    "id": "prod_xxxxxx",
    "name": "Premium Plan",
    "description": "Full access to all features",
    "metadata": {},
    "prices": [
      {
        "id": "price_xxxxxx",
        "unit_amount": 2999,
        "currency": "usd",
        "recurring": {
          "interval": "month"
        }
      }
    ]
  }
]

Returns 400 if Stripe API error occurs or invalid product_type provided.

Get Available Plans

GET /payments/get-plans

Retrieve all available subscription plans from the database.

Headers:

Authorization: Bearer {access_token}

Response:

[
  {
    "id": 1,
    "name": "Basic Plan",
    "stripe_price_id": "price_xxxxxx",
    "price": 999,
    "interval": "month",
    "features": ["Feature 1", "Feature 2"],
    "is_active": true
  }
]

Subscription Management

Get User Subscription

GET /payments/get-user-subscription

Get the current user's subscription details and status.

Headers:

Authorization: Bearer {access_token}

Response:

{
  "subscription": {
    "id": "sub_xxxxxx",
    "status": "active",
    "current_period_start": "2024-01-01T00:00:00Z",
    "current_period_end": "2024-02-01T00:00:00Z",
    "cancel_at_period_end": false,
    "plan": {
      "id": "price_xxxxxx",
      "nickname": "Premium Plan",
      "amount": 2999,
      "currency": "usd",
      "interval": "month"
    }
  }
}
{
  "subscription": null,
  "message": "No active subscription"
}

Cancel Subscription

DELETE /payments/cancel-subscription

Cancel the user's active subscription at the end of the current billing period.

Headers:

Authorization: Bearer {access_token}

Response:

{
  "message": "Subscription canceled successfully",
  "subscription": {
    "id": "sub_xxxxxx",
    "status": "active",
    "cancel_at_period_end": true,
    "current_period_end": "2024-02-01T00:00:00Z"
  }
}

Returns 404 if no active subscription found or 400 if Stripe API error occurs.

Checkout Sessions

Create Subscription Checkout

POST /payments/create-checkout-session

Create a Stripe checkout session for subscription payments.

Headers:

Authorization: Bearer {access_token}

Request Body:

{
  "price_id": "price_xxxxxx"
}

Response:

{
  "checkout_url": "https://checkout.stripe.com/pay/cs_xxxxxx",
  "session_id": "cs_xxxxxx"
}
Validation: Checks if user already has an active subscription
Customer Creation: Creates or retrieves Stripe customer

Session Creation: Creates Stripe checkout session with price ID

URL Generation: Returns checkout URL for frontend redirect

Returns 400 if user already has an active subscription. Cancel existing subscription first.

Create One-time Checkout

POST /payments/create-checkout-session-onetime

Create a Stripe checkout session for one-time payments.

Headers:

Authorization: Bearer {access_token}

Request Body:

{
  "price_id": "price_xxxxxx",
  "product_id": "prod_xxxxxx"
}

Response:

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

One-time payments don't require subscription status checks and can be purchased multiple times.

Billing Management

Create Billing Portal Session

GET /payments/create-billing-portal-session

Create a Stripe billing portal session for subscription management.

Headers:

Authorization: Bearer {access_token}

Response:

{
  "portal_url": "https://billing.stripe.com/session/xxxxxx"
}

Returns 400 if user has no customer_id (no active plan history).

The billing portal allows customers to:

  • View billing history and invoices
  • Update payment methods
  • Cancel or modify subscriptions
  • Download receipts

Webhook Handling

Stripe Webhook Endpoint

POST /payments/webhook

Handle Stripe webhook events for automated payment processing.

Headers:

stripe-signature: {webhook_signature}

Supported Events:

Event TypeDescription
checkout.session.completedPayment successful, activate subscription/product
invoice.paidSubscription renewal successful
invoice.payment_failedSubscription payment failed

Signature Verification: Validates webhook signature using Stripe secret

Event Processing: Routes event to appropriate handler function

Database Update: Updates user subscription status and details
Response: Returns success status to Stripe

Returns 400 for invalid payload/signature or 500 for processing errors.

Security Features

Webhook Security

  • Signature Verification: All webhooks verified using Stripe signature
  • Event Validation: Validates event structure and required fields
  • Idempotency: Prevents duplicate event processing

Payment Security

  • Stripe Hosted: All payment processing handled by Stripe
  • No Card Storage: No sensitive payment data stored locally
  • PCI Compliance: Inherits Stripe's PCI DSS compliance

User Authorization

  • JWT Required: All endpoints require valid access token
  • User Scoping: Users can only access their own subscription data
  • Role Validation: Ensures user permissions for payment operations

Error Handling

Common HTTP status codes returned by payment endpoints:

Status CodeDescription
200Success
400Bad request (validation error, Stripe error)
401Unauthorized (invalid token)
404Resource not found (no subscription)
500Internal server error (webhook processing)

Configuration

Payment system configuration through environment variables:

STRIPE_SECRET_KEY=sk_live_xxxxxx
STRIPE_PUBLISHABLE_KEY=pk_live_xxxxxx
WEBHOOK_SECRET=whsec_xxxxxx
FRONTEND_URL=https://yourdomain.com

Use live keys in production and test keys in development. Never expose secret keys in frontend code.

Integration Examples

Frontend Checkout Flow

import { useState } from 'react';

function CheckoutButton({ priceId }) {
  const [loading, setLoading] = useState(false);
  
  const handleCheckout = async () => {
    setLoading(true);
    try {
      const response = await fetch('/payments/create-checkout-session', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${localStorage.getItem('access_token')}`
        },
        body: JSON.stringify({ price_id: priceId })
      });
      
      const data = await response.json();
      window.location.href = data.checkout_url;
    } catch (error) {
      console.error('Checkout error:', error);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <button onClick={handleCheckout} disabled={loading}>
      {loading ? 'Processing...' : 'Subscribe Now'}
    </button>
  );
}
async function createSubscriptionCheckout(priceId) {
  const response = await fetch('/payments/create-checkout-session', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${localStorage.getItem('access_token')}`
    },
    body: JSON.stringify({ price_id: priceId })
  });
  
  const data = await response.json();
  
  if (response.ok) {
    window.location.href = data.checkout_url;
  } else {
    throw new Error(data.detail);
  }
}
import requests

def create_checkout_session(price_id, access_token):
    response = requests.post(
        '/payments/create-checkout-session',
        headers={
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/json'
        },
        json={'price_id': price_id}
    )
    
    if response.status_code == 200:
        return response.json()['checkout_url']
    else:
        raise Exception(response.json()['detail'])

Subscription Management

import { useEffect, useState } from 'react';

function SubscriptionManager() {
  const [subscription, setSubscription] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    fetchSubscription();
  }, []);
  
  const fetchSubscription = async () => {
    try {
      const response = await fetch('/payments/get-user-subscription', {
        headers: {
          'Authorization': `Bearer ${localStorage.getItem('access_token')}`
        }
      });
      const data = await response.json();
      setSubscription(data.subscription);
    } catch (error) {
      console.error('Error fetching subscription:', error);
    } finally {
      setLoading(false);
    }
  };
  
  const cancelSubscription = async () => {
    try {
      await fetch('/payments/cancel-subscription', {
        method: 'DELETE',
        headers: {
          'Authorization': `Bearer ${localStorage.getItem('access_token')}`
        }
      });
      fetchSubscription(); // Refresh subscription data
    } catch (error) {
      console.error('Error canceling subscription:', error);
    }
  };
  
  const openBillingPortal = async () => {
    try {
      const response = await fetch('/payments/create-billing-portal-session', {
        headers: {
          'Authorization': `Bearer ${localStorage.getItem('access_token')}`
        }
      });
      const data = await response.json();
      window.location.href = data.portal_url;
    } catch (error) {
      console.error('Error opening billing portal:', error);
    }
  };
  
  if (loading) return <div>Loading...</div>;
  
  return (
    <div>
      {subscription ? (
        <div>
          <h3>Current Subscription</h3>
          <p>Plan: {subscription.plan.nickname}</p>
          <p>Status: {subscription.status}</p>
          <p>Next billing: {subscription.current_period_end}</p>
          
          <button onClick={cancelSubscription}>
            Cancel Subscription
          </button>
          <button onClick={openBillingPortal}>
            Manage Billing
          </button>
        </div>
      ) : (
        <div>
          <p>No active subscription</p>
          <button onClick={() => window.location.href = '/pricing'}>
            View Plans
          </button>
        </div>
      )}
    </div>
  );
}
class SubscriptionManager {
  constructor(accessToken) {
    this.accessToken = accessToken;
  }
  
  async getSubscription() {
    const response = await fetch('/payments/get-user-subscription', {
      headers: {
        'Authorization': `Bearer ${this.accessToken}`
      }
    });
    return response.json();
  }
  
  async cancelSubscription() {
    const response = await fetch('/payments/cancel-subscription', {
      method: 'DELETE',
      headers: {
        'Authorization': `Bearer ${this.accessToken}`
      }
    });
    return response.json();
  }
  
  async openBillingPortal() {
    const response = await fetch('/payments/create-billing-portal-session', {
      headers: {
        'Authorization': `Bearer ${this.accessToken}`
      }
    });
    const data = await response.json();
    window.location.href = data.portal_url;
  }
}

Best Practices

Frontend Integration

  1. Error Handling: Always handle payment errors gracefully
  2. Loading States: Show loading indicators during checkout
  3. Success Pages: Redirect to success page after payment
  4. Token Management: Ensure valid access tokens for API calls

Webhook Security

  1. Signature Verification: Always verify webhook signatures
  2. Idempotency: Handle duplicate webhook events
  3. Error Logging: Log webhook processing errors
  4. Retry Logic: Implement retry for failed webhook processing

User Experience

  1. Clear Pricing: Display pricing clearly before checkout
  2. Billing Transparency: Provide easy access to billing portal
  3. Cancellation Flow: Make subscription cancellation straightforward
  4. Payment History: Show transaction history and receipts

Security Considerations

  1. Environment Variables: Secure API keys in environment variables
  2. HTTPS Only: Always use HTTPS for payment endpoints
  3. Token Validation: Validate JWT tokens on all endpoints
  4. Rate Limiting: Implement rate limiting on payment endpoints