Paddle
Use Paddle as a planned MainSDK payments driver - merchant of record checkout, sandbox and live keys, webhook secrets and swapping.
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. Pick Paddle when you sell software or subscriptions internationally and would rather not register for and file sales tax yourself.
Interfaces it serves
Pick Paddle in main.yaml for any of these. Your calls stay the same, and you can swap later.
payBuilding for 0.3Driver paddle PaddlePlanned driver
payments:
driver: paddleSwap to another provider
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.
- Create an API key in the sandbox account's developer settings.
- Add a webhook destination that points at the URL where your app receives
payevents, and copy the secret Paddle shows for it. - Set up your products and prices in Paddle; Paddle reviews your account before you can sell live.
- Repeat in the live account once it is approved.
Configure
payments: { default: paddle }
pay:
paddle: # [to be decided] 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 nameProvider keys are always secret: references; a literal key fails mainsdk check.
Use it
"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 }); // [to be decided] 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.
Limits and notes
- Planned driver, not in the spec yet. Paddle is out of scope for
pay@1; when and how it lands is not decided yet. - 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 checklists every use.
Swap to or from it
payments: { default: paddle } # was { default: stripe }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, with Stripe meters or Lago.
Next
Open questions
Was this page helpful?