Title: Pay4All Age Verification
Author: pay4all
Published: <strong>July 16, 2026</strong>
Last modified: July 16, 2026

---

Search plugins

![](https://ps.w.org/pay4all-age-verification/assets/icon-256x256.png?rev=3610333)

# Pay4All Age Verification

 By [pay4all](https://profiles.wordpress.org/pay4all/)

[Download](https://downloads.wordpress.org/plugin/pay4all-age-verification.1.0.0.zip)

 * [Details](https://wordpress.org/plugins/pay4all-age-verification/#description)
 * [Reviews](https://wordpress.org/plugins/pay4all-age-verification/#reviews)
 *  [Installation](https://wordpress.org/plugins/pay4all-age-verification/#installation)
 * [Development](https://wordpress.org/plugins/pay4all-age-verification/#developers)

 [Support](https://wordpress.org/support/plugin/pay4all-age-verification/)

## 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).

## FAQ

### 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.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“Pay4All Age Verification” is open source software. The following people have contributed
to this plugin.

Contributors

 *   [ pay4all ](https://profiles.wordpress.org/pay4all/)

[Translate “Pay4All Age Verification” into your language.](https://translate.wordpress.org/projects/wp-plugins/pay4all-age-verification)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/pay4all-age-verification/),
check out the [SVN repository](https://plugins.svn.wordpress.org/pay4all-age-verification/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/pay4all-age-verification/)
by [RSS](https://plugins.trac.wordpress.org/log/pay4all-age-verification/?limit=100&mode=stop_on_copy&format=rss).

## 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.

## Meta

 *  Version **1.0.0**
 *  Last updated **18 hours ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 5.9 or higher **
 *  Tested up to **7.0.1**
 *  PHP version ** 7.4 or higher **
 * Tags
 * [adult](https://wordpress.org/plugins/tags/adult/)[age gate](https://wordpress.org/plugins/tags/age-gate/)
   [age verification](https://wordpress.org/plugins/tags/age-verification/)[ID verification](https://wordpress.org/plugins/tags/id-verification/)
   [selfie](https://wordpress.org/plugins/tags/selfie/)
 *  [Advanced View](https://wordpress.org/plugins/pay4all-age-verification/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/pay4all-age-verification/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/pay4all-age-verification/reviews/)

## Contributors

 *   [ pay4all ](https://profiles.wordpress.org/pay4all/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/pay4all-age-verification/)