Overview
Icon for NMI

NMI

Accept card, ACH, and wallet payments via NMI Gateway

medusa-payment-nmi

A Medusa v2 payment provider for the NMI Gatewaycard, 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 clipboardauthorized); a settlement webhook captures it, an ACH return fails it.

Install

npm install medusa-payment-nmi

Or straight from GitHub (the Copy to clipboardprepare script builds Copy to clipboard.medusa/server on install):

npm 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)

module.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

  1. Copy to clipboardinitiatePayment returns Copy to clipboard{ tokenizationKey } to the storefront.
  2. 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.
  3. Copy to clipboardauthorizePayment charges the token:
    • card/walletCopy to clipboardauth or Copy to clipboardsale (per Copy to clipboardcaptureMethod) → Copy to clipboardauthorized / Copy to clipboardcaptured.
    • achCopy to clipboardsaleCopy to clipboardauthorized (not yet settled).
  4. For ACH, the settlement webhook → Copy to clipboardcaptured; an ACH return → Copy to clipboardfailed.

Webhooks

Medusa exposes one webhook route for the provider:

  • Copy to clipboardPOST 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 clipboardauthorized and Copy to clipboardcaptured outcomes. 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 clipboardcanceled webhook outcomes in current Medusa core, do not auto-transition the payment — add your own subscriber on the Copy to clipboardpayment.webhook_received event 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:

npm install # also builds .medusa/server via prepare
npm run dev # medusa plugin:develop — watch + publish to the local registry
npm test # vitest unit tests

To try local changes inside a Medusa app, use the local plugin workflow:

# in this repo
npx medusa plugin:publish
# in your Medusa app
npx 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

You may also like

Browse all integrations

Build your own

Develop your own custom integration

Build your own integration with our API to speed up your processes. Make your integration available via npm for it to be shared in our Library with the broader Medusa community.

gift card interface

Ready to build your custom commerce setup?