@easypayment/medusa-payment-paypal 0.9.18 → 0.9.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +287 -118
- package/.medusa/server/src/admin/index.mjs +287 -118
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts.map +1 -1
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js +386 -127
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js.map +1 -1
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.d.ts.map +1 -1
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js +4 -0
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js.map +1 -1
- package/.medusa/server/src/modules/paypal/service.d.ts +1 -0
- package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/service.js +5 -1
- package/.medusa/server/src/modules/paypal/service.js.map +1 -1
- package/package.json +1 -1
- package/src/admin/routes/settings/paypal/connection/page.tsx +419 -131
- package/src/api/admin/paypal/onboarding-link/route.ts +6 -0
- package/src/modules/paypal/service.ts +7 -2
|
@@ -308,6 +308,12 @@ function AdvancedCardPaymentsTab() {
|
|
|
308
308
|
)
|
|
309
309
|
] }) });
|
|
310
310
|
}
|
|
311
|
+
function PayPalApplePayPage() {
|
|
312
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
313
|
+
}
|
|
314
|
+
function PayPalGooglePayPage() {
|
|
315
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
316
|
+
}
|
|
311
317
|
const config = adminSdk.defineRouteConfig({
|
|
312
318
|
label: "PayPal Connection"
|
|
313
319
|
});
|
|
@@ -316,37 +322,97 @@ const PARTNER_JS_URLS = {
|
|
|
316
322
|
live: "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"
|
|
317
323
|
};
|
|
318
324
|
const SERVICE_URL = "/admin/paypal/onboarding-link";
|
|
319
|
-
const
|
|
325
|
+
const POPUP_NAME = "PPMiniBrowser";
|
|
326
|
+
const CACHE_PREFIX = "pp_onboard_cache";
|
|
320
327
|
const CACHE_EXPIRY = 10 * 60 * 1e3;
|
|
321
328
|
const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete";
|
|
322
329
|
const STATUS_ENDPOINT = "/admin/paypal/status";
|
|
323
330
|
const SAVE_CREDENTIALS_ENDPOINT = "/admin/paypal/save-credentials";
|
|
324
331
|
const DISCONNECT_ENDPOINT = "/admin/paypal/disconnect";
|
|
325
|
-
|
|
326
|
-
|
|
332
|
+
const ENVIRONMENT_ENDPOINT = "/admin/paypal/environment";
|
|
333
|
+
const cacheKeyFor = (env) => `${CACHE_PREFIX}_${env}`;
|
|
334
|
+
function readCachedUrl(env) {
|
|
335
|
+
if (typeof window === "undefined") return null;
|
|
327
336
|
try {
|
|
328
|
-
const
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
337
|
+
const raw = localStorage.getItem(cacheKeyFor(env));
|
|
338
|
+
if (!raw) return null;
|
|
339
|
+
const data = JSON.parse(raw);
|
|
340
|
+
if (data && typeof data.url === "string" && Date.now() - (Number(data.ts) || 0) < CACHE_EXPIRY) {
|
|
341
|
+
return data.url;
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
function writeCachedUrl(env, url) {
|
|
348
|
+
try {
|
|
349
|
+
localStorage.setItem(cacheKeyFor(env), JSON.stringify({ url, ts: Date.now() }));
|
|
350
|
+
} catch {
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function clearCachedUrl(env) {
|
|
354
|
+
try {
|
|
355
|
+
if (env) {
|
|
356
|
+
localStorage.removeItem(cacheKeyFor(env));
|
|
357
|
+
} else {
|
|
358
|
+
localStorage.removeItem(cacheKeyFor("sandbox"));
|
|
359
|
+
localStorage.removeItem(cacheKeyFor("live"));
|
|
360
|
+
}
|
|
361
|
+
} catch {
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function isPayPalOrigin(origin) {
|
|
365
|
+
try {
|
|
366
|
+
const host = new URL(origin).hostname.toLowerCase();
|
|
367
|
+
return host === "www.paypal.com" || host === "www.sandbox.paypal.com" || host.endsWith(".paypal.com") || host.endsWith(".paypalobjects.com");
|
|
368
|
+
} catch {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function extractAuth(data) {
|
|
373
|
+
if (!data) return {};
|
|
374
|
+
if (typeof data === "object") {
|
|
375
|
+
const obj = data;
|
|
376
|
+
const authCode = obj.authCode ?? obj.auth_code ?? obj.authcode;
|
|
377
|
+
const sharedId = obj.sharedId ?? obj.shared_id ?? obj.sharedid;
|
|
378
|
+
if (authCode && sharedId) {
|
|
379
|
+
return { authCode: String(authCode), sharedId: String(sharedId) };
|
|
380
|
+
}
|
|
381
|
+
for (const key of ["data", "payload", "message", "detail", "body"]) {
|
|
382
|
+
if (obj[key] && typeof obj[key] === "object") {
|
|
383
|
+
const inner = extractAuth(obj[key]);
|
|
384
|
+
if (inner.authCode && inner.sharedId) return inner;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return {};
|
|
388
|
+
}
|
|
389
|
+
if (typeof data === "string") {
|
|
390
|
+
const s = data.trim();
|
|
391
|
+
if (!s) return {};
|
|
392
|
+
if (s.startsWith("{")) {
|
|
393
|
+
try {
|
|
394
|
+
return extractAuth(JSON.parse(s));
|
|
395
|
+
} catch {
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (/auth_?code/i.test(s) && /shared_?id/i.test(s)) {
|
|
399
|
+
try {
|
|
400
|
+
const sp = new URLSearchParams(s.replace(/^[?#]/, ""));
|
|
401
|
+
const authCode = sp.get("authCode") || sp.get("auth_code") || void 0;
|
|
402
|
+
const sharedId = sp.get("sharedId") || sp.get("shared_id") || void 0;
|
|
403
|
+
if (authCode && sharedId) return { authCode, sharedId };
|
|
404
|
+
} catch {
|
|
333
405
|
}
|
|
334
406
|
}
|
|
335
|
-
} catch (e) {
|
|
336
|
-
console.error("Cache read error:", e);
|
|
337
407
|
}
|
|
408
|
+
return {};
|
|
338
409
|
}
|
|
339
410
|
function PayPalConnectionPage() {
|
|
340
411
|
const [env, setEnv] = react.useState("live");
|
|
341
|
-
react.
|
|
342
|
-
fetch("/admin/paypal/environment", { method: "GET", credentials: "include" }).then((r) => r.json()).then((d) => {
|
|
343
|
-
const v = (d == null ? void 0 : d.environment) === "sandbox" ? "sandbox" : "live";
|
|
344
|
-
setEnv(v);
|
|
345
|
-
}).catch(() => {
|
|
346
|
-
});
|
|
347
|
-
}, []);
|
|
412
|
+
const [envReady, setEnvReady] = react.useState(false);
|
|
348
413
|
const [connState, setConnState] = react.useState("loading");
|
|
349
414
|
const [error, setError] = react.useState(null);
|
|
415
|
+
const [popupBlocked, setPopupBlocked] = react.useState(false);
|
|
350
416
|
const [finalUrl, setFinalUrl] = react.useState("");
|
|
351
417
|
const [showManual, setShowManual] = react.useState(false);
|
|
352
418
|
const [clientId, setClientId] = react.useState("");
|
|
@@ -358,6 +424,17 @@ function PayPalConnectionPage() {
|
|
|
358
424
|
const errorLogRef = react.useRef(null);
|
|
359
425
|
const runIdRef = react.useRef(0);
|
|
360
426
|
const currentRunId = react.useRef(0);
|
|
427
|
+
const finalUrlRef = react.useRef(finalUrl);
|
|
428
|
+
finalUrlRef.current = finalUrl;
|
|
429
|
+
const connStateRef = react.useRef(connState);
|
|
430
|
+
connStateRef.current = connState;
|
|
431
|
+
const inProgressRef = react.useRef(onboardingInProgress);
|
|
432
|
+
inProgressRef.current = onboardingInProgress;
|
|
433
|
+
const envRef = react.useRef(env);
|
|
434
|
+
envRef.current = env;
|
|
435
|
+
const popupRef = react.useRef(null);
|
|
436
|
+
const popupPollRef = react.useRef(null);
|
|
437
|
+
const completedRef = react.useRef(false);
|
|
361
438
|
const ppBtnMeasureRef = react.useRef(null);
|
|
362
439
|
const [ppBtnWidth, setPpBtnWidth] = react.useState(null);
|
|
363
440
|
const canSaveManual = react.useMemo(() => {
|
|
@@ -367,8 +444,124 @@ function PayPalConnectionPage() {
|
|
|
367
444
|
setConnState("error");
|
|
368
445
|
setError(msg);
|
|
369
446
|
}, []);
|
|
447
|
+
const stopPopupPoll = react.useCallback(() => {
|
|
448
|
+
if (popupPollRef.current) {
|
|
449
|
+
clearInterval(popupPollRef.current);
|
|
450
|
+
popupPollRef.current = null;
|
|
451
|
+
}
|
|
452
|
+
}, []);
|
|
453
|
+
const openOnboardingPopup = react.useCallback(
|
|
454
|
+
(url) => {
|
|
455
|
+
const w = 450;
|
|
456
|
+
const h = 600;
|
|
457
|
+
const baseLeft = typeof window.screenX === "number" ? window.screenX : window.screenLeft || 0;
|
|
458
|
+
const baseTop = typeof window.screenY === "number" ? window.screenY : window.screenTop || 0;
|
|
459
|
+
const outerW = window.outerWidth || document.documentElement.clientWidth || 1024;
|
|
460
|
+
const outerH = window.outerHeight || document.documentElement.clientHeight || 768;
|
|
461
|
+
const left = Math.round(baseLeft + Math.max(0, (outerW - w) / 2));
|
|
462
|
+
const top = Math.round(baseTop + Math.max(0, (outerH - h) / 2));
|
|
463
|
+
const features = `popup=yes,width=${w},height=${h},left=${left},top=${top},scrollbars=yes,resizable=yes`;
|
|
464
|
+
let popup = null;
|
|
465
|
+
try {
|
|
466
|
+
popup = window.open(url, POPUP_NAME, features);
|
|
467
|
+
} catch {
|
|
468
|
+
popup = null;
|
|
469
|
+
}
|
|
470
|
+
if (popup) {
|
|
471
|
+
popupRef.current = popup;
|
|
472
|
+
try {
|
|
473
|
+
popup.focus();
|
|
474
|
+
} catch {
|
|
475
|
+
}
|
|
476
|
+
stopPopupPoll();
|
|
477
|
+
popupPollRef.current = setInterval(() => {
|
|
478
|
+
if (!popupRef.current || popupRef.current.closed) {
|
|
479
|
+
stopPopupPoll();
|
|
480
|
+
}
|
|
481
|
+
}, 1e3);
|
|
482
|
+
}
|
|
483
|
+
return popup;
|
|
484
|
+
},
|
|
485
|
+
[stopPopupPoll]
|
|
486
|
+
);
|
|
487
|
+
const openConnect = react.useCallback(() => {
|
|
488
|
+
if (connStateRef.current !== "ready" || !finalUrlRef.current || inProgressRef.current) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
completedRef.current = false;
|
|
492
|
+
const popup = openOnboardingPopup(finalUrlRef.current);
|
|
493
|
+
if (!popup) {
|
|
494
|
+
setPopupBlocked(true);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
setPopupBlocked(false);
|
|
498
|
+
}, [openOnboardingPopup]);
|
|
499
|
+
const completeOnboarding = react.useCallback(
|
|
500
|
+
async (authCode, sharedId) => {
|
|
501
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
502
|
+
if (!authCode || !sharedId) return;
|
|
503
|
+
if (completedRef.current) return;
|
|
504
|
+
completedRef.current = true;
|
|
505
|
+
try {
|
|
506
|
+
window.onbeforeunload = null;
|
|
507
|
+
} catch {
|
|
508
|
+
}
|
|
509
|
+
const activeEnv = envRef.current === "sandbox" ? "sandbox" : "live";
|
|
510
|
+
setOnboardingInProgress(true);
|
|
511
|
+
setConnState("loading");
|
|
512
|
+
setError(null);
|
|
513
|
+
setPopupBlocked(false);
|
|
514
|
+
try {
|
|
515
|
+
const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
|
|
516
|
+
method: "POST",
|
|
517
|
+
headers: { "content-type": "application/json" },
|
|
518
|
+
credentials: "include",
|
|
519
|
+
body: JSON.stringify({ authCode, sharedId, env: activeEnv })
|
|
520
|
+
});
|
|
521
|
+
if (!res.ok) {
|
|
522
|
+
const txt = await res.text().catch(() => "");
|
|
523
|
+
throw new Error(txt || `Onboarding exchange failed (${res.status})`);
|
|
524
|
+
}
|
|
525
|
+
try {
|
|
526
|
+
(_a = popupRef.current) == null ? void 0 : _a.close();
|
|
527
|
+
} catch {
|
|
528
|
+
}
|
|
529
|
+
stopPopupPoll();
|
|
530
|
+
try {
|
|
531
|
+
const close1 = (_e = (_d = (_c = (_b = window.PAYPAL) == null ? void 0 : _b.apps) == null ? void 0 : _c.Signup) == null ? void 0 : _d.MiniBrowser) == null ? void 0 : _e.closeFlow;
|
|
532
|
+
if (typeof close1 === "function") close1();
|
|
533
|
+
} catch {
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
const close2 = ((_h = (_g = (_f = window.PAYPAL) == null ? void 0 : _f.apps) == null ? void 0 : _g.Signup) == null ? void 0 : _h.miniBrowser) && window.PAYPAL.apps.Signup.miniBrowser.closeFlow;
|
|
537
|
+
if (typeof close2 === "function") close2();
|
|
538
|
+
} catch {
|
|
539
|
+
}
|
|
540
|
+
clearCachedUrl(activeEnv);
|
|
541
|
+
try {
|
|
542
|
+
const statusRes = await fetch(
|
|
543
|
+
`${STATUS_ENDPOINT}?environment=${activeEnv}`,
|
|
544
|
+
{ method: "GET", credentials: "include" }
|
|
545
|
+
);
|
|
546
|
+
const refreshedStatus = await statusRes.json().catch(() => ({}));
|
|
547
|
+
setStatusInfo(refreshedStatus || null);
|
|
548
|
+
} catch {
|
|
549
|
+
}
|
|
550
|
+
setConnState("connected");
|
|
551
|
+
setShowManual(false);
|
|
552
|
+
} catch (e) {
|
|
553
|
+
completedRef.current = false;
|
|
554
|
+
console.error(e);
|
|
555
|
+
setConnState("error");
|
|
556
|
+
setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
|
|
557
|
+
} finally {
|
|
558
|
+
setOnboardingInProgress(false);
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
[stopPopupPoll]
|
|
562
|
+
);
|
|
370
563
|
const fetchFreshLink = react.useCallback(
|
|
371
|
-
(runId) => {
|
|
564
|
+
(runId, targetEnv) => {
|
|
372
565
|
if (initLoaderRef.current) {
|
|
373
566
|
const loaderText = initLoaderRef.current.querySelector("#loader-text");
|
|
374
567
|
if (loaderText)
|
|
@@ -379,7 +572,10 @@ function PayPalConnectionPage() {
|
|
|
379
572
|
headers: { "content-type": "application/json" },
|
|
380
573
|
credentials: "include",
|
|
381
574
|
body: JSON.stringify({
|
|
382
|
-
products: ["PPCP"]
|
|
575
|
+
products: ["PPCP"],
|
|
576
|
+
// Generate the link for the explicitly selected environment so it can
|
|
577
|
+
// never depend on a racing POST /environment having landed yet.
|
|
578
|
+
environment: targetEnv
|
|
383
579
|
})
|
|
384
580
|
}).then((r) => {
|
|
385
581
|
if (!r.ok) throw new Error(`Service returned ${r.status}`);
|
|
@@ -392,10 +588,7 @@ function PayPalConnectionPage() {
|
|
|
392
588
|
return;
|
|
393
589
|
}
|
|
394
590
|
const url = href + (href.includes("?") ? "&" : "?") + "displayMode=minibrowser";
|
|
395
|
-
|
|
396
|
-
CACHE_KEY,
|
|
397
|
-
JSON.stringify({ url, ts: Date.now() })
|
|
398
|
-
);
|
|
591
|
+
writeCachedUrl(targetEnv, url);
|
|
399
592
|
setFinalUrl(url);
|
|
400
593
|
setConnState("ready");
|
|
401
594
|
}).catch(() => {
|
|
@@ -403,8 +596,21 @@ function PayPalConnectionPage() {
|
|
|
403
596
|
showError("Unable to connect to service.");
|
|
404
597
|
});
|
|
405
598
|
},
|
|
406
|
-
[
|
|
599
|
+
[showError]
|
|
407
600
|
);
|
|
601
|
+
react.useEffect(() => {
|
|
602
|
+
let alive = true;
|
|
603
|
+
fetch(ENVIRONMENT_ENDPOINT, { method: "GET", credentials: "include" }).then((r) => r.json()).then((d) => {
|
|
604
|
+
if (!alive) return;
|
|
605
|
+
setEnv((d == null ? void 0 : d.environment) === "sandbox" ? "sandbox" : "live");
|
|
606
|
+
}).catch(() => {
|
|
607
|
+
}).finally(() => {
|
|
608
|
+
if (alive) setEnvReady(true);
|
|
609
|
+
});
|
|
610
|
+
return () => {
|
|
611
|
+
alive = false;
|
|
612
|
+
};
|
|
613
|
+
}, []);
|
|
408
614
|
react.useEffect(() => {
|
|
409
615
|
var _a, _b, _c;
|
|
410
616
|
if (connState !== "ready" || !finalUrl) return;
|
|
@@ -468,12 +674,26 @@ function PayPalConnectionPage() {
|
|
|
468
674
|
};
|
|
469
675
|
}, [connState, finalUrl, env]);
|
|
470
676
|
react.useEffect(() => {
|
|
677
|
+
const onMessage = (ev) => {
|
|
678
|
+
if (!isPayPalOrigin(ev.origin)) return;
|
|
679
|
+
const { authCode, sharedId } = extractAuth(ev.data);
|
|
680
|
+
if (authCode && sharedId) {
|
|
681
|
+
completeOnboarding(authCode, sharedId);
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
window.addEventListener("message", onMessage);
|
|
685
|
+
return () => window.removeEventListener("message", onMessage);
|
|
686
|
+
}, [completeOnboarding]);
|
|
687
|
+
react.useEffect(() => {
|
|
688
|
+
if (!envReady) return;
|
|
471
689
|
currentRunId.current = ++runIdRef.current;
|
|
472
690
|
const runId = currentRunId.current;
|
|
473
691
|
let cancelled = false;
|
|
692
|
+
completedRef.current = false;
|
|
474
693
|
const run = async () => {
|
|
475
694
|
setConnState("loading");
|
|
476
695
|
setError(null);
|
|
696
|
+
setPopupBlocked(false);
|
|
477
697
|
setFinalUrl("");
|
|
478
698
|
try {
|
|
479
699
|
const r = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
|
|
@@ -492,84 +712,46 @@ function PayPalConnectionPage() {
|
|
|
492
712
|
} catch (e) {
|
|
493
713
|
console.error(e);
|
|
494
714
|
}
|
|
495
|
-
if (
|
|
496
|
-
|
|
715
|
+
if (cancelled || runId !== currentRunId.current) return;
|
|
716
|
+
const cached = readCachedUrl(env);
|
|
717
|
+
if (cached) {
|
|
718
|
+
setFinalUrl(cached);
|
|
497
719
|
setConnState("ready");
|
|
498
720
|
} else {
|
|
499
|
-
fetchFreshLink(runId);
|
|
721
|
+
fetchFreshLink(runId, env);
|
|
500
722
|
}
|
|
501
723
|
};
|
|
502
724
|
run();
|
|
503
725
|
return () => {
|
|
504
726
|
cancelled = true;
|
|
505
|
-
currentRunId.current = 0;
|
|
506
727
|
};
|
|
507
|
-
}, [env, fetchFreshLink]);
|
|
728
|
+
}, [env, envReady, fetchFreshLink]);
|
|
508
729
|
react.useLayoutEffect(() => {
|
|
509
|
-
window.onboardingCallback =
|
|
510
|
-
|
|
511
|
-
try {
|
|
512
|
-
window.onbeforeunload = null;
|
|
513
|
-
} catch {
|
|
514
|
-
}
|
|
515
|
-
setOnboardingInProgress(true);
|
|
516
|
-
setConnState("loading");
|
|
517
|
-
setError(null);
|
|
518
|
-
const payload = {
|
|
519
|
-
authCode,
|
|
520
|
-
sharedId,
|
|
521
|
-
env: env === "sandbox" ? "sandbox" : "live"
|
|
522
|
-
};
|
|
523
|
-
try {
|
|
524
|
-
const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
|
|
525
|
-
method: "POST",
|
|
526
|
-
headers: { "content-type": "application/json" },
|
|
527
|
-
credentials: "include",
|
|
528
|
-
body: JSON.stringify(payload)
|
|
529
|
-
});
|
|
530
|
-
if (!res.ok) {
|
|
531
|
-
const txt = await res.text().catch(() => "");
|
|
532
|
-
throw new Error(txt || `Onboarding exchange failed (${res.status})`);
|
|
533
|
-
}
|
|
534
|
-
try {
|
|
535
|
-
const close1 = (_d = (_c = (_b = (_a = window.PAYPAL) == null ? void 0 : _a.apps) == null ? void 0 : _b.Signup) == null ? void 0 : _c.MiniBrowser) == null ? void 0 : _d.closeFlow;
|
|
536
|
-
if (typeof close1 === "function") close1();
|
|
537
|
-
} catch {
|
|
538
|
-
}
|
|
539
|
-
try {
|
|
540
|
-
const close2 = ((_g = (_f = (_e = window.PAYPAL) == null ? void 0 : _e.apps) == null ? void 0 : _f.Signup) == null ? void 0 : _g.miniBrowser) && window.PAYPAL.apps.Signup.miniBrowser.closeFlow;
|
|
541
|
-
if (typeof close2 === "function") close2();
|
|
542
|
-
} catch {
|
|
543
|
-
}
|
|
544
|
-
try {
|
|
545
|
-
localStorage.removeItem(CACHE_KEY);
|
|
546
|
-
} catch {
|
|
547
|
-
}
|
|
548
|
-
try {
|
|
549
|
-
const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
|
|
550
|
-
method: "GET",
|
|
551
|
-
credentials: "include"
|
|
552
|
-
});
|
|
553
|
-
const refreshedStatus = await statusRes.json().catch(() => ({}));
|
|
554
|
-
setStatusInfo(refreshedStatus || null);
|
|
555
|
-
setConnState("connected");
|
|
556
|
-
setShowManual(false);
|
|
557
|
-
} catch {
|
|
558
|
-
setConnState("connected");
|
|
559
|
-
setShowManual(false);
|
|
560
|
-
}
|
|
561
|
-
setOnboardingInProgress(false);
|
|
562
|
-
} catch (e) {
|
|
563
|
-
console.error(e);
|
|
564
|
-
setConnState("error");
|
|
565
|
-
setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
|
|
566
|
-
setOnboardingInProgress(false);
|
|
567
|
-
}
|
|
730
|
+
window.onboardingCallback = (authCode, sharedId) => {
|
|
731
|
+
completeOnboarding(authCode, sharedId);
|
|
568
732
|
};
|
|
569
733
|
return () => {
|
|
570
734
|
window.onboardingCallback = void 0;
|
|
571
735
|
};
|
|
572
|
-
}, [
|
|
736
|
+
}, [completeOnboarding]);
|
|
737
|
+
react.useEffect(() => {
|
|
738
|
+
return () => {
|
|
739
|
+
stopPopupPoll();
|
|
740
|
+
};
|
|
741
|
+
}, [stopPopupPoll]);
|
|
742
|
+
react.useEffect(() => {
|
|
743
|
+
const onCaptureClick = (e) => {
|
|
744
|
+
const btn = paypalButtonRef.current;
|
|
745
|
+
if (!btn) return;
|
|
746
|
+
const target = e.target;
|
|
747
|
+
if (!target || !btn.contains(target)) return;
|
|
748
|
+
e.preventDefault();
|
|
749
|
+
e.stopImmediatePropagation();
|
|
750
|
+
openConnect();
|
|
751
|
+
};
|
|
752
|
+
document.addEventListener("click", onCaptureClick, true);
|
|
753
|
+
return () => document.removeEventListener("click", onCaptureClick, true);
|
|
754
|
+
}, [openConnect]);
|
|
573
755
|
react.useLayoutEffect(() => {
|
|
574
756
|
const el = ppBtnMeasureRef.current;
|
|
575
757
|
if (!el) return;
|
|
@@ -591,9 +773,8 @@ function PayPalConnectionPage() {
|
|
|
591
773
|
};
|
|
592
774
|
}, [connState, env, finalUrl]);
|
|
593
775
|
const handleConnectClick = (e) => {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
776
|
+
e.preventDefault();
|
|
777
|
+
openConnect();
|
|
597
778
|
};
|
|
598
779
|
const handleSaveManual = async () => {
|
|
599
780
|
if (!canSaveManual || onboardingInProgress) return;
|
|
@@ -623,10 +804,7 @@ function PayPalConnectionPage() {
|
|
|
623
804
|
setConnState("connected");
|
|
624
805
|
setStatusInfo(refreshedStatus || null);
|
|
625
806
|
setShowManual(false);
|
|
626
|
-
|
|
627
|
-
localStorage.removeItem(CACHE_KEY);
|
|
628
|
-
} catch {
|
|
629
|
-
}
|
|
807
|
+
clearCachedUrl(env);
|
|
630
808
|
} catch (e) {
|
|
631
809
|
console.error(e);
|
|
632
810
|
setConnState("error");
|
|
@@ -655,13 +833,11 @@ function PayPalConnectionPage() {
|
|
|
655
833
|
const t = await res.text().catch(() => "");
|
|
656
834
|
throw new Error(t || `Disconnect failed (${res.status})`);
|
|
657
835
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
} catch {
|
|
661
|
-
}
|
|
836
|
+
clearCachedUrl(env);
|
|
837
|
+
completedRef.current = false;
|
|
662
838
|
currentRunId.current = ++runIdRef.current;
|
|
663
839
|
const runId = currentRunId.current;
|
|
664
|
-
fetchFreshLink(runId);
|
|
840
|
+
fetchFreshLink(runId, env);
|
|
665
841
|
} catch (e) {
|
|
666
842
|
console.error(e);
|
|
667
843
|
setConnState("error");
|
|
@@ -672,10 +848,12 @@ function PayPalConnectionPage() {
|
|
|
672
848
|
};
|
|
673
849
|
const handleEnvChange = async (e) => {
|
|
674
850
|
const next = e.target.value;
|
|
851
|
+
completedRef.current = false;
|
|
852
|
+
setPopupBlocked(false);
|
|
853
|
+
clearCachedUrl();
|
|
675
854
|
setEnv(next);
|
|
676
|
-
cachedUrl = null;
|
|
677
855
|
try {
|
|
678
|
-
await fetch(
|
|
856
|
+
await fetch(ENVIRONMENT_ENDPOINT, {
|
|
679
857
|
method: "POST",
|
|
680
858
|
headers: { "content-type": "application/json" },
|
|
681
859
|
credentials: "include",
|
|
@@ -683,10 +861,6 @@ function PayPalConnectionPage() {
|
|
|
683
861
|
});
|
|
684
862
|
} catch {
|
|
685
863
|
}
|
|
686
|
-
try {
|
|
687
|
-
localStorage.removeItem(CACHE_KEY);
|
|
688
|
-
} catch {
|
|
689
|
-
}
|
|
690
864
|
};
|
|
691
865
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6", children: [
|
|
692
866
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
@@ -750,7 +924,7 @@ function PayPalConnectionPage() {
|
|
|
750
924
|
ppBtnMeasureRef.current = node;
|
|
751
925
|
},
|
|
752
926
|
id: "paypal-button",
|
|
753
|
-
target:
|
|
927
|
+
target: POPUP_NAME,
|
|
754
928
|
"data-paypal-button": "true",
|
|
755
929
|
href: finalUrl || "#",
|
|
756
930
|
"data-paypal-onboard-complete": "onboardingCallback",
|
|
@@ -764,6 +938,7 @@ function PayPalConnectionPage() {
|
|
|
764
938
|
children: "Connect to PayPal"
|
|
765
939
|
}
|
|
766
940
|
),
|
|
941
|
+
popupBlocked && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 text-left text-xs bg-amber-50 text-amber-700 p-3 border border-amber-200 rounded", children: "Your browser blocked the PayPal popup. Please allow popups for this site, then click “Connect to PayPal” again." }),
|
|
767
942
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
768
943
|
"div",
|
|
769
944
|
{
|
|
@@ -919,12 +1094,6 @@ function PayPalConnectionPage() {
|
|
|
919
1094
|
` })
|
|
920
1095
|
] });
|
|
921
1096
|
}
|
|
922
|
-
function PayPalApplePayPage() {
|
|
923
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
924
|
-
}
|
|
925
|
-
function PayPalGooglePayPage() {
|
|
926
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
927
|
-
}
|
|
928
1097
|
function PayPalPayLaterMessagingPage() {
|
|
929
1098
|
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
930
1099
|
}
|
|
@@ -1150,10 +1319,6 @@ const routeModule = {
|
|
|
1150
1319
|
Component: AdvancedCardPaymentsTab,
|
|
1151
1320
|
path: "/settings/paypal/advanced-card-payments"
|
|
1152
1321
|
},
|
|
1153
|
-
{
|
|
1154
|
-
Component: PayPalConnectionPage,
|
|
1155
|
-
path: "/settings/paypal/connection"
|
|
1156
|
-
},
|
|
1157
1322
|
{
|
|
1158
1323
|
Component: PayPalApplePayPage,
|
|
1159
1324
|
path: "/settings/paypal/apple-pay"
|
|
@@ -1162,6 +1327,10 @@ const routeModule = {
|
|
|
1162
1327
|
Component: PayPalGooglePayPage,
|
|
1163
1328
|
path: "/settings/paypal/google-pay"
|
|
1164
1329
|
},
|
|
1330
|
+
{
|
|
1331
|
+
Component: PayPalConnectionPage,
|
|
1332
|
+
path: "/settings/paypal/connection"
|
|
1333
|
+
},
|
|
1165
1334
|
{
|
|
1166
1335
|
Component: PayPalPayLaterMessagingPage,
|
|
1167
1336
|
path: "/settings/paypal/pay-later-messaging"
|