<?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: ThirdWeb</title>
    <description>The latest articles on DEV Community by ThirdWeb (@thirdweb_46663456c48a7ae4).</description>
    <link>https://dev.to/thirdweb_46663456c48a7ae4</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%2F4197408%2Fbcbaf4a8-6af1-4f63-a520-d7b3c0a3b2b9.png</url>
      <title>DEV Community: ThirdWeb</title>
      <link>https://dev.to/thirdweb_46663456c48a7ae4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thirdweb_46663456c48a7ae4"/>
    <language>en</language>
    <item>
      <title>Designing a no-match-no-charge flow for a song identification tool</title>
      <dc:creator>ThirdWeb</dc:creator>
      <pubDate>Wed, 21 Oct 2026 05:26:58 +0000</pubDate>
      <link>https://dev.to/thirdweb_46663456c48a7ae4/designing-a-no-match-no-charge-flow-for-a-song-identification-tool-2g3c</link>
      <guid>https://dev.to/thirdweb_46663456c48a7ae4/designing-a-no-match-no-charge-flow-for-a-song-identification-tool-2g3c</guid>
      <description>&lt;p&gt;Disclosure: this post is shared on behalf of &lt;a href="https://tiktoksongfinder.org/" rel="noopener noreferrer"&gt;TikTok Song Finder&lt;/a&gt;, an independent paid tool for identifying music from public TikTok links. The design below is a general implementation pattern, not a claim about measured performance or the exact internals of the service.&lt;/p&gt;

&lt;p&gt;A recognition request can produce a match, no match, or a failure to process the input. A credit-based product needs to distinguish these outcomes before charging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model outcomes explicitly
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Outcome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;matched&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no_match&lt;/span&gt;&lt;span class="dl"&gt;"&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="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unavailable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;retryable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unavailable video is not evidence that the catalog has no matching song. Keep input failure separate from recognition failure so the interface can explain whether a retry might help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make retries safe
&lt;/h2&gt;

&lt;p&gt;Give each attempt a server-generated identifier. Record its outcome and credit ledger entry together in a transaction. Add a uniqueness constraint on the ledger reference for that attempt so a duplicate callback cannot deduct twice. Check the balance atomically. A browser timeout must not cause another charge when the user refreshes: return the stored attempt status instead of creating a second attempt. If credits are reserved before processing, model a reservation and its release explicitly so failed requests do not hold credits indefinitely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate matches from listening links
&lt;/h2&gt;

&lt;p&gt;A title and artist may be available even when a particular listening provider has no link. Treat provider links as optional fields. Avoid presenting a search result as a verified recording, especially when a clip contains a remix, sped-up edit, or speech over music.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test failures as well as success
&lt;/h2&gt;

&lt;p&gt;Cover two requests competing for the last credit, a result delivered twice, a timeout after a result is stored, an inaccessible video, and a match with missing listening links. Assert both the displayed outcome and final ledger balance. A useful invariant is that a single completed match can consume at most one credit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explain payment before recognition
&lt;/h2&gt;

&lt;p&gt;Users should see sign-in and payment requirements before starting. TikTok Song Finder uses a one-time $9.99 pack of 10 credits; one credit is used only when a song is found. Credits never expire and there is no subscription. It accepts public TikTok links and is not affiliated with TikTok or ByteDance.&lt;/p&gt;

&lt;p&gt;Connecting billing to a durable outcome gives developers a concrete invariant to test and users a clearer explanation of what happened.&lt;/p&gt;

</description>
      <category>webdev</category>
    </item>
  </channel>
</rss>
