Short answer
Stripe sends a receipt email after each successful payment or refund when you turn on Successful payments or Refunds under Settings → Business → Customer emails. You can change the logo, colors, business details and language. You can't change the layout or the wording, and nothing is sent when a payment fails.
To send a receipt in your own template, listen for the invoice.paid event (subscriptions and invoiced payments) and send an email with the amount, the invoice number and the links Stripe gives you to the hosted invoice and its PDF. Then turn off Stripe's Successful payments email so customers don't get two receipts.
How Stripe receipt emails work
- When: after a successful payment, including invoice and subscription payments, and after a refund. For payment methods that confirm later, such as bank debits, the receipt waits until the payment succeeds.
- Turn on: Settings → Business → Customer emails, then Successful payments and Refunds.
- Test payments: Stripe's docs say to send a receipt for a test payment by hand, from the payment's Receipt history.
- Resend: open the payment, go to Receipt history, choose Send receipt and confirm the address.
- Link: every receipt has a web version at the charge's
receipt_url. The link expires after 30 days; after that Stripe asks for the customer's email and sends a fresh one. - From: stripe.com, unless you verify your own email domain in Stripe, which sends receipts from
receipts@on your domain.
For one-time payments through Checkout you can also have Stripe create a paid invoice (invoice_creation[enabled]=true), so the customer gets an invoice summary with PDF links to the invoice and the receipt. Stripe prices post-payment invoices for Checkout separately; see its receipts docs.
What you can customize
| Setting | Where | What it changes |
|---|---|---|
| Logo or icon | Settings → Business → Branding → Email receipts | Square PNG or JPG, at least 128 × 128 px, up to 512 KB |
| Brand and accent color | Same page | The colors of the email and Stripe's hosted pages |
| Business details | Settings → Business → Business details → Public details | Name, support email, support address and privacy policy link. Stripe requires these on receipts. |
| Language | Customer's preferred locale, or Default language in Customer emails | The language of the receipt |
| Sending domain | Customer emails → custom email domain | Sends from your domain instead of stripe.com |
From Stripe's receipts and branding docs.
Last checked on each vendor's public page.
That's the list. There's no field for your own subject line, intro text, product names in your words, a link back into your app, or a note like "your plan renews on the 5th". If you need any of those, send the receipt yourself.
Send your own receipt on invoice.paid
Stripe fires invoice.paid when an invoice is paid, which covers every subscription renewal and any invoice you charge. The invoice in the event has what a receipt needs:
| Receipt line | Invoice field |
|---|---|
| Amount paid | amount_paid (in the smallest currency unit) and currency |
| Invoice number | number |
| View online, update card | hosted_invoice_url |
invoice_pdf | |
| Who to send it to | customer_email, customer_name |
| What they bought | lines.data[0].description |
import express from "express";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const app = express();
// Stripe signs the raw body, so don't let a JSON parser touch this route.
app.post("/stripe/webhook", express.raw({ type: "application/json" }), async (req, res) => {
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
req.headers["stripe-signature"],
process.env.STRIPE_WEBHOOK_SECRET,
);
} catch (err) {
return res.status(400).send(`Webhook signature check failed: ${err.message}`);
}
if (event.type === "invoice.paid") {
const invoice = event.data.object;
// Skip $0 invoices, such as the one Stripe creates when a free trial starts.
if (invoice.amount_paid > 0 && invoice.customer_email) {
try {
await sendReceipt(event.id, invoice);
} catch (err) {
return res.status(500).send("Send failed; Stripe will retry"); // safe: the send is idempotent
}
}
}
res.sendStatus(200);
});
async function sendReceipt(eventId, invoice) {
const amount = new Intl.NumberFormat("en-US", {
style: "currency",
currency: invoice.currency.toUpperCase(),
}).format(invoice.amount_paid / 100); // divide by 1 for zero-decimal currencies like JPY
const item = invoice.lines.data[0]?.description ?? "your subscription";
const res = await fetch("https://api.groundmail.dev/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GROUNDMAIL_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `stripe:${eventId}`, // a retried event can't send twice
},
body: JSON.stringify({
from: "Example Billing <billing@mail.example.com>",
to: invoice.customer_email,
subject: `Your receipt for ${amount} (invoice ${invoice.number})`,
text: `We received ${amount} for ${item}.\n\nInvoice ${invoice.number}: ${invoice.hosted_invoice_url}\nPDF: ${invoice.invoice_pdf}`,
html: `<p>We received <strong>${amount}</strong> for ${escapeHtml(item)}.</p>
<p>Invoice ${invoice.number}</p>
<p><a href="${invoice.hosted_invoice_url}">View invoice</a> · <a href="${invoice.invoice_pdf}">Download PDF</a></p>`,
tags: [{ name: "type", value: "receipt" }],
}),
});
if (!res.ok) throw new Error(`Groundmail ${res.status}: ${await res.text()}`);
}escapeHtml stands in for your own helper: line item descriptions can contain characters that aren't safe in HTML. In Stripe, create the endpoint under Webhooks, subscribe it to invoice.paid, choose the snapshot payload so the event carries the whole invoice, and copy its whsec_ signing secret. For local testing, stripe listen --forward-to localhost:3000/stripe/webhook prints a secret and forwards test events.
The Idempotency-Key makes a resent event return the original send instead of a second email. Groundmail keeps keys for 24 hours and Stripe retries for up to three days, so also record the event ids you've handled; Send emails from Stripe webhooks shows how.
Or use the Receipt starter
With the Stripe integration connected, Automations → New → From Stripe → Receipt creates an automation that starts on stripe.invoice.paid and sends one email with the amount and a link to the invoice. Edit it like any template, with these fields:
| Field | Example |
|---|---|
{{event.data.customer.name}} | Maria |
{{event.data.invoice.amount}} | $29.00 |
{{event.data.invoice.number}} | A1F2-0042 |
{{event.data.invoice.url}} | Stripe's hosted invoice page |
{{event.data.invoice.pdf}} | Invoice PDF link |
{{event.data.plan.name}} | The first line item on the invoice |
It opens as a draft and sends from your verified domain on the transactional stream once you turn it on. The free receipt email template is a starting point for the design.
Turn off Stripe's receipt so customers don't get two
- 01Check your own receipt arrivesPay a test invoice and confirm your email went out with the right amount and links.
- 02Open Customer emails in StripeSettings → Business → Customer emails.
- 03Turn off Successful paymentsLeave Refunds on unless you also send your own refund emails.
Turning it off doesn't change the receipt itself: the charge still has a receipt_url, and you can still send Stripe's receipt by hand from the payment page when someone asks for it.
What a good receipt includes
- The amount and currency, and what it was for, in the words your customer uses.
- The date and the invoice or receipt number, so they can match it to a bank statement.
- A link to the invoice and the PDF, for their accountant.
- When the next payment is due, for subscriptions.
- Your business name and a way to reach support. If you replace Stripe's receipt, keep the details Stripe requires on its own: legal business name, support address, support email and privacy policy link.
Frequently asked questions
How do I turn on Stripe receipt emails?
In the Stripe dashboard open Settings → Business → Customer emails and turn on Successful payments (and Refunds if you want refund receipts).
Why didn't my test payment send a receipt?
Stripe's docs say to send receipts for test payments by hand: open the payment, go to Receipt history and choose Send receipt.
Can I change the text of a Stripe receipt?
No. You can change the logo, colors, business details and language. For your own subject and wording, send the receipt yourself on invoice.paid and turn off Stripe's Successful payments email.
Does Stripe send a receipt when a payment fails?
No. Receipts are only sent for successful payments. Failed subscription payments have their own email; see Stripe failed payment emails.
Which event should I send a receipt on?
invoice.paid for subscriptions and invoices. For one-time payments without an invoice, checkout.session.completed or payment_intent.succeeded.
Sources
Facts on this page about other companies' products, prices and policies come from these public pages. They change, so check them before you decide.