API Referansı
DiscoPay API'si, internet sitenizdeki siparişlerin ödemesini Discord sunucunuz üzerinden almanızı sağlar. Siteniz bir sipariş oluşturur, müşteriyi ödeme bağlantısına yönlendirir; ödeme sunucu yetkiliniz tarafından onaylandığında sitenize imzalı bir webhook gönderilir.
Tüm istekler ve yanıtlar application/json biçimindedir, karakter kodlaması UTF-8'dir. Tarihler ISO 8601 (+03:00) olarak döner.
Temel adres
Hızlı başlangıç
- Botu Discord sunucunuza ekleyin.
- Sunucunuzda
/panelyazın. Ayarlar bölümünden talep kategorisini, yetkili rolünü ve log kanalını seçin; Ödeme Bilgileri bölümüne IBAN veya Papara bilgilerinizi girin. - Siteler & API → Site Ekle ile sitenizi kaydedin. Webhook adresi olarak sitenizde oluşturacağınız dosyanın tam adresini yazın (ör.
https://magazam.com/discopay-callback.php). - Size verilen API Key ve API Secret ile ödeme adımında sipariş oluşturun ve müşteriyi
payment_urladresine yönlendirin. - Webhook dosyanızda imzayı doğrulayın ve
order.paidolayında siparişi tamamlayın.
Ödeme akışı
POST /v1/orders ile sipariş oluşturulur, yanıtta payment_url döner.ticket_open olur./ödeme-onayla komutunu kullanır veya "Ödemeyi Onayla" butonuna basar.order.paid isteği gönderilir. Müşteri return_url adresine dönebilir.return_url adresine dönmesi ödemenin yapıldığı anlamına gelmez.API anahtarları
Her site için iki bilgi üretilir. Bunlara Discord'da /panel → Siteler & API bölümünden ulaşabilirsiniz.
| Anahtar | Biçim | Kullanım |
|---|---|---|
| API Key | dp_live_… | Her istekte X-API-Key başlığında gönderilir. Gizli değildir. |
| API Secret | dps_… | İstekleri imzalamak ve webhook imzasını doğrulamak için kullanılır. Asla tarayıcıya, mobil uygulamaya veya herkese açık bir depoya koymayın. |
/panel → site → Secret Yenile ile hemen değiştirin. Eski secret anında geçersiz olur.İstek imzalama
Her API isteği üç başlık taşımalıdır:
| Başlık | Açıklama |
|---|---|
| X-API-Key | Sitenizin API anahtarı. |
| X-Timestamp | Unix zaman damgası (saniye). Sunucu saatiyle arasındaki fark 5 dakikadan fazla olursa istek reddedilir. |
| X-Signature | Aşağıdaki metnin API Secret ile alınmış HMAC-SHA256 özetinin küçük harf hex hali. |
İmzalanacak metin, dört parçanın aralarında boşluk olmadan birleştirilmesiyle elde edilir:
METHODbüyük harfle yazılır:GET,POST.path,/apiönekinden sonraki kısımdır ve sorgu dizesi içermez:/v1/orders,/v1/orders/10245/cancel.body, gönderdiğiniz JSON metninin birebir aynısıdır. Gövdesi olmayan isteklerde boş string kullanılır.
$ts = (string) time();
$method = 'POST';
$path = '/v1/orders'; // /api öneki ve sorgu dizesi hariç
$body = json_encode($data); // GET isteklerinde boş string
$signature = hash_hmac('sha256', $ts . $method . $path . $body, $apiSecret);const ts = Math.floor(Date.now() / 1000).toString();
const body = JSON.stringify(data); // GET isteklerinde ''
const signature = crypto.createHmac('sha256', apiSecret)
.update(ts + 'POST' + '/v1/orders' + body)
.digest('hex');ts = str(int(time.time()))
body = json.dumps(data, separators=(",", ":")) # imzaladığınız metni aynen gönderin
signature = hmac.new(api_secret.encode(), (ts + "POST" + "/v1/orders" + body).encode(), hashlib.sha256).hexdigest()Sipariş oluştur
Yeni bir ödeme siparişi oluşturur ve müşterinin yönlendirileceği payment_url adresini döndürür. Aynı order_id ile tekrar istek atarsanız ve sipariş hâlâ açıksa yeni kayıt açılmaz, mevcut sipariş döner.
Gövde parametreleri
| Alan | Tür | Açıklama |
|---|---|---|
| order_idZORUNLU | string | Sitenizdeki sipariş numarası. 1–100 karakter; harf, rakam ve _ . : # / -. Site başına benzersizdir. |
| amountZORUNLU | number | Ödenecek tutar. Nokta ile ondalık, en fazla 2 hane: 349.90 |
| currency | string | TRY (varsayılan), USD veya EUR. |
| description | string | Talep kanalında ve ödeme sayfasında gösterilen açıklama. En fazla 500 karakter. |
| customer.name | string | Müşteri adı. |
| customer.email | string | Müşteri e-postası. Yalnızca yetkililere gösterilir. |
| customer.phone | string | Müşteri telefonu. |
| return_url | string | Ödeme sonuçlandığında müşteriye gösterilen "Siteye Dön" butonunun adresi. |
| callback_url | string | Bu sipariş için webhook adresi. Boş bırakılırsa sitenin kayıtlı webhook adresi kullanılır. |
| metadata | object | Kendi verileriniz (ör. kullanıcı ID). Webhook'ta aynen geri gönderilir. En fazla 4 KB. |
İstek
require 'DiscoPay.php';
$kp = new DiscoPay('https://discopay.windevbilisim.com.tr', 'dp_live_xxxxxxxx', 'dps_xxxxxxxx');
try {
$order = $kp->createOrder([
'order_id' => '10245', // sitenizdeki sipariş numarası
'amount' => 349.90,
'currency' => 'TRY',
'description' => 'Premium paket (1 ay)',
'customer' => ['name' => 'Burak Yılmaz', 'email' => 'burak@ornek.com'],
'return_url' => 'https://magazam.com/siparis/10245',
'metadata' => ['user_id' => 812],
]);
header('Location: ' . $order['payment_url']);
exit;
} catch (RuntimeException $e) {
echo 'Ödeme başlatılamadı: ' . $e->getMessage();
}API_KEY="dp_live_xxxxxxxx"
API_SECRET="dps_xxxxxxxx"
TS=$(date +%s)
BODY='{"order_id":"10245","amount":349.90,"description":"Premium paket"}'
SIG=$(printf '%s' "${TS}POST/v1/orders${BODY}" | openssl dgst -sha256 -hmac "$API_SECRET" | sed 's/^.* //')
curl -X POST "https://discopay.windevbilisim.com.tr/api/v1/orders" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-H "X-Timestamp: $TS" \
-H "X-Signature: $SIG" \
-d "$BODY"const crypto = require('crypto');
const API = 'https://discopay.windevbilisim.com.tr/api';
const KEY = 'dp_live_xxxxxxxx';
const SECRET = 'dps_xxxxxxxx';
async function discopay(method, path, body) {
const json = body ? JSON.stringify(body) : '';
const ts = Math.floor(Date.now() / 1000).toString();
const sig = crypto.createHmac('sha256', SECRET).update(ts + method + path + json).digest('hex');
const res = await fetch(API + path, {
method,
headers: { 'Content-Type': 'application/json', 'X-API-Key': KEY, 'X-Timestamp': ts, 'X-Signature': sig },
body: json || undefined,
});
const data = await res.json();
if (!data.success) throw new Error(data.error.message);
return data.data;
}
const order = await discopay('POST', '/v1/orders', {
order_id: '10245',
amount: 349.90,
description: 'Premium paket',
return_url: 'https://magazam.com/siparis/10245',
});
// order.payment_url adresine yönlendirinYanıt 201 Created
{
"success": true,
"data": {
"id": "DP-7H2MXQ4R",
"order_id": "10245",
"amount": "349.90",
"currency": "TRY",
"status": "pending",
"description": "Premium paket (1 ay)",
"customer": {
"name": "Burak Yılmaz",
"email": "burak@ornek.com",
"discord_id": null,
"discord_username": null
},
"confirmed_by": null,
"reject_reason": null,
"metadata": { "user_id": 812 },
"paid_at": null,
"created_at": "2026-09-25T14:31:02+03:00",
"payment_url": "https://discopay.windevbilisim.com.tr/pay.php?t=3f9c…",
"expires_at": "2026-09-26T14:31:02+03:00",
"ticket_channel_id": null
}
} Sipariş sorgula
{id} yerine DiscoPay numarasını (DP-7H2MXQ4R) veya kendi order_id değerinizi yazabilirsiniz. Yanıt, sipariş nesnesidir.
$order = $kp->getOrder('10245'); // veya 'DP-7H2MXQ4R'
if ($order['status'] === 'paid') {
// siparişi tamamla
}TS=$(date +%s)
SIG=$(printf '%s' "${TS}GET/v1/orders/10245" | openssl dgst -sha256 -hmac "$API_SECRET" | sed 's/^.* //')
curl "https://discopay.windevbilisim.com.tr/api/v1/orders/10245" -H "X-API-Key: $API_KEY" -H "X-Timestamp: $TS" -H "X-Signature: $SIG"const order = await discopay('GET', '/v1/orders/10245');
console.log(order.status); // pending | ticket_open | paid | ...Sipariş iptal
Durumu pending veya ticket_open olan siparişi iptal eder. Talep kanalı açıksa müşteriye bilgi verilir ve order.cancelled webhook'u gönderilir. Gövde gönderilmez.
$order = $kp->cancelOrder('10245');TS=$(date +%s)
SIG=$(printf '%s' "${TS}POST/v1/orders/10245/cancel" | openssl dgst -sha256 -hmac "$API_SECRET" | sed 's/^.* //')
curl -X POST "https://discopay.windevbilisim.com.tr/api/v1/orders/10245/cancel" -H "X-API-Key: $API_KEY" -H "X-Timestamp: $TS" -H "X-Signature: $SIG"Bağlantı testi
API anahtarınızı ve imza hesaplamanızı doğrulamak için kullanın. Site adı, bağlı sunucu ve botun sunucuda olup olmadığı döner.
{
"success": true,
"data": { "site": "Mağazam", "guild": "Mağazam Destek", "bot_in_guild": true, "server_time": 1790335862 }
} Webhook olayları
Sipariş durumu değiştiğinde site kaydındaki webhook adresine (veya siparişe özel callback_url adresine) POST isteği gönderilir.
| Olay | Ne zaman |
|---|---|
| order.paid | Yetkili ödemeyi onayladığında. Siparişi bu olayda tamamlayın. |
| order.rejected | Yetkili ödemeyi reddettiğinde. Sebep reject_reason alanındadır. |
| order.cancelled | Talep ödeme alınmadan kapatıldığında veya API ile iptal edildiğinde. |
| order.expired | Müşteri ödeme bağlantısını geçerlilik süresi içinde kullanmadığında. |
Örnek istek
POST /discopay-callback.php HTTP/1.1
Content-Type: application/json
User-Agent: DiscoPay-Webhook/1.0
X-DiscoPay-Event: order.paid
X-DiscoPay-Timestamp: 1790335862
X-DiscoPay-Signature: 5c1f0e2b9d…
{
"event": "order.paid",
"order": {
"id": "DP-7H2MXQ4R",
"order_id": "10245",
"amount": "349.90",
"currency": "TRY",
"status": "paid",
"description": "Premium paket (1 ay)",
"customer": {
"name": "Burak Yılmaz",
"email": "burak@ornek.com",
"discord_id": "284019375102934017",
"discord_username": "Burak (@burak.y)"
},
"confirmed_by": "Yetkili Mert",
"reject_reason": null,
"metadata": { "user_id": 812 },
"paid_at": "2026-09-25T14:36:40+03:00",
"created_at": "2026-09-25T14:31:02+03:00"
}
} Webhook imzasını doğrulama
Webhook'un gerçekten DiscoPay'dan geldiğinden emin olmak için imzayı mutlaka kontrol edin:
Ham gövde, isteğin JSON olarak ayrıştırılmadan önceki halidir. Zaman damgası 5 dakikadan eskiyse isteği reddetmeniz önerilir. Ayrıca tutarın kendi kaydınızla eşleştiğini kontrol edin.
<?php
// discopay-callback.php
require 'DiscoPay.php';
$kp = new DiscoPay('https://discopay.windevbilisim.com.tr', 'dp_live_xxxxxxxx', 'dps_xxxxxxxx');
$event = $kp->verifyWebhook();
if ($event === null) {
http_response_code(400);
exit('INVALID');
}
$o = $event['order'];
$siparis = $db->siparisBul($o['order_id']); // kendi sipariş tablonuz
if ($siparis && (float)$siparis['tutar'] === (float)$o['amount']) {
switch ($event['event']) {
case 'order.paid':
if ($siparis['durum'] !== 'odendi') { // tekrar gelen webhook'ta iki kez işlemeyin
$db->siparisDurum($o['order_id'], 'odendi');
}
break;
case 'order.rejected':
case 'order.cancelled':
case 'order.expired':
$db->siparisDurum($o['order_id'], 'iptal');
break;
}
}
echo 'OK'; // 2xx yanıt verdiğinizde webhook teslim edilmiş sayılırconst express = require('express');
const crypto = require('crypto');
const SECRET = 'dps_xxxxxxxx';
const app = express();
app.post('/discopay-callback', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-DiscoPay-Timestamp');
const sig = req.get('X-DiscoPay-Signature') || '';
const raw = req.body.toString('utf8');
const expected = crypto.createHmac('sha256', SECRET).update(ts + '.' + raw).digest('hex');
const valid = sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)) &&
Math.abs(Date.now() / 1000 - Number(ts)) < 300;
if (!valid) return res.status(400).send('INVALID');
const { event, order } = JSON.parse(raw);
if (event === 'order.paid') {
// order.order_id siparişini "ödendi" yapın
}
res.send('OK');
});
app.listen(3000);import hmac, hashlib, time, json
from flask import Flask, request
SECRET = b"dps_xxxxxxxx"
app = Flask(__name__)
@app.post("/discopay-callback")
def callback():
ts = request.headers.get("X-DiscoPay-Timestamp", "")
sig = request.headers.get("X-DiscoPay-Signature", "")
raw = request.get_data()
expected = hmac.new(SECRET, ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, expected) or abs(time.time() - int(ts or 0)) > 300:
return "INVALID", 400
data = json.loads(raw)
if data["event"] == "order.paid":
pass # data["order"]["order_id"] siparişini tamamlayın
return "OK"Yanıt ve tekrar denemeler
Webhook adresiniz 10 saniye içinde 2xx durum koduyla yanıt verdiğinde teslimat başarılı sayılır. Yanıt gövdesi önemli değildir, OK yazmanız yeterlidir. Yönlendirmeler (3xx) takip edilmez.
Başarısız teslimatlar şu aralıklarla tekrar denenir:
| Deneme | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| Bekleme | hemen | 1 dk | 5 dk | 15 dk | 1 saat | 6 saat |
Sipariş nesnesi
| Alan | Tür | Açıklama |
|---|---|---|
| id | string | DiscoPay sipariş numarası, ör. DP-7H2MXQ4R. |
| order_id | string | Sizin gönderdiğiniz sipariş numarası. |
| amount | string | Tutar, 2 ondalık haneli metin: "349.90" |
| currency | string | TRY, USD, EUR |
| status | string | Sipariş durumu. |
| description | string | null | Açıklama. |
| customer | object | name, email, discord_id, discord_username. Discord alanları müşteri giriş yaptıktan sonra dolar. |
| confirmed_by | string | null | Onaylayan veya reddeden yetkilinin adı. |
| reject_reason | string | null | Red sebebi. |
| metadata | object | null | Gönderdiğiniz metadata. |
| paid_at | string | null | Onay zamanı. |
| created_at | string | Oluşturma zamanı. |
| payment_url | string | Müşterinin yönlendirileceği adres. Yalnızca API yanıtlarında bulunur. |
| expires_at | string | Ödeme bağlantısının son geçerlilik zamanı. Yalnızca API yanıtlarında bulunur. |
Sipariş durumları
| Durum | Anlamı |
|---|---|
| pending | Sipariş oluşturuldu, müşteri henüz Discord ile giriş yapmadı. |
| ticket_open | Talep kanalı açıldı, ödeme onayı bekleniyor. |
| paid | Ödeme onaylandı. Kesin durumdur. |
| rejected | Ödeme reddedildi. Kesin durumdur. |
| cancelled | Sipariş iptal edildi. Kesin durumdur. |
| expired | Müşteri bağlantıyı süresi içinde kullanmadı. Kesin durumdur. |
Hata kodları
Hatalı isteklerde success: false ve makine tarafından okunabilir bir code döner:
{
"success": false,
"error": {
"code": "invalid_amount",
"message": "amount 0'dan büyük bir sayı olmalı."
}
} | HTTP | code | Açıklama |
|---|---|---|
| 400 | invalid_json | Gövde geçerli bir JSON değil. |
| 401 | unauthorized | Kimlik doğrulama başlıklarından biri eksik. |
| 401 | invalid_timestamp | Zaman damgası 5 dakikadan eski ya da ileri. Sunucu saatinizi kontrol edin. |
| 401 | invalid_key | API anahtarı bulunamadı. |
| 401 | invalid_signature | İmza eşleşmedi. İmzaladığınız gövdeyi birebir gönderdiğinizden emin olun. |
| 403 | site_inactive | Site /panel üzerinden pasif yapılmış. |
| 403 | banned | Site veya sunucu sistemden uzaklaştırılmış. |
| 404 | not_found | Sipariş bulunamadı. |
| 404 | route_not_found | Adres veya yöntem hatalı. |
| 409 | bot_not_in_guild | Bot, siteye bağlı Discord sunucusunda değil. |
| 409 | duplicate_order | Bu order_id ile oluşturulmuş sipariş sonuçlanmış. Yeni bir numara kullanın. |
| 409 | not_cancellable | Sonuçlanmış sipariş iptal edilemez. |
| 422 | invalid_* | Parametre doğrulama hatası (invalid_amount, invalid_order_id, invalid_email …). |
| 429 | rate_limited | Dakikada 60'tan fazla sipariş oluşturuldu. |
| 503 | maintenance | Sistem bakımda. |
PHP istemcisi
İmzalama ve webhook doğrulamayı sizin yerinize yapan tek dosyalık sınıf. PHP 7.4 ve üzeriyle, curl eklentisi açıkken çalışır.
| Metot | Açıklama |
|---|---|
| createOrder(array $data) | Sipariş oluşturur, sipariş nesnesini döndürür. |
| getOrder(string $ref) | Siparişi sorgular. |
| cancelOrder(string $ref) | Siparişi iptal eder. |
| ping() | Bağlantı testi. |
| verifyWebhook() | Gelen webhook'u doğrular; geçerliyse olay dizisini, değilse null döndürür. |
Hata durumunda RuntimeException fırlatılır; mesaj, API'nin döndürdüğü açıklamadır.
Canlıya geçiş listesi
ping()çağrısı başarılı dönüyor.- API Secret yalnızca sunucu tarafında, yapılandırma dosyasında duruyor.
- Webhook adresi HTTPS ve dışarıdan erişilebilir; imza doğrulaması yapılıyor.
- Webhook işleyicisi aynı olayı iki kez aldığında siparişi tekrar işlemiyor.
- Webhook'taki
amountkendi kaydınızla karşılaştırılıyor. - Sunucunuzda talep kategorisi, yetkili rolü ve ödeme bilgileri ayarlı (
/panelana ekranındaki listede hepsi ✅). - Küçük bir tutarla gerçek bir test siparişi oluşturup onaylandı ve sitede tamamlandı.