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;
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!