Recently, my colleague Charles Watkins wrote a 4-part intro series to Stripe Terminal, our in-person payment solution. Charles' articles will teach you how to set up and register a Terminal device and build an app to accept or cancel payments. Let's take it a step further and look into an additional feature: saving card details via the Terminal device for future online reuse.
This feature can improve your customers’ overall experience by simplifying the checkout process.
For example, when checking into a hotel, you are often asked to pre-authorize your card in case the hotel needs to charge you for any damage done to the room or get any room service directly charged to your account. Your card details are usually saved via a terminal device and re-used later on without needing additional authorization.
Creating or retrieving a customer
With Stripe, in order to charge a payment method more than once, it must be attached to a customer object, so the first step in this process is to create or retrieve your customer.
Creating a new customer
Creating a customer can be done with the following code:
// See your keys here: https://dashboard.stripe.com/apikeys
const stripe = require('stripe')('your-API-key');
const customer = await stripe.customers.create();
This will create a standard customer object; however, if you’d like to assign specific properties, you can do so by passing them to the create
method.
// See your keys here: https://dashboard.stripe.com/apikeys
const stripe = require('stripe')('your-API-key');
const customer = await stripe.customers.create({
name: "Hedy Lamarr",
email: "hedy@lamarr.com",
description:
"Patented radio frequency hopping, the basis for Wi-Fi, GPS and Bluetooth",
});
Retrieving a customer
If your customer already exists, you can retrieve it using the retrieve
method and pass it the customer ID starting with cus_
:
// See your keys here: https://dashboard.stripe.com/apikeys
const stripe = require('stripe')('your-API-key');
const customer = await stripe.customers.retrieve("cus_xxx");
If you do not know the customer ID, you can search for it by using the search
method, for example:
const customers = await stripe.customers.search({
query: 'name:\'Hedy Lamarr\'', 'email:\'hedy@lamarr.com\''
});
This will return an object containing an array of customers that match the query. From there, you can loop through the array, find your customer and their ID.
Once you have your customer object, you can move on to saving their card details.
Attaching card details to a customer
First, you need to create a SetupIntent that will collect and record the customer’s consent to have their card details saved. This will authorize the payment method with the customer’s bank so future authentication will not be needed.
const intent = await stripe.setupIntents.create({
payment_method_types: ["card_present"],
customer: customer.id,
});
Then, you need to call the processSetupIntent
method on the reader to collect the payment method and attach it to the customer. You need to pass it the reader ID, as well as an object containing the setup intent ID and the property customer_consent_collected
set to a boolean value.
When testing, you can set this value to true
, however, when implementing a production-level solution, make sure to adapt your checkout flow so you can obtain consent from your customer. For example, if you have a custom UI, you can update it to collect consent and send the value true
or false
back to your server before continuing.
const reader = await stripe.terminal.readers.processSetupIntent(
"tmr_xxx",
{
setup_intent: intent.id,
customer_consent_collected: true,
}
);
If you do not know your terminal ID, you can find it using the following code:
const readers = await stripe.terminal.readers.list();
This will return an object containing an array of terminals you registered.
{
object: 'list',
data: [
{
id: 'tmr_xxx',
object: 'terminal.reader',
action: null,
device_sw_version: '2.6.2.0',
device_type: 'bbpos_wisepos_e',
ip_address: '192.000.0.00',
label: 'my-terminal',
livemode: false,
location: 'tml_xxx',
metadata: {},
serial_number: 'WSCxxx',
status: 'online'
}
],
has_more: false,
url: '/v1/terminal/readers'
}
Each terminal is associated with a location. Via the Stripe dashboard, you can add metadata to the location so it becomes easier to filter through the list of devices. For example, you could add a metadata object with the key-value pair {“reception”: 1}
and filter the list of terminal objects to return only the one that contains {“reception”: 1}
in its metadata. From there, you can find the device’s ID, starting with “tmr_”.
When the processSetupIntent
method is called, the terminal display updates, waiting for the customer to insert, tap or swipe their card.
Finally, if you want to verify that the payment method was successfully attached to the customer, you can call listPaymentMethods
.
const paymentMethods = await stripe.customers.listPaymentMethods(
customer.id,
{ type: "card" }
);
This will return an object containing an array of payment methods attached to that customer.
Voila! Your customer’s card details are now saved for online reuse, without charging them!
Saving a card after payment
When collecting payments with Terminal, you need to create a PaymentIntent, using paymentIntents.create
. This does not save the card details by default. You can change the default behavior by passing the additional properties paymentMethod
and setup_future_usage
.
const paymentIntent = await stripe.paymentIntents.create({
payment_method_types: ['card'],
amount: 1099,
currency: 'usd',
customer: customer.id,
payment_method: paymentMethods[0].id,
setup_future_usage: 'off_session',
});
Now, you should be able to update your checkout flow so you can save card details without charging them, or after payment, so your customers can reuse them online easily.
Let us know if you’re implementing this, and stay up to date with Stripe developer updates on the following platforms:
📣 Follow @StripeDev and our team on Twitter
📺 Subscribe to our Youtube channel
💬 Join the official Discord server
📧 Sign up for the Dev Digest
About the author
Charlie Gerard is a Developer Advocate at Stripe, a creative technologist and Google Developer Expert. She loves researching and experimenting with technologies. When she’s not coding, she enjoys spending time outdoors, trying new beers and reading.
Top comments (0)