Terminal
Android

Getting started with the Android SDK

Set up the Stripe Terminal Android SDK so that you can begin accepting in-person payments.

Getting started with the Android SDK requires five steps:

  1. Install the SDK in your app
  2. Configure your app
  3. Set up the connection token endpoint in your app and backend
  4. Initialize the SDK in your app
  5. Connect your app to the simulated reader

Step 1: Install the SDK Client-side

The Android SDK is compatible with apps supporting Android API level 21 and above. Apps can be written using Kotlin or Java 8, but must use AndroidX.

To install the SDK, add stripeterminal to the dependencies block of your app/build.gradle file:

apply plugin: 'com.android.application' android { ... } dependencies { implementation "com.stripe:stripeterminal:1.0.17" // ... }

Step 2: Configure your app Client-side

You must enable location access in order to use the Android SDK.

Add the ACCESS_FINE_LOCATION permission to your app’s manifest:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.stripe.example.app"> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- Change `StripeTerminalApplication` to your application name --> <application android:name=".StripeTerminalApplication"> <!-- Your content goes here --> </application> </manifest>

Before initializing the Terminal object, add the following check to make sure that the ACCESS_FINE_LOCATION permission is enabled in your app:

if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION}; // REQUEST_CODE_LOCATION should be defined on your app level ActivityCompat.requestPermissions(getActivity(), permissions, REQUEST_CODE_LOCATION); }
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { val permissions = arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION) // REQUEST_CODE_LOCATION should be defined on your app level ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_LOCATION) }

Also verify that the app user grants location permission—the SDK doesn’t function without it. To do this, override the onRequestPermissionsResult method in your app and check the permission result.

@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CODE_LOCATION && grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) { throw new RuntimeException("Location services are required in order to " + "connect to a reader."); } }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { if (requestCode == REQUEST_CODE_LOCATION && grantResults.isNotEmpty() && grantResults[0] != PackageManager.PERMISSION_GRANTED) { throw RuntimeException("Location services are required in order to " + "connect to a reader.") } }

Step 3: Set up the ConnectionToken endpoint Server-side Client-side

Server-side

To connect to a reader, your backend needs to give the SDK permission to use the reader with your Stripe account, by providing it with the secret from a ConnectionToken. Your backend should only create connection tokens for clients that it trusts.

curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' # In a new endpoint on your server, create a ConnectionToken and return the # `secret` to your app. The SDK needs the `secret` to connect to a reader. connection_token = Stripe::Terminal::ConnectionToken.create
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' # In a new endpoint on your server, create a ConnectionToken and return the # `secret` to your app. The SDK needs the `secret` to connect to a reader. connection_token = stripe.terminal.ConnectionToken.create()
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); // In a new endpoint on your server, create a ConnectionToken and return the // `secret` to your app. The SDK needs the `secret` to connect to a reader. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; // In a new endpoint on your server, create a ConnectionToken and return the // `secret` to your app. The SDK needs the `secret` to connect to a reader. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); // In a new endpoint on your server, create a ConnectionToken and return the // `secret` to your app. The SDK needs the `secret` to connect to a reader. let connectionToken = stripe.terminal.connectionTokens.create();
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" // In a new endpoint on your server, create a ConnectionToken and return the // `secret` to your app. The SDK needs the `secret` to connect to a reader. params := &stripe.TerminalConnectionTokenParams{} ct, _ := connectiontoken.New(params)
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; // In a new endpoint on your server, create a ConnectionToken and return the // `secret` to your app. The SDK needs the `secret` to connect to a reader. var options = new ConnectionTokenCreateOptions{}; var service = new ConnectionTokenService(); var connectionToken = service.Create(options);

Obtain the secret from the ConnectionToken on your server and pass it to the client side.

get '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
from flask import jsonify @app.route('/connection_token') def secret(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.get; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); get("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.get('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpGet] public ActionResult Get() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }

Client-side

To give the SDK access to this endpoint, implement the ConnectionTokenProvider interface in your app, which defines a single function that requests a ConnectionToken from your backend.

public class TokenProvider implements ConnectionTokenProvider { @Override public void fetchConnectionToken(ConnectionTokenCallback callback) { try { // Your backend should call /v1/terminal/connection_tokens and return the // JSON response from Stripe. When the request to your backend succeeds, // return the `secret` from the response to the SDK. callback.onSuccess(secret); } catch (Exception e) { callback.onFailure( new ConnectionTokenException("Failed to fetch connection token", e)); } } }
class TokenProvider : ConnectionTokenProvider { override fun fetchConnectionToken(callback: ConnectionTokenCallback) { try { // Your backend should call /v1/terminal/connection_tokens and return the // JSON response from Stripe. When the request to your backend succeeds, // return the `secret` from the response to the SDK. callback.onSuccess(secret) } catch (e: Exception) { callback.onFailure( ConnectionTokenException("Failed to fetch connection token", e) ) } } }

This function is called whenever the SDK is initialized. It’s also called when a new connection token is needed to connect to a reader (for example, when your app disconnects from a reader). If the SDK is unable to retrieve a new connection token from your backend, connecting to a reader fails with the error from your server.

Step 4: Initialize the SDK Client-side

The Android SDK is lifecycle aware. To prevent memory leaks and ensure proper cleanup of long-running Terminal SDK processes, your application must have the Application subclass where TerminalLifeCycleObserver is configured. This subclass should do the following:

  • Register activity lifecycle callbacks
  • Implement the onTrimMemory method to notify the SDK to prune its memory usage
// Substitute with your application name, and remember to keep it the same as your AndroidManifest.xml class StripeTerminalApplication extends Application { @NotNull private final TerminalLifecycleObserver observer; @Override public void onCreate() { super.onCreate(); // Register the observer for all lifecycle hooks observer = TerminalLifecycleObserver.getInstance() registerActivityLifecycleCallbacks(observer); } // Don't forget to let the observer know if your application is running low on memory @Override public void onTrimMemory(level: Int) { super.onTrimMemory(level); observer.onTrimMemory(level, this); } }
// Substitute with your application name, and remember to keep it the same as your AndroidManifest.xml class StripeTerminalApplication : Application() { private val observer: TerminalLifecycleObserver = TerminalLifecycleObserver.getInstance() override fun onCreate() { super.onCreate() // Register the observer for all lifecycle hooks registerActivityLifecycleCallbacks(observer) } // Don't forget to let the observer know if your application is running low on memory override fun onTrimMemory(level: Int) { super.onTrimMemory(level) observer.onTrimMemory(level, this) } }

The Terminal class made available by the Stripe Terminal SDK exposes a generic interface for discovering readers, connecting to a reader, creating payment, and updating reader software.

To get started, provide the current application context, the ConnectionTokenProvider implemented in Step 3, and a TerminalListener object. You can use this listener to handle events from the SDK, such as disconnects. For more information, see Handling disconnects.

// Create your listener object. Override any methods that you want to be notified about TerminalListener listener = new TerminalListener() {}; // Choose the level of messages that should be logged to your console LogLevel logLevel = LogLevel.VERBOSE; // Create your token provider. MyTokenProvider tokenProvider = new MyTokenProvider(); // Pass in the current application context, your desired logging level, your token provider, and the listener you created if (!Terminal.isInitialized()) { Terminal.initTerminal(getActivity(), logLevel, tokenProvider, listener); } // Since the Terminal is a singleton, you can call getInstance whenever you need it Terminal.getInstance();
// Create your listener object. Override any methods that you want to be notified about val listener = TerminalListener {} // Choose the level of messages that should be logged to your console val logLevel = LogLevel.VERBOSE // Create your token provider. val tokenProvider = MyTokenProvider() // Pass in the current application context, your desired logging level, your token provider, and the listener you created if (!Terminal.isInitialized()) { Terminal.initTerminal(activity, logLevel, tokenProvider, listener) } // Since the Terminal is a singleton, you can call getInstance whenever you need it Terminal.getInstance()

Step 5: Connect to the simulated reader Client-side

The Stripe Terminal SDK comes with a built-in simulated card reader, so you can develop and test your app without connecting to physical hardware. Whether your integration is complete or you’re just starting out, use the simulated reader to emulate all the Terminal flows in your app: connecting to a reader, updating reader software, and collecting payments.

Note that the simulated reader does not provide a UI. After connecting to it in your app, you can see it working when calls to the Stripe SDK succeed.

To use the simulated reader, call discoverReaders to search for readers, with the simulated option set to true. When discoverReaders returns a result, call connectReader to connect to the simulated reader.

// Handler for a "Connect Reader" button public void onConnect() { DiscoveryConfiguration config = new DiscoveryConfiguration(0, DeviceType.CHIPPER_2X, true); Terminal.getInstance().discoverReaders(config, readers -> { // Just select the first reader here. Reader firstReader = readers.get(0); Terminal.getInstance().connectReader(firstReader, new ReaderCallback() { @Override public void onSuccess(Reader r) { System.out.println("Connected to reader"); } @Override public void onFailure(@Nonnull TerminalException e) { e.printStackTrace(); } }); }, new Callback() { @Override public void onSuccess() { System.out.println("Finished discovering readers"); } @Override public void onFailure(TerminalException e) { e.printStackTrace(); } } }
// Handler for a "Connect Reader" button fun onConnect() { val config = DiscoveryConfiguration(0, DeviceType.CHIPPER_2X, true) Terminal.getInstance().discoverReaders(config, { readers: List<Reader> -> // Just select the first reader here. val firstReader = readers.first() Terminal.getInstance().connectReader(firstReader, object : ReaderCallback { override fun onSuccess(r: Reader) { println("Connected to reader") } override fun onFailure(e: TerminalException) { e.printStackTrace() } }) }, object : Callback { override fun onSuccess() { println("Finished discovering readers") } override fun onFailure(e: TerminalException) { e.printStackTrace() } }) }

SDK updates

Stripe periodically releases updates to the Stripe Terminal JavaScript SDK, the Stripe Terminal iOS SDK, and the Stripe Terminal Android SDK, which can include new functionality, bug fixes, and security updates. Update your integrated version of the Stripe Terminal JavaScript, iOS, or Android SDK as soon as a new version is available.

Next steps

Congratulations! You integrated the Stripe Terminal SDK into your app and connected to a simulated reader. Next, either continue your integration using the simulated reader to collect payments, or learn about physical reader types.

Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.