@easypayment/medusa-payment-paypal 0.9.24 → 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.
Files changed (22) hide show
  1. package/.medusa/server/src/admin/index.js +187 -287
  2. package/.medusa/server/src/admin/index.mjs +187 -287
  3. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts +0 -1
  4. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts.map +1 -1
  5. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js +204 -332
  6. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js.map +1 -1
  7. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts +7 -0
  8. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts.map +1 -1
  9. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js +7 -0
  10. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js.map +1 -1
  11. package/.medusa/server/src/api/store/paypal/onboard-return/route.d.ts +25 -0
  12. package/.medusa/server/src/api/store/paypal/onboard-return/route.d.ts.map +1 -0
  13. package/.medusa/server/src/api/store/paypal/onboard-return/route.js +82 -0
  14. package/.medusa/server/src/api/store/paypal/onboard-return/route.js.map +1 -0
  15. package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
  16. package/.medusa/server/src/modules/paypal/service.js +32 -2
  17. package/.medusa/server/src/modules/paypal/service.js.map +1 -1
  18. package/package.json +1 -1
  19. package/src/admin/routes/settings/paypal/connection/page.tsx +230 -354
  20. package/src/api/admin/paypal/onboard-complete/route.ts +7 -0
  21. package/src/api/store/paypal/onboard-return/route.ts +88 -0
  22. package/src/modules/paypal/service.ts +34 -3
@@ -224,9 +224,6 @@ function AdditionalSettingsTab() {
224
224
  )
225
225
  ] }) });
226
226
  }
227
- function PayPalApplePayPage() {
228
- return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
229
- }
230
227
  const DEFAULT_FORM = {
231
228
  enabled: true,
232
229
  title: "Credit or Debit Card",
@@ -311,15 +308,17 @@ function AdvancedCardPaymentsTab() {
311
308
  )
312
309
  ] }) });
313
310
  }
311
+ function PayPalGooglePayPage() {
312
+ return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
313
+ }
314
+ function PayPalApplePayPage() {
315
+ return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
316
+ }
314
317
  const config = adminSdk.defineRouteConfig({
315
318
  label: "PayPal Connection"
316
319
  });
317
- const PARTNER_JS_URLS = {
318
- sandbox: "https://www.sandbox.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js",
319
- live: "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"
320
- };
320
+ const PARTNER_JS_URL = "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js";
321
321
  const SERVICE_URL = "/admin/paypal/onboarding-link";
322
- const POPUP_NAME = "PPFrame";
323
322
  const CACHE_PREFIX = "pp_onboard_cache";
324
323
  const CACHE_EXPIRY = 6 * 60 * 60 * 1e3;
325
324
  const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete";
@@ -344,9 +343,7 @@ function readCachedUrl(env) {
344
343
  function writeCachedUrl(env, url) {
345
344
  try {
346
345
  localStorage.setItem(cacheKeyFor(env), JSON.stringify({ url, ts: Date.now() }));
347
- return readCachedUrl(env) === url;
348
346
  } catch {
349
- return false;
350
347
  }
351
348
  }
352
349
  function clearCachedUrl(env) {
@@ -360,62 +357,11 @@ function clearCachedUrl(env) {
360
357
  } catch {
361
358
  }
362
359
  }
363
- function isPayPalOrigin(origin) {
364
- try {
365
- const host = new URL(origin).hostname.toLowerCase();
366
- return host === "www.paypal.com" || host === "www.sandbox.paypal.com" || host.endsWith(".paypal.com") || host.endsWith(".paypalobjects.com");
367
- } catch {
368
- return false;
369
- }
370
- }
371
- function extractAuth(data) {
372
- if (!data) return {};
373
- if (typeof data === "object") {
374
- const obj = data;
375
- const authCode = obj.authCode ?? obj.auth_code ?? obj.authcode ?? obj.code;
376
- const sharedId = obj.sharedId ?? obj.shared_id ?? obj.sharedid;
377
- if (authCode && sharedId) {
378
- return { authCode: String(authCode), sharedId: String(sharedId) };
379
- }
380
- for (const key of ["data", "payload", "message", "detail", "body", "params"]) {
381
- if (obj[key] && typeof obj[key] === "object") {
382
- const inner = extractAuth(obj[key]);
383
- if (inner.authCode && inner.sharedId) return inner;
384
- }
385
- if (typeof obj[key] === "string") {
386
- const inner = extractAuth(obj[key]);
387
- if (inner.authCode && inner.sharedId) return inner;
388
- }
389
- }
390
- return {};
391
- }
392
- if (typeof data === "string") {
393
- const s = data.trim();
394
- if (!s) return {};
395
- if (s.startsWith("{") || s.startsWith("[")) {
396
- try {
397
- return extractAuth(JSON.parse(s));
398
- } catch {
399
- }
400
- }
401
- if (/auth_?code/i.test(s) && /shared_?id/i.test(s)) {
402
- try {
403
- const sp = new URLSearchParams(s.replace(/^[?#]/, ""));
404
- const authCode = sp.get("authCode") || sp.get("auth_code") || void 0;
405
- const sharedId = sp.get("sharedId") || sp.get("shared_id") || void 0;
406
- if (authCode && sharedId) return { authCode, sharedId };
407
- } catch {
408
- }
409
- }
410
- }
411
- return {};
412
- }
413
360
  function PayPalConnectionPage() {
414
361
  const [env, setEnv] = react.useState("live");
415
362
  const [envReady, setEnvReady] = react.useState(false);
416
363
  const [connState, setConnState] = react.useState("loading");
417
364
  const [error, setError] = react.useState(null);
418
- const [popupBlocked, setPopupBlocked] = react.useState(false);
419
365
  const [finalUrl, setFinalUrl] = react.useState("");
420
366
  const [showManual, setShowManual] = react.useState(false);
421
367
  const [clientId, setClientId] = react.useState("");
@@ -427,17 +373,11 @@ function PayPalConnectionPage() {
427
373
  const errorLogRef = react.useRef(null);
428
374
  const runIdRef = react.useRef(0);
429
375
  const currentRunId = react.useRef(0);
376
+ const completedRef = react.useRef(false);
430
377
  const envRef = react.useRef(env);
431
378
  envRef.current = env;
432
- const finalUrlRef = react.useRef(finalUrl);
433
- finalUrlRef.current = finalUrl;
434
- const connStateRef = react.useRef(connState);
435
- connStateRef.current = connState;
436
- const inProgressRef = react.useRef(onboardingInProgress);
437
- inProgressRef.current = onboardingInProgress;
438
- const popupRef = react.useRef(null);
439
- const pollRef = react.useRef(null);
440
- const completedRef = react.useRef(false);
379
+ const pollTimerRef = react.useRef(null);
380
+ const pollAttemptsRef = react.useRef(0);
441
381
  const ppBtnMeasureRef = react.useRef(null);
442
382
  const [ppBtnWidth, setPpBtnWidth] = react.useState(null);
443
383
  const canSaveManual = react.useMemo(() => {
@@ -447,44 +387,72 @@ function PayPalConnectionPage() {
447
387
  setConnState("error");
448
388
  setError(msg);
449
389
  }, []);
450
- const stopPoll = react.useCallback(() => {
451
- if (pollRef.current) {
452
- clearInterval(pollRef.current);
453
- 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 {
454
396
  }
455
- }, []);
456
- const openOnboardingPopup = react.useCallback((url) => {
457
- const w = 450;
458
- const h = 600;
459
- const baseLeft = typeof window.screenX === "number" ? window.screenX : window.screenLeft || 0;
460
- const baseTop = typeof window.screenY === "number" ? window.screenY : window.screenTop || 0;
461
- const outerW = window.outerWidth || document.documentElement.clientWidth || 1024;
462
- const outerH = window.outerHeight || document.documentElement.clientHeight || 768;
463
- const left = Math.round(baseLeft + Math.max(0, (outerW - w) / 2));
464
- const top = Math.round(baseTop + Math.max(0, (outerH - h) / 2));
465
- const features = `popup=yes,width=${w},height=${h},left=${left},top=${top},scrollbars=yes,resizable=yes`;
466
- let popup = null;
467
397
  try {
468
- 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();
469
400
  } catch {
470
- popup = null;
471
401
  }
472
- if (popup) {
473
- 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) => {
474
412
  try {
475
- 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;
476
430
  } catch {
431
+ return false;
477
432
  }
478
- }
479
- return popup;
480
- }, []);
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]);
481
451
  const completeOnboarding = react.useCallback(
482
452
  async (authCode, sharedId) => {
483
- var _a, _b, _c, _d, _e, _f, _g, _h;
484
453
  if (!authCode || !sharedId) return;
485
454
  if (completedRef.current) return;
486
455
  completedRef.current = true;
487
- stopPoll();
488
456
  try {
489
457
  window.onbeforeunload = null;
490
458
  } catch {
@@ -493,7 +461,6 @@ function PayPalConnectionPage() {
493
461
  setOnboardingInProgress(true);
494
462
  setConnState("loading");
495
463
  setError(null);
496
- setPopupBlocked(false);
497
464
  try {
498
465
  const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
499
466
  method: "POST",
@@ -505,32 +472,20 @@ function PayPalConnectionPage() {
505
472
  const txt = await res.text().catch(() => "");
506
473
  throw new Error(txt || `Onboarding exchange failed (${res.status})`);
507
474
  }
508
- try {
509
- (_a = popupRef.current) == null ? void 0 : _a.close();
510
- } catch {
511
- }
512
- try {
513
- 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;
514
- if (typeof close1 === "function") close1();
515
- } catch {
516
- }
517
- try {
518
- 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;
519
- if (typeof close2 === "function") close2();
520
- } catch {
521
- }
475
+ closeMiniBrowser();
522
476
  clearCachedUrl(activeEnv);
523
477
  try {
524
- const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${activeEnv}`, {
525
- method: "GET",
526
- credentials: "include"
527
- });
478
+ const statusRes = await fetch(
479
+ `${STATUS_ENDPOINT}?environment=${activeEnv}`,
480
+ { method: "GET", credentials: "include" }
481
+ );
528
482
  const refreshedStatus = await statusRes.json().catch(() => ({}));
529
483
  setStatusInfo(refreshedStatus || null);
530
484
  } catch {
531
485
  }
532
486
  setConnState("connected");
533
487
  setShowManual(false);
488
+ stopStatusPolling();
534
489
  } catch (e) {
535
490
  completedRef.current = false;
536
491
  console.error(e);
@@ -540,42 +495,72 @@ function PayPalConnectionPage() {
540
495
  setOnboardingInProgress(false);
541
496
  }
542
497
  },
543
- [stopPoll]
498
+ [closeMiniBrowser, stopStatusPolling]
544
499
  );
545
- const startStatusPoll = react.useCallback(() => {
546
- stopPoll();
547
- let ticks = 0;
548
- pollRef.current = setInterval(async () => {
549
- var _a;
550
- ticks += 1;
551
- if (completedRef.current || ticks > 150) {
552
- stopPoll();
553
- return;
554
- }
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") {
555
505
  try {
556
- const r = await fetch(`${STATUS_ENDPOINT}?environment=${envRef.current}`, {
557
- method: "GET",
558
- credentials: "include"
559
- });
560
- const st = await r.json().catch(() => ({}));
561
- if ((st == null ? void 0 : st.status) === "connected" && (st == null ? void 0 : st.seller_client_id_present) === true) {
562
- completedRef.current = true;
563
- stopPoll();
564
- try {
565
- (_a = popupRef.current) == null ? void 0 : _a.close();
566
- } catch {
567
- }
568
- clearCachedUrl(envRef.current);
569
- setStatusInfo(st);
570
- setConnState("connected");
571
- setShowManual(false);
572
- setOnboardingInProgress(false);
573
- }
574
- } catch {
506
+ init();
507
+ } catch (e) {
508
+ console.error("[paypal] partner.js init failed:", e);
509
+ }
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;
575
538
  }
576
- }, 2e3);
577
- }, [stopPoll]);
578
- const generateLinkAndReload = react.useCallback(
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(
579
564
  async (targetEnv, runId) => {
580
565
  if (initLoaderRef.current) {
581
566
  const loaderText = initLoaderRef.current.querySelector("#loader-text");
@@ -597,19 +582,14 @@ function PayPalConnectionPage() {
597
582
  return;
598
583
  }
599
584
  const url = href + (href.includes("?") ? "&" : "?") + "displayMode=minibrowser";
600
- const persisted = writeCachedUrl(targetEnv, url);
601
- if (persisted) {
602
- window.location.reload();
603
- return;
604
- }
605
- setFinalUrl(url);
606
- setConnState("ready");
585
+ writeCachedUrl(targetEnv, url);
586
+ activatePayPal(url, runId);
607
587
  } catch {
608
588
  if (runId !== currentRunId.current) return;
609
589
  showError("Unable to connect to service.");
610
590
  }
611
591
  },
612
- [showError]
592
+ [activatePayPal, showError]
613
593
  );
614
594
  react.useEffect(() => {
615
595
  let alive = true;
@@ -633,7 +613,6 @@ function PayPalConnectionPage() {
633
613
  const run = async () => {
634
614
  setConnState("loading");
635
615
  setError(null);
636
- setPopupBlocked(false);
637
616
  setFinalUrl("");
638
617
  try {
639
618
  const r = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
@@ -655,135 +634,43 @@ function PayPalConnectionPage() {
655
634
  if (cancelled || runId !== currentRunId.current) return;
656
635
  const cached = readCachedUrl(env);
657
636
  if (cached) {
658
- setFinalUrl(cached);
659
- setConnState("ready");
637
+ activatePayPal(cached, runId);
660
638
  return;
661
639
  }
662
- await generateLinkAndReload(env, runId);
640
+ await fetchFreshLink(env, runId);
663
641
  };
664
642
  run();
665
643
  return () => {
666
644
  cancelled = true;
667
645
  };
668
- }, [env, envReady, generateLinkAndReload]);
669
- react.useEffect(() => {
670
- var _a, _b, _c;
671
- if (connState !== "ready" || !finalUrl) return;
672
- const scriptUrl = PARTNER_JS_URLS[env];
673
- let cancelled = false;
674
- let pollTimer;
675
- const initPartner = (attempt = 0) => {
676
- var _a2, _b2;
677
- if (cancelled) return;
678
- const signup = (_b2 = (_a2 = window.PAYPAL) == null ? void 0 : _a2.apps) == null ? void 0 : _b2.Signup;
679
- 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;
680
- if (target == null ? void 0 : target.init) {
681
- try {
682
- target.init();
683
- } catch (e) {
684
- console.error("[paypal] partner.js init failed:", e);
685
- }
686
- return;
687
- }
688
- if (typeof (signup == null ? void 0 : signup.render) === "function") {
689
- try {
690
- signup.render();
691
- } catch (e) {
692
- console.error("[paypal] partner.js render failed:", e);
693
- }
694
- return;
695
- }
696
- if (attempt < 40) {
697
- pollTimer = setTimeout(() => initPartner(attempt + 1), 50);
698
- return;
699
- }
700
- try {
701
- document.dispatchEvent(new Event("DOMContentLoaded"));
702
- } catch {
703
- }
704
- };
705
- const existingScript = document.getElementById("paypal-partner-js");
706
- if (existingScript) {
707
- (_a = existingScript.parentNode) == null ? void 0 : _a.removeChild(existingScript);
708
- }
709
- if ((_c = (_b = window.PAYPAL) == null ? void 0 : _b.apps) == null ? void 0 : _c.Signup) {
710
- try {
711
- delete window.PAYPAL.apps.Signup;
712
- } catch {
713
- }
714
- }
715
- const ppScript = document.createElement("script");
716
- ppScript.id = "paypal-partner-js";
717
- ppScript.src = scriptUrl;
718
- ppScript.async = true;
719
- ppScript.onload = () => initPartner();
720
- document.body.appendChild(ppScript);
721
- return () => {
722
- cancelled = true;
723
- if (pollTimer) clearTimeout(pollTimer);
724
- if (ppScript.parentNode) ppScript.parentNode.removeChild(ppScript);
725
- };
726
- }, [connState, finalUrl, env]);
727
- react.useEffect(() => {
728
- const onMessage = (ev) => {
729
- let serialized = "";
730
- try {
731
- serialized = typeof ev.data === "string" ? ev.data : JSON.stringify(ev.data);
732
- } catch {
733
- serialized = String(ev.data);
734
- }
735
- const looksRelevant = isPayPalOrigin(ev.origin) || /auth_?code|shared_?id|onboard|merchantId/i.test(serialized || "");
736
- if (looksRelevant) {
737
- console.info(
738
- "[PayPal onboarding] message from",
739
- ev.origin,
740
- "·",
741
- (serialized || "").slice(0, 500)
742
- );
743
- }
744
- if (!isPayPalOrigin(ev.origin)) return;
745
- const { authCode, sharedId } = extractAuth(ev.data);
746
- if (authCode && sharedId) {
747
- completeOnboarding(authCode, sharedId);
748
- }
749
- };
750
- window.addEventListener("message", onMessage);
751
- return () => window.removeEventListener("message", onMessage);
752
- }, [completeOnboarding]);
646
+ }, [env, envReady, fetchFreshLink, activatePayPal]);
753
647
  react.useLayoutEffect(() => {
754
648
  window.onboardingCallback = (authCode, sharedId) => {
755
- completeOnboarding(authCode, sharedId);
649
+ void completeOnboarding(authCode, sharedId);
756
650
  };
757
651
  return () => {
758
652
  window.onboardingCallback = void 0;
759
653
  };
760
654
  }, [completeOnboarding]);
761
655
  react.useEffect(() => {
762
- const onCaptureClick = (e) => {
763
- const btn = paypalButtonRef.current;
764
- if (!btn) return;
765
- const target = e.target;
766
- if (!target || !btn.contains(target)) return;
767
- e.preventDefault();
768
- e.stopImmediatePropagation();
769
- if (connStateRef.current !== "ready" || !finalUrlRef.current || inProgressRef.current) {
770
- return;
771
- }
772
- completedRef.current = false;
773
- const popup = openOnboardingPopup(finalUrlRef.current);
774
- if (!popup) {
775
- setPopupBlocked(true);
776
- return;
777
- }
778
- setPopupBlocked(false);
779
- 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
+ })();
780
669
  };
781
- document.addEventListener("click", onCaptureClick, true);
782
- return () => document.removeEventListener("click", onCaptureClick, true);
783
- }, [openOnboardingPopup, startStatusPoll]);
784
- react.useEffect(() => {
785
- return () => stopPoll();
786
- }, [stopPoll]);
670
+ window.addEventListener("message", onMessage);
671
+ return () => window.removeEventListener("message", onMessage);
672
+ }, [completeOnboarding, refreshStatusAndMaybeConnect]);
673
+ react.useEffect(() => stopStatusPolling, [stopStatusPolling]);
787
674
  react.useLayoutEffect(() => {
788
675
  const el = ppBtnMeasureRef.current;
789
676
  if (!el) return;
@@ -805,7 +692,12 @@ function PayPalConnectionPage() {
805
692
  };
806
693
  }, [connState, env, finalUrl]);
807
694
  const handleConnectClick = (e) => {
808
- e.preventDefault();
695
+ if (connState !== "ready" || !finalUrl || onboardingInProgress) {
696
+ e.preventDefault();
697
+ return;
698
+ }
699
+ completedRef.current = false;
700
+ startStatusPolling();
809
701
  };
810
702
  const handleSaveManual = async () => {
811
703
  if (!canSaveManual || onboardingInProgress) return;
@@ -847,6 +739,7 @@ function PayPalConnectionPage() {
847
739
  const handleDisconnect = async () => {
848
740
  if (onboardingInProgress) return;
849
741
  if (!window.confirm("Disconnect PayPal for this environment?")) return;
742
+ stopStatusPolling();
850
743
  setOnboardingInProgress(true);
851
744
  setConnState("loading");
852
745
  setError(null);
@@ -867,7 +760,7 @@ function PayPalConnectionPage() {
867
760
  clearCachedUrl(env);
868
761
  completedRef.current = false;
869
762
  currentRunId.current = ++runIdRef.current;
870
- await generateLinkAndReload(env, currentRunId.current);
763
+ await fetchFreshLink(env, currentRunId.current);
871
764
  } catch (e) {
872
765
  console.error(e);
873
766
  setConnState("error");
@@ -879,9 +772,9 @@ function PayPalConnectionPage() {
879
772
  const handleEnvChange = async (e) => {
880
773
  const next = e.target.value;
881
774
  if (next === env || onboardingInProgress) return;
775
+ stopStatusPolling();
882
776
  completedRef.current = false;
883
777
  clearCachedUrl();
884
- setPopupBlocked(false);
885
778
  setConnState("loading");
886
779
  try {
887
780
  await fetch(ENVIRONMENT_ENDPOINT, {
@@ -915,7 +808,19 @@ function PayPalConnectionPage() {
915
808
  ) }),
916
809
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium pt-2", children: env === "sandbox" ? "Connect to PayPal (Sandbox)" : "Connect to PayPal" }),
917
810
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xl", children: connState === "connected" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
918
- /* @__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
+ ] }),
919
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: [
920
825
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-ui-fg-base", children: "Connected PayPal account" }),
921
826
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1", children: [
@@ -956,7 +861,6 @@ function PayPalConnectionPage() {
956
861
  ppBtnMeasureRef.current = node;
957
862
  },
958
863
  id: "paypal-button",
959
- target: POPUP_NAME,
960
864
  "data-paypal-button": "true",
961
865
  href: finalUrl || "#",
962
866
  "data-paypal-onboard-complete": "onboardingCallback",
@@ -970,7 +874,6 @@ function PayPalConnectionPage() {
970
874
  children: "Connect to PayPal"
971
875
  }
972
876
  ),
973
- 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." }),
974
877
  /* @__PURE__ */ jsxRuntime.jsx(
975
878
  "div",
976
879
  {
@@ -1126,9 +1029,6 @@ function PayPalConnectionPage() {
1126
1029
  ` })
1127
1030
  ] });
1128
1031
  }
1129
- function PayPalGooglePayPage() {
1130
- return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
1131
- }
1132
1032
  function PayPalPayLaterMessagingPage() {
1133
1033
  return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
1134
1034
  }
@@ -1350,22 +1250,22 @@ const routeModule = {
1350
1250
  Component: AdditionalSettingsTab,
1351
1251
  path: "/settings/paypal/additional-settings"
1352
1252
  },
1353
- {
1354
- Component: PayPalApplePayPage,
1355
- path: "/settings/paypal/apple-pay"
1356
- },
1357
1253
  {
1358
1254
  Component: AdvancedCardPaymentsTab,
1359
1255
  path: "/settings/paypal/advanced-card-payments"
1360
1256
  },
1361
- {
1362
- Component: PayPalConnectionPage,
1363
- path: "/settings/paypal/connection"
1364
- },
1365
1257
  {
1366
1258
  Component: PayPalGooglePayPage,
1367
1259
  path: "/settings/paypal/google-pay"
1368
1260
  },
1261
+ {
1262
+ Component: PayPalApplePayPage,
1263
+ path: "/settings/paypal/apple-pay"
1264
+ },
1265
+ {
1266
+ Component: PayPalConnectionPage,
1267
+ path: "/settings/paypal/connection"
1268
+ },
1369
1269
  {
1370
1270
  Component: PayPalPayLaterMessagingPage,
1371
1271
  path: "/settings/paypal/pay-later-messaging"