# 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. # Internet Readers Smart readers run Stripe reader software to communicate directly with Stripe over the internet. Connecting your app to a smart reader requires three steps: 1. [Register a reader](https://docs.stripe.com/terminal/payments/connect-reader.md#register-reader) to your Stripe account. 1. [Discover readers](https://docs.stripe.com/terminal/payments/connect-reader.md#discover-readers) with the SDK. 1. [Connect to a reader](https://docs.stripe.com/terminal/payments/connect-reader.md#connect-reader) with the SDK. ## Register a reader [Server-side] Before you can connect your application to a smart reader, you must register the reader to your account. #### Dashboard ### Register in the Dashboard You can add your reader directly in the [Dashboard](https://dashboard.stripe.com/test/terminal). #### Register by registration code 1. In the [Readers](https://dashboard.stripe.com/terminal/readers) page, click **Register reader**. 1. If you have a [smart reader](https://docs.stripe.com/terminal/smart-readers.md), enter the key sequence `0-7-1-3-9` to display a unique registration code. If you have a BBPOS WisePOS E or Stripe Reader S700/S710, go to the [reader settings](https://docs.stripe.com/terminal/payments/setup-reader/bbpos-wisepos-e.md#settings) and tap **Generate pairing code**. 1. Enter the registration code then click **Next**. 1. Optionally, choose a name for the reader. 1. If you already created a Location, select the reader’s new Location. Otherwise, create a Location by clicking **+ Add new**. 1. Click **Register** to finish registering your reader. #### Register by serial number 1. In the [Readers](https://dashboard.stripe.com/terminal/readers) page, click **Register reader**. 1. Find the serial number on the device and enter the serial number. To register multiple devices at once, you can enter multiple serial numbers separated by commas. 1. Optionally, choose a name for the reader. 1. If you already created a Location, select the reader’s new Location. Otherwise, create a Location by clicking **+ Add new**. 1. Click **Register** to finish registering your reader. #### Register by hardware order 1. In the [Hardware orders](https://dashboard.stripe.com/terminal/hardware_orders) page, find an order with a status of either “shipped” or “delivered.” Click the overflow menu (⋯) at the end of the row, then click **Register**. 1. On the **Register Readers** page, select one or more readers from the hardware order to register, then click **Register**. 1. Optionally, choose a name for the reader. If you selected multiple readers, the name serves as a prefix and we name the readers sequentially (for example, for a given input of “Test reader”, we name the readers “Test reader 1”, “Test reader 2”, and so on). 1. If you already created a Location, select the reader’s new Location. Otherwise, create a Location by clicking **+ Add new**. 1. Click **Register** to finish registering your readers. #### API For larger deployments, enable users in the field to receive and set up new readers on their own. In your app, build a flow to [register](https://docs.stripe.com/api/terminal/readers/create.md) a reader with the Stripe API. 1. If you have a [smart reader](https://docs.stripe.com/terminal/smart-readers.md), enter the key sequence `0-7-1-3-9` to display a unique registration code. If you have a BBPOS WisePOS E or Stripe Reader S700/S710, go to the [reader settings](https://docs.stripe.com/terminal/payments/setup-reader/bbpos-wisepos-e.md#settings) and tap **Generate pairing code**. 1. The user enters the code in your application. 1. Your application sends the code to Stripe: ```curl curl https://api.stripe.com/v1/terminal/readers \ -u "<>:" \ -d registration_code={{READER_REGISTRATION_CODE}} \ --data-urlencode "label=Alice's reader" \ -d "location={{TERMINALLOCATION_ID}}" ``` To confirm that you’ve registered a reader correctly, list all the readers you’ve registered at that location: #### curl ```bash curl https://api.stripe.com/v1/terminal/readers \ -u <>: ``` ## Discover readers [Client-side] - [discoverReaders (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)discoverReaders:delegate:completion:) - [InternetDiscoveryConfiguration (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPInternetDiscoveryConfiguration.html) After registering the reader to your account, search for previously registered readers to connect to your point-of-sale application with `discoverReaders`, using `InternetDiscoveryConfiguration`. You can scope your discovery using the `location` you registered the reader to in the previous step. #### Swift ```swift import StripeTerminal class DiscoverReadersViewController: UIViewController, DiscoveryDelegate { func discoverReadersAction() throws { let config = try InternetDiscoveryConfigurationBuilder() .setLocationId(""{{LOCATION_ID}}"") .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") } } } } ``` When discovering smart readers, the `DiscoveryDelegate.didUpdateDiscoveredReaders` method is only called once per call to `discoverReaders`. `didUpdateDiscoveredReaders` returns an empty list of readers if there are no registered readers or if there are no readers associated with the given location. If you make a subsequent `discoverReaders` call to refresh the list, you must first cancel the previous call with the `Cancelable` returned by `discoverReaders`. The `InternetDiscoveryConfiguration` supports an optional `timeout` value for discovering readers online. This ensures quicker fallback to offline reader discovery if the online attempt fails. ## Connect to a reader [Client-side] > In version `5.1.0` of the iOS SDK, you can use the `easyConnect` method to combine reader discovery and connection into a single API call to simplify integration. See the [SDK migration guide](https://docs.stripe.com/terminal/references/sdk-migration-guide.md#update-your-reader-connection-usage-ios) for details. To connect your point-of-sale application to a reader, call `connectReader` with the selected reader, using the `InternetConnectionConfiguration`. To prevent connection failures, make sure you always pass a reader object from the most recent discovery results. Don’t cache or reuse reader objects from a previous discovery session. #### Swift ```swift let config: InternetConnectionConfiguration do { config = try InternetConnectionConfigurationBuilder(delegate: yourInternetReaderDelegate) .build() } catch { // Handle error building the connection configuration return } Terminal.shared.connectReader(selectedReader, connectionConfig: config) { reader, error in if let reader = reader { print("Successfully connected to reader: \(reader)") } else if let error = error { print("connectReader failed: \(error)") } } ``` - [connectReader (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)connectReader:connectionConfig:completion:) - [InternetConnectionConfiguration](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPInternetConnectionConfiguration.html) ### Multiple connections Only one instance of the Stripe Terminal SDK can connect to a reader at a given time. By default, when you call `connectReader` from another application, the incoming connection replaces the existing SDK-to-reader connection, and the previously connected SDK disconnects from the reader. The `connectReader` method takes a configuration object with a `failIfInUse` property, whose default value is `false`. When your application sets `failIfInUse` to true, the `connectReader` call has an alternate behaviour where the incoming connection fails when the reader is in the middle of a `collectPaymentMethod` or `confirmPaymentIntent` call initiated by another SDK. If the reader is connected to another SDK but is idle (displaying the splash screen before `collectPaymentMethod` is called), setting `failIfInUse` has no change to the connection behaviour, and the incoming connection request can always break the existing SDK-to-reader connection. #### Swift ```swift let internetReaderDelegate = yourInternetReaderDelegate // implement your internetReaderDelegate let config: InternetConnectionConfiguration do { config = try InternetConnectionConfigurationBuilder(delegate: internetReaderDelegate) .setFailIfInUse(true) .build() } catch { // Handle error building the connection configuration return } Terminal.shared.connectReader(selectedReader, connectionConfig: config, completion: { reader, error in // ... }) ``` | | failIfInUse is false (default) | failIfInUse is true | | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `connectReader` called from a new SDK when the reader is idle. | The existing SDK-to-reader connection breaks and the new SDK connects to the reader. The next command from the previously-connected SDK fails with a reader error and that app’s `didDisconnect` method is called. | The existing SDK-to-reader connection breaks and the new SDK connects to the reader. The next command from the previously-connected SDK fails with a reader error and that app’s `didDisconnect` method is called. | | `connectReader` called from a new SDK when the reader is mid-transaction. | The existing SDK-to-reader connection breaks and the new SDK connects to the reader. The next command from the previously-connected SDK fails with a reader error and that app’s `didDisconnect` method is called. | The incoming connection fails with a reader error. The existing SDK-to-reader connection doesn’t break and the command in progress continues. | For the least-disruptive connection experience in multi-reader environments, we recommend setting `failIfInUse` to `true` on your application’s initial connection attempt. Then, allow your users to retry the connection with `failIfInUse` set to `false` if the connection fails the first time. With this setup, one of your users can’t accidentally interrupt a transaction by inadvertently connecting to an in-use reader, but can still connect if needed. ### Handle disconnects - [didDisconnect (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Protocols/SCPReaderDelegate.html#/c:objc\(pl\)SCPReaderDelegate\(im\)reader:didDisconnect:) Your app must implement the `reader(_, didDisconnect:)` callback to handle when a reader disconnects. When you implement this callback, display a UI that notifies your user of the disconnected reader. You can call `discoverReaders` to scan for readers and initiate reconnection. Your app can attempt to automatically reconnect to the disconnected reader or display a UI that prompts your user to reconnect to a different reader. The reader can disconnect from your app if it loses connection to the network. To simulate an unexpected disconnect, power off the reader. #### Swift ```swift import StripeTerminal class ReaderViewController: UIViewController, InternetReaderDelegate { 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 } } ``` ### Automatic reconnection 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 that persistent store for a saved serial number. If one is found, call the `discoverReaders` method so your application can try to find that reader again. 1. 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 taking place. ## 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.