Сценарии интеграции
Сайт и интернет-магазин
Подключение оплаты криптовалютой к сайту: кнопка оплаты, платёжная страница и выдача заказа.
Схема
- 1
Покупатель нажимает «Оплатить»
Ваш фронтенд отправляет запрос на ваш сервер с номером заказа.
- 2
Сервер создаёт счёт
Сервер берёт сумму заказа из своей базы и вызывает
POST /invoices. - 3
Редирект на оплату
Покупатель уходит на
pay_url, выбирает монету и оплачивает. - 4
Вебхук и выдача заказа
По событию
invoice.paidсервер отмечает заказ оплаченным, а покупатель возвращается наsuccess_url.
Сумму определяет только сервер
Никогда не принимайте сумму счёта из браузера. Берите её из своей базы по номеру заказа, иначе покупатель сможет изменить цену.
Пример на Next.js
// app/api/checkout/route.ts — создаёт счёт и возвращает ссылку на оплату
export async function POST(request: Request) {
const { orderId } = await request.json()
const order = await db.orders.find(orderId) // сумму берите только из своей базы
const response = await fetch('https://app.ketmus.com/api/merchant/v1/invoices', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KETMUS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: order.total,
currency: 'USD',
order_id: order.id,
description: `Заказ №${order.number}`,
callback_url: 'https://shop.example.com/api/webhooks/ketmus',
success_url: `https://shop.example.com/orders/${order.id}?paid=1`,
fail_url: `https://shop.example.com/orders/${order.id}`,
}),
})
const data = await response.json()
if (!response.ok) return Response.json({ error: data.error.message }, { status: 502 })
return Response.json({ url: data.invoice.pay_url })
}// app/api/webhooks/ketmus/route.ts
import { verifyKetmusSignature } from '@/lib/ketmus'
export async function POST(request: Request) {
const rawBody = await request.text()
const signature = request.headers.get('x-ketmus-signature') ?? ''
if (!verifyKetmusSignature(rawBody, signature, process.env.KETMUS_WEBHOOK_SECRET!)) {
return new Response('bad signature', { status: 400 })
}
const event = JSON.parse(rawBody)
if (await db.webhookDeliveries.exists(event.delivery_id)) return new Response('ok')
if (event.status === 'paid' || event.status === 'paid_over') {
await db.orders.markPaid(event.order_id, { invoiceId: event.id })
}
await db.webhookDeliveries.save(event.delivery_id)
return new Response('ok')
}Пример на PHP
<?php
// checkout.php — создаёт счёт и перенаправляет покупателя
$order = findOrder((int) $_POST['order_id']); // сумму берите из своей базы
$ch = curl_init('https://app.ketmus.com/api/merchant/v1/invoices');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('KETMUS_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'amount' => $order['total'],
'currency' => 'USD',
'order_id' => (string) $order['id'],
'callback_url' => 'https://shop.example.com/ketmus-webhook.php',
'success_url' => 'https://shop.example.com/order.php?id=' . $order['id'],
]),
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status !== 201 || empty($data['invoice']['pay_url'])) {
error_log('Ketmus error: ' . json_encode($data['error'] ?? $data));
http_response_code(502);
exit('Не удалось создать счёт, попробуйте позже.');
}
header('Location: ' . $data['invoice']['pay_url'], true, 303);Обработчик вебхука на PHP — в разделе Проверка подписи.
Оплата в своём интерфейсе
Если не хотите уводить покупателя на страницу Ketmus, передайте rail при создании счёта. В ответе сразу будут payment.address, payment.amount_due и payment_uri — покажите их вместе с QR-кодом из payment_uri, а статус узнавайте через вебхуки.
curl -X POST "https://app.ketmus.com/api/merchant/v1/invoices" \
-H "Authorization: Bearer $KETMUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 49,
"currency": "EUR",
"order_id": "ORDER-2077",
"rail": "usdt_ton"
}'Для монет с memo (например, XRP) обязательно покажите memo рядом с адресом — без него платёж не будет привязан к счёту.
СБП (sbp_qr) всегда оплачивается через pay_url. Прямую ссылку https://qr.nspk.ru/… из payment_uri покупателю не отправляйте — подробнее в разделе Оплата по СБП.