=== Business Accounts Router for GiveWP with PayPal Donations ===
Contributors: mralaminahamed
Donate link: https://alaminahamed.com/donate
Tags: givewp, paypal, donations, country-routing, charity
Requires at least: 6.4
Tested up to: 6.9
Requires PHP: 8.1
Stable tag: 1.0.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Route GiveWP donations to multiple PayPal Business accounts by donor country. Multi-entity charities, automatic credential switching.

== Description ==

Business Accounts Router for GiveWP with PayPal Donations extends the GiveWP PayPal Donations gateway so that a single WordPress installation can accept donations through several PayPal Business accounts, automatically routing each transaction to the correct account based on the donor's declared country.

This plugin is an independent, third-party extension. It is not affiliated with, endorsed by, or sponsored by GiveWP (StellarWP) or PayPal. The names "GiveWP" and "PayPal" appear here solely to describe compatibility with those products.

This plugin is designed for organisations that operate two or more legally distinct charity entities — for example a US 501(c)(3) and a Canadian CRA-registered charity — that share a single donation website but must process payments through separate PayPal accounts and issue separate tax receipts.

**Key features:**

* **Multi-account admin panel** — Add and manage any number of PayPal Business accounts under Donation Forms → PayPal Accounts. Each account stores legal entity details (name, tax ID, address, country) alongside AES-256-GCM encrypted API credentials.
* **Country routing rules** — Define which PayPal account and GiveWP form handles donations from each country. One active rule per country, fully configurable from the admin.
* **Country selector popup** — A donor-facing modal popup (shortcode `[ador_country_selector]`) asks donors to identify their country before proceeding to checkout. Selection is stored in sessionStorage to avoid repeated prompts.
* **Runtime credential routing** — The plugin intercepts GiveWP's IoC container at payment time, substituting the correct PayPal credentials transparently. No GiveWP core files are modified.
* **Per-account webhook handling** — Each PayPal account receives its own webhook endpoint (`/wp-json/ador/v1/webhook/{account_id}`) with independent HMAC signature verification.
* **Per-entity email tags** — Seven GiveWP email template tags populate receipt emails with the correct legal entity name, address, tax ID, tax-deductibility statement, and a unique per-entity receipt number.
* **PDF receipts** — Optional on-demand PDF receipt download on the GiveWP success page, generated by dompdf.
* **Security-first design** — AES-256-GCM credential storage, nonce protection, capability checks, webhook signature verification, and `$wpdb->prepare()` throughout.

== Installation ==

1. Upload the `alamin-donation-router` folder to the `/wp-content/plugins/` directory.
2. Run `composer install --no-dev --optimize-autoloader` inside the plugin directory.
3. Ensure `AUTH_KEY` and `SECURE_AUTH_SALT` are set to strong, unique values in `wp-config.php` — these are required for credential encryption.
4. Activate the plugin through the **Plugins** screen in WordPress.
5. Navigate to **Donation Forms → PayPal Accounts** and add your first PayPal Business account.
6. Configure country routing rules under the **Country Routing** tab.
7. Add the shortcode `[ador_country_selector]` to your donation landing page.
8. Register per-account webhook URLs in your PayPal Developer apps (see the **Webhook Setup** view for each account).

For detailed configuration instructions, see the OPERATIONS.md file included with the plugin.

== Frequently Asked Questions ==

= Which payment gateways does this plugin support? =

This plugin supports the GiveWP PayPal Donations gateway (also known as PayPal Commerce / PPCP) only. It does not affect or support other GiveWP gateways such as Stripe, Authorize.net, or PayPal Standard.

= How many PayPal accounts can I add? =

There is no hard limit on the number of PayPal Business accounts you can configure. Each account requires its own PayPal Developer REST app (Client ID and Client Secret) and its own webhook URL registered in PayPal's developer portal.

= How are PayPal credentials stored? Is it safe? =

Yes. PayPal Client IDs and Client Secrets are never stored in plaintext. They are encrypted with AES-256-GCM using a key derived via PBKDF2-SHA256 from your WordPress `AUTH_KEY` and `SECURE_AUTH_SALT` constants (100,000 iterations, 32-byte key). The random nonce and GCM authentication tag are stored alongside the ciphertext as a base64 blob. Credentials are never written to any log. If `AUTH_KEY` or `SECURE_AUTH_SALT` are changed, stored credentials become unreadable and must be re-entered.

= Can I test with PayPal sandbox accounts before going live? =

Yes. The plugin works with both PayPal sandbox and live environments. Add your sandbox accounts the same way as live accounts, using the Client IDs and Secrets from your sandbox REST apps. Register sandbox webhook URLs in PayPal's developer portal using a publicly accessible HTTPS URL (use a tunnelling tool such as ngrok for local development). See OPERATIONS.md for a full sandbox testing procedure.

= What happens if a routing rule is missing for a donor's country? =

If no active routing rule exists for the selected country, the credential router falls back to GiveWP's primary PayPal account. The donation is not blocked — it proceeds normally using the default account. An error entry is written to the PHP error log with prefix `[ADOR]` for the operator's attention.

= Does this plugin modify GiveWP core files? =

No. The plugin hooks into GiveWP exclusively through WordPress filters, actions, and GiveWP's public IoC container (`give()`). No GiveWP core files are modified.

= Can I use this plugin with GiveWP's built-in email receipts? =

Yes. The plugin registers additional GiveWP email template tags that you can insert into your existing GiveWP email templates (GiveWP → Settings → Emails). Tags include `{paypal_account_entity_name}`, `{paypal_account_entity_tax_id}`, `{paypal_account_receipt_number}`, and four others. Tags return an empty string for donations not routed through this plugin, so existing templates work without changes.

= The PDF receipt is not generating. What should I check? =

Verify that `dompdf/dompdf` is installed by running `composer install` in the plugin directory. Confirm that the `ador_pdf_receipts_enabled` option is set to a truthy value in `wp_options`. Check the PHP error log for a line beginning `[ADOR] PDF receipts disabled:` — this will include the underlying error message.

== Screenshots ==

1. Accounts list table — all configured PayPal Business accounts with Active/Inactive status badges, country, legal entity, and action buttons.
2. Add Account form — entering legal entity details and connecting a PayPal Business account via OAuth.
3. Country Routing list table — routing rules mapping donor countries to donation forms and PayPal accounts.
4. Webhook Setup view — per-account webhook URL with one-click copy and the required PayPal event type list.
5. Country selector popup — donor-facing modal that lets donors choose their country/legal entity before checkout.
6. Plugin Settings tab — configuring the country selector popup, geolocation provider, PDF receipts, and legal acknowledgement.
7. Edit Account form — per-account legal entity details and receipt template editor with available GiveWP email tags.

== External services ==

This plugin sends data to PayPal's REST API to authenticate with PayPal Business accounts and to verify the HMAC-SHA256 signatures of inbound webhook events.

**PayPal REST API** (`https://api-m.paypal.com`, `https://api-m.sandbox.paypal.com`, `https://api.paypal.com`, `https://api.sandbox.paypal.com`)

* **What is sent:** PayPal Client ID and Client Secret (decrypted in memory at request time, never stored in plaintext) are exchanged for OAuth 2.0 bearer tokens during donation checkout. Webhook event payloads are forwarded to PayPal's `/v1/notifications/verify-webhook-signature` endpoint for signature verification.
* **When it is sent:** Only when a donation is being processed through a PayPal Business account configured in this plugin, or when PayPal sends a webhook event to this site.
* **Service terms:** [PayPal User Agreement](https://www.paypal.com/us/legalhub/useragreement-full) | [PayPal Privacy Statement](https://www.paypal.com/us/legalhub/privacy-full)

== Changelog ==

= 1.0.0 - 2026-05-07 =

**Added**

* Multi-account admin panel (Donation Forms → PayPal Accounts) with Accounts, Country Routing, and Settings tabs.
* Account add/edit form with AES-256-GCM encrypted credential storage.
* Country routing rules with add/edit/bulk-deactivate actions.
* Custom database tables `{prefix}ador_accounts` and `{prefix}ador_country_routing` with schema versioning.
* `PayPal_Credential_Router` intercepting GiveWP's IoC container before every PayPal AJAX payment request to substitute per-account credentials.
* IoC container rebinding for both `MerchantDetail::class` and `MerchantDetails::class` covering all GiveWP PayPal credential resolution paths.
* Refund routing with inactive-account guard.
* Per-account REST webhook endpoint `POST /wp-json/ador/v1/webhook/{account_id}` with HMAC-SHA256 signature verification and idempotency checking.
* Country selector popup shortcode `[ador_country_selector]` with sessionStorage persistence.
* Geolocation hint (non-binding IP-based country pre-selection).
* Seven GiveWP email template tags for per-entity receipt details.
* Per-entity sequential receipt numbers allocated atomically via `FOR UPDATE` lock.
* Default tax-deductibility statement templates for US and Canada.
* PDF receipt generation via dompdf behind `ador_pdf_receipts_enabled` option flag.
* Action hooks `ador_credentials_routed` and `ador_webhook_processed`.
* PHPStan level 8, WPCS 3.x, PHPUnit 9.6 with Brain\Monkey test suite.

**Security**

* AES-256-GCM encryption for all stored PayPal credentials with PBKDF2-SHA256 key derivation.
* PayPal HMAC-SHA256 webhook signature verification on all inbound webhook events.
* WordPress nonce protection on all admin form submissions and AJAX endpoints.
* `current_user_can( 'manage_give_settings' )` capability checks on all admin-only actions.
* `$wpdb->prepare()` for all parameterised database queries.
