Skip to content
SaaS Billing & Receipts

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.

Customer Experience

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.

From: billing@yourdomain.com
S
Acme Cloud, Inc.
PAID · #INV-8891

Payment Confirmed

Thank you for your business. Your payment of $79.00 was processed on Nov 6, 2026.

SadaSend Pro Plan (Monthly)$79.00
Included Team Seats (5)$0.00
Total Paid$79.00
Webhook Ingestion

Stripe Webhook Event Explorer

Select an event payload to inspect how incoming Stripe billing webhooks map cleanly to SadaSend email dispatch parameters.

invoice.payment_succeeded (Payload JSON)Webhook Event ID: evt_1Qx8N42eZvKYlo2CLm89K1zP
JSON
{
  "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_email→ to

Customer billing destination

lines.data[].description→ html (items)

Formatted invoice item table

amount_paid→ html (total)

Formatted monetary total ($79.00)

invoice_pdf→ attachments

Direct link or PDF attachment

Idempotency Guarantee: SadaSend uses the Stripe event ID as an idempotency key to prevent duplicate receipt emails on webhook replays.
Developer Implementation

Production Webhook Recipes

Ready-to-deploy webhook handlers for Next.js 15, Express, FastAPI, and modular React Email components.

app/api/webhooks/stripe/route.tsProduction Next.js 15 route handler with Stripe webhook signature validation, React Email rendering, and SadaSend dispatch.
TYPESCRIPT
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 });
}
Setup Guide

3-Step Integration Workflow

1

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.

2

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.

3

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.

Architecture & Compliance

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 brand

Pre-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 checkouts

Idempotency 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 confusion

Tax & 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 tickets
Developer Questions

Frequently Asked Questions

Ready to brand your Stripe billing emails?

Start with 6,000 free emails per month. No credit card required.

Create Free Account