Playfood. Developers Войти Получить ключ

Playfood Partner API

Такси и курьер из точки А в точку Б для партнёров-разработчиков. Версия 1.0 · OpenAPI (JSON)

Начало работы

Типичный сценарий: POST /price → показать цену → POST /orders с offer_id → GET /orders/{id} (или вебхуки) → при необходимости POST /orders/{id}/cancel.

Авторизация и лимиты

Передайте ключ в одном из заголовков:

Authorization: Bearer pf_live_XXXXXXXXXXXXXXXX
X-Api-Key: pf_live_XXXXXXXXXXXXXXXX

Тарифы

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":"…"
}}

Отмена

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)

Ошибки

Все ошибки имеют вид {"error":{"code":"…","message":"…"}}; при ошибке валидации есть ещё fields.

HTTPcodeКогда
401unauthorizedнет ключа, ключ неверный, отозван или выключен
403service_not_allowedуслуга не подключена для вашего ключа
404order_not_foundнет такого заказа у вашего ключа
409cannot_cancelпоездка уже началась или завершена
409too_many_cancellationsс этого номера слишком много отмен за 24 часа
422validation_failedневерные поля (подробно в fields)
422idempotency_key_requiredнет или неверный заголовок Idempotency-Key
422invalid_route, same_pointsне две точки или А и Б совпадают
422out_of_service_areaточка А вне зоны работы Playfood
422no_tariffs, tariff_unavailableв точке А нет тарифов услуги / тариф недоступен
422offer_expired, offer_mismatchпредложение истекло или сделано для другого маршрута/тарифа — запросите цену заново
422invalid_phoneтелефон неполный
429rate_limitedпревышен лимит запросов