Surcharging with Card Type Detection

Implementing Credit Card Surcharges with the NMI Payment Component

This guide covers how to detect card type and apply a credit card surcharge when using the NMI Payment Component (@nmipayments/nmi-pay-react).

The general approach is:

  1. Detect the card category (credit, debit, etc.) from the onChange callback
  2. Calculate the surcharge amount when the category is "credit"
  3. Display the surcharge to the customer before they confirm payment
  4. Send the total (base amount + surcharge) as the charge amount

Card Category Detection

The Payment Component returns card lookup data through its onChange handler. The event payload includes a lookupData object once the card number has been identified. The structure looks like this:

{
  "token": "mJD3AvQd-H8Vgk2-y62Tm4-25xd8Rgz8y6u",
  "complete": true,
  "lookupData": {
    "card": {
      "number": "411111******1111",
      "bin": "411111",
      "exp": "1030",
      "type": "visa",
      "category": "credit",
      "hash": ""
    },
    "check": {
      "name": null,
      "account": null,
      "aba": null,
      "transit": null,
      "institution": null,
      "hash": null
    }
  }
}

The field you need is lookupData.card.category. It will be "credit", "debit", or "unknown" depending on BIN table coverage.

<NmiPayments
  tokenizationKey="your-tokenization-key"
  paymentMethods={['card']}
  onChange={(event) => {
    if (event.lookupData?.card?.category) {
      console.log('Card category:', event.lookupData.card.category);
      updateSurcharge(event.lookupData.card.category);
    }
  }}
  onPay={(event) => {
    // event.token contains the payment token
    // event.lookupData.card.category is also available here
    processPayment(event.token);
  }}
/>

The onChange event fires on every field change, but lookupData will only be present once the component has enough of the card number to perform a BIN lookup. Always check for its existence before reading.


Calculating the Surcharge

Keep the surcharge logic straightforward. Define your rate, check the card category, and compute the values:

const SURCHARGE_RATE = 0.029; // 2.9%

function calculateSurcharge(baseAmount, cardCategory) {
  const isCreditCard = cardCategory === 'credit';
  const surchargeAmount = isCreditCard
    ? parseFloat((baseAmount * SURCHARGE_RATE).toFixed(2))
    : 0;
  const totalAmount = parseFloat((baseAmount + surchargeAmount).toFixed(2));

  return { surchargeAmount, totalAmount };
}

A few things to note:

  • Round after each arithmetic step using .toFixed(2) to avoid floating-point drift. Rounding only at the end can produce totals that are off by a cent.
  • Treat "unknown" as no surcharge. If the BIN lookup can't determine the card category, do not apply a surcharge. Only surcharge when you have a definitive "credit" result.
  • Recalculate when either the amount or card category changes. If your UI allows the customer to edit the amount, the surcharge must update accordingly.

Displaying the Surcharge

Show the surcharge as a separate line item in your order summary so the customer understands the charge breakdown before confirming. Only render the surcharge line when it applies:

Subtotal:                    $25.00
Credit Card Surcharge (2.9%): $0.73
Total:                       $25.73

If the card is debit or unknown, the surcharge line should not appear and the total should equal the subtotal. This avoids confusion and makes it clear the surcharge is conditional.


Sending the Payment

When submitting the payment to your server, send the total amount (base + surcharge) as the charge amount. The surcharge calculation happens entirely on the client side — the NMI Payment API receives a single amount value and does not have a separate surcharge field.

const { totalAmount } = calculateSurcharge(baseAmount, cardCategory);

fetch('/your-payment-endpoint', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    token: paymentToken,
    amount: totalAmount.toFixed(2),
  }),
});

Your server-side handler then passes this amount through to the NMI Payment API as the amount parameter in the transaction request.


Handling Edge Cases

Card category is "unknown": Some card BINs may not be in NMI's lookup tables. Do not surcharge these transactions. Depending on your compliance requirements, you may want to log these for review.

Customer changes card number: If a customer enters a credit card number (triggering the surcharge), then clears the field and enters a debit card, onChange will fire again with the new category. Make sure your state updates accordingly so the surcharge is removed.

Amount changes after card entry: If your UI allows editing the amount after the card is entered, the surcharge must recalculate. Deriving the surcharge from both the current amount and the current card category (rather than caching a dollar value) handles this naturally.


Compliance Considerations

Credit card surcharging is subject to legal and card network regulations that vary by jurisdiction. Before implementing:

  • Verify surcharging is permitted in your region and for your merchant category
  • Confirm the surcharge rate does not exceed the maximum allowed by applicable card network rules
  • Ensure the surcharge is clearly disclosed to the customer before they confirm the transaction
  • Do not apply surcharges to debit card transactions

This guide covers the technical implementation only. Consult your payment processor and legal counsel for compliance guidance.