152 lines
6.0 KiB
PHP
152 lines
6.0 KiB
PHP
<?php
|
|
$config = require __DIR__ . '/config.php';
|
|
require __DIR__ . '/lib/PayPalClient.php';
|
|
require __DIR__ . '/lib/Storage.php';
|
|
|
|
$school = $_GET['school'] ?? '';
|
|
$track = $_GET['track'] ?? '';
|
|
$pref = $_GET['partner_referral_id'] ?? null;
|
|
|
|
if (!$school || !$track) {
|
|
header('Location: ' . rtrim($config['base_url'], '/') . '/index.php');
|
|
exit;
|
|
}
|
|
|
|
$client = new PayPalClient($config);
|
|
$storage = new Storage($config['storage_path']);
|
|
|
|
// First, PayPal may append merchantId to the return URL on successful onboarding
|
|
$merchantId = $_GET['merchantId']
|
|
?? $_GET['merchant_id']
|
|
?? $_GET['merchantIdInPayPal']
|
|
?? null;
|
|
|
|
// If not provided, use Partner Lookup API to retrieve merchant info by tracking_id.
|
|
// Docs: GET /v1/customer/partners/<partner_merchant_id>/merchant-integrations?tracking_id=
|
|
if (!$merchantId) {
|
|
$merchantId = null;
|
|
$lastResp = null;
|
|
// poll up to ~15s total
|
|
for ($i = 0; $i < 15; $i++) {
|
|
$path = '/v1/customer/partners/' . urlencode($config['partner_merchant_id']) . '/merchant-integrations?tracking_id=' . urlencode($track);
|
|
$resp = $client->api('GET', $path);
|
|
$lastResp = $resp;
|
|
if ($resp['status'] >= 200 && $resp['status'] < 300) {
|
|
if (!empty($resp['body']['merchant_id'])) {
|
|
$foundId = $resp['body']['merchant_id'];
|
|
// CRITICAL: Don't accept the partner's own ID as merchant ID
|
|
// This means the merchant hasn't completed onboarding yet
|
|
if (strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
|
|
$merchantId = $foundId;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
usleep(1000000);
|
|
}
|
|
if (!$merchantId) {
|
|
$debug = $storage->get('return_debug', []);
|
|
$debug[] = [
|
|
'ts' => time(),
|
|
'query' => $_GET,
|
|
'last_status' => $lastResp['status'] ?? null,
|
|
'last_body' => $lastResp['body'] ?? null,
|
|
'track' => $track,
|
|
'school' => $school,
|
|
'reason' => 'Merchant onboarding not completed or partner ID returned'
|
|
];
|
|
$storage->set('return_debug', $debug);
|
|
}
|
|
}
|
|
|
|
// Fallback by partner_referral_id if available and still missing
|
|
if (!$merchantId) {
|
|
// Load pending entry for additional clues
|
|
$pending = $storage->get('pending_onboarding', []);
|
|
$entry = $pending[$track] ?? [];
|
|
$prefFromStore = $entry['partner_referral_id'] ?? $entry['referral_token'] ?? null;
|
|
$pref = $pref ?: $prefFromStore;
|
|
}
|
|
|
|
if (!$merchantId && $pref) {
|
|
// Try list integrations and filter by partner_referral_id
|
|
$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 && !empty($resp['body']['merchant_integrations'])) {
|
|
foreach ($resp['body']['merchant_integrations'] as $mi) {
|
|
$rid = $mi['partner_referral_id'] ?? ($mi['referral_id'] ?? null);
|
|
$foundId = $mi['merchant_id'] ?? null;
|
|
// Only accept if it's a different merchant ID (not the partner's own)
|
|
if ($rid && strcasecmp($rid, $pref) === 0 && $foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
|
|
$merchantId = $foundId;
|
|
break;
|
|
}
|
|
}
|
|
// If still not found, but the list contains exactly one new integration with our tracking id, take it
|
|
if (!$merchantId) {
|
|
foreach ($resp['body']['merchant_integrations'] as $mi) {
|
|
$foundId = $mi['merchant_id'] ?? null;
|
|
if (!empty($mi['tracking_id']) && strcasecmp($mi['tracking_id'], $track) === 0 && $foundId && strcasecmp($foundId, $config['partner_merchant_id']) !== 0) {
|
|
$merchantId = $foundId;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// As a last resort, if merchants.json already contains the school mapping, use it
|
|
if (!$merchantId) {
|
|
$merchants = $storage->get('merchants', []);
|
|
if (!empty($merchants[$school]['merchant_id'])) {
|
|
$existingId = $merchants[$school]['merchant_id'];
|
|
// Only use if it's not the partner's own ID
|
|
if (strcasecmp($existingId, $config['partner_merchant_id']) !== 0) {
|
|
$merchantId = $existingId;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$merchantId) {
|
|
// Mark as incomplete onboarding
|
|
$pending = $storage->get('pending_onboarding', []);
|
|
if (isset($pending[$track])) {
|
|
$pending[$track]['status'] = 'incomplete_onboarding';
|
|
$pending[$track]['last_return_at'] = time();
|
|
$pending[$track]['note'] = 'Merchant did not complete PayPal onboarding. Partner ID was returned instead.';
|
|
$storage->set('pending_onboarding', $pending);
|
|
}
|
|
|
|
// Not available yet; merchant needs to complete onboarding
|
|
header('Location: ' . rtrim($config['base_url'], '/') . '/index.php?onboard=incomplete');
|
|
exit;
|
|
}
|
|
|
|
// Persist school -> merchant mapping
|
|
$merchants = $storage->get('merchants', []);
|
|
$merchants[$school] = [
|
|
'merchant_id' => $merchantId,
|
|
'tracking_id' => $track,
|
|
'connected_at' => time(),
|
|
];
|
|
$storage->set('merchants', $merchants);
|
|
|
|
// Also annotate the matching pending record so it's visible where you looked
|
|
$pending = $storage->get('pending_onboarding', []);
|
|
if (!isset($pending[$track])) {
|
|
// if for some reason it's missing, recreate a minimal record
|
|
$pending[$track] = [
|
|
'school' => $school,
|
|
'created_at' => time(),
|
|
];
|
|
}
|
|
$pending[$track]['merchant_id'] = $merchantId;
|
|
$pending[$track]['status'] = 'connected';
|
|
$pending[$track]['connected_at'] = time();
|
|
// Store the raw return query for troubleshooting
|
|
$pending[$track]['last_return_query'] = $_GET;
|
|
$storage->set('pending_onboarding', $pending);
|
|
|
|
header('Location: ' . rtrim($config['base_url'], '/') . '/index.php?onboard=ok');
|
|
exit;
|