Быстрый старт
От ключа до первой карты — пять запросов с примерами.
Понадобятся ключ API (идентификатор и секрет) и curl, openssl, uuidgen в терминале. Примеры на Node.js, Python и PHP используют функцию pp() из раздела Аутентификация и подпись — скопируйте её в проект.
Начните с тестового ключа
Ключ pp_test_… работает с тестовым аккаунтом: карты не настоящие, баланс виртуальный, остальное поведение — как у боевого. Запросите его у менеджера вместе с боевым.
0. Ключ в переменных окружения
export PP_KEY_ID="pp_test_9f2c1a7e"
export PP_SECRET="ваш-секрет"Секрет храните как пароль: в менеджере секретов, не в репозитории. Его показывают один раз при выпуске ключа.
1. Проверьте баланс
Самый простой подписанный запрос: GET без тела.
TS=$(date +%s)
SIG=$(printf '%s\n%s\n%s\n%s' "$TS" "GET" "/api/partner/v1/balance" "" \
| openssl dgst -sha256 -hmac "$PP_SECRET" -hex | awk '{print $NF}')
curl -sS "https://api.plativputi.com/api/partner/v1/balance" \
-H "X-Key-Id: $PP_KEY_ID" -H "X-Timestamp: $TS" -H "X-Signature: $SIG"const balance = await pp("GET", "/balance");balance = pp("GET", "/balance")$balance = pp('GET', '/balance');{ "currency": "USD", "balance": "1250.5", "hold": "0", "available": "1250.5" }Если пришёл 401 — проверьте часы сервера (окно ±5 минут) и строку подписи, см. частые ошибки подписи.
2. Выберите тариф
TS=$(date +%s)
SIG=$(printf '%s\n%s\n%s\n%s' "$TS" "GET" "/api/partner/v1/tariffs" "" \
| openssl dgst -sha256 -hmac "$PP_SECRET" -hex | awk '{print $NF}')
curl -sS "https://api.plativputi.com/api/partner/v1/tariffs" \
-H "X-Key-Id: $PP_KEY_ID" -H "X-Timestamp: $TS" -H "X-Signature: $SIG"const { items } = await pp("GET", "/tariffs");
const tariffId = items[0].id;tariff_id = pp("GET", "/tariffs")["items"][0]["id"]$tariffId = pp('GET', '/tariffs')['items'][0]['id'];{
"items": [
{
"id": "3f6c2a1e-8b4d-4e2a-9c71-5d0e8f1a2b34",
"name": "Visa Virtual USD",
"issue_fee": "5",
"close_fee": "1",
"close_refund_fee_pct": "0",
"topup_fee_pct": "3",
"topup_fee_const": "0.5",
"tx_fee_ok": "0.2",
"tx_fee_declined": "0.5",
"min_topup": "10",
"max_topup": "5000"
}
]
}Как из этих полей складывается цена — в разделе Деньги.
3. Зарегистрируйте держателя
external_id — ваш идентификатор клиента. Используйте один и тот же для всех карт одного человека: первая карта нового держателя ждёт проверки личности у эмитента, следующие — нет.
BODY='{"external_id":"cust-42","first_name":"Ivan","last_name":"Petrov","email":"ivan.petrov@example.com"}'
TS=$(date +%s)
SIG=$(printf '%s\n%s\n%s\n%s' "$TS" "POST" "/api/partner/v1/cardholders" "$BODY" \
| openssl dgst -sha256 -hmac "$PP_SECRET" -hex | awk '{print $NF}')
curl -sS -X POST "https://api.plativputi.com/api/partner/v1/cardholders" \
-H "X-Key-Id: $PP_KEY_ID" -H "X-Timestamp: $TS" -H "X-Signature: $SIG" \
-H "Content-Type: application/json" -d "$BODY"const holder = await pp("POST", "/cardholders", {
external_id: "cust-42",
first_name: "Ivan",
last_name: "Petrov",
email: "ivan.petrov@example.com",
});holder = pp("POST", "/cardholders", body={
"external_id": "cust-42",
"first_name": "Ivan",
"last_name": "Petrov",
"email": "ivan.petrov@example.com",
})$holder = pp('POST', '/cardholders', [
'external_id' => 'cust-42',
'first_name' => 'Ivan',
'last_name' => 'Petrov',
'email' => 'ivan.petrov@example.com',
]);{
"id": "b7e1c9d2-4a3f-4c8e-a6b5-1f2e3d4c5b6a",
"external_id": "cust-42",
"first_name": "Ivan",
"last_name": "Petrov",
"email": "ivan.petrov@example.com",
"created_at": "2026-10-07T09:12:44Z"
}Имя и фамилия — латиницей, как они будут на карте. Повторный вызов с тем же external_id вернёт этого же держателя.
4. Выпустите карту
amount — сколько положить на карту сразу. Idempotency-Key обязателен: сгенерируйте его один раз и сохраните до отправки — при обрыве связи повторите запрос с тем же ключом.
BODY='{"tariff_id":"3f6c2a1e-8b4d-4e2a-9c71-5d0e8f1a2b34","holder_external_id":"cust-42","amount":"50"}'
IDEM=$(uuidgen)
TS=$(date +%s)
SIG=$(printf '%s\n%s\n%s\n%s\n%s' "$TS" "POST" "/api/partner/v1/cards" "$BODY" "$IDEM" \
| openssl dgst -sha256 -hmac "$PP_SECRET" -hex | awk '{print $NF}')
curl -sS -X POST "https://api.plativputi.com/api/partner/v1/cards" \
-H "X-Key-Id: $PP_KEY_ID" -H "X-Timestamp: $TS" -H "X-Signature: $SIG" \
-H "Idempotency-Key: $IDEM" -H "Content-Type: application/json" -d "$BODY"import { randomUUID } from "node:crypto";
const idempotencyKey = randomUUID(); // сохраните рядом с вашим заказом
const op = await pp("POST", "/cards", {
tariff_id: tariffId,
holder_external_id: "cust-42",
amount: "50",
}, { idempotencyKey });import uuid
idempotency_key = str(uuid.uuid4()) # сохраните рядом с вашим заказом
op = pp("POST", "/cards", body={
"tariff_id": tariff_id,
"holder_external_id": "cust-42",
"amount": "50",
}, idempotency_key=idempotency_key)$idempotencyKey = uuid4(); // сохраните рядом с вашим заказом
$op = pp('POST', '/cards', [
'tariff_id' => $tariffId,
'holder_external_id' => 'cust-42',
'amount' => '50',
], $idempotencyKey);Ответ 202 Accepted — операция, а не карта:
{
"id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
"kind": "issue",
"status": "processing",
"hold_amount": "57.0464",
"net_amount": "50",
"created_at": "2026-10-07T09:13:02Z"
}hold_amount зарезервирован на балансе: 5 USD выпуск + 50 USD на карту + 2.0464 USD комиссия пополнения.
5. Дождитесь результата
Опрашивайте операцию, пока status равен processing. В боевой интеграции вместо опроса подпишитесь на события.
OP=c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f
TS=$(date +%s)
SIG=$(printf '%s\n%s\n%s\n%s' "$TS" "GET" "/api/partner/v1/operations/$OP" "" \
| openssl dgst -sha256 -hmac "$PP_SECRET" -hex | awk '{print $NF}')
curl -sS "https://api.plativputi.com/api/partner/v1/operations/$OP" \
-H "X-Key-Id: $PP_KEY_ID" -H "X-Timestamp: $TS" -H "X-Signature: $SIG"let current = op;
while (current.status === "processing") {
await new Promise((r) => setTimeout(r, 3000));
current = await pp("GET", `/operations/${op.id}`);
}
if (current.status !== "succeeded") throw new Error(current.error_code);
const card = await pp("GET", `/cards/${current.card_id}`);import time
current = op
while current["status"] == "processing":
time.sleep(3)
current = pp("GET", f"/operations/{op['id']}")
if current["status"] != "succeeded":
raise RuntimeError(current.get("error_code"))
card = pp("GET", f"/cards/{current['card_id']}")$current = $op;
while ($current['status'] === 'processing') {
sleep(3);
$current = pp('GET', '/operations/' . $op['id']);
}
if ($current['status'] !== 'succeeded') {
throw new RuntimeException($current['error_code'] ?? 'failed');
}
$card = pp('GET', '/cards/' . $current['card_id']);{
"id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"state": "active",
"holder_external_id": "cust-42",
"masked_number": "**** **** **** 4821",
"payment_system": "VISA",
"currency": "USD",
"balance": "50",
"expires_at": "10/29",
"created_at": "2026-10-07T09:13:02Z"
}Карта готова. Что дальше:
- пополнить —
POST /cards/{id}/topup; - показать держателю реквизиты — Реквизиты карты и PCI;
- закрыть с возвратом остатка —
POST /cards/{id}/close; - перейти с опроса на вебхуки — События и вебхуки.