Qeron Billing API
REST API pro integraci billing systému Qeron do externího softwaru. Vytvoř fakturu, přijmi platbu přes Stripe nebo bankovní převod, dostávej webhooky o stavu předplatného.
https://api.qeron.cz/api/v1Všechny požadavky musí být přes HTTPS. API vrací JSON, přijímá JSON.
🔑 Autentizace
Každý požadavek vyžaduje API klíč v hlavičce. Klíče spravuj na sprava.qeron.cz/api-keys.php.
# Primární - X-Api-Key header X-Api-Key: tvuj-api-klic # Alternativa - Bearer token Authorization: Bearer tvuj-api-klic
POST /api/v1/checkout
Vytvoří fakturu (proforma) pro zákazníka, odešle mu email s platebními instrukcemi
a vrátí odkaz na platbu. Pro subscription_days > 0 vytvoří také
Stripe Subscription Checkout s opakující se platbou.
Tělo požadavku
| Pole | Typ | Popis | |
|---|---|---|---|
product | string | POVINNÉ | Název produktu nebo služby |
amount | number | POVINNÉ | Částka bez DPH v dané měně |
email | string | POVINNÉ | Email zákazníka - faktura se pošle sem |
nazev | string | POVINNÉ* | Název firmy nebo jméno (* nebo ico) |
vat | number | volitelné | Sazba DPH: 0, 12 nebo 21 (výchozí: 21) |
currency | string | volitelné | CZK nebo EUR (výchozí: CZK) |
ico | string | volitelné | IČO - slouží k vyhledání existujícího zákazníka |
dic | string | volitelné | DIČ |
ulice | string | volitelné | Fakturační adresa - ulice |
mesto | string | volitelné | Město |
psc | string | volitelné | PSČ |
telefon | string | volitelné | Telefon zákazníka |
subscription_days | int | volitelné | 0 = jednorázová platba, >0 = předplatné na N dní (Stripe Subscription) |
pay_method | string | volitelné | stripe / qr / any (výchozí: any) |
subscription_discount | object | volitelné | Časově omezená Stripe sleva pro předplatné. Pouze se subscription_days > 0 a platbou kartou. Obsahuje percent_off, duration_months, volitelně code a label. |
callback_url | string | volitelné | Webhook URL - přepíše výchozí z API klíče. Musí být HTTPS na veřejné IP. |
return_url | string | volitelné | URL pro tlačítko "Zpět do aplikace" na platební stránce po zaplacení |
sandbox | bool | volitelné | true = Stripe testovací klíče, false = Stripe live klíče. Výchozí: false |
stat | string | volitelné | Název státu odběratele (např. "Slovensko", "Německo"). Výchozí: "Česká republika". |
reverse_charge | bool | volitelné | Přenesení daňové povinnosti pro EU B2B (Reverse Charge). Vyžaduje "vat": 0. Na faktuře se zobrazí RC doložka. Výchozí: false. |
curl -X POST https://api.qeron.cz/api/v1/checkout \ -H "X-Api-Key: tvuj-klic" \ -H "Content-Type: application/json" \ -d '{ "product": "Tarif Pro", "amount": 818.18, "vat": 21, "currency": "CZK", "email": "zakaznik@firma.cz", "nazev": "Firma s.r.o.", "ico": "12345678", "subscription_days": 30, "pay_method": "stripe", "subscription_discount": { "percent_off": 50, "duration_months": 3, "code": "WEBZAP50", "label": "WebZap 50 %" }, "callback_url": "https://tvujweb.cz/api/billing-webhook", "return_url": "https://tvujweb.cz/dashboard?activated=1", "sandbox": false }'
$payload = [ 'product' => 'Tarif Pro', 'amount' => 818.18, 'vat' => 21, 'currency' => 'CZK', 'email' => 'zakaznik@firma.cz', 'nazev' => 'Firma s.r.o.', 'ico' => '12345678', 'subscription_days' => 30, 'pay_method' => 'stripe', 'subscription_discount' => [ 'percent_off' => 50, 'duration_months' => 3, 'code' => 'WEBZAP50', 'label' => 'WebZap 50 %', ], 'callback_url' => 'https://tvujweb.cz/api/billing-webhook', 'return_url' => 'https://tvujweb.cz/dashboard?activated=1', 'sandbox' => false, ]; $resp = file_get_contents('https://api.qeron.cz/api/v1/checkout', false, stream_context_create(['http' => [ 'method' => 'POST', 'header' => "Content-Type: application/json\r\nX-Api-Key: {$apiKey}\r\n", 'content' => json_encode($payload), ]]) ); $data = json_decode($resp, true); // Pro předplatné přesměruj na Stripe Checkout header('Location: ' . ($data['stripe_url'] ?? $data['payment_url'])); exit;
import requests resp = requests.post( "https://api.qeron.cz/api/v1/checkout", headers={"X-Api-Key": api_key}, json={ "product": "Tarif Pro", "amount": 818.18, "vat": 21, "currency": "CZK", "email": "zakaznik@firma.cz", "nazev": "Firma s.r.o.", "ico": "12345678", "subscription_days": 30, "pay_method": "stripe", "subscription_discount": { "percent_off": 50, "duration_months": 3, "code": "WEBZAP50", "label": "WebZap 50 %", }, "callback_url": "https://tvujweb.cz/api/billing-webhook", "return_url": "https://tvujweb.cz/dashboard?activated=1", "sandbox": False, } ) resp.raise_for_status() data = resp.json() # Pro předplatné přesměruj na Stripe Checkout redirect_url = data.get("stripe_url") or data["payment_url"]
Odpověď 200
{
"payment_url": "https://platba.qeron.cz/faktura.php?token=abc123def456...",
"stripe_url": "https://checkout.stripe.com/c/pay/cs_live_...",
"invoice_id": 42,
"vs": "1704067200",
"token": "abc123def456ghi789...",
"is_sandbox": false,
"stripe_mode": "live",
"subscription_discount": {
"percent_off": 50,
"duration_months": 3,
"code": "WEBZAP50",
"label": "WebZap 50 %"
}
}
payment_url | Platební stránka na platba.qeron.cz - zákazník vidí fakturu, QR kód a Stripe tlačítko |
stripe_url | Přímý Stripe Checkout - pro subscription_days > 0 je to Subscription mode (opakující se platba). Je null pokud Stripe není nakonfigurován nebo pay_method=qr |
subscription_discount | Vrací použitou časově omezenou slevu pro Stripe předplatné, nebo null. amount v požadavku má zůstat plná běžná cena; Stripe odečte slevu jen po zadaný počet měsíců a potom účtuje plnou cenu. |
invoice_id | ID proformy - použij pro GET /status dotaz na stav |
vs | Variabilní symbol (Unix timestamp) - pro párování FIO platby |
token | Veřejný token faktury |
stripe_url pokud je k dispozici
- zákazník tam zadá kartu a souhlasí s opakujícím se strháváním. payment_url je záloha
pro případ QR platby nebo když Stripe není nakonfigurován. QR kód používá jako zprávu pro příjemce
první položku faktury, aby zákazník i účetnictví viděli, za co se platí.
"sandbox": true se použijí testovací Stripe klíče
(sk_test_ / pk_test_), při "sandbox": false se použijí live klíče
(sk_live_ / pk_live_) bez ohledu na globální přepínač adminu.
stripe_url povede na testovací Stripe Checkout - platit lze pouze
testovacími kartami.
Webhooky z testovacích sessions mají vlastní Stripe podpis - ujisti se, že tvůj server
zpracovává testovací webhooky správně.
GET /api/v1/status
Zjistí aktuální stav faktury nebo předplatného. Použij jako zálohu k webhookům - denní polling zachytí zameškaná volání.
Query parametry (jeden z)
| Parametr | Popis |
|---|---|
?invoice_id=42 | Interní ID faktury (z odpovědi /checkout) |
?vs=1704067200 | Variabilní symbol |
?token=abc123 | Veřejný token faktury |
curl -H "X-Api-Key: tvuj-klic" \ "https://api.qeron.cz/api/v1/status?invoice_id=42"
Odpověď 200
{
"invoice_id": 42,
"vs": "1704067200",
"cislo": "261100001",
"je_proforma": false,
"stav": "paid",
"paid": true,
"paid_at": "2026-04-30 10:30:00",
"amount": 1197.90,
"currency": "CZK",
"is_sandbox": false,
"customer": {
"email": "zakaznik@firma.cz",
"ico": "12345678",
"nazev": "Firma s.r.o."
},
"subscription": {
"stav": "active",
"obdobi_od": "2026-04-30",
"obdobi_do": "2026-05-30"
}
}
subscription je null pro jednorázové platby nebo dokud proforma nebyla zaplacena.is_sandbox: true pokud platba běží přes Stripe testovací klíče.
POST /api/v1/payment_cancel
Stornuje nezaplacenou platební výzvu vytvořenou přes API. Výzva se nemaže,
pouze se přepne do stavu cancelled. Pokud má otevřenou Stripe Checkout Session
nebo PaymentIntent, Qeron se ji pokusí zneplatnit. Pokud měla výzva callback_url,
Qeron pošle webhook payment.cancelled.
Tělo požadavku
| Pole | Typ | Popis | |
|---|---|---|---|
invoice_id | int | POVINNÉ* | ID platební výzvy z odpovědi /checkout |
vs | string | POVINNÉ* | Variabilní symbol - alternativa k invoice_id |
token | string | POVINNÉ* | Veřejný token faktury - alternativa k invoice_id |
reason | string | volitelné | Důvod storna pro audit a webhook, výchozí api_cancel |
* Stačí jeden identifikátor: invoice_id, vs nebo token.
curl -X POST https://api.qeron.cz/api/v1/payment_cancel \ -H "X-Api-Key: tvuj-klic" \ -H "Content-Type: application/json" \ -d '{ "invoice_id": 42, "reason": "customer_abandoned_checkout" }'
Odpověď 200
{
"ok": true,
"invoice_id": 42,
"vs": "1704067200",
"stav": "cancelled",
"stripe_error": null
}
Chybové stavy: 404 pokud výzva neexistuje, 403 pokud nepatří API klíči,
409 pokud už je zaplacená nebo nejde o platební výzvu.
POST /api/v1/subscription_cancel
Zruší předplatné zákazníka. Výchozí chování je zrušení na konci aktuálního
fakturačního období - zákazník má přístup do konce zaplacené periody,
žádné vracení peněz. Pro okamžité zrušení (porušení podmínek, refund) použij
"immediate": true.
Tělo požadavku
| Pole | Typ | Popis | |
|---|---|---|---|
subscription_id | int | POVINNÉ* | Interní Qeron ID předplatného |
stripe_subscription_id | string | POVINNÉ* | Stripe sub_... ID - alternativa k subscription_id |
immediate | bool | volitelné | false = zruš na konci periody (výchozí), true = zruš okamžitě |
* Stačí jedno z obou.
curl -X POST https://api.qeron.cz/api/v1/subscription_cancel \ -H "X-Api-Key: tvuj-klic" \ -H "Content-Type: application/json" \ -d '{"subscription_id": 42}'
curl -X POST https://api.qeron.cz/api/v1/subscription_cancel \ -H "X-Api-Key: tvuj-klic" \ -H "Content-Type: application/json" \ -d '{"subscription_id": 42, "immediate": true}'
Odpověď 200
{
"ok": true,
"subscription_id": 42,
"stav": "cancel_scheduled",
"obdobi_do": "2026-05-30",
"immediate": false,
"stripe_error": null
}
stav: "cancel_scheduled" = zruší se na konci periody,
Stripe doručí customer.subscription.deleted webhook —
Qeron následně pošle subscription.cancelled na tvůj callback.
stav: "cancelled" = okamžité zrušení, webhook dorazí do několika sekund.
stripe_error a předplatné zůstává aktivní
- lokálně se nikdy neruší něco, co Stripe dál účtuje. Požadavek opakuj později.
GET /api/v1/vies
Ověří DIČ DPH v evropském registru VIES (VAT Information Exchange System). Vrátí název a adresu firmy tak, jak jsou zapsány v registru. Použij před vystavením faktury s Reverse Charge pro ověření, že odběratel je platný plátce DPH v EU.
CZ04694074, SK2022489522, DE123456789.
Řecko používá kód EL (ne GR).
Query parametry
| Parametr | Popis | |
|---|---|---|
?dic=SK2022489522 | POVINNÉ | DIČ DPH ke kontrole (formát: kód státu + číslo plátce) |
curl -H "X-Api-Key: tvuj-klic" \ "https://api.qeron.cz/api/v1/vies?dic=SK2022489522"
$dic = 'SK2022489522'; $resp = file_get_contents( "https://api.qeron.cz/api/v1/vies?dic={$dic}", false, stream_context_create(['http' => [ 'header' => "X-Api-Key: {$apiKey}\r\n", ]]) ); $data = json_decode($resp, true); if ($data['valid']) { // Odběratel je platný plátce DPH - lze vystavit fakturu s RC $nazev = $data['name']; $adresa = $data['address']; }
import requests resp = requests.get( "https://api.qeron.cz/api/v1/vies", headers={"X-Api-Key": api_key}, params={"dic": "SK2022489522"}, ) data = resp.json() if data["valid"]: # Odběratel je platný plátce DPH - lze vystavit fakturu s RC nazev = data["name"] adresa = data["address"]
Odpověď 200 - platné DIČ
{
"valid": true,
"dic": "SK2022489522",
"country_code": "SK",
"vat_number": "2022489522",
"name": "Firma Slovakia s.r.o.",
"address": "Obchodná 1, 811 06 Bratislava",
"error": null
}
Odpověď 200 - neplatné nebo nenalezené DIČ
{
"valid": false,
"dic": "SK9999999999",
"country_code": "SK",
"vat_number": "9999999999",
"name": null,
"address": null,
"error": "INVALID_INPUT"
}
Endpoint vrací vždy 200 - pole valid určuje výsledek ověření.
Chybové HTTP kódy (401, 422, 429) signalizují problém s požadavkem, ne s DIČ.
error obsahuje kód z VIES API (INVALID_INPUT, SERVICE_UNAVAILABLE apod.)
nebo null při úspěchu.
SERVICE_UNAVAILABLE nebo MS_UNAVAILABLE zkus požadavek opakovat za několik minut.
Endpoint sdílí rate limit s /checkout (100 req/hod na API klíč).
Propojení s Reverse Charge
Typický flow pro EU B2B fakturu s přenesením daňové povinnosti:
/vies?dic=DE123456789 → valid: true/checkout s "vat": 0, "reverse_charge": true, "stat": "Německo", "dic": "DE123456789"🪝 Webhooky
Qeron automaticky odesílá POST na callback_url při klíčových událostech.
Callback URL musí být veřejná HTTPS adresa.
Ověření podpisu
Každý webhook obsahuje hlavičku X-Qeron-Signature s HMAC-SHA256 podpisem těla.
Klíč (HMAC secret) najdeš na stránce API klíčů v admin panelu.
$payload = file_get_contents('php://input'); $podpis = $_SERVER['HTTP_X_QERON_SIGNATURE'] ?? ''; $ocekavan = hash_hmac('sha256', $payload, QERON_HMAC_SECRET); if (!hash_equals($ocekavan, $podpis)) { http_response_code(403); exit('Neplatný podpis'); } $event = json_decode($payload, true); switch ($event['event']) { case 'invoice.paid': case 'subscription.renewed': aktivujPristup($event['customer']['email']); ulozDatum($event['customer']['email'], $event['subscription_until']); break; case 'payment.cancelled': zrusCekajiciObjednavku($event['invoice_id']); break; case 'subscription.cancelled': case 'subscription.payment_failed': vypniPristup($event['customer']['email']); break; }
import hmac, hashlib from flask import request, abort HMAC_SECRET = "tvuj-hmac-secret" def verify(payload: bytes, signature: str) -> bool: expected = hmac.new(HMAC_SECRET.encode(), payload, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature) @app.route('/api/billing-webhook', methods=['POST']) def webhook(): sig = request.headers.get('X-Qeron-Signature', '') if not verify(request.data, sig): abort(403) event = request.get_json() ev = event['event'] if ev in ('invoice.paid', 'subscription.renewed'): activate_access(event['customer']['email'], event['subscription_until']) elif ev == 'payment.cancelled': cancel_pending_order(event['invoice_id']) elif ev in ('subscription.cancelled', 'subscription.payment_failed'): revoke_access(event['customer']['email']) return '', 200
📩 invoice.paid
Zákazník zaplatil fakturu (Stripe Checkout nebo FIO bankovní převod).
Pro předplatné (subscription_days > 0) obsahuje subscription_until.
{
"event": "invoice.paid",
"invoice_id": 43,
"proforma_id": 42,
"vs": "1704067200",
"cislo": "261100001",
"amount": 1197.90,
"currency": "CZK",
"customer": {
"email": "zakaznik@firma.cz",
"ico": "12345678",
"nazev": "Firma s.r.o."
},
"paid_at": "2026-04-30 10:30:00",
"subscription_until": "2026-05-30"
}subscription_until je vyplněno pouze pro subscription_days > 0.
🧹 payment.cancelled
Nezaplacená platební výzva byla stornována přes POST /payment_cancel.
Použij pro vyčištění čekající objednávky v napojeném systému.
{
"event": "payment.cancelled",
"reason": "api_cancel",
"invoice_id": 42,
"vs": "1704067200",
"cislo": "261000042",
"stav": "cancelled",
"paid": false,
"amount": 1197.90,
"currency": "CZK",
"is_sandbox": false,
"customer": {
"email": "zakaznik@firma.cz",
"ico": "12345678",
"nazev": "Firma s.r.o."
}
}🔄 subscription.renewed
Stripe automaticky strhnul obnovu předplatného. Zákazník nic nemusel dělat.
{
"event": "subscription.renewed",
"invoice_id": 55,
"amount": 1197.90,
"currency": "CZK",
"customer": {
"email": "zakaznik@firma.cz",
"ico": "12345678",
"nazev": "Firma s.r.o."
},
"subscription_until": "2026-06-30"
}❌ subscription.cancelled
Předplatné bylo zrušeno. Může přijít ihned (okamžité zrušení) nebo na konci zaplacené periody.
{
"event": "subscription.cancelled",
"reason": "user_cancel",
"customer": {
"email": "zakaznik@firma.cz",
"nazev": "Firma s.r.o."
}
}
reason: "user_cancel" - zákazník nebo admin zrušil |
"api_cancel" - zrušeno přes API |
"payment_failed" - všechny Stripe retry selhaly |
"payment_overdue" - obnova bankovním převodem nebyla zaplacena do splatnosti
💳 subscription.payment_failed
Stripe se nepodařilo strhnout obnovu (karta bez pokrytí, expirovaná atd.).
Stripe se pokusí automaticky znovu. Teprve pokud všechny pokusy selžou,
přijde subscription.cancelled.
{
"event": "subscription.payment_failed",
"customer": {
"email": "zakaznik@firma.cz",
"nazev": "Firma s.r.o."
}
}🔁 Tok předplatného (Stripe)
GET /status jako záloha pro případ výpadku callbacku.
⚡ HTTP stavové kódy
Chybové odpovědi mají vždy tvar: { "error": "popis chyby v češtině" }