Skip to main content
NetPOS supports cash-out services, allowing merchants to provide cash withdrawal services to customers. This feature enables merchants to act as agent banking points where customers can withdraw cash using their debit cards.

Overview

Cash-out services transform the POS terminal into an ATM-like service point where customers can withdraw cash from their bank accounts using their debit cards.
Cash-out transactions require EMV card reading capabilities and proper merchant authorization as an agent banking provider.

Cash-Out Flow

1

Initiate Cash-Out

Merchant selects cash-out service and enters withdrawal amount
2

Card Authentication

Customer inserts or taps their debit card on the POS terminal
3

PIN Entry

Customer enters their card PIN for authorization
4

Process Transaction

System processes the cash withdrawal request through the payment gateway
5

Dispense Cash

Upon approval, merchant dispenses cash to the customer
6

Receipt Generation

System generates transaction receipt for both merchant and customer

Transaction Response Model

Cash-out transactions use the CashOutResponse model:
CashOutResponse.kt
@Root(strict = false, name = "transferResponse")
data class CashOutResponse @JvmOverloads constructor(
    @field:Element(name = "description")
    var description: String = "",
    
    @field:Element(name = "field39")
    var field39: String = "A3",
    
    @field:Element(name = "authId", required = false)
    var authId: String = "",
    
    @field:Path("hostEmvData")
    @field:Element(name = "AmountAuthorized", required = false)
    var amountAuthorized: Long = 0L,
    
    @field:Path("hostEmvData")
    @field:Element(name = "AmountOther", required = false)
    var AmountOther: Long = 0L,
    
    @field:Path("hostEmvData")
    @field:Element(name = "atc", required = false)
    var atc: String = "",
    
    @field:Path("hostEmvData")
    @field:Element(name = "iad", required = false)
    var iad: String = "",
    
    @field:Path("hostEmvData")
    @field:Element(name = "rc", required = false)
    var rc: Int = 0,
    
    @field:Element(name = "referenceNumber", required = false)
    var referenceNumber: String = "",
    
    @field:Element(name = "transactionChannelName", required = false)
    var transactionChannelName: String = "",
    
    @field:Element(name = "wasReceived", required = false)
    var wasReceived: Boolean = false,
    
    @field:Element(name = "wasSent", required = false)
    var wasSent: Boolean = false
)

Response Fields

FieldTypeDescription
descriptionStringTransaction description/status message
field39StringResponse code (e.g., “00” for approved, “A3” for default)
authIdStringAuthorization ID from the bank
amountAuthorizedLongAuthorized withdrawal amount in minor units
AmountOtherLongAdditional amount (e.g., fees)
atcStringApplication Transaction Counter
iadStringIssuer Application Data
rcIntResponse code
referenceNumberStringUnique transaction reference number
transactionChannelNameStringChannel used for transaction
wasReceivedBooleanIndicates if response was received
wasSentBooleanIndicates if request was sent

Transaction Response Conversion

The CashOutResponse is converted to TransactionResponse for storage:
fun CashOutResponse.toTransactionResponse(
    stan: String,
    cardData: CardData,
    transactionTime: Long,
    iswParameters: IswParameters,
    amount: Long,
    interswitchThreshold: Long = 0L,
    errorMessage: String? = null,
    reqRRN: String = ""
) = TransactionResponse().also {
    it.amount = amount
    it.RRN = if (this.referenceNumber.isEmpty()) reqRRN else this.referenceNumber
    it.responseCode = this.field39
    it.authCode = this.authId
    it.STAN = stan
    it.echoData = iswParameters.remark
    it.maskedPan = StringUtils.overlay(cardData.pan, "xxxxxx", 6, 12)
    it.cardExpiry = cardData.expiryDate
    it.cardLabel = ""
    it.transactionTimeInMillis = transactionTime
    it.transactionType = TransactionType.PURCHASE
    it.terminalId = iswParameters.terminalId
    it.merchantId = iswParameters.merchantId
    it.transmissionDateTime = SimpleDateFormat("MMddhhmmss", Locale.getDefault())
        .format(transactionTime)
    it.localTime_12 = SimpleDateFormat("hhmmss", Locale.getDefault())
        .format(transactionTime)
    it.localDate_13 = SimpleDateFormat("MMdd", Locale.getDefault())
        .format(transactionTime)
    it.accountType = IsoAccountType.DEFAULT_UNSPECIFIED
    it.source = PosMode.ISW
    it.errorMessage = errorMessage
    it.interSwitchThreshold = interswitchThreshold
}

TransactionResponse Fields

Card Information

it.maskedPan = StringUtils.overlay(cardData.pan, "xxxxxx", 6, 12)
it.cardExpiry = cardData.expiryDate
it.cardLabel = ""
  • maskedPan - Card number with middle digits masked (e.g., 5399xxxxxx1234)
  • cardExpiry - Card expiration date from EMV chip
  • cardLabel - Card scheme label (Visa, Mastercard, etc.)

Transaction Metadata

it.transactionTimeInMillis = transactionTime
it.transmissionDateTime = SimpleDateFormat("MMddhhmmss", Locale.getDefault())
    .format(transactionTime)
it.localTime_12 = SimpleDateFormat("hhmmss", Locale.getDefault())
    .format(transactionTime)
it.localDate_13 = SimpleDateFormat("MMdd", Locale.getDefault())
    .format(transactionTime)
  • transactionTimeInMillis - Transaction timestamp in milliseconds
  • transmissionDateTime - Formatted datetime (MMddhhmmss)
  • localTime_12 - Time in hhmmss format
  • localDate_13 - Date in MMdd format

Transaction Identifiers

it.STAN = stan
it.RRN = if (this.referenceNumber.isEmpty()) reqRRN else this.referenceNumber
it.authCode = this.authId
  • STAN - System Trace Audit Number (unique transaction ID)
  • RRN - Retrieval Reference Number
  • authCode - Authorization code from issuing bank

Card Data Processing

Cash-out transactions require reading card data from the EMV chip:
val cardData = CardData(
    pan = "5399XXXXXXXX1234",
    expiryDate = "12/25",
    cardholderName = "JOHN DOE",
    track2Data = "..."
)

ISW Parameters

Interswitch parameters required for cash-out:
data class IswParameters(
    val terminalId: String,
    val merchantId: String,
    val merchantCategoryCode: String,
    val merchantNameAndLocation: String,
    val remark: String
)

Parameter Sources

ParameterDescriptionSource
terminalIdPOS terminal IDUser configuration
merchantIdMerchant identifierUser configuration
merchantCategoryCodeMCC code for agent bankingConfiguration
merchantNameAndLocationMerchant detailsUser profile
remarkTransaction remarkSystem generated

Transaction Types

Cash-out is processed as a PURCHASE transaction:
it.transactionType = TransactionType.PURCHASE

Available Transaction Types

  • PURCHASE - Standard purchase/cash-out
  • REFUND - Refund transaction
  • CASH_ADVANCE - Cash advance on credit cards
  • PRE_AUTHORIZATION - Pre-auth hold

Account Types

it.accountType = IsoAccountType.DEFAULT_UNSPECIFIED

Supported Account Types

  • DEFAULT_UNSPECIFIED - Default account type
  • SAVINGS - Savings account
  • CURRENT - Current/checking account
  • CREDIT - Credit card account

POS Mode

it.source = PosMode.ISW
Transactions are processed through Interswitch (ISW) gateway.

Response Codes

  • 00 - Approved/successful
  • 10 - Partial approval (amount less than requested)

Transaction Storage

Completed transactions are stored with:
val transactionResponse = cashOutResponse.toTransactionResponse(
    stan = generateSTAN(),
    cardData = cardData,
    transactionTime = System.currentTimeMillis(),
    iswParameters = getIswParameters(),
    amount = withdrawalAmount,
    interswitchThreshold = threshold,
    errorMessage = null,
    reqRRN = requestRRN
)

// Save to database
transactionDao.insert(transactionResponse)

Receipt Information

Cash-out receipts include:

Transaction Details

  • Transaction type (Cash Withdrawal)
  • Amount
  • Date and time
  • Reference number (RRN)

Card Information

  • Masked PAN
  • Card type
  • Authorization code
  • Response message

Merchant Details

  • Merchant name
  • Terminal ID
  • Merchant ID
  • Location

Status

  • Approval status
  • Response code
  • STAN
  • Receipt number

Security Considerations

Critical Security Requirements:
  • Cash must only be dispensed after receiving approved response (field39 = “00”)
  • Verify authorization code is present and valid
  • Ensure card PIN is entered securely on POS device
  • Store transaction records securely for reconciliation
  • Implement daily cash-out limits as per banking regulations

Best Practices

1

Pre-Transaction Verification

Verify merchant has sufficient cash before initiating cash-out
2

Card Reading

Use EMV chip reading for enhanced security (avoid magnetic stripe)
3

PIN Entry

Ensure customer enters PIN on secure POS keypad, never on merchant device
4

Response Validation

Check response code is “00” before dispensing cash
5

Receipt Printing

Print receipt immediately after approved transaction
6

Cash Dispensing

Count cash in front of customer before handing over
7

Record Keeping

Store transaction records for audit and reconciliation

Error Handling

Error: Customer account has insufficient balanceAction: Inform customer and offer to retry with lower amount
Error: Customer entered wrong PINAction: Allow customer to retry (max 3 attempts before card lock)
Error: No response from payment gatewayAction: DO NOT dispense cash. Perform transaction reversal or inquiry
Error: Bank approved less than requested amountAction: Dispense approved amount or cancel and retry

Compliance Requirements

Merchants providing cash-out services must:
  • Be registered as authorized agent banking providers
  • Maintain adequate cash float for daily operations
  • Implement daily cash-out limits per customer
  • Keep detailed transaction records for regulatory compliance
  • Report suspicious transactions as per AML/CFT regulations

Transaction History

View and manage cash-out transaction records

Card Payments

Learn about card payment processing

Build docs developers (and LLMs) love