Playfood Partner API
Такси и курьер из точки А в точку Б для партнёров-разработчиков. Версия 1.0 · OpenAPI (JSON)
Начало работы
- Базовый адрес:
https://api.playfood.pro/partner/v1. Запросы и ответы — JSON (UTF-8). - Ключ: подайте заявку на developers.playfood.pro, после одобрения выпустите ключ в кабинете. Он показывается один раз — храните его на сервере, не в мобильном приложении или браузере.
- Маршрут — строго две точки: А (подача / забор посылки) и Б (назначение / доставка). Промежуточных точек нет.
- Услуги:
taxi— поездка,courier— курьерская доставка. - Оплата — наличными водителю, по обычному тарифу Playfood. Цена — в тенге (
KZT). - Заказ создаётся на номер телефона пассажира (отправителя); водитель звонит на этот номер.
Типичный сценарий: POST /price → показать цену → POST /orders с offer_id → GET /orders/{id} (или вебхуки) → при необходимости POST /orders/{id}/cancel.
Авторизация и лимиты
Передайте ключ в одном из заголовков:
Authorization: Bearer pf_live_XXXXXXXXXXXXXXXX
X-Api-Key: pf_live_XXXXXXXXXXXXXXXX
- Неверный, отозванный или выключенный ключ →
401 unauthorized. - Лимит — по умолчанию 60 запросов в минуту на ключ (может быть другим по договорённости); создание заказов — не более 20 в минуту. При превышении —
429 rate_limitedи заголовокRetry-After(секунды).
Тарифы
GET/tariffs?service=taxi&lat=..&lng=.. — тарифы услуги, доступные в точке.
curl "https://api.playfood.pro/partner/v1/tariffs?service=taxi&lat=42.5170&lng=69.7860" \
-H "Authorization: Bearer $PLAYFOOD_KEY"
{"data":[{"id":11,"name":"Эконом","service":"taxi","currency":"KZT",
"pickup_fee":500,"price_per_km":100,"min_fare":600,"is_night":false}]}
Цена
POST/price — стоимость поездки А → Б во всех тарифах услуги (или в одном — tariff_id). Каждое предложение имеет offer_id: цена держится 10 минут.
curl -X POST "https://api.playfood.pro/partner/v1/price" \
-H "Authorization: Bearer $PLAYFOOD_KEY" -H "Content-Type: application/json" \
-d '{
"service": "taxi",
"route": [
{"lat": 42.5170, "lng": 69.7860, "address": "Базар"},
{"lat": 42.5301, "lng": 69.8012, "address": "ул. Навои, 5"}
]
}'
{"data":[{"offer_id":"9b2f…","tariff":{"id":11,"name":"Эконом"},"price":800,"currency":"KZT",
"distance_km":3.1,"duration_min":9,"surge":1.0,"expires_at":"2026-11-13T12:10:00+05:00"}]}
Цена учитывает зоны, ночной тариф и повышенный спрос. Без offer_id цена пересчитывается в момент заказа и может отличаться.
Создать заказ
POST/orders — обязательный заголовок Idempotency-Key (до 64 символов: буквы, цифры, . _ : -). Повтор запроса с тем же ключом не создаёт второй заказ, а возвращает первый (200, заголовок Idempotent-Replayed: true). Новый заказ — 201.
| Поле | Обяз. | Описание |
|---|---|---|
service | да | taxi или courier |
route | да | ровно 2 точки {lat, lng, address}: А и Б |
contact.phone | да | телефон пассажира (такси) или отправителя (курьер), например +77001234567 |
contact.name | имя | |
offer_id | из /price — заказ по зафиксированной цене | |
tariff_id | тариф; по умолчанию — первый тариф услуги в точке А | |
recipient.name, recipient.phone | курьер: получатель в точке Б | |
comment | комментарий водителю (до 500 символов) | |
external_id | ваш номер заказа (можно искать: GET /orders?external_id=) |
curl -X POST "https://api.playfood.pro/partner/v1/orders" \
-H "Authorization: Bearer $PLAYFOOD_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: order-10045" \
-d '{
"service": "courier",
"offer_id": "9b2f…",
"route": [
{"lat": 42.5170, "lng": 69.7860, "address": "Склад, ул. Абая 1"},
{"lat": 42.5301, "lng": 69.8012, "address": "ул. Навои, 5, кв. 3"}
],
"contact": {"name": "Склад", "phone": "+77001234567"},
"recipient": {"name": "Алишер", "phone": "+77017654321"},
"comment": "Хрупкое",
"external_id": "10045"
}'
Ответ — объект заказа (см. Отслеживание) со статусом searching. Сохраните data.id.
Отслеживание
GET/orders/{id} — статус, исполнитель, машина, координаты водителя и ETA. Опрашивайте не чаще раза в 5–10 секунд или используйте вебхуки.
curl "https://api.playfood.pro/partner/v1/orders/7f1c…" -H "Authorization: Bearer $PLAYFOOD_KEY"
{"data":{
"id":"7f1c…","number":"97574","external_id":"10045","service":"courier",
"status":"driver_assigned",
"tariff":{"id":14,"name":"Доставка"},
"price":{"amount":800,"currency":"KZT","final":false,"payment_method":"cash"},
"route":[{"lat":42.517,"lng":69.786,"address":"Склад, ул. Абая 1"},
{"lat":42.5301,"lng":69.8012,"address":"ул. Навои, 5, кв. 3"}],
"distance_km":3.1,"duration_min":9,
"contact":{"name":"Склад","phone":"+77001234567"},
"recipient":{"name":"Алишер","phone":"+77017654321"},
"performer":{"name":"Бахтиёр К.","phone":"+77005556677","rating":4.93,"photo_url":"https://…",
"car":{"brand":"Chevrolet","model":"Cobalt","color":"Белый","plate":"123ABC13"}},
"location":{"lat":42.5201,"lng":69.7902,"heading":135,"updated_at":"2026-11-13T12:03:41+05:00"},
"eta_minutes":4,
"can_cancel":true,"cancellation":null,
"created_at":"…","arrived_at":null,"started_at":null,"completed_at":null,"cancelled_at":null,"updated_at":"…"
}}
performerиlocationпоявляются, когда водитель назначен;location—null, если координаты старше 15 минут.eta_minutes— минуты до точки А, пока водитель едет к вам; во время поездки — до точки Б.- GET
/orders?status=active|completed|cancelled&external_id=..&limit=20— список ваших заказов (новые сверху).
Отмена
POST/orders/{id}/cancel — можно до начала поездки (статусы searching, driver_assigned, driver_arrived). Платы за отмену нет. После начала поездки — 409 cannot_cancel. Повторная отмена отменённого заказа возвращает его без ошибки.
curl -X POST "https://api.playfood.pro/partner/v1/orders/7f1c…/cancel" \
-H "Authorization: Bearer $PLAYFOOD_KEY" -H "Content-Type: application/json" \
-d '{"reason": "Клиент передумал"}'
Статусы
| status | Что значит |
|---|---|
searching | ищем водителя |
driver_assigned | водитель назначен и едет к точке А |
driver_arrived | водитель на месте в точке А |
in_progress | поездка / доставка идёт |
completed | выполнен; price.final = true — итоговая сумма |
cancelled | отменён; cancellation.by: partner, client, driver, operator, system (например, водитель не найден) |
Водитель может отказаться от заказа после назначения — тогда статус возвращается в searching, и поиск продолжается. Итоговая цена может отличаться от предварительной (ожидание, фактический маршрут).
Вебхуки
Укажите адрес вебхука в кабинете — при каждой смене статуса или водителя мы отправляем на него POST:
POST https://partner.example.com/playfood/webhook
Content-Type: application/json
X-Playfood-Event: order.updated
X-Playfood-Delivery: 0d3c… (уникальный id доставки)
X-Playfood-Timestamp: 1763020000
X-Playfood-Signature: sha256=5f2a…
{"id":"0d3c…","event":"order.updated","occurred_at":"…",
"from_status":"searching","status":"driver_assigned",
"order":{ …тот же объект, что в GET /orders/{id}… }}
Проверка подписи: HMAC-SHA256("{X-Playfood-Timestamp}.{тело запроса}", секрет_вебхука) в hex, сравнить с X-Playfood-Signature без префикса sha256=. Секрет — в кабинете, рядом с адресом вебхука.
// PHP
$raw = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $_SERVER['HTTP_X_PLAYFOOD_TIMESTAMP'] . '.' . $raw, $secret);
if (! hash_equals($expected, $_SERVER['HTTP_X_PLAYFOOD_SIGNATURE'] ?? '')) { http_response_code(401); exit; }
# Python
expected = 'sha256=' + hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, signature_header)
- Ответьте
2xxв течение 10 секунд. Иначе повторим до 6 раз (через 10 с, 30 с, 1, 2 и 5 минут). - Доставки могут прийти не по порядку или повторно: ориентируйтесь на
order.updated_atиX-Playfood-Delivery. При сомнении — запроситеGET /orders/{id}.
Ошибки
Все ошибки имеют вид {"error":{"code":"…","message":"…"}}; при ошибке валидации есть ещё fields.
| HTTP | code | Когда |
|---|---|---|
| 401 | unauthorized | нет ключа, ключ неверный, отозван или выключен |
| 403 | service_not_allowed | услуга не подключена для вашего ключа |
| 404 | order_not_found | нет такого заказа у вашего ключа |
| 409 | cannot_cancel | поездка уже началась или завершена |
| 409 | too_many_cancellations | с этого номера слишком много отмен за 24 часа |
| 422 | validation_failed | неверные поля (подробно в fields) |
| 422 | idempotency_key_required | нет или неверный заголовок Idempotency-Key |
| 422 | invalid_route, same_points | не две точки или А и Б совпадают |
| 422 | out_of_service_area | точка А вне зоны работы Playfood |
| 422 | no_tariffs, tariff_unavailable | в точке А нет тарифов услуги / тариф недоступен |
| 422 | offer_expired, offer_mismatch | предложение истекло или сделано для другого маршрута/тарифа — запросите цену заново |
| 422 | invalid_phone | телефон неполный |
| 429 | rate_limited | превышен лимит запросов |