<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>PayWatcher</title>
        <link>https://paragraph.com/@paywatcher</link>
        <description>Payment verification without the payment processor.
Works like Stripe Payment Intents, but for on-chain USDC.

Non-custodial. Flat-rate. Three API calls.</description>
        <lastBuildDate>Sun, 12 Apr 2026 05:20:06 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>PayWatcher</title>
            <url>https://storage.googleapis.com/papyrus_images/364b26973d14c1bf3f424a5a5a96737fb093a9956103a0e8a7da0d4c0a870afc.jpg</url>
            <link>https://paragraph.com/@paywatcher</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Why Payment Verification Should Be Separate from Payment Processing]]></title>
            <link>https://paragraph.com/@paywatcher/why-payment-verification-should-be-separate-from-payment-processing</link>
            <guid>TFHw5I9RVjY3TcPU68Rn</guid>
            <pubDate>Sun, 05 Apr 2026 03:47:47 GMT</pubDate>
            <description><![CDATA[When developers need to accept USDC payments, they reach for a payment processor. Coinbase Commerce. Stripe Crypto. Request Network. These products handle the full stack: checkout UI, custody, settlement, currency conversion, and somewhere in there — verification. That bundling made sense in 2021. It doesn't anymore.]]></description>
            <content:encoded><![CDATA[<p>When developers need to accept USDC payments, they reach for a payment processor. Coinbase Commerce. Stripe Crypto. Request Network. These products handle the full stack: checkout UI, custody, settlement, currency conversion, and somewhere in there — verification.</p><p>That bundling made sense in 2021. It doesn't anymore.</p><hr><h2 id="h-the-new-reality-agents-dont-need-checkout" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The New Reality: Agents Don't Need Checkout</h2><p>The fastest-growing use case for stablecoin payments isn't a human buying something. It's an AI agent paying for API access.</p><p>An agent doesn't need a checkout page. It doesn't need a hosted UI. It doesn't need currency conversion. It already has USDC in a wallet. It just needs to send the payment and prove it happened.</p><p>The entire stack that payment processors offer is irrelevant. What the agent needs is one thing: <strong>verification</strong>.</p><blockquote><p>"Did this transaction happen? Was the amount correct? Did it land on the right address?"</p></blockquote><p>That's it.</p><hr><h2 id="h-the-hidden-cost-of-bundled-processing" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Hidden Cost of Bundled Processing</h2><p>Most payment processors charge 0.5–1% per transaction. That sounds small until you run the numbers.</p><p>Your agent processes 10,000 API calls per month at $1.00 each. That's $10,000 in volume. At 1%, you're paying $100/month in fees — not to a bank, not for fraud protection, not for anything you actually use. Just for the privilege of using someone else's checkout flow that your agent never sees.</p><p>With a verification-only approach, that same volume costs $500. Not $100 per month — $500 total. Flat fee per verification, no percentage, no minimum.</p><p>The $0.05 per verification isn't a promotional price. It's what verification actually costs when you strip out everything else.</p><hr><h2 id="h-the-architectural-argument" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Architectural Argument</h2><p>There's a deeper reason to separate verification from processing, and it's the same reason you don't use a monolith for everything else: <strong>separation of concerns</strong>.</p><p>Payment processors own three things simultaneously:</p><ol><li><p>The money flow (custody, settlement)</p></li><li><p>The user experience (checkout, hosted pages)</p></li><li><p>The verification signal ("did this payment succeed?")</p></li></ol><p>When you bundle these, you get lock-in. Your payment success logic is coupled to one provider's infrastructure. Switching processors means rewriting your integration. Changing chains means waiting for the processor to support it.</p><p>When you separate verification from the rest, you own your stack. You choose your wallet infrastructure. You choose your settlement path. You choose your chains. The verification layer just watches and reports.</p><hr><h2 id="h-the-x402-signal" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The x402 Signal</h2><p>The x402 protocol — pioneered by Coinbase, now spreading across AI agent frameworks — makes this separation explicit at the protocol level.</p><p>x402 defines a standard for machine payments: server returns HTTP 402, client pays on-chain, client proves payment via transaction hash, server verifies. No checkout. No redirect. No hosted page.</p><p>The server's job in this flow is pure verification. And verification doesn't require a processor.</p><p>What it requires is an API that can answer: "Did this txHash represent a valid $1.00 USDC transfer to my address on Base, within the last 5 minutes?" That's a $0.05 question, not a 1% question.</p><hr><h2 id="h-what-non-custodial-actually-means" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What "Non-Custodial" Actually Means</h2><p>Most crypto payment processors are custodial. They hold the funds between receipt and payout. That's not just a philosophical issue — it's a practical one.</p><p>When a processor holds funds:</p><ul><li><p>You're subject to their payout schedule</p></li><li><p>You're subject to their compliance decisions</p></li><li><p>You're exposed if they have liquidity problems</p></li><li><p>You need to trust their accounting</p></li></ul><p>When you're non-custodial:</p><ul><li><p>USDC goes directly from the sender to your wallet</p></li><li><p>You have it immediately</p></li><li><p>No intermediary, no delay, no exposure</p></li></ul><p>PayWatcher never touches the funds. The USDC goes from sender to your deposit address. We watch the blockchain and tell you when it arrived. That's the entire product.</p><hr><h2 id="h-the-right-tool-for-the-right-job" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Right Tool for the Right Job</h2><p>Payment processors are excellent products for what they do. If you're building an e-commerce checkout where humans enter credit cards, use Stripe. If you need fiat conversion at point of sale, use the right tool for that.</p><p>But if you're building:</p><ul><li><p>An API that AI agents pay to access</p></li><li><p>A service that accepts USDC natively</p></li><li><p>An x402-compatible endpoint</p></li><li><p>Anything where the "user" already has crypto and just needs to send it</p></li></ul><p>…then you don't need a processor. You need verification.</p><p>And verification should be cheap, simple, and chainless.</p><hr><h2 id="h-the-practical-test" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Practical Test</h2><p>Before choosing a payment approach, ask these questions:</p><ol><li><p>Do your users have USDC already, or do they need to buy it first?</p></li><li><p>Does your integration involve a human filling out a form, or a machine sending a transaction?</p></li><li><p>Are you paying a percentage fee on volume you could keep?</p></li><li><p>Are you locked to one chain because your processor doesn't support others?</p></li></ol><p>If the answers push toward "machine, already has USDC, paying percentage, locked chain" — you're using the wrong tool.</p><hr><h2 id="h-where-this-goes" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Where This Goes</h2><p>The stablecoin payment stack is unbundling. Settlement rails, custody, conversion, and verification are separating into specialized layers — the same way databases, caches, and queues separated from the monolith.</p><p>Verification is one of those layers. It's boring, it's infrastructure, it should cost almost nothing, and it should work on every chain.</p><p>That's what PayWatcher is.</p><hr><p><em>PayWatcher is a USDC payment verification API. $0.05 per verification. Non-custodial. No checkout. Supports Base, Arbitrum, Ethereum, Optimism, and Polygon.</em></p><p><em>Request access at </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://paywatcher.dev"><em>paywatcher.dev</em></a></p>]]></content:encoded>
            <author>paywatcher@newsletter.paragraph.com (PayWatcher)</author>
            <category>stablecoins</category>
            <category>usdc</category>
            <category>fintech</category>
            <category>web3</category>
            <category>ai</category>
            <category>agents</category>
            <category>paymentinfrastructure</category>
            <category>buildinginpublic</category>
            <category>defi</category>
            <category>crypto</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/952c891a393216b68c123a7b0150e023561c29d0b53665e39e5038fb745d9cba.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[How to Accept AI Agent Payments with x402 + PayWatcher]]></title>
            <link>https://paragraph.com/@paywatcher/how-to-accept-ai-agent-payments-with-x402-paywatcher</link>
            <guid>ugbLuK33Ip1TiJs7DeTG</guid>
            <pubDate>Sun, 05 Apr 2026 03:43:24 GMT</pubDate>
            <description><![CDATA[The x402 protocol is quietly becoming the standard for machine-to-machine payments. If you're building an API that AI agents consume — whether that's a data endpoint, a compute service, or a gated resource — x402 lets agents pay per request in USDC without human intervention. ]]></description>
            <content:encoded><![CDATA[<p>The x402 protocol is quietly becoming the standard for machine-to-machine payments. If you're building an API that AI agents consume — whether that's a data endpoint, a compute service, or a gated resource — x402 lets agents pay per request in USDC without human intervention.</p><br><p>This article walks through exactly how to implement x402 on your server using PayWatcher as the verification layer.</p><hr><h2 id="h-what-is-x402" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What is x402?</h2><p>x402 is an extension of HTTP that brings the long-forgotten 402 Payment Required status code back to life. The flow is simple:</p><ol><li><p>Client requests a resource</p></li><li><p>Server responds with <code>HTTP 402</code> + a payment descriptor (amount, address, chain)</p></li><li><p>Client sends USDC on-chain</p></li><li><p>Client retries the request with the transaction hash</p></li><li><p>Server verifies the payment and returns the resource</p></li></ol><p>The protocol was pioneered by Coinbase and is gaining adoption across AI agent frameworks including MCP servers, Coinbase AgentKit, and custom agent runtimes.</p><hr><h2 id="h-the-verification-problem" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Verification Problem</h2><p>The tricky part of x402 is step 5. Your server needs to answer one question:</p><blockquote><p>"Did this transaction actually happen — with the right amount, to the right address, on the right chain?"</p></blockquote><p>You have two options:</p><p><strong>Option A: Roll your own</strong> — query an RPC node directly, parse ERC-20 Transfer logs, handle confirmations, manage multi-chain RPCs. This takes days, not hours.</p><p><strong>Option B: Delegate to PayWatcher</strong> — one API call, $0.05 flat, works across Base, Arbitrum, Ethereum, Optimism, and Polygon.</p><p>This tutorial uses Option B.</p><hr><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><ul><li><p>A PayWatcher account (request access at <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://paywatcher.dev">paywatcher.dev</a>)</p></li><li><p>Your PayWatcher API key</p></li><li><p>Your deposit address configured in PayWatcher dashboard</p></li><li><p>Node.js + Express (or any HTTP framework)</p></li><li><p>The <code>coinbase/x402</code> npm package (optional but recommended)</p></li></ul><hr><h2 id="h-step-1-set-up-your-express-server" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 1: Set Up Your Express Server</h2><pre data-type="codeBlock" text="import express from &quot;express&quot;

const app = express()
app.use(express.json())

const PAYWATCHER_API_KEY = process.env.PAYWATCHER_API_KEY!
const DEPOSIT_ADDRESS = process.env.DEPOSIT_ADDRESS!
const RESOURCE_PRICE = &quot;1.00&quot; // $1.00 USDC
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">express</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"express"</span>

<span class="hljs-title">const</span> <span class="hljs-title">app</span> <span class="hljs-operator">=</span> <span class="hljs-title">express</span>()
<span class="hljs-title">app</span>.<span class="hljs-title">use</span>(<span class="hljs-title">express</span>.<span class="hljs-title">json</span>())

<span class="hljs-title">const</span> <span class="hljs-title">PAYWATCHER_API_KEY</span> <span class="hljs-operator">=</span> <span class="hljs-title">process</span>.<span class="hljs-title">env</span>.<span class="hljs-title">PAYWATCHER_API_KEY</span><span class="hljs-operator">!</span>
<span class="hljs-title">const</span> <span class="hljs-title">DEPOSIT_ADDRESS</span> <span class="hljs-operator">=</span> <span class="hljs-title">process</span>.<span class="hljs-title">env</span>.<span class="hljs-title">DEPOSIT_ADDRESS</span><span class="hljs-operator">!</span>
<span class="hljs-title">const</span> <span class="hljs-title">RESOURCE_PRICE</span> <span class="hljs-operator">=</span> <span class="hljs-string">"1.00"</span> <span class="hljs-comment">// $1.00 USDC</span>
</code></pre><hr><h2 id="h-step-2-return-http-402-with-payment-descriptor" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 2: Return HTTP 402 with Payment Descriptor</h2><p>When a request comes in without payment, respond with a 402 and tell the client what to pay:</p><pre data-type="codeBlock" text="app.get(&quot;/api/report/:id&quot;, async (req, res) =&gt; {
  const paymentHeader = req.headers[&quot;x-payment&quot;]

  if (!paymentHeader) {
    // No payment — return 402 with descriptor
    return res.status(402).json({
      scheme: &quot;exact&quot;,
      network: &quot;base&quot;,
      maxAmountRequired: &quot;1000000&quot;, // $1.00 in USDC atomic units (6 decimals)
      resource: req.path,
      description: &quot;Access to report&quot;,
      mimeType: &quot;application/json&quot;,
      payTo: DEPOSIT_ADDRESS,
      maxTimeoutSeconds: 300,
      asset: &quot;0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&quot;, // USDC on Base
      extra: { name: &quot;USDC&quot;, version: &quot;2&quot; }
    })
  }

  // Payment header present — verify it
  const { txHash, network } = JSON.parse(paymentHeader as string)
  const verified = await verifyWithPayWatcher(txHash, network)

  if (!verified) {
    return res.status(402).json({ error: &quot;Payment verification failed&quot; })
  }

  // Payment verified — return the resource
  return res.json({ data: await getReport(req.params.id) })
})
"><code>app.get(<span class="hljs-string">"/api/report/:id"</span>, async (req, res) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const paymentHeader <span class="hljs-operator">=</span> req.headers[<span class="hljs-string">"x-payment"</span>]

  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>paymentHeader) {
    <span class="hljs-comment">// No payment — return 402 with descriptor</span>
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">402</span>).json({
      scheme: <span class="hljs-string">"exact"</span>,
      network: <span class="hljs-string">"base"</span>,
      maxAmountRequired: <span class="hljs-string">"1000000"</span>, <span class="hljs-comment">// $1.00 in USDC atomic units (6 decimals)</span>
      resource: req.path,
      description: <span class="hljs-string">"Access to report"</span>,
      mimeType: <span class="hljs-string">"application/json"</span>,
      payTo: DEPOSIT_ADDRESS,
      maxTimeoutSeconds: <span class="hljs-number">300</span>,
      asset: <span class="hljs-string">"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"</span>, <span class="hljs-comment">// USDC on Base</span>
      extra: { name: <span class="hljs-string">"USDC"</span>, version: <span class="hljs-string">"2"</span> }
    })
  }

  <span class="hljs-comment">// Payment header present — verify it</span>
  const { txHash, network } <span class="hljs-operator">=</span> JSON.parse(paymentHeader <span class="hljs-keyword">as</span> <span class="hljs-keyword">string</span>)
  const verified <span class="hljs-operator">=</span> await verifyWithPayWatcher(txHash, network)

  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>verified) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">402</span>).json({ <span class="hljs-function"><span class="hljs-keyword">error</span>: "<span class="hljs-title">Payment</span> <span class="hljs-title">verification</span> <span class="hljs-title">failed</span>" })
  }

  <span class="hljs-comment">// Payment verified — return the resource</span>
  <span class="hljs-title"><span class="hljs-keyword">return</span></span> <span class="hljs-title">res</span>.<span class="hljs-title">json</span>(<span class="hljs-params">{ data: await getReport(<span class="hljs-params">req.params.id</span>) }</span>)
})
</span></code></pre><hr><h2 id="h-step-3-verify-with-paywatcher" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 3: Verify with PayWatcher</h2><pre data-type="codeBlock" text="async function verifyWithPayWatcher(
  txHash: string,
  network: string
): Promise&lt;boolean&gt; {
  const response = await fetch(&quot;https://api.paywatcher.dev/v1/payments/verify&quot;, {
    method: &quot;POST&quot;,
    headers: {
      &quot;x-api-key&quot;: PAYWATCHER_API_KEY,
      &quot;Content-Type&quot;: &quot;application/json&quot;,
    },
    body: JSON.stringify({
      tx_hash: txHash,
      network: network,
      amount: RESOURCE_PRICE,
      to: DEPOSIT_ADDRESS,
      tolerance_seconds: 300,
    }),
  })

  const { data } = await response.json()
  return data.verified === true
}
"><code>async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyWithPayWatcher</span>(<span class="hljs-params">
  txHash: <span class="hljs-keyword">string</span>,
  network: <span class="hljs-keyword">string</span>
</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">boolean</span>&gt; </span>{
  const response <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"https://api.paywatcher.dev/v1/payments/verify"</span>, {
    method: <span class="hljs-string">"POST"</span>,
    headers: {
      <span class="hljs-string">"x-api-key"</span>: PAYWATCHER_API_KEY,
      <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
    },
    body: JSON.stringify({
      tx_hash: txHash,
      network: network,
      amount: RESOURCE_PRICE,
      to: DEPOSIT_ADDRESS,
      tolerance_seconds: <span class="hljs-number">300</span>,
    }),
  })

  const { data } <span class="hljs-operator">=</span> await response.json()
  <span class="hljs-keyword">return</span> data.verified <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-literal">true</span>
}
</code></pre><p>That's it. Three fields in, one boolean out.</p><hr><h2 id="h-step-4-handle-retryafterseconds" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 4: Handle retry<em>after</em>seconds</h2><p>PayWatcher returns a <code>retry_after_seconds</code> hint when a transaction is found but not yet confirmed:</p><pre data-type="codeBlock" text="async function verifyWithPayWatcher(txHash: string, network: string) {
  const response = await fetch(&quot;https://api.paywatcher.dev/v1/payments/verify&quot;, {
    method: &quot;POST&quot;,
    headers: {
      &quot;x-api-key&quot;: PAYWATCHER_API_KEY,
      &quot;Content-Type&quot;: &quot;application/json&quot;,
    },
    body: JSON.stringify({
      tx_hash: txHash,
      network: network,
      amount: RESOURCE_PRICE,
      to: DEPOSIT_ADDRESS,
    }),
  })

  const { data } = await response.json()

  if (!data.verified &amp;&amp; data.retry_after_seconds) {
    // Transaction found but not confirmed yet — tell the client to retry
    return {
      verified: false,
      retryAfter: data.retry_after_seconds,
      reason: data.reason,
    }
  }

  return { verified: data.verified, retryAfter: null, reason: data.reason }
}
"><code>async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyWithPayWatcher</span>(<span class="hljs-params">txHash: <span class="hljs-keyword">string</span>, network: <span class="hljs-keyword">string</span></span>) </span>{
  const response <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"https://api.paywatcher.dev/v1/payments/verify"</span>, {
    method: <span class="hljs-string">"POST"</span>,
    headers: {
      <span class="hljs-string">"x-api-key"</span>: PAYWATCHER_API_KEY,
      <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
    },
    body: JSON.stringify({
      tx_hash: txHash,
      network: network,
      amount: RESOURCE_PRICE,
      to: DEPOSIT_ADDRESS,
    }),
  })

  const { data } <span class="hljs-operator">=</span> await response.json()

  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>data.verified <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> data.retry_after_seconds) {
    <span class="hljs-comment">// Transaction found but not confirmed yet — tell the client to retry</span>
    <span class="hljs-keyword">return</span> {
      verified: <span class="hljs-literal">false</span>,
      retryAfter: data.retry_after_seconds,
      reason: data.reason,
    }
  }

  <span class="hljs-keyword">return</span> { verified: data.verified, retryAfter: null, reason: data.reason }
}
</code></pre><p>Return a <code>Retry-After</code> header so the agent knows when to try again:</p><pre data-type="codeBlock" text="if (!result.verified &amp;&amp; result.retryAfter) {
  return res
    .status(402)
    .set(&quot;Retry-After&quot;, String(result.retryAfter))
    .json({ error: &quot;Payment pending confirmation&quot;, reason: result.reason })
}
"><code><span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>result.verified <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> result.retryAfter) {
  <span class="hljs-keyword">return</span> res
    .status(<span class="hljs-number">402</span>)
    .set(<span class="hljs-string">"Retry-After"</span>, String(result.retryAfter))
    .json({ <span class="hljs-function"><span class="hljs-keyword">error</span>: "<span class="hljs-title">Payment</span> <span class="hljs-title">pending</span> <span class="hljs-title">confirmation</span>", <span class="hljs-title">reason</span>: <span class="hljs-title">result</span>.<span class="hljs-title">reason</span> })
}
</span></code></pre><hr><h2 id="h-step-5-use-paywatchers-x402-descriptor-optional" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 5: Use PayWatcher's x402 Descriptor (Optional)</h2><p>If you want PayWatcher to generate the exact x402 descriptor for you — including the correct USDC contract address per chain and amount in atomic units — add <code>x402: true</code> to your payment intent:</p><pre data-type="codeBlock" text="const intentResponse = await fetch(&quot;https://api.paywatcher.dev/v1/payments&quot;, {
  method: &quot;POST&quot;,
  headers: {
    &quot;x-api-key&quot;: PAYWATCHER_API_KEY,
    &quot;Content-Type&quot;: &quot;application/json&quot;,
  },
  body: JSON.stringify({
    amount: RESOURCE_PRICE,
    network: &quot;base&quot;,
    x402: true,
  }),
})

const { data } = await intentResponse.json()

// Return the pre-built x402 descriptor directly
return res.status(402).json(data.x402)
"><code>const intentResponse <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"https://api.paywatcher.dev/v1/payments"</span>, {
  method: <span class="hljs-string">"POST"</span>,
  headers: {
    <span class="hljs-string">"x-api-key"</span>: PAYWATCHER_API_KEY,
    <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
  },
  body: JSON.stringify({
    amount: RESOURCE_PRICE,
    network: <span class="hljs-string">"base"</span>,
    x402: <span class="hljs-literal">true</span>,
  }),
})

const { data } <span class="hljs-operator">=</span> await intentResponse.json()

<span class="hljs-comment">// Return the pre-built x402 descriptor directly</span>
<span class="hljs-keyword">return</span> res.status(<span class="hljs-number">402</span>).json(data.x402)
</code></pre><p>This is the easiest path if you want multi-chain support without hardcoding contract addresses per network.</p><hr><h2 id="h-complete-flow" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Complete Flow</h2><pre data-type="codeBlock" text="Client                    Your Server              PayWatcher
  │                           │                        │
  │── GET /api/report/42 ────&gt;│                        │
  │&lt;─ 402 + descriptor ───────│                        │
  │                           │                        │
  │  [sends USDC on-chain]    │                        │
  │                           │                        │
  │── GET /api/report/42 ────&gt;│                        │
  │   x-payment: {txHash}     │                        │
  │                           │── POST /verify ───────&gt;│
  │                           │&lt;─ { verified: true } ──│
  │&lt;─ 200 + data ─────────────│                        │
"><code>Client                    Your Server              PayWatcher
  │                           │                        │
  │── GET <span class="hljs-operator">/</span>api<span class="hljs-operator">/</span>report<span class="hljs-operator">/</span><span class="hljs-number">42</span> ────<span class="hljs-operator">&gt;</span>│                        │
  │<span class="hljs-operator">&lt;</span>─ <span class="hljs-number">402</span> <span class="hljs-operator">+</span> descriptor ───────│                        │
  │                           │                        │
  │  [sends USDC on<span class="hljs-operator">-</span>chain]    │                        │
  │                           │                        │
  │── GET <span class="hljs-operator">/</span>api<span class="hljs-operator">/</span>report<span class="hljs-operator">/</span><span class="hljs-number">42</span> ────<span class="hljs-operator">&gt;</span>│                        │
  │   x<span class="hljs-operator">-</span>payment: {txHash}     │                        │
  │                           │── POST <span class="hljs-operator">/</span>verify ───────<span class="hljs-operator">&gt;</span>│
  │                           │<span class="hljs-operator">&lt;</span>─ { verified: <span class="hljs-literal">true</span> } ──│
  │<span class="hljs-operator">&lt;</span>─ <span class="hljs-number">200</span> <span class="hljs-operator">+</span> data ─────────────│                        │
</code></pre><hr><h2 id="h-supported-chains" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Supported Chains</h2><p>PayWatcher verifies USDC payments on:</p><table><colgroup><col><col><col></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Chain</p></th><th colspan="1" rowspan="1"><p>Network slug</p></th><th colspan="1" rowspan="1"><p>USDC Contract</p></th></tr><tr><td colspan="1" rowspan="1"><p>Base</p></td><td colspan="1" rowspan="1"><p><code>base</code></p></td><td colspan="1" rowspan="1"><p>0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913</p></td></tr><tr><td colspan="1" rowspan="1"><p>Arbitrum</p></td><td colspan="1" rowspan="1"><p><code>arbitrum</code></p></td><td colspan="1" rowspan="1"><p>0xaf88d065e77c8cC2239327C5EDb3A432268e5831</p></td></tr><tr><td colspan="1" rowspan="1"><p>Ethereum</p></td><td colspan="1" rowspan="1"><p><code>ethereum</code></p></td><td colspan="1" rowspan="1"><p>0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48</p></td></tr><tr><td colspan="1" rowspan="1"><p>Optimism</p></td><td colspan="1" rowspan="1"><p><code>optimism</code></p></td><td colspan="1" rowspan="1"><p>0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85</p></td></tr><tr><td colspan="1" rowspan="1"><p>Polygon</p></td><td colspan="1" rowspan="1"><p><code>polygon</code></p></td><td colspan="1" rowspan="1"><p>0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359</p></td></tr></tbody></table><p>Same API call, same $0.05 fee, regardless of chain.</p><hr><h2 id="h-what-you-get" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What You Get</h2><ul><li><p><strong>No RPC infrastructure</strong> — PayWatcher handles all blockchain queries</p></li><li><p><strong>Multi-chain out of the box</strong> — change <code>network</code> in your request, nothing else</p></li><li><p><strong>Replay attack protection</strong> — <code>tolerance_seconds</code> rejects old transactions</p></li><li><p><strong>Confirmation guarantees</strong> — PayWatcher waits for chain-specific required confirmations before returning <code>verified: true</code></p></li><li><p><strong>$0.05 flat</strong> — not 0.5–1% of the transaction value</p></li></ul><hr><h2 id="h-next-steps" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Next Steps</h2><ul><li><p>Get access at <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://paywatcher.dev">paywatcher.dev</a></p></li><li><p>Read the full API reference in the docs</p></li><li><p>Questions? <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="mailto:hello@paywatcher.dev">hello@paywatcher.dev</a></p></li></ul><br>]]></content:encoded>
            <author>paywatcher@newsletter.paragraph.com (PayWatcher)</author>
            <category>x402</category>
            <category>usdc</category>
            <category>ai</category>
            <category>agents</category>
            <category>web3dev</category>
            <category>stablecoins</category>
            <category>base</category>
            <category>arbitrum</category>
            <category>buildonbase</category>
            <category>paymentinfrastructure</category>
            <category>tutorial</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/e8a735700e9a717b58ba45741a6b8e969e17af09d84d198545da2e398ea885fb.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[How AI Agents Pay for API Calls — And How to Verify They Did]]></title>
            <link>https://paragraph.com/@paywatcher/how-ai-agents-pay-for-api-calls-—-and-how-to-verify-they-did</link>
            <guid>8uPCaXAzIL0Mapx20eD8</guid>
            <pubDate>Tue, 17 Mar 2026 09:46:50 GMT</pubDate>
            <description><![CDATA[HTTP has had a status code sitting unused for 30 years. 402: Payment Required. It was reserved for a future where machines could pay for things directly. That future is here. Stripe and Coinbase are both building around x402 — a protocol that revives 402 to enable AI agents to pay for API calls in USDC, automatically, without a human in the loop. An AI agent requests a resource. The server responds with 402. The agent pays in USDC. Access is granted. No checkout page. No credit card. No human...]]></description>
            <content:encoded><![CDATA[<p>HTTP has had a status code sitting unused for 30 years.</p><p><strong>402: Payment Required.</strong></p><p>It was reserved for a future where machines could pay for things directly. That future is here. Stripe and Coinbase are both building around x402 — a protocol that revives 402 to enable AI agents to pay for API calls in USDC, automatically, without a human in the loop.</p><p>An AI agent requests a resource. The server responds with 402. The agent pays in USDC. Access is granted. No checkout page. No credit card. No human approval.</p><p>This is what machine-to-machine payments look like.</p><hr><h2 id="h-the-problem-nobody-is-talking-about" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Problem Nobody Is Talking About</h2><p>Everyone is focused on the rails. Stripe built the payment layer. Coinbase built the protocol. A16z, Galaxy, Outlier Ventures are all writing theses about agentic payments.</p><p>But there's a step everyone is glossing over.</p><p><strong>How does the server know the payment actually arrived?</strong></p><p>An agent sends 0.01 USDC to your wallet on Base. Your server needs to:</p><ol><li><p>Detect the transfer happened</p></li><li><p>Match it to the specific request</p></li><li><p>Confirm it's not a reorg</p></li><li><p>Wait for enough block confirmations</p></li><li><p>Grant access</p></li></ol><p>If you build this yourself, you're looking at subscribing to USDC transfer events via ethers.js, handling edge cases like chain reorgs, managing confirmation thresholds, building retry logic for missed events. Two weeks of work, minimum — and that's if you've done it before.</p><p>If you use a payment processor, you're paying 0.5–1% per transaction. On $0.01 micropayments, that's a fee larger than the payment itself.</p><p><strong>The verification layer for x402 doesn't exist yet. That's the gap.</strong></p><hr><h2 id="h-what-x402-assumes-you-already-have" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What x402 Assumes You Already Have</h2><p>The x402 protocol defines the payment flow beautifully:</p><pre data-type="codeBlock" text="Agent → GET /api/resource
Server → 402 Payment Required
        { &quot;accepts&quot;: [{ &quot;scheme&quot;: &quot;exact&quot;, &quot;network&quot;: &quot;base&quot;, 
          &quot;maxAmountRequired&quot;: &quot;1000&quot;, &quot;asset&quot;: &quot;USDC&quot;,
          &quot;payTo&quot;: &quot;0xYourWallet&quot; }] }
Agent → Pays USDC on Base
Agent → GET /api/resource (with payment proof)
Server → 200 OK"><code>Agent → <span class="hljs-keyword">GET</span> /api/resource
Server → <span class="hljs-number">402</span> Payment Required
        { <span class="hljs-string">"accepts"</span>: [{ <span class="hljs-string">"scheme"</span>: <span class="hljs-string">"exact"</span>, <span class="hljs-string">"network"</span>: <span class="hljs-string">"base"</span>, 
          <span class="hljs-string">"maxAmountRequired"</span>: <span class="hljs-string">"1000"</span>, <span class="hljs-string">"asset"</span>: <span class="hljs-string">"USDC"</span>,
          <span class="hljs-string">"payTo"</span>: <span class="hljs-string">"0xYourWallet"</span> }] }
Agent → Pays USDC <span class="hljs-keyword">on</span> Base
Agent → <span class="hljs-keyword">GET</span> /api/resource (<span class="hljs-keyword">with</span> payment proof)
Server → <span class="hljs-number">200</span> OK</code></pre><p>Clean. Elegant. Stateless.</p><p>But that last step — "Server verifies payment proof" — assumes your server can confirm the transfer actually happened on-chain. In practice, that means you need:</p><ul><li><p>A blockchain listener watching your wallet</p></li><li><p>Amount matching logic (how do you distinguish two 0.01 USDC payments in the same block?)</p></li><li><p>Confirmation waiting (Base finalizes fast, but you still need to handle reorgs)</p></li><li><p>A webhook or callback system to notify your application</p></li></ul><p>That's infrastructure. Non-trivial infrastructure.</p><hr><h2 id="h-the-verification-layer" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Verification Layer</h2><p>This is exactly what PayWatcher is built for.</p><p>You create a Payment Intent before the agent pays. PayWatcher gives back a <code>unique_amount</code> — a slightly adjusted figure (e.g., $0.0103 instead of $0.01) that makes the payment uniquely identifiable on-chain.</p><p>bash</p><pre data-type="codeBlock" text="POST https://api.paywatcher.dev/v1/payments
{
  &quot;amount&quot;: &quot;0.01&quot;,
  &quot;token&quot;: &quot;USDC&quot;,
  &quot;network&quot;: &quot;base&quot;,
  &quot;recipient&quot;: &quot;0xYourWallet&quot;,
  &quot;webhook_url&quot;: &quot;https://yourapi.com/webhook/payment&quot;,
  &quot;reference&quot;: &quot;agent-request-abc123&quot;
}"><code>POST https://api.paywatcher.dev/v1/payments
{
  <span class="hljs-string">"amount"</span>: <span class="hljs-string">"0.01"</span>,
  <span class="hljs-string">"token"</span>: <span class="hljs-string">"USDC"</span>,
  <span class="hljs-string">"network"</span>: <span class="hljs-string">"base"</span>,
  <span class="hljs-string">"recipient"</span>: <span class="hljs-string">"0xYourWallet"</span>,
  <span class="hljs-string">"webhook_url"</span>: <span class="hljs-string">"https://yourapi.com/webhook/payment"</span>,
  <span class="hljs-string">"reference"</span>: <span class="hljs-string">"agent-request-abc123"</span>
}</code></pre><p>Response:</p><p>json</p><pre data-type="codeBlock" text="{
  &quot;id&quot;: &quot;pay_xyz789&quot;,
  &quot;status&quot;: &quot;watching&quot;,
  &quot;unique_amount&quot;: &quot;0.0103&quot;,
  &quot;expires_at&quot;: &quot;2026-03-16T15:00:00Z&quot;
}"><code><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"pay_xyz789"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"status"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"watching"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"unique_amount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.0103"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"expires_at"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2026-03-16T15:00:00Z"</span>
<span class="hljs-punctuation">}</span></code></pre><p>You give the agent <code>unique_amount</code> as the amount to pay. When the transfer hits Base, PayWatcher fires a webhook:</p><p>json</p><pre data-type="codeBlock" text="{
  &quot;event&quot;: &quot;payment.confirmed&quot;,
  &quot;payment_id&quot;: &quot;pay_xyz789&quot;,
  &quot;reference&quot;: &quot;agent-request-abc123&quot;,
  &quot;amount&quot;: &quot;0.0103&quot;,
  &quot;token&quot;: &quot;USDC&quot;,
  &quot;network&quot;: &quot;base&quot;,
  &quot;tx_hash&quot;: &quot;0xabc...def&quot;,
  &quot;confirmed_at&quot;: &quot;2026-03-16T14:47:22Z&quot;
}"><code><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"event"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"payment.confirmed"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"payment_id"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"pay_xyz789"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"reference"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"agent-request-abc123"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"amount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0.0103"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"token"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"USDC"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"network"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"base"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"tx_hash"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xabc...def"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"confirmed_at"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2026-03-16T14:47:22Z"</span>
<span class="hljs-punctuation">}</span></code></pre><p>Your server grants access. Done.</p><p><strong>$0.05 flat fee. No percentage. No custody. No checkout.</strong></p><hr><h2 id="h-the-full-x402-paywatcher-flow" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Full x402 + PayWatcher Flow</h2><pre data-type="codeBlock" text="Agent → GET /api/resource
Server → 402 + PayWatcher unique_amount
Agent → Sends USDC to your wallet
PayWatcher → Detects transfer on Base
PayWatcher → Fires webhook to your server
Server → Grants access
Agent → GET /api/resource (authorized)
Server → 200 OK"><code>Agent → <span class="hljs-keyword">GET</span> /api/resource
Server → <span class="hljs-number">402</span> + PayWatcher unique_amount
Agent → Sends USDC <span class="hljs-keyword">to</span> your wallet
PayWatcher → Detects transfer <span class="hljs-keyword">on</span> Base
PayWatcher → Fires webhook <span class="hljs-keyword">to</span> your server
Server → Grants access
Agent → <span class="hljs-keyword">GET</span> /api/resource (authorized)
Server → <span class="hljs-number">200</span> OK</code></pre><p>Your server never touches the funds. PayWatcher never touches the funds. The agent pays directly to your wallet.</p><hr><h2 id="h-why-flat-fee-matters-for-micropayments" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Why Flat Fee Matters for Micropayments</h2><p>Percentage fees break at micropayment scale.</p><table style="min-width: 75px"><colgroup><col><col><col></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Payment Size</p></th><th colspan="1" rowspan="1"><p>Coinbase Commerce (1%)</p></th><th colspan="1" rowspan="1"><p>PayWatcher ($0.05 flat)</p></th></tr><tr><td colspan="1" rowspan="1"><p>$0.01</p></td><td colspan="1" rowspan="1"><p>$0.0001 ✓ (but min fees apply)</p></td><td colspan="1" rowspan="1"><p>$0.05 ✗ too expensive</p></td></tr><tr><td colspan="1" rowspan="1"><p>$0.10</p></td><td colspan="1" rowspan="1"><p>$0.001</p></td><td colspan="1" rowspan="1"><p>$0.05</p></td></tr><tr><td colspan="1" rowspan="1"><p>$1.00</p></td><td colspan="1" rowspan="1"><p>$0.01</p></td><td colspan="1" rowspan="1"><p>$0.05</p></td></tr><tr><td colspan="1" rowspan="1"><p>$10.00</p></td><td colspan="1" rowspan="1"><p>$0.10</p></td><td colspan="1" rowspan="1"><p>$0.05 ✓</p></td></tr><tr><td colspan="1" rowspan="1"><p>$100.00</p></td><td colspan="1" rowspan="1"><p>$1.00</p></td><td colspan="1" rowspan="1"><p>$0.05 ✓</p></td></tr><tr><td colspan="1" rowspan="1"><p>$10,000.00</p></td><td colspan="1" rowspan="1"><p>$100.00</p></td><td colspan="1" rowspan="1"><p>$0.05 ✓✓✓</p></td></tr></tbody></table><p>For high-value payments, flat fee wins decisively. For true micropayments under $0.05, neither model works well — but that's a Base gas cost problem, not a PayWatcher problem.</p><p>The sweet spot for PayWatcher is <strong>$1–$10,000+ payments</strong> where you want automatic verification without giving up 0.5–1%.</p><hr><h2 id="h-who-this-is-for" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Who This Is For</h2><p><strong>Build with PayWatcher if you:</strong></p><ul><li><p>Accept USDC payments on Base and want automatic confirmation</p></li><li><p>Are building x402-compatible APIs that AI agents will call</p></li><li><p>Don't want to give up 0.5–1% per transaction</p></li><li><p>Want webhook notifications instead of polling Basescan</p></li><li><p>Need non-custodial verification (regulatory, preference, or principle)</p></li></ul><p><strong>Don't use PayWatcher if you:</strong></p><ul><li><p>Need fiat on/off ramps</p></li><li><p>Need a hosted checkout page</p></li><li><p>Need multi-currency support</p></li><li><p>Are transacting under $0.05 per payment (the fee exceeds the payment)</p></li></ul><hr><h2 id="h-try-it" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Try It</h2><p>PayWatcher is live on Base. Free tier: 50 verifications/month — enough to build and test your x402 integration.</p><p>→ <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://paywatcher.dev">Get API Key</a> → <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://docs.paywatcher.dev">Read the Docs</a></p><p>Built by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://masem.at">masemIT</a> — we also build <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://chainsights.one">ChainSights</a>, identity-first analytics for DAOs.</p><p>Questions? Reach out on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://warpcast.com/paywatcher">Farcaster</a> or <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://x.com/X_Sem_pre">X</a>.</p>]]></content:encoded>
            <author>paywatcher@newsletter.paragraph.com (PayWatcher)</author>
            <category>x402</category>
            <category>usdc</category>
            <category>base</category>
            <category>aiagents</category>
            <category>payments</category>
            <category>verification</category>
            <category>api</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/35e2822e8f515d7f8d864760773d8e083c5025d101cb69503869635c812e1de8.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[How to Verify USDC Payments on Base Without a Payment Processor]]></title>
            <link>https://paragraph.com/@paywatcher/how-to-verify-usdc-payments-on-base-without-a-payment-processor</link>
            <guid>7yqKolBzH5uheYY8BHyr</guid>
            <pubDate>Sun, 22 Feb 2026 07:27:39 GMT</pubDate>
            <description><![CDATA[The Problem Nobody Talks AboutYou want to accept a $10,000 USDC payment. You have two options: Option A: Integrate a payment processor like Coinbase Commerce. Set up an account, embed their checkout widget, handle their SDK. Pay $100 in fees (1%). Option B: Build your own blockchain listener. Learn ethers.js, subscribe to USDC transfer events, handle reorgs, confirmations, edge cases. Two weeks of work, minimum. There's no middle ground.]]></description>
            <content:encoded><![CDATA[<h2 id="h-the-problem-nobody-talks-about" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Problem Nobody Talks About</h2><p>You want to accept a $10,000 USDC payment. You have two options:</p><p><strong>Option A:</strong> Integrate a payment processor like Coinbase Commerce. Set up an account, embed their checkout widget, handle their SDK. Pay $100 in fees (1%).</p><p><strong>Option B:</strong> Build your own blockchain listener. Learn ethers.js, subscribe to USDC transfer events, handle reorgs, confirmations, edge cases. Two weeks of work, minimum.</p><p>There's no middle ground. No service that just tells you: <em>"Yes, this specific payment arrived."</em></p><p>Until now.</p><hr><h2 id="h-what-if-verification-was-a-simple-api-call" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What If Verification Was a Simple API Call?</h2><p>PayWatcher is a verification layer for stablecoin payments on Base. It doesn't process payments. It doesn't touch your funds. It doesn't require a checkout flow.</p><p>You tell it what payment you expect. It watches the blockchain. When the payment arrives, you get a webhook.</p><p><strong>That's it.</strong></p><p>Here's the cost comparison for verifying a $10,000 USDC transfer:</p><table style="min-width: 75px"><colgroup><col><col><col></colgroup><tbody><tr><th colspan="1" rowspan="1"><p>Service</p></th><th colspan="1" rowspan="1"><p>Fee</p></th><th colspan="1" rowspan="1"><p>Model</p></th></tr><tr><td colspan="1" rowspan="1"><p>Coinbase Commerce</p></td><td colspan="1" rowspan="1"><p>$100.00</p></td><td colspan="1" rowspan="1"><p>1% of transaction</p></td></tr><tr><td colspan="1" rowspan="1"><p>NOWPayments</p></td><td colspan="1" rowspan="1"><p>$50.00</p></td><td colspan="1" rowspan="1"><p>0.5% of transaction</p></td></tr><tr><td colspan="1" rowspan="1"><p>PayWatcher</p></td><td colspan="1" rowspan="1"><p>$0.05</p></td><td colspan="1" rowspan="1"><p>Flat fee, always</p></td></tr></tbody></table><p>The fee is the same whether you're verifying $1 or $1,000,000.</p><hr><h2 id="h-how-it-works-3-steps" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How It Works (3 Steps)</h2><h3 id="h-step-1-create-a-payment-intent" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 1: Create a Payment Intent</h3><p>Tell PayWatcher what payment you're expecting:</p><p>bash</p><pre data-type="codeBlock" text="curl -X POST https://api.paywatcher.dev/v1/payments \
  -H &quot;Authorization: Bearer YOUR_API_KEY&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d '{
    &quot;amount&quot;: &quot;10000.00&quot;,
    &quot;token&quot;: &quot;USDC&quot;,
    &quot;network&quot;: &quot;base&quot;,
    &quot;recipient&quot;: &quot;0xYourWalletAddress&quot;,
    &quot;webhook_url&quot;: &quot;https://yourapp.com/webhook/payment&quot;,
    &quot;reference&quot;: &quot;invoice-2026-001&quot;
  }'"><code>curl -X POST https:<span class="hljs-comment">//api.paywatcher.dev/v1/payments \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{</span>
    <span class="hljs-string">"amount"</span>: <span class="hljs-string">"10000.00"</span>,
    <span class="hljs-string">"token"</span>: <span class="hljs-string">"USDC"</span>,
    <span class="hljs-string">"network"</span>: <span class="hljs-string">"base"</span>,
    <span class="hljs-string">"recipient"</span>: <span class="hljs-string">"0xYourWalletAddress"</span>,
    <span class="hljs-string">"webhook_url"</span>: <span class="hljs-string">"https://yourapp.com/webhook/payment"</span>,
    <span class="hljs-string">"reference"</span>: <span class="hljs-string">"invoice-2026-001"</span>
  }'</code></pre><p>Response:</p><p>json</p><pre data-type="codeBlock" text="{
  &quot;id&quot;: &quot;pay_abc123&quot;,
  &quot;status&quot;: &quot;watching&quot;,
  &quot;unique_amount&quot;: &quot;10000.03&quot;,
  &quot;expires_at&quot;: &quot;2026-02-23T12:00:00Z&quot;
}"><code><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"pay_abc123"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"status"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"watching"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"unique_amount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"10000.03"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"expires_at"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2026-02-23T12:00:00Z"</span>
<span class="hljs-punctuation">}</span></code></pre><p>Notice the <code>unique_amount</code>? PayWatcher adds a few cents to make your payment uniquely identifiable on-chain. Your customer sends $10,000.03 instead of $10,000.00 — and that tiny difference is how we match the exact transfer to your intent.</p><h3 id="h-step-2-your-customer-sends-usdc" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 2: Your Customer Sends USDC</h3><p>Your customer sends USDC directly to your wallet. No checkout page, no intermediary, no redirect. Just a standard USDC transfer on Base.</p><p>You keep 100% of the payment. PayWatcher never has custody.</p><h3 id="h-step-3-get-notified" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 3: Get Notified</h3><p>When the transfer is confirmed on Base, PayWatcher fires a webhook:</p><p>json</p><pre data-type="codeBlock" text="{
  &quot;event&quot;: &quot;payment.confirmed&quot;,
  &quot;payment_id&quot;: &quot;pay_abc123&quot;,
  &quot;reference&quot;: &quot;invoice-2026-001&quot;,
  &quot;amount&quot;: &quot;10000.03&quot;,
  &quot;token&quot;: &quot;USDC&quot;,
  &quot;network&quot;: &quot;base&quot;,
  &quot;tx_hash&quot;: &quot;0xabc...def&quot;,
  &quot;confirmed_at&quot;: &quot;2026-02-22T10:30:00Z&quot;
}"><code><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"event"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"payment.confirmed"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"payment_id"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"pay_abc123"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"reference"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"invoice-2026-001"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"amount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"10000.03"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"token"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"USDC"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"network"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"base"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"tx_hash"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xabc...def"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"confirmed_at"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2026-02-22T10:30:00Z"</span>
<span class="hljs-punctuation">}</span></code></pre><p>Your backend processes the webhook, marks the invoice as paid, and you're done. No polling, no manual checking on Basescan.</p><hr><h2 id="h-javascript-example" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">JavaScript Example</h2><p>Here's a minimal Node.js integration:</p><p>javascript</p><pre data-type="codeBlock" text="// Create a payment intent
const response = await fetch('https://api.paywatcher.dev/v1/payments', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.PAYWATCHER_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    amount: '500.00',
    token: 'USDC',
    network: 'base',
    recipient: '0xYourWallet',
    webhook_url: 'https://yourapp.com/api/webhook',
    reference: `order-${orderId}`
  })
});

const payment = await response.json();
// Show payment.unique_amount to your customer"><code><span class="hljs-comment">// Create a payment intent</span>
<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">response</span> = await <span class="hljs-title function_ invoke__">fetch</span>(<span class="hljs-string">'https://api.paywatcher.dev/v1/payments'</span>, {
  <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
  <span class="hljs-attr">headers</span>: {
    <span class="hljs-string">'Authorization'</span>: `Bearer ${process.env.PAYWATCHER_API_KEY}`,
    <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>
  },
  <span class="hljs-attr">body</span>: JSON.<span class="hljs-title function_ invoke__">stringify</span>({
    <span class="hljs-attr">amount</span>: <span class="hljs-string">'500.00'</span>,
    <span class="hljs-attr">token</span>: <span class="hljs-string">'USDC'</span>,
    <span class="hljs-attr">network</span>: <span class="hljs-string">'base'</span>,
    <span class="hljs-attr">recipient</span>: <span class="hljs-string">'0xYourWallet'</span>,
    <span class="hljs-attr">webhook_url</span>: <span class="hljs-string">'https://yourapp.com/api/webhook'</span>,
    <span class="hljs-attr">reference</span>: `order-${orderId}`
  })
});

<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">payment</span> = await response.<span class="hljs-title function_ invoke__">json</span>();
<span class="hljs-comment">// Show payment.unique_amount to your customer</span></code></pre><p>And the webhook handler:</p><p>javascript</p><pre data-type="codeBlock" text="// Express webhook endpoint
app.post('/api/webhook', (req, res) =&gt; {
  const { event, reference, amount, tx_hash } = req.body;

  if (event === 'payment.confirmed') {
    // Mark order as paid
    await db.orders.update({
      where: { reference },
      data: { status: 'paid', tx_hash }
    });
  }

  res.status(200).send('ok');
});"><code><span class="hljs-comment">// Express webhook endpoint</span>
app.post(<span class="hljs-string">'/api/webhook'</span>, (req, res) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const { <span class="hljs-function"><span class="hljs-keyword">event</span>, <span class="hljs-title">reference</span>, <span class="hljs-title">amount</span>, <span class="hljs-title">tx_hash</span> } = <span class="hljs-title">req</span>.<span class="hljs-title">body</span></span>;

  <span class="hljs-keyword">if</span> (<span class="hljs-function"><span class="hljs-keyword">event</span> === '<span class="hljs-title">payment</span>.<span class="hljs-title">confirmed</span>') </span>{
    <span class="hljs-comment">// Mark order as paid</span>
    await db.orders.update({
      where: { reference },
      data: { status: <span class="hljs-string">'paid'</span>, tx_hash }
    });
  }

  res.status(<span class="hljs-number">200</span>).<span class="hljs-built_in">send</span>(<span class="hljs-string">'ok'</span>);
});</code></pre><hr><p><strong>Test Your Integration Without Sending Real USDC</strong></p><p>Before going live, you can verify your webhook handler works correctly using the built-in test endpoint:</p><p>bash</p><pre data-type="codeBlock" text="curl -X POST https://api.masem.at/v1/paywatcher/config/test-webhook \
  -H &quot;x-api-key: YOUR_API_KEY&quot;"><code>curl <span class="hljs-operator">-</span>X POST https:<span class="hljs-comment">//api.masem.at/v1/paywatcher/config/test-webhook \</span>
  <span class="hljs-operator">-</span>H <span class="hljs-string">"x-api-key: YOUR_API_KEY"</span></code></pre><p>PayWatcher sends a signed dummy payload to your configured <code>webhook_url</code>:</p><p>json</p><pre data-type="codeBlock" text="{
  &quot;event&quot;: &quot;payment.test&quot;,
  &quot;payment_id&quot;: &quot;test_abc123&quot;,
  &quot;timestamp&quot;: &quot;2026-02-17T10:00:00Z&quot;,
  &quot;data&quot;: {
    &quot;amount&quot;: &quot;1.00&quot;,
    &quot;exact_amount&quot;: &quot;1.000000&quot;,
    &quot;currency&quot;: &quot;USDC&quot;,
    &quot;chain&quot;: &quot;base&quot;,
    &quot;tx_hash&quot;: null,
    &quot;confirmations&quot;: 0
  }
}"><code><span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"event"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"payment.test"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"payment_id"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"test_abc123"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"timestamp"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2026-02-17T10:00:00Z"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"data"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
    <span class="hljs-attr">"amount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1.00"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"exact_amount"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1.000000"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"currency"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"USDC"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"chain"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"base"</span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"tx_hash"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span>
    <span class="hljs-attr">"confirmations"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span>
  <span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span></code></pre><p>The payload is signed with HMAC-SHA256 via the <code>x-paywatcher-signature</code> header — same mechanism as real payments. If your handler processes <code>payment.test</code> correctly, it will process <code>payment.confirmed</code> correctly too.</p><p>No real USDC. No blockchain transaction. Full signature verification.</p><hr><h2 id="h-when-to-use-paywatcher-and-when-not-to" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">When to Use PayWatcher (and When Not To)</h2><p><strong>Use PayWatcher when:</strong></p><ul><li><p>You accept USDC payments and want automatic confirmation</p></li><li><p>You don't want to give up 0.5–1% per transaction</p></li><li><p>You need a non-custodial solution (regulatory, preference, or principle)</p></li><li><p>You want webhook notifications instead of polling Basescan</p></li><li><p>You're building on Base</p></li></ul><p><strong>Don't use PayWatcher when:</strong></p><ul><li><p>You need fiat on/off ramps (use Coinbase Commerce or Stripe)</p></li><li><p>You need multi-currency checkout flows</p></li><li><p>You want a hosted payment page (PayWatcher is API-only)</p></li></ul><p>PayWatcher is verification, not processing. We don't hold, transfer, or have custody of funds.</p><hr><h2 id="h-whats-unique-about-this-approach" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What's Unique About This Approach?</h2><p>Most crypto payment solutions are trying to be the <em>next Stripe for crypto</em> — full checkout flows, currency conversion, custody, settlement. That's valuable for some use cases.</p><p>But if you're already comfortable with USDC on Base, you don't need all that. You need someone to watch the blockchain and tell you when money arrived. That's a fundamentally different service, and it should be priced differently.</p><p>A flat $0.05 per verification instead of a percentage of the transaction value.</p><hr><h2 id="h-architecture-overview" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Architecture Overview</h2><pre data-type="codeBlock" text="Your App                    PayWatcher                  Base Network
   │                            │                            │
   ├── POST /v1/payments ──────►│                            │
   │   (create intent)          │                            │
   │◄── { unique_amount } ──────┤                            │
   │                            │                            │
   │   Show amount to customer  │                            │
   │                            │                            │
   │                            │◄── Monitor USDC transfers ─┤
   │                            │    (event listener)        │
   │                            │                            │
   │                            │── Match transfer to intent │
   │                            │── Wait for confirmations   │
   │                            │                            │
   │◄── Webhook: confirmed ─────┤                            │
   │                            │                            │
   └── Process payment          │                            │"><code>Your App                    PayWatcher                  Base Network
   │                            │                            │
   ├── POST /v1/payments ──────►│                            │
   │   (create intent)          │                            │
   │◄── { unique_amount } ──────┤                            │
   │                            │                            │
   │   Show amount to customer  │                            │
   │                            │                            │
   │                            │◄── Monitor USDC transfers ─┤
   │                            │    (event listener)        │
   │                            │                            │
   │                            │── Match transfer to intent │
   │                            │── Wait for confirmations   │
   │                            │                            │
   │◄── Webhook: confirmed ─────┤                            │
   │                            │                            │
   └── Process payment          │                            │</code></pre><p>PayWatcher sits between your application and the blockchain. It handles the complexity of monitoring transfers, matching amounts, waiting for confirmations, and handling edge cases like reorgs — so you don't have to.</p><hr><h2 id="h-try-it" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Try It</h2><p>PayWatcher is live on Base. We're onboarding early testers now.</p><p><strong>Free tier:</strong> 50 verifications/month — enough to test and validate your integration.</p><p>→ <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://paywatcher.dev/#request-access"><strong>Request API Access</strong></a> → <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://paywatcher.dev/docs"><strong>Read the Docs</strong></a></p><p>Built by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://masem.at">masemIT</a> — we also build <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://chainsights.one">ChainSights</a>, identity-first analytics for DAOs.</p><hr><p><em>Questions? Reach out on </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://farcaster.xyz/paywatcher"><em>Farcaster</em></a><em> or </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://x.com/PayWatcher_dev"><em>X</em></a><em>.</em></p>]]></content:encoded>
            <author>paywatcher@newsletter.paragraph.com (PayWatcher)</author>
            <category>web3</category>
            <category>api</category>
            <category>blockchain</category>
            <category>tutorial</category>
            <category>stablecoins</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/f75e513daf2d473c0f7731e16a1c299619e345761f1c0f4b982bac2a5d76ae07.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>