Configuring Paddle Billing | Next.js Supabase SaaS Kit Turbo
Complete guide to integrating Paddle billing with your Next.js Supabase SaaS application. Learn how to set up payment processing, webhooks, and subscription management with Paddle as your Merchant of Record.
Paddle is a comprehensive billing solution that acts as a Merchant of Record (MoR), handling all payment processing, tax calculations, compliance, and regulatory requirements for your SaaS business.
This integration eliminates the complexity of managing global tax compliance, PCI requirements, and payment processing infrastructure.
Overview
This guide will walk you through:
- Setting up Paddle for development and production
- Configuring webhooks for real-time billing events
- Creating and managing subscription products
- Testing the complete billing flow
- Deploying to production
Prerequisites
Before starting, ensure you have:
- A Paddle account (sandbox for development, live for production)
- Access to your application's environment configuration
- A method to expose your local development server (ngrok, LocalTunnel, Localcan, etc.)
Step 0: Fetch the Paddle package from the plugins repository
The Paddle package is released as a plugin in the Plugins repository. You can fetch it by running the following command:
npx @makerkit/cli@latest plugins install
Please choose the Paddle plugin from the list of available plugins.
Step 1: Registering Paddle
Now we need to register the services from the Paddle plugin.
Install the Paddle package
Run the following command to add the Paddle package to our billing package:
pnpm --filter "@kit/billing-gateway" add "@kit/paddle"
Registering the Checkout component
Update the function loadCheckoutComponent
to include the paddle
block, which will dynamically import the Paddle checkout component:
packages/billing/gateway/src/components/embedded-checkout.tsx
function loadCheckoutComponent(provider: BillingProvider) { switch (provider) { case 'stripe': { return buildLazyComponent(() => { return import('@kit/stripe/components').then(({ StripeCheckout }) => { return { default: StripeCheckout, }; }); }); } case 'lemon-squeezy': { return buildLazyComponent(() => { return import('@kit/lemon-squeezy/components').then( ({ LemonSqueezyEmbeddedCheckout }) => { return { default: LemonSqueezyEmbeddedCheckout, }; }, ); }); } case 'paddle': { return buildLazyComponent(() => { return import('@kit/paddle/components').then( ({ PaddleCheckout }) => { return { default: PaddleCheckout, }; }, ); }); } default: throw new Error(`Unsupported provider: ${provider as string}`); }}
Registering the Webhook handler
At packages/billing/gateway/src/server/services/billing-event-handler /billing-event-handler-factory.service.ts
, add the snippet below at the bottom of the file:
packages/billing/gateway/src/server/services/billing-event-handler/billing-event-handler-factory.service.ts
// Register Paddle webhook handlerbillingWebhookHandlerRegistry.register('paddle', async () => { const { PaddleWebhookHandlerService } = await import('@kit/paddle'); return new PaddleWebhookHandlerService(planTypesMap);});
Registering the Billing service
Finally, at packages/billing/gateway/src/server/services/billing-event-handler /billing-gateway-registry.ts
, add the snippet below at the bottom of the file:
packages/billing/gateway/src/server/services/billing-gateway/billing-gateway-registry.ts
// Register Paddle billing strategybillingStrategyRegistry.register('paddle', async () => { const { PaddleBillingStrategyService } = await import('@kit/paddle'); return new PaddleBillingStrategyService();});
Step 2: Create Paddle Account
Development Account (Sandbox)
- Visit Paddle Developer Console
- Complete the registration process
- Verify your email address
- Navigate to your sandbox dashboard
Important Notes
- The sandbox environment allows unlimited testing without processing real payments
- All transactions in sandbox mode use test card numbers
- Webhooks and API calls work identically to production
- The Paddle payment provider currently only supports flat and per-seat plans (metered subscriptions are not supported)
Step 3: Configure Billing Provider
Database Configuration
Set Paddle as your billing provider in the database:
-- Update the billing provider in your configuration tableUPDATE public.configset billing_provider = 'paddle';
Environment Configuration
Add the following to your .env.local
file:
# Set Paddle as the active billing providerNEXT_PUBLIC_BILLING_PROVIDER=paddle
This environment variable tells your application to use Paddle-specific components and API endpoints for billing operations.
Step 4: API Key Configuration
Paddle requires two types of API keys for complete integration:
Server-Side API Key (Required)
- In your Paddle dashboard, navigate to Developer Tools → Authentication
- Click Generate New API Key
- Give it a descriptive name (e.g., "Production API Key" or "Development API Key")
- Configure the required permissions for your API key:
- Write Customer Portal Sessions - For managing customer billing portals
- Read Customers - For retrieving customer information
- Read Prices - For displaying pricing information
- Read Products - For product catalog access
- Read/Write Subscriptions - For subscription management
- Read Transactions - For payment and transaction tracking
- Copy the generated key immediately (it won't be shown again)
- Add to your
.env.local
:
PADDLE_API_KEY=your_server_api_key_here
Security Note: This key has access to the specified Paddle account permissions and should never be exposed to the client-side code. Only grant the minimum permissions required for your integration.
Client-Side Token (Required)
- In the same Authentication section, look for Client-side tokens
- Click Generate New Token
- Configure the allowed domains (for development, add
https://localhost:3000
) - Copy the client token
- Add to your
.env.local
:
NEXT_PUBLIC_PADDLE_CLIENT_TOKEN=your_client_token_here
Important: This token is safe to expose in client-side code but should be restricted to your specific domains.
Step 5: Webhook Configuration
Webhooks enable real-time synchronization between Paddle and your application for events like successful payments, subscription changes, and cancellations.
Set Up Local Development Tunnel
First, expose your local development server to the internet:
Using ngrok (Recommended)
# Install ngrok if not already installednpm install -g ngrok# Expose port 3000 (default Next.js port)ngrok http 3000
Using LocalTunnel
# Install localtunnelnpm install -g localtunnel# Expose port 3000lt --port 3000
Configure Webhook Destination
- In Paddle dashboard, go to Developer Tools → Notifications
- Click New Destination
- Configure the destination:
- Destination URL:
https://your-tunnel-url.ngrok.io/api/billing/webhook
- Description: "Local Development Webhook"
- Active: ✅ Checked
- Destination URL:
Select Webhook Events
Enable these essential events for proper billing integration:
Retrieve Webhook Secret
- After creating the destination, click on it to view details
- Copy the Endpoint Secret (used to verify webhook authenticity)
- Add to your
.env.local
:
PADDLE_WEBHOOK_SECRET_KEY=your_webhook_secret_here
Test Webhook Connection
You can test the webhook endpoint by making a GET request to verify it's accessible:
curl https://your-tunnel-url.ngrok.io/api/billing/webhook
Expected response: 200 OK
with a message indicating the webhook endpoint is active.
Step 6: Product and Pricing Configuration
Create Products in Paddle
- Navigate to Catalog → Products in your Paddle dashboard
- Click Create Product
- Configure your product:
Basic Information:
- Product Name: "Starter Plan", "Pro Plan", etc.
- Description: Detailed description of the plan features
- Tax Category: Select appropriate category (usually "Software")
Pricing Configuration:
- Billing Interval: Monthly, Yearly, or Custom
- Price: Set in your primary currency
- Trial Period: Optional free trial duration
Configure Billing Settings
Update your billing configuration file with the Paddle product IDs:
// apps/web/config/billing.config.tsexport const billingConfig = { provider: 'paddle', products: [ { id: 'starter', name: 'Starter Plan', description: 'Perfect for individuals and small teams', badge: 'Most Popular', features: [ 'Up to 5 projects', 'Basic support', '1GB storage' ], plans: [ { id: 'pri_starter_monthly_001', // Paddle Price ID paymentType: 'recurring', interval: 'month', intervalCount: 1, price: 900, // Price in cents currency: 'USD' } ] } // Add more products... ]};
Step 7: Checkout Configuration
Default Payment Link Configuration
- Go to Checkout → Checkout Settings in Paddle dashboard
- Configure Default Payment Link:
- Success URL:
http://localhost:3000/home/billing/success
- Cancel URL:
http://localhost:3000/home/billing/cancel
- Success URL:
- Save the configuration
Step 8: Testing the Integration
Development Testing Checklist
Environment Verification:
- [ ] All environment variables are set correctly
- [ ] Webhook tunnel is active and accessible
- [ ] Destination was defined using the correct URL
- [ ] Database billing provider is set to 'paddle' in both DB and ENV
Subscription Flow Testing:
- Navigate to your billing/pricing page (
/home/billing
or equivalent) - Click on a subscription plan
- Complete the checkout flow using Paddle test cards
- Verify successful redirect to success page
- Check that subscription appears in user dashboard
- Verify webhook events are received in your application logs
You can also test cancellation flows:
- cancel the subscription from the billing portal
- delete the account and verify the subscription is cancelled as well
Test Card Numbers
Follow this link to get the test card numbers
Webhook Testing
Monitor webhook delivery in your application logs:
# Watch your development logspnpm dev# In another terminal, monitor webhook requeststail -f logs/webhook.log
Step 9 (Optional)
If you are using Per Seat pricing plans, you should intoroduce an Invitation Feature Policy in order to block invitations being sent while in a trial. This is because Paddle doesn't support updating the quantity of a subscription during a trial period.
NB: this requires version 2.16.0, which introduced Feature Policies.
Please update the packages/features/team-accounts/src/server/policies/policies.ts
file to include the following policy:
packages/features/team-accounts/src/server/policies/policies.ts
invitationPolicyRegistry.registerPolicy(paddleBillingInvitationsPolicy);
Step 10: Production Deployment
Apply for Live Paddle Account
- In your Paddle dashboard, click Go Live
- Complete the application process:
- Business information and verification
- Tax information and documentation
- Banking details for payouts
- Identity verification for key personnel
Timeline: Live account approval typically takes 1-3 business days.
Production Environment Setup
Create production-specific configuration:
# Production environment variablesNEXT_PUBLIC_BILLING_PROVIDER=paddlePADDLE_API_KEY=your_production_api_keyNEXT_PUBLIC_PADDLE_CLIENT_TOKEN=your_production_client_tokenPADDLE_WEBHOOK_SECRET_KEY=your_production_webhook_secret
Production Webhook Configuration
- Create a new webhook destination for production
- Set the destination URL to your production domain:https://yourdomain.com/api/billing/webhook
- Enable the same events as configured for development
- Update your production environment with the new webhook secret
Production Products and Pricing
- Create production versions of your products in the live environment
- Update your production billing configuration with live Price IDs
- Test the complete flow on production with small-amount transactions
Support Resources
Refer to the Paddle Documentation for more information:
- Paddle Documentation: https://developer.paddle.com
- Status Page: https://status.paddle.com