Pro vývojáře / Webhooky

Podepsané HTTP notifikace o všem, co se děje ve vašem obchodě.

Objednávky, doručení, platby, spory, předplatná, zákazníci, zásoby i e-mailový marketing se samy ohlašují. Stačí jednou přihlásit koncový bod a reagovat ve vlastních systémech téměř v reálném čase.

Typy událostí38

Každá změna stavu, kterou platforma oznamuje, seskupená do kanálů níže.

Kanály14

Webhook obchodu přihlásí jednu URL k jednomu kanálu a dostává všechny jeho události.

Pokusy o doručení5

Neúspěšná doručení se opakují s exponenciálním odstupem, než se vzdají.

Přihlášení k odběru

Webhooky obchodu

Koncové body vytvoříte v administraci v sekci Integrace / Webhooky. Každý řádek přihlašuje jednu URL k jednomu kanálu; pro více kanálů přidejte více řádků.

Každý koncový bod má vlastní podpisový tajný klíč — zobrazí se jednou při vytvoření a lze jej kdykoli obměnit na stejné obrazovce. Ověřujte ho při každém doručení.

Rozšíření

Nainstalovaná rozšíření se přihlašují k jednotlivým názvům událostí: uveďte je v poli events manifestu a platforma je doručí na webhook URL rozšíření, podepsané klíčem dané instalace. Jednotlivé odběry lze přepínat přes API rozšíření.

Obálka doručení

Každé doručení je HTTP POST s JSON tělem. Doručení webhooků obchodu obsahují integrationId řádku koncového bodu; doručení rozšíření ho vynechávají.

{
  "event": "order.shipped",
  "eventId": "0d9f4c1e-6c3a-4f6e-9b1a-2f8f4f1c9d2b",
  "storeId": 12,
  "integrationId": 7,
  "schemaVersion": 1,
  "payload": {
    "shippingId": 9001,
    "orderId": 5501,
    "storeId": 12,
    "status": "shipped"
  },
  "timestamp": "2026-08-12T09:00:01.244Z"
}

Hlavičky

  • X-Storefront-Timestamp — čas podpisu pokusu o doručení ve formátu ISO-8601. Je zahrnut do podpisu, takže ho nelze vyměnit bez porušení ověření.
  • X-Storefront-Signature — hex HMAC-SHA256 přes časové razítko, tečku a surové tělo požadavku, s vaším podpisovým klíčem.
  • X-Storefront-Event-Id — stabilní id logické události, stejné napříč všemi doručeními a opakováními dané události. Zrcadlí se i v těle. Deduplikujte podle něj.
  • X-Storefront-Delivery-Id — identifikuje jedno doručení události na koncový bod napříč všemi jeho pokusy.
  • User-Agent — Storefront-Webhook/1.0 pro webhooky obchodu, Storefront-Extension/1.0 pro doručení rozšíření.

Doručování je at-least-once: opakování se podepisují novým časovým razítkem, takže platný podpis nikdy neznamená novou událost. Ukládejte zpracované hodnoty eventId a opakování přeskakujte.

Ověřování podpisů

HMAC počítejte přesně nad surovými bajty těla — JSON parsujte až po úspěšném porovnání. Odmítejte doručení starší než několik minut.

import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyStorefrontWebhook(rawBody, headers, signingSecret) {
  const timestamp = headers['x-storefront-timestamp'];
  const signature = headers['x-storefront-signature'];
  if (!timestamp || !signature) return false;

  // Reject stale deliveries (replay protection). 5 minutes matches the
  // official verifier's default tolerance.
  if (Math.abs(Date.now() - Date.parse(timestamp)) > 5 * 60 * 1000) return false;

  const expected = createHmac('sha256', signingSecret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');
  if (expected.length !== signature.length) return false;
  return timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'));
}

Autoři rozšíření mohou místo toho použít helper verifyWebhookSignature z balíčku @storefront/extension-sdk — stejné schéma s vestavěným oknem čerstvosti.

Doručování a opakování

  • Pět pokusů na doručení s exponenciálním odstupem, začíná se na dvou sekundách.
  • Odpověď 4xx je konečná — doručení se neopakuje. Odpovědi 5xx, síťové chyby a vypršení časového limitu se opakují.
  • Pokusy vyprší po 5 sekundách u webhooků obchodu a po 10 sekundách u doručení rozšíření.
  • Potvrďte libovolnou odpovědí 2xx co nejrychleji a skutečnou práci dělejte asynchronně.
  • Pořadí napříč událostmi není zaručeno — spoléhejte na časová razítka a id v payloadu, ne na pořadí doručení.

Katalog událostí

Seskupeno podle kanálů webhooků obchodu. Rozšíření se přihlašují k jednotlivým názvům událostí.

Objednávky

PURCHASE
order.created

Byla vytvořena nová objednávka, včetně objednávek čekajících na platbu.

Ukázka payloadu
{
  "orderId": 5501,
  "order": {
    "id": 5501,
    "storeId": 12,
    "status": "confirmed",
    "priceAmountMinor": "12900",
    "currency": "USD",
    "customerId": 31,
    "customerEmail": "jane@example.com",
    "createdAt": "2026-08-11T14:03:22.101Z",
    "confirmedAt": "2026-08-11T14:03:22.101Z",
    "cancelledAt": null,
    "trackingId": null,
    "items": [
      {
        "productId": 88,
        "name": "Poster",
        "quantity": 1,
        "priceMinor": "12900",
        "priceCurrency": "USD"
      }
    ]
  }
}
order.confirmed

Platba objednávky byla vypořádána a objednávka potvrzena.

Ukázka payloadu
{
  "orderId": 5501,
  "confirmedByUserId": 7,
  "order": {
    "id": 5501,
    "storeId": 12,
    "status": "confirmed",
    "priceAmountMinor": "12900",
    "currency": "USD"
  }
}
order.updated

Objednávka se změnila — stav, sledování zásilky nebo jiná pole; objekt changes uvádí, co se změnilo.

Ukázka payloadu
{
  "orderId": 5501,
  "changes": {
    "status": "shipped",
    "trackingNumber": "1Z999AA10123456784"
  },
  "order": {
    "id": 5501,
    "storeId": 12,
    "status": "shipped",
    "trackingId": "1Z999AA10123456784"
  }
}
order.cancelled

Objednávka byla zrušena a zásoby uvolněny.

Ukázka payloadu
{
  "orderId": 5501,
  "cancelledByUserId": 7,
  "order": {
    "id": 5501,
    "storeId": 12,
    "status": "cancelled",
    "cancelledAt": "2026-08-11T15:20:00.000Z"
  }
}
checkout.completed

Hostovaná platební relace byla úspěšně dokončena.

Ukázka payloadu
{
  "orderId": 5501,
  "paymentStatus": "paid",
  "amountTotalMinor": "12900",
  "currency": "USD",
  "providerPaymentIntentId": "pi_3PqXo42e"
}

Vrácení peněz

REFUND
order.refunded

Platba za objednávku byla vrácena, částečně nebo v plné výši.

Ukázka payloadu
{
  "orderId": 5501,
  "refundId": 301,
  "refundedAmountMinor": "12900",
  "refundedCurrency": "USD",
  "isFullyRefunded": true
}

Předplatné zrušeno

SUBSCRIPTION_CANCEL
subscription.cancelled

Předplatné doběhlo a je nyní zrušené.

Ukázka payloadu
{
  "subscriptionId": 9001,
  "customerId": 31,
  "productId": 88,
  "canceledAt": "2026-08-11T00:00:00.000Z"
}

Předplatné se brzy zruší

SUBSCRIPTION_CANCEL_SOON
subscription.cancel_soon

Zákazník naplánoval zrušení na konec aktuálního období.

Ukázka payloadu
{
  "subscriptionId": 9001,
  "customerId": 31,
  "productId": 88,
  "currentPeriodEnd": "2026-09-01T00:00:00.000Z"
}

Zákazník vytvořen

CUSTOMER_CREATED
customer.created

Byl vytvořen nový zákaznický účet — registrace, přihlášení přes OAuth nebo povýšení hosta.

Ukázka payloadu
{
  "customerId": 31,
  "storeId": 12,
  "email": "jane@example.com",
  "firstName": "Jane",
  "lastName": "Doe",
  "createdAt": "2026-08-11T14:00:00.000Z"
}

Produkty

PRODUCT
product.created

Do katalogu byl přidán produkt.

Ukázka payloadu
{
  "productId": 88,
  "product": {
    "id": 88,
    "storeId": 12,
    "slug": "poster",
    "name": "Poster",
    "priceMinor": "12900",
    "priceCurrency": "USD",
    "stock": 20,
    "isAvailable": true
  }
}
product.updated

Produkt se změnil. Hromadné úpravy vyvolají jednu událost na každý dotčený produkt, označenou bulk true a seznamem změněných polí.

Ukázka payloadu
{
  "productId": 88,
  "bulk": true,
  "changes": [
    "priceMinor"
  ],
  "product": {
    "id": 88,
    "storeId": 12,
    "slug": "poster",
    "name": "Poster",
    "priceMinor": "13900",
    "priceCurrency": "USD",
    "stock": 20,
    "isAvailable": true
  }
}
product.deleted

Produkt byl odstraněn; payload nese poslední známý snímek.

Ukázka payloadu
{
  "productId": 88,
  "product": {
    "id": 88,
    "storeId": 12,
    "slug": "poster",
    "name": "Poster"
  }
}
inventory.restocked

Sledované zásoby se z nuly vrátily do kladných hodnot.

Ukázka payloadu
{
  "storeId": 12,
  "productId": 88,
  "productOptionId": null,
  "stock": 25
}
inventory.out_of_stock

Sledované zásoby produktu nebo varianty klesly na nulu.

Ukázka payloadu
{
  "storeId": 12,
  "productId": 88,
  "productOptionId": null
}
inventory.low_stock

Zásoby klesly pod nastavený práh nízkého stavu produktu. Zapíná se u jednotlivých produktů; bez prahu se událost nespouští.

Ukázka payloadu
{
  "storeId": 12,
  "productId": 88,
  "productOptionId": null,
  "stock": 4,
  "threshold": 5
}

Obchod

STORE
store.updated

Změnila se identifikační nastavení obchodu, například vlastní doména.

Ukázka payloadu
{
  "storeId": 12,
  "changes": {
    "domain": "shop.example.com"
  },
  "store": {
    "id": 12,
    "name": "Example Shop",
    "slug": "example",
    "domain": "shop.example.com"
  }
}

Doprava

SHIPPING
order.shipped

Zásilka objednávky poprvé přešla do odeslaného stavu.

Ukázka payloadu
{
  "shippingId": 9001,
  "orderId": 5501,
  "storeId": 12,
  "status": "shipped",
  "trackingNumber": "1Z999AA10123456784",
  "trackingUrl": "https://tracking.example.com/1Z999AA10123456784",
  "shippedAt": "2026-08-12T09:00:00.000Z"
}
order.delivered

Zásilka byla doručena.

Ukázka payloadu
{
  "shippingId": 9001,
  "orderId": 5501,
  "storeId": 12,
  "status": "delivered",
  "trackingNumber": "1Z999AA10123456784",
  "trackingUrl": null,
  "shippedAt": "2026-08-12T09:00:00.000Z"
}
shipping.status_changed

Každá změna stavu přepravy, s hodnotami před a po.

Ukázka payloadu
{
  "shippingId": 9001,
  "orderId": 5501,
  "storeId": 12,
  "statusBefore": "shipped",
  "statusAfter": "in_transit",
  "status": "in_transit",
  "trackingNumber": "1Z999AA10123456784",
  "trackingUrl": null,
  "shippedAt": "2026-08-12T09:00:00.000Z"
}
shipping.tracking_updated

Bylo nastaveno nebo změněno sledovací číslo či sledovací URL.

Ukázka payloadu
{
  "shippingId": 9001,
  "orderId": 5501,
  "storeId": 12,
  "status": "shipped",
  "trackingNumber": "1Z999AA10123456784",
  "trackingUrl": "https://tracking.example.com/1Z999AA10123456784",
  "shippedAt": null
}

Předplatná

SUBSCRIPTION
subscription.created

Objednání předplatného bylo dokončeno a předplatné se aktivovalo.

Ukázka payloadu
{
  "subscriptionId": 9001,
  "customerId": 31,
  "productId": 88,
  "status": "active",
  "providerSubscriptionId": "sub_1PqXo4"
}
subscription.renewed

Opakovaná faktura byla zaplacena a vznikla objednávka prodloužení. Spouští se od druhé zaplacené faktury.

Ukázka payloadu
{
  "subscriptionId": 9001,
  "customerId": 31,
  "productId": 88,
  "orderId": 5610,
  "invoiceId": 441,
  "amountMinor": "1999",
  "currency": "USD",
  "periodEnd": "2026-09-11T00:00:00.000Z"
}
subscription.payment_failed

Platba za prodloužení selhala. Spouští se při prvním zaznamenání každé neúspěšné faktury.

Ukázka payloadu
{
  "subscriptionId": 9001,
  "customerId": 31,
  "productId": 88,
  "invoiceId": 442,
  "failureReason": "card_declined",
  "attemptCount": 2
}
subscription.resumed

Čekající zrušení bylo odvoláno — protějšek události subscription.cancel_soon.

Ukázka payloadu
{
  "subscriptionId": 9001,
  "customerId": 31,
  "productId": 88,
  "status": "active",
  "currentPeriodEnd": "2026-09-01T00:00:00.000Z"
}

Platby

PAYMENT
payment.succeeded

Platba byla vypořádána, včetně metod s odloženým asynchronním potvrzením.

Ukázka payloadu
{
  "paymentId": 771,
  "orderId": 5501,
  "storeId": 12,
  "amountMinor": "12900",
  "currency": "USD",
  "providerPaymentIntentId": "pi_3PqXo42e"
}
payment.failed

Platba definitivně selhala. Opakovatelné pokusy se neohlašují.

Ukázka payloadu
{
  "paymentId": 771,
  "orderId": 5501,
  "storeId": 12,
  "amountMinor": "12900",
  "currency": "USD",
  "providerPaymentIntentId": "pi_3PqXo42e",
  "failureMessage": "The shopper abandoned the 3DS challenge."
}

Spory

DISPUTE
dispute.created

Proti platbě nebo faktuře předplatného byl otevřen chargeback.

Ukázka payloadu
{
  "paymentId": 771,
  "orderId": 5501,
  "storeId": 12,
  "disputeStatus": "open",
  "providerPaymentIntentId": "pi_3PqXo42e",
  "source": "payment"
}
dispute.updated

Poskytovatel aktualizoval otevřený spor.

Ukázka payloadu
{
  "paymentId": 771,
  "orderId": 5501,
  "storeId": 12,
  "disputeStatus": "under_review",
  "providerPaymentIntentId": "pi_3PqXo42e",
  "source": "payment"
}
dispute.closed

Spor byl uzavřen; disputeStatus říká, zda byl vyhrán, nebo prohrán.

Ukázka payloadu
{
  "paymentId": 771,
  "orderId": 5501,
  "storeId": 12,
  "disputeStatus": "won",
  "providerPaymentIntentId": "pi_3PqXo42e",
  "source": "payment"
}

Změny zákazníka

CUSTOMER
customer.updated

Administrátor upravil profil zákazníka; changes uvádí dotčená pole.

Ukázka payloadu
{
  "customerId": 31,
  "storeId": 12,
  "email": "jane@example.com",
  "firstName": "Jane",
  "lastName": "Doe",
  "changes": [
    "firstName"
  ]
}
customer.deleted

Zákaznický účet byl smazán. Payload je posledním snímkem záznamu.

Ukázka payloadu
{
  "customerId": 31,
  "storeId": 12,
  "email": "jane@example.com",
  "firstName": "Jane",
  "lastName": "Doe"
}

E-mailový marketing

EMAIL_MARKETING
subscriber.added

Adresa se přidala do mailing listu — veřejné přihlášení, opětovné přihlášení nebo přidání administrátorem.

Ukázka payloadu
{
  "subscriptionId": 1201,
  "storeId": 12,
  "email": "jane@example.com"
}
subscriber.confirmed

Odběratel dokončil dvojité potvrzení, nebo ho potvrdil administrátor.

Ukázka payloadu
{
  "subscriptionId": 1201,
  "storeId": 12,
  "email": "jane@example.com"
}
subscriber.unsubscribed

Odběratel opustil seznam, přes veřejný odkaz nebo administrátora.

Ukázka payloadu
{
  "subscriptionId": 1201,
  "storeId": 12,
  "email": "jane@example.com"
}
campaign.sent

Začala rozesílka kampaně; publikum je vyhodnocené a spočítané.

Ukázka payloadu
{
  "campaignId": 55,
  "storeId": 12,
  "name": "Fall drop",
  "subject": "New arrivals",
  "recipientCount": 1240
}
campaign.completed

Kampaň dokončila rozesílku, s konečnými počty odeslaných a neúspěšných.

Ukázka payloadu
{
  "campaignId": 55,
  "storeId": 12,
  "name": "Fall drop",
  "status": "sent",
  "recipientCount": 1240,
  "sentCount": 1236,
  "failedCount": 4
}

Opuštěný nákup

CHECKOUT_ABANDONMENT
cart.abandoned

Košík zákazníka byl označen jako opuštěný.

Ukázka payloadu
{
  "cartId": 3301,
  "storeId": 12,
  "customerId": 31
}
checkout.expired

Hostovaná platební relace vypršela nezaplacená a objednávka byla vrácena zpět.

Ukázka payloadu
{
  "orderId": 5501,
  "storeId": 12,
  "providerPaymentIntentId": "pi_3PqXo42e"
}