Terminal-2 icon

Card reader solution for Android

Integrate your POS app with the Adyen Mobile SDK for Android to make mobile payments using a card reader.

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:
  • To get the SDK, you must have either an API credential with an API key and only the Allow SDK download for POS developers role, or a GitHub access token.
  • To establish a communication session, you must have an API credential with an API key, a client key, and the Checkout webservice role.
Webhooks To learn the outcome of refunds, set up Standard webhooks (if this hasn't been done already).
Hardware You need:
  • An Android commercial off-the-shelf (COTS) mobile device. Must not be a payment terminal.
  • An NYC1 card reader from Adyen.
See Android system requirements for the full hardware and software requirements.
Limitations Check the countries/regions, payment methods, and functionality that we support for Card reader on Android.
Setup steps Before you begin:
  • Ask our Support Team to enable your Adyen test account for using our Mobile solutions.
  • In your Customer Area, order a test NYC1 card reader and a test card, and assign the reader to your store.

How it works

Sample app
Hit the ground running with our Android sample app.

To build a Tap to Pay on Android solution:

  1. Add the Mobile SDK for Android to your project, either using an API key or using a GitHub access token.

  2. Implement a server-to-server API request to establish a secure communication session.

  3. In your POS app, enable the transaction functionality of the SDK.

  4. In your POS app, implement handling payments using the SDK.
    This creates the following flow:

    1. Your Android POS app creates a Terminal API payment request, or receives a Terminal API payment request from your backend.
    2. The POS app passes the payment request to the Android Mobile SDK.
    3. 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.
    4. The SDK passes the Terminal API payment response to the POS app.
  5. In your POS app, implement handling refunds using the SDK.

  6. 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.

  1. In your Customer Area create an API credential with an API key:

    1. Go to Developers > API credentials, and select Create new credential.

    2. Under Payments > Credential type select Web service user and then select Create credential.

    3. Under Server settings > Authentication select the API key tab and then select Generate API key.

    4. Select the copy icon and save your API key in a secure location.

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

    6. Select Save changes.

  2. In the repositories block of your project, usually defined in your settings.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.

    Copy code
    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")
    }
    }
    }
    }
  3. 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.
    Copy code
    val version = 1.5.0
    debugImplementation 'com.adyen.ipp:pos-mobile-debug:$version'

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:

  1. Log in to your Customer Area.
  2. Go to Developers > API credentials, and select the credential username for your integration, for example ws@Company.[YourCompanyAccount].
  3. Under Client settings > Authentication select the Client key tab.
  4. Select Generate client key.
  5. 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:

  1. From your backend, make a POST /checkout/possdk/v68/sessions request, specifying:

    Parameter Required Description
    merchantAccount -white_check_mark- The unique identifier of your merchant account.
    setupToken -white_check_mark- The setup token provided by the Mobile SDK through the PaymentServiceDelegate.register(with:) callback of PaymentServiceDelegate.
    store The unique identifier of the store that you want to process payments for.
    /sessions request
    Expand view
    Copy link to code block
    Copy code
    Copy code
    curl 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"
    }'
  2. 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 the MessageHeader of the payment request.
    /sessions response
    Expand view
    Copy link to code block
    Copy code
    Copy 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:

  1. Implement the AuthenticationProvider interface. Note that you need to extract the sdkData from the server response and create an AuthenticationResponse object with sdkData in the constructor. Below is an example of how you could do that, assuming your project uses OkHttp.

    Copy code
    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))
    }
    }
    }
    }
  2. Implement the MerchantAuthenticationService abstract class: in your implementation, provide an instance of your AuthenticationProvider from the previous step. If you use Dagger2/Hilt dependency injection, below is an example of how you could do this:

    Copy code
    class MyAuthenticationService : MerchantAuthenticationService() {
    @Inject
    override lateinit var authenticationProvider: AuthenticationProvider
    }
  3. Add the service to your POS app's AndroidManifest.xml file.

    Copy code
     <service android:name=".MyAuthenticationService"/>
  4. Make sure that the MerchantAuthenticationService can provide new sdkData at any time.
    If there is no session or the session has expired, the service is called using the MerchantAuthenticationService.authenticate(setupToken) callback. Using the provided setupToken you need to get the sdkData 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 using InPersonPayments.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 .

Copy code
<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.

Copy code
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:

Copy code
InPersonPayments.initialised
.filter { it } // Wait until it is true.
.take(1) // Take only one item.
.collect { ready ->
if (ready) {
val id = InPersonPayments.getInstallationId()
// ...
}
}

or

Copy code
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.

Copy code
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:

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.

Copy 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:

  1. If you create the Terminal API payment request in your POS app, use InPersonPayments.getInstallationId() as POIID 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.

  2. Create an instance of TransactionRequest using TransactionRequest.create(nexoRequest), and pass the Terminal API payment request from your POS app or backend.

    Copy code
    val transactionRequest = TransactionRequest.create(nexoRequest)
  3. Get a PaymentInterface from InPersonPayments using InPersonPayments.getPaymentInterface(CardReader).

    Copy code
    val paymentInterface = InPersonPayments.getPaymentInterface(CardReader)
  4. Register a listener for the PaymentResult and pass the transactionResponse to your POS app. This is the Terminal API payment response, including data you can use to generate a receipt.

    Copy code
    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
    },
    )
  5. 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 for Front are: Center, TopCenter, or BottomCenter.
      Options for Directional are: TopCenter or BottomCenter.
    • cardReaderUiParameters: the UI options to use for the "Present card" screen for card reader payments.
      Options for Simplified are: Center, CenterRight, or CenterLeft.
    Copy code
    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:

  1. 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.

  2. Create an instance of TransactionRequest using TransactionRequest.createReversal(nexoRequest), and pass the Terminal API payment request from your POS app or backend.

  3. Register a listener for the PaymentResult and pass the transactionResponse to your POS app. This is the Terminal API payment response, including data you can use to generate a receipt.

    Copy code
    val paymentLauncher = InPersonPayments.registerForPaymentResult(this) { refundResult ->
    // handle refund response here
    ...
    }
  4. Invoke performReversal() with your transaction data and authenticationProvider.
    Note that authenticationProvider is an implementation of the AuthenticationProvider interface, which extracts the sdkData from the server response.

    Copy code
    public fun performReversal(
    context: Context,
    paymentLauncher: ActivityResultLauncher<Intent>,
    transactionRequestReversal: TransactionRequestReversal,
    ): Result<PaymentResult>
  5. Check the refundResult that you receive in the paymentLauncher callback.

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

  1. Explicitly clear the communication session using InPersonPayments.clearSession() .
  2. 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 -white_check_mark-
Payment -white_check_mark-
Pre-authorization -white_check_mark-
Refund, referenced -white_check_mark-
Refund, unreferenced -white_check_mark-
Store and forward offline payments -x-
Surcharge -x-
Tax-free shopping -x-

Test your solution

To make test transactions:

  1. Make sure you are using the test version of the Mobile SDK.

  2. Initiate a test transaction using the following Adyen point-of-sale test cards to complete the payment:

    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:

  1. 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.

  2. 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.

  3. 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.

  4. Optional. Upload your app to Google Play.

  5. Register your app with Adyen.

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:

  1. In your live Customer Area, generate a new API key that only has the Allow SDK download for POS developers role.

  2. In your project's build.gradle file, change the URL to https://pos-mobile.cdn.adyen.com/adyen-pos-android and replace API_KEY with your new API key.

  3. Add the release dependency to your build.gradle file.

    • The live repository has both the debug and release 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).
    Copy code
    val version = 1.5.0
    releaseImplementation 'com.adyen.ipp:pos-mobile-release:$version'
    debugImplementation 'com.adyen.ipp:pos-mobile-debug:$version'

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.

Next steps