O OneiPay

Developer documentation

Accept USDT and USDC straight into your own wallets. You create an invoice through the API, send the customer to the hosted checkout, and receive a signed webhook as soon as the payment is confirmed on-chain or on Binance/Bybit.

Quickstart

  1. Create an account and add your receiving wallets (Dashboard → Wallets).
  2. Add and verify your website (Dashboard → Websites) with a DNS TXT record or a file.
  3. Create a test API key for that website and build your integration in the sandbox.
  4. Switch to a live key when everything works.

Authentication

Send your secret key as a Bearer token. Keys start with opk_live_ (real payments) or opk_test_ (sandbox). Each key belongs to one verified website: callback_url, success_url and cancel_url must be on that domain. Never put a key in browser or mobile code.

Authorization: Bearer opk_test_xxxxxxxxxxxxxxxx

Sandbox

Test keys create invoices with "livemode": false. They never match real transfers, do not count toward your plan limits and are hidden from your revenue statistics. Mark a test invoice as paid with the simulate endpoint (or the button on its checkout page) to receive a real signed invoice.paid webhook.

API reference

POST/api/v1/invoices

Creates an invoice. Send an Idempotency-Key header to make retries safe.

FieldTypeNotes
amountstringRequired. USD amount, max 2 decimals, e.g. "25.00"
networkstringOptional. One of the network keys. Without it the customer chooses.
external_order_idstringYour order number (max 120)
callback_urlhttps URLWebhook target on your website
success_url, cancel_urlhttps URLWhere the customer returns
customer_name, customer_emailstringOptional
ttl_minutesinteger5 – 1440, default 30
metaobjectUp to 20 keys, returned in webhooks
curl -X POST https://oneipay.com/api/v1/invoices \
  -H "Authorization: Bearer opk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001" \
  -d '{"amount":"25.00","external_order_id":"1001","network":"trc20",
       "callback_url":"https://shop.example/oneipay/webhook",
       "success_url":"https://shop.example/thank-you"}'

Response 201:

{
  "data": {
    "id": "inv_8Kq2...", "livemode": false, "status": "pending",
    "currency": "USDT", "amount": "25.00", "payable_amount": "25.000137",
    "network": "trc20", "address": "T...", "external_order_id": "1001",
    "tx_id": null, "confirmations": null,
    "expires_at": "2026-11-09T10:30:00+00:00", "paid_at": null,
    "checkout_url": "https://oneipay.com/pay/inv_8Kq2..."
  }
}

The customer must send exactly payable_amount. The small unique cents identify the payment, so no memo is needed on-chain.

GET/api/v1/invoices/{id}

Returns the same object. Status values: pending, paid, expired, cancelled, refunded. Always re-check an invoice with this call before you deliver goods.

POST/api/v1/invoices/{id}/simulate-payment

Test keys only. Marks a pending test invoice as paid and sends the webhook.

Webhooks

We POST JSON to your callback_url. Events: invoice.paid, invoice.expired, invoice.refunded. Answer with HTTP 2xx; failed deliveries are retried with back-off and can be re-sent from Dashboard → Webhooks.

{
  "id": "evt_inv_8Kq2..._invoice.paid",
  "event": "invoice.paid",
  "created_at": "2026-11-09T10:12:41+00:00",
  "data": { "id": "inv_8Kq2...", "livemode": true, "status": "paid", "amount": "25.00", "...": "...", "meta": {} }
}

Verify the signature

Header X-OneiPay-Signature: t=<unix time>,v1=<hex>, where v1 = HMAC_SHA256(webhook_secret, t + "." + raw_body). Reject requests older than 5 minutes and compare in constant time.

// PHP
[$t, $v1] = [null, null];
foreach (explode(',', $_SERVER['HTTP_X_ONEIPAY_SIGNATURE'] ?? '') as $p) {
    [$k, $v] = array_pad(explode('=', $p, 2), 2, '');
    if ($k === 't') $t = (int) $v; elseif ($k === 'v1') $v1 = $v;
}
$body = file_get_contents('php://input');
$ok = $t && $v1 && abs(time() - $t) <= 300
   && hash_equals(hash_hmac('sha256', $t . '.' . $body, $secret), $v1);
// Node.js
const crypto = require('crypto');
function verify(header, rawBody, secret) {
  const p = Object.fromEntries(header.split(',').map(s => s.split('=')));
  if (!p.t || !p.v1 || Math.abs(Date.now() / 1000 - p.t) > 300) return false;
  const mac = crypto.createHmac('sha256', secret).update(p.t + '.' + rawBody).digest('hex');
  return mac.length === p.v1.length && crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(p.v1));
}
# Python
import hmac, hashlib, time
def verify(header, raw_body: bytes, secret: str) -> bool:
    p = dict(x.split('=', 1) for x in header.split(','))
    if 't' not in p or 'v1' not in p or abs(time.time() - int(p['t'])) > 300:
        return False
    mac = hmac.new(secret.encode(), p['t'].encode() + b'.' + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, p['v1'])

Errors

HTTPerrorMeaning
401—Missing, wrong or revoked API key
402plan_restrictionYour plan does not allow this (limit reached, network not included, plan expired)
403test_key_requiredSimulation needs a test key
404—Invoice not found for this key's mode
422url_not_allowed, customer_blocked, validationFix the request; errors lists invalid fields
429—Too many requests – slow down

Networks

KeyNameToken
trc20USDT (TRC20 · Tron)USDT
bep20USDT (BEP20 · BNB Smart Chain)USDT
erc20USDT (ERC20 · Ethereum)USDT
arbitrumUSDT (Arbitrum One)USDT
optimismUSDT (Optimism)USDT
polygonUSDT (Polygon PoS)USDT
usdc_erc20USDC (Ethereum)USDC
usdc_bep20USDC (BNB Smart Chain)USDC
usdc_polygonUSDC (Polygon PoS)USDC
usdc_arbitrumUSDC (Arbitrum One)USDC
usdc_optimismUSDC (Optimism)USDC
usdc_baseUSDC (Base)USDC
usdt_tonUSDT (TON)USDT
usdt_solUSDT (Solana)USDT
usdc_solUSDC (Solana)USDC
btcBitcoin (BTC)BTC
ltcLitecoin (LTC)LTC
ethEthereum (ETH)ETH
bnbBNB (BNB Smart Chain)BNB
trxTRON (TRX)TRX
solSolana (SOL)SOL
okx_depositUSDT → OKX (deposit)USDT
bitget_depositUSDT → Bitget (deposit)USDT
kucoin_depositUSDT → KuCoin (deposit)USDT
payeerPayeer (USD)USD
perfectmoneyPerfect Money (USD)USD
binance_depositUSDT → Binance (deposit)USDT
bybit_payBybit Pay (USDT to UID)USDT
bybit_depositUSDT → Bybit (deposit)USDT
binance_payBinance Pay (USDT)USDT

Plugins & SDKs

Ready-made modules use the same API and verify every webhook twice (signature + API re-check).

// PHP SDK
require 'OneiPay.php';
$api = new OneiPay('https://oneipay.com', 'opk_test_YOUR_KEY', 'whsec_YOUR_SECRET');
$inv = $api->createInvoice(['amount' => '25.00', 'external_order_id' => '1001']);
header('Location: ' . $inv['checkout_url']);

// webhook endpoint – throws on a bad signature
$event = $api->verifyWebhook(file_get_contents('php://input'), $_SERVER['HTTP_X_ONEIPAY_SIGNATURE'] ?? '');
// Node.js SDK
const OneiPay = require('./oneipay');
const api = new OneiPay({ baseUrl: 'https://oneipay.com', apiKey: 'opk_test_YOUR_KEY', webhookSecret: 'whsec_YOUR_SECRET' });
const inv = await api.createInvoice({ amount: '25.00', external_order_id: '1001' });