@easypayment/medusa-paypal 0.2.4 → 0.2.6

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.
@@ -336,474 +336,101 @@ function AdditionalSettingsTab() {
336
336
  )
337
337
  ] }) });
338
338
  }
339
- const DEFAULT_FORM = {
340
- enabled: true,
341
- title: "Credit or Debit Card",
342
- disabledCards: [],
343
- threeDS: "when_required",
344
- cardSaveEnabled: false
345
- };
346
- function mergeWithDefaults(saved) {
347
- if (!saved) return { ...DEFAULT_FORM };
348
- const entries = Object.entries(saved).filter(([, value]) => value !== void 0);
349
- return {
350
- ...DEFAULT_FORM,
351
- ...Object.fromEntries(entries)
352
- };
339
+ function PayPalApplePayPage() {
340
+ return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
353
341
  }
354
- const CARD_BRANDS = [
355
- { value: "visa", label: "Visa" },
356
- { value: "mastercard", label: "Mastercard" },
357
- { value: "amex", label: "American Express" },
358
- { value: "discover", label: "Discover" },
359
- { value: "diners", label: "Diners Club" },
360
- { value: "jcb", label: "JCB" },
361
- { value: "unionpay", label: "UnionPay" }
362
- ];
363
- const THREE_DS_OPTIONS = [
364
- {
365
- value: "when_required",
366
- label: "3D Secure when required",
367
- hint: "Triggers 3DS only when the card / issuer requires it."
368
- },
369
- {
370
- value: "sli",
371
- label: "3D Secure (SCA) / liability shift (recommended)",
372
- hint: "Attempts to optimize for liability shift while remaining compliant."
373
- },
374
- {
375
- value: "always",
376
- label: "Always request 3D Secure",
377
- hint: "Forces 3DS challenge whenever possible (may reduce conversion)."
342
+ const config = adminSdk.defineRouteConfig({
343
+ label: "PayPal Connection",
344
+ hide: true
345
+ });
346
+ if (typeof window !== "undefined") {
347
+ const preloadHref = "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js";
348
+ const existingPreload = document.head.querySelector(
349
+ `link[rel="preload"][href="${preloadHref}"]`
350
+ );
351
+ if (!existingPreload) {
352
+ const preloadLink = document.createElement("link");
353
+ preloadLink.rel = "preload";
354
+ preloadLink.href = preloadHref;
355
+ preloadLink.as = "script";
356
+ document.head.appendChild(preloadLink);
357
+ }
358
+ const existingScript = document.getElementById(
359
+ "paypal-partner-js"
360
+ );
361
+ if (!existingScript) {
362
+ const ppScript = document.createElement("script");
363
+ ppScript.id = "paypal-partner-js";
364
+ ppScript.src = preloadHref;
365
+ ppScript.async = true;
366
+ document.head.appendChild(ppScript);
378
367
  }
379
- ];
380
- function cx$1(...parts) {
381
- return parts.filter(Boolean).join(" ");
382
368
  }
383
- function Pill$1({
384
- children,
385
- onRemove
386
- }) {
387
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-ui-border-base bg-ui-bg-base px-2 py-1 text-sm text-ui-fg-base", children: [
388
- children,
389
- onRemove ? /* @__PURE__ */ jsxRuntime.jsx(
390
- "button",
391
- {
392
- type: "button",
393
- onClick: onRemove,
394
- className: "ml-1 rounded px-1 text-ui-fg-subtle hover:text-ui-fg-base",
395
- "aria-label": "Remove",
396
- children: "×"
369
+ const SERVICE_URL = "/admin/paypal/onboarding-link";
370
+ const CACHE_KEY = "pp_onboard_cache";
371
+ const RELOAD_KEY = "pp_onboard_reloaded_once";
372
+ const CACHE_EXPIRY = 10 * 60 * 1e3;
373
+ const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete";
374
+ const STATUS_ENDPOINT = "/admin/paypal/status";
375
+ const SAVE_CREDENTIALS_ENDPOINT = "/admin/paypal/save-credentials";
376
+ const DISCONNECT_ENDPOINT = "/admin/paypal/disconnect";
377
+ let cachedUrl = null;
378
+ if (typeof window !== "undefined") {
379
+ try {
380
+ const cached = localStorage.getItem(CACHE_KEY);
381
+ if (cached) {
382
+ const data = JSON.parse(cached);
383
+ if ((/* @__PURE__ */ new Date()).getTime() - data.ts < CACHE_EXPIRY) {
384
+ cachedUrl = data.url;
397
385
  }
398
- ) : null
399
- ] });
400
- }
401
- function SectionCard$1({
402
- title,
403
- description,
404
- right,
405
- children
406
- }) {
407
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm", children: [
408
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 border-b border-ui-border-base p-4", children: [
409
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
410
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-semibold text-ui-fg-base", children: title }),
411
- description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-sm text-ui-fg-subtle", children: description }) : null
412
- ] }),
413
- right
414
- ] }),
415
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children })
416
- ] });
417
- }
418
- function FieldRow$1({
419
- label,
420
- hint,
421
- children
422
- }) {
423
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-12 items-start gap-4 py-3", children: [
424
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-12 md:col-span-4", children: [
425
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-ui-fg-base", children: label }),
426
- hint ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-xs text-ui-fg-subtle", children: hint }) : null
427
- ] }),
428
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-12 md:col-span-8", children })
429
- ] });
386
+ }
387
+ } catch (e) {
388
+ console.error("Cache read error:", e);
389
+ }
430
390
  }
431
- function AdvancedCardPaymentsTab() {
432
- var _a, _b;
433
- const [form, setForm] = react.useState(() => ({ ...DEFAULT_FORM }));
434
- const [loading, setLoading] = react.useState(false);
435
- const [saving, setSaving] = react.useState(false);
436
- const [toast, setToast] = react.useState(null);
437
- const didInit = react.useRef(false);
391
+ function PayPalConnectionPage() {
392
+ const [env, setEnv] = react.useState("sandbox");
438
393
  react.useEffect(() => {
439
- if (didInit.current) return;
440
- didInit.current = true;
441
- (async () => {
442
- try {
443
- setLoading(true);
444
- const r = await fetch("/admin/paypal/settings", {
445
- credentials: "include",
446
- headers: { "Accept": "application/json" }
447
- });
448
- if (!r.ok) return;
449
- const json = await r.json();
450
- const payload = (json == null ? void 0 : json.data) ?? json;
451
- const saved = payload == null ? void 0 : payload.advanced_card_payments;
452
- if (saved && typeof saved === "object") {
453
- setForm(mergeWithDefaults(saved));
454
- }
455
- } finally {
456
- setLoading(false);
457
- }
458
- })();
394
+ fetch("/admin/paypal/environment", { method: "GET" }).then((r) => r.json()).then((d) => {
395
+ const v = (d == null ? void 0 : d.environment) === "live" ? "live" : "sandbox";
396
+ setEnv(v);
397
+ }).catch(() => {
398
+ });
459
399
  }, []);
460
- async function onSave() {
461
- try {
462
- setSaving(true);
463
- const r = await fetch("/admin/paypal/settings", {
464
- method: "POST",
465
- credentials: "include",
466
- headers: {
467
- "Content-Type": "application/json",
468
- "Accept": "application/json"
469
- },
470
- body: JSON.stringify({ advanced_card_payments: form })
471
- });
472
- if (!r.ok) {
473
- const t = await r.text();
474
- setToast({ type: "error", message: "Failed to save settings. " + t });
475
- window.setTimeout(() => setToast(null), 3500);
476
- return;
477
- }
478
- const json = await r.json().catch(() => null);
479
- const payload = (json == null ? void 0 : json.data) ?? json;
480
- const saved = payload == null ? void 0 : payload.advanced_card_payments;
481
- if (saved && typeof saved === "object") {
482
- setForm(mergeWithDefaults(saved));
483
- }
484
- setToast({ type: "success", message: "Settings saved" });
485
- window.setTimeout(() => setToast(null), 2500);
486
- } finally {
487
- setSaving(false);
400
+ const [connState, setConnState] = react.useState("loading");
401
+ const [error, setError] = react.useState(null);
402
+ const [finalUrl, setFinalUrl] = react.useState("");
403
+ const [showManual, setShowManual] = react.useState(false);
404
+ const [clientId, setClientId] = react.useState("");
405
+ const [secret, setSecret] = react.useState("");
406
+ const [merchantId, setMerchantId] = react.useState("");
407
+ const [statusInfo, setStatusInfo] = react.useState(null);
408
+ const [onboardingInProgress, setOnboardingInProgress] = react.useState(false);
409
+ const initLoaderRef = react.useRef(null);
410
+ const paypalButtonRef = react.useRef(null);
411
+ const errorLogRef = react.useRef(null);
412
+ const runIdRef = react.useRef(0);
413
+ const currentRunId = react.useRef(0);
414
+ const ppBtnMeasureRef = react.useRef(null);
415
+ const [ppBtnWidth, setPpBtnWidth] = react.useState(null);
416
+ const canSaveManual = react.useMemo(() => {
417
+ return clientId.trim().length > 0 && secret.trim().length > 0;
418
+ }, [clientId, secret]);
419
+ const maskValue = react.useCallback((value, visibleChars = 4) => {
420
+ if (!value) return "";
421
+ if (value.length <= visibleChars) {
422
+ return "•".repeat(value.length);
488
423
  }
489
- }
490
- const disabledSet = react.useMemo(() => new Set(form.disabledCards), [form.disabledCards]);
491
- function toggleDisabledCard(value) {
492
- setForm((prev) => {
493
- const exists = prev.disabledCards.includes(value);
494
- return {
495
- ...prev,
496
- disabledCards: exists ? prev.disabledCards.filter((v) => v !== value) : [...prev.disabledCards, value]
497
- };
498
- });
499
- }
500
- function removeDisabledCard(value) {
501
- setForm((prev) => ({
502
- ...prev,
503
- disabledCards: prev.disabledCards.filter((v) => v !== value)
504
- }));
505
- }
506
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
507
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between gap-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-ui-fg-base", children: "PayPal Gateway By Easy Payment" }) }) }),
508
- /* @__PURE__ */ jsxRuntime.jsx(PayPalTabs, {}),
509
- toast ? /* @__PURE__ */ jsxRuntime.jsx(
510
- "div",
511
- {
512
- className: "fixed right-6 top-6 z-50 rounded-md border border-ui-border-base bg-ui-bg-base px-4 py-3 text-sm shadow-lg",
513
- role: "status",
514
- "aria-live": "polite",
515
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: toast.type === "success" ? "text-ui-fg-base" : "text-ui-fg-error", children: toast.message })
516
- }
517
- ) : null,
518
- /* @__PURE__ */ jsxRuntime.jsx(
519
- SectionCard$1,
520
- {
521
- title: "Advanced Card Payments",
522
- description: "Control card checkout settings, 3D Secure behavior, and card saving.",
523
- right: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
524
- /* @__PURE__ */ jsxRuntime.jsx(
525
- "button",
526
- {
527
- type: "button",
528
- onClick: onSave,
529
- disabled: saving || loading,
530
- className: "rounded-md bg-ui-button-neutral px-4 py-2 text-sm font-medium text-ui-fg-on-color shadow-sm hover:opacity-90 disabled:opacity-60",
531
- children: saving ? "Saving..." : "Save settings"
532
- }
533
- ),
534
- loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-subtle", children: "Loading…" }) : null
535
- ] }),
536
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "divide-y divide-ui-border-base", children: [
537
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow$1, { label: "Enable/Disable", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2", children: [
538
- /* @__PURE__ */ jsxRuntime.jsx(
539
- "input",
540
- {
541
- type: "checkbox",
542
- checked: form.enabled,
543
- onChange: (e) => setForm((p) => ({ ...p, enabled: e.target.checked })),
544
- className: "h-4 w-4 rounded border-ui-border-base"
545
- }
546
- ),
547
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Enable Advanced Credit/Debit Card" })
548
- ] }) }),
549
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow$1, { label: "Title", children: /* @__PURE__ */ jsxRuntime.jsx(
550
- "input",
551
- {
552
- value: form.title,
553
- onChange: (e) => setForm((p) => ({ ...p, title: e.target.value })),
554
- className: "w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive",
555
- placeholder: "Credit or Debit Card"
556
- }
557
- ) }),
558
- /* @__PURE__ */ jsxRuntime.jsx(
559
- FieldRow$1,
560
- {
561
- label: "Disable specific credit cards",
562
- hint: "Select card brands to hide from the card form.",
563
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
564
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: form.disabledCards.length ? form.disabledCards.map((v) => {
565
- var _a2;
566
- const label = ((_a2 = CARD_BRANDS.find((b) => b.value === v)) == null ? void 0 : _a2.label) ?? v;
567
- return /* @__PURE__ */ jsxRuntime.jsx(Pill$1, { onRemove: () => removeDisabledCard(v), children: label }, v);
568
- }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-subtle", children: "No card brands disabled." }) }),
569
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-ui-border-base p-3", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-2 md:grid-cols-2", children: CARD_BRANDS.map((b) => {
570
- const checked = disabledSet.has(b.value);
571
- return /* @__PURE__ */ jsxRuntime.jsxs(
572
- "label",
573
- {
574
- className: cx$1(
575
- "flex items-center gap-2 rounded-md p-2",
576
- "hover:bg-ui-bg-subtle"
577
- ),
578
- children: [
579
- /* @__PURE__ */ jsxRuntime.jsx(
580
- "input",
581
- {
582
- type: "checkbox",
583
- checked,
584
- onChange: () => toggleDisabledCard(b.value),
585
- className: "h-4 w-4 rounded border-ui-border-base"
586
- }
587
- ),
588
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: b.label })
589
- ]
590
- },
591
- b.value
592
- );
593
- }) }) })
594
- ] })
595
- }
596
- ),
597
- /* @__PURE__ */ jsxRuntime.jsx(
598
- FieldRow$1,
599
- {
600
- label: "Contingency for 3D Secure",
601
- hint: "Choose when 3D Secure should be triggered during card payments.",
602
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
603
- /* @__PURE__ */ jsxRuntime.jsx(
604
- "select",
605
- {
606
- value: form.threeDS,
607
- onChange: (e) => setForm((p) => ({ ...p, threeDS: e.target.value })),
608
- className: "w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive",
609
- children: THREE_DS_OPTIONS.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
610
- }
611
- ),
612
- ((_a = THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)) == null ? void 0 : _a.hint) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-ui-fg-subtle", children: (_b = THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)) == null ? void 0 : _b.hint }) : null
613
- ] })
614
- }
615
- ),
616
- /* @__PURE__ */ jsxRuntime.jsx(FieldRow$1, { label: "Card Save Enabled", hint: "Allow customers to save a card at checkout for future use.", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2", children: [
617
- /* @__PURE__ */ jsxRuntime.jsx(
618
- "input",
619
- {
620
- type: "checkbox",
621
- checked: form.cardSaveEnabled,
622
- onChange: (e) => setForm((p) => ({ ...p, cardSaveEnabled: e.target.checked })),
623
- className: "h-4 w-4 rounded border-ui-border-base"
624
- }
625
- ),
626
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Enable card saving at checkout" })
627
- ] }) })
628
- ] })
629
- }
630
- )
631
- ] }) });
632
- }
633
- function PayPalApplePayPage() {
634
- return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
635
- }
636
- function formatDate$2(value) {
637
- if (!value) {
638
- return "";
639
- }
640
- const parsed = new Date(value);
641
- if (Number.isNaN(parsed.getTime())) {
642
- return value;
643
- }
644
- return parsed.toLocaleString();
645
- }
646
- function PayPalAuditLogsPage() {
647
- const [logs, setLogs] = react.useState([]);
648
- const [loading, setLoading] = react.useState(false);
649
- const [error, setError] = react.useState(null);
650
- const fetchLogs = react.useCallback(async () => {
651
- try {
652
- setLoading(true);
653
- setError(null);
654
- const response = await fetch("/admin/paypal/audit-logs?limit=50", {
655
- credentials: "include",
656
- headers: {
657
- Accept: "application/json"
658
- }
659
- });
660
- if (!response.ok) {
661
- const message = await response.text().catch(() => "");
662
- throw new Error(message || "Failed to load audit logs.");
663
- }
664
- const data = await response.json().catch(() => ({}));
665
- setLogs((data == null ? void 0 : data.logs) || []);
666
- } catch (fetchError) {
667
- setError((fetchError == null ? void 0 : fetchError.message) || "Failed to load audit logs.");
668
- setLogs([]);
669
- } finally {
670
- setLoading(false);
671
- }
672
- }, []);
673
- react.useEffect(() => {
674
- fetchLogs();
675
- }, [fetchLogs]);
676
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
677
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
678
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-ui-fg-base", children: "PayPal Audit Logs" }),
679
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-ui-fg-subtle", children: "Track administrative changes and credential events for PayPal configuration." })
680
- ] }),
681
- /* @__PURE__ */ jsxRuntime.jsx(PayPalTabs, {}),
682
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm", children: [
683
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-ui-border-base p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-4", children: [
684
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-semibold text-ui-fg-base", children: "Latest events" }),
685
- /* @__PURE__ */ jsxRuntime.jsx(
686
- "button",
687
- {
688
- type: "button",
689
- onClick: fetchLogs,
690
- className: "rounded-md border border-ui-border-base px-3 py-2 text-sm text-ui-fg-base",
691
- disabled: loading,
692
- children: loading ? "Refreshing..." : "Refresh"
693
- }
694
- )
695
- ] }) }),
696
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
697
- error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-600", children: error }) : null,
698
- !error && logs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-ui-fg-subtle", children: loading ? "Loading audit logs..." : "No audit log entries found yet." }) : null,
699
- logs.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "min-w-full text-left text-sm", children: [
700
- /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "text-ui-fg-muted", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
701
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 pr-4 font-medium", children: "Timestamp" }),
702
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 pr-4 font-medium", children: "Event" }),
703
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 font-medium", children: "Details" })
704
- ] }) }),
705
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: logs.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-t border-ui-border-base", children: [
706
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-3 pr-4 text-ui-fg-base", children: formatDate$2(entry.created_at) || "—" }),
707
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-3 pr-4 text-ui-fg-base", children: entry.event_type || "—" }),
708
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-3 text-ui-fg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap rounded-md bg-ui-bg-subtle p-2 text-xs text-ui-fg-subtle", children: JSON.stringify(entry.metadata || {}, null, 2) }) })
709
- ] }, entry.id)) })
710
- ] }) }) : null
711
- ] })
712
- ] })
713
- ] }) });
714
- }
715
- const config = adminSdk.defineRouteConfig({
716
- label: "PayPal Connection",
717
- hide: true
718
- });
719
- if (typeof window !== "undefined") {
720
- const preloadHref = "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js";
721
- const existingPreload = document.head.querySelector(
722
- `link[rel="preload"][href="${preloadHref}"]`
723
- );
724
- if (!existingPreload) {
725
- const preloadLink = document.createElement("link");
726
- preloadLink.rel = "preload";
727
- preloadLink.href = preloadHref;
728
- preloadLink.as = "script";
729
- document.head.appendChild(preloadLink);
730
- }
731
- const existingScript = document.getElementById(
732
- "paypal-partner-js"
733
- );
734
- if (!existingScript) {
735
- const ppScript = document.createElement("script");
736
- ppScript.id = "paypal-partner-js";
737
- ppScript.src = preloadHref;
738
- ppScript.async = true;
739
- document.head.appendChild(ppScript);
740
- }
741
- }
742
- const SERVICE_URL = "/admin/paypal/onboarding-link";
743
- const CACHE_KEY = "pp_onboard_cache";
744
- const RELOAD_KEY = "pp_onboard_reloaded_once";
745
- const CACHE_EXPIRY = 10 * 60 * 1e3;
746
- const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete";
747
- const STATUS_ENDPOINT = "/admin/paypal/status";
748
- const SAVE_CREDENTIALS_ENDPOINT = "/admin/paypal/save-credentials";
749
- const DISCONNECT_ENDPOINT = "/admin/paypal/disconnect";
750
- let cachedUrl = null;
751
- if (typeof window !== "undefined") {
752
- try {
753
- const cached = localStorage.getItem(CACHE_KEY);
754
- if (cached) {
755
- const data = JSON.parse(cached);
756
- if ((/* @__PURE__ */ new Date()).getTime() - data.ts < CACHE_EXPIRY) {
757
- cachedUrl = data.url;
758
- }
759
- }
760
- } catch (e) {
761
- console.error("Cache read error:", e);
762
- }
763
- }
764
- function PayPalConnectionPage() {
765
- const [env, setEnv] = react.useState("sandbox");
766
- react.useEffect(() => {
767
- fetch("/admin/paypal/environment", { method: "GET" }).then((r) => r.json()).then((d) => {
768
- const v = (d == null ? void 0 : d.environment) === "live" ? "live" : "sandbox";
769
- setEnv(v);
770
- }).catch(() => {
771
- });
772
- }, []);
773
- const [connState, setConnState] = react.useState("loading");
774
- const [error, setError] = react.useState(null);
775
- const [finalUrl, setFinalUrl] = react.useState("");
776
- const [showManual, setShowManual] = react.useState(false);
777
- const [clientId, setClientId] = react.useState("");
778
- const [secret, setSecret] = react.useState("");
779
- const [merchantId, setMerchantId] = react.useState("");
780
- const [statusInfo, setStatusInfo] = react.useState(null);
781
- const [onboardingInProgress, setOnboardingInProgress] = react.useState(false);
782
- const initLoaderRef = react.useRef(null);
783
- const paypalButtonRef = react.useRef(null);
784
- const errorLogRef = react.useRef(null);
785
- const runIdRef = react.useRef(0);
786
- const currentRunId = react.useRef(0);
787
- const ppBtnMeasureRef = react.useRef(null);
788
- const [ppBtnWidth, setPpBtnWidth] = react.useState(null);
789
- const canSaveManual = react.useMemo(() => {
790
- return clientId.trim().length > 0 && secret.trim().length > 0;
791
- }, [clientId, secret]);
792
- const maskValue = react.useCallback((value, visibleChars = 4) => {
793
- if (!value) return "";
794
- if (value.length <= visibleChars) {
795
- return "•".repeat(value.length);
796
- }
797
- return `${"•".repeat(Math.max(0, value.length - visibleChars))}${value.slice(
798
- -visibleChars
799
- )}`;
800
- }, []);
801
- const fetchFreshLink = react.useCallback(
802
- (runId) => {
803
- if (initLoaderRef.current) {
804
- const loaderText = initLoaderRef.current.querySelector("#loader-text");
805
- if (loaderText)
806
- loaderText.textContent = "Generating onboarding session...";
424
+ return `${"•".repeat(Math.max(0, value.length - visibleChars))}${value.slice(
425
+ -visibleChars
426
+ )}`;
427
+ }, []);
428
+ const fetchFreshLink = react.useCallback(
429
+ (runId) => {
430
+ if (initLoaderRef.current) {
431
+ const loaderText = initLoaderRef.current.querySelector("#loader-text");
432
+ if (loaderText)
433
+ loaderText.textContent = "Generating onboarding session...";
807
434
  }
808
435
  fetch(SERVICE_URL, {
809
436
  method: "POST",
@@ -894,407 +521,783 @@ function PayPalConnectionPage() {
894
521
  } catch (e) {
895
522
  console.error(e);
896
523
  }
897
- if (cachedUrl) {
898
- console.log("Using prioritized cache...");
899
- activatePayPal(cachedUrl, runId);
900
- } else {
901
- fetchFreshLink(runId);
524
+ if (cachedUrl) {
525
+ console.log("Using prioritized cache...");
526
+ activatePayPal(cachedUrl, runId);
527
+ } else {
528
+ fetchFreshLink(runId);
529
+ }
530
+ };
531
+ run();
532
+ return () => {
533
+ cancelled = true;
534
+ currentRunId.current = 0;
535
+ };
536
+ }, [env, fetchFreshLink, activatePayPal]);
537
+ react.useLayoutEffect(() => {
538
+ window.onboardingCallback = async function(authCode, sharedId) {
539
+ var _a, _b, _c, _d, _e, _f, _g;
540
+ try {
541
+ ;
542
+ window.onbeforeunload = "";
543
+ } catch {
544
+ }
545
+ setOnboardingInProgress(true);
546
+ setConnState("loading");
547
+ setError(null);
548
+ const payload = {
549
+ authCode,
550
+ sharedId,
551
+ env: env === "sandbox" ? "sandbox" : "live"
552
+ };
553
+ try {
554
+ const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
555
+ method: "POST",
556
+ headers: { "content-type": "application/json" },
557
+ body: JSON.stringify(payload)
558
+ });
559
+ if (!res.ok) {
560
+ const txt = await res.text().catch(() => "");
561
+ throw new Error(txt || `Onboarding exchange failed (${res.status})`);
562
+ }
563
+ try {
564
+ 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;
565
+ if (typeof close1 === "function") close1();
566
+ } catch {
567
+ }
568
+ try {
569
+ 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;
570
+ if (typeof close2 === "function") close2();
571
+ } catch {
572
+ }
573
+ try {
574
+ localStorage.removeItem(CACHE_KEY);
575
+ localStorage.removeItem(RELOAD_KEY);
576
+ } catch {
577
+ }
578
+ window.location.href = window.location.href;
579
+ } catch (e) {
580
+ console.error(e);
581
+ setConnState("error");
582
+ setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
583
+ setOnboardingInProgress(false);
584
+ }
585
+ };
586
+ return () => {
587
+ window.onboardingCallback = void 0;
588
+ };
589
+ }, [env]);
590
+ react.useLayoutEffect(() => {
591
+ const el = ppBtnMeasureRef.current;
592
+ if (!el) return;
593
+ const update = () => {
594
+ const w = Math.round(el.getBoundingClientRect().width || 0);
595
+ if (w > 0) setPpBtnWidth(w);
596
+ };
597
+ update();
598
+ let ro = null;
599
+ if (typeof ResizeObserver !== "undefined") {
600
+ ro = new ResizeObserver(() => update());
601
+ ro.observe(el);
602
+ } else {
603
+ window.addEventListener("resize", update);
604
+ }
605
+ return () => {
606
+ if (ro) ro.disconnect();
607
+ else window.removeEventListener("resize", update);
608
+ };
609
+ }, [connState, env, finalUrl]);
610
+ const handleConnectClick = (e) => {
611
+ if (connState !== "ready" || !finalUrl || onboardingInProgress) {
612
+ e.preventDefault();
613
+ }
614
+ };
615
+ const handleSaveManual = async () => {
616
+ if (!canSaveManual || onboardingInProgress) return;
617
+ setOnboardingInProgress(true);
618
+ setConnState("loading");
619
+ setError(null);
620
+ try {
621
+ const res = await fetch(SAVE_CREDENTIALS_ENDPOINT, {
622
+ method: "POST",
623
+ headers: { "content-type": "application/json" },
624
+ body: JSON.stringify({
625
+ clientId: clientId.trim(),
626
+ clientSecret: secret.trim()
627
+ })
628
+ });
629
+ if (!res.ok) {
630
+ const txt = await res.text().catch(() => "");
631
+ throw new Error(txt || `Save credentials failed (${res.status})`);
632
+ }
633
+ setConnState("connected");
634
+ setStatusInfo({
635
+ seller_client_id_masked: maskValue(clientId.trim()),
636
+ seller_client_secret_masked: "••••••••"
637
+ });
638
+ setShowManual(false);
639
+ try {
640
+ localStorage.removeItem(CACHE_KEY);
641
+ localStorage.removeItem(RELOAD_KEY);
642
+ } catch {
643
+ }
644
+ } catch (e) {
645
+ console.error(e);
646
+ setConnState("error");
647
+ setError((e == null ? void 0 : e.message) || "Failed to save credentials.");
648
+ } finally {
649
+ setOnboardingInProgress(false);
650
+ }
651
+ };
652
+ const handleDisconnect = async () => {
653
+ if (onboardingInProgress) return;
654
+ if (!window.confirm("Disconnect PayPal for this environment?")) return;
655
+ setOnboardingInProgress(true);
656
+ setConnState("loading");
657
+ setError(null);
658
+ setFinalUrl("");
659
+ setShowManual(false);
660
+ try {
661
+ const res = await fetch(DISCONNECT_ENDPOINT, {
662
+ method: "POST",
663
+ headers: { "content-type": "application/json" },
664
+ body: JSON.stringify({ environment: env })
665
+ });
666
+ if (!res.ok) {
667
+ const t = await res.text().catch(() => "");
668
+ throw new Error(t || `Disconnect failed (${res.status})`);
902
669
  }
903
- };
904
- run();
905
- return () => {
906
- cancelled = true;
907
- currentRunId.current = 0;
908
- };
909
- }, [env, fetchFreshLink, activatePayPal]);
910
- react.useLayoutEffect(() => {
911
- window.onboardingCallback = async function(authCode, sharedId) {
912
- var _a, _b, _c, _d, _e, _f, _g;
913
670
  try {
914
- ;
915
- window.onbeforeunload = "";
671
+ localStorage.removeItem(CACHE_KEY);
672
+ localStorage.removeItem(RELOAD_KEY);
916
673
  } catch {
917
674
  }
918
- setOnboardingInProgress(true);
919
- setConnState("loading");
920
- setError(null);
921
- const payload = {
922
- authCode,
923
- sharedId,
924
- env: env === "sandbox" ? "sandbox" : "live"
925
- };
926
- try {
927
- const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
928
- method: "POST",
929
- headers: { "content-type": "application/json" },
930
- body: JSON.stringify(payload)
931
- });
932
- if (!res.ok) {
933
- const txt = await res.text().catch(() => "");
934
- throw new Error(txt || `Onboarding exchange failed (${res.status})`);
935
- }
936
- try {
937
- 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;
938
- if (typeof close1 === "function") close1();
939
- } catch {
940
- }
941
- try {
942
- 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;
943
- if (typeof close2 === "function") close2();
944
- } catch {
675
+ currentRunId.current = ++runIdRef.current;
676
+ const runId = currentRunId.current;
677
+ fetchFreshLink(runId);
678
+ } catch (e) {
679
+ console.error(e);
680
+ setConnState("error");
681
+ setError((e == null ? void 0 : e.message) || "Failed to disconnect.");
682
+ } finally {
683
+ setOnboardingInProgress(false);
684
+ }
685
+ };
686
+ const handleEnvChange = async (e) => {
687
+ const next = e.target.value;
688
+ setEnv(next);
689
+ cachedUrl = null;
690
+ try {
691
+ await fetch("/admin/paypal/environment", {
692
+ method: "POST",
693
+ headers: { "content-type": "application/json" },
694
+ body: JSON.stringify({ environment: next })
695
+ });
696
+ } catch {
697
+ }
698
+ try {
699
+ localStorage.removeItem(CACHE_KEY);
700
+ localStorage.removeItem(RELOAD_KEY);
701
+ } catch {
702
+ }
703
+ };
704
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6", children: [
705
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
706
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "PayPal Gateway By Easy Payment" }),
707
+ /* @__PURE__ */ jsxRuntime.jsx(PayPalTabs, {}),
708
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-ui-border-base p-4 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-y-6 md:grid-cols-[260px_1fr] md:items-start", children: [
709
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium pt-2", children: "Environment" }),
710
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xl", children: /* @__PURE__ */ jsxRuntime.jsxs(
711
+ "select",
712
+ {
713
+ value: env,
714
+ onChange: handleEnvChange,
715
+ disabled: onboardingInProgress,
716
+ className: "w-full rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm",
717
+ children: [
718
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "sandbox", children: "Sandbox (Test Mode)" }),
719
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "live", children: "Live (Production)" })
720
+ ]
721
+ }
722
+ ) }),
723
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium pt-2", children: env === "sandbox" ? "Connect to PayPal Sandbox" : "Connect to PayPal Live" }),
724
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xl", children: connState === "connected" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
725
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-green-600 bg-green-50 p-3 rounded border border-green-200", children: [
726
+ "✅ Successfully connected to PayPal!",
727
+ /* @__PURE__ */ jsxRuntime.jsx(
728
+ "a",
729
+ {
730
+ "data-paypal-button": "true",
731
+ "data-paypal-onboard-complete": "onboardingCallback",
732
+ href: "#",
733
+ style: { display: "none" },
734
+ children: "PayPal"
735
+ }
736
+ )
737
+ ] }),
738
+ /* @__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: [
739
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-ui-fg-base", children: "Connected PayPal account" }),
740
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1", children: [
741
+ "Email:",
742
+ " ",
743
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-ui-fg-base", children: (statusInfo == null ? void 0 : statusInfo.seller_email) || "Unavailable" })
744
+ ] })
745
+ ] }),
746
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
747
+ "button",
748
+ {
749
+ type: "button",
750
+ onClick: handleDisconnect,
751
+ disabled: onboardingInProgress,
752
+ className: "rounded-md border border-ui-border-base px-3 py-2 text-sm font-medium hover:bg-ui-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed",
753
+ children: "Disconnect"
754
+ }
755
+ ) })
756
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
757
+ /* @__PURE__ */ jsxRuntime.jsxs(
758
+ "div",
759
+ {
760
+ ref: initLoaderRef,
761
+ id: "init-loader",
762
+ className: `status-msg mb-4 ${connState !== "loading" ? "hidden" : "block"}`,
763
+ children: [
764
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "loader inline-block align-middle mr-2" }),
765
+ /* @__PURE__ */ jsxRuntime.jsx("span", { id: "loader-text", className: "text-sm", children: onboardingInProgress ? "Configuring connection to PayPal…" : "Checking connection..." })
766
+ ]
767
+ }
768
+ ),
769
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${connState === "ready" ? "block" : "hidden"}`, children: [
770
+ /* @__PURE__ */ jsxRuntime.jsx(
771
+ "a",
772
+ {
773
+ ref: (node) => {
774
+ paypalButtonRef.current = node;
775
+ ppBtnMeasureRef.current = node;
776
+ },
777
+ id: "paypal-button",
778
+ "data-paypal-button": "true",
779
+ href: finalUrl || "#",
780
+ "data-paypal-onboard-complete": "onboardingCallback",
781
+ onClick: handleConnectClick,
782
+ className: "btn-paypal",
783
+ style: {
784
+ borderRadius: "50px",
785
+ textDecoration: "none",
786
+ display: "inline-block",
787
+ fontWeight: "bold",
788
+ border: "none",
789
+ cursor: onboardingInProgress ? "not-allowed" : "pointer",
790
+ opacity: onboardingInProgress ? 0.6 : 1,
791
+ pointerEvents: onboardingInProgress ? "none" : "auto"
792
+ },
793
+ children: "Connect to PayPal"
794
+ }
795
+ ),
796
+ /* @__PURE__ */ jsxRuntime.jsx(
797
+ "div",
798
+ {
799
+ className: "mt-2",
800
+ style: {
801
+ width: ppBtnWidth ? `${ppBtnWidth}px` : "auto",
802
+ marginTop: "20px",
803
+ marginBottom: "10px"
804
+ },
805
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-ui-fg-muted leading-none", children: "OR" }) })
806
+ }
807
+ ),
808
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(
809
+ "button",
810
+ {
811
+ type: "button",
812
+ onClick: () => setShowManual(!showManual),
813
+ disabled: onboardingInProgress,
814
+ className: "text-sm text-ui-fg-interactive underline whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed",
815
+ children: "Click here to insert credentials manually"
816
+ }
817
+ ) })
818
+ ] }),
819
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${connState === "ready" ? "hidden" : "block"} mt-3`, children: /* @__PURE__ */ jsxRuntime.jsx(
820
+ "button",
821
+ {
822
+ type: "button",
823
+ onClick: () => setShowManual(!showManual),
824
+ disabled: onboardingInProgress,
825
+ className: "text-sm text-ui-fg-interactive underline whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed",
826
+ children: "Click here to insert credentials manually"
827
+ }
828
+ ) }),
829
+ /* @__PURE__ */ jsxRuntime.jsx(
830
+ "div",
831
+ {
832
+ ref: errorLogRef,
833
+ id: "error-log",
834
+ className: `mt-4 text-left text-xs bg-red-50 text-red-600 p-3 border border-red-200 rounded ${connState === "error" && error ? "block" : "hidden"}`,
835
+ children: error
836
+ }
837
+ )
838
+ ] }) }),
839
+ showManual && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "md:col-span-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-[260px] max-w-xl mt-4 grid grid-cols-1 gap-3 md:grid-cols-2", children: [
840
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
841
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Client ID" }),
842
+ /* @__PURE__ */ jsxRuntime.jsx(
843
+ "input",
844
+ {
845
+ type: "text",
846
+ value: clientId,
847
+ onChange: (e) => setClientId(e.target.value),
848
+ disabled: onboardingInProgress,
849
+ className: "rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm disabled:opacity-50",
850
+ placeholder: env === "sandbox" ? "Sandbox Client ID" : "Live Client ID"
851
+ }
852
+ )
853
+ ] }),
854
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
855
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Client Secret" }),
856
+ /* @__PURE__ */ jsxRuntime.jsx(
857
+ "input",
858
+ {
859
+ type: "password",
860
+ value: secret,
861
+ onChange: (e) => setSecret(e.target.value),
862
+ disabled: onboardingInProgress,
863
+ className: "rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm disabled:opacity-50",
864
+ placeholder: env === "sandbox" ? "Sandbox Secret" : "Live Secret"
865
+ }
866
+ )
867
+ ] }),
868
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 md:col-span-2", children: [
869
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Merchant ID (optional)" }),
870
+ /* @__PURE__ */ jsxRuntime.jsx(
871
+ "input",
872
+ {
873
+ type: "text",
874
+ value: merchantId,
875
+ onChange: (e) => setMerchantId(e.target.value),
876
+ disabled: onboardingInProgress,
877
+ className: "rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm disabled:opacity-50",
878
+ placeholder: "Merchant ID"
879
+ }
880
+ )
881
+ ] }),
882
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "md:col-span-2 flex items-center gap-2 mt-2", children: [
883
+ /* @__PURE__ */ jsxRuntime.jsx(
884
+ "button",
885
+ {
886
+ type: "button",
887
+ className: "rounded-md border border-ui-border-base px-3 py-2 text-sm font-medium hover:bg-ui-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed",
888
+ onClick: () => setShowManual(false),
889
+ disabled: onboardingInProgress,
890
+ children: "Cancel"
891
+ }
892
+ ),
893
+ /* @__PURE__ */ jsxRuntime.jsx(
894
+ "button",
895
+ {
896
+ type: "button",
897
+ className: "rounded-md border border-ui-border-base px-3 py-2 text-sm font-medium bg-ui-bg-base hover:bg-ui-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed",
898
+ disabled: !canSaveManual || onboardingInProgress,
899
+ onClick: handleSaveManual,
900
+ children: "Save credentials"
901
+ }
902
+ )
903
+ ] })
904
+ ] }) })
905
+ ] }) })
906
+ ] }),
907
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
908
+ .loader {
909
+ border: 3px solid #f3f3f3;
910
+ border-top: 3px solid #0070ba;
911
+ border-radius: 50%;
912
+ width: 18px;
913
+ height: 18px;
914
+ animation: spin 1s linear infinite;
915
+ display: inline-block;
916
+ vertical-align: middle;
917
+ margin-right: 8px;
945
918
  }
946
- try {
947
- localStorage.removeItem(CACHE_KEY);
948
- localStorage.removeItem(RELOAD_KEY);
949
- } catch {
919
+ @keyframes spin {
920
+ 0% { transform: rotate(0deg); }
921
+ 100% { transform: rotate(360deg); }
950
922
  }
951
- window.location.href = window.location.href;
952
- } catch (e) {
953
- console.error(e);
954
- setConnState("error");
955
- setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
956
- setOnboardingInProgress(false);
957
- }
958
- };
959
- return () => {
960
- window.onboardingCallback = void 0;
961
- };
962
- }, [env]);
963
- react.useLayoutEffect(() => {
964
- const el = ppBtnMeasureRef.current;
965
- if (!el) return;
966
- const update = () => {
967
- const w = Math.round(el.getBoundingClientRect().width || 0);
968
- if (w > 0) setPpBtnWidth(w);
969
- };
970
- update();
971
- let ro = null;
972
- if (typeof ResizeObserver !== "undefined") {
973
- ro = new ResizeObserver(() => update());
974
- ro.observe(el);
975
- } else {
976
- window.addEventListener("resize", update);
977
- }
978
- return () => {
979
- if (ro) ro.disconnect();
980
- else window.removeEventListener("resize", update);
981
- };
982
- }, [connState, env, finalUrl]);
983
- const handleConnectClick = (e) => {
984
- if (connState !== "ready" || !finalUrl || onboardingInProgress) {
985
- e.preventDefault();
986
- }
923
+ ` })
924
+ ] });
925
+ }
926
+ const DEFAULT_FORM = {
927
+ enabled: true,
928
+ title: "Credit or Debit Card",
929
+ disabledCards: [],
930
+ threeDS: "when_required",
931
+ cardSaveEnabled: false
932
+ };
933
+ function mergeWithDefaults(saved) {
934
+ if (!saved) return { ...DEFAULT_FORM };
935
+ const entries = Object.entries(saved).filter(([, value]) => value !== void 0);
936
+ return {
937
+ ...DEFAULT_FORM,
938
+ ...Object.fromEntries(entries)
987
939
  };
988
- const handleSaveManual = async () => {
989
- if (!canSaveManual || onboardingInProgress) return;
990
- setOnboardingInProgress(true);
991
- setConnState("loading");
992
- setError(null);
993
- try {
994
- const res = await fetch(SAVE_CREDENTIALS_ENDPOINT, {
995
- method: "POST",
996
- headers: { "content-type": "application/json" },
997
- body: JSON.stringify({
998
- clientId: clientId.trim(),
999
- clientSecret: secret.trim()
1000
- })
1001
- });
1002
- if (!res.ok) {
1003
- const txt = await res.text().catch(() => "");
1004
- throw new Error(txt || `Save credentials failed (${res.status})`);
940
+ }
941
+ const CARD_BRANDS = [
942
+ { value: "visa", label: "Visa" },
943
+ { value: "mastercard", label: "Mastercard" },
944
+ { value: "amex", label: "American Express" },
945
+ { value: "discover", label: "Discover" },
946
+ { value: "diners", label: "Diners Club" },
947
+ { value: "jcb", label: "JCB" },
948
+ { value: "unionpay", label: "UnionPay" }
949
+ ];
950
+ const THREE_DS_OPTIONS = [
951
+ {
952
+ value: "when_required",
953
+ label: "3D Secure when required",
954
+ hint: "Triggers 3DS only when the card / issuer requires it."
955
+ },
956
+ {
957
+ value: "sli",
958
+ label: "3D Secure (SCA) / liability shift (recommended)",
959
+ hint: "Attempts to optimize for liability shift while remaining compliant."
960
+ },
961
+ {
962
+ value: "always",
963
+ label: "Always request 3D Secure",
964
+ hint: "Forces 3DS challenge whenever possible (may reduce conversion)."
965
+ }
966
+ ];
967
+ function cx$1(...parts) {
968
+ return parts.filter(Boolean).join(" ");
969
+ }
970
+ function Pill$1({
971
+ children,
972
+ onRemove
973
+ }) {
974
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-ui-border-base bg-ui-bg-base px-2 py-1 text-sm text-ui-fg-base", children: [
975
+ children,
976
+ onRemove ? /* @__PURE__ */ jsxRuntime.jsx(
977
+ "button",
978
+ {
979
+ type: "button",
980
+ onClick: onRemove,
981
+ className: "ml-1 rounded px-1 text-ui-fg-subtle hover:text-ui-fg-base",
982
+ "aria-label": "Remove",
983
+ children: "×"
1005
984
  }
1006
- setConnState("connected");
1007
- setStatusInfo({
1008
- seller_client_id_masked: maskValue(clientId.trim()),
1009
- seller_client_secret_masked: "••••••••"
1010
- });
1011
- setShowManual(false);
985
+ ) : null
986
+ ] });
987
+ }
988
+ function SectionCard$1({
989
+ title,
990
+ description,
991
+ right,
992
+ children
993
+ }) {
994
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm", children: [
995
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 border-b border-ui-border-base p-4", children: [
996
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
997
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-semibold text-ui-fg-base", children: title }),
998
+ description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-sm text-ui-fg-subtle", children: description }) : null
999
+ ] }),
1000
+ right
1001
+ ] }),
1002
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children })
1003
+ ] });
1004
+ }
1005
+ function FieldRow$1({
1006
+ label,
1007
+ hint,
1008
+ children
1009
+ }) {
1010
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-12 items-start gap-4 py-3", children: [
1011
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-12 md:col-span-4", children: [
1012
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-ui-fg-base", children: label }),
1013
+ hint ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-xs text-ui-fg-subtle", children: hint }) : null
1014
+ ] }),
1015
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-12 md:col-span-8", children })
1016
+ ] });
1017
+ }
1018
+ function AdvancedCardPaymentsTab() {
1019
+ var _a, _b;
1020
+ const [form, setForm] = react.useState(() => ({ ...DEFAULT_FORM }));
1021
+ const [loading, setLoading] = react.useState(false);
1022
+ const [saving, setSaving] = react.useState(false);
1023
+ const [toast, setToast] = react.useState(null);
1024
+ const didInit = react.useRef(false);
1025
+ react.useEffect(() => {
1026
+ if (didInit.current) return;
1027
+ didInit.current = true;
1028
+ (async () => {
1012
1029
  try {
1013
- localStorage.removeItem(CACHE_KEY);
1014
- localStorage.removeItem(RELOAD_KEY);
1015
- } catch {
1016
- }
1017
- } catch (e) {
1018
- console.error(e);
1019
- setConnState("error");
1020
- setError((e == null ? void 0 : e.message) || "Failed to save credentials.");
1021
- } finally {
1022
- setOnboardingInProgress(false);
1023
- }
1024
- };
1025
- const handleDisconnect = async () => {
1026
- if (onboardingInProgress) return;
1027
- if (!window.confirm("Disconnect PayPal for this environment?")) return;
1028
- setOnboardingInProgress(true);
1029
- setConnState("loading");
1030
- setError(null);
1031
- setFinalUrl("");
1032
- setShowManual(false);
1030
+ setLoading(true);
1031
+ const r = await fetch("/admin/paypal/settings", {
1032
+ credentials: "include",
1033
+ headers: { "Accept": "application/json" }
1034
+ });
1035
+ if (!r.ok) return;
1036
+ const json = await r.json();
1037
+ const payload = (json == null ? void 0 : json.data) ?? json;
1038
+ const saved = payload == null ? void 0 : payload.advanced_card_payments;
1039
+ if (saved && typeof saved === "object") {
1040
+ setForm(mergeWithDefaults(saved));
1041
+ }
1042
+ } finally {
1043
+ setLoading(false);
1044
+ }
1045
+ })();
1046
+ }, []);
1047
+ async function onSave() {
1033
1048
  try {
1034
- const res = await fetch(DISCONNECT_ENDPOINT, {
1049
+ setSaving(true);
1050
+ const r = await fetch("/admin/paypal/settings", {
1035
1051
  method: "POST",
1036
- headers: { "content-type": "application/json" },
1037
- body: JSON.stringify({ environment: env })
1052
+ credentials: "include",
1053
+ headers: {
1054
+ "Content-Type": "application/json",
1055
+ "Accept": "application/json"
1056
+ },
1057
+ body: JSON.stringify({ advanced_card_payments: form })
1038
1058
  });
1039
- if (!res.ok) {
1040
- const t = await res.text().catch(() => "");
1041
- throw new Error(t || `Disconnect failed (${res.status})`);
1059
+ if (!r.ok) {
1060
+ const t = await r.text();
1061
+ setToast({ type: "error", message: "Failed to save settings. " + t });
1062
+ window.setTimeout(() => setToast(null), 3500);
1063
+ return;
1042
1064
  }
1043
- try {
1044
- localStorage.removeItem(CACHE_KEY);
1045
- localStorage.removeItem(RELOAD_KEY);
1046
- } catch {
1065
+ const json = await r.json().catch(() => null);
1066
+ const payload = (json == null ? void 0 : json.data) ?? json;
1067
+ const saved = payload == null ? void 0 : payload.advanced_card_payments;
1068
+ if (saved && typeof saved === "object") {
1069
+ setForm(mergeWithDefaults(saved));
1047
1070
  }
1048
- currentRunId.current = ++runIdRef.current;
1049
- const runId = currentRunId.current;
1050
- fetchFreshLink(runId);
1051
- } catch (e) {
1052
- console.error(e);
1053
- setConnState("error");
1054
- setError((e == null ? void 0 : e.message) || "Failed to disconnect.");
1071
+ setToast({ type: "success", message: "Settings saved" });
1072
+ window.setTimeout(() => setToast(null), 2500);
1055
1073
  } finally {
1056
- setOnboardingInProgress(false);
1057
- }
1058
- };
1059
- const handleEnvChange = async (e) => {
1060
- const next = e.target.value;
1061
- setEnv(next);
1062
- cachedUrl = null;
1063
- try {
1064
- await fetch("/admin/paypal/environment", {
1065
- method: "POST",
1066
- headers: { "content-type": "application/json" },
1067
- body: JSON.stringify({ environment: next })
1068
- });
1069
- } catch {
1070
- }
1071
- try {
1072
- localStorage.removeItem(CACHE_KEY);
1073
- localStorage.removeItem(RELOAD_KEY);
1074
- } catch {
1074
+ setSaving(false);
1075
1075
  }
1076
- };
1077
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6", children: [
1078
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
1079
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: "PayPal Gateway By Easy Payment" }),
1080
- /* @__PURE__ */ jsxRuntime.jsx(PayPalTabs, {}),
1081
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-ui-border-base p-4 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-y-6 md:grid-cols-[260px_1fr] md:items-start", children: [
1082
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium pt-2", children: "Environment" }),
1083
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xl", children: /* @__PURE__ */ jsxRuntime.jsxs(
1084
- "select",
1085
- {
1086
- value: env,
1087
- onChange: handleEnvChange,
1088
- disabled: onboardingInProgress,
1089
- className: "w-full rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm",
1090
- children: [
1091
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "sandbox", children: "Sandbox (Test Mode)" }),
1092
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "live", children: "Live (Production)" })
1093
- ]
1094
- }
1095
- ) }),
1096
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium pt-2", children: env === "sandbox" ? "Connect to PayPal Sandbox" : "Connect to PayPal Live" }),
1097
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xl", children: connState === "connected" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1098
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-green-600 bg-green-50 p-3 rounded border border-green-200", children: [
1099
- " Successfully connected to PayPal!",
1100
- /* @__PURE__ */ jsxRuntime.jsx(
1101
- "a",
1102
- {
1103
- "data-paypal-button": "true",
1104
- "data-paypal-onboard-complete": "onboardingCallback",
1105
- href: "#",
1106
- style: { display: "none" },
1107
- children: "PayPal"
1108
- }
1109
- )
1110
- ] }),
1111
- /* @__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: [
1112
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-ui-fg-base", children: "Connected PayPal account" }),
1113
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1", children: [
1114
- "Email:",
1115
- " ",
1116
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-ui-fg-base", children: (statusInfo == null ? void 0 : statusInfo.seller_email) || "Unavailable" })
1117
- ] })
1118
- ] }),
1119
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
1120
- "button",
1121
- {
1122
- type: "button",
1123
- onClick: handleDisconnect,
1124
- disabled: onboardingInProgress,
1125
- className: "rounded-md border border-ui-border-base px-3 py-2 text-sm font-medium hover:bg-ui-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed",
1126
- children: "Disconnect"
1127
- }
1128
- ) })
1129
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1130
- /* @__PURE__ */ jsxRuntime.jsxs(
1131
- "div",
1132
- {
1133
- ref: initLoaderRef,
1134
- id: "init-loader",
1135
- className: `status-msg mb-4 ${connState !== "loading" ? "hidden" : "block"}`,
1136
- children: [
1137
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "loader inline-block align-middle mr-2" }),
1138
- /* @__PURE__ */ jsxRuntime.jsx("span", { id: "loader-text", className: "text-sm", children: onboardingInProgress ? "Configuring connection to PayPal…" : "Checking connection..." })
1139
- ]
1140
- }
1141
- ),
1142
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${connState === "ready" ? "block" : "hidden"}`, children: [
1143
- /* @__PURE__ */ jsxRuntime.jsx(
1144
- "a",
1145
- {
1146
- ref: (node) => {
1147
- paypalButtonRef.current = node;
1148
- ppBtnMeasureRef.current = node;
1149
- },
1150
- id: "paypal-button",
1151
- "data-paypal-button": "true",
1152
- href: finalUrl || "#",
1153
- "data-paypal-onboard-complete": "onboardingCallback",
1154
- onClick: handleConnectClick,
1155
- className: "btn-paypal",
1156
- style: {
1157
- borderRadius: "50px",
1158
- textDecoration: "none",
1159
- display: "inline-block",
1160
- fontWeight: "bold",
1161
- border: "none",
1162
- cursor: onboardingInProgress ? "not-allowed" : "pointer",
1163
- opacity: onboardingInProgress ? 0.6 : 1,
1164
- pointerEvents: onboardingInProgress ? "none" : "auto"
1165
- },
1166
- children: "Connect to PayPal"
1167
- }
1168
- ),
1169
- /* @__PURE__ */ jsxRuntime.jsx(
1170
- "div",
1171
- {
1172
- className: "mt-2",
1173
- style: {
1174
- width: ppBtnWidth ? `${ppBtnWidth}px` : "auto",
1175
- marginTop: "20px",
1176
- marginBottom: "10px"
1177
- },
1178
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-ui-fg-muted leading-none", children: "OR" }) })
1179
- }
1180
- ),
1181
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(
1182
- "button",
1183
- {
1184
- type: "button",
1185
- onClick: () => setShowManual(!showManual),
1186
- disabled: onboardingInProgress,
1187
- className: "text-sm text-ui-fg-interactive underline whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed",
1188
- children: "Click here to insert credentials manually"
1189
- }
1190
- ) })
1191
- ] }),
1192
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${connState === "ready" ? "hidden" : "block"} mt-3`, children: /* @__PURE__ */ jsxRuntime.jsx(
1076
+ }
1077
+ const disabledSet = react.useMemo(() => new Set(form.disabledCards), [form.disabledCards]);
1078
+ function toggleDisabledCard(value) {
1079
+ setForm((prev) => {
1080
+ const exists = prev.disabledCards.includes(value);
1081
+ return {
1082
+ ...prev,
1083
+ disabledCards: exists ? prev.disabledCards.filter((v) => v !== value) : [...prev.disabledCards, value]
1084
+ };
1085
+ });
1086
+ }
1087
+ function removeDisabledCard(value) {
1088
+ setForm((prev) => ({
1089
+ ...prev,
1090
+ disabledCards: prev.disabledCards.filter((v) => v !== value)
1091
+ }));
1092
+ }
1093
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
1094
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between gap-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-ui-fg-base", children: "PayPal Gateway By Easy Payment" }) }) }),
1095
+ /* @__PURE__ */ jsxRuntime.jsx(PayPalTabs, {}),
1096
+ toast ? /* @__PURE__ */ jsxRuntime.jsx(
1097
+ "div",
1098
+ {
1099
+ className: "fixed right-6 top-6 z-50 rounded-md border border-ui-border-base bg-ui-bg-base px-4 py-3 text-sm shadow-lg",
1100
+ role: "status",
1101
+ "aria-live": "polite",
1102
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: toast.type === "success" ? "text-ui-fg-base" : "text-ui-fg-error", children: toast.message })
1103
+ }
1104
+ ) : null,
1105
+ /* @__PURE__ */ jsxRuntime.jsx(
1106
+ SectionCard$1,
1107
+ {
1108
+ title: "Advanced Card Payments",
1109
+ description: "Control card checkout settings, 3D Secure behavior, and card saving.",
1110
+ right: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1111
+ /* @__PURE__ */ jsxRuntime.jsx(
1193
1112
  "button",
1194
1113
  {
1195
1114
  type: "button",
1196
- onClick: () => setShowManual(!showManual),
1197
- disabled: onboardingInProgress,
1198
- className: "text-sm text-ui-fg-interactive underline whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed",
1199
- children: "Click here to insert credentials manually"
1200
- }
1201
- ) }),
1202
- /* @__PURE__ */ jsxRuntime.jsx(
1203
- "div",
1204
- {
1205
- ref: errorLogRef,
1206
- id: "error-log",
1207
- className: `mt-4 text-left text-xs bg-red-50 text-red-600 p-3 border border-red-200 rounded ${connState === "error" && error ? "block" : "hidden"}`,
1208
- children: error
1209
- }
1210
- )
1211
- ] }) }),
1212
- showManual && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "md:col-span-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-[260px] max-w-xl mt-4 grid grid-cols-1 gap-3 md:grid-cols-2", children: [
1213
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
1214
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Client ID" }),
1215
- /* @__PURE__ */ jsxRuntime.jsx(
1216
- "input",
1217
- {
1218
- type: "text",
1219
- value: clientId,
1220
- onChange: (e) => setClientId(e.target.value),
1221
- disabled: onboardingInProgress,
1222
- className: "rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm disabled:opacity-50",
1223
- placeholder: env === "sandbox" ? "Sandbox Client ID" : "Live Client ID"
1224
- }
1225
- )
1226
- ] }),
1227
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
1228
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Client Secret" }),
1115
+ onClick: onSave,
1116
+ disabled: saving || loading,
1117
+ className: "rounded-md bg-ui-button-neutral px-4 py-2 text-sm font-medium text-ui-fg-on-color shadow-sm hover:opacity-90 disabled:opacity-60",
1118
+ children: saving ? "Saving..." : "Save settings"
1119
+ }
1120
+ ),
1121
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-subtle", children: "Loading…" }) : null
1122
+ ] }),
1123
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "divide-y divide-ui-border-base", children: [
1124
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow$1, { label: "Enable/Disable", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2", children: [
1229
1125
  /* @__PURE__ */ jsxRuntime.jsx(
1230
1126
  "input",
1231
1127
  {
1232
- type: "password",
1233
- value: secret,
1234
- onChange: (e) => setSecret(e.target.value),
1235
- disabled: onboardingInProgress,
1236
- className: "rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm disabled:opacity-50",
1237
- placeholder: env === "sandbox" ? "Sandbox Secret" : "Live Secret"
1128
+ type: "checkbox",
1129
+ checked: form.enabled,
1130
+ onChange: (e) => setForm((p) => ({ ...p, enabled: e.target.checked })),
1131
+ className: "h-4 w-4 rounded border-ui-border-base"
1238
1132
  }
1239
- )
1240
- ] }),
1241
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 md:col-span-2", children: [
1242
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Merchant ID (optional)" }),
1133
+ ),
1134
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Enable Advanced Credit/Debit Card" })
1135
+ ] }) }),
1136
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow$1, { label: "Title", children: /* @__PURE__ */ jsxRuntime.jsx(
1137
+ "input",
1138
+ {
1139
+ value: form.title,
1140
+ onChange: (e) => setForm((p) => ({ ...p, title: e.target.value })),
1141
+ className: "w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive",
1142
+ placeholder: "Credit or Debit Card"
1143
+ }
1144
+ ) }),
1145
+ /* @__PURE__ */ jsxRuntime.jsx(
1146
+ FieldRow$1,
1147
+ {
1148
+ label: "Disable specific credit cards",
1149
+ hint: "Select card brands to hide from the card form.",
1150
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1151
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: form.disabledCards.length ? form.disabledCards.map((v) => {
1152
+ var _a2;
1153
+ const label = ((_a2 = CARD_BRANDS.find((b) => b.value === v)) == null ? void 0 : _a2.label) ?? v;
1154
+ return /* @__PURE__ */ jsxRuntime.jsx(Pill$1, { onRemove: () => removeDisabledCard(v), children: label }, v);
1155
+ }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-subtle", children: "No card brands disabled." }) }),
1156
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-ui-border-base p-3", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-2 md:grid-cols-2", children: CARD_BRANDS.map((b) => {
1157
+ const checked = disabledSet.has(b.value);
1158
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1159
+ "label",
1160
+ {
1161
+ className: cx$1(
1162
+ "flex items-center gap-2 rounded-md p-2",
1163
+ "hover:bg-ui-bg-subtle"
1164
+ ),
1165
+ children: [
1166
+ /* @__PURE__ */ jsxRuntime.jsx(
1167
+ "input",
1168
+ {
1169
+ type: "checkbox",
1170
+ checked,
1171
+ onChange: () => toggleDisabledCard(b.value),
1172
+ className: "h-4 w-4 rounded border-ui-border-base"
1173
+ }
1174
+ ),
1175
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: b.label })
1176
+ ]
1177
+ },
1178
+ b.value
1179
+ );
1180
+ }) }) })
1181
+ ] })
1182
+ }
1183
+ ),
1184
+ /* @__PURE__ */ jsxRuntime.jsx(
1185
+ FieldRow$1,
1186
+ {
1187
+ label: "Contingency for 3D Secure",
1188
+ hint: "Choose when 3D Secure should be triggered during card payments.",
1189
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1190
+ /* @__PURE__ */ jsxRuntime.jsx(
1191
+ "select",
1192
+ {
1193
+ value: form.threeDS,
1194
+ onChange: (e) => setForm((p) => ({ ...p, threeDS: e.target.value })),
1195
+ className: "w-full rounded-md border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm text-ui-fg-base outline-none focus:ring-2 focus:ring-ui-border-interactive",
1196
+ children: THREE_DS_OPTIONS.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
1197
+ }
1198
+ ),
1199
+ ((_a = THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)) == null ? void 0 : _a.hint) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-ui-fg-subtle", children: (_b = THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)) == null ? void 0 : _b.hint }) : null
1200
+ ] })
1201
+ }
1202
+ ),
1203
+ /* @__PURE__ */ jsxRuntime.jsx(FieldRow$1, { label: "Card Save Enabled", hint: "Allow customers to save a card at checkout for future use.", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2", children: [
1243
1204
  /* @__PURE__ */ jsxRuntime.jsx(
1244
1205
  "input",
1245
1206
  {
1246
- type: "text",
1247
- value: merchantId,
1248
- onChange: (e) => setMerchantId(e.target.value),
1249
- disabled: onboardingInProgress,
1250
- className: "rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm disabled:opacity-50",
1251
- placeholder: "Merchant ID"
1252
- }
1253
- )
1254
- ] }),
1255
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "md:col-span-2 flex items-center gap-2 mt-2", children: [
1256
- /* @__PURE__ */ jsxRuntime.jsx(
1257
- "button",
1258
- {
1259
- type: "button",
1260
- className: "rounded-md border border-ui-border-base px-3 py-2 text-sm font-medium hover:bg-ui-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed",
1261
- onClick: () => setShowManual(false),
1262
- disabled: onboardingInProgress,
1263
- children: "Cancel"
1207
+ type: "checkbox",
1208
+ checked: form.cardSaveEnabled,
1209
+ onChange: (e) => setForm((p) => ({ ...p, cardSaveEnabled: e.target.checked })),
1210
+ className: "h-4 w-4 rounded border-ui-border-base"
1264
1211
  }
1265
1212
  ),
1266
- /* @__PURE__ */ jsxRuntime.jsx(
1267
- "button",
1268
- {
1269
- type: "button",
1270
- className: "rounded-md border border-ui-border-base px-3 py-2 text-sm font-medium bg-ui-bg-base hover:bg-ui-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed",
1271
- disabled: !canSaveManual || onboardingInProgress,
1272
- onClick: handleSaveManual,
1273
- children: "Save credentials"
1274
- }
1275
- )
1276
- ] })
1277
- ] }) })
1278
- ] }) })
1279
- ] }),
1280
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
1281
- .loader {
1282
- border: 3px solid #f3f3f3;
1283
- border-top: 3px solid #0070ba;
1284
- border-radius: 50%;
1285
- width: 18px;
1286
- height: 18px;
1287
- animation: spin 1s linear infinite;
1288
- display: inline-block;
1289
- vertical-align: middle;
1290
- margin-right: 8px;
1291
- }
1292
- @keyframes spin {
1293
- 0% { transform: rotate(0deg); }
1294
- 100% { transform: rotate(360deg); }
1213
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Enable card saving at checkout" })
1214
+ ] }) })
1215
+ ] })
1216
+ }
1217
+ )
1218
+ ] }) });
1219
+ }
1220
+ function formatDate$2(value) {
1221
+ if (!value) {
1222
+ return "";
1223
+ }
1224
+ const parsed = new Date(value);
1225
+ if (Number.isNaN(parsed.getTime())) {
1226
+ return value;
1227
+ }
1228
+ return parsed.toLocaleString();
1229
+ }
1230
+ function PayPalAuditLogsPage() {
1231
+ const [logs, setLogs] = react.useState([]);
1232
+ const [loading, setLoading] = react.useState(false);
1233
+ const [error, setError] = react.useState(null);
1234
+ const fetchLogs = react.useCallback(async () => {
1235
+ try {
1236
+ setLoading(true);
1237
+ setError(null);
1238
+ const response = await fetch("/admin/paypal/audit-logs?limit=50", {
1239
+ credentials: "include",
1240
+ headers: {
1241
+ Accept: "application/json"
1295
1242
  }
1296
- ` })
1297
- ] });
1243
+ });
1244
+ if (!response.ok) {
1245
+ const message = await response.text().catch(() => "");
1246
+ throw new Error(message || "Failed to load audit logs.");
1247
+ }
1248
+ const data = await response.json().catch(() => ({}));
1249
+ setLogs((data == null ? void 0 : data.logs) || []);
1250
+ } catch (fetchError) {
1251
+ setError((fetchError == null ? void 0 : fetchError.message) || "Failed to load audit logs.");
1252
+ setLogs([]);
1253
+ } finally {
1254
+ setLoading(false);
1255
+ }
1256
+ }, []);
1257
+ react.useEffect(() => {
1258
+ fetchLogs();
1259
+ }, [fetchLogs]);
1260
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
1261
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1262
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-ui-fg-base", children: "PayPal Audit Logs" }),
1263
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-ui-fg-subtle", children: "Track administrative changes and credential events for PayPal configuration." })
1264
+ ] }),
1265
+ /* @__PURE__ */ jsxRuntime.jsx(PayPalTabs, {}),
1266
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm", children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-ui-border-base p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-4", children: [
1268
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-semibold text-ui-fg-base", children: "Latest events" }),
1269
+ /* @__PURE__ */ jsxRuntime.jsx(
1270
+ "button",
1271
+ {
1272
+ type: "button",
1273
+ onClick: fetchLogs,
1274
+ className: "rounded-md border border-ui-border-base px-3 py-2 text-sm text-ui-fg-base",
1275
+ disabled: loading,
1276
+ children: loading ? "Refreshing..." : "Refresh"
1277
+ }
1278
+ )
1279
+ ] }) }),
1280
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
1281
+ error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-600", children: error }) : null,
1282
+ !error && logs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-ui-fg-subtle", children: loading ? "Loading audit logs..." : "No audit log entries found yet." }) : null,
1283
+ logs.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "min-w-full text-left text-sm", children: [
1284
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "text-ui-fg-muted", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1285
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 pr-4 font-medium", children: "Timestamp" }),
1286
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 pr-4 font-medium", children: "Event" }),
1287
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 font-medium", children: "Details" })
1288
+ ] }) }),
1289
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: logs.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-t border-ui-border-base", children: [
1290
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-3 pr-4 text-ui-fg-base", children: formatDate$2(entry.created_at) || "—" }),
1291
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-3 pr-4 text-ui-fg-base", children: entry.event_type || "—" }),
1292
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-3 text-ui-fg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap rounded-md bg-ui-bg-subtle p-2 text-xs text-ui-fg-subtle", children: JSON.stringify(entry.metadata || {}, null, 2) }) })
1293
+ ] }, entry.id)) })
1294
+ ] }) }) : null
1295
+ ] })
1296
+ ] })
1297
+ ] }) });
1298
+ }
1299
+ function PayPalGooglePayPage() {
1300
+ return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
1298
1301
  }
1299
1302
  const EMPTY_FILTERS = {
1300
1303
  dispute_id: "",
@@ -1488,9 +1491,6 @@ function PayPalDisputesPage() {
1488
1491
  ] })
1489
1492
  ] }) });
1490
1493
  }
1491
- function PayPalGooglePayPage() {
1492
- return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
1493
- }
1494
1494
  function PayPalPayLaterMessagingPage() {
1495
1495
  return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
1496
1496
  }
@@ -2052,30 +2052,30 @@ const routeModule = {
2052
2052
  Component: AdditionalSettingsTab,
2053
2053
  path: "/settings/paypal/additional-settings"
2054
2054
  },
2055
- {
2056
- Component: AdvancedCardPaymentsTab,
2057
- path: "/settings/paypal/advanced-card-payments"
2058
- },
2059
2055
  {
2060
2056
  Component: PayPalApplePayPage,
2061
2057
  path: "/settings/paypal/apple-pay"
2062
2058
  },
2063
- {
2064
- Component: PayPalAuditLogsPage,
2065
- path: "/settings/paypal/audit-logs"
2066
- },
2067
2059
  {
2068
2060
  Component: PayPalConnectionPage,
2069
2061
  path: "/settings/paypal/connection"
2070
2062
  },
2071
2063
  {
2072
- Component: PayPalDisputesPage,
2073
- path: "/settings/paypal/disputes"
2064
+ Component: AdvancedCardPaymentsTab,
2065
+ path: "/settings/paypal/advanced-card-payments"
2066
+ },
2067
+ {
2068
+ Component: PayPalAuditLogsPage,
2069
+ path: "/settings/paypal/audit-logs"
2074
2070
  },
2075
2071
  {
2076
2072
  Component: PayPalGooglePayPage,
2077
2073
  path: "/settings/paypal/google-pay"
2078
2074
  },
2075
+ {
2076
+ Component: PayPalDisputesPage,
2077
+ path: "/settings/paypal/disputes"
2078
+ },
2079
2079
  {
2080
2080
  Component: PayPalPayLaterMessagingPage,
2081
2081
  path: "/settings/paypal/pay-later-messaging"