Overview
Accept cryptocurrency payments for borderless, instant transactions with no foreign exchange fees.
Supported Crypto USDT (Tether)
Why USDT?
USDT (Tether) is a stablecoin pegged 1:1 to USD:
✅ Stable value - no volatility like Bitcoin
✅ Low fees - especially on TRC20
✅ Fast transfers - minutes, not days
✅ Global - no currency conversion
✅ Privacy - no personal info needed
Supported Networks
TRC20 (Recommended)
ERC20
TRON Network (TRC20) Blockchain: TRONConfirmation Time: ~3 minutesTransaction Fee: ~$1-2 USDBlock Explorer: Tronscan Why Recommended:
⚡ Fastest confirmations
💰 Lowest fees
📈 Most popular in LATAM
Address Format: Starts with T...Example: TXYZabc123... Ethereum Network (ERC20) Blockchain: EthereumConfirmation Time: ~10-20 minutesTransaction Fee: $5-50 USD (varies with gas)Block Explorer: Etherscan Trade-offs:
🐌 Slower confirmations
💸 Higher fees
🏦 More exchange support
Address Format: Starts with 0x...Example: 0x742d35Cc6634C...
Recommendation: Use TRC20 for better user experience and lower fees.
How It Works
Create Crypto Payment
curl -X POST "https://api-sandbox.88pay.io/api/transactions/charges" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "x-session-id: YOUR_SESSION" \
-H "Content-Type: application/json" \
-d '{
"flow": "PAYIN",
"method_code": "USDT",
"method_category": "CRYPTO",
"amount": 50,
"currency": "USD",
"country": "COL",
"description": "Order #12345",
"customer_id": "user_001",
"notification_url": "https://yoursite.com/webhook",
"return_url": "https://yoursite.com/success"
}'
Important: Amount should be in USDT (USD), not local currency. If your product costs 50,000 COP, convert to USD first: amount: 12.50 (approximately)
Response
{
"status" : "Success" ,
"code" : 200 ,
"data" : {
"reference" : "IP9BD7CJES6" ,
"deposit_address" : "TXYZabc123def456ghi789jkl012mno345pqr" ,
"network" : "TRC20" ,
"amount" : 50 ,
"currency" : "USD" ,
"qr_code_url" : "https://qr.88pay.io/ABC123.png"
}
}
Customer Experience
Receive Address
Customer sees deposit address and QR code
Open Wallet
Opens their crypto wallet (Binance, Trust Wallet, etc.)
Send USDT
Sends exact USDT amount to address Critical: Must send exact amount
Wait for Confirmations
Transaction appears in ~30 seconds Confirmed after 3-6 blocks (~3-10 minutes)
Payment Complete
Webhook sent when confirmed Customer can return to site
Displaying Crypto Payment
Example UI
function CryptoPayment ({ data }) {
const [ status , setStatus ] = useState ( 'pending' );
// Poll for payment status
useEffect (() => {
const interval = setInterval ( async () => {
const status = await checkPaymentStatus ( data . reference );
if ( status === 'COMPLETED' ) {
setStatus ( 'completed' );
clearInterval ( interval );
}
}, 10000 ); // Check every 10 seconds
return () => clearInterval ( interval );
}, []);
return (
< div className = "crypto-payment" >
< h2 > Send USDT Payment </ h2 >
< div className = "amount-box" >
< label > Amount: </ label >
< div className = "amount" >
{ data . amount } USDT
</ div >
< p className = "warning" > ⚠️ Send EXACT amount </ p >
</ div >
< div className = "network-badge" >
Network: { data . network }
</ div >
< div className = "qr-section" >
< img src = { data . qr_code_url } alt = "QR Code" />
< p > Scan with your wallet app </ p >
</ div >
< div className = "divider" > OR </ div >
< div className = "address-section" >
< label > Deposit Address: </ label >
< div className = "address" >
< code > { data . deposit_address } </ code >
< button onClick = { () => copy ( data . deposit_address ) } >
Copy Address
</ button >
</ div >
</ div >
< div className = "instructions" >
< h3 > How to pay: </ h3 >
< ol >
< li > Open your crypto wallet </ li >
< li > Select "Send" or "Transfer" </ li >
< li > Choose USDT on { data . network } network </ li >
< li > Paste address or scan QR code </ li >
< li > Enter amount: < strong > { data . amount } USDT </ strong ></ li >
< li > Confirm and send </ li >
</ ol >
</ div >
< div className = "status" >
{ status === 'pending' && (
< div className = "pending" >
< Spinner />
< p > Waiting for payment... </ p >
< p className = "small" > This usually takes 3-10 minutes </ p >
</ div >
) }
{ status === 'completed' && (
< div className = "completed" >
< CheckIcon />
< p > Payment confirmed! </ p >
</ div >
) }
</ div >
< div className = "warnings" >
< p > ⚠️ Only send USDT on { data . network } network </ p >
< p > ⚠️ Sending other coins or wrong network will result in loss of funds </ p >
< p > ⚠️ Send exactly { data . amount } USDT </ p >
</ div >
</ div >
);
}
Important Warnings
Critical Information for Customers: ✅ Only send USDT - sending BTC, ETH, or other coins will result in permanent loss ✅ Correct network - TRC20 address only accepts TRC20 USDT (not ERC20 or other networks) ✅ Exact amount - send precisely the amount shown (e.g., 50.00 USDT, not 49.99 or 50.01) ❌ No refunds for wrong coin, wrong network, or wrong amount
Transaction Limits
Network Min Max Fee TRC20 10 USDT 100,000 USDT ~1-2 USDT ERC20 10 USDT 100,000 USDT ~5-50 USDT (varies)
Confirmation Times
Network Appears In Confirmations Needed Total Time TRC20 30 seconds 19 blocks ~3-5 minutes ERC20 30 seconds 12 blocks ~10-20 minutes
Webhook Notifications
PENDING
PROCESSING
COMPLETED
REJECTED
{
"transaction_status" : "PENDING" ,
"transaction_reference" : "IP9BD7CJES6" ,
"transaction_payment_method" : "USDT_TRC20"
}
Meaning: Payment created, awaiting transfer {
"transaction_status" : "PROCESSING" ,
"transaction_reference" : "IP9BD7CJES6" ,
"transaction_payment_method" : "USDT_TRC20" ,
"confirmations" : 5 ,
"confirmations_required" : 19
}
Meaning: Transfer detected, waiting for confirmations {
"transaction_status" : "COMPLETED" ,
"transaction_reference" : "IP9BD7CJES6" ,
"transaction_amount" : "50" ,
"transaction_payment_method" : "USDT_TRC20" ,
"transaction_hash" : "abc123def456..."
}
Meaning: Payment confirmed on blockchainAction: Fulfill order {
"transaction_status" : "REJECTED" ,
"transaction_reference" : "IP9BD7CJES6" ,
"transaction_payment_method" : "USDT_TRC20"
}
Reasons:
Payment timeout (2 hours)
Wrong amount sent
Wrong network used
Best Practices
Poll for transaction status while customer waits // Check status every 10 seconds
const checkStatus = setInterval ( async () => {
const status = await getTransactionStatus ( reference );
if ( status . transaction_status === 'PROCESSING' ) {
updateUI ( `Confirmations: ${ status . confirmations } / ${ status . confirmations_required } ` );
}
if ( status . transaction_status === 'COMPLETED' ) {
clearInterval ( checkStatus );
redirectToSuccess ();
}
}, 10000 );
Make it impossible to miss which network to use < div className = "network-warning" >
< AlertIcon />
< h3 > IMPORTANT: Use { network } Network Only </ h3 >
< p > Sending on other networks will result in loss of funds </ p >
</ div >
Provide Block Explorer Link
Let customers verify their transaction { transactionHash && (
< a
href = { `https://tronscan.org/#/transaction/ ${ transactionHash } ` }
target = "_blank"
>
View on Tronscan
</ a >
)}
What if customer sends 49.50 instead of 50? // Webhook handler
if ( req . body . transaction_status === 'REJECTED'
&& req . body . reason === 'partial_payment' ) {
const sent = req . body . amount_received ;
const required = req . body . amount_required ;
const remaining = required - sent ;
// Offer to complete payment
await sendEmail ({
to: customer . email ,
subject: 'Complete Your Payment' ,
message: `You sent ${ sent } USDT. Please send ${ remaining } USDT more to complete.`
});
}
Testing
Sandbox Behavior
Deposit address provided instantly
Auto-completes after 3 minutes
Webhook sent automatically
No real crypto needed
// Test flow
const payment = await createCryptoPayment ({
amount: 50 ,
currency: "USD"
});
console . log ( 'Address:' , payment . deposit_address );
console . log ( 'Network:' , payment . network );
// Wait 3 minutes
await sleep ( 180000 );
// Webhook arrives with COMPLETED status
Common Issues
Customer sent wrong amount
Problem: Sent 49 USDT instead of 50Solutions:
Accept as partial payment
Request remaining balance
Offer refund (minus fees)
Prevention:
Make amount extremely clear
Show exactly what to enter in wallet
Provide copy button for amount
Problem: Sent ERC20 to TRC20 address (or vice versa)Result: Funds lost (unrecoverable)Prevention:
Show BIG warning about network
Color-code network selection
Confirm network in UI multiple times
Show example of correct wallet selection
Transaction not appearing
Possible causes:
Still processing in blockchain
Sent to wrong address
Low gas fee (ERC20)
Action:
Wait 20 minutes
Check block explorer with transaction hash
Verify address matches exactly
Customer doesn't have USDT
Solution: Provide instructions to buy < div className = "help-section" >
< h3 > Don't have USDT? </ h3 >
< p > You can buy USDT on: </ p >
< ul >
< li >< a href = "https://binance.com" > Binance </ a ></ li >
< li >< a href = "https://coinbase.com" > Coinbase </ a ></ li >
< li > Local exchange in your country </ li >
</ ul >
</ div >
Next Steps