Интеграция API платежей USDT на Node.js
Создавайте страницы оплаты USDT через Node.js fetch, обрабатывайте безопасные повторы и проверяйте подписи исходного тела вебхука через node:crypto.
Интеграция работает на сервере
Пример использует Node.js 20+ и встроенные модули без SDK. Импорт не вызывает API. Никогда не передавайте API-ключ и ключ подписи в браузерную сборку.
Сначала настройте HTTPS-вебхук. Задайте DEDYX_API_KEY и DEDYX_WALLET в окружении сервера. Скачайте dedyx_client.mjs и импортируйте в серверном коде.
Это основа интеграции, а не полная система заказов. Добавьте постоянное хранение заказов, лимиты тела запроса, маршрутизацию приёмника и транзакцию выполнения. Примеры используют вымышленные заказы; перед реальным запросом замените поля.
Создание платежа с безопасными повторами
// Node.js 20+ server-side example. Importing this module makes no API calls.
import {createHmac, timingSafeEqual} from 'node:crypto';
import {setTimeout as sleep} from 'node:timers/promises';
const API = 'https://api.dedyx.com/api/v1';
export async function createPayment({apiKey, wallet, amount, description, idempotencyKey}) {
// Persist the key and fields with your order BEFORE calling this function.
if (!/^[A-Za-z0-9._:-]{8,128}$/.test(idempotencyKey)) throw new Error('Invalid idempotency key');
const body = JSON.stringify({merchant_wallet:wallet, amount, description});
for (let attempt=0; attempt<3; attempt++) {
let response, delay=500*(2**attempt)+Math.random()*250;
try {
response=await fetch(API+'/payments', {
method:'POST', redirect:'manual', signal:AbortSignal.timeout(10000),
headers:{'X-API-Key':apiKey,'Idempotency-Key':idempotencyKey,'Content-Type':'application/json'}, body
});
} catch (error) {
if (attempt===2) throw error; // Uncertain outcome; preserve key/body and reconcile.
await sleep(delay); continue;
}
if (response.ok) return response.json();
const status=response.status, retryAfter=response.headers.get('retry-after')||'';
// nginx may return HTML. Do not assume an error JSON body.
await response.body?.cancel();
if (![429,500,502,503,504].includes(status)||attempt===2) throw new Error(`Dedyx HTTP ${status}`);
if (/^[0-9]+$/.test(retryAfter)) {
const seconds=Number(retryAfter);
if (seconds>60) throw new Error(`Retry later with the same key/body: ${seconds}s`);
delay=Math.max(delay,seconds*1000);
}
await sleep(delay);
}
}Клиент использует таймаут 10 секунд и не более трёх попыток. Ответы 429 и выбранные 5xx повторяются с теми же сериализованным телом и ключом идемпотентности. Числовой Retry-After соблюдается; длительное ожидание передаётся планировщику задач. Ошибки nginx могут быть HTML, поэтому клиент проверяет статус без предположения о JSON.
409 не повторяется вслепую: проверьте настройки вебхука, занятый адрес и изменение тела при том же ключе. При исчерпанных или неопределённых повторах сохраняйте ключ и сверяйте состояние; стандартное хранение повторов — 24 часа. Ключ содержит 8–128 ASCII-букв, цифр или ._:-.
Связь ответа с заказом
import {createPayment} from './dedyx_client.mjs';
// These fields must come from a persisted order record.
const payment = await createPayment({
apiKey: process.env.DEDYX_API_KEY,
wallet: process.env.DEDYX_WALLET,
amount: '49.00',
description: 'Order #1024',
idempotencyKey: 'order-1024-v1',
});
// Persist payment.payment_id and the response before redirecting.
console.log(payment.payment_url);Сохраните ответ вместе с заказом до перенаправления клиента на payment_url. Для повторов этого заказа используйте тот же ключ и поля. Не помещайте персональные данные в description.
Проверка исходного тела вебхука
import {createHmac, timingSafeEqual} from 'node:crypto';
export function verifyWebhook({rawBody, timestamp, signature, eventId, keyId, keys, now=Date.now()/1000}) {
if (!Buffer.isBuffer(rawBody)) throw new Error('Use the original raw request Buffer');
if (!/^[0-9]{1,12}$/.test(timestamp||'')||Math.abs(now-Number(timestamp))>300) throw new Error('Invalid or stale timestamp');
const key=Object.hasOwn(keys,keyId)?keys[keyId]:undefined;
if (!key||!/^v1=[0-9a-f]{64}$/.test(signature||'')) throw new Error('Unknown key or signature format');
const expected=createHmac('sha256',key).update(timestamp+'.').update(rawBody).digest();
const received=Buffer.from(signature.slice(3),'hex');
if (!timingSafeEqual(expected,received)) throw new Error('Invalid signature');
const event=JSON.parse(rawBody.toString('utf8'));
if (!event||typeof event.event_id!=='string'||!event.event_id||event.event_id!==eventId) throw new Error('Event ID mismatch');
// Validate your own order before fulfilling; signature verification alone is insufficient.
return event;
}Передайте исходные байты запроса / Buffer, X-Dedyx-Timestamp, X-Dedyx-Signature, X-Dedyx-Event-ID и X-Dedyx-Key-ID. Словарь ключей содержит сохранённые ID и полные секреты whsec_... . Не запускайте JSON middleware до сохранения исходных байтов.
После проверки требуйте PAID, сопоставьте событие с сохранённым заказом и точно сравните сумму. Атомарно сохраняйте event_id и выполнение заказа. Повтор возвращает 2xx без повторного действия. См. доставку и исключение повторов; одна проверка подписи никогда не должна выдавать товар.
Перед включением клиентских платежей
- Проверьте верную подпись, изменённое тело, устаревшее время и неизвестный ключ.
- Проверьте повторное событие и сбой приёмника во время выполнения заказа.
- Проверьте таймаут API после успешного создания: повторите исходный ключ.
- Проверьте 429 с Retry-After, HTML-ошибки nginx и временный 503.
- Разберите истечение, отменённые заказы и поздние переводы с поддержкой.
Полный чеклист запуска и пример атомарного выполнения · Ошибки API · Запросить доступ к бете