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.0") }

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 replayed when your app comes back. 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.

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.