Det här dokumentet finns än så länge bara på turkiska.
Webhook
Mağazanda bir olay olunca adresine imzalı JSON gönderilir: sipariş, ürün, stok ve müşteri olayları. Abonelik oluşturma, imza doğrulama örnekleri, yeniden deneme kuralları ve olay gövdeleri.
Abonelik
Mağazam → Ayarlar → Geliştirici sayfasından ya da API'den açılır. Mağaza başına en çok 10 abonelik açılır, sınır aşılınca 422 döner. Adres herkese açık bir sunucuda olmalıdır; iç ağ ve localhost kabul edilmez.
Bir uygulama anahtarı yalnız kendi açtığı abonelikleri görür ve siler; uygulama kaldırılınca abonelikleri de silinir. Mağaza sınırı uygulamanın abonelikleriyle birlikte sayılır.
| Yöntem ve adres | Kapsam | Ne yapar |
|---|---|---|
GET /webhooks |
webhooks.read |
Abonelikleri, hata sayısını ve son gönderimi listeler |
POST /webhooks |
webhooks.write |
Abonelik açar, imza anahtarını döner |
DELETE /webhooks/{id} |
webhooks.write |
Siler, 204 döner |
curl -X POST "https://piyodo.com/api/store/v1/webhooks" \
-H "Authorization: Bearer psk_..." \
-H "Content-Type: application/json" \
-d '{"url": "https://ornek.com/piyodo", "events": ["order.paid", "stock.updated"]}'{
"data": {
"id": "01J...",
"url": "https://ornek.com/piyodo",
"events": ["order.paid", "stock.updated"],
"active": true,
"secret": "whsec_..."
}
}secret yalnız bu yanıtta döner, sakla. events içinde * tüm olayları abone eder.
Gönderim
Her olayda adresine POST gelir:
{
"event": "order.paid",
"store_id": "01J...",
"created_at": "2026-11-05T10:00:00+00:00",
"data": {}
}| Başlık | Değer |
|---|---|
Content-Type |
application/json |
X-Piyodo-Event |
Olay adı |
X-Piyodo-Signature |
sha256=<HMAC-SHA256(imza anahtarı, ham gövde)> |
User-Agent |
Piyodo-Webhook/1.0 |
İmza doğrulama
Gelen isteğin Piyodo'dan geldiğini imzayla doğrula. HMAC'i ayrıştırılmamış ham gövde üzerinden hesapla: JSON'u çözüp yeniden üretirsen imza tutmaz. Karşılaştırmayı sabit süreli yap.
PHP:
$body = file_get_contents('php://input');
$expected = 'sha256='.hash_hmac('sha256', $body, getenv('PIYODO_WEBHOOK_SECRET'));
if (! hash_equals($expected, $_SERVER['HTTP_X_PIYODO_SIGNATURE'] ?? '')) {
http_response_code(401);
exit;
}
$event = json_decode($body, true);
http_response_code(200);Node.js (Express):
import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post('/piyodo', express.raw({ type: 'application/json' }), (req, res) => {
const expected = Buffer.from('sha256=' + crypto.createHmac('sha256', process.env.PIYODO_WEBHOOK_SECRET).update(req.body).digest('hex'));
const received = Buffer.from(req.get('X-Piyodo-Signature') ?? '');
if (received.length !== expected.length || !crypto.timingSafeEqual(received, expected)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body);
res.sendStatus(200);
});Python (Flask):
import hashlib, hmac, os
from flask import Flask, abort, request
app = Flask(__name__)
@app.post('/piyodo')
def piyodo():
expected = 'sha256=' + hmac.new(os.environ['PIYODO_WEBHOOK_SECRET'].encode(), request.get_data(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get('X-Piyodo-Signature', '')):
abort(401)
event = request.get_json()
return '', 200Yeniden deneme
- Alıcı 10 saniye içinde 2xx dönmezse gönderim başarısız sayılır.
- Başarısız gönderim 30 saniye ve 5 dakika sonra yeniden denenir, toplam 3 deneme.
- Yönlendirme izlenmez:
301ya da302de başarısızdır. - 3 deneme de başarısızsa hata sayacı bir artar, son hata panelde görünür. Başarılı gönderim sayacı sıfırlar.
- Üst üste 20 olay başarısız olursa abonelik kapanır. Panelden yeniden açınca sayaç sıfırlanır.
Yanıtı kısa tut, uzun işi kuyruğa at. Aynı olay birden çok kez gelebilir: yanıtın yolda kaybolması yeniden deneme doğurur. Yazılımın aynı olayı iki kez işlemeyecek biçimde kurulmalıdır.
Olaylar
| Olay | Ne zaman | data |
|---|---|---|
order.created |
Sipariş oluşturuldu | Sipariş |
order.paid |
Sipariş ödendi | Sipariş |
order.confirmed |
Kapıda ödeme onaylandı | Sipariş |
order.shipped |
Sipariş kargolandı | Sipariş |
order.delivered |
Sipariş teslim edildi | Sipariş |
return.requested |
İade talebi açıldı | id, order_id, order_number, reason, note, quantities |
booking.created |
Randevu alındı | id, service, status, starts_at, ends_at, customer, order_id |
booking.cancelled |
Randevu iptal edildi | Aynı alanlar |
order.cancelled |
Sipariş iptal edildi | Sipariş |
order.refunded |
Sipariş iade edildi | Sipariş ve refund_amount |
product.created |
Ürün eklendi | Ürün |
product.updated |
Ürün güncellendi | Ürün |
product.deleted |
Ürün silindi | id, slug |
stock.updated |
Stok değişti | product_id, variant_id, sku, stock |
customer.created |
Müşteri eklendi | Müşteri |
ping |
Paneldeki Dene düğmesi | message |
Ürün ve müşteri gövdesi de API yanıtıyla aynıdır.
Sipariş gövdesi
Sipariş olaylarının data alanı API yanıtıyla aynıdır: tutarlar "899.90" biçiminde ondalık metindir, dövizli siparişte charge alanı gelir.
order.refunded ayrıca bu iadenin tutarını mağaza para biriminde refund_amount içinde taşır. Toplam iade refunded_total alanındadır.
{
"event": "order.refunded",
"store_id": "01J...",
"created_at": "2026-11-05T10:00:00+00:00",
"data": {
"id": "01J...",
"number": 1042,
"status": "paid",
"total": "1000.00",
"refunded_total": "250.00",
"refund_amount": "250.00"
}
}Örnek kısaltılmıştır, data içinde siparişin tüm alanları bulunur.