Integration guide

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

  1. Ensure the Rabo Tap to Pay App is installed on the merchant device.
  2. Tap to Pay requires:
    • The latest software version of iOS.
    • Android version 13 or above
  3. 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.
  4. 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.

  5. 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:

VarFormatDescription
idstringMandatory, unique value that can be used by the merchant to identify the transaction
amountintMandatory, amount in cents
callbackUrlstringMandatory when integrating using URL. The URL is used to provide status feedback
showResultbooleanOptional, shows result screen (default = true)

After the payment is complete, you receive feedback with the status of the payment with the following fields:

VarFormatDescription
idstringAs provided by the third-party app
amountintAs provided by the third-party app
statestringPayment status (see below)
transactionIdstringTransaction ID
terminalIdstringTerminal ID
AIDstringAID
truncatedPanstringCard number
authCodestringAuthorization code
errorCodestringError code

StatusMessageDescription
APPROVEDpayment is successful
DECLINEDpayment failed
CANCELLEDthe retailer cancelled payment
INVALIDa mandatory field is missing
IN_PROGRESSanother payment is already in progress
INCORRECT_PINthe entered PIN code is incorrect
INSUFFICIENT_FUNDSthe balance is insufficient
UNKNOWN_ERRORan unknown error occurred
INIT_FAILEDtap 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)

}