protopays · API

первый успешный запрос

Сценарий: ключ, подпись, запрос, ответ, webhook. Актуальный Base URL — на главной.

1. получите api key

В личном кабинете мерчанта откройте шлюз и выпустите credentials для API. Открыть кабинет

2. сформируйте подпись

Для POST с JSON телом: ключ HMAC — байты сырого секрета (как в X-Merchant-Secret), сообщение — те же UTF-8 байты, что уйдут в теле; Signature = hex(HMAC-SHA256) в нижнем регистре. Секрет в заголовке — тот же, что используется как ключ HMAC; его нужно передавать вместе с подписью. Подробности — в разделе аутентификация.

3. отправьте запрос (демо-скрипт)

Скрипт ниже каждый раз пересчитывает подпись. В тексте документа нет готового hex — его нельзя ошибочно скопировать как «боевой».

Сохраните как pay-demo.mjs, заполните PP_KEY_ID, PP_SECRET (и при необходимости проверьте PP_API — в публикации Base URL уже подставлен), затем выполните: node pay-demo.mjs.

// pay-demo.mjs — подпись считается при каждом запуске (готовых hex в тексте нет).
// Node.js 18+
import crypto from "node:crypto";

const PP_API = "https://api.protopays.io";
if (!PP_API) {
  console.error("Укажите PP_API — см. главную страницу документации, поле Base URL.");
  process.exit(1);
}

const PP_KEY_ID = ""; // публичный key_id строки ключа из кабинета
const PP_SECRET = ""; // секрет той же строки
if (!PP_KEY_ID) {
  console.error("Укажите PP_KEY_ID.");
  process.exit(1);
}
if (!PP_SECRET) {
  console.error("Укажите PP_SECRET.");
  process.exit(1);
}

const body = JSON.stringify({
  orderId: "merchant-order-" + Date.now(),
  amount: "5000",
  currency: "RUB",
  callbackUri: "https://your-site.example/hooks/protopays",
  method: "sbp",
  payer: {
    userId: "u-4095",
    userIp: "188.11.55.10",
    type: "trusted",
    payments: { successful: 5, expired: 1 },
  },
});

const signature = crypto.createHmac("sha256", PP_SECRET).update(body, "utf8").digest("hex");

const res = await fetch(PP_API + "/api/v1/payments", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Merchant-Key-Id": PP_KEY_ID,
    "X-Merchant-Secret": PP_SECRET,
    Signature: signature,
  },
  body,
});

const text = await res.text();
console.log("HTTP", res.status);
console.log(text);

4. ожидаемый ответ (успех)

При корректной подписи и валидных полях — 200 OK и JSON. В теле всегда есть message; в data — поля платежа; для СБП часто приходит requisite и expiresAt (срок резерва реквизита). Пример:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "message": "Payment created",
  "data": {
    "id": 1001,
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "orderId": "merchant-order-173",
    "status": "pending",
    "amount": "5000.00",
    "currency": "RUB",
    "requisite": {
      "value": "+79991234567",
      "holder": "И. Иванов",
      "bank": "Пример банк"
    },
    "expiresAt": "2026-04-20T15:30:00+00:00"
  }
}

Сохраните id / uuid, покажите клиенту реквизиты из requisite. Поля запроса и ответа — в справочнике по созданию платежа; машиночитаемая схема: /openapi/api-docs.json.

5. webhook

При смене статуса — POST на ваш URL. Дубликаты и порядок — в разделе webhooks.

если что-то пошло не так

Ошибки, идемпотентность.