123 lines
6.1 KiB
PHP
123 lines
6.1 KiB
PHP
<?php
|
|
/**
|
|
* Seed a Square connection for LOCAL TESTING — bypasses OAuth onboarding so you
|
|
* can test the "Pay with Square" widget + charge (Phase 3/4) without a public
|
|
* URL or the host app.
|
|
*
|
|
* It VALIDATES the access token against Square (GET /v2/locations) and
|
|
* auto-resolves the location + currency + merchant id, so you only need the
|
|
* token. Pass --location to override the auto-pick.
|
|
*
|
|
* Get the token from: Developer Dashboard → open your app → toggle to SANDBOX →
|
|
* Credentials → "Sandbox Access Token" (the EAAA… value). Use the SAME
|
|
* application whose Sandbox Application ID is your client_id in config.php.
|
|
*
|
|
* USAGE (CLI only):
|
|
* php seed_test_connection.php --school="Test School" --token=EAAA...
|
|
* php seed_test_connection.php --school="Test School" --token=EAAA... --location=L123 --currency=CAD
|
|
*
|
|
* Then: php -S 127.0.0.1:8000 and open the URL it prints.
|
|
*
|
|
* ⚠️ Sandbox only. Stored ENCRYPTED with your config token_encryption_key (set
|
|
* one first, or the token is stored in plaintext).
|
|
*/
|
|
|
|
if (PHP_SAPI !== 'cli') { http_response_code(403); echo "CLI only.\n"; exit(1); }
|
|
|
|
require_once __DIR__ . '/lib/SquareClient.php';
|
|
require_once __DIR__ . '/lib/MerchantStore.php';
|
|
$config = require __DIR__ . '/config.php';
|
|
|
|
$opts = getopt('', ['school:', 'token:', 'location::', 'merchant::', 'currency::', 'help']);
|
|
if (isset($opts['help']) || empty($opts['school']) || empty($opts['token'])) {
|
|
fwrite(STDOUT, "Usage: php seed_test_connection.php --school=\"Name\" --token=EAAA... [--location=L123] [--currency=CAD]\n");
|
|
exit(isset($opts['help']) ? 0 : 1);
|
|
}
|
|
if (($config['env'] ?? 'sandbox') !== 'sandbox') {
|
|
fwrite(STDERR, "Refusing to run: config env is not 'sandbox'. This helper is for sandbox testing only.\n");
|
|
exit(1);
|
|
}
|
|
if (empty($config['token_encryption_key'])) {
|
|
fwrite(STDERR, "WARNING: token_encryption_key is not set in config.php — the token will be stored in PLAINTEXT.\n");
|
|
}
|
|
|
|
$school = (string)$opts['school'];
|
|
$token = trim((string)$opts['token']);
|
|
$client = new SquareClient($config);
|
|
|
|
// ── Validate the token + discover locations. ────────────────────────────────
|
|
fwrite(STDOUT, "Checking token against " . $client->getConnectBase() . " …\n");
|
|
$resp = $client->api('GET', '/v2/locations', $token);
|
|
|
|
if ($resp['status'] === 401) {
|
|
fwrite(STDERR, "\n❌ HTTP 401 Unauthorized — Square rejected this access token.\n"
|
|
. " Fix: Developer Dashboard → open your app → toggle to SANDBOX → Credentials →\n"
|
|
. " copy the \"Sandbox Access Token\" (starts with EAAA…). Make sure:\n"
|
|
. " • you copied the SANDBOX token (not Production), and the whole string,\n"
|
|
. " • it's from the SAME app as your client_id (" . $config['client_id'] . "),\n"
|
|
. " • the token hasn't been rotated since you copied it.\n");
|
|
exit(1);
|
|
}
|
|
if ($resp['status'] < 200 || $resp['status'] >= 300) {
|
|
fwrite(STDERR, "\n❌ GET /v2/locations failed (HTTP " . $resp['status'] . "): "
|
|
. json_encode($resp['body']['errors'] ?? $resp['body']) . "\n");
|
|
exit(1);
|
|
}
|
|
|
|
$locations = $resp['body']['locations'] ?? [];
|
|
if (empty($locations)) {
|
|
fwrite(STDERR, "\n❌ Token is valid but the seller has no locations. Create one in the sandbox seller dashboard.\n");
|
|
exit(1);
|
|
}
|
|
|
|
// ── Pick a location. ─────────────────────────────────────────────────────────
|
|
$wanted = isset($opts['location']) ? (string)$opts['location'] : '';
|
|
$chosen = null;
|
|
foreach ($locations as $l) {
|
|
if ($wanted !== '') { if (($l['id'] ?? '') === $wanted) { $chosen = $l; break; } continue; }
|
|
$active = ($l['status'] ?? '') === 'ACTIVE';
|
|
$card = in_array('CREDIT_CARD_PROCESSING', $l['capabilities'] ?? [], true);
|
|
if ($active && $card) { $chosen = $l; break; } // best: active + can take cards
|
|
if ($active && $chosen === null) { $chosen = $l; } // fallback: any active
|
|
}
|
|
if ($chosen === null) $chosen = $locations[0];
|
|
if ($wanted !== '' && ($chosen['id'] ?? '') !== $wanted) {
|
|
fwrite(STDERR, "\n❌ Location '$wanted' not found for this token. Available: "
|
|
. implode(', ', array_map(fn($l) => $l['id'] ?? '?', $locations)) . "\n");
|
|
exit(1);
|
|
}
|
|
|
|
$locationId = $chosen['id'];
|
|
$currency = strtoupper((string)($opts['currency'] ?? ($chosen['currency'] ?? 'CAD')));
|
|
$canCards = in_array('CREDIT_CARD_PROCESSING', $chosen['capabilities'] ?? [], true);
|
|
|
|
// merchant id: --merchant, else GET /v2/merchants, else a placeholder.
|
|
$merchantId = (string)($opts['merchant'] ?? '');
|
|
if ($merchantId === '') {
|
|
$m = $client->api('GET', '/v2/merchants', $token);
|
|
$merchantId = $m['body']['merchant'][0]['id'] ?? 'SANDBOX_TEST_MERCHANT';
|
|
}
|
|
|
|
// ── Store it. ────────────────────────────────────────────────────────────────
|
|
$store = new MerchantStore($config);
|
|
$store->saveConnection($school, $merchantId, $token, '', gmdate('Y-m-d\TH:i:s\Z', time() + 365 * 86400));
|
|
$store->mergeMeta($school, [
|
|
'active_location_id' => $locationId,
|
|
'square_ready' => true,
|
|
'currency' => $currency,
|
|
'seeded_for_testing' => true,
|
|
'status_checked_at' => time(),
|
|
]);
|
|
|
|
fwrite(STDOUT, "\n✅ Seeded connection for \"$school\"\n");
|
|
fwrite(STDOUT, " merchant_id : $merchantId\n");
|
|
fwrite(STDOUT, " location_id : $locationId (" . ($chosen['status'] ?? '?') . ($canCards ? ", card-capable" : ", NO card capability") . ")\n");
|
|
fwrite(STDOUT, " currency : $currency\n");
|
|
fwrite(STDOUT, " token : stored " . (empty($config['token_encryption_key']) ? "(PLAINTEXT)" : "encrypted") . "\n");
|
|
if (!$canCards) {
|
|
fwrite(STDOUT, "\n⚠️ This location has no CREDIT_CARD_PROCESSING capability — card charges may fail.\n");
|
|
}
|
|
fwrite(STDOUT, "\nNext: php -S 127.0.0.1:8000 then open\n");
|
|
fwrite(STDOUT, " http://127.0.0.1:8000/demo-checkout.php?school=" . rawurlencode($school) . "\n");
|
|
exit(0);
|