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.
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.
So here is a different question to design around: what is the minimum a store needs to know, and where should "this order is paid" actually come from?
The store only needs an address
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.
That pushes all the trust into one place: deciding when an order is paid.
Things that must never mark an order paid
// 1. The thank-you redirect
add_action( 'woocommerce_thankyou', function ( $order_id ) {
wc_get_order( $order_id )->payment_complete(); // anyone can load this URL
} );
// 2. A transaction hash the buyer pastes in
if ( looks_like_tx_hash( $_POST['tx'] ) ) {
$order->payment_complete(); // a real hash, for a different payment
}
// 3. An unsigned or unverified callback
$order->payment_complete(); // because a POST arrived
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.
The only thing that should: a confirmed receipt
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:
- The transaction succeeded and has the confirmations you require for that chain.
- It is on the expected chain. The same address exists on Ethereum, Base and Arbitrum. A USDC transfer on the wrong one is not your payment.
- The right token moved. Check the token contract address, not the symbol. Anyone can deploy a token called "USDC".
- The right recipient got the right amount or more, read from the transfer itself, not from what the transaction claims it did.
- It is bound to this order. One transaction must not be able to pay two orders, so the payment has to carry something committed to the order it is for.
- It hasn't already been counted.
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.
Taking a fee without taking custody
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.
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.
Bitcoin and Litecoin are harder. A transaction can have several outputs, but a standard bitcoin: 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".
Where it leaves the buyer
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.
What we built
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 crypto payments for WooCommerce page.
Disclosure: I work on Vaultion. 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.
Top comments (0)