WP Manifestindependent plugin directory
manifest / ecommerce / lilac-hour-commerce

Lilac Hour Commerce

WooCommerce toolkit for high-risk stores: age verification, discreet packaging and billing, regional restrictions, hosted card gateway with signed webhooks.

★ 0stars

Install

Download source zip No release zip yet

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.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

A 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:

  1. 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.
  2. 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.
  3. Regional product restrictions. Rules such as "category toys can't ship to US: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.
  4. 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)
Checkout blocking a product from shipping to Alabama Sandbox hosted payment page Discreet packaging option

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

  1. Copy the folder to wp-content/plugins/lilac-hour-commerce and activate it. Activation generates the webhook signing secret and seeds an example restriction rule.
  2. WooCommerce → Settings → Lilac Hour: minimum age (18/21), sender name, card descriptor and restriction rules.
  3. 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.