Files
2026-10-19 15:22:52 -07:00

164 lines
7.7 KiB
PHP

<?php
/**
* Square Checkout — order details page
* Opened by square_button.php. Amounts come from $_SESSION['sq_checkout'],
* never the URL. After a successful charge, order-api.php books the payment
* into user_account and money settles in the school's Square account.
*/
if (session_status() !== PHP_SESSION_ACTIVE) {
@session_name('hotlunch');
@session_start();
}
$ctx = $_SESSION['sq_checkout'] ?? null;
$ctxValid = is_array($ctx)
&& (float)($ctx['total_amount'] ?? 0) > 0
&& (int)($ctx['school_num'] ?? 0) > 0
&& (time() - (int)($ctx['created_at'] ?? 0)) < 3600;
$schoolReady = false;
$schoolName = '';
$returnUrl = is_array($ctx) ? (string)($ctx['return_url'] ?? '/') : '/';
$readyIssues = [];
if ($ctxValid) {
require_once __DIR__ . '/lib/MerchantStore.php';
require_once __DIR__ . '/lib/SquareClient.php';
$cfg = require __DIR__ . '/config.php';
$store = new MerchantStore($cfg);
$client = new SquareClient($cfg);
$num = (int)$ctx['school_num'];
$meta = $store->getMetaByNum($num);
$schoolName = (string)($ctx['school_name'] ?: ($store->schoolNameByNum($num) ?? ''));
$readyIssues = is_array($meta['status_issues'] ?? null) ? $meta['status_issues'] : [];
// Same rule as the button: only an onboarded school can be paid.
$schoolReady = $store->isOnboarded($num, $client);
if (!$schoolReady) {
$meta = $store->getMetaByNum($num);
$readyIssues = is_array($meta['status_issues'] ?? null) ? $meta['status_issues'] : [];
}
$symbol = (string)($ctx['currency_symbol'] ?? '$');
$baseAmount = (string)$ctx['base_amount'];
$total = (string)$ctx['total_amount'];
$baseLabel = (string)($ctx['base_label'] ?? 'Account balance');
$buyerEmail = (string)($ctx['buyer_email'] ?? '');
$currency = strtoupper((string)($ctx['currency'] ?? ($meta['currency'] ?? 'CAD')));
if (!preg_match('/^[A-Z]{3}$/', $currency)) $currency = 'CAD';
$sqCheckout = [
'school_name' => $schoolName,
'school_num' => $num,
'user_num' => (int)($ctx['user_num'] ?? 0),
// First-party checkout is bound to the session; never fall back to the
// public embed key, or a paid checkout could be resubmitted as an embed.
'merchant_key' => '',
'amount' => $total,
'currency' => $currency,
'buyer_email' => $buyerEmail,
'order_api_url' => './order-api.php',
'success_url' => $returnUrl,
];
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $ctxValid ? 'Checkout — ' . htmlspecialchars($schoolName) : 'Checkout' ?></title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f7f8fa; color: #1a202c; min-height: 100vh; }
a { color: #006aff; }
.topbar { background: #fff; border-bottom: 1px solid #e2e8f0; padding: 14px 24px; display: flex; align-items: center; gap: 10px; }
.topbar-school { font-weight: 700; font-size: 16px; color: #006aff; }
.topbar-secure { margin-left: auto; font-size: 12px; color: #718096; }
.page { max-width: 920px; margin: 36px auto 48px; padding: 0 20px; }
.page-head { margin-bottom: 26px; }
.page-head h1 { font-size: 24px; font-weight: 700; margin-bottom: 6px; }
.page-head p { font-size: 14px; color: #718096; }
.cols { display: grid; grid-template-columns: 380px 1fr; gap: 28px; align-items: start; }
@media (max-width: 760px) { .cols { grid-template-columns: 1fr; } }
.card { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 24px; }
.card h2 { font-size: 15px; font-weight: 700; margin-bottom: 4px; }
.card .card-sub { font-size: 13px; color: #718096; margin-bottom: 18px; line-height: 1.5; }
.sum-row { display: flex; justify-content: space-between; gap: 12px; padding: 9px 0; font-size: 14px; color: #4a5568; border-bottom: 1px solid #f0f2f5; }
.sum-total { display: flex; justify-content: space-between; padding: 14px 0 2px; font-size: 16px; font-weight: 700; }
.sum-total .amt { font-size: 22px; }
.sum-note { margin-top: 16px; padding: 12px 14px; background: #f5f7fa; border-radius: 8px; font-size: 12.5px; color: #4a5568; line-height: 1.55; }
.sum-account { margin-top: 14px; font-size: 12.5px; color: #718096; }
.back-link { display: inline-block; margin-top: 12px; font-size: 13.5px; }
.notice { max-width: 480px; margin: 70px auto; background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 34px 30px; text-align: center; }
.notice h1 { font-size: 19px; margin-bottom: 10px; }
.notice p { font-size: 14px; color: #718096; line-height: 1.6; margin-bottom: 22px; }
.notice a.btn-back { display: inline-block; background: #006aff; color: #fff; text-decoration: none; font-size: 14px; font-weight: 600; padding: 11px 26px; border-radius: 8px; }
.foot { text-align: center; font-size: 12px; color: #a0aec0; margin-top: 34px; line-height: 1.6; }
</style>
</head>
<body>
<?php if (!$ctxValid): ?>
<div class="notice">
<h1>This checkout link has expired</h1>
<p>For your security, checkout sessions only stay open for a short while.
Head back to your account page and click <strong>Pay with Square</strong> again.</p>
<a class="btn-back" href="<?= htmlspecialchars($returnUrl) ?>">Back to my account</a>
</div>
<?php elseif (!$schoolReady): ?>
<div class="notice">
<h1>Online payment isn't available right now</h1>
<p><?= htmlspecialchars($schoolName) ?> can't accept Square payments at the moment.
Please go back and choose another payment method, or contact the school office.</p>
<?php if (!empty($readyIssues)): ?>
<p style="text-align:left;font-size:13px;color:#4a5568;margin-top:8px;">
<?= htmlspecialchars(implode(' ', array_map('strval', $readyIssues))) ?>
</p>
<?php endif; ?>
<a class="btn-back" href="<?= htmlspecialchars($returnUrl) ?>">Back to my account</a>
</div>
<?php else: ?>
<nav class="topbar">
<span class="topbar-school"><?= htmlspecialchars($schoolName) ?></span>
<span class="topbar-secure">Secure checkout</span>
</nav>
<div class="page">
<div class="page-head">
<h1>Review &amp; pay</h1>
<p>You're paying <?= htmlspecialchars($schoolName) ?> through Square. Funds go to the school's Square account.</p>
</div>
<div class="cols">
<div class="card">
<h2>Order summary</h2>
<p class="card-sub">Payment to <?= htmlspecialchars($schoolName) ?></p>
<div class="sum-row">
<span><?= htmlspecialchars($baseLabel) ?></span>
<span class="amt"><?= htmlspecialchars($symbol . number_format((float)$baseAmount, 2)) ?></span>
</div>
<div class="sum-total">
<span>Total</span>
<span class="amt"><?= htmlspecialchars($symbol . number_format((float)$total, 2)) ?></span>
</div>
<div class="sum-note">
Your payment goes directly to the school's Square account and is credited
to your family account as soon as Square confirms it.
</div>
<?php if ($buyerEmail !== ''): ?>
<p class="sum-account">Paying as <?= htmlspecialchars($buyerEmail) ?></p>
<?php endif; ?>
</div>
<div class="card">
<h2>Payment method</h2>
<p class="card-sub">Card details are tokenized by Square and never touch our servers.</p>
<?php require __DIR__ . '/square-checkout-widget.php'; ?>
<a class="back-link" href="<?= htmlspecialchars($returnUrl) ?>">&larr; Go back and choose a different payment method</a>
</div>
</div>
<p class="foot">Questions about a charge? Contact the school office at <?= htmlspecialchars($schoolName) ?>.</p>
</div>
<?php endif; ?>
</body>
</html>