İçeriğe geç
Docs

Webhook'lar

Webhook aboneliği, olay kataloğu ve gereken kapsamlar, yaşam döngüsü olayları, teslimat biçimi, yeniden deneme ve idempotency.

Son güncelleme:

Webhook'lar, mağazada bir olay olduğunda (sipariş ödendi, ürün güncellendi, müşteri oluşturuldu…) Tecof'un uygulamanıza imzalı bir POST isteği göndermesidir. Platform uygulamaları ayrıca kurulum, kaldırma, kapsam, abonelik ve ayar değişikliklerini bildiren yaşam döngüsü olaylarını abonelik gerekmeden alır.


Abonelik#

Abonelik tecof.app.json içinde tanımlanır:

JSON
{
  "scopes": ["orders:read", "customers:read"],
  "webhooks": {
    "path": "/api/webhooks",
    "events": ["order.paid", "order.cancelled", "order.refunded", "customer.created"]
  }
}
  • path, appUrl'e göredir ve / ile başlar. Teslimat adresi: appUrl + webhooks.path. Test mağazası kurulumlarında, tecof app dev açıksa appUrl yerine tünel adresi (devUrl) kullanılır; adres teslimat anında çözülür.
  • webhooks.path tanımlı değilse yaşam döngüsü olayları (app.installed, app.uninstalled…) ve uygulama olayları da teslim edilemez. Mağaza olayı dinlemeseniz bile path tanımlayın.
  • Her olayın gerektirdiği okuma kapsamı scopes içinde olmalıdır; aksi hâlde yapılandırma reddedilir.
  • * (tüm olaylar) jokeri uygulamalara kapalıdır.
  • Mağaza uygulamayı kurduğunda abonelik otomatik açılır. Yapılandırma değişince abonelik eşitlenir: etkin olaylar, yapılandırmadaki olaylarla mağazanın verdiği kapsamların kesişimidir.

Olay Kataloğu ve Gereken Kapsam#

OlayAçıklamaGereken kapsam
order.createdSipariş oluşturulduorders:read
order.paidSipariş ödendiorders:read
order.updatedSipariş durumu güncellendiorders:read
order.fulfilledSipariş kargolandıorders:read
order.cancelledSipariş iptal edildiorders:read
order.refundedSipariş iade edildiorders:read
order.return_requestedİade talebi oluşturulduorders:read
order.return_approvedİade talebi onaylandıorders:read
order.return_rejectedİade talebi reddedildiorders:read
order.return_receivedİade ürünü teslim alındıorders:read
order.return_resolvedİade talebi sonuçlandıorders:read
order.shipping_updatedKargo bilgisi güncellendiorders:read
product.createdÜrün oluşturulduproducts:read
product.updatedÜrün güncellendiproducts:read
product.deletedÜrün silindiproducts:read
inventory.low_stockDüşük stok uyarısıproducts:read
inventory.out_of_stockStok tükendiproducts:read
review.createdYorum gönderildiproducts:read
review.approvedYorum onaylandıproducts:read
customer.createdMüşteri oluşturulducustomers:read
customer.updatedMüşteri güncellendicustomers:read
customer.deletedMüşteri silindicustomers:read
subscriber.createdBülten abonesi eklendimarketing:read
cms.item.publishedCMS içeriği yayınlandıcms:read
form.submittedForm gönderildicms:read

Başka uygulamaların yayınladığı app.<slug>.<ad> olayları da aynı biçimde teslim edilir; bkz. Uygulamalar Arası İletişim.


Yaşam Döngüsü Olayları#

Bu olaylar her zaman gönderilir; webhooks.events içinde listelenmeleri gerekmez.

OlayNe zamandata
app.installedMağaza uygulamayı kurduğunda{ installationId, merchantId, scopes, planKey, subscription, isTestInstall }
app.uninstalledMağaza uygulamayı kaldırdığında. Tek deneme, 8 sn zaman aşımı{ installationId, merchantId }
app.scopes_updatedMağaza ek izin isteğini onayladığında{ scopes }
app.subscription.updatedAbonelik durumu her değiştiğinde{ planKey, status, trialEndsAt, currentPeriodEnd, chargeId }
app.settings.updatedMağaza paneldeki ayar formunu kaydettiğinde{ keys: [...], secretKeys: [...] } — değer içermez

app.uninstalled: Kurulumun token'ları bu teslimat bittikten sonra iptal edilir; işleyiciniz 8 saniye içinde Tecof API'sini hâlâ çağırabilir (ör. son durumu okumak için). Teslimattan sonra kurulum kaldırılır, kurulumun ayarları ve sırları silinir. Olay yeniden denenmez; kaçırırsanız temizliği bir sonraki API çağrısında aldığınız 401 ile tetikleyin.

app.settings.updated yalnız mağaza paneldeki ayar formunu kaydettiğinde gelir; uygulamanızın PUT /api/v1/app/settings ile yaptığı yazmalar bu olayı üretmez. Olay değer taşımaz: değişen ayarları api.settings.get() ve api.secrets.get(key) ile okuyun.


Teslimat Biçimi#

HTTP
POST /api/webhooks HTTP/1.1
Host: isbasi.apps.tecof.site
Content-Type: application/json
User-Agent: Tecof-Webhooks/1.0
X-Tecof-Event: order.paid
X-Tecof-Event-Id: evt_7f3a2c9e1b
X-Tecof-Delivery-Attempt: 1
X-Tecof-Merchant-Id: 66f0c0ffee0000000000abcd
X-Tecof-Installation-Id: 6720aa00bb11cc22dd33ee55
X-Tecof-App-Id: tcfa_xxxxxxxxxxxxxxxx
X-Tecof-Timestamp: 1760087770
X-Tecof-Signature: t=1760087770,v1=5f2c…e91a
X-Webhook-Event: order.paid
JSON
{
  "id": "evt_7f3a2c9e1b",
  "event": "order.paid",
  "test": false,
  "timestamp": "2026-10-10T09:16:10.000Z",
  "merchantId": "66f0c0ffee0000000000abcd",
  "installationId": "6720aa00bb11cc22dd33ee55",
  "data": {
    "orderId": "6720bb00cc11dd22ee33ff44",
    "orderNumber": "TCF-1001",
    "status": "confirmed",
    "paymentStatus": "paid",
    "totalPrice": 1050,
    "taxPrice": 175,
    "pricesIncludeTax": true,
    "currencyCode": "TRY",
    "customerEmail": "musteri@example.com",
    "merchantId": "66f0c0ffee0000000000abcd",
    "merchantName": "Örnek Mağaza"
  }
}
BaşlıkAçıklama
X-Tecof-EventOlay adı
X-Tecof-Event-IdOlay kimliği; yeniden denemelerde aynı kalır — idempotency anahtarı
X-Tecof-Delivery-AttemptDeneme sayısı (1'den başlar)
X-Tecof-Merchant-IdMağaza kimliği
X-Tecof-Installation-IdKurulum kimliği (kurulum token'ı almak için)
X-Tecof-App-IdUygulamanın istemci kimliği
X-Tecof-Timestamp, X-Tecof-Signatureİmza; bkz. İmzalı İstekler
X-Webhook-EventGeriye uyum için olay adı

Mağaza olaylarının data alanı, mağaza webhook'larının gönderdiği yükün aynısıdır; Tecof merchantId ve merchantName alanlarını ekler. Sipariş olaylarında orderId, orderNumber, status, paymentStatus, totalPrice, taxPrice, pricesIncludeTax, currencyCode, customerEmail gibi özet alanlar gelir. Satırlar, adresler ve vergi kırılımı dahil siparişin tam ve kararlı şekli için GET /api/v1/orders/:id ile okuyun (api.orders.get(data.orderId)). Bkz. API ile Veri Erişimi.

Platform uygulamalarına ham sır içeren X-Webhook-Secret başlığı gönderilmez (yalnız eski birinci parti ve mağaza webhook'larında sürer). Doğrulama yalnız X-Tecof-Signature ile yapılır.


Yeniden Deneme ve Otomatik Pasifleştirme#

KuralDavranış
BaşarıHerhangi bir 2xx yanıtı teslim edildi sayılır
Zaman aşımıDeneme başına 8 saniye
Başarısızlık2xx dışı yanıt, bağlantı hatası ya da zaman aşımında yeniden denenir. Toplam 3 deneme: ilk teslimat + 2 yeniden deneme (+2 dk, +15 dk)
GövdeYeniden denemeler ve elle yeniden gönderim tam gövdeyle (≤ 256 KB) yapılır; X-Tecof-Event-Id değişmez, X-Tecof-Delivery-Attempt artar
Otomatik pasifleştirme20 ardışık başarısız teslimattan sonra abonelik pasife alınır
Yeniden etkinleştirmetecof app config push (yapılandırma yazımı) uygulamanın tüm pasif aboneliklerini yeniden etkinleştirir; mağaza da paneldeki Webhook ekranından aboneliği açabilir
app.uninstalledTek deneme (8 sn); yeniden denenmez

Teslimat geçmişinizi geliştirici API'sinden izleyebilirsiniz: GET /api/developer/apps/:id/deliveries.


Idempotency#

Aynı olay birden fazla kez gelebilir (yeniden deneme, ağ kopması). İşleyicinizi idempotent yazın:

  1. Olay kimliği: X-Tecof-Event-Id (gövdede id) yeniden denemelerde aynıdır. İşlenen kimlikleri kendi deponuzda tutup tekrarı atlayın.
  2. Kaynak durumu: Daha sağlam yol, işin yapılıp yapılmadığını kaynağın kendisinden anlamaktır. Örneğin fatura kesen bir uygulama faturayı oluşturunca siparişe invoiceNumber metafield'ı yazar; aynı sipariş için gelen ikinci order.paid bu metafield'ı görüp atlanır.
  3. Sıra: Yeniden denemeler nedeniyle olaylar oluş sırasından farklı sırada gelebilir. Kararı olay yüküne değil, API'den okuduğunuz güncel duruma göre verin.

Örnek İşleyici (Next.js App Router)#

SDK'nın createWebhookHandler yardımcısı doğrulamayı, olay yönlendirmeyi ve yanıt kodlarını üstlenir. Dönen fonksiyon doğrudan bir Route Handler'dır:

TypeScript
// app/api/webhooks/route.ts
import { createWebhookHandler } from "@tecof/app-sdk";
import { tecof } from "@/lib/tecof";

export const POST = createWebhookHandler({
  app: tecof,
  handlers: {
    "app.installed": async (event) => {
      await tecof.client(event.installationId).settings.set({ invoiceMode: "draft" }, { merge: true });
    },

    "app.uninstalled": async (event) => {
      // Token'lar bu teslimat bittikten sonra iptal edilir; gerekirse burada API'yi çağırabilirsiniz.
      // Kendi sistemlerinizdeki kopyaları temizleyin (8 sn içinde).
    },

    "app.settings.updated": async (event) => {
      // Değerler gelmez; gerekirse api.settings.get() / api.secrets.get(key) ile okuyun
    },

    "order.paid": async (event) => {
      const api = tecof.client(event.installationId);
      const orderId = String(event.data.orderId);

      const existing = await api.metafields.list({ ownerType: "order", ownerId: orderId, namespace: "isbasi" });
      if (existing.some((m: { key: string }) => m.key === "invoiceNumber")) return; // zaten işlendi

      const order = await api.orders.get(orderId);
      // … faturayı oluştur, sonra siparişe invoiceNumber metafield'ı yaz
    },
  },
});
DurumcreateWebhookHandler yanıtı
İmza ya da zaman damgası geçersiz401
Test olayı (test: true)200 (varsayılan olarak işleyiciler çalıştırılmaz)
İşleyicisi tanımlı olmayan olay200
İşleyici başarıyla döndü200
İşleyici hata fırlattı2xx olmayan yanıt → Tecof yeniden dener

SDK yardımcısı olmadan#

TypeScript
// app/api/webhooks/route.ts
import { tecof } from "@/lib/tecof";

export async function POST(req: Request) {
  const rawBody = await req.text(); // ham gövde — imza bunun üzerinden hesaplanır

  let event;
  try {
    event = tecof.verifyWebhook(rawBody, req.headers); // Headers ya da düz nesne kabul eder
  } catch {
    return new Response("invalid signature", { status: 401 });
  }

  if (event.test) return new Response(null, { status: 204 });

  switch (event.event) {
    case "order.paid":
      // …
      break;
  }
  return new Response(null, { status: 204 });
}

İpuçları:

  • İşleyiciyi 8 saniyelik zaman aşımının altında tutun ve işlem bitince 2xx dönün. 2xx dışındaki her yanıt yeniden denemeye ve ardışık hata sayacına yazılır.
  • Kalıcı olarak işlenemeyecek bir olay için (ör. mağaza ayarları eksik) 2xx dönüp durumu kendi tarafınızda kaydedin; aksi hâlde yeniden denemeler ve ardışık hatalar aboneliği pasife düşürebilir.

Test Etme#

YöntemNasıl
Test mağazası + tüneltecof app dev çalışırken test mağazasında gerçek olay üretin (ör. sipariş oluşturup ödendi işaretleyin); webhook doğrudan yerel sunucunuza gelir
Panelden test teslimatıMağaza panelindeki Webhook ekranında aboneliği seçip "Test Et" ile test: true taşıyan bir teslimat gönderilebilir
Yerel imzalı istekAşağıdaki curl + openssl ile kendi uçunuza imzalı gövde gönderin
Teslimat günlüğüGET /api/developer/apps/:id/deliveries ile yanıt kodlarını ve denemeleri izleyin
Terminal
SECRET="$TECOF_CLIENT_SECRET"
T=$(date +%s)
BODY='{"id":"evt_local_1","event":"order.paid","test":false,"timestamp":"2026-10-10T09:16:10.000Z","merchantId":"66f0c0ffee0000000000abcd","installationId":"6720aa00bb11cc22dd33ee55","data":{"orderId":"6720bb00cc11dd22ee33ff44","orderNumber":"TCF-1001"}}'
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')

curl -X POST http://localhost:3000/api/webhooks \
  -H "Content-Type: application/json" \
  -H "X-Tecof-Event: order.paid" \
  -H "X-Tecof-Event-Id: evt_local_1" \
  -H "X-Tecof-Delivery-Attempt: 1" \
  -H "X-Tecof-Installation-Id: 6720aa00bb11cc22dd33ee55" \
  -H "X-Tecof-Timestamp: $T" \
  -H "X-Tecof-Signature: t=$T,v1=$SIG" \
  --data-binary "$BODY"

Elle imzalı istekte installationId gerçek bir test kurulumuna ait değilse API çağrıları (api.orders.get) başarısız olur; imza ve yönlendirme mantığını bu yolla, uçtan uca akışı test mağazasında tünelle doğrulayın.


Mağaza webhook’ları (panelden)#

Uygulama yazmadan, bir mağazanın olaylarını kendi sunucunuza göndermek için mağaza sahibi panelde (E-ticaret → Ayarlar → Webhook’lar) webhook tanımlayabilir. Bu webhook’lar aynı olay kataloğunu kullanır, * (tüm olaylar) jokerine izin verir ve eski usul X-Webhook-Secret başlığıyla doğrulanır. Panel aşağıdaki uçları JWT ile çağırır; üçüncü taraf entegrasyonlar için platform uygulaması önerilir.

MetotUçAçıklama
GET/api/merchant/ecommerce/webhooksListe
POST/api/merchant/ecommerce/webhooksOluştur
PUT · DELETE/api/merchant/ecommerce/webhooks/:idGüncelle · sil
POST/api/merchant/ecommerce/webhooks/:id/testTest gönderimi
GET/api/merchant/ecommerce/webhooks/deliveriesTeslimat geçmişi
POST/api/merchant/ecommerce/webhooks/deliveries/:id/redeliverAynı yükle yeniden gönder