medusa-payment-kadima
v0.2.1
Published
Medusa v2 payment provider for Kadima — direct card + ACH, no middle gateway
Maintainers
Readme
medusa-payment-kadima
Medusa v2 payment provider that connects a store directly to Kadima — cards and ACH — with no middle gateway (no NMI). Any merchant already boarded with Kadima can plug their own terminal/DBA credentials into Medusa and process natively.
- Two providers:
kadima-card(synchronous,terminal.id) andkadima-ach(asynchronous,dba.id). - Per-merchant tokens: each merchant supplies their own Kadima API token.
- Configurable card capture:
auth(authorize → capture) orsale(both). - PCI SAQ-A: card data is collected by Kadima Hosted Fields in the browser; the backend only ever handles tokens.
Prerequisite: a Kadima merchant account
This plugin processes through your own Kadima merchant account — it needs your API token, terminal id, DBA id, and webhook secret. Don't have a Kadima account yet? 👉 Apply here — card + ACH, no middle gateway, risk defined upfront. Once you're boarded you'll have the credentials below.
Install
npm install medusa-payment-kadimaConfigure
Register both providers on the Payment module in medusa-config.ts:
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/payment",
options: {
providers: [
{
resolve: "medusa-payment-kadima/providers/kadima-card",
id: "kadima-card",
options: {
apiToken: process.env.KADIMA_TOKEN,
terminalId: Number(process.env.KADIMA_TERMINAL_ID),
dbaId: Number(process.env.KADIMA_DBA_ID), // for saved cards
webhookSecret: process.env.KADIMA_WEBHOOK_SECRET,
captureMethod: "auth", // or "sale"
storeUrl: process.env.KADIMA_STORE_URL, // REQUIRED for Hosted Fields — your storefront URL
sandbox: process.env.KADIMA_SANDBOX === "true",
},
},
{
resolve: "medusa-payment-kadima/providers/kadima-ach",
id: "kadima-ach",
options: {
apiToken: process.env.KADIMA_TOKEN,
dbaId: Number(process.env.KADIMA_DBA_ID),
webhookSecret: process.env.KADIMA_WEBHOOK_SECRET,
secCode: "WEB",
sandbox: process.env.KADIMA_SANDBOX === "true",
},
},
],
},
},
],
})⚠️ Set
sandboxexplicitly — do not derive it fromNODE_ENV. Most hosting platforms (Railway, Render, Vercel, Heroku, Fly) setNODE_ENV=production, soNODE_ENV !== "production"silently evaluates tofalseand your test build hits the live Kadima hosts with sandbox credentials →HTTP 401: invalid credentials. While testing, setKADIMA_SANDBOX=truein your env; unset it (orfalse) for production.sandbox: trueusessandbox.kadimadashboard.com/sandbox-gateway.kadimadashboard.com;falseuses the live hosts. The token, terminal ID and DBA ID must come from the same environment as the flag (a sandbox token on a live host, or vice-versa, is a 401).
On startup each provider logs its resolved configuration so mismatches are obvious, e.g.:
[kadima-card] init — sandbox=true · gateway=https://sandbox-gateway.kadimadashboard.com · dashboard=https://sandbox.kadimadashboard.com · terminal=404 · token=setIf you see token=MISSING, the env var isn't set in your deployment. If sandbox or the
host is wrong for your token, fix the flag and redeploy.
Enable the providers in your sales channel / region, then register your Kadima
webhooks. Medusa's built-in webhook route is /hooks/payment/{identifier}_{id}
(Medusa prepends pp_ internally), so with the config above register:
https://<your-server>/hooks/payment/kadima-card_kadima-card
https://<your-server>/hooks/payment/kadima-ach_kadima-ach⚠️
/hooks/payment/kadima-card(without the_kadima-cardid suffix) does NOT match — Medusa will never process the event and orders will stay pending forever. If you change theid:in your config, the URL changes with it.
Then add the storefront components from storefront/.
Hosted Fields (card) requirements
Two things trip up the card flow specifically:
- Token permission. The API token must have the
api-creditcard-payment-read-writepermission (Kadima dashboard → Developers → Tokens). Without it, the Hosted Fields token mint / card processing fails. (A token with only reporting/tickets scopes will not work for card payments.) storeUrl/ domain. Hosted Fields locks the card iframe to a domain. SetstoreUrl(KADIMA_STORE_URL) to the exact origin your storefront runs on (e.g.https://shop.example.com, or your Railway/preview URL while testing). If the page origin doesn't match, Kadima raiseshostedFields.error: "Invalid Domain". The provider throws an actionable error at session creation ifstoreUrlis unset.
The plugin loads the HostedFields.js asset that matches sandbox automatically
(sandbox.kadimadashboard.com vs kadimadashboard.com) and passes it to the storefront
component as data.hfScriptUrl — you don't hard-code it.
Status
Card + ACH clients, both providers, CustomerVault, and webhook verification are
reference-verified against the canonical Kadima API and sandbox-tested live
(auth → capture → refund, ACH debit, full vault flow). The providers compile clean
against @medusajs/framework 2.17, ship as CommonJS (loadable by Medusa's
require()-based plugin loader on every Node 20+), and every export subpath
default-exports the ModuleProvider shape Medusa's loader requires. Build with
npm run build (tsup → .medusa/server).
| Module | Verified |
|---|---|
| Card client + Hosted Fields | 20 claims · live sandbox |
| ACH client + provider | 18 claims · live sandbox |
| CustomerVault (saved cards) | 12 claims · live sandbox |
| Webhook signature | 7 unit tests pass |
| Providers vs @medusajs/framework | tsc clean + runtime load OK |
Before production: set a strong encryption/secret env, switch sandbox:false
with live per-merchant credentials, register the webhook URL, and run a checkout
end-to-end in your Medusa app.
Layout
docs/
DESIGN.md Architecture, method mapping, phased plan
PHASE-0-CONFIRMATION.md Questions + sandbox credentials needed before coding
src/
index.ts Registers both providers on the Payment module
types.ts Per-merchant options, hosts, status enums
lib/
kadima-card-client.ts gateway.kadimadashboard.com + Hosted Fields token
kadima-ach-client.ts kadimadashboard.com/api/ach + Customer Vault
webhook.ts SHA-512(secret+id+module+action+date) verify
errors.ts Decline classification / KadimaError
providers/
kadima-card.ts Card provider (AbstractPaymentProvider)
kadima-ach.ts ACH provider (async → Medusa sync contract)
storefront/
hosted-fields-example.md Browser card-collection contractReference docs (in the website repo)
docs/2-Payment Gateway API.pdf, docs/4-Webhooks.pdf, docs/ACH Agreement V1.7.pdf,
and the boarding webhook scheme in api/webhooks/kadima.js.
Configure
See the example block in src/index.ts and .env.example.
