SDK integration
Overview
In addition to the backend B2B API integration, the App2App SDK provides the client-side component necessary to complete the enrollment and payment flows from within the merchant’s mobile application. Acting as a bridge between the merchant app and the Nexi POS app (SoftPOS or mPOS), the SDK ensures a seamless and secure user experience by handling device-level interactions and deeplinks during key operations like enrollment, payment, reversal, account closure and get last transaction.
The SDK relies on the App2AppIPC class, which must be instantiated in the merchant application and configured with core parameters such as appId, merchantUsername, redirectUri, and deviceId. A key element of this setup is the onGetRequestUri callback, which connects the SDK to the previously described PAR B2B API call, allowing the app to request a request_uri from the backend. This request_uri, once returned via callback, is injected into the SDK to complete the device enrollment process in a secure and standardized way.
Once enrollment is complete, the SDK can execute operations such as payment, reversal, and get last transaction, each of which triggers the Nexi POS app via inter-process communication (IPC). Results are returned to the merchant app via deeplinks using a predefined scheme (e.g., demonexi://) and include full transaction details.
The SDK design is fully asynchronous and coroutine-compatible, ensuring smooth integration with Kotlin and proper support for Java applications. Developers must handle thread management and UI updates carefully to avoid exceptions during background-to-main thread transitions.
By combining the B2B API layer with the client-side SDK, the App2App solution provides a robust, end-to-end integration path for managing secure payment workflows between the merchant backend, frontend, and Nexi's payment infrastructure.
Scope
The technical interaction between App2App SDK and Merchant App involves the exchange of data through defined operations. In this context:
• Incoming data: refers to data that App2App SDK receives from Merchant App during various operations.
• Outgoing data: represents the data that App2App SDK sends back to Merchant App as a result of the operations.
The scope of the interaction between the app (merchant application) and the App2App SDK is primarily related to facilitating secure and seamless payment transactions.
The SDK likely serves as a bridge between the merchant's application and Nexi POS application, enabling the app to initiate, authorize, and handle payments.
To perform any operation, the app is required to instance the App2AppIPC class, needed to send developer data and then, setup the connection to Nexi POS app.
code
class App2AppIPC( internal val activity: Activity, internal val appData: AppData, internal val onGetRequestUri: ( deviceId: String, onSuccess: (String) -> Unit, onError: (String) -> Unit ) -> Unit, internal val onConnectionEvent: (ConnectionEvent) -> Unit = {} )
The App2App IPC (Inter-Process Communication) class is responsible for setting up and handling the interaction with the App2App SDK. Hence, an instance of this class is needed before starting any operation. The class requires the following parameters:
-
activity , current active activity.
-
appData , data representing the app created by developer on web portal. AppData contains:
-
appId, the id of the app. (ID CLIENT on the web portal)
-
redirectUri, the URI chosen during app creation. (once created the App, save this info, cannot be retrieved if lost, and a new app logic Is required)
-
merchantUsername, the merchant username with which any operation will be run. ( If the email is not handled, please follow this setting: noreply@(companyname).com noreply2@(companyname).com noreplyn@(companyname).com)
-
merchantId, the merchant id corresponding to the account assigned on the portal. (ID AMMINISTRATORE on the web portal)
-
deeplinkScheme, the scheme used to get back to the caller app. “demonexi” as default.
-
domain, desired service domain among INTEGRATION, STAGING, PRODUCTION.
-
onGetRequestUri (deviceId, onSuccess, onError), the callback through which it will be possible to enroll the current device during the enrollment phase.
-
onConnectionEvent(ConnectionEvent): After having created an App2AppIPC instance, the connection must be opened by calling the connectToRemoteService method. This callback will notify any connection event, allowing the caller app to continue with any operation (pay, reversal,..)
The App2AppIPC instance should be created ahead (ex: Activity/Fragment onCreate) and kept in memory, since it works with remote time sync to ensure secure network calls.
Connection Event
After having called the connectToRemoteService method, the SDK will search for one of the following apps in your device:
- Nexi POS – package it.nexi.mpos
- Nexi POS debug – package it.nexi.mpos.debug
Aiming to connect the caller app with ONE instance of the Nexi POS application. The outcome of the search will be notified using the ConnectionEvent.
Let’s explore all the possible outcomes:
-
One app is found, and the connection is secured, event type will be CONNECTED. After having received this specific event, the caller can interact with the SDK.
-
No app is found, the event type will be ERROR_APP_NOT_INSTALLED, and a service message is filled within the object. The Nexi POS app should be installed.
-
Both apps are found, the event type will be ERROR_MULTIPLE_APPS_INSTALLED, and a service message is filled within the object. One app should be removed.
Throughout the caller app lifecycle, it could be useful receiving the DISCONNECTED event. This one can occur whether the Nexi App is closed or in case of OS memory cleaning.
Having received a disconnection, it will be possible to call connectToRemoteService again to fix it up.
To recap, the callback will make sure that the caller app is kept updated about the connection status.
SDK APIs
The caller app can run the following operations:
- Payment, using the pay method.
- Revert a transaction, using the revert method.
- Close MPOS account, using the closeAccount method.
- Retrieve the last transaction, using lastTransaction method.
When calling any of them, the SDK will check if the device is enrolled, which implies a group of security checks have been executed on the device. The enrollment involves SDK internal calls, requesting the merchant application to provide the requestUri parameter.
By passing the onGetRequestUri callback, the merchant app can prepare a specific flow to use the device id and produce the requestUri. The latter must be passed to the SDK, using the onSuccess callback, to allow the SDK to perform the enrollment
SDK outcome and App outcome
All methods will return an outcome via operationListener, which is not the real outcome of the operation (payment, reversal, close account), but the outcome of the SDK task. In case of failure, for example, the listener will tell why the SDK operation has failed. In case of success, instead, the SDK will notify about having completed its task and the Nexi POS app will be opened to finalize the operation.
Happy flow:
- App calls pay method
- OperationListener notifies a success (could be used for tracking or logging events)
- The Nexi POS app is opened for running the payment through mPOS or SoftPOS
- After having completed the payment, the result is sent back via deeplink, using given deeplinkScheme and an operation-based authority (in this case payment)
Unhappy flow:
- App calls pay method
- OperationListener notifies a failure, which can be used to show an error
- The Nexi POS is not opened
Outcome and threads
The operationListener will return the outcome in a background thread, so any operation inside success and failure callback should be a legit background operation.
For example, to show a Toast, the app will have to wrap the logic inside a runOnUiThread scope.
In the same way, a LiveData implementation should handle the result using the postValue method, instead of the classic value setter method.
Any violation of background thread rules will result into an exception.
Enrollment
During payment or any App2App operation, the enrollment could be requested. In case of need, the onGetRequestUri callback will be called from the SDK and the application should gather necessary params to send a network request and get a request URI from our side.
Request
This is used to request a specific URI from the App2App service. It takes the following parameters:
Name parameter | Description | Type | Mandatory |
---|---|---|---|
thirdAppUserna meMerchant | Represents the username associated with the third-party application or merchant making the request. | String | yes |
thirdAppDeviceId | Represents the unique identifier associated with the device of the third-party application or merchant. | String | yes |
appId | Represents the application identifier associated with the third-party application. | String | yes |
merchantId | Represents the merchant identifier associated with the third-party application. | String | yes |
terminalIds | Represents a list of terminal identifiers associated with the thirdparty application or merchant. | String | yes |
Enrollment Constraints
Keep in mind that:
- After having enrolled a merchantUsername, any previous enrollment linked to the same username will be disabled.
- After having enrolled a merchantUsername with a specific terminal ID, any previous enrollment linked to the same terminal ID will be disabled.
This implies that a merchantUsername or a single terminal ID cannot be used on two devices at the same time. Therefore, in case of active enrollment on device A and a successful enrollment on device B:
- The device B will have an active session
- The device A will keep the previous one. In case of transaction on device A, it will receive a server error, easily solvable by calling the enrollment again. The session of device B will be invalidated.
Payment
The payment can be run by calling the pay method.
code
suspend fun pay( paymentData: PaymentData, operationListener: OperationListener )
The pay method takes an instance of PaymentData as an argument and an operationListener, which will propagate the operation outcome to the caller application.
code
data class PaymentData( <br> val amount: String?,<br> val callerTrxId: String?,<br> val isSendTicket: Boolean,<br> val isUrlTicket: Boolean,<br> val callerName: String?,<br> val addInfo1: String?,<br> val addInfo2: String?,<br> val addInfo3: String?,<br> val addInfo4: String?,<br> val addInfo5: String?,<br> val email: String?,<br> val sms: String?,<br> val autoClose: Boolean = false ) interface OperationListener { <br> fun onSuccess(response: A2ASDKResponse? = null) <br> fun onFailure(error: A2ASDKResponse? = null) } data class A2ASDKResponse( @SerializedName("isSuccess") val isSuccess: Boolean, @SerializedName("exception") val exception: A2AException? ) data class A2AException( @SerializedName("isServiceError") val isServiceError: Boolean, @SerializedName("errorCode") var errorCode: String? = null, @SerializedName("errorMessage") val errorMessage: String? )
In OnSuccess callback, the exception field will contain a null value.
In OnFailure callback, the exception will be filled with an A2AException instance. In case of NetworkException, the HTTP error code and reason will be sent through errorCode and errorMessage fields. For any other error, the errorMessage field will be filled with a specific error message.
PaymentData
The PaymentData is a class used for holding information related to a payment. Each property in the class corresponds to a different aspect of the payment. PaymentData class properties are detailed below:
Name parameter | Description | Type | Mandatory |
---|---|---|---|
amount | Represents the amount of reversal in cents. | String | yes |
callerTrxId | Represents the transaction ID associated with the caller | String | yes |
isSendTicket | Flag that enables the display of the send receipt button on transaction result | Boolean | yes |
isUrlTicket *** | Indicates whether the ticket is a URL link | Boolean | yes |
callerName | Represents the name of the caller initiating the reversal | String | yes |
addInfo1 | Additional information fields that can be included with the payment | String | yes |
addInfo2 | Additional information fields that can be included with the payment | String | yes |
addInfo3 | Additional information fields that can be included with the payment | String | yes |
addInfo4 | Additional information fields that can be included with the payment | String | yes |
addInfo5 | Additional information fields that can be included with the payment | String | yes |
Represents the email associated with the reversal | String | yes | |
sms | Represents the SMS information associated with the reversal | String | yes |
autoClose | Flag that hides the buttons on transaction result and allows the application to automatically go back to caller application ONLY for not retryable transactions result | Boolean | no (default false) |
*** Available only for MobilePOS
Retryable Transaction Result
A retryable transaction result occurs when the transaction can be retried. Examples:
- Transaction canceled
- Card read timeout
- Wrong pin
This result requires a user interaction, more precisely the tap on “Retry”. Hence, the autoClose flag will not affect the flow in this case.
For not retryable ones, like a successful transaction or denied transaction, if autoClose value is true, the app will show the outcome animation and then close itself automatically. The caller app will receive the transaction data via deeplink.
Since autoClose flag implies the buttons hiding and the isSendTicket implies the visualization of “send ticket” button, the usage of both together will NOT be allowed. If isSendTicket and autoClose are requested within the same session (both are true), the application will send the error “-21”.
NB: The default isSendTicket value is true for backward compatibility, so to use the autoClose feature it is mandatory to specify the value isSendTicket false.
Payment response example
demonexi://payment?amount=00000000 0500&callerTrxId=316874221216&operationType=PAYMENT&f30=00000000 0500&F42=1000000278548%20%20&f12=171433&f11=000784&f55=9F36020345910A8113BDBAE2392050 0012&CT121=PIAZZA%20ARCOLE%20100%2C%2020143%2C%20MILANO%20(MI)&f13=210901&CT120=LA %20PATENTE%20(E- MAIL%2012)&f38=362077&9F14=1100&f37=424717057296&f39=000&CT118=978&CT117=Online&CT116 =00034&CT115=Contactless&CT114=NEXI%20Core&CT112=************5291&CT111=0&acquirerId=909 517&CT119=Grazie%20e%20arrivederci&f41=02546095&f43=LA%20PATENTE%20(E- MAIL%2012)%20PIAZZA%20ARCOLE%20100%2C%2020143%2C%20MILANO%20(MI)&f42=1000000278548 %20%20&CT110=2024-09- 03T17%3A14%3A36%2B02%3A00&f04=000000000500&CT107=17%3A14&CT106=03%2F09%2F2024&CT10 5=0100&CT104=LA%20PATENTE%20(E- MAIL%2012)%20PIAZZA%20ARCOLE%20100%2C%2020143%2C%20MILANO%20(MI)&CT103=42471705729 6&CT102=A0000000041010&CT101=96&CT123=9F360203459F2608D19D309F9058D6639F37046FCA3A23 95050000008001&CT100=5291&CT109=Mastercard&CT108=Mastercard&result=0&terminalId=02546095
Reversal
The reversal can be run by calling the reversal method.
code
suspend fun revert( reversalData: ReversalData, operationListener: OperationListener )
The reversal method takes an instance of ReversalData as an argument and an operationListener, which will propagate the operation outcome to the caller application.
code
data class ReversalData( val amount: String?, <br> val timestamp: String?, <br> val callerName: String?, <br> val email: String?,<br> val sms: String?,<br> val callerTrxId: String?,<br> val terminalId: String?,<br> val isSendTicket: Boolean,<br> val isUrlTicket: Boolean,<br> val autoClose: Boolean = false<br> )
ReversalData
The ReversalData is a class used for holding information related to a reversal operation. Each property in the class corresponds to a different aspect of the payment. ReversalData class properties are detailed below:
Name parameter | Description | Type | Mandatory |
---|---|---|---|
amount | Represents the amount of reversal in cents. | String | yes |
timestamp | Represents the timestamp of the reversal | String | yes |
callerName | Represents the name of the caller initiating the reversal | String | yes |
Represents the email associated with the reversal | String | yes | |
sms | Represents the SMS information associated with the reversal | String | yes |
callerTrxId | Represents the transaction ID associated with the caller | String | yes |
terminalId | Represents the ID of the terminal | String | yes |
isSendTicket | Flag that enables the display of the send receipt button on transaction result | Boolean | yes |
isUrlTicket *** | Indicates whether the ticket is a URL link | Boolean | yes |
autoClose | Flag that hides the buttons on transaction result and allows the application to automatically go back to caller application ONLY for not retryable | Boolean | no (default false) |
*** Available only for MobilePOS
Get Last Transaction
The get last transaction action can be run by calling the getLastTransaction method.
code
suspend fun getLastTransaction( getLastTransactionData: GetLastTransactionData, operationListener: OperationListener )
The getLastTransaction method takes an instance of GetLastTransactionData as an argument and an operationListener, which will propagate the operation outcome to the caller application.
code
data class GetLastTransactionData( val callerName: String?, val terminalId: String?, val callerTrxId: String? ) interface OperationListener { fun onSuccess(response: A2ASDKResponse? = null) fun onFailure(error: A2ASDKResponse? = null) }
GetLastTransactionData
The GetLastTransactionData class is used for holding information related to the getLastTransaction operation. Class properties are detailed below:
Name parameter | Description | Type | Mandatory |
---|---|---|---|
callerName | Represents the name of the caller app | String | yes |
terminalId | Represents the merchant terminal ID | String | yes |
callerTrxId | Represents the transaction ID associated with the caller | String | yes |
The last transaction related to the given terminalId will be returned by back deeplink, containing all receipt parameters. The parameters will be returned through receipt field keys (documented in app2app specifications > SoftPOS Android > Receipt Field Keys) for both MPOS and SOFTPOS transactions.
Moreover, to maintain the same signature of payment and reversal operations, the amount field is also returned via deeplink, valued with the same content of f30 receipt field.
Coherently with transaction result, the result key will contain 0 in case of success call. In case of missing receipt error, the result key will contain the value -41.
Back Deeplink Example
demonexi://last_transaction?operationType=LAST_TRANSACTION&callerTrxId=123123&result=0&amount =000000025200&terminalType=SOFTPOS&f30=25200&f32=09517&F42=8994712%20%20%20%20%20%20 %20%20&f53=01014&f12=172617&f11=000344&CT121=VIA%20CARLO%20CATTANEO%207%2C%2087100 %2C%20COSENZA%20(CS)&ABI=38937&CT120=STUDIO%20MEDICO%20DR%20ROMANO&f38=401531&9F 14=1100&f37=430517222236&f39=000&CT118=978&CT117=Online&CT116=00034&CT115=Contactless&C T114=NEXI%20Core&CT112=***7937&CT111=2&acquirerId=909517&CT119=Grazie%20e%20arr ivederci&CURRENCY=EUR&f41=97943348&f43=STUDIO%20MEDICO%20DR%20ROMANO%20VIA%20CARL O%20CATTANEO%207%2C%2087100%2C%20COSENZA%20(CS)&f42=8994712%20%20%20%20%20%20%2 0%20&CT110=2024-10- 31T17%3A27%3A07%2B01%3A00&f03=000000&f02=5167957937&f49=978&f04=000000025200&f 28=241031&CT107=17%3A26&CT106=31%2F10%2F2024&CT105=ACQUISTO&CT104=STUDIO%20MEDICO %20DR%20ROMANO%20VIA%20CARLO%20CATTANEO%207%2C%2087100%2C%20COSENZA%20(CS)&CT1 03=430517222236&CT102=A0000000041010&CT123=&CT101=46&CT100=7937&PROVINCE=PALERMO &CT109=Debit%20Mastercard&CT108=Mastercard&autoClose=false
Accounting closure
Account closure – MobilePOS Only! The account closure can be run by calling the closeAccount method. Only callable with an active MobilePOS TID
code
suspend fun closeAccount( closeAccountData: CloseAccountData, operationListener: OperationListener )
The closeAccount method takes an instance of CloseAccountData as an argument and an operationListener, which will propagate the operation outcome to the caller application.
code
data class CloseAccountData( val callerName: String?, val callerTrxId: String? ) interface OperationListener { <br> fun onSuccess(response: A2ASDKResponse? = null) fun onFailure(error: A2ASDKResponse? = null) }
CloseAccountData
The CloseAccountData is a class used for holding information related to an account closure operation. Each property in the class corresponds to a different aspect of the account closure. CloseAccountData class properties are detailed below:
Name parameter | Description | Type | Mandatory |
---|---|---|---|
callerName | Represents the name of the caller initiating the account closure | String | yes |
callerTrxId | Represents the transaction ID associated with the caller | String | yes |