How to Accept USDT Payments on Your Website

Pick the right USDT networks, connect a gateway by plugin or API, verify webhooks and handle the edge cases that cause most support tickets.

2026/10/16阅读约 10 分钟
Website checkout window with a USDT QR code, network tabs and a paid confirmation for 120 USDT

The simplest way to accept USDT payments on your website is through a crypto payment gateway: your site creates an invoice, the customer pays on a hosted page that shows a unique address and QR code, and the gateway notifies your server by webhook once the payment is confirmed. You never have to match transfers by hand, and you can switch on several USDT networks at once.

You can also post a wallet address and check payments yourself, which works for a handful of orders but breaks quickly. This guide walks through both options, then sets up the gateway route step by step: choosing networks, connecting a plugin or the API, verifying webhooks and handling the cases where the customer sends too little, too much or too late.

Three ways to accept USDT on a website

MethodHow it worksGood forWeak spots
Static wallet addressYou show one address and ask for a screenshot or transaction hashOccasional manual salesYou can't tell which payment belongs to which order; no automation
Self-hosted processorYou run software that watches your own walletsTeams that want full custody and can run serversSetup and maintenance; stablecoin and network support depends on your setup
Payment gatewayEach order gets an invoice with its own deposit address; a webhook tells your site when it is paidMost online stores, SaaS and digital goodsYou pay a commission; funds sit on the gateway balance until you withdraw

The static-address method fails at the first busy day. Two customers send 25 USDT within a minute and you can't tell who paid for what. A gateway solves this by giving every invoice its own address, so a payment is matched to an order automatically.

Step 1. Decide which USDT networks to accept

USDT exists on many blockchains, and they are not interchangeable. A customer's wallet or exchange decides which network they can send on, and the fee they pay depends heavily on it. These are rough third-party estimates of what one USDT transfer costs the sender, as of October 2026. Check a block explorer for current numbers.

NetworkTypical sender costAddress looks like
TRON (TRC20)About 6.5 TRX burned, or far less with energy (fee model)Starts with T
BSC (BEP20)About $0.05–0.30Starts with 0x
Ethereum (ERC20)About $0.20–2 when quiet, much more at peaksStarts with 0x
TONAbout $0.005–0.05UQ... or EQ...
SolanaUnder $0.01Base58 string

A sensible default is TRC20 and BEP20, because many exchange users hold USDT there, plus ERC20 for larger payments from customers who keep funds on Ethereum. Add TON if your audience lives in Telegram and Solana if your customers already use it. A gateway lets you enable all of them without extra work, so the real question is what to show first on your payment page.

The networks per coin are listed on the USDT payments page, and the USDT TRC20 page covers the most common one in detail.

Step 2. Choose how you price

You have two options:

  • Price in USD (or another fiat currency). The invoice is for, say, $49, and the customer chooses USDT and a network on the payment page. This is the usual choice: your catalogue stays in dollars and customers who prefer BTC or USDC can still pay.

  • Price in USDT. The invoice is for exactly 49 USDT. Useful when you want to force a specific coin, for example for B2B invoices.

On Zypher, an invoice priced in a coin expires after 30 minutes. A fiat-priced invoice gives the customer 60 minutes to pick a coin, then 30 minutes to pay after choosing.

Step 3. Connect the gateway to your site

If you use a CMS

Check for a ready plugin. Zypher has plugins for WooCommerce, WHMCS, OpenCart, PrestaShop, 1C-Bitrix, XenForo, Invision Community, MyBB and phpBB. The flow is the same everywhere:

  1. Create a store in the gateway dashboard and copy the API key and secret.

  2. Install the plugin and paste the key and secret into its settings.

  3. Copy the webhook URL the plugin shows you into your store settings in the gateway.

  4. Place a real test order and pay it.

If you have a custom site

Use the API. With Zypher, POST /api/v1/invoice creates an invoice and GET /api/v1/invoice/:id reads it. Every request carries two headers:

  • X-API-Key with your store's API key

  • X-API-Sign with a hex HMAC-SHA256 of the JSON body, keyed with your API secret (for GET requests, of an empty string)

Here is the signing in plain curl. The invoice fields (amount, currency, your order ID and so on) are described in the API docs, and the base URL is listed there too.

# Create an invoice
BODY=$(cat invoice.json)   # invoice fields as described in the docs
SIGN=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed 's/^.* //')

curl -X POST "$API_BASE/api/v1/invoice" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -H "X-API-Sign: $SIGN" \
  --data "$BODY"

# Read an invoice: sign an empty string
SIGN=$(printf '' | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed 's/^.* //')
curl "$API_BASE/api/v1/invoice/$INVOICE_ID" \
  -H "X-API-Key: $API_KEY" -H "X-API-Sign: $SIGN"

Sign the exact string you send. If you build the JSON, sign it, and then let your HTTP client serialize the object again, the signature will not match.

Redirect the customer to the hosted payment page from the response. It shows the amount, network, QR code and a live countdown, and is available in 11 languages. You can set success and fail redirect URLs per store.

Step 4. Mark orders paid only from the webhook

The success redirect only tells you the customer clicked back to your site. The payment may still be confirming, or it may never arrive. Your source of truth is the webhook (or a status check through the API).

On Zypher, the webhook is a POST with a JSON body {event, data, timestamp}. The X-Webhook-Signature header is a hex HMAC-SHA256 of the raw body using your API secret, and X-Webhook-Event names the event. The event is invoice.paid, and data.status is either PAID or OVERPAID. If your endpoint doesn't respond successfully, delivery is retried, up to 5 attempts.

A Python (Flask) handler:

import hmac, hashlib
from flask import Flask, request, abort

app = Flask(__name__)
API_SECRET = b"your-api-secret"

@app.post("/webhooks/zypher")
def zypher_webhook():
    raw = request.get_data()  # raw bytes, before any JSON parsing
    expected = hmac.new(API_SECRET, raw, hashlib.sha256).hexdigest()
    given = request.headers.get("X-Webhook-Signature", "")
    if not hmac.compare_digest(expected, given):
        abort(401)

    payload = request.get_json()
    if payload["event"] == "invoice.paid" and payload["data"]["status"] in ("PAID", "OVERPAID"):
        mark_order_paid(payload["data"])  # find the order by the invoice fields from the docs
    return "", 200

Make the handler idempotent. Because of retries, the same event can arrive twice, so check whether the order is already paid before you ship again or send a second email.

Step 5. Plan for the edge cases

Most support tickets about USDT payments come from four situations. Decide your policy before launch.

Underpaid invoices

A customer sends 48.20 instead of 49 because their exchange took its withdrawal fee out of the amount. Zypher treats 98% or more of the amount as paid. Below that the invoice is UNDERPAID, and the customer can top up the remainder; partial payments add up.

Overpaid invoices

Anything above 102% is marked OVERPAID. The order is paid, and you decide whether to refund the difference, keep it as credit or ignore small amounts. Write the policy into your terms.

Late payments

A customer starts paying, gets distracted and sends after the invoice has expired. On Zypher, a payment that arrives up to 30 minutes after expiry can still be matched to the invoice. After that, it becomes a support case.

Wrong network

A customer sends USDT over BEP20 to a TRC20 address. Between EVM networks (ERC20, BEP20, Base, Arbitrum) the 0x address is the same, so whoever holds the key can usually recover the funds. A transfer between a TRON T address and a 0x address usually can't be recovered. The best fix is prevention: show the network name next to the address and make customers pick it explicitly.

Step 6. Decide what happens to the money

Once payments come in, you have choices:

  • Keep USDT on your gateway balance and withdraw when you need to. Withdrawals pay the network fee.

  • Auto-convert every incoming payment to one coin. On Zypher, a store can automatically swap each payment, net of commission, into a chosen coin such as USDT, which helps if you also accept BTC or ETH.

  • Withdraw to an exchange if you need to sell. Exchanges set their own deposit and withdrawal rules, so check them first.

Zypher's commission is taken from the received amount: 1% to start, falling to 0.25% as 30-day paid volume grows, with no setup or monthly fees. The full schedule is on the pricing page.

Launch checklist

  • At least two USDT networks enabled (for example TRC20 and BEP20)

  • Network name shown clearly on the payment page and in order emails

  • Webhook signature verified against the raw body

  • Orders marked paid only on invoice.paid with status PAID or OVERPAID

  • Handler is idempotent and responds fast

  • Policy written for underpaid, overpaid and late payments

  • Real test payment made on a cheap network, plus one expired invoice

  • Success and fail redirect URLs set

  • Refund process defined (crypto refunds go to an address the customer gives you)

There is no sandbox on Zypher, so the test step uses real money. A couple of dollars on TRC20 or BEP20 is enough.

A note on rules and taxes

Accepting stablecoins may have accounting, tax or licensing consequences depending on your country and business. This guide is general information, not legal or tax advice. Check local rules, or ask an accountant who knows crypto.

If you are moving from another provider, our guide to Coinbase Commerce alternatives covers the migration side. For store-specific setups see crypto payments for ecommerce. When you are ready, open a store and create your first invoice.

FAQ

Can I accept USDT on my website without a payment gateway?

Yes, by posting a wallet address and checking transfers yourself. It works for very low volume, but you can't reliably match payments to orders and nothing updates automatically. A gateway gives every invoice its own address and confirms payment by webhook.

Which USDT network should a website accept?

Most merchants start with TRC20 and BEP20 because many exchange users hold USDT there and fees are low. ERC20 is worth adding for larger payments. TON and Solana make sense if your customers already use those ecosystems.

How long does a USDT payment take to confirm?

It depends on the network. Zypher credits payments after a set number of confirmations, for example 10 on TRON, 5 on BSC, 6 on Ethereum, 8 on Solana and 1 on TON. In practice that takes from a few seconds to a few minutes.

What if the customer sends less USDT than the invoice?

On Zypher, a payment of 98% or more of the amount counts as paid. Below that, the invoice is marked underpaid and the customer can send the rest. Partial payments are added together.

Do I need a company to accept USDT payments?

Opening a Zypher store and getting API keys needs no documents. Whether you need a registered business, and how to report income, depends on your country. Check local rules before you start selling.