---
title: "Paddle"
description: "Use Paddle as a planned MainSDK payments driver - merchant of record checkout, sandbox and live keys, webhook secrets and swapping."
section: "integrations"
canonical: "https://mainsdk.dev/docs/integrations/paddle"
updated: "2026-10-28"
---

# Paddle

> Use Paddle as a planned MainSDK payments driver - merchant of record checkout, sandbox and live keys, webhook secrets and swapping.

Canonical: https://mainsdk.dev/docs/integrations/paddle · Updated: 2026-10-28

Paddle is a merchant of record: it sells your software to your customers on your behalf and handles sales tax and invoicing in the countries it covers. It is a planned driver behind [`pay`](https://mainsdk.dev/docs/interfaces/pay). Pick Paddle when you sell software or subscriptions internationally and would rather not register for and file sales tax yourself.

> **Note:** Paddle is listed in the product requirements but the `pay` spec puts it out of scope for `pay@1`. It is a planned driver, not in the spec yet, so the configuration on this page is provisional.

## Interfaces it serves

### Payments (`pay`)

- Status: Building for 0.3 · ships as a stable API
- Driver: `paddle` (Paddle, planned driver)
- Docs: https://mainsdk.dev/docs/interfaces/pay.md

```yaml
payments:
  driver: paddle
```

Swap to another provider: `stripe`, `razorpay`, `local`.

## Before you start

You need a Paddle account. Paddle gives you a separate sandbox account for testing and a live account, each with its own keys and webhook destinations.

1. **Create an API key** in the sandbox account's developer settings.
2. **Add a webhook destination** that points at the URL where your app receives `pay` events, and copy the secret Paddle shows for it.
3. **Set up your products and prices** in Paddle; Paddle reviews your account before you can sell live.
4. **Repeat in the live account** once it is approved.

## Configure

```yaml title="main.yaml"
payments: { default: paddle }
pay:
  paddle:                                   # [TBD] Paddle is not in the pay spec; shape follows the other providers
    key: secret:PADDLE_API_KEY              # suggested name
    webhook: secret:PADDLE_WEBHOOK_SECRET   # suggested name
```

Provider keys are always `secret:` references; a literal key fails `mainsdk check`.

## Use it

```ts title="app/billing/actions.ts"
"use server";
import { auth, pay } from "mainsdk";

export async function subscribe(req: Request, plan: string) {
  const session = await auth.require(req);
  return pay.subscribe({ customer: session.user.id, plan });   // [TBD] how customer is identified
}
```

Subscription changes arrive as `subscription.updated` through `pay.on()`.

## Local development and testing

`mainsdk dev` with no provider key runs the local test card flow. Use Paddle's sandbox account for the real hosted checkout. In tests, `createTestApp()` from `@mainsdk/testing` runs `pay` on its in-memory fake, and `chaos.on("pay", { outage: true })` simulates an outage.

## What MainSDK adds on top

- Checkout only on Paddle's hosted checkout; card data never touches your app (`MAIN-PAY-0001`).
- Paddle webhooks verified before your handler runs, and duplicates handled once.
- Idempotency keys on checkout, subscribe and refund.
- Amounts in integer minor units with an ISO 4217 currency.

See [Payments](https://mainsdk.dev/docs/interfaces/pay).

## Limits and notes

- Planned driver, not in the spec yet. Paddle is out of scope for `pay@1`; when and how it lands is [TBD].
- Because Paddle is the seller, your customers' receipts and tax invoices come from Paddle, not from your business.
- Paddle sells digital products and software; check that what you sell is eligible.
- `pay.raw()` would return the Paddle client; `mainsdk check` lists every use.

## Swap to or from it

```yaml title="main.yaml"
payments: { default: paddle }   # was { default: stripe }
```

> **Warning:** Payments are stateful. Customers and subscriptions live in the provider, so moving them between Paddle and another provider is a migration that MainSDK runs as a guided job with a rollback, not a silent switch. With a merchant of record, the customer relationship sits with Paddle, so plan the move with Paddle too.

## FAQ

**Can I use Paddle today?** Not through `pay@1`. It is planned; use Stripe or Razorpay until it lands.

**Does Paddle replace Stripe for usage billing?** No. Usage metering goes through [`meter`](https://mainsdk.dev/docs/interfaces/meter), with Stripe meters or Lago.

## Next

- [Payments](https://mainsdk.dev/docs/interfaces/pay)
- [Stripe](https://mainsdk.dev/docs/integrations/stripe) and [Razorpay](https://mainsdk.dev/docs/integrations/razorpay)
- [Swap a driver](https://mainsdk.dev/docs/guides/swap-a-driver)
