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

https://discopay.windevbilisim.com.tr/api
Ücretsiz kullanım. API ve bot için herhangi bir ücret veya komisyon alınmaz. Para doğrudan müşteriden sizin hesabınıza geçer; DiscoPay yalnızca sipariş ve onay sürecini yönetir.

Hızlı başlangıç

  1. Botu Discord sunucunuza ekleyin.
  2. Sunucunuzda /panel yazı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.
  3. 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).
  4. Size verilen API Key ve API Secret ile ödeme adımında sipariş oluşturun ve müşteriyi payment_url adresine yönlendirin.
  5. Webhook dosyanızda imzayı doğrulayın ve order.paid olayında siparişi tamamlayın.

Ödeme akışı

Siteniz → APIPOST /v1/orders ile sipariş oluşturulur, yanıtta payment_url döner.
Müşteri → Ödeme sayfasıTutar ve sipariş özeti gösterilir; müşteri "Discord ile Devam Et" butonuna basar.
Discord girişiMüşteri sunucunuza otomatik eklenir, bot yalnızca müşterinin ve yetkililerin görebildiği bir kanal açar. Sipariş durumu ticket_open olur.
Ödeme ve onayMüşteri ödemeyi yapar ve dekontu kanala gönderir. Yetkili /ödeme-onayla komutunu kullanır veya "Ödemeyi Onayla" butonuna basar.
API → SitenizWebhook adresinize imzalı order.paid isteği gönderilir. Müşteri return_url adresine dönebilir.
Önemli: Siparişi yalnızca webhook ile veya sipariş sorgulama sonucuna göre tamamlayın. Müşterinin 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.

AnahtarBiçimKullanım
API Keydp_live_…Her istekte X-API-Key başlığında gönderilir. Gizli değildir.
API Secretdps_…İ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.
Secret'ın ele geçirildiğini düşünüyorsanız /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ıkAçıklama
X-API-KeySitenizin API anahtarı.
X-TimestampUnix zaman damgası (saniye). Sunucu saatiyle arasındaki fark 5 dakikadan fazla olursa istek reddedilir.
X-SignatureAş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:

timestamp + METHOD + path + body
  • METHOD bü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

POSThttps://discopay.windevbilisim.com.tr/api/v1/orders

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

AlanTürAçıklama
order_idZORUNLUstringSitenizdeki sipariş numarası. 1–100 karakter; harf, rakam ve _ . : # / -. Site başına benzersizdir.
amountZORUNLUnumberÖdenecek tutar. Nokta ile ondalık, en fazla 2 hane: 349.90
currencystringTRY (varsayılan), USD veya EUR.
descriptionstringTalep kanalında ve ödeme sayfasında gösterilen açıklama. En fazla 500 karakter.
customer.namestringMüşteri adı.
customer.emailstringMüşteri e-postası. Yalnızca yetkililere gösterilir.
customer.phonestringMüşteri telefonu.
return_urlstringÖdeme sonuçlandığında müşteriye gösterilen "Siteye Dön" butonunun adresi.
callback_urlstringBu sipariş için webhook adresi. Boş bırakılırsa sitenin kayıtlı webhook adresi kullanılır.
metadataobjectKendi 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önlendirin

Yanı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

GEThttps://discopay.windevbilisim.com.tr/api/v1/orders/{id}

{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

POSThttps://discopay.windevbilisim.com.tr/api/v1/orders/{id}/cancel

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

GEThttps://discopay.windevbilisim.com.tr/api/v1/ping

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.

OlayNe zaman
order.paidYetkili ödemeyi onayladığında. Siparişi bu olayda tamamlayın.
order.rejectedYetkili ödemeyi reddettiğinde. Sebep reject_reason alanındadır.
order.cancelledTalep ödeme alınmadan kapatıldığında veya API ile iptal edildiğinde.
order.expiredMüş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:

HMAC_SHA256( X-DiscoPay-Timestamp + "." + ham_gövde , api_secret )

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ır
const 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:

Deneme123456
Beklemehemen1 dk5 dk15 dk1 saat6 saat
Aynı olay birden fazla kez gelebilir. İşleminizi idempotent yapın: sipariş zaten "ödendi" ise tekrar işlem yapmayın.

Sipariş nesnesi

AlanTürAçıklama
idstringDiscoPay sipariş numarası, ör. DP-7H2MXQ4R.
order_idstringSizin gönderdiğiniz sipariş numarası.
amountstringTutar, 2 ondalık haneli metin: "349.90"
currencystringTRY, USD, EUR
statusstringSipariş durumu.
descriptionstring | nullAçıklama.
customerobjectname, email, discord_id, discord_username. Discord alanları müşteri giriş yaptıktan sonra dolar.
confirmed_bystring | nullOnaylayan veya reddeden yetkilinin adı.
reject_reasonstring | nullRed sebebi.
metadataobject | nullGönderdiğiniz metadata.
paid_atstring | nullOnay zamanı.
created_atstringOluşturma zamanı.
payment_urlstringMüşterinin yönlendirileceği adres. Yalnızca API yanıtlarında bulunur.
expires_atstringÖdeme bağlantısının son geçerlilik zamanı. Yalnızca API yanıtlarında bulunur.

Sipariş durumları

DurumAnlamı
pendingSipariş oluşturuldu, müşteri henüz Discord ile giriş yapmadı.
ticket_openTalep kanalı açıldı, ödeme onayı bekleniyor.
paidÖdeme onaylandı. Kesin durumdur.
rejectedÖdeme reddedildi. Kesin durumdur.
cancelledSipariş iptal edildi. Kesin durumdur.
expiredMüş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ı."
  }
}
HTTPcodeAçıklama
400invalid_jsonGövde geçerli bir JSON değil.
401unauthorizedKimlik doğrulama başlıklarından biri eksik.
401invalid_timestampZaman damgası 5 dakikadan eski ya da ileri. Sunucu saatinizi kontrol edin.
401invalid_keyAPI anahtarı bulunamadı.
401invalid_signatureİmza eşleşmedi. İmzaladığınız gövdeyi birebir gönderdiğinizden emin olun.
403site_inactiveSite /panel üzerinden pasif yapılmış.
403bannedSite veya sunucu sistemden uzaklaştırılmış.
404not_foundSipariş bulunamadı.
404route_not_foundAdres veya yöntem hatalı.
409bot_not_in_guildBot, siteye bağlı Discord sunucusunda değil.
409duplicate_orderBu order_id ile oluşturulmuş sipariş sonuçlanmış. Yeni bir numara kullanın.
409not_cancellableSonuçlanmış sipariş iptal edilemez.
422invalid_*Parametre doğrulama hatası (invalid_amount, invalid_order_id, invalid_email …).
429rate_limitedDakikada 60'tan fazla sipariş oluşturuldu.
503maintenanceSistem 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.

DiscoPay.php dosyasını indir

MetotAçı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 amount kendi kaydınızla karşılaştırılıyor.
  • Sunucunuzda talep kategorisi, yetkili rolü ve ödeme bilgileri ayarlı (/panel ana ekranındaki listede hepsi ✅).
  • Küçük bir tutarla gerçek bir test siparişi oluşturup onaylandı ve sitede tamamlandı.