Giving Tuesday 2026 falls on 1 December. If you build or maintain WordPress sites for nonprofits, you will get the "is the site ready?" email sometime in early November, and the honest answer is that nobody can tell you without opening four dashboards and checking. This page is that check, in order, with what a pass looks like.
How do I make sure our donation page can handle Giving Tuesday?
Start from the failure mode, not the feature list. On a busy day, donation pages almost never fail by refusing payments. They fail by taking the payment and never recording it. The donor's card is charged, the money lands in the Stripe or PayPal balance, and the website shows nothing. No receipt, no campaign thermometer movement, no row in the donations table. Then someone reconciles in January and finds a gap.
That single failure pattern has four common causes. Work them in this order, because the first one is the most common and the cheapest to fix.
- The webhook endpoint points at the wrong host. Usually a staging URL left behind after a rebuild, or a URL from the site's old domain.
- Test keys in production, or the reverse. Real gifts charged against a sandbox, or test gifts that look real in your reports.
- A page cache or CDN is intercepting the plugin's REST routes. The processor gets a cached or 403 response and records a delivery failure.
- Receipt email has never been tested at volume. It works for you at your desk; it silently fails to 200 Gmail addresses in ten minutes.
1. Is the webhook endpoint live, and pointed at production?
Donor Merchant 2.5.2 listens on two REST paths:
https://yourdomain.org/wp-json/donor-merchant/v1/webhook/stripehttps://yourdomain.org/wp-json/donor-merchant/v1/webhook/paypal
Open the Stripe Dashboard, go to Developers then Webhooks, and read the destination URL character by character. The failure you are hunting for is a hostname like staging.yourdomain.org, yourdomain.wpengine.com, or an old pre-migration domain. It will look plausible at a glance. Do the same in the PayPal Developer dashboard under your app's webhooks.
Then read the delivery log, which is the part most people skip. Stripe shows recent attempts and response codes per endpoint. A healthy endpoint shows a run of 200s. A sick one shows 404, 403, 301 or timeouts. Stripe's webhook documentation says it retries a failed delivery with exponential backoff for up to three days in live mode (commonly reported as roughly 16 attempts) and then disables the endpoint. PayPal retries a non-2xx response up to 25 times over about three days per PayPal's webhooks overview, and stops the moment it receives a 2xx.
The practical consequence: if your endpoint is broken on 1 December and you notice on 5 December, the events are gone. Stripe will not replay them automatically. You will be re-keying gifts by hand from the processor's payments list. Our guide on Stripe donations not showing in WordPress covers the recovery path if that has already happened to you.
2. Test keys versus live keys, and how to test without spending real money
Donor Merchant ships a test mode. Use it deliberately in the first week of November: turn it on, put Stripe's test keys in, run a one-off gift, a monthly recurring gift, a PayPal gift, a tribute gift, and a gift with fee recovery enabled. Confirm each one lands in the donations table, fires a receipt, and moves the campaign thermometer. That is how you test a donation form without spending real money, and it is far better than the alternative of charging your own card £1 and refunding it, which costs you the fee and teaches you less.
Then switch back. Write it on a sticky note. The number of sites that go into December still in test mode is not zero, and the symptom is deeply confusing: the form works perfectly, the thank-you page appears, and no money ever arrives.
The reverse disaster is worse and more common. You point the webhook at staging while you test, then push to live and forget to move it back. Real donors are now really charged, and your site records nothing. This is the number one cause of the January reconciliation gap, and it is why reconciling payouts against your donation records is worth doing monthly rather than annually.
3. Is a page cache or CDN swallowing your REST routes?
This one is genuinely hard to spot because the site gets faster and the donation form still looks fine. What breaks is the machine-to-machine traffic behind it.
Caching layers that serve a stored copy of /wp-json/ responses, or that strip cookies and nonces from the donation page, will break a payment integration in ways that look like a plugin bug. GiveWP's own support forum carries repeated threads on exactly this, including cached donor payment history; the standard remedy across those threads is to exclude the donation, donation-confirmation and donor-dashboard paths from cache. Cloudflare's community has the parallel case for the REST API, where a cached nonce produced 403s on wp-json. The rule that follows from both: never cache a page that carries a nonce, and never cache a REST namespace that handles payments.
Exclude, at minimum: the REST namespace /wp-json/donor-merchant/, your donation page, your thank-you or confirmation page, and the donor portal page (/my-donations/ by default).
| Layer | Where the exclusion lives |
|---|---|
| WP Rocket | Advanced Rules, Never Cache URL(s). Add the donation, confirmation and portal paths plus /wp-json/donor-merchant/(.*) |
| W3 Total Cache | Page Cache, Advanced, "Never cache the following pages" |
| LiteSpeed Cache | Cache, Excludes, "Do Not Cache URIs" |
| Cloudflare (including APO) | A Cache Rule with "Bypass cache" where URI Path starts with /wp-json/. Do not stack a Cache Everything rule on top of APO |
| SiteGround Optimizer | Caching, "Exclude URLs from caching" (applies to Dynamic Cache too) |
| WP Engine | Cache exclusions are set through the user portal or support; object cache also needs checking |
| Kinsta | MyKinsta, Tools, cache exclusion URLs |
Verify the fix rather than trusting it. Request the Stripe webhook URL directly in a browser: you should get a JSON error from WordPress, not an HTML page, not a 403, and not a Cloudflare challenge page. If the response carries a cf-cache-status: HIT header, you are not done.
4. Will the receipt email actually arrive?
Test this in November, not on 2 December. Send a real test receipt to three mailboxes: a Gmail address, an Outlook or Hotmail address, and an address on the organisation's own domain. Then check where it landed, not just whether it sent.
Validity's 2023 Email Deliverability Benchmark Report, summarised here, found that over 16% of nonprofit email either goes to spam or is never delivered at all. The usual causes on a WordPress site are mundane: host-level throttling of wp_mail() once you exceed an hourly quota, a From address on a domain the server is not authorised to send for, and missing SPF, DKIM or DMARC alignment.
Check that the From address on your email receipts is on a domain whose DNS publishes SPF and DKIM records covering whatever is actually sending, and that DMARC alignment passes. If your host throttles, route transactional mail through a dedicated SMTP service before the spike, not after. A donation receipt is transactional email, not marketing: it should not be sent through your newsletter tool, and it should not carry an unsubscribe link that a donor could use to switch off their own tax documentation.
A donation made on December 31 was receipted for the next year - why?
Because three systems disagree about what day it is, and each of them is right from its own point of view.
- Your website dates the gift using the WordPress timezone setting in Settings, General. If that is still on UTC and your organisation is in Los Angeles, a gift at 4:30pm on 31 December Pacific is already 1 January in UTC.
- The processor dates the charge by its own account timezone, which you set separately in Stripe or PayPal and probably never looked at.
- The bank sees the money later still, because a card charge on 31 December typically settles in the first days of January and ACH later than that.
Fix the controllable part first: open Settings, General and set the WordPress timezone to the organisation's actual city before December, not to a UTC offset (a city handles daylight saving; an offset does not). Then set the Stripe and PayPal account timezones to match. Then decide, once, which date your receipts and annual statements use, and write it down for whoever prepares the January mailing.
On substance rather than software: for a US donor, a gift charged to a credit card on 31 December is generally treated as made on 31 December even though it settles in January, and a cheque is generally treated by the postmark date. That is the practical rule most organisations work to, and it is why the settlement date is the wrong field to build your statements on. Tax treatment varies by jurisdiction and by circumstance, so confirm the specifics with your accountant before you send anything a donor will file. Donor Merchant's annual year-end tax statements are generated from the donation date recorded on the site, which is exactly why the timezone setting matters.
Will per-IP rate limiting block real donors during a surge?
Donor Merchant's donation form protection is a honeypot field plus per-IP rate limiting. There is no CAPTCHA, and we are not going to claim parity with Stripe Radar plus reCAPTCHA. The tradeoff is deliberate: a honeypot costs the donor nothing and blocks unsophisticated bots, while rate limiting caps how fast a single address can hammer the form.
There is one real false-positive case, and it is worth knowing before the day: many legitimate donors behind a single NAT. A church giving appeal made from the pews on the building's wifi, a workplace matching drive, a school. Every one of those donors presents the same public IP. If legitimate donors report being blocked, that is almost always what happened. Check the IP in your server logs against the venue, and confirm your host is passing the real visitor IP rather than a proxy address (if you are behind Cloudflare without mod_remoteip or the equivalent, every visitor looks like the same handful of edge IPs, which will absolutely produce false positives).
Card-testing bots pick high-traffic days on purpose
Donation forms are the preferred target for card testing because they accept arbitrary amounts, do not require an account, and are often on sites with no fraud tooling. Attackers prefer busy periods because the noise hides them. Because you own the Stripe integration directly rather than renting it through a platform, you can configure Radar rules in your own Stripe account before the event: block or review small-amount charges from high-risk countries, throttle repeat attempts from one IP, and turn on the 3D Secure rules that suit your donor base. Our guide on stopping a card-testing attack on a donation form has the specific rule set and the cleanup steps if one is already running.
Campaign, fund and form hygiene
The boring half hour that prevents the awkward email on the day:
- Set the campaign goal and start date so the thermometer starts where you intend rather than at whatever last year left behind.
- Check that your designations and funds are named exactly as the appeal copy names them. If the email says "Winter Shelter Fund" and the dropdown says "Shelter (winter)", donors will ask, and your finance volunteer will have to map them by hand.
- Confirm the form's accent colour and button shape still read correctly against whatever December banner or seasonal theme change is going up. Theme changes and form styling are set in different places and nobody checks both.
- Load the donation page on a real phone on cellular data, not on desktop with devtools. Then complete a test gift on it.
- Confirm recurring options are switched on. Monthly giving is the highest-value thing on the page and the easiest to leave disabled.
December cheques that arrive in January
Cheques postmarked in December routinely land in the first two weeks of January, and the donor expects them on the December statement. Enter them through offline donation entry with the correct December date, before you generate annual statements. Doing it in that order means the statement is right the first time and you are not reissuing corrected documents in February. Offline entry is free, as is everything else in the plugin.
The pre-flight table
| # | Check | Where | Pass looks like | Time |
|---|---|---|---|---|
| 1 | Stripe webhook URL | Stripe, Developers, Webhooks | Live hostname, HTTPS, path /wp-json/donor-merchant/v1/webhook/stripe | 5 min |
| 2 | PayPal webhook URL | PayPal Developer, your app | Live hostname, path ends /webhook/paypal | 5 min |
| 3 | Delivery logs | Both processor dashboards | Recent attempts all 200 | 5 min |
| 4 | Live keys, test mode off | Donor Merchant settings | Live keys saved, test mode disabled | 5 min |
| 5 | Cache and CDN exclusions | Cache plugin plus Cloudflare | /wp-json/, donation, confirmation and portal pages bypassed | 20 min |
| 6 | Receipt deliverability | Gmail, Outlook, own domain | Inbox, not spam, in all three | 20 min |
| 7 | SPF, DKIM, DMARC | DNS plus a mail tester | All three pass and align with the From domain | 20 min |
| 8 | WordPress timezone | Settings, General | Set to a city, matching the processor account | 2 min |
| 9 | Stripe Radar rules | Stripe, Radar | Card-testing rules active before the event | 20 min |
| 10 | Campaign, goal, funds | Donor Merchant, Campaigns | Goal and dates set, fund names match the appeal | 15 min |
| 11 | End-to-end test gift | Test mode, then live | Recorded, receipted, thermometer moved | 30 min |
What this checklist is not
It is not a marketing plan. There is nothing here about email sequences, countdown timers, UTM parameters or social banners, and plenty of other people write those well. It is also honest about what Donor Merchant does not ship: there is no load-testing tool and no CAPTCHA in the plugin. Load testing, if you need it, is a question for your host, and most shared hosting will not survive a genuine spike regardless of what your plugin does.
The reason a checklist like this exists here and not on a platform vendor's blog is structural. If the infrastructure being checked is the vendor's own, publishing the failure list admits the failures. On a self-hosted plugin the infrastructure is yours, so the list is just useful. If you want a hand working through it before December, that is what the support plans are for; the plugin itself stays free either way.