Surcharging
Control how surcharges are applied across Sale, Preauth, Confirm, and Capture flows in the Koard Merchant SDK.
What You Learn
- How Koard auto-applies surcharges when none are specified
- The rate hierarchy across merchant, terminal, location, and per-transaction overrides
- SDK structures for passing
percentage,amount, orbypass - How to confirm
surcharge_pendingtransactions or run a custom surcharge workflow
Automatic Surcharging
By default, Koard inspects the card BIN and the location's configured state to determine whether a surcharge can be applied. If the SDK call (Sale or Preauth) omits a surcharge in the payment breakdown and no fixed surcharge is configured on the merchant, terminal, or location, Koard calculates a compliant surcharge automatically.
Automatic surcharging currently applies to iOS and Android SDK flows. The server recalculates total_amount and annotates the transaction response with surcharge fields.
Surcharge Pending Responses
When Koard adds a surcharge on your behalf, the resulting Sale transaction returns status: "surcharge_pending" along with populated surcharge_amount and surcharge_rate. The app must show a disclosure to the payer and call confirm (or cancel) before the funds can settle.
{
"transaction_id": "YOUR_TRANSACTION_ID",
"event_id": "YOUR_EVENT_ID",
"mid": "YOUR_KOARD_MID",
"tid": "YOUR_KOARD_TID",
"processor_mid": "YOUR_PROCESSOR_MID",
"processor_tid": "YOUR_PROCESSOR_TID",
"account_id": "YOUR_ACCOUNT_ID",
"device_id": "YOUR_DEVICE_ID",
"processor": "tsys",
"gateway": "sierra",
"currency": "USD",
"location_id": "YOUR_LOCATION_ID",
"gateway_transaction_id": "GATEWAY_TRANSACTION_ID",
"subtotal": 1136,
"tip_amount": 0,
"tip_type": "fixed",
"tax_amount": 110,
"tax_rate": 10,
"total_amount": 1283,
"surcharge_applied": true,
"surcharge_amount": 37,
"surcharge_rate": 3.0,
"status": "surcharge_pending",
"status_reason": "approved",
"payment_method": "contactlessIcc",
"card_type": "Visa Credit",
"card_brand": "Visa Credit",
"card": "451593******8495",
"additional_details": {
"emv_tags": "EMV_TAGS",
"approval_code": "APPROVAL_CODE",
"retrieval_reference_number": "YOUR_RRN",
"transaction_sequence_number": "YOUR_TSN",
"merchant_identifier": "YOUR_MERCHANT_ID",
"merchant_name": "YOUR_MERCHANT_NAME"
},
"gateway_transaction_response": {
"type": "sale",
"status": "ready",
"currency": "USD",
"approvalCode": "470382",
"responseCode": "A",
"responseMessage": "APPROVAL",
"authorizedAmount": 1246,
"processorResponseCode": "00"
}
}
Only Sale transactions become surcharge_pending. You must present a disclosure that states the surcharge percentage before calling confirm. Declining the disclosure cancels the transaction as canceled_by_payer.
Rate Hierarchy
When a surcharge is configured in multiple places, the most granular configuration wins:
| Priority | Level | Where it is managed |
|---|---|---|
| 1 | Transaction | Passed in the SDK PaymentBreakdown |
| 2 | Location | MMS or Location APIs |
| 3 | Terminal | MMS or Terminal APIs |
| 4 | Merchant | MMS or Merchant APIs |
- Merchant-level: Apply a default percentage or fixed amount for all transactions under the merchant.
- Terminal-level: Override the merchant default for a specific terminal (e.g., countertop vs kiosk).
- Location-level: Override both merchant and terminal defaults for a physical address.
- Per-transaction: Passing a surcharge in the SDK request overrides all upstream settings for that payment only.
Fixed surcharges that are configured ahead of time will be added automatically for eligible card types. Ensure the payer view discloses the surcharge before the tap is completed.
iOS SDK Fields
The surcharge struct lives within PaymentBreakdown.Surcharge. Only one of percentage or amount is required; bypass explicitly skips all surcharge logic.
| Field | Type | Description |
|---|---|---|
percentage |
Float |
Whole-number value (3% = 3.0). |
amount |
Int |
Minor units (cents). |
bypass |
Bool |
When true, percentage and amount must be omitted and Koard will skip surcharging for that transaction. |
Surcharge data can be supplied in Preauth, Sale, Confirm, and Capture flows.
let surcharge = PaymentBreakdown.Surcharge(
amount: nil, // Optional fixed amount in cents
percentage: 3.0, // Optional percentage (3%)
bypass: false // true bypasses surcharging
)
let breakdown = PaymentBreakdown(
subtotal: 2500,
taxRate: 0.0875,
taxAmount: 219,
tipAmount: 500,
tipType: .fixed,
surcharge: surcharge
)
let currency = CurrencyCode(currencyCode: "USD", displayName: "US Dollar")
let transactionId = UUID().uuidString
do {
let response = try await KoardMerchantSDK.shared.sale(
amount: 3219,
breakdown: breakdown,
currency: currency,
transactionId: transactionId
)
print("Sale completed: \(response.transactionId ?? "Unknown")")
} catch {
print("Sale failed: \(error)")
}
Confirming a Pending Surcharge
For responses with status: "surcharge_pending", display the disclosure prompt. Afterwards call:
try await KoardMerchantSDK.shared.confirm(
confirm: true,
transactionId: transactionId,
breakdown: updatedBreakdown // Optional: override surcharge here
)
- Passing
confirm: falsecancels the transaction (canceled_by_payer). - Passing
confirm: truefinalizes the surcharge. You may provide a new breakdown to override the automatically calculated rate.
Custom Surcharge Workflow
If you prefer to run your own BIN lookup or surcharge logic:
- Call
preauthwithsurcharge.bypass = true. - Inspect
additionalDetails["bin"]in the response (9-digit BIN) alongside location data. - Calculate the surcharge externally.
- Call
capture(orconfirm) with the calculated surcharge in the breakdown.
This pattern lets you integrate a proprietary surcharge service while still leveraging Koard's transaction lifecycle.
BIN-Based Custom Surcharge with Incremental Auth
For scenarios where you need to calculate a custom surcharge based on the card BIN (e.g., different rates for debit vs credit, or custom BIN routing rules), you can use incremental authorization to add the surcharge amount after the initial preauth.
This workflow requires running a transaction first to obtain the BIN, then calculating and applying the surcharge:
Step 1: Preauth with Bypassed Surcharge
Start by running a preauth with bypass: true to hold the base amount without any surcharge applied:
let surcharge = PaymentBreakdown.Surcharge(bypass: true)
let breakdown = PaymentBreakdown(
subtotal: 2500,
taxRate: 875,
taxAmount: 263,
tipAmount: 500,
tipType: .fixed,
surcharge: surcharge
)
let currency = CurrencyCode(currencyCode: "USD", displayName: "US Dollar")
let transactionResponse = try await KoardMerchantSDK.shared.preauth(
amount: 3263,
breakdown: breakdown,
currency: currency
)
Step 2: Retrieve BIN and Calculate Surcharge
Extract the card BIN from the transaction response and calculate your custom surcharge:
let cardBin = transactionResponse.additionalDetails?["bin"] as? String ?? ""
// Implement your custom surcharge calculation logic
let surchargeAmount = calculateCustomSurcharge(
cardBin: cardBin,
baseAmount: transactionResponse.totalAmount ?? 3263
)
The calculateCustomSurcharge function is your implementation. Use it to apply custom rates based on BIN ranges, card type, or integrate with third-party surcharge services.
Step 3: Incremental Auth for Surcharge
Use incremental authorization to add the calculated surcharge amount to the preauth:
guard let txnId = transactionResponse.transactionId else {
throw NSError(domain: "CustomSurcharge", code: 1,
userInfo: [NSLocalizedDescriptionKey: "Missing transaction ID"])
}
let incrementalAuth = try await KoardMerchantSDK.shared.auth(
transactionId: txnId,
amount: surchargeAmount
)
Step 4: Capture with Surcharge Breakdown
Capture the full authorized amount including the surcharge:
let finalAmount = (transactionResponse.totalAmount ?? 0) + surchargeAmount
let captureBreakdown = PaymentBreakdown(
subtotal: 2500,
taxRate: 875,
taxAmount: 263,
tipAmount: 500,
tipType: .fixed,
surcharge: PaymentBreakdown.Surcharge(
amount: surchargeAmount,
bypass: false
)
)
let capture = try await KoardMerchantSDK.shared.capture(
transactionId: txnId,
amount: finalAmount,
breakdown: captureBreakdown
)
This workflow requires you to run a transaction before calculating the surcharge. Ensure you display the surcharge disclosure to the cardholder before completing the capture, and handle cancellation appropriately if they decline.