Dla integratorów · plan Pro

API i webhooki

REST API do odczytu voucherów, zamówień i realizacji oraz webhooki powiadamiające Twój system o zdarzeniach. Dokumentacja wersji v1.

Na tej stronie
API i webhooki są częścią planu Pro. Token wygenerujesz w panelu, w ustawieniach firmy. Jeśli abonament wygaśnie, API przestaje odpowiadać (kod SUBSCRIPTION_INACTIVE) — dane pozostają nienaruszone.

Szybki start

Sprawdź, czy token działa:

curl -H "Authorization: Bearer TWOJ_TOKEN" \
     https://vouchermarket.pl/api/v1/me

W odpowiedzi dostaniesz dane firmy, plan i uprawnienia tokenu:

{
  "success": true,
  "data": {
    "company": { "uuid": "…", "display_name": "Salon Luna", "city": "Wrocław" },
    "plan":    { "name": "Pro", "slug": "pro" },
    "token":   { "name": "Integracja GabinetOnline", "abilities": ["vouchers:read","codes:redeem"] }
  }
}

Uwierzytelnianie

Każde zapytanie wymaga nagłówka Authorization: Bearer <token>. Token generujesz w panelu — przy tworzeniu wybierasz, co ma móc robić. Token widać tylko raz, w momencie utworzenia; potem przechowujemy wyłącznie jego skrót.

Dostępne uprawnienia:

UprawnienieCo odblokowuje
vouchers:readLista i szczegóły Twoich voucherów
orders:readZamówienia i historia realizacji
codes:readSprawdzenie statusu kodu przed realizacją
codes:redeemRealizacja kodu

Token jest zawsze związany z jedną firmą — nie trzeba (i nie da się) przekazywać company_id w zapytaniu. Każda odpowiedź zawiera wyłącznie Twoje dane.

Konwencje i limity

Kody błędów, które warto obsłużyć:

KodHTTPZnaczenie
MISSING_TOKEN401Brak nagłówka Authorization
INVALID_TOKEN401Token nieznany lub odwołany
TOKEN_EXPIRED401Token po dacie ważności
SUBSCRIPTION_INACTIVE402Abonament nieopłacony
PLAN_WITHOUT_API403Plan inny niż Pro
INSUFFICIENT_SCOPE403Token bez wymaganego uprawnienia
NOT_FOUND404Zasób nie istnieje lub nie należy do firmy
ALREADY_USED / EXPIRED / CANCELED409Kod nie nadaje się do realizacji

Endpointy

EndpointUprawnienieOpis
GET/api/v1/me—Firma, plan, uprawnienia tokenu
GET/api/v1/vouchersvouchers:readLista voucherów. Filtry: published, q
GET/api/v1/vouchers/{uuid}vouchers:readSzczegóły vouchera
GET/api/v1/ordersorders:readZamówienia. Filtry: payment_status, from, to
GET/api/v1/orders/{uuid}orders:readZamówienie wraz z kodami
GET/api/v1/redemptionsorders:readHistoria realizacji
GET/api/v1/codes/{code}codes:readStatus kodu przed realizacją
POST/api/v1/codes/{code}/redeemcodes:redeemRealizacja kodu

Realizacja vouchera

Najpierw sprawdź kod — dowiesz się, czy nadaje się do realizacji i ile na nim zostało:

curl -H "Authorization: Bearer TWOJ_TOKEN" \
     https://vouchermarket.pl/api/v1/codes/LUNA-9F3K-220
{
  "success": true,
  "data": {
    "code": "LUNA-9F3K-220",
    "status": "active",
    "can_redeem": true,
    "voucher_name": "Masaż relaksacyjny 90 min",
    "face_value": 220.0,
    "remaining_value": 220.0,
    "sessions_total": null,
    "expires_at": "2027-10-26T12:00:00+02:00"
  }
}

Następnie zrealizuj. Dla voucherów kwotowych możesz rozliczyć część wartości:

curl -X POST \
     -H "Authorization: Bearer TWOJ_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"amount": 150, "note": "Wizyta 26.10"}' \
     https://vouchermarket.pl/api/v1/codes/LUNA-9F3K-220/redeem
{
  "success": true,
  "message": "Voucher zrealizowany pomyślnie.",
  "amount_used": 150.0,
  "remaining_value": 70.0,
  "new_status": "partially_used"
}
Vouchery na usługę lub pakiet ignorują pole amount — każda realizacja odejmuje jedno wejście. Pole remaining_value dotyczy wyłącznie voucherów kwotowych.

Webhooki

Zamiast odpytywać API w pętli, podaj adres, na który mamy wysyłać zdarzenia. Konfigurujesz go w panelu: adres musi używać HTTPS, wybierasz listę zdarzeń i dostajesz sekret do weryfikacji podpisu.

Każde zadanie przychodzi jako POST z nagłówkami:

NagłówekZawartość
X-VM-EventNazwa zdarzenia, np. order.paid
X-VM-DeliveryUUID dostawy — ten sam przy ponowieniach, użyj go do idempotencji
X-VM-TimestampCzas uniksowy podpisania
X-VM-Signaturesha256=<HMAC>

Ponowienia: odpowiedz kodem 2xx, żeby potwierdzić odbiór. Przy innym kodzie lub braku odpowiedzi ponawiamy do 5 razy z odstępami 1 min, 5 min, 15 min, 1 h i 3 h. Po 20 kolejnych nieudanych dostawach endpoint jest automatycznie wyłączany — zobaczysz to w panelu i włączysz go z powrotem jednym kliknięciem.

Idempotencja: ta sama dostawa może przyjść więcej niż raz (np. gdy Twoja odpowiedź nie dotarła). Zapamiętuj X-VM-Delivery i pomijaj powtórzenia.

Weryfikacja podpisu

Podpisujemy <timestamp>.<surowe ciało żądania> algorytmem HMAC-SHA256 Twoim sekretem. Porównaj wynik metodą odporną na pomiar czasu i odrzuć żądania starsze niż kilka minut:

// PHP
$raw       = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_VM_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_VM_SIGNATURE'] ?? '';

// odrzuć stare żądania (ochrona przed powtórzeniem)
if (abs(time() - (int) $timestamp) > 300) {
    http_response_code(400);
    exit;
}

$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $raw, $SEKRET);

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit;
}

$event = json_decode($raw, true);
// … obsłuż $event['event'] i $event['data']
http_response_code(200);

Zdarzenia

Każde żądanie ma tę samą kopertę:

{
  "event": "order.paid",
  "created_at": "2026-10-26T13:45:02+02:00",
  "data": { … }
}
order.paid — voucher sprzedany i opłacony

Wysyłane raz, przy faktycznym przejściu zamówienia na status opłacony. Powtórzone powiadomienie od operatora płatności nie generuje drugiego zdarzenia.

"data": {
  "order_uuid": "…",
  "order_number": "VM/2026/000420",
  "voucher_name": "Masaż relaksacyjny 90 min",
  "code": "LUNA-9F3K-220",
  "customer_email": "klient@example.com",
  "recipient_name": "Anna",
  "total": 220.0,
  "company_payout": 211.2,
  "currency": "PLN",
  "payment_method": "stripe",
  "paid_at": "2026-10-26T13:45:02+02:00"
}
code.redeemed — voucher zrealizowany

Wysyłane zarówno przy realizacji w panelu, jak i przez API. Pole status mówi, czy voucher został wykorzystany w całości.

"data": {
  "code": "LUNA-9F3K-220",
  "voucher_name": "Masaż relaksacyjny 90 min",
  "method": "qr",
  "amount_used": 150.0,
  "remaining_value": 70.0,
  "sessions_total": null,
  "sessions_used": 1,
  "status": "partially_used",
  "redeemed_at": "2026-10-26T14:02:11+02:00"
}
order.refunded — zwrot zamówienia

Wysyłane po zatwierdzeniu zwrotu i unieważnieniu kodu.

"data": {
  "order_uuid": "…",
  "order_number": "VM/2026/000420",
  "voucher_name": "Masaż relaksacyjny 90 min",
  "total": 220.0,
  "currency": "PLN",
  "reason": "Rezygnacja klienta",
  "refunded_at": "2026-10-27T09:12:00+02:00"
}
voucher.published — zmiana w katalogu

Jedno zdarzenie dla publikacji i wycofania — rozróżniasz je po polu is_published.

"data": {
  "voucher_uuid": "…",
  "name": "Masaż relaksacyjny 90 min",
  "slug": "masaz-relaksacyjny-90-min-salon-luna-wroclaw",
  "type": "service",
  "price": 220.0,
  "currency": "PLN",
  "is_published": true,
  "published_at": "2026-10-26T10:00:00+02:00"
}

Potrzebujesz czegoś, czego tu nie ma?

Napisz, z czym integrujesz — rozwijamy API pod realne zastosowania.

Napisz do nas