Integrate the Payment Component
Mount secure payment fields and use the documented callback contract.
Install the component
For React, install @nmipayments/nmi-pay-react and import NmiPayments. The equivalent vanilla package is @nmipayments/nmi-pay, exposing mountNmiPayments. Keep card collection inside the component rather than adding your own card-number inputs. import { NmiPayments } from '@nmipayments/nmi-pay-react';
Implement a basic React checkout
This original example uses NMI’s documented tokenizationKey and onPay contract. submitPayment is an application-owned function you supply; it is not an NMI SDK method. It should call your authenticated server with orderId and paymentToken, then return a normalized result. Keep order pricing on that server. import { NmiPayments } from '@nmipayments/nmi-pay-react'; export function Checkout({ publicKey, orderId, submitPayment }) { return <NmiPayments paymentMethods={['card']} tokenizationKey={publicKey} onPay={async ({ token }) => { try { const result = await submitPayment({ orderId, paymentToken: token }); return result.success === true ? true : 'Payment was not confirmed. Check the order status.'; } catch { return 'We could not confirm the payment. Check the order before retrying.'; } }} />; }
Choose callbacks deliberately
Use onPay for the component-managed submit button and loading state. For custom orchestration or 3DS, onChange exposes readiness and the token; completion is reversible when the customer clears a field. A token being available does not mean a charge was approved. Callback What it provides Implementation rule onPay Payment event containing token; Promise<true|string> return Return true only after server-confirmed success onChange token and complete state Clear stale readiness when complete becomes false onExpressCheckout Wallet button event Route through the same server validation and payment handling onFieldsAvailable Fields finished initializing Use for a loading indicator, not a payment result
Recovery and field lifecycle
For a custom onChange flow, NMI documents resetFields() after a payment submission fails before another attempt. The onPay integration handles this reset itself. If the network response is uncertain, first reconcile the order; resetting fields must not become an automatic second charge. Require a deliberate user action to submit a completed form. Prevent duplicate in-flight attempts in your application, including multiple tabs. Render status in accessible text and preserve a usable keyboard path. Do not log token lookup data or the entire payment event.
Acceptance checks
Test initialization, incomplete fields, successful submission, decline and network failure. Confirm that a completion callback can be invalidated by an edit. Verify that the private API key never appears in page source, source maps or browser network requests. For 3DS, use the dedicated authentication flow rather than treating onPay alone as authentication. Next: implement your server
Start with card payments
Set paymentMethods to ['card'] for the first integration milestone. Enable wallets separately once their amount, domain, HTTPS and browser requirements are configured. In an October 16 sandbox check, the card-only component completed tokenization, a REST sale and a void in Chrome. The in-app WebKit browser failed initialization on HTTP localhost during Apple Pay capability detection; this was not a payment decline. This verifies the basic card flow in the tested environment. It does not establish Apple Pay, Google Pay, 3DS, device or production readiness.