=== Donor Merchant ===
Contributors: mattmcwilliam
Tags: donations, donate, fundraising, recurring donations, nonprofit
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 2.5.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Free donation plugin for WordPress - one-time and recurring donations via Stripe and PayPal, with donor tracking, receipts, and reports.

== Description ==

**Donor Merchant** is a lightweight donation plugin for nonprofits, churches, schools, and community groups. Drop the donation form on any page with a block or shortcode, connect Stripe or PayPal, and start accepting gifts in minutes.

Unlike most donation plugins, **recurring giving is included in the free plugin** - monthly, quarterly, and yearly gifts powered by Stripe subscriptions, at no extra cost and with no transaction fees added by us.

= Features =

* **One-time and recurring donations** - monthly, quarterly, and yearly giving built in, free.
* **Campaigns with goal thermometers** - run multiple donation forms, each with its own goal, suggested amounts, and description, and show a live progress bar.
* **Donor portal** - a passwordless, magic-link dashboard where donors view their giving history, download receipts, and manage recurring gifts (update card or cancel) via Stripe's secure billing portal.
* **Printable receipts & annual tax statements** - every gift gets a save-as-PDF receipt, and donors can download a year-end giving statement. No PDF library bloat.
* **Import your history** - move donor and donation records in from GiveWP, Charitable, Donorbox, or a spreadsheet by CSV, with automatic column detection, a preview step, and duplicate protection.
* **Offline donations** - record checks, cash, and gifts taken elsewhere so donor totals stay complete.
* **Stripe** - cards, Apple Pay, Google Pay, and **bank debit (ACH)** via Stripe's secure Payment Element. Card details never touch your server.
* **PayPal** - one-time gifts through Smart Buttons, plus **recurring PayPal subscriptions**.
* **Designations / funds** - let donors choose which fund their gift supports.
* **Integrations** - outgoing webhooks (Zapier, Make, or anything) and Mailchimp audience sync.
* **Custom fields** - add your own questions to the donation form.
* **Fee recovery** - let donors add a little extra so 100% of the gift reaches you.
* **Tributes and memorials** - donors can dedicate a gift in honor or in memory of someone.
* **Donor management** - every donor and gift tracked automatically, with lifetime totals and CSV export.
* **Email receipts** - customizable thank-you receipts and new-donation notifications.
* **Reports dashboard** - total raised, monthly totals, average gift, recurring count at a glance.
* **Test mode** - try everything end to end with Stripe test keys and the PayPal sandbox before going live.
* **International** - 20 currencies supported.
* **Block and shortcodes** - the Donation Form block, plus `[donor_merchant]`, `[donor_merchant campaign="N"]`, `[donor_merchant_progress campaign="N"]`, and `[donor_merchant_portal]`.

= How it stays secure =

Payment details are collected by Stripe and PayPal directly in the donor's browser. No card number, expiration date, or CVC is ever transmitted to or stored on your server. Donation totals are always recomputed server-side, payment status is confirmed server-to-gateway, and webhook signatures are verified.

= External services =

To process donations this plugin connects to third-party payment and (optionally) marketing services. Nothing is sent until you enable and configure a given service.

* **Stripe** (only when Stripe is enabled). The Stripe.js library is loaded from `https://js.stripe.com` on pages that display the donation form, so payment details can be collected securely in the donor's browser. Your server also calls the Stripe API at `https://api.stripe.com` to create payment intents, customers, and subscriptions, and to verify payments. Data sent: donation amount and currency, the donor's name and email, and the payment details the donor enters (handled by Stripe - they do not touch your server). Stripe terms: https://stripe.com/legal - privacy: https://stripe.com/privacy
* **PayPal** (only when PayPal is enabled). The PayPal JS SDK is loaded from `https://www.paypal.com` on pages with the donation form. Your server calls the PayPal API (`https://api-m.paypal.com`, or `https://api-m.sandbox.paypal.com` in sandbox mode) to create and capture orders and subscriptions. Data sent: donation amount, currency, and an internal reference. PayPal terms: https://www.paypal.com/legalhub - privacy: https://www.paypal.com/privacy
* **Mailchimp** (optional - only when you enable it and the donor opts in). Your server calls the Mailchimp API (`https://<dc>.api.mailchimp.com`) to add the donor to the audience you choose. Data sent: donor email and name. Mailchimp terms: https://mailchimp.com/legal/terms/ - privacy: https://mailchimp.com/legal/privacy/
* **Outgoing webhooks** (optional). If you enter one or more webhook URLs, completed-donation data is POSTed to those URLs - which you control and choose (e.g. your Zapier or Make endpoint). No third party receives data unless you configure it.

= Getting started =

1. Install and activate the plugin.
2. Go to **Donor Merchant → Settings → Payments** and paste your Stripe and/or PayPal API keys.
3. Add the **Donation Form** block (or `[donor_merchant]`) to any page.
4. Test with Stripe test mode, then switch to live.

== Frequently Asked Questions ==

= Is recurring giving really free? =

Yes. Monthly, quarterly, and yearly donations via Stripe are part of the free plugin. You pay only the payment processor's own fees.

= Do I need an SSL certificate? =

Yes. Stripe and PayPal require your site to be served over HTTPS.

= Where do I find my Stripe API keys? =

In your Stripe dashboard under **Developers → API keys**. Use the test keys with Test mode to try things out safely.

= Does the plugin store card numbers? =

No, never. Card details are entered into fields hosted by Stripe (or on PayPal's site) and never reach your server.

= Does the plugin run background tasks or phone home? =

No. Donor Merchant sends no telemetry and performs no analytics, tracking, or license checks. It never contacts our servers.

When a page loads and nobody is donating, the plugin makes no external or background server requests. The donation form and the goal thermometer render entirely from your own database, and there is no AJAX polling or heartbeat activity.

On pages that contain the donation form, the visitor's browser loads Stripe.js and/or the PayPal SDK from the gateway's own CDN. That is required so card details go straight into the gateway's secure fields and never touch your server. No donor or payment data is sent until someone submits, and on pages without a form those scripts are not loaded at all.

Your server contacts Stripe or PayPal only when a visitor submits a donation, when a gateway sends an incoming signature-verified webhook, when a donor manages a recurring gift in the donor portal, and, if you enable them, when an outgoing webhook or Mailchimp sync fires on a completed donation.

The plugin schedules one WordPress cron event. On most days it does nothing; on the first of each month it emails the fundraising summary to your own admin address if that option is enabled. It makes no external request.

= Can donors cover the processing fees? =

Yes - enable fee recovery in settings and donors can opt in to add the estimated processing fee to their gift.

= Are webhooks required? =

Recommended, but not required for one-time gifts. Recurring renewal payments are recorded via the Stripe webhook, so set it up if you accept recurring gifts. The settings page shows the exact URL and events to use.

= Does it work with my theme? =

The form ships with clean, self-contained styling that adapts to any well-built theme, and everything is namespaced to avoid conflicts.

== Screenshots ==

1. The donation form with recurring options, suggested amounts, and fee recovery.
2. Dashboard with fundraising totals at a glance.
3. Donations list with status filters, search, and CSV export.
4. Donor list with lifetime giving totals.
5. Settings - payments tab.

== Changelog ==

= 2.5.2 =
* Fix: in the donation form, a dropdown rendered a few pixels taller than the text field beside it in the same row, because inputs and selects were using different line heights. All form controls now share one line height and match exactly.
* The optional dedication section now uses a solid border rather than a dashed one, which reads as a real part of the form instead of a placeholder, and its heading and field labels have been shortened.

= 2.5.1 =
* Security: the public donation endpoint no longer reuses a donor's stored Stripe customer. Every recurring checkout creates its own customer, so knowing a donor's email address cannot attach a subscription to their Stripe record.
* Security: PayPal subscription activation now verifies the subscription uses the plan this site priced for that donation, so a cheaper plan cannot settle a larger donation record.
* Security: an existing donor's name and phone can no longer be rewritten through the public donation endpoint. Blank fields are still filled in.
* Security: Stripe webhook events are bound to this install and to the stored payment reference, so an event from another site sharing the same Stripe account cannot complete a local donation.
* Fix: a PayPal order is only recorded as paid when the capture itself is COMPLETED. A PENDING capture (eCheck or review) now stays pending until it settles.
* Fix: donation completion is a single atomic update, so simultaneous verify and webhook calls can no longer send duplicate receipts or fire integrations twice.
* Fix: the fee recovery line showed "$NaN" instead of the estimated fee. Numeric settings passed to the front end arrive as strings, which turned the fee calculation into string concatenation; they are now coerced to numbers and fall back to zero if a setting is empty or invalid.
* Fix: in the donation form, fields inside the optional dedication section could overflow their column and overlap the field beside them. The form set box-sizing by inheritance, and that chain did not hold inside the collapsible section, so those controls added their padding on top of their width. Box sizing is now stated outright.
* Fix: dropdowns in the donation form now match the height and width of the text fields beside them instead of keeping their browser default appearance.
* Hardening: rate limiting groups IPv6 clients by /64 rather than by single address, and the PayPal capture and activate endpoints are now throttled.

= 2.5.0 =
* New: Import donation history from another plugin or platform. Upload a CSV exported from GiveWP, Charitable, Donorbox, Givebutter, or a spreadsheet; columns are detected automatically and can be corrected before anything is written. Preview the result first, and re-run safely: gifts already on file are detected by transaction ID, or by matching donor, amount, and date.
* Imported donations are written silently. Historical records never trigger receipt emails, admin notifications, outgoing webhooks, or Mailchimp syncs.

= 2.4.1 =
* Sanitization: every register_setting() call now declares an explicit sanitize_callback. Secret fields use sanitize_text_field and preserve their stored value on a blank save through a pre_update_option filter (WordPress.org review follow-up).

= 2.4.0 =
* Form style customization: accent colour swatches with a live preview, plus a button-shape control (pill / rounded / sharp).
* Contact form REST endpoint for the marketing site (rate-limited, honeypot-protected).
* Security: payment secret keys are no longer echoed back into the settings page; secret fields render blank and keep their stored value on save.
* Compliance: all admin scripts and the receipt stylesheet are now loaded through the enqueue API; all custom option, transient, and shortcode names use the donor_merchant prefix; every database query is bound through wpdb::prepare() with the %i identifier placeholder.
* Requires at least: WordPress 6.2 (for the %i placeholder in wpdb::prepare()).

= 2.3.1 =
* Security: CSV exports now neutralize spreadsheet formula injection (donor-supplied values beginning with =, +, -, or @ are escaped).
* Security: PayPal subscription activation now strictly requires the subscription's custom_id to match the donation, removing an edge-case bypass.

= 2.3.0 =
* New: Monthly fundraising summary email - totals, new donors, recurring count, and top campaign, sent on the first of each month (with a "send a sample now" button).
* Docs: documented the developer hooks (actions, filters) and the signed outgoing-webhook payload.

= 2.2.0 =
* New: Bank debit (ACH) payments through Stripe - lower fees than cards.
* New: Recurring PayPal donations via PayPal Subscriptions, with a webhook for renewals.
* New: Designations / funds - donors choose which fund their gift supports.
* New: Integrations - outgoing webhooks (Zapier/Make) signed with HMAC, and Mailchimp audience sync with an opt-in checkbox.
* New: Custom form fields (text, paragraph, dropdown, checkbox), saved with each donation and included in exports.
* Improved: Donations CSV now includes designation and custom-field columns; admin notifications list them too.
* Developer: webhook payload includes campaign, designation, and custom fields.

= 2.1.0 =
* New: Campaigns - multiple donation forms, each with its own goal, suggested amounts, and description, plus a live goal thermometer (`[donor_merchant campaign="N"]` and `[donor_merchant_progress campaign="N"]`).
* New: Donor self-service portal - passwordless magic-link dashboard for giving history, receipts, and managing recurring gifts through Stripe's billing portal (`[donor_merchant_portal]`).
* New: Printable receipts (save as PDF) for every donation, plus year-end annual tax statements, served securely over signed links.
* New: Offline / manual donation entry from the admin (checks, cash, bank, other).
* New: Tax-receipt settings (Tax ID/EIN, organization address, tax-deductible language) and a donor-portal page setting.
* Improved: Donations CSV export now includes the campaign column.
* Developer: `donor_merchant_suppress_receipt` and `donor_merchant_suppress_admin_notification` filters.

= 2.0.0 =
* Complete rewrite for production.
* Stripe Payment Element with one-time and recurring (monthly/quarterly/yearly) donations.
* PayPal Smart Buttons for one-time donations.
* Fee recovery, tributes/memorials, and donor notes.
* Donor and donation management with search, filters, and CSV export.
* Customizable email receipts and admin notifications.
* Stripe webhooks with signature verification (renewals, refunds, failures).
* Reports dashboard.
* Donation Form block for the editor; legacy [donation_form] shortcode still supported.
* Internationalization-ready; 20 currencies.

= 1.0 =
* Initial prototype.

== Upgrade Notice ==

= 2.5.2 =
Small appearance fixes to the donation form: form controls in the same row now line up exactly, and the optional dedication section is tidier.

= 2.5.1 =
Security release. Hardens the public donation endpoint, PayPal subscription activation, and Stripe webhook handling. Recommended for all users.

= 2.3.1 =
Security hardening: CSV export formula-injection protection and stricter PayPal subscription verification. Recommended for all users.

= 2.3.0 =
Adds an automatic monthly fundraising summary email and documents the developer hooks. No database changes.

= 2.2.0 =
Adds ACH bank payments, recurring PayPal, designations/funds, Zapier & Mailchimp integrations, and custom form fields. Database updates automatically; existing data is preserved.

= 2.1.0 =
Adds campaigns with goal thermometers, a donor self-service portal, printable/annual tax receipts, and offline donation entry. Database updates automatically; existing donations and settings are preserved.

= 2.0.0 =
Complete production rewrite. Settings carry over; the [donation_form] shortcode keeps working. Raw card fields are gone - payments now run through Stripe's hosted Payment Element and PayPal.
