Başlarken API v2

API bilgileri
API Key
api_key
Genel
Mağazanızı API isteklerinde ve giden webhook'larda tanımlar.
API Secret
api_secret
Gizli
Oturum oluşturma ve webhook doğrulama için HMAC-SHA256 anahtarı. Tarayıcıya veya mobil uygulamaya koymayın.
Store ID
store_id
Genel
Her oturum isteğinde gönderilen sayısal mağaza numarası.

Entegrasyon adımları

  • 1 Oturum oluşturun — Backend'inizden müşteri bilgilerini ve HMAC-SHA256 hash'ini /checkout/token.php adresine POST edin. Başarılı olursa imzalı bir token ve redirect URL'si döner. Token 2 saat geçerlidir.
  • 2 Kullanıcıyı yönlendirin — Tarayıcıyı 1. adımda gelen redirect adresine hemen gönderin (önerilen yöntem). Aynı adresi bir iframe içinde de açabilirsiniz. Token'ı saklamayın, tekrar kullanmayın.
  • 3 Ödeme sayfası — Müşteri Dodopin sayfasında ürünü seçer ve ödemeyi tamamlar. Kart ve hassas ödeme verisi sizin sunucunuza gelmez.
  • 4 Webhook alın — Ödeme başarılı olunca Dodopin, kayıtlı Webhook URL adresinize imzalı bir HTTP POST gönderir. hash imzasını doğrulayın, status === "success" olduğundan emin olun ve siparişi order_ref ile kaydedin. HTTP 200 ve gövde olarak OK dönün.
Hash yalnızca sunucuda üretilir. api_secret değerini JavaScript'e veya mobil uygulamaya koymayın. /checkout/token.php istekleri backend'inizden gitmelidir.

iframe entegrasyonu

En temiz yöntem tam sayfa yönlendirmedir. Ürün seçimini kendi sayfanızda göstermek istiyorsanız ödeme URL'sini bir iframe içinde de açabilirsiniz. Sayfa çerçevelemeye izin verir (frame-ancestors *).

Nasıl çalışır

  • 1 Sunucunuzda Oturum oluşturma adımındaki gibi bir oturum alın. Dönen redirect URL'sini iframe src olarak kullanın.
  • 2 URL'de geçerli bir oturum token'ı varsa Dodopin kendi üst ve alt çubuğunu gizler; ödeme sayfanızın içine sığar. Geri butonu, başlık veya “tarayıcıda aç” gibi dış çerçeve size aittir — Dodopin bunları eklemez.
  • 3 Müşteri ödeme yöntemini onayladığında (ör. Devam et) Dodopin önce ödeme sağlayıcısını üst pencerede açmayı dener. Buna izin yoksa yeni bir pencere (sistem tarayıcısı) açılır; o da engellenirse iframe içinde kalır. Bu yüzden gömme ortamınızın üst sayfaya yönlendirmeye veya popup'a izin vermesi gerekir. sandbox kullanıyorsanız en az şunları ekleyin: allow-scripts allow-same-origin allow-forms allow-top-navigation allow-popups. Hem üst yönlendirmeyi hem popup'ı kesen oyun motoru / uygulama içi WebView ortamlarında ödemeyi sistem tarayıcısında açın — aksi halde ödeme butonu tıklanınca hiçbir şey olmuyor gibi görünür.
  • 4 Siparişi iframe'deki olaylara veya dönüş sayfasındaki JavaScript'e bakarak tamamlamayın. Kaynak imzalı webhook'tur. Müşteri ödemeyi çerçevenizin dışında da bitirebilir.

Örnek

<!-- redirect = value from Create Session JSON -->
<!-- Do not omit allow-top-navigation if you add sandbox="" -->
<iframe
  id="dodopin-checkout"
  src="https://dodopin.com/tr/store/your-store-slug?token=…&cur=TRY"
  title="Dodopin Checkout"
  style="width:100%;min-width:1024px;height:900px;border:0;overflow:auto;"
  scrolling="yes"
  allow="payment *"
  referrerpolicy="strict-origin-when-cross-origin"
></iframe>
<script>
window.addEventListener("message", function (e) {
  if (!e.data || e.data.source !== "dodopin") return;
  var f = document.getElementById("dodopin-checkout");
  if (!f) return;
  if (e.data.type === "embed-height" && e.data.height) {
    f.style.height = e.data.height + "px";
  }
  if (e.data.type === "embed-scroll-into-view") {
    try { f.scrollIntoView({ block: "nearest", inline: "nearest" }); } catch (err) {}
  }
});
</script>
Üst sayfayı HTTPS üzerinden yayın. iframe'e yer verin: en az 1024px genişlik, yaklaşık 900px yükseklik; kaydırmayı kapatmayın (scrolling="no" veya overflow:hidden). Ödeme sayfası çerçevenin içinde kayar; ayrıca {source:"dodopin", type:"embed-height", height} mesajı gönderir, böylece iframe yüksekliğini içeriğe göre büyütebilirsiniz. Sipariş formu açılınca {source:"dodopin", type:"embed-scroll-into-view"} da gelir; çerçeveyi görünür tutmak için kullanın. Yaklaşık 992px'in altında ödeme sayfası mobil görünüme geçer.
Ödeme butonu çalışmıyor mu? Stripe Checkout ve çoğu banka / 3D Secure sayfası X-Frame-Options / frame-ancestors ile iframe içine alınmayı reddeder. Dodopin sırayla dener: window.top → yeni pencere → aynı iframe. Gömme ortamınız (özellikle oyun motoru WebView) hem üst yönlendirmeyi hem popup'ı kesiyorsa ödeme iframe içinde ilerleyemez — sistem tarayıcısında açın veya tam sayfa yönlendirme kullanın. Ödeme onayı için postMessage'e güvenmeyin; webhook kullanın.

Kimlik doğrulama

API iki yerde HMAC-SHA256 kullanır: (1) Oturum oluşturma isteğini siz imzalarsınız; böylece api_secret istek gövdesine girmez. (2) Platform her webhook'u imzalar; böylece bildirimin sahte olmadığını doğrularsınız.

İstek hash'i (oturum oluşturma)

  hash_string =
 api_key
+ "|" +store_id
+ "|" +user_id
+ "|" +username
+ "|" +user_email
hash = base64_encode( HMAC_SHA256( hash_string, api_secret ))
Alanları gösterilen sırayla, aralarına pipe (|) koyarak birleştirin. base64_encode için HMAC'in ham (binary) çıktısını kullanın — önce hex'e çevirmeyin. api_secret imza anahtarıdır; istek gövdesinde asla gönderilmez.

Webhook imza formülü

  hash_string =
 merchant_id
+order_ref
+user_fullname
+invoice_mail
+gateway_name
+status
+api_key
hash = base64_encode( HMAC_SHA256( hash_string, api_secret ))

Webhook doğrulama örneği

$API_KEY    = 'YOUR_API_KEY';
$API_SECRET = 'YOUR_API_SECRET';

// All fields come directly from the flat POST body
$hash_string =
    ($_POST['merchant_id']  ?? '') .
    ($_POST['order_ref']    ?? '') .
    ($_POST['user_fullname'] ?? '') .
    ($_POST['invoice_mail'] ?? '') .
    ($_POST['gateway_name'] ?? '') .
    ($_POST['status']       ?? '') .
    $API_KEY;

$expected = base64_encode(
    hash_hmac('sha256', $hash_string, $API_SECRET, true)
);

// hash_equals() prevents timing attacks
if (!hash_equals($expected, $_POST['hash'] ?? '')) {
    http_response_code(403);
    die('invalid_hash');
}
const crypto = require('crypto');

// All fields come directly from the flat POST body
const hashString =
    String(req.body.merchant_id  ?? '') +
    String(req.body.order_ref    ?? '') +
    String(req.body.user_fullname ?? '') +
    String(req.body.invoice_mail ?? '') +
    String(req.body.gateway_name ?? '') +
    String(req.body.status       ?? '') +
    API_KEY;

const expected = Buffer
    .from(crypto.createHmac('sha256', API_SECRET).update(hashString).digest())
    .toString('base64');

// timingSafeEqual prevents timing attacks
const a = Buffer.from(req.body.hash ?? '');
const b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(403).send('invalid_hash');
}
import hmac, hashlib, base64
from flask import request

API_KEY    = 'YOUR_API_KEY'
API_SECRET = 'YOUR_API_SECRET'

# All fields come directly from the flat POST body
f = lambda k: request.form.get(k, '')
hash_string = (
    f('merchant_id')  +
    f('order_ref')    +
    f('user_fullname') +
    f('invoice_mail') +
    f('gateway_name') +
    f('status')       +
    API_KEY
)

expected = base64.b64encode(
    hmac.new(API_SECRET.encode(), hash_string.encode(), hashlib.sha256).digest()
).decode()

# compare_digest prevents timing attacks
if not hmac.compare_digest(f('hash'), expected):
    abort(403)

Ödeme oturumu oluşturma

API bilgilerinizi ve müşteri verilerini sunucunuzdan token endpoint'ine POST edin. Başarılı yanıtta tek kullanımlık, imzalı bir redirect URL'si gelir. Ödemeyi başlatmak için müşterinin tarayıcısını bu adrese yönlendirin.

POST https://dodopin.com/checkout/token.php Content-Type: form-urlencoded → JSON

Kod örnekleri

<?php
$API_KEY    = 'YOUR_API_KEY';
$API_SECRET = 'YOUR_API_SECRET';
$store_id   = 12345;
$user_id    = 678;
$username   = 'username';
$user_email = '[email protected]';

$hash = base64_encode(
    hash_hmac('sha256', $API_KEY . '|' . $store_id . '|' . $user_id . '|' . $username . '|' . $user_email, $API_SECRET, true)
);

$post_data = [
    'api_key'       => $API_KEY,
    'hash'          => $hash,
    'store_id'      => $store_id,
    'user_id'       => $user_id,
    'username'      => $username,
    'user_email'    => $user_email,
    'user_ip'       => $_SERVER['REMOTE_ADDR'] ?? '',
    'user_fullname' => 'John Doe',
    'user_phone'    => '+13125550100',
    'lang'          => 'tr',        // page opening language (default: tr)
    'currency'      => 'TRY',       // page opening currency (default: TRY)
];

$ch = curl_init('https://dodopin.com/checkout/token.php');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $post_data,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_TIMEOUT        => 30,
]);

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

if ($response['success'] ?? false) {
    header('Location: ' . $response['redirect']);
    exit;
}

throw new RuntimeException('Token error: ' . ($response['message'] ?? 'unknown'));
?>
const https  = require('https');
const qs     = require('querystring');
const crypto = require('crypto');

const API_KEY    = 'YOUR_API_KEY';
const API_SECRET = 'YOUR_API_SECRET';
const store_id   = 12345;
const user_id    = 678;
const username   = 'username';
const user_email = '[email protected]';

const hash = Buffer
    .from(crypto.createHmac('sha256', API_SECRET)
        .update(String(API_KEY) + '|' + String(store_id) + '|' + String(user_id) + '|' + username + '|' + user_email)
        .digest())
    .toString('base64');

const body = qs.stringify({
    api_key:       API_KEY,
    hash:          hash,
    store_id:      store_id,
    user_id:       user_id,
    username:      username,
    user_email:    user_email,
    user_ip:       '1.2.3.4',
    user_fullname: 'John Doe',
    user_phone:    '+393331234567',
    lang:          'tr',        // page opening language (default: tr)
    currency:      'TRY',       // page opening currency (default: TRY)
});

const url = new URL('https://dodopin.com/checkout/token.php');

const req = https.request({
    hostname: url.hostname,
    path:     url.pathname,
    method:   'POST',
    headers:  {
        'Content-Type':   'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(body),
    },
}, (res) => {
    let data = '';
    res.on('data', c => data += c);
    res.on('end',  () => {
        const json = JSON.parse(data);
        if (json.success) console.log('Redirect →', json.redirect);
        else throw new Error(json.message);
    });
});

req.on('error', err => { throw err; });
req.write(body);
req.end();
import requests, hmac, hashlib, base64

API_KEY    = 'YOUR_API_KEY'
API_SECRET = 'YOUR_API_SECRET'
store_id   = 12345
user_id    = 678
username   = 'username'
user_email = '[email protected]'

hash_string = API_KEY + '|' + str(store_id) + '|' + str(user_id) + '|' + username + '|' + user_email
hash_val = base64.b64encode(
    hmac.new(API_SECRET.encode(), hash_string.encode(), hashlib.sha256).digest()
).decode()

resp = requests.post(
    'https://dodopin.com/checkout/token.php',
    data={
        'api_key':       API_KEY,
        'hash':          hash_val,
        'store_id':      store_id,
        'user_id':       user_id,
        'username':      username,
        'user_email':    user_email,
        'user_ip':       '1.2.3.4',
        'user_fullname': 'John Doe',
        'user_phone':    '+201001234567',
        'lang':          'tr',        # page opening language (default: tr)
        'currency':      'TRY',       # page opening currency (default: TRY)
    },
    timeout=30,
)
resp.raise_for_status()
data = resp.json()

if data.get('success'):
    redirect_url = data['redirect']   # redirect user here
else:
    raise RuntimeError(data.get('message'))
# Compute hash first (shell)
HASH=$(printf '%s' 'YOUR_API_KEY|12345|678|username|[email protected]' \
  | openssl dgst -sha256 -hmac 'YOUR_API_SECRET' -binary | base64)

curl -X POST 'https://dodopin.com/checkout/token.php' \
  -d 'api_key=YOUR_API_KEY' \
  --data-urlencode "hash=$HASH" \
  -d 'store_id=12345' \
  -d 'user_id=678' \
  -d 'username=username' \
  -d '[email protected]' \
  -d 'user_ip=1.2.3.4' \
  -d 'user_fullname=John+Doe' \
  -d 'user_phone=%2B13125550100' \
  -d 'lang=tr' \
  -d 'currency=TRY'

Yanıt

200 — Başarılı
{
  "success": true,
  "token": "eyJzIjoxMjM0NX0...",
  "redirect": "https://dodopin.com/tr/store/your-store-slug?token=eyJzIjoxMjM0NX0...&cur=TRY"
}
200 — Hata
{
  "success": false,
  "message": "Invalid API credentials or store is not active."
}

İstek parametreleri

Alan Tip Açıklama Doğrulama Zorunlu
api_key String Mağaza API anahtarınız. Mağazalarım → Mağaza Düzenle → Mağazanın API Bilgileri bölümünde. Boş olamaz zorunlu
hash String HMAC-SHA256 istek imzası. Formül için Kimlik doğrulama bölümüne bakın. api_secret imza anahtarıdır — istek gövdesinde göndermeyin. base64_encode(HMAC-SHA256(api_key|store_id|user_id|username|user_email, api_secret)) — alanlar pipe | ile birleştirilir zorunlu
store_id Integer Mağazanızın sayısal ID'si. Mağazalarım → Mağaza Düzenle → Mağazanın API Bilgileri bölümünde. Pozitif tam sayı zorunlu
user_id String / Int Müşterinin sizin sisteminizdeki benzersiz kimliği. Webhook'ta alıcıyı bu değerle eşlersiniz. En fazla 64 karakter. Kontrol karakterleri silinir. zorunlu
username String Müşterinin kullanıcı adı. Ödeme sayfasında gösterilir. Yalnızca harf, rakam, _ ve -. En fazla 64 karakter. Boşluk ve özel karakter kabul edilmez. zorunlu
user_email String Müşterinin e-posta adresi. Faturada kullanılır. Geçerli e-posta adresi (RFC 5321). zorunlu
user_ip String Müşterinin IP adresi. Kendi sunucu IP'nizi değil, gerçek istemci IP'sini gönderin. Geçerli IPv4 veya IPv6 adresi. zorunlu
user_fullname String Müşterinin adı soyadı. Faturada kullanılır. En fazla 128 karakter. Kontrol karakterleri silinir. zorunlu
user_phone String Ülke kodu dahil telefon numarası. Yalnızca rakam ve +. En fazla 20 karakter. En az bir rakam içermeli
(ör. +905551234567, +393331234567).
zorunlu
lang String Sayfanın açılacağı dil. Varsayılan: tr tr, en, de, fr, es, it, pt, nl, pl, ro, cs isteğe bağlı
currency String Sayfanın açılacağı para birimi. Varsayılan: TRY AED, AMD, ARS, AUD, AZN, BGN, BHD, BRL, CAD, CHF, CLP, CNY, COP, CZK, DKK, EGP, EUR, GBP, GEL, GHS, HKD, HUF, IDR, ILS, INR, ISK, JOD, JPY, KES, KRW, KWD, KZT, MAD, MXN, MYR, NGN, NOK, NZD, OMR, PEN, PHP, PKR, PLN, QAR, RON, RUB, SAR, SEK, SGD, THB, TND, TRY, UAH, USD, UYU, VND, ZAR, HRK isteğe bağlı

Webhook (IPN) API v2

Her başarılı ödemeden sonra platform, kayıtlı Webhook URL adresinize sipariş, müşteri ve ödeme bilgilerini imzalı HTTP POST ile gönderir. Endpoint'iniz hash imzasını doğrulamalı, status = success ise siparişi işlemeli ve HTTP 200 + gövde OK dönmelidir. Farklı bir yanıt otomatik yeniden denemeyi başlatır.

API v2. Webhook tek username alanı gönderir (user_name yok) ve api_version değeri 2 olur. İmza formülü aynıdır. Mağazalarım → Mağaza Düzenle ekranından v2 seçin. Mevcut mağazalar siz değiştirene kadar v1'de kalır.
Webhook URL'yi Mağazalarım → Mağaza Düzenle ekranından kaydedin. Adres dışarıdan erişilebilir olmalı, HTTPS kullanmalı ve 15 saniye içinde yanıt vermelidir. Webhook yalnızca başarılı ödemelerde gider. Teslim başarısız olursa sistem cron ile dakikada bir, en fazla 100 kez tekrar dener.

Örnek POST gövdesi

POST Kayıtlı Webhook URL Content-Type: application/x-www-form-urlencoded
{
    "order_ref": "O14092026ABCDEF1",
    "merchant_id": "12345",
    "status": "success",
    "gateway_name": "payop",
    "product_id": "42",
    "product_name": "13000 Diamonds",
    "quantity": "1",
    "product_topup_amount": "13000.00",
    "total_topup_amount": "13000.00",
    "product_currency": "EUR",
    "unit_price": "50.00",
    "total_price": "50.00",
    "net_merchant_earning": "47.50",
    "user_fullname": "John Doe",
    "invoice_mail": "[email protected]",
    "user_id": "678",
    "user_phone": "+13125550100",
    "username": "username",
    "api_version": "2",
    "hash": "oIJlok5zdmQtIK19QKqEL9uGo/xQGI/ArQNq1lwVg=="
}

Canlı istek düz form POST'tur (application/x-www-form-urlencoded), JSON gövde değil. Aşağıdaki nesne aynı alanların JSON görünümüdür — sipariş ekranındaki giden webhook paneliyle aynıdır. api_key gövdede gelmez; hash doğrularken kendi kayıtlı anahtarınızı kullanın.

Örnek handler

<?php
$API_KEY    = 'YOUR_API_KEY';
$API_SECRET = 'YOUR_API_SECRET';

// Payload is a flat POST body — all fields at root level
$merchant_id  = $_POST['merchant_id']  ?? '';
$order_ref    = $_POST['order_ref']    ?? '';
$user_fullname = $_POST['user_fullname'] ?? '';
$invoice_mail = $_POST['invoice_mail'] ?? '';
$gateway_name = $_POST['gateway_name'] ?? '';
$status       = $_POST['status']       ?? '';
$received_hash = $_POST['hash']        ?? '';

$expected = base64_encode(hash_hmac('sha256',
    $merchant_id . $order_ref . $user_fullname .
    $invoice_mail . $gateway_name . $status . $API_KEY,
    $API_SECRET, true
));

if (!hash_equals($expected, $received_hash)) {
    http_response_code(403);
    die('invalid_hash');
}

if ($status === 'success') {
    $topup = (float) ($_POST['total_topup_amount'] ?? '0');
    $user  = $_POST['username'] ?? '';
    // Credit $topup to the player's in-game balance
    // e.g. addBalance($user, $topup);
}

header('Content-Type: text/plain');
echo 'OK';
?>
const express = require('express');
const crypto  = require('crypto');

const app = express();
app.use(express.urlencoded({ extended: true }));

app.post('/webhook', (req, res) => {
    // Payload is a flat POST body — all fields at root level
    const {
        merchant_id  = '',
        order_ref    = '',
        user_fullname = '',
        invoice_mail = '',
        gateway_name = '',
        status       = '',
        hash         = '',
    } = req.body;

    const expected = Buffer.from(crypto
        .createHmac('sha256', process.env.API_SECRET)
        .update(merchant_id + order_ref + user_fullname + invoice_mail + gateway_name + status + process.env.API_KEY)
        .digest()
    ).toString('base64');

    const hBuf = Buffer.from(hash);
    const eBuf = Buffer.from(expected);
    if (hBuf.length !== eBuf.length || !crypto.timingSafeEqual(hBuf, eBuf))
        return res.status(403).send('invalid_hash');

    if (status === 'success') {
        const topup = parseFloat(req.body.total_topup_amount || '0');
        const user  = req.body.username || '';
        // Credit topup to the player's in-game balance
        // e.g. addBalance(user, topup);
    }

    res.type('text').send('OK');
});

app.listen(3000);
import hmac, hashlib, base64
from flask import Flask, request, abort

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    # Payload is a flat POST body — all fields at root level
    f = lambda k: request.form.get(k, '')
    hash_string = (
        f('merchant_id')  +
        f('order_ref')    +
        f('user_fullname') +
        f('invoice_mail') +
        f('gateway_name') +
        f('status')       +
        API_KEY
    )
    expected = base64.b64encode(
        hmac.new(API_SECRET.encode(), hash_string.encode(), hashlib.sha256).digest()
    ).decode()

    if not hmac.compare_digest(f('hash'), expected):
        abort(403)

    if f('status') == 'success':
        topup = float(f('total_topup_amount') or '0')
        user  = f('username')
        # Credit topup to the player's in-game balance
        # e.g. add_balance(user, topup)

    return 'OK', 200, {'Content-Type': 'text/plain'}

Gelen alanlar

AlanTipAçıklamaHash'te
merchant_idStringMağaza / satıcı ID'nizevet — 1. sıra
order_refStringDodopin tarafından üretilen benzersiz sipariş numarasıevet — 2. sıra
user_fullnameStringMüşteri adı soyadıevet — 3. sıra
invoice_mailStringMüşteri e-posta adresievet — 4. sıra
gateway_nameStringÖdeme yöntemi (ör. stripe, tazapay, lidio)evet — 5. sıra
statusStringOtomatik webhook'larda her zaman successevet — 6. sıra
api_keyStringMağaza API anahtarınız — POST gövdesinde gelmez, hash metninin 7. alanıdır. İmzayı doğrularken kendi kayıtlı api_key değerinizi kullanın.evet — 7. sıra
user_phoneStringÜlke kodu dahil telefonhayır
product_idIntegerÜrün ID'si — siparişte yoksa gönderilmezhayır
product_nameStringÖdeme anındaki ürün adı (product_name_snapshot) — boşsa gönderilmezhayır
quantityStringAdet (en az 1)hayır
product_topup_amountStringÜrün başına yükleme / kredi tutarı (ör. 1.00)hayır
total_topup_amountStringKullanıcıya eklenecek toplam kredi. quantity × product_topup_amount (ör. 250.00). Oyunda veya platformunuzda bakiyeyi bu değerle artırın.hayır
product_currencyStringÜrün para birimi (ör. TRY, USD, EUR)hayır
unit_priceStringBirim fiyat (ör. 49.90)hayır
total_priceStringSipariş tutarı: quantity × unit_price (ör. 149.70)hayır
net_merchant_earningStringKomisyon düşüldükten sonra net kazancınız (ör. 127.25)hayır
user_idStringOturum isteğindeki müşteri kimliği — yoksa gönderilmezhayır
usernameStringVarsa oyun / hesap kullanıcı adı (oturum isteğindeki username ile aynı)hayır
api_versionStringv2 teslimlerinde her zaman 2. Hash'te yer almaz.hayır
hashStringHMAC-SHA256 base64 imza — işlemeden önce her zaman doğrulayın—
Bakiyeyi yükleme. Webhook imzasını doğruladıktan sonra total_topup_amount alanına bakın. Bu tutarı username (veya oturum isteğindeki user_id) ile eşleşen kullanıcıya ekleyin. Müşterinin satın aldığı kesin miktar budur.

Hata kodları

Token endpoint her zaman HTTP 200 döner. Sonucu success alanından okuyun. Hata yanıtında ayrıca bir message metni vardır.

Webhook imzası tutmuyorsa isteği HTTP 403 ve gövde invalid_hash ile reddedin — bu bildirimi işlemeyin.

Hata listesi

DurumHTTPsuccessmessageSık görülen neden
Zorunlu bir alan boş veya eksik 200 false Missing required field: <field_name>. Alan POST gövdesine eklenmemiş
username geçersiz karakter içeriyor 200 false Invalid value for username. Kullanıcı adında boşluk, Türkçe karakter veya sembol var. Yalnızca a-z A-Z 0-9 _ - kabul edilir.
user_email geçerli bir adres değil 200 false Invalid value for user_email. E-posta formatı bozuk
user_ip geçerli bir IP değil 200 false Invalid value for user_ip. İstemci IP yerine sunucu IP'si gönderilmiş veya format hatalı
Yanlış api_key / store_id çifti veya mağaza aktif değil 200 false Invalid API credentials or store is not active. api_key store_id ile eşleşmiyor veya mağaza durumu approved_admin değil
Hash imzası tutmuyor 200 false Invalid hash. Please recalculate… Alan sırası yanlış, api_secret yanlış veya pipe eksik. Alanlar tam sırada | ile birleştirilmelidir.
Webhook imzası tutmuyor 403 — Düz metin: invalid_hash Endpoint imzayı reddetti — bu bildirimi işlemeyin

Rate limit

Token endpoint üç ayrı rate limit uygular. Oturum ancak üçünü de geçerseniz oluşur. Herhangi biri aşılırsa HTTP 429 ve ne kadar bekleyeceğinizi söyleyen Retry-After başlığı döner.

Limit katmanları

Katman Kapsam Varsayılan limit Süre HTTP
Hızlı IP limiti İstemci IP başına 60 istek 60 saniye 429
Token IP limiti İstemci IP başına 30 istek 1 dakika 429
Token API Key limiti api_key başına 60 istek 1 dakika 429
Tablodaki değerler varsayılandır; platform yöneticisi mağaza bazında değiştirebilir. Backend'inizden giden istekler istemci IP katmanına yazılır — çıkış IP'nizin sabit olduğundan ve birçok servisle paylaşılmadığından emin olun.

Öneriler

  • 1 Oturumu ihtiyaç anında oluşturun — token'ı yalnızca kullanıcı ödeme yapmak üzereyken üretin. Toplu ön üretim yapmayın.
  • 2 Retry-After değerine uyun — başlığı okuyun ve tam o kadar saniye bekleyin. Hemen tekrar denemek hem reddedilir hem kotanızı yer.
  • 3 Token'ı saklayıp tekrar kullanmayın — token tek kullanımlıktır ve 2 saat sonra dolar. Kullanıcılar arasında paylaşılmaz.
  • 4 Üstel bekleme kullanın — 429 alırsanız rastgele gecikmeli (jitter) exponential backoff uygulayın. Hep birlikte aynı anda denemeyin.