Görünüm
Bildirim (callback)
Bir işlem sonuçlandığında TLHavale, sitenizin bildirim adresine imzalı bir POST isteği gönderir. Bakiye değişikliklerini yalnızca bu bildirime göre yapın.
Adresler
Yatırım ve çekim için ayrı birer HTTPS adresi tanımlanır. Adresleri hesap yöneticinize iletin; sitenizin ayarlarına o girer.
İstek
http
POST /tlhavale/bildirim HTTP/1.1
Host: siteniz.com
Content-Type: application/json
{
"transactionId": "dep-2026-000184",
"status": "CONFIRMED",
"amount": 1500,
"hash": "5f1c0e9b7d3a6c1e8f4b2a9d0c7e6f5a4b3c2d1e",
"type": "deposit"
}| Alan | Açıklama |
|---|---|
transactionId | Talebi oluştururken verdiğiniz numara |
status | İşlemin son durumu (bkz. İşlem durumları) |
amount | İşlemin son tutarı (TL). Yatırımda talepteki tutardan farklı olabilir. |
type | deposit veya withdraw |
hash | İmza |
İmzayı doğrulama
hash = sha1( secret + "transactionId=" + transactionId + "&amount=" + amount + "&status=" + status )secret sizin secKey'inizdir. Hesap yöneticiniz sitenize ayrı bir bildirim anahtarı tanımladıysa o kullanılır. amount metne gelen JSON'daki sayı biçimiyle girer (1500, 1500.00 değil).
İmza tutmazsa isteği işlemeyin ve 401 dönün.
js
const crypto = require("crypto");
function verify(body, secret) {
const text = `transactionId=${body.transactionId}&amount=${body.amount}&status=${body.status}`;
const expected = crypto.createHash("sha1").update(secret + text).digest("hex");
const given = String(body.hash || "");
return given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}php
function verify(array $body, string $secret): bool {
$text = "transactionId={$body['transactionId']}&amount={$body['amount']}&status={$body['status']}";
return hash_equals(sha1($secret . $text), (string) ($body['hash'] ?? ''));
}
// Gövdeyi okurken: $body = json_decode(file_get_contents('php://input'), true);python
import hashlib, hmac
def verify(body: dict, secret: str) -> bool:
text = f"transactionId={body['transactionId']}&amount={body['amount']}&status={body['status']}"
expected = hashlib.sha1((secret + text).encode("utf-8")).hexdigest()
return hmac.compare_digest(expected, str(body.get("hash", "")))Python'da tutar biçimi
json.loads tam sayıları int olarak okur, bu doğru. Gövdeyi float'a çeviren bir katman kullanıyorsanız 1500.0 metne girer ve imza tutmaz.
Yanıt ve yeniden deneme
- İsteği işledikten sonra HTTP 2xx dönün. Gövde önemli değildir.
- 15 saniye içinde yanıt gelmezse ya da 2xx dışında bir kod dönerseniz bildirim 1 dakika ve 5 dakika sonra yeniden gönderilir (toplam 3 deneme).
- Üç deneme de başarısız olursa ekibimiz bildirimi elle yeniden gönderir.
Aynı bildirimi birden çok kez işlemeyin
Bildirim yeniden denemeler ve elle gönderimler yüzünden aynı işlem için birden fazla gelebilir. Ayrıca ekip bir kararı düzeltirse aynı işlem için önce REJECTED, sonra CONFIRMED gelebilir.
Önerilen işleyiş:
- İmzayı doğrulayın.
transactionIdile kendi kaydınızı bulun ve kilitleyin.- Kayıt zaten
CONFIRMEDolarak işlendiyse hiçbir şey yapmadan200dönün. - Değilse durumu güncelleyin;
CONFIRMEDise bakiyeyiamountkadar değiştirin. 200dönün.
js
// Express örneği
app.post("/tlhavale/bildirim", express.json(), async (req, res) => {
if (!verify(req.body, process.env.TLHAVALE_SECRET)) return res.sendStatus(401);
await db.transaction(async (tx) => {
const order = await tx.deposits.lockByTransactionId(req.body.transactionId);
if (!order || order.status === "CONFIRMED") return; // bilinmiyor ya da zaten işlendi
order.status = req.body.status;
if (req.body.status === "CONFIRMED") {
await tx.wallets.credit(order.userId, req.body.amount);
}
await tx.deposits.save(order);
});
res.sendStatus(200);
});