<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>boogsbunny</title>
<generator>Emacs webfeeder.el</generator>
<link href="https://boogs.life/"/>
<link href="https://boogs.life/atom.xml" rel="self"/>
<id>https://boogs.life/atom.xml</id>
<updated>2025-08-20T18:39:30+00:00</updated>
<entry>
  <title>Add billing to your Common Lisp app</title>
  <author><name>root</name></author>
  <content type="html"><![CDATA[<div id="content" class="content">
 <header> <h1 class="title">Add billing to your Common Lisp app</h1>
</header> <nav id="table-of-contents" role="doc-toc"> <h2>Table of Contents</h2>
 <div id="text-table-of-contents" role="doc-toc">
 <ul> <li> <a href="#orgb027a33">Libraries</a></li>
 <li> <a href="#org1edbf13">Installation</a>
 <ul> <li> <a href="#orge07bb7f">Package managers</a>
 <ul> <li> <a href="#orge522287">quicklisp</a></li>
 <li> <a href="#org854c965">ocicl</a></li>
</ul></li>
 <li> <a href="#org4d2d7e6">From source</a></li>
</ul></li>
 <li> <a href="#org7b559ac">Requirements</a></li>
 <li> <a href="#orgb9c7288">Minimal example</a></li>
 <li> <a href="#org5ef0ee0">Handling environment variables</a></li>
 <li> <a href="#orgcfe25aa">Billing flow</a></li>
</ul></div>
</nav> <p>
Most web applications you want to build will include billing at some
point. While it’s relatively straightforward to incorporate Stripe using one of
their official SDKs, it isn’t for languages like Common Lisp. Sure, you can just
handle everything via the REST API directly but chances are that if you’re just
starting out with Common Lisp that you already have your hands full grokking
other concepts and want to write minimal code for basic things. Ideally, we’d
have some resource that you can look over and copy-paste.
</p>

 <p>
Well, sorry to burst your bubble, but said resources don’t exist. I tried to look
up how others have added billing to their projects but I couldn’t find anything.
</p>

 <p>
The most common sentiment I’ve read is that it’s not hard and most just interact
with the API directly. Sure, it’s not hard but sharing how others go on about
building their projects would be helpful to newcomers.
</p>

 <p>
So I’m going to share how I approached it.
</p>

 <div id="outline-container-orgb027a33" class="outline-2">
 <h2 id="orgb027a33">Libraries</h2>
 <div class="outline-text-2" id="text-orgb027a33">
 <p>
First things first, we want to see which libraries are available for our use
case. As it turns out, this  <a href="https://github.com/CodyReichert/awesome-cl">Awesome Common Lisp repo</a> is a great resource for
finding good libraries that are used within the community.
</p>

 <p>
Under the  <a href="https://github.com/CodyReichert/awesome-cl#third-party-apis">Third-party APIs</a> section we’ll find that there are two Stripe
libraries. Yes, just two, although neither is polished and one is a wrapper
around various payment processors. There are more libraries available outside of
this list but they’re not working with the latest API version.
</p>

 <p>
I would recommend  <a href="https://github.com/boogsbunny/stripe">the other library</a>, which was originally created by Michael
Fiano. However, he has since quit the community, and I’ve taken over
maintenance. This transition allows us to bring the library up to speed with the
latest API features, including webhook support.
</p>
</div>
</div>

 <div id="outline-container-org1edbf13" class="outline-2">
 <h2 id="org1edbf13">Installation</h2>
 <div class="outline-text-2" id="text-org1edbf13">
</div>
 <div id="outline-container-orge07bb7f" class="outline-3">
 <h3 id="orge07bb7f">Package managers</h3>
 <div class="outline-text-3" id="text-orge07bb7f">
</div>
 <div id="outline-container-orge522287" class="outline-4">
 <h4 id="orge522287"> <a href="https://www.quicklisp.org/beta/">quicklisp</a></h4>
 <div class="outline-text-4" id="text-orge522287">
 <p>
There’s currently an  <a href="https://github.com/quicklisp/quicklisp-projects/issues/2420">open PR</a> to update the source location of the Stripe
library, which includes updates like  <a href="https://github.com/boogsbunny/stripe/commit/5936c43f44a197454095e1b83175dcdf3a303fd0">webhooks</a>.
</p>

 <p>
The current version in Quicklisp doesn’t include these updates yet.
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">bash code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>(ql:quickload :stripe)
</code></pre>
</div>
</div>
</div>

 <div id="outline-container-org854c965" class="outline-4">
 <h4 id="org854c965"> <a href="https://github.com/ocicl/ocicl">ocicl</a></h4>
 <div class="outline-text-4" id="text-org854c965">
 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">bash code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>ocicl install stripe
</code></pre>
</div>
</div>
</div>
</div>

 <div id="outline-container-org4d2d7e6" class="outline-3">
 <h3 id="org4d2d7e6">From source</h3>
 <div class="outline-text-3" id="text-org4d2d7e6">
 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">bash code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>git clone https://github.com/boogsbunny/stripe
</code></pre>
</div>
</div>
</div>
</div>


 <div id="outline-container-org7b559ac" class="outline-2">
 <h2 id="org7b559ac">Requirements</h2>
 <div class="outline-text-2" id="text-org7b559ac">
 <p>
You have to  <a href="https://dashboard.stripe.com/register">have a Stripe account</a> with an  <a href="https://docs.stripe.com/keys">API key</a> and should read through how to
 <a href="https://docs.stripe.com/webhooks#verify-official-libraries">receive Stripe events in your webhook endpoint</a>.
</p>
</div>
</div>

 <div id="outline-container-orgb9c7288" class="outline-2">
 <h2 id="orgb9c7288">Minimal example</h2>
 <div class="outline-text-2" id="text-orgb9c7288">
 <p>
These are the libraries we’ll be using to get started:
</p>

 <ul class="org-ul"> <li> <a href="https://github.com/cl-babel/babel">babel:</a> This is a charset encoding/decoding library. It helps with converting
octets (byte vectors) to strings and vice versa. In this example, it’s used to
decode JSON webhook payloads from Stripe.</li>
 <li> <a href="https://github.com/Zulu-Inuoe/jzon">com.inuoe.jzon:</a> This is a JSON reader/writer. It parses JSON data from
incoming webhook requests and serializes JSON when needed.</li>
 <li> <a href="https://github.com/edicl/flexi-streams">flexi-streams</a>: A flexible library that allows efficient reading from and
writing to streams, particularly useful for handling binary data. It’s used
here for stream-related parsing of webhook payloads.</li>
 <li> <a href="https://github.com/joaotavora/snooze">snooze</a>: A minimalistic web framework for routing and handling HTTP
requests. It’s used to define our API routes, including handling webhooks.</li>
 <li> <a href="https://github.com/boogsbunny/stripe">stripe</a>: The client library we chose for interacting with the Stripe API,
including creating checkout sessions and validating webhook events.</li>
</ul></div>
</div>

 <div id="outline-container-org5ef0ee0" class="outline-2">
 <h2 id="org5ef0ee0">Handling environment variables</h2>
 <div class="outline-text-2" id="text-org5ef0ee0">
 <p>
Before diving into the billing flow, let’s first store the Stripe  <a href="https://docs.stripe.com/keys">API key</a> and
 <a href="https://docs.stripe.com/webhooks#verify-official-libraries">webhook signing secret</a>.
</p>

 <p>
We’ll do this by reading in the environment variables that you’ll have to set.
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">lisp code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>(defmacro env-var (name var)
  "Define a function that retrieves the value of an environment variable."
  `(defun ,name ()
     (or (uiop:getenv ,var) "")))

(env-var stripe-secret-key "STRIPE_SECRET_KEY")
(env-var stripe-webhook-secret "STRIPE_WEBHOOK_SECRET")
</code></pre>
</div>

 <p>
Next, we need to set the API key:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">lisp code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>(setf stripe:*api-key* (stripe-secret-key))
</code></pre>
</div>
</div>
</div>

 <div id="outline-container-orgcfe25aa" class="outline-2">
 <h2 id="orgcfe25aa">Billing flow</h2>
 <div class="outline-text-2" id="text-orgcfe25aa">
 <p>
Let’s focus on user subscriptions specifically, where customers are making
recurring payments to access your product.  <a href="https://docs.stripe.com/billing/subscriptions/overview">Here’s an article</a> by Stripe on how
subscriptions work.
</p>

 <p>
I’ll keep it simple for my use case. I have a landing page with a pricing
section describing different tiers of my product with varying price points. The
user can click on any of these sections to subscribe to that tier.
</p>

 <p>
Our frontend needs to include this script element:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">html code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code><script src=https://js.stripe.com/v3/></script>
</code></pre>
</div>

 <p>
After they select a tier, we want to redirect them to the checkout
page. Facilitating this process is called a  <a href="https://docs.stripe.com/api/checkout/sessions">session</a>. We need to add buttons for
each subscription tier that hit our API endpoint to redirect them to our session
URL.
</p>

 <p>
Here’s the function that handles the redirection:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">lisp code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>(defun redirect-to (url &optional (format-control "Redirected") format-args)
  "Redirects the client to the specified URL with an optional message."
  (setf (getf snooze::*clack-response-headers* :location) url)
  (snooze:http-condition 302 (format nil "~?" format-control format-args)))
</code></pre>
</div>

 <p>
Now, we’ll define the `add-subscription` function, which creates a checkout
session with Stripe and redirects the user to the appropriate URL:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">lisp code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>(defun add-subscription ()
  "Redirects the user to the Stripe checkout session URL for the selected plan."
  (redirect-to
   (stripe:session-url
    (stripe:create-session
     :cancel-url "<your-cancel-url>"
     :line-items '(("price" "<price-id>" "quantity" 1))
     :mode "subscription"
     :payment-method-types '("card")
     :success-url "<your-success-url>"))))
</code></pre>
</div>

 <p>
Stripe provides webhook notifications to inform your application about events
like payments or subscription status changes. We need to handle these events by
processing the incoming JSON data.
</p>

 <p>
Let’s start by defining a utility function `parse-stream` that reads the
contents of a stream and returns it as a vector of unsigned bytes:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">lisp code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>;;;; Original code provided by Eitaro Fukamachi.
;;;; Copyright (c) 2014 Eitaro Fukamachi
;;;; github.com/fukamachi/http-body
(defun parse-stream (stream &optional content-length)
  "Reads the contents of a stream and returns it as a vector of unsigned bytes.

- `stream`: The input stream from which to read.
- `content-length`: If provided, specifies the exact number of bytes to read."
  (if (typep stream 'flexi-streams:vector-stream)
      (coerce (flexi-streams::vector-stream-vector stream) '(simple-array (unsigned-byte 8) (*)))
      (if content-length
          (let ((buffer (make-array content-length :element-type '(unsigned-byte 8))))
            (read-sequence buffer stream)
            buffer)
          (apply #'concatenate
                 '(simple-array (unsigned-byte 8) (*))
                 (loop with buffer = (make-array 1024 :element-type '(unsigned-byte 8))
                       for read-bytes = (read-sequence buffer stream)
                       collect (subseq buffer 0 read-bytes)
                       while (= read-bytes 1024))))))
</code></pre>
</div>

 <p>
Next, we’ll define a macro `with-parsed-json` to handle JSON parsing in our
webhook handler:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">lisp code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>(defmacro with-parsed-json (&body body)
  "Parses the JSON body of an incoming HTTP request and binds it to a local
variable `json`.

Within BODY, the variable `json` will contain the parsed JSON object."
  `(let* ((content-type (getf snooze:*clack-request-env* :content-type))
          (content-length (getf snooze:*clack-request-env* :content-length))
          (raw-body (getf snooze:*clack-request-env* :raw-body))
          (json-stream (parse-stream raw-body content-length))
          (raw-json (babel:octets-to-string json-stream
                                            :encoding (detect-charset content-type :utf-8)))
          (json (handler-case (com.inuoe.jzon:parse raw-json)
                  (error (e)
                    (format t "Malformed JSON (~a)~%!" e)
                    (http-condition 400 "Malformed JSON!")))))
     (declare (ignorable json))
     ,@body))
</code></pre>
</div>

 <p>
Now, let’s define the `handle-webhook-event` function, which validates and
processes incoming webhook events from Stripe:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">lisp code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>(defun handle-webhook-event ()
  "Handles incoming webhook events from Stripe webhooks."
  (with-parsed-json
      (let* ((is-valid-webhook (stripe:validate-webhook-payload
                                json-stream
                                (gethash "stripe-signature" (getf snooze:*clack-request-env* :headers))
                                (stripe-webhook-secret)))
             (event (stripe:construct-webhook-event
                     json-stream
                     (gethash "stripe-signature" (getf snooze:*clack-request-env* :headers))
                     (stripe-webhook-secret)
                     :ignore-api-version-mismatch t))
             (event-type (gethash "type" json)))
        (if is-valid-webhook
            (progn
              (format t "Valid webhook received.~%")
              (cond ((string= "payment_intent.created" event-type)
                     (format t "Payment intent created!~%")
                     ;; TODO: Proceed with creating a user or processing the payment intent here
                     )
                    ((string= "customer.subscription.created" event-type)
                     (format t "Subscription created!~%")
                     ;; TODO: Handle subscription creation
                     )
                    ((string= "invoice.payment_succeeded" event-type)
                     (format t "Payment succeeded for invoice!~%")
                     ;; TODO: Handle the successful payment
                     )
                    ;; etc.
                    (t
                     (format t "Unhandled event type: ~a~%" event-type))))
            (format t "Invalid webhook signature.~%")))))
</code></pre>
</div>

 <p>
Lastly, we define the route to handle webhook requests:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">lisp code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>(snooze:defroute webhook (:post :application/json)
  (handle-webhook-event))
</code></pre>
</div>

 <p>
That’s it. I hope I left you off in a better position than before and you have
a clearer idea of how to add billing to your next Common Lisp app.
</p>
</div>
</div>
</div>]]></content>
  <link href="https://boogs.life/add-billing-to-your-common-lisp-app/index.html"/>
  <id>https://boogs.life/add-billing-to-your-common-lisp-app/index.html</id>
  <updated>2025-08-20T00:00:00+00:00</updated>
</entry>
<entry>
  <title>Tree-sitter in Guix</title>
  <author><name>root</name></author>
  <content type="html"><![CDATA[<div id="content" class="content">
 <header> <h1 class="title">Tree-sitter in Guix</h1>
</header> <nav id="table-of-contents" role="doc-toc"> <h2>Table of Contents</h2>
 <div id="text-table-of-contents" role="doc-toc">
 <ul> <li> <a href="#orgee494ba">Installation</a></li>
 <li> <a href="#org7088658">Emacs configuration</a></li>
 <li> <a href="#org2f9f831">Conclusion</a></li>
</ul></div>
</nav> <p>
I’m impartial to syntax highlighting in code since it can be a source
of distraction more often than not, however, I tend to use the favored
major mode for the language I’m writing in Emacs paired with
 <a href="https://github.com/joaotavora/eglot">eglot</a>. Lately though, this has become a pain point when working with
all-things JavaScript on large files. My quick solution was to forego
 <code>eglot</code> and  <a href="https://github.com/fxbois/web-mode">web-mode</a>, which improved the editing speed tremendously.
</p>

 <p>
It looks like the latter is a  <a href="https://github.com/fxbois/web-mode/issues/1162">common reason</a> for the slowdown when
working with  <code>TSX</code> files. I figured this is a good reason to finally get
 <a href="https://tree-sitter.github.io/tree-sitter/">tree-sitter</a> in my Emacs setup working as I’ve failed previously.
</p>

 <p>
Tree-sitter is a parser generator tool that can build a concrete
syntax tree for a source file and efficiently update the tree during
edits. Syntax highlighting is one of those features that can leverage
this tool. Code navigation is another, though we’ll only talk about
syntax highlighting.
</p>

 <p>
Syntax highlighting tools usually rely on custom regular expressions,
so using Tree-sitter is faster and has better highlighting since it’s
more context-aware.
</p>

 <div id="outline-container-orgee494ba" class="outline-2">
 <h2 id="orgee494ba">Installation</h2>
 <div class="outline-text-2" id="text-orgee494ba">
 <p>
I’ve had trouble installing the language grammars manually before on
my Guix system, so I figured it’s better to install the packages from
Guix.
</p>

 <p>
I use  <a href="https://guix.gnu.org/cookbook/en/html_node/Guix-Profiles-in-Practice.html">Guix Profiles</a> to group package installations together e.g., a
default profile, an Emacs profile, a Common Lisp profile etc. and make
them available on login.
</p>

 <p>
In this case I’m adding the relevant packages to my default profile:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">scheme code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>;; guix-default-manifest.scm
;; default packages but example only includes Tree-sitter ones
(specifications->manifest
 '("tree-sitter"
   "tree-sitter-bash"
   "tree-sitter-css"
   "tree-sitter-dockerfile"
   "tree-sitter-go"
   "tree-sitter-html"
   "tree-sitter-javascript"
   "tree-sitter-json"
   "tree-sitter-python"
   "tree-sitter-typescript"))
</code></pre>
</div>

 <p>
Then I run:
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">bash code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>guix package --manifest=/home/boogs/.package-lists/guix-default-manifest.scm --profile=/home/boogs/.guix-extra-profiles/default/default
</code></pre>
</div>

 <p>
to update my default profile to include these packages.
</p>
</div>
</div>

 <div id="outline-container-org7088658" class="outline-2">
 <h2 id="org7088658">Emacs configuration</h2>
 <div class="outline-text-2" id="text-org7088658">
 <p>
Now we’ve installed the tool and the language grammars. Next, we need
to tweak our Emacs configuration slightly to know where to find the
language grammars.
</p>

 <div class="org-src-container">

 <div class="org-src-container__header" data-element="code-block"> <div class="language-tag">elisp code</div> <button type="button" data-element="code-copy-button" class="clipboard-tag">copy to clipboard</button> <div role="alert"></div></div> <pre class="src"> <code>(setq treesit-extra-load-path '("~/.guix-extra-profiles/default/default/lib/tree-sitter"))

(add-to-list 'auto-mode-alist '("\\.js\\'" . js-ts-mode))
(add-to-list 'auto-mode-alist '("\\.json\\'" . json-ts-mode))
(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode))
(add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode))
(add-to-list 'auto-mode-alist '("\\.html\\'" . html-ts-mode))
(add-to-list 'auto-mode-alist '("\\.css\\'" . css-ts-mode))
(add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))
</code></pre>
</div>

 <p>
The load path may vary depending on how you install the packages. If
you install them to the current profile, then the path starts with
 <code>"~/.guix-profile/lib/tree-sitter"</code>.
</p>
</div>
</div>

 <div id="outline-container-org2f9f831" class="outline-2">
 <h2 id="org2f9f831">Conclusion</h2>
 <div class="outline-text-2" id="text-org2f9f831">
 <p>
With Tree-sitter set up via Guix, editing large JavaScript,
 <code>TypeScript</code>, or  <code>TSX</code> files in Emacs should feel snappier, thanks to
efficient parsing and context-aware highlighting. This setup not only
resolves slowdowns from modes like  <code>web-mode</code> but also opens the door to
advanced features like structural editing.
</p>

 <p>
If you encounter issues (e.g., missing grammars or incompatible Emacs
versions), check the  <a href="https://github.com/tree-sitter/tree-sitter/issues">Tree-sitter GitHub repository</a> or the  <a href="https://codeberg.org/guix/guix/issues">Guix
Codeberg repository</a>. Happy hacking!
</p>
</div>
</div>
</div>]]></content>
  <link href="https://boogs.life/tree-sitter-in-guix/index.html"/>
  <id>https://boogs.life/tree-sitter-in-guix/index.html</id>
  <updated>2025-08-20T00:00:00+00:00</updated>
</entry>
</feed>
