MekanteMekante Geliştirici
Başlangıç
Kasa API'si
Webhook'lar
llms.txt
  • Türkçe
  • English
Başlangıç
Kasa API'si
Webhook'lar
llms.txt
  • Türkçe
  • English
  • Başlarken

    • Mekante Geliştirici
    • Başlangıç
    • Kimlik doğrulama
  • Kasa API'si

    • Kasa akışı
    • Uç noktalar
    • Hatalar
    • Limitler
  • Webhook'lar

    • Webhook'lar
    • İmza doğrulama

İmza doğrulama

Webhook adresin herkese açıktır; birinin sahte bir istek göndermesini engellemek için her isteği imzayla doğrula.

Nasıl imzalanır

Her istekte X-Mekante-Signature başlığı bulunur:

X-Mekante-Signature: t=1792749428,v1=794b3947df267c3354af75b5d8931538c765d768e5ca83983dcd52f7c6f93270
  • t: isteğin imzalandığı an, saniye cinsinden Unix zamanı.
  • v1: şu metnin HMAC-SHA256 özeti (küçük harfli onaltılık):
HMAC_SHA256(imza_anahtarın, "<t>.<ham gövde>")

imza_anahtarın, uç noktayı eklerken gösterilen whsec_… değeridir. Gövde, sunucunun gönderdiği ham metindir: JSON'u çözüp yeniden oluşturma, boşluklar ve alan sırası değişir ve imza tutmaz.

Doğrulama adımları

  1. Başlıktan t ve v1 değerlerini al.
  2. t değerinin şimdiki zamandan en fazla 5 dakika farklı olduğunu kontrol et (eski bir isteğin yeniden gönderilmesini engeller).
  3. "<t>.<ham gövde>" metninin HMAC-SHA256'sını kendi anahtarınla hesapla.
  4. Sonucu v1 ile sabit zamanlı karşılaştır (=== kullanma).
  5. Tutuyorsa isteği işle ve 200 dön; tutmuyorsa 400 dön.

Node.js (Express)

import crypto from "node:crypto";

import express from "express";

const SECRET = process.env.MEKANTE_WEBHOOK_SECRET; // whsec_…
const TOLERANCE_SECONDS = 300;

function verify(rawBody, header) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  const v1 = parts.v1;
  if (!t || !v1) return false;
  if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_SECONDS) return false;

  const expected = crypto.createHmac("sha256", SECRET).update(`${t}.${rawBody}`).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(v1);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();
// Ham gövde gerekli: express.json() KULLANMA.
app.post("/hooks", express.raw({ type: "application/json" }), (req, res) => {
  const raw = req.body.toString("utf8");
  if (!verify(raw, req.get("X-Mekante-Signature") ?? "")) return res.sendStatus(400);

  const event = JSON.parse(raw);
  // event.id'yi daha önce işlediysen yok say, sonra event.event'e göre işle.
  res.sendStatus(200);
});

Python (Flask)

import hashlib, hmac, os, time
from flask import Flask, request, abort

SECRET = os.environ["MEKANTE_WEBHOOK_SECRET"].encode()  # whsec_…
TOLERANCE = 300
app = Flask(__name__)

def verify(raw: bytes, header: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t, v1 = parts.get("t"), parts.get("v1")
    if not t or not v1 or abs(time.time() - int(t)) > TOLERANCE:
        return False
    expected = hmac.new(SECRET, f"{t}.".encode() + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, v1)

@app.post("/hooks")
def hooks():
    raw = request.get_data()  # ham gövde
    if not verify(raw, request.headers.get("X-Mekante-Signature", "")):
        abort(400)
    event = request.get_json()
    # event["id"]'yi daha önce işlediysen yok say.
    return "", 200

PHP

<?php
$secret = getenv('MEKANTE_WEBHOOK_SECRET'); // whsec_…
$raw = file_get_contents('php://input');     // ham gövde
$header = $_SERVER['HTTP_X_MEKANTE_SIGNATURE'] ?? '';

parse_str(str_replace(',', '&', $header), $parts);
$t = $parts['t'] ?? '';
$v1 = $parts['v1'] ?? '';

$expected = hash_hmac('sha256', $t . '.' . $raw, $secret);
$fresh = ctype_digit($t) && abs(time() - (int) $t) <= 300;

if (!$fresh || !hash_equals($expected, $v1)) {
    http_response_code(400);
    exit;
}

$event = json_decode($raw, true);
// $event['id']'yi daha önce işlediysen yok say.
http_response_code(200);

Hata ayıklama

  • İmza hiç tutmuyorsa genellikle ham gövde değiştirilmiştir (JSON'a çevirip geri yazmak, satır sonu dönüşümü, karakter kodlaması).
  • Anahtarın başında ya da sonunda boşluk olmadığından emin ol.
  • Panelde Test gönder ile bir ping gönder ve kodunu bu istekle dene.
Prev
Webhook'lar