Sale
A sale authorizes and captures a payment in a single step—use it when the final amount is known at checkout.
Prerequisites
- Authenticated merchant with
login() - Active location set via
setActiveLocationID() - Card reader prepared with
prepare()(iOS) or device enrolled (Android)
Basic Sale
iOS:
let breakdown = PaymentBreakdown(
subtotal: 10000, // $100.00 in cents
taxRate: 0.0875, // 8.75% as a decimal
taxAmount: 875, // $8.75 in cents
tipAmount: 2000, // $20.00 tip
tipType: .fixed
)
let currency = CurrencyCode(currencyCode: "USD", displayName: "US Dollar")
let response = try await KoardMerchantSDK.shared.sale(
amount: 12875, // subtotal + tax + tip
breakdown: breakdown,
currency: currency,
eventId: UUID().uuidString
)
Android:
val breakdown = PaymentBreakdown(
subtotal = 10000,
taxRate = 0.0875,
taxAmount = 875,
tipAmount = 2000,
tipType = "fixed"
)
sdk.sale(
activity = this,
amount = 12875,
breakdown = breakdown,
eventId = UUID.randomUUID().toString()
).collect { event ->
when (event.actionStatus) {
ActionStatus.OnComplete -> {
val txn = event.response?.transaction
println("Sale complete: ${txn?.transactionId}")
}
ActionStatus.OnFailure -> {
println("Sale failed: ${event.response?.message}")
}
else -> { /* reader progress */ }
}
}
Sale with Surcharge
The surcharge percentage is applied to the full transaction amount (subtotal + tax + tip):
surcharge = (subtotal + taxAmount + tipAmount) × surchargeRate
= (10000 + 875 + 2000) × 0.035
= 451 cents ($4.51)
iOS:
let breakdown = PaymentBreakdown(
subtotal: 10000,
taxRate: 0.0875,
taxAmount: 875,
tipAmount: 2000,
tipType: .fixed,
surcharge: PaymentBreakdown.Surcharge(
amount: 451, // surcharge on full amount
percentage: 0.035
)
)
let response = try await KoardMerchantSDK.shared.sale(
amount: 13326, // 12875 + 451 surcharge
breakdown: breakdown,
currency: currency,
eventId: UUID().uuidString
)
Android:
val breakdown = PaymentBreakdown(
subtotal = 10000,
taxRate = 0.0875,
taxAmount = 875,
tipAmount = 2000,
tipType = "fixed",
surcharge = Surcharge(
amount = 451,
percentage = 0.035
)
)
sdk.sale(
activity = this,
amount = 13326,
breakdown = breakdown,
eventId = UUID.randomUUID().toString()
).collect { event ->
when (event.actionStatus) {
ActionStatus.OnComplete -> {
println("Sale complete: ${event.response?.transaction?.transactionId}")
}
ActionStatus.OnConfirmSurcharge -> {
val txn = event.response?.transaction
sdk.confirm(
transactionId = txn?.transactionId ?: "",
confirm = true
)
}
ActionStatus.OnFailure -> {
println("Sale failed: ${event.response?.message}")
}
else -> { /* reader progress */ }
}
}
Surcharge Confirmation
If the terminal has surcharging enabled and the card is eligible (credit only—debit cards are automatically excluded), the transaction returns surchargePending. You must present the disclosure and confirm.
iOS:
if response.transaction?.status == .surchargePending {
let disclosure = response.transaction?.surchargeDisclosure ?? ""
let approved = await showSurchargeDisclosure(disclosure)
let confirmed = try await KoardMerchantSDK.shared.confirm(
transaction: response.transactionId ?? "",
confirm: approved,
amount: nil,
breakdown: nil,
eventId: nil
)
}
Android:
ActionStatus.OnConfirmSurcharge -> {
val txn = event.response?.transaction
// Present disclosure to customer, then:
sdk.confirm(
transactionId = txn?.transactionId ?: "",
confirm = true
)
}
Bypassing Automatic Surcharge
Set bypass: true to skip the processor's automatic surcharge. Useful for custom BIN-based surcharging:
iOS:
let breakdown = PaymentBreakdown(
subtotal: 10000,
taxRate: 0.0875,
taxAmount: 875,
tipType: .fixed,
surcharge: PaymentBreakdown.Surcharge(bypass: true)
)
Android:
val breakdown = PaymentBreakdown(
subtotal = 10000,
taxRate = 0.0875,
taxAmount = 875,
tipType = "fixed",
surcharge = Surcharge(bypass = true)
)
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
amount |
Int |
Yes | Total amount in minor units (cents) |
breakdown |
PaymentBreakdown? |
No | Itemized breakdown (see below) |
currency |
CurrencyCode |
Yes (iOS) | Currency for the transaction |
eventId |
String? |
No | Idempotency key (UUID recommended) |
activity |
Activity |
Yes (Android) | Android activity for NFC access |
PaymentBreakdown
| Field | Type | Description |
|---|---|---|
subtotal |
Int |
Base amount in minor units |
taxRate |
Double? |
Tax rate as decimal (0.0875 = 8.75%) |
taxAmount |
Int |
Calculated tax in minor units |
tipAmount |
Int? |
Tip in minor units |
tipRate |
Double? |
Tip rate as decimal (alternative to fixed tip) |
tipType |
TipType |
.fixed / .percentage (iOS) or "fixed" / "percentage" (Android) |
surcharge |
Surcharge? |
Nested surcharge object |
Surcharge
| Field | Type | Default | Description |
|---|---|---|---|
amount |
Int? |
nil |
Fixed surcharge in minor units |
percentage |
Double? |
nil |
Surcharge rate as decimal (0.035 = 3.5%). Applied to subtotal + tax + tip. |
bypass |
Bool |
false |
Skip automatic surcharge calculation |
See Also
- Preauth — Hold funds now, capture later
- Surcharging — Automatic and custom surcharge workflows
- Payment Lifecycle — End-to-end payment flow
