Nuxtstop

For all things nuxt.js

Ultrasonic payments

Ultrasonic payments
230 7

Recently, my colleague Mike Bifulco wrote a blog post about using Near-Field Communication (NFC) technology to request payment using NFC tags and Stripe Payment Links. In a similar spirit, I came across the concept of “Ultrasonic payments”, a technology based on transferring data through inaudible sounds, and decided to experiment and look into how to implement such a payment method with Stripe.

Here’s the result below. My iPad is in airplane mode, so not connected to the internet, and transmits a payment link to my phone via ultrasound.

If you would like to try this out, check out the live demo or have a look at the repository on GitHub.

Disclaimer: This is an experiment. The ultimate goal would have been to implement a solution similar to tap-to-pay using ultrasounds, however, even though this is possible and has been done by companies such as LISNR, this is not something that can be easily prototyped. Instead, I decided to explore by sending Payment Links, a no-code payment solution that allows you to request payment from customers with a simple link. I still wanted to share what I tried and learned, as this technology can still be applied to different applications than payments.

Soundwaves

Before focusing on payments, it’s important to understand how devices transform analog sound into digital data so, without going into too much detail, here’s a brief explanation of how it works.

Sound is created by vibrations that produce shifts in the flow of molecules. These molecules bump into each other, creating a chain reaction called a sound wave. When this wave reaches a microphone, the movement of air molecules creates pressure against the thin membrane found in the device, creating electrical signals that are then converted to digital values using an Analog to Digital converter (ADC).

Animation showing how sound waves create pressure against the membrane of a microphone, creating electrical energy.
Source: "How do microphones work?"

Humans can usually hear sounds from a frequency range of 20Hz-20kHz. However, it doesn’t mean that sound does not exist outside of these frequencies, it only means we cannot hear it. As a result, you can transmit data at frequencies outside of what the human ear can detect, through inaudible sounds. This is a technique called ultrasonic data transmission.

Graphic representing the electromagnetic spectrum where the spectrum of frequencies humans can hear falls between infrasound and ultrasound
Source: What is ultrasound?

So, how can we use this to transmit a payment?

Ultrasonic data transmission in JavaScript

This prototype is going to rely on quiet.js, a JavaScript library to transmit data using sound.

The same way that usual tap-to-pay systems require both the terminal and personal device to be NFC-enabled, sending data through sound requires the devices involved to have a microphone and speaker.

For the purpose of this experiment, the merchant’s device should have a speaker as it will be transmitting the payment link, and the customer’s device should have a microphone to receive it.
This scenario would allow merchants to use an iPad in-store to sell their products, without having to acquire a terminal device.

Transmitting data

Using quiet.js to transmit text via sound can be done by creating a transmitter instance, specifying some parameters including the frequency, gain and frame length, and finally calling the .transmit() method with the payload.

Let’s look into the code needed to do this.

Initial setup

To start, the library needs to be imported. Quiet.js includes a blob of libquiet that it relies on, and JavaScript bindings, so in my prototype, I used script tags to import quiet.js and quiet-emscripten.js. I also initiated Quiet with the path to a couple of required files; profilesPrefix indicates where the quiet-profiles.json file is located and memoryInitializerPrefix, indicates where quiet-emscripten.js.mem can be found.

<script type="text/javascript" src="quiet.js"></script>
<script>
  Quiet.init({
    profilesPrefix: "/",
    memoryInitializerPrefix: "/"
   });
</script>
<script
  async
  type="text/javascript"
  src="quiet-emscripten.js"
></script>
Enter fullscreen mode Exit fullscreen mode

Creating a transmitter

To create a transmitter, there is a transmitter() method where you need to indicate which profile you’d like to use, as well as a callback function that is called when the transmission has ended.

var transmit = Quiet.transmitter({
  profile: profilename,
  onFinish: onFinish
});
Enter fullscreen mode Exit fullscreen mode

The profile is the set of parameters I mentioned previously, containing information about how the data should be transmitted (the frame length, gain, etc.).

The one I used looks like this:

 "ultrasonic": {
   "mod_scheme": "gmsk",
   "checksum_scheme": "crc32",
   "inner_fec_scheme": "v27",
   "outer_fec_scheme": "none",
   "frame_length": 34,
   "modulation": {
     "center_frequency": 19000,
     "gain": 0.15
   },
   "interpolation": {
     "shape": "rrcos",
     "samples_per_symbol": 14,
     "symbol_delay": 4,
     "excess_bandwidth": 0.35
   },
   "encoder_filters": {
     "dc_filter_alpha": 0.01
   },
   "resampler": {
     "delay": 13,
     "bandwidth": 0.45,
     "attenuation": 60,
     "filter_bank_size": 64
   }
 },
Enter fullscreen mode Exit fullscreen mode

No need to dive into what all these properties do right now, they mainly indicate how the data should be encoded.

Next, a data string can be transmitted using the transmit() method and passing a value converted to an array buffer. For this prototype, I’m sending a payment link.

transmit.transmit(Quiet.str2ab(https://buy.stripe.com/test_00gfZ73t04dxaGI3cc”));
Enter fullscreen mode Exit fullscreen mode

Creating a receiver

Creating a receiver is similar to creating a transmitter. First you need to use the receiver() method, passing the profile and some callback functions, and then decode the data received.

 Quiet.receiver({
   profile: profilename,
   onReceive: onReceive
 });
Enter fullscreen mode Exit fullscreen mode

The onReceive() function receives a payload, converts the array buffer in UTF8 to string, creates a URL object and checks if the host is “buy.stripe.com” to ensure the link sent is a payment link. If so, it opens it in the browser window.

const onReceive = (recvPayload) => {
 const link = Quiet.ab2str(recvPayload);
 const linkURL = new URL(link);

 if (linkURL.host === "buy.stripe.com") {
   window.location.href = linkURL;
 }
};
Enter fullscreen mode Exit fullscreen mode

More code would be needed to implement error handling or working with more complex data structures but for the purpose of this experiment, that’s pretty much it!

Learnings

Before starting this experiment, I asked myself a few questions I wanted to be able to answer.

Will it work in a noisy environment?

I tested this while playing music in the background and the receiver still detected the payment link with no problem. Considering that the music was playing at a different frequency, it makes sense.

Will it work if the transmitter is playing music at the same time?

Yes. I tested this with my laptop as the transmitter. While playing a video, it still successfully transmitted the payment link.

How far can the data be transmitted?

In this experiment, it successfully transmitted the payment link up to 55cm (21.65 inches) away from the transmitter (yes, I measured). However, the further away the receiver was from the transmitter, the more failed attempts there were.

How much data can be transmitted?

I did not experiment too much in that sense, but according to their official demo, it is possible to transmit images using quiet.js.

Why experiment with this?

With the growth in adoption of contactless payments, I wanted to look into alternatives to NFC technology. The COVID-19 pandemic made people more concerned about touching surfaces, including in stores, and even though NFC is contactless, the distance between a merchant's terminal and a customer’s phone or card is quite small. NFC technology enables communication between two electronic devices over a distance of 4 cm (1.5 inches) or less. Ultrasonic payment solutions would enable communication over a greater distance.

Moreover, this technology transmitting data through sounds also means that it does not require the devices to be connected to the internet, potentially making this more secure. To process payments, a connection would end up being needed but there could be other opportunities than payments where it would be a valid solution.

Considering that this technology only uses a microphone and speaker, it could allow more devices to become payment terminals, such as laptops, TVs, etc.

Conclusion

This technology isn’t new, and can be used for more purposes than payments. Advertising companies have used this technology to send out inaudible marketing beacons from TV ads to nearby phones running specific applications for targeted advertising for years (yikes). Google has also implemented this technology in some of their products to automatically detect nearby devices. When it comes to payments, while there are more regulations and security concerns to take into consideration, it is still interesting to explore this space, and understand its opportunities and limitations.

What would you do with this technology? Feel free to clone the repo and let us know what you build!

📣 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's profile picture

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.