With our Card reader on Android solution you can accept mobile in-person payments using a card reader as the payment interface, and process these payments on the Adyen payments platform.
The card reader is paired with an Android mobile device through Bluetooth. On the Android mobile device, payment requests are initiated from a POS app. On the card reader, the customer can tap, insert, or swipe their card, or use a digital wallet like Apple Pay.
Requirements
Before you begin, take into account the following requirements, limitations, and preparations.
Requirement | Description |
---|---|
Integration type | You must have a POS app that is integrated with Terminal API. |
API credentials | You need the following API credentials:
|
Webhooks | To learn the outcome of refunds, set up Standard webhooks (if this hasn't been done already). |
Hardware | You need:
|
Limitations | Check the countries/regions, payment methods, and functionality that we support for Card reader on Android. |
Setup steps | Before you begin:
|
How it works
Sample app
Hit the ground running with our Android sample app.
To build a Tap to Pay on Android solution:
-
Add the Mobile SDK for Android to your project, either using an API key or using a GitHub access token.
-
Implement a server-to-server API request to establish a secure communication session.
-
In your POS app, enable the transaction functionality of the SDK.
-
In your POS app, implement handling payments using the SDK.
This creates the following flow:- Your Android POS app creates a Terminal API payment request, or receives a Terminal API payment request from your backend.
- The POS app passes the payment request to the Android Mobile SDK.
- The SDK initiates the transaction on the card reader.
To complete the payment, the customer taps the card reader with their payment card or phone (or other device) that has a digital wallet like Apple Pay. - The SDK passes the Terminal API payment response to the POS app.
-
In your POS app, implement handling refunds using the SDK.
-
If the same device will be used at multiple locations, implement clearing the communication session.
1. Add the SDK to your project
You can add the Mobile SDK for Android to your POS app through a private Adyen repository. To get access, you need to have an Adyen API key that is used only for the purpose of getting the SDK.
For SDK versions earlier than version 1.6.0 it is possible to get access using a GitHub access token. However, we recommend using the API key method.
Our SDK only runs on Android 12 or later. You can still target earlier Android versions, but our SDK will not initialize.
We recommend you create an API credential with an API key that only has the Allow SDK download for POS developers role.
This API key is only meant to be used to get access to the SDK repository. To make a sessions
request, you need a different API key.
You can share this API key with anybody in your team who needs to get the SDK, or you can create multiple API keys for internal use.
-
In your Customer Area create an API credential with an API key:
-
Go to Developers > API credentials, and select Create new credential.
-
Under Payments > Credential type select Web service user and then select Create credential.
-
Under Server settings > Authentication select the API key tab and then select Generate API key.
-
Select the copy icon and save your API key in a secure location.
-
Go to Permissions > Roles > POS, select Allow SDK download for POS developers, and deselect any other permissions and roles.
Contact Support Team if you don't see the Allow SDK download for POS developers role.
-
Select Save changes.
-
-
In the
repositories
block of your project, usually defined in yoursettings.gradle
file, add the URL to the remote repository where the SDK is stored, and add the API key that you created in the previous step.dependencyResolutionManagement { //... repositories { google() mavenCentral() maven { url = uri("https://pos-mobile-test.cdn.adyen.com/adyen-pos-android") credentials(HttpHeaderCredentials::class) { name = "x-api-key" value = "<YOUR_API_KEY>" } authentication { create<HttpHeaderAuthentication>("header") } } } } -
In your project's
build.gradle
file, add the package dependencies.- To find the latest
val version
check the release notes for the Mobile SDK for Android. - For the build type, note that
-debug
can only access the test environment, and-release
can only access the live environment. - In case of other custom build variants, use
<buildVariantName>Implementation 'com.adyen.ipp:pos-mobile-<type>:$version'
where<type>
can be release for production builds, or debug for debug or development builds.
val version = 1.5.0 debugImplementation 'com.adyen.ipp:pos-mobile-debug:$version' - To find the latest
2. Establish a session
The Mobile SDK has to communicate in a secure way with the Adyen payments platform. For more information, see Communication session.
To authenticate your server-to-server API request for establishing a communication session, you need to have an Adyen API credential in your test Customer Area. This credential must have a client key and an API key with the following role:
- Checkout webservice role. This role is assigned by default when the API key is created.
To add a client key to an existing API credential, create a client key as follows:
- Log in to your Customer Area.
- Go to Developers > API credentials, and select the credential username for your integration, for example ws@Company.[YourCompanyAccount].
- Under Client settings > Authentication select the Client key tab.
- Select Generate client key.
-
Select Save changes.
The client key is part of the setup but is not used later on. Therefore, you do not need to specify allowed origins, and you do not need to save the client key in your system.
To let your backend establish a session:
-
From your backend, make a POST /checkout/possdk/v68/sessions request, specifying:
Parameter Required Description merchantAccount The unique identifier of your merchant account. setupToken The setup token provided by the Mobile SDK through the PaymentServiceDelegate.register(with:)
callback ofPaymentServiceDelegate
.store The unique identifier of the store that you want to process payments for. /sessions requestExpand viewCopy link to code blockCopy codecurl https://checkout-test.adyen.com/checkout/possdk/v68/sessions \ -H 'content-type: application/json' \ -H 'x-API-key: ADYEN_API_KEY' \ -X POST \ -d '{ "merchantAccount": "YOUR_MERCHANT_ACCOUNT", "setupToken": "SETUP_TOKEN", "store": "YOUR_STORE_ID" }' -
When you receive the response:
- Check that you get a 201 Created HTTP status code.
- Return the sdkData to your POS app.
- If you create the Terminal API request on your backend, save the installationId and use this as the
POIID
in theMessageHeader
of the payment request.
/sessions responseExpand viewCopy link to code blockCopy code{ "id": "APP_SESSION_ID", "installationId": "INSTALLATION_ID", "merchantAccount": "YOUR_MERCHANT_ACCOUNT", "store": "YOUR_STORE_ID", "sdkData": "SDK_DATA_BLOB" }
3. Enable transactions
To enable the payments functionality of the Mobile SDK for Android, add code to your Android POS app:
-
Implement the
AuthenticationProvider
interface. Note that you need to extract the sdkData from the server response and create anAuthenticationResponse
object withsdkData
in the constructor. Below is an example of how you could do that, assuming your project uses OkHttp.class MyAuthenticationProvider : AuthenticationProvider() { override suspend fun authenticate(setupToken: String): Result<AuthenticationResponse> { // Make a call to your backend to trigger a `/checkout/possdk/v68/sessions` request, specifying the `setupToken` provided by the SDK val client = OkHttpClient() val request = Request.Builder() .url("ADDRESS_OF_YOUR_BACKEND_API") .build() client.newCall(request).execute().use { response -> response.body?.let { // parse your own back-end response and return AuthenticationResponse ... return Result.success(AuthenticationResponse(sdkData)) } } } } -
Implement the
MerchantAuthenticationService
abstract class: in your implementation, provide an instance of yourAuthenticationProvider
from the previous step. If you use Dagger2/Hilt dependency injection, below is an example of how you could do this:class MyAuthenticationService : MerchantAuthenticationService() { @Inject override lateinit var authenticationProvider: AuthenticationProvider } -
Add the service to your POS app's
AndroidManifest.xml
file.<service android:name=".MyAuthenticationService"/>
-
Make sure that the
MerchantAuthenticationService
can provide newsdkData
at any time.
If there is no session or the session has expired, the service is called using theMerchantAuthenticationService.authenticate(setupToken)
callback. Using the providedsetupToken
you need to get thesdkData
through your backend and return it. For instructions, see Establish a session.The Adyen POS Mobile SDK detects the
MerchantAuthenticationService
automatically. If the SDK fails to detect the service, an error will occur when SDK methods are called. To resolve this, you can manually set the service usingInPersonPayments.setAuthenticationServiceClass()
.
Manage automatic initialization
The initializes automatically, using the Android App Startup library. If you prefer to manually initialize the , add the following code to the POS app's AndroidManifest.xml
file to disable the .
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <meta-data android:name="com.adyen.ipp.InPersonPaymentsInitializer" tools:node="remove" /> </provider>
To manually initialize that component at a later point, add the following code to the POS app.
AppInitializer.getInstance(this) .initializeComponent(InPersonPaymentsInitializer::class.java)
Initialization status check
To check the status of the SDK initialization, you can use one of the following options:
InPersonPayments.initialised .filter { it } // Wait until it is true. .take(1) // Take only one item. .collect { ready -> if (ready) { val id = InPersonPayments.getInstallationId() // ... } }
or
InPersonPayments.initialised.filter { it }.take(1).single() // Suspends until it is true. val id = InPersonPayments.getInstallationId()
Manage POS app dependency initialization
The code that you add to the Application.onCreate()
method is executed for all processes. In some cases, it can be beneficial to skip the re-execution of the code, for example the initialization of your dependency graph or analytics libraries.
The following method evaluates specific conditions and returns a boolean value that indicates whether the initialization should be skipped.
if (InPersonPaymentsTools.shouldSkipAppInitialize(context)) { // Perform actions when initialization should be skipped } else { // Proceed with application initialization }
4. Manage the UI
To use the card reader, operators need to:
- Pair the mobile device with the card reader.
- See an overview of card readers. For example, to switch to a different card reader.
- View details of the card reader they are using. For example, to check the battery charge level.
- Update the firmware of the card reader they are using.
To handle these tasks, you can:
- Use the built-in UI. The Mobile SDK for Android includes device managements screens.
- Build a custom UI.
If you have disabled the automatic initialization of the POS Mobile SDK, you need to initialize it manually. If you are unsure of the status of the POS Mobile SDK initialization you can run a status check.
To use the device management screens built into the Mobile SDK for Android, call the following code.
DeviceManagementActivity.start(activity)
5. Handle a payment
In this step you add code to start a transaction with:
- A Terminal API payment request.
- The card reader as the payment interface to use.
To let the Mobile SDK handle transactions:
-
If you create the Terminal API payment request in your POS app, use
InPersonPayments.getInstallationId()
asPOIID
in the MessageHeader of the payment request.
Note that if you create the Terminal API payment request in the backend, the backend uses the installationId from the/checkout/posskd/v68/sessions
response.For the structure of a Terminal API payment request see PaymentRequest.
-
Create an instance of
TransactionRequest
usingTransactionRequest.create(nexoRequest)
, and pass the Terminal API payment request from your POS app or backend.val transactionRequest = TransactionRequest.create(nexoRequest)
-
Get a
PaymentInterface
fromInPersonPayments
usingInPersonPayments.getPaymentInterface(CardReader)
.val paymentInterface = InPersonPayments.getPaymentInterface(CardReader)
-
Register a listener for the
PaymentResult
and pass thetransactionResponse
to your POS app. This is the Terminal API payment response, including data you can use to generate a receipt.InPersonPayments.registerForPaymentResult(context) { result -> result.fold( onSuccess = { paymentResult -> { // Here your logic for paymentResult if (paymentResult.success) "Payment Successful" else "Payment Failed" }, onFailure = { error -> { // Here your logic for failure case }, ) -
Invoke
InPersonPayments.performTransaction()
with your transaction data, payment launcher, and authentication service.Then customize the user interface using
merchantUiParameters
with the following optional fields:merchantLogo
: a logo in WEBP, JPEG, or PNG format to show on your mobile device during the transaction flow. The logo will be shown at 100dp x 32dp (width x height). Trim any transparent pixels from the logo asset, to show your logo as large as possible within the available space.autoDismissDelay
: indicates how long the SDK shows the screen that indicates the transaction succeeded. If not specified, this success screen is dismissed after four seconds. You can set a time in milliseconds as a Long with a minimum of 0.5 seconds (500L) and a maximum of 4 seconds (4000L).tapToPayUiParameters
: the UI option to use for the "Present card" screen for Tap to Pay payments.
Options forFront
are:Center
,TopCenter
, orBottomCenter
.
Options forDirectional
are:TopCenter
orBottomCenter
.cardReaderUiParameters
: the UI options to use for the "Present card" screen for card reader payments.
Options forSimplified
are:Center
,CenterRight
, orCenterLeft
.
InPersonPayments.performTransaction( context = this@DevAmountEntryActivity, paymentLauncher = paymentLauncher, paymentInterface = result.paymentInterface, transactionRequest = result.transactionRequest, merchantUiParameters = MerchantUiParameters.create( merchantLogo = commonR.drawable.temp_merchant_logo_int, tapToPayUiParameters = TapToPayUiParameters.create(TapToPayAnimationType.front(NfcFrontPosition.TopCenter)), cardReaderUiParameters = CardReaderUiParameters(CardReaderAnimationType.simplified(Position.CenterLeft)), ), ) The Mobile SDK checks for a session, starts the transaction, and shows screens on your mobile device to help the customer.
If the shopper does not present their payment method within 30 seconds, the payment request times out. If that happens, you need to make another payment request.
6. Handle a refund
There are two types of refund: referenced and unreferenced. The main difference is that a referenced refund is connected to the original payment, and an unreferenced refund isn't. That makes unreferenced refunds a bit riskier. For an overview of the differences, see Refund a payment.
Refunds are usually not processed synchronously. When you send a request for a referenced or unreferenced refund, the Terminal API response only confirms we received the request.
We inform you about the outcome of the refund asynchronously, through a webhook.
- For a referenced refund, we return a CANCEL_OR_REFUND webhook.
- For an unreferenced refund, we return a REFUND_WITH_DATA webhook.
Depending on the card scheme and country/region where the card is used, unreferenced refunds are sometimes processed synchronously. In that case the Terminal API response includes an acquirerResponseCode
to indicate the outcome.
To learn the outcome of a refund, you need to set up webhooks.
Handle a referenced refund
The Terminal API request for a referenced refund is a reversal request. The SDK contains a dedicated function for this.
In your Android POS app, add code for the following steps:
-
If you create the Terminal API reversal request in your POS app, use
InPersonPayments.getInstallationId()
as the POIID in the MessageHeader of the reversal request.
Note that if you create the Terminal API reversal request in the backend, the backend uses the installationId from the/checkout/posskd/v68/sessions
response.For the structure of a Terminal API referenced refund request see ReversalRequest.
-
Create an instance of
TransactionRequest
usingTransactionRequest.createReversal(nexoRequest)
, and pass the Terminal API payment request from your POS app or backend. -
Register a listener for the
PaymentResult
and pass thetransactionResponse
to your POS app. This is the Terminal API payment response, including data you can use to generate a receipt.val paymentLauncher = InPersonPayments.registerForPaymentResult(this) { refundResult -> // handle refund response here ... } -
Invoke
performReversal()
with your transaction data andauthenticationProvider
.
Note thatauthenticationProvider
is an implementation of the AuthenticationProvider interface, which extracts thesdkData
from the server response.public fun performReversal( context: Context, paymentLauncher: ActivityResultLauncher<Intent>, transactionRequestReversal: TransactionRequestReversal, ): Result<PaymentResult> -
Check the
refundResult
that you receive in thepaymentLauncher
callback. -
Pass the
refundResult
to your POS app.
Handle an unreferenced refund
The Terminal API request for an unreferenced refund is a payment request with an additional parameter:
PaymentData.PaymentType
: Refund
This means you can use the same code as for handling a payment. The only difference is the structure of the Terminal API payment request that you pass as the payload to the TransactionRequest
.
For the structure of the Terminal API request, see Unreferenced refund.
7. (Optional) Clear the session token
If the same device is used at multiple locations, you need to ensure that transactions are logged for the correct location.
The request to create a secure communication sessions with Adyen is made from the backend, for example for store A. If the device is moved to store B and a transaction is started there, on the Adyen side the transaction will appear to belong to store A instead of store B. You can prevent this by clearing the existing session and establishing a new one.
When the device is moved to a different location:
- Explicitly clear the communication session using
InPersonPayments.clearSession()
. - Establish a new communication session.
Other payment features
In addition to payments and refunds, the Mobile solutions support other payment features. These are the same features that are supported in Terminal API integrations using Adyen-provided payment terminals.
For some features you need to add parameters to your Terminal API payment request, similar to unreferenced refunds described above. Other features only require enabling the feature for your Adyen account. You can find the details on the pages dedicated to those features. Where the details differ between an integration using payment terminals and a mobile solution, this is clearly indicated.
Feature | Supported with Card reader Android |
---|---|
Partial authorization | ![]() |
Payment | ![]() |
Pre-authorization | ![]() |
Refund, referenced | ![]() |
Refund, unreferenced | ![]() |
Store and forward offline payments | ![]() |
Surcharge | ![]() |
Tax-free shopping | ![]() |
Test your solution
To make test transactions:
-
Make sure you are using the test version of the Mobile SDK.
-
Initiate a test transaction using the following Adyen point-of-sale test cards to complete the payment:
- White-green test card
- Blue-green test card version 2.4 or later
The instructions are the same for both cards; see either of the pages mentioned above.
Go live
When you have finished testing your integration and are ready to go live:
-
If new to Adyen, get a live account. You need to have access to your organization's live Customer Area to generate API credentials for the live environment.
-
Get the live SDK. You need to generate a new, live API key exclusively for downloading the SDK.
If you use a GitHub access token to get the SDK, you do not need to change anything to get the live SDK because you already have the release package dependency in your project's
build.gradle
file. -
Use the live endpoint for establishing a session. To access the live endpoint, you need to generate a new, live API key that is different from the API key used for downloading the SDK.
-
Optional. Upload your app to Google Play.
Get the live SDK
When going live, you need to get the release version of the SDK, which is available on the live repository. To access it, you need to change the repository URL as well as your API key.
To pull in the live version of the SDK:
-
In your live Customer Area, generate a new API key that only has the Allow SDK download for POS developers role.
-
In your project's
build.gradle
file, change theURL
tohttps://pos-mobile.cdn.adyen.com/adyen-pos-android
and replaceAPI_KEY
with your new API key. -
Add the
release
dependency to yourbuild.gradle
file.- The live repository has both the
debug
andrelease
artefacts. The-debug
version can only access the test environment and the-release
version can only access the live environment. - When you have access to the live repository, you no longer need to use the test repository.
- In case of other custom build variants, use
<buildVariantName>Implementation 'com.adyen.ipp:pos-mobile-<type>:$version'
where<type>
can be release (for production builds) or debug (for debug or development builds).
val version = 1.5.0 releaseImplementation 'com.adyen.ipp:pos-mobile-release:$version' debugImplementation 'com.adyen.ipp:pos-mobile-debug:$version' - The live repository has both the
Establish a live session
When going live, you must change the /sessions
endpoint as well as the API key that you use to authenticate /sessions
requests.
- To access the live endpoint, generate a new API key from your live Customer Area.
-
The live endpoint URL contains a prefix which is unique to your company account, for example:
https://{PREFIX}-checkout-live.adyenpayments.com/checkout/possdk/v68/sessions
Get your
{PREFIX}
from your live Customer Area under Developers > API URLs > Prefix.