First Commit

This commit is contained in:
mark committed 2026-10-19 15:22:52 -07:00
1 parent 5eb9dd037d
commit 2e62c0e60b
11877 files changed
+2035312

No files matched your search

+211
View File
@@ -0,0 +1,211 @@
<?php
/**
* square_connected.php — TEMPORARY admin view of Square-connected schools.
*
* Read-only. Lists every school that has a Square connection (and recent pending
* onboarding attempts) so you can confirm onboarding worked without DB access.
* NEVER displays access/refresh tokens — only whether they are present.
*
* ⚠️ DELETE THIS FILE FROM PRODUCTION when you're done. It is gated by a key
* below to avoid casual exposure, but it is not part of the real app.
*
* Usage: https://dev.hotlunches.net/square/square_connected.php?key=sq-admin-7f3a
*/
ini_set('display_errors', 0);
// ── Simple access gate ───────────────────────────────────────────────────────
// Change this value (or delete the file) before leaving it on a public server.
$ACCESS_KEY = 'sq-admin-7f3a';
if (($_GET['key'] ?? '') !== $ACCESS_KEY) {
http_response_code(403);
header('Content-Type: text/plain; charset=utf-8');
echo "Forbidden. Append ?key=YOUR_KEY (see the top of square_connected.php).";
exit;
}
// ── Bootstrap the DB layer (MerchantStore loads the host include/database.php) ─
require_once __DIR__ . '/lib/MerchantStore.php';
$config = require __DIR__ . '/config.php';
$store = new MerchantStore($config); // constructor bootstraps the db_* layer
header('Content-Type: text/html; charset=utf-8');
$dbReady = function_exists('db_query');
/** Format a unix timestamp (or '-' when empty). */
function ts($v) {
$v = (int)$v;
return $v > 0 ? date('Y-m-d H:i', $v) : '-';
}
function h($v) { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); }
// ── Connected schools (tokens are NOT selected) ──────────────────────────────
$connected = [];
$pending = [];
$error = '';
if ($dbReady) {
$sql = "SELECT record_num, school_name,
square_merchant_id, square_merchant_name,
square_location_id, square_currency, square_country,
square_ready, square_connected_at, square_token_expires_at,
square_merchant_key, square_status_issues, square_status_checked_at,
square_authorization_revoked,
(square_access_token IS NOT NULL AND square_access_token <> '') AS has_access,
(square_refresh_token IS NOT NULL AND square_refresh_token <> '') AS has_refresh
FROM schools
WHERE square_merchant_id IS NOT NULL AND square_merchant_id <> ''
ORDER BY square_connected_at DESC";
$r = db_query($sql);
if ($r) {
while ($row = db_fetch_assoc($r)) {
$connected[] = $row;
}
}
$pr = db_query(
"SELECT state, school_num, status, merchant_id, created_at, connected_at, last_callback
FROM square_pending_onboarding
ORDER BY created_at DESC
LIMIT 50"
);
if ($pr) {
while ($row = db_fetch_assoc($pr)) {
$pending[] = $row;
}
}
} else {
$error = 'Host database layer (db_query) is not available — cannot read.';
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Square — Connected Schools</title>
<style>
body { font-family: system-ui, -apple-system, Segoe UI, Arial, sans-serif; margin: 0; background: #f7fafc; color: #1a202c; }
.wrap { max-width: 1200px; margin: 0 auto; padding: 28px 20px 60px; }
h1 { font-size: 22px; margin: 0 0 4px; }
.sub { color: #718096; font-size: 13px; margin: 0 0 24px; }
.warn { background: #fffaf0; border: 1px solid #fbd38d; color: #7b341e; padding: 10px 14px; border-radius: 8px; font-size: 13px; margin-bottom: 20px; }
.err { background: #fff5f5; border: 1px solid #feb2b2; color: #c53030; padding: 10px 14px; border-radius: 8px; font-size: 13px; margin-bottom: 20px; }
h2 { font-size: 16px; margin: 28px 0 10px; }
table { width: 100%; border-collapse: collapse; background: #fff; border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; font-size: 13px; }
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid #edf2f7; vertical-align: top; }
th { background: #f1f5f9; font-weight: 600; color: #475569; white-space: nowrap; }
tr:last-child td { border-bottom: none; }
code { background: #f1f5f9; padding: 1px 5px; border-radius: 4px; font-size: 12px; }
.pill { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; }
.ok { background: #c6f6d5; color: #22543d; }
.no { background: #fed7d7; color: #742a2a; }
.muted{ color: #a0aec0; }
.count { color: #718096; font-weight: 400; font-size: 13px; }
</style>
</head>
<body>
<div class="wrap">
<h1>Square — Connected Schools</h1>
<p class="sub">Read-only snapshot from the live database. Tokens are never shown.</p>
<div class="warn">⚠️ Temporary diagnostic page — delete <code>square_connected.php</code> from production when finished.</div>
<?php if ($error !== ''): ?>
<div class="err"><?php echo h($error); ?></div>
<?php endif; ?>
<h2>Connected schools <span class="count">(<?php echo count($connected); ?>)</span></h2>
<?php if (empty($connected)): ?>
<p class="muted">No schools have a Square connection yet.</p>
<?php else: ?>
<table>
<thead>
<tr>
<th>record_num</th>
<th>School</th>
<th>Merchant</th>
<th>Location</th>
<th>Currency</th>
<th>Ready</th>
<th>Tokens</th>
<th>Connected</th>
<th>Token expires</th>
<th>Embed key</th>
<th>Issues</th>
</tr>
</thead>
<tbody>
<?php foreach ($connected as $c): ?>
<tr>
<td><code><?php echo h($c['record_num']); ?></code></td>
<td><?php echo h($c['school_name']); ?></td>
<td>
<?php echo h($c['square_merchant_name'] ?: '—'); ?><br>
<code><?php echo h($c['square_merchant_id']); ?></code>
</td>
<td><?php echo h($c['square_location_id'] ?: '—'); ?></td>
<td><?php echo h($c['square_currency'] ?: '—'); ?><?php echo $c['square_country'] ? ' / ' . h($c['square_country']) : ''; ?></td>
<td>
<?php if (!empty($c['square_authorization_revoked'])): ?>
<span class="pill no">revoked</span>
<?php elseif (!empty($c['square_ready'])): ?>
<span class="pill ok">ready</span>
<?php else: ?>
<span class="pill no">not ready</span>
<?php endif; ?>
</td>
<td>
<span class="pill <?php echo !empty($c['has_access']) ? 'ok' : 'no'; ?>">access</span>
<span class="pill <?php echo !empty($c['has_refresh']) ? 'ok' : 'no'; ?>">refresh</span>
</td>
<td><?php echo h($c['square_connected_at'] ?: '—'); ?></td>
<td><?php echo h($c['square_token_expires_at'] ?: '—'); ?></td>
<td><?php echo $c['square_merchant_key'] ? '<code>' . h($c['square_merchant_key']) . '</code>' : '<span class="muted">—</span>'; ?></td>
<td><?php echo $c['square_status_issues'] ? nl2br(h($c['square_status_issues'])) : '<span class="muted">none</span>'; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<h2>Recent onboarding attempts <span class="count">(<?php echo count($pending); ?>)</span></h2>
<?php if (empty($pending)): ?>
<p class="muted">No onboarding attempts recorded.</p>
<?php else: ?>
<table>
<thead>
<tr>
<th>school_num<br>(record_num)</th>
<th>Status</th>
<th>Merchant id</th>
<th>Created</th>
<th>Connected</th>
<th>Last callback</th>
<th>State</th>
</tr>
</thead>
<tbody>
<?php foreach ($pending as $p): ?>
<tr>
<td><code><?php echo h($p['school_num']); ?></code></td>
<td>
<?php
$st = (string)$p['status'];
$cls = $st === 'connected' ? 'ok' : ($st === 'pending' ? '' : 'no');
echo '<span class="pill ' . $cls . '">' . h($st) . '</span>';
?>
</td>
<td><?php echo $p['merchant_id'] ? '<code>' . h($p['merchant_id']) . '</code>' : '<span class="muted">—</span>'; ?></td>
<td><?php echo ts($p['created_at']); ?></td>
<td><?php echo ts($p['connected_at']); ?></td>
<td><?php echo ts($p['last_callback']); ?></td>
<td><span class="muted"><?php echo h(substr((string)$p['state'], 0, 12)); ?>…</span></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</body>
</html>