T-SOFT/Uygulama Geliştirme/Uygulama Webhooks

Uygulama Webhooks

Uygulamanız üç tür bildirim alır: uygulama kaldırıldığında T-SOFT'un gönderdiği kaldırma bildirimi, veri silmeniz gerektiğinde gelen gizlilik bildirimleri ve mağazadaki olaylar için Admin API'den kendinizin kurduğu olay abonelikleri.

Uygulama kaldırıldı

Uygulama kaydınızda uninstall_url tanımlıysa, mağaza uygulamayı kaldırdığında bu adrese bir POST gelir:

{ "token": "<jwt>" }

Token, oturum token'ı ile aynı anahtarla ES256 imzalıdır; aynı JWKS adresiyle doğrulayın.

ClaimDeğer
isshttps://api.tsoftapps.com
audUygulamanızın prefix'i
eventapp.uninstalled
store_idUygulamayı kaldıran mağaza
destMağaza alan adı
expGönderimden 5 dakika sonra
uninstall.ts
const { payload } = await jwtVerify(body.token, jwks, {
  algorithms: ["ES256"],
  issuer: "https://api.tsoftapps.com",
  audience: process.env.TSOFT_APP_PREFIX,
})
if (payload.event !== "app.uninstalled") return res.sendStatus(400)

await stores.forget(payload.store_id)
res.sendStatus(200)
event alanını mutlaka kontrol edin. Oturum token'ında bu alan yoktur; ikisini birbirinin yerine kabul etmeyin.

Adres yönlendirme izlenmeden çağrılır ve 10 saniye içinde 2xx dönmelidir. Başarısız gönderim 1 dakika ve 5 dakika sonra iki kez daha denenir. Bildirim gelmese de o mağazadaki erişiminiz kapandığı için o mağaza için token istekleriniz başarısız olur; bunu da kaldırma işareti sayabilirsiniz.

Gizlilik webhook'ları

Mağaza ya da müşteri verisi tutan her uygulama, bu verinin silinmesi gereken anlarda bildirim almalıdır. Uygulama kaydınıza (tsoft.app.toml → [webhooks] privacy_url) bir https:// adresi yazın; iki olay bu adrese gelir.

eventNe zamanEk claimYapmanız gereken
shop.redactUygulama mağazadan kaldırıldıktan 48 saat sonra. Bu sürede yeniden kurulursa gönderilmez.—O mağazaya ait tüm veriyi silin
customers.redactMağaza bir müşteriyi sildiğinde (panelden, Admin API'den ya da müşteri hesabını kendisi kapattığında)customer_ids: silinen müşterilerin kimlikleriBu müşterilere ait veriyi silin

Gövde, imza ve claim'ler Uygulama kaldırıldı bildirimiyle aynıdır: { "token": "<jwt>" }, ES256, aynı JWKS. Olayı event claim'inden ayırın.

privacy.ts
const { payload } = await jwtVerify(body.token, jwks, {
  algorithms: ["ES256"],
  issuer: "https://api.tsoftapps.com",
  audience: process.env.TSOFT_APP_PREFIX,
})

if (payload.event === "shop.redact") {
  await queue.add("redact-store", { storeId: payload.store_id })
} else if (payload.event === "customers.redact") {
  await queue.add("redact-customers", { storeId: payload.store_id, customerIds: payload.customer_ids })
} else {
  return res.sendStatus(400)
}
res.sendStatus(200)
Silmeyi bildirimden sonraki 15 gün içinde tamamlayın. Müşterinin başvurusuna yanıt verme süresi mağaza için 30 gündür (KVKK md. 13); sizin payınız bunun yarısıdır. Vergi ya da muhasebe mevzuatı gereği saklamak zorunda olduğunuz kayıtları silmeyin, ama yalnız o amaçla kullanın.

Gönderim kuralları kaldırma bildirimiyle aynıdır: yönlendirme izlenmez, 10 saniye içinde 2xx beklenir, başarısız gönderim 1 ve 5 dakika sonra tekrar denenir. Aynı olay birden fazla gelebilir; silme işleminiz tekrar çalıştığında hata vermemelidir.

Mağaza olaylarına abone olma

Olay abonelikleri Admin API'nin webhooks kaynağıyla yönetilir ve write_webhooks kapsamını gerektirir. Bu kapsam onay ekranında hassas olarak işaretlenir.

terminal
curl -X POST https://magaza.com/api/v3/admin/2026-01/config/webhooks \
  -H "Authorization: Bearer <access_token>" \
  -H "Accept: application/vnd.api+json" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{
    "data": {
      "type": "webhooks",
      "attributes": {
        "payloadUrl": "https://uygulamaniz.com/webhooks/tsoft",
        "payloadSecret": "<kendi ürettiğiniz rastgele değer>",
        "format": "2026-01",
        "events": ["order.created", "order.status_changed"]
      }
    }
  }'
AlanAçıklama
payloadUrlOlayların gönderileceği adres. Alan adı olmalı; IP adresi kabul edilmez.
payloadSecretİmza anahtarı. Her mağaza için ayrı ve rastgele üretin.
format2026-01. Verilmezse olay adlarından çıkarılır: konu adları (order.created) 2026-01 sayılır.
eventsAbone olunan konular (aşağıdaki tablo).
timeoutYanıt bekleme süresi, saniye: 3, 5 ya da 7. Varsayılan 3.

Uygulamanız yalnız kendi açtığı abonelikleri görür: listeleme, güncelleme, silme, ping ve teslimat geçmişi (config/webhook-deliveries?filter[webhookId]=) yalnız bu kayıtlar için çalışır. Mağazanın kendi aboneliklerine ve başka uygulamalarınkine erişemezsiniz; bunlar yokmuş gibi 404 döner. Uygulama kaldırılınca açtığınız abonelikler kendiliğinden silinir.

Uygulama abonelikleri mağazanın elle ekleyebileceği webhook sınırına sayılmaz ve mağazanın paketinden bağımsızdır. Bir mağazada uygulamaların toplam abonelik sınırı 50'dir.

Konular

KonuTetiklenmedataGereken kapsam
order.createdSipariş oluşturulduğundaordersread_orders
order.status_changedSipariş durumu değiştiğindeordersread_orders
order.invoice_updatedFatura bilgisi güncellendiğindeordersread_orders
order.shippedSipariş kargoya verildiğindeordersread_orders
order.provision_report_updatedProvizyon raporu sonuçlandığındaordersread_orders
order.transferredSipariş aktarıldığındaordersread_orders
customer.createdÜye kaydolduğundacustomersread_customers
customer.deletedÜye silindiğinde{ type, id }read_customers
customer.notification_settings_updatedÜyenin e-posta/SMS izni değiştiğindecustomersread_customers
comment.createdÜrün yorumu yazıldığındaproduct-commentsread_reviews
message.createdMüşteri mesajı geldiğindecustomer-messagesread_customer_messages
return.createdİade talebi oluşturulduğundaorder-returnsread_returns
return.updatedİade talebi güncellendiğindeorder-returnsread_returns
return.status_changedİade durumu değiştiğindeorder-returnsread_returns
product.createdÜrün eklendiğindeproductsread_products
product.updatedÜrün güncellendiğindeproductsread_products
product.deletedÜrün silindiğinde{ type, id }read_products
product.stock_changedÜrün stoku değiştiğindeproductsread_products
category.createdKategori eklendiğindecategoriesread_products
category.updatedKategori güncellendiğindecategoriesread_products
category.deletedKategori silindiğinde{ type, id }read_products

Bildirim konuları (notification.*) yalnız mağazanın kendi aboneliklerine açıktır; uygulamalar bu konuları alamaz.

Konunun kapsamı uygulamanızda yoksa abone olabilirsiniz ama olay gönderilmez. Örneğin read_orders olmadan order.created hiç gelmez. Kurulumda gereken kapsamları isteyin.

product.updated ve product.stock_changed toplu güncellemelerde kısa sürede çok sayıda olay üretebilir; yalnız gerçekten kullandığınız konulara abone olun.

Gövde

Her olay aynı zarfla gelir. data, aynı kaydı Admin API 2026-01'de GET ile okuduğunuzda dönen data nesnesidir: alanlar, adlar ve biçim API ile aynıdır.

order.created
{
  "id": "6af384d126c5ea4a417fb6b6",
  "topic": "order.created",
  "api_version": "2026-01",
  "created_at": "2026-11-11T09:30:12.000Z",
  "shop": { "id": "6eaab115-…", "domain": "magaza.tsoft.biz", "primary_domain": "magaza.com" },
  "data_format": "2026-01",
  "data": {
    "type": "orders",
    "id": "48213",
    "attributes": { "orderNumber": "TS-48213", "customerEmail": null, "…": "…" }
  }
}

shop.domain mağazanın kalıcı adresidir (*.tsoft.biz); mağaza alan adını değiştirse de aynı kalır, mağazayı bununla (ya da shop.id ile) eşleyin. shop.primary_domain mağazanın kullandığı alan adıdır; müşteriye gösterilecek bağlantılar için kullanın.

Belge, uygulamanızın yetkileriyle üretilir. API'de göremediğiniz alanı webhook'ta da göremezsiniz. read_customer_personal_data kapsamı olmayan uygulamaya müşterinin adı, e-postası, telefonu ve adresi null gelir; alan gövdede kalır. Silinen kayıtlarda data yalnız { "type": "products", "id": "…" } taşır.

Başlıklar

BaşlıkDeğer
X-Tsoft-Hmac-Sha256Gövdenin imzası: base64(HMAC-SHA256(payloadSecret, ham gövde))
X-Tsoft-TopicKonu, ör. order.created
X-Tsoft-Api-Version2026-01
X-Tsoft-Event-IdOlayın kimliği; aynı olayın tekrar denemelerinde aynıdır
X-Tsoft-Webhook-IdBu teslimatın kimliği
X-Tsoft-Shop-IdMağaza kimliği (store_id ile aynı)
X-Tsoft-Shop-DomainMağazanın kalıcı adresi (*.tsoft.biz); gövdede shop.domain
X-Tsoft-Triggered-AtOlayın zamanı (ISO 8601)

İmzayı doğrulama

İmzayı ham gövde üzerinden hesaplayın; JSON'u çözüp yeniden yazdığınız metin farklı olur. Karşılaştırmayı sabit süreli yapın.

webhook.ts
import { createHmac, timingSafeEqual } from "node:crypto"

app.post("/webhooks/tsoft", express.raw({ type: "*/*" }), async (req, res) => {
  const expected = createHmac("sha256", process.env.TSOFT_WEBHOOK_SECRET!).update(req.body).digest()
  const received = Buffer.from(req.header("X-Tsoft-Hmac-Sha256") ?? "", "base64")
  if (received.length !== expected.length || !timingSafeEqual(received, expected)) return res.sendStatus(401)

  const event = JSON.parse(req.body.toString("utf8"))
  if (await seen(event.id)) return res.sendStatus(200)
  await queue.add(event.topic, event)
  res.sendStatus(200)
})

Teslim kuralları

  • 2xx başarılı sayılır. Yönlendirme izlenmez.
  • Yanıt timeout süresi içinde gelmelidir; gelmezse başarısız sayılır.
  • Başarısız teslimat, ilk denemeden sonra 7 kez daha denenir: 1, 5, 15 ve 30 dakika, 1, 1 ve 2 saat arayla (toplam yaklaşık 5 saat).
  • 404 ve 410 tekrar denenmez.
  • Aynı olay birden fazla gelebilir ve sıra garantisi yoktur. id (X-Tsoft-Event-Id) ile tekrarı ayıklayın, kaydın güncel halini gerekirse API'den okuyun.

Eski olay adları

ORDER.CREATE gibi büyük harfli adlar ve format: "legacy" eski uygulamalar için çalışmaya devam eder. Bu abonelikler eski gövdeyi alır: Admin API biçiminde değildir ve yetkiye göre maskelenmez. Yeni uygulamalarda kullanmayın. ORDER.BEFORE (siparişi tamamlanmadan durdurabilen senkron çağrı) 2026-01'de yoktur.

Endpoint önerileri

  • Hemen 2xx dönün, işi kuyruğa alın; yavaş yanıt gönderimi başarısız sayar.
  • Her isteğin imzasını X-Tsoft-Hmac-Sha256 ile doğrulayın; payloadSecret'i ortam değişkeninde ya da gizli anahtar deposunda saklayın.
  • Olayları id ile tekilleştirin.
  • Gövdeyi yalnız ihtiyaç duyduğunuz kadar saklayın; kişisel veri tutuyorsanız gizlilik webhook'larını uygulayın.

Sıradaki adım