<?php
declare(strict_types=1);

session_start();
error_reporting(E_ALL);
ini_set('display_errors','0');
ini_set('log_errors','1');

define('ROOT_DIR', realpath(__DIR__ . '/..') ?: dirname(__DIR__));
define('APP_DIR', ROOT_DIR . '/app');
define('SQL_FILE', ROOT_DIR . '/sql/schema.sql');
define('CONFIG_FILE', APP_DIR . '/config.php');
define('LOCK_FILE', APP_DIR . '/install.lock');
define('STORAGE_DIR', ROOT_DIR . '/storage');

require_once APP_DIR . '/helpers.php';
require_once APP_DIR . '/Db.php';
require_once APP_DIR . '/ChildDatabaseManager.php';

if (empty($_SESSION['csrf'])) $_SESSION['csrf'] = bin2hex(random_bytes(32));

function ih(?string $v): string { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); }
function postv(string $k, string $d=''): string { return (string)($_POST[$k] ?? $d); }
function install_digits(string $v): string { return preg_replace('/\D+/', '', $v) ?? ''; }
function install_guess_url(): string {
    $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
    $scheme = $https ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? 'domain.com';
    $dir = rtrim(str_replace('\\','/', dirname($_SERVER['SCRIPT_NAME'] ?? '/')), '/');
    if ($dir === '/' || $dir === '.') $dir = '';
    return $scheme . '://' . $host . $dir;
}
function install_requirements(): array {
    return [
        ['PHP 8.1+', version_compare(PHP_VERSION, '8.1.0', '>=')],
        ['PDO', extension_loaded('pdo')],
        ['pdo_mysql', extension_loaded('pdo_mysql')],
        ['mysqli', extension_loaded('mysqli')],
        ['curl', extension_loaded('curl')],
        ['openssl', extension_loaded('openssl')],
        ['mbstring', extension_loaded('mbstring')],
        ['فایل schema.sql', is_file(SQL_FILE)],
        ['پوشه app قابل نوشتن', is_writable(APP_DIR)],
        ['پوشه storage قابل نوشتن', is_writable(STORAGE_DIR)],
    ];
}
function install_validate_token(string $token): void {
    if (!preg_match('/^\d{6,12}:[A-Za-z0-9_\-]{30,}$/', $token)) {
        throw new RuntimeException('فرمت توکن ربات مادر اشتباه است. توکن را کامل از BotFather کپی کن.');
    }
}
function tg_call(string $token, string $method, array $params=[]): array {
    if (!function_exists('curl_init')) throw new RuntimeException('curl روی هاست فعال نیست.');
    $lastError = null;
    for ($attempt = 1; $attempt <= 5; $attempt++) {
        $ch = curl_init('https://api.telegram.org/bot' . $token . '/' . $method);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Expect:'],
            CURLOPT_POSTFIELDS => json_encode($params, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
            CURLOPT_CONNECTTIMEOUT => 15,
            CURLOPT_TIMEOUT => 45,
            CURLOPT_ENCODING => '',
            CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
        ]);
        $body = curl_exec($ch);
        $err = curl_error($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($body === false) {
            $lastError = 'اتصال به تلگرام انجام نشد: ' . ($err ?: 'curl error');
            if ($attempt < 5) { sleep(min(8, $attempt)); continue; }
            throw new RuntimeException($lastError);
        }
        $json = json_decode($body, true);
        if (!is_array($json)) {
            $lastError = 'پاسخ تلگرام JSON نبود. HTTP ' . $code;
            if ($attempt < 5 && ($code === 429 || $code >= 500 || $code === 0)) { sleep(min(8, $attempt)); continue; }
            throw new RuntimeException($lastError);
        }
        if (($json['ok'] ?? false) === true) {
            usleep(250000);
            return $json;
        }
        $desc = (string)($json['description'] ?? 'Telegram API Error');
        $errorCode = (int)($json['error_code'] ?? $code);
        $retryAfter = 0;
        if (isset($json['parameters']['retry_after']) && is_numeric($json['parameters']['retry_after'])) {
            $retryAfter = max(1, (int)$json['parameters']['retry_after']);
        } elseif (preg_match('/retry after\s+(\d+)/i', $desc, $m)) {
            $retryAfter = max(1, (int)$m[1]);
        }
        $lastError = $desc;
        if (($code === 429 || $errorCode === 429 || stripos($desc, 'Too Many Requests') !== false) && $attempt < 5) {
            sleep($retryAfter > 0 ? min(30, $retryAfter + 1) : min(8, $attempt));
            continue;
        }
        if (($code >= 500 || in_array($errorCode, [500,502,503,504], true)) && $attempt < 5) {
            sleep(min(8, $attempt));
            continue;
        }
        if ($errorCode === 429 || stripos($desc, 'Too Many Requests') !== false) {
            throw new RuntimeException('تلگرام موقتاً محدودیت درخواست داده است. چند ثانیه صبر کن و دوباره امتحان کن. متن خطا: ' . $desc);
        }
        throw new RuntimeException($desc);
    }
    throw new RuntimeException($lastError ?: 'Telegram API Error');
}
function install_pdo(array $db): PDO {
    if (!preg_match('/^[A-Za-z0-9_\-]+$/', $db['name'])) throw new RuntimeException('نام دیتابیس فقط حروف انگلیسی، عدد، خط تیره و آندرلاین باشد.');
    $opt = [PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES=>false];
    $dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4', $db['host'], (int)$db['port'], $db['name']);
    return new PDO($dsn, $db['user'], $db['pass'], $opt);
}
function install_split_sql(string $sql): array {
    $sql = preg_replace('/^\s*--.*$/m', '', $sql) ?? $sql;
    $sql = preg_replace('/\/\*.*?\*\//s', '', $sql) ?? $sql;
    return array_values(array_filter(array_map('trim', explode(';', $sql)), fn($s) => $s !== ''));
}
function install_import_schema(PDO $pdo): int {
    $sql = file_get_contents(SQL_FILE);
    if ($sql === false || trim($sql) === '') throw new RuntimeException('schema.sql خوانده نشد یا خالی است.');
    $count = 0;
    foreach (install_split_sql($sql) as $stmt) {
        $pdo->exec($stmt);
        $count++;
    }
    return $count;
}
function install_write_config(array $config): void {
    if (!is_writable(APP_DIR)) throw new RuntimeException('پوشه app قابل نوشتن نیست. Permission را درست کن.');
    $php = "<?php\n/** Generated by installer. Do not edit from public web. */\nreturn " . var_export($config, true) . ";\n";
    if (file_put_contents(CONFIG_FILE, $php, LOCK_EX) === false) throw new RuntimeException('ساخت app/config.php ناموفق بود.');
}
function install_seed(PDO $pdo, array $config): void {
    $now = date('Y-m-d H:i:s');
    $settings = [
        'app_name' => $config['app_name'],
        'app_url' => $config['app_url'],
        'default_card_holder' => $config['default_card']['holder'],
        'default_card_number' => $config['default_card']['number'],
        'default_card_iban' => $config['default_card']['iban'],
        'activity_log_chat_id' => $config['activity_log_chat_id'] ?? '',
    ];
    $st = $pdo->prepare('INSERT INTO settings(`key`, value, updated_at) VALUES(?,?,?) ON DUPLICATE KEY UPDATE value=VALUES(value), updated_at=VALUES(updated_at)');
    foreach ($settings as $k => $v) $st->execute([$k, (string)$v, $now]);
    foreach ($config['super_admin_ids'] as $tgId) {
        $tgId = install_digits((string)$tgId);
        if ($tgId === '') continue;
        $pdo->prepare('INSERT INTO users(telegram_id, username, first_name, role, balance, status, created_at, updated_at) VALUES(?,NULL,"Super Admin","admin",0,"active",?,?) ON DUPLICATE KEY UPDATE role="admin", status="active", updated_at=VALUES(updated_at)')->execute([$tgId, $now, $now]);
        $q = $pdo->prepare('SELECT id FROM users WHERE telegram_id=? LIMIT 1'); $q->execute([$tgId]); $uid = (int)$q->fetchColumn();
        if ($uid) $pdo->prepare('INSERT INTO admins(user_id, level, created_at) VALUES(?,"super",?) ON DUPLICATE KEY UPDATE level="super"')->execute([$uid, $now]);
    }
    $holder = trim((string)$config['default_card']['holder']);
    $card = trim((string)$config['default_card']['number']);
    if ($holder !== '' && strlen(install_digits($card)) >= 16) {
        $pdo->prepare('UPDATE payment_cards SET is_active=0')->execute();
        $pdo->prepare('INSERT INTO payment_cards(title, holder, card_number, iban, is_active, created_at, updated_at) VALUES("کارت اصلی",?,?,?,?,?,?)')
            ->execute([$holder, $card, $config['default_card']['iban'], 1, $now, $now]);
    }
}

$errors = [];
$success = null;
$forceInstall = (($_GET['force'] ?? $_POST['force'] ?? '') === '1');
$installed = !$forceInstall && (is_file(LOCK_FILE) || is_file(CONFIG_FILE));

if ($_SERVER['REQUEST_METHOD'] === 'POST' && !$installed) {
    try {
        if (!hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf'] ?? '')) throw new RuntimeException('توکن امنیتی فرم نامعتبر است. صفحه را رفرش کن.');
        if (($_POST['confirm_install'] ?? '') !== '1') throw new RuntimeException('تیک تأیید نصب را بزن.');
        foreach (install_requirements() as [$name,$ok]) if (!$ok) throw new RuntimeException('پیش‌نیاز نصب برقرار نیست: ' . $name);

        $appUrl = rtrim(trim(postv('app_url')), '/');
        $appName = trim(postv('app_name', 'ربات ساز میرزا احسان نت')) ?: 'ربات ساز میرزا احسان نت';
        $timezone = trim(postv('timezone','Asia/Tehran')) ?: 'Asia/Tehran';
        $token = trim(postv('main_bot_token'));
        $adminIdsRaw = trim(postv('super_admin_ids'));
        $adminIds = array_values(array_unique(array_filter(array_map('install_digits', preg_split('/[\s,،]+/u', $adminIdsRaw) ?: []))));
        $panelLoginKey = trim(postv('panel_login_key')) ?: bin2hex(random_bytes(18));
        $appKey = trim(postv('app_key')) ?: bin2hex(random_bytes(32));
        $webhookSecret = bin2hex(random_bytes(24));

        if ($appUrl === '') throw new RuntimeException('آدرس سایت را وارد کن.');
        if (!preg_match('~^https://~i', $appUrl) && !preg_match('~^http://(localhost|127\.0\.0\.1)~i', $appUrl)) throw new RuntimeException('برای وبهوک، آدرس سایت باید HTTPS باشد.');
        install_validate_token($token);
        if (!$adminIds) throw new RuntimeException('حداقل یک آیدی عددی ادمین اصلی وارد کن.');
        if (strlen($appKey) < 32) throw new RuntimeException('App Key باید حداقل ۳۲ کاراکتر باشد.');
        if (strlen($panelLoginKey) < 16) throw new RuntimeException('کلید ورود پنل حداقل ۱۶ کاراکتر باشد.');

        $dbConf = [
            'host' => trim(postv('db_host','localhost')),
            'port' => (int)(postv('db_port','3306') ?: 3306),
            'name' => trim(postv('db_name')),
            'user' => trim(postv('db_user')),
            'pass' => postv('db_pass'),
            'charset' => 'utf8mb4',
        ];
        if ($dbConf['host']==='' || $dbConf['name']==='' || $dbConf['user']==='') throw new RuntimeException('اطلاعات دیتابیس کامل نیست.');

        // Child bots need no database, panel API, database user or token input.
        // Every child receives a private table namespace in this existing DB.
        $childDbConf = [
            'driver' => 'table_namespace',
            'host' => $dbConf['host'],
            'port' => $dbConf['port'],
            'charset' => 'utf8mb4',
            'storage_root' => 'storage/child_databases',
        ];

        // Telegram token must be tested before writing config.
        $botInfo = tg_call($token, 'getMe')['result'];
        $pdo = install_pdo($dbConf);
        $sqlCount = install_import_schema($pdo);

        // Confirm that the existing database can create/drop isolated child table namespaces.
        (new ChildDatabaseManager(['db' => $dbConf, 'child_db' => $childDbConf]))->verifyProvisioningPrivilege();

        $config = [
            'app_name' => $appName,
            'app_url' => $appUrl,
            'timezone' => $timezone,
            'app_key' => $appKey,
            'panel_login_key' => $panelLoginKey,
            'main_bot_token' => $token,
            'main_webhook_secret' => $webhookSecret,
            'super_admin_ids' => $adminIds,
            'activity_log_chat_id' => trim(postv('activity_log_chat_id')),
            'db' => $dbConf,
            'child_db' => $childDbConf,
            'default_card' => [
                'holder' => trim(postv('card_holder')),
                'number' => trim(postv('card_number')),
                'iban' => trim(postv('card_iban')),
            ],
        ];

        install_write_config($config);
        install_seed($pdo, $config);

        $webhookUrl = $appUrl . '/bot.php?secret=' . $webhookSecret;
        $webhook = tg_call($token, 'setWebhook', [
            'url' => $webhookUrl,
            'drop_pending_updates' => true,
            'allowed_updates' => ['message','callback_query'],
            'secret_token' => $webhookSecret,
        ]);
        $menuOk = true;
        try {
            tg_call($token, 'setChatMenuButton', ['menu_button' => ['type'=>'web_app','text'=>'پنل مدیریت','web_app'=>['url'=>$appUrl . '/index.php']]]);
        } catch (Throwable $e) { $menuOk = false; }

        $lock = [
            'installed_at' => date('c'),
            'bot_username' => $botInfo['username'] ?? null,
            'app_url' => $appUrl,
            'webhook_url' => $webhookUrl,
            'sql_statements' => $sqlCount,
        ];
        file_put_contents(LOCK_FILE, json_encode($lock, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT), LOCK_EX);
        $success = $lock + ['panel_login_key' => $panelLoginKey, 'menu_ok' => $menuOk, 'telegram' => $webhook];
        $installed = true;
    } catch (Throwable $e) {
        $errors[] = $e->getMessage();
        if (is_file(CONFIG_FILE) && !is_file(LOCK_FILE)) @unlink(CONFIG_FILE);
    }
}
?><!doctype html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>نصب گرافیکی ربات‌ساز</title>
<style>
:root{--bg:#07111f;--card:rgba(255,255,255,.11);--card2:rgba(255,255,255,.055);--line:rgba(255,255,255,.18);--txt:#f8fafc;--muted:#b6c4d5;--ok:#34d399;--bad:#fb7185;--blue:#7dd3fc;--violet:#c4b5fd}*{box-sizing:border-box}body{margin:0;min-height:100vh;background:radial-gradient(circle at top right,rgba(125,211,252,.26),transparent 34%),radial-gradient(circle at bottom left,rgba(196,181,253,.22),transparent 36%),var(--bg);font-family:Tahoma,Arial,sans-serif;color:var(--txt);padding:20px}.wrap{max-width:1080px;margin:auto}.glass{background:linear-gradient(145deg,var(--card),var(--card2));border:1px solid var(--line);border-radius:28px;box-shadow:0 28px 90px rgba(0,0,0,.33);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}.hero{padding:26px;margin-bottom:18px}.hero h1{margin:0 0 12px;font-size:28px}.hero p,.muted{color:var(--muted);line-height:2}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.card{padding:22px;margin-bottom:18px}label{display:flex;flex-direction:column;gap:8px;color:#dbeafe;font-size:13px;margin-bottom:13px}input,select{width:100%;border:1px solid rgba(255,255,255,.18);background:rgba(7,17,31,.6);color:var(--txt);border-radius:15px;padding:13px;outline:none}.check{display:flex;flex-direction:row;gap:8px;align-items:center}.check input{width:auto}.btn{border:0;border-radius:18px;padding:15px 22px;color:#07111f;background:linear-gradient(135deg,var(--blue),var(--violet));font-weight:700;cursor:pointer}.req{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.req div{display:flex;justify-content:space-between;gap:10px;padding:12px;border-radius:16px;background:rgba(255,255,255,.07);color:var(--muted)}.ok{color:var(--ok)}.bad{color:var(--bad)}.alert{padding:14px 16px;border-radius:18px;margin-bottom:14px;line-height:2;border:1px solid}.alert.bad{border-color:rgba(251,113,133,.4);background:rgba(251,113,133,.12);color:#fecdd3}.alert.ok{border-color:rgba(52,211,153,.4);background:rgba(52,211,153,.12);color:#bbf7d0}.mono{direction:ltr;text-align:left;font-family:Consolas,monospace}.result{display:grid;gap:10px}.kv{display:flex;justify-content:space-between;gap:12px;padding:12px;border-radius:15px;background:rgba(255,255,255,.07)}.kv span{direction:ltr;text-align:left}.small{font-size:12px;color:var(--muted);line-height:2}@media(max-width:800px){body{padding:10px}.grid,.req{grid-template-columns:1fr}.hero h1{font-size:22px}.kv{flex-direction:column}}
</style>
</head>
<body><div class="wrap">
  <section class="hero glass">
    <h1>نصب گرافیکی ربات‌ساز شیشه‌ای</h1>
    <p>این نصب‌کننده فقط بعد از تکمیل فرم و زدن دکمه نصب اجرا می‌شود؛ روی باز شدن صفحه هیچ نصب خودکاری انجام نمی‌دهد.</p>
  </section>

  <section class="card glass">
    <h2>پیش‌نیازها</h2>
    <div class="req">
    <?php foreach (install_requirements() as [$name,$ok]): ?>
      <div><span><?=ih($name)?></span><b class="<?=$ok?'ok':'bad'?>"><?=$ok?'OK':'خطا'?></b></div>
    <?php endforeach; ?>
    </div>
  </section>

  <?php foreach ($errors as $err): ?><div class="alert bad">❌ <?=ih($err)?></div><?php endforeach; ?>

  <?php if ($success): ?>
    <section class="card glass">
      <div class="alert ok">✅ نصب کامل شد. کانفیگ ساخته شد، دیتابیس مادر آماده شد، جداسازی خودکار ربات‌های فرزند فعال شد، ادمین ثبت شد و Webhook ست شد.</div>
      <div class="result">
        <div class="kv"><b>ربات مادر</b><span>@<?=ih((string)$success['bot_username'])?></span></div>
        <div class="kv"><b>Webhook</b><span><?=ih((string)$success['webhook_url'])?></span></div>
        <div class="kv"><b>پنل</b><span><?=ih(postv('app_url') . '/index.php')?></span></div>
        <div class="kv"><b>کلید ورود دستی پنل</b><span><?=ih((string)$success['panel_login_key'])?></span></div>
        <div class="kv"><b>SQL</b><span><?=ih((string)$success['sql_statements'])?> دستور</span></div>
        <div class="kv"><b>دکمه منوی تلگرام</b><span><?=$success['menu_ok']?'فعال شد':'فعال نشد؛ نصب اصلی کامل است'?></span></div>
      </div>
      <p class="small">حالا در تلگرام به ربات مادر /start بده. برای امنیت، فایل <span class="mono">public/install.php</span> را حذف کن یا دسترسی آن را ببند.</p>
      <a class="btn" href="index.php" style="display:inline-block;text-decoration:none;margin-top:12px">ورود به پنل</a>
    </section>
  <?php elseif ($installed): ?>
    <section class="card glass">
      <div class="alert ok">این پروژه قبلاً نصب شده است. برای جلوگیری از نصب اشتباهی، فرم نصب قفل شده است.</div>
      <p class="small">اگر نصب قبلی اشتباه بوده، از لینک نصب مجدد استفاده کن. این کار فایل config.php را بازنویسی می‌کند.</p>
      <a class="btn" href="index.php" style="display:inline-block;text-decoration:none">ورود به پنل</a>
      <a class="btn" href="install.php?force=1" style="display:inline-block;text-decoration:none;margin-right:8px">نصب مجدد</a>
    </section>
  <?php else: ?>
    <form method="post" class="glass card" autocomplete="off">
      <input type="hidden" name="csrf" value="<?=ih($_SESSION['csrf'])?>">
      <?php if ($forceInstall): ?><input type="hidden" name="force" value="1"><?php endif; ?>
      <?php if ($forceInstall): ?><div class="alert bad">حالت نصب مجدد فعال است. بعد از ارسال فرم، کانفیگ قبلی بازنویسی می‌شود.</div><?php endif; ?>
      <h2>۱) اطلاعات اصلی</h2>
      <div class="grid">
        <label>نام پروژه<input name="app_name" value="<?=ih(postv('app_name','ربات ساز میرزا احسان نت'))?>" required></label>
        <label>آدرس سایت HTTPS<input name="app_url" value="<?=ih(postv('app_url', install_guess_url()))?>" required placeholder="https://domain.com"></label>
        <label>توکن ربات مادر<input name="main_bot_token" value="<?=ih(postv('main_bot_token'))?>" required placeholder="123456789:AA..."></label>
        <label>آیدی عددی ادمین اصلی<input name="super_admin_ids" value="<?=ih(postv('super_admin_ids'))?>" required placeholder="123456789 یا چندتا با کاما"></label>
        <label>منطقه زمانی<select name="timezone"><option value="Asia/Tehran">Asia/Tehran</option><option value="Europe/Berlin">Europe/Berlin</option><option value="UTC">UTC</option></select></label>
        <label>کلید ورود دستی پنل<input name="panel_login_key" value="<?=ih(postv('panel_login_key', bin2hex(random_bytes(12))))?>" required></label>
        <label>Chat ID گروه/چنل گزارش فعالیت<input name="activity_log_chat_id" value="<?=ih(postv('activity_log_chat_id'))?>" placeholder="اختیاری: -1001234567890 یا @channel"></label>
        <label>App Key رمزنگاری<input name="app_key" value="<?=ih(postv('app_key', bin2hex(random_bytes(32))))?>" required></label>
      </div>
      <h2>۲) دیتابیس موجود ربات مادر</h2>
      <div class="grid">
        <label>هاست دیتابیس<input name="db_host" value="<?=ih(postv('db_host','localhost'))?>" required></label>
        <label>پورت<input name="db_port" value="<?=ih(postv('db_port','3306'))?>" required></label>
        <label>نام دیتابیس<input name="db_name" value="<?=ih(postv('db_name'))?>" required></label>
        <label>یوزر دیتابیس<input name="db_user" value="<?=ih(postv('db_user'))?>" required></label>
        <label>پسورد دیتابیس<input name="db_pass" value="<?=ih(postv('db_pass'))?>" type="password"></label>
      </div>
      <h2>۳) جداسازی خودکار ربات‌های فرزند</h2>
      <div class="alert ok">✅ هیچ اطلاعاتی برای دیتابیس ربات‌های فرزند لازم نیست. سیستم برای هر ربات، جدول‌های اختصاصی با شناسه غیرتکراری داخل همین دیتابیس می‌سازد؛ پنل‌ها، کاربران، سفارش‌ها و تنظیمات ربات‌ها با یکدیگر و با ربات مادر مخلوط نمی‌شوند.</div>
      <p class="small">برای هر ربات یک پوشه محافظت‌شده در <span class="ltr">storage/child_databases</span> نیز خودکار ساخته می‌شود. ساخت ربات به cPanel، DirectAdmin، API Token، CREATE DATABASE یا CREATE USER وابسته نیست.</p>
      <h2>۴) شماره کارت پیش‌فرض</h2>
      <div class="grid">
        <label>صاحب کارت<input name="card_holder" value="<?=ih(postv('card_holder'))?>" placeholder="نام صاحب کارت"></label>
        <label>شماره کارت<input name="card_number" value="<?=ih(postv('card_number'))?>" placeholder="6037..."></label>
        <label>شبا<input name="card_iban" value="<?=ih(postv('card_iban'))?>" placeholder="IR..."></label>
      </div>
      <label class="check"><input type="checkbox" name="confirm_install" value="1" required> تأیید می‌کنم اطلاعات بالا درست است و نصب انجام شود.</label>
      <button class="btn" type="submit">شروع نصب و ست‌کردن Webhook</button>
      <p class="small">اگر خطایی دیدی، نصب ناقص قفل نمی‌شود و می‌توانی همان فرم را اصلاح کنی و دوباره بفرستی.</p>
    </form>
  <?php endif; ?>
</div></body></html>
