Getting Started with the iOS SDK Invite Only

    Set up the Stripe Terminal iOS SDK, and the BBPOS reader, so that you can begin accepting in-person payments.

    Setting up the iOS SDK requires just four steps:

    1. Set up the iOS SDK (via the SDK)
    2. Set up the connection token endpoint (SDK and backend)
    3. Initialize the SDK (SDK)
    4. Discover the reader (SDK)

    After completing these steps, you can collect your first payment. Before developing your app, you might find it helpful to first examine our example app.

    Try out the example app (optional)

    The Stripe Terminal SDK includes an example iOS app, which you can use to familiarize yourself with the SDK before starting your own integration. The SDK also includes a reader simulator, so you can begin your integration before receiving a physical reader. To get started with the example app, navigate to our repo on GitHub.

    Step 1: Set up the iOS SDK

    1. If you haven't already done so, install the latest version of CocoaPods.
    2. Add this line to your Podfile:
      pod 'StripeTerminal', '1.0.0-b5'
    3. Run the following command:
      pod install
    4. From here on, use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file.

    In the future, to update to the latest version of the SDK, just run:

    pod update StripeTerminal

    1. Navigate to our GitHub releases page, download StripeTerminal.framework.zip, and unzip it.

    2. Drag StripeTerminal.framework to your Xcode project’s General settings > Embedded Binaries section. Make sure to select Copy items if needed.

    3. Navigate to your Xcode project settings' Build Phases section, and create a new Run Script Build Phase. Paste the following snippet into the text field:
      bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/StripeTerminal.framework/integrate-framework.sh"
    4. When new versions of the SDK are released, repeat the first two steps to update your installation.

    Configure your app

    You must enable location services in order to use the iOS SDK. Add the following key-value pair to your app’s Info.plist file:

    • Privacy – Location When In Use Usage Description
      • Key: NSLocationWhenInUseUsageDescription
      • Value: “Location access is required in order to accept payments.”

    For your app to run in the background and remain connected to the reader, add this key-value pair to your Info.plist file:

    • Required background modes
      • Key: UIBackgroundModes
      • Value: “bluetoooth-central” (Uses Bluetooth LE accessories)

    For your app to pass validation when submitting to the App Store, add the following key-value pairs as well:

    • Privacy – Bluetooth Peripheral Usage Description
      • Key: NSBluetoothPeripheralUsageDescription
      • Value: “Bluetooth access is required in order to connect to supported card readers.”
    • Privacy – Microphone Usage Description
      • Key: NSMicrophoneUsageDescription
      • Value: “Microphone access is required in order to connect to supported card readers.”

    Although we do not currently support any audio-jack card readers, you still need to add a Microphone Usage Description to your Info.plist to pass App Store validation, as our underlying hardware SDK accesses these APIs. Since the SDK never uses microphone access, your users should not see a prompt for the microphone permission.

    Step 2: Set up the connection token endpoint

    To connect to a reader, the iOS SDK needs to retrieve a short-lived connection token from Stripe, proxied through your server. On your backend, add an endpoint that creates a connection token and returns its value.

    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"
    
    # Using Sinatra
    post 'terminal_connection_token' do
      token = Stripe::Terminal::ConnectionToken.create
      token.to_json
    end
    
    # 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"
    
    # Flask
    from flask import Flask, jsonify, request
    @app.route('terminal_connection_token', methods=['POST'])
    def terminal_connection_token():
        token = stripe.terminal.ConnectionToken.create()
        return jsonify(token)
    
    // 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
    var stripe = require("stripe")("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    // Using Express
    app.post('terminal_connection_token', (req, res) => {
        var token = stripe.terminal.connectionTokens.create().then((token) => {
            res.status(200).json(token);
        }).catch((err) => {
            res.status(500).end();
        });
    });
    
    // 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";
    
    // Using Spark framework (http://sparkjava.com)
    post(new Route("terminal_connection_token") {
        @Override
        public Object handle(final Request request,
                             final Response response) {
            try {
                Map<String, Object> params = new HashMap<String, Object>();
                ConnectionToken token = ConnectionToken.create(params);
                return token.getRawJson();
            } catch (StripeException e) {
                response.status(500);
                return e;
            }
        }
    });
    
    // 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"
    
    // net/http
    func terminalConnectionTokenHandler(w http.ResponseWriter, r *http.Request, customerId string) {
        token, err := New(&stripe.TerminalConnectionTokenParams{})
        if err != nil {
            log.Printf("Stripe bindings call failed, %v
    ", err)
            w.WriteHeader(500)
            return
        }
        w.Write(token.RawJSON)
    }
    

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

    import StripeTerminal
    import Alamofire
    
    class APIClient: ConnectionTokenProvider {
    
        // Your backend should call v1/terminal/connection_tokens and return the JSON response from Stripe
        func fetchConnectionToken(_ completion: @escaping ConnectionTokenCompletionBlock) {
            let url = yourBackendUrl.appendingPathComponent("terminal_connection_token")
            Alamofire.request(url, method: .get, parameters: params)
                .responseJSON { responseJSON in
                    switch responseJSON.result {
                    case .success(let json):
                        let json = value as! [String: AnyObject]
                        completion(json["secret"], nil)
                    case .failure(let error):
                        completion(nil, error)
                    }
            }
        }
    }
    

    This function is called whenever the SDK is initialized. It's also called when a new 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 token from your backend, connecting to a reader fails with the error from your server.

    Step 3: Initialize the SDK

    The Terminal object made available by the Stripe Terminal SDK exposes a generic interface for discovering readers, connecting to a reader, and creating payments. To initialize a Terminal instance, provide your ConnectionTokenProvider implemented in Step 2. The delegate you provide can be used to handle events from the SDK, such as disconnects.

    let config = TerminalConfiguration()
    let terminal = Terminal(configuration: config,
                            // Your app's API client can implement ConnectionTokenProvider
                            tokenProvider: self.apiClient,
                            delegate: self)
    

    Step 4: Connect to a reader

    After setting up your reader, call terminal.discoverReaders in your application to find and display readers to connect to. You will need to provide a DiscoveryDelegate to handle updating your app as the SDK updates the list of discovered readers. You can use the readerSimulator device type to discover a reader simulator, which you can use to develop and test your integration without any physical hardware.

    let config = DiscoveryConfiguration(deviceType: .readerSimulator)
    let cancelable = terminal.discoverReaders(config, delegate: self) { error in
        if let error = error {
            self.print("Failed to discover: \(error)")
        }
        else {
            self.print("Finished discovering readers")
        }
    }
    

    When creating a DiscoveryConfiguration, you can optionally specify a method. Note that this initializer returns an optional value because not all deviceType and method combinations are valid. For the Chipper 2X, we recommend using the bluetoothProximity discovery method, which lets you discover a single reader by holding it next to the iOS device.

    if let config = DiscoveryConfiguration(deviceType: .chipper2X, method: .bluetoothProximity) {
        let cancelable = terminal.discoverReaders(config, delegate: self) { error in
            // ...
        }
    }
    

    When discovering readers, your application can either display an updating list of discovered readers, or automatically select a reader (e.g. if you are discovering using bluetooth proximity). To connect to a reader, call connectReader.

    // MARK: DiscoveryDelegate
    func terminal(_ terminal: Terminal, didUpdateDiscoveredReaders readers: [Reader]) {
        self.readers = readers
        self.tableView.reloadData()
    }
    
    // MARK: UITableViewDelegate
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let reader = readers[indexPath.row]!
        self.terminal.connectReader(reader) { reader, error in
            if let reader = reader {
                self.print("Connected to reader: \(reader)")
                self.dismiss(animated: true, completion: nil)
            }
            else if let error = error {
                self.print("Error connecting to reader: \(error)")
            }
        }
    }
    

    Behind the scenes, the Terminal object uses the fetchConnectionToken function that you defined in Step 2 to fetch a connection token, if it does not already have one. It then uses the connection token and reader information to create a reader session.

    Next step

    Congratulations! You've set up your SDK and reader. Next, collect your first Stripe Terminal payment.

    Questions?

    We're always happy to help with code or other questions you might have! Search our documentation, contact support, or connect with our sales team. You can also chat live with other developers in #stripe on freenode.

    Was this page helpful? Yes No

    Send

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