Integrate your Android POS app with the Tyro Tap to Pay app.
- 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
- Your POS app calls
TapToPayClientmethods. - For foreground operations (pairing, transactions), the client launches the Tap to Pay app.
- Tap to Pay processes the request and returns a transaction result.
- Your app handles the result and continues.
Only supported in production. These are the steps for a merchant to authorise your POS to call the Tap to Pay app:
- 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]. - The merchant signs into the Tyro Integration Portal with a System Admin role.
- The merchant authorises the POS for every MID that will use Tap to Pay.
- Your POS receives or retrieves confirmation.
- 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.
dependencies {
implementation("com.tyro:taptopay-app-client:<VERSION>")
}import com.tyro.taptopay.mpoc.client.api.TapToPayClient
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Create singleton instance
TapToPayClient.createInstance(applicationContext)
}
}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 */ }
}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.
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.
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)
}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.
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)")
}
}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")
}
}Enable or disable tactile feedback:
client.toggleHapticFeedback(enabled = true)val version = client.getSdkVersion() // e.g., "1.2.3"After Tap to Pay processes a transaction:
| Status | Meaning | Action |
|---|---|---|
TXN_SUCCESS | Payment approved | Proceed with fulfilment |
TXN_DECLINED | Card declined | Prompt for another card |
TXN_FAILED | Transaction failed (generic) | Show error, retry or fall back |
TXN_CANCELLED | User cancelled | Prompt user to try again |
TXN_TIMEOUT | Transaction timed out | Network issue; retry |
TXN_CARD_EXPIRED | Card expired | Ask for a different card |
TXN_ONLINE_ERROR | Connection lost during authorisation | Retry or save for later |
TXN_OFFLINE_DECLINE | Offline decline | Try a different card |
TXN_NO_APP_ERROR | Tap to Pay not installed | Direct the user to the Play Store |
TXN_PERMISSIONS_ERROR | Missing permissions | Check NFC/location permissions |
- App not installed: catch
TapToPayAppNotInstalledExceptionand direct the user to the Play Store. - Version mismatch:
UNSUPPORTED_TAPTOPAY_APP_VERSIONinPairingResult— update Tap to Pay. - Permissions: check
INIT_REQUEST_PERMISSIONS_ERRORand request missing permissions. - Network: retry with exponential backoff on
INIT_CONNECTION_ERRORorTXN_ONLINE_ERROR.
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.