Connect SDK
The Rootline Connect SDK lets the app you run on the payment device integrate with the Rootline Connect app. Use the SDK to start the Connect app, observe connection state and payment results, and print receipts.
How a payment flows through the SDK
Your app starts the Connect app using the SDK. When a payment request is initiated on the payment device, the Connect SDK opens the Connect app to let the customer complete the payment. Once the payment is completed, your app returns to the foreground.
Step by step
- On startup, your app creates the
RootlineConnectclient, registers the handlers and finally callsconnect.start(). - Your app starts a payment. Your backend sends a
POST /paymentsrequest to Rootline with thepayment_rails.payment_device_id. - Rootline initiates the payment on the device, the device acknowledges, and Rootline responds with
checkout_status=open. - The Connect SDK automatically foregrounds the Rootline Connect app, displaying the payment screen to the customer.
- The customer presents their card, the payment finalizes and the result is displayed.
- The Connect app sends the result to Rootline's servers and to your app. The
onPaymentStatehandler delivers the result init.outcome. - Rootline additionally sends the result asynchronously via webhooks. Optionally, you can retrieve the status with
GET /payments/{payment_id}.
SDK integration
Before you begin, contact your Rootline point of contact to add your app to the integrator list of the Connect app.
Add the SDK to your project
Add the Connect SDK dependency to your Gradle build.
Version compatibility
The SDK talks to the Connect app installed on the terminal, so each SDK version needs a minimum Connect app version to work against. Newer Connect apps always support older SDKs, so you can upgrade the terminal fleet ahead of your app.
| Connect SDK | Minimum Connect app | Notes |
|---|---|---|
| 1.0.0 – 1.0.1 | 1.0.0 | Works with every released Connect app. |
| 1.0.2 | 1.0.6 | Presents payments reliably when the terminal screen has gone to sleep. |
These minimums apply to terminals where your app drives the payment. A terminal running the Connect app on its own, with no integrator app installed, is unaffected by the SDK version — Connect raises the card prompt itself there.
What changed in each version is listed in the Connect SDK release notes.
Running an SDK against a Connect app below its minimum is not a build or install error — it fails at payment time. On a terminal running Connect 1.0.5 or earlier with your app installed as the integrator, an app built against SDK 1.0.2 will not display payments started from the Rootline backend: they stay open and fail after about ten seconds, with nothing shown to the customer.
Confirm the Connect app version on your terminals with your Rootline point of contact before rolling out an SDK upgrade. Staying on 1.0.1 is safe on any Connect app version.
Starting the SDK
Create the RootlineConnect client, register the event handlers and start the Connect app.
The start() function is asynchronous. The onRuntimeStarted handler fires when the runtime is fully up or once it has failed to boot. We recommend waiting for the handler rather than assuming readiness when start() returns.
If the Connect app is already running when you call start(), you still get a RuntimeStarted(success = true) event. The handler works regardless of ordering. A failed boot is not cached: the next start() attempts to start the Connect app again.
The event handlers
The event handlers are split into two channels: status and outcomes.
The onRuntimeStarted and onConnectionState handlers report the current status. The onConnectionState handler also fires once right after registration so that you know the current state without waiting for a change.
Outcomes are handled by onPaymentState and onPrintResult. Outcomes are delivered durably: an outcome that occurs while your app is backgrounded, killed, or offline is held and replayed when your app comes back — specifically on the next onStart of the Activity you created the client with, not at the moment the payment settles. Delivery is at-least-once. Deduplicate payment outcomes by PaymentState.id and print outcomes by PrintResult.requestId (the id printReceipt returned).
| Handler — event | Fields | Meaning |
|---|---|---|
onRuntimeStarted — RuntimeStarted | success, reason | The runtime finished booting or failed, with a coarse reason. |
onConnectionState — ConnectionState | connected, reason | The terminal's connection to Rootline became reachable/unreachable. |
onPaymentState — PaymentState | id, outcome, amountMinor, currency, clientId | A payment reached a terminal outcome. |
onPrintResult — PrintResult | paymentId, recipient, outcome, requestId | The result of a receipt print you requested. |
All four handlers are bound to the lifecycle of the Activity you passed to RootlineConnect. They fire between onStart and onStop, never while your app is backgrounded, its screen is off, or its process has been killed. Nothing is lost — an outcome that lands while you are stopped is held durably and replayed on your next onStart — but it is delayed, by as long as it takes the user to bring your app back.
Do not rely on these handlers for anything that must happen regardless of what is on screen, such as recording the sale in your own system. Verify the payment from your backend instead.
A payment can also be started from the Rootline backend rather than from your app, and the terminal has to show the card prompt for it. The SDK handles this for you: the trigger is delivered on the same durable channel as outcomes and acted on when your Activity starts, which is why the terminal can wake from an idle screen and still present the payment.
You do not receive an event for it and there is nothing to implement — but note that the Connect app fails a payment that is never presented after about ten seconds, so an integration that keeps its Activity permanently stopped will not be able to take these payments.
PaymentState fields
| Field | Description |
|---|---|
id | The payment's payment_id. |
outcome | The result of the payment: succeeded, failed or cancelled. |
amountMinor | The payment amount in minor units. |
currency | The currency of the payment, e.g. EUR. |
clientId | This field is currently null |
PrintResult fields
| Field | Description |
|---|---|
paymentId | The id of the payment the receipt belongs to. |
recipient | CUSTOMER or MERCHANT. |
outcome | The result of the print operation: Ok, OutOfPaper, etc. See printing receipts for the full list. |
requestId | The id of the print operation, as returned by printReceipt. |
On-device outcomes are authenticated by the SDK, making them safe to act on in the UI. For business operations, we strongly recommend verifying the payment's final status against the Rootline API from your backend.
Initiating a payment from your app
To initiate a payment from your app you need to call the Rootline Payments API as explained in the accept in-person payments guide.
On the payment device the Connect SDK automatically brings the Connect app to the foreground to complete the payment, and returns your app to the foreground afterwards.
The payment status is communicated directly to your app through the onPaymentState handler. Additionally, Rootline sends payment.* webhooks to your configured webhook endpoint and you can use the GET /payments/{payment_id} API to retrieve the status.
Make sure you do not include your Rootline API key in your app, and follow the security best practices.
Printing receipts
A PaymentState carries the payment's id, so when a payment succeeds you can ask Connect to print its receipt. It's fire-and-forget; the outcome arrives durably on onPrintResult.
PrintResult.outcome is a sealed PrintOutcome with these cases (an unrecognized wire value maps to Unknown(raw)):
| Outcome | Meaning |
|---|---|
Ok | The receipt printed successfully. |
OutOfPaper | The printer is out of paper. |
Overheated | The print head is too hot to print right now. |
LowVoltage | Not enough power to drive the printer. |
NoPrinter | The device has no printer (e.g. an A77/A35 without the printer module). |
NotFound | No payment with that id is known to Connect, so there was nothing to print. |
Error | The request was invalid (e.g. an unknown recipient) or the printer reported a malfunction / unknown failure. |
The result is correlated back to your request by requestId — the id printReceipt returned, echoed on PrintResult.requestId. Unlike paymentId + recipient, it stays unique across reprints of the same receipt, so it is also the key that tells a redelivered outcome apart from a genuine reprint's.
App design considerations
When building your own app to run on the payment device, keep the following device constraints in mind:
Build for kiosk mode
Your app runs in kiosk mode. The Android navigation bar is not available. You should provide in-app navigation for every screen the user needs to reach.
Device management via Android system settings
The payment device user does not have access to the Android system settings. You must build in explicit ways for your users to manage the device. You can do this by launching the relevant Android system settings screens directly from your app:
- Wi-Fi: launch
ACTION_WIFI_SETTINGSso the user can join a wireless network. - LAN / Ethernet: launch
ACTION_WIRELESS_SETTINGSso the user can configure a wired connection. - Bluetooth: launch
ACTION_BLUETOOTH_SETTINGSso the user can pair Bluetooth devices.
Use your own logging service
Your app should ship its logs to your own remote logging / observability service.