# Connect to a reader Connect your application to a Stripe Terminal reader. > If you haven’t chosen a reader yet, compare the available [Terminal readers](https://docs.stripe.com/terminal/payments/setup-reader.md) and choose one that best suits your needs. # Bluetooth Readers Bluetooth-connected readers are Bluetooth LE devices. They collect payment details, but rely on a paired mobile device for communication with Stripe. Follow these steps to connect your app to a Terminal reader using Bluetooth: 1. [Discover readers](https://docs.stripe.com/terminal/payments/connect-reader.md#discover-readers). 2. [Connect to a reader](https://docs.stripe.com/terminal/payments/connect-reader.md#connect-reader). > Don’t use mobile device settings to pair with your reader. Pairing the reader through device settings makes the reader unavailable to connect to your app. ## Discover readers [Client-side] - [discoverReaders (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)discoverReaders:delegate:completion:) - [BluetoothScanDiscoveryConfiguration (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPBluetoothScanDiscoveryConfiguration.html) To start, make sure your reader is powered on and within close proximity. Then from your app, search for nearby Bluetooth-connected readers with the `discoverReaders` method, using `BluetoothScanDiscoveryConfiguration`. #### Swift ```swift import StripeTerminal class DiscoverReadersViewController: UIViewController, DiscoveryDelegate { var discoverCancelable: Cancelable? // ... // Action for a "Discover Readers" button func discoverReadersAction() throws { let config = try BluetoothScanDiscoveryConfigurationBuilder().build() // In addition to Terminal's completion block methods, Swift async alternatives are available. // See our Example app for usage examples: https://github.com/stripe/stripe-terminal-ios/tree/master/Example self.discoverCancelable = Terminal.shared.discoverReaders(config, delegate: self) { error in if let error = error { print("discoverReaders failed: \(error)") } else { print("discoverReaders succeeded") } } } // ... // MARK: DiscoveryDelegate func terminal(_ terminal: Terminal, didUpdateDiscoveredReaders readers: [Reader]) { // In your app, display the discovered readers to the user. // Call `connectReader` after the user selects a reader to connect to. } } ``` #### Bluetooth proximity * (BBPOS Chipper 2X BT only) Bluetooth proximity filters search results to return the closest reader. When discovered, the reader flashes multicoloured lights, allowing your user to identify the discovered reader among many other readers. After the SDK discovers a reader, it won’t switch to a closer reader unless you turn off the discovered reader. Note that when using Bluetooth proximity, the SDK returns the reader to your app’s callback twice. The first time, your app receives a `Reader` object populated with only the reader’s serial number. After a short delay, your app receives the same `Reader` object populated with new information, such as the reader’s battery level. We recommend displaying the discovered reader in your app’s UI, letting the user either confirm connection to the reader or cancel if they don’t want to connect to this reader. #### Bluetooth scan Bluetooth scan searches for all nearby readers and returns a list of discovered readers to your app. As the discovery process continues, the SDK continues to invoke the `DiscoveryDelegate.didUpdateDiscoveredReaders` method with the latest list of nearby readers. During the discovery process, the Terminal’s `SCPConnectionStatus` transitions to `SCPConnectionStatus.SCPConnectionStatusDiscovering` while the discovery is in progress. With the Bluetooth scan discovery method, you can set a timeout to scan for a set period of time, which you can use for managing battery life or triggering an error message if no devices are found. In your mobile application, we recommend displaying an auto-updating list of discovered readers with serial numbers to help users identify their mobile reader. The `label` property isn’t populated for mobile readers during reader discovery. If you need to display friendly names for readers, maintain your own mapping of serial numbers to labels in your application. #### Bluetooth pairing To improve security and comply with EU regulations, as of November 2025 Stripe uses the numeric comparison Bluetooth pairing process for WisePad 3 card readers. The numeric comparison process requires you to verify a passkey on both your card reader and the POS device when pairing. After you update your device to the [latest software version](https://docs.stripe.com/terminal/readers/bbpos-wisepad3.md#reader-software-releases), follow these steps when connecting your WisePad 3 to a new mobile application. After your POS device discovers and displays the WisePad 3 reader: 1. Verify that the 6-digit code matches on both the WisePad 3 and on the POS device. 2. Select **Confirm** on the WisePad 3. 3. Select **Pair** on your POS device. > #### Note > > You only need to do numeric comparison pairing when you pair a WisePad 3 with a new POS device or when re-pairing with an existing “forgotten” POS device. ## Connect to a reader [Client-side] - [connectReader (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)connectReader:connectionConfig:completion:) - [BluetoothConnectionConfiguration (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPBluetoothConnectionConfiguration.html) To connect to a discovered reader, call the `connectReader` method from your app. Always pass a reader object from the most recent discovery results. Don’t cache or reuse reader objects from a previous discovery session, because stale reader objects can cause connection failures. Mobile readers don’t need to be registered in the Dashboard or API ahead of time. Instead, you associate your mobile reader with a [location](https://docs.stripe.com/api/terminal/locations.md) at connection time. To do so, create and use a `BluetoothConnectionConfiguration` with the `locationId` set to the relevant location ID when connecting. #### Swift ```swift // Call `connectReader` with the selected reader and a connection config // to register to a location as set by your app. var connectionConfig: BluetoothConnectionConfiguration do { connectionConfig = try BluetoothConnectionConfigurationBuilder(delegate: yourMobileReaderDelegate, locationId: ""{{LOCATION_ID}}"") .build() } catch { // Handle the error building the connection configuration return } Terminal.shared.connectReader(selectedReader, connectionConfig: connectionConfig) { reader, error in if let reader = reader { print("Successfully connected to reader: \(reader)") } else if let error = error { print("connectReader failed: \(error)") } } ``` For your app to run in the background and remain connected to the reader, [configure your app](https://docs.stripe.com/terminal/payments/setup-integration.md?terminal-sdk-platform=ios#configure) to include the required background mode. > #### Use standby mode > > Don’t program your app to call `disconnectReader` to conserve power. The reader efficiently handles power management using its standby mode. ## Handle reader disconnects - [MobileReaderDelegate (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Protocols/SCPMobileReaderDelegate.html) - [DisconnectReason (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPDisconnectReason.html) Reader disconnects can sometimes occur between your app and the reader. For example, the reader can disconnect from your app if it’s out of range, or runs out of battery. You can simulate an unexpected disconnect while testing by powering off the reader. The `MobileReaderDelegate` includes a `reader:didDisconnect:` method that provides your application with the `DisconnectReason` to help identify why the reader disconnected. To handle reader disconnects yourself, you can do the following: 1. Set `autoReconnectOnUnexpectedDisconnect` to `false` during connection. 2. Handle the disconnect callback to display a message in the app alerting the user that the reader unexpectedly disconnected and initiate reader discovery and connection. #### Swift ```swift import StripeTerminal class ReaderViewController: UIViewController, MobileReaderDelegate { override func viewDidLoad() { super.viewDidLoad() // Set the reader delegate when connecting to a reader } // ... func reader(_ reader: Reader, didDisconnect reason: DisconnectReason) { // Consider displaying a UI to notify the user and start rediscovering readers } } ``` ### Reboot the connected reader - [rebootReader (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)rebootReader:.html) Stripe Reader M2 and BBPOS WisePad 3 automatically reboot after operating for 24 hours. However, you can force the reader to reboot and reset its 24-hour timer by using the `rebootReader` API. After this action, the reader disconnects from the SDK and then reboots. If you’re using automatic reconnect, the SDK attempts to restore the connection with the reader. #### Swift ```swift Terminal.shared.rebootReader { error in if let error = error { // Placeholder for handling the error } else { // Reboot succeeded and the reader will disconnect. // If your app is using automatic reconnect the reconnect will begin. } } ``` #### Automatically attempt reconnection When a reader disconnects, we automatically attempt reconnection by default and recommend that you display notifications in your app relaying the reader status throughout the process. To display notifications in your app during automatic reconnection, do the following: 1. Implement the reader reconnect callbacks in the `MobileReaderDelegate`. 2. Pass the `MobileReaderDelegate` to your `BluetoothConnectionConfiguration`. 3. When the SDK sends [`reader:didStartReconnect:disconnectReason:`](https://stripe.dev/stripe-terminal-ios/docs/Protocols/SCPReaderDelegate.html#/c:objc\(pl\)SCPReaderDelegate\(im\)reader:didStartReconnect:disconnectReason:) to your app, display a message announcing that the reader lost connection and reconnection is in progress. - You can use the `Cancelable` object to stop the reconnection attempt at any time. 4. When the SDK indicates successful reconnection by sending [`readerDidSucceedReconnect:`](https://stripe.dev/stripe-terminal-ios/docs/Protocols/SCPReaderDelegate.html#/c:objc\(pl\)SCPReaderDelegate\(im\)readerDidSucceedReconnect:), display a message announcing the connection was restored and to continue normal operations. 5. If the SDK can’t reconnect to the reader and sends both [`readerDidFailReconnect:`](https://stripe.dev/stripe-terminal-ios/docs/Protocols/SCPReaderDelegate.html#/c:objc\(pl\)SCPReaderDelegate\(im\)readerDidFailReconnect:) and `reader:didDisconnect:`, display a message stating that an unexpected disconnect occurred. #### Swift ```swift import StripeTerminal extension ReaderViewController: MobileReaderDelegate { // MARK: MobileReaderDelegate func reader(_ reader: Reader, didStartReconnect cancelable: Cancelable, disconnectReason: DisconnectReason) { // 1. Notified at the start of a reconnection attempt // Use cancelable to stop reconnection at any time } func readerDidSucceedReconnect(_ reader: Reader) { // 2. Notified when reader reconnection succeeds // App is now connected } func readerDidFailReconnect(_ reader: Reader) { // 3. Notified when reader reconnection fails // App is now disconnected } } ``` #### Automatic reconnection on application start Stripe Terminal doesn’t automatically reconnect to a reader when your application starts. Instead, you can build a reconnection flow by storing reader IDs and attempting to connect to a known reader on startup. 1. When you successfully connect to a reader, save its serial number in a persistent data storage location, such as the [UserDefaults API](https://developer.apple.com/documentation/foundation/userdefaults) (iOS). 1. When your app launches, check the persistent data storage location for a saved serial number. If one is found, call the `discoverReaders` method so your application can try to find that reader again. 2. If the saved serial number matches any of the discovered readers, try connecting to that reader with the matching reader object returned from the call to `discoverReaders`. If the previously connected reader isn’t found, stop the discovery process. Display some UI during the discovery and connection process to indicate that an automatic reconnection is happening. ## Update reader software [Client-side] - [MobileReaderDelegate (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Protocols/SCPMobileReaderDelegate.html) Your application must update mobile readers to apply the following: - Regional configurations that keep you up to date with card network and issuer requirements - Security updates Required updates start installing on connection to the reader. You can’t use the reader until updating completes. > To install updates, the reader’s battery level must be higher than 50%. ### Required updates When immediately required updates are available for the reader, the integration’s `MobileReaderDelegate` receives the `didStartInstallingUpdate` callback with a `ReaderSoftwareUpdate`. The `ReaderSoftwareUpdate` provides the necessary details of the update, including an estimate of the total update duration, indicated by `durationEstimate`. During the installation process, the Terminal’s `connectionStatus` transitions to `connecting` while the update installs on the reader. Your application must notify users that an update is installing and display the progress in your UI. Make it clear why connecting might take longer than usual. If the required update process fails, Stripe communicates the error to the `MobileReaderDelegate` with `didFinishInstallingUpdate`. You can’t reconnect to the reader after a required update fails, unless the following conditions are met: - The reader runs the latest software version for the location within the last 30 days. - The iOS SDK version is greater than or equal to `3.5.0`. If the conditions are met, the connection process succeeds despite an incomplete update. Stripe retries the required update the next time you connect to that reader until it’s successfully installed. #### Swift ```swift import UIKit import StripeTerminal class ReaderViewController: UIViewController, MobileReaderDelegate { // ... // MARK: MobileReaderDelegate func reader(_ reader: Reader, didStartInstallingUpdate update: ReaderSoftwareUpdate, cancelable: Cancelable?) { // Show UI communicating that a required update has started installing } func reader(_ reader: Reader, didReportReaderSoftwareUpdateProgress progress: Float) { // Update the progress of the installation } func reader(_ reader: Reader, didFinishInstallingUpdate update: ReaderSoftwareUpdate?, error: Error?) { // Report success or failure of the update } // ... } ``` You can cancel required updates using the `Cancelable` object, which also results in a failed connection to the reader. You can’t cancel in-progress incremental-only updates. ### Optional updates You can defer optional updates until the specified date, after which they become required. The SDK notifies you of optional updates through the `MobileReaderDelegate` any time the reader is connected but not performing a transaction. If an optional update is available, your application’s `MobileReaderDelegate` receives the `didReportAvailableUpdate` callback with the `ReaderSoftwareUpdate` object containing the update details, including: - Estimated time for update to complete (`durationEstimate`) - Timestamp after which the update becomes required (`requiredAt`) In your application, notify users that an update is available, and display a prompt to optionally continue with the update. To proceed with the update previously reported with `didReportAvailableUpdate`, call `Terminal.shared.installAvailableUpdate`. The available update is also stored on the reader object as `reader.availableUpdate`. As the update proceeds, block the user from leaving the page in your app, and instruct the user to keep the reader in range and powered on until the update completes. We recommend also providing your user with a visual indicator of the update’s progress. The `MobileReaderDelegate` reports the update’s progress in the `didReportReaderSoftwareUpdateProgress` method. When an optional update’s `requiredAt` date has passed, the update installs the next time the reader is connected. #### Swift ```swift import UIKit import StripeTerminal class ReaderViewController: UIViewController, MobileReaderDelegate { // ... // MARK: MobileReaderDelegate func reader(_ reader: Reader, didReportAvailableUpdate update: ReaderSoftwareUpdate) { // An update is available for the connected reader. Show this update in your application. // Install this update using `Terminal.shared.installAvailableUpdate`. } } ``` See [Testing reader updates](https://docs.stripe.com/terminal/references/testing.md#simulated-reader-updates) to learn more about making sure your application handles the different update types that a reader can have. ## Next steps You’ve connected your application to the reader. Next, [collect your first Stripe Terminal payment](https://docs.stripe.com/terminal/payments/collect-card-payment.md). The BBPOS and Chipper™ name and logo are trademarks or registered trademarks of BBPOS Limited in the United States and/or other countries. The Verifone® name and logo are either trademarks or registered trademarks of Verifone in the United States and/or other countries. Use of the trademarks does not imply any endorsement by BBPOS or Verifone.