Skip to main content
Under development

This page describes a feature we are building. It is not available in a released SDK or API version. Read it to plan an integration, and confirm availability with your Handpoint contact before you build against it.

Fee Mitigation

A merchant pays a cost to accept a card. Fee mitigation lets the merchant move part of that cost to the customer, or reward a customer who pays another way.

Handpoint supports four programs. A transaction carries one program. It never carries two.

The four programs​

ProgramWhat the customer seesTenders
SurchargeThe price, plus a separate fee for the use of a credit cardCredit cards only
Dual pricingTwo prices: a cash price, and a higher card priceEvery card
Cash discountThe card price, and a lower price if they pay cashCash and e-gift only
Admin feeThe price, plus a fee that applies to every payment methodEvery tender

Why the difference matters​

The difference between these programs is legal, not arithmetic. Two programs can charge the same money and follow different rules.

  • A surcharge is a fee. The card networks regulate it, and several US states prohibit it.
  • Dual pricing is a price, not a fee. The merchant posts two prices, so the card price is simply the price. This is why dual pricing avoids the restrictions that apply to a surcharge.
  • A cash discount reduces the price when the customer pays cash. The card price is the standard price.
  • An admin fee applies to every payment method. It is deliberately universal. A carve-out for cash would turn it into a cash discount, and the rules would change.

Who calculates the fee​

You calculate the fee. The gateway decides whether it applies.

Only your application knows the merchant configuration, the tax rate, and the rounding you printed on the customer's screen. So you send the amount you calculated, and the gateway never recalculates it.

The gateway then makes one decision: does this program apply to this card? If it does not, the gateway removes the fee and tells you why.

Your application The gateway
───────────────── ───────────
calculate the fee ──▶ check the card type
apply, or drop and explain
show the result ◀── return the outcome

What the platform guarantees​

  • The fee is calculated before the authorization.
  • The final transaction amount includes the fee.
  • A debit card never carries a surcharge.
  • The merchant configures the rules, and the platform enforces them.
  • The customer sees the final price before they confirm the payment.

Supported integration paths​

OperationCloud APIAndroid (PAX)Android (HiLite)iOS (HiLite)Cordova
Sale🕓✅❌❌❌
Sale with tip🕓✅❌❌❌
Sale with tokenization🕓✅❌❌❌
Remote Sale🕓✅❌❌❌
Pre-Authorization, capture🕓✅❌❌❌
Every other operation❌❌❌❌❌

✅ available · 🕓 planned · ❌ not supported

The Android SDK on a PAX terminal carries the feature first. The Cloud API needs one more change before it can forward the fee object; see the Code section.

Eligibility​

Two facts decide the outcome: the type of the card, and the type of the transaction.

Cards​

ProgramCards
SurchargeCredit only. Never debit, never prepaid
Dual pricingEvery card
Admin feeEvery card
Cash discountNone. The gateway refuses it

The gateway refuses a cash discount on a card transaction. A card transaction under a cash discount program carries no fee, because the card price is the posted price. Such a request cannot be correct, so the gateway rejects it rather than accepting it quietly.

Every program follows the surcharge rule today

The gateway applies the debit rule to every program in the current build, and it does not yet refuse a cash discount. Treat the table above as the target behaviour, and read reason on every result.

Dropped and refused are different​

OutcomeWhat happensExample
DroppedThe transaction continues. The gateway removes the fee, and the result says whyA surcharge on a debit card
RefusedThe transaction never reaches the acquirer. The gateway returns an errorA fee that conflicts with surchargeAmount

Always read the result. A dropped fee means the customer paid less than you asked for, and your receipt and your records must show the amount the gateway actually charged.

Transaction types​

Transaction typeSurchargeDual pricingAdmin feeCash discount
Sale✅✅✅❌
Sale with tip✅✅✅❌
Sale with tokenization✅✅✅❌
Remote Sale✅✅✅❌
Pre-Authorization, create❌❌❌❌
Pre-Authorization, capture✅❌✅❌
Remote Pre-Authorization, capture✅❌✅❌
Refund❌❌❌❌
Remote Refund❌❌❌❌
ReversalAutomaticAutomaticAutomatic❌
Capture reversalAutomaticAutomaticAutomatic❌
Tip adjustment❌❌❌❌
Tokenization❌❌❌❌
Batch operations❌❌❌❌

Four rules explain every ❌ in that table.

A pre-authorization carries no fee. The fee belongs to the capture, because the capture sets the amount the customer pays. If you set a fee on a pre-authorization, the SDK removes it. Set it on the capture instead.

Dual pricing needs a cash price. A pre-authorization has no cash alternative to price against, so dual pricing applies to a sale and to a Remote Sale only.

A refund carries no fee. The card networks require a merchant to return the fee in proportion to the refund. The platform does not calculate that proportion, so you calculate it. Read the note below.

An operation with no amount carries no fee. Tokenization, a tip adjustment and every batch operation set no transaction amount, so no fee can apply to them.

A reversal is automatic. The reversal carries the same fee as the original transaction, because the acquirer requires the two values to match. You set nothing.

A refund does not return the fee for you

RefundOptions accepts a fee, because it inherits the field from the base options type. The gateway ignores it.

Calculate the returned fee in your own application, and add it to the refund amount you send. A refund that does not return the fee in proportion breaks card network rules, and the merchant carries that risk.

Code​

Coming soon

This feature is implemented in the gateway but not yet publicly released for this integration path. Contact your Handpoint integration engineer for availability.

The terminal already accepts a nested fee object on a cloud request. The Cloud API does not send one yet: POST /transactions carries the deprecated flat surchargeAmount only. When the Cloud API adds fee, the shape matches the object below.

{
"operation": "sale",
"amount": "10000",
"currency": "USD",
"terminal_type": "PAXA920",
"serial_number": "082104578",
"fee": {
"amount": "360",
"mitigationProgram": "surcharge",
"taxOnFee": "60"
}
}

Amounts inside fee follow the rest of POST /transactions: minor units, as a string.

Fee fields​

The object you send.

NameTypeRequiredDescription
amountinteger, minor unitsYesThe whole fee, including any tax inside it. The SDK adds it to the amount of the operation. Never negative
mitigationProgramenumYesSURCHARGE, ADMIN_FEE, CASH_DISCOUNT or DUAL_PRICING. On the wire: surcharge, adminFee, cashDiscount, dualPricing
taxOnFeeinteger, minor unitsNoThe part of amount that is tax. Never added on top. Never negative. Absent reads as zero

Three rules apply before the transaction starts.

  1. amount and taxOnFee must not be negative.
  2. If you set fee and the deprecated surchargeAmount together, the two amounts must match, and mitigationProgram must be SURCHARGE.
  3. taxOnFee must not exceed taxInformation.taxAmount, when you set taxInformation.

What comes back​

Every transaction that carried a fee returns a fee result. A transaction that carried none returns nothing, not a zero.

NameTypeRequiredDescription
amountdecimal, major unitsYesThe fee the gateway recorded. When applied is false, this is what you asked for, not what the customer paid
mitigationProgramenumNoThe program the gateway applied the fee under. Empty when the gateway reports a program the SDK does not recognise
taxOnFeedecimal, major unitsYesThe part of amount that is tax
appliedbooleanYesWhether the gateway charged the fee
reasonenumYesWhy the gateway applied or dropped the fee
reasonDetailstringNoThe raw text from the gateway. Set when reason is UNKNOWN

The amount you send and the amount you read use different units. You send minor units. You read major units, the same as every other amount on the result.

Why the fee was applied or dropped​

NameMeaningWhat you do
APPLIEDThe gateway charged the feePrint the fee line. Settle the total that the result carries
NOT_ELIGIBLE_DEBITThe customer presented a debit card, and the program does not allow onePrint no fee line. Tell the customer the fee was removed
NOT_ELIGIBLE_PREPAIDThe customer presented a prepaid card, and the program does not allow onePrint no fee line. Tell the customer the fee was removed
PROGRAM_NOT_ENABLEDThe merchant is not configured for this programPrint no fee line. Ask the merchant to enable the program
PROGRAM_NOT_SUPPORTEDThe gateway does not carry this programPrint no fee line. Handle the program in your own application
UNKNOWNThe gateway returned a reason this version of the SDK does not recogniseRead reasonDetail. Treat the fee as dropped unless applied is true
Two reasons occur today

The current build returns APPLIED or NOT_ELIGIBLE_DEBIT. Handle all six values, because the other four arrive with the per-program rules and need no change on your side.

Errors​

The gateway refuses these requests before the authorization. The transaction never reaches the acquirer.

CodeMeaningRecovery
4258 SURCHARGE_NOT_ENABLEDThe merchant is not configured to surcharge, and the request carries a surchargeAsk the merchant to enable surcharging, or send no fee
4268 FEE_SURCHARGE_AMOUNT_CONFLICTThe request carries both fee and the deprecated surchargeAmount, and they disagreeSend one field or the other. If you send both, match the amounts and set mitigationProgram to surcharge
4269 FEE_TAX_ON_FEE_EXCEEDS_L2_TAXfee.taxOnFee is greater than taxInformation.taxAmountLower taxOnFee, or raise the tax you declare
FIELD_REQUIREDfee.amount or fee.mitigationProgram is missingSend both fields. Neither one is optional
AMOUNT_FORMATfee.amount or fee.taxOnFee is malformed or negativeSend a positive amount in the documented format
MITIGATION_PROGRAM_INVALIDfee.mitigationProgram is not one of the four valuesSend surcharge, adminFee, cashDiscount or dualPricing
A half-migrated integration fails loudly, on purpose

The gateway refuses a request that carries two different fee amounts. It never picks one for you. Migrate a call site completely, in one change, from surchargeAmount to fee.

The Android SDK checks the same three rules before it sends, and it raises a verification error instead of a transaction. See Error handling.

Tax on the fee​

A merchant can calculate the fee before the tax, or after it. Both orders reach the same total. They do not reach the same tax.

taxOnFee names the part of fee.amount that is tax. It sits inside the fee. The platform never adds it on top of the transaction.

Example. The base is 100.00. The tax is 20%, so the tax on the base is 20.00. The merchant surcharges 3%, and the merchant configuration applies the surcharge to the tax as well.

ValueAmountWhat you send
Base100.00The amount of the operation, 10000
Fee on 120.00 at 3%3.60fee.amount, 360
The tax part inside that fee0.60fee.taxOnFee, 60
What the customer pays123.60The SDK adds the base and the fee

You send the part of the tax that belongs to the fee, because only you know how you calculated it. When the fee applies, that figure changes nothing.

The tax on a dropped fee

When the gateway drops a fee, it removes the fee. The matching reduction of the declared tax is still in development. Until it ships, reconcile the tax yourself when applied is false.

What reaches the card networks​

Only a surcharge. The acquirer protocol has one field for a fee, and its specification calls that field the surcharge. It has no representation for the other three programs.

An admin fee and a dual price travel inside the transaction amount, and nothing else. Reporting either one as a surcharge would misstate a fee to the card networks.

The receipt​

The receipt is the only record a customer keeps, and the only one a regulator inspects. Each program has its own rule.

ProgramThe receipt shows
SurchargeA separate line, clearly labelled. The card networks require it. Never inside the total, never mixed with the tax
Admin feeA separate line, with its own label
Dual pricingNo fee line. The card price is the posted price
Cash discountNothing. It is not a card transaction
Dual pricing prints no fee line, on purpose

This looks like an omission. It is not. An itemised fee would present a price as a fee, and would undermine the model that makes dual pricing lawful. Do not add a fee line to a dual pricing receipt.

See Receipt Compliance for the fields that every Handpoint receipt carries.

Compliance for a surcharge​

These obligations belong to the merchant

Neither the SDK nor the terminal can discharge them. Confirm them with the merchant before you enable a surcharge.

  • The merchant tells the cardholder before the transaction. Signage at the entrance, and at the point of sale.
  • The merchant gives the card networks and the acquirer 30 days notice before the first surcharge.
  • The maximum is 3% of the transaction, or the actual cost of acceptance, whichever is lower.
  • A debit card or a prepaid card never carries a surcharge, even when the customer selects "credit" at the terminal.
  • Several US states and Puerto Rico prohibit a surcharge.

Reporting​

Every fee is recorded, so a merchant can answer these questions:

  • How much did I collect in fees this month, and under which program?
  • On this transaction, did the platform charge a fee? If it did not, why not?
  • Which of my terminals run which program?
  • Can I export all of it?

Four values reach the transaction list and the exported report: the fee amount, the program, whether the fee applied, and the tax attributed to the fee.

A transaction with no fee shows empty values, never 0.00. A fee of zero and no fee at all are different facts.

Reporting is still in development

The four values are not yet in the merchant reports or in the Transaction Feed API. Persist the fee result in your own records until they arrive.

Scope​

Supported:

  • Card transactions.
  • Surcharge, admin fee and dual pricing.
  • The Android SDK on PAX terminals.

Not supported:

ItemReason
Cash and e-gift tendersThe gateway processes card transactions. Your point of sale handles cash
Cash discount at the gatewayIt applies to a cash tender, which never reaches the gateway
The Cloud APIThe terminal accepts a fee object, but the Cloud API does not send one yet
The iOS, Windows and Cordova SDKsThe Android SDK carries the feature
A recalculation of the fee on a partial refundThe platform does not calculate it. You do
Routing that depends on the feeOut of scope
Rules for each BIN or each card brandOut of scope

Terms​

TermMeaning
FeeThe money the merchant adds, under any of the four programs
ProgramWhich of the four rules applies
AppliedThe gateway charged the fee
DroppedThe gateway removed the fee, and the transaction continued
RefusedThe gateway rejected the request, and the transaction stopped