NMI + Maverick · Interactive Examples
Card, ACH, and terminal payments — tied to one POS order. In about 40 lines.
They said this integration was hard. It isn't. NMI handles card-not-present and ACH, Maverick reports your card-present terminal sales, and one reconciliation key ties every rail back to the order. Walk the interactive examples below — every response is simulated, the code is the real thing.
The architecture
Three rails → one order → one reconciled report.
This is the exact shape your POS needs: take payment at the register three different ways, and have every one of them roll back to the same order and the same deposit report.
Card — card-not-present
Collect.js hosted fields tokenize the card in the browser, then your server posts transact.php type=sale. No PCI scope.
source='nmi' · entry=card_not_presentACH — eCheck
Bank routing + account post server-side to transact.php payment=check. Bank data isn't PCI card data.
source='nmi' · type=ach_debitCard-PRESENT — terminal
A physical PAX terminal rings the sale; it settles into Maverick's batches / authorizations reporting feed.
source='maverick' · entry=card_present · TID 840356The reconciliation spine
That match is the spine. In our integration, Maverick stamps the originating gateway transaction id into purchaseId on the settled batch — so a card sale that ran through NMI lines up with its NMI transaction id, and we enrich the existing row instead of counting it twice. A terminal item with purchaseId: null is usually a card-present-only sale you link to the order via a dropdown — but some networks (e.g. Amex) can settle without a purchaseId, so fall back to a last4 + amount + date match against your NMI rows before treating it as net-new.
NMI Collect.js · styling preview
Hosted-field card capture — restyle it live.
This is how NMI Collect.js hosted fields look in your checkout. Flip the theme presets and watch the field styling change — the exact style JSON updates alongside, ready to paste. Submitting here returns a simulated approval; the real Collect.js tokenize-and-charge code is in the snippet below.
Theme / style preset
CollectJS.configure({ … }) style object
Live · NMI eCheck
ACH debit — bank rails, same API.
Routing + account post straight to your server (bank data isn't PCI card data), then to NMI as payment=check. ACH submits pending and clears in a few business days — we show that honestly.
Live · NMI recurring
Schedule a payment for later — pay-later, built in.
NMI inline custom subscriptions handle the "charge this later / charge it every month" path. A future start_date = the first charge fires later. Cancel proves you own the lifecycle.
Maverick · card-present
Track the physical terminal sales.
Pull settled card-present sales from Maverick's reporting feed and resolve which physical device rang each one. The settled-batch terminal object is often empty in practice, so in our integration we match each batch to its /reporting/authorizations row by last4 + nearest settlement date, then map the TID to the device.
| Date | Card | Amount | Terminal | Batch | purchaseId | Status |
|---|---|---|---|---|---|---|
| Loading terminal sales… | ||||||
A non-null purchaseId means this terminal row is the same sale that already ran through NMI — we enrich, never double-count. purchaseId: null is usually a card-present-only sale → link it to an order in the POS screen below; but because some networks (e.g. Amex) can settle without a purchaseId, fall back to a last4 + amount + date match against your NMI rows first so you don't double-count one that already came through the gateway.
The POS screen
One order. Pay now, schedule, or tag.
The literal No Limits workflow: at the register the operator pays one of three ways — and all three converge on the same thing: order.transaction_id points at a transaction, paid_method names the rail.
Recent settled Maverick terminal sales not yet linked to an order. The dropdown is fed by GET /pos/candidates.
Level 2/3 line-item data · both rails
Attach line-item data — built once, sent right.
Commercial-card sales carry enhanced line-item data the bank and card networks require for acceptance — PO number, per-line commodity codes, tax, ship-from / ship-to. You build it once, generically, from the POS line items plus a small per-merchant template, and emit the correct shape for whichever rail runs the card.
Acquirers and the card networks require this itemized data for a sale to be accepted as a commercial-card transaction.
Richer, structured transaction data strengthens fraud and risk screening on every sale.
A complete, itemized record per sale — PO number, line items, tax, ship-from / ship-to — is an auditable transaction record.
Itemized Level 3 data is the evidence you present to defend a chargeback or dispute (representment).
One builder · both rails · buildLevel3(lineItems, template, amount)
You input the fields generically — the POS already has the line items, the merchant sets a small template once, and one function reconciles and emits the rail-specific payload. It degrades safely: if it cannot reconcile or a required field is missing, it falls back to a normal sale instead of sending data NMI would silently drop or that Maverick would reject.
- 1Take the POS line items{ description, quantity, unit_price } — already in the cart; nothing re-keyed.
- 2Add a per-merchant templateStatic stock values set once: commodity code, unit of measure, ship-from ZIP, summary commodity code.
- 3Compute each item totalitem_total = round(unit_cost × qty − discount, 2) — tax excluded.
- 4Reconcile to the chargeΣ item_total + tax + shipping + duty == amount.
- 5Emit the rail shapeNMI flat item_*_N fields, or Maverick's nested order object with level: 3.
- 6Fall back if it can'tReconcile fails or a required field is missing → send a normal sale. Never silently dropped on NMI, never a 422 on Maverick.
Field reference · Required vs Optional
Every field is flagged Required or Optional, grouped required-first so the must-send set is obvious at a glance. Required-ness is sourced from the integration modules and the API collections — not guessed.
Boarded forwarding · silently drops if it doesn't reconcile
"Level III Advantage" is a per-merchant boarded service — boarding only lets the gateway forward L2/L3; you still send every field yourself on the sale (and on captures — a partial capture must carry the data or it's lost). If Σ item_total + tax + shipping + duty ≠ amount (tax excluded from item totals), NMI silently discards the L3 data — the sale still succeeds, but the enhanced data is gone with no error. Reconcile before you send.
Source: server/nmi_level3.py docstring + reconcile logic.
No boarding flag · strict 422 · category is an enum
L3 is active on all gateway accounts whenever the order is sent — no boarding step (different from NMI). The gateway strictly validates the order and returns 422 if a required field is missing. The tax detail's category must be a valid enum — VAT and National are accepted; Sales is rejected as a category (the separate type field may be "Sales"). Ship-from and ship-to ZIP are required — missing required value → fall back to a normal sale, don't eat the 422.
Source: server/maverick_level3.py docstring + Maverick Postman.
Try it · the generic build
Edit the example line items and template, then watch buildLevel3() reconcile and emit both rail shapes. This computes only the baked-in example in your browser — it calls nothing.
Line items (from the POS)
Per-merchant template (set once)
Unified reporting
Every rail, reconciled to the order.
One table across all three rails, plus the deposit/funding view — exactly what the Lifted portal dashboard compiles. This is sample data; anything you submit in the examples above appears on top of it. The key trick: a card sale that runs through the NMI gateway also lands in Maverick's batch, so it would show twice — we dedupe on purchaseId == NMI txn id and enrich the NMI row instead of double-counting.
Money pipeline · sales → deposits
Watch each sale travel authorization → settlement batch → money-in-the-bank, see exactly what is still pending, and catch anything stuck (slow ACH). Example lifecycle — dates and stages are illustrative and simulated; no live API call was made.
Transactions · all rails → order #NL-1042
| Date | Customer | Rail | Method | Card / Acct | Amount | Status | Batch | Order |
|---|---|---|---|---|---|---|---|---|
| Loading reporting… | ||||||||
Deposits / funding
| Date | Source | Gross | Fees | CB / Returns | Net deposited |
|---|
The whole integration
Tokenize → charge → reconcile. ~40 lines.
The hero claim, in one file: capture a card, take an ACH debit, then reconcile all three rails to the order. Every snippet on this page is copy-paste real and Postman-linked.
Drop-in plugin code
Six functions. Copy, paste, ship.
The "impossible" integration is six small functions. Here they are — real, correct, and runnable in your own NMI / Maverick sandbox. Switch languages, copy, open the matching Postman request. Every result below is an example rendered from baked-in data: nothing here calls an API or shows live money.