39496-vm/checkout.php
2026-04-14 02:26:12 +00:00

288 lines
16 KiB
PHP

<?php
require_once __DIR__ . '/includes/app.php';
$plans = plans_catalog();
$planKey = (string) ($_GET['plan'] ?? $_POST['plan_key'] ?? 'plus');
$plan = get_plan($planKey) ?? get_plan('plus');
$cycle = (string) ($_GET['cycle'] ?? $_POST['billing_cycle'] ?? 'monthly');
$cycle = in_array($cycle, ['monthly', 'yearly'], true) ? $cycle : 'monthly';
$courseIdForView = (int) ($_GET['course_id'] ?? $_POST['course_id'] ?? 0);
$course = null;
if ($courseIdForView > 0) {
$course = db()->query("SELECT * FROM courses WHERE id = $courseIdForView")->fetch();
if ($course) {
if (!$course['registration_open']) {
$msg = current_lang() === 'ar' ? 'التسجيل في هذه الدورة/الدفعة مغلق حالياً.' : 'Registration for this course/batch is currently closed.';
die("<div style='padding:2rem;text-align:center;font-family:sans-serif'><h1>" . $msg . "</h1><a href='index.php'>" . (current_lang() === 'ar' ? 'العودة' : 'Back') . "</a></div>");
}
if ($course['max_students'] > 0) {
$enrolled = db()->query("SELECT COUNT(*) FROM course_students WHERE course_id = $courseIdForView")->fetchColumn();
if ($enrolled >= $course['max_students']) {
$msg = current_lang() === 'ar' ? 'لقد وصلت هذه الدورة إلى الحد الأقصى لعدد الطلاب.' : 'This course has reached its maximum capacity.';
die("<div style='padding:2rem;text-align:center;font-family:sans-serif'><h1>" . $msg . "</h1><a href='index.php'>" . (current_lang() === 'ar' ? 'العودة' : 'Back') . "</a></div>");
}
}
} else {
die('Course not found');
}
}
$errors = [];
$form = [
'full_name' => trim((string) ($_POST['full_name'] ?? '')),
'email' => trim((string) ($_POST['email'] ?? '')),
'password' => (string) ($_POST['password'] ?? ''),
'whatsapp' => trim((string) ($_POST['whatsapp'] ?? '')),
'preferred_language' => (string) ($_POST['preferred_language'] ?? current_lang()),
'billing_cycle' => $cycle,
'plan_key' => $plan['key'],
'wablas_opt_in' => isset($_POST['wablas_opt_in']) ? 1 : 0,
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($form['full_name'] === '' || strlen($form['full_name']) < 2) {
$errors[] = t('Please enter a valid full name.', 'يرجى إدخال اسم كامل صحيح.');
}
if (empty($form['password']) || strlen($form['password']) < 6) {
$errors[] = t('Password must be at least 6 characters.', 'يجب أن تتكون كلمة المرور من 6 أحرف على الأقل.');
}
if (!filter_var($form['email'], FILTER_VALIDATE_EMAIL)) {
$errors[] = t('Please enter a valid email address.', 'يرجى إدخال بريد إلكتروني صحيح.');
}
if (!preg_match('/^[0-9+\-\s]{8,20}$/', $form['whatsapp'])) {
$errors[] = t('Please enter a valid WhatsApp number.', 'يرجى إدخال رقم واتساب صحيح.');
}
if (!in_array($form['preferred_language'], ['en', 'ar'], true)) {
$errors[] = t('Please select a supported language.', 'يرجى اختيار لغة مدعومة.');
}
if (!$errors) {
$reference = 'THW-' . date('YmdHis') . '-' . random_int(100, 999);
$id = create_subscription([
'full_name' => $form['full_name'],
'email' => $form['email'],
'password' => password_hash($form['password'], PASSWORD_DEFAULT),
'whatsapp' => $form['whatsapp'],
'preferred_language' => $form['preferred_language'],
'plan_key' => $plan['key'],
'billing_cycle' => $form['billing_cycle'],
'payment_status' => 'active', // MVP: Mark active immediately
'payment_gateway' => 'Thawani',
'thawani_reference' => $reference,
'wablas_opt_in' => $form['wablas_opt_in'],
]);
$courseId = (int) ($_GET['course_id'] ?? $_POST['course_id'] ?? 0);
if ($courseId > 0) {
try {
$stmt = db()->prepare('INSERT IGNORE INTO course_students (course_id, student_id) VALUES (?, ?)');
$stmt->execute([$courseId, $id]);
} catch (Exception $e) {}
}
// --- Thawani Integration ---
$stmtThawani = db()->query("SELECT thawani_secret_key, thawani_publishable_key, thawani_mode FROM platform_profile WHERE id = 1");
$thawaniConfig = $stmtThawani->fetch(PDO::FETCH_ASSOC);
$thawaniRedirect = null;
if ($thawaniConfig && !empty($thawaniConfig['thawani_secret_key']) && !empty($thawaniConfig['thawani_publishable_key'])) {
$mode = $thawaniConfig['thawani_mode'] ?? 'test';
$baseUrl = $mode === 'live' ? 'https://checkout.thawani.om' : 'https://uatcheckout.thawani.om';
$priceOMR = $course ? (float) $course['price'] : (float) ($cycle === 'yearly' ? $plan['price_yearly'] : $plan['price_monthly']);
$priceBaisa = (int) ($priceOMR * 1000);
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$protocol = $_SERVER['HTTP_X_FORWARDED_PROTO'] . "://";
}
$host = $_SERVER['HTTP_HOST'];
$scriptDir = str_replace('\\', '/', dirname($_SERVER['PHP_SELF']));
$appBaseUrl = $protocol . $host . $scriptDir;
$successUrl = $appBaseUrl . '/subscription.php?id=' . $id . '&created=1';
$cancelUrl = $appBaseUrl . '/checkout.php?plan=' . $plan['key'] . '&cycle=' . $cycle;
if ($courseId > 0) {
$cancelUrl .= '&course_id=' . $courseId;
}
$productName = $course ? (current_lang() === 'ar' ? $course['name_ar'] : $course['name_en']) : plan_name($plan);
$productName = mb_substr($productName, 0, 40); // Thawani character limit
if (empty($productName)) $productName = 'Course Enrollment';
$payload = [
'client_reference_id' => (string) $id,
'mode' => 'payment',
'products' => [
[
'name' => $productName,
'quantity' => 1,
'unit_amount' => $priceBaisa
]
],
'success_url' => $successUrl,
'cancel_url' => $cancelUrl,
'metadata' => [
'subscription_id' => $id,
'customer_email' => $form['email'],
'name' => $form['full_name'],
'contact_number' => $form['whatsapp'],
'email_id' => $form['email']
]
];
$ch = curl_init("$baseUrl/api/v1/checkout/session");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"thawani-api-key: {$thawaniConfig['thawani_secret_key']}",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
$response = curl_exec($ch);
curl_close($ch);
if ($response) {
$resData = json_decode($response, true);
if (isset($resData['success']) && $resData['success'] && isset($resData['data']['session_id'])) {
$sessionId = $resData['data']['session_id'];
db()->query("UPDATE student_subscriptions SET thawani_reference = " . db()->quote($sessionId) . " WHERE id = " . $id);
$thawaniRedirect = "$baseUrl/pay/$sessionId?key={$thawaniConfig['thawani_publishable_key']}";
} else {
error_log('Thawani Session Error: ' . $response);
}
}
}
if ($thawaniRedirect) {
$_SESSION['subscription_id'] = $id;
$_SESSION['student_email'] = $form['email'];
header('Location: ' . $thawaniRedirect);
exit;
} else {
db()->query("DELETE FROM student_subscriptions WHERE id = " . $id);
if ($courseId > 0) {
db()->query("DELETE FROM course_students WHERE course_id = " . $courseId . " AND student_id = " . $id);
}
$errors[] = t('Thawani payment gateway is not fully configured (missing keys) or the API call failed. Please update your API keys in the Teacher Panel > Integrations.', 'بوابة دفع ثواني غير مهيأة بالكامل (المفاتيح مفقودة) أو فشل الاتصال. يرجى تحديث مفاتيحك في لوحة المعلم > التكاملات.');
}
}
}
render_head(
t('Checkout', 'الدفع'),
t('Capture a student subscription with bilingual preferences, WhatsApp opt-in, and a Thawani-ready payment reference.', 'التقط اشتراك طالب مع تفضيلات ثنائية اللغة وخيار واتساب ومرجع دفع جاهز لثواني.')
);
render_nav('pricing.php');
?>
<main class="py-5">
<div class="container">
<div class="row g-4">
<div class="col-lg-7">
<div class="panel-card">
<span class="eyebrow"><?= h(t('Checkout flow', 'مسار الدفع')) ?></span>
<h1 class="section-title mb-2"><?= h(t('Create a subscription and unlock the student dashboard', 'أنشئ اشتراكاً وافتح لوحة الطالب')) ?></h1>
<p class="text-secondary mb-4"><?= h(t('This first slice records a real subscription row locally, reserves a Thawani reference, and prepares WhatsApp reminders.', 'تسجل هذه الشريحة الأولى صف اشتراك حقيقياً محلياً وتحجز مرجع Thawani وتجهز تذكيرات واتساب.')) ?></p>
<?php if ($errors): ?>
<div class="alert alert-danger border">
<ul class="mb-0 ps-3">
<?php foreach ($errors as $error): ?>
<li><?= h($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form method="post" class="row g-3">
<input type="hidden" name="plan_key" value="<?= h($plan['key']) ?>">
<?php if (isset($_GET['course_id']) || isset($_POST['course_id'])): ?>
<input type="hidden" name="course_id" value="<?= h($_GET['course_id'] ?? $_POST['course_id'] ?? 0) ?>">
<?php endif; ?>
<div class="col-md-6">
<label class="form-label" for="full_name"><?= h(t('Full name', 'الاسم الكامل')) ?></label>
<input class="form-control" id="full_name" name="full_name" required value="<?= h($form['full_name']) ?>">
</div>
<div class="col-md-6">
<label class="form-label" for="email"><?= h(t('Email', 'البريد الإلكتروني')) ?></label>
<input class="form-control" id="email" type="email" name="email" required value="<?= h($form['email']) ?>">
</div>
<div class="col-md-6">
<label class="form-label" for="password"><?= h(t('Password', 'كلمة المرور')) ?></label>
<input class="form-control" id="password" type="password" name="password" required>
</div>
<div class="col-md-6">
<label class="form-label" for="whatsapp"><?= h(t('WhatsApp', 'واتساب')) ?></label>
<input class="form-control" id="whatsapp" name="whatsapp" placeholder="+968 9000 0000" required value="<?= h($form['whatsapp']) ?>">
</div>
<div class="col-md-3">
<label class="form-label" for="preferred_language"><?= h(t('Language', 'اللغة')) ?></label>
<select class="form-select" id="preferred_language" name="preferred_language">
<option value="en" <?= $form['preferred_language'] === 'en' ? 'selected' : '' ?>>English</option>
<option value="ar" <?= $form['preferred_language'] === 'ar' ? 'selected' : '' ?>>العربية</option>
</select>
</div>
<?php if ($course): ?>
<input type="hidden" name="billing_cycle" value="monthly">
<?php else: ?>
<div class="col-md-3">
<label class="form-label" for="billing_cycle"><?= h(t('Billing', 'الدفع الدوري')) ?></label>
<select class="form-select" id="billing_cycle" name="billing_cycle">
<option value="monthly" <?= $form['billing_cycle'] === 'monthly' ? 'selected' : '' ?>><?= h(t('Monthly', 'شهري')) ?></option>
<option value="yearly" <?= $form['billing_cycle'] === 'yearly' ? 'selected' : '' ?>><?= h(t('Yearly', 'سنوي')) ?></option>
</select>
</div>
<?php endif; ?>
<div class="col-12">
<div class="form-check border rounded-3 p-3">
<input class="form-check-input" type="checkbox" id="wablas_opt_in" name="wablas_opt_in" value="1" <?= $form['wablas_opt_in'] ? 'checked' : '' ?>>
<label class="form-check-label" for="wablas_opt_in">
<?= h(t('Send payment success and class reminders through WhatsApp.', 'أرسل نجاح الدفع وتذكيرات الحصص عبر واتساب.')) ?>
</label>
</div>
</div>
<div class="col-12 d-flex flex-wrap gap-2 pt-2">
<button class="btn btn-dark btn-lg" type="submit"><?= h(t('Confirm subscription', 'تأكيد الاشتراك')) ?></button>
<a class="btn btn-outline-dark btn-lg" href="<?= h(app_url('pricing.php')) ?>"><?= h(t('Back to plans', 'العودة إلى الخطط')) ?></a>
</div>
</form>
</div>
</div>
<div class="col-lg-5">
<aside class="panel-card sticky-card">
<div class="d-flex justify-content-between align-items-start mb-3">
<div>
<span class="eyebrow"><?= h(t('Order summary', 'ملخص الطلب')) ?></span>
<?php if ($course): ?>
<h2 class="h5 mb-1"><?= h(current_lang() === 'ar' ? $course['name_ar'] : $course['name_en']) ?></h2>
<p class="small text-secondary mb-0"><?= h(t('Short course enrollment', 'تسجيل في دورة قصيرة')) ?></p>
<?php else: ?>
<h2 class="h5 mb-1"><?= h(plan_name($plan)) ?></h2>
<p class="small text-secondary mb-0"><?= h(t('Single-platform access', 'وصول إلى منصة موحدة')) ?></p>
<?php endif; ?>
</div>
<span class="badge bg-dark-subtle text-dark-emphasis border"><?= h(t('Step 1 MVP', 'النسخة الأولى')) ?></span>
</div>
<div class="summary-row"><span><?= h(t('Billing cycle', 'دورة الدفع')) ?></span><strong>
<?php if ($course): ?>
<?= h(t('One-time', 'لمرة واحدة')) ?>
<?php else: ?>
<?= h($cycle === 'yearly' ? t('Yearly', 'سنوي') : t('Monthly', 'شهري')) ?>
<?php endif; ?>
</strong></div>
<div class="summary-row"><span><?= h(t('Price', 'السعر')) ?></span><strong data-cycle-price>
<?php if ($course): ?>
<?= h(format_price((float)$course['price'])) ?>
<?php else: ?>
<?= h(price_label($plan, $cycle)) ?>
<?php endif; ?>
</strong></div>
<hr>
<ul class="list-unstyled compact-list compact-list-tight mb-0">
<?php foreach (current_lang() === 'ar' ? $plan['features_ar'] : $plan['features_en'] as $feature): ?>
<li><?= h($feature) ?></li>
<?php endforeach; ?>
</ul>
</aside>
</div>
</div>
</div>
</main>
<?php render_footer(); ?>