Інтеграція криптоплатежів на сайт
Прийом криптоплатежів без посередників — це пряма робота з блокчейном: генерація адрес, мониторинг трансакцій, підтвердження блоків. З посередниками (NOWPayments, CoinGate, BitPay) — API подібний до класичного платіжного шлюзу, але з особливостями волатильності та необоротності. Обидва підходи мають сенс у різних ситуаціях.
Реалізація з посередником займає 2–3 робочих дні. Самостійний прийом на одну мережу — 5–8 днів.
Через платіжний шлюз (рекомендується)
NOWPayments, CoinGate, BitPay беруть комісію 0.5–1% та вирішують для вас: конвертацію, мониторинг мережі, підтвердження, курсові ризики (якщо включена негайна конвертація). Інтеграція аналогічна Stripe.
Прямий прийом USDT/USDC (TRC-20, ERC-20)
Без посередників, але з курсовим ризиком тільки для стейблкоїнів. Для кожного замовлення генерується унікальна адреса:
use kornrunner\Ethereum\Ethereum;
class WalletService
{
private string $masterPrivateKey;
public function generateDepositAddress(int $orderId): string
{
// HD Wallet: мастер-ключ + шлях m/44'/195'/0'/0/{orderId}
$derivedKey = $this->deriveKey($this->masterPrivateKey, $orderId);
$eth = new Ethereum();
return $eth->getAddress($derivedKey->getPublicKey());
}
}
Для TRC-20 (Tron) використовується інша деривація, але концепція та ж — HD Wallet з детерміністичними адресами. Знаючи мастер-ключ та index, завжди можна відновити адресу та перевірити баланс.
Мониторинг надходження коштів
Blockchain-события не приходять у webhook — потрібно опитувати мережу. Для Ethereum/ERC-20:
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider(process.env.ETH_RPC_URL); // Alchemy, Infura
// Підписатися на Transfer события ERC-20 контракту
const usdcContract = new ethers.Contract(USDC_ADDRESS, ERC20_ABI, provider);
usdcContract.on('Transfer', async (from, to, amount, event) => {
const pendingOrder = await Order.findByDepositAddress(to);
if (!pendingOrder) return;
const decimals = 6; // USDC
const receivedUSDC = Number(ethers.formatUnits(amount, decimals));
if (receivedUSDC >= pendingOrder.amount_usd * 0.99) { // допуск 1% на комісії
await confirmOrderPayment(pendingOrder, event.transactionHash, event.blockNumber);
}
});
Чекайте мінімум 6 підтверджень (блоків) для Ethereum, 20+ для Bitcoin, 19+ для Tron перед остаточним підтвердженням замовлення.
Очікування підтверджень
async function waitForConfirmations(
txHash: string,
requiredConfirmations: number = 6
): Promise<boolean> {
const tx = await provider.getTransaction(txHash);
if (!tx) return false;
const receipt = await provider.waitForTransaction(txHash, requiredConfirmations);
return receipt !== null && receipt.status === 1;
}
Зберігання інформації про платіж
CREATE TABLE crypto_payments (
id bigserial PRIMARY KEY,
order_id bigint NOT NULL REFERENCES orders(id),
network varchar(20) NOT NULL, -- 'eth', 'tron', 'btc'
token varchar(20) NOT NULL, -- 'USDT', 'USDC', 'BTC'
deposit_address varchar(100) NOT NULL UNIQUE,
expected_amount numeric(18,8) NOT NULL,
received_amount numeric(18,8),
tx_hash varchar(100),
confirmations int DEFAULT 0,
status varchar(20) NOT NULL DEFAULT 'awaiting',
expires_at timestamptz NOT NULL, -- зазвичай 30–60 хвилин
created_at timestamptz NOT NULL DEFAULT now()
);
Обмеження часу на оплату (expires_at) — обов'язково. Курс зафіксований на момент створення замовлення. Якщо клієнт не оплатив за 30 хвилин — замовлення скасовується, адреса може бути переіспользована (хоча краще не переіспользовувати).
Курс та конвертація
При створенні замовлення курс зафіксований з зовнішнього джерела (CoinGecko, Binance API). Сума в крипто розраховується з суми в фіатній валюті:
$btcRate = $this->priceService->getRate('BTC', 'USD'); // наприклад, 65000
$btcAmount = round($order->total_usd / $btcRate, 8);
// Відображати покупцю: заплатити 0.00023076 BTC
Допуск ±1–2% на комісії мережі та часовий розрив — стандартна практика.
Повернення
Повернення криптоплатежу — ручний процес. Автоматика неможлива без зберігання приватних ключів у гарячому гаманці, що створює ризики безпеки. Стандартна практика: менеджер вручну відправляє трансакцію на гаманець клієнта. Адреса гаманця потрібна при ініціації повернення.
Безпека
Приватні ключи зберігаються в HSM або HashiCorp Vault, не в БД та не в .env. Deposit-адреси — похідні від мастер-ключа, не самостійні ключі. Доступ до мастер-ключа — тільки з серверного процесу мониторингу, не з веб-додатку.







