--- title: "Tap to Pay on Android" description: "Integrate your Android POS app with the Mobile SDK to make Tap to Pay payments." url: "https://docs.adyen.com/point-of-sale/mobile-android/build/tap-to-pay" source_url: "https://docs.adyen.com/point-of-sale/mobile-android/build/tap-to-pay.md" canonical: "https://docs.adyen.com/point-of-sale/mobile-android/build/tap-to-pay" last_modified: "2023-05-10T21:46:00+02:00" language: "en" --- # Tap to Pay on Android Integrate your Android POS app with the Mobile SDK to make Tap to Pay payments. [View source](/point-of-sale/mobile-android/build/tap-to-pay.md) With our Tap to Pay on Android solution you can accept contactless in-person payments using an Android mobile device as the payment interface, and process these payments on the Adyen payments platform. ## 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** | An Android commercial off-the-shelf (COTS) mobile device with an integrated NFC reader. Must not be a payment terminal. 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 Tap to Pay on Android. | | **Setup steps** | Before you begin:- Ask our [Support Team](https://ca-test.adyen.com/ca/ca/contactUs/support.shtml?form=other) enable your Adyen test account for using our Mobile solutions. - [Order](/point-of-sale/managing-terminals/order-terminals#sales-order-steps) a test card. | ## 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, 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. The SDK initializes automatically, but you can change that. 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 Mobile SDK. 3. The SDK initiates the transaction on the card reader.\ To complete the payment, the customer taps the NFC reader on your mobile device 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. Our SDK only runs on Android 12 or later. You can still [target earlier Android versions](https://developer.android.com/build/manage-manifests#override_uses-sdk_for_imported_libraries), but our SDK 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 = 2.2.0 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` * Asia Pacific South East: `https://softposconfig-live-apse.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 Mobile SDK [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 Mobile SDK, add the following code to the POS app's `AndroidManifest.xml` file to disable the Mobile SDK. ```kotlin ``` To manually initialize that component at a later point, add the following code to the POS app. For Mobile SDK 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 Mobile SDK 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 Mobile SDK 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 Mobile SDK 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. 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 The default UI is designed for devices with the NFC antenna at the top rear. It shows an animation that points at the antenna's location on the back of the device. If the NFC antenna on your device is located in a different position, you can customize the position of the tap indicator on the mobile device screen. You need to identify the position of the device's NFC antenna and use `tapToPayUiParameters`. Options can be `Directional` to show a chevron-type arrow, pointing in the specified direction, or `Front` to a static image of an NFC logo. * Options for `Directional` are: `TopCenter`, or `BottomCenter`. | TopCenter | BottomCenter | | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_TopCenter.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_BottomCenter.svg?decoding=auto\&fetchpriority=auto) | ```kotlin (...) merchantUiParameters = MerchantUiParameters.create( tapToPayUiParameters = TapToPayUiParameters.create( animation = TapToPayAnimationType.directional(NfcDirectionalPosition.BottomCenter), ), // ... ) ``` * Options for `Front` are: `TopCenter`, `Center`, or `BottomCenter`. | TopCenter | Center | BottomCenter | | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_NFCTopCenter.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_NFCCenter.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_NFCBottomCenter.svg?decoding=auto\&fetchpriority=auto) | ```kotlin (...) merchantUiParameters = MerchantUiParameters.create( tapToPayUiParameters = TapToPayUiParameters.create( animation = TapToPayAnimationType.front(NfcFrontPosition.BottomCenter), ), // ... ) ``` ** #### Customize position of NFC tap indicator in kiosk mode When in kiosk mode, the default position of the tap indicator is at the bottom-center of the device screen. You need to identify the position of your mobile device's NFC antenna to customize the position of the NFC tap indicator on your mobile device screen in [kiosk mode](/point-of-sale/mobile-android/understand#kiosk-mode). Then use `TapToPayKioskAnimationType` for placement options for the NFC tap indicator. The indicator can either be a static image of an NFC logo (`Front`) or an animated chevron-type arrow (`Directional`). * Options for `Front` are: `TopCenter`, `Center`, `BottomCenter`, `LeftCenter`, or `RightCenter`. | TopCenter | Center | BottomCenter | | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_NFCTopCenter.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_NFCCenter.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_NFCBottomCenter.svg?decoding=auto\&fetchpriority=auto) | | LeftCenter | RightCenter | | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_NFCLeftCenter.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_NFCRightCenter.svg?decoding=auto\&fetchpriority=auto) | ```kotlin (...) merchantUiParameters = MerchantUiParameters.create( kioskModeUiParameters = KioskModeUiParameters.create( tapToPayKioskAnimation = TapToPayKioskAnimationType.front(NfcFrontPosition.TopCenter), ), // ... ) ``` From mobile SDK version 2.16.0 or later, you can also position the NFC indicator at any location on the screen using fractional `xBias` and `yBias` values. Values range from 0.0 (left/top) to 1.0 (right/bottom). ```kotlin (...) merchantUiParameters = MerchantUiParameters.create( kioskModeUiParameters = KioskModeUiParameters.create( tapToPayKioskAnimation = TapToPayKioskAnimationType.front(xBias = 0.5f, yBias = 0.8f), ), // ... ) ``` * Options for `Directional` are: `TopLeftFacingLeft`, `TopLeft`, `TopCenter`, `TopRight`, `TopRightFacingRight`, `CenterLeft`, `CenterRight`, `BottomLeftFacingLeft`, `BottomLeft`, `BottomCenter` (default value), `BottomRight`, or `BottomRightFacingRight`. | TopLeft | TopCenter | TopRight | | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_TopLeft.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_TopCenter.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_TopRight.svg?decoding=auto\&fetchpriority=auto) | | BottomLeft | BottomCenter | BottomRight | | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_BottomLeft.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_BottomCenter.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_BottomRight.svg?decoding=auto\&fetchpriority=auto) | | BottomLeftFacingLeft | TopRightFacingRight | BottomRightFacingRight | | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_BottomLeftFacingLeft.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_TopRightFacingRight.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_BottomRightFacingRight.svg?decoding=auto\&fetchpriority=auto) | | TopLeftFacingLeft | CenterLeft | CenterRight | | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_TopLeftFacingLeft.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_CenterLeft.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/kiosk_CenterRight.svg?decoding=auto\&fetchpriority=auto) | ```kotlin (...) merchantUiParameters = MerchantUiParameters.create( kioskModeUiParameters = KioskModeUiParameters.create( tapToPayKioskAnimation = TapToPayKioskAnimationType.directional(NfcDirectionalPosition.CenterLeft), ), // ... ) ``` ** #### Customize position of PIN input area in kiosk mode To customize the position of the PIN input area on your mobile device screen in [kiosk mode](/point-of-sale/mobile-android/understand#kiosk-mode) use `PinInputAlignment`. * Options are: `Left` or `Right` (default value). | Left | Right | | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/pin-Left-Portrait.svg?decoding=auto\&fetchpriority=auto) | ![](/user/pages/reuse/pos-mobile-sdk/android-build/transaction-ui-options/pin-Right-Portrait.svg?decoding=auto\&fetchpriority=auto) | ```kotlin (...) merchantUiParameters = MerchantUiParameters.create( kioskModeUiParameters = KioskModeUiParameters.create( tapToPayKioskAnimation = TapToPayKioskAnimationType.directional(NfcDirectionalPosition.CenterLeft), pinInputAlignment = PinInputAlignment.Right, ), // ... ) ``` ** #### Apply kiosk mode on all smaller tablets To enable [kiosk mode](/point-of-sale/mobile-android/understand#kiosk-mode) on all tablets with a screen width that is smaller than 9 inches, then you need to pass `applyToAllTablets = true` in your `KioskModeUiParameters` object. * Possible values are: * **true**: Globally enables kiosk mode on all tablets within this screen width range. * **false**: Disables global kiosk mode enforcement for tablets within this screen width range. ## 6. 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 NFC reader on your mobile device. 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(TapToPay)`. ```kotlin val paymentInterface = InPersonPayments.getPaymentInterface(TapToPay) ``` 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-android/build/tap-to-pay#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 and shows the transaction screen on your mobile device. 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. ## 7. 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). ## 8. 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). ## 9. (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). ## 9. (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 Tap to Pay 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/) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | | [Tax-free shopping](/point-of-sale/shopper-recognition/tax-free-shopping/) | ![-white\_check\_mark-](/user/data/smileys/emoji/white_check_mark.png "-white_check_mark-") | ## 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 = 2.2.0 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` * Asia Pacific South East: `https://softposconfig-live-apse.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)