Why default Merchant of Record receipts fall short for modern SaaS
Paddle is the leading Merchant of Record (MoR) for global software companies, automating international sales tax, VAT calculation, currency conversion, and fraud prevention across 200+ countries.
However, Paddle default transaction emails are utilitarian plain-text notifications. They cannot be themed to match your product design system, offer no support for interactive onboarding CTAs, and lack custom tracking telemetry.
By consuming Paddle webhook notifications directly and dispatching responsive React Email templates through SadaSend, you turn a mundane tax invoice into a delightful customer touchpoint while maintaining complete European VAT compliance.
| Paddle Webhook Event | Customer Lifecycle Trigger | Receipt Action |
|---|---|---|
| transaction.completed | Checkout or recurring renewal succeeds | Dispatches branded receipt with itemized VAT breakdown and PDF link |
| subscription.created | New customer initiates SaaS subscription | Sends welcome receipt with workspace onboarding links |
| subscription.past_due | Card payment fails on renewal attempt | Sends dunning notification with 1-click payment update link |
HMAC signature verification with constant-time comparison
Security is paramount when processing financial webhooks. An attacker could forge a transaction.completed payload to obtain paid software access without transferring funds.
Paddle includes an HMAC-SHA256 signature in the paddle-signature request header. Your webhook route handler must verify this signature using crypto.timingSafeEqual before parsing event contents.
Next.js 15 route handler: Automated Paddle receipt pipeline
Here is a production-ready Next.js 15 App Router handler verifying webhook authenticity and dispatching custom receipts via SadaSend:
import { NextRequest, NextResponse } from 'next/server';
import crypto from 'node:crypto';
import { SadaSend } from 'sadasend';
const sadasend = new SadaSend({ apiKey: process.env.SADASEND_API_KEY! });
export async function POST(req: NextRequest) {
const signature = req.headers.get('paddle-signature') || '';
const rawBody = await req.text();
// 1. Verify Paddle webhook HMAC signature
const hmac = crypto.createHmac('sha256', process.env.PADDLE_WEBHOOK_SECRET!);
const digest = hmac.update(rawBody).digest('hex');
const isValid = crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
if (!isValid) {
return NextResponse.json({ error: 'Unauthorized signature' }, { status: 401 });
}
const event = JSON.parse(rawBody);
// 2. Handle transaction.completed
if (event.event_type === 'transaction.completed') {
const { customer, details, currency_code } = event.data;
await sadasend.emails.send({
from: 'billing@yourdomain.com',
to: customer.email,
subject: `Receipt for Order #${event.data.id}`,
html: `<h1>Payment Receipt</h1><p>Amount: ${details.totals.total} ${currency_code}</p>`,
});
}
return NextResponse.json({ received: true });
}Frequently Asked Questions
Building AI agents that send email?
Scoped API keys, per-key recipient allowlists, approval mode and a hosted MCP server with twelve tools — 6,000 free sends monthly, without a credit card.
Building SadaSend — transactional email with an MCP server that has a ceiling. Writes about deliverability, email infrastructure, and what happens when you hand an autonomous agent a sending credential.