Ketmus
Esc

Сценарии интеграции

Сайт и интернет-магазин

Подключение оплаты криптовалютой к сайту: кнопка оплаты, платёжная страница и выдача заказа.

Схема

  1. 1

    Покупатель нажимает «Оплатить»

    Ваш фронтенд отправляет запрос на ваш сервер с номером заказа.

  2. 2

    Сервер создаёт счёт

    Сервер берёт сумму заказа из своей базы и вызывает POST /invoices.

  3. 3

    Редирект на оплату

    Покупатель уходит на pay_url, выбирает монету и оплачивает.

  4. 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 покупателю не отправляйте — подробнее в разделе Оплата по СБП.