Інтеграція криптоплатежів в інтернет-магазини
Типова помилка при інтеграції крипти в e-commerce: ставитися до неї як до ще одного payment method в існуючому checkout. На справді це інший flow — у криптоплатежів немає instant finality (крім L2), немає chargebacks, курс змінюється поки користувач йде до гаманця, а partial payment — реальний edge case, а не теоретичний.
Вибір підходу: hosted vs кастомний
Для інтернет-магазину з обсягом до кількох сотень платежів на місяць розумніше використовувати готові рішення — NOWPayments, CoinGate, BitPay. Вони беруть 0.5–1% комісію, але визволяють від інфраструктурних задач.
Кастомна інтеграція оправдана при:
- Потрібна конкретна set монет/мереж, яку не підтримує провайдер
- Вимоги до приватності (не хочете, щоб третя сторона бачила транзакції)
- Високі обсяги, де комісія провайдера значна
- Спеціальна логіка (наприклад, автоматична конвертація через DEX)
Нижче — кастомна інтеграція, оскільки вона потребує більше технічних рішень.
WooCommerce: кастомний payment gateway plugin
WooCommerce надає абстрактний клас WC_Payment_Gateway — просто його розширте:
class WC_Crypto_Gateway extends WC_Payment_Gateway {
public function __construct() {
$this->id = 'crypto_payment';
$this->title = 'Оплата криптовалютою';
$this->method_description = 'Bitcoin, Ethereum, USDT та інші';
$this->supports = ['products'];
$this->init_form_fields();
$this->init_settings();
add_action('woocommerce_update_options_payment_gateways_' . $this->id,
[$this, 'process_admin_options']);
add_action('woocommerce_api_crypto_payment', [$this, 'handle_webhook']);
}
public function process_payment($order_id): array {
$order = wc_get_order($order_id);
// Створюємо платіж у зовнішньому сервісі або генеруємо адрес
$payment = $this->create_crypto_payment($order);
// Зберігаємо дані для відображення інструкцій
$order->update_meta_data('_crypto_payment_id', $payment['id']);
$order->update_meta_data('_crypto_pay_address', $payment['address']);
$order->update_meta_data('_crypto_pay_amount', $payment['amount']);
$order->update_meta_data('_crypto_expires_at', $payment['expires_at']);
$order->set_status('pending', 'Очикування криптоплатежу');
$order->save();
return [
'result' => 'success',
'redirect' => $this->get_return_url($order),
];
}
public function handle_webhook(): void {
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PAYMENT_SIGNATURE'] ?? '';
if (!$this->verify_signature($payload, $signature)) {
wp_die('Invalid signature', 401);
}
$data = json_decode($payload, true);
$order = wc_get_order($data['order_id']);
if (!$order) wp_die('Order not found', 404);
if ($data['status'] === 'confirmed') {
$order->payment_complete($data['transaction_hash']);
$order->add_order_note(
sprintf('Криптоплатіж підтверджений. TX: %s', $data['transaction_hash'])
);
}
wp_die('OK', 200);
}
}
Сторінка thank you (після редиректу) повинна показувати адресу, QR-код та суму з таймером. WooCommerce викликає get_return_url() який ведає на стандартну thank you сторінку — кастомізуйте через woocommerce_thankyou_{gateway_id} action.
Shopify: використання Payment Apps API
Shopify не дозволяє довільний кастомний PHP. Для інтеграції крипти створіть Shopify App через Partner Dashboard, використовуйте Payments Apps API.
Принцип: ваше додаток реєструється як payment provider. При оформленні замовлення Shopify робить HTTP запит до вашого endpoint з даними замовлення, ви повертаєте URL для редиректу на вашу payment page, та після підтвердження відправляєте resolved/rejected через GraphQL mutation.
// Shopify викликає цей endpoint
app.post('/shopify/payment', async (req, res) => {
const { gid, amount, currency, cancelUrl, kind } = req.body;
// Створюємо внутрішній платіж
const payment = await createCryptoInvoice({
shopifyOrderGid: gid,
fiatAmount: parseFloat(amount),
fiatCurrency: currency,
});
// Редиректимо на нашу payment page
res.json({
redirect_url: `${process.env.APP_URL}/pay/${payment.id}`,
});
});
// Після підтвердження платежу
async function notifyShopifyPaymentComplete(paymentGid: string, txHash: string) {
const mutation = `
mutation PaymentSessionResolve($id: ID!) {
paymentSessionResolve(id: $id) {
paymentSession {
id
state { ... on PaymentSessionStateResolved { code } }
}
userErrors { field message }
}
}
`;
await shopifyGraphQL(mutation, { id: paymentGid });
}
Курс та expiration: критичні деталі UX
Користувач бачить цену 99$, нажимає "оплатити криптою", та попадає на сторінку з суммою 0.0271 ETH. Ця сума дійсна 15–30 хвилин. Якщо користувач медлить або курс сильно змінюється — потрібен refresh механізм.
Таймер на сторінці оплати повинен працювати — при закінченні автоматично оновлюємо invoice:
// Клієнтський код
let expiresAt = new Date(invoice.expiresAt);
const timer = setInterval(async () => {
const remaining = expiresAt.getTime() - Date.now();
if (remaining <= 0) {
clearInterval(timer);
// Запитуємо новий invoice з актуальним курсом
const refreshed = await fetch(`/api/payment/${invoiceId}/refresh`, {
method: 'POST'
});
const newInvoice = await refreshed.json();
expiresAt = new Date(newInvoice.expiresAt);
updateUI(newInvoice); // Оновлюємо QR та суму
}
}, 1000);
На бекенді при refresh — пересчитуємо суму в крипте по актуальному курсу, оновлюємо запис у БД, той же адрес (якщо використовуємо unique address per payment).
Статуси замовлення та комунікація з клієнтом
| Статус замовлення | Триггер | Email клієнту |
|---|---|---|
pending |
Створен invoice | "Очикуємо оплату, адреса: X" |
processing |
Транзакція знайдена, чекаємо confirmations | "Платіж знайден, підтверджуємо" |
paid |
Confirmations досягнуті | "Оплата підтверджена" |
partially_paid |
Отримана неповна сума | "Отримана неповна сума, зв'яжіться з нами" |
expired |
Таймер закінчився без оплати | "Invoice істік, створіть новий" |
Reconciliation та звітність
Для бухгалтерії потрібна конвертація крипто-суми в фіат на момент отримання. Фіксуємо у БД: crypto_amount, crypto_currency, fiat_amount, fiat_currency, exchange_rate, confirmed_at. Джерело курсу — Chainlink (on-chain) або CoinGecko API (off-chain) з timestamp. Це критично для податкового обліку.







