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

800 lines
36 KiB
PHP

<?php
ini_set('display_errors', 0);
$ValidAccess = 2010;
// require_once("../include/globals.php");
$cdir = "/m/data1/app.hotlunches.net";
$AppDir = $cdir;
ob_start();
require_once("../include/logging-functions.php");
startLog("includes");
require_once("../include/date-time.php");
require_once("../include/config.php");
require_once("../include/school-functions.php");
require_once("../include/database.php");
require_once("../include/session.php");
$sessionName = 'hotlunch';
$sessionTimeout = 60 * 30;
$cookiePath = '/';
if (session_status() !== PHP_SESSION_ACTIVE) {
session_name($sessionName);
session_start();
}
// ── Diagnostic endpoint ──────────────────────────────────────────────────
// Visit index.php?debug=1 in a browser to see exactly where data is stored,
// whether it is writable, and what is currently saved. No login required.
if (isset($_GET['debug'])) {
header('Content-Type: text/plain');
$dataDir = __DIR__ . '/data';
$merchantsFile = $dataDir . '/school_merchants.json';
$pendingFile = $dataDir . '/pending_onboarding.json';
echo "=== index.php diagnostics ===\n";
echo "__DIR__ : " . __DIR__ . "\n";
echo "realpath(__DIR__) : " . realpath(__DIR__) . "\n";
echo "SCRIPT_FILENAME : " . ($_SERVER['SCRIPT_FILENAME'] ?? 'n/a') . "\n";
echo "dataDir : $dataDir\n";
echo "dataDir realpath : " . (realpath($dataDir) ?: '(does not exist)') . "\n";
echo "dataDir is_dir : " . (is_dir($dataDir) ? 'yes' : 'NO') . "\n";
echo "dataDir is_writable : " . (is_writable($dataDir) ? 'yes' : 'NO') . "\n";
$probe = $dataDir . '/_write_probe.txt';
$ok = @file_put_contents($probe, 'probe ' . date('c'));
echo "test write : " . ($ok === false ? 'FAILED' : "ok ($ok bytes)") . "\n";
if ($ok !== false) @unlink($probe);
echo "\n--- school_merchants.json (" . (file_exists($merchantsFile) ? filesize($merchantsFile) . ' bytes' : 'MISSING') . ") ---\n";
echo file_exists($merchantsFile) ? file_get_contents($merchantsFile) : '(none)';
echo "\n\n--- pending_onboarding.json (" . (file_exists($pendingFile) ? filesize($pendingFile) . ' bytes' : 'MISSING') . ") ---\n";
echo file_exists($pendingFile) ? file_get_contents($pendingFile) : '(none)';
exit();
}
// PayPal redirects the merchant's browser back to this URL when onboarding
// finishes. That is an external, top-level return from paypal.com — the app
// session is often no longer active by then, so the auth gate below must NOT
// apply to it or the callback never runs and the merchant is never saved.
// The callback is authenticated instead by the tracking_id we generated.
$isPayPalCallback = isset($_GET['callback']) && $_GET['callback'] == '1';
if (!$isPayPalCallback) {
if (SessionActive() != 1 || ! UserLoggedIn()) {
header("Location: {$FormTarget}");
exit();
}
$AllowedUserTypes = array('SuperUser','System Administrator');
if (checkPermission($AllowedUserTypes) != 1) {
$error_message = getLanguageText('ERRORMSG-ACCESS-DENIED');
writeLog($error_message);
$naction = '';
header("Location: {$FormTarget}?action=$action&naction=$naction$headerArgs&form_message=" . urlencode($form_message) . "&error_message=" . urlencode($error_message));
exit();
}
}
$buffer = ob_get_contents();
if (strlen($buffer)) writeLog("buffer: $buffer");
ob_end_clean();
writeLog("Header complete");
startLog("pp-integration");
// Database connection helper
function getDbConnection() {
writeLog("getDbConnection: ".__DIR__ . '/config.php');
$dbConfig = require __DIR__ . '/db_config.php';
$conn = new mysqli(
$dbConfig['host'],
$dbConfig['username'],
$dbConfig['password'],
$dbConfig['database']
);
if ($conn->connect_error) {
throw new Exception('Database connection failed: ' . $conn->connect_error);
}
$conn->set_charset($dbConfig['charset']);
return $conn;
}
// Handle AJAX requests
if (isset($_POST['action'])) writeLog("POST action: ".$_POST['action']);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
header('Content-Type: application/json');
writeLog(__DIR__ . '/config.php');
$config = require __DIR__ . '/config.php';
require __DIR__ . '/lib/PayPalClient.php';
$dataDir = __DIR__ . '/data';
writeLog("dataDir: $dataDir");
if (!is_dir($dataDir)) {
mkdir($dataDir, 0755, true);
}
try {
writeLog("new PayPalClient");
$client = new PayPalClient($config);
writeLog("new PayPalClient - complete");
// ACTION: Create connection and redirect to PayPal
writeLog($_POST['action']);
if ($_POST['action'] === 'connect') {
$schoolName = trim($_POST['school_name'] ?? '');
if ($schoolName === '') {
echo json_encode(['success' => false, 'error' => 'School name required']);
exit();
}
writeLog("schoolName: $schoolName");
$trackingId = 'school_' . substr(md5($schoolName . time()), 0, 10);
writeLog("trackingId: $trackingId");
// base_url already resolves to the directory this script is served from
// (auto-detected from SCRIPT_NAME). Don't append a hardcoded subdir like
// '/sm' or the return URL points to a path that doesn't exist (404).
$baseUrl = rtrim($config['base_url'], '/');
writeLog("baseUrl: $baseUrl");
$returnUrl = $baseUrl . '/index.php?callback=1&school=' . urlencode($schoolName) . '&track=' . urlencode($trackingId);
writeLog("returnUrl: $returnUrl");
$payload = [
'tracking_id' => $trackingId,
'operations' => [[
'operation' => 'API_INTEGRATION',
'api_integration_preference' => [
'rest_api_integration' => [
'integration_method' => 'PAYPAL',
'integration_type' => 'THIRD_PARTY',
'third_party_details' => ['features' => ['PAYMENT', 'REFUND', 'VAULT', 'BILLING_AGREEMENT']]
]
]
]],
'products' => ['PPCP', 'PAYMENT_METHODS', 'ADVANCED_VAULTING'],
'capabilities' => ['APPLE_PAY', 'GOOGLE_PAY', 'PAYPAL_WALLET_VAULTING_ADVANCED'],
'legal_consents' => [['type' => 'SHARE_DATA_CONSENT', 'granted' => true]],
'partner_config_override' => [
'return_url' => $returnUrl,
'return_url_description' => 'Return to School Setup',
'show_add_credit_card' => false
]
];
writeLog("api call");
$resp = $client->api('POST', '/v2/customer/partner-referrals', $payload);
writeLog('Partner referral status: ' . $resp['status']);
writeLog('Partner referral body: ' . json_encode($resp['body']));
writeLog("api call - complete");
writeLog("resp status: ".$resp['status']);
if ($resp['status'] < 200 || $resp['status'] >= 300) {
echo json_encode(['success' => false, 'error' => 'Partner referral failed']);
exit();
}
$body = $resp['body'];
writeLog("body partner_referral_id: ".$body['partner_referral_id']);
$partnerReferralId = $body['partner_referral_id'] ?? $body['referral_id'] ?? null;
$actionUrl = null;
foreach ($body['links'] ?? [] as $link) {
if (($link['rel'] ?? '') === 'action_url') {
$actionUrl = $link['href'];
break;
}
}
writeLog("actionUrl: $actionUrl");
if (!$actionUrl) {
writeLog("No onboarding URL from PayPal");
echo json_encode(['success' => false, 'error' => 'No onboarding URL from PayPal']);
exit();
}
$pendingFile = $dataDir . '/pending_onboarding.json';
writeLog("pendingFile: $pendingFile");
$pending = file_exists($pendingFile) ? json_decode(file_get_contents($pendingFile), true) ?: [] : [];
$pending[$trackingId] = [
'school' => $schoolName,
'created_at' => time(),
'partner_referral_id' => $partnerReferralId,
'status' => 'pending'
];
file_put_contents($pendingFile, json_encode($pending, JSON_PRETTY_PRINT));
writeLog("json_encode call");
echo json_encode(['success' => true, 'redirect' => $actionUrl]);
exit();
writeLog("still going...");
}
// ACTION: Sync pending merchants
if ($_POST['action'] === 'sync') {
$pendingFile = $dataDir . '/pending_onboarding.json';
if (!file_exists($pendingFile)) {
echo json_encode(['success' => false, 'error' => 'No pending schools']);
exit();
}
$pending = json_decode(file_get_contents($pendingFile), true) ?: [];
$synced = 0;
$db = getDbConnection();
foreach ($pending as $trackingId => $data) {
if (!in_array($data['status'] ?? '', ['pending', 'waiting_for_paypal', 'incomplete_onboarding'])) {
continue;
}
$school = $data['school'] ?? 'Unknown';
$partnerReferralId = $data['partner_referral_id'] ?? null;
$merchantId = null;
// Try 3 approaches
$path = '/v1/customer/partners/' . urlencode($config['partner_merchant_id'])
. '/merchant-integrations?tracking_id=' . urlencode($trackingId);
$resp = $client->api('GET', $path);
if ($resp['status'] >= 200 && $resp['status'] < 300) {
$foundId = $resp['body']['merchant_id'] ?? null;
if ($foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
$merchantId = $foundId;
}
if (!$merchantId) {
foreach ($resp['body']['merchant_integrations'] ?? [] as $integration) {
$foundId = $integration['merchant_id'] ?? null;
if ($foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
$merchantId = $foundId;
break;
}
}
}
}
if (!$merchantId && $partnerReferralId) {
$path = '/v1/customer/partners/' . urlencode($config['partner_merchant_id'])
. '/merchant-integrations?onboarded=true';
$resp = $client->api('GET', $path);
if ($resp['status'] >= 200 && $resp['status'] < 300) {
foreach ($resp['body']['merchant_integrations'] ?? [] as $integration) {
$refId = $integration['partner_referral_id'] ?? $integration['referral_id'] ?? null;
$foundId = $integration['merchant_id'] ?? null;
if ($refId && strcasecmp($refId, $partnerReferralId) === 0
&& $foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
$merchantId = $foundId;
break;
}
}
}
}
if (!$merchantId) {
$path = '/v1/customer/partners/' . urlencode($config['partner_merchant_id'])
. '/merchant-integrations?page_size=200';
$resp = $client->api('GET', $path);
if ($resp['status'] >= 200 && $resp['status'] < 300) {
foreach ($resp['body']['merchant_integrations'] ?? [] as $integration) {
$tid = $integration['tracking_id'] ?? null;
$refId = $integration['partner_referral_id'] ?? $integration['referral_id'] ?? null;
$foundId = $integration['merchant_id'] ?? null;
if ((($tid && strcasecmp($tid, $trackingId) === 0)
|| ($partnerReferralId && $refId && strcasecmp($refId, $partnerReferralId) === 0))
&& $foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
$merchantId = $foundId;
break;
}
}
}
}
if ($merchantId) {
// Save to database. Match by school_name — $school_num is not
// defined in this sync scope; the callback path keys on school_name
// too, so this keeps both write paths consistent.
$stmt = $db->prepare("UPDATE schools SET paypal_merchant_id = ?, paypal_connected_at = NOW(), paypal_tracking_id = ? WHERE school_name = ?");
$stmt->bind_param('sss', $merchantId, $trackingId, $school);
$stmt->execute();
$stmt->close();
// Check seller status
$sellerStatus = $client->getSellerStatus($config['partner_merchant_id'], $merchantId);
writeLog('Seller status [sync] ' . $school . ': paypal_ready=' . ($sellerStatus['paypal_ready'] ? 'true' : 'false') . ' debug_id=' . ($sellerStatus['debug_id'] ?? 'n/a'));
if (!empty($sellerStatus['issues'])) {
writeLog('Seller status issues [sync] ' . $school . ': ' . implode(' | ', $sellerStatus['issues']));
}
// Also keep in JSON as backup
$merchantsFile = $dataDir . '/school_merchants.json';
$merchants = file_exists($merchantsFile) ? json_decode(file_get_contents($merchantsFile), true) ?: [] : [];
$merchants[$school] = [
'merchant_id' => $merchantId,
'tracking_id' => $trackingId,
'connected_at' => time(),
'paypal_ready' => $sellerStatus['paypal_ready'],
'email_confirmed' => $sellerStatus['email_confirmed'],
'payments_receivable' => $sellerStatus['payments_receivable'],
'oauth_ok' => $sellerStatus['oauth_ok'],
'seller_email' => $sellerStatus['seller_email'],
'status_issues' => $sellerStatus['issues'],
'status_checked_at' => time(),
];
file_put_contents($merchantsFile, json_encode($merchants, JSON_PRETTY_PRINT));
$pending[$trackingId]['merchant_id'] = $merchantId;
$pending[$trackingId]['status'] = 'connected';
$pending[$trackingId]['connected_at'] = time();
$pending[$trackingId]['paypal_ready'] = $sellerStatus['paypal_ready'];
$synced++;
}
}
$db->close();
file_put_contents($pendingFile, json_encode($pending, JSON_PRETTY_PRINT));
echo json_encode([
'success' => true,
'synced' => $synced,
'message' => $synced > 0 ? "Connected $synced school(s)" : 'No merchant IDs found yet'
]);
exit();
}
// ACTION: Re-check seller status for a specific school
if ($_POST['action'] === 'check_status') {
$schoolName = trim($_POST['school_name'] ?? '');
if ($schoolName === '') {
echo json_encode(['success' => false, 'error' => 'School name required']);
exit();
}
$merchantsFile = $dataDir . '/school_merchants.json';
if (!file_exists($merchantsFile)) {
echo json_encode(['success' => false, 'error' => 'No merchant data found']);
exit();
}
$merchants = json_decode(file_get_contents($merchantsFile), true) ?: [];
$merchantId = $merchants[$schoolName]['merchant_id'] ?? null;
if (!$merchantId) {
echo json_encode(['success' => false, 'error' => 'School not found or not yet connected']);
exit();
}
$sellerStatus = $client->getSellerStatus($config['partner_merchant_id'], $merchantId);
writeLog('Seller status [manual] ' . $schoolName . ': paypal_ready=' . ($sellerStatus['paypal_ready'] ? 'true' : 'false') . ' debug_id=' . ($sellerStatus['debug_id'] ?? 'n/a'));
if (!empty($sellerStatus['issues'])) {
writeLog('Seller status issues [manual] ' . $schoolName . ': ' . implode(' | ', $sellerStatus['issues']));
}
$merchants[$schoolName]['paypal_ready'] = $sellerStatus['paypal_ready'];
$merchants[$schoolName]['email_confirmed'] = $sellerStatus['email_confirmed'];
$merchants[$schoolName]['payments_receivable'] = $sellerStatus['payments_receivable'];
$merchants[$schoolName]['oauth_ok'] = $sellerStatus['oauth_ok'];
$merchants[$schoolName]['seller_email'] = $sellerStatus['seller_email'];
$merchants[$schoolName]['status_issues'] = $sellerStatus['issues'];
$merchants[$schoolName]['status_checked_at'] = time();
file_put_contents($merchantsFile, json_encode($merchants, JSON_PRETTY_PRINT));
echo json_encode([
'success' => true,
'paypal_ready' => $sellerStatus['paypal_ready'],
'email_confirmed' => $sellerStatus['email_confirmed'],
'payments_receivable' => $sellerStatus['payments_receivable'],
'oauth_ok' => $sellerStatus['oauth_ok'],
'seller_email' => $sellerStatus['seller_email'],
'issues' => $sellerStatus['issues'],
]);
exit();
}
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
exit();
}
}
// Handle PayPal callback
if (isset($_GET['callback']) && $_GET['callback'] == '1') {
writeLog('PayPal callback received: ' . ($_SERVER['QUERY_STRING'] ?? ''));
$config = require __DIR__ . '/config.php';
require __DIR__ . '/lib/PayPalClient.php';
$school = $_GET['school'] ?? '';
$trackingId = $_GET['track'] ?? '';
// NOTE: PayPal's "merchantId" return param is OUR tracking_id, not the PayPal
// merchant ID. The real PayPal merchant ID is "merchantIdInPayPal".
$merchantIdFromUrl = $_GET['merchantIdInPayPal'] ?? $_GET['merchant_id'] ?? null;
writeLog("callback parsed: school=$school track=$trackingId merchantIdInPayPal=" . ($merchantIdFromUrl ?? 'null'));
$dataDir = __DIR__ . '/data';
if (!is_dir($dataDir)) {
@mkdir($dataDir, 0755, true);
writeLog("created dataDir: $dataDir");
}
if (!is_writable($dataDir)) {
writeLog("WARNING: dataDir not writable: $dataDir");
}
if ($school && $trackingId) {
try {
$client = new PayPalClient($config);
$merchantId = $merchantIdFromUrl;
$pendingFile = $dataDir . '/pending_onboarding.json';
$partnerReferralId = null;
if (file_exists($pendingFile)) {
$pending = json_decode(file_get_contents($pendingFile), true) ?: [];
$partnerReferralId = $pending[$trackingId]['partner_referral_id'] ?? null;
}
if (!$merchantId) {
for ($i = 0; $i < 15; $i++) {
$path = '/v1/customer/partners/' . urlencode($config['partner_merchant_id'])
. '/merchant-integrations?tracking_id=' . urlencode($trackingId);
$resp = $client->api('GET', $path);
if ($resp['status'] >= 200 && $resp['status'] < 300) {
$foundId = $resp['body']['merchant_id'] ?? null;
if ($foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
$merchantId = $foundId;
break;
}
foreach ($resp['body']['merchant_integrations'] ?? [] as $integration) {
$foundId = $integration['merchant_id'] ?? null;
if ($foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
$merchantId = $foundId;
break 2;
}
}
}
if (!$merchantId && $partnerReferralId) {
$path = '/v1/customer/partners/' . urlencode($config['partner_merchant_id'])
. '/merchant-integrations?onboarded=true';
$resp = $client->api('GET', $path);
if ($resp['status'] >= 200 && $resp['status'] < 300) {
foreach ($resp['body']['merchant_integrations'] ?? [] as $integration) {
$refId = $integration['partner_referral_id'] ?? $integration['referral_id'] ?? null;
$foundId = $integration['merchant_id'] ?? null;
if ($refId && strcasecmp($refId, $partnerReferralId) === 0
&& $foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
$merchantId = $foundId;
break 2;
}
}
}
}
if (!$merchantId) {
$path = '/v1/customer/partners/' . urlencode($config['partner_merchant_id'])
. '/merchant-integrations?page_size=200';
$resp = $client->api('GET', $path);
if ($resp['status'] >= 200 && $resp['status'] < 300) {
foreach ($resp['body']['merchant_integrations'] ?? [] as $integration) {
$tid = $integration['tracking_id'] ?? null;
$refId = $integration['partner_referral_id'] ?? $integration['referral_id'] ?? null;
$foundId = $integration['merchant_id'] ?? null;
if ((($tid && strcasecmp($tid, $trackingId) === 0)
|| ($partnerReferralId && $refId && strcasecmp($refId, $partnerReferralId) === 0))
&& $foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
$merchantId = $foundId;
break 2;
}
}
}
}
if ($i < 14) sleep(1);
}
}
if ($merchantId) {
// Persist the merchant ID immediately. We already have it from the
// callback URL, so the connection MUST be recorded (and the dashboard
// updated) even if the follow-up seller-status check fails or PayPal
// hasn't finished propagating the new integration yet.
$merchantsFile = $dataDir . '/school_merchants.json';
$merchants = file_exists($merchantsFile) ? json_decode(file_get_contents($merchantsFile), true) ?: [] : [];
$merchants[$school] = array_merge($merchants[$school] ?? [], [
'merchant_id' => $merchantId,
'tracking_id' => $trackingId,
'connected_at' => time(),
]);
$written = file_put_contents($merchantsFile, json_encode($merchants, JSON_PRETTY_PRINT));
writeLog('Merchant saved [callback] ' . $school . ' => ' . $merchantId
. ' (' . ($written === false ? 'WRITE FAILED to ' . $merchantsFile : $written . ' bytes to ' . $merchantsFile) . ')');
// If we can't persist, do NOT show a fake success page — surface the
// real problem (almost always: the data/ directory is not writable
// by the web-server user) so it can be fixed.
if ($written === false) {
$writable = is_writable($dataDir) ? 'writable' : 'NOT writable';
header('Location: index.php?status=error&msg=' . urlencode(
'Could not save merchant data. data/ directory (' . $dataDir . ') is ' . $writable
. '. Fix filesystem permissions so PHP can write to it.'));
exit();
}
if (file_exists($pendingFile)) {
$pending = json_decode(file_get_contents($pendingFile), true) ?: [];
if (isset($pending[$trackingId])) {
$pending[$trackingId]['merchant_id'] = $merchantId;
$pending[$trackingId]['status'] = 'connected';
$pending[$trackingId]['connected_at'] = time();
file_put_contents($pendingFile, json_encode($pending, JSON_PRETTY_PRINT));
}
}
// Save to database (non-fatal — a DB failure must not lose the JSON record)
try {
$db = getDbConnection();
$stmt = $db->prepare("UPDATE schools SET paypal_merchant_id = ?, paypal_connected_at = NOW(), paypal_tracking_id = ? WHERE school_name = ?");
$stmt->bind_param('sss', $merchantId, $trackingId, $school);
$stmt->execute();
writeLog('DB update [callback] ' . $school . ': affected_rows=' . $stmt->affected_rows);
$stmt->close();
$db->close();
} catch (Exception $e) {
writeLog('DB Error [callback]: ' . $e->getMessage());
}
// Enrich with seller status — guarded so a status-check failure
// never discards the connection we just saved above.
try {
$sellerStatus = $client->getSellerStatus($config['partner_merchant_id'], $merchantId);
writeLog('Seller status [callback] ' . $school . ': paypal_ready=' . ($sellerStatus['paypal_ready'] ? 'true' : 'false') . ' debug_id=' . ($sellerStatus['debug_id'] ?? 'n/a'));
if (!empty($sellerStatus['issues'])) {
writeLog('Seller status issues [callback] ' . $school . ': ' . implode(' | ', $sellerStatus['issues']));
}
$merchants[$school] = array_merge($merchants[$school], [
'paypal_ready' => $sellerStatus['paypal_ready'],
'email_confirmed' => $sellerStatus['email_confirmed'],
'payments_receivable' => $sellerStatus['payments_receivable'],
'oauth_ok' => $sellerStatus['oauth_ok'],
'seller_email' => $sellerStatus['seller_email'],
'status_issues' => $sellerStatus['issues'],
'status_checked_at' => time(),
]);
file_put_contents($merchantsFile, json_encode($merchants, JSON_PRETTY_PRINT));
if (file_exists($pendingFile)) {
$pending = json_decode(file_get_contents($pendingFile), true) ?: [];
if (isset($pending[$trackingId])) {
$pending[$trackingId]['paypal_ready'] = $sellerStatus['paypal_ready'];
file_put_contents($pendingFile, json_encode($pending, JSON_PRETTY_PRINT));
}
}
} catch (Exception $e) {
writeLog('Seller status check failed [callback] ' . $school . ': ' . $e->getMessage());
}
header('Location: index.php?success=1&school=' . urlencode($school));
exit();
} else {
if (file_exists($pendingFile)) {
$pending = json_decode(file_get_contents($pendingFile), true) ?: [];
if (isset($pending[$trackingId])) {
$pending[$trackingId]['status'] = 'incomplete_onboarding';
$pending[$trackingId]['last_callback'] = time();
file_put_contents($pendingFile, json_encode($pending, JSON_PRETTY_PRINT));
}
}
header('Location: index.php?status=error&msg=' . urlencode('Please complete PayPal onboarding or sync later'));
exit();
}
} catch (Exception $e) {
header('Location: index.php?status=error&msg=' . urlencode($e->getMessage()));
exit();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>School Merchant ID Setup</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
padding: 40px;
max-width: 500px;
width: 100%;
}
h1 { color: #333; margin-bottom: 10px; font-size: 28px; }
.subtitle { color: #666; margin-bottom: 30px; font-size: 14px; }
.form-group { margin-bottom: 20px; }
label { display: block; margin-bottom: 8px; color: #555; font-weight: 500; }
input[type="text"] {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 6px;
font-size: 16px;
}
input[type="text"]:focus { outline: none; border-color: #667eea; }
.btn {
width: 100%;
padding: 14px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
}
.btn:hover { transform: translateY(-2px); box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3); }
.btn:disabled { background: #ccc; cursor: not-allowed; }
.btn.success { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); }
.alert {
padding: 12px;
border-radius: 6px;
margin-bottom: 20px;
font-size: 14px;
}
.alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.alert-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.success-page {
text-align: center;
padding: 20px;
}
.success-icon {
font-size: 64px;
color: #28a745;
margin-bottom: 20px;
}
.success-page h2 {
color: #28a745;
margin-bottom: 15px;
font-size: 24px;
}
.success-page p {
color: #666;
margin-bottom: 10px;
line-height: 1.6;
}
.school-name {
font-weight: bold;
color: #667eea;
font-size: 18px;
}
.sync-section {
margin-top: 30px;
padding-top: 30px;
border-top: 2px solid #f0f0f0;
}
</style>
</head>
<body>
<div class="container">
<?php if (isset($_GET['success']) && $_GET['success'] == '1'): ?>
<!-- Success Page -->
<div class="success-page">
<div class="success-icon">✓</div>
<h2>Welcome!</h2>
<p>You have successfully connected your PayPal account.</p>
<p class="school-name"><?php echo htmlspecialchars($_GET['school'] ?? 'Your school'); ?></p>
<p style="margin-top: 20px; font-size: 13px;">Your merchant ID has been saved to the database.</p>
<button onclick="window.location.href='index.php'" class="btn success" style="margin-top: 30px;">
← Back to Home
</button>
</div>
<?php else: ?>
<!-- Main Form -->
<h1>🏫 School Merchant Setup</h1>
<p class="subtitle">Connect your school's PayPal account</p>
<?php
$message = '';
$messageType = '';
if (isset($_GET['status'])) {
if ($_GET['status'] === 'error') {
$message = $_GET['msg'] ?? 'An error occurred. Please try again.';
$messageType = 'error';
}
}
if ($message): ?>
<div class="alert alert-<?php echo htmlspecialchars($messageType); ?>">
<?php echo htmlspecialchars($message); ?>
</div>
<?php endif; ?>
<form id="connectForm">
<div class="form-group">
<label for="school_name"><?php displayLanguageText('TEXT-SCHOOL-NAME'); ?></label>
<input type="text" id="school_name" name="school_name" placeholder="Enter school name" value="<?php echo($school_record['school_name']) ?>" required>
</div>
<button type="submit" class="btn" id="connectBtn">Connect PayPal Account</button>
</form>
<?php endif; ?>
</div>
<script>
// Connect form handler (the form is absent on the success page)
var connectForm = document.getElementById('connectForm');
if (connectForm) connectForm.addEventListener('submit', function(e) {
e.preventDefault();
const btn = document.getElementById('connectBtn');
const schoolName = document.getElementById('school_name').value.trim();
if (!schoolName) {
alert('Please enter a school name');
return;
}
btn.disabled = true;
btn.textContent = 'Connecting...';
fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'action=connect&school_name=' + encodeURIComponent(schoolName)
})
.then(r => r.json())
.then(data => {
if (data.success && data.redirect) {
window.location.href = data.redirect;
} else {
alert('Error: ' + (data.error || 'Connection failed'));
btn.disabled = false;
btn.textContent = 'Connect PayPal Account';
}
})
.catch(err => {
alert('Error: ' + err.message);
btn.disabled = false;
btn.textContent = 'Connect PayPal Account';
});
});
</script>
</body>
</html>