PHP WebShell
Текущая директория: /var/www/bitcardoApp/models/fiat
Просмотр файла: verify_paystack.php
<?php
// fiat/verify_paystack.php
session_start();
header('Content-Type: text/html; charset=UTF-8');
require_once '../../config/db_config.php'; // provides $conn (mysqli)
// If your Paystack secret is stored in DB/settings, fetch it accordingly:
if (!isset($paystackSecret)) {
// Example: from settings table (adjust to your storage)
$stmt = $conn->prepare("SELECT setting_value FROM site_settings WHERE `setting_key`='paystack_secret' LIMIT 1");
if ($stmt) {
$stmt->execute();
$stmt->bind_result($paystackSecretVal);
if ($stmt->fetch()) $paystackSecret = $paystackSecretVal;
$stmt->close();
}
}
$paystackSecret = $paystackSecret ?? ''; // ensure string
// helper: site_settings
function get_site_setting(mysqli $conn, string $setting_key, $default = null) {
$stmt = $conn->prepare("SELECT setting_value FROM site_settings WHERE `setting_key` = ? LIMIT 1");
if ($stmt) {
$stmt->bind_param('s', $setting_key);
$stmt->execute();
$stmt->bind_result($val);
if ($stmt->fetch()) { $stmt->close(); return $val; }
$stmt->close();
}
return $default;
}
// 1) Read setting: how fees are handled
$add_naira_deposit_fee = (int) get_site_setting($conn, 'add_naira_deposit_fee', 1); // 1=add (wallet gets full typed amount), 0=deduct
// 2) Read inputs
$reference = isset($_GET['reference']) ? trim($_GET['reference']) : '';
$wallet_id = isset($_GET['wallet_id']) ? trim($_GET['wallet_id']) : '';
$user_amount_hint = isset($_GET['amount']) ? (float)$_GET['amount'] : null; // amount typed in UI
$mode_hint = isset($_GET['mode']) ? (int)$_GET['mode'] : null; // 1 or 0; just a hint
$_SESSION['deposit'] = [
'ok' => false,
'message' => '',
'reference' => $reference,
'wallet_id' => $wallet_id,
'amount' => 0,
'currency' => 'NGN',
'new_balance' => null,
];
if ($reference === '' || $wallet_id === '') {
$_SESSION['deposit']['message'] = 'Missing reference or wallet_id.';
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
// 3) Fetch wallet (we’ll need coin, address, current balance, user_id)
$sql_wallet = "SELECT wallet_id, user_id, wallet_add, bank_name, coin, balance, wallet_status
FROM user_wallets WHERE wallet_id = ? LIMIT 1";
$stmt = mysqli_prepare($conn, $sql_wallet);
mysqli_stmt_bind_param($stmt, 's', $wallet_id);
mysqli_stmt_execute($stmt);
$res_wallet = mysqli_stmt_get_result($stmt);
$wallet = mysqli_fetch_assoc($res_wallet);
mysqli_stmt_close($stmt);
if (!$wallet) {
$_SESSION['deposit']['message'] = 'Wallet not found.';
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
if (strtoupper($wallet['wallet_status'] ?? '') === 'INACTIVE') {
$_SESSION['deposit']['message'] = 'Wallet is inactive.';
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
$user_id_for_tx = (int)($wallet['user_id'] ?? 0);
// 4) Verify with Paystack
$verify_url = "https://api.paystack.co/transaction/verify/" . urlencode($reference);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $verify_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$paystackSecret}",
"Accept: application/json"
]);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$raw = curl_exec($ch);
$curl_err = curl_error($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($curl_err) {
$_SESSION['deposit']['message'] = 'Network error: ' . $curl_err;
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
if ($http_code < 200 || $http_code >= 300) {
$_SESSION['deposit']['message'] = 'Paystack verify HTTP ' . $http_code;
$_SESSION['deposit']['raw'] = $raw;
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
$resp = json_decode($raw, true);
if (!is_array($resp) || empty($resp['status'])) {
$_SESSION['deposit']['message'] = 'Malformed Paystack response.';
$_SESSION['deposit']['raw'] = $raw;
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
if (!$resp['status']) {
$_SESSION['deposit']['message'] = isset($resp['message']) ? $resp['message'] : 'Verification failed.';
$_SESSION['deposit']['raw'] = $raw;
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
$data = $resp['data'] ?? null;
if (!$data) {
$_SESSION['deposit']['message'] = 'No transaction data from Paystack.';
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
$ps_status = $data['status'] ?? '';
$ps_currency = strtoupper($data['currency'] ?? 'NGN');
$ps_amount = isset($data['amount']) ? (float)$data['amount'] / 100.0 : 0.0; // kobo -> NGN
$ps_ref = $data['reference'] ?? $reference;
$ps_id = isset($data['id']) ? (string)$data['id'] : null;
$ps_cust_eml = $data['customer']['email'] ?? 'Paystack';
$ps_fees = isset($data['fees']) ? (float)$data['fees'] / 100.0 : null; // sometimes present (in kobo)
if ($ps_status !== 'success') {
$_SESSION['deposit']['message'] = 'Transaction not successful: ' . $ps_status;
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
if ($ps_currency !== 'NGN') {
$_SESSION['deposit']['message'] = 'Unsupported currency: ' . $ps_currency;
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
if ($ps_amount <= 0) {
$_SESSION['deposit']['message'] = 'Invalid amount from Paystack.';
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
// 5) Fee determination
// Fallback estimator if Paystack didn't return "fees"
function estimate_paystack_fee(float $amountNgn): float {
$pct = 0.015;
$extra = $amountNgn > 2500 ? 100.0 : 0.0;
$cap = 2000.0;
$fee = ($amountNgn * $pct) + $extra;
if ($fee > $cap) $fee = $cap;
// NOTE: This excludes VAT; if you need to include VAT, adjust here and in front-end preview.
return round($fee, 2);
}
$feeNgn = ($ps_fees !== null && $ps_fees > 0) ? $ps_fees : estimate_paystack_fee($ps_amount);
// 6) Compute credit amount based on admin setting (and optional mode hint)
$mode = ($mode_hint === 0 || $mode_hint === 1) ? $mode_hint : $add_naira_deposit_fee;
if ($mode === 1) {
// ADD-ON-TOP: user intended wallet_credit == user_amount_hint
// We charged ~amount + fee. Credit the intended amount if sensible; otherwise fallback to net.
$intended = ($user_amount_hint && $user_amount_hint > 0) ? (float)$user_amount_hint : null;
$netFromPs = max(0.0, $ps_amount - $feeNgn);
if ($intended !== null && $ps_amount >= $intended) {
// If Paystack actually charged enough, credit exactly the intended amount
$credit_amount = $intended;
} else {
// safety fallback
$credit_amount = $netFromPs;
}
} else {
// DEDUCT: wallet receives ps_amount - fee
$credit_amount = max(0.0, $ps_amount - $feeNgn);
}
// 7) Idempotency check by txid (reference)
$sql_check = "SELECT trans_id, applied FROM transactions WHERE txid = ? LIMIT 1";
$stmt = mysqli_prepare($conn, $sql_check);
mysqli_stmt_bind_param($stmt, 's', $ps_ref);
mysqli_stmt_execute($stmt);
$res_check = mysqli_stmt_get_result($stmt);
$existing_tx = mysqli_fetch_assoc($res_check);
mysqli_stmt_close($stmt);
// 8) Apply atomically
mysqli_begin_transaction($conn);
$applied_now = false;
$now = date('Y-m-d H:i:s');
if ($existing_tx && intval($existing_tx['applied']) === 1) {
$applied_now = false;
} else {
// Lock wallet row to avoid race
$sql_lock = "SELECT balance, coin, wallet_add FROM user_wallets WHERE wallet_id = ? FOR UPDATE";
$stmt = mysqli_prepare($conn, $sql_lock);
mysqli_stmt_bind_param($stmt, 's', $wallet_id);
mysqli_stmt_execute($stmt);
$res_lock = mysqli_stmt_get_result($stmt);
$lock_row = mysqli_fetch_assoc($res_lock);
mysqli_stmt_close($stmt);
if (!$lock_row) {
mysqli_rollback($conn);
$_SESSION['deposit']['message'] = 'Wallet lock failed.';
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
$current_balance = (float)$lock_row['balance'];
$new_balance = $current_balance + $credit_amount;
if ($existing_tx) {
// Update existing tx
$sql_upd = "UPDATE transactions
SET coin=?, user_id=?, wallet_id=?, sender_address=?, receiver_address=?, amount=?,
type='deposit', confirmation=3, status='success', applied=1,
note=?, updated_at=?
WHERE txid=? LIMIT 1";
$stmt = mysqli_prepare($conn, $sql_upd);
$sender = $ps_cust_eml;
$recv = $lock_row['wallet_add'] ?? '';
$noteArr = [
'ps_amount' => $ps_amount,
'ps_fee' => $feeNgn,
'mode' => $mode === 1 ? 'add' : 'deduct',
];
$noteJson = json_encode($noteArr, JSON_UNESCAPED_SLASHES);
mysqli_stmt_bind_param($stmt, 'sisssdsss',
$lock_row['coin'], $user_id_for_tx, $wallet_id, $sender, $recv, $credit_amount, $noteJson, $now, $ps_ref
);
if (!mysqli_stmt_execute($stmt)) {
mysqli_rollback($conn);
$_SESSION['deposit']['message'] = 'Failed to update transaction.';
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
mysqli_stmt_close($stmt);
} else {
// Insert fresh tx
$sql_ins = "INSERT INTO transactions
(coin, user_id, wallet_id, sender_address, receiver_address, amount, type, txid, confirmation, status, applied, note, updated_at, created_at)
VALUES (?, ?, ?, ?, ?, ?, 'deposit', ?, 3, 'success', 1, ?, ?, ?)";
$stmt = mysqli_prepare($conn, $sql_ins);
$sender = $ps_cust_eml;
$recv = $lock_row['wallet_add'] ?? '';
$noteArr = [
'ps_amount' => $ps_amount,
'ps_fee' => $feeNgn,
'mode' => $mode === 1 ? 'add' : 'deduct',
];
$noteJson = json_encode($noteArr, JSON_UNESCAPED_SLASHES);
mysqli_stmt_bind_param($stmt, 'sisssdssss',
$lock_row['coin'], $user_id_for_tx, $wallet_id, $sender, $recv, $credit_amount, $ps_ref, $noteJson, $now, $now
);
if (!mysqli_stmt_execute($stmt)) {
mysqli_rollback($conn);
$_SESSION['deposit']['message'] = 'Failed to insert transaction.';
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
mysqli_stmt_close($stmt);
}
// Credit wallet
$sql_wu = "UPDATE user_wallets SET balance = ?, updated_at = ? WHERE wallet_id = ? LIMIT 1";
$stmt = mysqli_prepare($conn, $sql_wu);
mysqli_stmt_bind_param($stmt, 'dss', $new_balance, $now, $wallet_id);
if (!mysqli_stmt_execute($stmt)) {
mysqli_rollback($conn);
$_SESSION['deposit']['message'] = 'Failed to update wallet balance.';
header('Location: ../../user/fiat/deposit_success.php'); exit;
}
mysqli_stmt_close($stmt);
mysqli_commit($conn);
$applied_now = true;
$_SESSION['deposit']['new_balance'] = $new_balance;
}
// 9) Fill session & redirect to confirmation
$_SESSION['deposit']['ok'] = true;
$_SESSION['deposit']['message'] = 'Deposit verified' . ($applied_now ? ' and applied.' : ' (already applied).');
$_SESSION['deposit']['reference'] = $ps_ref;
$_SESSION['deposit']['amount'] = $credit_amount; // Show credited amount to user
$_SESSION['deposit']['currency'] = $ps_currency;
header('Location: ../../user/fiat/deposit_success.php');
exit;
Выполнить команду
Для локальной разработки. Не используйте в интернете!