Payment Lifecycle

Understand how Koard transactions progress from initial authorization through capture, adjustment, reversal, and refund.

What You Learn
  • How sale and preauthorization flows differ
  • Which follow-up operations are available and when to use them
  • iOS SDK entry points and their matching REST endpoints
  • How to monitor state transitions and handle errors

Before You Begin

Transaction Categories

Tap-Initiated Transactions (card present)

Transaction Description iOS SDK Method API Entry Point
Sale One-step auth + capture KoardMerchantSDK.shared.sale() POST /v4/payment
Preauth Authorization hold KoardMerchantSDK.shared.preauth() POST /v4/preauth

These operations require card data from Tap to Pay or another compliant reader.

Follow-Up Operations (card-not-present)

Transaction Purpose iOS SDK REST Endpoint
Capture Settle an authorized amount capture() POST /v3/payments/{id}/capture
Incremental Auth Increase an existing hold auth() POST /v3/payments/{id}/auth
Tip Adjust Update gratuity before settlement adjust() POST /v1/payments/{id}/adjust
Reverse Release held funds reverse() POST /v1/payments/{id}/reverse
Refund Return captured funds refund() POST /v1/payments/{id}/refund

REST vs SDK: After a successful tap, you can perform every follow-up operation via the REST API, the iOS SDK, or both—choose the channel that fits your workflow.

Lifecycle Flows

Sale Flow

Tap → Sale (status: captured) → [Optional] Refund → Complete

Sales capture funds immediately. Refunds return money after settlement.

Preauth Flow

Tap → Preauth (status: authorized)
         ├─ Incremental Auth (optional, status stays authorized)
         ├─ Capture (status: captured) → Refund (optional)
         └─ Reverse (status: reversed)

Preauths require an explicit capture to collect funds. If plans change, reverse the authorization instead of refunding.

Successive auths: If a follow-up authorization is declined, Koard automatically reverts to the last successfully authorized amount.

Swift SDK Reference

// Sale (tap required)
KoardMerchantSDK.shared.sale(
    amount: Int,
    breakdown: PaymentBreakdown? = nil,
    currency: CurrencyCode,
    transactionId: String? = nil,
    type: PaymentType = .sale
) async throws -> TransactionResponse

// Preauthorization (tap required)
KoardMerchantSDK.shared.preauth(
    amount: Int,
    currency: CurrencyCode,
    transactionId: String? = nil,
    breakdown: PaymentBreakdown? = nil
) async throws -> TransactionResponse

// Follow-up operations
KoardMerchantSDK.shared.capture(transactionId: String, amount: Int? = nil, breakdown: PaymentBreakdown? = nil)
KoardMerchantSDK.shared.auth(transactionId: String, amount: Int, breakdown: PaymentBreakdown? = nil)
KoardMerchantSDK.shared.adjust(transactionId: String, type: AdjustmentType, amount: Int? = nil, percentage: Double? = nil)
KoardMerchantSDK.shared.reverse(transactionId: String, amount: Int? = nil)
KoardMerchantSDK.shared.refund(transactionId: String, amount: Int? = nil)

Updated Breakdown Example

let breakdown = PaymentBreakdown(
    subtotal: 2500,
    taxRate: 0.0875,
    taxAmount: 219,
    tipAmount: 500,
    tipType: .fixed,
    surchargeAmount: 75,
    surchargeRate: 0.03
)

taxRate and surchargeRate are now floating-point decimals (e.g., 0.0875 for 8.75%).

REST Quick Reference

Operation Endpoint Notes
Sale POST /v4/payment Requires encrypted card data from Tap to Pay
Preauth POST /v4/preauth Returns transaction_id for follow-ups
Capture POST /v3/payments/{transaction_id}/capture Include breakdown to reconcile tips/surcharge
Incremental Auth POST /v3/payments/{transaction_id}/auth Amount is the incremental delta
Tip Adjust POST /v1/payments/{transaction_id}/adjust percentage is a decimal (e.g., 0.18)
Reverse POST /v1/payments/{transaction_id}/reverse Releases uncaptured funds
Refund POST /v1/payments/{transaction_id}/refund Works on captured transactions

See the individual transaction guides for full payload examples.

Transaction States

State Description Transitions
pending Request accepted processing, failed
processing Gateway evaluating authorized, captured, declined, failed
authorized Funds on hold captured, reversed, cancelled
captured Funds collected refunded, cancelled
declined Processor rejected Terminal
failed Processing error Terminal
reversed Hold released Terminal
refunded Funds returned Terminal
cancelled Flow cancelled Terminal

Visual Flow

Sale:
  pending → processing → captured → [refunded | cancelled]

Preauth:
  pending → processing → authorized
                     ├─ capture → captured → [refunded | cancelled]
                     └─ reverse → reversed

Monitoring State Changes

  • Webhooks: Subscribe to transaction.* events (created, authorized, captured, adjusted, reversed, refunded, settled). See Available Events.
  • iOS SDK: Inspect TransactionResponse.transaction.status to update UI immediately.
switch transaction.status {
case .approved, .captured:
    // Success
case .declined:
    // Inform user
case .error:
    // Retry or escalate
default:
    // Handle intermediate states
}

Error Handling & Retries

func processPaymentWithRetry(maxRetries: Int = 3) async throws {
    var attempts = 0

    while attempts < maxRetries {
        do {
            let response = try await KoardMerchantSDK.shared.sale(...)
            // Success
            return
        } catch {
            attempts += 1
            if attempts >= maxRetries { throw error }
            try await Task.sleep(nanoseconds: UInt64(pow(2.0, Double(attempts))) * 1_000_000_000)
        }
    }
}

Best Practices

  • Choose the right entry point: Use sales for immediate capture; use preauth when totals may change.
  • Store transaction IDs: Needed for every follow-up operation and webhook reconciliation.
  • Keep breakdowns accurate: Supply tax, tip, and surcharge data with the latest values to keep reports aligned.
  • Use idempotency keys: Provide transaction_id or event_id to guard against duplicate requests.
  • Monitor via webhooks: Use asynchronous events to update order states reliably.

Troubleshooting Checklist

  • Transaction not found: Confirm the transaction belongs to your Koard account and that the ID is spelled correctly.
  • Invalid state transition: Verify the current state (authorized, captured, etc.) before calling a new operation.
  • Amount validation errors: Capture/Refund amounts cannot exceed the available balances; partial operations require explicit amounts.
  • Tap to Pay issues: Ensure the device has Developer Mode enabled, an active Sandbox Apple Account, and that prepare() was called.
  • SDK errors: Authenticate with login(), set an active location, and handle KoardMerchantSDKError cases explicitly.

See Also