zsty.us

Before / After · Case Study

BMH payment rails — Wix payments to a two-rail Medusa module with hard caps

  • Hemp / CBD
  • Local Pro Services

Big Moose Hemp ran customer payments through WIX-PAY-PRO + Square. Hemp/CBD is a high-risk MID category and the federal hemp ban (P.L. 119-37, effective Nov 12 2026) accelerates the need to move off a stack that doesn't expose payment-policy controls. The rebuild ships a two-rail payment provider abstraction inside the BMH Medusa 2.x backend — Clover (card, capped at $250) via Global Payments Solutions ISO for the high-risk MID, and Aeropay (ACH) for any purchase over the cap and for the 50% deposit on bulk clone orders. Hard caps are enforced at three levels — admin, API, and storefront — so no path can bypass them.

Belt and suspenders — the card cap is enforced in three places so neither the admin nor the API can bypass it.

Receipts — measured

Numbers that moved.

1
Payment rails under one Medusa module
was 1
0
Cap-enforcement layers (module + API + storefront)
was 0
0 $
Hard card cap
was 0 $
0
Webhook reconciliation surfaces
was 0
  • 2 payment rails (Clover card + Aeropay ACH) under one Medusa module
  • Hard $250 card cap enforced at admin + API + storefront
  • 13-method contract surface across the two providers
  • Feature-flagged rail toggle (FEATURE_CLOVER_CARD_RAIL)

Architecture

One chain replaced by another. Receipts above.

Old stack
  • Wix payments
  • Square (single MID)
  • No ACH fallback
  • No cap enforcement
  • No admin policy
New stack
  • Medusa 2.x modular payments
  • payment-clover (card ≤ $250)
  • payment-aeropay (ACH > $250)
  • /admin/settings/payment-policy
  • HMAC webhooks (settlement + reversal)
  • Three-level cap enforcement
  • Feature flag toggle

What changed

Grouped by what kind of system shipped.

Each claim ships with concrete evidence — env vars, table names, cadence chips. No marketing fluff.

Agent backbone

Two-rail abstraction — card under the cap, ACH over

`payment-clover/index.ts` and `payment-aeropay/index.ts` both implement the Medusa 2.x payment provider contract. The card rail (Clover Ecomm SDK via Global Payments Solutions ISO) handles tokenize → auth → capture → refund → void → status sync → webhook decode. The ACH rail (Aeropay) handles the bank-linked pull (1-2 business days) → settlement → reversal — used for any card-over-cap purchase and for the 50% deposit on the 500+ clone Standard-tier orders.

Before
Single payment surface inside Wix — no rail switching, no cap enforcement, no admin policy.
After
Two independent rails under one Medusa module, swappable per order amount, with a single admin policy endpoint.
  • Clover: 7-method contract (initiate, authorize, capture, refund, cancel, retrieve, webhook)
  • Aeropay: 6-method contract (initiate, authorize, capture-n/a, refund, retrieve, webhook)
  • Both modules expose sandbox vs production URLs via environment switch
  • Sandbox merchant id JM3X09S2DHK06 (B-IT) for Clover dev
Agent backbone

Hard cap at three levels — belt and suspenders

The $250 card cap (NEXT_PUBLIC_CARD_MAX_CENTS, default 25000) is enforced in the Clover module's `authorize()` (rejects any over-cap call), in the `/api/checkout` storefront API, AND in the `/admin/settings/payment-policy` surface. No path — including admin-authed manual orders — can route a card charge above the cap. Above-cap orders automatically route to the Aeropay ACH rail.

  • Module-level: payment-clover rejects authorize() with amount > cardMaxCents
  • Storefront: /api/checkout enforces the same cap before calling the provider
  • Admin: /admin/settings/payment-policy returns the cap + ACH-required-above flag
  • Feature flag FEATURE_CLOVER_CARD_RAIL gates the card rail entirely
Agent backbone

Reconciliation by webhook — settlement + reversal land in the same surface

Both rails publish settlement + reversal events. Clover's `getWebhookActionAndData()` verifies the HMAC + decodes the event into a typed action; Aeropay's webhook handles settlement + reversal asynchronously. Both feed back into the Medusa order-state machine so the operator sees one reconciliation view, not two.

Retention

Designed to swap rails as the federal hemp ban hits

The architecture is rail-agnostic on purpose. As P.L. 119-37 (Nov 12 2026) re-classifies most of the current catalog, the storefront swaps from `payment-clover` (high-risk hemp MID) → `payment-stripe` (compliant wellness MID) by changing one provider registration in `medusa-config.ts`. ACH stays on Aeropay throughout.

While she sleeps.

Autonomous surfaces

The agent backbone keeps the brand earning between gigs. Jackie approves; the system runs.

  • Routes payments by amount, not by operator choice

    on every checkout

    Storefront and admin both consult the payment-policy endpoint; over-cap purchases auto-route to ACH without operator intervention.

  • Reconciles webhook events into the Medusa order-state machine

    real-time on each event

    Settlement + reversal events from both rails land in the same operator surface — no separate dashboards.

  • Gate-flips the card rail without a redeploy

    instant on env flip

    FEATURE_CLOVER_CARD_RAIL=false takes the card rail offline immediately; storefront automatically presents ACH-only checkout.

← All rebuilds

BMH payment rails — Wix payments to a two-rail Medusa module with hard caps — zsty.us