Skip to main content

Sentinel Service Mode

How an ECR, till or third-party application drives a Sentinel payment terminal — over local HTTP, over a USB serial cable, or through Android intents.

SettingValue
ApplicationSentinel 4.0.8
Packagecom.paylands.posms.sentinel
Intent schemecom.paynopain.sentinelservice
Local port6889

1. Introduction​

Sentinel exposes its payment operations to other applications so a cash register can take a card payment without anyone touching the terminal. This document is the contract: what you can call, what you must send, and what comes back.

Three transports carry the same operations and return the same payloads. Pick the one that matches how your till reaches the terminal:

  • Local HTTP — your software runs on the terminal itself, or on the same host, and posts JSON to 127.0.0.1:6889.
  • USB serial — your till is a separate machine and sees the terminal as a COM port.
  • Android intents — your software is another app on the same Android terminal.

Not every operation reaches every transport, and the availability differs per operation. Each entry below opens with the three transports marked as available or not, so you can see at a glance what a given integration can call.

note

Service mode. The terminal only answers external operations while service mode is enabled in its configuration. When it is off, operations are refused with a fixed JSON body and nothing is charged. Health check is the sole exception — it answers either way, so a till polling for presence can tell a terminal that is switched off from one that is merely not configured.

2. Package name and intent scheme​

Sentinel Service used to ship as a separate application under the package com.paynopain.sentinelservice. Service mode is now part of the main Sentinel app, and the package name changed with it.

UseValue
Package nameCurrent package. Use this for PackageManager installation checks and anywhere you reference the installed app: com.paylands.posms.sentinel
Intent schemeUnchanged, for backward compatibility. Keep using it in every intent URI: com.paynopain.sentinelservice://
danger

Update your installation check. Intent URIs need no changes, but any pre-flight check that looks for com.paynopain.sentinelservice as an installed package will now report the app as missing. Point it at com.paylands.posms.sentinel. The old name survives only as a URI scheme.

3. Transports​

3.1 Local HTTP​

POST JSON to a per-operation path on port 6889. There is no single dispatch endpoint — the path is the operation.

POST http://127.0.0.1:6889/payment
Content-Type: application/json
Authorization: Bearer <token supplied by Paynopain>

{"amount":100,"currencyCode":978,"stan":"7f328336-f3fa-4e37-92d5-b7f1311c0f4d"}

The server answers 200 OK with the operation's result as the body, or 404 Not Found for an unknown path. Successful card operations return the transaction result JSON described in §24; validation failures return a plain-text message (§4.4).

Paths currently served:

/payment          /preauthorization   /transaction
/refund /confirmation /transaction_by_stan
/referral /cancellation /list_transactions
/reversal /decrement /reports
/cardverify /cft /close_batch
/healthcheck /identify /reprint
/sync

3.2 HTTPS​

The same port serves TLS once a certificate has been provisioned to the terminal. The certificate and private key arrive through device configuration as a base64 PKCS#12 keystore; until one is present the server listens in plain HTTP, so existing integrations keep working unchanged. Check the terminal's service status screen to see which is active.

3.3 USB serial​

The terminal enumerates as a USB serial device and your till opens it as a COM port. Supported on PAX terminals; other models report no ECR link and the USB server does not start.

SettingValue
Line115200 baud, 8 data bits, no parity, 1 stop bit
Framing4-byte big-endian length header, then that many bytes of UTF-8 JSON
Request{"id":"…","type":"payment","body":{ …same fields as HTTP… }}
Response{"id":"…","type":"payment","result":"…"}

The id is echoed back so you can match responses to requests. type takes the same names as the HTTP paths without the leading slash. One operation runs at a time: a second request arriving mid-operation is answered immediately with {"error":"busy: another operation is in progress"} rather than being queued or dropped. Health check is answered by the reader thread, so it stays responsive even while a payment is waiting on the cardholder.

3.4 Android intents​

Launch a view intent and read the result in onActivityResult. Authorization is handled by whitelisting your application with the Paynopain team rather than by a token.

String uri = "com.paynopain.sentinelservice://payment?amount=100&currencyCode=978&stan=" + stan;
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
startActivityForResult(intent, PAYMENT_RESULT_CODE);

Results come back as two string extras: transactionResult carries the operation payload, and error_message explains a cancellation or failure. See §26 for a full example.

4. Conventions​

4.1 Amounts​

Every amount is an integer in the currency's minor units — 100 is one euro. ISK, which has no minor unit, is the exception and is sent as whole króna.

4.2 STAN​

The stan is your reference for the operation, and you generate it: a UUID, unique per request. It is echoed in the result and it is the key you use to recover an operation whose response you never received — see §16. Reusing a STAN makes that recovery ambiguous.

4.3 Currency​

currencyCode is the ISO 4217 numeric code — 978 euro, 840 US dollar, 826 pound sterling. It must match the currency the terminal is configured for; a mismatch is refused before anything reaches the card.

4.4 Errors​

Validation and policy failures return a plain-text string, not JSON. Test for these before parsing a result body:

ResponseMeaning
Error: Missing required fields — …Named fields were absent or empty. The list follows the dash.
Error: No data received in the requestEmpty request body.
Max Amount ExceededAbove the terminal's configured ceiling. With a fixed tip, the ceiling applies to base plus tip.
Currency is not allowedcurrencyCode is not the terminal's currency.
Too much pending transactions. Please sync.The offline queue is full. Call sync, then retry.
Tokenization is not enabled for this merchanttokenize: true on a merchant without tokenization.
Operative not allowedThe operation is not available on this terminal's operator.
Error: …An unexpected failure; the exception message follows.
danger

These strings are localised. A terminal set to Spanish returns Error: Campos requeridos faltantes rather than the English above. Do not match on the full text — match on a prefix you control, or treat any non-JSON body as a failure.

Two responses are JSON rather than plain text. Service mode disabled:

{"error":"Service mode disabled: terminal does not accept external operations"}

And, on the USB transport only, a second concurrent operation:

{"error":"busy: another operation is in progress"}

4.5 Timeouts​

An operation that never produces a result — an abandoned payment, a dropped socket — is abandoned after 100 seconds and returns a timeout error rather than holding the transport open. Printing is capped at 30 seconds and reports as not printed. Size your own client timeout above 100 seconds so you see the terminal's answer rather than your own.


5. Payment​

Charge a debit or credit card. Beyond a plain sale, the same operation covers tipping, QR payment and per-sale tokenization.

Operation: payment

TransportOperationAvailable
HTTP/paymentYes
USBpaymentYes
Intent//paymentYes

Availability: Service mode.

Request​

FieldTypeRequirementNotes
amountIntegerRequiredMinor units. Excludes a fixed tip, which is added on top.
currencyCodeIntegerRequiredISO 4217 numeric. Must match the terminal.
stanStringRequiredYour UUID for this operation.
referenceStringOptionalFree-text reference stored with the transaction and shown on the receipt.
customer_ext_idStringOptionalYour customer identifier, echoed back in the result.
tipBoolean or IntegerOptionaltrue prompts for a tip on the terminal screen. An integer is a fixed tip in minor units, added to amount, with no prompt. Omit for no tip.
channelStringOptionalcard (default) or qr.
qrMethodStringOptionalWith channel: "qr", fixes the QR method (for example BIZUM) and skips the on-screen picker. Omit to let the cashier choose.
tokenizeBooleanOptionalOverrides the terminal's tokenization switch for this sale only. Omit to follow the terminal setting.

Response​

The transaction result payload (§24), once the card operation completes.

Example​

POST /payment
{"amount":100,"currencyCode":978,"stan":"7f328336-f3fa-4e37-92d5-b7f1311c0f4d"}
com.paynopain.sentinelservice://payment
?amount=100&currencyCode=978&stan=ebdd1b6a-02f4-4366-9a9e-d19758123746

5.1 Tipping​

tip is overloaded deliberately, because the two tipping models are genuinely different operations from your side:

  • "tip": true brings Sentinel to the foreground and asks the cardholder for a tip on the terminal. Your call blocks until the whole flow finishes and then returns the result as usual.
  • "tip": 150 adds a fixed 1.50 tip with no screen and no interaction. The amount charged is amount + tip, and that total is what must stay under the terminal's ceiling.

5.2 QR payment​

With channel: "qr" the terminal displays a QR code, which the customer may take minutes to scan. Rather than hold your connection open, the call returns immediately:

{"status":"QR_SHOWN","stan":"…","message":"QR mostrado en el terminal. Consulta el resultado con transaction_by_stan."}

Poll get by STAN with the same stan to collect the outcome.

5.3 Tokenization​

tokenize decides per sale instead of following the terminal-wide switch. Two limits are worth knowing before you rely on it:

  • The merchant's own tokenization flag still wins. Asking for a token on a merchant without tokenization is refused outright rather than charging silently without one.
  • Per-sale control only reaches the ECOPAYNET route. On MONET the payment app decides from the merchant configuration, so tokenize: false will not suppress a token that the merchant is configured to receive.

6. Preauthorization​

Reserve an amount on a card without capturing it. Settle it later with a confirmation, reduce it with a decrement, or release it with a cancellation.

Operation: preauthorization

TransportOperationAvailable
HTTP/preauthorizationYes
USBpreauthorizationYes
Intent//preauthorizationYes

Availability: Service mode.

Request​

FieldTypeRequirementNotes
amountIntegerRequiredAmount to reserve, in minor units.
currencyCodeIntegerRequiredISO 4217 numeric.
stanStringRequiredYour UUID for this operation.
referenceStringOptionalFree-text reference.
customer_ext_idStringOptionalYour customer identifier.

Response​

Transaction result with operative of DEFERRED. Keep its transaction_id, auth_code and refund_id — the follow-up operations need them.

7. Confirmation​

Capture a preauthorization, in full or for less than was reserved.

Operation: confirmation

TransportOperationAvailable
HTTP/confirmationYes
USBconfirmationYes
Intent//confirmationYes

Availability: Service mode.

Request​

FieldTypeRequirementNotes
amountIntegerRequiredAmount to capture. May be less than reserved.
currencyCodeIntegerRequiredISO 4217 numeric.
parentIdentifierStringRequiredtransaction_id of the preauthorization.
stanStringRequiredYour UUID for this operation.
dateStringConditionalRequired on ECOPAYNET. Date of the original operation.
authCodeStringConditionalRequired on ECOPAYNET. From the preauthorization result.
refundIdStringConditionalRequired on ECOPAYNET. From the preauthorization result.
customer_ext_idStringOptionalYour customer identifier.

Response​

Transaction result with operative of CONFIRMATION.

note

The conditional fields depend on the terminal's operator. On ECOPAYNET, date, authCode and refundId are mandatory and their absence is reported as a missing-fields error. On other operators they are ignored. If you do not know which operator a terminal runs, call identify — it returns the operator name — or simply always send all three.

8. Cancellation​

Release a preauthorization without capturing it.

Operation: cancellation

TransportOperationAvailable
HTTP/cancellationYes
USBcancellationYes
Intent//cancellationYes

Availability: Service mode.

Request​

Same fields as confirmation: amount, currencyCode, parentIdentifier and stan are required; date, authCode and refundId are required on ECOPAYNET; customer_ext_id is optional.

Response​

Transaction result with operative of CANCELLATION.

9. Decrement​

Lower the amount held by a preauthorization while keeping the reservation open — the case where a final bill lands under the estimate but the tab is still running.

Operation: decrement

TransportOperationAvailable
HTTP/decrementYes
USBdecrementYes
Intent//decrementYes

Availability: Service mode.

Request​

FieldTypeRequirementNotes
amountIntegerRequiredAmount currently held by the preauthorization.
decrementAmountIntegerRequiredAmount to release, in minor units.
currencyCodeIntegerRequiredISO 4217 numeric.
parentIdentifierStringRequiredtransaction_id of the preauthorization.
stanStringRequiredYour UUID for this operation.
authCodeStringOptionalFrom the preauthorization result.
refundIdStringOptionalFrom the preauthorization result.
dateStringOptionalDate of the original operation.
customer_ext_idStringOptionalYour customer identifier.

Response​

Transaction result with operative of DECREMENT.

10. Refund​

Return money to the card used for an earlier sale. The card must be presented.

Operation: refund

TransportOperationAvailable
HTTP/refundYes
USBrefundYes
Intent//refundYes

Availability: Service mode.

Request​

FieldTypeRequirementNotes
amountIntegerRequiredAmount to return, in minor units.
currencyCodeIntegerRequiredISO 4217 numeric.
parentIdentifierStringRequiredtransaction_id of the original sale.
refundIdStringRequiredrefund_id from the original sale's result.
stanStringRequiredYour UUID for this operation.
dateStringConditionalRequired on ECOPAYNET.
authCodeStringConditionalRequired on ECOPAYNET.
customer_ext_idStringOptionalYour customer identifier.

Example​

POST /refund
{"amount":100,"currencyCode":978,
"parentIdentifier":"9c76341c-fcbd-492d-8b7a-ed14d0fc6a8d",
"refundId":"1248529069100765184",
"stan":"79f5ae33-cad9-45f4-983e-d7a9a8fc1cc9"}

11. Referral refund​

Refund an earlier sale without the card present, using the original transaction's credentials.

Operation: referral

TransportOperationAvailable
HTTP/referralYes
USBreferralYes
Intent//referralYes

Availability: Service mode.

Request​

Identical to refund: amount, currencyCode, parentIdentifier, refundId and stan are required; date and authCode are required on ECOPAYNET; customer_ext_id is optional.

Response​

Transaction result with operative of REFERRAL_REFUND.

Example​

com.paynopain.sentinelservice://referral
?amount=100&currencyCode=978
&parentIdentifier=1e7da5d3-4aec-4f97-9029-4dce55216964
&refundId=1334440644315197440
&stan=234ec44e-a377-4380-a4fb-bf15933301e0

12. Reversal​

Void a sale on the same day it was taken, before it settles. Cheaper and faster than a refund, and it leaves no refund on the cardholder's statement.

Operation: reversal

TransportOperationAvailable
HTTP/reversalYes
USBreversalYes
Intent//reversalYes

Availability: Service mode.

Request​

FieldTypeRequirementNotes
currencyCodeIntegerRequiredISO 4217 numeric.
parentIdentifierStringRequiredtransaction_id of the sale being voided.
refundIdStringRequiredrefund_id from the original sale's result.
stanStringRequiredYour UUID for this operation.
customer_ext_idStringOptionalYour customer identifier.

No amount: a reversal always voids the full original amount.

Response​

Transaction result with operative of REVERSAL. On ECOPAYNET terminals the operation is not supported and returns Operative not allowed.

13. Card verify​

Read and validate a card without charging it, and receive a reusable token for it.

Operation: cardverify

TransportOperationAvailable
HTTP/cardverifyYes
USBcardverifyYes
Intent//cardverifyYes

Availability: Service mode.

Request​

FieldTypeRequirementNotes
currencyCodeIntegerRequiredISO 4217 numeric.
stanStringRequiredYour UUID for this operation.
dniStringOptionalCardholder identity document.
referenceStringOptionalFree-text reference.
customer_ext_idStringOptionalYour customer identifier.

Response​

Transaction result with operative of CARD_VERIFY, plus the token fields card_token, par and transaction_type. The amount is zero.

Example​

POST /cardverify
{"currencyCode":978,"stan":"5ad8fb83-9540-4a46-bfaa-d0b3f9bbc196"}

14. CFT — credit to card​

Pay money onto a beneficiary's card. The amount is not yours to set: the backend fixes it from the order token, and the terminal only confirms it against the card.

Operation: cft

TransportOperationAvailable
HTTP/cftYes
USBcftYes
Intent//cftYes

Availability: Service mode.

Request​

FieldTypeRequirementNotes
tokenStringRequiredOrder token issued by the backend. Determines the amount.
nameStringRequiredBeneficiary given name.
surnameStringRequiredBeneficiary surname.
emailStringRequiredBeneficiary email.
stanStringRequiredYour UUID for this operation.

15. Get transaction​

Look up a transaction on the backend by its identifier.

Operation: transaction

TransportOperationAvailable
HTTP/transactionYes
USBtransactionYes
Intent//transactionYes

Availability: Service mode.

Request​

FieldTypeRequirementNotes
transactionIdStringRequiredThe transaction's identifier.
stanStringRequiredYour UUID for this lookup.

Response​

{
"id": "665d6dea126521c8926056ef",
"identifier": "919ff6d6-7727-4801-9035-9f57d2e8041a",
"merchant": "649c0329052f100dceea50e9",
"device": "64d1ef302ff0833b33ecffe6",
"operative": "AUTHORIZATION",
"status": "PAID",
"amount": 100,
"currency": "978",
"incoming": "DEVICE",
"integrator": "615d7d35be2246344cc49adf",
"integrator_external_id": "919FF6D6-7727-4801-9035-9F57D2E8041A",
"created_at": "2024-06-03T07:16:58.183Z",
"updated_at": "2024-06-03T07:16:58.384Z",
"historic": [{ "status": "PAID", "date": "2024-06-03T07:16:58.181Z" }]
}

16. Get transaction by STAN​

Recover an operation using the STAN you generated. This is how you resolve a payment whose response you never received, and how you collect the outcome of a QR payment.

Operation: transaction_by_stan

TransportOperationAvailable
HTTP/transaction_by_stanYes
USBtransaction_by_stanYes
Intent//transactionbystanYes

Availability: Service mode.

note

The intent host is transactionbystan, without underscores. The HTTP path and USB type are transaction_by_stan, with them.

Request​

FieldTypeRequirementNotes
stanStringRequiredThe STAN you sent with the original operation.

Response while the operation is still running​

Sentinel answers from its local pending queue, so an operation still in flight is reported rather than reported missing:

{
"identifier": "4ad6624f-8ad4-4ef7-8557-d41616770f63",
"operative": "AUTHORIZATION",
"amount": 100,
"currency": "978",
"incoming": "DEVICE",
"response_status": "Pending",
"response_message": "In progress",
"stan": "2fdf5900-22ae-46b1-8398-8fcda2bcce04"
}

Response once it has finished​

The full backend record, with the card result nested under result:

{
"id": "67a5f4e1d0a4c0353b3e04d6",
"identifier": "4ad6624f-8ad4-4ef7-8557-d41616770f63",
"merchant": "66e80b75a1a978f055c8fe07",
"device": "64d1ef302ff0833b33ecffe6",
"operative": "AUTHORIZATION",
"status": "PAID",
"amount": 100,
"currency": "978",
"incoming": "DEVICE",
"result": {
"aid": "A0000000031010",
"amount": 100,
"currency": "978",
"auth_code": "050107",
"brand": "VISA",
"pan": "476173******0119",
"reversal": false,
"date_terminal": "2025-02-07T11:56:17.836Z",
"date_server": "2025-02-07T11:56:17.836Z",
"response_status": "PAID",
"card_input": "CLESS",
"cardholder_method": ["[SIGNATURE]"],
"response_message": "OK: success",
"terminal_acquirer": "TPPESNA004",
"merchant_acquirer": "TPPESNA004",
"refund_id": "1337391474924330111",
"stan": "2fdf5900-22ae-46b1-8398-8fcda2bcce04"
},
"created_at": "2025-02-07T11:56:17.872Z",
"updated_at": "2025-02-07T11:56:18.452Z",
"historic": [{ "status": "PAID", "date": "2025-02-07T11:56:17.862Z" }]
}

17. List transactions​

Retrieve recent transactions to reconcile a till or to recover after an interruption — the case where the STAN itself was lost and the single-transaction lookups cannot help.

Operation: list_transactions

TransportOperationAvailable
HTTP/list_transactionsYes
USBlist_transactionsYes
Intent—No

Availability: Service mode.

Request​

All fields optional; an empty body returns the 50 most recent.

FieldTypeRequirementNotes
limitIntegerOptionalDefault 50, clamped to 200.
dateFromStringOptionalLower bound.
dateToStringOptionalUpper bound.
statusStringOptionalOne TransactionStatus value. An unrecognised value returns an error listing the accepted ones.

Response​

{
"status": "OK",
"count": 2,
"limit": 50,
"truncated": false,
"transactions": [
{
"identifier": "919ff6d6-7727-4801-9035-9f57d2e8041a",
"amount": 100,
"remaining_amount": null,
"currency": "978",
"operative": "AUTHORIZATION",
"status": "PAID",
"created_at": "2026-08-31T09:14:22Z",
"auth_code": "478893",
"refund_id": "1305910960162082816",
"parent_id": null,
"pan": "476173******0119",
"reference": null,
"aid": "A0000000031010",
"brand": "VISA",
"tip_amount": 0,
"is_qr": false,
"token": null,
"customer_ext_id": null
}
]
}

Dates are ISO-8601 in UTC. There is no paging: truncated is true when exactly limit rows came back, meaning more may exist. Narrow with dateFrom and dateTo rather than expecting a cursor.

18. Reports​

Totals for the period still open — the equivalent of an X report. Reads only; it changes nothing.

Operation: reports

TransportOperationAvailable
HTTP/reportsYes
USBreportsYes
Intent—No

Availability: Service mode.

Request​

No body.

Response​

{
"status": "OK",
"dateFrom": "2026-08-31 06:00:00",
"transactions": 42,
"totalAmount": 128400,
"authorizations": 38,
"totalAuthorizationsAmount": 131900,
"refunds": 2,
"totalRefundsAmount": 3500,
"deferred": 1,
"totalDeferredAmount": 5000,
"confirmations": 1,
"totalConfirmedAmount": 5000,
"cancelled": 0,
"totalCancelledAmount": 0
}

All totals are in minor units, covering everything since dateFrom — the cut-off set by the last close.

19. Close batch​

Close the till period — the equivalent of a Z report. Prints the report and moves the cut-off forward so the next report starts where this one ends.

Operation: close_batch

TransportOperationAvailable
HTTP/close_batchYes
USBclose_batchYes
Intent—No

Availability: Service mode.

Request​

FieldTypeRequirementNotes
printBooleanOptionalDefault true. Set false to close without printing, when the till prints its own copy.

Response​

The same totals as reports, with status of CLOSED plus cutoff and printed.

danger

This does not settle the batch with the acquirer. Acquirer settlement is initiated by the backend, not the terminal. Close batch does exactly what the button in the app does: print the report and advance the local cut-off date. Treat it as a till operation, not a financial one.

20. Reprint receipt​

Print a receipt again, or have the backend issue it as a link — for when the paper ran out, the cut was crooked, or the customer comes back ten minutes later.

Operation: reprint

TransportOperationAvailable
HTTP/reprintYes
USBreprintYes
Intent—No

Availability: Service mode.

Request​

FieldTypeRequirementNotes
transactionIdStringOptionalOmit to reprint the most recent transaction. Searched among the last 50.
copyStringOptionalclient (default) or merchant.
giftTicketBooleanOptionalDefault false. With true, this print omits the subtotal, tip and total. It does not modify the transaction or future prints.
emailStringOptionalSends the receipt by email instead of printing, and returns its URL.

Response​

{"status":"PRINTED","identifier":"919ff6d6-…","copy":"client","giftTicket":false}
{"status":"SENT","identifier":"919ff6d6-…","email":"a@b.com","url":"https://…"}
{"status":"NOT_FOUND","message":"…"}
{"status":"ERROR","message":"El terminal no pudo imprimir (sin papel, sin impresora o error)"}

The printer only reports success. A terminal with no paper, or no printer at all, produces ERROR after the 30-second print timeout rather than hanging.

Every printed copy also includes a QR code containing only the transaction UUID. In the interactive app, the operator can scan it from the Transactions top bar to retrieve and open that transaction; no amount or card data is encoded in the QR.


21. Health check​

Ask whether the terminal is up and the transport is reaching it. The only operation that answers regardless of service mode.

Operation: healthcheck

TransportOperationAvailable
HTTP/healthcheckYes
USBhealthcheckYes
Intent//healthcheckYes

Availability: Always available.

Response​

The literal string READY or NOT_READY — not JSON.

This deliberately answers a different question from "will you take a payment". A till polling for presence needs to tell a terminal that is off, or a cable that is loose, from one that is powered up and simply not in service mode. For the latter, ask for an operation and read the service-mode error.

On USB, health check is answered by the reader thread rather than the worker, so it stays responsive while a payment is waiting on the cardholder.

22. Identify​

Ask the terminal who it is. Usually the first call an integration makes: you need the identifier to match your receipts against the backend's, and the serial number for your own inventory.

Operation: identify

TransportOperationAvailable
HTTP/identifyYes
USBidentifyYes
Intent—No

Availability: Service mode.

Response​

{
"status": "OK",
"identifier": "352966108076163",
"serialNumber": "1853237957",
"model": "A920Pro",
"manufacturer": "PAX",
"appVersion": "4.0.8",
"operator": "MONET"
}

A terminal that has not finished configuration returns {"status":"ERROR","message":"Terminal sin identificador: falta configuración"}. operator is what tells you whether the ECOPAYNET conditional fields apply.

23. Sync​

Flush the offline queue. Without this, a terminal that hits its pending limit refuses every operation and, in service mode, has nobody standing in front of it to clear the jam.

Operation: sync

TransportOperationAvailable
HTTP/syncYes
USBsyncYes
Intent//syncYes

Availability: Service mode.

Response​

{"status":"NOTHING_TO_SYNC","pendingBefore":0,"pendingAfter":0}
{"status":"SYNCED","pendingBefore":7,"pendingAfter":0}
{"status":"IN_PROGRESS","pendingBefore":7,"pendingAfter":3,"message":"…"}
{"status":"ERROR","message":"…","pendingBefore":7}
note

Sync starts the flush; it does not wait for it. The terminal cannot know when the backend has finished, so it reports the pending count before and after a short settling window and returns. IN_PROGRESS means work continues in the background — retry your operation, and call sync again if it is still refused. Do not treat SYNCED as a guarantee that every queued transaction reached the backend.


24. Transaction result payload​

What every card operation returns. Field names are snake_case, with panExpiryDate the lone exception.

FieldTypeRequirementNotes
deviceStringAlwaysTerminal identifier.
transaction_idStringAlwaysBackend identifier. Use as parentIdentifier in follow-ups.
stanStringAlwaysThe STAN you sent.
operativeOperativeAlwaysSee §25.
amountIntegerAlwaysMinor units. Includes the tip when there is one.
tip_amountIntegerOptionalTip portion of amount.
currencyStringAlwaysISO 4217 numeric, as a string.
response_statusTransactionStatusAlwaysThe outcome. See §25.
response_codeStringOptionalAcquirer response code.
response_messageStringAlwaysHuman-readable outcome.
incomingIncomingAlwaysDEVICE or API.
auth_codeStringOptionalAuthorization code. Needed for ECOPAYNET follow-ups.
refund_idStringOptionalRequired to refund or reverse this transaction. Store it.
parent_transaction_idStringOptionalSet on operations that reference an earlier one.
aidStringOptionalEMV application identifier.
brandStringOptionalCard scheme.
panStringOptionalMasked card number.
panExpiryDateStringOptionalMasked expiry.
card_input_typeCardInputTypeOptionalHow the card was read. See §25.
cvm_type_listStringOptionalCardholder verification method.
reversalBooleanOptionalWhether this transaction was reversed.
date_terminalDateOptionalTerminal clock.
date_serverDateOptionalBackend clock. Prefer this when they disagree.
emv_responseObjectOptionalEMV detail.
terminal_acquirerStringOptionalAcquirer terminal ID.
merchant_acquirerStringOptionalAcquirer merchant ID.
terminal_issuerStringOptionalIssuer terminal ID.
merchant_issuerStringOptionalIssuer merchant ID.
card_tokenStringOptionalCard token from a card verify.
additional_tokenStringOptionalToken from a tokenized sale, on either operator route.
parStringOptionalPayment account reference.
transaction_typeStringOptionalScheme transaction type.
card_holder_messageStringOptionalMessage shown to the cardholder.
invoice_numberStringOptionalAcquirer invoice number.
referenceStringOptionalThe reference you sent.
customer_ext_idStringOptionalThe customer identifier you sent.

24.1 Example — one euro, approved​

{
"device": "352966108076163",
"transaction_id": "1876c5dd-3450-4f65-9ca4-872398aa61c6",
"stan": "cec60447-7467-4457-a542-40a791f11f02",
"operative": "AUTHORIZATION",
"amount": 100,
"currency": "978",
"response_status": "PAID",
"response_message": "success",
"incoming": "DEVICE",
"aid": "A0000000031010",
"auth_code": "478893",
"brand": "VISA",
"pan": "476173******0119",
"panExpiryDate": "****",
"card_input_type": "CLESS",
"cvm_type_list": "SIGNATURE",
"reversal": false,
"date_terminal": "Nov 12, 2024 04:03:44 PM",
"date_server": "Nov 12, 2024 04:03:48 PM",
"emv_response": { "smartCardScheme": 1, "authorisationResponseCode": "00" },
"terminal_acquirer": "TPPESNA004",
"merchant_acquirer": "TPPESNA004",
"refund_id": "1305910960162082816"
}

24.2 Example — one euro, declined​

A decline is a normal response, not an error: it arrives as a well-formed result with a non-PAID status. Branch on response_status, never on whether the call returned.

{
"device": "352966108076163",
"transaction_id": "e1d7a4b1-2a8a-43e6-be62-001154f0d2f4",
"stan": "6162ab8a-92ab-4d7d-8393-26d3fd6b666c",
"operative": "AUTHORIZATION",
"amount": 100,
"currency": "978",
"response_status": "REFUSED",
"response_message": "KO - refused",
"incoming": "DEVICE",
"aid": "A0000000041010",
"auth_code": "327674",
"brand": "MASTERCARD",
"pan": "****************",
"card_input_type": "CLESS",
"cvm_type_list": "NO",
"reversal": false,
"terminal_acquirer": "M1TPNP0005",
"merchant_acquirer": "TPNP0005",
"refund_id": "128937286421796"
}

25. Enumerations​

Operative​

ValueMeaning
AUTHORIZATIONSale.
DEFERREDPreauthorization.
CONFIRMATIONCapture of a preauthorization.
CANCELLATIONRelease of a preauthorization.
DECREMENTPartial release of a preauthorization.
DEFERRED_UPDATEAdjustment to a preauthorization.
REFUNDRefund with the card present.
REFERRAL_REFUNDRefund without the card.
REVERSALSame-day void.
CARD_VERIFYCard validation, no charge.
CFTCredit to a card.
PAYOUTPayout.
LIMITED_PAYMENTRestricted payment.

TransactionStatus​

ValueMeaning
PAIDApproved and settled locally. The only success value.
REFUSEDDeclined by the issuer or acquirer.
PENDINGUnder way; the outcome is not yet known.
STARTEDBegun on the terminal.
CANCELLEDCancelled before completion.
EXPIREDTimed out before completion.
ERRORFailed for a technical reason.
DUPLICATEDRejected as a duplicate.
danger

CHIP_ERROR is not a transaction status. Earlier revisions of this document listed it; it is not one of the values the SDK can return. A chip failure surfaces as ERROR with detail in response_message.

CardInputType​

ValuesMeaning
CONTACTChip, inserted.
CLESSContactless.
CLESS_MAGSTRIPEContactless magnetic-stripe profile.
MAGNETICSwiped.
MAGNETIC_FALLBACKSwiped after a chip failure.
CHIPChip.
MANUALKeyed by the operator.
KEY_ENTRYKeyed entry.
NON_PRESENTIALCard not present.
UNKNOWNNot reported by the terminal.

Incoming​

DEVICE — started on the terminal, which includes everything in this document. API — started by the backend.

26. Intent integration​

26.1 Launching an operation​

const val PAYMENT_RESULT_CODE = 1001

val stan = UUID.randomUUID()
val uri = "com.paynopain.sentinelservice://payment" +
"?amount=100&currencyCode=978&stan=$stan"

startActivityForResult(Intent(Intent.ACTION_VIEW, Uri.parse(uri)), PAYMENT_RESULT_CODE)

26.2 Reading the result​

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != PAYMENT_RESULT_CODE) return

when (resultCode) {
RESULT_OK -> {
// Same payload as the HTTP and USB transports return.
val result = data?.getStringExtra("transactionResult")
}
RESULT_CANCELED -> {
val errorMessage = data?.getStringExtra("error_message")
}
}
}

26.3 Available intent hosts​

Every URI takes the form com.paynopain.sentinelservice://<host>?<query>. Query parameter names match the JSON field names of the corresponding operation.

payment           confirmation      transaction
preauthorization cancellation transactionbystan
refund decrement healthcheck
referral cardverify sync
reversal cft

Operations absent from this list — list transactions, reports, close batch, reprint and identify — are reachable over HTTP and USB only.

26.4 Checking availability first​

val installed = try {
packageManager.getPackageInfo("com.paylands.posms.sentinel", 0)
true
} catch (e: PackageManager.NameNotFoundException) {
false
}

Check the package name, not the intent scheme. They are deliberately different — see §2.

The information contained in this document is property of PAYNOPAIN SL and may not be copied, used or disclosed in whole or in part, except with the prior written consent of PAYNOPAIN SL.