Authentication
Every request from your server uses HTTP Basic auth: your project ID as the user name and your project secret as the password. Keep the secret on your server; it also signs your webhooks.
https://payments.rubysoft.io/api
Authorization: Basic base64(PROJECT_ID:PROJECT_SECRET)Create an Invoice
Use an invoice when the customer pays for one order. Pass your own 24-character order ID as payload, the price in USD, how far an amount may fall short (0 to 5%), the expiry time and the URL that receives this invoice's webhooks.
payloadYour order ID, 24 hexadecimal characters (a MongoDB ObjectId works)meta[price]Price in USDmeta[tolerancePercent]Allowed shortfall, 0 to 5meta[expiresAt]Expiry, ISO 8601, in the futuremeta[webhookUrl]Where this invoice's events are sentcurl -G https://payments.rubysoft.io/api/invoice/load \
-u "$PROJECT_ID:$PROJECT_SECRET" \
--data-urlencode "payload=65f2c1a9e4b0d3f7a2c91e04" \
--data-urlencode "meta[price]=120" \
--data-urlencode "meta[tolerancePercent]=1" \
--data-urlencode "meta[expiresAt]=2026-12-01T12:00:00Z" \
--data-urlencode "meta[webhookUrl]=https://shop.example/webhooks/rubynex"The response carries an addresses object keyed by network. Show your customer the networks you want to offer.
{
"id": "0b6f3c1e-8a52-4d7e-9c41-2f6a8d0e5b17",
"payload": "65f2c1a9e4b0d3f7a2c91e04",
"meta": {
"type": "invoice",
"status": "pending",
"price": "120",
"paid": "0",
"expiresAt": "2026-12-01T12:00:00.000Z"
},
"addresses": {
"tron": "T…",
"ethereum": "0x…",
"bsc": "0x…",
"bitcoin": "bc1q…"
}
}Permanent Deposit Addresses
Use permanent addresses for top-ups: wallets, games, exchanges, subscriptions. Pass your customer's 24-character ID as payload and get the same addresses every time.
curl -G https://payments.rubysoft.io/api/charge/load \
-u "$PROJECT_ID:$PROJECT_SECRET" \
--data-urlencode "payload=66a0b7d2c5e8f1a3b6d904c7"
# the same customer ID always returns the same addressesInvoice Statuses
pendingWaiting for paymentpaidThe full price arrivedunderpaidLess than the price arrivedoverpaidMore than 1% above the price arrivedexpiredNo payment before the expiry timeWebhooks
Each event is a POST with a JSON body and an X-Webhook-Signature header: the HMAC-SHA256 of the raw body, keyed with your project secret decoded from hex, written as hex. Answer with any 2xx status. Anything else is retried: invoice events every 15 seconds until delivered, payment events every 10 minutes, up to 150 times.
Events
invoice:<status>An invoice changed statuspending-paymentA payment was seen and is waiting for confirmationsconfirmed-paymentA payment was confirmed and creditedfailed-paymentA payment could not be processedPOST /webhooks/rubynex
Content-Type: application/json
X-Webhook-Signature: 9c1fd2…e07a
{
"id": "4e1a…c93f",
"message": {
"event": "invoice:paid",
"data": { "id": "0b6f3c1e-…", "payload": "65f2c1a9e4b0d3f7a2c91e04" }
},
"attempt": 0,
"timestamp": 1764590400
}Verify the Signature
import crypto from 'node:crypto';
import express from 'express';
const secret = Buffer.from(process.env.RUBYNEX_SECRET, 'hex');
// keep the raw body: the signature covers its exact bytes
app.post('/webhooks/rubynex', express.raw({ type: 'application/json' }), (req, res) => {
const expected = crypto.createHmac('sha256', secret).update(req.body).digest('hex');
const given = req.get('X-Webhook-Signature') ?? '';
if (given.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected))) {
return res.sendStatus(401);
}
const { message } = JSON.parse(req.body);
if (message.event === 'invoice:paid') markOrderPaid(message.data.payload);
res.sendStatus(200);
});Confirmations by Network
| Network | Coins | Confirmations | Time to Credit |
|---|---|---|---|
| TRXUSDT | 1 | about 3 s | |
| ETHUSDTUSDCDAI | 5 | about 1 min | |
| BNBUSDTUSDCDAI | 10 | about 8 s | |
| POLUSDTUSDCDAI | 20 | about 40 s | |
| ETHUSDTUSDC | 10 | about 20 s | |
| BTC | 1 | about 10 min | |
| XRP | 1 | about 4 s | |
| DOGE | 3 | about 3 min |
Limits Worth Knowing
- Payments of $1 or less are ignored as dust
- Invoice shortfall tolerance: up to 5%
- An invoice is overpaid when more than 1% above its price arrives
- A project's secret can be rotated once every 5 minutes
Ready for Your Keys?
We create your project and send you its ID and secret.