USDT Invoice
Self-hosted crypto payment gateway for USDT (TRC20) and TRX. Customers pay straight into your wallet. No middleman, no fees, and no private key ever touches your server.
- Merchant dashboard: invoices, on-chain payments, API keys, settings.
- Hosted payment page with QR code that updates itself and returns the customer to your store.
- Two wallet modes: Simple (one address) and Advanced (a fresh address per invoice from your xpub).
- REST API + signed webhooks (HMAC-SHA256) with automatic retries.
- Ready-made WooCommerce payment method (works with the new block checkout).
- Live pricing: USD, EUR, GBP and 150+ fiat currencies.
How it works
- Your store (or the dashboard) creates an invoice: an amount in fiat, converted to USDT or TRX at the live rate.
- The customer opens the payment page, scans the QR with any TRON wallet (Trust Wallet, TronLink, Binance withdrawal, etc.) and sends the exact amount.
- USDT Invoice watches the blockchain for that address and amount. As soon as the transfer appears the page shows paid, and once it has the required confirmations it becomes confirmed.
- Your store receives a signed webhook and marks the order as paid. The customer is sent back to your thank-you page.
Requirements
| Item | Requirement |
|---|---|
| PHP | 8.0 or newer |
| Database | MySQL 5.7+ / MariaDB 10.3+ (PDO MySQL extension) |
| PHP extensions | curl, json, pdo_mysql, bcmath or gmp, sodium (all standard on cPanel hosts). gmp is required only for Advanced (xpub) mode. |
| HTTPS | Strongly recommended (a free Let's Encrypt certificate is fine). |
| Cron | Ability to run a cron job every 5 minutes, or an external cron service (free ones exist). |
| TronGrid key | Free at trongrid.io. Optional but recommended (higher rate limits). |
The installer checks all of this for you before it starts.
Installation
1. Create a database
In cPanel (or your host's panel) create a MySQL database and a user with all privileges on it. Keep the name, user and password handy.
2. Upload the files
Upload the contents of the usdtinvoice/ folder from the package to the location you want, e.g. public_html/pay/ (so the gateway lives at https://yourdomain.com/pay/) or to a sub-domain such as pay.yourdomain.com.
3. Enter the database details
Open inc/db.php and fill in the four database lines near the top. This is the only file you ever edit by hand.
define('CG_DB_HOST', 'localhost');
define('CG_DB_NAME', 'your_db_name');
define('CG_DB_USER', 'your_db_user');
define('CG_DB_PASS', 'your_db_password');
4. Run the installer
- Open
https://yourdomain.com/pay/install.phpin your browser. - Ownership check. The installer writes a file
inc/install.key. Open it in your file manager, copy the key and paste it into the installer. This proves you own the server, so nobody else can run the installer on your site. - Requirements. All rows must be green.
- Purchase code. Paste the purchase code from your order. It is bound to this domain (see License & domains).
- Admin account. Choose a username and a password (8+ characters).
- Where payments go. Pick Simple and paste your TRON address (starts with
T), or Advanced and paste your xpub. You can change this later in Settings. - Click Install USDT Invoice. The installer creates the tables, then locks itself (it renames to
install.php.locked, which you may delete).
5. Set up the cron job
See the next section. Without the cron, payments are still detected while a customer has the payment page open, but webhooks, expiry and retries need the scanner.
Payment scanner (cron)
The scanner runs every 5 minutes: it expires old invoices, checks the blockchain for open ones, matches transfers, and delivers webhooks (with retries). Set it up in one of two ways.
Option A - server cron (cPanel → Cron Jobs)
*/5 * * * * /usr/local/bin/php /home/USER/public_html/pay/cron/scan.php >/dev/null 2>&1
The exact PHP path and folder are shown on the dashboard's Settings → Payment scanner card - copy them from there.
Option B - URL cron (any external cron service)
https://yourdomain.com/pay/cron/scan.php?key=YOUR_CRON_SECRET
The secret is shown in Settings. Use Rotate secret if it ever leaks. The top bar of the dashboard shows Scanner active (green) when the cron is running normally, idle (amber) after 3 minutes without a run and stopped (red) after 15.
Receiving wallet modes
| Simple | Advanced (xpub) | |
|---|---|---|
| What you enter | One TRON address | Extended public key (xpub) |
| How invoices are told apart | Each open invoice gets a slightly different amount (e.g. 25.0031 USDT) | Each invoice gets its own fresh address; amount is exact |
| Best for | Most merchants, fastest setup | High volume, or when customers must pay round amounts |
| Needs | Nothing extra | PHP gmp extension + a wallet that exports an xpub |
Getting your xpub
Export the account-level extended public key for TRON (derivation path m/44'/195'/0') from your wallet or from a tool that supports it. Paste it in Settings. USDT Invoice immediately shows "First address derived from this key (#0)" - it must match the first receiving address in your wallet. If it does not, you pasted the wrong key.
Merchant dashboard
Sign in at https://yourdomain.com/pay/admin/.
- Dashboard - totals for today / 7 days / 30 days, recent invoices, one-click open of any payment page.
- Invoices - search and filter, create an invoice manually (send the link to a customer), see on-chain payments, Check blockchain now, cancel, or accept an underpaid invoice.
- Payments - every on-chain transfer that was matched, with a Tronscan link.
- API keys - create keys for your store, see the webhook signing secret and a quick-start snippet.
- Settings - wallet, invoice rules, branding, cron, password.
Every destructive action (cancel, delete, accept underpaid) asks for confirmation in a dialog that matches the dashboard - no browser pop-ups.
Invoices & statuses
| Status | Meaning | What you should do |
|---|---|---|
pending | Waiting for payment | Nothing |
paid | Transfer seen on chain, waiting for confirmations | Put the order on hold |
confirmed | Final - funds are in your wallet | Deliver the order |
underpaid | Customer sent less than required (beyond the tolerance) | Ask the customer to send the rest, or accept it from the dashboard |
expired | Lifetime passed with nothing received | Cancel the order |
cancelled | Cancelled from the dashboard | - |
A payment that arrives after an invoice expired is still matched (the scanner looks 15 minutes before creation and until expiry); the invoice moves to paid/confirmed as usual.
Settings reference
| Setting | Default | Notes |
|---|---|---|
| Invoice lifetime | 30 min | 5-1440. The payment page shows a countdown. |
| Confirmations required | 1 | 1 is safe on TRON for most stores; raise it for high-value goods. |
| Underpayment tolerance | 0.02 (2%) | Covers wallet rounding. An invoice paid within tolerance is treated as fully paid. |
| Default currency | USDT | USDT (TRC20) or TRX. Can be overridden per invoice. |
| Fiat currency | USD | Any 3-letter code (EUR, GBP, AED, ...). Rates refresh every 10 minutes. |
| Brand name | USDT Invoice | Shown on the payment page and dashboard. |
| TronGrid API key | - | Free. Raises the blockchain query limit a lot. |
Forgot password
There is deliberately no email reset. On the sign-in page click Forgot your password?, create the file inc/reset.key on your server with the one-time code shown, then set a new password. Only someone with access to the server files can do this; the file is deleted automatically afterwards.
WooCommerce plugin
Install
- In WordPress go to Plugins → Add New → Upload Plugin and upload
usdtinvoice-woocommerce.zipfrom the package (or copy theusdtinvoice-woocommercefolder towp-content/plugins/). - Click Activate.
- In USDT Invoice's dashboard open API keys, create a key named e.g. "My store", and copy the API key, the API secret (shown once) and the Webhook signing secret.
- In WordPress go to WooCommerce → Settings → Payments → USDT Invoice, tick Enable, paste the gateway URL (e.g.
https://yourdomain.com/pay, no trailing slash) and the three values, choose USDT or TRX, and Save.
What it does
- Adds "Pay with crypto (USDT)" to checkout (classic and block-based).
- Creates the invoice through the API and redirects the customer to the payment page.
- Receives the webhook at
https://yourstore.com/?wc-api=usdtinvoice, verifies the signature and updates the order:paid→ On hold,confirmed→ Processing (payment complete),underpaid→ On hold with a note,expired/cancelled→ Cancelled. - Compatible with HPOS (High-Performance Order Storage).
Tick Debug log to write every request and webhook to WooCommerce → Status → Logs (source: usdtinvoice) while you test.
Free WordPress plugin
A free, limited version is published on WordPress.org as USDT Invoice - Crypto Payments for USDT. It lets a WooCommerce store accept USDT (TRC20) without buying the full script, so you can try the checkout on your own store first.
USDT only. No merchant dashboard and no API. Installs from your WordPress admin like any other plugin.
https://wordpress.org/plugins/khaled-hisham-crypto-payments-usdt/
Open the plugin page
USDT and TRX, merchant dashboard, REST API, signed webhooks, xpub mode and the WooCommerce plugin described above. One-time price, full source code.
https://usdtinvoice.org/buy/
Buy direct, 89 USDT
Install the free plugin
- In WordPress go to Plugins → Add New and search for USDT Invoice, or download the zip from the plugin page above and use Upload Plugin.
- Click Install Now, then Activate.
- Follow the plugin's own settings screen to add your TRON receiving address.
The free plugin and the full script's WooCommerce plugin are separate. When you move to the full version, deactivate the free plugin before activating usdtinvoice-woocommerce so checkout shows one crypto payment method.
API reference
Base URL: your USDT Invoice location, e.g. https://yourdomain.com/pay. All responses are JSON.
Authentication
Send your key and secret as headers on private endpoints:
X-CG-Key: cg_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-CG-Secret: your_api_secret
Rate limits: 60 requests/minute per key on create_invoice, 120 requests/minute per IP on invoice_status.
POST /api/create_invoice.php
| Field | Type | Required | Description |
|---|---|---|---|
amount | string/number | yes | Amount in fiat, up to 2 decimals, e.g. "49.99" |
fiat_currency | string | no | 3-letter code. Default: your setting (USD). |
currency | string | no | USDT or TRX. Default: your setting. |
order_id | string | no | Your reference, shown on the payment page and returned in webhooks. |
customer_email | string | no | Stored with the invoice. |
redirect_url | string | no | Where to send the customer after payment. |
callback_url | string | no | Your webhook endpoint (HTTPS recommended). |
meta | object | no | Anything you want back in the webhook (e.g. your internal order id). |
Response 200:
{
"ok": true,
"invoice": {
"id": "inv_8db57e14a74d65a960e6",
"order_id": "1042",
"status": "pending",
"currency": "USDT",
"network": "TRC20",
"amount": "49.9931",
"amount_paid": "0",
"amount_fiat": "49.99",
"fiat_currency": "USD",
"address": "TXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"confirmations": 0,
"expires_at": "2026-09-11T12:39:00Z",
"paid_at": null,
"created_at": "2026-09-11T12:09:00Z",
"checkout_url": "https://yourdomain.com/pay/checkout.php?id=inv_8db57e14a74d65a960e6",
"redirect_url": "https://yourstore.com/thank-you"
}
}
Send the customer to checkout_url. Errors return {"ok": false, "error": "..."} with HTTP 400/401/429.
GET /api/invoice_status.php?id=inv_...
Public (no auth): the invoice id is a 20-character random token that only the customer holds. Returns the same invoice object. The payment page polls this every 8 seconds; you can too.
Webhooks
When an invoice changes status USDT Invoice POSTs a JSON body to your callback_url. The body is the invoice object plus:
{ ..., "event": "confirmed", "sent_at": "2026-09-11T12:15:02+00:00", "meta": { "wc_order_id": 14 } }
| Header | Value |
|---|---|
X-USDTInvoice-Event | paid, confirmed, underpaid, expired or cancelled |
X-USDTInvoice-Signature | HMAC-SHA256 (hex) of the raw request body with your webhook signing secret |
Reply with any 2xx status. Anything else is retried after 1 min, 5 min, 15 min, 1 h, 6 h and 24 h. Webhooks are delivered instantly when the customer's payment page detects the change, and by the cron otherwise.
confirmed. Never trust a "paid" URL parameter or the redirect alone - those can be faked, the signed webhook cannot.Code examples
Create an invoice (PHP)
$payload = [
'amount' => '49.99',
'fiat_currency' => 'USD',
'currency' => 'USDT',
'order_id' => '1042',
'redirect_url' => 'https://yourstore.com/thank-you',
'callback_url' => 'https://yourstore.com/usdtinvoice-webhook.php',
'meta' => ['internal_id' => 555],
];
$ch = curl_init('https://yourdomain.com/pay/api/create_invoice.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-CG-Key: cg_xxxxxxxx',
'X-CG-Secret: your_secret',
],
]);
$res = json_decode(curl_exec($ch), true);
if (!empty($res['ok'])) {
header('Location: ' . $res['invoice']['checkout_url']);
exit;
}
Receive a webhook (PHP)
$secret = 'your_webhook_signing_secret';
$body = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_USDTINVOICE_SIGNATURE'] ?? '';
if (!hash_equals(hash_hmac('sha256', $body, $secret), $sig)) {
http_response_code(401);
exit('bad signature');
}
$inv = json_decode($body, true);
if ($inv['event'] === 'confirmed') {
// mark order $inv['order_id'] as paid - $inv['amount_paid'] $inv['currency'] received
}
http_response_code(200);
Create an invoice (cURL)
curl -X POST https://yourdomain.com/pay/api/create_invoice.php \
-H "Content-Type: application/json" \
-H "X-CG-Key: cg_xxxxxxxx" -H "X-CG-Secret: your_secret" \
-d '{"amount":"49.99","currency":"USDT","order_id":"1042"}'
License & domains
- One purchase code activates one domain. Sub-domains count as different domains.
localhost,127.0.0.1,*.testand*.localare always allowed for development.- Moving to a new domain? Contact support with your purchase code and the old domain will be released.
- The license is verified offline with a signed token; a quiet re-check runs every 7 days. If the license server is unreachable your site keeps working for 30 days, so it can never block your business because of an outage.
Security notes
- No private keys, seed phrases or exchange API keys are ever requested or stored.
- Admin sign-in is throttled (8 attempts / 15 minutes), sessions expire after 30 minutes of inactivity, all forms carry CSRF tokens.
- The
inc/folder is protected by.htaccess; the installer locks itself after use. - API secrets are shown once and stored hashed; webhooks are signed.
- Keep PHP and your host updated, use HTTPS, and keep a backup of your database.
FAQ & troubleshooting
The dashboard shows "Scanner stopped"
The cron job is not running. Re-check the command in cPanel → Cron Jobs (copy it from Settings) or use the URL cron with an external service.
The customer paid but the invoice is still pending
Open the invoice and click Check blockchain now. If the amount differs from what was requested (Simple mode) the transfer cannot be matched automatically; check Payments for an unmatched transfer and use Accept underpaid if appropriate. Adding a TronGrid key also makes detection more reliable.
"Exchange rate unavailable"
The rate providers could not be reached. Check that the server can make outgoing HTTPS requests (some hosts block them - ask support to allow api.coingecko.com and open.er-api.com).
WooCommerce shows no payment methods
Make sure the gateway is enabled and that URL, key and secret are filled in; the method hides itself when they are empty. Enable Debug log and look at WooCommerce → Status → Logs.
"Advanced mode needs the gmp extension"
Enable gmp in cPanel → Select PHP Version → Extensions, or use Simple mode.
Is there a free version?
Yes. The free WordPress plugin on WordPress.org accepts USDT on WooCommerce stores. It has no dashboard, no API and no TRX; those come with the full script.
I lost my API secret
Secrets are shown once. Create a new key from API keys and disable the old one.
Changelog
1.0.0 - Initial release.
Updates & support
Where you get updates and help depends on where you bought your copy.
For updates, support and new versions, visit the official product page on Codester:
https://www.codester.com/items/70692/usdt-invoice-usdt-trc20-trx-payment-gateway?ref=usdtinvoice
Open the Codester item page
Your purchase code and download link are in your order email. For help or a domain change, write to support with your purchase code.
support@usdtinvoice.org
Email support
Thank you for choosing USDT Invoice. Available on Codester and at usdtinvoice.org. Free version on WordPress.org.