Create an omnichannel shopping experience with Stripe Terminal in Node.js
Tags: stripe, terminal, payments, node
Published: 2022-08-29 16:59:35

---
title: Create an omnichannel shopping experience with Stripe Terminal in Node.js
published: true
description: Learn how to save your customers' card details using a Terminal device, so you can create an omnichannel checkout experience. 
tags: stripe, terminal, payments, nodejs
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkkmp3ou2rhlcs2l6koi.png
---

Recently, my colleague [Charles Watkins](https://twitter.com/charlesw_dev) wrote a [4-part intro series to Stripe Terminal](https://dev.to/charlesw_dev/series/18169), 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:

```javascript
// 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.

```javascript
// 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_`:

```javascript
// 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:

```javascript
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](https://stripe.com/docs/api/setup_intents) 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.

```javascript
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.

```javascript
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:

```javascript
const readers = await stripe.terminal.readers.list();
```

This will return an object containing an array of terminals you registered.

```javascript
{
 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](https://dashboard.stripe.com/terminal/locations), 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_”.

![Screenshot of the Stripe dashboard showing a location with a terminal instance and a metadata object with key "reception" and value "1".](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2fdrv7x52tvhk9p6l480.png)

When the `processSetupIntent` method is called, the terminal display updates, waiting for the customer to insert, tap or swipe their card. 

![GIF showing the terminal display waiting for a customer to insert, tap or swipe their card.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/of4tkqfu710fzc5r54y8.gif)

Finally, if you want to verify that the payment method was successfully attached to the customer, you can call `listPaymentMethods`.

```javascript
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](https://stripe.com/docs/terminal/payments/collect-payment#create-payment), 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`.

```javascript
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](https://twitter.com/stripedev) and [our team](https://twitter.com/i/lists/1459720002567868418) on [Twitter](https://twitter.com/StripeDev)
📺 Subscribe to our [Youtube channel](https://www.youtube.com/StripeDevelopers)
💬 Join the official [Discord server](https://discord.com/invite/RuJnSBXrQn)
📧 Sign up for the [Dev Digest](https://go.stripe.global/dev-digest)

## About the author

![Charlie's profile picture. She is a caucasian woman with brown hair and brown eyes. She is wearing silver glasses. She is standing in front of a plain white wall with purple lights.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1n6g5avk2h7gelnfsbwe.png)

[Charlie Gerard](https://twitter.com/@devdevcharlie) is a Developer Advocate at Stripe, a [creative technologist](https://charliegerard.dev/) and [Google Developer Expert](https://developers.google.com/community/experts). She loves researching and [experimenting with technologies](https://charliegerard.dev/). When she’s not coding, she enjoys spending time outdoors, trying new beers and reading.