Week 3: checkout + order engine (Helcim gateway, pre-charge recheck, order state machine)
Wires up Helcim's free WooCommerce plugin (installed inert until a sandbox token exists) alongside a synchronous pre-charge supplier/margin recheck and a fraud-check hook, both firing immediately before payment per the Appendix B immediate-charge decision. Adds a custom order-status sequence (bsc-ordered/bsc-shipped/bsc-exception) as the state machine and review queue, an Action Scheduler-backed supplier-submission job with idempotency guards, a bsc_order_audit ledger, and two new transactional emails. Hooks both the classic WooCommerce checkout actions and the real woocommerce_checkout_validate_order_before_payment hook, since this site's storefront runs WooCommerce Blocks/Store API rather than the classic shortcode checkout (confirmed by reading WooCommerce's own source after the classic hooks turned out not to fire in live testing). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D2gmgaDVXcRGCwLfdXQajD
This commit is contained in:
1 parent
73c64d94c1
commit
4079bfceac
20 files changed
+1224
-15
No files matched your search
@@ -5,10 +5,11 @@ pre-launch plan and the technical design doc (`bookstore-core` schema,
|
||||
interfaces, order state machine — the design doc is the source of truth for
|
||||
*why* this repo is laid out the way it is).
|
||||
|
||||
This week's scope (Week 1 of the 5-week plan): environments, deploy
|
||||
pipeline, and the WooCommerce/HPOS plugin scaffold. Catalog schema, pricing,
|
||||
supplier adapters, and the order state machine are Week 2+ and live under
|
||||
`wp-content/plugins/bookstore-core/includes/`, currently empty.
|
||||
Weeks 1–2 of the 5-week plan are done: environments, deploy pipeline, the
|
||||
WooCommerce/HPOS plugin scaffold, catalog schema, Gutenberg importer,
|
||||
pricing engine, and membership shipping all live under
|
||||
`wp-content/plugins/bookstore-core/includes/`. Checkout, the order state
|
||||
machine, and live supplier integrations are Week 3+.
|
||||
|
||||
## Layout
|
||||
|
||||
@@ -97,6 +98,31 @@ After that, the starter site's content and Customizer settings persist in
|
||||
the database like any other WordPress content — a redeploy or a fresh
|
||||
`deploy.sh` run doesn't touch it or need to repeat it.
|
||||
|
||||
### Payments: Helcim
|
||||
|
||||
`deploy.sh` installs and activates the free, wordpress.org-hosted **Helcim
|
||||
for WooCommerce** plugin (`helcim-commerce-for-woocommerce`) automatically
|
||||
in every environment — nothing to do here for that part.
|
||||
|
||||
The plugin needs a Helcim **sandbox API token**, which isn't something a
|
||||
public API can hand out, so it's a one-time manual step per environment:
|
||||
|
||||
1. Sign up for a free Helcim account.
|
||||
2. Helcim dashboard → **Integrations → API Configuration** → create an API
|
||||
token.
|
||||
3. Put it in `HELCIM_API_TOKEN` in `.env.<environment>`, then re-run
|
||||
`make deploy ENV=<environment>`.
|
||||
|
||||
`deploy.sh` writes the token to a gitignored secrets file the same way it
|
||||
already does for `HARDCOVER_API_TOKEN` (see `secrets/<env>/` above), and
|
||||
`Payments\HelcimConfig::ensure_configured_from_env()` seeds the gateway's
|
||||
WooCommerce settings (sandbox mode, disabled by default — a human still
|
||||
enables it at checkout once ready) the first time it finds a token. Until a
|
||||
token exists, the plugin is installed and inert; nothing else in this repo
|
||||
is blocked on it. Go-live (switching the gateway from sandbox to Helcim's
|
||||
live environment) is a deliberate manual step on the settings screen, not
|
||||
something this repo automates.
|
||||
|
||||
## Deploy pipeline
|
||||
|
||||
The Git server (Gitea) and the staging/production Docker hosts are separate
|
||||
@@ -140,10 +166,58 @@ run a real restore drill:
|
||||
`restore.sh` only ever targets staging — there's no production argument, so
|
||||
a restore drill can't accidentally overwrite the live site.
|
||||
|
||||
## Catalog pipeline (Week 2)
|
||||
|
||||
Staging data is built with `wp bookstore` sub-commands (see `wp bookstore
|
||||
help` for full options):
|
||||
|
||||
```
|
||||
wp bookstore import-gutenberg --limit=10000 # Work/Edition/ISBN rows from the real PG catalog feed
|
||||
wp bookstore generate-offers # synthetic SupplierOffer rows: stock, condition, price
|
||||
wp bookstore sync-products # one WooCommerce product per Work
|
||||
wp bookstore sync-covers --limit=20 # optional: real cover art via Open Library
|
||||
wp bookstore sync-hardcover-tags --limit=20 # optional: genre/mood/content-warning tags
|
||||
```
|
||||
|
||||
`import-gutenberg --limit` counts only *new* titles per run, so re-running
|
||||
with a bigger `--limit` continues forward instead of rescanning. Pricing
|
||||
(25% target margin) and the $25/$15 free-member shipping thresholds are in
|
||||
`includes/Pricing/PricingEngine.php` and
|
||||
`includes/Shipping/BSC_Free_Shipping.php`.
|
||||
|
||||
## Checkout + order engine (Week 3)
|
||||
|
||||
Helcim's WooCommerce plugin is installed (see "Payments: Helcim" above),
|
||||
paired with a synchronous pre-charge supplier/margin recheck and a
|
||||
fraud-check hook — both run right before the payment gateway is called
|
||||
(`Order\CheckoutRecheck`), never after. A passing order moves through a
|
||||
custom status sequence beyond WooCommerce's own pending/processing/
|
||||
completed: `bsc-ordered` (submitted to the supplier via Action
|
||||
Scheduler, `Order\SupplierOrderJob`) → `bsc-shipped` → `completed`. Any
|
||||
failure at any stage — no sellable offer, margin no longer holds, fraud
|
||||
check fails, or the supplier submission itself fails after charging — lands
|
||||
the order in `bsc-exception` ("Needs Review"), which is the review queue:
|
||||
just a filterable WooCommerce order status, not a separate table. A
|
||||
post-charge failure also triggers an automatic refund
|
||||
(`wc_create_refund(..., refund_payment: true)`), per the design doc's
|
||||
Appendix B decision that an undo is always a refund, never a void.
|
||||
|
||||
`wp bookstore force-offer-status <isbn13> <active|stale|discontinued>` (see
|
||||
`wp bookstore help`) forces a specific ISBN's synthetic offer into a
|
||||
specific state — `SyntheticOfferGenerator`'s rolls are random, so this is
|
||||
how to reliably exercise the recheck's failure paths (price-increase,
|
||||
out-of-stock, discontinued-after-charge) in a real checkout.
|
||||
|
||||
Real BooksRun/Ingram supplier adapters are still Week 4 — `SupplierOrderJob`
|
||||
runs against the synthetic `gutenberg_test` supplier for now, deliberately
|
||||
built through the same Action Scheduler/locking path Week 4's real
|
||||
integration will reuse rather than restructure.
|
||||
|
||||
## What's deliberately not here yet
|
||||
|
||||
- Catalog tables, pricing engine, supplier adapters, order state machine —
|
||||
Week 2–4, see the design doc.
|
||||
- Live BooksRun/Ingram supplier adapters — Week 4, see the design doc.
|
||||
- Automated tests for the pricing/shipping-threshold/checkout-recheck
|
||||
logic — called for by the design doc's launch gates, not yet written.
|
||||
- A real TLS-terminating public domain — Caddy will auto-provision certs
|
||||
once `SITE_DOMAIN` points at a real host with 80/443 reachable; until
|
||||
then `docker-compose.dev.yml` is the only one that works from
|
||||
|
||||
Reference in new issue
Block a user