Розробка системи повернення крипто-платежів
Повернення в крипті—нетривіальна задача, і не тому що технічно складно відправити транзакцію назад. Проблема в тому, що курс міг змінитися, вихідна адреса могла бути біржевим депозитом (тобто користувач не має до неї доступу), а в деяких юрисдикціях повернення крипто створює податкові события. Без продуманої архітектури система або втрачає гроші на курсовій різниці, або повернення зависають навсігди.
Фундаментальна проблема: адреса відправника ≠ адреса отримувача
На Ethereum tx.origin та msg.sender—це адреса, з якої пішла транзакція. Але якщо користувач відправляв з Binance, Coinbase або будь-якої біржі—ця адреса належить біржі, не користувачу. Повернення на біржевий депозитний адрес:
- Найкращий випадок: біржа зарахує кошти користувачу якщо memo/tag збігаються
- Реальний випадок: біржа зарахує на свій гаманець, користувач відкриває dispute місяцями пізніше
- Гірший випадок: транзакція буде відхилена (особливо для токенів), кошти втрачені
Тому повернення "по адресі відправника"—це не рішення. Правильне рішення—збирати адресу для повернення явно, на етапі оплати або створення заявки на повернення.
Архітектура системи
Смарт-контрактний варіант (EVM мережі)
Для on-chain логіки—escrow контракт зі станами:
enum PaymentStatus { Pending, Confirmed, Refunded, Disputed }
struct Payment {
address payer;
address refundAddress; // явно вказана адреса повернення
uint256 amount;
uint256 confirmedAt;
PaymentStatus status;
uint256 refundDeadline; // до якого моменту повернення можливо
}
Функція повернення з захистами:
function refund(bytes32 paymentId) external onlyOperator {
Payment storage p = payments[paymentId];
require(p.status == PaymentStatus.Confirmed, "Not refundable");
require(block.timestamp <= p.refundDeadline, "Deadline passed");
p.status = PaymentStatus.Refunded;
// CEI паттерн: статус змінений до передачі
(bool success, ) = p.refundAddress.call{value: p.amount}("");
require(success, "Transfer failed");
emit PaymentRefunded(paymentId, p.refundAddress, p.amount);
}
Для ERC-20 токенів—SafeERC20.safeTransfer. USDT на Ethereum з нестандартним інтерфейсом—окрема обробка.
Off-chain варіант (Bitcoin, Dogecoin, UTXO мережі)
Тут смарт-контрактів немає. Логіка повністю в бекенді:
- При створенні замовлення—збираємо
refund_addressу користувача - Зберігаємо історію: яка транзакція, скільки, з якої/на яку адресу
- При поверненні—будуємо UTXO транзакцію з sweep-гаманця на
refund_address - Сума повернення: оригінальна сума мінус комісія мережі (розраховується в момент повернення)
Курсова різниця: політика та реалізація
Це бізнес-рішення, але воно впливає на архітектуру:
| Політика | Реалізація | Ризик |
|---|---|---|
| Повернення в тій же криптовалюті | Просто, чесно | Курс виріс—користувач отримує менше у фіаті |
| Повернення USD-еквівалента на момент оплати | Потрібен stablecoin або конвертація | Курс впав—ви доплачуєте різницю |
| Повернення по поточному курсу | Просто | Курс впав—користувач програє |
Найпоширеніший підхід для e-commerce: повернення в тій же криптовалюті, сума = оригінальна мінус процент обробки. Політика прописується в ToS та явно показується користувачу.
Заявки на повернення: флоу користувача
Користувач → Створює заявку → Вказує refund_address →
Оператор перевіряє (або автоматично) → Транзакція повернення →
Користувач отримує txHash для верифікації
Автоматичні повернення—для сум нижче порога (напр., < $200), якщо бізнес-логіка однозначна (замовлення скасовано до відправки). Транзакція ініціюється без участі оператора.
Ручна перевірка—для великих сум, спірних ситуацій, коли refund_address виглядає підозріло (та ж адреса що приймає платежі—red flag для фрода).
Мультивалютність
Якщо система приймає кілька валют—повернення в тій же валюті вимагає зберігати достатній баланс кожної. Альтернатива—конвертація через DEX (Uniswap, 1inch) з slippage tolerance, але тоді точна сума повернення невідома заздалегідь.
Для DEX-конвертації потрібна додаткова логіка: pre-quote, перевірка liquidity, захист від MEV (deadline + мінімальний output). Це окрема задача з окремими ризиками.
Що реалізується за 3–5 днів
Escrow смарт-контракт або off-chain логіка для вашої валюти, база даних платіжних записів з історією повернень, збір refund_address від користувачів, автоматичні та ручні сценарії повернення, admin інтерфейс для операторів, сповіщення користувачам з txHash, базове логування та аудит-трейл.







