WooCommerce Offer
Pricing Pro
A complete price negotiation system for your WooCommerce store. Customers propose a price, you control everything from the dashboard: accept, reject, or counter-offer every proposal.
📦 Overview
What this plugin does and how it integrates with WooCommerce
WooCommerce Offer Pricing Pro adds a complete "Make an Offer" system to any WooCommerce store. Customers see a form on the product page where they can suggest a custom price. The administrator receives offers in real time and can accept, reject, or counter-offer them directly from the admin panel.
The plugin works with both simple products and variable products. It supports auto-acceptance thresholds, configurable limits per product and per variation, CSV export, a dashboard widget, and a dedicated tab in the customer's "My Account" section.
✨ Features
All capabilities included in version 1.0.0
- 📝 "Make an Offer" form on product page (AJAX, no page reload)
- 🔄 Counter-offers with HMAC-signed links for the customer
- ✅ Configurable auto-acceptance via percentage threshold
- 📊 Admin dashboard with status tabs and bulk actions
- 📈 Analytics page with KPIs, top products and daily trend
- 🛒 "My Offers" tab in My Account with one-click "Add to Cart"
- 🎨 6 frontend themes (Inherit / Light / Dark / Pro / Art Floral / Custom)
- 📧 Admin + customer email notifications on every status change
- 🛡️ Rate limiting per user and per IP, configurable from settings
- 🔑 HMAC SHA-256 tokens on all customer-facing action links
- 📦 HPOS compatible (High-Performance Order Storage)
- 📤 CSV export for any filtered view
- 🏷️ Min/max limits per product and per variation
- 🔒 Ownership check on all customer actions
- 🔔 30-day KPI dashboard widget + pending shortcut
- 🗑️ Full cleanup option on uninstall (opt-in)
🖥️ Server Requirements
Minimum and recommended configuration for optimal operation
| Component | Minimum | Recommended | Notes |
|---|---|---|---|
| WordPress | 6.0 | 6.7+ | Tested up to 6.9 |
| WooCommerce | 7.0 | 9.0+ | Tested with 10.7 |
| PHP | 8.1 | 8.2 / 8.3 | Uses: typed properties, named args, fibers-ready |
| MySQL / MariaDB | MySQL 5.7 / MariaDB 10.3 | MySQL 8.0+ / MariaDB 10.6+ | Requires DECIMAL(19,4), ON UPDATE CURRENT_TIMESTAMP support |
| PHP Extensions | openssl, mbstring, json | Idem | openssl required for AES-256 + HMAC licensing |
| wp-cron | Enabled or server cron | Server cron (more reliable) | Required for offer expiration (wop_expire_offers_cron — hourly) |
| HTTPS | Recommended | Mandatory | HMAC-signed links are secure only over HTTPS |
| Memorie PHP | 128 MB | 256 MB+ | License Manager and Analytics graphics use more memory |
| Browser | — | Chrome, Firefox, Edge, Safari, Opera | Frontend fully responsive, JS ES2017+ |
Checking PHP Extensions
Run via SSH or temporarily add to wp-config.php:
php -m | grep -E "openssl|mbstring|json"
<?php
echo extension_loaded('openssl') ? 'openssl: OK' : 'openssl: MISSING';
echo extension_loaded('mbstring') ? ' | mbstring: OK' : ' | mbstring: MISSING';
echo extension_loaded('json') ? ' | json: OK' : ' | json: MISSING';
🗄️ Database Structure
The plugin creates a single dedicated table upon activation: {prefix}wop_offers
| Column | Type | Description |
|---|---|---|
| id | BIGINT UNSIGNED PK | Unique auto-increment ID |
| user_id | BIGINT UNSIGNED | 0 for guests; WP user ID |
| product_id | BIGINT UNSIGNED | WooCommerce product ID |
| variation_id | BIGINT UNSIGNED | 0 if not a variation |
| quantity | INT | Offered quantity |
| offer_price | DECIMAL(19,4) | Price proposed by the customer |
| counter_price | DECIMAL(19,4) NULL | Counter price proposed by admin |
| original_price | DECIMAL(19,4) | Catalog price at the time of the offer |
| currency | VARCHAR(10) | ISO currency code (e.g. USD, EUR) |
| status | VARCHAR(20) | pending / accepted / rejected / countered / expired / converted |
| note | TEXT NULL | Customer note when submitting the offer |
| admin_note | TEXT NULL | Internal admin note |
| order_id | BIGINT NULL | Populated after "Add to Cart" + order conversion |
| expires_at | DATETIME NULL | NULL if no expiry is configured |
| created_at | DATETIME | Creation timestamp (UTC) |
| updated_at | DATETIME | Automatically updated on any change |
Automatically created indexes: idx_user, idx_product, idx_status, idx_expires, idx_order
🚀 Plugin Installation
Two methods: via the WordPress interface or manually via FTP/SSH
Method 1 — Upload via WordPress Interface (recommended)
-
1Go to Plugins → Add New → Upload Plugin
From the WordPress admin menu, navigate to Plugins → Add New Plugin, then click the Upload Plugin button in the top right corner.
-
2Select the ZIP file
Choose the file
woocommerce-offer-pricing-pro-1_0_0.zipfrom your computer and click Install Now. -
3Activate the plugin
After installation, click Activate Plugin. The plugin will automatically create the
wop_offerstable in the database and set default values. -
4Confirm the trial notification
You will see an admin banner confirming the start of the 14-day trial period. No action is required — the plugin works immediately.
Method 2 — Manual FTP/SSH Upload
-
1Extract the ZIP locally
Extract the contents — you will get the folder
woocommerce-offer-pricing-pro/. -
2Upload the folder to the server
Transfer the folder via FTP/SFTP/SSH to the
/wp-content/plugins/directory. -
3Activate from WP Admin
Go to Plugins → Installed Plugins, locate WooCommerce Offer Pricing Pro and click Activate.
WooCommerce class exists. If WooCommerce is inactive, an admin notice is displayed and frontend features are not initialized. The license page remains accessible.
🔑 License Activation
License management via the Xania Code License Manager
-
1Go to WooCommerce → Offer License
The submenu appears automatically in the WooCommerce menu after activating the plugin.
-
2Enter the license key
Paste the key received after purchase from
xaniacode.cominto the "License Key" field and click Activate. -
3Activation confirmation
The
xaniacode.comserver validates the key. On success, the page displays the "Active" status and expiry date. The key is stored encrypted (AES-256-CBC + HMAC).
Started automatically on activation. The offer form works fully. No card or key required.
All features activated. Auto-update available directly from the WP plugins panel.
The frontend form is disabled. Admin remains accessible. Data is intact.
Update notifications appear in the WordPress plugins list. Updating is done with a single click, just like any other plugin.
⚙️ General Settings
WooCommerce → Offer Settings → General section
| Option | Type | Default | Description |
|---|---|---|---|
| wop_enable_globally | Checkbox | Yes | Master switch — disabling immediately hides the form from all products. |
| wop_guest_offers | Checkbox | No | Allows guests (non-logged-in users) to submit offers. If off, a login message is displayed. |
| wop_offer_expiry_days | Number | 7 | Pending offers expire after this period. 0 = no expiry. |
| wop_min_offer_percent | Number (0–100) | 0 | Global minimum offer as % of the displayed price. 0 = disabled. Overridable per product. |
| wop_auto_accept_percent | Number (0–100) | 0 | Offers ≥ this % of the displayed price are automatically accepted. 0 = disabled. |
| wop_button_label | Text | Make an Offer | The button text on the product page. |
| wop_success_message | Textarea | Your offer has been submitted… | Message displayed to the customer after a successful submission. |
| wop_terms_text | Text | (empty) | Label for the Terms & Conditions checkbox. If empty, the checkbox does not appear. |
| wop_terms_url | URL | (empty) | Link opened when clicking the T&C label. |
🛡️ Security & Limits
WooCommerce → Offer Settings → Security & Limits section
| Option | Type | Default | Description |
|---|---|---|---|
| wop_max_offers_per_product | Number | 3 | Maximum number of active (non-rejected, non-expired) offers a customer can submit per product. 0 = unlimited. |
| wop_rate_limit_count | Number | 5 | Maximum number of submissions allowed in the time window. 0 = disabled. |
| wop_rate_limit_window | Number (sec) | 3600 | Duration of the rate limiting window in seconds. Minimum 60. Default: 3600 (1 hour). |
Built-in Security Mechanisms
All customer-facing links (accept counter, add to cart) are signed with HMAC SHA-256 tokens with scope and expiry.
A logged-in user cannot use another user's link, even if they know the URL.
All admin POST and AJAX actions use WordPress nonces. Any request without a valid nonce is rejected.
CSV export protected against CSV Injection: cells beginning with = + - @ \t \r are prefixed with '.
Client IP is correctly resolved behind proxies, checking forwarded headers only when REMOTE_ADDR is private/reserved.
All admin pages and actions verify the manage_woocommerce capability.
🎨 Visual Themes
WooCommerce → Offer Settings → Appearance section
The plugin includes 6 frontend themes based on CSS variables (--wop-*). The theme applies to the offer form and the My Account tab.
Custom Theme Colors
When the Custom theme is selected, the Custom Theme Colors section appears with 6 color picker fields:
| Option | Default | Role |
|---|---|---|
| wop_custom_bg | #ffffff | Form card background |
| wop_custom_bg2 | #f7f6fb | Secondary background (form interior) |
| wop_custom_border | #e2dff0 | Card and input borders |
| wop_custom_accent | #7f54b3 | Primary color (button, focus, links) |
| wop_custom_text | #1a1a2e | Primary text color |
| wop_custom_muted | #6b6880 | Secondary text (hints, labels) |
📧 Email Notifications
WooCommerce → Offer Settings → Email Notifications section
| Option | Default | Description |
|---|---|---|
| wop_notify_admin_email | WP admin email | Administrator email addresses. Separate multiple addresses with a comma. |
| wop_notify_admin_new | Yes | Sends an admin email for every new offer received. |
| wop_notify_customer_update | Yes | Sends an email to the customer when the offer is accepted, rejected, or counter-offered. |
Automatically Sent Emails
Sent immediately when a customer submits a new offer. Contains: product, offered price, catalog price, customer, note.
Sent when the customer accepts a counter-offer via the signed link in the email.
Includes a signed "Add to Cart" link at the offered price. The link is valid for a limited period.
Notification that the offer could not be honored. Optionally includes an admin note.
Includes the new price proposed by the admin and a signed link for direct acceptance from the email, no login required.
🏷️ Per-Product Configuration
Individual product-level settings, from the WooCommerce product editor
In the editor of any WooCommerce product, under Product data → Pricing, a new Offer Pricing subsection appears with the following fields:
| Field | Description |
|---|---|
| Enable Make an Offer | Per-product checkbox. If the global switch is off, this field has no effect. |
| Minimum Offer Price | Minimum accepted amount for offers on this product (overrides the global percentage if set). |
| Maximum Offer Price | Maximum amount — offers above this value are automatically rejected (rarely used). |
Variable Products
For each variation, the same Min Offer Price and Max Offer Price fields appear. The frontend form dynamically updates limits when the customer changes the selected variation.
📊 Admin Dashboard
WooCommerce → Offers — complete list of offers
The main admin page displays all offers organized by status tabs:
- ⏳ Pending
- ✅ Accepted
- ↩️ Countered
- 🔄 Converted
- ❌ Rejected
- 💀 Expired
Available Actions per Offer
Marks the offer as accepted and sends the customer an email with the signed "Add to Cart" link.
Rejects the offer and (if enabled) sends a notification email to the customer.
Proposes an alternative price. The customer receives an email with a signed link for direct acceptance.
Detailed offer page with status history, admin notes and all customer data.
Bulk Actions
Select multiple offers (checkbox) and apply from the dropdown: Bulk Accept or Bulk Reject. Emails are sent individually for each offer.
CSV Export
The Export CSV button in the corner of the page exports any filtered view (tab + active filters). Exported columns include ID, product, variation, customer, offered price, catalog price, status, date.
WordPress Dashboard Widget
A widget automatically appears on the main WordPress Dashboard screen with KPIs from the last 30 days: total offers, accepted, rejected, conversion rate, and a direct shortcut to Pending offers.
🔄 Offer Flow
From customer submission to order conversion
-
1Customer — submits the offer
On the product page, the customer enters the desired price and clicks "Make an Offer". The form is submitted via AJAX (no page reload). The value is validated server-side (min/max, rate limit, nonce).
-
2Server — checks auto-accept
If
wop_auto_accept_percent > 0and the price ≥ threshold, the offer is automatically accepted and the acceptance email is sent immediately. Otherwise, status remains pending. -
3Admin — notified by email
If
wop_notify_admin_new = yes, the administrator receives an email with offer details and a direct link to the admin detail page. -
4Admin — acts on the offer
From the dashboard, the administrator chooses: Accept, Reject or Counter (with an alternative price).
-
5Customer — receives status email
On accept: receives a signed "Add to Cart" link. On counter: receives a signed counter-offer acceptance link. On reject: receives a notification.
-
6Customer — adds to cart and checks out
Via the link in the email or from the "My Offers" tab, the customer adds the product at the negotiated price to the cart and completes the order normally through WooCommerce.
-
7System — marks as "Converted"
When the order is created, the offer is marked converted and
order_idis saved in the table.
↩️ Counter-Offers
Two-way negotiation between admin and customer
The Counter-Offer feature allows the administrator to propose a different price from the customer's offer, without directly accepting or rejecting it.
How it works
-
1Admin enters the counter price
On the offer details page, the administrator fills in the Counter Price field and clicks Send Counter Offer.
-
2Customer receives the email
The email contains the customer's original price, the counter price proposed by the admin, and an accept button with a signed HMAC link.
-
3Customer accepts the counter
By clicking the button (or from the My Offers tab), the offer moves to accepted status at the counter price. The admin receives a confirmation notification.
(offer_id, user_id, status=countered, action=accept_counter, expiry). It cannot be reused, cannot be used by another user, and does not work after expiry.
📈 Analytics
WooCommerce → Offer Analytics
Total offers, accepted offers, conversion rate, average percentage vs catalog price.
Distribution by status: pending, accepted, rejected, countered, expired, converted.
Most offered products, with number of offers and conversion rate.
Chart showing daily evolution of offers received over a selectable period.
The Export CSV button on the Analytics page exports filtered data for processing in Excel or other tools.
👤 My Account — My Offers
Customer experience after submitting offers
Logged-in customers see an additional My Offers tab on the WooCommerce My Account page. The tab displays all submitted offers with their current status.
For offers with Accepted or Counter Accepted status, the Add to Cart at Offer Price button appears, adding the product to the cart at the negotiated price with a single click, using a secure signed link.
🗑️ Uninstall
How cleanup works when deleting the plugin
wop_offers table or the wop_* settings. Reinstalling will find all data intact.
Full Deletion (irreversible)
If you want a complete cleanup on uninstall:
-
1Enable the option in settings
Go to WooCommerce → Offer Settings → Uninstall and check Delete data on uninstall, then save.
-
2Delete the plugin
From Plugins → Installed Plugins, deactivate the plugin and then click Delete.
The uninstall script will delete: the {prefix}wop_offers table, all wop_* options, all per-product metadata, all per-order-item metadata, and the license status.
🔧 Troubleshooting
Common issues and their solutions
- WooCommerce → Offer Settings → Enable offer system is checked (master switch).
- On the product, under Product data → Pricing → Enable Make an Offer is checked.
- The plugin is activated and WooCommerce is active.
- The trial or license has not expired (check WooCommerce → Offer License).
- The WordPress theme does not override WooCommerce hooks — test with Storefront or Twenty Twenty-Four.
wp-cron for hourly expiration (wop_expire_offers_cron). If the site has low traffic, wp-cron may not execute frequently enough.Recommended solution: disable wp-cron in
wp-config.php and configure a real server cron:
# În wp-config.php
define('DISABLE_WP_CRON', true);
# Server cron (cPanel / SSH) — every 15 minutes
*/15 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1
- Check that
wop_notify_admin_newandwop_notify_customer_updateare enabled in Offer Settings → Email. - Check the admin address configured in
wop_notify_admin_email. - WordPress sends emails via PHP
mail()by default — use an SMTP plugin (e.g. WP Mail SMTP) for reliable delivery. - Check the recipient's Spam folder.
- Test with WP Mail SMTP → Email Test.
- The link has expired (check
wop_offer_expiry_days). - The offer status has changed (e.g. admin rejected it in the meantime).
- The URL was modified / truncated by the email client — ensure emails are sent in HTML format.
- The customer is logged in with a different account than the one that submitted the offer.
If PHP is 8.1+ and the error persists, verify the
openssl extension is active:
php -m | grep openssl
.wop-btn-spinner { display: inline-flex } overrode the HTML5 [hidden] attribute. The fix is already implemented via the rule .wop-offer-form [hidden] { display: none !important }. If you see this issue, clear the site's CSS cache.
register_activation_hook hook. If it was skipped (e.g. FTP upload without admin activation):
- Deactivate the plugin from admin.
- Reactivate it — activation will run
WOP_Activator::activate()and create the table viadbDelta().
- The server can make outbound requests to
https://xaniacode.com(check firewall / WAF). - You have not exceeded the license activation limit (check your account on xaniacode.com).
- You have not hit the rate limit of 5 attempts / 15 min — wait and try again.
- The key has not expired and is correct (no extra spaces when pasting).
- Make sure you have selected the Custom theme in the Appearance dropdown — the Custom Theme Colors section only appears for this theme.
- Clear the cache plugin's cache (WP Rocket, W3TC, LiteSpeed etc.) after saving settings.
- Verify that JavaScript is enabled — the Iris color picker requires JS.
📋 Changelog
Version history
Added
- Make an Offer form on the product page (simple + variable, min/max per variation)
- Admin dashboard: tabbed lists, offer details, bulk accept/reject, CSV export
- Counter-offer with signed HMAC tokens for customer acceptance
- Auto-accept threshold (% of catalog price)
- "My Offers" tab in WooCommerce My Account with one-click Add-to-cart
- Analytics page: KPIs, status breakdown, top products, daily trend
- Dashboard widget with 30-day KPIs
- 6 frontend themes (Inherit / Light / Dark / Professional / Art Floral / Custom)
- Custom theme with WordPress color picker (Iris) for a full palette
- Admin and customer email notifications
- Rate limiting per user / per IP, configurable from settings
- Offer cap per customer per product
- Declared HPOS compatibility
- Xania Code License Manager v2.1.1 (14-day trial, auto-update)
- Uninstall script with optional cleanup (opt-in)
Fixed
- Submit button was showing both the label and spinner simultaneously (fix:
[hidden] { display:none !important }defender)
Security
- HMAC SHA-256 tokens on all customer URLs
- Server-side ownership check on token actions
- Encrypted license key storage (AES-256-CBC + HMAC)
- Rate limiting on license activation (5 / 15 min)
- Nonce verification on all admin and AJAX POSTs
- CSV injection mitigation
- Trusted-proxy-aware IP resolution
- wp_safe_redirect on all cart redirects