In-store payment: Android Intent API
Overview
The Android Intent API is for POS applications running on the terminal itself (or on the same Android device as Yavin Pay). Your app sends a deep link Intent to Yavin Pay and receives the result in onActivityResult. Choose it when the POS and the payment application share the same device.
At a glance
| Base URL | Deep link scheme: yavin://com.yavin.macewindu/v4/<action>?data=$queryParams |
| Authentication | Merchant login in Yavin Pay with My Yavin credentials |
| Naming convention | camelCase for all fields, except the entries of tax_breakdown which are snake_case |
| Current version | v4 |
| Result delivery | Synchronous: returned to your activity via onActivityResult, in the response extra (JSON) and, on error, the message extra |
Endpoints
| Action | Deep link | Purpose |
|---|---|---|
| Payment | /v4/payment | Start a debit or refund transaction |
/v4/print | Print free content | |
| Share receipt | /v4/share-receipt | Share a receipt by SMS, email or print |
| Transactions | /v4/transactions | Fetch the transaction history |
| Reversal | /v4/reversal | Reverse a recent transaction (less than 16 hours old) |
| NFC reader | /v4/nfc-reader | Read an NFC tag via Yavin Pay |
Versions
| API version | Last updated |
|---|---|
v4 | 22/08/2024 |
v1 | 02/05/2022 |
Before you start
Payload encoding. The request is a JSON object serialized then URI-encoded into the data query parameter of the deep link.
Request code. When calling startActivityForResult, define your own request code (any integer, eg 8888). It is your reference to match the asynchronous response with the request, similar to a webhook correlation ID.
Reading the response. The result JSON is in the response extra. When an error message exists, it is in a separate message extra, never inside the response JSON. The response extra can be absent (for example when the request is rejected before any transaction is created, or when data itself is null): read the extras defensively and never pass a null string to your JSON parser.
Responses. The payment actions return a Transaction object, documented at the end of this page along with the other shared objects, with a status field: ok on success, ko on failure. Keys whose value is null are omitted: tolerate missing keys. The transactions action has no status field (see Fetch transactions).
Never send a negative amount. On the Android Intent API, the terminal does not reject a negative amount or giftAmount: it uses its absolute value. An amount of -1000 is processed as a 10,00 € payment. Validate amounts on the POS side before sending the Intent.
Idempotency. Send an idempotentUuid on every payment request, one per payment attempt. See IdempotentUuid Management.
Currency. The currency is the one configured on the merchant profile in the Yavin database.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != REQUEST_CODE_PAYMENT) return
// Both extras are optional: data can be null (eg resultCode == RESULT_CANCELED)
val json: String? = data?.getStringExtra("response")
val message: String? = data?.getStringExtra("message")
if (json == null) {
// No transaction was returned: treat the attempt as failed
onPaymentFailed(message ?: "No response from Yavin Pay (resultCode=$resultCode)")
return
}
val response = Gson().fromJson(json, TransactionResponse::class.java)
if (response.status == "ok") {
onPaymentSucceeded(response)
} else {
// A declined payment carries no message in the JSON: use the message extra if any
onPaymentFailed(message ?: "Payment declined")
}
}Start a debit transaction
Starts a debit payment on the terminal.
Parameters
amountIntegerrequired- Amount in cents, must be greater than 0. Never send a negative value: it is not rejected and its absolute value is charged
transactionTypeStringdefault debit- Use debit. Values are strict (debit or refund): any other value, credit included, is silently processed as a debit
idempotentUuidStringdefault autogenerated- Unique identifier of the payment attempt, see IdempotentUuid Management. A ko transaction is returned as is: generate a new UUID to retry
customerCustomer- Pre-filled customer info for receipt sharing
Show Customer parametersHide Customer parameters
customer.firstNameString- First name
customer.lastNameString- Last name
customer.emailStringcustomer.phoneString- International format starting with +
customer.birthDateString- Birthdate
enableGiftScreenBoolean- true: show the tips screen. false: skip it
giftAmountIntegerdefault 0- Tip or donation in cents
receiptTicketReceiptTicket- Receipt printed with the card ticket
Show ReceiptTicket parametersHide ReceiptTicket parameters
receiptTicket.dataStringrequired- Content to print
receiptTicket.formatStringdefault text- Format of the content
receiptTicket.tax_breakdownArray- Array of Tax objects, one per tax rate
receiptTicketJsonString- Additional JSON payload as string
referenceString- Waiter or person processing the transaction
vendorVendor- Your software name and version
Show Vendor parametersHide Vendor parameters
vendor.softwareNameString- Name of the POS software
vendor.softwareVersionString- Version of the POS software
checkoutExternalIdString- Merchant-unique checkout ID, required when any external order field below is sent. It must be unique: a request is rejected if a completed ok transaction already uses this value
externalTableNumberString- Table number. Requires checkoutExternalId
externalOrderNumberString- Human-readable order number. Requires checkoutExternalId
externalOrderIdString- Unique technical order ID from your POS, for reconciliation. Requires checkoutExternalId
Request
val request = TransactionRequest(
amount = 100,
transactionType = "debit",
idempotentUuid = "2f1c2e2a-6c21-4a4b-9c2b-9f6f2c8e9b51",
customer = Customer("John", "Doe", "john@yavin.com"),
vendor = Vendor("Awesome Partner", "1.2.3"),
receiptTicket = ReceiptTicket(data = "This is a wonderful\n receipt ticket to print", format = "text"),
receiptTicketJson = JSONObject("{\"transactionId\": \"123456\", \"amount\": 3500 }").toString(),
checkoutExternalId = "order-123",
externalTableNumber = "12",
externalOrderNumber = "A-123",
externalOrderId = "order-123"
)
val jsonData = Gson().toJson(request)
val queryParams = Uri.encode(jsonData)
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("yavin://com.yavin.macewindu/v4/payment?data=$queryParams")
}
startActivityForResult(intent, REQUEST_CODE_PAYMENT)Response
On success, the response is a Transaction object with status = ok and a transactionId; the request fields (reference, customer, idempotentUuid, checkoutExternalId and the external order fields) are echoed back.
If a transaction already exists for this idempotentUuid, it is returned as is, including when it is still in progress: in that case it has no status field.
On failure, the response extra contains a Transaction object with status = ko and no message. When there is an error message, it is in the separate message extra. When the request is rejected before a transaction is created, the response extra is absent and only message is set. Possible messages:
Error: refund need to be activated contact support: refunds are not enabled for this merchantTransaction cannot proceed: unknown payment gateway or amount is not greater than 0Error: amount exceeds the maximum authorized credit amount (N): refund above the maximum authorized amount NError: checkoutExternalId must not be null if any of the following values are provided: externalOrderId, externalOrderNumber, externalTableNumber, or items: an external order field was sent withoutcheckoutExternalIdError: checkoutExternalId: The checkoutExternalId is already in use. Please ensure you provide a unique value: a completedoktransaction already uses thischeckoutExternalIdA request is already in progress: another request is in progress- A timeout message when the optional screens (tips, reference, review) exceed 60 seconds
Unknown Erroror an exception message: Android internal error
Start a refund transaction
Credits funds back to the customer card. Same deep link and payload as debit, with transactionType set to refund.
Parameters
transactionTypeStringrequireddefault debit- Must be set to refund. Any other value, such as credit, starts a debit
Response
Same as the debit action: a Transaction object with transactionType = refund on success, status = ko with the same message list on failure.
Print a receipt ticket
Prints free content, an invoice for example.
Parameters
dataStringrequired- Content to print
formatStringdefault text- text or escpos
Request
val request = PrintRequest(format = "text", data = "Text to print")
val queryParams = Uri.encode(Gson().toJson(request))
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("yavin://com.yavin.macewindu/v4/print?data=$queryParams")
}
startActivityForResult(intent, REQUEST_CODE_PRINT)Response
status = ok means the print request was accepted, not that something was printed: the content is not validated, so an empty data can also return ok. When the data query parameter of the deep link is missing or cannot be decoded, status = ko with the message Error: bad request print.
Fetch transactions
Retrieves the transactions of the terminal. Use limit and offset for pagination. If no date filters are set, the last 30 days are fetched.
Parameters
startDateStringdefault last 30 days- Earliest date to include, ISO 8601
endDateStringdefault today- Latest date to include, ISO 8601
startTimeStringdefault 00:00:00Z- Start time on startDate, ISO 8601
endTimeStringdefault 23:59:59Z- End time on endDate, ISO 8601
limitIntegerdefault 50- Max transactions returned, up to 200
offsetIntegerdefault 0- Pagination offset
Request
val request = TransactionsRequest(
startDate = "2022-01-01",
endDate = "2022-02-01",
startTime = "00:00:00",
endTime = "23:59:59",
limit = 50
)
val queryParams = Uri.encode(Gson().toJson(request))
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("yavin://com.yavin.macewindu/v4/transactions?data=$queryParams")
}
startActivityForResult(intent, REQUEST_CODE_TRANSACTIONS)Response
Returns total, count, limit, offset, and transactions. There is no status field.
totalis the number of transactions in this response (equal tocount), not the total number of matching transactions. To paginate, increaseoffsetbylimituntil a page returns fewer thanlimittransactions.- Each item of
transactionshas the fields ofItemTransactionResponseabove. It is a reduced object, not the full Transaction object: it has nocardToken,paymentApplication, tickets oridempotentUuid. createdAtis the terminal timestamp of the transaction, in ISO 8601 UTC.
If data is missing or invalid, no response is returned and the Yavin Pay screen stays open. Validate the request on the POS side before sending it.
Reverse a transaction
Reverses a recent transaction.
Reversal conditions. The original transaction is an ok debit processed on the same terminal issuing the reversal, less than 16 hours ago (terminal time), and the reversal amount is equal to the original total amount, tip included (amount + giftAmount).
Parameters
amountIntegerrequireddefault 0- Amount in cents, must equal the original total amount (amount • giftAmount)
initialTransactionIdStringrequired- Transaction to reverse
Response
status = ok with reversalTransactionId (the new reversal transaction) and initialTransactionId (the reversed one). On failure, status = ko with one of:
Transaction ID is mandatory: initialTransactionId missingTransaction not found for ID: <transactionId>: unknown transaction on this terminalAmount mismatch: amount differs from the original total amount (amount+giftAmount)Transaction has already been reversedCannot reverse this Transaction or time window expired: the transaction is not an ok debit or VAD transaction, or is more than 16 hours oldNo suitable gateway found to handle reversalAn unexpected error occurred. Please try again later.: generic error
Swapped fields on error. In an error response, reversalTransactionId contains the ID of the initial transaction, and initialTransactionId contains the ID of the reversal attempt. Read them accordingly.
Read NFC tag via Pay
Reads an NFC tag through Yavin Pay.
Parameters
readerIncentiveString- Text displayed while waiting for the tag
timeoutLongdefault 10000- Read timeout in milliseconds, maximum 10000: a higher value is capped at 10000
Request
val request = NFCReaderRequestV4(10000, "Approchez votre carte")
val queryParams = Uri.encode(Gson().toJson(request))
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("yavin://com.yavin.macewindu/v4/nfc-reader?data=$queryParams")
}
startActivityForResult(intent, REQUEST_CODE_READ)Response
status is a boolean: true with a TagInfo object when the tag was read, false otherwise.
The Transaction object
Returned by the payment actions. Fields coming from your request are echoed back as is. Keys whose value is null are omitted: tolerate missing keys. A declined payment has status = ko and no message (see Reading the response).
Attributes
statusString- ok or ko. Absent on a transaction still in progress
transactionIdString- Server-side identifier
amountInteger- Amount in cents, excluding giftAmount
giftAmountInteger- Tip or donation in cents
currencyCodeString- ISO 4217 code
transactionTypeString- Lowercase: debit or refund. Tolerate other values (eg na)
appVersionString- Yavin Pay app version
cardTokenString- Unique token of the customer card
clientCardTicket / merchantCardTicketString- Customer and merchant card tickets
paymentApplicationString- eg EMV_CONTACTLESS
schemeString- Acceptance network (eg VISA)
issuerString- Card issuer
idempotentUuidString- UUID from the request
reference, customer- Echoed from the request
checkoutExternalId, externalTableNumber, externalOrderNumber, externalOrderIdString- Echoed from the request
{
"status": "ok",
"transactionId": "xPUyi4fmdibD",
"amount": 1000,
"giftAmount": 0,
"currencyCode": "EUR",
"transactionType": "debit",
"appVersion": "3.2.8",
"cardToken": "1234567890",
"clientCardTicket": "...",
"merchantCardTicket": "...",
"paymentApplication": "EMV_CONTACTLESS",
"scheme": "CB",
"issuer": "VISA",
"idempotentUuid": "2f1c2e2a-6c21-4a4b-9c2b-9f6f2c8e9b51",
"cartId": "ORDER-2026-000123",
"reference": "Luke",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "john@yavin.com"
},
"checkoutExternalId": "order-123",
"externalTableNumber": "12",
"externalOrderNumber": "A-123",
"externalOrderId": "order-123"
}The Customer object
Optional on the payment and share-receipt actions. Pre-fills the customer information so the receipt can be sent by SMS or email without prompting on the terminal.
Attributes
firstNameString- First name
lastNameString- Last name
emailStringphoneString- International format starting with +
birthDateString- Birthdate
{
"customer": {
"firstName": "John",
"lastName": "Doe",
"phone": "+33612345678",
"email": "john@yavin.com"
}
}The ReceiptTicket object
Content printed alongside the card ticket on the payment action, or shared on the share-receipt action.
Attributes
dataStringrequired- Content to print
formatStringdefault text- Format of the content
tax_breakdownArray- Array of Tax objects, one per tax rate
{
"receiptTicket": {
"data": "Receipt ticket here to print if needed",
"format": "text"
}
}The Tax object
Optional breakdown attached to a ReceiptTicket, under the tax_breakdown key, with one entry per tax rate.
The key is tax_breakdown (not tax), and its entries are in snake_case.
Attributes
tax_amountInteger- Calculated tax amount in cents
tax_percentageInteger- Percentage applied to the amount (eg 20 for 20%)
{
"tax_breakdown": [
{ "tax_amount": 167, "tax_percentage": 20 },
{ "tax_amount": 50, "tax_percentage": 10 }
]
}The Vendor object
Identifies your POS software. Optional everywhere, but strongly recommended: it is what lets our support team trace an issue back to a specific integration and version.
Attributes
softwareNameString- Name of the POS software
softwareVersionString- Version of the POS software
{
"vendor": {
"softwareName": "MyPOS",
"softwareVersion": "1.0"
}
}The TagInfo object
Returned by the NFC reader action, describing the tag that was read.
Attributes
serialNumberString- Tag identifier
{
"tagInfo": {
"serialNumber": "04A2B3C4D5E6F7"
}
}