<?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>Forem: Richard</title>
    <description>The latest articles on Forem by Richard (@twitchard).</description>
    <link>https://forem.com/twitchard</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%2F18817%2F31cbdf50-8d8a-4fb1-878e-4095353bc96c.jpg</url>
      <title>Forem: Richard</title>
      <link>https://forem.com/twitchard</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/twitchard"/>
    <language>en</language>
    <item>
      <title>stripe-node with Deno</title>
      <dc:creator>Richard</dc:creator>
      <pubDate>Wed, 06 Sep 2023 19:32:32 +0000</pubDate>
      <link>https://forem.com/4thzoa/stripe-node-with-deno-1fc4</link>
      <guid>https://forem.com/4thzoa/stripe-node-with-deno-1fc4</guid>
      <description>&lt;p&gt;This post shows an example of &lt;code&gt;stripe-node&lt;/code&gt; usage in a Deno environment. Beyond this post, there's a &lt;a href="https://www.youtube.com/watch?v=epCHqHEdz8I" rel="noopener noreferrer"&gt;video tutorial&lt;/a&gt;, a &lt;a href="https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/deno/main.ts" rel="noopener noreferrer"&gt;Deno example&lt;/a&gt; in the stripe-node repository itself, and there is also a &lt;a href="https://github.com/stripe-samples/stripe-node-deno-samples/blob/main/checkout-integration/main.js" rel="noopener noreferrer"&gt;Stripe sample&lt;/a&gt; that you can read or clone from Github. If you just want to see code, wander over to Github. Stay here if you prefer your code examples with a side of dramatic commentary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Earlier this year, in &lt;code&gt;stripe-node&lt;/code&gt; &lt;a href="https://github.com/stripe/stripe-node/releases/tag/v11.15.0" rel="noopener noreferrer"&gt;v11.15.0&lt;/a&gt;, we added support to import stripe-node as an ES Module. This means it is much more convenient to use Stripe in non-NodeJS runtime environments, like Cloudflare Workers or Deno. Something funny: "stripe-node" is a bit misnomerous now. Node is not the only option anymore. Maybe &lt;code&gt;stripe-server-js&lt;/code&gt; would be a more accurate name, but that doesn't quite roll off the tongue, does it? We'll stick with &lt;code&gt;stripe-node&lt;/code&gt;, I'm sure you know what we mean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You'll need the &lt;a href="https://stripe.com/docs/stripe-cli" rel="noopener noreferrer"&gt;Stripe CLI&lt;/a&gt; installed.&lt;/li&gt;
&lt;li&gt;Deno, version &lt;a href="https://github.com/denoland/deno/releases/tag/v1.28.0" rel="noopener noreferrer"&gt;1.28&lt;/a&gt; or later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start your project
&lt;/h2&gt;

&lt;p&gt;If you're starting from scratch, then now it's time to run &lt;code&gt;deno init&lt;/code&gt;. If you've already got a Deno project going, way to go! You're ahead of the game, but you might have to adapt the rest of the instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing Stripe
&lt;/h2&gt;

&lt;p&gt;Deno init creates several files for you, among them &lt;code&gt;main.ts&lt;/code&gt;, the entry point for your app. Open it with your code editor. No need to &lt;code&gt;npm install&lt;/code&gt; with Deno. You can simply put&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Stripe&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;npm:stripe@^13.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;at the top of your &lt;code&gt;main.ts&lt;/code&gt;, and the package will be installed automatically. At the time of this writing, &lt;code&gt;13.x.x&lt;/code&gt; is the latest major line of stripe-node, but if you're from the future you can &lt;code&gt;npm view stripe version&lt;/code&gt; to see the latest version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; you will need to import the &lt;em&gt;npm&lt;/em&gt; versions of stripe-node. The versions at unpkg.com or deno.land aren't quite working due to how the import statements in our source code are written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Deno's import maps
&lt;/h2&gt;

&lt;p&gt;If you're going to be importing &lt;code&gt;stripe&lt;/code&gt; inside multiple files, you might not want to have to update the version in every import statement when it comes time to update the Stripe library. In this case, you will instead want to use a &lt;a href="https://deno.land/manual@v1.36.4/basics/import_maps" rel="noopener noreferrer"&gt;bare specifier and Deno's import maps&lt;/a&gt;. Change that import statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- import Stripe from "npm:stripe@^13.0.0"
&lt;/span&gt;&lt;span class="gi"&gt;+ import Stripe from "stripe"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then edit &lt;code&gt;deno.jsonc&lt;/code&gt; to add an entry for stripe in the "imports" map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  {
    "tasks": {
      "dev": "deno run --watch main.ts"
&lt;span class="gd"&gt;-   }
&lt;/span&gt;&lt;span class="gi"&gt;+   },
+   "imports": {
+     "stripe": "npm:stripe@^13.0.0"
+   }
&lt;/span&gt;  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila!&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Stripe
&lt;/h2&gt;

&lt;p&gt;In Deno, you use Stripe just like you would in any other Typescript codebase. For example, suppose you want to make an API call to list customers. Initialize the Stripe client with your API key, and make the API call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR (TEST MODE) API KEY GOES HERE&amp;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;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2023-08-16&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&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;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;crustomers&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;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&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;customers&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;length&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;customers&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="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&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;h2&gt;
  
  
  Fetch vs. npm:https
&lt;/h2&gt;

&lt;p&gt;In recent versions of Deno, this will Just Work, because Deno has added compatibility shims for the Node standard library. In versions of Deno before the Node compatibility mode (introduced in &lt;a href="https://deno.com/blog/v1.15#improving-node-compatibility" rel="noopener noreferrer"&gt;v1.15&lt;/a&gt;, stabilized in &lt;a href="https://deno.com/blog/v1.28" rel="noopener noreferrer"&gt;1.28&lt;/a&gt;), you would have had to write&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  const stripe = new Stripe("&amp;lt;YOUR API KEY GOES HERE&amp;gt;", {
    apiVersion: "2023-08-16",
&lt;span class="gi"&gt;+   httpClient: Stripe.createFetchHttpClient(),
&lt;/span&gt;  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to tell stripe-node that you want to use the "fetch" API (native to the web, and to Deno), rather than attempting to use the "node:https" module from the Node standard library (which stripe-node tries to use by default). I recommend do this even if you are on a new version of Deno, though, just to skip the compatibility layer and keep things behind the scenes simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typecheck it, run it, get permission
&lt;/h2&gt;

&lt;p&gt;Before we run the code, let's type check it:&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="nv"&gt;$ &lt;/span&gt;deno check main.ts
error: TS2551 &lt;span class="o"&gt;[&lt;/span&gt;ERROR]: Property &lt;span class="s1"&gt;'crustomers'&lt;/span&gt; does not exist on &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'Stripe'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Did you mean &lt;span class="s1"&gt;'customers'&lt;/span&gt;?
  const customers &lt;span class="o"&gt;=&lt;/span&gt; await stripe.crustomers.list&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whoops! An embarassing typo, but at least we know the types are all set up and working. Fix the typo, run &lt;code&gt;deno check main.ts&lt;/code&gt; and once we get a clean bill of health, let's go ahead and run our program!&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="nv"&gt;$ &lt;/span&gt;deno run main.ts

┌ ⚠️  Deno requests net access to &lt;span class="s2"&gt;"api.stripe.com"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├ Requested by &lt;span class="sb"&gt;`&lt;/span&gt;fetch&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; API.
├ Run again with &lt;span class="nt"&gt;--allow-net&lt;/span&gt; to bypass this prompt.
└ Allow? &lt;span class="o"&gt;[&lt;/span&gt;y/n/A] &lt;span class="o"&gt;(&lt;/span&gt;y &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;, allow&lt;span class="p"&gt;;&lt;/span&gt; n &lt;span class="o"&gt;=&lt;/span&gt; no, deny&lt;span class="p"&gt;;&lt;/span&gt; A &lt;span class="o"&gt;=&lt;/span&gt; allow all net permissions&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is Deno's hallmark &lt;em&gt;permissions system&lt;/em&gt; at play. In exchange for a bit of inconvenience, it offers you some peace of mind. If you see "api.stripe.com" as the domain name, nobody has compromised your supply chain with a malicious version of &lt;code&gt;stripe-node&lt;/code&gt; that transmits your Stripe credentials to some other domain. Go ahead and press &lt;code&gt;y&lt;/code&gt;, and the program will run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✅ Granted net access to &lt;span class="s2"&gt;"api.stripe.com"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
5
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"richardm+1@example.com"&lt;/span&gt;, &lt;span class="s2"&gt;"richardm+2@example.com"&lt;/span&gt;, ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run it with the command-line flag to bypass the prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ deno run main.ts --allow-net=api.stripe.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's quite a mouthful. If you don't want to have to type all that every time, you should create a "task" in your &lt;code&gt;deno.jsonc&lt;/code&gt;. There's already one in there called "dev" created by "deno init" that you can repurpose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  {
    "tasks": {
&lt;span class="gd"&gt;-     "dev": "deno run --watch main.ts"
&lt;/span&gt;&lt;span class="gi"&gt;+     "dev": "deno run --watch --allow-net=api.strippe.com main.ts"
&lt;/span&gt;    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I'm a suspicious person by nature. So I've deliberately typoed the domain name as a test.&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="nv"&gt;$ &lt;/span&gt;deno task dev
Task dev deno run &lt;span class="nt"&gt;--watch&lt;/span&gt; &lt;span class="nt"&gt;--allow-net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;api.strippe.com main.ts
Watcher Process started.
┌ ⚠️  Deno requests net access to &lt;span class="s2"&gt;"api.stripe.com"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├ Requested by &lt;span class="sb"&gt;`&lt;/span&gt;fetch&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; API.
├ Run again with &lt;span class="nt"&gt;--allow-net&lt;/span&gt; to bypass this prompt.
└ Allow? &lt;span class="o"&gt;[&lt;/span&gt;y/n/A] &lt;span class="o"&gt;(&lt;/span&gt;y &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;, allow&lt;span class="p"&gt;;&lt;/span&gt; n &lt;span class="o"&gt;=&lt;/span&gt; no, deny&lt;span class="p"&gt;;&lt;/span&gt; A &lt;span class="o"&gt;=&lt;/span&gt; allow all net permissions&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I press &lt;code&gt;n&lt;/code&gt; and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❌ Denied net access to &lt;span class="s2"&gt;"api.stripe.com"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
error: Uncaught Error: An error occurred with our connection to Stripe. Request was retried 1 times.
    at file:///Users/richardm/Library/Caches/deno/npm/registry.npmjs.org/stripe/13.4.0/esm/RequestSender.js:319:37
    at eventLoopTick &lt;span class="o"&gt;(&lt;/span&gt;ext:core/01_core.js:197:13&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything seems to be in order! I fix the typo, run &lt;code&gt;npm task dev&lt;/code&gt; again, and everything is hunky-dory! Not only did I confirm the workings of Deno's permission system, but this helps me save face and pretend my previous embarassing typo "crustomers" was a deliberate choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webhooks
&lt;/h2&gt;

&lt;p&gt;Now for completeness, let's listen for webhooks too.&lt;/p&gt;

&lt;p&gt;First, run &lt;code&gt;stripe listen&lt;/code&gt; to receive events from the Stripe API and direct them to localhost. Let's be cliché and pick port 3000.&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="nv"&gt;$ &lt;/span&gt;stripe listen &lt;span class="nt"&gt;--latest&lt;/span&gt; &lt;span class="nt"&gt;--forward-to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost:3000
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Ready! You are using Stripe API Version &lt;span class="o"&gt;[&lt;/span&gt;2023-08-16]. Your webhook signing secret is whsec_... &lt;span class="o"&gt;(&lt;/span&gt;^C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the webhook signing secret, and let's edit &lt;code&gt;main.ts&lt;/code&gt; one last time and add the following snippet of code, lightly adapted from the &lt;a href="https://github.com/stripe/stripe-node/blob/c384675a45f6ef62d9cfb061e7987925eceb767a/examples/webhook-signing/deno/main.ts#L16" rel="noopener noreferrer"&gt;examples in github.com/stripe/stripe-node&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&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="nx"&gt;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;127.0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&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="mi"&gt;3000&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;`Webhook endpoint available at http://127.0.0.1:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&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;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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-Signature&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;body&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;event&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="nx"&gt;event&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;webhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constructEventAsync&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="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR SIGNING SECRET GOES HERE&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kc"&gt;undefined&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;err&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;`❌ Error message: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&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="mi"&gt;400&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="s1"&gt;✅ Success:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payment_intent.succeeded&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&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;object&lt;/span&gt; &lt;span class="k"&gt;as&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;PaymentIntent&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;`💰 PaymentIntent status: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&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="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;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`❌Unhandled event type: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&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="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;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;received&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="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&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;await&lt;/span&gt; &lt;span class="nf"&gt;serveListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&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 time to run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;deno&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="nx"&gt;dev&lt;/span&gt;
&lt;span class="nx"&gt;Task&lt;/span&gt; &lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="nx"&gt;deno&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&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;com&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;┌&lt;/span&gt; &lt;span class="err"&gt;⚠️&lt;/span&gt;  &lt;span class="nx"&gt;Deno&lt;/span&gt; &lt;span class="nx"&gt;requests&lt;/span&gt; &lt;span class="nx"&gt;net&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;127.0.0.1:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="err"&gt;├&lt;/span&gt; &lt;span class="nx"&gt;Requested&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="s2"&gt;`Deno.listen()`&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="err"&gt;├&lt;/span&gt; &lt;span class="nx"&gt;Run&lt;/span&gt; &lt;span class="nx"&gt;again&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;net&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;bypass&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="err"&gt;└&lt;/span&gt; &lt;span class="nx"&gt;Allow&lt;/span&gt;&lt;span class="p"&gt;?&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="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;]&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="nx"&gt;yes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deny&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="nx"&gt;all&lt;/span&gt; &lt;span class="nx"&gt;net&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The permissions system is at it again! Let's update &lt;code&gt;deno.jsonc&lt;/code&gt; again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;    "tasks": {
&lt;span class="gd"&gt;-     "dev": "deno run --watch --allow-net=api.stripe.com main.ts"
&lt;/span&gt;&lt;span class="gi"&gt;+     "dev": "deno run --watch --allow-net=api.stripe.com,127.0.0.1:3000 main.ts"
&lt;/span&gt;    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;deno task dev&lt;/code&gt;, and it will run in the background, listening for events. Make sure you've got &lt;code&gt;stripe listen --latest --forward-to=localhost:3000&lt;/code&gt; running in another terminal, and then in a third terminal, run &lt;code&gt;stripe trigger payment_intent.succeeded&lt;/code&gt; and you should see your Deno process log output like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Webhook endpoint available at http://127.0.0.1:3000/
✅ Success: evt_3Nnabcdefghijklmnopqrstu
❌ Unhandled event &lt;span class="nb"&gt;type&lt;/span&gt;: payment_intent.created
✅ Success: evt_3Nnabcdefghijklmnopqrstu
❌ Unhandled event &lt;span class="nb"&gt;type&lt;/span&gt;: charge.succeeded
✅ Success: evt_3Nnabcdefghijklmnopqrstu
💰 PaymentIntent status: succeeded
✅ Success: evt_3Nnabcdefghijklmnopqrstu
❌ Unhandled event &lt;span class="nb"&gt;type&lt;/span&gt;: payment_intent.created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;That wraps it up for the post, but some obvious next steps would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use environment variables for your signing secret and API key.&lt;/li&gt;
&lt;li&gt;Deploy to a server on the Internet instead of localhost, and create a webhook endpoint to send Stripe events to that server.&lt;/li&gt;
&lt;li&gt;Write the Stripe-related logic you actually need for your app. Listing customers is just the start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I believe in you. You've got this. Don't let the haters get you down. I will leave you with these words of inspiration: if you have a suggestion, or something seems wrong with stripe-node's Deno support, you can always leave feedback in a &lt;a href="https://github.com/stripe/stripe-node/issues" rel="noopener noreferrer"&gt;Github issue at github.com/stripe/stripe-node&lt;/a&gt;. Good luck!&lt;/p&gt;

</description>
      <category>deno</category>
      <category>typescript</category>
      <category>stripe</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
