Skip to main content
Поддерживаемые банки: Сбербанк, ВТБ, Т-банк, Газпромбанк, Солидарность

Создание платежа

POST https://api.meridian.vip/api/v1/transgran/payments

Обязательные параметры

ПараметрТипОписание
amountnumberСумма платежа в рублях (не в копейках). Пример: 1000 = 1000 RUB
currencystringКод валюты. Только "RUB"
banknumberКод банка для получения реквизитов. См. Коды банков
internalIdstringУникальный идентификатор заказа в вашей системе для идемпотентности
customerEmailstringEmail клиента (требуется для Трансгран)
notificationUrlstringURL для webhook уведомлений по этому платежу
notificationTokenstringСекретный токен для подписи webhook уведомлений

Коды банков

КодБанк
1VTB
2Sberbank
3Gazprombank
4T-Bank
5Solidarnost

Пример запроса

const crypto = require('crypto');

function calculateSignature(method, url, body, secret) {
  const stringToSign = method + url + (body || '');
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(stringToSign);
  return hmac.digest('base64');
}

const method = 'POST';
const url = 'https://api.meridian.vip/api/v1/transgran/payments';
const body = JSON.stringify({
  amount: 1000,
  currency: 'RUB',
  bank: 2, // Sberbank
  internalId: 'order-12345',
  customerEmail: '[email protected]',
  notificationUrl: 'https://your-site.com/webhooks/transgran',
  notificationToken: 'your-webhook-secret-token'
});

const apiKey = 'meridian_abc123...:meridian_xyz789...';
const [keyId, secret] = apiKey.split(':');
const signature = calculateSignature(method, url, body, secret);

const response = await fetch(url, {
  method,
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': apiKey,
    'X-Signature': signature
  },
  body
});

const result = await response.json();
console.log(result);

Пример ответа (успех)

HTTP Status: 200 OK
{
  "id": "cm3k8x7y80001z8j4k5m6n7o8",
  "status": "new",
  "bankName": "sberbank",
  "amount": "1000",
  "currency": "RUB",
  "expireAt": "2025-11-03T15:10:00.000Z",
  "dealRequisites": "{\"bankName\":\"sberbank\",\"cardNumber\":\"**** 1234\",\"fullName\":\"Ivan Ivanov\"}",
  "dealRate": "95.50",
  "createdAt": "2025-11-03T15:00:00.000Z",
  "updatedAt": "2025-11-03T15:00:00.000Z",
  "internalId": "order-12345",
  "merchantName": "MerchantName",
  "clientEmail": "[email protected]"
}

Поля ответа

ПолеТипОписание
idstringУникальный идентификатор платежа в системе Meridian
statusstringСтатус: new, processing, paid, expired, failed
bankNamestringКод банка (sberbank, vtb, tbank, gazprombank, solidarnost)
amountstringСумма платежа
currencystringВалюта (RUB)
expireAtstringВремя истечения блокировки реквизита. ISO 8601
dealRequisitesstringJSON-строка с реквизитами для оплаты. Передайте клиенту
dealRatestringКурс сделки
createdAtstringВремя создания платежа. ISO 8601
updatedAtstringВремя последнего обновления. ISO 8601
internalIdstringВаш идентификатор заказа
merchantNamestringНазвание мерчанта
clientEmailstringEmail клиента
Важно: Реквизиты заблокированы только на 5 минут. После истечения expireAt реквизит освобождается и платеж переходит в статус expired.

Ошибки создания платежа

При создании платежа могут возникнуть следующие ошибки:
HTTP кодСообщениеОписание
400Сумма X RUB выходит за пределы лимитов мерчантаСумма платежа выходит за установленные лимиты мерчанта. Обратитесь к администратору для уточнения лимитов
400amount must be a positive numberСумма должна быть положительным числом
400currency must be RUBВалюта должна быть RUB
400bank must be 1, 2, 3, 4, or 5Неверный код банка
400internalId is requiredНе указан идентификатор заказа
400customerEmail must be a valid emailНеверный формат email
400notificationUrl must be a valid URLНеверный URL для webhook
400Transgran service not configuredСервис временно недоступен
400No requisites in responseНет доступных реквизитов для выбранного банка. Попробуйте другой банк
403API key authentication requiredТребуется API-ключ аутентификации

Пример ответа с ошибкой

HTTP Status: 400 Bad Request
{
  "error": "Сумма 50000 RUB выходит за пределы лимитов мерчанта"
}

Проверка статуса

Endpoint для проверки статуса платежа:
GET https://api.meridian.vip/api/v1/transgran/payments/:id

Пример запроса

const method = 'GET';
const paymentId = 'cm3k8x7y80001z8j4k5m6n7o8';
const url = `https://api.meridian.vip/api/v1/transgran/payments/${paymentId}`;

const signature = calculateSignature(method, url, '', secret);

const response = await fetch(url, {
  method,
  headers: {
    'X-API-Key': apiKey,
    'X-Signature': signature
  }
});

const payment = await response.json();
console.log('Status:', payment.status);

Пример ответа

{
  "id": "cm3k8x7y80001z8j4k5m6n7o8",
  "status": "paid",
  "bankName": "sberbank",
  "amount": "1000",
  "currency": "RUB",
  "expireAt": "2025-11-03T15:10:00.000Z",
  "dealRequisites": "{\"bankName\":\"sberbank\",\"cardNumber\":\"**** 1234\",\"fullName\":\"Ivan Ivanov\"}",
  "dealRate": "95.50",
  "createdAt": "2025-11-03T15:00:00.000Z",
  "updatedAt": "2025-11-03T15:05:00.000Z",
  "internalId": "order-12345",
  "merchantName": "MerchantName",
  "clientEmail": "[email protected]"
}

Webhook-уведомления

При изменении статуса платежа или разрешении апелляции система отправляет webhook на указанный notificationUrl.

HTTP запрос

POST {notificationUrl}
Content-Type: application/json
X-Webhook-Signature: {hmac_sha256_signature}
X-Webhook-Event: {event_type}
X-Webhook-Delivery-Id: {unique_delivery_id}

Формат тела webhook

{
  "id": "cm3k8x7y80001z8j4k5m6n7o8",
  "status": "paid",
  "bankName": "sberbank",
  "amount": "1000",
  "currency": "RUB",
  "expireAt": "2025-11-03T15:10:00.000Z",
  "dealRequisites": "{\"bankName\":\"sberbank\",\"cardNumber\":\"**** 1234\",\"fullName\":\"Ivan Ivanov\"}",
  "dealRate": "95.50",
  "createdAt": "2025-11-03T15:00:00.000Z",
  "updatedAt": "2025-11-03T15:05:00.000Z",
  "internalId": "order-12345",
  "merchantName": "MerchantName",
  "clientEmail": "[email protected]"
}

Поля webhook

ПолеТипОписание
idstringID платежа в Meridian
statusstringНовый статус платежа
bankNamestringКод банка
amountstringСумма платежа
currencystringВалюта
expireAtstringВремя истечения. ISO 8601
dealRequisitesstringJSON-строка с реквизитами
dealRatestringКурс сделки
createdAtstringВремя создания платежа
updatedAtstringВремя последнего обновления
internalIdstringВаш идентификатор заказа
merchantNamestringНазвание мерчанта
clientEmailstringEmail клиента

Верификация подписи

Webhook подписывается HMAC-SHA256. Используйте ваш notificationToken для верификации:
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload);
  const expectedSignature = hmac.digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

Создание апелляции

Если автоматическая привязка платежа не сработала (клиент оплатил, но статус не изменился), создайте апелляцию для ручной проверки.
Когда создавать апелляцию: Клиент утверждает, что оплатил, но платеж остается в статусе new или expired.
Важно: После создания апелляции платеж переходит в статус dispute, и все средства замораживаются.

Endpoint

POST https://api.meridian.vip/api/v1/transgran/payments/:id/disputes

Параметры запроса

URL параметры

ПараметрТипОбязательныйОписание
idstringДаУникальный идентификатор платежа. Пример: cm3k8x7y80001z8j4k5m6n7o8

Тело запроса (multipart/form-data)

ПараметрТипОбязательныйОписание
reasonstringДаПричина спора. См. допустимые значения ниже
descriptionstringНетПодробное описание проблемы (макс. 5000 символов)
disputeReasonDatastringНетJSON-строка. Обязательно для reason="invalid_sum": {"amount": number}
attachmentfileНетФайл-доказательство (скриншот, чек). Макс. 10 МБ. Форматы: JPG, PNG, PDF
Допустимые значения reason:
ЗначениеОписаниеОграничения
invalid_sumНеверная сумма платежаТребует disputeReasonData.amount (фактическая сумма)
has_paymentПлатеж был совершен, но не засчитан-
invalid_requisitesНекорректные реквизиты-
unknownНеясная причина спора-

Пример запроса

METHOD="POST"
PAYMENT_ID="cm3k8x7y80001z8j4k5m6n7o8"
URL="https://api.meridian.vip/api/v1/transgran/payments/${PAYMENT_ID}/disputes"

JSON_DATA='{"reason":"invalid_sum","description":"Клиент отправил 500 RUB","disputeReasonData":{"amount":500}}'
SIGNATURE=$(echo -n "${METHOD}${URL}${JSON_DATA}" | openssl dgst -sha256 -hmac "$API_SECRET" -binary | base64)

curl -X POST "${URL}" \
  -H "X-API-Key: ${API_KEY}" \
  -H "X-Signature: ${SIGNATURE}" \
  -F "reason=invalid_sum" \
  -F "description=Клиент отправил 500 RUB вместо 1000 RUB" \
  -F 'disputeReasonData={"amount":500}' \
  -F "attachment=@/path/to/screenshot.jpg"

Пример ответа

HTTP Status: 201 Created
{
  "id": "disp_abc123xyz789",
  "paymentId": "cm3k8x7y80001z8j4k5m6n7o8",
  "reason": "invalid_sum",
  "description": "Клиент отправил 500 RUB вместо 1000 RUB",
  "disputeReasonData": {
    "amount": 500
  },
  "attachmentUrl": "https://meridian-disputes.s3.amazonaws.com/disp_abc123xyz789/screenshot.jpg",
  "attachmentFilename": "screenshot.jpg",
  "status": "open",
  "resolution": null,
  "resolutionNotes": null,
  "createdAt": "2025-11-03T15:10:00.000Z",
  "resolvedAt": null,
  "autoResolveAt": "2025-11-03T16:10:00.000Z",
  "amount": "1000",
  "currency": "RUB",
  "bankName": "sberbank",
  "dealRequisites": "{\"bankName\":\"sberbank\",\"cardNumber\":\"**** 1234\",\"fullName\":\"Ivan Ivanov\"}",
  "internalId": "order-12345",
  "merchantName": "MerchantName",
  "clientEmail": "[email protected]"
}

Поля ответа апелляции

ПолеТипОписание
idstringУникальный идентификатор апелляции
paymentIdstringID платежа, по которому создана апелляция
reasonstringПричина спора: invalid_sum, has_payment, invalid_requisites, unknown
descriptionstring | nullОписание проблемы
disputeReasonDataobject | nullДополнительные данные. Для invalid_sum содержит {"amount": number}
attachmentUrlstring | nullURL файла-доказательства (7 дней)
attachmentFilenamestring | nullИмя загруженного файла
statusstringopen (ожидает рассмотрения) или closed (разрешен)
resolutionstring | nullРешение: merchant_win, trader_win или null
resolutionNotesstring | nullКомментарий администратора
createdAtstringВремя создания. ISO 8601
resolvedAtstring | nullВремя разрешения. ISO 8601
autoResolveAtstring | nullВремя автоматического разрешения. ISO 8601
amountstringСумма платежа
currencystringВалюта (RUB)
bankNamestringКод банка
dealRequisitesstringJSON-строка с реквизитами
internalIdstring | nullВаш идентификатор заказа
merchantNamestring | nullНазвание мерчанта
clientEmailstring | nullEmail клиента

Автоматическое разрешение

Важно: Если апелляция не будет разрешена в течение 60 минут, система автоматически разрешит её в пользу мерчанта (merchant_win).

Проверка статуса апелляции

GET https://api.meridian.vip/api/v1/transgran/disputes/:id

Пример запроса

const method = 'GET';
const disputeId = 'disp_abc123xyz789';
const url = `https://api.meridian.vip/api/v1/transgran/disputes/${disputeId}`;

const signature = calculateSignature(method, url, '', secret);

const response = await fetch(url, {
  method,
  headers: {
    'X-API-Key': apiKey,
    'X-Signature': signature
  }
});

const dispute = await response.json();
console.log('Status:', dispute.status);
console.log('Resolution:', dispute.resolution);

Пример ответа

{
  "id": "disp_abc123xyz789",
  "paymentId": "cm3k8x7y80001z8j4k5m6n7o8",
  "reason": "invalid_sum",
  "description": "Клиент отправил 500 RUB вместо 1000 RUB",
  "disputeReasonData": {
    "amount": 500
  },
  "attachmentUrl": "https://meridian-disputes.s3.amazonaws.com/disp_abc123xyz789/screenshot.jpg",
  "attachmentFilename": "screenshot.jpg",
  "status": "closed",
  "resolution": "merchant_win",
  "resolutionNotes": "Платеж подтвержден на сумму 500 RUB",
  "createdAt": "2025-11-03T15:10:00.000Z",
  "resolvedAt": "2025-11-03T15:30:00.000Z",
  "autoResolveAt": "2025-11-03T16:10:00.000Z",
  "amount": "1000",
  "currency": "RUB",
  "bankName": "sberbank",
  "dealRequisites": "{\"bankName\":\"sberbank\",\"cardNumber\":\"**** 1234\",\"fullName\":\"Ivan Ivanov\"}",
  "internalId": "order-12345",
  "merchantName": "MerchantName",
  "clientEmail": "[email protected]"
}