=== Genvoris Virtual Try-On ===
Contributors: dev2production
Tags: virtual try-on, ai, woocommerce, fashion, ecommerce
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 8.0
Stable tag: 1.2.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
WC requires at least: 7.0
WC tested up to: 9.5

AI-powered Virtual Try-On for WooCommerce. Customers upload a photo and see how your products look on them before buying.

== Description ==

Genvoris Virtual Try-On lets your shoppers upload a photo and instantly preview how a product looks on them, right from the WooCommerce product page. It works for clothing, accessories, jewelry, bags, and home textiles.

The plugin is a WordPress front-end for the **Genvoris** SaaS service (https://genvoris.org). The heavy AI work — image analysis and try-on generation — runs on Genvoris servers; this plugin handles connection, monetization, and widget injection on your store.

= Key features =

* AI-generated try-on previews from a single shopper photo.
* Five built-in monetization models: free for everyone, subscription, credits-with-purchase, pay-per-use, freemium.
* Per-customer quota tracking with monthly resets.
* Tight WooCommerce integration: order events, optional WooCommerce Subscriptions support.
* No theme code changes — the widget injects itself into product pages.
* All sensitive credentials encrypted at rest using WordPress salts.
* HPOS / custom order tables compatible.

= Requires a Genvoris account =

You'll need a free account at https://genvoris.org. The plugin walks you through connecting on first activation. The API key is provisioned automatically — you never copy/paste secrets.

== External services ==

This plugin connects to Genvoris-operated services to deliver its functionality. Connections are only made AFTER you authorize the plugin via the in-plugin "Connect with Genvoris" button. Until then, the plugin is dormant and contacts no third-party server.

= 1. Genvoris portal API (https://genvoris.org/api/v1) =

* What it does: receives the merchant's site metadata during OAuth, then handles plan/customer/usage/session-token operations server-to-server.
* What is sent: site URL, site name, admin email (during OAuth only), and per-customer identifiers (`wp_<user_id>`), email (optional), and metadata (`source: wordpress`, `wp_user_id`, `site_url`).
* When it is sent: only after the merchant clicks "Connect with Genvoris", and on subsequent storefront try-on activity.
* Terms of service: https://genvoris.org/terms-of-service
* Privacy policy: https://genvoris.org/privacy-policy

= 2. Genvoris try-on backend (https://api.genvoris.org) =

* What it does: serves the storefront widget bundle (`/widget.js`) and processes the actual try-on (`/api/analyze`, `/api/tryon`).
* What is sent: shopper-uploaded photo (image bytes), the product reference (id/title/image URL/page URL), and the merchant's API key (server-side only — the key is never exposed to the browser).
* When it is sent: only when a shopper interacts with the try-on widget on a connected store.
* Terms of service: https://genvoris.org/terms-of-service
* Privacy policy: https://genvoris.org/privacy-policy

The hosted widget bundle (`/widget.js`) is loaded on WooCommerce product pages only, and only when the plugin is connected and the widget is enabled in settings. Disabling the widget or disconnecting the account stops every external call.

== Installation ==

1. Upload the `genvoris-virtual-tryon` folder to `/wp-content/plugins/`, or install via Plugins -> Add New.
2. Activate the plugin through the Plugins menu in WordPress.
3. Go to **WooCommerce -> Genvoris Try-On -> General**.
4. Click **Connect with Genvoris** and authorize on genvoris.org.
5. Pick a monetization model under **Monetization** and (optionally) tweak the widget under **Widget**.
6. Visit any product page on your storefront — the try-on button is live.

== Frequently Asked Questions ==

= Do I need a Genvoris account? =

Yes. The plugin is the WordPress front-end for the Genvoris try-on service. The account is free and includes a starter quota.

= Does it work without WooCommerce? =

No. The plugin requires WooCommerce for products, customers, and orders. It refuses to load without it.

= Where is my API key stored? =

Encrypted in `wp_options` using AES-256-CBC with a key derived from your WordPress AUTH_KEY + SECURE_AUTH_KEY. The raw key is never written to disk in plaintext and is never sent to the browser. The widget uses a same-origin REST proxy so the API key never leaves the server.

= What data leaves my site? =

Only what is required for the try-on flow:

* During OAuth: site URL, site name, admin email.
* On every status check: customer identifier (`wp_<user_id>`), email (optional), and metadata (`source: wordpress`, `wp_user_id`, `site_url`).
* On every try-on: the shopper-uploaded photo, the product reference (id/title/image URL/page URL), and the merchant API key (server-to-server).

WordPress passwords, payment details, and order line items are NEVER sent to Genvoris.

= How are credits deducted? =

The Genvoris portal deducts one credit per successful try-on, server-to-server, after the image is generated. The plugin never decides credit consumption client-side.

= Can I use this with WooCommerce Subscriptions? =

Yes. Activating a subscription flips an `is_subscribed` flag on the local customer row; cancellations and expirations revert it. The SUBSCRIPTION and FREEMIUM monetization models use this flag to gate access.

= Is the plugin HPOS-compatible? =

Yes. We declare `custom_order_tables` compatibility and use the WooCommerce high-level order API (no direct `wp_postmeta` queries on orders).

= Can I uninstall cleanly? =

Yes. Uninstall (not just deactivate) removes every option, transient, and database table the plugin created. Customer data is preserved through deactivation so you can re-enable without losing state.

== Screenshots ==

1. The try-on button on a WooCommerce product page.
2. The five monetization model picker in WP admin.
3. Per-customer quota dashboard.

== Changelog ==

= 1.2.1 =
* UI/UX: Added full customization settings for the try-on button (Background Color, Text Color, Border Radius) directly in the WordPress admin panel.

= 1.2.0 =
* Security: REST proxy `permission_callback` now uses a dedicated `check_proxy_permission()` method instead of `__return_true`. All access-control checks (plugin-connected, same-origin, path allow-list, per-IP rate limit) are enforced in the permission callback where WordPress expects them.
* Security: Origin or Referer header is now required on every proxy request. Requests without either header (e.g. direct curl/scripted abuse) are rejected with 403. Previously, missing Origin/Referer was silently allowed.
* Compat: synchronised the readme `Stable tag` with the plugin file `Version` header (now 1.2.0).

= 1.1.0 =
* Auto-style detection: the widget now matches your store's colors automatically.
* Security: webhook deliveries are now de-duplicated by delivery id (`X-Genvoris-Delivery`), so a retried portal webhook can never re-apply the same side effect.
* UX: all five monetization model labels are shown in plain English throughout the admin.
* Improved error handling across the storefront loader and REST proxy.
* Performance: storefront loader improvements.
* Compat: synchronised the readme `Stable tag` and the plugin `Version` header at 1.1.0 (resolves the prior 1.0.9 header / 1.1.0 constant mismatch).

= 1.0.5 =
* Security: REST proxy now sanitizes every forwarded query parameter and returns the upstream JSON via WP_REST_Response (no more raw `echo`), so all output is escaped by WordPress.
* Security: REST proxy and per-user `/status` endpoint now require a valid `wp_rest` nonce in addition to the existing same-origin check; guests still get a generic status payload without a nonce.
* Security: storefront widget config now ships `wp_create_nonce('wp_rest')` so the bundle can attach `X-WP-Nonce` to every proxy/status call.
* I18n: text domain renamed from `genvoris-virtual-tryon` to `genvoris-virtual-try-on` to match the plugin slug; affects all gettext calls and the Plugin Header.
* Compat: `Tested up to` bumped to 6.9.
* Docs: fixed a Plugin Header description that was split mid-word.

= 1.0.8 =
* Fix: added a `rest_authentication_errors` filter at priority 200 (runs after WordPress core's `rest_cookie_check_errors` at priority 100) to clear `rest_cookie_invalid_nonce` errors for the `/wp-json/genvoris/v1/proxy/*` route only. The widget bundle sends `X-WP-Nonce` on every call; on cached/guest pages that nonce could not be validated, causing a 403 that the widget surfaced as "This domain is not authorized for try-on. Please contact the store."
* Fix: relaxed the same-origin Origin/Referer check so that requests missing both headers (normal for same-origin simple GETs under a strict Referrer-Policy) are allowed through rather than rejected.
* Compat: synchronised the readme `Stable tag` with the plugin file `Version` header (now 1.0.8).

= 1.0.7 =
* Fix: removed the `wp_rest` nonce requirement from the same-origin `/wp-json/genvoris/v1/proxy/*` route. On cached storefront pages and in guest sessions, server-minted nonces frequently failed `wp_verify_nonce()`, so every widget call to `/api/status`, `/api/config`, `/api/analyze`, and `/api/tryon` returned 403 `rest_cookie_invalid_nonce` — surfaced to shoppers as "This domain is not authorized for try-on". The proxy is still protected by the server-held API key, same-origin Origin/Referer check, per-IP rate limit, and an explicit upstream path allow-list, matching the v1.0.6 fix already applied to `/wp-json/genvoris/v1/status`.
* Fix: added `api/status` to the proxy's upstream allow-list so the widget's status poll no longer 404s through the proxy after the nonce gate was lifted.
* Compat: synchronised the readme `Stable tag` with the plugin file `Version` header (now 1.0.7).

= 1.0.6 =
* Fix: removed `X-WP-Nonce` header from the public status endpoint request in the storefront widget; sending a nonce to a `permission_callback: '__return_true'` endpoint caused WordPress to return 403 `rest_cookie_invalid_nonce` when the nonce was stale or guest-generated.
* Compat: synchronised the readme `Stable tag` with the plugin file `Version` header (now 1.0.6).

= 1.0.4 =
* Fix: PHP 8 TypeError when the session-mint REST endpoint received a `WP_Error` without status data.
* Fix: dead code path in the front-end placement guard could trigger on themes that fired both the configured WC hook AND `woocommerce_after_single_product_summary`.
* Tweak: hosted widget bundle cache-buster now tracks plugin version (no more drifting magic constant).
* Docs: full WP.org-compliant readme with explicit External Services disclosure.
* Compat: synchronised the readme `Stable tag` with the plugin file `Version` header (now 1.0.4).

= 1.0.3 =
* Default widget position changed to "below Add to Cart" so the button is discoverable on themes that don't render a floating fallback.
* Activation hook now seeds defaults for every monetization-related option.

= 1.0.0 =
* Initial public release.
* Five monetization models.
* OAuth-based connection to genvoris.org.
* Per-customer quota tracking.
* WooCommerce order + subscription event hooks.

== Upgrade Notice ==

= 1.2.1 =
Recommended for all users. Adds new widget UI customization settings to perfectly match your store's branding.

= 1.2.0 =
Security hardening release. The REST proxy permission_callback now enforces same-origin, rate-limit, and path-allowlist checks directly, and requires an Origin or Referer header on every request. Recommended for all users.

= 1.1.0 =
Recommended for all users. Adds automatic store-color matching, hardens webhook handling with delivery de-duplication, and aligns the version header with the readme. No action required after updating.

= 1.0.7 =
Critical fix — restores try-on on cached / guest storefront pages. Earlier builds rejected legitimate widget calls with 403 `rest_cookie_invalid_nonce` (surfaced as "This domain is not authorized for try-on"). Strongly recommended for all users.

= 1.0.5 =
Security + WP.org compliance release. Tightens the REST proxy (sanitization, escaping, nonce), aligns the text domain with the plugin slug, and bumps Tested up to 6.9. Recommended for all users.

= 1.0.4 =
Recommended for all users on PHP 8 — fixes a TypeError in the session-mint endpoint.
