PHP WebShell
Текущая директория: /var/www/bitcardoApp/models/fiat
Просмотр файла: paystack_webhook.php
<?php
require_once '../../config/db_config.php';
// Verify signature
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] ?? '';
$calc = hash_hmac('sha512', $raw, $paystackSecret);
if (!$sig || !hash_equals($calc, $sig)) {
http_response_code(401);
echo 'Invalid signature';
exit;
}
$evt = json_decode($raw, true);
if (!$evt || empty($evt['event'])) {
http_response_code(400);
echo 'Bad payload';
exit;
}
$etype = $evt['event']; // transfer.success | transfer.failed
$data = $evt['data'] ?? [];
$reference = $data['reference'] ?? '';
$status = $data['status'] ?? ''; // success | failed
$reason = $data['reason'] ?? '';
if (!$reference) {
http_response_code(200);
echo 'No reference';
exit;
}
try {
$conn->begin_transaction();
// Lock transfer
$selT = $conn->prepare("SELECT transfer_id, user_id, wallet_id, amount, fee, status FROM transfers WHERE reference = ? FOR UPDATE");
$selT->bind_param("s", $reference);
$selT->execute();
$trs = $selT->get_result();
if ($trs->num_rows === 0) {
$conn->commit();
http_response_code(200);
echo 'No transfer';
exit;
}
$tr = $trs->fetch_assoc();
$transferId = (int)$tr['transfer_id'];
$userId = (int)$tr['user_id'];
$walletId = (int)$tr['wallet_id'];
$curStatus = $tr['status'];
if ($curStatus !== 'pending') {
$conn->commit();
http_response_code(200);
echo 'Already processed';
exit;
}
$newStatus = (($etype === 'transfer.success') || ($status === 'success')) ? 'success'
: (($etype === 'transfer.failed') || ($status === 'failed')) ? 'failed'
: 'pending';
if ($newStatus === 'pending') {
$conn->commit();
http_response_code(200);
echo 'Ignored';
exit;
}
// Update transfer
$rsn = substr($reason ?? '', 0, 255);
$updT = $conn->prepare("UPDATE transfers SET status = ?, reason = ?, updated_at = NOW() WHERE transfer_id = ?");
// s s i
$updT->bind_param("ssi", $newStatus, $rsn, $transferId);
$updT->execute();
// Lock related transactions
$selX = $conn->prepare("
SELECT trans_id, amount, type, applied, status
FROM transactions
WHERE reference = ?
FOR UPDATE
");
$selX->bind_param("s", $reference);
$selX->execute();
$xrs = $selX->get_result();
$reservedTotal = 0.0;
while ($r = $xrs->fetch_assoc()) {
if ((int)$r['applied'] === 1 && in_array($r['type'], ['withdrawal','fee']) && in_array($r['status'], ['pending','success'])) {
$reservedTotal += (float)$r['amount'];
}
}
// Update all pending rows to new status
$updX = $conn->prepare("UPDATE transactions SET status = ?, updated_at = NOW() WHERE reference = ? AND status = 'pending'");
$updX->bind_param("ss", $newStatus, $reference);
$updX->execute();
if ($newStatus === 'failed' && $reservedTotal > 0) {
// Refund wallet
$selW = $conn->prepare("SELECT balance FROM user_wallets WHERE wallet_id = ? FOR UPDATE");
$selW->bind_param("i", $walletId);
$selW->execute();
$wrs = $selW->get_result();
if ($wrs->num_rows > 0) {
$w = $wrs->fetch_assoc();
$newBal = (float)$w['balance'] + $reservedTotal;
$updW = $conn->prepare("UPDATE user_wallets SET balance = ? WHERE wallet_id = ?");
$updW->bind_param("di", $newBal, $walletId);
$updW->execute();
}
// Reversal credit (single consolidated)
$note = 'Reversal: Paystack transfer failed' . ($rsn ? " ({$rsn})" : '');
$insR = $conn->prepare("
INSERT INTO transactions
(coin, user_id, wallet_id, transfer_id, amount, type, txid, reference, provider, status, applied, note, updated_at, created_at)
VALUES ('NGN', ?, ?, ?, ?, 'reversal', '', ?, 'paystack', 'success', 1, ?, NOW(), NOW())
");
// user_id i, wallet_id i, transfer_id i, amount d, reference s, note s
$insR->bind_param("iiidss", $userId, $walletId, $transferId, $reservedTotal, $reference, $note);
$insR->execute();
}
$conn->commit();
http_response_code(200);
echo 'OK';
exit;
} catch (Throwable $e) {
if ($conn->errno === 0) $conn->rollback();
http_response_code(200); // avoid endless retries
echo 'ERR';
exit;
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!