NMI
Accept card, ACH, and wallet payments via NMI Gateway
medusa-payment-nmi
A Medusa v2 payment provider for the NMI Gateway — card, ACH/eCheck, and Apple/Google Pay through one provider, collected with NMI's official Copy to clipboard<NmiPayments> component (Copy to clipboard@nmipayments/nmi-pay-react). The card PAN / bank number is tokenized in the browser and never touches your server.
You register a single NMI payment method; the customer picks card or bank (or a wallet) inside the payment element, and the provider runs the right lifecycle:
- Card / Apple Pay / Google Pay authorize synchronously — the backend charges the token via the Payment API and knows the result immediately.
- ACH is asynchronous — the backend submits the debit (Copy to clipboard
authorized); a settlement webhook captures it, an ACH return fails it.
Install
1npm install medusa-payment-nmi
Or straight from GitHub (the Copy to clipboardprepare script builds Copy to clipboard.medusa/server on install):
1npm install github:Kaelbroersma/medusa-payment-nmi
Requires: Medusa Copy to clipboard>= 2.5, Node Copy to clipboard>= 20.
This package is a standard Medusa plugin built with Copy to clipboardmedusa plugin:build, so it follows the official exports layout — Copy to clipboardmedusa-payment-nmi/providers/nmi resolves the payment module provider.
Configure (Copy to clipboardmedusa-config.ts)
1234567891011121314151617181920module.exports = defineConfig({modules: [{resolve: "@medusajs/medusa/payment",options: {providers: [{resolve: "medusa-payment-nmi/providers/nmi",options: {securityKey: process.env.NMI_SECURITY_KEY,tokenizationKey: process.env.NMI_TOKENIZATION_KEY,webhookSecret: process.env.NMI_WEBHOOK_SECRET,captureMethod: "auth", // card/wallet: "auth" or "sale"secCode: "WEB", // ACH SEC code (internet-initiated consumer debit)sandbox: process.env.NODE_ENV !== "production",},},],},},
Option Required Notes Copy to clipboardsecurityKey ✅ Private API key for Copy to clipboardtransact.php. Copy to clipboardtokenizationKey ✅ Public key; sent to the storefront Copy to clipboard<NmiPayments> component. Copy to clipboardwebhookSecret ✅ Webhook signing key (HMAC-SHA256). Copy to clipboardcaptureMethod — Card/wallet: Copy to clipboardauth (default) or Copy to clipboardsale. Copy to clipboardsecCode — ACH: Copy to clipboardWEB (default), Copy to clipboardPPD, Copy to clipboardCCD, Copy to clipboardTEL. Copy to clipboardsandbox — Use Copy to clipboardsandbox.nmi.com.
How a payment flows
- Copy to clipboard
initiatePaymentreturns Copy to clipboard{ tokenizationKey }to the storefront. - Copy to clipboard
<NmiPayments>tokenizes the chosen method → a single-use token; the storefront writes Copy to clipboard{ payment_token, payment_method }onto the payment session and completes the cart. - Copy to clipboard
authorizePaymentcharges the token:- card/wallet → Copy to clipboard
author Copy to clipboardsale(per Copy to clipboardcaptureMethod) → Copy to clipboardauthorized/ Copy to clipboardcaptured. - ach → Copy to clipboard
sale→ Copy to clipboardauthorized(not yet settled).
- card/wallet → Copy to clipboard
- For ACH, the settlement webhook → Copy to clipboard
captured; an ACH return → Copy to clipboardfailed.
Webhooks
Medusa exposes one webhook route for the provider:
- Copy to clipboard
POST https://<your-backend>/hooks/payment/nmi
In NMI Merchant Portal → Settings → Webhooks, add that URL, paste the signing key (your Copy to clipboardwebhookSecret), and subscribe to these events:
Events Copy to clipboardtransaction.sale.success, Copy to clipboardtransaction.auth.success, Copy to clipboardtransaction.capture.success, Copy to clipboardtransaction.refund.success, Copy to clipboardtransaction.void.success, Copy to clipboardtransaction.sale.failure, Copy to clipboardsettlement.batch.complete
The handler verifies the Copy to clipboardWebhook-Signature HMAC and maps each event to the right action, disambiguating card vs ACH by the event body. NMI requires public HTTPS — for local dev, tunnel (e.g. Copy to clipboardcloudflared, Copy to clipboardngrok) to your backend.
Note on async outcomes: Medusa's built-in payment-webhook subscriber acts on the Copy to clipboardauthorizedand Copy to clipboardcapturedoutcomes. ACH settlement (→ captured) works out of the box. ACH returns and voids are detected and mapped by this provider but, like all Copy to clipboardfailed/Copy to clipboardcanceledwebhook outcomes in current Medusa core, do not auto-transition the payment — add your own subscriber on the Copy to clipboardpayment.webhook_receivedevent if you need automated return/void reconciliation.
Storefront
See Copy to clipboardstorefront/README.md for the copy-paste Copy to clipboard<NmiPayments> component and checkout wiring.
Local development
The repo uses the official Medusa plugin toolchain:
123npm install # also builds .medusa/server via preparenpm run dev # medusa plugin:develop — watch + publish to the local registrynpm test # vitest unit tests
To try local changes inside a Medusa app, use the local plugin workflow:
12345# in this reponpx medusa plugin:publish# in your Medusa appnpx medusa plugin:add medusa-payment-nmi
Not in v1
Saved cards / NMI Customer Vault. The account-holder methods are implemented as no-ops around a synthetic holder; no card is stored. (Easy to add later.)
License
MIT

