=== Dry Run - staging safety for WooCommerce ===
Contributors: lightningbyrd
Tags: woocommerce, staging, email, testing, clone
Requires at least: 6.0
Tested up to: 6.6
Requires PHP: 7.4
Stable tag: 1.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Knows when your WooCommerce store is a copy, and stops the copy from touching real customers. Blocks and captures outbound email, refuses live gateways, stamps test orders.

== Description ==

Every WooCommerce store gets cloned. Hosts do it in one click, agencies do it constantly, and the clone comes up with live gateway credentials, live mail credentials, the whole customer table and every automation still armed. WooCommerce has no concept of "this is not the real store".

Dry Run supplies that concept.

It works out whether this store is a copy, shows you exactly how it decided, and once you arm it, stops the copy reaching a real customer or a real card.

**The problem it removes is remembering.** Manual switches get missed on the pull from production, and the miss is only visible afterwards, in the outbox.

= What it does =

**1. Environment detection you can audit.** Dry Run reads several independent signals: the WordPress environment type, host and staging-tool constants, the hostname, whether the site address has changed since a known-good one was recorded, and WP_DEBUG. Every signal is listed on the Status tab with its reading, which way it points and how much it counts for, so the verdict is never a black box. A declaration in wp-config.php decides on its own.

**2. An outbound-email block that holds.** Dry Run hooks `pre_wp_mail` and `phpmailer_init`. The second one matters: `phpmailer_init` is the hook the common SMTP plugins use to point mail at an external provider, and replacing `wp_mail()` alone does not stop them. An optional one-click must-use loader registers the hooks before any regular plugin loads, so nothing gets in front of the block.

**3. A captured inbox, because blocking is only half the job.** Every blocked message is stored with its recipients, subject, headers and rendered HTML body, and previewable in wp-admin. You can still check that the order email looks right. Retention keeps 14 days or 500 messages by default, whichever bites first, and clips each stored body.

**4. Gateway live-mode lockdown.** Every enabled gateway is classified from its own stored settings, and the report names each one and says what Dry Run did about it. With enforcement armed, a gateway in live mode is removed from checkout, which covers classic checkout and the Store API block checkout because both read the same list. No gateway setting is ever edited, so there is nothing to undo.

**5. A banner you cannot miss, and stamped orders.** WooCommerce documentation states there is currently no visual difference or special status for test orders. Dry Run adds one: every order created while armed is stamped through the order CRUD, which works on both the legacy tables and High-Performance Order Storage, and shows as a TEST ORDER badge in the orders list.

**6. Automation freeze.** Scheduled jobs that email, webhook deliveries, subscription renewals and stock notifications are paused while armed. Inventory work, order editing and imports keep running. Every frozen item is listed, and the freeze is reversible.

= Safe by default =

On activation Dry Run detects and reports. It blocks nothing until an administrator arms it, and **it can never arm on a site it scores as production, or on a site it cannot decide about**. That refusal is enforced in code, not hidden in the interface. Declaring "this IS production" always wins and switches enforcement off immediately. Every override, arm and disarm is written to an audit trail with the time and the user.

= What it never does =

No external service. No API key, no licence check, no telemetry, no phone-home, no account. It never edits a gateway's settings, never deletes an order or a customer, never arms itself, and never shows a banner to a logged-out visitor.

= Honest limits =

* The email block covers `wp_mail()` and the PHPMailer instance WordPress hands round. A plugin that builds its own SMTP connection, or posts to an email API over HTTP without touching either, is out of reach of these hooks.
* Gateway mode is read from each gateway's stored settings. A gateway that records neither a test-mode flag nor a credential in a recognisable form is reported as unreadable, and treated as live.
* Hostname matching is a heuristic. A hosting platform's temporary domain can also be a live store's real address, which is why hostname counts for less than a declared constant.
* The automation freeze matches job names, so a job with an opaque name needs a pattern adding in Settings.
* On multisite this plugin operates per site.

= Pro add-on =

Everything above is free and stays free. A separate Dry Run Pro add-on covers professional workflow: a recipient allowlist so your own team can still receive mail, named environment profiles, an integration freeze for outbound calls to shipping, accounting, ERP and CRM systems, bulk test-order creation and purge, a promote-to-production checklist, multisite and WP-CLI. Details at https://lightningbyrd.com

== Installation ==

1. Upload the plugin through **Plugins, Add New Plugin, Upload Plugin**, or copy the folder to `wp-content/plugins/`.
2. Activate it. Nothing is blocked and nobody is emailed by activating.
3. Go to **WooCommerce, Dry Run**.

The recommended first move is to install it on the **live** store and click "Record this address as the known-good one". That one recorded address is what makes every future clone detectable. Then pull your next copy and arm Dry Run there.

The most reliable setup is a line in `wp-config.php` on the copy:

`define( 'LB_DRYRUN_ENVIRONMENT', 'staging' );`

and the opposite on the live store:

`define( 'LB_DRYRUN_ENVIRONMENT', 'production' );`

== Frequently Asked Questions ==

= Will activating this stop my live store sending order emails? =

No. Activation enforces nothing at all, and enforcement cannot be armed on a site scored as production even if you click the button. The button is refused in code.

= I use an SMTP plugin. Does the block actually work? =

That is the case Dry Run was built for. Hooking `wp_mail()` alone does not stop a plugin that has already reconfigured the PHPMailer instance, so Dry Run also hooks `phpmailer_init` and strips the mailer there. The must-use loader makes sure those hooks are registered before any regular plugin loads.

= What about a plugin that sends through its own API client? =

Out of reach of these hooks, and the plugin says so rather than pretending otherwise. Blocking outbound HTTP calls of that kind is what the Pro add-on's integration freeze does.

= It thinks my live store is a copy. What now? =

Click "No, this IS production" on the Status tab. It takes effect immediately and it is recorded in the audit trail. Then add the wp-config constant so it can never be misread again. Nothing was enforced in the meantime, because enforcement never arms itself.

= It thinks my staging site is production. What now? =

Click "No, this IS a copy", then arm. Better, add the wp-config constant to your clone routine so the next copy is protected from its first request.

= Does it work with High-Performance Order Storage? =

Yes. Order stamps are written through the order CRUD and read through `wc_get_orders()`, so nothing addresses either storage backend directly. HPOS and block-checkout compatibility are both declared.

= Does it work with the block checkout? =

Yes. Gateway lockdown filters the available payment gateways, which is the same list the Store API reads, so classic and block checkout are both covered.

= Will the captured inbox bloat my database? =

It is capped by age and by count, whichever bites first, and each stored body is clipped. Pruning runs daily and again whenever you open the inbox. The defaults are 14 days, 500 messages and 128 KB per body.

= What happens when I delete the plugin? =

Settings, counters and the scheduled task go, and the must-use loader file is removed. The audit trail and the captured messages are kept deliberately, because the audit trail records who told the plugin to stop protecting. Define `LB_DRYRUN_REMOVE_ALL_DATA` to remove those too.

= Does it phone home? =

No. Dry Run makes no outbound request of any kind. There is no API key, no licence check and no telemetry.

== Screenshots ==

1. Status tab: the verdict, and every signal that produced it with its reading and weight.
2. The captured inbox with a message preview open, showing recipients, headers and the rendered body.
3. The gateway lockdown report: what each gateway's mode is and what Dry Run did about it.
4. The orders list with TEST ORDER badges on orders created while armed.
5. Settings: what enforcement does, visibility, and captured-email retention.
6. Help: the safety posture, both directions of detection failure, and the honest limits.

== Changelog ==

= 1.0.0 =
* First release.
* Environment detection from independent weighted signals, with every signal shown in the UI and a wp-config declaration that decides on its own.
* Operator override in both directions, each written to an audit trail with a timestamp and user.
* Outbound email blocked at `pre_wp_mail` and `phpmailer_init`, with an optional must-use loader for load ordering.
* Captured-email inbox with full preview, capped by age, count and body size.
* Gateway live-mode lockdown covering classic checkout and the Store API.
* Order stamping through the order CRUD, HPOS-safe, with a badge in the orders list.
* Automation freeze for scheduled jobs, cron events and webhook deliveries, listed and reversible.
* Persistent admin banner and admin-bar marker, with an optional front-end banner for logged-in staff only.

== Upgrade Notice ==

= 1.0.0 =
First release.
