WebProCMS includes a built-in donations system — accept one-time and recurring gifts through Stripe, right on your own site. Stripe handles the payment form and the recurring billing; the CMS handles everything else: a configurable donate form, preset amounts and designations, goal-driven campaigns with thermometers, tribute gifts, fee-cover, peer-to-peer fundraisers, branded receipts and annual tax statements, a donor self-service portal, donor records, and a dashboard that tracks every gift and subscription.
What it does
- One-time and recurring gifts: donors choose One-time, Monthly, or Yearly. Recurring gifts are real Stripe subscriptions — they renew automatically and each renewal is recorded as its own donation. Monthly and yearly can each be turned off independently.
- A drop-in donate form: a single design-library row places a full donate form on any page — preset amount buttons, an optional custom-amount field, a frequency switch, a designation dropdown, and donor fields. It hydrates per visitor as a lazy island, so it works on fully cached pages without leaking anyone's input into the cache.
- Preset amounts + custom amounts: configure suggested amounts (e.g. 25 / 50 / 100 / 250) shown as buttons, a default pre-selected amount, and a minimum gift. Allow or disallow free-entry custom amounts.
- Designations ("Donate for X or Y"): maintain a simple admin list of causes or funds donors can direct their gift to (e.g. "General Fund", "Scholarships"). Leave the list empty to hide the dropdown entirely.
- Configurable donor fields: choose exactly which donor details to collect — name, phone, company, and a full mailing address — and which are required. Email is always collected and required so receipts can be sent.
- Embedded Stripe Checkout: the form hands off to Stripe's payment form rendered on your own
/donate/checkoutpage (no redirect to stripe.com). Apple Pay / Google Pay / Link come free, and Venmo, Cash App Pay, and PayPal appear automatically once you enable them as payment methods in your Stripe dashboard — no extra setup in the CMS. - No tax math: donations aren't taxable sales, so checkout deliberately ships with no automatic tax.
- A donations dashboard: every gift in one place — Donations (all completed gifts, searchable, filterable by donor email), Recurring (active and past subscriptions with last/next payment and one-click cancel-at-period-end), and Donors (everyone who's given, aggregated by email with total given and gift count).
- CRM integration: every completed donation creates or updates a CRM contact (source:
donation) and logs adonationinteraction on their timeline with the amount, frequency, and designation — so you can segment donors, send them campaigns, and see their giving history. - Donor buttons anywhere: a Donate Button item and a Donate CTA Banner row let you point visitors at the donate page from any header, footer, or content section.
Find it under Dashboard → Donations (off by default; toggle under Settings → Features). It uses the same site-wide Stripe keys as every other payment feature (Settings → API Keys).
The donate page
Toggling the feature on materializes one editable page into the page tree (same mechanism as Events and Ecommerce):
/donate(pages/donate/⚡index.blade.php) — a builder-editable page that leads with an editable heading and subheadline and embeds the donate form island. Edit the copy and design in the page builder like any other page; the form itself is driven entirely by your Donation Settings.
The checkout (/donate/checkout) and thank-you (/donate/checkout/return) pages stay module-owned — they're transactional, per-visitor pages that are never response-cached and never appear in the page editor.
Design library rows
Four pieces live in the design library's Donations category (visible only when the feature is enabled):
- Donations – Donate Form — the full donate form (heading + subheadline + the form island). Drop it on any page.
- Donations – Donate CTA Banner — a compact call-to-action band with a heading and a button to
/donate. - Donations – Campaign Thermometer — open campaigns as goal-thermometer cards with per-campaign Donate buttons.
- Donate Button — an "add item" snippet (a single button linking to
/donate) for headers, footers, and content rows.
All amounts, designations, and donor fields render from your Donation Settings, so one settings change updates every donate form on the site at once.
Campaigns with goal thermometers
Create goal-driven appeals under Dashboard → Donations → Campaigns: each campaign has a name, slug, description, a goal amount, optional start/end dates, and an active toggle. The dashboard lists every campaign with a live progress bar (raised ÷ goal, net of refunds), its paid-donation count, and a status badge (Active / Scheduled / Ended / Inactive). Campaigns without donations can be deleted; ones with recorded gifts deactivate instead, so giving history keeps its attribution.
- Donors pick a campaign on the donate form — when open campaigns exist, the form shows a Campaign select (empty option = general support).
/donate?campaign={slug}deep-links preselect one, and the thermometer row's Donate buttons use exactly that link. - Campaign attribution flows everywhere: onto the donation, onto the linked recurring plan, and onto every renewal — so campaign totals keep rolling up cycle after cycle.
- The Campaign Thermometer row (design library → Donations) shows open campaigns as cards with a progress bar, raised/goal amounts, percent, a days-left label, and a per-campaign Donate button. It's a data-driven collection row backed by the
donation_campaignspreset.
Tribute gifts + fee cover
- Dedicate this gift: donors can dedicate a gift In memory of or In honor of someone, and optionally send an ecard notification to a recipient email with a short personal message. Tributes show on the dashboard as a badge and in the receipt.
- Fee cover: an optional checkbox — "Add $X to cover processing fees" — computed live from the selected amount as
amount × percent + fixed(defaults 2.9% + $0.30, matching Stripe's standard card fee; editable on the General settings tab, or turn the offer off entirely). When checked, the charged total is base + fee, and the fee portion is recorded on the donation and shown on the dashboard.
Receipts, the donor portal, and tax statements
- Branded receipt emails (Donations → Settings → Emails; on by default) go out when a gift completes and on every recurring renewal, through the shared Marketing mail transport. Receipts include the gift summary (amount, date, frequency, designation, campaign, dedication, fee cover), the standard tax-receipt line ("No goods or services were provided in exchange for this contribution."), a pointer to the tax-statement page, and — for recurring gifts — the donor's private manage link. Sends are idempotent per donation (an email log claim), and subject/heading/body are editable with
{{tokens}}. - Tribute ecards (on by default) notify the person the donor named, quoting the donor's message.
- Donor self-service portal at
/donate/manage/{token}— the opaque link from recurring receipts. Donors see their plan (amount, frequency, designation, campaign, next payment, status) and giving history, can jump to Stripe's billing portal to update their payment method, and can cancel at period end with a confirmation step. Never cached, never indexed. - Annual tax statements at
/donate/tax-statement: a donor enters their email + year, and if paid donations exist a 7-day signed download link is emailed to them for a PDF statement (every gift that year with date, campaign/designation, amounts net of refunds, and the year total). The form never reveals whether an email has donations (no enumeration), is honeypotted, and is rate-limited per IP. - Member giving history at
/members/donations(the Donations pill in the account nav, plus a card on the account dashboard): every completed gift made with the signed-in account's email, their live recurring plans with a link to the self-service portal above, and a per-year statement download for each year they gave in. The statement link is the same signed URL the emailed flow mints — that flow exists to prove ownership of an address before sending donor data to it, and a signed-in donor has already proved it, so it's minted directly here for their own address only. See memberships.md → The account area.
Peer-to-peer fundraisers
Supporters can raise money on your behalf:
- Apply at
/donate/fundraise— pick an open campaign, name the fundraiser, set a goal, add a personal message. Submissions land as Pending review (honeypot + per-IP rate limit + human review keep spam out). - Approve from Donations → Campaigns → Fundraisers (the tab badges the pending count). Approval emails the owner their public share link and a private manage link.
- The public page at
/donate/f/{slug}shows the fundraiser's story, its own thermometer, the parent campaign, and a Donate button that deep-links the donate form (/donate?fundraiser={slug}). Gifts record both the fundraiser and its campaign, so they roll up into both thermometers. Pending/closed pages 404. - Owners self-manage at their private link: edit the message and goal, watch progress, see recent donations, and close the page. Managers can also close or delete any fundraiser from the dashboard.
Settings
Dashboard → Donations → Settings has four tabs:
- General — currency, preset amounts, default selected amount, allow/disallow custom amounts, minimum amount, which recurring frequencies (monthly/yearly) are offered, the fee-cover offer (on/off, percent, fixed amount), the designation prompt label, and the designation list.
- Donor Fields — per-field Collect / Required toggles for name, phone, company, and address fields. Email is always on.
- Emails — the donations sender (from name/email, falling back to the Marketing default), plus per-type enable switches and editable subject/heading/body for the receipt, tribute ecard, and fundraiser-approved emails. Copy supports
{{tokens}}listed on the tab. - Stripe — the donations webhook URL to register in Stripe (with the exact events to subscribe to), its signing secret, and a reminder that the publishable/secret keys live on the shared API Keys page.
How payments flow
- The visitor fills out the donate form. On submit, their choices (amount, frequency, designation, donor details) are stashed in their session and they're sent to
/donate/checkout. - The checkout page creates a pending donation and a Stripe Checkout Session —
paymentmode for one-time gifts,subscriptionmode for recurring — and renders Stripe's embedded payment form. - On success, Stripe sends the visitor to the thank-you page and fires a webhook. Whichever lands first finalizes the donation (it's idempotent — the other is a no-op): the gift is marked paid, the donor snapshot is backfilled from Stripe, a recurring gift gets a linked subscription record, the donor is pushed to the CRM, and the receipt (plus any tribute ecard) goes out exactly once.
- Recurring renewals arrive later as
invoice.paidwebhooks and are recorded as additional donations against the same subscription (inheriting its campaign, with their own receipt). Cancellations, refunds, and abandoned checkouts are all handled via webhook too.
Requirements
- A Stripe account with the site-wide keys configured on Settings → API Keys (shared with Memberships and Ecommerce).
- A Stripe webhook endpoint pointed at the donations webhook URL (shown on the Stripe settings tab), subscribed to
checkout.session.completed,checkout.session.expired,invoice.paid,customer.subscription.deleted, andcharge.refunded.
Donations is a membership-gated feature.