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:
{
"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 devaçıksaappUrlyerine tünel adresi (devUrl) kullanılır; adres teslimat anında çözülür.webhooks.pathtanımlı değilse yaşam döngüsü olayları (app.installed,app.uninstalled…) ve uygulama olayları da teslim edilemez. Mağaza olayı dinlemeseniz bilepathtanımlayın.- Her olayın gerektirdiği okuma kapsamı
scopesiç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#
| Olay | Açıklama | Gereken kapsam |
|---|---|---|
order.created | Sipariş oluşturuldu | orders:read |
order.paid | Sipariş ödendi | orders:read |
order.updated | Sipariş durumu güncellendi | orders:read |
order.fulfilled | Sipariş kargolandı | orders:read |
order.cancelled | Sipariş iptal edildi | orders:read |
order.refunded | Sipariş iade edildi | orders:read |
order.return_requested | İade talebi oluşturuldu | orders:read |
order.return_approved | İade talebi onaylandı | orders:read |
order.return_rejected | İade talebi reddedildi | orders:read |
order.return_received | İade ürünü teslim alındı | orders:read |
order.return_resolved | İade talebi sonuçlandı | orders:read |
order.shipping_updated | Kargo bilgisi güncellendi | orders:read |
product.created | Ürün oluşturuldu | products:read |
product.updated | Ürün güncellendi | products:read |
product.deleted | Ürün silindi | products:read |
inventory.low_stock | Düşük stok uyarısı | products:read |
inventory.out_of_stock | Stok tükendi | products:read |
review.created | Yorum gönderildi | products:read |
review.approved | Yorum onaylandı | products:read |
customer.created | Müşteri oluşturuldu | customers:read |
customer.updated | Müşteri güncellendi | customers:read |
customer.deleted | Müşteri silindi | customers:read |
subscriber.created | Bülten abonesi eklendi | marketing:read |
cms.item.published | CMS içeriği yayınlandı | cms:read |
form.submitted | Form gönderildi | cms: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.
| Olay | Ne zaman | data |
|---|---|---|
app.installed | Mağaza uygulamayı kurduğunda | { installationId, merchantId, scopes, planKey, subscription, isTestInstall } |
app.uninstalled | Mağaza uygulamayı kaldırdığında. Tek deneme, 8 sn zaman aşımı | { installationId, merchantId } |
app.scopes_updated | Mağaza ek izin isteğini onayladığında | { scopes } |
app.subscription.updated | Abonelik durumu her değiştiğinde | { planKey, status, trialEndsAt, currentPeriodEnd, chargeId } |
app.settings.updated | Mağ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#
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{
"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ık | Açıklama |
|---|---|
X-Tecof-Event | Olay adı |
X-Tecof-Event-Id | Olay kimliği; yeniden denemelerde aynı kalır — idempotency anahtarı |
X-Tecof-Delivery-Attempt | Deneme sayısı (1'den başlar) |
X-Tecof-Merchant-Id | Mağaza kimliği |
X-Tecof-Installation-Id | Kurulum kimliği (kurulum token'ı almak için) |
X-Tecof-App-Id | Uygulamanın istemci kimliği |
X-Tecof-Timestamp, X-Tecof-Signature | İmza; bkz. İmzalı İstekler |
X-Webhook-Event | Geriye 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#
| Kural | Davranış |
|---|---|
| Başarı | Herhangi bir 2xx yanıtı teslim edildi sayılır |
| Zaman aşımı | Deneme başına 8 saniye |
| Başarısızlık | 2xx 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övde | Yeniden 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ştirme | 20 ardışık başarısız teslimattan sonra abonelik pasife alınır |
| Yeniden etkinleştirme | tecof 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.uninstalled | Tek 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:
- Olay kimliği:
X-Tecof-Event-Id(gövdedeid) yeniden denemelerde aynıdır. İşlenen kimlikleri kendi deponuzda tutup tekrarı atlayın. - 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
invoiceNumbermetafield'ı yazar; aynı sipariş için gelen ikinciorder.paidbu metafield'ı görüp atlanır. - 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:
// 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
},
},
});| Durum | createWebhookHandler yanıtı |
|---|---|
| İmza ya da zaman damgası geçersiz | 401 |
Test olayı (test: true) | 200 (varsayılan olarak işleyiciler çalıştırılmaz) |
| İşleyicisi tanımlı olmayan olay | 200 |
| İşleyici başarıyla döndü | 200 |
| İşleyici hata fırlattı | 2xx olmayan yanıt → Tecof yeniden dener |
SDK yardımcısı olmadan#
// 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
2xxdönün.2xxdışı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)
2xxdö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öntem | Nasıl |
|---|---|
| Test mağazası + tünel | tecof 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ı istek | Aş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 |
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.
| Metot | Uç | Açıklama |
|---|---|---|
GET | /api/merchant/ecommerce/webhooks | Liste |
POST | /api/merchant/ecommerce/webhooks | Oluştur |
PUT · DELETE | /api/merchant/ecommerce/webhooks/:id | Güncelle · sil |
POST | /api/merchant/ecommerce/webhooks/:id/test | Test gönderimi |
GET | /api/merchant/ecommerce/webhooks/deliveries | Teslimat geçmişi |
POST | /api/merchant/ecommerce/webhooks/deliveries/:id/redeliver | Aynı yükle yeniden gönder |