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 уведомлений , минимальная длина 32 символа

Коды банков

КодБанк
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, paid, expired, canceled
bankNamestringЗапрошенный банк из параметра bank (sberbank, vtb, tbank, gazprombank, solidarnost). Не является банком назначения — см. dealRequisites
amountstringСумма платежа
currencystringВалюта (RUB)
expireAtstringВремя истечения блокировки реквизита. ISO 8601
dealRequisitesstringJSON-строка с реквизитами для оплаты. Это фактический банк назначения. См. Структура dealRequisites
dealRatestringКурс сделки
createdAtstringВремя создания платежа. ISO 8601
updatedAtstringВремя последнего обновления. ISO 8601
internalIdstringВаш идентификатор заказа
merchantNamestringНазвание мерчанта
clientEmailstringEmail клиента

Структура dealRequisites

Поле dealRequisites содержит JSON-строку с реквизитами, куда клиент должен отправить платеж.
Важно: Поле bankName в ответе — это запрошенный российский банк. Фактический банк назначения находится в dealRequisites.bankName и является одним из узбекских банков-партнеров.

Поля объекта

ПолеТипОписание
bankNamestringБанк назначения: poytaxt_bank, garant_bank или asia_alliance
cardNumberstringНомер карты
phoneNumberstringНомер телефона
fullNamestringФИО получателя

Пример парсинга

const payment = await response.json();
const requisites = JSON.parse(payment.dealRequisites);

// bankName в ответе = "sberbank" (запрошенный банк)
// requisites.bankName = "garant_bank" (фактический банк назначения)

console.log('Банк назначения:', requisites.bankName);  // "garant_bank"
console.log('Номер карты:', requisites.cardNumber);     // "1234 5678 8765 4321"
console.log('Получатель:', requisites.fullName);        // "Ivan Ivanov"

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

При создании платежа могут возникнуть следующие ошибки:
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\":\"garant_bank\",\"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\":\"garant_bank\",\"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-строка с реквизитами. См. Структура dealRequisites
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\":\"garant_bank\",\"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-строка с реквизитами. См. Структура dealRequisites
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\":\"garant_bank\",\"cardNumber\":\"**** 1234\",\"fullName\":\"Ivan Ivanov\"}",
  "internalId": "order-12345",
  "merchantName": "MerchantName",
  "clientEmail": "[email protected]"
}