@easypayment/medusa-payment-paypal 0.9.17 → 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.
Files changed (44) hide show
  1. package/.medusa/server/src/admin/index.js +326 -130
  2. package/.medusa/server/src/admin/index.mjs +326 -130
  3. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts +4 -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 +434 -113
  6. package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js.map +1 -1
  7. package/.medusa/server/src/api/admin/paypal/onboarding-link/route.d.ts.map +1 -1
  8. package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js +4 -0
  9. package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js.map +1 -1
  10. package/.medusa/server/src/api/middlewares.d.ts.map +1 -1
  11. package/.medusa/server/src/api/middlewares.js +9 -0
  12. package/.medusa/server/src/api/middlewares.js.map +1 -1
  13. package/.medusa/server/src/api/store/paypal/webhook/route.d.ts.map +1 -1
  14. package/.medusa/server/src/api/store/paypal/webhook/route.js +13 -8
  15. package/.medusa/server/src/api/store/paypal/webhook/route.js.map +1 -1
  16. package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts.map +1 -1
  17. package/.medusa/server/src/modules/paypal/payment-provider/service.js +54 -31
  18. package/.medusa/server/src/modules/paypal/payment-provider/service.js.map +1 -1
  19. package/.medusa/server/src/modules/paypal/payment-provider/status-utils.d.ts +32 -0
  20. package/.medusa/server/src/modules/paypal/payment-provider/status-utils.d.ts.map +1 -0
  21. package/.medusa/server/src/modules/paypal/payment-provider/status-utils.js +65 -0
  22. package/.medusa/server/src/modules/paypal/payment-provider/status-utils.js.map +1 -0
  23. package/.medusa/server/src/modules/paypal/service.d.ts +13 -1
  24. package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
  25. package/.medusa/server/src/modules/paypal/service.js +19 -4
  26. package/.medusa/server/src/modules/paypal/service.js.map +1 -1
  27. package/.medusa/server/src/modules/paypal/utils/secret-crypto.d.ts +2 -1
  28. package/.medusa/server/src/modules/paypal/utils/secret-crypto.d.ts.map +1 -1
  29. package/.medusa/server/src/modules/paypal/utils/secret-crypto.js +82 -26
  30. package/.medusa/server/src/modules/paypal/utils/secret-crypto.js.map +1 -1
  31. package/.medusa/server/src/modules/paypal/utils/webhook-verify.d.ts +28 -0
  32. package/.medusa/server/src/modules/paypal/utils/webhook-verify.d.ts.map +1 -0
  33. package/.medusa/server/src/modules/paypal/utils/webhook-verify.js +58 -0
  34. package/.medusa/server/src/modules/paypal/utils/webhook-verify.js.map +1 -0
  35. package/package.json +1 -1
  36. package/src/admin/routes/settings/paypal/connection/page.tsx +471 -127
  37. package/src/api/admin/paypal/onboarding-link/route.ts +6 -0
  38. package/src/api/middlewares.ts +9 -0
  39. package/src/api/store/paypal/webhook/route.ts +22 -8
  40. package/src/modules/paypal/payment-provider/service.ts +73 -29
  41. package/src/modules/paypal/payment-provider/status-utils.ts +59 -0
  42. package/src/modules/paypal/service.ts +24 -5
  43. package/src/modules/paypal/utils/secret-crypto.ts +90 -27
  44. package/src/modules/paypal/utils/webhook-verify.ts +61 -0
@@ -310,6 +310,9 @@ function AdvancedCardPaymentsTab() {
310
310
  function PayPalApplePayPage() {
311
311
  return /* @__PURE__ */ jsx(Navigate, { to: "/settings/paypal/connection", replace: true });
312
312
  }
313
+ function PayPalGooglePayPage() {
314
+ return /* @__PURE__ */ jsx(Navigate, { to: "/settings/paypal/connection", replace: true });
315
+ }
313
316
  const config = defineRouteConfig({
314
317
  label: "PayPal Connection"
315
318
  });
@@ -318,37 +321,97 @@ const PARTNER_JS_URLS = {
318
321
  live: "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"
319
322
  };
320
323
  const SERVICE_URL = "/admin/paypal/onboarding-link";
321
- const CACHE_KEY = "pp_onboard_cache";
324
+ const POPUP_NAME = "PPMiniBrowser";
325
+ const CACHE_PREFIX = "pp_onboard_cache";
322
326
  const CACHE_EXPIRY = 10 * 60 * 1e3;
323
327
  const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete";
324
328
  const STATUS_ENDPOINT = "/admin/paypal/status";
325
329
  const SAVE_CREDENTIALS_ENDPOINT = "/admin/paypal/save-credentials";
326
330
  const DISCONNECT_ENDPOINT = "/admin/paypal/disconnect";
327
- let cachedUrl = null;
328
- if (typeof window !== "undefined") {
331
+ const ENVIRONMENT_ENDPOINT = "/admin/paypal/environment";
332
+ const cacheKeyFor = (env) => `${CACHE_PREFIX}_${env}`;
333
+ function readCachedUrl(env) {
334
+ if (typeof window === "undefined") return null;
329
335
  try {
330
- const cached = localStorage.getItem(CACHE_KEY);
331
- if (cached) {
332
- const data = JSON.parse(cached);
333
- if ((/* @__PURE__ */ new Date()).getTime() - data.ts < CACHE_EXPIRY) {
334
- cachedUrl = data.url;
336
+ const raw = localStorage.getItem(cacheKeyFor(env));
337
+ if (!raw) return null;
338
+ const data = JSON.parse(raw);
339
+ if (data && typeof data.url === "string" && Date.now() - (Number(data.ts) || 0) < CACHE_EXPIRY) {
340
+ return data.url;
341
+ }
342
+ } catch {
343
+ }
344
+ return null;
345
+ }
346
+ function writeCachedUrl(env, url) {
347
+ try {
348
+ localStorage.setItem(cacheKeyFor(env), JSON.stringify({ url, ts: Date.now() }));
349
+ } catch {
350
+ }
351
+ }
352
+ function clearCachedUrl(env) {
353
+ try {
354
+ if (env) {
355
+ localStorage.removeItem(cacheKeyFor(env));
356
+ } else {
357
+ localStorage.removeItem(cacheKeyFor("sandbox"));
358
+ localStorage.removeItem(cacheKeyFor("live"));
359
+ }
360
+ } catch {
361
+ }
362
+ }
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;
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"]) {
381
+ if (obj[key] && typeof obj[key] === "object") {
382
+ const inner = extractAuth(obj[key]);
383
+ if (inner.authCode && inner.sharedId) return inner;
384
+ }
385
+ }
386
+ return {};
387
+ }
388
+ if (typeof data === "string") {
389
+ const s = data.trim();
390
+ if (!s) return {};
391
+ if (s.startsWith("{")) {
392
+ try {
393
+ return extractAuth(JSON.parse(s));
394
+ } catch {
395
+ }
396
+ }
397
+ if (/auth_?code/i.test(s) && /shared_?id/i.test(s)) {
398
+ try {
399
+ const sp = new URLSearchParams(s.replace(/^[?#]/, ""));
400
+ const authCode = sp.get("authCode") || sp.get("auth_code") || void 0;
401
+ const sharedId = sp.get("sharedId") || sp.get("shared_id") || void 0;
402
+ if (authCode && sharedId) return { authCode, sharedId };
403
+ } catch {
335
404
  }
336
405
  }
337
- } catch (e) {
338
- console.error("Cache read error:", e);
339
406
  }
407
+ return {};
340
408
  }
341
409
  function PayPalConnectionPage() {
342
410
  const [env, setEnv] = useState("live");
343
- useEffect(() => {
344
- fetch("/admin/paypal/environment", { method: "GET", credentials: "include" }).then((r) => r.json()).then((d) => {
345
- const v = (d == null ? void 0 : d.environment) === "sandbox" ? "sandbox" : "live";
346
- setEnv(v);
347
- }).catch(() => {
348
- });
349
- }, []);
411
+ const [envReady, setEnvReady] = useState(false);
350
412
  const [connState, setConnState] = useState("loading");
351
413
  const [error, setError] = useState(null);
414
+ const [popupBlocked, setPopupBlocked] = useState(false);
352
415
  const [finalUrl, setFinalUrl] = useState("");
353
416
  const [showManual, setShowManual] = useState(false);
354
417
  const [clientId, setClientId] = useState("");
@@ -360,6 +423,17 @@ function PayPalConnectionPage() {
360
423
  const errorLogRef = useRef(null);
361
424
  const runIdRef = useRef(0);
362
425
  const currentRunId = useRef(0);
426
+ const finalUrlRef = useRef(finalUrl);
427
+ finalUrlRef.current = finalUrl;
428
+ const connStateRef = useRef(connState);
429
+ connStateRef.current = connState;
430
+ const inProgressRef = useRef(onboardingInProgress);
431
+ inProgressRef.current = onboardingInProgress;
432
+ const envRef = useRef(env);
433
+ envRef.current = env;
434
+ const popupRef = useRef(null);
435
+ const popupPollRef = useRef(null);
436
+ const completedRef = useRef(false);
363
437
  const ppBtnMeasureRef = useRef(null);
364
438
  const [ppBtnWidth, setPpBtnWidth] = useState(null);
365
439
  const canSaveManual = useMemo(() => {
@@ -369,8 +443,124 @@ function PayPalConnectionPage() {
369
443
  setConnState("error");
370
444
  setError(msg);
371
445
  }, []);
446
+ const stopPopupPoll = useCallback(() => {
447
+ if (popupPollRef.current) {
448
+ clearInterval(popupPollRef.current);
449
+ popupPollRef.current = null;
450
+ }
451
+ }, []);
452
+ const openOnboardingPopup = useCallback(
453
+ (url) => {
454
+ const w = 450;
455
+ const h = 600;
456
+ const baseLeft = typeof window.screenX === "number" ? window.screenX : window.screenLeft || 0;
457
+ const baseTop = typeof window.screenY === "number" ? window.screenY : window.screenTop || 0;
458
+ const outerW = window.outerWidth || document.documentElement.clientWidth || 1024;
459
+ const outerH = window.outerHeight || document.documentElement.clientHeight || 768;
460
+ const left = Math.round(baseLeft + Math.max(0, (outerW - w) / 2));
461
+ const top = Math.round(baseTop + Math.max(0, (outerH - h) / 2));
462
+ const features = `popup=yes,width=${w},height=${h},left=${left},top=${top},scrollbars=yes,resizable=yes`;
463
+ let popup = null;
464
+ try {
465
+ popup = window.open(url, POPUP_NAME, features);
466
+ } catch {
467
+ popup = null;
468
+ }
469
+ if (popup) {
470
+ popupRef.current = popup;
471
+ try {
472
+ popup.focus();
473
+ } catch {
474
+ }
475
+ stopPopupPoll();
476
+ popupPollRef.current = setInterval(() => {
477
+ if (!popupRef.current || popupRef.current.closed) {
478
+ stopPopupPoll();
479
+ }
480
+ }, 1e3);
481
+ }
482
+ return popup;
483
+ },
484
+ [stopPopupPoll]
485
+ );
486
+ const openConnect = useCallback(() => {
487
+ if (connStateRef.current !== "ready" || !finalUrlRef.current || inProgressRef.current) {
488
+ return;
489
+ }
490
+ completedRef.current = false;
491
+ const popup = openOnboardingPopup(finalUrlRef.current);
492
+ if (!popup) {
493
+ setPopupBlocked(true);
494
+ return;
495
+ }
496
+ setPopupBlocked(false);
497
+ }, [openOnboardingPopup]);
498
+ const completeOnboarding = useCallback(
499
+ async (authCode, sharedId) => {
500
+ var _a, _b, _c, _d, _e, _f, _g, _h;
501
+ if (!authCode || !sharedId) return;
502
+ if (completedRef.current) return;
503
+ completedRef.current = true;
504
+ try {
505
+ window.onbeforeunload = null;
506
+ } catch {
507
+ }
508
+ const activeEnv = envRef.current === "sandbox" ? "sandbox" : "live";
509
+ setOnboardingInProgress(true);
510
+ setConnState("loading");
511
+ setError(null);
512
+ setPopupBlocked(false);
513
+ try {
514
+ const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
515
+ method: "POST",
516
+ headers: { "content-type": "application/json" },
517
+ credentials: "include",
518
+ body: JSON.stringify({ authCode, sharedId, env: activeEnv })
519
+ });
520
+ if (!res.ok) {
521
+ const txt = await res.text().catch(() => "");
522
+ throw new Error(txt || `Onboarding exchange failed (${res.status})`);
523
+ }
524
+ try {
525
+ (_a = popupRef.current) == null ? void 0 : _a.close();
526
+ } catch {
527
+ }
528
+ stopPopupPoll();
529
+ try {
530
+ 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;
531
+ if (typeof close1 === "function") close1();
532
+ } catch {
533
+ }
534
+ try {
535
+ 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;
536
+ if (typeof close2 === "function") close2();
537
+ } catch {
538
+ }
539
+ clearCachedUrl(activeEnv);
540
+ try {
541
+ const statusRes = await fetch(
542
+ `${STATUS_ENDPOINT}?environment=${activeEnv}`,
543
+ { method: "GET", credentials: "include" }
544
+ );
545
+ const refreshedStatus = await statusRes.json().catch(() => ({}));
546
+ setStatusInfo(refreshedStatus || null);
547
+ } catch {
548
+ }
549
+ setConnState("connected");
550
+ setShowManual(false);
551
+ } catch (e) {
552
+ completedRef.current = false;
553
+ console.error(e);
554
+ setConnState("error");
555
+ setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
556
+ } finally {
557
+ setOnboardingInProgress(false);
558
+ }
559
+ },
560
+ [stopPopupPoll]
561
+ );
372
562
  const fetchFreshLink = useCallback(
373
- (runId) => {
563
+ (runId, targetEnv) => {
374
564
  if (initLoaderRef.current) {
375
565
  const loaderText = initLoaderRef.current.querySelector("#loader-text");
376
566
  if (loaderText)
@@ -381,7 +571,10 @@ function PayPalConnectionPage() {
381
571
  headers: { "content-type": "application/json" },
382
572
  credentials: "include",
383
573
  body: JSON.stringify({
384
- products: ["PPCP"]
574
+ products: ["PPCP"],
575
+ // Generate the link for the explicitly selected environment so it can
576
+ // never depend on a racing POST /environment having landed yet.
577
+ environment: targetEnv
385
578
  })
386
579
  }).then((r) => {
387
580
  if (!r.ok) throw new Error(`Service returned ${r.status}`);
@@ -394,10 +587,7 @@ function PayPalConnectionPage() {
394
587
  return;
395
588
  }
396
589
  const url = href + (href.includes("?") ? "&" : "?") + "displayMode=minibrowser";
397
- localStorage.setItem(
398
- CACHE_KEY,
399
- JSON.stringify({ url, ts: Date.now() })
400
- );
590
+ writeCachedUrl(targetEnv, url);
401
591
  setFinalUrl(url);
402
592
  setConnState("ready");
403
593
  }).catch(() => {
@@ -405,37 +595,104 @@ function PayPalConnectionPage() {
405
595
  showError("Unable to connect to service.");
406
596
  });
407
597
  },
408
- [env, showError]
598
+ [showError]
409
599
  );
600
+ useEffect(() => {
601
+ let alive = true;
602
+ fetch(ENVIRONMENT_ENDPOINT, { method: "GET", credentials: "include" }).then((r) => r.json()).then((d) => {
603
+ if (!alive) return;
604
+ setEnv((d == null ? void 0 : d.environment) === "sandbox" ? "sandbox" : "live");
605
+ }).catch(() => {
606
+ }).finally(() => {
607
+ if (alive) setEnvReady(true);
608
+ });
609
+ return () => {
610
+ alive = false;
611
+ };
612
+ }, []);
410
613
  useEffect(() => {
411
614
  var _a, _b, _c;
412
615
  if (connState !== "ready" || !finalUrl) return;
413
616
  const scriptUrl = PARTNER_JS_URLS[env];
617
+ let cancelled = false;
618
+ let pollTimer;
619
+ const initPartner = (attempt = 0) => {
620
+ var _a2, _b2;
621
+ if (cancelled) return;
622
+ const signup = (_b2 = (_a2 = window.PAYPAL) == null ? void 0 : _a2.apps) == null ? void 0 : _b2.Signup;
623
+ 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;
624
+ if (target == null ? void 0 : target.init) {
625
+ try {
626
+ target.init();
627
+ } catch (e) {
628
+ console.error("[paypal] partner.js init failed:", e);
629
+ }
630
+ return;
631
+ }
632
+ if (typeof (signup == null ? void 0 : signup.render) === "function") {
633
+ try {
634
+ signup.render();
635
+ } catch (e) {
636
+ console.error("[paypal] partner.js render failed:", e);
637
+ }
638
+ return;
639
+ }
640
+ if (attempt < 40) {
641
+ pollTimer = setTimeout(() => initPartner(attempt + 1), 50);
642
+ return;
643
+ }
644
+ try {
645
+ document.dispatchEvent(new Event("DOMContentLoaded"));
646
+ } catch {
647
+ }
648
+ };
414
649
  const existingScript = document.getElementById("paypal-partner-js");
415
650
  if (existingScript) {
416
651
  (_a = existingScript.parentNode) == null ? void 0 : _a.removeChild(existingScript);
417
652
  }
418
653
  if ((_c = (_b = window.PAYPAL) == null ? void 0 : _b.apps) == null ? void 0 : _c.Signup) {
419
- delete window.PAYPAL.apps.Signup;
654
+ try {
655
+ delete window.PAYPAL.apps.Signup;
656
+ } catch {
657
+ }
420
658
  }
421
659
  const ppScript = document.createElement("script");
422
660
  ppScript.id = "paypal-partner-js";
423
661
  ppScript.src = scriptUrl;
424
662
  ppScript.async = true;
663
+ ppScript.onload = () => initPartner();
425
664
  document.body.appendChild(ppScript);
426
665
  return () => {
666
+ cancelled = true;
667
+ if (pollTimer) {
668
+ clearTimeout(pollTimer);
669
+ }
427
670
  if (ppScript.parentNode) {
428
671
  ppScript.parentNode.removeChild(ppScript);
429
672
  }
430
673
  };
431
674
  }, [connState, finalUrl, env]);
432
675
  useEffect(() => {
676
+ const onMessage = (ev) => {
677
+ if (!isPayPalOrigin(ev.origin)) return;
678
+ const { authCode, sharedId } = extractAuth(ev.data);
679
+ if (authCode && sharedId) {
680
+ completeOnboarding(authCode, sharedId);
681
+ }
682
+ };
683
+ window.addEventListener("message", onMessage);
684
+ return () => window.removeEventListener("message", onMessage);
685
+ }, [completeOnboarding]);
686
+ useEffect(() => {
687
+ if (!envReady) return;
433
688
  currentRunId.current = ++runIdRef.current;
434
689
  const runId = currentRunId.current;
435
690
  let cancelled = false;
691
+ completedRef.current = false;
436
692
  const run = async () => {
437
693
  setConnState("loading");
438
694
  setError(null);
695
+ setPopupBlocked(false);
439
696
  setFinalUrl("");
440
697
  try {
441
698
  const r = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
@@ -454,84 +711,46 @@ function PayPalConnectionPage() {
454
711
  } catch (e) {
455
712
  console.error(e);
456
713
  }
457
- if (cachedUrl) {
458
- setFinalUrl(cachedUrl);
714
+ if (cancelled || runId !== currentRunId.current) return;
715
+ const cached = readCachedUrl(env);
716
+ if (cached) {
717
+ setFinalUrl(cached);
459
718
  setConnState("ready");
460
719
  } else {
461
- fetchFreshLink(runId);
720
+ fetchFreshLink(runId, env);
462
721
  }
463
722
  };
464
723
  run();
465
724
  return () => {
466
725
  cancelled = true;
467
- currentRunId.current = 0;
468
726
  };
469
- }, [env, fetchFreshLink]);
727
+ }, [env, envReady, fetchFreshLink]);
470
728
  useLayoutEffect(() => {
471
- window.onboardingCallback = async function(authCode, sharedId) {
472
- var _a, _b, _c, _d, _e, _f, _g;
473
- try {
474
- window.onbeforeunload = null;
475
- } catch {
476
- }
477
- setOnboardingInProgress(true);
478
- setConnState("loading");
479
- setError(null);
480
- const payload = {
481
- authCode,
482
- sharedId,
483
- env: env === "sandbox" ? "sandbox" : "live"
484
- };
485
- try {
486
- const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
487
- method: "POST",
488
- headers: { "content-type": "application/json" },
489
- credentials: "include",
490
- body: JSON.stringify(payload)
491
- });
492
- if (!res.ok) {
493
- const txt = await res.text().catch(() => "");
494
- throw new Error(txt || `Onboarding exchange failed (${res.status})`);
495
- }
496
- try {
497
- 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;
498
- if (typeof close1 === "function") close1();
499
- } catch {
500
- }
501
- try {
502
- 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;
503
- if (typeof close2 === "function") close2();
504
- } catch {
505
- }
506
- try {
507
- localStorage.removeItem(CACHE_KEY);
508
- } catch {
509
- }
510
- try {
511
- const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
512
- method: "GET",
513
- credentials: "include"
514
- });
515
- const refreshedStatus = await statusRes.json().catch(() => ({}));
516
- setStatusInfo(refreshedStatus || null);
517
- setConnState("connected");
518
- setShowManual(false);
519
- } catch {
520
- setConnState("connected");
521
- setShowManual(false);
522
- }
523
- setOnboardingInProgress(false);
524
- } catch (e) {
525
- console.error(e);
526
- setConnState("error");
527
- setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
528
- setOnboardingInProgress(false);
529
- }
729
+ window.onboardingCallback = (authCode, sharedId) => {
730
+ completeOnboarding(authCode, sharedId);
530
731
  };
531
732
  return () => {
532
733
  window.onboardingCallback = void 0;
533
734
  };
534
- }, [env]);
735
+ }, [completeOnboarding]);
736
+ useEffect(() => {
737
+ return () => {
738
+ stopPopupPoll();
739
+ };
740
+ }, [stopPopupPoll]);
741
+ useEffect(() => {
742
+ const onCaptureClick = (e) => {
743
+ const btn = paypalButtonRef.current;
744
+ if (!btn) return;
745
+ const target = e.target;
746
+ if (!target || !btn.contains(target)) return;
747
+ e.preventDefault();
748
+ e.stopImmediatePropagation();
749
+ openConnect();
750
+ };
751
+ document.addEventListener("click", onCaptureClick, true);
752
+ return () => document.removeEventListener("click", onCaptureClick, true);
753
+ }, [openConnect]);
535
754
  useLayoutEffect(() => {
536
755
  const el = ppBtnMeasureRef.current;
537
756
  if (!el) return;
@@ -553,9 +772,8 @@ function PayPalConnectionPage() {
553
772
  };
554
773
  }, [connState, env, finalUrl]);
555
774
  const handleConnectClick = (e) => {
556
- if (connState !== "ready" || !finalUrl || onboardingInProgress) {
557
- e.preventDefault();
558
- }
775
+ e.preventDefault();
776
+ openConnect();
559
777
  };
560
778
  const handleSaveManual = async () => {
561
779
  if (!canSaveManual || onboardingInProgress) return;
@@ -585,10 +803,7 @@ function PayPalConnectionPage() {
585
803
  setConnState("connected");
586
804
  setStatusInfo(refreshedStatus || null);
587
805
  setShowManual(false);
588
- try {
589
- localStorage.removeItem(CACHE_KEY);
590
- } catch {
591
- }
806
+ clearCachedUrl(env);
592
807
  } catch (e) {
593
808
  console.error(e);
594
809
  setConnState("error");
@@ -617,13 +832,11 @@ function PayPalConnectionPage() {
617
832
  const t = await res.text().catch(() => "");
618
833
  throw new Error(t || `Disconnect failed (${res.status})`);
619
834
  }
620
- try {
621
- localStorage.removeItem(CACHE_KEY);
622
- } catch {
623
- }
835
+ clearCachedUrl(env);
836
+ completedRef.current = false;
624
837
  currentRunId.current = ++runIdRef.current;
625
838
  const runId = currentRunId.current;
626
- fetchFreshLink(runId);
839
+ fetchFreshLink(runId, env);
627
840
  } catch (e) {
628
841
  console.error(e);
629
842
  setConnState("error");
@@ -634,10 +847,12 @@ function PayPalConnectionPage() {
634
847
  };
635
848
  const handleEnvChange = async (e) => {
636
849
  const next = e.target.value;
850
+ completedRef.current = false;
851
+ setPopupBlocked(false);
852
+ clearCachedUrl();
637
853
  setEnv(next);
638
- cachedUrl = null;
639
854
  try {
640
- await fetch("/admin/paypal/environment", {
855
+ await fetch(ENVIRONMENT_ENDPOINT, {
641
856
  method: "POST",
642
857
  headers: { "content-type": "application/json" },
643
858
  credentials: "include",
@@ -645,10 +860,6 @@ function PayPalConnectionPage() {
645
860
  });
646
861
  } catch {
647
862
  }
648
- try {
649
- localStorage.removeItem(CACHE_KEY);
650
- } catch {
651
- }
652
863
  };
653
864
  return /* @__PURE__ */ jsxs("div", { className: "p-6", children: [
654
865
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
@@ -671,20 +882,7 @@ function PayPalConnectionPage() {
671
882
  ) }),
672
883
  /* @__PURE__ */ jsx("div", { className: "text-sm font-medium pt-2", children: env === "sandbox" ? "Connect to PayPal (Sandbox)" : "Connect to PayPal" }),
673
884
  /* @__PURE__ */ jsx("div", { className: "max-w-xl", children: connState === "connected" ? /* @__PURE__ */ jsxs("div", { children: [
674
- /* @__PURE__ */ jsxs("div", { className: "text-sm text-green-600 bg-green-50 p-3 rounded border border-green-200", children: [
675
- "✅ Successfully connected to PayPal!",
676
- /* @__PURE__ */ jsx(
677
- "a",
678
- {
679
- target: "_blank",
680
- "data-paypal-button": "true",
681
- "data-paypal-onboard-complete": "onboardingCallback",
682
- href: "#",
683
- style: { display: "none" },
684
- children: "PayPal"
685
- }
686
- )
687
- ] }),
885
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-green-600 bg-green-50 p-3 rounded border border-green-200", children: "✅ Successfully connected to PayPal!" }),
688
886
  /* @__PURE__ */ 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: [
689
887
  /* @__PURE__ */ jsx("div", { className: "font-medium text-ui-fg-base", children: "Connected PayPal account" }),
690
888
  /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
@@ -725,7 +923,7 @@ function PayPalConnectionPage() {
725
923
  ppBtnMeasureRef.current = node;
726
924
  },
727
925
  id: "paypal-button",
728
- target: "_blank",
926
+ target: POPUP_NAME,
729
927
  "data-paypal-button": "true",
730
928
  href: finalUrl || "#",
731
929
  "data-paypal-onboard-complete": "onboardingCallback",
@@ -739,6 +937,7 @@ function PayPalConnectionPage() {
739
937
  children: "Connect to PayPal"
740
938
  }
741
939
  ),
940
+ popupBlocked && /* @__PURE__ */ 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." }),
742
941
  /* @__PURE__ */ jsx(
743
942
  "div",
744
943
  {
@@ -894,9 +1093,6 @@ function PayPalConnectionPage() {
894
1093
  ` })
895
1094
  ] });
896
1095
  }
897
- function PayPalGooglePayPage() {
898
- return /* @__PURE__ */ jsx(Navigate, { to: "/settings/paypal/connection", replace: true });
899
- }
900
1096
  function PayPalPayLaterMessagingPage() {
901
1097
  return /* @__PURE__ */ jsx(Navigate, { to: "/settings/paypal/connection", replace: true });
902
1098
  }
@@ -1126,14 +1322,14 @@ const routeModule = {
1126
1322
  Component: PayPalApplePayPage,
1127
1323
  path: "/settings/paypal/apple-pay"
1128
1324
  },
1129
- {
1130
- Component: PayPalConnectionPage,
1131
- path: "/settings/paypal/connection"
1132
- },
1133
1325
  {
1134
1326
  Component: PayPalGooglePayPage,
1135
1327
  path: "/settings/paypal/google-pay"
1136
1328
  },
1329
+ {
1330
+ Component: PayPalConnectionPage,
1331
+ path: "/settings/paypal/connection"
1332
+ },
1137
1333
  {
1138
1334
  Component: PayPalPayLaterMessagingPage,
1139
1335
  path: "/settings/paypal/pay-later-messaging"
@@ -5,11 +5,14 @@ declare global {
5
5
  apps?: {
6
6
  Signup?: {
7
7
  miniBrowser?: {
8
- init: () => void;
8
+ init?: () => void;
9
+ closeFlow?: () => void;
9
10
  };
10
11
  MiniBrowser?: {
12
+ init?: () => void;
11
13
  closeFlow?: () => void;
12
14
  };
15
+ render?: () => void;
13
16
  };
14
17
  };
15
18
  };
@@ -1 +1 @@
1
- {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../../../../src/admin/routes/settings/paypal/connection/page.tsx"],"names":[],"mappings":"AAWA,eAAO,MAAM,MAAM,2CAEjB,CAAA;AAQF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,MAAM,CAAC,EAAE;YACP,IAAI,CAAC,EAAE;gBACL,MAAM,CAAC,EAAE;oBACP,WAAW,CAAC,EAAE;wBAAE,IAAI,EAAE,MAAM,IAAI,CAAA;qBAAE,CAAA;oBAClC,WAAW,CAAC,EAAE;wBAAE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;qBAAE,CAAA;iBACzC,CAAA;aACF,CAAA;SACF,CAAA;QACD,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;KAClE;CACF;AA4BD,MAAM,CAAC,OAAO,UAAU,oBAAoB,4CAynB3C"}
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../../../../src/admin/routes/settings/paypal/connection/page.tsx"],"names":[],"mappings":"AAWA,eAAO,MAAM,MAAM,2CAEjB,CAAA;AAQF,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,MAAM,CAAC,EAAE;YACP,IAAI,CAAC,EAAE;gBACL,MAAM,CAAC,EAAE;oBACP,WAAW,CAAC,EAAE;wBAAE,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC;wBAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;qBAAE,CAAA;oBAC3D,WAAW,CAAC,EAAE;wBAAE,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC;wBAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;qBAAE,CAAA;oBAC3D,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;iBACpB,CAAA;aACF,CAAA;SACF,CAAA;QACD,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;KAClE;CACF;AAmID,MAAM,CAAC,OAAO,UAAU,oBAAoB,4CAy2B3C"}