<?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: Vaultion</title>
    <description>The latest articles on DEV Community by Vaultion (@vaultion).</description>
    <link>https://dev.to/vaultion</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%2F4026336%2F0bfb1e13-e424-46ce-bc60-d4b0bf51c213.png</url>
      <title>DEV Community: Vaultion</title>
      <link>https://dev.to/vaultion</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vaultion"/>
    <language>en</language>
    <item>
      <title>Accept USDT and USDC on a custom site without a payment processor account</title>
      <dc:creator>Vaultion</dc:creator>
      <pubDate>Wed, 14 Oct 2026 18:09:23 +0000</pubDate>
      <link>https://dev.to/vaultion/accept-usdt-and-usdc-on-a-custom-site-without-a-payment-processor-account-3f39</link>
      <guid>https://dev.to/vaultion/accept-usdt-and-usdc-on-a-custom-site-without-a-payment-processor-account-3f39</guid>
      <description>&lt;p&gt;Most ways to take crypto on a custom website start with a sign-up: create a processor account, pass business verification, generate an API key, store it somewhere safe, and let the processor collect your payments into a balance you withdraw later.&lt;/p&gt;

&lt;p&gt;If you just want USDT and USDC paid to your own wallet, there's a lighter shape. This post walks through it with one server-side request and no API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  The request
&lt;/h2&gt;

&lt;p&gt;Your server creates a checkout with the price and the wallets you want to be paid in. &lt;code&gt;escrow: 'off'&lt;/code&gt; means direct payments only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server only. Browser calls from other sites are refused.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;randomBytes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://checkout.vaultion.org/v1/stores/sessions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourshop.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your Shop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;orderRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`order-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// same ref = same checkout&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;priceMinor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalCents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// US cents&lt;/span&gt;
    &lt;span class="na"&gt;escrow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;off&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                         &lt;span class="c1"&gt;// direct payments only&lt;/span&gt;
    &lt;span class="na"&gt;receiving&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;evm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0xYourEvmWallet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;// Base, Ethereum, Arbitrum, Polygon, BNB Smart Chain&lt;/span&gt;
      &lt;span class="na"&gt;tron&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TYourTronWallet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// USDT and TRX on TRON&lt;/span&gt;
      &lt;span class="na"&gt;solana&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YourSolanaWallet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// USDC, USDT and SOL on Solana&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;feePayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;merchant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                  &lt;span class="c1"&gt;// or 'buyer' to add the 0.75% fee on top&lt;/span&gt;
    &lt;span class="na"&gt;returnUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://yourshop.com/orders/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checkoutPath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// redirect the buyer to 'https://checkout.vaultion.org' + checkoutPath&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store &lt;code&gt;orderRef&lt;/code&gt; before redirecting: the same &lt;code&gt;orderRef&lt;/code&gt; returns the same checkout, so a timed-out request is safe to retry. Keep the random part, so nobody can guess it and open that checkout first.&lt;/p&gt;

&lt;p&gt;The buyer sees a hosted checkout with every network you sent an address for, and pays from their own wallet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the money goes
&lt;/h2&gt;

&lt;p&gt;On EVM networks, Solana and TRON, the buyer's wallet sends one transaction that pays your share to your address and the 0.75% fee to the fee address, both inside the same transaction. You're paid when it confirms, and there is no balance to withdraw.&lt;/p&gt;

&lt;p&gt;Bitcoin and Litecoin work differently: a standard payment link names one address, so those payments go to a per-order deposit address and are forwarded to you after confirmation. That's a short period of custody, and it's worth knowing which of your coins work which way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Marking the order paid
&lt;/h2&gt;

&lt;p&gt;Read the status from your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://checkout.vaultion.org/v1/stores/sessions/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* confirmed on chain: fulfil the order */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reverted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* reversed by a chain reorganization: hold the order */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A status changes only from a confirmed on-chain receipt. Don't mark an order paid because the buyer came back to your &lt;code&gt;returnUrl&lt;/code&gt; or pasted a transaction hash: both are claims, not proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional: offer escrow on big orders
&lt;/h2&gt;

&lt;p&gt;A confirmed crypto payment can't be charged back, which is good for you and a reason some buyers hesitate to pay a shop they don't know. Set &lt;code&gt;escrow: 'choice'&lt;/code&gt; and the checkout offers both: pay directly, or pay into an escrow contract that holds the money until the buyer confirms delivery. The buyer pays the escrow fee (4% below 500 USDC, 3% from 500, 2% from 5,000), escrow is offered from $50, and disputes go to a Vaultion reviewer, which is not decentralized: you are trusting Vaultion's reviewers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to know first
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prices are in US dollars (&lt;code&gt;priceMinor&lt;/code&gt; is US cents).&lt;/li&gt;
&lt;li&gt;Call it from your server; browser calls from other origins are refused.&lt;/li&gt;
&lt;li&gt;There's no sandbox and no webhooks yet: test with a small real payment and poll the status.&lt;/li&gt;
&lt;li&gt;Only send addresses you control. A payment to a mistyped address can't be recovered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Field reference, every status, and samples in Node, PHP and cURL: &lt;a href="https://vaultion.org/developers" rel="noopener noreferrer"&gt;accept crypto payments with one API call&lt;/a&gt;. The same walkthrough, with the custody note for each coin, is on the site: &lt;a href="https://vaultion.org/blog/accept-usdc-usdt-payments" rel="noopener noreferrer"&gt;accept USDC and USDT without a processor account&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt;, the API used here. This post was drafted with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>ecommerce</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Adding escrow to a freelance platform with one API call</title>
      <dc:creator>Vaultion</dc:creator>
      <pubDate>Wed, 14 Oct 2026 18:07:57 +0000</pubDate>
      <link>https://dev.to/vaultion/adding-escrow-to-a-freelance-platform-with-one-api-call-5ec1</link>
      <guid>https://dev.to/vaultion/adding-escrow-to-a-freelance-platform-with-one-api-call-5ec1</guid>
      <description>&lt;p&gt;Freelance platforms live and die on one moment: the client has to pay before the work is done, and the freelancer has to start work before they've been paid. Every platform solves it the same way in principle: hold the money in the middle until the work is delivered.&lt;/p&gt;

&lt;p&gt;This post shows how to do that with an escrow contract instead of your own balance, using one API call per milestone. No account, no API key, and your platform never holds the funds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The client accepts a proposal. Your server creates an escrow checkout for the milestone.&lt;/li&gt;
&lt;li&gt;The client funds it from their own wallet. The money locks in a smart contract.&lt;/li&gt;
&lt;li&gt;You see the milestone as funded, and the freelancer starts.&lt;/li&gt;
&lt;li&gt;The client approves the work and releases, or the freelancer claims once the review period ends. If they disagree, either side opens a dispute.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1: create the checkout
&lt;/h2&gt;

&lt;p&gt;Call this from your server when a milestone is accepted. Each request carries that freelancer's wallet, so every milestone pays its own freelancer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server only. Browser calls from other sites are refused.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;randomBytes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`ms-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;milestone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// keep a random part&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://checkout.vaultion.org/v1/stores/sessions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourplatform.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your Platform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;freelancer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;// the freelancer gets the escrow emails&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;orderRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Milestone &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;milestone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;priceMinor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;milestone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amountCents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// US cents; escrow needs at least 5000 ($50)&lt;/span&gt;
    &lt;span class="na"&gt;escrow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="c1"&gt;// escrow only&lt;/span&gt;
    &lt;span class="na"&gt;receiving&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;evm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;freelancer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;// one EVM address covers Base, Ethereum, Arbitrum, BNB Smart Chain&lt;/span&gt;
    &lt;span class="na"&gt;returnUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://yourplatform.com/projects/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;milestones&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="nx"&gt;milestone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;orderRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// redirect the client to 'https://checkout.vaultion.org' + session.checkoutPath&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details worth getting right:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Store the &lt;code&gt;orderRef&lt;/code&gt; before you redirect.&lt;/strong&gt; The same &lt;code&gt;orderRef&lt;/code&gt; returns the same checkout, so a timed-out request can be retried without opening a second escrow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep a random part in it.&lt;/strong&gt; Someone who knows your store settings and a guessable &lt;code&gt;orderRef&lt;/code&gt; could open that checkout first, and yours would then fail with &lt;code&gt;order_ref_conflict&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: turn statuses into project states
&lt;/h2&gt;

&lt;p&gt;Your backend reads the session status and maps it to what your UI shows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;th&gt;Your project state&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unpaid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No confirmed payment yet&lt;/td&gt;
&lt;td&gt;Awaiting payment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;escrow_funded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Money is locked in the contract&lt;/td&gt;
&lt;td&gt;Work can start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;escrow_disputed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A dispute is open&lt;/td&gt;
&lt;td&gt;Paused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;escrow_pending&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A ruling was given and its challenge window is running&lt;/td&gt;
&lt;td&gt;Paused, outcome pending&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;escrow_released&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The freelancer was paid (or can claim it)&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;escrow_refunded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The client was refunded (or can claim it)&lt;/td&gt;
&lt;td&gt;Refunded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;escrow_closed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The amount was split between both sides&lt;/td&gt;
&lt;td&gt;Settled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;escrow_late&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Funded after the price deadline&lt;/td&gt;
&lt;td&gt;Review before starting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 3: poll, don't wait for a webhook
&lt;/h2&gt;

&lt;p&gt;There are no webhooks yet, so read the status from a job. Poll every 30 to 60 seconds while the client is on the checkout, then every few minutes until the escrow resolves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;syncMilestone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://checkout.vaultion.org/v1/stores/sessions/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                          &lt;span class="c1"&gt;// try again next run&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;milestones&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="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;notifyProjectParticipants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// your own in-app notifications&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A status changes only from a confirmed on-chain receipt. A client landing back on your &lt;code&gt;returnUrl&lt;/code&gt; proves nothing on its own, so don't mark anything funded from the redirect.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you don't have to build
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custody.&lt;/strong&gt; The contract holds the money and can only pay the client or that milestone's freelancer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escrow emails.&lt;/strong&gt; Both sides are emailed when the escrow is funded, when a dispute opens, when a ruling is given, before the review period ends, and when it completes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dispute handling.&lt;/strong&gt; Either side opens a dispute and submits evidence, and a Vaultion reviewer rules at no charge. That is not decentralized: you are trusting Vaultion's reviewers. A separate guardian can pause a ruling but never redirect funds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The limits, so you can decide
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Clients pay in USDC or USDT on one of six networks, so this sits next to card payments rather than replacing them.&lt;/li&gt;
&lt;li&gt;$50 minimum per escrow, so very small milestones don't fit.&lt;/li&gt;
&lt;li&gt;No sandbox: integrate against production with a real $50 escrow.&lt;/li&gt;
&lt;li&gt;No platform commission inside the escrow. The only fee in it is Vaultion's, paid by the client (4% below 500 USDC, 3% from 500, 2% from 5,000), so charge your platform fee separately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full field reference, every status and code samples in Node, PHP and cURL are in the &lt;a href="https://vaultion.org/developers" rel="noopener noreferrer"&gt;escrow API docs&lt;/a&gt;. A longer version of this walkthrough, with every status mapped, is on the site: &lt;a href="https://vaultion.org/blog/freelance-platform-escrow" rel="noopener noreferrer"&gt;escrow for freelance platforms&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt;, the escrow API used here. This post was drafted with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Holding marketplace funds until delivery: Stripe Connect vs an escrow contract</title>
      <dc:creator>Vaultion</dc:creator>
      <pubDate>Wed, 14 Oct 2026 05:25:14 +0000</pubDate>
      <link>https://dev.to/vaultion/holding-marketplace-funds-until-delivery-stripe-connect-vs-an-escrow-contract-3mll</link>
      <guid>https://dev.to/vaultion/holding-marketplace-funds-until-delivery-stripe-connect-vs-an-escrow-contract-3mll</guid>
      <description>&lt;p&gt;Every marketplace hits the same question in its first month: &lt;strong&gt;between "the buyer paid" and "the seller delivered", who is holding the money?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the seller is paid immediately, the buyer carries all the risk and your support inbox fills up. If the platform holds it, &lt;em&gt;you&lt;/em&gt; are now the one holding other people's money, with everything that implies. This post walks through the two ways developers usually solve it, with code, and the constraints that only show up once you are in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option A: hold funds with Stripe Connect
&lt;/h2&gt;

&lt;p&gt;Start with what Stripe itself says. Its Connect docs state that escrow has a precise legal definition and that Stripe doesn't provide escrow services or support escrow accounts. Escrow services also appear on Stripe's restricted-businesses list. What Connect does give you is control over &lt;em&gt;when&lt;/em&gt; money moves.&lt;/p&gt;

&lt;p&gt;The usual pattern is &lt;strong&gt;separate charges and transfers&lt;/strong&gt;: the buyer pays the platform, and the seller's share is transferred once delivery is confirmed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. The buyer pays your platform&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// $250.00&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;transfer_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-1042&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ...the seller ships, the buyer confirms...&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Move the seller's share to their connected account&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transfers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// your commission stays on the platform&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;acct_seller123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;transfer_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-1042&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;source_transaction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chargeId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// waits until the charge's funds are available&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The alternative is to transfer straight away and use &lt;strong&gt;manual payouts&lt;/strong&gt; on the connected account, so the money sits in the seller's Stripe balance until you release the payout.&lt;/p&gt;

&lt;p&gt;The constraints that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How long you can hold.&lt;/strong&gt; Manual payouts can be delayed up to 90 days in most countries (two years in the US, 10 days in Thailand). A plain card authorization lasts 7 days for online payments, and extended authorization (about 30 days) needs IC+ pricing and is generally meant for cases where the final amount isn't known.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's still your balance.&lt;/strong&gt; With separate charges and transfers, the funds are in your platform's Stripe balance. Stripe recommends this charge type only when you are responsible for connected accounts' negative balances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chargebacks outlive delivery.&lt;/strong&gt; Stripe notes that cardholders can typically dispute within 120 days, and with destination charges or separate charges and transfers your platform balance is debited for the disputed amount and fee. A buyer can dispute after the seller has delivered and been paid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe's own guidance&lt;/strong&gt; is to hold funds only with a clear purpose and a commitment to release them when a condition is met, and to talk to a legal advisor rather than hold funds arbitrarily.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that makes Connect wrong. For card payments with short fulfilment it is the obvious choice. But you are building escrow-like behaviour on a platform that tells you it isn't escrow, and your balance is the shock absorber.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: let a contract hold the money
&lt;/h2&gt;

&lt;p&gt;If your buyers can pay in stablecoins, the holding can move off your books entirely: the payment locks in a smart contract that can only pay the buyer or that order's seller. Here is the same order through the Vaultion escrow API, which takes no account or API key and is called from your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Runs on your server. Never in the browser.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://checkout.vaultion.org/v1/stores/sessions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourplatform.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your Platform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;seller@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;orderRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-1042-k7f3q9&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// same ref returns the same checkout, so retries are safe; keep a random part so nobody can guess it&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Logo design: final files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;priceMinor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// $250.00&lt;/span&gt;
    &lt;span class="na"&gt;escrow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;// escrow only for this order&lt;/span&gt;
    &lt;span class="na"&gt;receiving&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;evm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0xSellerWalletAddress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// this order's seller&lt;/span&gt;
    &lt;span class="na"&gt;returnUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourplatform.com/orders/1042&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Redirect the buyer to 'https://checkout.vaultion.org' + session.checkoutPath&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The buyer funds the escrow from their own wallet on a hosted page. Your backend then treats the order as a small state machine by reading the session status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://checkout.vaultion.org/v1/stores/sessions/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;escrow_funded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="cm"&gt;/* money is locked: tell the seller to ship */&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;escrow_disputed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="cm"&gt;/* hold fulfilment until it is ruled */&lt;/span&gt;        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;escrow_released&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="cm"&gt;/* the seller is paid */&lt;/span&gt;                        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;escrow_refunded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="cm"&gt;/* the buyer got the money back */&lt;/span&gt;              &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The seller is paid when the buyer releases, or can claim it once the review period ends, so a buyer who goes silent doesn't strand the money. A confirmed on-chain payment can't be charged back.&lt;/p&gt;

&lt;p&gt;What you give up, honestly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Buyers need crypto.&lt;/strong&gt; USDC or USDT on one of six networks. It is a second rail next to cards, not a replacement for them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No webhooks yet.&lt;/strong&gt; You poll the status (every 30 to 60 seconds while the buyer is on the checkout, then every few minutes).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No sandbox,&lt;/strong&gt; and a $50 minimum per escrow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No platform commission inside the escrow.&lt;/strong&gt; The only fee in it is Vaultion's, paid by the buyer (4% below 500 USDC, 3% from 500, 2% from 5,000), so you charge your own fee separately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disputes go to a Vaultion reviewer.&lt;/strong&gt; It's free, but it's not decentralized: you are trusting Vaultion's reviewers. A separate guardian can pause a ruling but never redirect funds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Stripe Connect hold&lt;/th&gt;
&lt;th&gt;Escrow contract&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Who holds the money&lt;/td&gt;
&lt;td&gt;Your platform's or the seller's Stripe balance&lt;/td&gt;
&lt;td&gt;A smart contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Longest hold&lt;/td&gt;
&lt;td&gt;7-day auth; payouts up to 90 days (most countries)&lt;/td&gt;
&lt;td&gt;Until release or the review period ends&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buyer reversal after payout&lt;/td&gt;
&lt;td&gt;Card disputes, typically up to 120 days&lt;/td&gt;
&lt;td&gt;None; disputes are settled before release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform commission&lt;/td&gt;
&lt;td&gt;Built in (transfer less than you charged)&lt;/td&gt;
&lt;td&gt;Charged separately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment methods&lt;/td&gt;
&lt;td&gt;Cards and local methods&lt;/td&gt;
&lt;td&gt;USDC and USDT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration&lt;/td&gt;
&lt;td&gt;Charges, transfers, payouts, webhooks&lt;/td&gt;
&lt;td&gt;One POST per order, poll the status&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Which one
&lt;/h2&gt;

&lt;p&gt;Card buyers and delivery inside a week: authorize and capture. Card buyers and longer fulfilment: Connect with separate charges and transfers or manual payouts, plus legal advice and a chargeback plan. Buyers who pay in crypto, cross-border deals, or high-ticket orders between strangers: let a contract hold it. A lot of marketplaces will end up running both.&lt;/p&gt;

&lt;p&gt;If you want the second route, the flow, pricing and limits are on the &lt;a href="https://vaultion.org/marketplace-escrow" rel="noopener noreferrer"&gt;escrow for marketplaces&lt;/a&gt; page, with a link through to the API reference.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt;, the escrow API in Option B. Stripe details are from Stripe's own documentation as of October 2026; check the current docs before you build. This post was drafted with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>stripe</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>A WooCommerce crypto checkout with no API key: where "paid" should come from</title>
      <dc:creator>Vaultion</dc:creator>
      <pubDate>Wed, 14 Oct 2026 03:33:47 +0000</pubDate>
      <link>https://dev.to/vaultion/a-woocommerce-crypto-checkout-with-no-api-key-where-paid-should-come-from-gea</link>
      <guid>https://dev.to/vaultion/a-woocommerce-crypto-checkout-with-no-api-key-where-paid-should-come-from-gea</guid>
      <description>&lt;p&gt;Most crypto payment plugins for WooCommerce start the same way: create an account with the processor, generate an API key, paste it into the plugin settings, and wait for a webhook to tell your store an order is paid.&lt;/p&gt;

&lt;p&gt;That design has two weak points that have nothing to do with crypto. The API key is a secret sitting in your WordPress database, and the webhook is a message from someone else's server that your store has to believe. In 2026 several merchants learned a third weak point: when the processor changes course, the plugin goes with it. Coinbase Commerce's official WooCommerce plugin was closed on WordPress.org on 5 October 2026, and Cryptomus's was closed in April 2025.&lt;/p&gt;

&lt;p&gt;So here is a different question to design around: &lt;strong&gt;what is the minimum a store needs to know, and where should "this order is paid" actually come from?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The store only needs an address
&lt;/h2&gt;

&lt;p&gt;To receive a payment you need one thing: where the money should go. A receiving address is public by definition. If the plugin stores only that, there is nothing in the database worth stealing. No key to leak in a backup, no secret to rotate when a contractor leaves.&lt;/p&gt;

&lt;p&gt;That pushes all the trust into one place: deciding when an order is paid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that must never mark an order paid
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. The thank-you redirect&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'woocommerce_thankyou'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$order_id&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;wc_get_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$order_id&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;payment_complete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// anyone can load this URL&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 2. A transaction hash the buyer pastes in&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;looks_like_tx_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$_POST&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'tx'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;payment_complete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// a real hash, for a different payment&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 3. An unsigned or unverified callback&lt;/span&gt;
&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;payment_complete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// because a POST arrived&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each one trusts a claim instead of checking a fact. The redirect is a URL. The pasted hash may be real but belong to someone else's payment, or to this buyer's payment for a different order. The callback is a message from a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  The only thing that should: a confirmed receipt
&lt;/h2&gt;

&lt;p&gt;A blockchain transaction receipt is a fact anyone can check. A verifier that marks an order paid should confirm, on its own node or RPC, that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The transaction succeeded&lt;/strong&gt; and has the confirmations you require for that chain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is on the expected chain.&lt;/strong&gt; The same address exists on Ethereum, Base and Arbitrum. A USDC transfer on the wrong one is not your payment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The right token moved.&lt;/strong&gt; Check the token contract address, not the symbol. Anyone can deploy a token called "USDC".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The right recipient got the right amount&lt;/strong&gt; or more, read from the transfer itself, not from what the transaction claims it did.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is bound to this order.&lt;/strong&gt; One transaction must not be able to pay two orders, so the payment has to carry something committed to the order it is for.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It hasn't already been counted.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only after all six does the store flip the order. Everything else the buyer or the browser says is a hint about where to look, never the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking a fee without taking custody
&lt;/h2&gt;

&lt;p&gt;If the service charges a percentage, the usual answer is custody: the buyer pays the processor, the processor keeps its fee and pays the merchant later. That is where balances, payout schedules and frozen accounts come from.&lt;/p&gt;

&lt;p&gt;On EVM networks, Solana and TRON there is another way. The buyer's wallet sends a single transaction that pays the merchant's share to the merchant's address and the fee to the fee address, both inside the same transaction. The merchant is paid the moment it confirms, and there is no balance anywhere to withdraw.&lt;/p&gt;

&lt;p&gt;Bitcoin and Litecoin are harder. A transaction can have several outputs, but a standard &lt;code&gt;bitcoin:&lt;/code&gt; payment link (BIP21) names one address and one amount, and that is all most wallets will pay from a QR code. So the practical design is a per-order deposit address that is forwarded after confirmation. That is a short period of custody, and an honest checkout should say so instead of calling the whole thing "non-custodial".&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it leaves the buyer
&lt;/h2&gt;

&lt;p&gt;A confirmed crypto payment cannot be charged back. Merchants like that; buyers paying a store they don't know may not. For high-value orders, the fix is not a chargeback but escrow: the payment is locked in a contract until the buyer confirms delivery or a review period ends, and the merchant can see the funds are there before shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built
&lt;/h2&gt;

&lt;p&gt;This is the design behind Vaultion Payments, a free WooCommerce plugin on WordPress.org: you paste receiving addresses and the checkout is live, with no account and no API key in the plugin. Orders are marked paid only from a confirmed on-chain receipt verified on the server. Direct payments on EVM networks, Solana and TRON split the merchant's amount and a 0.75% fee in the buyer's own transaction; Bitcoin, Litecoin and a "send to address" ETH option go through a deposit address and are forwarded after confirmation. Escrow in USDC or USDT is optional per store. The details, coin list and requirements are on the &lt;a href="https://vaultion.org/payments" rel="noopener noreferrer"&gt;crypto payments for WooCommerce&lt;/a&gt; page.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt;. Escrow disputes on it go to Vaultion-assisted human arbitration, which is not decentralized: you are trusting Vaultion's reviewers. This post was drafted with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>woocommerce</category>
      <category>webdev</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Selling a $5,000 Pokémon card to a stranger: what escrow solves, and what it can't</title>
      <dc:creator>Vaultion</dc:creator>
      <pubDate>Fri, 09 Oct 2026 06:32:16 +0000</pubDate>
      <link>https://dev.to/vaultion/selling-a-5000-pokemon-card-to-a-stranger-what-escrow-solves-and-what-it-cant-57in</link>
      <guid>https://dev.to/vaultion/selling-a-5000-pokemon-card-to-a-stranger-what-escrow-solves-and-what-it-cant-57in</guid>
      <description>&lt;p&gt;High-value trading cards have a trust problem that most payment tools were never built for. A PSA 10 Charizard, a sealed booster box or a vintage sports rookie can be worth thousands, the buyer and seller are usually strangers from a forum or a Discord, and the classic deal goes like this: one side sends first and hopes.&lt;/p&gt;

&lt;p&gt;This post breaks down where card deals actually go wrong, which of those risks an escrow can remove, and which ones no escrow can touch, so you know what still has to be checked by a human.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four ways a card deal fails
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Payment, no card.&lt;/strong&gt; The buyer pays first and the seller disappears. "Friends and family" payments and bank transfers usually leave no way to claw the money back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Card, no payment.&lt;/strong&gt; The seller ships first and the buyer disputes the payment afterwards, or the payment was never real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong card.&lt;/strong&gt; A swapped slab, a lower grade, a raw card that was "near mint" in photos and isn't in hand, or a counterfeit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Damaged or missing in transit.&lt;/strong&gt; The box arrives crushed, empty, or not at all.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are different problems. It helps to be precise about which one a tool actually fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What escrow fixes: the payment side
&lt;/h2&gt;

&lt;p&gt;An escrow holds the buyer's payment until the deal is done. With a smart-contract escrow the money sits in a contract rather than with a company, and the rules are fixed at creation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;buyer funds  -&amp;gt;  contract holds payment  -&amp;gt;  seller ships
                                         -&amp;gt;  buyer receives, checks, releases
                                         -&amp;gt;  or either side disputes, a ruling decides
                                         -&amp;gt;  or the review window ends and the seller can claim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That removes failures 1 and 2 outright:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The seller can ship knowing the money exists and is committed. It cannot vanish mid-deal.&lt;/li&gt;
&lt;li&gt;The buyer is not trusting the seller with the money. Nothing moves to the seller until the buyer releases or a dispute is ruled.&lt;/li&gt;
&lt;li&gt;Neither side can quietly take the funds and walk away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Paying in a stablecoin such as USDC or USDT also means the price does not swing while the card is in the mail.&lt;/p&gt;

&lt;h2&gt;
  
  
  What escrow cannot fix: the card itself
&lt;/h2&gt;

&lt;p&gt;This is the part a lot of "safe trading" advice glosses over. &lt;strong&gt;An escrow secures the payment, not the card.&lt;/strong&gt; No contract can open a box, look at a slab or tell a real hologram from a fake. Failures 3 and 4 are still decided by evidence, and if there is a dispute, whoever rules can only rule on what they are shown.&lt;/p&gt;

&lt;p&gt;So the job before funding is to make the card verifiable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agree the exact card in writing.&lt;/strong&gt; Set, number, grade, and for a graded slab the certificate number. For a raw card, agree the condition and use the listing photos as the reference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the cert yourself.&lt;/strong&gt; PSA, Beckett (BGS) and CGC all run public certificate lookups. Confirm the cert number exists and matches the card, grade and label in the photos. Counterfeit slabs exist, so compare the label details, not just the number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship tracked and insured, with a signature for high values.&lt;/strong&gt; Delivery is the natural release trigger, and the tracking record is evidence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Film the unboxing.&lt;/strong&gt; One continuous video from sealed package to slab in hand is the strongest evidence a buyer can have if something is wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick a review window that fits.&lt;/strong&gt; It needs to cover shipping time plus a day or two to inspect. Too short and the buyer is rushed; too long and the seller waits for no reason.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scams to watch for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fake escrow sites.&lt;/strong&gt; A "trusted middleman" link sent by the other party is a classic setup. Use an escrow you chose yourself, and with a smart-contract escrow you can check the contract address on a block explorer before sending anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send-first pressure.&lt;/strong&gt; "My last buyer flaked, I need payment today" is a script. A real seller who is willing to use escrow loses nothing by it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Off-platform switches.&lt;/strong&gt; Moving the conversation somewhere with no record removes your evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;For a high-value card, the safest version of the deal is: agree the card and cert number, verify the cert, fund an escrow, ship tracked and insured, film the unboxing, then release, or open a dispute with the evidence if it is not what was agreed. The escrow handles the money; the cert, the tracking and the video handle the card.&lt;/p&gt;

&lt;p&gt;If you want to see how that flow works in practice for graded slabs, sealed product and vintage singles, we wrote up how &lt;a href="https://vaultion.org/trading-card-escrow" rel="noopener noreferrer"&gt;trading card escrow&lt;/a&gt; works with stablecoins, including what happens in a dispute and what it costs.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt;, a non-custodial smart-contract escrow. Vaultion does not authenticate cards. This post was drafted with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>blockchain</category>
      <category>ecommerce</category>
      <category>web3</category>
    </item>
    <item>
      <title>Multisig, hold invoices and smart contracts: how non-custodial P2P escrow actually holds your coins</title>
      <dc:creator>Vaultion</dc:creator>
      <pubDate>Fri, 09 Oct 2026 04:52:16 +0000</pubDate>
      <link>https://dev.to/vaultion/multisig-hold-invoices-and-smart-contracts-how-non-custodial-p2p-escrow-actually-holds-your-coins-4i8l</link>
      <guid>https://dev.to/vaultion/multisig-hold-invoices-and-smart-contracts-how-non-custodial-p2p-escrow-actually-holds-your-coins-4i8l</guid>
      <description>&lt;p&gt;"Non-custodial" is the most overused word in peer-to-peer crypto trading. Every platform claims it. But the platforms that genuinely earn it do so in very different ways, and the mechanism decides what can go wrong, who can stall a trade, and what happens if the platform disappears.&lt;/p&gt;

&lt;p&gt;This post compares the four escrow designs you will run into when looking at HODL HODL and the platforms people compare it with: a 2-of-3 multisig, Lightning hold invoices, security deposits with mediation, and smart contracts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test for "non-custodial"
&lt;/h2&gt;

&lt;p&gt;A useful one-line test: &lt;strong&gt;if a support employee could move the funds on their own, it is custodial.&lt;/strong&gt; Real non-custodial escrow means the money sits under rules that no single party, including the platform, can override. Trades settle to your own wallet, not to an internal balance.&lt;/p&gt;

&lt;p&gt;Everything below passes that test. They just pass it differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. A 2-of-3 multisig (HODL HODL)
&lt;/h2&gt;

&lt;p&gt;HODL HODL locks each Bitcoin trade in a 2-of-3 multisig address. Three keys exist; any two can move the coins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OP_2 &amp;lt;buyer&amp;gt; &amp;lt;seller&amp;gt; &amp;lt;escrow_agent&amp;gt; OP_3 OP_CHECKMULTISIG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Buyer and seller agree: two signatures, the coins move, nobody else is involved.&lt;/li&gt;
&lt;li&gt;They disagree: the third key signs with whichever side the dispute process favours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; plain Bitcoin script, nothing exotic, and the coins sit on-chain where anyone can see them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; on-chain fees and confirmation times, Bitcoin only, and no built-in clock. If one side goes silent, someone still has to act on the dispute.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Lightning hold invoices (RoboSats)
&lt;/h2&gt;

&lt;p&gt;RoboSats does escrow without an on-chain address at all. The seller pays a Lightning &lt;strong&gt;hold invoice&lt;/strong&gt;: the payment is locked in-flight, not settled. The preimage that would settle it is only revealed once the buyer confirms the fiat payment, and if the trade fails, the invoice is cancelled and the sats return to the seller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; fast, cheap, private (it runs over Tor with no registration), and nothing ever lands in a long-lived escrow address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; hold invoices tie up liquidity along the route while they are open, so trade sizes are modest by design, and you need a Lightning wallet ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Security deposits and mediation (Bisq)
&lt;/h2&gt;

&lt;p&gt;Bisq is desktop software rather than a website, with trades negotiated peer-to-peer over Tor and no central order book server. Both traders post &lt;strong&gt;security deposits&lt;/strong&gt; alongside the trade amount, so each side has something to lose by misbehaving, and a mediation process handles disputes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; no company acting as coordinator, strong privacy, and a deposit that makes stalling expensive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; a heavier setup than a web signup, thinner liquidity, and deposits that raise the capital you need to trade.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Smart contracts
&lt;/h2&gt;

&lt;p&gt;The fourth design moves the rules into a contract. Funds are locked in a contract that releases them on explicit conditions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// simplified
function release() external onlyBuyer { _pay(seller); }
function claimAfterTimeout() external onlySeller {
    require(block.timestamp &amp;gt;= deadline, "review window open");
    _pay(seller);
}
function rule(uint8 ruling) external onlyArbitrator {
    ruling == 1 ? _pay(seller) : _pay(buyer);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt; timeouts are part of the contract, so a silent counterparty cannot block the other side forever. It works with stablecoins, which removes the incentive to stall a dispute while the price moves. And the arbitrator can be a decentralized court such as Kleros instead of a single person.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; you are trusting the contract code, so it needs to be open source and verified on a block explorer. And a contract on its own is not a marketplace: it settles a deal, it does not find you a counterparty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Multisig (HODL HODL)&lt;/th&gt;
&lt;th&gt;Hold invoice (RoboSats)&lt;/th&gt;
&lt;th&gt;Deposits (Bisq)&lt;/th&gt;
&lt;th&gt;Smart contract&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Where funds sit&lt;/td&gt;
&lt;td&gt;On-chain 2-of-3 address&lt;/td&gt;
&lt;td&gt;In-flight Lightning payment&lt;/td&gt;
&lt;td&gt;On-chain, with deposits&lt;/td&gt;
&lt;td&gt;Contract you can inspect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Silent counterparty&lt;/td&gt;
&lt;td&gt;Needs the dispute key&lt;/td&gt;
&lt;td&gt;Invoice expires or is cancelled&lt;/td&gt;
&lt;td&gt;Mediation&lt;/td&gt;
&lt;td&gt;Built-in timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Assets&lt;/td&gt;
&lt;td&gt;BTC&lt;/td&gt;
&lt;td&gt;BTC (Lightning)&lt;/td&gt;
&lt;td&gt;BTC and others&lt;/td&gt;
&lt;td&gt;Stablecoins and tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finds you a trader&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The question to ask first
&lt;/h2&gt;

&lt;p&gt;The most useful question is not "which is best" but &lt;strong&gt;"do I need a marketplace, or do I need escrow?"&lt;/strong&gt; If you want to be matched with strangers to buy or sell Bitcoin for fiat, you need a marketplace, and the first three designs are built for that. If you have already agreed a deal with one specific person (an OTC trade, a freelance milestone, a domain sale), the order book is overhead and a plain escrow contract is the narrower tool.&lt;/p&gt;

&lt;p&gt;For a fuller comparison of the platforms themselves, including which ones have shut down and which fake "revived" versions to avoid, see our write-up of &lt;a href="https://vaultion.org/blog/hodl-hodl-alternatives" rel="noopener noreferrer"&gt;Hodl Hodl alternatives&lt;/a&gt;, which covers Bisq, RoboSats, Peach and LocalCoinSwap in more depth.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt;, a non-custodial smart-contract escrow, which is the fourth design above. Platform details change, so check current rules on any service before trading. This post was drafted with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>lightning</category>
      <category>blockchain</category>
      <category>security</category>
    </item>
    <item>
      <title>From Bitrated's 2-of-3 multisig to smart-contract escrow: how trustless escrow actually works</title>
      <dc:creator>Vaultion</dc:creator>
      <pubDate>Fri, 09 Oct 2026 04:46:21 +0000</pubDate>
      <link>https://dev.to/vaultion/from-bitrateds-2-of-3-multisig-to-smart-contract-escrow-how-trustless-escrow-actually-works-6n5</link>
      <guid>https://dev.to/vaultion/from-bitrateds-2-of-3-multisig-to-smart-contract-escrow-how-trustless-escrow-actually-works-6n5</guid>
      <description>&lt;p&gt;Back in 2014, Bitrated did something clever: it let two strangers trade Bitcoin without either of them, or the platform, holding the money. A decade later the site is widely described as inactive, but the design problem it solved hasn't gone anywhere. Freelancers, OTC traders and domain buyers still need a way to say "I'll pay, but only when you deliver" without trusting a middleman with the funds.&lt;/p&gt;

&lt;p&gt;This post walks through how Bitrated's model worked at the protocol level, where it broke down in practice, and how the same idea looks when you move it from a Bitcoin multisig script into a smart contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Bitrated escrow worked: a 2-of-3 multisig
&lt;/h2&gt;

&lt;p&gt;Bitrated never held coins. Each trade created a pay-to-script-hash (P2SH) address controlled by three keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the buyer&lt;/li&gt;
&lt;li&gt;the seller&lt;/li&gt;
&lt;li&gt;an arbitrator the two parties chose from a marketplace&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The redeem script looked roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OP_2 &amp;lt;buyer_pubkey&amp;gt; &amp;lt;seller_pubkey&amp;gt; &amp;lt;arbitrator_pubkey&amp;gt; OP_3 OP_CHECKMULTISIG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any two of the three signatures could spend the output. That gives you three paths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Happy path:&lt;/strong&gt; buyer and seller both sign, and the funds go to the seller. The arbitrator never sees the trade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buyer wins a dispute:&lt;/strong&gt; buyer and arbitrator sign a refund.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seller wins a dispute:&lt;/strong&gt; seller and arbitrator sign the release.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The elegant part is that the platform is not in the signing set at all. Even if Bitrated disappeared, the coins would still sit in a standard multisig address on the Bitcoin blockchain, recoverable by any two key holders with a wallet that can import the redeem script (Sparrow and Electrum both can).&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the model struggled
&lt;/h2&gt;

&lt;p&gt;The cryptography was fine. The problems were operational:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Finding an arbitrator.&lt;/strong&gt; The 2-of-3 design only works if the third key belongs to someone who is still around and still responsive when the dispute happens. As the marketplace thinned out, that stopped being a safe assumption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volatility during disputes.&lt;/strong&gt; A trade priced in BTC can move 10% while two people argue over a delivery. Whoever benefits from the price move has a reason to stall.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No programmable exits.&lt;/strong&gt; A multisig has no clock. If the buyer simply goes silent, the seller needs the arbitrator to act; there is no built-in "release after N days" rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitcoin only.&lt;/strong&gt; No stablecoins, so every deal carried price risk by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The same idea as a smart contract
&lt;/h2&gt;

&lt;p&gt;A smart-contract escrow keeps the core property, that no operator holds the funds, but replaces "any two of three keys" with explicit rules in code. A minimal version is a small state machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;enum State { Funded, Released, Refunded, Disputed, Resolved }

function release() external onlyBuyer inState(State.Funded) {
    state = State.Released;
    token.transfer(seller, amount);
}

function claimAfterTimeout() external onlySeller inState(State.Funded) {
    require(block.timestamp &amp;gt;= deadline, "review window still open");
    state = State.Released;
    token.transfer(seller, amount);
}

function raiseDispute() external onlyParty inState(State.Funded) {
    state = State.Disputed;
    // hand the case to an arbitration venue
}

function rule(uint256 ruling) external onlyArbitrator inState(State.Disputed) {
    state = State.Resolved;
    // ruling 1 = pay seller, ruling 2 = refund buyer
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Simplified for illustration: a production contract also needs reentrancy protection, a pull-payment ledger instead of direct transfers, and careful handling of fees.)&lt;/p&gt;

&lt;p&gt;Compared with the multisig, three things change:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Timeouts are first-class.&lt;/strong&gt; A silent buyer can no longer block the seller forever. The deadline is written into the contract when the escrow is created, so both sides know the rules up front.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stablecoins by default.&lt;/strong&gt; Escrowing USDC or USDT removes the volatility incentive to stall a dispute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The arbitrator can be a protocol instead of a person.&lt;/strong&gt; Instead of a single third key, a dispute can be routed to a decentralized court such as &lt;a href="https://kleros.io" rel="noopener noreferrer"&gt;Kleros&lt;/a&gt;, where jurors are drawn at random and stake tokens on their rulings. Some services also offer a human review panel as an alternative venue; the trade-off there is that you are trusting that panel rather than a jury, so it is worth checking what the panel can and cannot do with the funds.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What to check before you trust any escrow contract
&lt;/h2&gt;

&lt;p&gt;Whether you are picking a service or writing your own, the checklist is short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Is it actually non-custodial?&lt;/strong&gt; The funds should sit in a contract you can inspect, not in a company wallet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the source verified on the block explorer?&lt;/strong&gt; You should be able to read the release, refund and timeout logic yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What happens if one side goes silent?&lt;/strong&gt; Look for an explicit deadline path, not "contact support".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who can rule on a dispute, and what can they do?&lt;/strong&gt; The ideal answer is "pay the buyer or the seller, and nothing else". No party, including the operator, should be able to send the funds anywhere else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What does it settle in?&lt;/strong&gt; For anything that takes more than a day, stablecoins are the safer default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Bitrated proved that escrow doesn't need a custodian. Smart contracts keep that property and fix the parts that wore down over time: timeouts replace absent arbitrators, stablecoins remove the incentive to stall, and decentralized courts replace a marketplace you had to hope was still alive.&lt;/p&gt;

&lt;p&gt;If you are moving an old Bitrated trade off the platform, or comparing what replaced it, we put together a longer &lt;a href="https://vaultion.org/blog/bitrated-alternatives" rel="noopener noreferrer"&gt;guide to Bitrated alternatives&lt;/a&gt; that covers multisig wallets, smart-contract escrow, custodial services and P2P marketplaces, plus a step-by-step migration playbook for recovering funds from an existing Bitrated multisig.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt;, a non-custodial smart-contract escrow. This post was drafted with AI assistance and reviewed before publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>bitcoin</category>
      <category>smartcontract</category>
      <category>web3</category>
    </item>
    <item>
      <title>Escrow.com doesn't process crypto — here's the honest alternative</title>
      <dc:creator>Vaultion</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:59:37 +0000</pubDate>
      <link>https://dev.to/vaultion/escrowcom-doesnt-process-crypto-heres-the-honest-alternative-304i</link>
      <guid>https://dev.to/vaultion/escrowcom-doesnt-process-crypto-heres-the-honest-alternative-304i</guid>
      <description>&lt;p&gt;If you've gone looking for an "escrow.com alternative" because your deal is in crypto, there's a fact worth knowing up front: escrow.com is a &lt;em&gt;fiat&lt;/em&gt; escrow service, and its own currency documentation states that it doesn't process cryptocurrency. That's not a knock on escrow.com — it's excellent at what it does — it just means a crypto-denominated deal needs a different kind of tool. Here's an honest comparison and where the crypto-native options fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What escrow.com is genuinely good at
&lt;/h2&gt;

&lt;p&gt;Escrow.com has been the default online escrow service since 1999. It's licensed and regulated, holds funds in a regulated account, verifies both parties with KYC, and runs a formal dispute process with a real team and legal recourse behind it. For buying a domain, a vehicle, a business, or settling a high-value services deal in dollars, euros, pounds, or Australian dollars, it's about as trusted and battle-tested as online escrow gets.&lt;/p&gt;

&lt;p&gt;None of that is in question. If your transaction is in fiat — especially a large or traditional-asset deal where you want a regulated company and a paper trail — escrow.com or a comparable licensed service is very likely the right answer. A crypto-native tool is not automatically better just because it's newer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why people look for a crypto alternative
&lt;/h2&gt;

&lt;p&gt;The catch is currency. Escrow.com's documentation lists the currencies it handles — US dollars, Australian dollars, euros, and British pounds — and states plainly that anything not listed, including any cryptocurrency, cannot be processed. So if your deal is denominated in Bitcoin, a stablecoin, or any other crypto asset, escrow.com simply isn't built to hold it, no matter how trusted it is for fiat.&lt;/p&gt;

&lt;p&gt;On top of that, its model is account-based, KYC-gated, and settles over bank rails. That's the right design for regulated fiat, but a poor match for a wallet-to-wallet crypto deal where both sides expect to move on-chain in minutes without onboarding to a financial institution. No crypto support, plus a fiat-shaped workflow, is what sends crypto users looking elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three shapes of crypto escrow
&lt;/h2&gt;

&lt;p&gt;Within crypto escrow there are really three models, and they map to different priorities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Licensed custodial crypto-escrow&lt;/strong&gt; — the closest in spirit to escrow.com. A regulated company holds the crypto, requires KYC, and handles disputes with a team. Good if you specifically want a company to call and formal recourse on a large crypto deal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-custodial smart-contract escrow&lt;/strong&gt; — the opposite end. Funds live in an on-chain contract no company controls, released on agreed conditions, with disputes routed to a decentralized court rather than an employee. You hold no account and can verify everything yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P2P marketplace escrow&lt;/strong&gt; — escrow bundled into trade-matching. Better for crypto-for-fiat trades than for arbitrary deals.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The honest trade-off between the first two is the important one: a regulated custodian gives you institutional recourse and a legal department; a non-custodial contract gives you verifiable proof that &lt;em&gt;no company — including the operator — can move the funds outside the contract's rules&lt;/em&gt;. Those are different risk models: institutional protection versus on-chain verifiability. Neither is universally safer; it depends on what you're guarding against.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to choose
&lt;/h2&gt;

&lt;p&gt;The decision is mostly about what your deal is denominated in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fiat&lt;/strong&gt; — a domain, a vehicle, a business, a services contract in dollars or euros — use escrow.com or a comparable licensed service. That's what they're for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crypto, and you want a company holding the funds&lt;/strong&gt; with KYC and formal recourse — look at a licensed custodial crypto-escrow service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crypto, and you'd rather hold no account, keep custody out of any company's hands, and verify the whole thing on-chain&lt;/strong&gt; — a non-custodial smart-contract escrow fits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no single winner here. Escrow.com and a non-custodial crypto escrow are solving different problems; the mistake is forcing one to do the other's job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a non-custodial option fits
&lt;/h2&gt;

&lt;p&gt;For the crypto-denominated deal escrow.com can't take, &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt; is one non-custodial smart-contract option: funds lock in a published, open-source contract — not held by Vaultion — and release by the buyer, by a timeout, or by a ruling from Kleros, an independent decentralized court. It settles in stablecoins, needs no account, and lets you read the contract and the arbitrator on a block explorer before you commit anything.&lt;/p&gt;

&lt;p&gt;The honest scope matters, though. Vaultion is &lt;strong&gt;not&lt;/strong&gt; a licensed, regulated, fiat escrow — that's precisely escrow.com's territory, and if you need a regulated institution or fiat settlement, escrow.com remains the better choice. And like any escrow, it secures the &lt;em&gt;payment&lt;/em&gt; side of a deal, not the counterparty — your own due diligence on the other person still applies. What a non-custodial design offers is the one thing a fiat service structurally can't: verifiable, self-custodied escrow for a crypto deal, with no operator able to touch the funds.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by the team at &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt;, a non-custodial crypto escrow for stablecoin deals. Not a licensed or regulated institution — the model is verify-on-chain rather than trust-a-company.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>The honest limits of crypto escrow (and when it isn't worth the fee)</title>
      <dc:creator>Vaultion</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:53:10 +0000</pubDate>
      <link>https://dev.to/vaultion/the-honest-limits-of-crypto-escrow-and-when-it-isnt-worth-the-fee-2hm5</link>
      <guid>https://dev.to/vaultion/the-honest-limits-of-crypto-escrow-and-when-it-isnt-worth-the-fee-2hm5</guid>
      <description>&lt;p&gt;Most articles about crypto escrow only list the upsides. This one does the opposite, because knowing where a tool stops working is what lets you use it well. Escrow is genuinely useful, but it is not a magic guarantee, and treating it like one is how people still lose money with it. Here's what crypto escrow actually can't do, where the trade-offs bite, and how to decide whether it's worth paying for on a given deal.&lt;/p&gt;

&lt;h2&gt;
  
  
  It secures the payment, not the counterparty
&lt;/h2&gt;

&lt;p&gt;Escrow guarantees the money is handled by rules both sides agreed to. It says nothing about whether the person on the other end is honest, or whether what they deliver is any good. If a seller ships something that doesn't match the description, escrow can't turn it into what you wanted — what it can do is hold the funds so the disagreement goes to a dispute instead of the money simply being gone.&lt;/p&gt;

&lt;p&gt;That distinction matters before you start. Escrow closes the "who goes first" risk and the "middleman runs off with the money" risk. It does not vouch for quality, identity, or intent. Due diligence on the counterparty is still on you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crypto is final — there's no chargeback
&lt;/h2&gt;

&lt;p&gt;Once an escrow releases funds on-chain, that's the end of it. No bank to call, no card issuer to reverse the charge, no chargeback window. That finality is the feature: nobody can claw a payment back from you the way they can with a card. But it's also the hard limit — if you release too early, or you get a dispute ruling you disagree with, there is no undo.&lt;/p&gt;

&lt;p&gt;This is exactly why escrow holds funds until conditions are met instead of paying out directly: the protection is built in up front, not bolted on afterward. Confirm the other side actually delivered before you approve, because approval is a one-way door.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disputes take time, and they rule on the evidence
&lt;/h2&gt;

&lt;p&gt;A dispute is not instant. There's a window to submit evidence, the arbitrator reviews it, and only then does the contract carry out the result. For a small, time-sensitive deal, that wait can be frustrating — and anyone advertising an "instant" resolution is misrepresenting how arbitration works.&lt;/p&gt;

&lt;p&gt;Just as important: the arbitrator rules on the evidence each side submits, not on a physical inspection. A clear, well-documented case can win; a true-but-poorly-presented one can lose. Keep your records — the agreed terms, proof of delivery, the transaction hashes — because that is what a ruling is built on.&lt;/p&gt;

&lt;h2&gt;
  
  
  It costs money, and it isn't always worth it
&lt;/h2&gt;

&lt;p&gt;Escrow isn't free, and it isn't automatically the cheapest way to transact. There's a platform fee (on most non-custodial services, a small percentage of the deal taken once), network gas on top, and — if a dispute is raised — a separate arbitration fee. None of it is hidden, but it adds up, and it changes the math on small deals.&lt;/p&gt;

&lt;p&gt;The honest rule of thumb: escrow is worth it when the cost of being stiffed clearly exceeds the fee. On a $50 deal, the fee can outweigh the risk it covers. On a $500-plus deal with someone you don't know, it usually pays for itself. For a tiny amount, or a counterparty you already trust, escrow may just be friction you don't need.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistakes that cause most avoidable losses
&lt;/h2&gt;

&lt;p&gt;A few habits account for most of the losses escrow should have prevented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Releasing early.&lt;/strong&gt; "I'll release now as a gesture of good faith" is how people get burned. Approval is one-way; wait for delivery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drifting off the agreed flow.&lt;/strong&gt; If a counterparty pushes you into a side channel or changes the deal, change the escrow terms — don't improvise around them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vague terms.&lt;/strong&gt; "A logo" is a dispute waiting to happen; "three concepts, two revisions, final files in SVG and PNG" is something an arbitrator can actually rule on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is unique to crypto — it's the discipline any escrow demands. The difference on-chain is that the rules are fixed in code, so the clearer your terms and the better your records, the more the system behaves the way you expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it's actually worth it
&lt;/h2&gt;

&lt;p&gt;Escrow earns its fee when you're dealing with a stranger, when the handover is hard to reverse, or when there's no practical legal recourse if things go wrong — the situations where going first is a real risk. For everyday purchases from an established merchant with its own buyer protection, it's usually overkill.&lt;/p&gt;

&lt;p&gt;And if you do use one, the detail that matters most is who holds the money. A non-custodial design — where funds sit in an open-source smart contract that no company controls, and you can read the contract and the arbitrator on a block explorer before committing — is easier to trust than a service asking you to rely on an account you can't see. Escrow isn't a guarantee; it's a tool whose limits and rules you can verify for yourself up front. For how to tell a real escrow service from a fake one, &lt;a href="https://vaultion.org/blog/is-crypto-escrow-safe" rel="noopener noreferrer"&gt;this guide covers the red flags&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by the team at &lt;a href="https://vaultion.org" rel="noopener noreferrer"&gt;Vaultion&lt;/a&gt;, a non-custodial crypto escrow for stablecoin deals — funds lock in an open-source smart contract and release on agreement or by an independent dispute ruling.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>security</category>
    </item>
  </channel>
</rss>
