<p>Die <strong>Kalkhan API</strong> ermöglicht es Ihnen, Lizenzprüfung, Aktivierung und Händler-Integration direkt aus Ihren eigenen Systemen heraus abzuwickeln. Dieses Dokument beschreibt den Aufbau von Anfragen und Antworten, das Authentifizierungsverfahren und die Fehlercodes der Endpunkte.</p> <h2 id="genel-bakis" style="scroll-margin-top:120px;">Überblick</h2> <p>Die Kalkhan API ist eine ressourcenorientierte <strong>REST</strong>-Schnittstelle. Alle Anfrage- und Antwortkörper werden im Format <strong>JSON</strong> mit der Zeichenkodierung <strong>UTF-8</strong> übertragen. Verbindungen müssen mit <strong>TLS 1.2 oder höher</strong> verschlüsselt sein; unverschlüsselte Anfragen (HTTP) werden abgelehnt.</p> <div class="table-responsive"> <table class="table" style="background:#ffffff;"> <tbody> <tr><th scope="row" style="width:32%;">Base URL</th><td><code>https://api.kalkhan.com.tr/v1</code></td></tr> <tr><th scope="row">Protokoll</th><td>HTTPS / TLS 1.2+ (verpflichtend)</td></tr> <tr><th scope="row">Datenformat</th><td>JSON · <code>Content-Type: application/json; charset=utf-8</code></td></tr> <tr><th scope="row">Zeichenkodierung</th><td>UTF-8</td></tr> <tr><th scope="row">Datumsformat</th><td>ISO 8601 (UTC), z. B. <code>2027-07-23T00:00:00Z</code></td></tr> <tr><th scope="row">Ratenbegrenzung</th><td>120 Anfragen pro Minute (je Schlüssel)</td></tr> </tbody> </table> </div> <p>Alle Endpunktadressen werden an die Base URL angehängt. Die vollständige Adresse des Endpunkts zur Lizenzprüfung lautet beispielsweise <code>https://api.kalkhan.com.tr/v1/licenses/verify</code>.</p> <h2 id="kurulum" style="scroll-margin-top:120px;">Einrichtung & Einstieg</h2> <p>Wenn Sie die Integration von Grund auf beginnen, führen Sie die folgenden Schritte der Reihe nach aus. Der gesamte Vorgang dauert im Schnitt <strong>15 Minuten</strong> und erfordert keinerlei Kalkhan Software auf Ihrem Server – eine Umgebung, die HTTPS-Anfragen senden kann, genügt.</p> <h3 style="margin-top:26px;">Voraussetzungen</h3> <ul> <li>Ein freigegebenes <strong>Kalkhan Händlerkonto</strong> (Antrag über das <a href="/iletisim">Kontaktformular</a>)</li> <li>Eine Serverumgebung mit Unterstützung für TLS 1.2 oder höher (PHP 7.4+, Node.js 16+, .NET 6+, Python 3.8+ usw.)</li> <li>Eine Möglichkeit, den API-Schlüssel <em>serverseitig</em> abzulegen (Umgebungsvariablen empfohlen)</li> </ul> <h3 style="margin-top:26px;">Schritt für Schritt</h3> <ol> <li style="margin-bottom:10px;"><strong>Melden Sie sich im Händlerportal an.</strong> Sobald Ihr Konto von der Administration freigegeben wurde, erhalten Sie Zugang zum Portal.</li> <li style="margin-bottom:10px;"><strong>Erzeugen Sie einen API-Schlüssel.</strong> Legen Sie im Portal unter “API-Schlüssel” zunächst einen Testschlüssel mit dem Präfix <code>klk_test_</code> an.</li> <li style="margin-bottom:10px;"><strong>Definieren Sie die Umgebungsvariablen.</strong> Tragen Sie Schlüssel und Base URL in die Konfiguration Ihrer Anwendung ein (Beispiel unten).</li> <li style="margin-bottom:10px;"><strong>Senden Sie die erste Anfrage.</strong> Rufen Sie den Health-Endpunkt auf, um die Verbindung zu prüfen.</li> <li style="margin-bottom:10px;"><strong>Hinterlegen Sie Ihre Webhook-Adresse.</strong> Tragen Sie im Portal Ihre HTTPS-Webhook-Adresse ein, um Lizenzereignisse in Echtzeit zu empfangen.</li> <li><strong>Gehen Sie live.</strong> Nach abgeschlossenen Tests wechseln Sie auf einen <code>klk_live_</code>-Schlüssel; im Code ändert sich nur der Schlüssel selbst.</li> </ol> <h4 style="margin-top:22px;">1) Umgebungskonfiguration</h4> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;"># .env KALKHAN_API_URL=https://api.kalkhan.com.tr/v1 KALKHAN_API_KEY=klk_test_xxxxxxxxxxxxxxxx KALKHAN_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx</code></pre> <h4 style="margin-top:22px;">2) Verbindungstest</h4> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">curl -s https://api.kalkhan.com.tr/v1/ping \ -H "Authorization: Bearer $KALKHAN_API_KEY" # Erfolgreiche Antwort { "ok": true, "env": "test", "dealer": "BYI-1001" }</code></pre> <div style="background:#eef3ff;border-left:4px solid #3F82FB;padding:16px 18px;border-radius:8px;margin:22px 0;"> <strong>Tipp:</strong> In der Testumgebung erzeugte Lizenzen fließen nicht in die Abrechnung ein und lassen sich jederzeit löschen. Testen Sie vor dem Live-Betrieb die vollständigen Abläufe zum Erstellen, Prüfen und Stornieren von Lizenzen mit dem Testschlüssel. </div> <h2 id="kimlik-dogrulama" style="scroll-margin-top:120px;">Authentifizierung</h2> <p>API-Anfragen werden über das <strong>Bearer</strong>-Schema mit Ihrem API-Schlüssel autorisiert. Der Schlüssel muss bei jeder Anfrage im Header <code>Authorization</code> mitgesendet werden.</p> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">Authorization: Bearer klk_live_xxx Content-Type: application/json; charset=utf-8 Accept: application/json</code></pre> <p>Ihren API-Schlüssel erzeugen und widerrufen Sie im Bereich “API-Schlüssel” <strong>Ihres Händlerportals</strong>. Für die Testumgebung gelten Schlüssel mit dem Präfix <code>klk_test_</code>, für die Produktivumgebung <code>klk_live_</code>.</p> <p><strong>Sicherheitshinweis:</strong> Der API-Schlüssel ist ein geheimes Zugangsmerkmal und gehört ausschließlich auf den Server. Betten Sie ihn nicht in Browser-Code, mobile App-Pakete oder öffentliche Code-Repositories ein. Bei Verdacht auf Kompromittierung widerrufen Sie ihn sofort im Portal und erzeugen einen neuen.</p> <h2 id="lisans-dogrulama" style="scroll-margin-top:120px;">Lizenzprüfung</h2> <p>Dient dazu, Gültigkeit, Produkt und Restlaufzeit eines Lizenzschlüssels abzufragen. Lizenzschlüssel haben das Format <code>KLKN-XXXX-XXXX-XXXX-XXXX</code>.</p> <div class="table-responsive"> <table class="table" style="background:#ffffff;"> <thead> <tr><th>Methode</th><th>Endpunkt</th><th>Berechtigung</th></tr> </thead> <tbody> <tr><td><strong>POST</strong></td><td><code>/licenses/verify</code></td><td>Bearer API-Schlüssel</td></tr> </tbody> </table> </div> <h3>Anfrage</h3> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">POST /v1/licenses/verify HTTP/1.1 Host: api.kalkhan.com.tr Authorization: Bearer klk_live_xxx Content-Type: application/json; charset=utf-8 { "key": "KLKN-4F2A-9C71-B3D8-A15E", "deviceId": "WIN-8f2c41ab9d", "productId": "kalkhan-av-windows" }</code></pre> <h3>Antwort (200 OK)</h3> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">{ "key": "KLKN-4F2A-9C71-B3D8-A15E", "status": "active", "product": "Kalkhan Anti-Virüs (Windows)", "edition": "business", "seats": 25, "seatsUsed": 11, "expiresAt": "2027-03-14T23:59:59Z" }</code></pre> <div class="table-responsive"> <table class="table" style="background:#ffffff;"> <thead> <tr><th style="width:24%;">Feld</th><th style="width:18%;">Typ</th><th>Beschreibung</th></tr> </thead> <tbody> <tr><td><code>status</code></td><td>string</td><td><code>active</code>, <code>expired</code>, <code>suspended</code> oder <code>cancelled</code></td></tr> <tr><td><code>product</code></td><td>string</td><td>Anzeigename des Produkts, zu dem die Lizenz gehört</td></tr> <tr><td><code>edition</code></td><td>string</td><td><code>home</code>, <code>business</code> oder <code>enterprise</code></td></tr> <tr><td><code>seats</code></td><td>integer</td><td>Gesamtzahl der von der Lizenz abgedeckten Geräte</td></tr> <tr><td><code>expiresAt</code></td><td>string</td><td>Ablaufdatum im ISO-8601-Format (UTC)</td></tr> </tbody> </table> </div> <p>Für einen ungültigen oder im System nicht vorhandenen Schlüssel wird <code>404 not_found</code> zurückgegeben, für einen formal fehlerhaften Schlüssel <code>400 invalid_request</code>.</p> <h2 id="lisans-olusturma" style="scroll-margin-top:120px;">Lizenz erstellen (Händler)</h2> <p>Dient autorisierten Händlern dazu, im Namen von Endkunden neue Lizenzen zu erzeugen. Dieser Endpunkt kann ausschließlich mit API-Schlüsseln aufgerufen werden, die über eine Händlerberechtigung verfügen.</p> <div class="table-responsive"> <table class="table" style="background:#ffffff;"> <thead> <tr><th>Methode</th><th>Endpunkt</th><th>Berechtigung</th></tr> </thead> <tbody> <tr><td><strong>POST</strong></td><td><code>/licenses</code></td><td>Händler-API-Schlüssel</td></tr> </tbody> </table> </div> <h3>Anfrage</h3> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">POST /v1/licenses HTTP/1.1 Host: api.kalkhan.com.tr Authorization: Bearer klk_live_xxx Content-Type: application/json; charset=utf-8 { "productId": "kalkhan-av-windows", "edition": "business", "seats": 25, "customer": { "name": "Örnek Bilişim A.Ş.", "email": "muhasebe@ornekbilisim.com.tr", "taxId": "1234567890", "taxOffice": "Ümraniye", "address": "Mithatpaşa Cd. No:116, Ümraniye/İstanbul" } }</code></pre> <h3>Antwort (201 Created)</h3> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">{ "key": "KLKN-7B10-CE45-2F93-D8A6", "status": "active", "product": "Kalkhan Anti-Virüs (Windows)", "edition": "business", "seats": 25, "amount": 34750.00, "currency": "TRY", "expiresAt": "2027-07-23T23:59:59Z" }</code></pre> <p><strong>Bei Geschäftskunden</strong> (wenn <code>edition</code> den Wert <code>business</code> oder <code>enterprise</code> hat) ist die Übermittlung von <code>customer.taxId</code> (Steuernummer), <code>customer.taxOffice</code> (Finanzamt) und <code>customer.address</code> für die Rechnungsstellung <strong>verpflichtend</strong>. Fehlen diese Felder, wird die Anfrage mit <code>400 invalid_request</code> abgelehnt. Bei Privatlizenzen (<code>home</code>) sind die Steuerfelder optional.</p> <p>Innerhalb kurzer Zeit wiederholte Anfragen für denselben Kunden und dasselbe Produkt werden mit <code>409 duplicate</code> beantwortet; um doppelte Lizenzen zu vermeiden, empfehlen wir, Ihrer Anfrage den Header <code>Idempotency-Key</code> hinzuzufügen.</p> <h2 id="bayi-api" style="scroll-margin-top:120px;">Händler-API</h2> <p>Für die Abfrage des eigenen Lizenzbestands und der Verkaufsübersicht stehen Händlern zwei Endpunkte zur Verfügung.</p> <div class="table-responsive"> <table class="table" style="background:#ffffff;"> <thead> <tr><th style="width:14%;">Methode</th><th style="width:32%;">Endpunkt</th><th>Beschreibung</th></tr> </thead> <tbody> <tr><td><strong>GET</strong></td><td><code>/dealer/licenses</code></td><td>Seitenweise Liste der Lizenzen des Händlers</td></tr> <tr><td><strong>GET</strong></td><td><code>/dealer/stats</code></td><td>Übersicht zu Verkaufszahlen, Umsatz und anstehenden Verlängerungen</td></tr> </tbody> </table> </div> <h3>Parameter für die Seitennavigation</h3> <div class="table-responsive"> <table class="table" style="background:#ffffff;"> <thead> <tr><th style="width:18%;">Parameter</th><th style="width:16%;">Standard</th><th>Beschreibung</th></tr> </thead> <tbody> <tr><td><code>page</code></td><td>1</td><td>Nummer der angezeigten Seite (beginnt bei 1)</td></tr> <tr><td><code>limit</code></td><td>25</td><td>Datensätze pro Seite (höchstens 100)</td></tr> <tr><td><code>status</code></td><td>—</td><td>Optionaler Filter: <code>active</code>, <code>expired</code>, <code>cancelled</code></td></tr> </tbody> </table> </div> <h3>Anfrage und Antwort</h3> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">GET /v1/dealer/licenses?page=2&limit=25&status=active HTTP/1.1 Host: api.kalkhan.com.tr Authorization: Bearer klk_live_xxx { "data": [ { "key": "KLKN-4F2A-9C71-B3D8-A15E", "status": "active", "product": "Kalkhan Anti-Virüs (Windows)", "edition": "business", "seats": 25, "customer": "Örnek Bilişim A.Ş.", "expiresAt": "2027-03-14T23:59:59Z" } ], "pagination": { "page": 2, "limit": 25, "total": 143, "totalPages": 6 } }</code></pre> <p>Der Endpunkt <code>GET /dealer/stats</code> liefert eine Periodenübersicht: Gesamtzahl aktiver Lizenzen, monatliche Verkaufszahlen, Gesamtumsatz sowie die Anzahl der Lizenzen, die in den nächsten 30 Tagen ablaufen.</p> <h2 id="webhooks" style="scroll-margin-top:120px;">Webhooks</h2> <p>Mit Webhooks übermitteln Sie Änderungen im Lebenszyklus einer Lizenz in Echtzeit an Ihre Systeme. Die Zieladresse legen Sie im Händlerportal fest; Kalkhan sendet an diese Adresse eine <code>POST</code>-Anfrage mit JSON-Körper.</p> <div class="table-responsive"> <table class="table" style="background:#ffffff;"> <thead> <tr><th style="width:34%;">Ereignis</th><th>Wann wird es ausgelöst?</th></tr> </thead> <tbody> <tr><td><code>license.created</code></td><td>Wenn eine neue Lizenz erstellt wird</td></tr> <tr><td><code>license.expired</code></td><td>Wenn die Laufzeit der Lizenz endet</td></tr> <tr><td><code>license.cancelled</code></td><td>Wenn eine Lizenz storniert oder eine Rückerstattung verarbeitet wird</td></tr> </tbody> </table> </div> <h3>Beispiel für einen Ereigniskörper</h3> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">POST /webhooks/kalkhan HTTP/1.1 X-Kalkhan-Signature: t=1784937600,v1=9f8c1d2b7e4a63f05c8b1a9d4e7f2c30b6a5d8e1f4c7b0a3d6e9f2c5b8a1d4e7 Content-Type: application/json; charset=utf-8 { "id": "evt_01J9X2K7QF", "event": "license.created", "createdAt": "2026-07-23T09:41:02Z", "data": { "key": "KLKN-7B10-CE45-2F93-D8A6", "product": "Kalkhan Anti-Virüs (Windows)", "edition": "business", "seats": 25, "expiresAt": "2027-07-23T23:59:59Z" } }</code></pre> <h3>Signaturprüfung</h3> <p>Jede Webhook-Anfrage wird mit dem Header <code>X-Kalkhan-Signature</code> signiert. Die Signatur ist der Hash aus Zeitstempel und rohem Anfragekörper, berechnet mit <strong>HMAC-SHA256</strong> und Ihrem geheimen Webhook-Schlüssel. Prüfen Sie die Signatur unbedingt, bevor Sie die Anfrage verarbeiten, und weisen Sie Zeitstempel zurück, die älter als 5 Minuten sind.</p> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">// Node.js — Signaturprüfung const crypto = require('crypto'); function dogrula(rawBody, header, secret) { const parts = Object.fromEntries( header.split(',').map(p => p.split('=')) ); const beklenen = crypto .createHmac('sha256', secret) .update(parts.t + '.' + rawBody) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(beklenen), Buffer.from(parts.v1) ); }</code></pre> <p>Antwortet Ihr Server nicht mit <code>2xx</code>, wird das Ereignis in wachsenden Abständen (1 Min., 5 Min., 30 Min., 2 Std., 12 Std.) bis zu fünfmal erneut zugestellt. Gehen Sie davon aus, dass dasselbe Ereignis mehrfach eintreffen kann, und prüfen Sie anhand des Feldes <code>id</code> auf Wiederholungen.</p> <h2 id="hata-kodlari" style="scroll-margin-top:120px;">Fehlercodes</h2> <p>Fehler werden mit den üblichen HTTP-Statuscodes zurückgegeben und enthalten im Körper einen maschinenlesbaren <code>code</code> sowie eine erläuternde <code>message</code>.</p> <div class="table-responsive"> <table class="table" style="background:#ffffff;"> <thead> <tr><th style="width:14%;">HTTP</th><th style="width:26%;">Code</th><th>Beschreibung</th></tr> </thead> <tbody> <tr><td><strong>400</strong></td><td><code>invalid_request</code></td><td>Parameter fehlt oder hat ein falsches Format</td></tr> <tr><td><strong>401</strong></td><td><code>unauthorized</code></td><td>API-Schlüssel fehlt, ist ungültig oder wurde widerrufen</td></tr> <tr><td><strong>403</strong></td><td><code>forbidden</code></td><td>Der Schlüssel hat keine Berechtigung für diesen Endpunkt</td></tr> <tr><td><strong>404</strong></td><td><code>not_found</code></td><td>Angeforderte Lizenz oder Ressource nicht gefunden</td></tr> <tr><td><strong>409</strong></td><td><code>duplicate</code></td><td>Datensatz existiert bereits; doppelte Anfrage blockiert</td></tr> <tr><td><strong>429</strong></td><td><code>rate_limited</code></td><td>Ratenbegrenzung überschritten; warten Sie den Header <code>Retry-After</code> ab</td></tr> <tr><td><strong>500</strong></td><td><code>server_error</code></td><td>Unerwarteter Fehler auf Serverseite</td></tr> </tbody> </table> </div> <h3>Beispiel einer Fehlerantwort</h3> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">HTTP/1.1 400 Bad Request Content-Type: application/json; charset=utf-8 { "error": { "code": "invalid_request", "message": "Das Feld customer.taxId ist für Geschäftslizenzen verpflichtend.", "field": "customer.taxId", "requestId": "req_01J9X2K7QF" } }</code></pre> <p>Wenn Sie eine Supportanfrage stellen, beschleunigt die Angabe der <code>requestId</code> aus der Antwort die Bearbeitung erheblich.</p> <h2 id="sdk" style="scroll-margin-top:120px;">SDK & Beispiele</h2> <p>Offizielle SDKs befinden sich in Vorbereitung. Bis dahin rufen Sie die API direkt über HTTP auf. Nachfolgend finden Sie die Lizenzprüfung als cURL- und JavaScript-Beispiel.</p> <h3>cURL</h3> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">curl -X POST https://api.kalkhan.com.tr/v1/licenses/verify \ -H "Authorization: Bearer klk_live_xxx" \ -H "Content-Type: application/json; charset=utf-8" \ -d '{ "key": "KLKN-4F2A-9C71-B3D8-A15E", "deviceId": "WIN-8f2c41ab9d", "productId": "kalkhan-av-windows" }'</code></pre> <h3>JavaScript (fetch)</h3> <pre><code style="background:#0F2453;color:#e6edff;padding:18px;border-radius:10px;overflow-x:auto;font-size:14px;line-height:1.6;display:block;">const API_KEY = process.env.KALKHAN_API_KEY; // serverseitig aufbewahren async function lisansDogrula(key, deviceId) { const res = await fetch( 'https://api.kalkhan.com.tr/v1/licenses/verify', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json; charset=utf-8' }, body: JSON.stringify({ key, deviceId, productId: 'kalkhan-av-windows' }) } ); if (!res.ok) { const { error } = await res.json(); throw new Error(`${error.code}: ${error.message}`); } return res.json(); // { status, product, edition, seats, expiresAt } } lisansDogrula('KLKN-4F2A-9C71-B3D8-A15E', 'WIN-8f2c41ab9d') .then(lisans => console.log(lisans.status, lisans.expiresAt)) .catch(err => console.error('Prüfung fehlgeschlagen:', err.message));</code></pre> <p>Diese Beispiele sind ausschließlich für den serverseitigen Einsatz gedacht. Betten Sie den API-Schlüssel nicht in Code ein, der im Browser ausgeführt wird; leiten Sie Prüfanfragen aus Ihren Client-Anwendungen über Ihr eigenes Backend.</p> <!-- Uyarı Kutusu Start --> <div role="note" style="margin-top:40px;background:#FFF8E6;border:1px solid #F0D28A;border-left:5px solid #C9A227;border-radius:12px;padding:22px 24px;display:flex;gap:16px;align-items:flex-start;"> <span style="flex:0 0 auto;color:#C9A227;font-size:22px;line-height:1.2;"><i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i></span> <span style="color:#4a3d12;font-size:15px;line-height:1.7;"> <strong style="display:block;color:#0F2453;font-size:16px;margin-bottom:4px;">Geschlossene Beta</strong> Die API befindet sich derzeit in einer geschlossenen Beta; den Zugang beantragen Sie unter <a href="mailto:bilgi@kalkhan.com.tr" style="color:#0F2453;font-weight:600;">bilgi@kalkhan.com.tr</a>. Während der Beta sind rückwärtsinkompatible Änderungen an den Endpunkten möglich. </span> </div> <!-- Uyarı Kutusu End -->