Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/cezarys/lilac-hour-commerce/archive/refs/heads/main.zipA WooCommerce toolkit for high-risk stores (adult, CBD, vape and similar), the kind that can't rely on Stripe, Shopify apps or "just install a plugin". It adds:
- Age verification, twice. An entry gate on every page, then a date-of-birth check at checkout validated on the server. Only the result is stored on the order ("18+ confirmed …"), never the date.
- Discreet by default. A plain-packaging option at checkout, the card statement descriptor shown before payment, neutral email subjects, headings and sender name for discreet orders, and a Packaging column in the orders list.
- Regional product restrictions. Rules such as "category
toyscan't ship toUS:AL", set in the admin. They're checked on the cart, live while the customer edits the checkout address, again on submit against the posted address, and shown on the product page. - Hosted card gateway with signed webhooks. The pattern high-risk processors use: create a payment session, redirect to a hosted payment page (card data never touches the store), and mark the order paid only when an HMAC-signed webhook confirms it. Refunds go back through the gateway.
It was built for Lilac Hour, a concept store and not a real business. The payment processor is a built-in sandbox, so the whole flow runs offline.
| Checkout restriction + age field | Hosted payment page | Discreet checkout (mobile) |
|---|---|---|
![]() |
![]() |
![]() |
Requirements
- WordPress 6.5+, WooCommerce 9+ (tested on 11.2), PHP 7.4+
- Classic
[woocommerce_checkout]shortcode checkout (the hooks used are classic-checkout hooks) - HPOS compatible
Install
- Copy the folder to
wp-content/plugins/lilac-hour-commerceand activate it. Activation generates the webhook signing secret and seeds an example restriction rule. - WooCommerce → Settings → Lilac Hour: minimum age (18/21), sender name, card descriptor and restriction rules.
- WooCommerce → Settings → Payments: enable Hosted card (high-risk processor).
Restriction rules use one line each:
category-slug | CC or CC:STATE, … | message shown to the customer
toys | US:AL, US:TX | Not available in your state.
wellness | CA | Can't ship to Canada yet.
Payment flow
Checkout ──process_payment()──▶ create session ──▶ redirect to hosted page
│ customer pays
Order paid ◀── payment_complete() ◀── POST /wp-json/lh/v1/webhook (signed event)
Every webhook is checked before it can touch an order:
| Check | Result |
|---|---|
X-LH-Signature: t=…,v1=… is HMAC-SHA256 of timestamp.body |
401 if wrong |
| Timestamp within 5 minutes | 400 if stale (blocks replays of captured requests) |
| Event belongs to the order's payment session | ignored otherwise |
| Event ID not seen before | duplicates acknowledged and skipped (processors retry) |
| Amount and currency match the order | order put on hold with a note if not |
| Order not already paid | a late "failed" event can't undo a paid order |
Sandbox test cards (any future expiry, any CVC):
| Card | Result |
|---|---|
4242 4242 4242 4242 |
approved |
4000 0000 0000 0002 |
declined |
4000 0000 0000 9995 |
insufficient funds |
4000 0000 0000 0119 |
processing error |
Plugging in a real processor
LH_Sandbox_Processor stands in for the processor's servers. To go live, replace create_session() and refund() in LH_Gateway_Hosted_Card with the processor's API calls, set its signing secret in lh_webhook_secret, and point its webhook at /wp-json/lh/v1/webhook. Adapt LH_Webhook::handle() if its signature header or event names differ.
The sandbox delivers webhooks in-process rather than over HTTP, because PHP's single-threaded dev server can't receive a request while it's still serving one. The handler is the same either way.
Structure
lilac-hour-commerce.php bootstrap, HPOS declaration, activation
includes/class-lh-settings.php settings tab + typed getters
includes/class-lh-age-gate.php entry gate + checkout DOB check
includes/class-lh-discreet.php packaging option, descriptor, neutral emails, orders column
includes/class-lh-restrictions.php regional rules on cart, checkout refresh and submit
includes/class-lh-gateway-hosted-card.php WC_Payment_Gateway: sessions + refunds
includes/class-lh-webhook.php signed webhook endpoint
includes/class-lh-sandbox-processor.php simulated processor: sessions, hosted page, test cards
assets/ age gate and hosted page styles/scripts
License
GPL-2.0-or-later. Built by Cezary Siwa.


