--- title: "Card reader solution for Android" description: "Integrate your POS app with the Adyen Mobile SDK for Android to make mobile payments using a card reader." url: "https://docs.adyen.com/point-of-sale/mobile-android/build/card-reader" source_url: "https://docs.adyen.com/point-of-sale/mobile-android/build/card-reader.md" canonical: "https://docs.adyen.com/point-of-sale/mobile-android/build/card-reader" last_modified: "2026-09-09T12:56:33+02:00" language: "en" --- # 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 connected with an Android mobile device through Bluetooth, or through USB using a dock. 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](/point-of-sale/design-your-integration/terminal-api/). | | **[API credentials](/development-resources/api-credentials/)** | You need the following API credentials:- To get the SDK, you must have an API credential with an API key and only the **Allow SDK download for POS developers** role. - To establish a communication session, you must have an API credential with an API key, a client key, and the **Checkout webservice** role. | | **[Webhooks](/development-resources/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 and optionally an [NYC1 dock](/point-of-sale/user-manuals/nyc1-with-dock/). For PIN transactions your card reader needs to be an [NYC1](/point-of-sale/mobile-android/understand#pin-transactions) model.See [Android system requirements](/point-of-sale/mobile-android/requirements) for the full hardware and software requirements. | | **Limitations** | Check the countries/regions, payment methods, and functionality that we [support](/point-of-sale/ipp-mobile) for Card reader on Android. | | **Setup steps** | Before you begin:- Ask our [Support Team](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other) to enable your Adyen test account for using our Mobile solutions. - In your [Customer Area](https://ca-test.adyen.com/), [order](/point-of-sale/managing-terminals/order-terminals#sales-order-steps) a test NYC1 card reader and a test card, and [assign](/point-of-sale/managing-terminals/assign-terminals) the reader to your store. - If you want to disable PIN support for NYC1 card readers in the US from Mobile SDK for Android version **2.9.0** or later, contact our [Support Team](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other) and ask them to disable PIN support on the company, merchant, or store level. | ## How it works **Sample app**\ Hit the ground running with our [Android sample app](https://github.com/Adyen/adyen-pos-mobile-android/tree/main/app-default). To build a Tap to Pay on Android solution: 1. Add the Mobile SDK for Android to your project, either using an API key. 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. From your POS app, call the warmup function to speed up initiating transactions. 5. 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. 6. In your POS app, implement handling refunds using the SDK. 7. 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. Your app's `minSdkVersion` must be **26** or higher. Do not use `tools:overrideLibrary` in your manifest to bypass this requirement, as this can cause the Adyen SDK to fail or behave unexpectedly. Also note that our SDK only runs on devices with Android 12 or later; on earlier Android OS versions, it will not initialize. To add the Mobile SDK to your project, you need to 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](#session). 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. To add the SDK to your project: 1. In your [Customer Area](https://ca-test.adyen.com/) 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](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other) 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. ```kotlin 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 = "" } authentication { create("header") } } } } ``` 3. In your project's `build.gradle` file, add the package dependencies. * To find the latest `val version` check the [release notes](/point-of-sale/firmware-release-notes?title%5B0%5D=Android%2BSDK%2Bon%2Bmobile) 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 `Implementation 'com.adyen.ipp:pos-mobile-:$version'` where `` can be **release** for production builds, or **debug** for debug or development builds. ```groovy val version = "LATEST_SDK_VERSION" debugImplementation 'com.adyen.ipp:pos-mobile-debug:$version' // Be aware that importing additional modules will increase the size of your application. // To optimize your app's size and build times, only include the specific payment features you require. debugImplementation 'com.adyen.ipp:payment-tap-to-pay-debug:$version' debugImplementation 'com.adyen.ipp:payment-card-reader-debug:$version' ``` ## 2. Establish a session To ensure the Mobile SDK and the Adyen payments platform can collaborate in a secure way, you need to implement a server-to-server API request for establishing a communication session. ![Authentication flow](/user/pages/reuse/pos-mobile-sdk/ios-build/session/ipp-mobile-authentication-flow.svg?decoding=auto\&fetchpriority=auto) To authenticate this API request, you need to have an Adyen [API credential](/development-resources/api-credentials) in your [test Customer Area](https://ca-test.adyen.com/). This credential must have an [API key](/development-resources/api-credentials#generate-api-key) with the following [role](/development-resources/api-credentials#manage-api-permissions): * **Checkout webservice role**. This role is assigned by default when the API key is created. The credential also needs to have a client key. ** #### Create a client key If you want to use an existing API credential that does not have a client key yet, create a client key as follows: 1. Log in to your [Customer Area](https://ca-test.adyen.com/). 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. #### Implement the request To establish a communication session: 1. From your backend, make a POST request to the applicable test or regional [/auth/certificate](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate) endpoint, specifying: | Parameter | Required | Description | | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [merchantAccount](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#request-merchantAccount) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The unique identifier of your [merchant account](/point-of-sale/design-your-integration/determine-account-structure#request-merchant-accounts). | | [setupToken](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#request-setupToken) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The setup token provided by the Mobile SDK through the implementation of [`AuthenticationProvider` ](#enable-transactions). | | [store](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#request-store) | Not a payment facilitator | The reference of the [store](/point-of-sale/design-your-integration/determine-account-structure#create-stores) that you want to process payments for. Do not include this parameter if your [account structure](/point-of-sale/design-your-integration/determine-account-structure#determine-account-structure) uses merchant accounts as stores, or if you are a registered payment facilitator. | | [subMerchantData](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#request-subMerchantData) | Payment facilitator | An object with the details of the sub-merchant. See the next table for the parameters. This object is required if you are a registered payment facilitator. If you are not a payment facilitator, do not include this object. | The `subMerchantData` object includes the following parameters: | Parameter | Required | Description | | ------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | Your unique identifier of the sub-merchant. | | `name` | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The name of the sub-merchant. | | `displayName` | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The name of the sub-merchant as it should appear on the display of the mobile device during transactions. | | `mcc` | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | The sub-merchant's four-digit Merchant Category Code (MCC). This parameter is used to correctly route the transaction. | | `street` | | The street name and house number of the sub-merchant's address. | | `city` | | The city of the sub-merchant's address. | | `postalCode` | | The postal code of the sub-merchant's address, without dashes. | | `country` | | The country/region of the sub-merchant's address, specified as the three-letter country code in [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) format. | | `state` | | The state code of the sub-merchant's address, if applicable for the country or region. | | `taxId` | | The tax ID of the sub-merchant. Required only in Brazil and for Cartes Bancaires in France. For Brazil, this is the 11-digit CPF or 14-digit CNPJ. For France, this is the SIRET, with a maximum of 14 digits. | | `email` | | The email address of the sub-merchant. Required for American Express. | | `phoneNumber` | | The phone number of the sub-merchant. Required for American Express. | ### Tab: Not a payment facilitator If you are *not* a registered payment facilitator, the `/auth/certificate` request to establish a communication session must include the `store` parameter, unless your account structure does not have stores and instead uses merchant accounts as stores. If previously you have used [/checkout/possdk/v68/session](https://docs.adyen.com/api-explorer/possdk/latest/post/sessions) test and live endpoints, we recommend you migrate to [new endpoints](#endpoints-to-use). **/auth/certificate request with store to process payments for** ```bash curl https://softposconfig-test.adyen.com/softposconfig/v3/auth/certificate \ -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_REFERENCE" }' ``` ### Tab: Payment facilitator If you are a payment facilitator, card schemes need to receive certain data about your sub-merchant with each transaction. That is because your sub-merchant is considered the Merchant of Record when a transaction is facilitated by you. Adyen automatically adds certain sub-merchant data to transactions that you facilitate. The remaining data must be supplied by you in the `subMerchantData` object of the `/auth/certificate` request to establish a communication session. You must **not** submit sub-merchant data in your Terminal API requests. **/auth/certificate request with sub-merchant to process payments for** ```bash curl https://softposconfig-test.adyen.com/softposconfig/v3/auth/certificate \ -H 'content-type: application/json' \ -H 'x-API-key: ADYEN_API_KEY' \ -X POST \ -d '{ "merchantAccount": "YOUR_MERCHANT_ACCOUNT", "setupToken": "SETUP_TOKEN", "subMerchantData": { "id": "123456", "name": "Test Merchant", "mcc": "5734", "street": "123 Sample Street", "city": "Minneapolis", "state": "MN", "postalCode": "94107", "country": "USA", "taxId": "TAX123456", "displayName": "MN Computer Software", "email": "test-merchant@example.com", "phoneNumber": "PHONE_NUMBER" } }' ``` 2. When you receive the response: * Check that you get a **201 Created** HTTP status code. * Return the [sdkData](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-sdkData) to your POS app. * If you create the Terminal API request on your backend, save the [installationId](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-installationId) and use this as the `POIID` in the [`MessageHeader` ](/point-of-sale/design-your-integration/terminal-api#request-message-header)of the payment request. **Successful /auth/certificate response** ```json { "id": "APP_SESSION_ID", "installationId": "INSTALLATION_ID", "merchantAccount": "YOUR_MERCHANT_ACCOUNT", "store": "YOUR_STORE_ID", "sdkData": "SDK_DATA_BLOB" } ``` If you receive a response with an error message similar to the following, ask our [Support Team](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other) to configure the MCC that you specified in the request. **/auth/certificate error message** ```raw Refused (905_3 Could not find an acquirer account for the provided currency (USD).) ``` #### Endpoints to use * Test endpoint: `https://softposconfig-test.adyen.com/softposconfig/v3/auth/certificate` * Regional live endpoints: * Australia: `https://softposconfig-live-au.adyen.com/softposconfig/v3/auth/certificate` * Northeast Asia: `https://softposconfig-live-nea.adyen.com/softposconfig/v3/auth/certificate` * Europe: `https://softposconfig-live.adyen.com/softposconfig/v3/auth/certificate` * North East Asia: `https://softposconfig-live-nea.adyen.com/softposconfig/v3/auth/certificate` * United States: `https://softposconfig-live-us.adyen.com/softposconfig/v3/auth/certificate` If you are using old [/checkout/possdk/v68/sessions](https://docs.adyen.com/api-explorer/possdk/latest/post/sessions) test and live endpoints, we recommend you migrate to the latest endpoints mentioned above. You can still use the `/checkout/possdk/v68/sessions` endpoints, but we are deprecating them. ## 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. ```kotlin class MyAuthenticationProvider : AuthenticationProvider() { override suspend fun authenticate(setupToken: String): Result { // Make a call to your backend to trigger a `/softposconfig/v{version}/auth/certificate` 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: ```kotlin class MyAuthenticationService : MerchantAuthenticationService() { @Inject override lateinit var authenticationProvider: AuthenticationProvider } ``` 3. Add the service to your POS app's `AndroidManifest.xml` file. ```xml ``` 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](#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](#enable-transactions), using the [Android App Startup library](https://developer.android.com/topic/libraries/app-startup). If you prefer to manually initialize the , add the following code to the POS app's `AndroidManifest.xml` file to disable the . ```kotlin ``` To manually initialize that component at a later point, add the following code to the POS app. For version 2.6.0 or later: Since the SDK requires the application to be in the foreground to bind to the service, you need to trigger initialization when your main Activity is visible. Use `repeatOnLifecycle` to ensure that the initialization runs when the Activity is in the `RESUMED` state. ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { // Suspend execution until the Activity is RESUMED repeatOnLifecycle(Lifecycle.State.RESUMED) { val state = InPersonPaymentsTools.initializeManually(this@MainActivity) if (state is InitializationState.SuccessfulInitialization) { // SDK is ready } else { // Handle failure } } } } ``` For versions earlier than version 2.6.0: ```kotlin 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. For version 2.6.0 or later: ```kotlin when (val state = InPersonPaymentsTools.getInitializationState()){ InitializationState.SuccessfulInitialization -> { // SDK is ready. You can now safely proceed. } is InitializationState.FailedInitialization -> { // Handle initialization failure. Log errors or notify the user. Log.e("MyApp", "SDK initialization failed: ${state.failureReasons}") } else -> Unit } ``` or for versions earlier than version 2.6.0 if you need more precise control over the state flow: ```kotlin InPersonPayments.initialized .filter { it == InitializationState.SuccessfulInitialization } // Wait until it is true. .take(1) // Take only one item. .collect { ready -> if (ready) { 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. ```kotlin if (InPersonPaymentsTools.shouldSkipAppInitialize(context)) { // Perform actions when initialization should be skipped } else { // Proceed with application initialization } ``` Additionally, you can use the following functions: * `suspend InPersonPaymentsTools.getInitializationState()`: this function observes the initialization state of the SDK and pauses the calling coroutine until the SDK initialization finishes and reaches a success or failure state. You can use this suspend function if an action in your app depends on the SDK being fully initialized. * `suspend InPersonPaymentsTools.initializeManually()`: this function triggers initialization of the SDK and waits for the initialization to finish. ## 4. Use the warm-up function To speed up initiating transactions, you can use the warm-up function. This function checks for a session, configuration changes, and security checks if needed. As a best practice, call the warm-up function after the SDK has initialized: * When the POS app starts. * When the POS app returns to the foreground. To call the warm-up function: ```kotlin InPersonPayments.warmUp() ``` ## 5. (Optional) Avoid delays You can avoid delays and speed up the transaction by preparing the card reader outside a transaction flow with configuration and security checks. When you start a transaction, the Mobile SDK connects with the card reader to check the reader's configuration. If the configuration is not up to date, the Mobile SDK sends the latest configuration files to the reader. This can take up to to 30 seconds. Additionally, the Mobile SDK performs a security check every 24 hours. To avoid a delay during the transaction flow, you can prepare the card reader by running these checks outside the transaction flow. It is possible to start a transaction while the card reader preparation is running. There can be a short delay during the transaction while any remaining checks and configuration updates are completed. We recommend preparing the device for transaction every 24 hours or when the configuration has changed. To prepare the device outside a transaction flow, call the **DeviceManager** `prepareDeviceForTransaction` function as follows, where the `device` is the card reader to prepare. ```kotlin // Get the DeviceManager val deviceManager = AdyenCardReaders.getInstance(context).deviceManager // Listen for when a device is connected deviceManager.connectionState.collect { connectionState -> if(connectionState is CardReaderState.Connected) { val device = connectionState.cardReaderInfo // Prepare the connected device deviceManager.prepareDeviceForTransaction(device) } } ``` ## 6. Manage the Device Manager UI To use the card reader, operators need to: * Connect the mobile device with the card reader through Bluetooth pairing or through USB using a dock or direct USB (if the Android device supports USB host mode). * View 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 or the dock they are using. To handle these tasks, the SDK provides Device Manager functions that enable you to either: * Use the built-in UI. * Build a custom UI. ### Tab: Use the built-in UI To use the built-in UI, you only need to: * **Launch the Device Manager**.\ This automatically takes care of all screens and flows that end users need. For a preview, see: * [UI for card reader operations](/point-of-sale/mobile-android/understand#ui-for-card-reader-operations) * [Bluetooth pairing](/point-of-sale/mobile-android/understand#bt-pairing) * [Updating the card reader firmware](/point-of-sale/mobile-android/understand#updating-card-reader) * [Connecting through a dock](/point-of-sale/mobile-android/understand#connecting-usb-dock) * [Updating the dock firmware](/point-of-sale/mobile-android/understand#updating-usb-dock) * Recommended: **avoid delays when connected through Bluetooth**.\ When connected through Bluetooth, the card reader enters sleep mode after five minutes of inactivity. When you start a transaction after the reader has gone to sleep, there is a slight delay while the mobile device reconnects to the last connected reader. * **Alert users to available firmware updates**.\ The built-in UI automatically signals that a firmware update is available. In practice however, end users often need an extra reminder. For a secure transaction flow, it is very important that end users update the devices. We therefore strongly recommend that you implement a command to check for firmware updates and create a UI to alert end users and direct them to the built-in screen for performing the update. Proceed as follows: 1. If you disabled automatic initialization of the Mobile SDK, [initialize the SDK manually](#manage-automatic-initialization). If you are unsure of the status of the SDK initialization you can run a [status check](#initialization-status-check). 2. To use the device management screens built into the Mobile SDK for Android, call the following code corresponding to your launching method. ```kotlin // Launching from an Activity DeviceManagementActivity.start(this) // Launching from a Fragment DeviceManagementActivity.start(requireActivity()) // Launching from Compose val context = LocalContext.current (context as? Activity)?.let { activity -> DeviceManagementActivity.start(activity) } ``` 3. Avoid delays when connected through Bluetooth by taking either of the following measures: * Before starting the transaction, wake up the card reader by calling the `DeviceManager.connect(cardReader: CardReader)` function. * Regularly refresh the connection between the mobile device and the card reader by implementing a timer that calls the `DeviceManager.connect(cardReader: CardReader)` function every couple of minutes. However, implementing a timer can significantly decrease the battery life of the card reader. When connected through USB, the card reader is always ready for use without delay. There is no need to reconnect to the card reader before initiating a transaction. 4. Regularly check for firmware updates for the card reader and, if used, the dock: * To check for card reader updates, call the `DeviceManager.firmwareUpdateState()` function. The result returns the following possible values: | Value | Description | | -------------------------- | ---------------------------------------------------- | | `NoneAvailable` | There are no new updates available. | | `DeviceUpdateAvailable` | A device firmware update is available. | | `BluetoothUpdateAvailable` | A Bluetooth firmware update is available. | | `MultipleUpdatesAvailable` | Bluetooth and device firmware updates are available. | If a new update is available, the result also returns the date by which transactions will be refused if the card reader is not updated to this firmware version. When connected through USB, Bluetooth updates cannot be completed and the SDK does not return information about available Bluetooth updates. * To check for dock updates, call the `DeviceManager.dockFirmwareUpdateSummary()` function. If a new update is available, the result returns `AvailableDockFirmwareUpdate` and the date by which transactions will be refused if the dock is not updated to this firmware version. If there is no new update, a null value is returned. 5. Create a UI to prompt end users to update their devices, redirecting them to the built-in UI for performing the update. This is in addition to the built-in screens that alert to available updates. 6. Optionally, you can hide firmware update indicators. By default, the built-in UI shows an indicator and a prompt to update the card reader or dock firmware. To hide these indicators, pass a `DeviceManagementConfiguration` to `DeviceManagementActivity.start()`. This requires Mobile SDK for Android version **2.18.0** or later. | Parameter | Description | | ------------------------------ | ---------------------------------------------------------------------------------------------- | | `showCardReaderFirmwareUpdate` | Set to **false** to hide the firmware update indicator for card readers. Defaults to **true**. | | `showDockFirmwareUpdate` | Set to **false** to hide the firmware update indicator for docks. Defaults to **true**. | ```kotlin DeviceManagementActivity.start( activity = this, configuration = DeviceManagementConfiguration( showCardReaderFirmwareUpdate = false, showDockFirmwareUpdate = false, ), ) ``` ```java DeviceManagementActivity.start( this, new DeviceManagementConfiguration(false, false) ); ``` If you do not pass a configuration, the built-in UI shows all indicators. ### Tab: Build a custom UI If you disabled automatic initialization of the Mobile SDK, you need to [initialize the SDK manually](#manage-automatic-initialization). If you intend to build your own UI for device management, use the `DeviceManager` interface to obtain all the necessary information. ```kotlin val deviceManager = AdyenCardReaders.getInstance(context).deviceManager ``` For an overview, see the [List of DeviceManager functions](#devicemanager-functions). The next sections provide information about the Device Manager functions you can use when building a UI for: * [Bluetooth pairing](#bt-pairing) * [Updating the card reader firmware](#updating-card-reader) * [Connecting through USB using a dock](#connecting-usb-dock) * [Updating the dock firmware](#updating-usb-dock) ### Bluetooth pairing To build a UI for the Bluetooth pairing flow: * Get a list of nearby card readers by calling the `DeviceManager.startBluetoothDiscovery()` function. * Connect to a card reader by calling the `DeviceManager.connect(cardReader: CardReader)` function. * Check if the Bluetooth pairing succeeded by listening to the `DeviceManager.connectionState` flow. If the state changes to **CardReaderState.Connected**, the reader was paired successfully. * To disconnect from a card reader, call the `disconnect()` function. * As an example, [see the built-in UI screens](/point-of-sale/mobile-android/understand#bt-pairing). When connected through Bluetooth, the card reader enters sleep mode after five minutes of inactivity. When you start a transaction after the reader has gone to sleep, there is a slight delay while the mobile device reconnects to the last connected reader. To avoid this delay, you can take either of the following measures: * Before starting the transaction, wake up the card reader by calling the `DeviceManager.connect(cardReader: CardReader)` function. * Regularly refresh the connection between the mobile device and the card reader by implementing a timer that calls the `DeviceManager.connect(cardReader: CardReader)` function every couple of minutes. However, implementing a timer can significantly decrease the battery life of the card reader. ### Updating the card reader firmware You must check for new firmware updates regularly, and provide a way to update the card reader to the latest version. Firmware updates for the card reader are critical for secure transactions, and do not happen automatically. It is required that you build logic into your POS app to: * Check for new firmware updates often. For example, when the app is launched. * Handle the firmware update flow correctly within your POS app. Your UI must prompt end users to update the card reader firmware, and inform them about the progress and result of the update process. To build your UI: * Check if updates are available by calling the `DeviceManager.firmwareUpdateState()` function. The available update can be for the device layer, the application layer, the Bluetooth layer, or multiple layers at the same time. The result returns the following possible values: | Value | Description | | -------------------------- | ---------------------------------------------------- | | `NoneAvailable` | There are no new updates available. | | `DeviceUpdateAvailable` | A device firmware update is available. | | `BluetoothUpdateAvailable` | A Bluetooth firmware update is available. | | `MultipleUpdatesAvailable` | Bluetooth and device firmware updates are available. | If a new update is available, the result also returns the date by which transactions will be refused if the card reader is not updated to this firmware version. When connected through USB, Bluetooth updates cannot be completed and the SDK does not return information about available Bluetooth updates. * Start the update using the `DeviceManager.startFirmwareUpdate()` function. * Listen to the `FirmwareUpdatingState` flow to build UI screens showing the progress and result of the update. | State | Description | | -------------------------------------- | ---------------------------------------------------------------------- | | `FirmwareUpdatingState.Downloading` | The SDK retrieves all necessary files from Adyen. | | `FirmwareUpdatingState.UpdatingDevice` | The SDK installs the update on the card reader. | | `FirmwareUpdatingState.Applying` | The SDK applies the update. The card reader will reboot and reconnect. | | `FirmwareUpdatingState.Finished` | Indicates the process has completed. | * Errors that can happen during software updates are: | Error | Description | | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DownloadFailed` | Downloading the firmware update files did not succeed. | | `UpdateFailed(needsBluetoothRecovery: Boolean)` | The update was not completed. The variable `needsBluetoothRecovery` indicates if the Bluetooth update failed and card reader must be reset before retrying the update. | If the card reader must be reset before retrying the update, your UI should provide the following instructions: > 1. In the Bluetooth settings of the mobile device, select "Forget This Device". > 2. Re-pair the reader and retry installing the update. * As an example, [see the built-in UI screens](/point-of-sale/mobile-android/understand#updating-card-reader). ### Connecting through USB using a dock A USB connection is automatically established when the user connects the dock to power, connects the mobile device to the dock using the USB-C cable provided with the card reader, and places the reader in the dock. Your UI must clearly show the difference between these situations: * The mobile device is connected to the dock through USB, but no card reader is placed in the dock. * The card reader is placed in the dock, but the mobile device is connected to the card reader through Bluetooth. * The card reader is placed in the dock and the mobile device is connected to the dock and the reader through USB. To build your UI for dock connections: * Connect to a dock by calling the `connectToDock(usbAccessory: UsbAccessory)` function. * Check if the connection succeeded by listening to the `DeviceManager.dockConnectionState: StateFlow` flow. If the state changes to **DockState.Connected**, the reader and the dock were was connected successfully. * To refresh the USB connection to the dock, call the `refreshConnectionStateForAttachedDock()` function. * To disconnect from a dock, call the `disconnect()` function. * As an example, [see the built-in UI screens](/point-of-sale/mobile-android/understand#connecting-usb-dock). When connected through USB, the card reader is always ready for use without delay. There is no need to reconnect to the card reader before initiating a transaction. ### Updating the dock firmware You must check for new firmware updates regularly, and provide a way to update the dock to the latest version. Firmware updates for the dock are critical for secure transactions, and do not happen automatically. It is required that you build logic into your POS app to: * Check for new firmware updates often. For example, when the app is launched. * Handle the firmware update flow correctly within your POS app. Your UI must prompt end users to update the dock firmware, and inform them about the progress and result of the update process. To build your UI: * Check if updates are available by calling the `DeviceManager.dockFirmwareUpdateSummary()` function. If a new update is available, the result returns `AvailableDockFirmwareUpdate` and the date by which transactions will be refused if the dock is not updated to this firmware version. If there is no new update, a null value is returned. * Start the update using the `DeviceManager.startDockFirmwareUpdate()` function. * Listen to the `FirmwareUpdatingState` flow to build UI screens showing the progress and result of the update. | State | Description | | -------------------------------------- | ------------------------------------------------- | | `FirmwareUpdatingState.Downloading` | The SDK retrieves all necessary files from Adyen. | | `FirmwareUpdatingState.UpdatingDevice` | The SDK installs the update on the dock. | | `FirmwareUpdatingState.Applying` | The SDK applies the update. | | `FirmwareUpdatingState.Finished` | Indicates the process has completed. | * Errors that can happen during software updates are: | Error | Description | | ---------------- | ------------------------------------------------------ | | `DownloadFailed` | Downloading the firmware update files did not succeed. | * As an example, [see the built-in UI screens](/point-of-sale/mobile-android/understand#updating-usb-dock). ### List of DeviceManager tools `DeviceManager` lets you scan for card readers through Bluetooth device discovery, connect and disconnect a card reader, connect and disconnect a USB dock, get information about the currently connected card reader, get information about the currently USB dock, and manage firmware updates. You can use the following functions: | DeviceManager tool | Description | | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `connectionState: StateFlow` | Returns the connection state of the currently connected card reader. | | `startBluetoothDiscovery(): Flow>>` | Starts scanning for nearby card readers. Returns an error if scanning failed. | | `stopBluetoothDiscovery()` | Stops scanning for nearby card readers. | | `getKnownDevices(): List` | Returns a list of paired card readers. | | `connect(cardReaderDevice: CardReaderDevice)` | Connects to a card reader. | | `disconnect()` | Disconnects from the currently connected card reader or USB dock. | | `firmwareUpdateSummary(): Result` | Returns information about available firmware updates for the card reader that is currently connected to the mobile device. The result includes:- `requiresBluetoothConnection`: indicates if a Bluetooth connection is required to update the card reader. - `requiredDate`: **null**, or the date by which transactions will be refused if the card reader is not updated to this firmware version. | | `startFirmwareUpdate()` | Implements the available card reader firmware update. Shows an error if the update failed because the card reader was connected through USB and the update required a Bluetooth connection. | | `dockConnectionState: StateFlow` | Returns the connection state of the currently connected USB dock. | | `connectToDock(usbAccessory: UsbAccessory)` | Connects to a USB dock. | | `dockFirmwareUpdateSummary(): Result` | Returns information about available firmware updates for the USB Dock that is currently connected to the mobile device. The result includes:- `requiredDate`: **null**, or the date by which transactions will be refused if the dock is not updated to this firmware version. | | `startDockFirmwareUpdate()` | Implements the available firmware update for the USB dock. | | `refreshConnectionStateForAttachedDock()` | Refreshes the connected and attached status of USB dock. | ## 7. Decide on transaction UI options In this step you decide on the options that are available for customizing the transaction user interface. If you want the UI to appear in dark mode, you need to enable this options in the settings of your mobile device. When you [start a transaction](#payment), you can customize the user interface using `merchantUiParameters` and the optional fields for the following UI options: ** #### Add a logo To show a logo on your mobile device during the transaction flow use: * `merchantLogo`: Format can be SVG (recommended), WEBP, JPEG, or PNG. The logo is shown at 100dp x 32dp (width x height) on top of the payment screen. Trim any transparent pixels from the logo asset, to show your logo as large as possible within the available space. ```kotlin (...) merchantUiParameters = MerchantUiParameters.create( merchantLogo = R.drawable.merchant_logo, // ... ) ``` ** #### Configure success screen duration To specify how long the success screen shows after a successful transaction use: * `autoDismissDelay`: If not specified, this success screen is dismissed after 4 seconds. You can set a time in milliseconds with a minimum of 0.5 seconds (500L) and a maximum of 4 seconds (4000L). ```kotlin (...) merchantUiParameters = MerchantUiParameters.create( autoDismissDelay = 3.seconds, // ... ) ``` ** #### Customize position of the NFC tap indicator By default, the NFC tap indicator is an animation that points at the back of the device. You can customize the position of the NFC tap indicator on your mobile device screen to point at the location of your card reader. Then use`CardReaderAnimationType`. This allows you to show a chevron-type arrow (`Simplified`), pointing in the specified direction. If there is not enough room on the mobile device screen, no chevron-type arrow can be shown. Instead, a tap indicator is shown in the center of the screen. * Options for `Simplified` are: `CenterRight`, or `CenterLeft`. | CenterLeft | CenterRight | | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/simplified_centerleft.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/simplified_centerright.svg?decoding=auto\&fetchpriority=auto) | ```kotlin (...) merchantUiParameters = MerchantUiParameters.create( cardReaderUiParameters = CardReaderUiParameters.create( animation = CardReaderAnimationType.simplified(Position.Center), ), // ... ) ``` ## 8. Handle a payment In this step you add code to start a transaction with: * A [Terminal API](/point-of-sale/design-your-integration/terminal-api) payment request. * The card reader as the payment interface to use. To let the Mobile SDK handle transactions: 1. In your POS app, create a Terminal API payment request with: * `MessageHeader.POIID`: the installation ID of the SDK. * If you create the Terminal API payment request in your POS app, use `InPersonPayments.getInstallationId()` as the `POIID` in the [MessageHeader](/point-of-sale/design-your-integration/terminal-api#request-message-header) of the request. * If you create the Terminal API payment request in the backend, this uses the [installationId](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-installationId) from the [`/auth/certificate` response](#session). * The remaining `MessageHeader` parameters and the request body. For details, see [Make a payment](/point-of-sale/basic-tapi-integration/make-a-payment) and [PaymentRequest](https://docs.adyen.com/api-explorer/terminal-api/latest/post/payment). 2. Create an instance of `TransactionRequest` using `TransactionRequest.create(nexoRequest)`, and pass the Terminal API payment request from your POS app or backend. ```kotlin val transactionRequest = TransactionRequest.create(nexoRequest) ``` 3. Get a `PaymentInterface` from `InPersonPayments` using `InPersonPayments.getPaymentInterface(CardReader)`. ```kotlin 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. ```kotlin InPersonPayments.registerForPaymentResult(context) { result -> result.fold( onSuccess = { paymentResult: PaymentResult -> /* * Here is your success case logic, for example: * if (paymentResult.success) "Payment Successful" else "Payment Unsuccessful" */ }, onFailure = { error: Throwable -> /* * Here is your failure case logic, for example: * Log.e("InPersonPaymentsResult", "Payment Failed", error) */ }, ) ``` 5. Invoke `InPersonPayments.performTransaction()` with your transaction data, payment launcher, and [authentication service](/point-of-sale/mobile-ios/build/card-reader#enable-transactions). Then customize the user interface using `merchantUiParameters` with the [optional fields that you decided on](#transaction-ui-options) in the previous step. The following example invokes `InPersonPayments.performTransaction()` and includes parameters to add a custom logo and customizing the position of the NFC tap indicator on your mobile device screen: ```kotlin InPersonPayments.performTransaction( context = this@DevAmountEntryActivity, paymentLauncher = paymentLauncher, paymentInterface = result.paymentInterface, transactionRequest = result.transactionRequest, merchantUiParameters = MerchantUiParameters.create( merchantLogo = R.drawable.merchant_logo, tapToPayUiParameters = TapToPayUiParameters.create( animation = TapToPayAnimationType.front(NfcFrontPosition.TopCenter), ), cardReaderUiParameters = CardReaderUiParameters( animation = 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. ## 9. Handle a refund There are two types of refund: [referenced](#referenced-refund) and [unreferenced](#unreferenced-refund). 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](/point-of-sale/basic-tapi-integration/refund-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](/point-of-sale/basic-tapi-integration/refund-payment/refund-webhooks), 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. Create a Terminal API reversal request with: * `MessageHeader.POIID`: the installation ID of the SDK. * If you create the Terminal API reversal request in your POS app, use `InPersonPayments.getInstallationId()` as the `POIID` in the [MessageHeader](/point-of-sale/design-your-integration/terminal-api#request-message-header) of the request. * If you create the Terminal API reversal request in the backend, this uses the [installationId](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-installationId) from the [`/auth/certificate` response](#session). * The remaining `MessageHeader` parameters and the request body. For details, see [Referenced refund](/point-of-sale/basic-tapi-integration/refund-payment/referenced) and [ReversalRequest](https://docs.adyen.com/api-explorer/terminal-api/latest/post/reversal). 2. Create an instance of `TransactionRequest` using `TransactionReversalRequest.createReversal(nexoRequest: String)`, 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. ```kotlin 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](/#enable-transactions), which extracts the `sdkData` from the server response. ```kotlin public suspend fun performReversal( TransactionReversalRequest: TransactionReversalRequest, ): Result ``` 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 `paymentType` parameter: ```kotlin /* * Assuming you've defined serializable [PaymentRequest] and [PaymentData] classes */ val paymentRequest = PaymentRequest( paymentData = PaymentData( paymentType = PaymentType.Refund, ), ) ``` This means you can use the same code as for [handling a payment](#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](/point-of-sale/basic-tapi-integration/refund-payment/unreferenced). ## 10. Check the transaction status We recommend checking the transaction status if your POS app doesn't receive a payment response, for example due to a communication or technical issue. In your Android POS app, add code for the following steps: 1. Call `InPersonPayments.getTransactionStatus()`, passing a `TransactionStatusRequest`: ```kotlin val transactionStatusResult: Result = InPersonPayments.getTransactionStatus(request) ``` 2. Check the `TransactionStatusResult` that you receive: * `data`: the serialized NEXO `TransactionStatusResponse` JSON string. Deserialize this to check the `Result`/`ErrorCondition`. * `success`: `false` if the transaction was found but the original transaction failed, or if no matching transaction was found. Use this if you only need a quick pass/fail check. For a detailed explanation of the request and response, see [Verify transaction status](/point-of-sale/basic-tapi-integration/verify-transaction-status). ## 11. Diagnose the device We recommend implementing the Terminal API diagnosis request. This enables you to do the following: * Check for **security threats** that would block transactions. If threats are detected that an operator can solve, the response includes information about the cause and solution of the problem. * Check the **expiry date of the SDK** that is used on the mobile device. [Transactions will be blocked](/point-of-sale/mobile-android/manage/#keep-the-mobile-sdk-up-to-date) if mandatory updates are not carried out. In your Android POS app, add code for the following steps: 1. Create a Terminal API diagnosis request with: * `MessageHeader.POIID`: the installation ID of the SDK. * If you create the Terminal API diagnosis request in your POS app, use `InPersonPayments.getInstallationId()` as the `POIID` in the [MessageHeader](/point-of-sale/design-your-integration/terminal-api#request-message-header) of the request. * If you create the Terminal API diagnosis request in the backend, this uses the [installationId](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate#responses-201-installationId) from the [`/auth/certificate` response](#session). * The remaining `MessageHeader` parameters. * The request body consisting of `DiagnosisRequest.HostDiagnosisFlag`: set to **true**, so that the SDK will try to perform a security scan. For details, see [Diagnose a Mobile SDK solution](/point-of-sale/diagnostics/request-diagnosis#diagnosis-request-mobile) and [DiagnosisRequest](https://docs.adyen.com/api-explorer/terminal-api/latest/post/diagnosis). 2. Create an instance of `DiagnosisRequest` using `DiagnosisRequest.create(nexoRequest)`, and pass the Terminal API diagnosis request from your POS app or backend. ```kotlin val diagnosisRequest = DiagnosisRequest.create(nexoRequest) ``` 3. Invoke `InPersonPayments.performDiagnosis()` with your diagnosis data and diagnosis launcher. ```kotlin InPersonPayments.performDiagnosis(diagnosisRequest) ``` 4. Check the response that you received: * Base64-decode the `attestationStatus` value for information about any security issues. If issues are detected that can be resolved by the end user, the resulting JSON object includes messages with the details. These are the same [error messages](/point-of-sale/mobile-android/troubleshooting#error-messages-for-the-android-sdk) that we show automatically on the end user's mobile device when these issues are detected during a transaction. * See the `sdkExpiry` for the date when the installed SDK version expires. For a detailed explanation of the response, see [Diagnose a Mobile SDK solution](/point-of-sale/diagnostics/request-diagnosis#diagnosis-request-mobile). ## 12. (Optional) Clear the session token There are several situations when you need to clear the existing session to remove all session information from your mobile device. When you have cleared the session, configuration updates are fetched and a new session is established when you start a new transaction or call the warm-up function. As a best practice, clear the session to: * **Re-establish a session after switching between merchant accounts or stores in your POS app and your Customer Area**. If the device is reassigned from store A to store B and a transaction is started there, on the Adyen side the transaction will continue to appear to belong to store A instead of store B. Clearing the session prevents this issue. * **Force a refresh of the configuration**. After clearing the session, the latest configuration is fetched and stored on your mobile device the next time the Mobile SDK for Android connects to the Adyen backend. * **Test the session establishment flow in your POS app**, specifically how it interacts with your and Adyen's backend to securely establish a session with the Mobile SDK for Android. To clear the session: 1. Explicitly clear the communication session using `InPersonPayments.clearSession()` . 2. [Establish a new communication session](#session). ## 13. (Optional) Optimize app size with Play Feature Delivery It is possible to optimize the size of your Android Mobile SDK-enabled app with [Android's Play Feature Delivery](https://developer.android.com/guide/playcore/feature-delivery/on-demand).\ With Play Feature Delivery, you can separate features from the base module of your app. This means that instead of a large, single download, users initially receive only your app's core functionalities (embedded features). Users can then later download and install the other (dynamic) features on demand. As a result, you will have a smaller initial app size, stay within [Google Play's size limits](https://support.google.com/googleplay/android-developer/answer/9859372?hl=en), and provide a faster download experience. After you have followed the [Google Play instructions](https://developer.android.com/guide/playcore/feature-delivery) to set up your app to use Play Feature Delivery, do the following: * Add this dependency to your app module implementation: `com.adyen.ipp:dynamic-base:$version` * Add the other SDK dependencies to your feature module as you would normally do. [Refer to this example implementation](https://github.com/Adyen/adyen-pos-mobile-android/tree/main/app-dynamic) of the Adyen POS Mobile SDK using an Android Dynamic Feature Module. ## Other supported features In addition to [payments](#payment), [refunds](#referenced-refund), and [diagnosis](#diagnosis), 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](#unreferenced-refund) 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 | | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | Diagnosis | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | [Partial authorization](/point-of-sale/partial-authorizations/) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | Payment | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | [Pre-authorization](/point-of-sale/pre-authorisation/) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | Refund, referenced | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | Refund, unreferenced | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | [Store and forward](/point-of-sale/offline-payment/) offline payments | ![-x-](/user/data/smileys/emoji/x.png "-x-") | | [Surcharge](/point-of-sale/surcharge/) | ![-x-](/user/data/smileys/emoji/x.png "-x-") | | [Tax-free shopping](/point-of-sale/shopper-recognition/tax-free-shopping/) | ![-x-](/user/data/smileys/emoji/x.png "-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: * [White-green test card](/point-of-sale/testing-pos-payments/test-card-v3/) * [Blue-green test card](/point-of-sale/testing-pos-payments/test-card-v2/) 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: 1. If new to Adyen, get a [live account](/get-started-with-adyen/#apply-for-your-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](#get-the-live-sdk). You need to generate a new, live API key exclusively for downloading the SDK. 3. [Use the live endpoint for establishing a session](#establish-a-live-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](/point-of-sale/mobile-android/manage#google-play). 5. [Register your app with Adyen](/point-of-sale/mobile-android/manage#register-app). ### 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](https://ca-live.adyen.com/), generate a [new API key](#add-sdk) 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` artifacts. 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 `Implementation 'com.adyen.ipp:pos-mobile-:$version'` where `` can be release (for production builds) or debug (for debug or development builds). ```groovy val version = "LATEST_SDK_VERSION" releaseImplementation 'com.adyen.ipp:pos-mobile-release:$version' // Be aware that importing additional modules will increase the size of your application. // To optimize your app's size and build times, only include the specific payment features you require. releaseImplementation 'com.adyen.ipp:payment-tap-to-pay-release:$version' releaseImplementation 'com.adyen.ipp:payment-card-reader-release:$version' ``` ### Establish a live session When going live, you must change the [/auth/certificate](https://docs.adyen.com/api-explorer/softpos-configuration-api/latest/post/auth/certificate) endpoint where you send requests to establish a secure communication session, as well as the API key that you use to authenticate those requests. * To access the live endpoint, generate a new API key from your [live Customer Area](https://ca-live.adyen.com/). * The live endpoint URL to use depends on the region: * Australia: `https://softposconfig-live-au.adyen.com/softposconfig/v{version}/auth/certificate` * Northeast Asia: `https://softposconfig-live-nea.adyen.com/softposconfig/v{version}/auth/certificate` * Europe: `https://softposconfig-live.adyen.com/softposconfig/v{version}/auth/certificate` * North East Asia: `https://softposconfig-live-nea.adyen.com/softposconfig/v{version}/auth/certificate` * United States: `https://softposconfig-live-us.adyen.com/softposconfig/v{version}/auth/certificate` ## Next steps [required](/point-of-sale/mobile-android/manage) [![](/user/themes/adyen/images/illustrations/settings.svg)](/point-of-sale/mobile-android/manage) ###### [Manage your solution](/point-of-sale/mobile-android/manage) [Make your solution available and keep the software up-to-date.](/point-of-sale/mobile-android/manage) [![](/user/themes/adyen/images/illustrations/checkmark.svg)](/point-of-sale/mobile-android/checklists) ###### [Checklists](/point-of-sale/mobile-android/checklists) [Get a list of what needs to be done to get started and go live with a Mobile solution.](/point-of-sale/mobile-android/checklists) [![](/user/themes/adyen/images/illustrations/close.svg)](/point-of-sale/mobile-android/troubleshooting) ###### [Error handling](/point-of-sale/mobile-android/troubleshooting) [Resolve errors that appear on the mobile Android device.](/point-of-sale/mobile-android/troubleshooting) [![RFID card icon](/user/pages/reuse/image-library/01.icons/rfid-card/rfid-card.svg?decoding=auto\&fetchpriority=auto)](/point-of-sale/user-manuals/nyc1) ###### [Use the card reader](/point-of-sale/user-manuals/nyc1) [Learn how to connect and operate the NYC1 card reader.](/point-of-sale/user-manuals/nyc1) [![RFID card icon](/user/pages/reuse/image-library/01.icons/rfid-card/rfid-card.svg?decoding=auto\&fetchpriority=auto)](/point-of-sale/user-manuals/nyc1-with-dock) ###### [Use the card reader with a dock](/point-of-sale/user-manuals/nyc1-with-dock) [Learn how to connect and operate the NYC1 card reader and NYC1 dock.](/point-of-sale/user-manuals/nyc1-with-dock)