Android
The Tap on Phone Application (ToPP) is expected to be used as described in the Data Flow diagram using an SDK provided by Geopagos with its own documentation.
Transaction flow
- Initialize the SDK with the application ID provided by Geopagos
- Request application data from the ToPP app — returns the device and app metadata required to create the session
- Create a session intent from your backend with that metadata — the API returns the encrypted session payload
- Start the transaction by passing the encrypted session to the SDK — the SDK launches the ToPP app to handle the payment
- Upload the cardholder signature from your backend, only when the transaction requires it
- Verify the transaction from your backend before showing a success message to the user
- Release the SDK when you no longer need it
Prerequisites
Before getting started, ensure the following requirements are met:
- A physical device that meets the general device requirements
- The ToPP application installed on the device. It is provisioned by Geopagos for each tenant — contact the Geopagos team to obtain it and the distribution instructions for your target devices
- An application ID provided by Geopagos, used to initialize the SDK
- A valid authorization token for the Geopagos API — see Authentication for details on how to generate it
Setup
Add the Geopagos Maven repository to your buildScript repositories declaration:
repositories {
maven { url 'https://nexus.devops.geopagos.com/repository/android-geopagos_public/' }
}
And the SDK to your module dependencies:
dependencies {
implementation "com.geopagos.payments.taponphoneinvocation:topInvocationSdk:$topInvocationSdkVersion"
}
Where topInvocationSdkVersion is the SDK version you want to integrate. The latest version is 3.0.3.
Download the Invocation SDK API documentationInitialization
First of all, SDK initialization is required:
TapOnPhoneInvocationSdk.init({CONTEXT}, {APPLICATION_ID})
Where:
- CONTEXT is the ApplicationContext.
- APPLICATION_ID is the application ID provided by Geopagos, which may include a suffix to identify different build types.
init throws SDKAlreadyInitializedException if the SDK is already initialized and release() was not
called in between. If your integration may reach this call more than once, catch the exception to make it idempotent.
Installation verification
Installation verification lets the merchant confirm that the ToPP installation on their device is the genuine one, and not a phishing or otherwise malicious app impersonating it. It is independent of the transaction flow: the merchant can trigger it at any time, without a session intent.
This flow must be implemented by the integrating application. Expose it as an action the merchant can reach on demand — it is required for compliance.
Create an Activity Result Launcher with the function getInstallationVerificationContract()
val verifyInstallationActivity = registerForActivityResult(
TapOnPhoneInvocationSdk.getInstallationVerificationContract()) { result ->
handleVerificationResult(result)
}
Then launch it. SdkInstallationVerificationIntent takes no arguments:
verifyInstallationActivity.launch(SdkInstallationVerificationIntent())
The ToPP app takes over from here: it asks the merchant for the verification code and displays the outcome of the
verification on its own screens. The contract returns an InstallationVerificationSdkResult, which is either Success
or Error and carries no additional data.
Request application data
To get Tap On Phone with PIN Application information you simply have to call:
TapOnPhoneInvocationSdk.requestApplicationData({CALLBACK}, {AUTH_TOKEN})
Where:
- CALLBACK is a function that receives
ApplicationDataSdkResultobject as a parameter. This object represents the outcome of the request and can be either:Success: contains the necessary data to create a Session Intent.Error: contains a specific error type that needs to be handled appropriately.
- AUTH_TOKEN is an Authorization token required to interact with custom services by the ToPP. To generate this token, please refer to Authentication.
ApplicationDataSdkResult.Success
| Property | Type | Description |
|---|---|---|
deviceId | String | Unique identifier for the device. Used as the client fingerprint when creating the session intent |
versionName | String | Version of the installed ToPP app |
applicationName | String | Name of the installed ToPP app |
sdkVersion | String? | Version of the Transaction SDK embedded in the ToPP app. Send it only when present |
operatingSystem | String? | Operating system of the device. Send it only when present |
These are the values your backend needs to build the client_id of the session intent request. See
Get session intent.
ApplicationDataSdkResult.Error
Besides the error type, Error also exposes deviceId, which may be null. Log it — it is the identifier the Geopagos
support team needs when the request fails before a session exists.
The error property is an ApplicationDataSdkError:
| Error | Description |
|---|---|
Connection | The connection could not be established, possibly due to a poor network connection |
DeveloperSettingsOn | The device has developer settings enabled |
DeviceIsBlocked | The device has been blocked |
Inaccessible | The ToPP service is unreachable, either because the app is not installed or because binding to the service failed |
Internal | An internal SDK error occurred |
KeyStore | The Android Keystore could not be accessed or used |
MessageNotSupported | The message sent to the ToPP app is unknown or not supported |
SecurityError | A security check failed |
Timeout | The ToPP app took too long to respond |
Unknown | The cause of the failure could not be determined |
UpdateRequired | The ToPP app must be updated before proceeding |
SecurityError, DeviceIsBlocked, Internal, Connection and KeyStore also carry an errorCode with more detail
about the failure. The remaining types carry no extra data.
Handling the Result
Both variants are delivered to the callback you pass to requestApplicationData:
TapOnPhoneInvocationSdk.requestApplicationData({ result ->
when (result) {
is ApplicationDataSdkResult.Success -> createSessionIntent(result)
is ApplicationDataSdkResult.Error ->
Log.e(TAG, "Application data request failed: ${result.error} - deviceId = ${result.deviceId}")
}
}, AUTH_TOKEN)
On first usage if ToPP application is not loaded in memory this callback may have a delay around 5 seconds because of the security checks that are performed on first usage. You may receive a timeout on first usage, if this happen we recommend you to retry the request at least once.
To ensure a smooth transaction flow, it is recommended to initialize the SDK (TapOnPhoneInvocationSdk.init) and request the application data (TapOnPhoneInvocationSdk.requestApplicationData) as early as possible.
You can pass null for the AUTH_TOKEN in requestApplicationData if the user is not logged in yet.
This approach helps to reduce any delays or interruptions during the transaction process.
You still need to request the application data (passing the AUTH_TOKEN) when starting the transaction in case the app data has changed (e.g. the ToPP app was updated). In case the app data hasn't changed, the response will be immediate.
Get session intent
Once you have the ApplicationDataSdkResult you should use it to get the Session Intent.
The session intent is generated with a request to https://[API_ENDPOINT]/api/v4/payments/tap-to-phone, built from the
ApplicationDataSdkResult.Success values plus the billing details of the transaction.
This request MUST be performed by a Third Party Backend when a Third Party App requests it.
This request must have a header Authorization with a valid token provided to the Tenant. To generate this token, please refer to Authentication.
The most important part of the request is:
{
"client_id": {
"application_name": "geopagos",
"application_version": "1.0.0",
"fingerprint_id": "61:A2:0B:70:70:D2:DB:43:52:61:CB:75:E5:04:53:40:A8:38:7C",
"sdk_version": "1.2.3",
"operating_system": "Android"
}
}
Every value comes from ApplicationDataSdkResult.Success: applicationName, versionName and deviceId map to
application_name, application_version and fingerprint_id respectively. Send sdk_version and operating_system
only when the SDK returns them, since both are nullable.
If all provided data are valid the endpoint will answer a JSON object with the created session id and the encrypted session intent.
The values encrypted_data, hash and encrypted_key are required to start a transaction, and
reference_number is required to verify it afterwards.
Any support needed to track issues will require the sessionId linked to the session that causes the issue. Please save this value for further assistance.
For the full request and response schema see the API reference.
Start transaction
Once you have the Session Intent to start the transaction, you need to create an Activity Result Launcher with the function getTransactionContractWithToken()
val transactionActivity = registerForActivityResult(
TapOnPhoneInvocationSdk.getTransactionContractWithToken()) { result ->
handleResult(result)
}
Then launch the transaction passing the session intent
transactionActivity.launch(
SdkSessionWithTokenIntent(SESSION_INTENT, AUTH_TOKEN)
)
Where:
- SESSION_INTENT is an instance of
SdkEncryptedSessionIntent, which wraps the encrypted data, the hash and the encrypted key. - AUTH_TOKEN is an Authorization token required to interact with custom services by the ToPP. To generate this token, please refer to Authentication.
Both types map directly to the values returned by the session intent request:
SdkSessionWithTokenIntent(
SdkEncryptedSessionIntent(
sessionIntent = session.encryptedData,
hash = session.hash,
encryptedKey = session.encryptedKey
),
jwtToken = AUTH_TOKEN
)
TapOnPhoneInvocationSdkResult.Success
It exposes a data property of type TapOnPhoneInvocationSdkSuccessData:
| Property | Type | Description |
|---|---|---|
cvmResult | TapOnPhoneInvocationSdkCvmResult | How the cardholder was verified during the transaction |
authorizationCode | String? | Authorization code returned by the Online Processor. Required to verify the transaction |
cvmResult can take the following values:
| Value | Description |
|---|---|
NO_CVM | No cardholder verification was needed |
SIGNATURE | The cardholder must be requested to sign. See Upload the signature |
PIN | A PIN was requested and entered |
CONFIRMATION_CODE_VERIFIED | A confirmation code was verified |
TapOnPhoneInvocationSdkResult.Error
It exposes an error property. Every TapOnPhoneInvocationSdkError carries an errorCode and an errorDescription,
and TransactionAborted additionally carries errorHistory with the errors that occurred before the transaction was
aborted. Log these values — they are what the Geopagos support team needs to trace the transaction.
| Error | Description |
|---|---|
InvalidTransactionIntent | The session intent was malformed or expired |
TransactionDenied | The transaction was declined |
TransactionAborted | The transaction was interrupted or timed out. errorHistory holds the sequence of events |
ConfigurationError | The ToPP app configuration is invalid |
SecurityError.UpdateRequired | The ToPP app must be updated before proceeding |
SecurityError.Other | A security check failed (e.g. device integrity) |
InternalError | An unexpected internal error occurred |
NfcNotAvailable | NFC is not enabled or not available on the device |
EULANotAccepted | The user has not accepted the end user license agreement in the ToPP app |
Handling the Result
errorCode and errorDescription are available on every error, so you only need to narrow the type for the cases your
integration treats differently:
fun handleResult(result: TapOnPhoneInvocationSdkResult) {
when (result) {
is TapOnPhoneInvocationSdkResult.Success -> {
if (result.data.cvmResult == TapOnPhoneInvocationSdkCvmResult.SIGNATURE) {
uploadSignature()
}
// Verify the transaction before showing an approved message to the user
confirmTransaction(result.data.authorizationCode)
}
is TapOnPhoneInvocationSdkResult.Error -> {
val error = result.error
Log.e(TAG, "Transaction failed: ${error.errorDescription} (${error.errorCode})")
if (error is TapOnPhoneInvocationSdkError.TransactionAborted) {
Log.e(TAG, "Error history: ${error.errorHistory}")
}
}
}
}
Upload the signature
When cvmResult is SIGNATURE, the cardholder must be requested to sign. Capture the signature in your app and upload
it before verifying the transaction.
Call https://[API_ENDPOINT]/api/v4/payments/:ref_number/signature sending the signature as a Base64-encoded image.
The ref_number param refers to the transaction reference number. And it is obtained when you Get a session intent.
For the full request and response schema see the API reference.
Transaction verification
When a transaction result is Success, it is a requirement to verify the transaction status via API prior to show the "approved" message to your user. This is to ensure the transaction state and avoid fraud attempts.
Call https://[API_ENDPOINT]/api/v4/payments/:ref_number/confirm sending the authorizationCode obtained from
TapOnPhoneInvocationSdkResult.Success.
The ref_number param refers to the transaction reference number. And it is obtained when you Get a session intent.
For the full request and response schema see the API reference.
Release
When you no longer need to use the SDK, you must call
TapOnPhoneInvocationSdk.release()
This will release all resources used by the SDK. All objects retained after release() was called will no longer be valid. You can always init the SDK (as seen previously in this setup guide) to use it again after it was released.