@easypayment/medusa-payment-paypal 0.9.25 → 0.9.26

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.
@@ -308,21 +308,17 @@ function AdvancedCardPaymentsTab() {
308
308
  )
309
309
  ] }) });
310
310
  }
311
- function PayPalApplePayPage() {
311
+ function PayPalGooglePayPage() {
312
312
  return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
313
313
  }
314
- function PayPalGooglePayPage() {
314
+ function PayPalApplePayPage() {
315
315
  return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
316
316
  }
317
317
  const config = adminSdk.defineRouteConfig({
318
318
  label: "PayPal Connection"
319
319
  });
320
- const PARTNER_JS_URLS = {
321
- sandbox: "https://www.sandbox.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js",
322
- live: "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"
323
- };
320
+ const PARTNER_JS_URL = "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js";
324
321
  const SERVICE_URL = "/admin/paypal/onboarding-link";
325
- const POPUP_NAME = "PPFrame";
326
322
  const CACHE_PREFIX = "pp_onboard_cache";
327
323
  const CACHE_EXPIRY = 6 * 60 * 60 * 1e3;
328
324
  const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete";
@@ -347,9 +343,7 @@ function readCachedUrl(env) {
347
343
  function writeCachedUrl(env, url) {
348
344
  try {
349
345
  localStorage.setItem(cacheKeyFor(env), JSON.stringify({ url, ts: Date.now() }));
350
- return readCachedUrl(env) === url;
351
346
  } catch {
352
- return false;
353
347
  }
354
348
  }
355
349
  function clearCachedUrl(env) {
@@ -363,62 +357,11 @@ function clearCachedUrl(env) {
363
357
  } catch {
364
358
  }
365
359
  }
366
- function isPayPalOrigin(origin) {
367
- try {
368
- const host = new URL(origin).hostname.toLowerCase();
369
- return host === "www.paypal.com" || host === "www.sandbox.paypal.com" || host.endsWith(".paypal.com") || host.endsWith(".paypalobjects.com");
370
- } catch {
371
- return false;
372
- }
373
- }
374
- function extractAuth(data) {
375
- if (!data) return {};
376
- if (typeof data === "object") {
377
- const obj = data;
378
- const authCode = obj.authCode ?? obj.auth_code ?? obj.authcode ?? obj.code;
379
- const sharedId = obj.sharedId ?? obj.shared_id ?? obj.sharedid;
380
- if (authCode && sharedId) {
381
- return { authCode: String(authCode), sharedId: String(sharedId) };
382
- }
383
- for (const key of ["data", "payload", "message", "detail", "body", "params"]) {
384
- if (obj[key] && typeof obj[key] === "object") {
385
- const inner = extractAuth(obj[key]);
386
- if (inner.authCode && inner.sharedId) return inner;
387
- }
388
- if (typeof obj[key] === "string") {
389
- const inner = extractAuth(obj[key]);
390
- if (inner.authCode && inner.sharedId) return inner;
391
- }
392
- }
393
- return {};
394
- }
395
- if (typeof data === "string") {
396
- const s = data.trim();
397
- if (!s) return {};
398
- if (s.startsWith("{") || s.startsWith("[")) {
399
- try {
400
- return extractAuth(JSON.parse(s));
401
- } catch {
402
- }
403
- }
404
- if (/auth_?code/i.test(s) && /shared_?id/i.test(s)) {
405
- try {
406
- const sp = new URLSearchParams(s.replace(/^[?#]/, ""));
407
- const authCode = sp.get("authCode") || sp.get("auth_code") || void 0;
408
- const sharedId = sp.get("sharedId") || sp.get("shared_id") || void 0;
409
- if (authCode && sharedId) return { authCode, sharedId };
410
- } catch {
411
- }
412
- }
413
- }
414
- return {};
415
- }
416
360
  function PayPalConnectionPage() {
417
361
  const [env, setEnv] = react.useState("live");
418
362
  const [envReady, setEnvReady] = react.useState(false);
419
363
  const [connState, setConnState] = react.useState("loading");
420
364
  const [error, setError] = react.useState(null);
421
- const [popupBlocked, setPopupBlocked] = react.useState(false);
422
365
  const [finalUrl, setFinalUrl] = react.useState("");
423
366
  const [showManual, setShowManual] = react.useState(false);
424
367
  const [clientId, setClientId] = react.useState("");
@@ -430,17 +373,11 @@ function PayPalConnectionPage() {
430
373
  const errorLogRef = react.useRef(null);
431
374
  const runIdRef = react.useRef(0);
432
375
  const currentRunId = react.useRef(0);
376
+ const completedRef = react.useRef(false);
433
377
  const envRef = react.useRef(env);
434
378
  envRef.current = env;
435
- const finalUrlRef = react.useRef(finalUrl);
436
- finalUrlRef.current = finalUrl;
437
- const connStateRef = react.useRef(connState);
438
- connStateRef.current = connState;
439
- const inProgressRef = react.useRef(onboardingInProgress);
440
- inProgressRef.current = onboardingInProgress;
441
- const popupRef = react.useRef(null);
442
- const pollRef = react.useRef(null);
443
- const completedRef = react.useRef(false);
379
+ const pollTimerRef = react.useRef(null);
380
+ const pollAttemptsRef = react.useRef(0);
444
381
  const ppBtnMeasureRef = react.useRef(null);
445
382
  const [ppBtnWidth, setPpBtnWidth] = react.useState(null);
446
383
  const canSaveManual = react.useMemo(() => {
@@ -450,44 +387,72 @@ function PayPalConnectionPage() {
450
387
  setConnState("error");
451
388
  setError(msg);
452
389
  }, []);
453
- const stopPoll = react.useCallback(() => {
454
- if (pollRef.current) {
455
- clearInterval(pollRef.current);
456
- pollRef.current = null;
390
+ const closeMiniBrowser = react.useCallback(() => {
391
+ var _a, _b, _c, _d, _e, _f, _g, _h;
392
+ try {
393
+ 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;
394
+ if (typeof close1 === "function") close1();
395
+ } catch {
457
396
  }
458
- }, []);
459
- const openOnboardingPopup = react.useCallback((url) => {
460
- const w = 450;
461
- const h = 600;
462
- const baseLeft = typeof window.screenX === "number" ? window.screenX : window.screenLeft || 0;
463
- const baseTop = typeof window.screenY === "number" ? window.screenY : window.screenTop || 0;
464
- const outerW = window.outerWidth || document.documentElement.clientWidth || 1024;
465
- const outerH = window.outerHeight || document.documentElement.clientHeight || 768;
466
- const left = Math.round(baseLeft + Math.max(0, (outerW - w) / 2));
467
- const top = Math.round(baseTop + Math.max(0, (outerH - h) / 2));
468
- const features = `popup=yes,width=${w},height=${h},left=${left},top=${top},scrollbars=yes,resizable=yes`;
469
- let popup = null;
470
397
  try {
471
- popup = window.open(url, POPUP_NAME, features);
398
+ const close2 = (_h = (_g = (_f = (_e = window.PAYPAL) == null ? void 0 : _e.apps) == null ? void 0 : _f.Signup) == null ? void 0 : _g.miniBrowser) == null ? void 0 : _h.closeFlow;
399
+ if (typeof close2 === "function") close2();
472
400
  } catch {
473
- popup = null;
474
401
  }
475
- if (popup) {
476
- popupRef.current = popup;
402
+ }, []);
403
+ const stopStatusPolling = react.useCallback(() => {
404
+ if (pollTimerRef.current) {
405
+ clearTimeout(pollTimerRef.current);
406
+ pollTimerRef.current = null;
407
+ }
408
+ pollAttemptsRef.current = 0;
409
+ }, []);
410
+ const refreshStatusAndMaybeConnect = react.useCallback(
411
+ async (activeEnv) => {
477
412
  try {
478
- popup.focus();
413
+ const res = await fetch(`${STATUS_ENDPOINT}?environment=${activeEnv}`, {
414
+ method: "GET",
415
+ credentials: "include"
416
+ });
417
+ const st = await res.json().catch(() => ({}));
418
+ const connected = (st == null ? void 0 : st.status) === "connected" && (st == null ? void 0 : st.seller_client_id_present) === true;
419
+ if (connected) {
420
+ completedRef.current = true;
421
+ setStatusInfo(st);
422
+ setConnState("connected");
423
+ setShowManual(false);
424
+ setOnboardingInProgress(false);
425
+ clearCachedUrl(activeEnv);
426
+ closeMiniBrowser();
427
+ stopStatusPolling();
428
+ }
429
+ return connected;
479
430
  } catch {
431
+ return false;
480
432
  }
481
- }
482
- return popup;
483
- }, []);
433
+ },
434
+ [closeMiniBrowser, stopStatusPolling]
435
+ );
436
+ const startStatusPolling = react.useCallback(() => {
437
+ stopStatusPolling();
438
+ const MAX_ATTEMPTS = 100;
439
+ const tick = async () => {
440
+ pollAttemptsRef.current += 1;
441
+ const connected = await refreshStatusAndMaybeConnect(envRef.current);
442
+ if (connected || completedRef.current) return;
443
+ if (pollAttemptsRef.current >= MAX_ATTEMPTS) {
444
+ stopStatusPolling();
445
+ return;
446
+ }
447
+ pollTimerRef.current = setTimeout(tick, 3e3);
448
+ };
449
+ pollTimerRef.current = setTimeout(tick, 3e3);
450
+ }, [refreshStatusAndMaybeConnect, stopStatusPolling]);
484
451
  const completeOnboarding = react.useCallback(
485
452
  async (authCode, sharedId) => {
486
- var _a, _b, _c, _d, _e, _f, _g, _h;
487
453
  if (!authCode || !sharedId) return;
488
454
  if (completedRef.current) return;
489
455
  completedRef.current = true;
490
- stopPoll();
491
456
  try {
492
457
  window.onbeforeunload = null;
493
458
  } catch {
@@ -496,7 +461,6 @@ function PayPalConnectionPage() {
496
461
  setOnboardingInProgress(true);
497
462
  setConnState("loading");
498
463
  setError(null);
499
- setPopupBlocked(false);
500
464
  try {
501
465
  const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
502
466
  method: "POST",
@@ -508,32 +472,20 @@ function PayPalConnectionPage() {
508
472
  const txt = await res.text().catch(() => "");
509
473
  throw new Error(txt || `Onboarding exchange failed (${res.status})`);
510
474
  }
511
- try {
512
- (_a = popupRef.current) == null ? void 0 : _a.close();
513
- } catch {
514
- }
515
- try {
516
- 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;
517
- if (typeof close1 === "function") close1();
518
- } catch {
519
- }
520
- try {
521
- 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;
522
- if (typeof close2 === "function") close2();
523
- } catch {
524
- }
475
+ closeMiniBrowser();
525
476
  clearCachedUrl(activeEnv);
526
477
  try {
527
- const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${activeEnv}`, {
528
- method: "GET",
529
- credentials: "include"
530
- });
478
+ const statusRes = await fetch(
479
+ `${STATUS_ENDPOINT}?environment=${activeEnv}`,
480
+ { method: "GET", credentials: "include" }
481
+ );
531
482
  const refreshedStatus = await statusRes.json().catch(() => ({}));
532
483
  setStatusInfo(refreshedStatus || null);
533
484
  } catch {
534
485
  }
535
486
  setConnState("connected");
536
487
  setShowManual(false);
488
+ stopStatusPolling();
537
489
  } catch (e) {
538
490
  completedRef.current = false;
539
491
  console.error(e);
@@ -543,42 +495,72 @@ function PayPalConnectionPage() {
543
495
  setOnboardingInProgress(false);
544
496
  }
545
497
  },
546
- [stopPoll]
498
+ [closeMiniBrowser, stopStatusPolling]
547
499
  );
548
- const startStatusPoll = react.useCallback(() => {
549
- stopPoll();
550
- let ticks = 0;
551
- pollRef.current = setInterval(async () => {
552
- var _a;
553
- ticks += 1;
554
- if (completedRef.current || ticks > 150) {
555
- stopPoll();
556
- return;
557
- }
500
+ const initPartner = react.useCallback(() => {
501
+ var _a, _b;
502
+ const signup = (_b = (_a = window.PAYPAL) == null ? void 0 : _a.apps) == null ? void 0 : _b.Signup;
503
+ const init = (signup == null ? void 0 : signup.miniBrowser) && signup.miniBrowser.init || (signup == null ? void 0 : signup.MiniBrowser) && signup.MiniBrowser.init;
504
+ if (typeof init === "function") {
558
505
  try {
559
- const r = await fetch(`${STATUS_ENDPOINT}?environment=${envRef.current}`, {
560
- method: "GET",
561
- credentials: "include"
562
- });
563
- const st = await r.json().catch(() => ({}));
564
- if ((st == null ? void 0 : st.status) === "connected" && (st == null ? void 0 : st.seller_client_id_present) === true) {
565
- completedRef.current = true;
566
- stopPoll();
567
- try {
568
- (_a = popupRef.current) == null ? void 0 : _a.close();
569
- } catch {
570
- }
571
- clearCachedUrl(envRef.current);
572
- setStatusInfo(st);
573
- setConnState("connected");
574
- setShowManual(false);
575
- setOnboardingInProgress(false);
576
- }
577
- } catch {
506
+ init();
507
+ } catch (e) {
508
+ console.error("[paypal] partner.js init failed:", e);
578
509
  }
579
- }, 2e3);
580
- }, [stopPoll]);
581
- const generateLinkAndReload = react.useCallback(
510
+ }
511
+ }, []);
512
+ react.useEffect(() => {
513
+ const existingScript = document.getElementById("paypal-partner-js");
514
+ if (existingScript) return;
515
+ const preloadHref = PARTNER_JS_URL;
516
+ let preloadLink = null;
517
+ if (!document.head.querySelector(`link[rel="preload"][href="${preloadHref}"]`)) {
518
+ preloadLink = document.createElement("link");
519
+ preloadLink.rel = "preload";
520
+ preloadLink.href = preloadHref;
521
+ preloadLink.as = "script";
522
+ document.head.appendChild(preloadLink);
523
+ }
524
+ const ppScript = document.createElement("script");
525
+ ppScript.id = "paypal-partner-js";
526
+ ppScript.src = preloadHref;
527
+ ppScript.async = true;
528
+ document.head.appendChild(ppScript);
529
+ return () => {
530
+ if (preloadLink == null ? void 0 : preloadLink.parentNode) preloadLink.parentNode.removeChild(preloadLink);
531
+ if (ppScript.parentNode) ppScript.parentNode.removeChild(ppScript);
532
+ };
533
+ }, []);
534
+ const activatePayPal = react.useCallback(
535
+ (url, runId) => {
536
+ if (paypalButtonRef.current) {
537
+ paypalButtonRef.current.href = url;
538
+ }
539
+ setFinalUrl(url);
540
+ let attempts = 0;
541
+ const MAX_ATTEMPTS = 200;
542
+ const tryInit = () => {
543
+ var _a, _b;
544
+ if (runId !== currentRunId.current) return;
545
+ if ((_b = (_a = window.PAYPAL) == null ? void 0 : _a.apps) == null ? void 0 : _b.Signup) {
546
+ initPartner();
547
+ setConnState("ready");
548
+ return;
549
+ }
550
+ attempts++;
551
+ if (attempts >= MAX_ATTEMPTS) {
552
+ showError(
553
+ "PayPal partner script failed to load. Please refresh and try again."
554
+ );
555
+ return;
556
+ }
557
+ setTimeout(tryInit, 50);
558
+ };
559
+ tryInit();
560
+ },
561
+ [initPartner, showError]
562
+ );
563
+ const fetchFreshLink = react.useCallback(
582
564
  async (targetEnv, runId) => {
583
565
  if (initLoaderRef.current) {
584
566
  const loaderText = initLoaderRef.current.querySelector("#loader-text");
@@ -600,19 +582,14 @@ function PayPalConnectionPage() {
600
582
  return;
601
583
  }
602
584
  const url = href + (href.includes("?") ? "&" : "?") + "displayMode=minibrowser";
603
- const persisted = writeCachedUrl(targetEnv, url);
604
- if (persisted) {
605
- window.location.reload();
606
- return;
607
- }
608
- setFinalUrl(url);
609
- setConnState("ready");
585
+ writeCachedUrl(targetEnv, url);
586
+ activatePayPal(url, runId);
610
587
  } catch {
611
588
  if (runId !== currentRunId.current) return;
612
589
  showError("Unable to connect to service.");
613
590
  }
614
591
  },
615
- [showError]
592
+ [activatePayPal, showError]
616
593
  );
617
594
  react.useEffect(() => {
618
595
  let alive = true;
@@ -636,7 +613,6 @@ function PayPalConnectionPage() {
636
613
  const run = async () => {
637
614
  setConnState("loading");
638
615
  setError(null);
639
- setPopupBlocked(false);
640
616
  setFinalUrl("");
641
617
  try {
642
618
  const r = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
@@ -658,149 +634,43 @@ function PayPalConnectionPage() {
658
634
  if (cancelled || runId !== currentRunId.current) return;
659
635
  const cached = readCachedUrl(env);
660
636
  if (cached) {
661
- setFinalUrl(cached);
662
- setConnState("ready");
637
+ activatePayPal(cached, runId);
663
638
  return;
664
639
  }
665
- await generateLinkAndReload(env, runId);
640
+ await fetchFreshLink(env, runId);
666
641
  };
667
642
  run();
668
643
  return () => {
669
644
  cancelled = true;
670
645
  };
671
- }, [env, envReady, generateLinkAndReload]);
672
- react.useEffect(() => {
673
- var _a, _b, _c;
674
- if (connState !== "ready" || !finalUrl) return;
675
- const scriptUrl = PARTNER_JS_URLS[env];
676
- let cancelled = false;
677
- let pollTimer;
678
- const initPartner = (attempt = 0) => {
679
- var _a2, _b2;
680
- if (cancelled) return;
681
- const signup = (_b2 = (_a2 = window.PAYPAL) == null ? void 0 : _a2.apps) == null ? void 0 : _b2.Signup;
682
- const target = (signup == null ? void 0 : signup.miniBrowser) && typeof signup.miniBrowser.init === "function" ? signup.miniBrowser : (signup == null ? void 0 : signup.MiniBrowser) && typeof signup.MiniBrowser.init === "function" ? signup.MiniBrowser : null;
683
- if (target == null ? void 0 : target.init) {
684
- try {
685
- target.init();
686
- } catch (e) {
687
- console.error("[paypal] partner.js init failed:", e);
688
- }
689
- return;
690
- }
691
- if (typeof (signup == null ? void 0 : signup.render) === "function") {
692
- try {
693
- signup.render();
694
- } catch (e) {
695
- console.error("[paypal] partner.js render failed:", e);
696
- }
697
- return;
698
- }
699
- if (attempt < 40) {
700
- pollTimer = setTimeout(() => initPartner(attempt + 1), 50);
701
- return;
702
- }
703
- try {
704
- document.dispatchEvent(new Event("DOMContentLoaded"));
705
- } catch {
706
- }
707
- };
708
- const existingScript = document.getElementById("paypal-partner-js");
709
- if (existingScript) {
710
- (_a = existingScript.parentNode) == null ? void 0 : _a.removeChild(existingScript);
711
- }
712
- if ((_c = (_b = window.PAYPAL) == null ? void 0 : _b.apps) == null ? void 0 : _c.Signup) {
713
- try {
714
- delete window.PAYPAL.apps.Signup;
715
- } catch {
716
- }
717
- }
718
- const ppScript = document.createElement("script");
719
- ppScript.id = "paypal-partner-js";
720
- ppScript.src = scriptUrl;
721
- ppScript.async = true;
722
- ppScript.onload = () => initPartner();
723
- document.body.appendChild(ppScript);
724
- return () => {
725
- cancelled = true;
726
- if (pollTimer) clearTimeout(pollTimer);
727
- if (ppScript.parentNode) ppScript.parentNode.removeChild(ppScript);
728
- };
729
- }, [connState, finalUrl, env]);
730
- react.useEffect(() => {
731
- const onMessage = (ev) => {
732
- var _a;
733
- let serialized = "";
734
- try {
735
- serialized = typeof ev.data === "string" ? ev.data : JSON.stringify(ev.data);
736
- } catch {
737
- serialized = String(ev.data);
738
- }
739
- const looksRelevant = isPayPalOrigin(ev.origin) || /auth_?code|shared_?id|onboard|merchantId/i.test(serialized || "");
740
- if (looksRelevant) {
741
- console.info(
742
- "[PayPal onboarding] message from",
743
- ev.origin,
744
- "·",
745
- (serialized || "").slice(0, 500)
746
- );
747
- }
748
- const d = ev.data;
749
- if (d && typeof d === "object" && d.source === "paypal-onboarding-return") {
750
- try {
751
- (_a = popupRef.current) == null ? void 0 : _a.close();
752
- } catch {
753
- }
754
- const fromReturn = extractAuth(d.params);
755
- if (fromReturn.authCode && fromReturn.sharedId) {
756
- completeOnboarding(fromReturn.authCode, fromReturn.sharedId);
757
- } else if (!completedRef.current) {
758
- startStatusPoll();
759
- }
760
- return;
761
- }
762
- if (!isPayPalOrigin(ev.origin)) return;
763
- const { authCode, sharedId } = extractAuth(ev.data);
764
- if (authCode && sharedId) {
765
- completeOnboarding(authCode, sharedId);
766
- }
767
- };
768
- window.addEventListener("message", onMessage);
769
- return () => window.removeEventListener("message", onMessage);
770
- }, [completeOnboarding, startStatusPoll]);
646
+ }, [env, envReady, fetchFreshLink, activatePayPal]);
771
647
  react.useLayoutEffect(() => {
772
648
  window.onboardingCallback = (authCode, sharedId) => {
773
- completeOnboarding(authCode, sharedId);
649
+ void completeOnboarding(authCode, sharedId);
774
650
  };
775
651
  return () => {
776
652
  window.onboardingCallback = void 0;
777
653
  };
778
654
  }, [completeOnboarding]);
779
655
  react.useEffect(() => {
780
- const onCaptureClick = (e) => {
781
- const btn = paypalButtonRef.current;
782
- if (!btn) return;
783
- const target = e.target;
784
- if (!target || !btn.contains(target)) return;
785
- e.preventDefault();
786
- if (connStateRef.current !== "ready" || !finalUrlRef.current || inProgressRef.current) {
787
- return;
788
- }
789
- completedRef.current = false;
790
- const popup = openOnboardingPopup(finalUrlRef.current);
791
- if (!popup) {
792
- setPopupBlocked(true);
793
- return;
794
- }
795
- setPopupBlocked(false);
796
- startStatusPoll();
656
+ const onMessage = (event) => {
657
+ const data = event == null ? void 0 : event.data;
658
+ if (!data || data.source !== "paypal-onboarding-return") return;
659
+ const params = data.params || {};
660
+ const authCode = params.authCode || params.auth_code;
661
+ const sharedId = params.sharedId || params.shared_id;
662
+ const activeEnv = envRef.current;
663
+ void (async () => {
664
+ const connected = await refreshStatusAndMaybeConnect(activeEnv);
665
+ if (!connected && authCode && sharedId) {
666
+ await completeOnboarding(authCode, sharedId);
667
+ }
668
+ })();
797
669
  };
798
- document.addEventListener("click", onCaptureClick, true);
799
- return () => document.removeEventListener("click", onCaptureClick, true);
800
- }, [openOnboardingPopup, startStatusPoll]);
801
- react.useEffect(() => {
802
- return () => stopPoll();
803
- }, [stopPoll]);
670
+ window.addEventListener("message", onMessage);
671
+ return () => window.removeEventListener("message", onMessage);
672
+ }, [completeOnboarding, refreshStatusAndMaybeConnect]);
673
+ react.useEffect(() => stopStatusPolling, [stopStatusPolling]);
804
674
  react.useLayoutEffect(() => {
805
675
  const el = ppBtnMeasureRef.current;
806
676
  if (!el) return;
@@ -822,7 +692,12 @@ function PayPalConnectionPage() {
822
692
  };
823
693
  }, [connState, env, finalUrl]);
824
694
  const handleConnectClick = (e) => {
825
- e.preventDefault();
695
+ if (connState !== "ready" || !finalUrl || onboardingInProgress) {
696
+ e.preventDefault();
697
+ return;
698
+ }
699
+ completedRef.current = false;
700
+ startStatusPolling();
826
701
  };
827
702
  const handleSaveManual = async () => {
828
703
  if (!canSaveManual || onboardingInProgress) return;
@@ -864,6 +739,7 @@ function PayPalConnectionPage() {
864
739
  const handleDisconnect = async () => {
865
740
  if (onboardingInProgress) return;
866
741
  if (!window.confirm("Disconnect PayPal for this environment?")) return;
742
+ stopStatusPolling();
867
743
  setOnboardingInProgress(true);
868
744
  setConnState("loading");
869
745
  setError(null);
@@ -884,7 +760,7 @@ function PayPalConnectionPage() {
884
760
  clearCachedUrl(env);
885
761
  completedRef.current = false;
886
762
  currentRunId.current = ++runIdRef.current;
887
- await generateLinkAndReload(env, currentRunId.current);
763
+ await fetchFreshLink(env, currentRunId.current);
888
764
  } catch (e) {
889
765
  console.error(e);
890
766
  setConnState("error");
@@ -896,9 +772,9 @@ function PayPalConnectionPage() {
896
772
  const handleEnvChange = async (e) => {
897
773
  const next = e.target.value;
898
774
  if (next === env || onboardingInProgress) return;
775
+ stopStatusPolling();
899
776
  completedRef.current = false;
900
777
  clearCachedUrl();
901
- setPopupBlocked(false);
902
778
  setConnState("loading");
903
779
  try {
904
780
  await fetch(ENVIRONMENT_ENDPOINT, {
@@ -932,7 +808,19 @@ function PayPalConnectionPage() {
932
808
  ) }),
933
809
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium pt-2", children: env === "sandbox" ? "Connect to PayPal (Sandbox)" : "Connect to PayPal" }),
934
810
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xl", children: connState === "connected" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
935
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-green-600 bg-green-50 p-3 rounded border border-green-200", children: "✅ Successfully connected to PayPal!" }),
811
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-green-600 bg-green-50 p-3 rounded border border-green-200", children: [
812
+ "✅ Successfully connected to PayPal!",
813
+ /* @__PURE__ */ jsxRuntime.jsx(
814
+ "a",
815
+ {
816
+ "data-paypal-button": "true",
817
+ "data-paypal-onboard-complete": "onboardingCallback",
818
+ href: "#",
819
+ style: { display: "none" },
820
+ children: "PayPal"
821
+ }
822
+ )
823
+ ] }),
936
824
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 rounded-md border border-ui-border-base bg-ui-bg-subtle p-3 text-xs text-ui-fg-subtle", children: [
937
825
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-ui-fg-base", children: "Connected PayPal account" }),
938
826
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1", children: [
@@ -973,7 +861,6 @@ function PayPalConnectionPage() {
973
861
  ppBtnMeasureRef.current = node;
974
862
  },
975
863
  id: "paypal-button",
976
- target: POPUP_NAME,
977
864
  "data-paypal-button": "true",
978
865
  href: finalUrl || "#",
979
866
  "data-paypal-onboard-complete": "onboardingCallback",
@@ -987,7 +874,6 @@ function PayPalConnectionPage() {
987
874
  children: "Connect to PayPal"
988
875
  }
989
876
  ),
990
- 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." }),
991
877
  /* @__PURE__ */ jsxRuntime.jsx(
992
878
  "div",
993
879
  {
@@ -1368,14 +1254,14 @@ const routeModule = {
1368
1254
  Component: AdvancedCardPaymentsTab,
1369
1255
  path: "/settings/paypal/advanced-card-payments"
1370
1256
  },
1371
- {
1372
- Component: PayPalApplePayPage,
1373
- path: "/settings/paypal/apple-pay"
1374
- },
1375
1257
  {
1376
1258
  Component: PayPalGooglePayPage,
1377
1259
  path: "/settings/paypal/google-pay"
1378
1260
  },
1261
+ {
1262
+ Component: PayPalApplePayPage,
1263
+ path: "/settings/paypal/apple-pay"
1264
+ },
1379
1265
  {
1380
1266
  Component: PayPalConnectionPage,
1381
1267
  path: "/settings/paypal/connection"