Transaction
Transaction flow and transaction intent
A transaction here is one end-to-end attempt to perform a sale, refund, or cancel through the SDK: from creating an intent until the SDK reaches an approved, declined, or terminal error outcome.
The transaction flow you pass when building SDKPaymentsConfiguration (see Installation and configuration — Transaction flow) selects how the SDK runs that attempt internally.
After SDKPayments.init, you hold a SDKPaymentsFlowHandle. For the current V1 flow, narrow it to SDKPaymentsFlowHandle.V1 and use it for the lifetime of the SDK session—details.
A SDKTransactionIntent is the object that represents one in-flight transaction: it receives state updates (SDKV1TransactionState) and reader updates until the attempt finishes. You obtain it only after createTransactionIntent succeeds on the V1 handle.
Create transaction intent
To start a transaction you create a SDKTransactionIntent, passing the transaction kind (sale, refund, or cancel) as SDKV1TransactionData and an SDKV1TransactionListener.
Call createTransactionIntent on the SDKPaymentsFlowHandle.V1 instance returned from SDKPayments.init (after configuring the flow as SDKPaymentsFlow.V1). The method receives SDKV1TransactionData and your SDKV1TransactionListener implementation.
If a transaction is already in progress, the SDK tries to abort it first so you do not run more than one active intent at a time.
val txData = SDKV1TransactionData(SDKV1TransactionType.Sale)
flowHandle.createTransactionIntent(txData, object : SDKV1TransactionListener {
override fun onTransactionCreated(result: SDKTransactionIntentResult) {
when (result) {
is SDKTransactionIntentResult.Success -> {
currentTx = result.transactionIntent
}
is SDKTransactionIntentResult.Error -> {
appendInfo("Error creating a new transaction: ${result.message}")
}
}
}
override fun onTransactionStateChanged(state: SDKV1TransactionState) {
handleTxState(state)
}
override fun onReaderStateChanged(state: SDKReaderState) {
handleReaderState()
}
})
SDKTransactionIntentResult.Error is reported when a previous transaction exists and cannot be aborted. The transaction cannot be aborted during reader connection or after confirmation.
Explaining the SDKV1TransactionState states
Provide a device
Provide a device which was previously scanned.
Call the method provideDevice in SDKV1TransactionState.DeviceRequired state
fun handleTxState(state: SDKV1TransactionState) {
when (state) {
is SDKV1TransactionState.DeviceRequired -> {
state.provideDevice(selectedDevice!!) // device previously found by the scanner
}
...
}
}
Provide reader configuration
Create an SDKV1ReadConfig object and call provideReadConfig in the SDKV1TransactionState.ReadConfigRequired state.
fun handleTxState(state: SDKV1TransactionState) {
val readConfig = SDKV1ReadConfig(
timeout = 60.seconds,
readModes = setOf(SDKCardReadMode.Chip, SDKCardReadMode.Swipe, SDKCardReadMode.Nfc),
cardInsertionStatus = SDKCardInsertionStatus.NotInserted,
transactionTotals = getSDKV1TransactionTotals()
)
when (state) {
is SDKV1TransactionState.ReadConfigRequired -> {
state.provideReadConfig(readConfig)
}
...
}
}
Note: After providing the device, the SDK automatically makes a preliminary request to the
/dummyendpoint before starting the card reading. This request aims to establish the TLS connection with the server before performing the actual transaction, which allows reducing up to 200ms of the TLS handshake time in the first payment request.
Providing a transaction totals
The SDKV1TransactionTotals object allows you to specify the net transaction amount, taxes, and tip.
It is configured within the SDKV1ReadConfig object to ensure that all necessary transaction details are provided for accurate reporting.
private fun getSDKV1TransactionTotalsWithList(tax1 : BigDecimal, tax2 : BigDecimal, tip : BigDecimal): SDKV1TransactionTotals {
return SDKV1TransactionTotals(
net = SDKMoney(TRANSACTION_TOTAL, SDKCurrency.ars()),
taxes = SDKV1Taxes.BreakDown(
listOf(
SDKV1Tax(SDKMoney(tax1, SDKCurrency.ars()), "tax1"),
SDKV1Tax(SDKMoney(tax2, SDKCurrency.ars()), "tax2")
)
),
tip = SDKMoney(tip, SDKCurrency.ars())
)
}
- net: Represents the total amount of the transaction, specified in the
SDKMoneyobject. This includes both the amount and the currency. - taxes: Represents the tax amounts, included within the net amount for reference purposes.
SDKV1Taxes.Total(...)— Use this to specify a single tax amount if applicable.SDKV1Taxes.BreakDown(listOf(... , ...))— Use this option to specify multiple taxes as a list ofSDKV1Taxobjects, each with a labeled description (e.g., "iva" and "iva2" in the example).
- tip: Specifies the tip amount, also provided in an
SDKMoneyobject. This field is optional and can be omitted if a tip is not applicable.
Each component (net, taxes, and tip) uses SDKMoney to ensure accurate and consistent handling of currency and decimal values across all transaction amounts.
Select EMV app
There are cards that have more than one EMV application internally in the chip. This is a way that allows the standard to have several logic cards in the same plastic (for example, a credit card and a debit card), In this state the Readers SDK provides the list of available applications and you must choose which one you want to use (if the card only has one emv application, this state is not called).
Call the method selectEmvApp in SDKV1TransactionState.SelectEmvApp state.
fun handleTxState(state: SDKV1TransactionState) {
when (state) {
is SDKV1TransactionState.SelectEmvApp -> {
val availableEmvApps = state.availableEmvApps
customMethodForSelectEmvApp(availableEmvApps) { emvApp ->
state.selectEmvApp(emvApp)
}
}
}
}
Confirm transaction
When the card is read, this data is provided to the Readers SDK user who decides to confirm or reject the transaction, then online processing is performed. Finally, an approved or declined, or error response is obtained,the data necessary to confirm the transaction will be explained in using JSON for confirmation section.
There is a deprecated version of the confirm() function that accepts typed parameters through the SDKConfirmation class.
Confirm a sale.
fun handleTxState(state: SDKV1TransactionState) {
when (state) {
is SDKV1TransactionState.ConfirmTransaction.Sale -> {
val confirmation: SDKConfirmationJson = getNewSaleConfirmationJson()
state.confirm(confirmation)
}
...
}
}
Confirm a refund.
fun handleTxState(state: SDKV1TransactionState) {
when (state) {
is SDKV1TransactionState.ConfirmTransaction.Refund -> {
val confirmation: SDKConfirmationJson = getNewRefundConfirmationJson()
state.confirm(confirmation)
}
...
}
}
Confirm a cancel.
fun handleTxState(state: SDKV1TransactionState) {
when (state) {
is SDKV1TransactionState.ConfirmTransaction.Cancel -> {
val confirmation: SDKConfirmationJson = getNewCancelConfirmationJson()
state.confirm(confirmation)
}
...
}
}
Transaction approved
If the transactions is approved, the response is obtained in the following methods.
Check for SDKV1TransactionState.TransactionApproved state.
fun handleTxState(state: SDKV1TransactionState) {
when (state) {
is SDKV1TransactionState.TransactionApproved -> {
if (state is SDKV1TransactionState.TransactionApproved.Sale) {
// Approved sale
} else {
// Approved refund or cancel
}
}
...
}
}
Transaction error
During a transaction, two kind of errors can occur: recoverable and not recoverable. In the recoverable, it’s possible to retry the transaction calling the associate callback, in the not recoverable errors, the transaction finished and it's necessary to create another one.
Check whether state is SDKV1TransactionState.RecoverableError or SDKV1TransactionState.NonRecoverableError. After a NonRecoverableError, create a new transaction intent with createTransactionIntent on your SDKPaymentsFlowHandle.V1 (the handle from SDKPayments.init), as you did for the first attempt.
fun handleTxState(state: SDKV1TransactionState) {
when (state) {
is SDKV1TransactionState.RecoverableError -> {
...
state.recover() //if you want try recover an error
}
is SDKV1TransactionState.NonRecoverableError -> {
//transaction is finished by an error
}
...
}
}
Explaining the SDKReaderState states
SDKReaderState tells the user the current state of the reader
Connecting
Reader is connecting
Connected.Idle
Reader is connected and idle
Connected.WaitingForCard
Reader is connected and waiting for card
Connected.Processing.Pin
Reader is connected and processing Pin
Connected.Processing.Card
Reader is connected and processing card
Connected.UpdatingConfiguration
Reader is connected and updating its configuration
NotConnected
Reader is not connected