Реализация приёма криптоплатежей через мобильное приложение (мерчант)
Мерчанту нужно принять криптоплатёж в точке продажи или в интернет-магазине. Мобильное приложение — это кассовый терминал: ввёл сумму, показал QR-код покупателю, дождался подтверждения, зафиксировал продажу. Всё это должно работать быстро и без сложной настройки.
Генерация платёжного адреса и QR
Для каждой транзакции лучше генерировать новый адрес — это упрощает сопоставление «какой платёж за какой заказ». HD-кошелёк (BIP-32/BIP-44) позволяет получать адреса через деривацию: m/44'/60'/0'/0/N.
// iOS — деривация следующего адреса из HD wallet (secp256k1)
import HDWalletKit
func nextReceivingAddress(masterKey: HDPrivateKey, accountIndex: UInt32) -> String {
let derived = masterKey
.derived(at: 44, hardened: true)
.derived(at: 60, hardened: true) // ETH
.derived(at: 0, hardened: true) // account
.derived(at: 0) // external chain
.derived(at: accountIndex)
return derived.publicKey.ethereumAddress
}
Хранить accountIndex локально, инкрементировать при каждом новом платеже. Серверная часть должна мониторить все задействованные адреса.
QR-код с таймером курса
Криптовалютный курс меняется. Чтобы покупатель заплатил ровно столько, сколько нужно:
- Мерчант вводит сумму в фиате (1 000 RUB)
- Приложение запрашивает текущий курс (CoinGecko
/simple/price?ids=tether&vs_currencies=rub) - Рассчитывает сумму в USDT с буфером:
amount_usdt = fiat_amount / rate * 1.005(0.5% слиппаж) - Фиксирует курс на 15 минут, показывает таймер
- Генерирует QR с URI:
ethereum:0xUsdtAddress@1/transfer?address=0xMerchant&uint256=<raw_amount>
// Android — расчёт суммы в криптовалюте с буфером
fun calculateCryptoAmount(
fiatAmount: BigDecimal,
ratePerUnit: BigDecimal,
slippagePercent: BigDecimal = BigDecimal("0.5"),
decimals: Int = 6 // USDT: 6
): BigInteger {
val slippageFactor = BigDecimal.ONE + slippagePercent / BigDecimal(100)
val cryptoAmount = fiatAmount.divide(ratePerUnit, 10, RoundingMode.HALF_UP) * slippageFactor
return cryptoAmount.movePointRight(decimals).toBigInteger()
}
При истечении таймера — обновить QR с новым курсом. Предупреждение: «Время действия QR истекло, обновляем цену».
Ожидание и подтверждение транзакции
После показа QR — приложение слушает входящие транзакции на платёжный адрес. Два варианта:
Polling через API. Запрашивать Etherscan API / Blockscout каждые 5–10 секунд:
GET https://api.etherscan.io/api?module=account&action=tokentx&address={paymentAddress}&startblock={latest}
WebSocket от сервера. Серверная часть мониторит блокчейн и отправляет событие через WebSocket или push при получении транзакции.
Первый вариант — проще в реализации, но дороже при масштабировании. Второй — правильный для продакшена.
Статусы платежа
// iOS — конечный автомат статусов платежа
enum PaymentStatus {
case awaitingPayment(expiresAt: Date)
case detected(txHash: String, confirmations: Int)
case confirmed(txHash: String, amountReceived: BigDecimal)
case underpaid(expected: BigDecimal, received: BigDecimal)
case expired
case failed(reason: String)
}
При detected — зелёный экран с сообщением «Платёж обнаружен, ожидаем подтверждений (1/3)». При confirmed — звуковой сигнал (кассовый «дзынь»), полный экран успеха с суммой.
Частичная оплата и таймаут
Если покупатель заплатил меньше — underpaid. Три варианта логики:
- Ждать доплату до истечения расширенного таймера (1–24 часа)
- Принять частично с записью задолженности
- Автовозврат (сложно — требует хранение ключей для возврата)
Выбор стратегии — в настройках приложения мерчанта.
История транзакций и отчёты
Список принятых платежей: дата, сумма в крипте и фиате по курсу на момент оплаты, статус, TxHash (ссылка на Explorer). Фильтры по дате и статусу. Экспорт в CSV — для бухгалтерии.
Сроки: 5 рабочих дней: HD-адрес per order, генерация QR с URI-форматом и таймером курса, polling/WS входящих транзакций, экран подтверждения со звуком, история с фильтрами.







