09 · Test hattı cihaz entegrasyonu
Bu rehber, test hattı cihazı üreticilerinin cihaz ölçümlerini ekspertiz.app'e göndermesi için uçtan uca entegrasyon adımlarını anlatır. Markadan bağımsızdır — her üretici aynı endpoint'i kullanır (örneklerdeki Mates, entegre olan ilk üreticidir). Endpoint referansı: test-line-measurements.md
Genel akış
┌─────────────┐ ölçüm ┌──────────────────┐ HTTPS POST ┌─────────────────┐
│ Test hattı │ ────────► │ Üretici yazılımı │ ────────────► │ ekspertiz.app │
│ cihazı │ │ (istasyon PC │ HMAC imzalı │ Partner API │
│ (fren/süsp.)│ │ yazılımı) │ └────────┬────────┘
└─────────────┘ └──────────────────┘ │ bayi kodu + plaka
▼
┌──────────────────────────┐
│ Ekspertiz raporu │
│ "Test Hattı Kontrolleri" │
│ formu — değerler otomatik │
└──────────────────────────┘
- Araç test hattından geçer, operatör plakayı cihaz yazılımına girer (veya plaka tanıma sisteminden gelir).
- Üretici yazılımı ölçüm sonuçlarını tek bir JSON paketi olarak
POST /api/v1/partner/test-line/measurements'a gönderir. Paketdekibranch_code(bayi kodu) ölçümün hangi ekspertiz bayisine ait olduğunu söyler — kurulumda istasyon yazılımına bir kez yazılır. - ekspertiz.app bayi kodundan firmayı + bayiyi bulur, plakaya açık rapor varsa değerleri anında rapora işler; yoksa havuzda bekletir ve eksper rapor açtığında tek tıkla uygular.
Sıralama serbesttir. Ölçüm rapordan önce de sonra da gönderilebilir. Rapor açıkken gelirse otomatik işlenir; önce gelirse eksper ekranında "Cihazdan ölçüm geldi" bandı olarak görünür.
Devreye alma (onboarding)
| Adım | Kim | Ne |
|---|---|---|
| 1 | ekspertiz.app | Partner hesabı + API anahtarı üretir (önce tmk_test_…, canlıya geçişte tmk_live_…). Secret yalnız bir kez gösterilir |
| 2 | ekspertiz.app | Partner'ın hangi ekspertiz firmalarına veri gönderebileceğini tanımlar (erişim kapsamı). Kapsam dışı firmalara gönderim 404 döner |
| 3 | ekspertiz.app | Cihaz kurulacak bayilerin bayi kodlarını paylaşır (örn. YVZ-SB-01). Kodlar GET /organizations/{orgId}/branches ile API'den de çekilebilir |
| 4 | Üretici | Her istasyon kurulumunda yazılıma o bayinin kodunu yazar; anahtarları güvenli saklar (üretici başına tek anahtar yeterlidir, cihaz başına değil) |
| 5 | Üretici | Test ortamında örnek gönderimleri çalıştırır (aşağıdaki senaryolar) |
| 6 | Birlikte | Canlı anahtara geçiş + ilk sahada doğrulama |
Yanlış/erişim dışı bayi koduyla gönderim 404 not_found döner — yeni
bayi eklenirken kodu bizden teyit edin.
Kimlik doğrulama — özet
Tüm istekler HMAC-SHA256 ile imzalanır. Ayrıntı:
01 · Kimlik doğrulama ·
örnek kodlar: 02 · İmza örnekleri ·
hazır script'ler: sign.node.mjs ·
sign.php · sign.py
Dört header:
X-Trameks-Key-Id: tmk_test_XXXXXXXXXXXXXXXXXXXXXX
X-Trameks-Timestamp: 1781090246 # unix saniye (UTC), ±5 dk tolerans
X-Trameks-Nonce: 9f8a3b... # istek başına benzersiz, ≥16 karakter
X-Trameks-Signature: sha256=ab12cd34... # kanonik string'in HMAC-SHA256'sı
Kanonik string (satırlar \n ile ayrılır):
POST
/api/v1/partner/test-line/measurements
← query yok: boş satır
1781090246
<nonce>
<hex SHA-256(gövde bayt'ları)>
Önemli: İmza, gövdenin gönderilen bayt'ları üzerinden hesaplanır. İmzayı hesapladıktan sonra JSON'u yeniden serialize etmeyin — aynı string'i gönderin.
Node.js (bağımlılıksız, test edilmiş örnek)
import { createHash, createHmac, randomBytes } from "node:crypto";
const KEY_ID = process.env.TMK_KEY_ID;
const SECRET = process.env.TMK_SECRET;
const BASE = "https://api.ekspertiz.app";
const PATH = "/api/v1/partner/test-line/measurements";
const body = JSON.stringify({
branch_code: "YVZ-SB-01",
plate: "06 ABC 123",
measured_at: new Date().toISOString(),
external_id: "MATES-OLCUM-90002",
measurements: { BRAKE_FRONT_LEFT: 2.4, BRAKE_FRONT_RIGHT: 2.6 },
});
const ts = String(Math.floor(Date.now() / 1000));
const nonce = randomBytes(16).toString("hex");
const bodyHash = createHash("sha256").update(body, "utf8").digest("hex");
const canonical = ["POST", PATH, "", ts, nonce, bodyHash].join("\n");
const signature = "sha256=" + createHmac("sha256", SECRET).update(canonical).digest("hex");
const res = await fetch(BASE + PATH, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Trameks-Key-Id": KEY_ID,
"X-Trameks-Timestamp": ts,
"X-Trameks-Nonce": nonce,
"X-Trameks-Signature": signature,
},
body,
});
console.log(res.status, await res.json());
C# (.NET 6+)
using System.Security.Cryptography;
using System.Text;
var keyId = Environment.GetEnvironmentVariable("TMK_KEY_ID");
var secret = Environment.GetEnvironmentVariable("TMK_SECRET");
var path = "/api/v1/partner/test-line/measurements";
var body = """
{"branch_code":"YVZ-SB-01","plate":"06 ABC 123","external_id":"MATES-OLCUM-90002","measurements":{"BRAKE_FRONT_LEFT":2.4,"BRAKE_FRONT_RIGHT":2.6}}
""";
var ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
var nonce = Convert.ToHexString(RandomNumberGenerator.GetBytes(16)).ToLower();
var bodyHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(body))).ToLower();
var canonical = string.Join("\n", "POST", path, "", ts, nonce, bodyHash);
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret!));
var signature = "sha256=" + Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(canonical))).ToLower();
using var http = new HttpClient { BaseAddress = new Uri("https://api.ekspertiz.app") };
var req = new HttpRequestMessage(HttpMethod.Post, path)
{
Content = new StringContent(body, Encoding.UTF8, "application/json"),
};
req.Headers.Add("X-Trameks-Key-Id", keyId);
req.Headers.Add("X-Trameks-Timestamp", ts);
req.Headers.Add("X-Trameks-Nonce", nonce);
req.Headers.Add("X-Trameks-Signature", signature);
var res = await http.SendAsync(req);
Console.WriteLine($"{(int)res.StatusCode} {await res.Content.ReadAsStringAsync()}");
PHP ve Python örnekleri için: 02 · İmza örnekleri.
İstemci tarafı öneriler
external_idher zaman gönderin. Ağ kopması / timeout durumunda aynıexternal_idile güvenle tekrar deneyin — sunucu çift kayıt açmaz, mevcut sonucu döner (idempotent_replay: true).- Bayi kodunu konfigürasyonda tutun. İstasyon yazılımına kurulumda yazılır, ölçüm başına değişmez. Koda elle her seferinde girilmesi gereken bir alan muamelesi yapmayın — yazım hatası yanlış bayiye veri akıtmaz (erişim dışıysa 404 alırsınız) ama akışı durdurur.
- Yerel kuyruk tutun. İnternet kesintisinde ölçümleri diske kuyruğa
yazıp bağlantı dönünce sırayla gönderin.
measured_atölçümün gerçek zamanı olduğundan geç gönderim sorun yaratmaz. - Tekrar denemede yeni nonce + yeni timestamp + yeni imza üretin
(nonce tek kullanımlıktır; aynısını göndermek
replay_detectedüretir).external_idise sabit kalır. - Saat senkronu: istasyon PC'sinin saati NTP ile senkron olsun —
±5 dakikadan fazla sapma
expired_timestampüretir. - 429 yanıtında
Retry-Aftersüresine uyun (varsayılan limit dk'da 60 istek — normal saha kullanımında erişilmesi pek mümkün değil).
Test senaryoları (kabul kriterleri)
| # | Senaryo | Beklenen |
|---|---|---|
| 1 | Açık rapora sahip plakaya ölçüm | 201 + status: "applied" + applied_points dolu |
| 2 | Aynı external_id ile tekrar gönderim | 200 + idempotent_replay: true, çift kayıt yok |
| 3 | Raporu olmayan plakaya ölçüm | 201 + status: "pending" — eksper rapor açınca bant görünür |
| 4 | Bilinmeyen / erişim dışı bayi kodu | 404 not_found |
| 5 | Hatalı imza | 401 invalid_signature |
| 6 | Geçersiz plaka ("ISTANBUL") | 400 invalid_request |
Tüm senaryolar ekspertiz.app tarafında entegrasyon testlerinden geçirilmiştir. Canlıya geçiş öncesi 1, 2 ve 3'ü kendi yazılımınızdan koşmanızı rica ediyoruz.