First Commit
This commit is contained in:
1 parent
5eb9dd037d
commit
2e62c0e60b
11877 files changed
+2035312
No files matched your search
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* Square token-refresh cron job (Phase 2)
|
||||
* ───────────────────────────────────────
|
||||
* Square access tokens expire after 30 days; refresh tokens (code flow) do not
|
||||
* expire. This sweep refreshes every connected school's access token while it
|
||||
* still has plenty of runway, so a token is never allowed to lapse and break
|
||||
* payments.
|
||||
*
|
||||
* It is also self-healing for the web side: helper.php refreshes lazily on use,
|
||||
* but a school that hasn't transacted in a month would otherwise drift toward
|
||||
* expiry — this job covers them.
|
||||
*
|
||||
* USAGE (CLI only — refuses to run over HTTP):
|
||||
* php refresh_tokens.php # refresh tokens expiring within 5 days
|
||||
* php refresh_tokens.php --days=10 # widen the window
|
||||
* php refresh_tokens.php --force # refresh ALL connected schools now
|
||||
* php refresh_tokens.php --dry-run # report what WOULD happen, change nothing
|
||||
* php refresh_tokens.php --school="Name"# limit to one school
|
||||
*
|
||||
* CRON — Square recommends renewing access tokens every 7 days or less, so the
|
||||
* RECOMMENDED schedule is a weekly --force sweep (rotates every token weekly):
|
||||
* 30 3 * * 0 /usr/bin/php /path/to/square/refresh_tokens.php --force >> /var/log/square_refresh.log 2>&1
|
||||
* Optionally add a daily threshold sweep as a safety net against any lapse:
|
||||
* 15 3 * * * /usr/bin/php /path/to/square/refresh_tokens.php --days=7 >> /var/log/square_refresh.log 2>&1
|
||||
*
|
||||
* Exit codes: 0 = all good (or dry-run), 1 = one or more refreshes failed.
|
||||
*/
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
http_response_code(403);
|
||||
header('Content-Type: text/plain');
|
||||
echo "refresh_tokens.php is a CLI script and must not be run over HTTP.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Optional app logging, if these helpers are on the include path. Wrapped so the
|
||||
// script also runs standalone (e.g. from a bare cron environment).
|
||||
if (file_exists(__DIR__ . '/../include/logging-functions.php')) {
|
||||
require_once __DIR__ . '/../include/logging-functions.php';
|
||||
if (function_exists('startLog')) startLog('square-refresh-cron');
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/lib/SquareClient.php';
|
||||
require_once __DIR__ . '/lib/MerchantStore.php';
|
||||
|
||||
$config = require __DIR__ . '/config.php';
|
||||
|
||||
// ── Parse flags ──────────────────────────────────────────────────────────────
|
||||
$opts = getopt('', ['days::', 'force', 'dry-run', 'school::', 'help']);
|
||||
if (isset($opts['help'])) {
|
||||
fwrite(STDOUT, "Usage: php refresh_tokens.php [--days=N] [--force] [--dry-run] [--school=\"Name\"]\n");
|
||||
exit(0);
|
||||
}
|
||||
$thresholdDays = isset($opts['days']) ? max(0, (int)$opts['days']) : 5;
|
||||
$force = isset($opts['force']);
|
||||
$dryRun = isset($opts['dry-run']);
|
||||
$onlySchool = isset($opts['school']) ? (string)$opts['school'] : null;
|
||||
|
||||
$ts = date('c');
|
||||
fwrite(STDOUT, "[$ts] Square token refresh starting"
|
||||
. " (days=$thresholdDays" . ($force ? ", force" : "") . ($dryRun ? ", dry-run" : "") . ($onlySchool ? ", school=\"$onlySchool\"" : "") . ")\n");
|
||||
|
||||
try {
|
||||
$client = new SquareClient($config);
|
||||
$store = new MerchantStore($config);
|
||||
} catch (Throwable $e) {
|
||||
fwrite(STDERR, "FATAL: " . $e->getMessage() . "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// record_num => name. Keyed by the unique record_num so same-named schools are
|
||||
// each refreshed (a name-keyed sweep would refresh only the first of them).
|
||||
$schools = $store->connectedSchoolNums();
|
||||
if ($onlySchool !== null) {
|
||||
$schools = array_filter($schools, fn($name) => $name === $onlySchool);
|
||||
}
|
||||
|
||||
if (empty($schools)) {
|
||||
fwrite(STDOUT, "No connected schools to process.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$counts = ['refreshed' => 0, 'skipped' => 0, 'would_refresh' => 0, 'failed' => 0, 'no_refresh_token' => 0, 'not_connected' => 0];
|
||||
|
||||
foreach ($schools as $schoolNum => $school) {
|
||||
$result = $store->ensureFreshToken($school, $client, [
|
||||
'threshold_days' => $thresholdDays,
|
||||
'force' => $force,
|
||||
'dry_run' => $dryRun,
|
||||
'school_num' => $schoolNum,
|
||||
]);
|
||||
|
||||
$action = $result['action'] ?? 'failed';
|
||||
$counts[$action] = ($counts[$action] ?? 0) + 1;
|
||||
|
||||
$line = sprintf(" %-16s %s (#%d)", $action, $school, $schoolNum);
|
||||
if (!empty($result['expires_at'])) $line .= " (expires {$result['expires_at']})";
|
||||
if (!empty($result['detail'])) $line .= " — {$result['detail']}";
|
||||
fwrite(STDOUT, $line . "\n");
|
||||
}
|
||||
|
||||
$ts = date('c');
|
||||
fwrite(STDOUT, "[$ts] Done. "
|
||||
. "refreshed={$counts['refreshed']} skipped={$counts['skipped']} "
|
||||
. "would_refresh={$counts['would_refresh']} failed={$counts['failed']} "
|
||||
. "no_refresh_token={$counts['no_refresh_token']} not_connected={$counts['not_connected']}\n");
|
||||
|
||||
// Non-zero exit if anything failed, so cron / monitoring can alert.
|
||||
exit($counts['failed'] > 0 ? 1 : 0);
|
||||
Reference in new issue
Block a user