Skip to content
Last updated

Integration guide for Android POS app

Integrate your Android POS app with the Tyro Tap to Pay app.

Prerequisites

  • Your native Android POS app (minimum version: Android 12 / API 31)
  • Tyro Tap to Pay installed on the merchant's device
  • Location permission is granted for the Tap to Pay app
  • A valid Tyro POS client ID (provided by Tyro)
  • A single client library (TapToPayClient) handles both activity launches and headless commands

How app-to-app integration works

  1. Your POS app calls TapToPayClient methods.
  2. For foreground operations (pairing, transactions), the client launches the Tap to Pay app.
  3. Tap to Pay processes the request and returns a transaction result.
  4. Your app handles the result and continues.

Implement merchant authorisation

Only supported in production. These are the steps for a merchant to authorise your POS to call the Tap to Pay app:

  1. Your POS generates and sends the merchant the unique Tyro Integration Portal URL, for example https://integrate.tyro.com/embedded-payments?posId=[client_id]&posReference=[pos_reference].
  2. The merchant signs into the Tyro Integration Portal with a System Admin role.
  3. The merchant authorises the POS for every MID that will use Tap to Pay.
  4. Your POS receives or retrieves confirmation.
  5. Your POS can access the Embedded Payments API, including transaction results from the authorised MID(s).

This is the same Integration Portal mechanism used for account authorisation on the SDK path — the merchant authorises your POS for a MID once, regardless of which integration model you're using.

Set up the client

1. Add the client SDK to your build file

dependencies {
    implementation("com.tyro:taptopay-app-client:<VERSION>")
}

2. Initialise in Application.onCreate()

import com.tyro.taptopay.mpoc.client.api.TapToPayClient

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // Create singleton instance
        TapToPayClient.createInstance(applicationContext)
    }
}

3. Register result handlers in Activity.onCreate()

In your payment activity, register handlers before you start any transactions:

import com.tyro.taptopay.mpoc.client.api.TapToPayClient
import com.tyro.taptopay.sdk.api.data.TransactionStatus

class PaymentActivity : AppCompatActivity() {
    private lateinit var client: TapToPayClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_payment)

        client = TapToPayClient.instance

        // REQUIRED: Register result handler before any transactions or pairing
        client.registerTransactionResultHandler(this) { result ->
            when (result.status) {
                TransactionStatus.TXN_SUCCESS -> {
                    showSuccess("Payment approved")
                    println("Approved: $${result.detail?.amount?.toLong()?.div(100)}")
                }
                TransactionStatus.TXN_DECLINED -> {
                    showError("Card declined")
                }
                TransactionStatus.TXN_CANCELLED -> {
                    showMessage("Payment cancelled by user")
                }
                else -> {
                    showError("Payment failed: ${result.errorMessage}")
                }
            }
        }
    }

    private fun showSuccess(msg: String) { /* your UI */ }
    private fun showError(msg: String) { /* your UI */ }
    private fun showMessage(msg: String) { /* your UI */ }
}

Core operations

1. Pair your POS with Tap to Pay (once)

Call this once to establish trust between your POS and Tap to Pay.

fun onPairClicked() = lifecycleScope.launch(Dispatchers.Main.immediate) {
    val result = client.pairWithTapToPayApp(posClientId = "YOUR-POS-CLIENT-ID")

    when (result) {
        is PairingResult.TapToPayAppPairingSuccess -> {
            showMessage("Paired successfully! Ready to process payments.")
        }
        is PairingResult.TapToPayAppPairingFailed -> {
            val reason = result.reason  // USER_CANCELLED, PAIRING_FAILED, etc.
            val msg = result.errorMessage ?: reason.toString()
            showError("Pairing failed: $msg")
        }
    }
}

What happens:

  • Tap to Pay shows a login screen.
  • The merchant logs in with their Tyro account with the System Admin permisison.
  • Trust is established; future transactions won't require re-login.

2. Set POS info

Tell Tap to Pay about your POS. This shows up in receipts and Tyro's reporting.

fun setupPosInfo() {
    val posInfo = PosInfo(
        posName = "Register 1",
        posVendor = "My POS Software Inc",
        posVersion = "2.1.0",
        siteReference = "STORE-456"
    )
    client.setPosInfo(posInfo)
}

If you don't set POS info here, you must pass it with each transaction request instead.

3. UX customisation (optional)

Control where the NFC tap icon appears on the present card screen — for example a small device 1x3 grid vs a large device 3x3 grid. See Configure Tap to Pay UX Options for more details.

fun setTapZone() {
    val uiOptions = TapToPayUxOptions.Builder()
        .setTapZone(
            TapZone.Large(
                NfcPosition.TopRight,
                DeviceOrientation.PORTRAIT
            )
        )
        .build()

    client.setTapToPayUxOptions(uiOptions)
}

4. Start a transaction

Process a card-present transaction.

fun onPayClicked(amountCents: Int, orderReference: String) {
    val request = TransactionRequest(
        type = TransactionType.PURCHASE,
        amountInCents = amountCents,
        reference = orderReference
    )

    // Result comes back via the registered handler
    client.startTransaction(this, request)
}

The result comes back via the transaction handler already registered in onCreate() — result.status will be one of the transaction status codes below, with full detail available in result.detail.

5. Cancel a transaction (optional)

If the transaction is still in progress, cancel it.

fun onCancelClicked() = lifecycleScope.launch {
    val success = client.cancelTransaction()
    if (success) {
        showMessage("Transaction cancelled")
    } else {
        showError("Could not cancel (may already be complete)")
    }
}

6. Send a digital receipt (optional)

Email a receipt to the customer.

fun onSendReceiptClicked(transactionId: String, email: String) = lifecycleScope.launch {
    val success = client.sendDigitalReceipt(transactionId, email)
    if (success) {
        showMessage("Receipt sent to $email")
    } else {
        showError("Could not send receipt")
    }
}

Other operations

Haptic feedback

Enable or disable tactile feedback:

client.toggleHapticFeedback(enabled = true)

Getting the SDK version

val version = client.getSdkVersion()  // e.g., "1.2.3"

Transaction status codes

After Tap to Pay processes a transaction:

StatusMeaningAction
TXN_SUCCESSPayment approvedProceed with fulfilment
TXN_DECLINEDCard declinedPrompt for another card
TXN_FAILEDTransaction failed (generic)Show error, retry or fall back
TXN_CANCELLEDUser cancelledPrompt user to try again
TXN_TIMEOUTTransaction timed outNetwork issue; retry
TXN_CARD_EXPIREDCard expiredAsk for a different card
TXN_ONLINE_ERRORConnection lost during authorisationRetry or save for later
TXN_OFFLINE_DECLINEOffline declineTry a different card
TXN_NO_APP_ERRORTap to Pay not installedDirect the user to the Play Store
TXN_PERMISSIONS_ERRORMissing permissionsCheck NFC/location permissions

Error handling

  1. App not installed: catch TapToPayAppNotInstalledException and direct the user to the Play Store.
  2. Version mismatch: UNSUPPORTED_TAPTOPAY_APP_VERSION in PairingResult — update Tap to Pay.
  3. Permissions: check INIT_REQUEST_PERMISSIONS_ERROR and request missing permissions.
  4. Network: retry with exponential backoff on INIT_CONNECTION_ERROR or TXN_ONLINE_ERROR.

Support and next steps

Contact Tyro to receive your unique posClientId for production use, and to have your account set up in UAT with test credentials. Work with Tyro's integration team to verify your manifest setup and dependencies, test pairing and transactions in UAT, resolve version compatibility issues, and optimise NFC tap zones for your UI.