PHP WebShell

Текущая директория: /var/www/bitcardoApp/models/crypto

Просмотр файла: create_btc_user_address.php

<?php
/**
 * File: create_btc_user_address.php
 * Purpose: Create or rotate a user's BTC address via BitGo Express.
 *
 * Depends on (must be included FIRST):
 *   create_user_address_helper.php
 *     - starts session (if needed)
 *     - flash_success(string), flash_error(string)
 *     - ensure_user_wallets_has_wallet_add_column(mysqli $conn)
 *     - bitgo_request(string $method, string $path, array $payload=null): array
 *     - pick_bitgo_wallet_id_for_coin(string $coin): ?string
 *       (and/or pick_bitgo_btc_wallet_id(): ?string)
 *
 * Triggers:
 *   - POST/GET create_btc_address=1  -> create if none exists (else reuse current Active)
 *   - POST/GET rotate_btc_address=1  -> always create NEW and archive existing Active
 */

try {
    if (!isset($conn) || !($conn instanceof mysqli)) {
        throw new RuntimeException('DB connection not available.');
    }
    foreach (['BITGO_API_BASE_URL','BITGO_ACCESS_TOKEN'] as $c) {
        if (!defined($c) || constant($c) === '') {
            throw new RuntimeException("Missing config constant: $c");
        }
    }

    // Determine action
    $create = (isset($_POST['create_btc_address']) && $_POST['create_btc_address'] == '1')
           || (isset($_GET['create_btc_address'])  && $_GET['create_btc_address']  == '1');
    $rotate = (isset($_POST['rotate_btc_address']) && $_POST['rotate_btc_address'] == '1')
           || (isset($_GET['rotate_btc_address'])  && $_GET['rotate_btc_address']  == '1');

    if (!$create && !$rotate) {
        return; // included but not triggered
    }

    if (session_status() !== PHP_SESSION_ACTIVE) {
        session_start();
    }
    if (empty($_SESSION['user_id'])) {
        throw new RuntimeException('You must be logged in.');
    }

    $userId = (int) $_SESSION['user_id'];
    $coin   = 'BTC';

    // Fetch user email for labeling the BitGo address
    $userEmail = null;
    $stmt = $conn->prepare("SELECT email FROM users WHERE user_id = ? LIMIT 1");
    if ($stmt) {
        $stmt->bind_param('i', $userId);
        $stmt->execute();
        $res = $stmt->get_result();
        if ($res && $row = $res->fetch_assoc()) {
            $userEmail = $row['email'] ?? null;
        }
        $stmt->close();
    }

    // Build a label for BitGo
    $bitgoLabel = $userEmail ? ("User: " . $userEmail) : ("UserID: " . $userId);


    // Ensure schema has wallet_add column (after user_id)
    ensure_user_wallets_has_wallet_add_column($conn);

    // Look up current Active BTC address (if any)
    $active = null;
    $stmt = $conn->prepare("
        SELECT cwallet_id, wallet_add
        FROM user_wallets
        WHERE user_id = ? AND coin = ? AND wallet_status = 'Active'
        LIMIT 1
    ");
    if (!$stmt) throw new RuntimeException('DB prepare failed: ' . $conn->error);
    $stmt->bind_param('is', $userId, $coin);
    $stmt->execute();
    $res = $stmt->get_result();
    if ($res && $res->num_rows) {
        $active = $res->fetch_assoc();
    }
    $stmt->close();

    // If only "create" (not rotate) and we already have an active address, reuse it
    if ($create && $active && !empty($active['wallet_add'])) {
        flash_success("BTC address already exists: " . htmlspecialchars($active['wallet_add']));
        header('Location: index.php');
        exit;
    }


    // Pick the BitGo BTC wallet id (only for API; NOT stored in DB)
    if (function_exists('pick_bitgo_btc_wallet_id')) {
        $bitgoWalletId = pick_bitgo_btc_wallet_id();
    } else {
        // Fallback to generic picker if helper exposes only that
        $bitgoWalletId = pick_bitgo_wallet_id_for_coin('btc');
    }
    if (!$bitgoWalletId) {
        throw new RuntimeException('No BTC wallet found in BitGo.');
    }

    // Create one new BTC receive address
    $addrObj = bitgo_request(
        'POST',
        'btc/wallet/' . rawurlencode($bitgoWalletId) . '/address',
        ['label' => $bitgoLabel]
    );    
    
    $address = $addrObj['address'] ?? null;
    if (!$address) {
        throw new RuntimeException('BitGo did not return a BTC address.');
    }

    // Archive old active (if any) when rotating (or if you prefer, always archive any stray active)
    $bankName = 'bitcardo';
    $icon     = 'btc.png';
    $balance  = '0.00000000';
    $type     = 'crypto';
    $label    = 'BTC Wallet';

    $conn->begin_transaction();
    try {
        if (($rotate || $active) && !empty($active['cwallet_id'])) {
            $stmt = $conn->prepare("UPDATE user_wallets SET wallet_status = 'Archived' WHERE cwallet_id = ?");
            if (!$stmt) throw new RuntimeException('DB prepare failed: ' . $conn->error);
            $stmt->bind_param('i', $active['cwallet_id']);
            if (!$stmt->execute()) throw new RuntimeException('DB archive failed: ' . $stmt->error);
            $stmt->close();
        }

        // Insert new Active entry — NOTE: we do NOT write to your table's wallet_id PK
        $sql = "INSERT INTO user_wallets
                  (user_id, wallet_add, bank_name, coin, icon, balance, type, label, wallet_status)
                VALUES
                  (?,?,?,?,?,?,?,?,?)";
        $stmt = $conn->prepare($sql);
        if (!$stmt) throw new RuntimeException('DB prepare failed: ' . $conn->error);
        $status = 'Active';
        $stmt->bind_param('issssssss',
            $userId, $address, $bankName, $coin, $icon, $balance, $type, $label, $status
        );
        if (!$stmt->execute()) throw new RuntimeException('DB insert failed: ' . $stmt->error);
        $stmt->close();

        $conn->commit();
    } catch (Throwable $tx) {
        $conn->rollback();
        throw $tx;
    }

    $verb = ($rotate || $active) ? 'rotated' : 'created';
    flash_success("BTC address $verb: " . $address);


    // Redirect back to dashboard so the new wallet shows immediately (PRG pattern)
    header('Location: index.php');
    exit;

} catch (Throwable $e) {
    // Short message for UI
    flash_error($e->getMessage());
    // Also redirect so we don't stay on a POST response
    header('Location: index.php');
    exit;
}

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


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