Skip to main content

React Native

The @geopagos/react-native-softpos-sdk package wraps the native ToPP (Tap on Phone with PIN) SDK for React Native applications. It exposes a TypeScript API to initialize the SDK, retrieve device and application metadata, and execute sale transactions through the certified standalone ToPP app.

Android only

This SDK targets Android exclusively. iOS is not supported.

Transaction flow

The following steps apply to both Android and React Native integrations:

  1. Initialize the SDK with your application key
  2. Request application data from the standalone ToPP app — returns device and app metadata required to create the session
  3. Create a session intent via your backend calling POST /api/v4/payments/tap-to-phone with the application data — the API returns an encrypted session payload
  4. Start the transaction by passing the encrypted session to the SDK — the SDK launches the ToPP standalone app to handle the payment
  5. Verify the transaction result via your backend calling PATCH /api/v4/payments/:ref_number/confirm before showing a success message to the user

Prerequisites

Before getting started, ensure the following requirements are met:

  • React Native development environment is configured
  • React Native >= 0.72.4
  • React >= 18.2.0
  • Expo SDK 52+ (if using Expo)
  • A physical device that meets the general device requirements
  • The ToPP standalone application installed on the device (provided by Geopagos — see below)
  • An application key (applicationKey) provided by Geopagos for your integration
  • A valid authorization token for the Geopagos API — see Authentication for details on how to generate it

About the ToPP standalone app

The ToPP standalone application is a certified, separate Android app that handles the actual card reading and PIN entry. It runs independently from your app and is launched by the SDK when a transaction is started.

This application is provisioned by Geopagos for each tenant. Contact the Geopagos team to obtain the standalone app and the distribution instructions for your target devices. The SDK provides the method isStandaloneAppInstalled() to verify its presence before starting a transaction.

About the authorization token

Several SDK methods and API calls in this guide require an authToken (also referred to as a JWT or Bearer token). This is the same token throughout the entire flow — from requestAppData through the session intent creation and the transaction itself.

To learn how to generate this token, refer to the Authentication documentation.

Configuration

  1. Request access credentials
    Contact the Geopagos team to obtain a username and password for accessing the private Nexus repository where the SDK is hosted.

  2. Encode credentials in Base64
    Convert your credentials using the format {username:password}:

    echo -n 'username:password' | base64
  3. Create the .npmrc file
    In the root of your project, create a .npmrc file with the following content:

    @geopagos:registry=https://nexus.geopagos.io/repository/geo-npm-hosted-internal/
    //nexus.geopagos.io/repository/geo-npm-hosted-internal/:_auth=YOUR_BASE64_AUTH_TOKEN
    always-auth=true

    Replace YOUR_BASE64_AUTH_TOKEN with the Base64 string from step 2.

  4. Proceed with installation
    You can now install the SDK as shown in the next section.

Installation

Install the SDK in the root of your React Native project:

# Using npm
npm install @geopagos/react-native-softpos-sdk

# Using Yarn
yarn add @geopagos/react-native-softpos-sdk

Expo (managed / bare workflow)

Register the config plugin in your app.json or app.config.ts. The plugin automatically adds the GeoPagos Nexus Maven repository to android/build.gradle during expo prebuild, which is required to resolve the native ToPP SDK artifact.

{
"plugins": ["@geopagos/react-native-softpos-sdk"]
}

Bare React Native

If you are not using Expo, add the GeoPagos Nexus Maven repository to your project-level android/build.gradle:

allprojects {
repositories {
// ... other repositories
maven { url 'https://nexus.devops.geopagos.com/repository/android-geopagos_public/' }
}
}

Then run npx react-native run-android (or autolink will handle the rest during a normal build).

SDK initialization

Call softPosSdk.init(applicationKey) before performing any other SDK operation. The applicationKey is provided by Geopagos and identifies your integration.

import { softPosSdk } from '@geopagos/react-native-softpos-sdk'

await softPosSdk.init('YOUR_APPLICATION_KEY')

The SDK throws an error with code SDKAlreadyInitialized if init is called more than once without releasing first. Catch and ignore this error to make the call idempotent:

try {
await softPosSdk.init(applicationKey)
} catch (error: any) {
if (error?.code !== 'SDKAlreadyInitialized') throw error
}
tip

Initialize the SDK as early as possible in your application lifecycle, ideally when the user logs in or when the app configuration is loaded. Early initialization reduces delays when the user initiates a payment.

Check standalone app installation

The ToPP flow depends on a certified standalone application installed on the device. Before attempting a transaction, verify it is present:

const isInstalled = await softPosSdk.isStandaloneAppInstalled()

if (!isInstalled) {
// Prompt the user to install the ToPP app from the store
}

isStandaloneAppInstalled() returns true if the standalone app matching the applicationKey used during init is installed, and false otherwise.

Check NFC support

Verify that the device supports NFC before starting a transaction:

const isSupported = await softPosSdk.isNFCSupported()

if (!isSupported) {
// Show an error — this device cannot process contactless payments
}

Request application data

requestAppData retrieves metadata from the standalone ToPP app: the app name, version, and a device fingerprint. This data is required to create the session intent on your backend.

const appData = await softPosSdk.requestAppData(authToken)

Parameters

ParameterTypeDescription
authTokenstring | nullOptional. Authorization token for the ToPP service. Pass null if the user is not yet authenticated.

Return value

interface AppData {
appName: string // Name of the standalone ToPP app
versionName: string // Version of the standalone ToPP app
deviceId: string // Device fingerprint identifier
}
important

On first usage, if the ToPP standalone app is not yet loaded in memory, this call may take up to 5 seconds while security checks run. You may receive a timeout error on the first call — retry at least once before treating it as a fatal error.

It is recommended to call requestAppData early (e.g. right after init) and cache the result. When starting a transaction, call it again to detect updates to the standalone app. If the app data has not changed, the response is immediate.

Get session intent

The session intent is an encrypted payment payload that must be created by your backend before the SDK can start a transaction. Your mobile app requests it by sending the AppData fields to your server, which in turn calls the Geopagos API.

Backend call: POST /api/v4/payments/tap-to-phone

The request body must include the device and app identifiers from AppData, along with the billing details:

{
"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"
},
"billing_info": {
"currency": "ARS",
"amounts": [
{ "amount": 1500, "label": "Total" }
],
"taxes": [],
"tip": { "amount": 0 }
}
}

The application_name, application_version, and fingerprint_id values come directly from the AppData returned by requestAppData (appName, versionName, and deviceId respectively).

Response

A successful response returns a JSON:API object. The relevant fields are under data.attributes:

FieldTypeDescription
reference_numberstringUnique transaction identifier — save this for the confirmation step and for support
encrypted_datastringEncrypted session payload
hashstringIntegrity hash for the session
encrypted_keystringEncryption key for the session
important

Save the reference_number from the response. It is required to confirm the transaction after the SDK returns, and it is the primary identifier for any support requests.

See the API reference for the full request and response schema.

Start transaction

Once you have the session payload from your backend, pass it to the SDK to launch the ToPP standalone app and process the payment.

What happens on screen

When you call this method, the SDK transfers control to the ToPP standalone app. The standalone app displays its own UI to the user: it prompts the customer to tap their card on the device's NFC antenna, handles PIN entry if required, and processes the payment. Once the transaction completes (success or error), control returns to your app and the promise resolves or rejects.

Your app is in the background during this process — you do not need to build any card-reading or PIN-entry UI.

Use getTransactionContractWithToken when you have an auth token available (recommended), or getTransactionContract without one:

// Recommended: pass session data and auth token
const result = await softPosSdk.getTransactionContractWithToken(sessionData, authToken)

// Without auth token
const result = await softPosSdk.getTransactionContract(sessionData)

sessionData shape

The sessionData argument must be an object containing the three encrypted fields from the session intent response:

const sessionData = {
encrypted_data: 'eyJhbGci...',
hash: 'abc123...',
encrypted_key: 'xyz789...',
}

TransactionResult

On success, the SDK returns a TransactionResult object:

interface TransactionResult {
cvmResult: string // Cardholder verification method used (e.g. "NO_CVM", "SIGNATURE")
authorizationCode: string // Authorization code — always present, must be sent to the confirm API
}

The cvmResult field indicates how the cardholder was verified:

ValueDescription
"NO_CVM"No cardholder verification was required
"SIGNATURE"The cardholder must sign — prompt for a signature before confirming

If cvmResult is "SIGNATURE", you must collect the cardholder's signature and upload it before calling the confirm API.

Upload signature (when required)

When cvmResult is "SIGNATURE", capture the cardholder's signature (e.g. using a signature pad component) and upload it to the Geopagos API before confirming the transaction:

Backend call: POST /api/v4/payments/:ref_number/signature

{
"signature_image": "<base64-encoded image>"
}

The ref_number is the reference_number from the session intent. The signature_image field must contain the signature as a Base64-encoded image string.

After uploading the signature, proceed to the Transaction verification step.

See the API reference for the full request and response schema.

Handle errors

When a transaction fails, the SDK rejects the promise with an error object that matches the following shape:

interface TransactionError {
code: string // Raw numeric or string error code from the native SDK
message: string // Error type name (same as errorType)
errorCode: string // Detailed error code
errorType: string // Error class name — use this to branch your error handling
errorHistory?: string[] // Ordered sequence of error events; only present when errorType is "TransactionAborted"
}

Known errorType values

errorTypeDescription
TransactionAbortedTransaction was interrupted or timed out — errorHistory contains the sequence of events
TransactionDeniedCard was declined by the issuer
EULANotAcceptedUser has not accepted the end-user license agreement in the ToPP app
InvalidTransactionIntentThe session payload was malformed or expired
ConfigurationErrorSDK or standalone app configuration is invalid
SecurityErrorA security check failed (e.g. device integrity)
UpdateRequiredThe ToPP standalone app must be updated before proceeding
NfcNotAvailableNFC is not enabled or not available on the device
InternalErrorAn unexpected internal error occurred

Error handling example

try {
const result = await softPosSdk.getTransactionContractWithToken(sessionData, authToken)
// handle success
} catch (error: any) {
const errorType: string = error?.userInfo?.errorType ?? 'Unknown'
const errorHistory: string[] | undefined = error?.userInfo?.errorHistory

switch (errorType) {
case 'TransactionAborted':
// Show the errorHistory details to the user if available
console.log('Transaction aborted. History:', errorHistory)
break
case 'TransactionDenied':
// Inform the user the card was declined
break
case 'EULANotAccepted':
// Prompt the user to open the ToPP app and accept the EULA
break
case 'UpdateRequired':
// Prompt the user to update the ToPP standalone app
break
case 'NfcNotAvailable':
// Prompt the user to enable NFC in device settings
break
default:
// Generic error message for all other cases
break
}
}

Transaction verification

important

When the SDK returns a TransactionResult, you must confirm the transaction via your backend before showing a success message to the user. Skipping this step exposes the integration to fraud attempts.

Backend call: PATCH /api/v4/payments/:ref_number/confirm

Use the reference_number saved from the session intent step. The authorizationCode from TransactionResult is always present and must always be sent in the request body:

{
"authorization_code": "ABC123"
}

See the API reference for the full request and response schema.

Release

warning

Calling release() is not recommended for most integrations. Only use it if you fully understand the SDK lifecycle and have a specific reason to deallocate its resources. In normal usage the SDK manages its own lifecycle and calling release() prematurely can lead to unexpected behavior.

Call release() when the SDK is no longer needed to free all native resources:

await softPosSdk.release()

After calling release(), any SDK objects obtained previously are no longer valid. You can call init(applicationKey) again to reinitialize the SDK when needed.


Complete integration example

The following example shows the full flow using the softPosSdk singleton. It assumes your backend exposes an endpoint to create the payment session and another to confirm it.

import { softPosSdk, AppData } from '@geopagos/react-native-softpos-sdk'

async function runPayment(
applicationKey: string,
authToken: string,
amount: number,
currency: string,
): Promise<void> {
// 1. Initialize the SDK (idempotent — safe to call on every payment attempt)
try {
await softPosSdk.init(applicationKey)
} catch (error: any) {
if (error?.code !== 'SDKAlreadyInitialized') throw error
}

// 2. Check that the standalone ToPP app is installed
const isInstalled = await softPosSdk.isStandaloneAppInstalled()
if (!isInstalled) {
throw new Error('The ToPP standalone application is not installed on this device.')
}

// 3. Check NFC availability
const nfcSupported = await softPosSdk.isNFCSupported()
if (!nfcSupported) {
throw new Error('NFC is not available on this device.')
}

// 4. Request application data
const appData: AppData = await softPosSdk.requestAppData(authToken)

// 5. Create the payment session on your backend
// Your backend calls POST /api/v4/payments/tap-to-phone and returns the encrypted session
const session = await createPaymentSessionOnBackend({
appName: appData.appName,
versionName: appData.versionName,
deviceId: appData.deviceId,
amount,
currency,
})
// session = { reference_number, encrypted_data, hash, encrypted_key }

// 6. Start the transaction
const result = await softPosSdk.getTransactionContractWithToken(
{
encrypted_data: session.encrypted_data,
hash: session.hash,
encrypted_key: session.encrypted_key,
},
authToken,
)

// 7. Confirm the transaction on your backend (required before showing success)
// Your backend calls PATCH /api/v4/payments/:ref_number/confirm
await confirmPaymentOnBackend(session.reference_number, result.authorizationCode)

console.log('Payment successful. CVM:', result.cvmResult)
}

Alternative: using the React Context

The SDK also provides a React Context (SoftPosSdkProvider) that manages the SDK lifecycle and exposes hooks for use within component trees. This is an alternative to calling the singleton directly.

Wrap your component tree

import { SoftPosSdkProvider } from '@geopagos/react-native-softpos-sdk'

function App() {
return (
<SoftPosSdkProvider applicationKey="YOUR_APPLICATION_KEY">
{/* rest of your app */}
</SoftPosSdkProvider>
)
}

The provider initializes the SDK and fetches initial application data automatically.

Consume the context in a component

import { useSoftPostSdk } from '@geopagos/react-native-softpos-sdk'

function PaymentButton({ session, authToken }) {
const { isInitialized, appData, startSaleWithToken } = useSoftPostSdk()

async function handlePress() {
if (!isInitialized) return

const result = await startSaleWithToken(session, authToken)
// result: TransactionResult
}

return <Button onPress={handlePress} title="Pay" />
}

Hook return values

FieldTypeDescription
isInitializedbooleanWhether the SDK has been successfully initialized
appDataRequestedbooleanWhether the initial requestAppData call has completed
appDataAppDataApplication and device metadata from the standalone ToPP app
startSale(session) => Promise<TransactionResult>Starts a sale without an auth token
startSaleWithToken(session, jwt) => Promise<TransactionResult>Starts a sale with a JWT auth token (recommended)
important

startSale / startSaleWithToken expect the session argument in the same shape described in the Start transaction section above.