GitHub Repository
Source code for the FastAPI and Dodo Payments boilerplate.
Overview
The FastAPI boilerplate is a Python backend with Dodo Payments already connected. It has endpoints that create checkout sessions and Customer Portal sessions, a webhook endpoint that verifies signatures, and a pricing page rendered from Jinja2 templates.This boilerplate uses FastAPI with
async route handlers, Pydantic for validation and settings, and the dodopayments Python SDK. The handlers call the synchronous DodoPayments client. To avoid blocking the event loop, switch to AsyncDodoPayments and await its calls.Features
The boilerplate includes:- Quick Setup: Go from clone to a running server in about five minutes.
- Async Handlers: Route handlers are FastAPI
async deffunctions. - Checkout Sessions: A pre-configured checkout endpoint that uses the Python SDK.
- Webhook Handling: A webhook endpoint that verifies each signature with the SDK’s
unwrapmethod. - Customer Portal: An endpoint that creates Customer Portal sessions.
- Type Safety: Pydantic models validate request bodies, and the code uses type hints.
- Environment Configuration:
pydantic-settingsloads and validates the configuration from.env.
Prerequisites
Before you begin, you need:- Python 3.9 or later, which the
dodopaymentsSDK requires. Python 3.11 or later is recommended. - pip or uv for package management.
- A Dodo Payments account, to create an API key and a webhook signing secret in the dashboard.
Quick Start
1
Clone the Repository
2
Create Virtual Environment
Set up an isolated Python environment:Or use uv for faster dependency management:
3
Install Dependencies
4
Get API Credentials
Sign up at Dodo Payments, then get your credentials from the dashboard:
- API Key: Create a key under Dashboard → Developer → API Keys.
- Webhook Key: Add an endpoint under Dashboard → Developer → Webhooks, then copy its signing secret. The endpoint URL must be public and use HTTPS. To receive events on your machine, see Testing Webhooks Locally.
5
Configure Environment Variables
Copy the example file to create a Set the values to your Dodo Payments credentials:All four variables are required.
.env file in the root directory:.env
app/core/config.py loads them with pydantic-settings, and the app fails to start if one is missing or empty. DODO_PAYMENTS_RETURN_URL is where checkout sends the customer after payment.6
Add Your Products
Replace the sample products in
app/lib/products.py with your own. Set each product_id to the ID of a product under Products in your dashboard. The pricing page displays these products.7
Run the Development Server
Swagger UI lists the
/api/checkout/, /api/webhook/, and /api/customer-portal/ endpoints, ready to test.http://localhost:8000, serves the pricing page.Project Structure
API Endpoints
app/main.py mounts each router under an /api prefix:
Each path ends with a slash. FastAPI answers a request to the path without the slash with a
307 redirect, so use the exact path, especially in your webhook URL.
Code Examples
These examples are condensed from the files inapp/api/.
Creating a Checkout Session
app/api/checkout.py creates a checkout session and returns its checkout_url. The request body takes a product_id, an optional quantity, and an optional customer object with name and email:
Handling Webhooks
app/api/webhook.py verifies the signature with the SDK’s unwrap method, then branches on the event type:
Customer Portal Integration
app/api/portal.py creates a Customer Portal session for a customer ID and returns the portal link as url:
app/templates/index.html sends a hardcoded customer ID (cus_001) to this endpoint, and a hardcoded name and email to the checkout endpoint. Replace them with the signed-in user’s values.
Webhook Events
The handler inapp/api/webhook.py branches on these events:
To handle another event, add a branch for its type, such as
refund.succeeded for a refund that processed successfully. For every event type, see the Webhook Event Guide.
Add your business logic inside the webhook handler to:
- Update user permissions in your database
- Send confirmation emails
- Provision access to digital products
- Track analytics and metrics
Testing Webhooks Locally
Dodo Payments can’t reachlocalhost. For local development, use a tool such as ngrok to expose your local server:
/api/webhook/, as an endpoint in your Dodo Payments Dashboard:
DODO_PAYMENTS_WEBHOOK_KEY in .env, then restart the server. The app reads .env only at startup.
Deployment
Docker
The repository doesn’t include aDockerfile. To run the app in a container, add this Dockerfile to the repository root:
COPY . . copies every file in the build context, including .env. To keep your keys out of the image, add a .dockerignore file that lists .env. Then build the image and run it with your environment file:
Production Considerations
Troubleshooting
Import errors or missing modules
Import errors or missing modules
Make sure your virtual environment is activated and dependencies are installed:
Server fails to start with Directory 'app/static' does not exist
Server fails to start with Directory 'app/static' does not exist
app/main.py serves static files from app/static, but the repository doesn’t include that directory. Create it with mkdir app/static, then start the server again.Checkout session creation fails
Checkout session creation fails
Check for these common causes:
- The product ID doesn’t exist in your Dodo Payments dashboard.
- The API key or
DODO_PAYMENTS_ENVIRONMENTin.envis wrong. A test mode key works only withtest_mode.
400 response. Check the FastAPI logs for detailed error messages.Webhooks not receiving events
Webhooks not receiving events
For local testing, use ngrok to expose your server:In your Dodo dashboard, add an endpoint with the ngrok URL followed by
/api/webhook/, including the trailing slash. Copy that endpoint’s signing secret into DODO_PAYMENTS_WEBHOOK_KEY in your .env file.Webhook signature verification fails
Webhook signature verification fails
- Make sure
DODO_PAYMENTS_WEBHOOK_KEYin.envmatches the endpoint’s signing secret. - Verify the signature against the raw request body, before you parse it as JSON.
- Pass all three
webhook-id,webhook-timestamp, andwebhook-signatureheaders toclient.webhooks.unwrap(). The Standard Webhooks signature coversid.timestamp.body, not the body alone.
Learn More
Python SDK
Complete Python SDK documentation with async support
Webhooks Documentation
Learn about all webhook events and best practices
Checkout Sessions
Deep dive into checkout session configuration
API Reference
Complete Dodo Payments API documentation
Support
For help with the boilerplate:- Ask questions in the Discord community.
- Report issues and follow updates in the GitHub repository.
- Email the support team.