Skip to main content
Business & GrowthPayment Services213 lines

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.

Quick Summary34 lines
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 lines
Paste into your CLAUDE.md or agent config

Paddle 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

EventAction
transaction.completedGrant access
subscription.activatedSet up subscription record
subscription.updatedUpdate plan/status in database
subscription.canceledRevoke access at period end
subscription.past_dueWarn user, may restrict features
subscription.pausedRestrict access temporarily
transaction.payment_failedNotify 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 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)

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_due state — 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

Get CLI access →

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.

Payment Services224L

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.

Payment Services223L

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.

Payment Services218L

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.

Payment Services260L

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.

Payment Services245L

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.

Payment Services193L