Tap to Pay on Pocket
Initiate and accept contactless payments from your POS application on the Dojo Pocket.
Overview
The code samples in this document use kotlin.
Tap to Pay on Pocket (TaP for short) allows users to process mobile point of sale (MPos) transactions via a 3rd Party POS Android application loaded onto their Dojo Pocket, and receive in-person contactless payments from physical payment cards and other mobile devices by making use of the Pocket's built-in NFC reader and Dojo Pay App.
Facilitating communication between a POS application and the Dojo Pay App is intent-based messaging, one of the Inter-Process-Communication (IPC) mechanisms that allow Android components to communicate with one another. User-defined intent messages with optional payloads are passed between processes (for example, apps and Activities) and parsed by the receiving component and acted upon.
Intent sequence
Click one of the intent endpoints in the sequence below to jump to the relevant section.
- TaP request: Initializes the TaP application
- POS response: Sends initialization results
- TaP: Start or refund transaction
- POS: Send sale or refund results
- TaP: Send last transaction results
- POS: Receive last transaction results
- TaP: Request a split bill and set gratuity information
- POS: Send split bill and gratuity information
Format
Intents and extras are written as literal string values, but payload data types can be formatted as string, boolean, or integer.
loading...
Additional flags
Intents must be created using the SingleTop
flag. This
means if, for example, a sale request is sent when an identical sale is already at the top of the stack, the system will reuse that existing instance instead of creating a new one.
loading...
Initialize
Before defining any sale, refund, or split bill and gratuity logic, the first step is to initialize and provision TaP.
Request message
Begin the initialization and provisioning process:
val intent = Intent("com.dojo.action.INIT")
TaP provisioning may take up to two minutes to complete.
Prerequisites
- The device must not be in developer mode, or connected via Android Debug Bridge (ADB) to a client.
- The device system images must not be modified (no custom bootloader or SU binary).
- The bootloader must be locked.
- The device must be NFC-enabled.
Should these checks fail, TaP will not be able to provision.
TaP Action
Initialization is currently a UI-interactive process in TaP, requiring user input to enter the activation code and complete the device setup procedure. In future releases, this third party driven initialization will become a silent background process in TaP, with no UI interaction required. Any required setup data would be passed as a payload within the intent message.
Response
TaP will return its response in the onActivityResult
API call of the calling activity:
- An
INIT_COMPLETE
response is returned to indicate the status of the initialization. - The
INIT_RESULT
andINIT_RESULT_DETAIL
extra fields reflect the initialization status.INIT_RESULT
returns a boolean value oftrue
if it is successful andfalse
if it is not.- The
INIT_RESULT_DETAIL
value will be eitherOK
if initialization completes successfully orCOMPLETED
if it was already initialised before the call was made.
- If there are any problems an error code will be provided as a string.
This message returns TaP initialization status information to the POS:
loading...
Required POS Action
In the onActivityResult
Activity callback:
val initResult = data?.getBooleanExtra("com.dojo.extra.INIT_RESULT")
val initResultDetail = data?.getStringExtra("com.dojo.extra.INIT_RESULT_DETAIL")
The POS should parse this message to determine whether the TaP initialization succeeded or failed by parsing the INIT_RESULT
boolean. Further information on the failure status will be available in the INIT_RESULT_DETAIL
string.
Error codes include:
E1
- a payment session failed to start.A10
- a provisioning error occurred.T1
- a timeout caused the provisioning to be cancelled.
The POS app should respond to the messages using one of the following approaches:
- No action required in the event of a successful device initialization.
- Re-trying the initialization.
- Notifying the user of a technical fault after multiple retries.
Sale
Request message
This message is to be used by the POS to request TaP to begin a contactless transaction.
Sender example code:
loading...
Prerequisites
TaP must have been previously provisioned using INIT
in order for this request to succeed.
TaP Action
On receiving the request, TaP will begin a transaction, display the contactless acceptance screen, and wait for the presentation of a contactless EMV card.
Response
The user should present a card to the device for contactless payment. TaP will then process the transaction, contact the acquiring bank for authorization, then show the result to the user.
Should the transaction fail, the reason will be displayed to the user (for example, card declined or card expired). These status codes will be returned to the POS in the response message.
This message returns transaction status and payment data to the POS.
Sender example (TaP):
loading...
Expected POS Action
The POS should parse this message to determine whether the transaction succeeded or failed by parsing the TRX_RESULT
Boolean
and TRX_RESULT_DETAIL
String.
OnActivityResult
example:
loading...
Refund
Request message
This message is to be used by the POS to request TaP to begin a contactless refund transaction.
Sender example code:
loading...
Prerequisites
TaP must have been previously provisioned using INIT
in order for this request to succeed.
TaP Action
On receiving the request, TaP will begin a transaction, display the contactless acceptance screen, and wait for the presentation of a contactless EMV card.
The user should present a card to the device for contactless payment. TaP will then process the refund transaction, contacting the acquiring bank for authorization and show the result to the user.
Response
This message returns transaction status and payment data to the POS.
Should the transaction fail, the reason will be displayed to the user (for example, card declined or card expired). These status codes will be returned to the POS in the response message.
Sender example (TaP):
loading...
Key (literal string) | Value | Type |
---|---|---|
com.dojo.extra.REFUND_STATUS | NOT_PROVISIONED / OK | String |
com.dojo.extra.BASE_AMOUNT | Base transaction amount | Integer |
com.dojo.extra.TRANSACTION_RESULT | AUTHORISED / CANCELLED / DECLINED / FAILED | String |
com.dojo.extra.AUTH_CODE | Tx Authorization Code | String |
com.dojo.extra.PAYMENT_METHOD | Contactless | String |
com.dojo.extra.CARDHOLDER_VERIFICATION_METHOD | Contactless/PIN | String |
com.dojo.extra.CARD_SCHEME | Mastercard/Visa/Amex/Discover | String |
com.dojo.extra.CARD_APPLICATION_LABEL | EMV application label | String |
com.dojo.extra.CARD_APPLICATION_ID | EMV application ID | String |
com.dojo.extra.TRANSACTION_ID | TaP transaction ID | String |
Expected POS Action
The POS should parse this message to determine whether the refund succeeded or failed:
TRX_RESULT
(boolean)TRX_RESULT_DETAIL
(string)
OnActivityResult
example:
loading...
Last transaction details
Request message
This message is used by the POS to request details of the last executed transaction. It should be utilized in scenarios where the POS fails to receive a response from the preceding transaction. Example situations include:
- The TaP app failed to send the response due to an internal failure.
- The POS app failed during processing the response.
- The device restarted before the POS could process the response.
val intent = Intent("com.dojo.action.LAST_TX")
Prerequisites
TaP must have been previously provisioned using INIT
for this request to succeed.
TaP Action
Upon receiving this message, TaP will return the available details from the last completed transaction, regardless of the transaction outcome (authorized, declined, or failed).
It is not currently possible to request details for a specific transaction.
Response
The LAST_TX_COMPLETE
message is returned to the caller in the onActivityResult
activity callback handler, containing transaction status information. The field SPLIT_BILL
indicates whether the last transaction was split between two or more customers.
Fields default to empty strings where data was not successfully retrieved, for example, where card data could not be fetched successfully. The payload contains the following data:
Key (literal string) | Value | Type |
---|---|---|
com.dojo.extra.SPLIT_BILL | False | Boolean |
com.dojo.extra.TX_STATUS | OK | String |
com.dojo.extra.BASE_AMOUNT | Base transaction amount | Integer |
com.dojo.extra.GRATUITY_AMOUNT | Gratuity transaction amount | Integer |
com.dojo.extra.TRANSACTION_RESULT | AUTHORISED / CANCELLED / DECLINED / FAILED | String |
com.dojo.extra.AUTH_CODE | Tx Authorization Code | String |
com.dojo.extra.PAYMENT_METHOD | CONTACTLESS / KEYED | String |
com.dojo.extra.CARDHOLDER_VERIFICATION_METHOD | Contactless / PIN | String |
com.dojo.extra.CARD_SCHEME | Mastercard / Visa / Amex / Discover | String |
com.dojo.extra.CARD_APPLICATION_LABEL | EMV application label | String |
com.dojo.extra.CARD_APPLICATION_ID | EMV application ID | String |
com.dojo.extra.TRANSACTION_ID | TaP transaction ID | String |
loading...
Split bills and gratuities
At the payment stage, you can split a bill into constituent transactions and then recombine those into a bundle. You can also determine
the amount of gratuity using TOTAL_GRATUITY_AMOUNT
.
Split bills and gratuities can be enabled by sending a specific request message to TaP:
val intent = Intent("com.dojo.action.ENABLE_SPLIT_BILLS")
intent.putExtra("com.dojo.extra.ENABLE_SPLIT_BILLS", true :Boolean)
intent.putExtra("com.dojo.extra.ENABLE_GRATUITIES", true :Boolean)
Response
This message returns the status of the request to enable split bills and gratuities.
loading...
These are the response schema associated with a bundled collection, as a whole:
Key (literal string) | Value | Type |
---|---|---|
com.dojo.extra.PAYMENTS_MADE | Number of payments made | Integer |
com.dojo.extra.TOTAL_BASE_AMOUNT | Total amount to be paid in pennies | Integer |
com.dojo.extra.TOTAL_PAID_AMOUNT | Total amount paid in pennies | Integer |
com.dojo.extra.TOTAL_GRATUITY_AMOUNT | Total gratuity amount paid in pennies | String |
com.dojo.extra.PAYMENT_KEYS | List of payment keys | List(String) |
The value passed for PAYMENT_KEYS
will be a list of strings. These keys are
associated with individual transactions inside the split bill bundle.
The details of each transaction can then be extracted as a bundle from the intent response using that key.
Individual bundle item schema
You can extract individual transaction information from a split bill bundle:
loading...
These response schema are associated with individual transactions extracted from a bundle:
Key (literal string) | Value | Type |
---|---|---|
com.dojo.extra.TX_STATUS | NOT_PROVISIONED | OK |
com.dojo.extra.BASE_AMOUNT | Base transaction amount | Integer |
com.dojo.extra.GRATUITY_AMOUNT | Gratuity transaction amount | Integer |
com.dojo.extra.TRANSACTION_RESULT | AUTHORISED / DECLINED / FAILED | String |
com.dojo.extra.AUTH_CODE | Tx Authorisation Code | String |
com.dojo.extra.PAYMENT_METHOD | Contactless | String |
com.dojo.extra.CARDHOLDER_VERIFICATION_METHOD | Contactless/PIN | String |
com.dojo.extra.CARD_SCHEME | Mastercard/Visa/Amex/Discover | String |
com.dojo.extra.CARD_APPLICATION_LABEL | EMV application label | String |
com.dojo.extra.CARD_APPLICATION_ID | EMV application ID | String |
com.dojo.extra.TRANSACTION_ID | TaP transaction ID | String |
Expected POS action
The POS should parse this message to determine whether the request succeeded by parsing
the ENABLE_SPLIT_BILLS_RESULT
and ENABLE_GRATUITIES_RESULT
booleans.
OnActivityResult
example:
loading...