Blog/Workflows

Stripe to Donation Receipt: The Complete Nonprofit Workflow

How nonprofits turn every Stripe charge into an IRS-compliant tax receipt — including the edge cases nobody warns you about until tax season.

Updated May 202610 min read

Stripe is the rails for online giving at most modern nonprofits — whether donations flow through Donorbox, Givebutter, Classy, a custom checkout, or a Payment Link. The problem isn't accepting the gift. It's everything that has to happen between Stripe's charge.succeeded webhook and a donor receipt that holds up to IRS scrutiny.

Done well, the workflow is invisible. Donor donates, donor gets a real receipt within seconds, your CRM stays in sync, and your year-end statements practically write themselves. Done poorly, you're exporting CSVs in late January, deduping by hand, and apologizing to donors whose receipts never arrived.

This guide walks through the full pipeline — what data to capture, how to handle fees, refunds, and recurring gifts, and where most nonprofits trip themselves up.

The Five Stages of the Stripe → Receipt Pipeline

1. Capture

The donor pays through your checkout. Stripe creates a Charge or PaymentIntent and fires charge.succeeded. Attach metadata at this step (campaign, donor email, designation) — you cannot add it reliably after the fact.

2. Normalize

Translate the Stripe object into a donation record: gross amount, currency, donor email, donor name, date, source, fee amount, net amount. Store the Stripe charge_id as your idempotency key.

3. Match

Look up or create a donor record. Email is the most reliable match; Stripe Customer IDs help for recurring donors. Avoid creating duplicates by normalizing the email (lowercase, trimmed) before matching.

4. Receipt

Generate a PDF receipt with your organization name, EIN, donor name, gross amount, date, and the required goods-and-services statement. Email it. Log the delivery so you can prove the donor was acknowledged.

5. Reconcile

Listen for charge.refunded, charge.dispute.created, and refund.updated. Mark the donation as refunded, invalidate the receipt, and send a corrected acknowledgment if needed.

What to Capture from Each Stripe Charge

From the Charge

  • id — your idempotency key
  • amount and currency — the gross gift
  • created — donation date (use the donor's locale, not UTC)
  • billing_details.email and name
  • balance_transaction.fee — for reconciliation, not the receipt

From Metadata

  • campaign — which appeal generated the gift
  • designation — restricted vs. unrestricted
  • fee_covered — true if the donor covered processing
  • on_behalf_of — tribute or memorial gifts
  • donor_id — your internal CRM key, if you have one

The single highest-leverage change most nonprofits can make: start writing metadata on every charge today, even if you don't have a pipeline to use it yet. Future-you will be grateful when you're trying to attribute last year's giving day spike.

The Edge Cases That Bite at Tax Time

Donor-covered fees

When a donor opts to "cover fees," the gross charge goes up — that's the contribution. Receipt the full amount, not the original base. If you receipt only the base, you're underreporting their deductible gift.

Refunds and disputes

A refund issued in February for a December gift means you have a receipt out in the world that's no longer accurate. Send a void/correction notice. Your annual statement should exclude refunded charges entirely.

Recurring subscriptions

Each invoice.payment_succeeded webhook is a separate gift. Send a receipt every time. Then deliver an annual summary that combines all charges from the calendar year — donors and their accountants vastly prefer one statement to twelve emails.

Year-end timing

A donation made at 11:58 PM on December 31 still counts for that tax year. Use the charge's created timestamp converted to the donor's locale — not your server's UTC clock — to assign the gift to the right calendar year.

Duplicate donors

The same person can show up as JANE@example.com, jane@example.com, and Jane Smith with no email at all. Normalize on insert and reconcile in your year-end summary, or you'll send three smaller statements instead of one.

What the Receipt Must Actually Say

The IRS doesn't care that the gift came through Stripe. The acknowledgment still has to meet Publication 1771's requirements. Every receipt your pipeline produces needs:

  • Your organization's legal name and EIN
  • The donor's name (Stripe gives you billing_details.name)
  • The contribution amount in USD
  • The date of the contribution
  • A statement that no goods or services were provided in exchange (or a good-faith description and value if any were)
  • For gifts of $250 or more, delivery before the donor files their return

Stripe's built-in email receipt — the one you toggle on in the dashboard — is a payment confirmation. It doesn't include your EIN, your 501(c)(3) status, or the goods-and-services language. Don't rely on it for tax purposes.

Build It Yourself or Plug In a Service?

A minimum-viable internal version is genuinely buildable: a webhook handler, a PDF template, an email sender, a donor table. Most nonprofits we talk to had one until they didn't — until the engineer who built it left, until refund webhooks started failing silently, until someone realized last year's recurring donors never got annual statements.

The break-even point usually shows up around the second tax season, not the first. The pipeline isn't hard to build. It's hard to keep working through schema changes, recurring-gift edge cases, and IRS guidance updates while your team also runs a nonprofit.

Frequently Asked Questions

Should the donation receipt show the gross gift or the amount after Stripe fees?

Show the gross amount the donor paid. The IRS cares about the donor's actual contribution, not what the nonprofit netted. If the donor opted to cover processing fees, that full amount is the contribution. Reconciling Stripe fees against the gross is an accounting concern, not a receipting one.

Do recurring Stripe donations need a receipt every month?

Yes. Each successful charge is a separate contribution and should be acknowledged. Most nonprofits send a per-charge receipt and an annual summary in January that lists every gift made during the prior calendar year. The IRS requires written acknowledgment for any single contribution of $250 or more before the donor files their return.

What happens when a Stripe charge is refunded or disputed?

If a refund happens after a receipt was issued, send a corrected acknowledgment that voids the original. Listen for the charge.refunded and charge.dispute.created webhooks so the receipt is never out of sync with the underlying charge. A donor who deducts a refunded gift is a real audit risk for both sides.

Can I use Stripe receipts as donation receipts?

No. Stripe's built-in email receipt is a payment confirmation — it doesn't include your EIN, 501(c)(3) status, or the required goods-and-services statement. The IRS treats them as transaction records, not contemporaneous written acknowledgment. You need a separate receipt that meets Publication 1771 requirements.

How do I attribute Stripe charges to the right donor record?

Use Stripe metadata. When the charge is created, attach donor_id, campaign, or any internal IDs you need. On the webhook, look up the donor by email first, then by metadata. Stripe Customer objects help for recurring donors, but the source of truth for receipting should be your own donor table.

Connect Stripe, Send Compliant Receipts

Thankly connects to Stripe in a few clicks, listens for every charge, refund, and recurring payment, and sends IRS-compliant receipts automatically. Works with Donorbox, Givebutter, Classy, and custom Stripe checkouts.

Related Resources