Accept payments using Tap to Pay for Android with Stripe
Tags: stripe, android
Published: 2023-06-21 20:07:32

---
title: Accept payments using Tap to Pay for Android with Stripe
published: true
description: In this tutorial, learn how to implement Tap to Pay with Stripe in your Android application, using Kotlin.
tags: stripe, android
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/alfgt365wq7jn61x0zxk.png
# Use a ratio of 100:42 for best results.
---

Stripe recently announced Tap to Pay for Android. Let’s look into how to implement this so you can start collecting in-person payments directly using a mobile phone! In this blog post, let’s pretend that you work for a (fake) food truck company called CoolBeans and you need to create an Android app so you can collect payments on the go.

Here’s what we’ll be building: 

![GIF of the demo Android application with 3 screens, one to connect to a Terminal, then one to indicate an amount, and finally, the Tap to Pay screen.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bzwmu37yxiukxz2het6m.gif)

This tutorial uses Kotlin; however, this feature can also be implemented using Java. If you need more information, please refer to [our docs](https://stripe.com/docs/terminal/payments/connect-reader?reader-type=tap-to-pay-on-android) or check out the [Stripe Terminal Android SDK repository](https://github.com/stripe/stripe-terminal-android) that contains an example in both Kotlin and Java.

If you learn better with videos, you can check out [the video tutorial on YouTube](https://youtu.be/2y0abSgxPXw)!

## Prerequisites

To follow along with this tutorial, you will need:
- Android SDK version 33
- AndroidStudio
- Kotlin 1.7.10
- Stripe account
- [Stripe Terminal Android SDK](https://github.com/stripe/stripe-terminal-android)

If you are using a different version of the Android SDK or Kotlin, the code samples in this tutorial may still work but this is not guaranteed.

This post will only cover the code samples needed to get Tap to Pay working in your Android application. I will purposely not cover how to build and structure an Android app or go into details on the specific flow I implemented as this is more relative to what you want to build.

If you want to get started quickly, you can [clone my demo application on GitHub](https://github.com/stripe-samples/tap-to-pay-android-demo), follow the instructions in the README to make sure you **update the URL to the back-end server**, and follow along as I explain the different parts.

After cloning and running the demo application, you should see the following screens. The first part of the application is responsible for connecting your mobile phone as a reader. Then, the user can input an amount, and finally collect the payment using Tap to Pay.

![GIF of the demo Android application with 3 screens, one to connect to a Terminal, then one to indicate an amount, and finally, the Tap to Pay screen.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bzwmu37yxiukxz2het6m.gif)

## Install the Stripe Terminal Android SDK

### Configuration

If you’ve already cloned the [Tap to Pay demo application](https://github.com/stripe-samples/tap-to-pay-android-demo), you can skip this section. If you have already integrated with the Stripe Terminal Android SDK before and would like to update it to also use Tap to Pay, keep reading to understand the few changes you will need to make.

In your module’s `build.gradle` file, replace the Stripe Terminal dependencies with the latest version and add the `stripeterminal-localmobile` package:

```kotlin
implementation "com.stripe:stripeterminal-localmobile:2.20.0"
implementation "com.stripe:stripeterminal-core:2.20.0"
```

### Permissions

The Stripe Terminal Android SDK requires different permissions depending on the type of terminal device you’re using. For Tap to Pay, Bluetooth and Location are required.

To do this, make sure you’re adding the following lines to the AndroidManifest file:

```kotlin
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```

And in your application logic, check that the permission is granted before continuing. If the user has granted Bluetooth permission, you can start initializing a terminal instance.

## Initializing a Terminal instance

One of the first things you need to do is initialize a terminal instance with the following code:

```kotlin
Terminal.initTerminal(
  applicationContext, LogLevel.VERBOSE, TokenProvider(),
  TerminalEventListener()
)
```

This will initialize a terminal for the given application context that you need to pass as the first parameter. Then you can pass the level of logging verbosity, an instance of the ConnectTokenProvider interface to use when a new token is needed and finally, a listener to inform of events in the Terminal lifecycle.

If you have used the Stripe Terminal Android SDK before, you should already have created the interface for the token provider and the listener. If not, feel free to check the [TokenProvider](https://github.com/stripe-samples/tap-to-pay-android-demo/blob/main/app/src/main/java/com/example/taptopayandroid/TokenProvider.kt) and [TerminalEventListener](https://github.com/stripe-samples/tap-to-pay-android-demo/blob/main/app/src/main/java/com/example/taptopayandroid/TerminalEventListener.kt) files in the demo application. 

## Loading locations

To use Stripe Terminal, you need to register one or more locations to manage readers and their activity by associating them with a physical location. Even though food trucks can be located in various places, there are regulations and parking permits needed to sell food in the street so CoolBeans will likely have a set list of locations where the business can operate. You can create these locations via [the Stripe Dashboard](https://dashboard.stripe.com/terminal/locations) or using the [API](https://stripe.com/docs/api/terminal/locations/create).

![Stripe dashboard showing 2 locations for the fake customer's food trucks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jziuni78lud6l7daaovv.png)

This way, the activity of a reader associated with a location will be reflected in the dashboard and you will be able to collect data about how the different locations are performing in terms of sales.

In your Android app, you can then list your locations, passing a limit for the number of locations you would like to fetch, and a callback. If you’re following along with the demo application, this code can be found in the [MainActivity.kt file](https://github.com/stripe-samples/tap-to-pay-android-demo/blob/6553e8f408a81ce02863e49f75b86fe2e7527ac2/app/src/main/java/com/example/taptopayandroid/MainActivity.kt#L235).

```kotlin
Terminal.getInstance().listLocations(
  ListLocationsParameters.Builder().apply {
      limit = 100
  }.build(),
  locationCallback
)
```
 
The callback will save the location objects in a mutable list.

```kotlin
private val mutableListState = MutableStateFlow(LocationListState())

private val locationCallback = object : LocationListCallback {
  override fun onFailure(e: TerminalException) {
      e.printStackTrace()
  }

  override fun onSuccess(locations: List<Location>, hasMore: Boolean) {
      mutableListState.value = mutableListState.value.let {
          it.copy(
              locations = it.locations + locations,
              hasMore = hasMore,
              isLoading = false,
          )
      }
  }
}
```

Once you have fetched your locations, you need to write the logic that will identify the reader you will use.

## Discovering readers

To find available readers and select the one you want to use, you need to call the `discoverReaders` method.

When implementing Tap to Pay, the configuration object needs to have its discoveryMethod argument set to `DiscoveryMethod.LOCAL_MOBILE` so it considers the mobile phone running the application as a terminal device. 

```kotlin
val config = DiscoveryConfiguration(
           timeout = 0,
           discoveryMethod = DiscoveryMethod.LOCAL_MOBILE,
           isSimulated = false,
           location = mutableListState.value.locations[0].id
       )
```

Call the `discoverReaders` method passing this configuration object as well as a DiscoveryListener instance that will provide the list of discovered readers.


```kotlin
Terminal.getInstance().discoverReaders(config, discoveryListener = object :
DiscoveryListener {
override fun onUpdateDiscoveredReaders(readers: List<Reader>) {
   // Filtering the list of readers discovered to only store the ones currently online
   readers.filter { it.networkStatus != Reader.NetworkStatus.OFFLINE }
  // For simplicity, I’m only using the first reader retrieved but this code would need to be updated if you wanted to show the list in the UI and let the user select a specific location.
   var reader = readers[0]
   // Handle the connection to the reader in a separate function
   connectToReader(reader)


}, object : Callback {
  override fun onSuccess() {
     println("Finished discovering readers")
  }
  override fun onFailure(e: TerminalException) {
     e.printStackTrace()
  }
})
```

## Connecting a reader to a location

Now that we listed our locations and discovered the reader we want to use, let’s connect the location to the reader using the `connectLocalMobileReader` method in the `connectToReader` function created in the code sample above.

```kotlin
private fun connectToReader(reader: Reader){
	// Pass the location chosen to the LocalMobileConnectionConfiguration method.
       val config = ConnectionConfiguration.LocalMobileConnectionConfiguration("${mutableListState.value.locations[0].id}")
	// Call the connectLocalMobileReader method passing the reader selected, the config object and a callback function.
       Terminal.getInstance().connectLocalMobileReader(
           reader,
           config,
           object: ReaderCallback {
               override fun onFailure(e: TerminalException) {
                   e.printStackTrace()
               }


               override fun onSuccess(reader: Reader) {
                   // [Optional] Update the UI with the location name and terminal ID to indicate to the user that the reader is successfully connected.
                   runOnUiThread {
                       val manager: FragmentManager = supportFragmentManager
                       val fragment: Fragment? = manager.findFragmentByTag(ConnectReaderFragment.TAG)


                       if(reader.id !== null && mutableListState.value.locations[0].displayName !== null){
                           (fragment as ConnectReaderFragment).updateReaderId(
                               mutableListState.value.locations[0].displayName!!, reader.id!!
                           )
                       }
                   }
               }
           }
       )
   }
```

## Collecting payment

When the reader is successfully connected, you should be ready to accept payments the same way as you would when using other Terminal devices. You’ll need a back-end server with endpoints to create a connection token and handle the different events that happen when capturing and processing payments with Stripe. If you don’t already have one, you can clone our [Stripe Terminal backend example repo](https://github.com/stripe/example-terminal-backend) and follow the instructions in the README to set it up.

### Creating a payment intent

First, you need to create a [PaymentIntent](https://stripe.com/docs/payments/payment-intents). To do this, your Android application will need to make a POST request to your server implementing the Stripe API. In the demo application, the [ApiClient singleton object](https://github.com/stripe-samples/tap-to-pay-android-demo/blob/main/app/src/main/java/com/example/taptopayandroid/ApiClient.kt) handles the calls to the back-end and provides a `createPaymentIntent` method you can call and pass payment details including the amount, currency, authorization details and a callback.
For simplicity, the code sample below uses hardcoded values but you would need to adapt your application to your particular use case.

```kotlin
ApiClient.createPaymentIntent(
   amount = “100”.toLong() ,
   currency = “usd”,
   extendedAuth = false,
   incrementalAuth = false,
   callback = object : retrofit2.Callback<PaymentIntentCreationResponse> {
       override fun onResponse(
           call: Call<PaymentIntentCreationResponse>,
           response: Response<PaymentIntentCreationResponse>
       ) {
           if (response.isSuccessful && response.body() != null) {
		// Retrieve the payment intent once it is created successfully
               Terminal.getInstance().retrievePaymentIntent(
                   response.body()?.secret!!,
                   createPaymentIntentCallback
               )
           } else {
               println("Request not successful: ${response.body()}")
           }
       }
       override fun onFailure(
           call: Call<PaymentIntentCreationResponse>,
           t: Throwable
       ) {
           t.printStackTrace()
       }
   }
)
```

When the payment intent is successfully created, the code sample above calls the `retrievePaymentIntent` method with the secret present in the response body to retrieve the Payment Intent.
The code sample below shows how the callback function is then implemented.

```kotlin
private val createPaymentIntentCallback by lazy {
   object : PaymentIntentCallback {
       override fun onSuccess(paymentIntent: PaymentIntent) {
		collectPaymentMethod(paymentIntent)
       }
       override fun onFailure(e: TerminalException) {
           e.printStackTrace()
       }
   }
}
```

### Collecting a payment method

After the Payment Intent is created and retrieved, we can pass it into the `collectPaymentMethod` method with a callback and the tipping configuration.

```kotlin
private fun collectPaymentMethod(paymentIntent: PaymentIntent){
  // Hardcoded for the purpose of this tutorial
   val skipTipping = true
   val collectConfig = CollectConfiguration.Builder()
       .skipTipping(skipTipping)
       .build()


   Terminal.getInstance().collectPaymentMethod(
       paymentIntent, collectPaymentMethodCallback, collectConfig
   )
}

private val collectPaymentMethodCallback by lazy {
   object : PaymentIntentCallback {
       override fun onSuccess(paymentIntent: PaymentIntent) {
	    processPayment(paymentIntent)
       }


       override fun onFailure(e: TerminalException) {
           e.printStackTrace()
       }
   }
}
```

When the payment method is successfully collected, we can start processing the payment.

### Processing and capturing the payment

To process the payment, you need to call the `processPayment` method passing the Payment Intent and a callback function that will capture the payment.

```kotlin
private fun processPayment(paymentIntent: PaymentIntent){
   Terminal.getInstance().processPayment(paymentIntent, processPaymentCallback)
}

private val processPaymentCallback by lazy {
   object : PaymentIntentCallback {
       override fun onSuccess(paymentIntent: PaymentIntent) {
           ApiClient.capturePaymentIntent(paymentIntent.id)
	    // Return to previous screen
           navigateTo(PaymentDetails.TAG, PaymentDetails(), true)
       }
       override fun onFailure(e: TerminalException) {
           e.printStackTrace()
       }
   }
}
```

At this point, if all the previous steps were successful, you should be able to run your application, connect your mobile phone as a reader, see the Tap to Pay screen after calling the `collectPaymentMethod`, and tap a credit card behind your device to process the payment. CoolBeans is now ready to accept payment on the go!

## Conclusion

If you have used the [Stripe Terminal Android SDK](https://github.com/stripe/stripe-terminal-android) before, you only need to make minimal code changes to update your application to implement the Tap to Pay feature. If you want to get started quickly, feel free to clone the [repository of the demo application](https://github.com/stripe-samples/tap-to-pay-android-demo) and customize it to adapt to your use case. You can also check out [our documentation](https://stripe.com/docs/terminal/payments/connect-reader?reader-type=tap-to-pay-on-android) and the [Stripe Terminal Android SDK repository](https://github.com/stripe/stripe-terminal-android) if you want to learn more.

We hope you’ll share with us how you’re planning to use Tap to Pay with Stripe!

You can also stay up to date with Stripe developer updates on the following platforms:
📣 Follow [@StripeDev](https://twitter.com/stripedev) and [our team](https://twitter.com/i/lists/1459720002567868418) on [Twitter](https://twitter.com/StripeDev)
📺 Subscribe to our [YouTube channel](https://www.youtube.com/StripeDevelopers)
💬 Join the official [Discord server](https://discord.com/invite/RuJnSBXrQn)
📧 Sign up for the [Dev Digest](https://go.stripe.global/dev-digest)

## About the author

![Charlie Gerard's profile picture. She is a caucasian woman with long brown hair, wearing glasses. She is standing in front of a white wall with purple lights.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i2d816y9ckxwy2giqfct.png)

[Charlie Gerard](https://twitter.com/@devdevcharlie) is a Developer Advocate at Stripe, a [published author](https://www.amazon.com/Practical-Machine-Learning-JavaScript-TensorFlow-js/dp/1484264177) and a [creative technologist](https://charliegerard.dev/). She loves researching and [experimenting with technologies](https://charliegerard.dev/). When she’s not coding, she enjoys spending time outdoors, reading and setting herself random challenges.