@easypayment/medusa-payment-paypal 0.9.22 → 0.9.25

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 (54) hide show
  1. package/.medusa/server/src/admin/index.js +264 -80
  2. package/.medusa/server/src/admin/index.mjs +264 -80
  3. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts.map +1 -1
  4. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js +340 -146
  5. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js.map +1 -1
  6. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -1
  7. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js +6 -8
  8. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js.map +1 -1
  9. package/.medusa/server/src/api/admin/paypal/environment/route.d.ts.map +1 -1
  10. package/.medusa/server/src/api/admin/paypal/environment/route.js +11 -3
  11. package/.medusa/server/src/api/admin/paypal/environment/route.js.map +1 -1
  12. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts +7 -0
  13. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts.map +1 -1
  14. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js +7 -0
  15. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js.map +1 -1
  16. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.d.ts.map +1 -1
  17. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js +4 -1
  18. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js.map +1 -1
  19. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -1
  20. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js +6 -8
  21. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js.map +1 -1
  22. package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts.map +1 -1
  23. package/.medusa/server/src/api/store/paypal/capture-order/route.js +11 -0
  24. package/.medusa/server/src/api/store/paypal/capture-order/route.js.map +1 -1
  25. package/.medusa/server/src/api/store/paypal/onboard-return/route.d.ts +23 -0
  26. package/.medusa/server/src/api/store/paypal/onboard-return/route.d.ts.map +1 -0
  27. package/.medusa/server/src/api/store/paypal/onboard-return/route.js +63 -0
  28. package/.medusa/server/src/api/store/paypal/onboard-return/route.js.map +1 -0
  29. package/.medusa/server/src/api/store/paypal-complete/route.d.ts.map +1 -1
  30. package/.medusa/server/src/api/store/paypal-complete/route.js +2 -4
  31. package/.medusa/server/src/api/store/paypal-complete/route.js.map +1 -1
  32. package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts.map +1 -1
  33. package/.medusa/server/src/modules/paypal/payment-provider/card-service.js +55 -34
  34. package/.medusa/server/src/modules/paypal/payment-provider/card-service.js.map +1 -1
  35. package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
  36. package/.medusa/server/src/modules/paypal/service.js +5 -1
  37. package/.medusa/server/src/modules/paypal/service.js.map +1 -1
  38. package/.medusa/server/src/modules/paypal/utils/core-workflow.d.ts +32 -0
  39. package/.medusa/server/src/modules/paypal/utils/core-workflow.d.ts.map +1 -0
  40. package/.medusa/server/src/modules/paypal/utils/core-workflow.js +40 -0
  41. package/.medusa/server/src/modules/paypal/utils/core-workflow.js.map +1 -0
  42. package/package.json +1 -1
  43. package/src/admin/routes/settings/paypal/connection/page.tsx +364 -160
  44. package/src/api/admin/payment-collections/[id]/payment-sessions/route.ts +6 -8
  45. package/src/api/admin/paypal/environment/route.ts +10 -3
  46. package/src/api/admin/paypal/onboard-complete/route.ts +7 -0
  47. package/src/api/admin/paypal/onboarding-status/route.ts +4 -1
  48. package/src/api/store/payment-collections/[id]/payment-sessions/route.ts +6 -8
  49. package/src/api/store/paypal/capture-order/route.ts +15 -0
  50. package/src/api/store/paypal/onboard-return/route.ts +63 -0
  51. package/src/api/store/paypal-complete/route.ts +6 -4
  52. package/src/modules/paypal/payment-provider/card-service.ts +72 -38
  53. package/src/modules/paypal/service.ts +5 -1
  54. package/src/modules/paypal/utils/core-workflow.ts +46 -0
@@ -363,12 +363,62 @@ function clearCachedUrl(env) {
363
363
  } catch {
364
364
  }
365
365
  }
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
+ }
366
416
  function PayPalConnectionPage() {
367
417
  const [env, setEnv] = react.useState("live");
368
418
  const [envReady, setEnvReady] = react.useState(false);
369
419
  const [connState, setConnState] = react.useState("loading");
370
- const [partnerReady, setPartnerReady] = react.useState(false);
371
420
  const [error, setError] = react.useState(null);
421
+ const [popupBlocked, setPopupBlocked] = react.useState(false);
372
422
  const [finalUrl, setFinalUrl] = react.useState("");
373
423
  const [showManual, setShowManual] = react.useState(false);
374
424
  const [clientId, setClientId] = react.useState("");
@@ -376,11 +426,20 @@ function PayPalConnectionPage() {
376
426
  const [statusInfo, setStatusInfo] = react.useState(null);
377
427
  const [onboardingInProgress, setOnboardingInProgress] = react.useState(false);
378
428
  const initLoaderRef = react.useRef(null);
429
+ const paypalButtonRef = react.useRef(null);
379
430
  const errorLogRef = react.useRef(null);
380
431
  const runIdRef = react.useRef(0);
381
432
  const currentRunId = react.useRef(0);
382
433
  const envRef = react.useRef(env);
383
434
  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);
384
443
  const completedRef = react.useRef(false);
385
444
  const ppBtnMeasureRef = react.useRef(null);
386
445
  const [ppBtnWidth, setPpBtnWidth] = react.useState(null);
@@ -391,13 +450,139 @@ function PayPalConnectionPage() {
391
450
  setConnState("error");
392
451
  setError(msg);
393
452
  }, []);
453
+ const stopPoll = react.useCallback(() => {
454
+ if (pollRef.current) {
455
+ clearInterval(pollRef.current);
456
+ pollRef.current = null;
457
+ }
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
+ try {
471
+ popup = window.open(url, POPUP_NAME, features);
472
+ } catch {
473
+ popup = null;
474
+ }
475
+ if (popup) {
476
+ popupRef.current = popup;
477
+ try {
478
+ popup.focus();
479
+ } catch {
480
+ }
481
+ }
482
+ return popup;
483
+ }, []);
484
+ const completeOnboarding = react.useCallback(
485
+ async (authCode, sharedId) => {
486
+ var _a, _b, _c, _d, _e, _f, _g, _h;
487
+ if (!authCode || !sharedId) return;
488
+ if (completedRef.current) return;
489
+ completedRef.current = true;
490
+ stopPoll();
491
+ try {
492
+ window.onbeforeunload = null;
493
+ } catch {
494
+ }
495
+ const activeEnv = envRef.current === "sandbox" ? "sandbox" : "live";
496
+ setOnboardingInProgress(true);
497
+ setConnState("loading");
498
+ setError(null);
499
+ setPopupBlocked(false);
500
+ try {
501
+ const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
502
+ method: "POST",
503
+ headers: { "content-type": "application/json" },
504
+ credentials: "include",
505
+ body: JSON.stringify({ authCode, sharedId, env: activeEnv })
506
+ });
507
+ if (!res.ok) {
508
+ const txt = await res.text().catch(() => "");
509
+ throw new Error(txt || `Onboarding exchange failed (${res.status})`);
510
+ }
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
+ }
525
+ clearCachedUrl(activeEnv);
526
+ try {
527
+ const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${activeEnv}`, {
528
+ method: "GET",
529
+ credentials: "include"
530
+ });
531
+ const refreshedStatus = await statusRes.json().catch(() => ({}));
532
+ setStatusInfo(refreshedStatus || null);
533
+ } catch {
534
+ }
535
+ setConnState("connected");
536
+ setShowManual(false);
537
+ } catch (e) {
538
+ completedRef.current = false;
539
+ console.error(e);
540
+ setConnState("error");
541
+ setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
542
+ } finally {
543
+ setOnboardingInProgress(false);
544
+ }
545
+ },
546
+ [stopPoll]
547
+ );
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
+ }
558
+ 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 {
578
+ }
579
+ }, 2e3);
580
+ }, [stopPoll]);
394
581
  const generateLinkAndReload = react.useCallback(
395
582
  async (targetEnv, runId) => {
396
583
  if (initLoaderRef.current) {
397
584
  const loaderText = initLoaderRef.current.querySelector("#loader-text");
398
- if (loaderText) {
399
- loaderText.textContent = "Generating onboarding session...";
400
- }
585
+ if (loaderText) loaderText.textContent = "Generating onboarding session...";
401
586
  }
402
587
  try {
403
588
  const res = await fetch(SERVICE_URL, {
@@ -451,6 +636,7 @@ function PayPalConnectionPage() {
451
636
  const run = async () => {
452
637
  setConnState("loading");
453
638
  setError(null);
639
+ setPopupBlocked(false);
454
640
  setFinalUrl("");
455
641
  try {
456
642
  const r = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
@@ -489,13 +675,6 @@ function PayPalConnectionPage() {
489
675
  const scriptUrl = PARTNER_JS_URLS[env];
490
676
  let cancelled = false;
491
677
  let pollTimer;
492
- setPartnerReady(false);
493
- const readyFallback = setTimeout(() => {
494
- if (!cancelled) setPartnerReady(true);
495
- }, 6e3);
496
- const markReady = () => {
497
- if (!cancelled) setPartnerReady(true);
498
- };
499
678
  const initPartner = (attempt = 0) => {
500
679
  var _a2, _b2;
501
680
  if (cancelled) return;
@@ -507,7 +686,6 @@ function PayPalConnectionPage() {
507
686
  } catch (e) {
508
687
  console.error("[paypal] partner.js init failed:", e);
509
688
  }
510
- markReady();
511
689
  return;
512
690
  }
513
691
  if (typeof (signup == null ? void 0 : signup.render) === "function") {
@@ -516,7 +694,6 @@ function PayPalConnectionPage() {
516
694
  } catch (e) {
517
695
  console.error("[paypal] partner.js render failed:", e);
518
696
  }
519
- markReady();
520
697
  return;
521
698
  }
522
699
  if (attempt < 40) {
@@ -527,7 +704,6 @@ function PayPalConnectionPage() {
527
704
  document.dispatchEvent(new Event("DOMContentLoaded"));
528
705
  } catch {
529
706
  }
530
- markReady();
531
707
  };
532
708
  const existingScript = document.getElementById("paypal-partner-js");
533
709
  if (existingScript) {
@@ -544,74 +720,54 @@ function PayPalConnectionPage() {
544
720
  ppScript.src = scriptUrl;
545
721
  ppScript.async = true;
546
722
  ppScript.onload = () => initPartner();
547
- ppScript.onerror = () => markReady();
548
723
  document.body.appendChild(ppScript);
549
724
  return () => {
550
725
  cancelled = true;
551
- clearTimeout(readyFallback);
552
- if (pollTimer) {
553
- clearTimeout(pollTimer);
554
- }
555
- if (ppScript.parentNode) {
556
- ppScript.parentNode.removeChild(ppScript);
557
- }
726
+ if (pollTimer) clearTimeout(pollTimer);
727
+ if (ppScript.parentNode) ppScript.parentNode.removeChild(ppScript);
558
728
  };
559
729
  }, [connState, finalUrl, env]);
560
- const completeOnboarding = react.useCallback(async (authCode, sharedId) => {
561
- var _a, _b, _c, _d, _e, _f, _g;
562
- if (!authCode || !sharedId) return;
563
- if (completedRef.current) return;
564
- completedRef.current = true;
565
- try {
566
- window.onbeforeunload = null;
567
- } catch {
568
- }
569
- const activeEnv = envRef.current === "sandbox" ? "sandbox" : "live";
570
- setOnboardingInProgress(true);
571
- setConnState("loading");
572
- setError(null);
573
- try {
574
- const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
575
- method: "POST",
576
- headers: { "content-type": "application/json" },
577
- credentials: "include",
578
- body: JSON.stringify({ authCode, sharedId, env: activeEnv })
579
- });
580
- if (!res.ok) {
581
- const txt = await res.text().catch(() => "");
582
- throw new Error(txt || `Onboarding exchange failed (${res.status})`);
583
- }
730
+ react.useEffect(() => {
731
+ const onMessage = (ev) => {
732
+ var _a;
733
+ let serialized = "";
584
734
  try {
585
- 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;
586
- if (typeof close1 === "function") close1();
735
+ serialized = typeof ev.data === "string" ? ev.data : JSON.stringify(ev.data);
587
736
  } catch {
737
+ serialized = String(ev.data);
588
738
  }
589
- try {
590
- 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;
591
- if (typeof close2 === "function") close2();
592
- } catch {
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
+ );
593
747
  }
594
- clearCachedUrl(activeEnv);
595
- try {
596
- const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${activeEnv}`, {
597
- method: "GET",
598
- credentials: "include"
599
- });
600
- const refreshedStatus = await statusRes.json().catch(() => ({}));
601
- setStatusInfo(refreshedStatus || null);
602
- } catch {
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;
603
761
  }
604
- setConnState("connected");
605
- setShowManual(false);
606
- } catch (e) {
607
- completedRef.current = false;
608
- console.error(e);
609
- setConnState("error");
610
- setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
611
- } finally {
612
- setOnboardingInProgress(false);
613
- }
614
- }, []);
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]);
615
771
  react.useLayoutEffect(() => {
616
772
  window.onboardingCallback = (authCode, sharedId) => {
617
773
  completeOnboarding(authCode, sharedId);
@@ -620,6 +776,31 @@ function PayPalConnectionPage() {
620
776
  window.onboardingCallback = void 0;
621
777
  };
622
778
  }, [completeOnboarding]);
779
+ 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();
797
+ };
798
+ document.addEventListener("click", onCaptureClick, true);
799
+ return () => document.removeEventListener("click", onCaptureClick, true);
800
+ }, [openOnboardingPopup, startStatusPoll]);
801
+ react.useEffect(() => {
802
+ return () => stopPoll();
803
+ }, [stopPoll]);
623
804
  react.useLayoutEffect(() => {
624
805
  const el = ppBtnMeasureRef.current;
625
806
  if (!el) return;
@@ -640,6 +821,9 @@ function PayPalConnectionPage() {
640
821
  else window.removeEventListener("resize", update);
641
822
  };
642
823
  }, [connState, env, finalUrl]);
824
+ const handleConnectClick = (e) => {
825
+ e.preventDefault();
826
+ };
643
827
  const handleSaveManual = async () => {
644
828
  if (!canSaveManual || onboardingInProgress) return;
645
829
  setOnboardingInProgress(true);
@@ -714,6 +898,7 @@ function PayPalConnectionPage() {
714
898
  if (next === env || onboardingInProgress) return;
715
899
  completedRef.current = false;
716
900
  clearCachedUrl();
901
+ setPopupBlocked(false);
717
902
  setConnState("loading");
718
903
  try {
719
904
  await fetch(ENVIRONMENT_ENDPOINT, {
@@ -784,6 +969,7 @@ function PayPalConnectionPage() {
784
969
  "a",
785
970
  {
786
971
  ref: (node) => {
972
+ paypalButtonRef.current = node;
787
973
  ppBtnMeasureRef.current = node;
788
974
  },
789
975
  id: "paypal-button",
@@ -791,19 +977,17 @@ function PayPalConnectionPage() {
791
977
  "data-paypal-button": "true",
792
978
  href: finalUrl || "#",
793
979
  "data-paypal-onboard-complete": "onboardingCallback",
794
- "aria-disabled": onboardingInProgress || !partnerReady,
980
+ onClick: handleConnectClick,
795
981
  className: "transition-fg relative inline-flex w-fit items-center justify-center overflow-hidden rounded-md outline-none no-underline shadow-buttons-neutral text-ui-fg-base bg-ui-button-neutral after:transition-fg after:absolute after:inset-0 after:content-[''] after:button-neutral-gradient hover:bg-ui-button-neutral-hover hover:after:button-neutral-hover-gradient active:bg-ui-button-neutral-pressed active:after:button-neutral-pressed-gradient focus-visible:shadow-buttons-neutral-focus disabled:bg-ui-bg-disabled disabled:border-ui-border-base disabled:text-ui-fg-disabled disabled:shadow-buttons-neutral disabled:after:hidden txt-compact-small-plus px-3 py-1.5",
796
982
  style: {
797
- cursor: onboardingInProgress || !partnerReady ? "not-allowed" : "pointer",
798
- opacity: onboardingInProgress || !partnerReady ? 0.6 : 1,
799
- // Keep the anchor mounted (partner.js must find it to bind)
800
- // but un-clickable until partner.js has taken over the
801
- // click — so it can never open a tab via the native href.
802
- pointerEvents: onboardingInProgress || !partnerReady ? "none" : "auto"
983
+ cursor: onboardingInProgress ? "not-allowed" : "pointer",
984
+ opacity: onboardingInProgress ? 0.6 : 1,
985
+ pointerEvents: onboardingInProgress ? "none" : "auto"
803
986
  },
804
- children: partnerReady || onboardingInProgress ? "Connect to PayPal" : "Preparing PayPal…"
987
+ children: "Connect to PayPal"
805
988
  }
806
989
  ),
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." }),
807
991
  /* @__PURE__ */ jsxRuntime.jsx(
808
992
  "div",
809
993
  {