Surcharging with Card Type Detection (Collect.js)
Implementing Credit Card Surcharges with Collect.js
This guide covers how to detect card type and apply a credit card surcharge when using Collect.js for tokenization.
The general approach is:
- Detect the card category in real time via
lookUpCallbackas the customer enters their card number - Calculate the surcharge amount when the category is
"credit" - Display the surcharge to the customer before they confirm payment
- Verify the card category in the tokenization
callbackand send the total (base amount + surcharge) as the charge amount
Card Category Detection
Collect.js includes automatic card type detection using BIN (Bank Identification Number) lookup. There are two callbacks that provide this information, and you should use both.
Real-Time Detection (lookUpCallback)
lookUpCallback)The lookUpCallback fires as soon as the card number is long enough to identify. This happens while the customer is still filling out the form, before they click pay — so you can update the order total and show a surcharge notice immediately.
The response structure looks like this:
{
"number": "411111******1111",
"bin": "411111",
"exp": null,
"type": "visa",
"category": "credit",
"hash": ""
}The field you need is category. The exp value is typically null in this callback since it fires before the customer fills out their expiration date. If the expiration date is already available, it will be formatted as MMYY.
Tokenization Verification (callback)
callback)When the form is submitted, the standard tokenization callback also includes card category data nested under card. This serves as the final system of record to confirm the card type associated with the generated token.
{
"tokenType": "inline",
"token": "SW8eBa72-jkaqt9-xyuEX2-DC8qceRbe6z6",
"card": {
"number": "411111******1111",
"bin": "411111",
"exp": "1030",
"type": "visa",
"category": "credit",
"hash": ""
}
}Category Values
creditdebitprepaiddeferred_debitchargeunknown
Implementation Guide
Step 1: Configure Callbacks
Update your CollectJS.configure call to include the lookUpCallback alongside the standard callback:
CollectJS.configure({
fields: {
ccnumber: {
selector: "#card-number",
title: "Card Number"
},
ccexp: {
selector: "#card-expiration",
title: "Card Expiration"
},
cvv: {
selector: "#cvv",
title: "CVV"
}
},
// Fires as soon as the card number is valid
lookUpCallback: function(response) {
if (response.category) {
updateSurchargeUI(response.category);
}
},
// Fires after the user submits the form
callback: function(response) {
if (response.token) {
// Verify the category one last time before sending to your server
const finalCategory = response.card.category;
submitPayment(response.token, finalCategory);
}
}
});Step 2: Implement Real-Time Surcharge Logic
Create a function to handle the UI updates triggered by the lookUpCallback. This ensures the customer sees the surcharge before they submit:
const SURCHARGE_RATE = 0.029; // 2.9%
function updateSurchargeUI(category) {
const baseAmount = parseFloat(document.getElementById("base-amount").value);
let surchargeAmount = 0;
let finalAmount = baseAmount;
if (category === "credit") {
surchargeAmount = parseFloat((baseAmount * SURCHARGE_RATE).toFixed(2));
finalAmount = parseFloat((baseAmount + surchargeAmount).toFixed(2));
document.getElementById("surcharge-notice").textContent =
`Credit card surcharge: $${surchargeAmount.toFixed(2)}`;
document.getElementById("surcharge-notice").style.display = "block";
} else {
document.getElementById("surcharge-notice").style.display = "none";
}
document.getElementById("total-amount").textContent =
`Total: $${finalAmount.toFixed(2)}`;
}Step 3: Verify and Submit
When the user submits the form, use the callback response to confirm the card category before sending the final amount to your server. This handles edge cases where the card number may have changed between the lookup and submission:
function submitPayment(token, cardCategory) {
const baseAmount = parseFloat(document.getElementById("base-amount").value);
let finalAmount = baseAmount;
if (cardCategory === "credit") {
const surchargeAmount = parseFloat((baseAmount * SURCHARGE_RATE).toFixed(2));
finalAmount = parseFloat((baseAmount + surchargeAmount).toFixed(2));
}
fetch('/your-payment-endpoint', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token: token,
amount: finalAmount.toFixed(2),
}),
});
}Your server-side handler then passes this amount through to the NMI Payment API as the amount parameter in the transaction request.
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, prepaid, 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.
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 via lookUpCallback), then clears the field and enters a debit card, lookUpCallback will fire again with the new category. Make sure your UI updates accordingly so the surcharge is removed. The callback on submission serves as a final verification in case the lookup and submission get out of sync.
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.
lookUpCallback timing: This callback only fires when the card number is deemed valid. If a user types a partial or invalid number, it will not trigger. Never rely on lookUpCallback exclusively for the final transaction amount — always use the category from the tokenization callback to confirm the charged amount matches the actual card type tokenized.
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.
Updated about 24 hours ago
