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.0"
      // ...
    }
    

    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 change this 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.
    Stripe::Terminal::ConnectionToken.create
    
    # Set your secret key: remember to change this 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.
    stripe.terminal.ConnectionToken.create()
    
    // Set your secret key: remember to change this 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.
    stripe.terminal.connectionTokens.create();
    
    // Set your secret key: remember to change this 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.
    \Stripe\Terminal\ConnectionToken::create();
    
    // Set your secret key: remember to change this 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.
    Map<String, Object> params = new HashMap<String, Object>();
    ConnectionToken.create(params);
    
    // Set your secret key: remember to change this 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{}
    connectiontoken, err := connectiontoken.New(params)
    
    // Set your secret key: remember to change this 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();
    ConnectionToken connectionToken = service.Create(options);
    

    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 ConnectionToken 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 ConnectionToken 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?

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    On this page