=== Pay4All Age Verification ===
Contributors: pay4all
Tags: age verification, age gate, id verification, adult, selfie
Requires at least: 5.9
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.0.0
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Age verification and legal-age gate for Pay4All Shop. ID + selfie capture, cross-device QR, AES-256-GCM encryption. 100% local.

== Description ==

**Pay4All Age Verification** blocks any order containing age-restricted products until the customer has uploaded a valid ID (recto + verso) and/or two selfies. Photos are captured either directly on the customer's desktop or, more commonly, through a QR code that hands off to their smartphone.

The plugin plugs into **Pay4All Shop** (pay4all-form). Install it alongside Pay4All Shop and it will only surface where a form contains an age-restricted product.

= How it works =

1. Merchant ticks *Requires age verification* on any product edit screen (Pay4All Shop product).
2. When a customer adds such a product to their form, a panel appears in the KYC step showing a QR code.
3. Customer scans the QR with their phone -> the mobile capture page opens -> they take the required photos with their phone camera.
4. The desktop form polls the server every 4 seconds and unlocks automatically when every required slot is filled — no page refresh needed.
5. On order submit, the encrypted photos are moved from the session bucket to the order-scoped bucket, and are only decrypted on demand by an admin from the order edit screen.

= Security =

* Photos are encrypted with **AES-256-GCM** using a per-order (or per-user) subkey derived via HMAC-SHA256 from a master key stored in `wp-content/uploads/p4all-age-secure/age.key` (outside the database backup).
* Ciphertexts live in a private folder guarded by `.htaccess`, `index.php` and `web.config` so they are never web-servable.
* Admin decryption endpoint is nonce-protected and requires the `edit_users` capability.
* Photos are auto-purged when the order (or user) is permanently deleted.

= WooCommerce support =

Pay4All Age Verification focuses on **Pay4All Shop**. The free plugin does not ship any merchant-facing WooCommerce surfaces (no checkout panel, no order metabox, no product-level « Requires age verification » checkbox, no register-form opt-in). If you run a WooCommerce shop and want the full verification flow (cross-device QR checkout panel, order edit metabox with decrypted photo lightbox, product-level checkbox with Quick Edit, My Account register-form opt-in, HPOS + Checkout Blocks compatibility), the WooCommerce integration is available in the paid add-on **Pay4All Pro**, distributed from pay4all.ch and not through WordPress.org.

= WPML / Polylang =

Multilingual-aware : when the *Requires age verification* flag is set on a source product, every translation is treated as age-restricted too — even if the flag hasn't been mirrored to the translation.

= Available languages =

Français (fr_FR), Deutsch (de_DE), Italiano (it_IT), English (en_US).

== External services ==

This plugin does not connect to any external service. Everything runs on the site: photo capture, encryption, storage and admin decryption are all local. No data is sent to any third-party server.

== Source code ==

The complete, non-minified source of this plugin is bundled inside the ZIP itself. Every PHP, CSS and JavaScript file is human-readable and directly editable. No compiler, transpiler or bundler is required to work on this plugin.

== Installation ==

1. Upload the plugin to `/wp-content/plugins/pay4all-age-verification/` or install via *Plugins > Add New*.
2. Activate it from the *Plugins* menu.
3. Open *Pay4All AGE > Paramètres* to tune what documents you require and to override the customer-facing texts if needed.
4. On each product that requires an age check, tick the *Requires age verification* checkbox on the product edit screen (Pay4All Shop product).

== Frequently Asked Questions ==

= Do I need Pay4All Shop or WooCommerce? =

Yes. Pay4All Age Verification is a companion plugin — it plugs into either **Pay4All Shop** (free, ships the form-based checkout) or **WooCommerce** (via the paid Pay4All Pro add-on). On a bare WordPress install without either, the plugin stays dormant and shows an admin notice on the Plugins screen.

= How are photos encrypted? =

**AES-256-GCM** with a per-order (or per-user) subkey derived via HMAC-SHA256 from a master key. The master key is generated once and stored in `wp-content/uploads/p4all-age-secure/age.key` (mode 0600). Ciphertexts live in the same private folder, guarded by `.htaccess`, `index.php` and `web.config` so they are never web-servable — a direct URL always returns 403 / 404. See the *Security* section above for the full threat model.

= Where is the master encryption key stored? =

Outside the database, in `wp-content/uploads/p4all-age-secure/age.key`. This means a stolen SQL dump alone is not enough to decrypt the photos — the attacker would also need read access to that specific file. Please make sure your backup strategy either encrypts `wp-content/uploads/p4all-age-secure/` or excludes it entirely from the backup archive.

= What happens to the photos when an order is deleted? =

They are auto-purged. Both the WooCommerce order lifecycle (`woocommerce_delete_order` hook) and Pay4All Shop order deletion trigger `KycStorage::purge_order()`, which removes every encrypted blob and the order-scoped directory. A daily cron also purges session-scoped buckets older than the configured TTL (default 24 h).

= What happens if a user account is deleted? =

The `delete_user` hook fires `KycStorage::purge_user()` — every stored user photo (recto / verso / selfie_1 / selfie_2) is removed together with the user record. GDPR-friendly by default.

= Does the mobile capture page work on all phones? =

The page uses the native `<input type="file" accept="image/*" capture="…">` API — supported by Safari (iOS 6+), Chrome, Firefox, Edge on both Android and iOS. On the desktop side, the QR code is generated with a pure-JS library (no external service call) and the pairing is polled every 4 seconds via a REST endpoint scoped to the session token.

= Does it work with WPML / Polylang? =

Yes. When a product's *Requires age verification* flag is ticked on a source-language product, every translation is treated as age-restricted too — even if the flag hasn't been mirrored to the translation post. Same behaviour on WooCommerce products via Pay4All Pro.

= Which languages ship out of the box? =

French (fr_FR), German (de_DE), Italian (it_IT) and English (en_US). Every customer-facing string is also overridable per-language from the settings page — merchants can rewrite the KYC panel copy without touching a `.po` file.

= Does the plugin contact any external service? =

No. Photo capture, encryption, storage and admin decryption are all local — nothing leaves the site. The QR code is generated client-side ; the mobile page polls only your own WordPress REST API.

== Screenshots ==

1. Product edit screen : the *Requires age verification* checkbox, added right next to the price row on Pay4All Shop products.
2. Customer checkout with an age-restricted product in the cart : the KYC panel appears in the « Vérification d'âge » step with the QR code + status of each photo slot.
3. Mobile capture page (loaded from the QR) : native camera capture for ID recto / verso and selfies, live upload progress, encrypted at rest before storage.
4. Admin lightbox : merchant clicks the shield icon on the order edit screen — the encrypted photos are streamed, decrypted on-the-fly, and displayed side-by-side without being written back to disk.
5. Settings page : merchant chooses required documents (ID / selfies / both), OR / AND mode, purge delay, and can override the customer-facing texts in each activated language.

== Changelog ==

= 1.0.0 =
* Initial release.
* Product-level *Requires age verification* checkbox (Pay4All Shop).
* Cross-device QR flow (desktop QR -> mobile capture page).
* AES-256-GCM encryption at rest, per-order and per-user derived subkeys.
* Metabox on the Pay4All Shop order edit screen with nonce-protected decrypted-photo streaming and a lightbox to view every document side-by-side.
* Shield column on the WordPress users list — one click opens the customer's verified documents.
* Settings page with OR / AND mode, purge delay, per-locale text overrides.
* FR / DE / IT / EN built-in translations, with 4-language text override per string from the settings page.
* WPML / Polylang aware : translated products inherit the *Requires age verification* flag from their source.

== Upgrade Notice ==

= 1.0.0 =
Initial release.
