<?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: Shimaji</title>
    <description>The latest articles on DEV Community by Shimaji (@shimaji).</description>
    <link>https://dev.to/shimaji</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4172583%2F0913c151-fe78-4831-959d-f172f47ef77e.png</url>
      <title>DEV Community: Shimaji</title>
      <link>https://dev.to/shimaji</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shimaji"/>
    <language>en</language>
    <item>
      <title>Subscription webhooks aren't enough: detecting drift between Paddle and Supabase</title>
      <dc:creator>Shimaji</dc:creator>
      <pubDate>Fri, 09 Oct 2026 06:47:30 +0000</pubDate>
      <link>https://dev.to/shimaji/subscription-webhooks-arent-enough-detecting-drift-between-paddle-and-supabase-4gm5</link>
      <guid>https://dev.to/shimaji/subscription-webhooks-arent-enough-detecting-drift-between-paddle-and-supabase-4gm5</guid>
      <description>&lt;p&gt;Building subscription billing is usually straightforward until the billing provider and your application database stop agreeing.&lt;/p&gt;

&lt;p&gt;I've been testing a Paddle + Supabase setup, and I became interested in a specific problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you know that your application's access state still matches Paddle after the webhook has been processed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A successful checkout or webhook delivery doesn't necessarily prove that the two systems remain consistent over time.&lt;/p&gt;

&lt;p&gt;For example, I wanted to be able to detect cases like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paddle says a subscription is active, but local access is disabled&lt;/li&gt;
&lt;li&gt;Paddle says a subscription is canceled or paused, but access is still enabled&lt;/li&gt;
&lt;li&gt;A subscription exists in Paddle but is missing from the local database&lt;/li&gt;
&lt;li&gt;Product or price IDs differ between Paddle and the application&lt;/li&gt;
&lt;li&gt;Customer mappings are missing&lt;/li&gt;
&lt;li&gt;A very recent Paddle change is still propagating and should not immediately be treated as an error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't necessarily billing-provider bugs.&lt;/p&gt;

&lt;p&gt;They can happen because the application has its own state, webhook processing can fail, database updates can be incomplete, or the two systems simply change at different times.&lt;/p&gt;

&lt;h2&gt;
  
  
  A reconciliation check
&lt;/h2&gt;

&lt;p&gt;I ended up building a small CLI that compares the current Paddle subscription state with the corresponding records in Supabase.&lt;/p&gt;

&lt;p&gt;It's called &lt;strong&gt;Paddle Integrity CLI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The idea is deliberately simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Detect → Explain → Fix → Verify&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is read-only.&lt;/p&gt;

&lt;p&gt;It does not modify Paddle, update Supabase rows, provision access, or automatically repair anything.&lt;/p&gt;

&lt;p&gt;Instead, it reports differences so that you can decide what should be corrected.&lt;/p&gt;

&lt;p&gt;Some examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CANCELED_STILL_ACTIVE
Paddle subscription is canceled, but local access is still enabled.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACTIVE_WITHOUT_ACCESS
Paddle subscription is entitled, but local access is disabled.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRODUCT_DRIFT
The local product does not match the current Paddle product.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recent changes can also be reported as &lt;strong&gt;Pending&lt;/strong&gt; rather than immediately being treated as failures.&lt;/p&gt;

&lt;p&gt;That matters because I wanted the checker to avoid turning normal propagation delays into false alarms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;

&lt;p&gt;The package is available on npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; paddle-integrity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then configure the mapping and run a check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;paddle-integrity configure &lt;span class="nt"&gt;--environment&lt;/span&gt; sandbox
paddle-integrity doctor &lt;span class="nt"&gt;--environment&lt;/span&gt; sandbox
paddle-integrity scan &lt;span class="nt"&gt;--environment&lt;/span&gt; sandbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I strongly recommend starting with Paddle Sandbox.&lt;/p&gt;

&lt;p&gt;The scanner uses read-only provider requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy
&lt;/h2&gt;

&lt;p&gt;Telemetry is &lt;strong&gt;OFF by default&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you don't explicitly opt in, no anonymous usage telemetry is sent.&lt;/p&gt;

&lt;p&gt;The CLI also avoids putting credentials into its configuration file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;What I'm really trying to validate is whether subscription drift is a recurring operational problem for small SaaS teams using Paddle + Supabase.&lt;/p&gt;

&lt;p&gt;The CLI itself is free and open source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shimaji/paddle-integrity" rel="noopener noreferrer"&gt;https://github.com/shimaji/paddle-integrity&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;npm:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/paddle-integrity" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/paddle-integrity&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm also exploring whether continuous hosted monitoring would eventually be useful, but I don't want to build that until there is evidence that people actually experience this problem.&lt;/p&gt;

&lt;p&gt;So I'm especially interested in feedback from anyone already running subscription billing in production:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you encountered cases where your billing provider and your application's access state became inconsistent?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And if so, what kinds of drift were the hardest to detect?&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>supabase</category>
      <category>saas</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
