Paddle
Accept payments with Paddle as merchant of record. Use this skill when the project needs to integrate Paddle for subscription billing, one-time payments, checkout overlay, tax compliance, invoicing, or global payment processing where Paddle handles tax, compliance, and payouts. Covers Paddle Billing API, checkout, subscriptions, transactions, webhooks, and Paddle.js.
You are a payments specialist who integrates Paddle into projects. Paddle is a
merchant of record (MoR) — it handles tax collection, compliance, invoicing, and
payouts globally. You sell through Paddle, not directly to customers.
## Key Points
- Dashboard: `sandbox-vendors.paddle.com`
- API: `sandbox-api.paddle.com`
- Use test card numbers for checkout testing
- Use `customData` on all transactions and subscriptions to link to your users
- Always verify webhook signatures
- Handle `subscription.past_due` — give users a grace period to update payment
- Use `effectiveFrom: 'next_billing_period'` for cancellations to honor paid time
- Use Paddle's pricing preview API for localized pricing on your pricing page
- Test the full lifecycle in sandbox before going live
- Use Paddle Retain for subscription recovery (failed payment dunning)
- Granting access on checkout completion callback without webhook verification
- Not storing Paddle customer IDs in your database
## Quick Example
```bash
npm install @paddle/paddle-node-sdk
```
```typescript
import { Paddle } from '@paddle/paddle-node-sdk';
const paddle = new Paddle(process.env.PADDLE_API_KEY, {
environment: process.env.PADDLE_ENV === 'sandbox' ? 'sandbox' : 'production',
});
```skilldb get payment-services-skills/paddleFull skill: 213 linesPaddle Payment Integration
You are a payments specialist who integrates Paddle into projects. Paddle is a merchant of record (MoR) — it handles tax collection, compliance, invoicing, and payouts globally. You sell through Paddle, not directly to customers.
Core Philosophy
Merchant of record simplifies everything
Paddle is the legal seller. It handles VAT/GST collection, invoicing, refunds, and chargebacks. You don't need to register for tax in each country or handle PCI compliance. The trade-off is less control over the payment experience and Paddle's fee.
Products and prices live in Paddle
Unlike Stripe where you can create prices on the fly, Paddle products and prices are configured in the Paddle dashboard. Your code references price IDs, not amounts.
Webhooks are the integration backbone
Every state change in Paddle triggers a webhook. Your application must process webhooks to stay in sync — subscriptions, payments, refunds, and cancellations.
Setup
Install
npm install @paddle/paddle-node-sdk
Initialize
import { Paddle } from '@paddle/paddle-node-sdk';
const paddle = new Paddle(process.env.PADDLE_API_KEY, {
environment: process.env.PADDLE_ENV === 'sandbox' ? 'sandbox' : 'production',
});
Key Techniques
Checkout (Paddle.js overlay)
<!-- Include Paddle.js -->
<script src="https://cdn.paddle.com/paddle/v2/paddle.js"></script>
<script>
Paddle.Initialize({
token: 'live_xxx', // Client-side token
eventCallback: function(data) {
if (data.name === 'checkout.completed') {
// Redirect to success page
}
}
});
</script>
// Open checkout
Paddle.Checkout.open({
items: [{ priceId: 'pri_xxx', quantity: 1 }],
customer: { email: 'user@example.com' },
customData: { userId: '123' },
settings: {
successUrl: 'https://yourdomain.com/success',
theme: 'dark',
},
});
Server-side transaction
const transaction = await paddle.transactions.create({
items: [{ priceId: 'pri_xxx', quantity: 1 }],
customerId: 'ctm_xxx',
customData: { userId: '123' },
});
Subscription management
// Get subscription
const sub = await paddle.subscriptions.get('sub_xxx');
// Update subscription (change plan)
await paddle.subscriptions.update('sub_xxx', {
items: [{ priceId: 'pri_new_xxx', quantity: 1 }],
prorationBillingMode: 'prorated_immediately',
});
// Cancel subscription
await paddle.subscriptions.cancel('sub_xxx', {
effectiveFrom: 'next_billing_period',
});
// Pause subscription
await paddle.subscriptions.pause('sub_xxx');
// Resume subscription
await paddle.subscriptions.resume('sub_xxx', {
effectiveFrom: 'immediately',
});
Customer management
// Create customer
const customer = await paddle.customers.create({
email: 'user@example.com',
name: 'Alice',
customData: { userId: '123' },
});
// List customer subscriptions
const subs = await paddle.subscriptions.list({
customerId: ['ctm_xxx'],
});
Pricing preview
// Get localized pricing for a customer
const preview = await paddle.pricingPreviews.preview({
items: [{ priceId: 'pri_xxx', quantity: 1 }],
address: { countryCode: 'DE' },
});
console.log(preview.details.lineItems[0].formattedTotals);
// { subtotal: '€29.00', tax: '€5.51', total: '€34.51' }
Webhook Processing
| Event | Action |
|---|---|
transaction.completed | Grant access |
subscription.activated | Set up subscription record |
subscription.updated | Update plan/status in database |
subscription.canceled | Revoke access at period end |
subscription.past_due | Warn user, may restrict features |
subscription.paused | Restrict access temporarily |
transaction.payment_failed | Notify user |
import { Paddle } from '@paddle/paddle-node-sdk';
const paddle = new Paddle(process.env.PADDLE_API_KEY);
export async function POST(req: Request) {
const body = await req.text();
const signature = req.headers.get('paddle-signature');
let event;
try {
event = paddle.webhooks.unmarshal(body, process.env.PADDLE_WEBHOOK_SECRET, signature);
} catch {
return new Response('Invalid signature', { status: 401 });
}
switch (event.eventType) {
case 'transaction.completed':
const txn = event.data;
const userId = txn.customData?.userId;
await grantAccess(userId, txn.subscriptionId);
break;
case 'subscription.canceled':
const sub = event.data;
await scheduleAccessRevocation(sub.customData?.userId, sub.currentBillingPeriod?.endsAt);
break;
case 'subscription.past_due':
await notifyPastDue(event.data.customData?.userId);
break;
}
return new Response('OK');
}
Sandbox Testing
Paddle provides a full sandbox environment:
- Dashboard:
sandbox-vendors.paddle.com - API:
sandbox-api.paddle.com - Use test card numbers for checkout testing
Best Practices
- Use
customDataon all transactions and subscriptions to link to your users - Always verify webhook signatures
- Handle
subscription.past_due— give users a grace period to update payment - Use
effectiveFrom: 'next_billing_period'for cancellations to honor paid time - Use Paddle's pricing preview API for localized pricing on your pricing page
- Test the full lifecycle in sandbox before going live
- Use Paddle Retain for subscription recovery (failed payment dunning)
Anti-Patterns
- Granting access on checkout completion callback without webhook verification
- Not storing Paddle customer IDs in your database
- Immediately revoking access on cancellation instead of honoring the paid period
- Creating prices programmatically — Paddle requires dashboard configuration
- Not handling the
past_duestate — users get confused by silent access loss - Ignoring Paddle's tax handling — it's the whole point of using a MoR
Install this skill directly: skilldb add payment-services-skills
Related Skills
Paypal
Accept payments with PayPal. Use this skill when the project needs to integrate PayPal for checkout, subscriptions, one-time payments, PayPal buttons, venmo, invoicing, payouts, or PayPal REST API. Covers the PayPal JavaScript SDK, REST API v2, Orders, Subscriptions, Webhooks, and Smart Payment Buttons.
Razorpay
Accept payments with Razorpay. Use this skill when the project needs to integrate Razorpay for payments in India and international markets, UPI, subscriptions, payment links, invoicing, route (split payments), or Razorpay Checkout. Covers the Razorpay API, Checkout.js, orders, subscriptions, webhooks, and UPI.
Square
Accept payments with Square. Use this skill when the project needs to integrate Square for online payments, in-person POS, invoicing, subscriptions, catalog management, or omnichannel commerce. Covers the Square API, Web Payments SDK, Orders, Subscriptions, Invoices, Catalog, and webhooks.
Stripe
Accept payments and manage subscriptions with Stripe. Use this skill when the project needs to integrate Stripe for one-time payments, recurring subscriptions, checkout sessions, payment intents, customer management, invoicing, webhook processing, billing portal, metered billing, or payment method management. Covers the Stripe API, Stripe.js, Checkout, Elements, Billing, and webhooks.
Adyen
Accept payments with Adyen. Use this skill when the project needs to integrate Adyen for online payments, drop-in checkout, tokenization, subscriptions, payouts, or omnichannel payment processing. Covers Adyen Checkout API, drop-in components, webhooks, and recurring payments.
Braintree
Accept payments with Braintree (PayPal). Use this skill when the project needs to integrate Braintree for credit card payments, PayPal, Venmo, Apple Pay, Google Pay, subscriptions, vaulting payment methods, or drop-in UI. Covers the Braintree SDK, Drop-in UI, hosted fields, vaulting, subscriptions, and webhooks.