The Stitch Payment App offers merchants the ability to integrate their POS system directly with the Stitch Payment App via an Android Intent SDK.
Stitch Android Intent SDK
Add the Maven Repository and include the dependency:
app/build.gradle.kts
dependencyResolutionManagement {
repositories {
...
maven("https://gitlab.com/api/v4/projects/69499542/packages/maven")
}
}
See required lib versions.
Initiating a Transaction
override fun onCreate(savedInstanceState: Bundle?) {
...
val sale = Sale.Builder(
this,
object : SaleTransactionListener {
override fun onSaleResult(result: SaleResult) {
}
override fun alertError(error: IntentError) {
}
},
).build()
...
}
fun onButtonClick() {
val externalPosData = mapOf(
"posEg1" to UUID.randomUUID().toString(),
"posEg2" to Random().nextInt().toString(),
"posEg3" to true.toString(),
)
if (cashback) {
sale.startTransactionWithCashback(amount, cashAmount, externalPosData)
} else {
sale.startTransaction(amount, externalPosData)
}
...
}
The actions have a Builder that require at least the following parameters: context & listener. The context is what Android needs to start the Intent and can be the current Activity, the listener is a callback interface where the library will provide the result or error. The builder should be be created in the onCreate() before, content is rendered, and on a button tap the transaction can be initiated.
externalPosData a map representing key value pairs that'll be echoed in the result and be sent on the webhooks (payment app may add additional keys). Can be used for adding identifiers aiding in matching, like a DB row id or an order number, etc.
disableReceiptingOnOutcome disables receipt printing and QR code on the outcome screen.
timeoutSeconds sets the timeout for card reads (60s if not supplied, must be in the range of 30-300).
autoCloseDurationOnOutcome if supplied, the outcome screen will automatically close after the provided number of seconds (ranged between 5 - 90)
merchantPin auto login (if required) without prompting for the PIN.
Sale Result
Sale responses have the following format
| Field Path | Type | Description | Example / Notes |
|---|
| outcome | Outcome | Outcome of the transaction | |
| posData | Map<String, String> | Additional 3rd party meta data to be associated to this transaction. | optional |
| response | TransactionResponseDto | Response data | optional |
| receipt | Receipt | Receipt info | optional |
Outcome
| Field Path | Type | Description | Example / Notes |
|---|
| status | Status | The status enum, used for logic | |
| detail | Detail | The detail, used for display | |
Detail
| Field Path | Type | Description | Example / Notes |
|---|
| transactionType | String | The type of transaction | |
| totalAmount | String | The total amount formatted in transaction currency | |
| saleAmount | String | The sale amount in transaction currency | optional |
| cashbackAmount | String | The cashback amount in transaction currency | optional |
| outcome | String | The outcome display text | |
| code | String | The response code. See REST API for values | |
| description | String | The response code description | |
Receipt
| Field Path | Type | Description | Example / Notes |
|---|
| dateTimeFormatted | String | date time of transaction | format = yyyy-MM-dd HH:mm |
| merchantName | String | name of the merchant this terminal is assigned to | |
| transactionType | String | Type of transactions performed. | example values: Sale, Refund |
| status | String | status of the transaction. | example values: Approved, Declined, Failed, Reversed |
| totalAmount | String | full amount. This the same amount plus any additional amount e.g. cashback | |
| saleAmount | String | sale amount of transaction formated in currency | |
| cashbackAmount | String | cashback amount formatted in the currency of the transaction | optional |
| declinedDetail | String | succinct description of decline response | optional: not present for approvals |
| mid | String | The acquiring institution Merchant Identifier (MID) used for this transaction. | optional |
| tid | String | The acquiring institution Terminal Identifier (TID) used for this transaction. | optional |
| scheme | String | scheme of the card presented | optional |
| aid | String | application id of the card presented | optional |
| cardHolderName | String | cardholder name if present on the card | optional |
| applicationName | String | name of the card application used | optional |
| auth | String | The code issued by the authorizing institution indicating the approval status. | optional |
| tvr | String | terminal verification results | optional. format: hex |
| tsi | String | transaction status information | optional. format: hex |
| maskedPan | String | PAN for presented card masked for security purposes | optional |
| entryMode | String | method in which the card was presented | optional |
| rrn | String | The RRN (Retrieval Reference Number) submitted to the acquirer for the transaction | optional |
| cvm | String | cardholder verification method used | optional. example values: no_cvm_required, online_pin, fail_cvm |
| cardholderReceiptUrl | String | URL where the cardholder receipt can be viewed or downloaded. | optional |
| merchantReceiptUrl | String | URL where the merchant receipt can be viewed or downloaded. | optional |
TransactionResponseDto
| Field Path | Type | Description | Example / Notes |
|---|
| transactionId | UUID | The ExiPay generated transaction ID. | |
| transactionResult | TransactionResult | The overall processing result status. Should the result be approved then a follow up confirmation message is required. | |
| originalTransactionId | UUID | The ExiPay generated transaction ID for the original transaction that this transaction relates to. | optional |
| referenceId | String | The 3rd party reference ID. Used to more easily identify transactions by a 3rd party. This value must be unique per transaction per terminal. | optional |
| errorMessage | String | Should the processing result be a failure or a decline then the error message will contain a description of the error. | optional |
| cardNotPresentRefundableStatus | CardNotPresentRefundableStatus | Indicates whether or not a card not present refund is possible for this transaction. | optional |
| merchantReceiptUrl | String | URL where the merchant receipt can be viewed or downloaded. | optional |
| cardholderReceiptUrl | String | URL where the cardholder receipt can be viewed or downloaded. | optional |
| transactionType | ValueWithDescriptionInteger | Details of the transaction type used to perform the transaction. | optional |
| responseCode | ResponseCodeValueWithDescription | Details of the response code for the transaction. See REST API for values | optional |
| cardTransactionData | CardTransactionResponseData | The card transaction response data. | optional |
| voidableUntilTime | OffsetDateTime | The UTC time that the transaction will be allowed to be voided in ISO 8601 format. | optional |
| transactionTime | OffsetDateTime | The UTC time the transaction was received by ExiPay in ISO 8601 format. | optional |
| posData | Map<String, String> | Additional 3rd party meta data to be associated to this transaction. | optional |
TransactionResult
| Value | Description |
|---|
| approved_confirmed | Transaction was Approved and confirmation sent |
| authorized_confirmed | Pre-authorization was Approved and confirmation sent |
| declined | Declined |
| failed | Failed |
| reversed | Reversed |
| voided | Voided |
other values are not valid for intentapi scenarios
CardTransactionResponseData
| Field Path | Type | Description | Example / Notes |
|---|
| rrn | String | The RRN (Retrieval Reference Number) submitted to the acquirer for the transaction. | optional |
| stan | String | The STAN (System Trace Number) submitted to the acquirer for the transaction. | optional |
| settlementDate | LocalDate | The date indicating when the transaction will be settled. Format yyyy-MM-dd. | optional |
| merchant | MerchantResponseData | The merchant data related to the transaction. | optional |
| amount | AmountResponseData | Amounts related to the transaction. | optional |
| card | CardResponseData | Detailed card response data for the transaction. | optional |
| mid | String | The acquiring institution Merchant Identifier (MID) used for this transaction. | optional |
| tid | String | The acquiring institution Terminal Identifier (TID) used for this transaction. | optional |
| approvalCode | String | The code issued by the authorizing institution indicating the approval status. | optional |
| cryptogram | String | The response cryptogram for EMV transactions. | optional |
| issuerTerminalData | String | The issuer response data to return to the terminal. | optional |
MerchantResponseData
| Field Path | Type | Description | Example / Notes |
|---|
| name | String | The merchant name. | |
| city | String | The merchant city. | optional |
| countryCode | String | The merchant ISO 3166 alpha2 country code. A complete list of country codes can be found here: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2ISO. | optional |
AmountResponseData
| Field Path | Type | Description | Example / Notes |
|---|
| amount | Long | The transaction's amount represented as cents. The total transaction amount including otherAmount for this transaction. | |
| currencyCode | String | The default ISO 4217 currency code that was used to process the transaction. A complete list of currency codes can be found here: https://en.wikipedia.org/wiki/ISO_4217. | |
| displayValue | String | A user friendly formatted amount using the currency symbol and decimal places related to the transaction currency. | |
| otherAmountDisplayValue | String | A user friendly formatted other amount using the currency symbol and decimal places related to the transaction currency. | |
| otherAmount | Long | The transaction's other amount's represented as cents. | optional |
CardResponseData
| Field Path | Type | Description | Example / Notes |
|---|
| maskedPan | String | Masked PAN (Primary Account Number) of the card. | optional |
| binNumber | String | The card BIN (Bank Identification Number). | optional |
| scheme | String | The card scheme name. | optional |
| cardholderName | String | The name of the card holder. | optional |
| expiryYear | String | The card expiry year. Format yy. | optional |
| expiryMonth | String | The card expiry month. Format MM. | optional |
| budgetPeriod | Int | The number of months the customer has chosen to repay the issuer for the purchase. | optional |
| accountType | ValueWithDescriptionInteger | Details of the card holder account type. | optional |
| panEntryMode | ValueWithDescriptionInteger | The method used on the terminal to perform the transaction. | optional |
| cvmMethod | ValueWithDescriptionInteger | The card verification method used on the terminal to perform the transaction. | optional |
| cvmConditionCode | ValueWithDescriptionInteger | The CVM (Card Verification Mode) condition code. | optional |
| cvmRuleResult | ValueWithDescriptionInteger | The CVM (Card Verification Mode) rule result. | optional |
| applicationId | String | The application ID selected for an EMV transaction. | optional |
| applicationName | String | The textual representation of the application ID for an EMV transaction. | optional |
| terminalVerificationResult | String | The terminal verification result for performing the transaction. | optional |
| transactionStatusInformation | String | The transaction status information (TSI) extracted from EMV tag 0x9B. | optional |
| productType | String | The product type of the card. | optional |
| bankName | String | The name of the bank which has been allocated this BIN range. | optional |
| countryCode | String | The ISO 3166 alpha-2 country code related to the bank that own the BIN range. | optional |
| aip | String | Application Interchange Profile. Indicates the capabilities of the card to support specific functions in the application | optional |
| aipSupportsCDCVM | Boolean | Indicates if the aip supports Consumer Device Cardholder Verification Method. CDCVM is a type of Cardholder Verification supported by the card networks when accepting contactless transactions originating from mobile devices | optional |
| tokenized | Boolean | Indicates if the card pan is a confirmed tokenized PAN, based of a bin lookup. | optional |
| cardHmac | String | Unique Stitch card PAN HMAC. | optional |
| cardFingerprint | String | Unique Stitch card PAN fingerprint. | optional |
| token | String | A card token that can be used to initiate follow-up transactions via Stitch Card. A token is only returned for tokenisation-enabled tenants. | optional |
Voiding a Transaction
override fun onCreate(savedInstanceState: Bundle?) {
...
val voider = VoidTransaction.Builder(
this,
object : VoidTransactionListener {
override fun onVoidResult(result: VoidResult) {
}
override fun alertError(error: IntentError) {
}
},
).build()
...
if (TransactionUtil.isVoidable(response)) {
}
...
}
fun onButtonClick() {
voider.voidTransaction(transactionId)
...
}
It is the responsibility of the calling app to verify that the user is authorised to perform void actions.
externalPosData a map representing key value pairs that'll be echoed in the result and be sent on the webhooks (payment app may add additional keys). Can be used for adding identifiers aiding in matching, like a DB row id or an order number, etc.
merchantPin auto login (if required) without prompting for the PIN.
supervisorPin a PIN to perform privileged actions. If not supplied the UI will prompt for one.
Void Result
| Field Path | Type | Description | Example / Notes |
|---|
| success | Boolean | Was the transaction voided successfully | |
| posData | Map<String, String> | Additional 3rd party meta data to be associated to this transaction. | optional |
| response | ReversalResponseDto | Response data | optional |
ReversalResponseDto
| Field Path | Type | Description | Example / Notes |
|---|
| transactionId | java.util.UUID | The ExiPay generated transaction ID. | |
| transactionResult | TransactionResult | The overall processing result status. Should the result be approved then a follow up confirmation message is required. | |
| originalTransactionId | java.util.UUID | The ExiPay generated transaction ID for the original transaction that this transaction relates to. | optional |
| referenceId | String | The 3rd party reference ID. Used to more easily identify transactions by a 3rd party. This value must be unique per transaction per terminal. | optional |
| errorMessage | String | Should the processing result be a failure or a decline then the error message will contain a description of the error. | optional |
| cardNotPresentRefundableStatus | CardNotPresentRefundableStatus | Indicates whether or not a card not present refund is possible for this transaction. | optional |
| merchantReceiptUrl | String | URL where the merchant receipt can be viewed or downloaded. | optional |
| cardholderReceiptUrl | String | URL where the cardholder receipt can be viewed or downloaded. | optional |
| responseCode | String | The response code for the reversal. | optional. See REST API for values |
| posData | Map<String, String> | The meta data from the original transaction will be combined with reversal/void meta data (overwrites if same keys used). | optional |
Refunding a Transaction
Refunds initiated via the Android Intent SDK are processed as a card-present refund. This means the customer must present their physical card at the terminal in-store to complete the refund.
override fun onCreate(savedInstanceState: Bundle?) {
...
val refund = Refund.Builder(
this,
object : RefundTransactionListener {
override fun onRefundResult(result: RefundResult) {
}
override fun alertError(error: IntentError) {
}
},
).build()
...
if (TransactionUtil.isRefundable(response)) {
}
...
}
fun onButtonClick() {
refund.startRefund(amount, transactionId)
...
}
It is the responsibility of the calling app to verify that the user is authorised to perform refund actions.
externalPosData a map representing key value pairs that'll be echoed in the result and be sent on the webhooks (payment app may add additional keys). Can be used for adding identifiers aiding in matching, like a DB row id or an order number, etc.
disableReceiptingOnOutcome disables receipt printing and QR code on the outcome screen.
timeoutSeconds sets the timeout for card reads (60s if not supplied, must be in the range of 30-300).
autoCloseDurationOnOutcome if supplied, the outcome screen will automatically close after the provided number of seconds (ranged between 5 - 90)
merchantPin auto login (if required) without prompting for the PIN.
supervisorPin a PIN to perform privileged actions. If not supplied the UI will prompt for one.
Refund Result
The refund response follows the same form as the SaleResult with the addition of the refundBalanceData field in the RefundResponseDto. This contains the current refundable balance of the original transaction as well as a history of all approved refunds that have been submitted against that transaction.
RefundBalanceResponseData
| Field Path | Type | Description | Example / Notes |
|---|
| amount | AmountResponseData | The original transaction amount. | optional |
| balance | AmountResponseData | Balance remaining to be refunded for the original transaction. | optional |
| refundHistory | List<RefundHistoryData> | History of all successful refunds performed against the original transaction. | optional |
RefundHistoryData
| Field Path | Type | Description | Example / Notes |
|---|
| transactionId | UUID | The ExiPay generated transaction ID for the refund event. | optional |
| referenceId | String | The 3rd party reference ID. Used to more easily identify transactions by a 3rd party. This value must be unique per transaction per terminal. | optional |
| amount | AmountResponseData | Amounts related to the transaction. | optional |
| transactionTime | OffsetDateTime | The UTC time the transaction was received by ExiPay in ISO 8601 format. | optional |
Pre-authorization
override fun onCreate(savedInstanceState: Bundle?) {
...
val preauth = PreAuth.Builder(
this,
object : PreAuthTransactionListener {
override fun onAuthResult(result: SaleResult) {
}
override fun onError(error: IntentError) {
}
},
).build()
...
}
fun onButtonClick() {
val externalPosData = mapOf(
"posEg1" to UUID.randomUUID().toString()
)
preauth.startTransaction(
amount = amount,
externalPosData = externalPosData,
disableReceiptingOnOutcome = false,
timeoutSeconds = 3L
)
...
}
externalPosData a map representing key value pairs that'll be echoed in the result and be sent on the webhooks (payment app may add additional keys). Can be used for adding identifiers aiding in matching, like a DB row id or an order number, etc.
disableReceiptingOnOutcome disables receipt printing and QR code on the outcome screen.
timeoutSeconds sets the timeout for card reads (60s if not supplied, must be in the range of 30-300).
autoCloseDurationOnOutcome if supplied, the outcome screen will automatically close after the provided number of seconds (ranged between 5 - 90)
merchantPin auto login (if required) without prompting for the PIN.
Card Query
Card query is functionality for the capturing of non-payment cards like loyalty cards. When using this feature a secure whitelist of allowed cards must be agreed and configured with stitch
override fun onCreate(savedInstanceState: Bundle?) {
...
val cardQuery = CardQuery.Builder(
this,
object : CardQueryListener {
override fun onCardQueryResult(result: CardQueryResult) {
}
override fun onError(error: IntentError) {
}
},
).build()
...
}
fun onButtonClick() {
val externalPosData = mapOf(
"posEg1" to UUID.randomUUID().toString()
)
cardQuery.cardQuery(externalPosData = externalPosData)
...
}
externalPosData a map representing key value pairs that'll be echoed in the result and be sent on the webhooks (payment app may add additional keys). Can be used for adding identifiers aiding in matching, like a DB row id or an order number, etc.
merchantPin auto login (if required) without prompting for the PIN.
Card Query Result
| Field Path | Type | Description | Example / Notes |
|---|
| status | Int | Status code describing the result of the card query operation | 0 = success, > 0 = online failure, < 0 = local failures |
| referenceId | String | Reference to operation on server | optional |
| error | String | Description of the specific error | optional: only present if status != 0 |
| success | Success | Success result | optional: only present if status == 0 |
CardQueryResult.Success
| Field Path | Type | Description | Example / Notes |
|---|
| track1 | String | Track1 data if present | optional. format: ascii |
| track2 | String | Track2 data from card | optional. format: ascii |
| pan | String | PAN extracted from track 2 | format: ascii |
Get app and terminal Info
The Info command gets important info about the Terminal and the Stitch payment apps.
override fun onCreate(savedInstanceState: Bundle?) {
...
val info = Info.Builder(
this,
object : InfoListener {
override fun onInfoResult(result: InfoResult) {
}
override fun onError(error: IntentError) {
}
},
).build()
...
}
fun onButtonClick() {
info.get()
}
Info Response
| Field Path | Type | Description | Example / Notes |
|---|
| manufacturer | String | Terminal manufacturer | optional. example: Sunmi, Feitian ... |
| model | String | Terminal model | optional |
| serialNumber | String | Serial number of terminal device | optional |
| printer | Boolean | Has the terminal got printer hardware | optional |
| currencyCode | Int | The ISO 4217 currency code used to process transactions. A complete list of currency codes can be found here: https://en.wikipedia.org/wiki/ISO_4217 | optional |
| version | Version | Stitch payment app versions | optional |
| paymentMethodSettings | PaymentMethodSettings | Allowed payment methods | optional |
| loginRequired | Boolean | Do the apps require login to process commands | |
InfoResult.Version
| Field Path | Type | Description | Example / Notes |
|---|
| core | String | Version of the Stitch Core app | optional |
| pay | String | Version of the Stitch Pay app | optional |
PaymentMethodSettings
| Field Path | Type | Description | Example / Notes |
|---|
| enableCard | Boolean | Allow customers to pay using a card | optional |
| enableMobileMoney | Boolean | Allow customers to pay using MobileMoney (MoMo) | optional |
| enablePbb | Boolean | Allow customers to pay using Pay by Bank (PBB) | optional |
| enablePbc | Boolean | Allow customers to pay using Pay by Crypto (PBC) | optional |
Bypass PIN entry on the Stitch Payment App
The Stitch Payment App normally requires a PIN to authenticate the user and terminal before a transaction can be initiated. This PIN is also required for void and refund transactions.
However, the Merchant POS App can bypass the manual PIN entry by logging the user in automatically in the background. This approach is recommended when the Merchant POS App has already implemented the necessary permission checks, ensuring security without requiring additional user input on the Stitch Payment App.
override fun onCreate(savedInstanceState: Bundle?) {
...
val login = Login.Builder(
this,
object : LoginListener {
override fun onLoginResult(result: Boolean) {
}
override fun alertError(error: IntentError) {
}
},
).build()
...
}
fun onButtonClick() {
login.login(merchantPin)
...
}
merchantPin
merchantPin auto login without prompting for the PIN.
Errors
All listeners can return an IntentError for the cases where errors occur outside of the operation of the command
sent. For example if the Pay app isnt installed or if the request parameters are invalid.
Intent Error
| Field Path | Type | Description | Example / Notes |
|---|
| code | Code | Error case | |
| message | String | Description of error | |
Code
| Value | Description |
|---|
| APP_NOT_FOUND | Payment Application not found |
| INVALID_AMOUNT | Invalid amount |
| INVALID_CASHBACK_AMOUNT | Invalid cashback amount |
| INVALID_REQUEST | Invalid request data |
| USER_CANCELLED | Operation cancelled by user |
| TIMEOUT | Timeout |
| AUTH_FAILED | Authentication failed |
| UNKNOWN | Unknown error |
| INVALID_TIMEOUT | Timeout supplied is invalid |
Lib Version Mapping
| Pay Version | Core Version | dto Version | intent-lib Version |
|---|
| v1.0.0 | v1.0.0 | 1.0.3 | 3.0.2 |
| v1.0.1 | v1.0.1 | 1.0.5 | 3.0.2 |
| v1.0.2 | v1.0.2 | 1.0.8 | 3.0.3 |
| v1.0.6 | v1.0.6 | 1.0.17 | 3.0.7 |
| v1.0.7 | v1.0.7 | 1.0.18 | 3.0.8 |
Gradle base config (same for all versions):
app/build.gradle.kts
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
implementation("com.exipay.terminal.modules:dto:<version>")
implementation("com.exipay.terminal.modules:intent-lib:<version>")
}