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

  1. On startup, your app creates the RootlineConnect client, registers the handlers and finally calls connect.start().
  2. Your app starts a payment. Your backend sends a POST /payments request to Rootline with the payment_rails.payment_device_id.
  3. Rootline initiates the payment on the device, the device acknowledges, and Rootline responds with checkout_status=open.
  4. The Connect SDK automatically foregrounds the Rootline Connect app, displaying the payment screen to the customer.
  5. The customer presents their card, the payment finalizes and the result is displayed.
  6. The Connect app sends the result to Rootline's servers and to your app. The onPaymentState handler delivers the result in it.outcome.
  7. 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.

dependencies { implementation("com.rootline.connect:connect-sdk:1.0.2") }

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 SDKMinimum Connect appNotes
1.0.0 – 1.0.11.0.0Works with every released Connect app.
1.0.21.0.6Presents 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.

Check your terminals before upgrading the SDK

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.

class CheckoutActivity : ComponentActivity() { private val connect = RootlineConnect(this) .onRuntimeStarted { /* it.success / it.reason */ } .onConnectionState { /* it.connected */ } .onPaymentState { /* it.id, it.outcome, it.amountMinor, it.currency, it.clientId */ } .onPrintResult { /* it.requestId, it.paymentId, it.recipient, it.outcome */ } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) connect.start() // boots the runtime and registers this app as the reply target } }

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.

onRuntimeStarted { started -> if (started.success) { /* Connect is ready — safe to expect payments */ } else { /* boot failed; started.reason is a coarse failure category */ } }

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 — eventFieldsMeaning
onRuntimeStartedRuntimeStartedsuccess, reasonThe runtime finished booting or failed, with a coarse reason.
onConnectionStateConnectionStateconnected, reasonThe terminal's connection to Rootline became reachable/unreachable.
onPaymentStatePaymentStateid, outcome, amountMinor, currency, clientIdA payment reached a terminal outcome.
onPrintResultPrintResultpaymentId, recipient, outcome, requestIdThe result of a receipt print you requested.
Handlers only fire while your Activity is started

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.

Presenting a payment started elsewhere

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

FieldDescription
idThe payment's payment_id.
outcomeThe result of the payment: succeeded, failed or cancelled.
amountMinorThe payment amount in minor units.
currencyThe currency of the payment, e.g. EUR.
clientIdThis field is currently null

PrintResult fields

FieldDescription
paymentIdThe id of the payment the receipt belongs to.
recipientCUSTOMER or MERCHANT.
outcomeThe result of the print operation: Ok, OutOfPaper, etc. See printing receipts for the full list.
requestIdThe id of the print operation, as returned by printReceipt.
Best practice: verify from your backend

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.

Do not expose your Rootline API key

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.

val requestId = connect.printReceipt(paymentId, ReceiptRecipient.CUSTOMER) // or ReceiptRecipient.MERCHANT // the matching outcome arrives with PrintResult.requestId == requestId

PrintResult.outcome is a sealed PrintOutcome with these cases (an unrecognized wire value maps to Unknown(raw)):

OutcomeMeaning
OkThe receipt printed successfully.
OutOfPaperThe printer is out of paper.
OverheatedThe print head is too hot to print right now.
LowVoltageNot enough power to drive the printer.
NoPrinterThe device has no printer (e.g. an A77/A35 without the printer module).
NotFoundNo payment with that id is known to Connect, so there was nothing to print.
ErrorThe 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_SETTINGS so the user can join a wireless network.
  • LAN / Ethernet: launch ACTION_WIRELESS_SETTINGS so the user can configure a wired connection.
  • Bluetooth: launch ACTION_BLUETOOTH_SETTINGS so the user can pair Bluetooth devices.

Use your own logging service

Your app should ship its logs to your own remote logging / observability service.