Rabo Tap to Pay is Rabobank’s standalone app to accept contactless card payments directly on Android and/or iOS devices, No external hardware or SmartPin integration is required. The app turns a compatible smartphone or (Android) tablet with an NFC chip into a payment terminal, allowing merchants to process transactions without additional equipment. It is linked to the cash register system of a partner, enabling seamless integration with existing point-of-sale setups. Rabo Tap to Pay is part of the Rabo Smart Pay platform, offering merchants a flexible and integrated payment solution.
This is best suited for:
- Business customers of Rabobank (SMEs and large businesses)
- Customers using a cash register system from a connected partner
- Users with a compatible iPhone (XS or newer) or an Android device with NFC and Google Play Services
Device Compatibility
- iPhone Requirements:
- To use Rabo Tap to Pay on iPhone, iPhone XS or newer is required.
- iPads are not supported.
- Android Requirements:
- Apps that monitor or record screen activity must be disabled, as they interfere with PIN entry.
- Devices must have:
- NFC capabilities.
- Google Mobile Services (GMS) installed.
Using the app
- Ensure the Rabo Tap to Pay App is installed on the merchant device.
- Tap to Pay requires:
- The latest software version of iOS.
- Android version 13 or above
- For optimal stability and security, you should Initiate the payment flow via a deep link. This helps prevent app termination due to inactivity or OS-level background restrictions.
- Enable Location sharing (required) for transaction security.
A security token may be reactivated on the device once per day or less. This process is occasionally visible to the user and typically completes within 20 seconds.
- For troubleshooting and known issues, refer to the Tap to Pay Support page (in Dutch)
Refunds and return payments (Retourpinnen) are not yet supported in Rabo Tap to Pay.
App to App connection
Request:
You can initiate a payment from a third-party app using a special URL. The following data can be included:
| Var | Format | Description |
|---|---|---|
| id | string | Mandatory, unique value that can be used by the merchant to identify the transaction |
| amount | int | Mandatory, amount in cents |
| callbackUrl | string | Mandatory when integrating using URL. The URL is used to provide status feedback |
| showResult | boolean | Optional, shows result screen (default = true) |
After the payment is complete, you receive feedback with the status of the payment with the following fields:
| Var | Format | Description |
|---|---|---|
| id | string | As provided by the third-party app |
| amount | int | As provided by the third-party app |
| state | string | Payment status (see below) |
| transactionId | string | Transaction ID |
| terminalId | string | Terminal ID |
| AID | string | AID |
| truncatedPan | string | Card number |
| authCode | string | Authorization code |
| errorCode | string | Error code |
| StatusMessage | Description |
|---|---|
| APPROVED | payment is successful |
| DECLINED | payment failed |
| CANCELLED | the retailer cancelled payment |
| INVALID | a mandatory field is missing |
| IN_PROGRESS | another payment is already in progress |
| INCORRECT_PIN | the entered PIN code is incorrect |
| INSUFFICIENT_FUNDS | the balance is insufficient |
| UNKNOWN_ERROR | an unknown error occurred |
| INIT_FAILED | tap to pay is not configured, open the Rabo Tap to Pay app |
Initiating a payment using URL (iOS)
Request
let url = URL(string: "rabotaptopay://payment")
if let url = url, let urlComponents = NSURLComponents(url: url, resolvingAgainstBaseURL: false) {
var parameters : [URLQueryItem] = []
parameters.append(URLQueryItem(name: "id", value: "abcdefg"))
parameters.append(URLQueryItem(name: "amount", value: "1234"))
parameters.append(URLQueryItem(name: "callbackUrl", value: "thisappscheme://finished"))
urlComponents.queryItems = parameters
if let compiledCallbackUrl = urlComponents.url {
if UIApplication.shared.canOpenURL(compiledCallbackUrl) {
// Perform the operation on Rabo Tap to Pay by calling the Rabo Tap to Pay URL scheme + parameters
UIApplication.shared.openURL(compiledCallbackUrl)
} else {
// If application is not installed: display an error message
let alertController = UIAlertController(title: "Not installed", message: “Rabo Tap to Pay
application not installed, please install and try again.", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
}
}
The scheme thisappscheme:// must also be registered in the app’s Info.plist.
Response
func application(_ app: UIApplication, open url: URL, options:
[UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false),
urlComponents.host == "finished" {
// Move the URL query paramets to a String dictionary
var parameters : [String:String] = [:]
if let queryItems = urlComponents.queryItems {
for item in queryItems {
if let value = item.value {
parameters[item.name] = value;
}
}
}
let id = parameters["id"]
let amount = parameters["amount"]
let state = parameters["state"]
let transactionId = parameters["transactionId"]
let terminalId = parameters["terminalId"]
let AID = parameters["AID"]
let truncatedPan = parameters["truncatedPan"]
let authCode = parameters["authCode"]
let errorCode = parameters["errorCode"]
// Handle transaction with above parameters
return true
}
}
Initiating payment (Android)
Request
final Intent intent = new Intent();
val id: String = [your id]
val amount: Int = [your amount in cents]
intent.setAction("nl.rabobank.taptopay.PAY")
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra("id", id)
intent.putExtra("amount", amount)
intent.putExtra("showResult", true)
if (deeplink){
val uri = Uri.parse("rabotaptopay://payment?id=" + id +"&amount="+ amount +
"&showResult=true" + “&url=[your result url]“)
val mapIntent = Intent(Intent.ACTION_VIEW, uri)
startActivity(mapIntent)
}else {
startActivityForResult(intent, [your request code])
}
Response
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == [your result code]) {
when (resultCode) {
Activity.RESULT_OK -> {
val paymentFinishedFragment = PaymentFinishedFragment()
val id = data.getStringExtra("id")
val amount = data.getIntExtra("amount", 0)
val state = data.getStringExtra("state")
val transactionId = data.getStringExtra("transactionId")
val AID = data.getStringExtra("AID")
val truncatedPan = data.getStringExtra("truncatedPan")
val authCode = data.getStringExtra("authCode")
val errorCode = data.getStringExtra("errorCode
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}