Custom Stripe Receipts & Invoicing with React Email
Replace Stripe’s generic default receipts with pixel-perfect, tax-compliant branded emails sent from your own domain. Powered by Webhooks, React Email, and sub-150ms European dispatch.
Default Stripe receipts vs. Branded React Email
Default receipts from Stripe arrive from a shared @stripe.com domain with rigid branding. With SadaSend and React Email, receipts originate directly from your custom domain with your exact aesthetic.
Payment Confirmed
Thank you for your business. Your payment of $79.00 was processed on Nov 6, 2026.
Stripe Webhook Event Explorer
Select an event payload to inspect how incoming Stripe billing webhooks map cleanly to SadaSend email dispatch parameters.
{
"id": "evt_1Qx8N42eZvKYlo2CLm89K1zP",
"type": "invoice.payment_succeeded",
"data": {
"object": {
"id": "in_1Qx8N42eZvKYlo2C",
"customer_email": "finance@acme-corp.com",
"customer_name": "Acme Corporation",
"amount_paid": 7900,
"currency": "usd",
"subtotal": 7900,
"tax": 0,
"invoice_pdf": "https://pay.stripe.com/invoice/inv_123/pdf",
"hosted_invoice_url": "https://invoice.stripe.com/i/acct_123/inv_123",
"lines": {
"data": [
{
"description": "SadaSend Pro Plan (Monthly)",
"amount": 7900,
"quantity": 1
}
]
}
}
}
}Field Mapping Contract
Triggered whenever a customer’s recurring SaaS subscription or one-time invoice is paid.
Customer billing destination
Formatted invoice item table
Formatted monetary total ($79.00)
Direct link or PDF attachment
Production Webhook Recipes
Ready-to-deploy webhook handlers for Next.js 15, Express, FastAPI, and modular React Email components.
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';
import Stripe from 'stripe';
import { SadaSend } from 'sadasend';
import { render } from '@react-email/render';
import { ReceiptEmail } from '@/emails/ReceiptEmail';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-12-18.acacia' as Stripe.LatestApiVersion,
});
const sadasend = new SadaSend({
apiKey: process.env.SADASEND_API_KEY!,
});
export async function POST(req: Request) {
const body = await req.text();
const headersList = await headers();
const signature = headersList.get('stripe-signature');
if (!signature) {
return NextResponse.json({ error: 'Missing signature' }, { status: 400 });
}
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
const message = err instanceof Error ? err.message : 'Unknown signature error';
return NextResponse.json({ error: `Webhook signature verification failed: ${message}` }, { status: 400 });
}
if (event.type === 'invoice.payment_succeeded') {
const invoice = event.data.object as Stripe.Invoice;
const recipientEmail = invoice.customer_email;
if (!recipientEmail) {
return NextResponse.json({ received: true, note: 'No recipient email present' });
}
// Render branded React Email receipt component
const emailHtml = await render(
ReceiptEmail({
customerName: invoice.customer_name || 'Valued Customer',
invoiceNumber: invoice.number || invoice.id,
amountFormatted: `$${(invoice.amount_paid / 100).toFixed(2)}`,
items: invoice.lines.data.map((l) => ({
description: l.description || 'Subscription',
amount: `$${(l.amount / 100).toFixed(2)}`,
})),
pdfUrl: invoice.invoice_pdf || undefined,
date: new Date(invoice.created * 1000).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
}),
})
);
// Dispatch via SadaSend with custom domain DKIM and Idempotency key
const result = await sadasend.emails.send({
from: 'billing@yourdomain.com',
to: recipientEmail,
subject: `Your Receipt: ${invoice.number || 'Payment Confirmed'}`,
html: emailHtml,
idempotencyKey: `stripe_evt_${event.id}`,
});
if (!result.ok) {
console.error('SadaSend receipt dispatch rejected:', result.error);
return NextResponse.json({ error: 'Failed to send receipt email' }, { status: 500 });
}
}
return NextResponse.json({ received: true });
}3-Step Integration Workflow
Disable Generic Stripe Receipts
In your Stripe Dashboard, navigate to Settings → Customer emails. Toggle OFF "Successful payments". This prevents Stripe from sending generic grey receipts from its shared domain.
Register Your Secure Webhook Endpoint
In Developers → Webhooks, add your endpoint URL (e.g. https://yourdomain.com/api/webhooks/stripe) and subscribe to the invoice.payment_succeeded and checkout.session.completed events.
Render & Dispatch with SadaSend
Listen for the webhook, verify the cryptographic signature using your webhook signing secret, render your branded React Email template, and dispatch with 100% custom domain DKIM alignment.
Engineered for Sovereign B2B Deliverability
100% Custom Domain DKIM Alignment
Default Stripe receipts originate from shared @stripe.com pools. Sending receipts from billing@yourdomain.com with Dual-DKIM establishes strong domain reputation and avoids customer confusion.
✓ Eliminates spoofing warnings & reinforces your SaaS brandPre-Send Bounce Defense on Signups
In-flight syntax, disposable domain, and MX verification filter out typoed or throwaway emails before dispatch, protecting your sender score and saving delivery fees.
✓ Protects domain health against burner checkoutsIdempotency Key Webhook Protection
Stripe retries webhook dispatches upon network timeouts. Passing idempotencyKey: `stripe_evt_${event.id}` guarantees SadaSend will never send duplicate receipt emails to paying customers.
✓ Zero double-charges or duplicate receipt confusionTax & VAT Invoice Transparency
Incorporate customer tax IDs, European reverse-charge VAT notes, and direct links to Stripe-hosted PDF invoices directly inside your styled email layout.
✓ Full B2B tax compliance without manual accounting ticketsFrequently Asked Questions
Ready to brand your Stripe billing emails?
Start with 6,000 free emails per month. No credit card required.