API Документация для внешних разработчиков

Содержание


Общая информация

API использует JSON формат для обмена данными. Все запросы должны содержать обязательные заголовки для аутентификации и подписи.

Базовый URL: https://your-api-domain.com (уточните у администратора)

Формат данных: JSON

Кодировка: UTF-8


Аутентификация и подпись запросов

Обязательные заголовки

Каждый запрос должен содержать следующие заголовки:

Заголовок Описание Обязательность
X-Key-ID Идентификатор ключа (выдается администратором) Обязательно
X-Signature Подпись тела запроса в формате base64 Обязательно (если требуется для ключа)

Пример заголовков:

X-Key-ID: your-key-id-12345
X-Signature: base64-encoded-signature

Формирование X-Signature

Подпись X-Signature обеспечивает целостность и аутентичность данных запроса. Процесс формирования подписи состоит из следующих шагов:

Шаг 1: Подготовка тела запроса

  1. Сформируйте JSON-объект с данными запроса
  2. Преобразуйте JSON в байтовый массив (UTF-8)

Пример тела запроса:

{"order_id":"order-12345","amount":10000,"external_id":"ext-12345"}

Шаг 2: Вычисление хеша

Вычислите SHA-256 хеш от байтового представления тела запроса:

hash = SHA256(body_bytes)

Шаг 3: Создание подписи

Подпишите хеш с помощью приватного RSA ключа, соответствующего X-Key-ID. Используется алгоритм RSA PKCS#1 v1.5:

signature_bytes = RSA_Sign(hash, private_key, PKCS1v15)

Параметры:

Шаг 4: Кодирование в base64

Закодируйте полученную подпись в base64:

X-Signature = base64_encode(signature_bytes)

Поддерживаются два варианта кодирования:

Важные замечания

  1. Пробелы и форматирование: Форматирование JSON (пробелы, переносы строк) не влияет на формирование подписи, так как подпись вычисляется от байтового представления JSON.
  2. Кодировка: Всегда используйте UTF-8 для кодирования тела запроса.
  3. Проверка подписи на сервере: Сервер проверяет подпись используя публичный ключ из сертификата, связанного с X-Key-ID. Убедитесь, что используете правильный приватный ключ, соответствующий сертификату.
  4. Ошибки подписи: При ошибке проверки подписи сервер возвращает MD5 хеш публичного ключа в сообщении об ошибке для отладки. Для проверки соответствия ключа и сертификата см. раздел Проверка соответствия ключа и сертификата.

API Endpoints

Check - Проверка заказа

Эндпоинт: POST /check

Описание: Проверяет существование заказа и возвращает его данные (сумму, телефон, описание, статус).

Запрос

Заголовки:

X-Key-ID: your-key-id
X-Signature: base64-encoded-signature

Тело запроса:

{
  "order_id": "order-12345"
}

Параметры:

Успешный ответ (200 OK)

{
  "success": true,
  "amount": 10000,
  "phone": "+79991234567",
  "description": "Оплата заказа",
  "status": "new"
}

Поля ответа:

Ответ с ошибкой (400 Bad Request)

Если заказ найден, но есть ошибка валидации (например, заказ уже оплачен):

{
  "success": false,
  "amount": 10000,
  "phone": "+79991234567",
  "description": "Оплата заказа",
  "status": "completed",
  "error": "validation error: completed"
}

Если заказ просрочен:

{
  "success": false,
  "amount": 10000,
  "phone": "+79991234567",
  "description": "Оплата заказа",
  "status": "expired",
  "expired_at": "2024-12-31T23:59:59Z",
  "error": "validation error: expired"
}

Если заказ не найден или данные недоступны:

{
  "success": false,
  "error": "system error"
}

Пример запроса (curl)

curl -X POST "https://api.example.com/check" \
  -H "X-Key-ID: your-key-id" \
  -H "X-Signature: base64-encoded-signature" \
  -H "Content-Type: application/json" \
  -d '{"order_id":"order-12345"}'

Pay - Оплата заказа

Эндпоинт: POST /pay

Описание: Инициирует и завершает транзакцию оплаты заказа.

Важно: Параметр external_id является обязательным и должен быть уникальным для каждой попытки оплаты.

Запрос

Заголовки:

X-Key-ID: your-key-id
X-Signature: base64-encoded-signature

Тело запроса (успешная оплата):

{
  "order_id": "order-12345",
  "amount": 10000,
  "external_id": "ext-12345"
}

Параметры:

Успешный ответ (200 OK)

{
  "success": true,
  "transaction_id": "txn-12345",
  "status": "completed"
}

Поля ответа:

Ответ с ошибкой (400 Bad Request)

Если транзакция была создана, но есть ошибка валидации:

{
  "success": false,
  "transaction_id": "txn-12345",
  "status": "declined",
  "error": "validation error: transaction amount not match order amount: 10000, 5000"
}

Если транзакция не была создана:

{
  "success": false,
  "error": "system error"
}

Пример запроса (curl)

curl -X POST "https://api.example.com/pay" \
  -H "X-Key-ID: your-key-id" \
  -H "X-Signature: base64-encoded-signature" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "order-12345",
    "amount": 10000,
    "external_id": "ext-12345"
  }'

Статусы заказов

Статусы заказа

API возвращает статус заказа в поле status ответов. Доступны следующие статусы:

Статус Значение Финальный Описание
new Новый заказ Нет Заказ создан и готов к оплате. В этом статусе заказ может быть оплачен через эндпоинт /pay
in_progress Заказ в процессе Нет Заказ находится в процессе оплаты. Транзакция создана, но еще не завершена
completed Заказ завершен Да Заказ успешно оплачен. Транзакция завершена без ошибок
declined Заказ отклонен Да Заказ отклонен из-за ошибки при оплате. Транзакция завершена с ошибкой
expired Заказ просрочен Нет Срок действия заказа истек (TTL). Заказ автоматически переходит в этот статус при истечении срока действия

Жизненный цикл заказа

1. Создание заказа
   └─> status: "new"
   
2. Начало оплаты (POST /pay)
   ├─> Заказ: status: "in_progress"
   
3. Завершение оплаты
   ├─> Если успешно (error отсутствует):
   │   ├─> Заказ: status: "completed"
   │
   └─> Если ошибка (error указан):
       ├─> Заказ: status: "declined"

4. Истечение срока действия (TTL)
   └─> Заказ: status: "expired" (автоматически)

5. Оплата просроченного заказа (POST /pay для заказа со статусом expired)
   ├─> Если успешно (error отсутствует):
   │   ├─> Заказ: status: "completed"
   │
   └─> Если ошибка (error указан):
       ├─> Заказ: status: "declined"

Важные замечания

  1. Оплата заказа: Заказ может быть оплачен в статусе new или expired. Если заказ уже в статусе in_progress, completed или declined, попытка оплаты вернет ошибку. При оплате заказа в статусе expired статус изменится в соответствии с результатом транзакции (completed или declined).
  2. Просроченные заказы: Заказ может иметь срок действия (TTL). Если срок истек, заказ автоматически переходит в статус expired. Заказ в статусе expired может быть оплачен.
  3. Повторная оплата: После завершения транзакции (статус completed или declined) заказ не может быть оплачен повторно. Для новой попытки оплаты необходимо создать новый заказ.
  4. Статус в ответе Check: Эндпоинт /check возвращает текущий статус заказа. Если заказ в статусе new, он готов к оплате.
  5. Статус в ответе Pay: Эндпоинт /pay возвращает финальный статус заказа после завершения транзакции (completed или declined).

Обработка ошибок

HTTP коды ответов

Код Описание
200 OK Запрос успешно обработан
400 Bad Request Ошибка валидации, проверки подписи или бизнес-логики
500 Internal Server Error Внутренняя ошибка сервера

Формат ошибок

Все ошибки возвращаются в формате JSON:

{
  "success": false,
  "error": "Описание ошибки"
}

Типичные ошибки

Ошибки валидации

Ошибки подписи

Ошибки бизнес-логики


Дополнительная информация

Безопасность

  1. Храните приватные ключи в безопасности: Никогда не коммитьте приватные ключи в систему контроля версий
  2. Используйте HTTPS: Всегда используйте HTTPS для передачи данных
  3. Проверяйте сертификаты: Убедитесь, что используете правильный сертификат для проверки подписи ответов (если применимо)
  4. Ротация ключей: Регулярно обновляйте ключи в соответствии с политикой безопасности

Поддержка

При возникновении проблем:

  1. Проверьте формат JSON в теле запроса
  2. Убедитесь, что используете правильный X-Key-ID
  3. Проверьте, что подпись формируется корректно (сравните с примерами)
  4. Проверьте соответствие приватного ключа и сертификата (см. раздел Генерация сертификата)
  5. Проверьте логи сервера (если есть доступ)
  6. Обратитесь к администратору API с указанием:
    • X-Key-ID
    • MD5 хеш публичного ключа (если указан в ошибке)
    • Пример запроса (без чувствительных данных)

Генерация сертификата

Для работы с API необходимо сгенерировать пару ключей (приватный ключ и сертификат). Приватный ключ используется для формирования подписи запросов, а сертификат передается администратору API для настройки вашего X-Key-ID.

Полный пример генерации

Выполните команды последовательно:

# 1. Генерация приватного ключа
openssl genrsa -out private_key.pem 2048

# 2. Генерация самоподписанного сертификата
openssl req -new -x509 -key private_key.pem -out certificate.crt -days 36500 \
  -subj "/C=RU/ST=Moscow/L=Moscow/O=OOO MAP/CN=OOO MAP"

Параметры команды генерации ключа:

Параметры команды генерации сертификата:

Результат:

Важно:

Полезные команды для работы с сертификатами

Просмотр информации о сертификате

openssl x509 -in certificate.crt -text -noout

Проверка срока действия сертификата

openssl x509 -in certificate.crt -noout -dates

Проверка соответствия ключа и сертификата

# MD5 хеш публичного ключа из сертификата
openssl x509 -noout -modulus -in certificate.crt | openssl md5

# MD5 хеш публичного ключа из приватного ключа
openssl rsa -noout -modulus -in private_key.pem | openssl md5

Хеши должны совпадать, если ключ и сертификат соответствуют друг другу.

Используйте эту команду для проверки соответствия ключа и сертификата, если сервер возвращает следующую ошибку:

{
  "success": false,
  "error": "signature verification failed: public key MD5 hash: e0916118770af2f7dca5403adf4a9ef9"
}

Сравните MD5 хеш из ошибки с хешем, полученным из вашего приватного ключа. Если хеши не совпадают, значит используется неправильный приватный ключ или сертификат.


Пример формирования подписи

Ниже приведен практический пример формирования подписи для запроса к API.

Исходные данные

В примерах ниже указаны приватный и публичный ключ (сертификат), соответствующие друг другу. MD5 hash публичного и приватного ключа: e0916118770af2f7dca5403adf4a9ef9.

Тело запроса (payload):

{"order_id":"order-12345","amount":10000,"external_id":"ext-12345"}

Полный пример (bash)

#!/bin/bash

# Тело запроса
PAYLOAD='{"order_id":"order-12345","amount":10000,"external_id":"ext-12345"}'

# Сохраняем payload
echo -n "$PAYLOAD" > payload.json

# Сохраняем приватный ключ
cat > private_key.pem << 'EOF'
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAsW+Lcu8SSUVHDL0GMsEAOQVkjMrBLoCUcpb+icwbRG8aI+En
OWv5lg6x6tayXR/8accVtKiT9aPeGRW00NGFwm5Uhyx0wlUfAH/ZYWYfbQndRdOI
nI6mEJrgO3Q3lNMhfdXIdVVDij5KGdF7OVfGETRGceqKgXVU6Ku1BiJBsHxf7dKO
/f++kXdVNF4QW7N+tp5LluE/GsA0NNWvRQrM5rlqwstwsCiTbMQugnKRYTy8gKRk
kNLaZma+v/854z9zbtbmCe8iHedLNx4Q9A+kRr5cJDQG1QOsoh95hj62dtlKA8Cp
ZVNil1zrBZ8fqXWG1Q9p4qeg05EwGLbKuPgrHwIDAQABAoIBAE3xlwttE9ZV9WkW
HAPtnmBuCwaCBqyb1RolVDlKDOxZ9dyvCmECGoidefCUUZPw+hupVdKffyeXzXet
flmAwMZxWvZqQ/weEllQ0Dgl+UYX4DjNPKSxUSfYMQpM+iGJQZwabk2En/+5bym6
SOwer6ZdFVjzU9acqSjwTLweDkctD8gsTakDMAp55I3uYClpqJF+9eHyeoLednUj
uhJEsBwaCMhUH3kwmT0RXJdImbOgswRB3tVT9ADnLRTF7fVfWU3H90nscPKQVWK8
XfyJi0t96U9PiPOnS3LLCqdOt7R8TZxGiYy5munHb34WIRDksNnfItNZgXHPtUEW
nH2mgiECgYEA5vsCiar55c3273CWL6RqK9MdgPvNjQw0IiL73PcEVI9106H5xuxQ
w1+843GzxYOcA5Z6hSC4W+rcoVxTUPxeaujd3aI0jjzB1bKhnjnJyzl3devlD82N
g0BH5myWDAL/Drnt+AIuCRhmXA+bL58Sop11Zvy7pHGG6RcF/He+TLECgYEAxKfC
zKjsdXLgM+X9cbBTSs3hmS7qtz8wLMAIA9pGyyOJTEmZkEARTnCd4xmXZ1YfpSN+
PfPGAWny7+xtBqKOj2sLZ1cYAEtPDr+vLBaR7obC8szxwziUkoYXcaT0BNmG208M
5PykpVf+eFxuYscMFA/DqBX2wZomLACmxSEEqM8CgYALXcTFM4Wymk0RgU/SrluP
JodoJnv5+eTC3UfZmso6wwiATpM1B8H9q0NnSdwX1x8hShFjZbXMyCTtR9bNwG2B
A06ProC5kyHSu0SavatBdeV8Bwyxl2LkV5ByNVu44Zjdh6a/MpRDXFEFLLttP32y
RN9XHw64y+FgrQJdZyMVMQKBgCLaVKTu/1FldaTdCwj+JhTo3iXG8eReN4yG0CTW
p5tTBf9WP/gX0rljLihUnce7tMoQu0wBi0Mu4tZwwXXek4OJhjDfd6p9rlo/0Kzw
pxZuHdjoR6TAv1wklb0XbgP6BXOS1Ac1W3zOVpRAVXP+MP7ROGzuz5fKWR+NUgE3
89pzAoGAF9h3ORH0mzEPYPYlZgiVj78aXsySSpz0AIo62qaFTvYUDE6310wdlzkF
zUULvRilHWR0lMEUzij55d/cGx79oJ2sg7jKtBt8Wi2p/JlKlsITiXitOVAUSKmO
p5Qz2mJsvXiNBGZClWBcmOqA97JzIHc4B5V2SPP9JNsAObjsZi4=
-----END RSA PRIVATE KEY-----
EOF

# Вычисляем SHA-256 хеш и подписываем его
SIGNATURE=$(openssl dgst -sha256 -sign private_key.pem payload.json | base64)

# Выводим результат
echo "X-Signature: $SIGNATURE"

# Очистка
rm -f payload.json private_key.pem

Результат

После выполнения скрипта вы получите подпись в формате base64, которую нужно использовать в заголовке X-Signature:

X-Signature: JU2Y+dqw1pk5pENGjeos6TjfNPheSUEFh2wjt2QE+QSw+UeQQ38+54VOEtF4VbQPXx2hcpZXmeNcO9vmn+L6VqIrd4G6OTEonYKbde49izWJexIrvC7rTPUf1DTSSQjQcwgjeCTR/VbJ9RcasHv3jPAntPgJe6WnDcTaOiD58TOCIK2onGBJLJrktnhPIqkbQeOwQsytWiu0qYfVOVAJhHmTIHKgfh01aktyFCxZwml1vPuW5S3nXy1286+COSmPCwLqG12WmrXxkj3oAzhvO3JEaABZyzMt8/xlOiWC5SSGgP9OfY2Osk280928K65NNfW8ddG3i78taW7rTpCZRg==

Пример готового запроса:

curl -X POST "https://api.example.com/pay" \
  -H "X-Key-ID: your-key-id" \
  -H "X-Signature: JU2Y+dqw1pk5pENGjeos6TjfNPheSUEFh2wjt2QE+QSw+UeQQ38+54VOEtF4VbQPXx2hcpZXmeNcO9vmn+L6VqIrd4G6OTEonYKbde49izWJexIrvC7rTPUf1DTSSQjQcwgjeCTR/VbJ9RcasHv3jPAntPgJe6WnDcTaOiD58TOCIK2onGBJLJrktnhPIqkbQeOwQsytWiu0qYfVOVAJhHmTIHKgfh01aktyFCxZwml1vPuW5S3nXy1286+COSmPCwLqG12WmrXxkj3oAzhvO3JEaABZyzMt8/xlOiWC5SSGgP9OfY2Osk280928K65NNfW8ddG3i78taW7rTpCZRg==" \
  -H "Content-Type: application/json" \
  -d '{"order_id":"order-12345","amount":10000,"external_id":"ext-12345"}'

Проверка подписи

Для проверки корректности формирования подписи можно использовать следующий скрипт. Скрипт проверяет, что подпись была создана с помощью приватного ключа, соответствующего публичному ключу из сертификата.

Скрипт проверки подписи

#!/bin/bash

# Параметры
PAYLOAD='{"order_id":"order-12345","amount":10000,"external_id":"ext-12345"}'
SIGNATURE="JU2Y+dqw1pk5pENGjeos6TjfNPheSUEFh2wjt2QE+QSw+UeQQ38+54VOEtF4VbQPXx2hcpZXmeNcO9vmn+L6VqIrd4G6OTEonYKbde49izWJexIrvC7rTPUf1DTSSQjQcwgjeCTR/VbJ9RcasHv3jPAntPgJe6WnDcTaOiD58TOCIK2onGBJLJrktnhPIqkbQeOwQsytWiu0qYfVOVAJhHmTIHKgfh01aktyFCxZwml1vPuW5S3nXy1286+COSmPCwLqG12WmrXxkj3oAzhvO3JEaABZyzMt8/xlOiWC5SSGgP9OfY2Osk280928K65NNfW8ddG3i78taW7rTpCZRg=="

CERT="-----BEGIN CERTIFICATE-----
MIIDiTCCAnGgAwIBAgIUE1slBc9rLKOtRSAKpTOZuByJ3UswDQYJKoZIhvcNAQEL
BQAwUzELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9z
Y293MRAwDgYDVQQKDAdPT08gTUFQMRAwDgYDVQQDDAdPT08gTUFQMCAXDTI1MTEw
ODE1MTUwNFoYDzIxMjUxMDE1MTUxNTA0WjBTMQswCQYDVQQGEwJSVTEPMA0GA1UE
CAwGTW9zY293MQ8wDQYDVQQHDAZNb3Njb3cxEDAOBgNVBAoMB09PTyBNQVAxEDAO
BgNVBAMMB09PTyBNQVAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx
b4ty7xJJRUcMvQYywQA5BWSMysEugJRylv6JzBtEbxoj4Sc5a/mWDrHq1rJdH/xp
xxW0qJP1o94ZFbTQ0YXCblSHLHTCVR8Af9lhZh9tCd1F04icjqYQmuA7dDeU0yF9
1ch1VUOKPkoZ0Xs5V8YRNEZx6oqBdVToq7UGIkGwfF/t0o79/76Rd1U0XhBbs362
nkuW4T8awDQ01a9FCszmuWrCy3CwKJNsxC6CcpFhPLyApGSQ0tpmZr6//znjP3Nu
1uYJ7yId50s3HhD0D6RGvlwkNAbVA6yiH3mGPrZ22UoDwKllU2KXXOsFnx+pdYbV
D2nip6DTkTAYtsq4+CsfAgMBAAGjUzBRMB0GA1UdDgQWBBTZpDlJyLzXMhMaBReB
rK2OKvZehDAfBgNVHSMEGDAWgBTZpDlJyLzXMhMaBReBrK2OKvZehDAPBgNVHRMB
Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZssMBGG4xp7i4zp5ZOOWmYBSS
Wb5DYdytreSRbEeB+mXC2Kp1hZ7sET2YANjTctDX3KS42IreiCK5VUK+JDvIZsE3
25/6eI92QdWIzpjUI9QI92pPgyyPqPhI0GQ7kkHF2HYrsvtNlKD5sA0Q4o6xjZrK
bN4StPPs9qSu+66+PqB9ZRN7F7orOAfa2bmLBOBXpt/3aRb2Ua1+gNYlJSfEF9wP
r6hnARwAZNj20DkxuN36FXqf9BQ07BgieWvYtl9COnaBnM+/pWrPKSyvzU2FR98o
KZxQPNQmDWQ33g149rG6j19rkaZWSvfAR9PbqJI8Rpr1tcnPNE2SiSOtZ9EI
-----END CERTIFICATE-----"


# Временные файлы
TMP_CERT=$(mktemp)
TMP_PUBKEY=$(mktemp)
TMP_SIGNATURE=$(mktemp)
TMP_PAYLOAD=$(mktemp)
trap "rm -f $TMP_CERT $TMP_PUBKEY $TMP_SIGNATURE $TMP_PAYLOAD" EXIT

# Запись сертификата во временный файл
echo "$CERT" > "$TMP_CERT"

# Запись payload во временный файл
echo -n "$PAYLOAD" > "$TMP_PAYLOAD"

# Декодирование base64 подписи во временный файл
if ! echo "$SIGNATURE" | base64 -d > "$TMP_SIGNATURE" 2>/dev/null; then
    echo "✗ Ошибка: не удалось декодировать подпись из base64"
    exit 1
fi

# Извлечение публичного ключа из сертификата
if ! openssl x509 -in "$TMP_CERT" -pubkey -noout > "$TMP_PUBKEY" 2>/dev/null; then
    echo "✗ Ошибка: не удалось извлечь публичный ключ из сертификата"
    exit 1
fi

# Проверка подписи (RSA с SHA256, как в коде)
if openssl dgst -sha256 -verify "$TMP_PUBKEY" -signature "$TMP_SIGNATURE" "$TMP_PAYLOAD"; then
    echo "✓ Подпись верна"
    exit 0
else
    echo "✗ Подпись неверна"
    exit 1
fi

Описание скрипта

Скрипт выполняет следующие действия:

  1. Подготовка данных: Сохраняет тело запроса (payload), подпись в формате base64 и сертификат в переменные.
  2. Декодирование подписи: Декодирует подпись из base64 в бинарный формат.
  3. Извлечение публичного ключа: Извлекает публичный ключ из сертификата.
  4. Проверка подписи: Использует openssl dgst для проверки подписи с алгоритмом SHA-256 и RSA PKCS#1 v1.5.

Использование

  1. Сохраните скрипт в файл (например, verify_signature.sh).
  2. Убедитесь, что у файла есть права на выполнение: chmod +x verify_signature.sh.
  3. Замените значения переменных PAYLOAD, SIGNATURE и CERT на ваши данные.
  4. Запустите скрипт: ./verify_signature.sh.

Результат