Terminal
Connecting to a reader
Verifone P400

Connecting to the Verifone P400

Register your Verifone P400 reader and connect your application over the internet

The Verifone P400 runs Stripe reader software to communicate directly with Stripe over the internet. Connecting your app to an internet-enabled reader requires three steps:

Registering a reader Server-side

Before you can connect your application to the Verifone P400, you must register the reader to your account.

Register in the Dashboard

The simplest way is to add your reader in the Dashboard.

  1. Click on a location, and under the Readers section, click + New.
  2. On your reader, enter the key sequence 0-7-1-3-9 to display a unique registration code.
  3. Enter the code when prompted.

Register using the 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 a reader with the Stripe API.

  1. On the reader, the user enters the key sequence 0-7-1-3-9 to display a unique registration code.
  2. The user enters the code in your application.
  3. Your application sends the code to Stripe:
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d label="Alice's Reader"
# 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' Stripe::Terminal::Reader.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", })
# 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' stripe.terminal.Reader.create( registration_code="{READER_REGISTRATION_CODE}", label="Alice's Reader", )
// 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'); \Stripe\Terminal\Readers::create([ 'registration_code' => "{READER_REGISTRATION_CODE}", 'label' => "Alice's Reader", ]);
// 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"; ReaderCreateParams params = ReaderCreateParams.builder() .setRegistrationCode("{READER_REGISTRATION_CODE}") .setLabel("Alice's Reader") .build(); Reader.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'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", });
// 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" params := &stripe.TerminalReaderParams{ Label: stripe.String("Alice's Reader"), RegistrationCode: stripe.String("{READER_REGISTRATION_CODE}"), } r, _ := reader.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"; var options = new ReaderCreateOptions { RegistrationCode = "{READER_REGISTRATION_CODE}, Label = "Alice's Reader", }; var service = new ReaderService(); service.Create(options);

To confirm that you’ve registered a reader correctly, list all the readers you’ve registered:

curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc:
# 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' Stripe::Terminal::Reader.list
# 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' stripe.terminal.Reader.list()
// 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'); \Stripe\Terminal\Readers::all();
// 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"; ReaderCollectionParams params = ReaderCollectionParams.builder() .build(); ReaderCollection readers = Reader.list(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'); const readers = await stripe.terminal.readers.list();
// 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" i := reader.List(nil) for i.Next() { r := i.TerminalReader() }
// 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"; var service = new ReaderService(); StripeList<Reader> readers = service.List();

Discovering readers Client-side

After registering the reader to your account, search for previously registered readers to connect to your point of sale application using the discoverReaders method.

function discoverReaders() { const config = {simulated: false} terminal.discoverReaders(config).then(function(discoverResult) { if (discoverResult.error) { console.log('Failed to discover: ', discoverResult.error); } else if (discoverResult.discoveredReaders.length === 0) { console.log('No available readers.'); } else { // You should show the list of discoveredReaders to the // cashier here and let them select which to connect to (see below). connectReader(discoverResult); } }); }
async function discoverReaders() { const config = {simulated: false} const discoverResult = await terminal.discoverReaders(config); if (discoverResult.error) { console.log('Failed to discover: ', discoverResult.error); } else if (discoverResult.discoveredReaders.length === 0) { console.log('No available readers.'); } else { // You should show the list of discoveredReaders to the // cashier here and let them select which to connect to (see below). connectReader(discoverResult); } }
import StripeTerminal class ReaderDiscoveryViewController: UIViewController, DiscoveryDelegate { func discoverReadersAction() { let config = DiscoveryConfiguration( discoveryMethod: .internet, simulated: false ) self.discoverCancelable = Terminal.shared.discoverReaders(config, delegate: self) { error in if let error = error { print("discoverReaders failed: \(error)") } else { print("discoverReaders succeeded") } } } }
#import "APPReaderDiscoveryViewController.h" #import <StripeTerminal/StripeTerminal.h> @interface APPReaderDiscoveryViewController () <SCPDiscoveryDelegate> @property (nonatomic, nullable, strong) SCPCancelable *discoverCancelable; // ... @end @implementation APPReaderDiscoveryViewController // Action for a "Discover Readers" button - (void)discoverReadersAction { SCPDiscoveryConfiguration *config = [[SCPDiscoveryConfiguration alloc] initWithDiscoveryMethod:SCPDiscoveryMethodInternet simulated:NO]; self.discoverCancelable = [[SCPTerminal shared] discoverReaders:config delegate:self completion:^(NSError *error) { if (error != nil) { NSLog(@"discoverReaders failed: %@", error); } else { NSLog(@"discoverReaders succeeded"); } }]; }

When discovering Verifone P400 readers, the DiscoveryDelegate.didUpdateDiscoveredReaders method will only be called once per call to discoverReaders. didUpdateDiscoveredReaders will return an empty list of readers if no readers are registered or associated with the given location. If you make a subsequent call to discoverReaders to refresh the list, you must first cancel the previous call with the Cancelable returned by discoverReaders.

Connecting to a reader Client-side

To connect your point of sale application to a reader, call connectReader with the selected reader.

function connectReader(discoverResult) { // Just select the first reader here. var selectedReader = discoverResult.discoveredReaders[0]; terminal.connectReader(selectedReader).then(function(connectResult) { if (connectResult.error) { console.log('Failed to connect: ', connectResult.error); } else { console.log('Connected to reader: ', connectResult.reader.label); } }); }
async function connectReader(discoverResult) { // Just select the first reader here. const selectedReader = discoverResult.discoveredReaders[0]; const connectResult = await terminal.connectReader(selectedReader); if (connectResult.error) { console.log('Failed to connect:', connectResult.error); } else { console.log('Connected to reader:', connectResult.reader.label); } }
let config = ConnectionConfiguration(failIfInUse: true) 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)") } }
SCPConnectionConfiguration *connectionConfig = [[SCPConnectionConfiguration alloc] initWithFailIfInUse:YES]; [[SCPTerminal shared] connectReader:selectedReader connectionConfig:connectionConfig completion:^(SCPReader *reader, NSError *error) { if (reader != nil) { NSLog(@"Successfully connected to reader: %@", reader); } else { NSLog(@"connectReader failed: %@", error); } }];

The Verifone P400 can only connect to one instance of the SDK at a time. When you call connectReader from your app in a different browser tab or device, the new instance breaks the existing reader-to-SDK connection and takes over the Verifone P400, even if the reader’s currently in use. To disable this behavior, pass the fail_if_in_use parameter (named failIfInUse on iOS) when calling connectReader.

  • Regardless of the value of failIfInUse, if your application connects to another application’s reader while the other application is idle, that application’s next attempt to collect payment will fail with an unexpected disconnect.
  • When failIfInUse is set to true, your application does not interrupt a reader that’s currently collecting a payment. Instead, your application’s call to connectReader fails with the connectFailedReaderIsInUse error.
  • When failIfInUse is set to false, your application connects to the other application’s reader immediately. If the other application is currently collecting payment, that payment fails with an unexpected disconnect. Otherwise, that application’s next attempt to collect a payment will fail with an unexpected disconnect.

We recommend setting failIfInUse: true for the initial connection attempt, and allowing your users to retry the connection with failIfInUse: false if appropriate.

terminal.connectReader(reader, {fail_if_in_use: true}).then(function(connectResult) { // ... });
const connectResult = await terminal.connectReader(reader, {fail_if_in_use: true});
let config = ConnectionConfiguration(failIfInUse: true) Terminal.shared.connectReader(selectedReader, connectionConfig: config) { reader, error in // ... }
SCPConnectionConfiguration *connectionConfig = [[SCPConnectionConfiguration alloc] initWithFailIfInUse:YES]; [[SCPTerminal shared] connectReader:selectedReader connectionConfig:connectionConfig completion:^(SCPReader *reader, NSError *error) { // ... }

Handling disconnects

Your app must implement the UnexpectedReaderDisconnect callback to handle when a reader is disconnected.

In your implementation of this callback, display a UI to notify the user that the reader disconnected. You may also want to call discoverReaders to begin scanning for readers and reconnect. Your app can attempt to automatically reconnect to the reader that was disconnected, or display a UI for your user to reconnect to another reader.

The Verifone P400 can disconnect from your app if it loses connection to the internet. To simulate an unexpected disconnect, power off the reader.

var terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, }); function unexpectedDisconnect() { // You might want to display UI to notify the user and start re-discovering readers }
const terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, }); function unexpectedDisconnect() { // You might want to display UI to notify the user and start re-discovering readers }
import StripeTerminal class ReaderViewController: UIViewController, TerminalDelegate { override func viewDidLoad() { super.viewDidLoad() Terminal.shared.delegate = self } // ... // MARK: TerminalDelegate func terminal(_ terminal: Terminal, didReportUnexpectedReaderDisconnect reader: Reader) { // You might want to display UI to notify the user and start re-discovering readers } }
#import "APPReaderViewController.h" #import <StripeTerminal/StripeTerminal.h> @interface APPViewController () <SCPTerminalDelegate> @property (nonatomic, nullable, strong) SCPCancelable *discoverCancelable; // ... @end @implementation APPReaderViewController - (void)viewDidLoad { [super viewDidLoad]; [SCPTerminal shared].delegate = self; } // ... #pragma mark – SCPTerminalDelegate - (void)terminal:(SCPTerminal *)terminal didReportUnexpectedReaderDisconnect:(SCPReader *)reader { // You might want to display UI to notify the user and start re-discovering readers } }

Next steps

Congratulations! You've connected your application to the reader. Next, collect your first Stripe Terminal payment.

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.

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