<?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: Eric Shepard</title>
    <description>The latest articles on DEV Community by Eric Shepard (@doit2win).</description>
    <link>https://dev.to/doit2win</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%2F4132061%2F1dcfd886-7a37-463b-8b0d-fc6e99d25807.png</url>
      <title>DEV Community: Eric Shepard</title>
      <link>https://dev.to/doit2win</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/doit2win"/>
    <language>en</language>
    <item>
      <title>Four bugs shipping a live x402 endpoint</title>
      <dc:creator>Eric Shepard</dc:creator>
      <pubDate>Fri, 18 Sep 2026 20:40:45 +0000</pubDate>
      <link>https://dev.to/doit2win/four-bugs-shipping-a-live-x402-endpoint-3bg3</link>
      <guid>https://dev.to/doit2win/four-bugs-shipping-a-live-x402-endpoint-3bg3</guid>
      <description>&lt;h1&gt;
  
  
  Four bugs shipping a live x402 endpoint
&lt;/h1&gt;

&lt;p&gt;Four bugs reached production in a pay-per-call scraping API that settles USDC on&lt;br&gt;
Base. Every one of them passed a green test suite. In each case the thing meant&lt;br&gt;
to catch the bug was more forgiving than production was.&lt;/p&gt;
&lt;h2&gt;
  
  
  The dependency nobody declared
&lt;/h2&gt;

&lt;p&gt;Every payment through the Coinbase facilitator had been failing for weeks, and&lt;br&gt;
the error said nothing useful: clients got &lt;code&gt;facilitator_unreachable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The cause was one line that did not exist. &lt;code&gt;cdp_facilitator.py&lt;/code&gt; imported PyJWT&lt;br&gt;
lazily, inside the function that signs the bearer token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_bearer_jwt&lt;/span&gt;&lt;span class="p"&gt;(...):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;   &lt;span class="c1"&gt;# never declared in requirements.txt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PyJWT was installed on every development machine as somebody else's transitive&lt;br&gt;
dependency. It was not in the container. The import only ran when a real payment&lt;br&gt;
arrived, so nothing failed at startup, no health check noticed, and the test&lt;br&gt;
suite passed because the tests ran where the package happened to exist.&lt;/p&gt;

&lt;p&gt;A lazy import is exactly the shape that survives local development and dies in&lt;br&gt;
production. It runs on the one code path nobody exercises before shipping.&lt;/p&gt;

&lt;p&gt;The fix is a test, not a discipline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;RUNTIME_IMPORTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jwt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pyjwt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cryptography&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cryptography&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stripe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stripe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_runtime_imports_are_declared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distribution&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;RUNTIME_IMPORTS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;distribution&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;declared_distributions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Worth knowing the limit of that guard: it checks a hardcoded list rather than&lt;br&gt;
scanning source for imports. It protects the dependencies someone remembered to&lt;br&gt;
add to it, which is better than nothing and less than it sounds.&lt;/p&gt;
&lt;h2&gt;
  
  
  A wallet that can be paid and can never pay
&lt;/h2&gt;

&lt;p&gt;The x402 &lt;code&gt;exact&lt;/code&gt; scheme on Base settles with EIP-3009&lt;br&gt;
&lt;code&gt;transferWithAuthorization&lt;/code&gt;. The payer signs an authorization off-chain, the&lt;br&gt;
facilitator submits it and pays the gas. That signature is ECDSA, produced by a&lt;br&gt;
private key.&lt;/p&gt;

&lt;p&gt;A smart-contract wallet has no private key. It cannot produce that signature, so&lt;br&gt;
it can never be the payer ΓÇö even though it receives USDC perfectly well and&lt;br&gt;
looks identical in a block explorer.&lt;/p&gt;

&lt;p&gt;One call tells them apart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://mainnet.base.org &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'content-type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"eth_getCode","params":["0xYOUR_ADDRESS","latest"]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;0x&lt;/code&gt; means an EOA, which can sign. Anything longer is a contract, which cannot.&lt;br&gt;
The receiving wallet here returned 48 bytes of code.&lt;/p&gt;

&lt;p&gt;Two related things cost real time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A deposit address is not proof of control.&lt;/strong&gt; An earlier receiving address
had been pasted from an exchange deposit screen and treated as verified.
On-chain it had never received or sent anything ΓÇö zero transfers, ever. The
test deposit it was supposed to have received did not exist. The rule that
replaced it: send a dollar in, send it back out. If you cannot send it back,
you do not control it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contract wallets pay gas in tokens.&lt;/strong&gt; The one here pays via a paymaster,
charging roughly 1.5 cents in USDC per outbound transfer. Fine for receiving
revenue, expensive to spend from.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The transaction that never committed
&lt;/h2&gt;

&lt;p&gt;This one shipped to production and reported success every time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;customer_doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customer_ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit_used_micro&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_value&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{...})&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DebitResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Transaction.update()&lt;/code&gt; and &lt;code&gt;.set()&lt;/code&gt; in google-cloud-firestore append to an&lt;br&gt;
internal write buffer. Those writes are sent by &lt;code&gt;commit()&lt;/code&gt;, which is called by&lt;br&gt;
the &lt;code&gt;@firestore.transactional&lt;/code&gt; decorator or by using the transaction as a&lt;br&gt;
context manager. This code does neither, so the buffer is discarded when the&lt;br&gt;
object is garbage collected.&lt;/p&gt;

&lt;p&gt;Nothing raises. The function returns &lt;code&gt;ok&lt;/code&gt;, the request is served, and the&lt;br&gt;
customer's balance never moves. Refunds wrote directly rather than through a&lt;br&gt;
transaction, so a balance could only ever go &lt;strong&gt;up&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The reason 678 passing tests said nothing about it is the part worth copying.&lt;br&gt;
The fake Firestore client applied transaction writes immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeTransaction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# real client buffers; this does not
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fake modelled the API I assumed rather than the one that exists. &lt;strong&gt;A test&lt;br&gt;
double more forgiving than production hides exactly the class of bug it exists&lt;br&gt;
to catch.&lt;/strong&gt; The suite was not weak on coverage; it was confidently wrong about&lt;br&gt;
the dependency.&lt;/p&gt;

&lt;p&gt;Two changes, in order of importance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The fake now buffers until &lt;code&gt;commit()&lt;/code&gt;, and a test asserts writes are
invisible before it. The regression test for the bug is a runner that never
commits, asserting the balance stays put while the call claims success.&lt;/li&gt;
&lt;li&gt;Begin, retry and commit moved into a single injected runner, so no individual
method drives a transaction by hand and no future method can forget.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It cost nothing, because no customer had a key yet. It would have hit the first&lt;br&gt;
one.&lt;/p&gt;
&lt;h2&gt;
  
  
  Completed does not mean paid
&lt;/h2&gt;

&lt;p&gt;The card path had the same shape of error, caught before it shipped.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;checkout.session.completed&lt;/code&gt; sounds like the fulfilment event. For card payments&lt;br&gt;
it is. For delayed payment methods ΓÇö bank debits and similar ΓÇö the session&lt;br&gt;
completes when the customer finishes the flow, which is &lt;em&gt;before&lt;/em&gt; the money&lt;br&gt;
arrives. Crediting on that event alone grants credit for funds that may never&lt;br&gt;
settle.&lt;/p&gt;

&lt;p&gt;So the handler gated on payment status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payment_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct, and half a feature. Nothing listened for&lt;br&gt;
&lt;code&gt;checkout.session.async_payment_succeeded&lt;/code&gt;, the event that fires when the&lt;br&gt;
delayed payment actually settles. A customer paying by bank debit would have&lt;br&gt;
been charged and never credited ΓÇö the failure mode that generates a support&lt;br&gt;
ticket and a chargeback rather than a quiet loss.&lt;/p&gt;

&lt;p&gt;Both events fulfil now, both gated on payment status.&lt;/p&gt;

&lt;p&gt;Two smaller things from the same reading of Stripe's own guidance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting the module-level &lt;code&gt;stripe.api_key&lt;/code&gt; on each call is deprecated, and in a
concurrent server it is shared mutable state. A &lt;code&gt;StripeClient&lt;/code&gt; instance
carries the key instead.&lt;/li&gt;
&lt;li&gt;Passing &lt;code&gt;payment_method_types&lt;/code&gt; disables dynamic payment methods, which decide
what to show each buyer. Omitting it is the default for a reason.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The webhook is also the most attacker-interesting endpoint in the service: a&lt;br&gt;
public URL that moves money into an account. It verifies the signature before&lt;br&gt;
parsing the body, grants only what the processor reports as paid, and is&lt;br&gt;
idempotent on the event id, because a webhook is retried on any non-2xx. A&lt;br&gt;
verified event that does nothing still returns 200 ΓÇö otherwise it is redelivered&lt;br&gt;
forever.&lt;/p&gt;
&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;None of these were hard bugs. Each one hid behind something that was supposed to&lt;br&gt;
catch it and was more permissive than production:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bug&lt;/th&gt;
&lt;th&gt;What should have caught it&lt;/th&gt;
&lt;th&gt;Why it didn't&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PyJWT undeclared&lt;/td&gt;
&lt;td&gt;The test suite&lt;/td&gt;
&lt;td&gt;Ran where the package happened to exist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contract wallet as payer&lt;/td&gt;
&lt;td&gt;The address looking valid&lt;/td&gt;
&lt;td&gt;Only &lt;code&gt;eth_getCode&lt;/code&gt; distinguishes them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction never committed&lt;/td&gt;
&lt;td&gt;678 passing tests&lt;/td&gt;
&lt;td&gt;The fake applied writes the real client buffers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit on &lt;code&gt;completed&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Reading the event name&lt;/td&gt;
&lt;td&gt;The name describes the session, not the money&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The cheap checks, in the order I wish I had done them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Assert your declared dependencies, do not trust your machine.&lt;/strong&gt; A lazy
import is the one that gets you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prove wallet control by moving money out, not in.&lt;/strong&gt; Anyone can send to an
address nobody holds the key to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make test doubles pessimistic.&lt;/strong&gt; If the real client buffers, the fake
buffers. A double that is kinder than production converts a green suite into
false confidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the payment provider's own guidance before going live, not after.&lt;/strong&gt;
Two of these came straight out of it, and it took ten minutes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The one that generalises furthest is the third. Coverage was never the problem ΓÇö&lt;br&gt;
the money path had tests for concurrency, idempotency, expiry and refunds, and&lt;br&gt;
they all passed. They were all asking a stub that had been written to agree with&lt;br&gt;
me.&lt;/p&gt;



&lt;p&gt;The endpoint these came from is a hosted Crawl4AI service that takes x402&lt;br&gt;
payments: page in, markdown or LLM-extracted JSON out. There is a demo that&lt;br&gt;
needs no signup, if you want something to point an agent at.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.doit2winsolutions.co/demo/scrape &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url":"https://example.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is new and has no customers yet. I would rather hear that it breaks on your&lt;br&gt;
pages than hear nothing.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>testing</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
