Create account
Sign in
Home
Payments
Business operations
Financial services
Developer tools
Security
All products
Home
Payments
Business operations
Home
Payments
Business operations
Financial services
Developer tools
Support
Overview
Overview
Sample integration
Example applications
Designing an integration
Integrate your application and readers
Getting started
JavaScript
iOS
Android
Readers
Reader setup
Connecting to a reader
Fleet management
Placing orders
Transactions
Collecting payments
Connect platforms
Saving cards
Refunds
Checkout experience
Cart display
Receipts
Beta
Beta migration guide
Testing
Checklist
Testing
terminal
·
HomePaymentsIn-person payments

Integrate your application and readers

Learn how to set up your Terminal integration by country, SDK platform, and reader.

When you start integrating your environment with Terminal, you need to know which countries you want to do business in, select an SDK platform, and decide on a compatible reader for the in-person payments experience you want to build. Not all SDK platforms and readers are available yet in every country. Refer to the availability table to see which readers and SDK platforms you can use in each country.

View the guide for

Looking for a demo?

You can download an example application and run it in the card reader simulator that's included in the Stripe SDK. For details, see Example applications.

Country:
SDK Platform:
Reader:

To order readers in Canada, please reach out to your Account Manager. If you do not have an Account Manager, let us know if you are interested.

Set up Terminal for Canada

Based on your selections above, this guide walks you through how to set your integration up for the Verifone P400 reader using JavaScript in Canada. To accept Terminal charges in Canada, either your platform account or connected account must be in Canada.

Stripe supports Visa, Mastercard, American Express, and Interac payments in Canada. All transactions must be made in Canadian dollars (CAD).

About Interac payments

Interac is the interbank network that handles routing of debit payments in Canada. Consumer debit cards in Canada are branded with an Interac logo and may also be co-branded with another payment network’s logo. Even if the card is co-branded, however, all Interac debit transactions must be routed through Interac.

To maximize card acceptance, you will need to build Interac support into your integration. Unlike Visa, Mastercard, and American Express transactions, Interac transactions are authorized and automatically captured in a single step.

Interac Flash (contactless) payments are limited to 100 CAD and generally up to three consecutive transactions. Transactions higher than 100 CAD or the fourth contactless transaction in a row require the customer to insert their Interac card and enter their PIN. You do not need to adjust your integration to account for these limitations: the reader automatically prompts for PIN in cases where it is required.

1 Install the SDK Client-side

To get started, include this script on your checkout page. This script must always load directly from https://js.stripe.com for compatibility with the latest reader software. Do not include the script in a bundle or host a copy yourself; this could break your integration without warning.

<script src="https://js.stripe.com/terminal/v1/"></script>
<script src="https://js.stripe.com/terminal/v1/"></script>

Using the Terminal JS SDK as a module

We also provide an npm package that makes it easier to load and use the Terminal JS SDK as a module. For more information, check out the project on GitHub.

For information on migrating from beta versions of the JavaScript SDK, see the Stripe Terminal Beta Migration Guide.

2 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 Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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"; // 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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" // 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{} ct, _ := connectiontoken.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 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{} ct, _ := connectiontoken.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"; // 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(); var connectionToken = service.Create(options);
// 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"; // 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(); var connectionToken = service.Create(options);

Obtain the secret from the ConnectionToken on your server and pass it to the client side.

Ruby Python PHP Java Node Go .NET
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }

The ConnectionToken's secret lets you connect to any Stripe Terminal reader and take payments with your Stripe account. Be sure to authenticate the endpoint for creating connection tokens.

Client-side

To give the SDK access to this endpoint, create a function in your web application that requests a connection token from your backend and returns the secret from the ConnectionToken object.

JavaScript JavaScript (ESNext)
function fetchConnectionToken() { // Your backend should call /v1/terminal/connection_tokens and return the JSON response from Stripe return fetch('https://{YOUR_BACKEND_URL}/connection_token', { method: "POST" }) .then(function(response) { return response.json(); }) .then(function(data) { return data.secret; }); }
function fetchConnectionToken() { // Your backend should call /v1/terminal/connection_tokens and return the JSON response from Stripe return fetch('https://{YOUR_BACKEND_URL}/connection_token', { method: "POST" }) .then(function(response) { return response.json(); }) .then(function(data) { return data.secret; }); }
async function fetchConnectionToken() { // Your backend should call /v1/terminal/connection_tokens and return the JSON response from Stripe const response = await fetch('https://{YOUR_BACKEND_URL}/connection_token', { method: "POST" }); const data = await response.json(); return data.secret; }
async function fetchConnectionToken() { // Your backend should call /v1/terminal/connection_tokens and return the JSON response from Stripe const response = await fetch('https://{YOUR_BACKEND_URL}/connection_token', { method: "POST" }); const data = await response.json(); return data.secret; }

This function is called whenever the SDK needs to authenticate with Stripe or the Reader. It’s also called when a new connection 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 connection token from your backend, connecting to a reader fails with the error from your server.

Do not cache or hardcode the connection token. The SDK manages the connection token's lifecycle.

3 Initialize the SDK Client-side

The StripeTerminal object made available by the SDK exposes a generic interface for discovering readers, connecting to a reader, and creating payments. To initialize a StripeTerminal instance in your JavaScript application, provide the ConnectionToken function implemented in Step 2.

You must also provide a function to handle unexpected disconnects from the reader, onUnexpectedReaderDisconnect. In this function, your app should notify the user that the reader disconnected. You can also include a way to attempt to reconnect to a reader. For more information, see Handling disconnects.

JavaScript JavaScript (ESNext)
var terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, });
var terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, });
const terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, });
const terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, });

4 Develop and test with 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.

JavaScript JavaScript (ESNext)
// Handler for a "Connect Reader" button function connectReaderHandler() { var config = {simulated: true}; 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 { // 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); } }); } }); }
// Handler for a "Connect Reader" button function connectReaderHandler() { var config = {simulated: true}; 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 { // 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); } }); } }); }
// Handler for a "Connect Reader" button async function connectReaderHandler() { const config = {simulated: true}; 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 { // 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); } } }
// Handler for a "Connect Reader" button async function connectReaderHandler() { const config = {simulated: true}; 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 { // 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); } } }

Simulated reader configuration

The simulated reader supports a small amount of configuration, enabling you to test different flows within your point of sale application such as different card brands or error scenarios like a declined charge.

Currently, the simulated reader configuration API is only available in our JavaScript SDK. The simulator configuration accepts either a testCardNumber or a testPaymentMethod options.

For more details about the configuration options API, see the Stripe Terminal JavaScript API reference.

5 Set up your Verifone P400 reader

To start testing with your physical reader, you need to configure it for its region of use.

First, create a Location in Canada. We recommend creating a unique location for each physical operating site. The reader uses its registered location to configure itself to accept in-person payments in Canada. In the absence of a registered location, the readers configure themselves based on the country in which your Stripe account is based.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="3040 Bur Oak Ave" \ -d "address[city]"=Markham \ -d "address[state]"=ON \ -d "address[country]"=CA \ -d "address[postal_code]"="L6B 0R1"
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="3040 Bur Oak Ave" \ -d "address[city]"=Markham \ -d "address[state]"=ON \ -d "address[country]"=CA \ -d "address[postal_code]"="L6B 0R1"
# 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::Location.create({ display_name: 'HQ', address: { line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', } })
# 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::Location.create({ display_name: 'HQ', address: { line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', } })
# 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.Location.create( display_name='HQ', address={ line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', }, )
# 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.Location.create( display_name='HQ', address={ line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', }, )
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "3040 Bur Oak Ave", 'city' => "Markham", 'state' => "ON", 'country' => "CA", 'postal_code' => "L6B 0R1", ] ]);
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "3040 Bur Oak Ave", 'city' => "Markham", 'state' => "ON", 'country' => "CA", 'postal_code' => "L6B 0R1", ] ]);
// 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"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("3040 Bur Oak Ave") .setCity("Markham") .setState("ON") .setCountry("CA") .setPostalCode("L6B 0R1") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("3040 Bur Oak Ave") .setCity("Markham") .setState("ON") .setCountry("CA") .setPostalCode("L6B 0R1") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', } })
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', } })
// 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.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("3040 Bur Oak Ave"), City: stripe.String("Markham"), State: stripe.String("ON"), Country: stripe.String("CA"), PostalCode: stripe.String("L6B 0R1"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("3040 Bur Oak Ave"), City: stripe.String("Markham"), State: stripe.String("ON"), Country: stripe.String("CA"), PostalCode: stripe.String("L6B 0R1"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "3040 Bur Oak Ave", City = "Markham", State = "ON", Country = "CA", PostalCode = "L6B 0R1", }, }; var service = new LocationService(); service.Create(options);
// 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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "3040 Bur Oak Ave", City = "Markham", State = "ON", Country = "CA", PostalCode = "L6B 0R1", }, }; var service = new LocationService(); service.Create(options);

Next, register your reader to the location. On the Verifone P400, this is done by entering the key sequence 0-7-1-3-9 to display a unique, three-word registration code. This code must be passed to Stripe in order to complete registration of the reader to your account and location; you can do so in the Dashboard or via the API.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -d label="Alice's Reader"
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -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", location: "{LOCATION_ID}", })
# 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", location: "{LOCATION_ID}", })
# 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", location="{LOCATION_ID}", )
# 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", location="{LOCATION_ID}", )
// 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", 'location' => "{LOCATION_ID}", ]);
// 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", 'location' => "{LOCATION_ID}", ]);
// 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") .setLocation("{LOCATION_ID}") .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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; ReaderCreateParams params = ReaderCreateParams.builder() .setRegistrationCode("{READER_REGISTRATION_CODE}") .setLabel("Alice's Reader") .setLocation("{LOCATION_ID}") .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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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"), Location: stripe.String("{LOCATION_ID}"), 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalReaderParams{ Label: stripe.String("Alice's Reader"), Location: stripe.String("{LOCATION_ID}"), 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);
// 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);

When your reader is registered, you can discover and connect to it as you did with the simulated reader.

With the simulated option empty or set to false, call discoverReaders to search for readers. You can filter the discoverReaders call by the location the reader is registered to. When discoverReaders returns a result, call connectReader to connect your application to the reader.

For more information on the Verifone P400, see:

  • Verifone P400 reader guide
  • Connecting to the Verifone P400

Readers operating in Canada must be using the reader software version 3.0.1.15 or later.

Translation

Language regulations require that services, including point-of-sale services, be provided in French unless English has been agreed upon by the cardholder and their card issuer. Terminal is built to help you comply with these requirements if they apply to your business.

The Verifone P400 interface displays text in both French and English if it is registered to a location with a Canadian address. Once the cardholder has presented their card, the reader determines the cardholder’s preferred language. Each screen after that point is translated according to the cardholder’s preferences.

If you are required to provide services in French or would like to translate text into both French and English, you should ensure any of your custom reader screens and receipts display the appropriate translations.

Supported browsers

The Stripe Terminal JavaScript SDK strives to support all recent versions of major browsers. It supports:

  • Internet Explorer 11 and Edge on Windows
  • Firefox on desktop platforms
  • Chrome and Safari on all platforms
  • The Android native browser on Android 4.4 and later

If you have issues with the Stripe Terminal JavaScript SDK on a specific browser, please email support-terminal@stripe.com.

See also

  • Accept payments
  • Accept multi-party payments
  • Accept Interac payments
  • Send receipts
  • Provide refunds
Reader:

The Verifone P400 reader using iOS in Canada is in beta. For more details, please contact Stripe.

To order readers in Canada, please reach out to your Account Manager. If you do not have an Account Manager, let us know if you are interested.

Set up Terminal for Canada

Based on your selections above, this guide walks you through how to set your integration up for the Verifone P400 reader using iOS in Canada. To accept Terminal charges in Canada, either your platform account or connected account must be in Canada.

Stripe supports Visa, Mastercard, American Express, and Interac payments in Canada. All transactions must be made in Canadian dollars (CAD).

About Interac payments

Interac is the interbank network that handles routing of debit payments in Canada. Consumer debit cards in Canada are branded with an Interac logo and may also be co-branded with another payment network’s logo. Even if the card is co-branded, however, all Interac debit transactions must be routed through Interac.

To maximize card acceptance, you will need to build Interac support into your integration. Unlike Visa, Mastercard, and American Express transactions, Interac transactions are authorized and automatically captured in a single step.

Interac Flash (contactless) payments are limited to 100 CAD and generally up to three consecutive transactions. Transactions higher than 100 CAD or the fourth contactless transaction in a row require the customer to insert their Interac card and enter their PIN. You do not need to adjust your integration to account for these limitations: the reader automatically prompts for PIN in cases where it is required.

1 Install the SDK Client-side

The iOS SDK is compatible with apps supporting iOS 9 and above. To install the SDK, follow these steps:

Cocoapods Manual
  1. If you haven't already done so, install a recent version of CocoaPods.
  2. If you don't have an existing Podfile, run the following command to create one:
    pod init
    pod init
  3. Add this line to your Podfile:
    Podfile
    pod 'StripeTerminal', '1.4.0'
    pod 'StripeTerminal', '1.4.0'
  4. Run the following command:
    pod install
    pod install
  5. From now on, use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file.
  1. Visit the Stripe Terminal iOS repository on Github and navigate to the latest release
  2. Download the StripeTerminal.xcframework.zip file attached to the Github release and unzip it.
  3. Drag and drop the downloaded XCFramework into your Xcode project.
  4. If the framework's symbols are unable to be loaded, navigate to the "General" pane of your target and find the "Frameworks, Libraries, and Embedded Content" dropdown. Switch StripeTerminal.xcframework from "Do Not Embed" to "Embed and Sign"

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository or subscribe to the Github Releases RSS feed.

For information on migrating from beta versions of the iOS SDK, see the Stripe Terminal Beta Migration Guide.

2 Configure your app Client-side

To prepare your app to work with the Stripe Terminal SDK, make a few changes to your Info.plist file in Xcode.

  1. Enable location services with the following key-value pair.
    Privacy – Location When In Use Usage Description
    Key NSLocationWhenInUseUsageDescription
    Value Location access is required in order to accept payments.

    To reduce fraud risks associated with payments, and to minimize disputes, Stripe needs to know where payments occur. If the SDK can't determine the location of the iOS device, payments are disabled until location access is restored.

  2. Ensure that your app runs in the background and remains connected to the reader.
    Required background modes
    Key UIBackgroundModes
    Value bluetooth-central (Uses Bluetooth LE accessories)

    Setting the bluetooth-central background mode lets the reader remain in standby mode when your app is backgrounded, or when the iOS device is locked. Without this value, standby fails. When your app is running in the background, the reader can turn off automatically to conserve power.

  3. Pass app validation checks when submitting to the App Store.
    Privacy – Bluetooth Peripheral Usage Description
    Key NSBluetoothPeripheralUsageDescription
    Value Bluetooth access is required in order to connect to supported card readers.

    This is an example—you can rephrase the prompt for user permission in your app.


  4. iOS 13 SDK and later: Allow your app to display a Bluetooth permission dialog.
    Privacy - Bluetooth Always Usage Description
    Key NSBluetoothAlwaysUsageDescription
    Value This app uses Bluetooth to connect to supported card readers.

    Starting in iOS 13, any apps that use the device's Bluetooth APIs require more granular permissions. These apps must include this key in their Info.plist file, or else they will crash on first launch.


Save your app's Info.plist. Now it's configured correctly and ready for use with the Stripe Terminal SDK.

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 Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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"; // 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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" // 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{} ct, _ := connectiontoken.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 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{} ct, _ := connectiontoken.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"; // 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(); var connectionToken = service.Create(options);
// 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"; // 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(); var connectionToken = service.Create(options);

Obtain the secret from the ConnectionToken on your server and pass it to the client side.

Ruby Python PHP Java Node Go .NET
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }

The ConnectionToken's secret lets you connect to any Stripe Terminal reader and take payments with your Stripe account. Be sure to authenticate the endpoint for creating connection tokens.

Client-side

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.

Swift Objective-C
APIClient.swift
Download
import StripeTerminal // Example API client class for communicating with your backend class APIClient: ConnectionTokenProvider { // For simplicity, this example class is a singleton static let shared = APIClient() // Fetches a ConnectionToken from your backend func fetchConnectionToken(_ completion: @escaping ConnectionTokenCompletionBlock) { let config = URLSessionConfiguration.default let session = URLSession(configuration: config) guard let url = URL(string: "https://{YOUR BACKEND URL}/connection_token") else { fatalError("Invalid backend URL") } var request = URLRequest(url: url) request.httpMethod = "POST" let task = session.dataTask(with: request) { (data, response, error) in if let data = data { do { // Warning: casting using `as? [String: String]` looks simpler, but isn't safe: let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] if let secret = json?["secret"] as? String { completion(secret, nil) } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 2000, userInfo: [NSLocalizedDescriptionKey: "Missing `secret` in ConnectionToken JSON response"]) completion(nil, error) } } catch { completion(nil, error) } } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 1000, userInfo: [NSLocalizedDescriptionKey: "No data in response from ConnectionToken endpoint"]) completion(nil, error) } } task.resume() } }
import StripeTerminal // Example API client class for communicating with your backend class APIClient: ConnectionTokenProvider { // For simplicity, this example class is a singleton static let shared = APIClient() // Fetches a ConnectionToken from your backend func fetchConnectionToken(_ completion: @escaping ConnectionTokenCompletionBlock) { let config = URLSessionConfiguration.default let session = URLSession(configuration: config) guard let url = URL(string: "https://{YOUR BACKEND URL}/connection_token") else { fatalError("Invalid backend URL") } var request = URLRequest(url: url) request.httpMethod = "POST" let task = session.dataTask(with: request) { (data, response, error) in if let data = data { do { // Warning: casting using `as? [String: String]` looks simpler, but isn't safe: let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] if let secret = json?["secret"] as? String { completion(secret, nil) } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 2000, userInfo: [NSLocalizedDescriptionKey: "Missing `secret` in ConnectionToken JSON response"]) completion(nil, error) } } catch { completion(nil, error) } } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 1000, userInfo: [NSLocalizedDescriptionKey: "No data in response from ConnectionToken endpoint"]) completion(nil, error) } } task.resume() } }
APPAPIClient.h
Download
#import <StripeTerminal/StripeTerminal.h> // Example API client class for communicating with your backend @interface APPAPIClient : NSObject <SCPConnectionTokenProvider> // For simplicity, this example class is a singleton + (instancetype)shared; @end
#import <StripeTerminal/StripeTerminal.h> // Example API client class for communicating with your backend @interface APPAPIClient : NSObject <SCPConnectionTokenProvider> // For simplicity, this example class is a singleton + (instancetype)shared; @end
APPAPIClient.m
Download
#import "APPAPIClient.h" @implementation APPAPIClient + (instancetype)shared { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _shared = [APPAPIClient new]; }); return _shared; } // Fetches a ConnectionToken from your backend - (void)fetchConnectionToken:(SCPConnectionTokenCompletionBlock)completion { NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; NSURL *url = [NSURL URLWithString:@"https://{YOUR BACKEND URL}/connection-token"]; if (!url) { NSAssert(NO, @"Invalid backend URL"); } NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; request.HTTPMethod = @"POST"; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { id jsonObject = nil; NSError *jsonSerializationError; if (data) { jsonObject = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)kNilOptions error:&jsonSerializationError]; } else { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:1000 userInfo:@{NSLocalizedDescriptionKey: @"No data in response from ConnectionToken endpoint"}]; completion(nil, error); } if (!(jsonObject && [jsonObject isKindOfClass:[NSDictionary class]])) { completion(nil, jsonSerializationError); return; } NSDictionary *json = (NSDictionary *)jsonObject; id secret = json[@"secret"]; if (!(secret && [secret isKindOfClass:[NSString class]])) { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:2000 userInfo:@{NSLocalizedDescriptionKey: @"Missing `secret` in ConnectionToken JSON response"}]; completion(nil, error); return; } completion((NSString *)secret, nil); }]; [task resume]; } // ... @end
#import "APPAPIClient.h" @implementation APPAPIClient + (instancetype)shared { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _shared = [APPAPIClient new]; }); return _shared; } // Fetches a ConnectionToken from your backend - (void)fetchConnectionToken:(SCPConnectionTokenCompletionBlock)completion { NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; NSURL *url = [NSURL URLWithString:@"https://{YOUR BACKEND URL}/connection-token"]; if (!url) { NSAssert(NO, @"Invalid backend URL"); } NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; request.HTTPMethod = @"POST"; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { id jsonObject = nil; NSError *jsonSerializationError; if (data) { jsonObject = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)kNilOptions error:&jsonSerializationError]; } else { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:1000 userInfo:@{NSLocalizedDescriptionKey: @"No data in response from ConnectionToken endpoint"}]; completion(nil, error); } if (!(jsonObject && [jsonObject isKindOfClass:[NSDictionary class]])) { completion(nil, jsonSerializationError); return; } NSDictionary *json = (NSDictionary *)jsonObject; id secret = json[@"secret"]; if (!(secret && [secret isKindOfClass:[NSString class]])) { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:2000 userInfo:@{NSLocalizedDescriptionKey: @"Missing `secret` in ConnectionToken JSON response"}]; completion(nil, error); return; } completion((NSString *)secret, nil); }]; [task resume]; } // ... @end

This function is called whenever the SDK needs to authenticate with Stripe or the Reader. It’s also called when a new connection 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 connection token from your backend, connecting to a reader fails with the error from your server.

Do not cache or hardcode the connection token. The SDK manages the connection token's lifecycle.

4 Initialize the SDK Client-side

The Terminal class made available by the Stripe Terminal SDK exposes a generic interface for discovering readers, connecting to a reader, creating payments, and updating reader software.

To get started, provide your ConnectionTokenProvider implemented in Step 3. You can only call setTokenProvider once in your app, and must call it before accessing Terminal.shared. We recommend calling setTokenProvider in your AppDelegate's application:didFinishLaunchingWithOptions method. Alternatively, you can use dispatch_once in Objective-C, or a static constructor in Swift.

Swift Objective-C
AppDelegate.swift
import UIKit import StripeTerminal @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Terminal.setTokenProvider(APIClient.shared) // ... return true } // ... }
import UIKit import StripeTerminal @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Terminal.setTokenProvider(APIClient.shared) // ... return true } // ... }
APPAppDelegate.m
#import <StripeTerminal/StripeTerminal.h> #import "APPAppDelegate.h" #import "APPAPIClient.h" @implementation APPAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [SCPTerminal setTokenProvider:[APPAPIClient shared]]; // ... return YES; } // ... @end
#import <StripeTerminal/StripeTerminal.h> #import "APPAppDelegate.h" #import "APPAPIClient.h" @implementation APPAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [SCPTerminal setTokenProvider:[APPAPIClient shared]]; // ... return YES; } // ... @end

5 Develop and test with 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.

To run your app using the iOS Simulator, enable a simulated location. Select Simulate Location from the Xcode Debug menu.

Swift Objective-C
ReaderDiscoveryViewController.swift
Download
import UIKit import StripeTerminal class ReaderDiscoveryViewController: UIViewController, DiscoveryDelegate { var discoverCancelable: Cancelable? // ... // Action for a "Discover Readers" button func discoverReadersAction() { let config = DiscoveryConfiguration( discoveryMethod: .internet, simulated: true ) self.discoverCancelable = Terminal.shared.discoverReaders(config, delegate: self) { error in if let error = error { print("discoverReaders failed: \(error)") } else { print("discoverReaders succeeded") } }) } // ... // MARK: DiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. func terminal(_ terminal: Terminal, didUpdateDiscoveredReaders readers: [Reader]) { // Select the first reader we discover guard let selectedReader = readers.first else { return } // Only connect if we aren't currently connected. guard terminal.connectionStatus == .notConnected else { return } Terminal.shared.connectReader(selectedReader) { reader, error in if let reader = reader { print("Successfully connected to reader: \(reader)") } else if let error = error { print("connectReader failed: \(error)") } }) } }
import UIKit import StripeTerminal class ReaderDiscoveryViewController: UIViewController, DiscoveryDelegate { var discoverCancelable: Cancelable? // ... // Action for a "Discover Readers" button func discoverReadersAction() { let config = DiscoveryConfiguration( discoveryMethod: .internet, simulated: true ) self.discoverCancelable = Terminal.shared.discoverReaders(config, delegate: self) { error in if let error = error { print("discoverReaders failed: \(error)") } else { print("discoverReaders succeeded") } }) } // ... // MARK: DiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. func terminal(_ terminal: Terminal, didUpdateDiscoveredReaders readers: [Reader]) { // Select the first reader we discover guard let selectedReader = readers.first else { return } // Only connect if we aren't currently connected. guard terminal.connectionStatus == .notConnected else { return } Terminal.shared.connectReader(selectedReader) { reader, error in if let reader = reader { print("Successfully connected to reader: \(reader)") } else if let error = error { print("connectReader failed: \(error)") } }) } }
APPReaderDiscoveryViewController.m
Download
#import <StripeTerminal/StripeTerminal.h> #import "APPReaderDiscoveryViewController.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:YES]; self.discoverCancelable = [[SCPTerminal shared] discoverReaders:config delegate:self completion:^(NSError *error) { if (error != nil) { NSLog(@"discoverReaders failed: %@", error); } else { NSLog(@"discoverReaders succeeded"); } }]; } // ... #pragma mark - SCPDiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. - (void)terminal:(SCPTerminal *)terminal didUpdateDiscoveredReaders:(NSArray<SCPReader *> *)readers { // Select the first reader we discover SCPReader *selectedReader = [readers firstObject]; // Only connect if we aren't currently connected. if (terminal.connectionStatus != SCPConnectionStatusNotConnected) { return; } [[SCPTerminal shared] connectReader:selectedReader delegate:self connectionConfig:connectionConfiguration completion:^(SCPReader *reader, NSError *error) { if (reader != nil) { NSLog(@"Successfully connected to reader: %@", reader); } else { NSLog(@"connectReader failed: %@", error); } }]; }
#import <StripeTerminal/StripeTerminal.h> #import "APPReaderDiscoveryViewController.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:YES]; self.discoverCancelable = [[SCPTerminal shared] discoverReaders:config delegate:self completion:^(NSError *error) { if (error != nil) { NSLog(@"discoverReaders failed: %@", error); } else { NSLog(@"discoverReaders succeeded"); } }]; } // ... #pragma mark - SCPDiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. - (void)terminal:(SCPTerminal *)terminal didUpdateDiscoveredReaders:(NSArray<SCPReader *> *)readers { // Select the first reader we discover SCPReader *selectedReader = [readers firstObject]; // Only connect if we aren't currently connected. if (terminal.connectionStatus != SCPConnectionStatusNotConnected) { return; } [[SCPTerminal shared] connectReader:selectedReader delegate:self connectionConfig:connectionConfiguration completion:^(SCPReader *reader, NSError *error) { if (reader != nil) { NSLog(@"Successfully connected to reader: %@", reader); } else { NSLog(@"connectReader failed: %@", error); } }]; }

6 Set up your Verifone P400 reader

To start testing with your physical reader, you need to configure it for its region of use.

First, create a Location in Canada. We recommend creating a unique location for each physical operating site. The reader uses its registered location to configure itself to accept in-person payments in Canada. In the absence of a registered location, the readers configure themselves based on the country in which your Stripe account is based.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="3040 Bur Oak Ave" \ -d "address[city]"=Markham \ -d "address[state]"=ON \ -d "address[country]"=CA \ -d "address[postal_code]"="L6B 0R1"
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="3040 Bur Oak Ave" \ -d "address[city]"=Markham \ -d "address[state]"=ON \ -d "address[country]"=CA \ -d "address[postal_code]"="L6B 0R1"
# 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::Location.create({ display_name: 'HQ', address: { line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', } })
# 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::Location.create({ display_name: 'HQ', address: { line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', } })
# 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.Location.create( display_name='HQ', address={ line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', }, )
# 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.Location.create( display_name='HQ', address={ line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', }, )
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "3040 Bur Oak Ave", 'city' => "Markham", 'state' => "ON", 'country' => "CA", 'postal_code' => "L6B 0R1", ] ]);
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "3040 Bur Oak Ave", 'city' => "Markham", 'state' => "ON", 'country' => "CA", 'postal_code' => "L6B 0R1", ] ]);
// 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"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("3040 Bur Oak Ave") .setCity("Markham") .setState("ON") .setCountry("CA") .setPostalCode("L6B 0R1") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("3040 Bur Oak Ave") .setCity("Markham") .setState("ON") .setCountry("CA") .setPostalCode("L6B 0R1") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', } })
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '3040 Bur Oak Ave', city: 'Markham', state: 'ON', country: 'CA', postal_code: 'L6B 0R1', } })
// 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.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("3040 Bur Oak Ave"), City: stripe.String("Markham"), State: stripe.String("ON"), Country: stripe.String("CA"), PostalCode: stripe.String("L6B 0R1"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("3040 Bur Oak Ave"), City: stripe.String("Markham"), State: stripe.String("ON"), Country: stripe.String("CA"), PostalCode: stripe.String("L6B 0R1"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "3040 Bur Oak Ave", City = "Markham", State = "ON", Country = "CA", PostalCode = "L6B 0R1", }, }; var service = new LocationService(); service.Create(options);
// 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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "3040 Bur Oak Ave", City = "Markham", State = "ON", Country = "CA", PostalCode = "L6B 0R1", }, }; var service = new LocationService(); service.Create(options);

Next, register your reader to the location. On the Verifone P400, this is done by entering the key sequence 0-7-1-3-9 to display a unique, three-word registration code. This code must be passed to Stripe in order to complete registration of the reader to your account and location; you can do so in the Dashboard or via the API.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -d label="Alice's Reader"
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -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", location: "{LOCATION_ID}", })
# 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", location: "{LOCATION_ID}", })
# 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", location="{LOCATION_ID}", )
# 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", location="{LOCATION_ID}", )
// 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", 'location' => "{LOCATION_ID}", ]);
// 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", 'location' => "{LOCATION_ID}", ]);
// 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") .setLocation("{LOCATION_ID}") .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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; ReaderCreateParams params = ReaderCreateParams.builder() .setRegistrationCode("{READER_REGISTRATION_CODE}") .setLabel("Alice's Reader") .setLocation("{LOCATION_ID}") .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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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"), Location: stripe.String("{LOCATION_ID}"), 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalReaderParams{ Label: stripe.String("Alice's Reader"), Location: stripe.String("{LOCATION_ID}"), 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);
// 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);

When your reader is registered, you can discover and connect to it as you did with the simulated reader.

With the simulated option empty or set to false, call discoverReaders to search for readers. You can filter the discoverReaders call by the location the reader is registered to. When discoverReaders returns a result, call connectReader to connect your application to the reader.

For more information on the Verifone P400, see:

  • Verifone P400 reader guide
  • Connecting to the Verifone P400

Readers operating in Canada must be using the reader software version 3.0.1.15 or later.

Translation

Language regulations require that services, including point-of-sale services, be provided in French unless English has been agreed upon by the cardholder and their card issuer. Terminal is built to help you comply with these requirements if they apply to your business.

The Verifone P400 interface displays text in both French and English if it is registered to a location with a Canadian address. Once the cardholder has presented their card, the reader determines the cardholder’s preferred language. Each screen after that point is translated according to the cardholder’s preferences.

If you are required to provide services in French or would like to translate text into both French and English, you should ensure any of your custom reader screens and receipts display the appropriate translations.

See also

  • Accept payments
  • Accept multi-party payments
  • Accept Interac payments
  • Send receipts
  • Provide refunds
SDK Platform:
Reader:

Set up Terminal for the United States

Based on your selections above, this guide walks you through how to set your integration up for the Verifone P400 reader using JavaScript in the United States. To accept Terminal charges in the United States, either your platform account or connected account must be in the United States.

Stripe supports Visa, Mastercard, American Express, and Discover payments in the United States. All transactions must be made in U.S. dollars (USD).

1 Install the SDK Client-side

To get started, include this script on your checkout page. This script must always load directly from https://js.stripe.com for compatibility with the latest reader software. Do not include the script in a bundle or host a copy yourself; this could break your integration without warning.

<script src="https://js.stripe.com/terminal/v1/"></script>
<script src="https://js.stripe.com/terminal/v1/"></script>

Using the Terminal JS SDK as a module

We also provide an npm package that makes it easier to load and use the Terminal JS SDK as a module. For more information, check out the project on GitHub.

For information on migrating from beta versions of the JavaScript SDK, see the Stripe Terminal Beta Migration Guide.

2 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 Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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"; // 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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" // 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{} ct, _ := connectiontoken.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 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{} ct, _ := connectiontoken.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"; // 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(); var connectionToken = service.Create(options);
// 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"; // 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(); var connectionToken = service.Create(options);

Obtain the secret from the ConnectionToken on your server and pass it to the client side.

Ruby Python PHP Java Node Go .NET
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }

The ConnectionToken's secret lets you connect to any Stripe Terminal reader and take payments with your Stripe account. Be sure to authenticate the endpoint for creating connection tokens.

Client-side

To give the SDK access to this endpoint, create a function in your web application that requests a connection token from your backend and returns the secret from the ConnectionToken object.

JavaScript JavaScript (ESNext)
function fetchConnectionToken() { // Your backend should call /v1/terminal/connection_tokens and return the JSON response from Stripe return fetch('https://{YOUR_BACKEND_URL}/connection_token', { method: "POST" }) .then(function(response) { return response.json(); }) .then(function(data) { return data.secret; }); }
function fetchConnectionToken() { // Your backend should call /v1/terminal/connection_tokens and return the JSON response from Stripe return fetch('https://{YOUR_BACKEND_URL}/connection_token', { method: "POST" }) .then(function(response) { return response.json(); }) .then(function(data) { return data.secret; }); }
async function fetchConnectionToken() { // Your backend should call /v1/terminal/connection_tokens and return the JSON response from Stripe const response = await fetch('https://{YOUR_BACKEND_URL}/connection_token', { method: "POST" }); const data = await response.json(); return data.secret; }
async function fetchConnectionToken() { // Your backend should call /v1/terminal/connection_tokens and return the JSON response from Stripe const response = await fetch('https://{YOUR_BACKEND_URL}/connection_token', { method: "POST" }); const data = await response.json(); return data.secret; }

This function is called whenever the SDK needs to authenticate with Stripe or the Reader. It’s also called when a new connection 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 connection token from your backend, connecting to a reader fails with the error from your server.

Do not cache or hardcode the connection token. The SDK manages the connection token's lifecycle.

3 Initialize the SDK Client-side

The StripeTerminal object made available by the SDK exposes a generic interface for discovering readers, connecting to a reader, and creating payments. To initialize a StripeTerminal instance in your JavaScript application, provide the ConnectionToken function implemented in Step 2.

You must also provide a function to handle unexpected disconnects from the reader, onUnexpectedReaderDisconnect. In this function, your app should notify the user that the reader disconnected. You can also include a way to attempt to reconnect to a reader. For more information, see Handling disconnects.

JavaScript JavaScript (ESNext)
var terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, });
var terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, });
const terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, });
const terminal = StripeTerminal.create({ onFetchConnectionToken: fetchConnectionToken, onUnexpectedReaderDisconnect: unexpectedDisconnect, });

4 Develop and test with 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.

JavaScript JavaScript (ESNext)
// Handler for a "Connect Reader" button function connectReaderHandler() { var config = {simulated: true}; 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 { // 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); } }); } }); }
// Handler for a "Connect Reader" button function connectReaderHandler() { var config = {simulated: true}; 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 { // 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); } }); } }); }
// Handler for a "Connect Reader" button async function connectReaderHandler() { const config = {simulated: true}; 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 { // 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); } } }
// Handler for a "Connect Reader" button async function connectReaderHandler() { const config = {simulated: true}; 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 { // 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); } } }

Simulated reader configuration

The simulated reader supports a small amount of configuration, enabling you to test different flows within your point of sale application such as different card brands or error scenarios like a declined charge.

Currently, the simulated reader configuration API is only available in our JavaScript SDK. The simulator configuration accepts either a testCardNumber or a testPaymentMethod options.

For more details about the configuration options API, see the Stripe Terminal JavaScript API reference.

5 Set up your Verifone P400 reader

To start testing with your physical reader, you need to configure it for its region of use.

First, create a Location in the United States. We recommend creating a unique location for each physical operating site. The reader uses its registered location to configure itself to accept in-person payments in the United States. In the absence of a registered location, the readers configure themselves based on the country in which your Stripe account is based.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="1272 Valencia Street" \ -d "address[city]"="San Francisco" \ -d "address[state]"=CA \ -d "address[country]"=US \ -d "address[postal_code]"=94110
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="1272 Valencia Street" \ -d "address[city]"="San Francisco" \ -d "address[state]"=CA \ -d "address[country]"=US \ -d "address[postal_code]"=94110
# 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::Location.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
# 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::Location.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
# 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.Location.create( display_name='HQ', address={ line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', }, )
# 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.Location.create( display_name='HQ', address={ line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', }, )
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "1272 Valencia Street", 'city' => "San Francisco", 'state' => "CA", 'country' => "US", 'postal_code' => "94110", ] ]);
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "1272 Valencia Street", 'city' => "San Francisco", 'state' => "CA", 'country' => "US", 'postal_code' => "94110", ] ]);
// 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"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("1272 Valencia Street") .setCity("San Francisco") .setState("CA") .setCountry("US") .setPostalCode("94110") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("1272 Valencia Street") .setCity("San Francisco") .setState("CA") .setCountry("US") .setPostalCode("94110") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
// 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.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("1272 Valencia Street"), City: stripe.String("San Francisco"), State: stripe.String("CA"), Country: stripe.String("US"), PostalCode: stripe.String("94110"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("1272 Valencia Street"), City: stripe.String("San Francisco"), State: stripe.String("CA"), Country: stripe.String("US"), PostalCode: stripe.String("94110"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "1272 Valencia Street", City = "San Francisco", State = "CA", Country = "US", PostalCode = "94110", }, }; var service = new LocationService(); service.Create(options);
// 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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "1272 Valencia Street", City = "San Francisco", State = "CA", Country = "US", PostalCode = "94110", }, }; var service = new LocationService(); service.Create(options);

Next, register your reader to the location. On the Verifone P400, this is done by entering the key sequence 0-7-1-3-9 to display a unique, three-word registration code. This code must be passed to Stripe in order to complete registration of the reader to your account and location; you can do so in the Dashboard or via the API.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -d label="Alice's Reader"
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -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", location: "{LOCATION_ID}", })
# 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", location: "{LOCATION_ID}", })
# 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", location="{LOCATION_ID}", )
# 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", location="{LOCATION_ID}", )
// 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", 'location' => "{LOCATION_ID}", ]);
// 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", 'location' => "{LOCATION_ID}", ]);
// 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") .setLocation("{LOCATION_ID}") .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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; ReaderCreateParams params = ReaderCreateParams.builder() .setRegistrationCode("{READER_REGISTRATION_CODE}") .setLabel("Alice's Reader") .setLocation("{LOCATION_ID}") .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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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"), Location: stripe.String("{LOCATION_ID}"), 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalReaderParams{ Label: stripe.String("Alice's Reader"), Location: stripe.String("{LOCATION_ID}"), 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);
// 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);

When your reader is registered, you can discover and connect to it as you did with the simulated reader.

With the simulated option empty or set to false, call discoverReaders to search for readers. You can filter the discoverReaders call by the location the reader is registered to. When discoverReaders returns a result, call connectReader to connect your application to the reader.

For more information on the Verifone P400, see:

  • Verifone P400 reader guide
  • Connecting to the Verifone P400

Supported browsers

The Stripe Terminal JavaScript SDK strives to support all recent versions of major browsers. It supports:

  • Internet Explorer 11 and Edge on Windows
  • Firefox on desktop platforms
  • Chrome and Safari on all platforms
  • The Android native browser on Android 4.4 and later

If you have issues with the Stripe Terminal JavaScript SDK on a specific browser, please email support-terminal@stripe.com.

See also

  • Accept payments
  • Accept multi-party payments
  • Send receipts
  • Provide refunds
Reader:

The Verifone P400 reader using iOS in the United States is in beta. For more details, please contact Stripe.

Set up Terminal for the United States

Based on your selections above, this guide walks you through how to set your integration up for the Verifone P400 reader using iOS in the United States. To accept Terminal charges in the United States, either your platform account or connected account must be in the United States.

Stripe supports Visa, Mastercard, American Express, and Discover payments in the United States. All transactions must be made in U.S. dollars (USD).

1 Install the SDK Client-side

The iOS SDK is compatible with apps supporting iOS 9 and above. To install the SDK, follow these steps:

Cocoapods Manual
  1. If you haven't already done so, install a recent version of CocoaPods.
  2. If you don't have an existing Podfile, run the following command to create one:
    pod init
    pod init
  3. Add this line to your Podfile:
    Podfile
    pod 'StripeTerminal', '1.4.0'
    pod 'StripeTerminal', '1.4.0'
  4. Run the following command:
    pod install
    pod install
  5. From now on, use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file.
  1. Visit the Stripe Terminal iOS repository on Github and navigate to the latest release
  2. Download the StripeTerminal.xcframework.zip file attached to the Github release and unzip it.
  3. Drag and drop the downloaded XCFramework into your Xcode project.
  4. If the framework's symbols are unable to be loaded, navigate to the "General" pane of your target and find the "Frameworks, Libraries, and Embedded Content" dropdown. Switch StripeTerminal.xcframework from "Do Not Embed" to "Embed and Sign"

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository or subscribe to the Github Releases RSS feed.

For information on migrating from beta versions of the iOS SDK, see the Stripe Terminal Beta Migration Guide.

2 Configure your app Client-side

To prepare your app to work with the Stripe Terminal SDK, make a few changes to your Info.plist file in Xcode.

  1. Enable location services with the following key-value pair.
    Privacy – Location When In Use Usage Description
    Key NSLocationWhenInUseUsageDescription
    Value Location access is required in order to accept payments.

    To reduce fraud risks associated with payments, and to minimize disputes, Stripe needs to know where payments occur. If the SDK can't determine the location of the iOS device, payments are disabled until location access is restored.

  2. Ensure that your app runs in the background and remains connected to the reader.
    Required background modes
    Key UIBackgroundModes
    Value bluetooth-central (Uses Bluetooth LE accessories)

    Setting the bluetooth-central background mode lets the reader remain in standby mode when your app is backgrounded, or when the iOS device is locked. Without this value, standby fails. When your app is running in the background, the reader can turn off automatically to conserve power.

  3. Pass app validation checks when submitting to the App Store.
    Privacy – Bluetooth Peripheral Usage Description
    Key NSBluetoothPeripheralUsageDescription
    Value Bluetooth access is required in order to connect to supported card readers.

    This is an example—you can rephrase the prompt for user permission in your app.


  4. iOS 13 SDK and later: Allow your app to display a Bluetooth permission dialog.
    Privacy - Bluetooth Always Usage Description
    Key NSBluetoothAlwaysUsageDescription
    Value This app uses Bluetooth to connect to supported card readers.

    Starting in iOS 13, any apps that use the device's Bluetooth APIs require more granular permissions. These apps must include this key in their Info.plist file, or else they will crash on first launch.


Save your app's Info.plist. Now it's configured correctly and ready for use with the Stripe Terminal SDK.

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 Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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"; // 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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" // 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{} ct, _ := connectiontoken.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 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{} ct, _ := connectiontoken.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"; // 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(); var connectionToken = service.Create(options);
// 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"; // 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(); var connectionToken = service.Create(options);

Obtain the secret from the ConnectionToken on your server and pass it to the client side.

Ruby Python PHP Java Node Go .NET
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }

The ConnectionToken's secret lets you connect to any Stripe Terminal reader and take payments with your Stripe account. Be sure to authenticate the endpoint for creating connection tokens.

Client-side

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.

Swift Objective-C
APIClient.swift
Download
import StripeTerminal // Example API client class for communicating with your backend class APIClient: ConnectionTokenProvider { // For simplicity, this example class is a singleton static let shared = APIClient() // Fetches a ConnectionToken from your backend func fetchConnectionToken(_ completion: @escaping ConnectionTokenCompletionBlock) { let config = URLSessionConfiguration.default let session = URLSession(configuration: config) guard let url = URL(string: "https://{YOUR BACKEND URL}/connection_token") else { fatalError("Invalid backend URL") } var request = URLRequest(url: url) request.httpMethod = "POST" let task = session.dataTask(with: request) { (data, response, error) in if let data = data { do { // Warning: casting using `as? [String: String]` looks simpler, but isn't safe: let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] if let secret = json?["secret"] as? String { completion(secret, nil) } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 2000, userInfo: [NSLocalizedDescriptionKey: "Missing `secret` in ConnectionToken JSON response"]) completion(nil, error) } } catch { completion(nil, error) } } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 1000, userInfo: [NSLocalizedDescriptionKey: "No data in response from ConnectionToken endpoint"]) completion(nil, error) } } task.resume() } }
import StripeTerminal // Example API client class for communicating with your backend class APIClient: ConnectionTokenProvider { // For simplicity, this example class is a singleton static let shared = APIClient() // Fetches a ConnectionToken from your backend func fetchConnectionToken(_ completion: @escaping ConnectionTokenCompletionBlock) { let config = URLSessionConfiguration.default let session = URLSession(configuration: config) guard let url = URL(string: "https://{YOUR BACKEND URL}/connection_token") else { fatalError("Invalid backend URL") } var request = URLRequest(url: url) request.httpMethod = "POST" let task = session.dataTask(with: request) { (data, response, error) in if let data = data { do { // Warning: casting using `as? [String: String]` looks simpler, but isn't safe: let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] if let secret = json?["secret"] as? String { completion(secret, nil) } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 2000, userInfo: [NSLocalizedDescriptionKey: "Missing `secret` in ConnectionToken JSON response"]) completion(nil, error) } } catch { completion(nil, error) } } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 1000, userInfo: [NSLocalizedDescriptionKey: "No data in response from ConnectionToken endpoint"]) completion(nil, error) } } task.resume() } }
APPAPIClient.h
Download
#import <StripeTerminal/StripeTerminal.h> // Example API client class for communicating with your backend @interface APPAPIClient : NSObject <SCPConnectionTokenProvider> // For simplicity, this example class is a singleton + (instancetype)shared; @end
#import <StripeTerminal/StripeTerminal.h> // Example API client class for communicating with your backend @interface APPAPIClient : NSObject <SCPConnectionTokenProvider> // For simplicity, this example class is a singleton + (instancetype)shared; @end
APPAPIClient.m
Download
#import "APPAPIClient.h" @implementation APPAPIClient + (instancetype)shared { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _shared = [APPAPIClient new]; }); return _shared; } // Fetches a ConnectionToken from your backend - (void)fetchConnectionToken:(SCPConnectionTokenCompletionBlock)completion { NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; NSURL *url = [NSURL URLWithString:@"https://{YOUR BACKEND URL}/connection-token"]; if (!url) { NSAssert(NO, @"Invalid backend URL"); } NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; request.HTTPMethod = @"POST"; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { id jsonObject = nil; NSError *jsonSerializationError; if (data) { jsonObject = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)kNilOptions error:&jsonSerializationError]; } else { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:1000 userInfo:@{NSLocalizedDescriptionKey: @"No data in response from ConnectionToken endpoint"}]; completion(nil, error); } if (!(jsonObject && [jsonObject isKindOfClass:[NSDictionary class]])) { completion(nil, jsonSerializationError); return; } NSDictionary *json = (NSDictionary *)jsonObject; id secret = json[@"secret"]; if (!(secret && [secret isKindOfClass:[NSString class]])) { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:2000 userInfo:@{NSLocalizedDescriptionKey: @"Missing `secret` in ConnectionToken JSON response"}]; completion(nil, error); return; } completion((NSString *)secret, nil); }]; [task resume]; } // ... @end
#import "APPAPIClient.h" @implementation APPAPIClient + (instancetype)shared { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _shared = [APPAPIClient new]; }); return _shared; } // Fetches a ConnectionToken from your backend - (void)fetchConnectionToken:(SCPConnectionTokenCompletionBlock)completion { NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; NSURL *url = [NSURL URLWithString:@"https://{YOUR BACKEND URL}/connection-token"]; if (!url) { NSAssert(NO, @"Invalid backend URL"); } NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; request.HTTPMethod = @"POST"; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { id jsonObject = nil; NSError *jsonSerializationError; if (data) { jsonObject = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)kNilOptions error:&jsonSerializationError]; } else { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:1000 userInfo:@{NSLocalizedDescriptionKey: @"No data in response from ConnectionToken endpoint"}]; completion(nil, error); } if (!(jsonObject && [jsonObject isKindOfClass:[NSDictionary class]])) { completion(nil, jsonSerializationError); return; } NSDictionary *json = (NSDictionary *)jsonObject; id secret = json[@"secret"]; if (!(secret && [secret isKindOfClass:[NSString class]])) { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:2000 userInfo:@{NSLocalizedDescriptionKey: @"Missing `secret` in ConnectionToken JSON response"}]; completion(nil, error); return; } completion((NSString *)secret, nil); }]; [task resume]; } // ... @end

This function is called whenever the SDK needs to authenticate with Stripe or the Reader. It’s also called when a new connection 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 connection token from your backend, connecting to a reader fails with the error from your server.

Do not cache or hardcode the connection token. The SDK manages the connection token's lifecycle.

4 Initialize the SDK Client-side

The Terminal class made available by the Stripe Terminal SDK exposes a generic interface for discovering readers, connecting to a reader, creating payments, and updating reader software.

To get started, provide your ConnectionTokenProvider implemented in Step 3. You can only call setTokenProvider once in your app, and must call it before accessing Terminal.shared. We recommend calling setTokenProvider in your AppDelegate's application:didFinishLaunchingWithOptions method. Alternatively, you can use dispatch_once in Objective-C, or a static constructor in Swift.

Swift Objective-C
AppDelegate.swift
import UIKit import StripeTerminal @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Terminal.setTokenProvider(APIClient.shared) // ... return true } // ... }
import UIKit import StripeTerminal @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Terminal.setTokenProvider(APIClient.shared) // ... return true } // ... }
APPAppDelegate.m
#import <StripeTerminal/StripeTerminal.h> #import "APPAppDelegate.h" #import "APPAPIClient.h" @implementation APPAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [SCPTerminal setTokenProvider:[APPAPIClient shared]]; // ... return YES; } // ... @end
#import <StripeTerminal/StripeTerminal.h> #import "APPAppDelegate.h" #import "APPAPIClient.h" @implementation APPAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [SCPTerminal setTokenProvider:[APPAPIClient shared]]; // ... return YES; } // ... @end

5 Develop and test with 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.

To run your app using the iOS Simulator, enable a simulated location. Select Simulate Location from the Xcode Debug menu.

Swift Objective-C
ReaderDiscoveryViewController.swift
Download
import UIKit import StripeTerminal class ReaderDiscoveryViewController: UIViewController, DiscoveryDelegate { var discoverCancelable: Cancelable? // ... // Action for a "Discover Readers" button func discoverReadersAction() { let config = DiscoveryConfiguration( discoveryMethod: .internet, simulated: true ) self.discoverCancelable = Terminal.shared.discoverReaders(config, delegate: self) { error in if let error = error { print("discoverReaders failed: \(error)") } else { print("discoverReaders succeeded") } }) } // ... // MARK: DiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. func terminal(_ terminal: Terminal, didUpdateDiscoveredReaders readers: [Reader]) { // Select the first reader we discover guard let selectedReader = readers.first else { return } // Only connect if we aren't currently connected. guard terminal.connectionStatus == .notConnected else { return } Terminal.shared.connectReader(selectedReader) { reader, error in if let reader = reader { print("Successfully connected to reader: \(reader)") } else if let error = error { print("connectReader failed: \(error)") } }) } }
import UIKit import StripeTerminal class ReaderDiscoveryViewController: UIViewController, DiscoveryDelegate { var discoverCancelable: Cancelable? // ... // Action for a "Discover Readers" button func discoverReadersAction() { let config = DiscoveryConfiguration( discoveryMethod: .internet, simulated: true ) self.discoverCancelable = Terminal.shared.discoverReaders(config, delegate: self) { error in if let error = error { print("discoverReaders failed: \(error)") } else { print("discoverReaders succeeded") } }) } // ... // MARK: DiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. func terminal(_ terminal: Terminal, didUpdateDiscoveredReaders readers: [Reader]) { // Select the first reader we discover guard let selectedReader = readers.first else { return } // Only connect if we aren't currently connected. guard terminal.connectionStatus == .notConnected else { return } Terminal.shared.connectReader(selectedReader) { reader, error in if let reader = reader { print("Successfully connected to reader: \(reader)") } else if let error = error { print("connectReader failed: \(error)") } }) } }
APPReaderDiscoveryViewController.m
Download
#import <StripeTerminal/StripeTerminal.h> #import "APPReaderDiscoveryViewController.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:YES]; self.discoverCancelable = [[SCPTerminal shared] discoverReaders:config delegate:self completion:^(NSError *error) { if (error != nil) { NSLog(@"discoverReaders failed: %@", error); } else { NSLog(@"discoverReaders succeeded"); } }]; } // ... #pragma mark - SCPDiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. - (void)terminal:(SCPTerminal *)terminal didUpdateDiscoveredReaders:(NSArray<SCPReader *> *)readers { // Select the first reader we discover SCPReader *selectedReader = [readers firstObject]; // Only connect if we aren't currently connected. if (terminal.connectionStatus != SCPConnectionStatusNotConnected) { return; } [[SCPTerminal shared] connectReader:selectedReader delegate:self connectionConfig:connectionConfiguration completion:^(SCPReader *reader, NSError *error) { if (reader != nil) { NSLog(@"Successfully connected to reader: %@", reader); } else { NSLog(@"connectReader failed: %@", error); } }]; }
#import <StripeTerminal/StripeTerminal.h> #import "APPReaderDiscoveryViewController.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:YES]; self.discoverCancelable = [[SCPTerminal shared] discoverReaders:config delegate:self completion:^(NSError *error) { if (error != nil) { NSLog(@"discoverReaders failed: %@", error); } else { NSLog(@"discoverReaders succeeded"); } }]; } // ... #pragma mark - SCPDiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. - (void)terminal:(SCPTerminal *)terminal didUpdateDiscoveredReaders:(NSArray<SCPReader *> *)readers { // Select the first reader we discover SCPReader *selectedReader = [readers firstObject]; // Only connect if we aren't currently connected. if (terminal.connectionStatus != SCPConnectionStatusNotConnected) { return; } [[SCPTerminal shared] connectReader:selectedReader delegate:self connectionConfig:connectionConfiguration completion:^(SCPReader *reader, NSError *error) { if (reader != nil) { NSLog(@"Successfully connected to reader: %@", reader); } else { NSLog(@"connectReader failed: %@", error); } }]; }

6 Set up your Verifone P400 reader

To start testing with your physical reader, you need to configure it for its region of use.

First, create a Location in the United States. We recommend creating a unique location for each physical operating site. The reader uses its registered location to configure itself to accept in-person payments in the United States. In the absence of a registered location, the readers configure themselves based on the country in which your Stripe account is based.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="1272 Valencia Street" \ -d "address[city]"="San Francisco" \ -d "address[state]"=CA \ -d "address[country]"=US \ -d "address[postal_code]"=94110
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="1272 Valencia Street" \ -d "address[city]"="San Francisco" \ -d "address[state]"=CA \ -d "address[country]"=US \ -d "address[postal_code]"=94110
# 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::Location.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
# 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::Location.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
# 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.Location.create( display_name='HQ', address={ line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', }, )
# 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.Location.create( display_name='HQ', address={ line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', }, )
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "1272 Valencia Street", 'city' => "San Francisco", 'state' => "CA", 'country' => "US", 'postal_code' => "94110", ] ]);
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "1272 Valencia Street", 'city' => "San Francisco", 'state' => "CA", 'country' => "US", 'postal_code' => "94110", ] ]);
// 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"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("1272 Valencia Street") .setCity("San Francisco") .setState("CA") .setCountry("US") .setPostalCode("94110") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("1272 Valencia Street") .setCity("San Francisco") .setState("CA") .setCountry("US") .setPostalCode("94110") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
// 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.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("1272 Valencia Street"), City: stripe.String("San Francisco"), State: stripe.String("CA"), Country: stripe.String("US"), PostalCode: stripe.String("94110"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("1272 Valencia Street"), City: stripe.String("San Francisco"), State: stripe.String("CA"), Country: stripe.String("US"), PostalCode: stripe.String("94110"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "1272 Valencia Street", City = "San Francisco", State = "CA", Country = "US", PostalCode = "94110", }, }; var service = new LocationService(); service.Create(options);
// 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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "1272 Valencia Street", City = "San Francisco", State = "CA", Country = "US", PostalCode = "94110", }, }; var service = new LocationService(); service.Create(options);

Next, register your reader to the location. On the Verifone P400, this is done by entering the key sequence 0-7-1-3-9 to display a unique, three-word registration code. This code must be passed to Stripe in order to complete registration of the reader to your account and location; you can do so in the Dashboard or via the API.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -d label="Alice's Reader"
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -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", location: "{LOCATION_ID}", })
# 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", location: "{LOCATION_ID}", })
# 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", location="{LOCATION_ID}", )
# 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", location="{LOCATION_ID}", )
// 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", 'location' => "{LOCATION_ID}", ]);
// 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", 'location' => "{LOCATION_ID}", ]);
// 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") .setLocation("{LOCATION_ID}") .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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; ReaderCreateParams params = ReaderCreateParams.builder() .setRegistrationCode("{READER_REGISTRATION_CODE}") .setLabel("Alice's Reader") .setLocation("{LOCATION_ID}") .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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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"), Location: stripe.String("{LOCATION_ID}"), 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalReaderParams{ Label: stripe.String("Alice's Reader"), Location: stripe.String("{LOCATION_ID}"), 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);
// 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);

When your reader is registered, you can discover and connect to it as you did with the simulated reader.

With the simulated option empty or set to false, call discoverReaders to search for readers. You can filter the discoverReaders call by the location the reader is registered to. When discoverReaders returns a result, call connectReader to connect your application to the reader.

For more information on the Verifone P400, see:

  • Verifone P400 reader guide
  • Connecting to the Verifone P400

See also

  • Accept payments
  • Accept multi-party payments
  • Send receipts
  • Provide refunds

Set up Terminal for the United States

Based on your selections above, this guide walks you through how to set your integration up for the BBPOS Chipper 2X BT reader using iOS in the United States. To accept Terminal charges in the United States, either your platform account or connected account must be in the United States.

Stripe supports Visa, Mastercard, American Express, and Discover payments in the United States. All transactions must be made in U.S. dollars (USD).

1 Install the SDK Client-side

The iOS SDK is compatible with apps supporting iOS 9 and above. To install the SDK, follow these steps:

Cocoapods Manual
  1. If you haven't already done so, install a recent version of CocoaPods.
  2. If you don't have an existing Podfile, run the following command to create one:
    pod init
    pod init
  3. Add this line to your Podfile:
    Podfile
    pod 'StripeTerminal', '1.4.0'
    pod 'StripeTerminal', '1.4.0'
  4. Run the following command:
    pod install
    pod install
  5. From now on, use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file.
  1. Visit the Stripe Terminal iOS repository on Github and navigate to the latest release
  2. Download the StripeTerminal.xcframework.zip file attached to the Github release and unzip it.
  3. Drag and drop the downloaded XCFramework into your Xcode project.
  4. If the framework's symbols are unable to be loaded, navigate to the "General" pane of your target and find the "Frameworks, Libraries, and Embedded Content" dropdown. Switch StripeTerminal.xcframework from "Do Not Embed" to "Embed and Sign"

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository or subscribe to the Github Releases RSS feed.

For information on migrating from beta versions of the iOS SDK, see the Stripe Terminal Beta Migration Guide.

2 Configure your app Client-side

To prepare your app to work with the Stripe Terminal SDK, make a few changes to your Info.plist file in Xcode.

  1. Enable location services with the following key-value pair.
    Privacy – Location When In Use Usage Description
    Key NSLocationWhenInUseUsageDescription
    Value Location access is required in order to accept payments.

    To reduce fraud risks associated with payments, and to minimize disputes, Stripe needs to know where payments occur. If the SDK can't determine the location of the iOS device, payments are disabled until location access is restored.

  2. Ensure that your app runs in the background and remains connected to the reader.
    Required background modes
    Key UIBackgroundModes
    Value bluetooth-central (Uses Bluetooth LE accessories)

    Setting the bluetooth-central background mode lets the reader remain in standby mode when your app is backgrounded, or when the iOS device is locked. Without this value, standby fails. When your app is running in the background, the reader can turn off automatically to conserve power.

  3. Pass app validation checks when submitting to the App Store.
    Privacy – Bluetooth Peripheral Usage Description
    Key NSBluetoothPeripheralUsageDescription
    Value Bluetooth access is required in order to connect to supported card readers.

    This is an example—you can rephrase the prompt for user permission in your app.


  4. iOS 13 SDK and later: Allow your app to display a Bluetooth permission dialog.
    Privacy - Bluetooth Always Usage Description
    Key NSBluetoothAlwaysUsageDescription
    Value This app uses Bluetooth to connect to supported card readers.

    Starting in iOS 13, any apps that use the device's Bluetooth APIs require more granular permissions. These apps must include this key in their Info.plist file, or else they will crash on first launch.


Save your app's Info.plist. Now it's configured correctly and ready for use with the Stripe Terminal SDK.

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 Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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"; // 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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" // 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{} ct, _ := connectiontoken.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 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{} ct, _ := connectiontoken.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"; // 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(); var connectionToken = service.Create(options);
// 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"; // 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(); var connectionToken = service.Create(options);

Obtain the secret from the ConnectionToken on your server and pass it to the client side.

Ruby Python PHP Java Node Go .NET
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }

The ConnectionToken's secret lets you connect to any Stripe Terminal reader and take payments with your Stripe account. Be sure to authenticate the endpoint for creating connection tokens.

Client-side

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.

Swift Objective-C
APIClient.swift
Download
import StripeTerminal // Example API client class for communicating with your backend class APIClient: ConnectionTokenProvider { // For simplicity, this example class is a singleton static let shared = APIClient() // Fetches a ConnectionToken from your backend func fetchConnectionToken(_ completion: @escaping ConnectionTokenCompletionBlock) { let config = URLSessionConfiguration.default let session = URLSession(configuration: config) guard let url = URL(string: "https://{YOUR BACKEND URL}/connection_token") else { fatalError("Invalid backend URL") } var request = URLRequest(url: url) request.httpMethod = "POST" let task = session.dataTask(with: request) { (data, response, error) in if let data = data { do { // Warning: casting using `as? [String: String]` looks simpler, but isn't safe: let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] if let secret = json?["secret"] as? String { completion(secret, nil) } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 2000, userInfo: [NSLocalizedDescriptionKey: "Missing `secret` in ConnectionToken JSON response"]) completion(nil, error) } } catch { completion(nil, error) } } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 1000, userInfo: [NSLocalizedDescriptionKey: "No data in response from ConnectionToken endpoint"]) completion(nil, error) } } task.resume() } }
import StripeTerminal // Example API client class for communicating with your backend class APIClient: ConnectionTokenProvider { // For simplicity, this example class is a singleton static let shared = APIClient() // Fetches a ConnectionToken from your backend func fetchConnectionToken(_ completion: @escaping ConnectionTokenCompletionBlock) { let config = URLSessionConfiguration.default let session = URLSession(configuration: config) guard let url = URL(string: "https://{YOUR BACKEND URL}/connection_token") else { fatalError("Invalid backend URL") } var request = URLRequest(url: url) request.httpMethod = "POST" let task = session.dataTask(with: request) { (data, response, error) in if let data = data { do { // Warning: casting using `as? [String: String]` looks simpler, but isn't safe: let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] if let secret = json?["secret"] as? String { completion(secret, nil) } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 2000, userInfo: [NSLocalizedDescriptionKey: "Missing `secret` in ConnectionToken JSON response"]) completion(nil, error) } } catch { completion(nil, error) } } else { let error = NSError(domain: "com.stripe-terminal-ios.example", code: 1000, userInfo: [NSLocalizedDescriptionKey: "No data in response from ConnectionToken endpoint"]) completion(nil, error) } } task.resume() } }
APPAPIClient.h
Download
#import <StripeTerminal/StripeTerminal.h> // Example API client class for communicating with your backend @interface APPAPIClient : NSObject <SCPConnectionTokenProvider> // For simplicity, this example class is a singleton + (instancetype)shared; @end
#import <StripeTerminal/StripeTerminal.h> // Example API client class for communicating with your backend @interface APPAPIClient : NSObject <SCPConnectionTokenProvider> // For simplicity, this example class is a singleton + (instancetype)shared; @end
APPAPIClient.m
Download
#import "APPAPIClient.h" @implementation APPAPIClient + (instancetype)shared { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _shared = [APPAPIClient new]; }); return _shared; } // Fetches a ConnectionToken from your backend - (void)fetchConnectionToken:(SCPConnectionTokenCompletionBlock)completion { NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; NSURL *url = [NSURL URLWithString:@"https://{YOUR BACKEND URL}/connection-token"]; if (!url) { NSAssert(NO, @"Invalid backend URL"); } NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; request.HTTPMethod = @"POST"; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { id jsonObject = nil; NSError *jsonSerializationError; if (data) { jsonObject = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)kNilOptions error:&jsonSerializationError]; } else { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:1000 userInfo:@{NSLocalizedDescriptionKey: @"No data in response from ConnectionToken endpoint"}]; completion(nil, error); } if (!(jsonObject && [jsonObject isKindOfClass:[NSDictionary class]])) { completion(nil, jsonSerializationError); return; } NSDictionary *json = (NSDictionary *)jsonObject; id secret = json[@"secret"]; if (!(secret && [secret isKindOfClass:[NSString class]])) { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:2000 userInfo:@{NSLocalizedDescriptionKey: @"Missing `secret` in ConnectionToken JSON response"}]; completion(nil, error); return; } completion((NSString *)secret, nil); }]; [task resume]; } // ... @end
#import "APPAPIClient.h" @implementation APPAPIClient + (instancetype)shared { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _shared = [APPAPIClient new]; }); return _shared; } // Fetches a ConnectionToken from your backend - (void)fetchConnectionToken:(SCPConnectionTokenCompletionBlock)completion { NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; NSURL *url = [NSURL URLWithString:@"https://{YOUR BACKEND URL}/connection-token"]; if (!url) { NSAssert(NO, @"Invalid backend URL"); } NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; request.HTTPMethod = @"POST"; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { id jsonObject = nil; NSError *jsonSerializationError; if (data) { jsonObject = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)kNilOptions error:&jsonSerializationError]; } else { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:1000 userInfo:@{NSLocalizedDescriptionKey: @"No data in response from ConnectionToken endpoint"}]; completion(nil, error); } if (!(jsonObject && [jsonObject isKindOfClass:[NSDictionary class]])) { completion(nil, jsonSerializationError); return; } NSDictionary *json = (NSDictionary *)jsonObject; id secret = json[@"secret"]; if (!(secret && [secret isKindOfClass:[NSString class]])) { NSError *error = [NSError errorWithDomain:@"com.stripe-terminal-ios.example" code:2000 userInfo:@{NSLocalizedDescriptionKey: @"Missing `secret` in ConnectionToken JSON response"}]; completion(nil, error); return; } completion((NSString *)secret, nil); }]; [task resume]; } // ... @end

This function is called whenever the SDK needs to authenticate with Stripe or the Reader. It’s also called when a new connection 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 connection token from your backend, connecting to a reader fails with the error from your server.

Do not cache or hardcode the connection token. The SDK manages the connection token's lifecycle.

4 Initialize the SDK Client-side

The Terminal class made available by the Stripe Terminal SDK exposes a generic interface for discovering readers, connecting to a reader, creating payments, and updating reader software.

To get started, provide your ConnectionTokenProvider implemented in Step 3. You can only call setTokenProvider once in your app, and must call it before accessing Terminal.shared. We recommend calling setTokenProvider in your AppDelegate's application:didFinishLaunchingWithOptions method. Alternatively, you can use dispatch_once in Objective-C, or a static constructor in Swift.

Swift Objective-C
AppDelegate.swift
import UIKit import StripeTerminal @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Terminal.setTokenProvider(APIClient.shared) // ... return true } // ... }
import UIKit import StripeTerminal @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Terminal.setTokenProvider(APIClient.shared) // ... return true } // ... }
APPAppDelegate.m
#import <StripeTerminal/StripeTerminal.h> #import "APPAppDelegate.h" #import "APPAPIClient.h" @implementation APPAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [SCPTerminal setTokenProvider:[APPAPIClient shared]]; // ... return YES; } // ... @end
#import <StripeTerminal/StripeTerminal.h> #import "APPAppDelegate.h" #import "APPAPIClient.h" @implementation APPAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [SCPTerminal setTokenProvider:[APPAPIClient shared]]; // ... return YES; } // ... @end

5 Develop and test with 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.

To run your app using the iOS Simulator, enable a simulated location. Select Simulate Location from the Xcode Debug menu.

Swift Objective-C
ReaderDiscoveryViewController.swift
Download
import UIKit import StripeTerminal class ReaderDiscoveryViewController: UIViewController, DiscoveryDelegate { var discoverCancelable: Cancelable? // ... // Action for a "Discover Readers" button func discoverReadersAction() { let config = DiscoveryConfiguration( discoveryMethod: .bluetoothScan, simulated: true ) self.discoverCancelable = Terminal.shared.discoverReaders(config, delegate: self) { error in if let error = error { print("discoverReaders failed: \(error)") } else { print("discoverReaders succeeded") } }) } // ... // MARK: DiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. func terminal(_ terminal: Terminal, didUpdateDiscoveredReaders readers: [Reader]) { // Select the first reader we discover guard let selectedReader = readers.first else { return } // Only connect if we aren't currently connected. guard terminal.connectionStatus == .notConnected else { return } Terminal.shared.connectReader(selectedReader) { reader, error in if let reader = reader { print("Successfully connected to reader: \(reader)") } else if let error = error { print("connectReader failed: \(error)") } }) } }
import UIKit import StripeTerminal class ReaderDiscoveryViewController: UIViewController, DiscoveryDelegate { var discoverCancelable: Cancelable? // ... // Action for a "Discover Readers" button func discoverReadersAction() { let config = DiscoveryConfiguration( discoveryMethod: .bluetoothScan, simulated: true ) self.discoverCancelable = Terminal.shared.discoverReaders(config, delegate: self) { error in if let error = error { print("discoverReaders failed: \(error)") } else { print("discoverReaders succeeded") } }) } // ... // MARK: DiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. func terminal(_ terminal: Terminal, didUpdateDiscoveredReaders readers: [Reader]) { // Select the first reader we discover guard let selectedReader = readers.first else { return } // Only connect if we aren't currently connected. guard terminal.connectionStatus == .notConnected else { return } Terminal.shared.connectReader(selectedReader) { reader, error in if let reader = reader { print("Successfully connected to reader: \(reader)") } else if let error = error { print("connectReader failed: \(error)") } }) } }
APPReaderDiscoveryViewController.m
Download
#import <StripeTerminal/StripeTerminal.h> #import "APPReaderDiscoveryViewController.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:SCPDiscoveryMethodBluetoothScan simulated:YES]; self.discoverCancelable = [[SCPTerminal shared] discoverReaders:config delegate:self completion:^(NSError *error) { if (error != nil) { NSLog(@"discoverReaders failed: %@", error); } else { NSLog(@"discoverReaders succeeded"); } }]; } // ... #pragma mark - SCPDiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. - (void)terminal:(SCPTerminal *)terminal didUpdateDiscoveredReaders:(NSArray<SCPReader *> *)readers { // Select the first reader we discover SCPReader *selectedReader = [readers firstObject]; // Only connect if we aren't currently connected. if (terminal.connectionStatus != SCPConnectionStatusNotConnected) { return; } [[SCPTerminal shared] connectReader:selectedReader delegate:self connectionConfig:connectionConfiguration completion:^(SCPReader *reader, NSError *error) { if (reader != nil) { NSLog(@"Successfully connected to reader: %@", reader); } else { NSLog(@"connectReader failed: %@", error); } }]; }
#import <StripeTerminal/StripeTerminal.h> #import "APPReaderDiscoveryViewController.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:SCPDiscoveryMethodBluetoothScan simulated:YES]; self.discoverCancelable = [[SCPTerminal shared] discoverReaders:config delegate:self completion:^(NSError *error) { if (error != nil) { NSLog(@"discoverReaders failed: %@", error); } else { NSLog(@"discoverReaders succeeded"); } }]; } // ... #pragma mark - SCPDiscoveryDelegate // This delegate method can get called multiple times throughout the discovery process. // You might want to update a UITableView and display all available readers. // Here, we're automatically connecting to the first reader we discover. - (void)terminal:(SCPTerminal *)terminal didUpdateDiscoveredReaders:(NSArray<SCPReader *> *)readers { // Select the first reader we discover SCPReader *selectedReader = [readers firstObject]; // Only connect if we aren't currently connected. if (terminal.connectionStatus != SCPConnectionStatusNotConnected) { return; } [[SCPTerminal shared] connectReader:selectedReader delegate:self connectionConfig:connectionConfiguration completion:^(SCPReader *reader, NSError *error) { if (reader != nil) { NSLog(@"Successfully connected to reader: %@", reader); } else { NSLog(@"connectReader failed: %@", error); } }]; }

6 Set up your BBPOS Chipper 2X BT reader

You can discover and connect to physical readers as you did with the simulated reader. With the simulated option empty, call discoverReaders to search for nearby Bluetooth readers. When discoverReaders returns a result, call connectReader to connect your application to the reader.

For more information on the BBPOS Chipper 2X BT, see:

  • BBPOS Chipper 2X BT reader guide
  • Connecting to the BBPOS Chipper 2X BT

See also

  • Accept payments
  • Accept multi-party payments
  • Send receipts
  • Provide refunds
Reader:

Set up Terminal for the United States

Based on your selections above, this guide walks you through how to set your integration up for the BBPOS Chipper 2X BT reader using Android in the United States. To accept Terminal charges in the United States, either your platform account or connected account must be in the United States.

Stripe supports Visa, Mastercard, American Express, and Discover payments in the United States. All transactions must be made in U.S. dollars (USD).

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.

The SDK is no longer compatible with the support libraries, as we use Room to store and maintain state across the app lifecycle. Make sure your app has migrated to AndroidX.

To install the SDK, add stripeterminal to the dependencies block of your app/build.gradle file:

build.gradle
apply plugin: 'com.android.application' android { ... } dependencies { implementation "com.stripe:stripeterminal:1.0.17" // ... }
apply plugin: 'com.android.application' android { ... } dependencies { implementation "com.stripe:stripeterminal:1.0.17" // ... }

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository.

For information on migrating from earlier beta versions of the Android SDK, see the Stripe Terminal Beta Migration Guide.

2 Configure your app Client-side

You must enable location access in order to use the Android SDK.

Before initializing the Terminal object, add the following check to make sure that the ACCESS_FINE_LOCATION permission is enabled in your app:

Java Kotlin
MainActivity.java
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(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); }
MainActivity.kt
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) }
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.

Java Kotlin
MainActivity.java
@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 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."); } }
MainActivity.kt
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.") } }
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.") } }

To reduce fraud risks associated with payments, and to minimize disputes, Stripe needs to know where payments occur. If the SDK can’t determine the location of the Android device, payments are disabled until location access is restored.

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 Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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"; // 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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" // 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{} ct, _ := connectiontoken.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 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{} ct, _ := connectiontoken.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"; // 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(); var connectionToken = service.Create(options);
// 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"; // 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(); var connectionToken = service.Create(options);

Obtain the secret from the ConnectionToken on your server and pass it to the client side.

Ruby Python PHP Java Node Go .NET
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }

The ConnectionToken's secret lets you connect to any Stripe Terminal reader and take payments with your Stripe account. Be sure to authenticate the endpoint for creating connection tokens.

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.

Java Kotlin
TokenProvider.java
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)); } } }
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)); } } }
TokenProvider.kt
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) ) } } }
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 needs to authenticate with Stripe or the Reader. It’s also called when a new connection 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 connection token from your backend, connecting to a reader fails with the error from your server.

Do not cache or hardcode the connection token. The SDK manages the connection token's lifecycle.

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
Java Kotlin
TerminalApplication.java
// 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 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); } }
TerminalApplication.kt
// 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) } }
// 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.

Java Kotlin
MainActivity.java
// 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 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();
MainActivity.kt
// 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()
// 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()

If you are currently upgrading from a version below 1.0.0-rc2, note that TerminalLifecycleObserver and Application classes are now mandatory.

5 Develop and test with 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.

Java Kotlin
ReaderActivity.java
// Handler for a "Connect Reader" button public void onConnect() { // Create a DiscoveryConfiguration with the isSimulated field set to true 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 public void onConnect() { // Create a DiscoveryConfiguration with the isSimulated field set to true 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(); } } }
ReaderActivity.kt
// Handler for a "Connect Reader" button fun onConnect() { // Create a DiscoveryConfiguration with the isSimulated field set to true val config = DiscoveryConfiguration(0, DeviceType.CHIPPER_2X, isSimulated = 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() } }) }
// Handler for a "Connect Reader" button fun onConnect() { // Create a DiscoveryConfiguration with the isSimulated field set to true val config = DiscoveryConfiguration(0, DeviceType.CHIPPER_2X, isSimulated = 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() } }) }

6 Set up your BBPOS Chipper 2X BT reader

You can discover and connect to physical readers as you did with the simulated reader. With the simulated option empty, call discoverReaders to search for nearby Bluetooth readers. When discoverReaders returns a result, call connectReader to connect your application to the reader.

For more information on the BBPOS Chipper 2X BT, see:

  • BBPOS Chipper 2X BT reader guide
  • Connecting to the BBPOS Chipper 2X BT

See also

  • Accept payments
  • Accept multi-party payments
  • Send receipts
  • Provide refunds

The Verifone P400 reader using Android in the United States is in beta. For more details, please contact Stripe.

Set up Terminal for the United States

Based on your selections above, this guide walks you through how to set your integration up for the Verifone P400 reader using Android in the United States. To accept Terminal charges in the United States, either your platform account or connected account must be in the United States.

Stripe supports Visa, Mastercard, American Express, and Discover payments in the United States. All transactions must be made in U.S. dollars (USD).

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.

The SDK is no longer compatible with the support libraries, as we use Room to store and maintain state across the app lifecycle. Make sure your app has migrated to AndroidX.

To install the SDK, add stripeterminal to the dependencies block of your app/build.gradle file:

build.gradle
apply plugin: 'com.android.application' android { ... } dependencies { implementation "com.stripe:stripeterminal:1.0.17" // ... }
apply plugin: 'com.android.application' android { ... } dependencies { implementation "com.stripe:stripeterminal:1.0.17" // ... }

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository.

For information on migrating from earlier beta versions of the Android SDK, see the Stripe Terminal Beta Migration Guide.

2 Configure your app Client-side

You must enable location access in order to use the Android SDK.

Before initializing the Terminal object, add the following check to make sure that the ACCESS_FINE_LOCATION permission is enabled in your app:

Java Kotlin
MainActivity.java
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(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); }
MainActivity.kt
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) }
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.

Java Kotlin
MainActivity.java
@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 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."); } }
MainActivity.kt
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.") } }
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.") } }

To reduce fraud risks associated with payments, and to minimize disputes, Stripe needs to know where payments occur. If the SDK can’t determine the location of the Android device, payments are disabled until location access is restored.

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 Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
curl https://api.stripe.com/v1/terminal/connection_tokens \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -X POST
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = Stripe::Terminal::ConnectionToken.create
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
# 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' # 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. connection_token = stripe.terminal.ConnectionToken.create()
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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'); // 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. $connectionToken = \Stripe\Terminal\ConnectionToken::create();
// 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"; // 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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 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. ConnectionTokenCreateParams params = ConnectionTokenCreateParams.builder() .build(); ConnectionToken connectionToken = ConnectionToken.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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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'); const stripe = 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. let connectionToken = stripe.terminal.connectionTokens.create();
// 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" // 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{} ct, _ := connectiontoken.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 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{} ct, _ := connectiontoken.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"; // 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(); var connectionToken = service.Create(options);
// 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"; // 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(); var connectionToken = service.Create(options);

Obtain the secret from the ConnectionToken on your server and pass it to the client side.

Ruby Python PHP Java Node Go .NET
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
post '/connection_token' do token = # ... Create or retrieve the ConnectionToken {secret: token.secret}.to_json end
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
from flask import jsonify @app.route('/connection_token', methods=['POST']) def token(): token = # ... Create or retrieve the ConnectionToken return jsonify(secret=token.secret)
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
<?php $token = # ... Create or retrieve the ConnectionToken echo json_encode(array('secret' => $token->secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
import java.util.HashMap; import java.util.Map; import com.stripe.model.ConnectionToken; import com.google.gson.Gson; import static spark.Spark.post; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); post("/connection_token", (request, response) -> { ConnectionToken token = // ... Fetch or create the ConnectionToken Map<String, String> map = new HashMap(); map.put("secret", token.getSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
const express = require('express'); const app = express(); app.post('/connection_token', async (req, res) => { const token = // ... Fetch or create the ConnectionToken res.json({secret: token.secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
package main import ( "encoding/json" "net/http" ) type TokenData struct { Secret string `json:"secret"` } func main() { http.HandleFunc("/connection_token", func(w http.ResponseWriter, r *http.Request) { token := // ... Fetch or create the ConnectionToken data := TokenData{ Secret: token.secret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }
using System; using Microsoft.AspNetCore.Mvc; namespace StripeExampleApi.Controllers { [Route("connection_token")] [ApiController] public class StripeApiController : Controller { [HttpPost] public ActionResult Post() { var token = // ... Fetch or create the ConnectionToken return Json(new {secret = token.Secret}); } } }

The ConnectionToken's secret lets you connect to any Stripe Terminal reader and take payments with your Stripe account. Be sure to authenticate the endpoint for creating connection tokens.

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.

Java Kotlin
TokenProvider.java
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)); } } }
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)); } } }
TokenProvider.kt
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) ) } } }
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 needs to authenticate with Stripe or the Reader. It’s also called when a new connection 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 connection token from your backend, connecting to a reader fails with the error from your server.

Do not cache or hardcode the connection token. The SDK manages the connection token's lifecycle.

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
Java Kotlin
TerminalApplication.java
// 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 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); } }
TerminalApplication.kt
// 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) } }
// 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.

Java Kotlin
MainActivity.java
// 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 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();
MainActivity.kt
// 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()
// 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()

If you are currently upgrading from a version below 1.0.0-rc2, note that TerminalLifecycleObserver and Application classes are now mandatory.

5 Develop and test with 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.

Java Kotlin
ReaderActivity.java
// Handler for a "Connect Reader" button public void onConnect() { // Create a DiscoveryConfiguration with the isSimulated field set to true DiscoveryConfiguration config = new DiscoveryConfiguration(0, DeviceType.VERIFONE_P400, 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 public void onConnect() { // Create a DiscoveryConfiguration with the isSimulated field set to true DiscoveryConfiguration config = new DiscoveryConfiguration(0, DeviceType.VERIFONE_P400, 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(); } } }
ReaderActivity.kt
// Handler for a "Connect Reader" button fun onConnect() { // Create a DiscoveryConfiguration with the isSimulated field set to true val config = DiscoveryConfiguration(0, DeviceType.VERIFONE_P400, isSimulated = 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() } }) }
// Handler for a "Connect Reader" button fun onConnect() { // Create a DiscoveryConfiguration with the isSimulated field set to true val config = DiscoveryConfiguration(0, DeviceType.VERIFONE_P400, isSimulated = 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() } }) }

6 Set up your Verifone P400 reader

To start testing with your physical reader, you need to configure it for its region of use.

First, create a Location in the United States. We recommend creating a unique location for each physical operating site. The reader uses its registered location to configure itself to accept in-person payments in the United States. In the absence of a registered location, the readers configure themselves based on the country in which your Stripe account is based.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="1272 Valencia Street" \ -d "address[city]"="San Francisco" \ -d "address[state]"=CA \ -d "address[country]"=US \ -d "address[postal_code]"=94110
curl https://api.stripe.com/v1/terminal/locations \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d display_name=HQ \ -d "address[line1]"="1272 Valencia Street" \ -d "address[city]"="San Francisco" \ -d "address[state]"=CA \ -d "address[country]"=US \ -d "address[postal_code]"=94110
# 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::Location.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
# 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::Location.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
# 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.Location.create( display_name='HQ', address={ line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', }, )
# 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.Location.create( display_name='HQ', address={ line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', }, )
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "1272 Valencia Street", 'city' => "San Francisco", 'state' => "CA", 'country' => "US", 'postal_code' => "94110", ] ]);
// 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\Location::create([ 'display_name' => 'HQ', 'address' => [ 'line1' => "1272 Valencia Street", 'city' => "San Francisco", 'state' => "CA", 'country' => "US", 'postal_code' => "94110", ] ]);
// 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"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("1272 Valencia Street") .setCity("San Francisco") .setState("CA") .setCountry("US") .setPostalCode("94110") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; LocationCreateParams.Address address = LocationCreateParams.Address.builder() .setLine1("1272 Valencia Street") .setCity("San Francisco") .setState("CA") .setCountry("US") .setPostalCode("94110") .build(); LocationCreateParams params = LocationCreateParams.builder() .setDisplayName("HQ") .setAddress(address) .build(); Location.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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const location = await stripe.terminal.locations.create({ display_name: 'HQ', address: { line1: '1272 Valencia Street', city: 'San Francisco', state: 'CA', country: 'US', postal_code: '94110', } })
// 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.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("1272 Valencia Street"), City: stripe.String("San Francisco"), State: stripe.String("CA"), Country: stripe.String("US"), PostalCode: stripe.String("94110"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalLocationParams{ Address: &stripe.AccountAddressParams{ Line1: stripe.String("1272 Valencia Street"), City: stripe.String("San Francisco"), State: stripe.String("CA"), Country: stripe.String("US"), PostalCode: stripe.String("94110"), }, DisplayName: stripe.String("HQ"), } l, _ := location.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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "1272 Valencia Street", City = "San Francisco", State = "CA", Country = "US", PostalCode = "94110", }, }; var service = new LocationService(); service.Create(options);
// 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 LocationCreateOptions { DisplayName = "HQ", Address = new AddressOptions { Line1 = "1272 Valencia Street", City = "San Francisco", State = "CA", Country = "US", PostalCode = "94110", }, }; var service = new LocationService(); service.Create(options);

Next, register your reader to the location. On the Verifone P400, this is done by entering the key sequence 0-7-1-3-9 to display a unique, three-word registration code. This code must be passed to Stripe in order to complete registration of the reader to your account and location; you can do so in the Dashboard or via the API.

curl Ruby Python PHP Java Node Go .NET
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -d label="Alice's Reader"
curl https://api.stripe.com/v1/terminal/readers \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d registration_code="{READER_REGISTRATION_CODE}" \ -d location="{LOCATION_ID}" \ -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", location: "{LOCATION_ID}", })
# 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", location: "{LOCATION_ID}", })
# 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", location="{LOCATION_ID}", )
# 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", location="{LOCATION_ID}", )
// 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", 'location' => "{LOCATION_ID}", ]);
// 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", 'location' => "{LOCATION_ID}", ]);
// 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") .setLocation("{LOCATION_ID}") .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 Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; ReaderCreateParams params = ReaderCreateParams.builder() .setRegistrationCode("{READER_REGISTRATION_CODE}") .setLabel("Alice's Reader") .setLocation("{LOCATION_ID}") .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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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'); const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const reader = await stripe.terminal.readers.create({ registration_code: "{READER_REGISTRATION_CODE}", label: "Alice's Reader", location: "{LOCATION_ID}", });
// 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"), Location: stripe.String("{LOCATION_ID}"), 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 stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.TerminalReaderParams{ Label: stripe.String("Alice's Reader"), Location: stripe.String("{LOCATION_ID}"), 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);
// 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", Location = "{LOCATION_ID}", }; var service = new ReaderService(); service.Create(options);

When your reader is registered, you can discover and connect to it as you did with the simulated reader.

With the simulated option empty or set to false, call discoverReaders to search for readers. You can filter the discoverReaders call by the location the reader is registered to. When discoverReaders returns a result, call connectReader to connect your application to the reader.

For more information on the Verifone P400, see:

  • Verifone P400 reader guide
  • Connecting to the Verifone P400

See also

  • Accept payments
  • Accept multi-party payments
  • Send receipts
  • Provide refunds
Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.
You can unsubscribe at any time. Read our privacy policy.