@easypayment/medusa-payment-paypal 0.9.4 → 0.9.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.
- package/.medusa/server/src/admin/index.d.ts +2 -0
- package/.medusa/server/src/admin/index.d.ts.map +1 -0
- package/.medusa/server/src/admin/index.js +190 -233
- package/.medusa/server/src/admin/index.js.map +1 -0
- package/.medusa/server/src/admin/index.mjs +191 -234
- package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.d.ts +8 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.js +8 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/FieldRow.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.d.ts +8 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.js +8 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/SectionCard.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.d.ts +2 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.js +25 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Tabs.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.d.ts +12 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.js +22 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_components/Toast.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.d.ts +7 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.js +40 -0
- package/.medusa/server/src/admin/routes/settings/paypal/_utils/adminFetch.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.d.ts +2 -0
- package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.js +76 -0
- package/.medusa/server/src/admin/routes/settings/paypal/additional-settings/page.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.d.ts +2 -0
- package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.js +81 -0
- package/.medusa/server/src/admin/routes/settings/paypal/advanced-card-payments/page.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.d.ts +2 -0
- package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.js +9 -0
- package/.medusa/server/src/admin/routes/settings/paypal/apple-pay/page.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts +20 -0
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js +440 -0
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.d.ts +2 -0
- package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.js +9 -0
- package/.medusa/server/src/admin/routes/settings/paypal/google-pay/page.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/page.d.ts +4 -0
- package/.medusa/server/src/admin/routes/settings/paypal/page.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/page.js +14 -0
- package/.medusa/server/src/admin/routes/settings/paypal/page.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.d.ts +2 -0
- package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.js +9 -0
- package/.medusa/server/src/admin/routes/settings/paypal/pay-later-messaging/page.js.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.d.ts +2 -0
- package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.d.ts.map +1 -0
- package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.js +109 -0
- package/.medusa/server/src/admin/routes/settings/paypal/paypal-settings/page.js.map +1 -0
- package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.d.ts +1 -1
- package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -1
- package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js +24 -9
- package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js.map +1 -1
- package/.medusa/server/src/api/admin/paypal/disconnect/route.d.ts.map +1 -1
- package/.medusa/server/src/api/admin/paypal/disconnect/route.js +10 -3
- package/.medusa/server/src/api/admin/paypal/disconnect/route.js.map +1 -1
- package/.medusa/server/src/api/admin/paypal/environment/route.js +2 -2
- package/.medusa/server/src/api/admin/paypal/environment/route.js.map +1 -1
- package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts.map +1 -1
- package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js +3 -2
- package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js.map +1 -1
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js +1 -1
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js.map +1 -1
- package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js +4 -4
- package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js.map +1 -1
- package/.medusa/server/src/api/admin/paypal/save-credentials/route.d.ts.map +1 -1
- package/.medusa/server/src/api/admin/paypal/save-credentials/route.js +21 -8
- package/.medusa/server/src/api/admin/paypal/save-credentials/route.js.map +1 -1
- package/.medusa/server/src/api/admin/paypal/settings/route.d.ts.map +1 -1
- package/.medusa/server/src/api/admin/paypal/settings/route.js +34 -4
- package/.medusa/server/src/api/admin/paypal/settings/route.js.map +1 -1
- package/.medusa/server/src/api/admin/paypal/status/route.d.ts.map +1 -1
- package/.medusa/server/src/api/admin/paypal/status/route.js +11 -5
- package/.medusa/server/src/api/admin/paypal/status/route.js.map +1 -1
- package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts.map +1 -1
- package/.medusa/server/src/api/store/paypal/capture-order/route.js +21 -3
- package/.medusa/server/src/api/store/paypal/capture-order/route.js.map +1 -1
- package/.medusa/server/src/api/store/paypal/config/route.js +2 -2
- package/.medusa/server/src/api/store/paypal/config/route.js.map +1 -1
- package/.medusa/server/src/api/store/paypal/create-order/route.d.ts.map +1 -1
- package/.medusa/server/src/api/store/paypal/create-order/route.js +13 -9
- package/.medusa/server/src/api/store/paypal/create-order/route.js.map +1 -1
- package/.medusa/server/src/api/store/paypal/settings/route.js +3 -3
- package/.medusa/server/src/api/store/paypal/settings/route.js.map +1 -1
- package/.medusa/server/src/api/store/paypal/webhook/route.d.ts.map +1 -1
- package/.medusa/server/src/api/store/paypal/webhook/route.js +10 -3
- package/.medusa/server/src/api/store/paypal/webhook/route.js.map +1 -1
- package/.medusa/server/src/api/store/paypal-complete/route.d.ts.map +1 -1
- package/.medusa/server/src/api/store/paypal-complete/route.js +7 -4
- package/.medusa/server/src/api/store/paypal-complete/route.js.map +1 -1
- package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js +3 -3
- package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js.map +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.d.ts +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.js +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.js.map +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.d.ts +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.js +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.js.map +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.d.ts +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.js +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.js.map +1 -1
- package/.medusa/server/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.js +25 -0
- package/.medusa/server/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.js.map +1 -0
- package/.medusa/server/src/modules/paypal/models/paypal_connection.d.ts +1 -1
- package/.medusa/server/src/modules/paypal/models/paypal_connection.js +3 -3
- package/.medusa/server/src/modules/paypal/models/paypal_connection.js.map +1 -1
- package/.medusa/server/src/modules/paypal/models/paypal_metric.d.ts +1 -1
- package/.medusa/server/src/modules/paypal/models/paypal_metric.js +1 -1
- package/.medusa/server/src/modules/paypal/models/paypal_metric.js.map +1 -1
- package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.d.ts +1 -1
- package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.js +1 -1
- package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.js.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.js +95 -76
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.js.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/service.js +39 -89
- package/.medusa/server/src/modules/paypal/payment-provider/service.js.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.js +7 -0
- package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.js.map +1 -1
- package/.medusa/server/src/modules/paypal/service.d.ts +10 -8
- package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/service.js +56 -51
- package/.medusa/server/src/modules/paypal/service.js.map +1 -1
- package/.medusa/server/src/modules/paypal/types/config.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/types/config.js +11 -2
- package/.medusa/server/src/modules/paypal/types/config.js.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/paypal-auth.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/utils/paypal-auth.js +1 -0
- package/.medusa/server/src/modules/paypal/utils/paypal-auth.js.map +1 -1
- package/.medusa/server/src/modules/paypal/webhook-processor.d.ts.map +1 -1
- package/.medusa/server/src/modules/paypal/webhook-processor.js +1 -1
- package/.medusa/server/src/modules/paypal/webhook-processor.js.map +1 -1
- package/.medusa/server/src/subscribers/paypal-order-invoice.js +4 -4
- package/.medusa/server/src/subscribers/paypal-order-invoice.js.map +1 -1
- package/package.json +1 -1
- package/src/admin/index.ts +1 -7
- package/src/admin/routes/settings/paypal/_components/FieldRow.tsx +29 -0
- package/src/admin/routes/settings/paypal/_components/SectionCard.tsx +26 -0
- package/src/admin/routes/settings/paypal/_components/Toast.tsx +4 -3
- package/src/admin/routes/settings/paypal/_utils/adminFetch.ts +32 -0
- package/src/admin/routes/settings/paypal/additional-settings/page.tsx +23 -83
- package/src/admin/routes/settings/paypal/advanced-card-payments/page.tsx +17 -77
- package/src/admin/routes/settings/paypal/connection/page.tsx +88 -45
- package/src/admin/routes/settings/paypal/paypal-settings/page.tsx +23 -101
- package/src/api/admin/payment-collections/[id]/payment-sessions/route.ts +26 -9
- package/src/api/admin/paypal/disconnect/route.ts +9 -3
- package/src/api/admin/paypal/environment/route.ts +2 -2
- package/src/api/admin/paypal/onboard-complete/route.ts +4 -3
- package/src/api/admin/paypal/onboarding-link/route.ts +1 -1
- package/src/api/admin/paypal/onboarding-status/route.ts +5 -5
- package/src/api/admin/paypal/save-credentials/route.ts +24 -9
- package/src/api/admin/paypal/settings/route.ts +35 -4
- package/src/api/admin/paypal/status/route.ts +10 -5
- package/src/api/store/paypal/capture-order/route.ts +27 -3
- package/src/api/store/paypal/config/route.ts +2 -2
- package/src/api/store/paypal/create-order/route.ts +16 -10
- package/src/api/store/paypal/settings/route.ts +3 -3
- package/src/api/store/paypal/webhook/route.ts +13 -3
- package/src/api/store/paypal-complete/route.ts +8 -4
- package/src/modules/paypal/clients/paypal-seller.client.ts +3 -3
- package/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.ts +1 -1
- package/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.ts +1 -1
- package/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.ts +1 -1
- package/src/modules/paypal/migrations/20270202000000_align_nullable_defaults.ts +44 -0
- package/src/modules/paypal/models/paypal_connection.ts +3 -3
- package/src/modules/paypal/models/paypal_metric.ts +1 -1
- package/src/modules/paypal/models/paypal_webhook_event.ts +1 -1
- package/src/modules/paypal/payment-provider/card-service.ts +120 -80
- package/src/modules/paypal/payment-provider/service.ts +44 -93
- package/src/modules/paypal/payment-provider/webhook-utils.ts +9 -0
- package/src/modules/paypal/service.ts +79 -66
- package/src/modules/paypal/types/config.ts +18 -2
- package/src/modules/paypal/utils/paypal-auth.ts +1 -0
- package/src/modules/paypal/webhook-processor.ts +8 -5
- package/src/subscribers/paypal-order-invoice.ts +4 -4
|
@@ -1,38 +1,9 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from "react"
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from "react"
|
|
2
2
|
import PayPalTabs from "../_components/Tabs"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
query?: Record<string, string>
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async function adminFetch<T = unknown>(path: string, opts: AdminFetchOptions = {}): Promise<T> {
|
|
11
|
-
const { method = "GET", body, query } = opts
|
|
12
|
-
let url = path
|
|
13
|
-
if (query && Object.keys(query).length > 0) {
|
|
14
|
-
const params = new URLSearchParams(query)
|
|
15
|
-
url = `${path}?${params.toString()}`
|
|
16
|
-
}
|
|
17
|
-
const headers: Record<string, string> = { Accept: "application/json" }
|
|
18
|
-
if (body !== undefined) headers["Content-Type"] = "application/json"
|
|
19
|
-
if (typeof window !== "undefined") {
|
|
20
|
-
const token = (window as any).__medusa__?.token
|
|
21
|
-
if (token) headers["Authorization"] = `Bearer ${token}`
|
|
22
|
-
}
|
|
23
|
-
const res = await fetch(url, {
|
|
24
|
-
method, headers, credentials: "include",
|
|
25
|
-
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
26
|
-
})
|
|
27
|
-
const text = await res.text().catch(() => "")
|
|
28
|
-
if (!res.ok) {
|
|
29
|
-
if (res.status === 401) throw new Error("Unauthorized (401) - session may have expired. Please reload and log in again.")
|
|
30
|
-
if (res.status === 403) throw new Error("Forbidden (403) - you do not have permission to perform this action.")
|
|
31
|
-
throw new Error(text || `Request failed with status ${res.status}`)
|
|
32
|
-
}
|
|
33
|
-
if (!text) return {} as T
|
|
34
|
-
try { return JSON.parse(text) as T } catch { return {} as T }
|
|
35
|
-
}
|
|
3
|
+
import SectionCard from "../_components/SectionCard"
|
|
4
|
+
import FieldRow from "../_components/FieldRow"
|
|
5
|
+
import Toast, { ToastState } from "../_components/Toast"
|
|
6
|
+
import { adminFetch } from "../_utils/adminFetch"
|
|
36
7
|
|
|
37
8
|
type PaymentAction = "capture" | "authorize"
|
|
38
9
|
type LandingPage = "no_preference" | "login" | "billing"
|
|
@@ -53,7 +24,7 @@ const DEFAULT_FORM: AdditionalSettingsForm = {
|
|
|
53
24
|
landingPage: "no_preference",
|
|
54
25
|
requireInstantPayment: false,
|
|
55
26
|
sendItemDetails: true,
|
|
56
|
-
invoicePrefix: "
|
|
27
|
+
invoicePrefix: "PP-",
|
|
57
28
|
creditCardStatementName: "PayPal",
|
|
58
29
|
}
|
|
59
30
|
|
|
@@ -63,40 +34,15 @@ function mergeWithDefaults(saved?: Partial<AdditionalSettingsForm> | null) {
|
|
|
63
34
|
return { ...DEFAULT_FORM, ...(Object.fromEntries(entries) as Partial<AdditionalSettingsForm>) }
|
|
64
35
|
}
|
|
65
36
|
|
|
66
|
-
function SectionCard({ title, description, right, children }: { title: string; description?: React.ReactNode; right?: React.ReactNode; children: React.ReactNode }) {
|
|
67
|
-
return (
|
|
68
|
-
<div className="rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm">
|
|
69
|
-
<div className="flex items-start justify-between gap-4 border-b border-ui-border-base p-4">
|
|
70
|
-
<div>
|
|
71
|
-
<div className="text-base font-semibold text-ui-fg-base">{title}</div>
|
|
72
|
-
{description ? <div className="mt-1 text-sm text-ui-fg-subtle">{description}</div> : null}
|
|
73
|
-
</div>
|
|
74
|
-
{right}
|
|
75
|
-
</div>
|
|
76
|
-
<div className="p-4">{children}</div>
|
|
77
|
-
</div>
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function FieldRow({ label, hint, children }: { label: string; hint?: React.ReactNode; children: React.ReactNode }) {
|
|
82
|
-
return (
|
|
83
|
-
<div className="grid grid-cols-12 items-start gap-4 py-3">
|
|
84
|
-
<div className="col-span-12 md:col-span-4">
|
|
85
|
-
<div className="text-sm font-medium text-ui-fg-base">{label}</div>
|
|
86
|
-
{hint ? <div className="mt-1 text-xs text-ui-fg-subtle">{hint}</div> : null}
|
|
87
|
-
</div>
|
|
88
|
-
<div className="col-span-12 md:col-span-8">{children}</div>
|
|
89
|
-
</div>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
37
|
export default function AdditionalSettingsTab() {
|
|
94
38
|
const [form, setForm] = useState<AdditionalSettingsForm>(() => ({ ...DEFAULT_FORM }))
|
|
95
39
|
const [loading, setLoading] = useState(false)
|
|
96
40
|
const [saving, setSaving] = useState(false)
|
|
97
|
-
const [toast, setToast] = useState<
|
|
41
|
+
const [toast, setToast] = useState<ToastState>(null)
|
|
98
42
|
const didInit = useRef(false)
|
|
99
43
|
|
|
44
|
+
const dismissToast = useCallback(() => setToast(null), [])
|
|
45
|
+
|
|
100
46
|
useEffect(() => {
|
|
101
47
|
if (didInit.current) return
|
|
102
48
|
didInit.current = true
|
|
@@ -122,11 +68,9 @@ export default function AdditionalSettingsTab() {
|
|
|
122
68
|
const payload = json?.data ?? json
|
|
123
69
|
const saved = payload?.additional_settings
|
|
124
70
|
if (saved && typeof saved === "object") setForm(mergeWithDefaults(saved))
|
|
125
|
-
setToast({
|
|
126
|
-
window.setTimeout(() => setToast(null), 2500)
|
|
71
|
+
setToast({ kind: "success", message: "Settings saved" })
|
|
127
72
|
} catch (e: unknown) {
|
|
128
|
-
setToast({
|
|
129
|
-
window.setTimeout(() => setToast(null), 3500)
|
|
73
|
+
setToast({ kind: "error", message: e instanceof Error ? e.message : "Failed to save settings" })
|
|
130
74
|
} finally {
|
|
131
75
|
setSaving(false)
|
|
132
76
|
}
|
|
@@ -139,11 +83,7 @@ export default function AdditionalSettingsTab() {
|
|
|
139
83
|
<div><h1 className="text-xl font-semibold text-ui-fg-base">PayPal Gateway By Easy Payment</h1></div>
|
|
140
84
|
</div>
|
|
141
85
|
<PayPalTabs />
|
|
142
|
-
{toast
|
|
143
|
-
<div 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" role="status" aria-live="polite">
|
|
144
|
-
<span className={toast.type === "success" ? "text-ui-fg-base" : "text-ui-fg-error"}>{toast.message}</span>
|
|
145
|
-
</div>
|
|
146
|
-
) : null}
|
|
86
|
+
<Toast toast={toast} onClose={dismissToast} />
|
|
147
87
|
<SectionCard
|
|
148
88
|
title="Additional Settings"
|
|
149
89
|
description="These settings control checkout behavior and PayPal experience."
|
|
@@ -152,22 +92,22 @@ export default function AdditionalSettingsTab() {
|
|
|
152
92
|
<button type="button" onClick={onSave} disabled={saving || loading} className="transition-fg relative inline-flex w-fit items-center justify-center overflow-hidden rounded-md outline-none shadow-buttons-neutral text-ui-fg-base bg-ui-button-neutral after:transition-fg after:absolute after:inset-0 after:content-[''] after:button-neutral-gradient hover:bg-ui-button-neutral-hover hover:after:button-neutral-hover-gradient active:bg-ui-button-neutral-pressed active:after:button-neutral-pressed-gradient focus-visible:shadow-buttons-neutral-focus disabled:bg-ui-bg-disabled disabled:border-ui-border-base disabled:text-ui-fg-disabled disabled:shadow-buttons-neutral disabled:after:hidden txt-compact-small-plus px-3 py-1.5">
|
|
153
93
|
{saving ? "Saving..." : "Save settings"}
|
|
154
94
|
</button>
|
|
155
|
-
{loading ? <span className="text-sm text-ui-fg-subtle">Loading
|
|
95
|
+
{loading ? <span className="text-sm text-ui-fg-subtle">Loading…</span> : null}
|
|
156
96
|
</div>
|
|
157
97
|
)}
|
|
158
98
|
>
|
|
159
99
|
<div className="divide-y divide-ui-border-base">
|
|
160
|
-
<FieldRow label="Payment action">
|
|
161
|
-
<select value={form.paymentAction} onChange={(e) => setForm((p) => ({ ...p, paymentAction: e.target.value as PaymentAction }))} 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">
|
|
100
|
+
<FieldRow label="Payment action" htmlFor="as-payment-action">
|
|
101
|
+
<select id="as-payment-action" value={form.paymentAction} onChange={(e) => setForm((p) => ({ ...p, paymentAction: e.target.value as PaymentAction }))} 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">
|
|
162
102
|
<option value="capture">Capture</option>
|
|
163
103
|
<option value="authorize">Authorize</option>
|
|
164
104
|
</select>
|
|
165
105
|
</FieldRow>
|
|
166
|
-
<FieldRow label="Brand Name">
|
|
167
|
-
<input value={form.brandName} onChange={(e) => setForm((p) => ({ ...p, brandName: e.target.value }))} 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" placeholder="PayPal" />
|
|
106
|
+
<FieldRow label="Brand Name" htmlFor="as-brand-name">
|
|
107
|
+
<input id="as-brand-name" value={form.brandName} onChange={(e) => setForm((p) => ({ ...p, brandName: e.target.value }))} 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" placeholder="PayPal" />
|
|
168
108
|
</FieldRow>
|
|
169
|
-
<FieldRow label="Landing Page">
|
|
170
|
-
<select value={form.landingPage} onChange={(e) => setForm((p) => ({ ...p, landingPage: e.target.value as LandingPage }))} 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">
|
|
109
|
+
<FieldRow label="Landing Page" htmlFor="as-landing-page">
|
|
110
|
+
<select id="as-landing-page" value={form.landingPage} onChange={(e) => setForm((p) => ({ ...p, landingPage: e.target.value as LandingPage }))} 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">
|
|
171
111
|
<option value="no_preference">No Preference</option>
|
|
172
112
|
<option value="login">Login</option>
|
|
173
113
|
<option value="billing">Billing</option>
|
|
@@ -185,11 +125,11 @@ export default function AdditionalSettingsTab() {
|
|
|
185
125
|
<span className="text-sm text-ui-fg-base">Send line item details to PayPal</span>
|
|
186
126
|
</label>
|
|
187
127
|
</FieldRow>
|
|
188
|
-
<FieldRow label="Invoice prefix">
|
|
189
|
-
<input value={form.invoicePrefix} onChange={(e) => setForm((p) => ({ ...p, invoicePrefix: e.target.value }))} 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" placeholder="
|
|
128
|
+
<FieldRow label="Invoice prefix" htmlFor="as-invoice-prefix">
|
|
129
|
+
<input id="as-invoice-prefix" value={form.invoicePrefix} onChange={(e) => setForm((p) => ({ ...p, invoicePrefix: e.target.value }))} 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" placeholder="PP-" />
|
|
190
130
|
</FieldRow>
|
|
191
|
-
<FieldRow label="Credit Card Statement Name">
|
|
192
|
-
<input value={form.creditCardStatementName} onChange={(e) => setForm((p) => ({ ...p, creditCardStatementName: e.target.value }))} 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" placeholder="PayPal" />
|
|
131
|
+
<FieldRow label="Credit Card Statement Name" htmlFor="as-cc-statement">
|
|
132
|
+
<input id="as-cc-statement" value={form.creditCardStatementName} onChange={(e) => setForm((p) => ({ ...p, creditCardStatementName: e.target.value }))} 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" placeholder="PayPal" />
|
|
193
133
|
</FieldRow>
|
|
194
134
|
</div>
|
|
195
135
|
</SectionCard>
|
|
@@ -1,38 +1,9 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from "react"
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from "react"
|
|
2
2
|
import PayPalTabs from "../_components/Tabs"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
query?: Record<string, string>
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async function adminFetch<T = unknown>(path: string, opts: AdminFetchOptions = {}): Promise<T> {
|
|
11
|
-
const { method = "GET", body, query } = opts
|
|
12
|
-
let url = path
|
|
13
|
-
if (query && Object.keys(query).length > 0) {
|
|
14
|
-
const params = new URLSearchParams(query)
|
|
15
|
-
url = `${path}?${params.toString()}`
|
|
16
|
-
}
|
|
17
|
-
const headers: Record<string, string> = { Accept: "application/json" }
|
|
18
|
-
if (body !== undefined) headers["Content-Type"] = "application/json"
|
|
19
|
-
if (typeof window !== "undefined") {
|
|
20
|
-
const token = (window as any).__medusa__?.token
|
|
21
|
-
if (token) headers["Authorization"] = `Bearer ${token}`
|
|
22
|
-
}
|
|
23
|
-
const res = await fetch(url, {
|
|
24
|
-
method, headers, credentials: "include",
|
|
25
|
-
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
26
|
-
})
|
|
27
|
-
const text = await res.text().catch(() => "")
|
|
28
|
-
if (!res.ok) {
|
|
29
|
-
if (res.status === 401) throw new Error("Unauthorized (401) - session may have expired. Please reload and log in again.")
|
|
30
|
-
if (res.status === 403) throw new Error("Forbidden (403) - you do not have permission to perform this action.")
|
|
31
|
-
throw new Error(text || `Request failed with status ${res.status}`)
|
|
32
|
-
}
|
|
33
|
-
if (!text) return {} as T
|
|
34
|
-
try { return JSON.parse(text) as T } catch { return {} as T }
|
|
35
|
-
}
|
|
3
|
+
import SectionCard from "../_components/SectionCard"
|
|
4
|
+
import FieldRow from "../_components/FieldRow"
|
|
5
|
+
import Toast, { ToastState } from "../_components/Toast"
|
|
6
|
+
import { adminFetch } from "../_utils/adminFetch"
|
|
36
7
|
|
|
37
8
|
type ThreeDSContingency = "sli" | "when_required" | "always"
|
|
38
9
|
|
|
@@ -60,40 +31,15 @@ const THREE_DS_OPTIONS: { value: ThreeDSContingency; label: string; hint?: strin
|
|
|
60
31
|
{ value: "always", label: "Always request 3D Secure", hint: "Forces 3DS challenge whenever possible (may reduce conversion)." },
|
|
61
32
|
]
|
|
62
33
|
|
|
63
|
-
function SectionCard({ title, description, right, children }: { title: string; description?: string; right?: React.ReactNode; children: React.ReactNode }) {
|
|
64
|
-
return (
|
|
65
|
-
<div className="rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm">
|
|
66
|
-
<div className="flex items-start justify-between gap-4 border-b border-ui-border-base p-4">
|
|
67
|
-
<div>
|
|
68
|
-
<div className="text-base font-semibold text-ui-fg-base">{title}</div>
|
|
69
|
-
{description ? <div className="mt-1 text-sm text-ui-fg-subtle">{description}</div> : null}
|
|
70
|
-
</div>
|
|
71
|
-
{right}
|
|
72
|
-
</div>
|
|
73
|
-
<div className="p-4">{children}</div>
|
|
74
|
-
</div>
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function FieldRow({ label, hint, children }: { label: string; hint?: React.ReactNode; children: React.ReactNode }) {
|
|
79
|
-
return (
|
|
80
|
-
<div className="grid grid-cols-12 items-start gap-4 py-3">
|
|
81
|
-
<div className="col-span-12 md:col-span-4">
|
|
82
|
-
<div className="text-sm font-medium text-ui-fg-base">{label}</div>
|
|
83
|
-
{hint ? <div className="mt-1 text-xs text-ui-fg-subtle">{hint}</div> : null}
|
|
84
|
-
</div>
|
|
85
|
-
<div className="col-span-12 md:col-span-8">{children}</div>
|
|
86
|
-
</div>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
34
|
export default function AdvancedCardPaymentsTab() {
|
|
91
35
|
const [form, setForm] = useState<AdvancedCardPaymentsForm>(() => ({ ...DEFAULT_FORM }))
|
|
92
36
|
const [loading, setLoading] = useState(false)
|
|
93
37
|
const [saving, setSaving] = useState(false)
|
|
94
|
-
const [toast, setToast] = useState<
|
|
38
|
+
const [toast, setToast] = useState<ToastState>(null)
|
|
95
39
|
const didInit = useRef(false)
|
|
96
40
|
|
|
41
|
+
const dismissToast = useCallback(() => setToast(null), [])
|
|
42
|
+
|
|
97
43
|
useEffect(() => {
|
|
98
44
|
if (didInit.current) return
|
|
99
45
|
didInit.current = true
|
|
@@ -121,11 +67,9 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
121
67
|
const payload = json?.data ?? json
|
|
122
68
|
const saved = payload?.advanced_card_payments
|
|
123
69
|
if (saved && typeof saved === "object") setForm(mergeWithDefaults(saved))
|
|
124
|
-
setToast({
|
|
125
|
-
window.setTimeout(() => setToast(null), 2500)
|
|
70
|
+
setToast({ kind: "success", message: "Settings saved" })
|
|
126
71
|
} catch (e: unknown) {
|
|
127
|
-
setToast({
|
|
128
|
-
window.setTimeout(() => setToast(null), 3500)
|
|
72
|
+
setToast({ kind: "error", message: (e instanceof Error ? e.message : "") || "Failed to save settings." })
|
|
129
73
|
} finally {
|
|
130
74
|
setSaving(false)
|
|
131
75
|
}
|
|
@@ -138,11 +82,7 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
138
82
|
<div><h1 className="text-xl font-semibold text-ui-fg-base">PayPal Gateway By Easy Payment</h1></div>
|
|
139
83
|
</div>
|
|
140
84
|
<PayPalTabs />
|
|
141
|
-
{toast
|
|
142
|
-
<div 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" role="status" aria-live="polite">
|
|
143
|
-
<span className={toast.type === "success" ? "text-ui-fg-base" : "text-ui-fg-error"}>{toast.message}</span>
|
|
144
|
-
</div>
|
|
145
|
-
) : null}
|
|
85
|
+
<Toast toast={toast} onClose={dismissToast} />
|
|
146
86
|
<SectionCard
|
|
147
87
|
title="Advanced Card Payments"
|
|
148
88
|
description="Control card checkout settings and 3D Secure behavior."
|
|
@@ -151,7 +91,7 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
151
91
|
<button type="button" onClick={onSave} disabled={saving || loading} className="transition-fg relative inline-flex w-fit items-center justify-center overflow-hidden rounded-md outline-none shadow-buttons-neutral text-ui-fg-base bg-ui-button-neutral after:transition-fg after:absolute after:inset-0 after:content-[''] after:button-neutral-gradient hover:bg-ui-button-neutral-hover hover:after:button-neutral-hover-gradient active:bg-ui-button-neutral-pressed active:after:button-neutral-pressed-gradient focus-visible:shadow-buttons-neutral-focus disabled:bg-ui-bg-disabled disabled:border-ui-border-base disabled:text-ui-fg-disabled disabled:shadow-buttons-neutral disabled:after:hidden txt-compact-small-plus px-3 py-1.5">
|
|
152
92
|
{saving ? "Saving..." : "Save settings"}
|
|
153
93
|
</button>
|
|
154
|
-
{loading ? <span className="text-sm text-ui-fg-subtle">Loading
|
|
94
|
+
{loading ? <span className="text-sm text-ui-fg-subtle">Loading…</span> : null}
|
|
155
95
|
</div>
|
|
156
96
|
)}
|
|
157
97
|
>
|
|
@@ -162,12 +102,12 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
162
102
|
<span className="text-sm text-ui-fg-base">Enable Advanced Credit/Debit Card</span>
|
|
163
103
|
</label>
|
|
164
104
|
</FieldRow>
|
|
165
|
-
<FieldRow label="Title">
|
|
166
|
-
<input value={form.title} onChange={(e) => setForm((p) => ({ ...p, title: e.target.value }))} 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" placeholder="Credit or Debit Card" />
|
|
105
|
+
<FieldRow label="Title" htmlFor="acp-title">
|
|
106
|
+
<input id="acp-title" value={form.title} onChange={(e) => setForm((p) => ({ ...p, title: e.target.value }))} 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" placeholder="Credit or Debit Card" />
|
|
167
107
|
</FieldRow>
|
|
168
|
-
<FieldRow label="Contingency for 3D Secure" hint="Choose when 3D Secure should be triggered during card payments.">
|
|
108
|
+
<FieldRow label="Contingency for 3D Secure" hint="Choose when 3D Secure should be triggered during card payments." htmlFor="acp-threeds">
|
|
169
109
|
<div className="flex flex-col gap-2">
|
|
170
|
-
<select value={form.threeDS} onChange={(e) => setForm((p) => ({ ...p, threeDS: e.target.value as ThreeDSContingency }))} 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">
|
|
110
|
+
<select id="acp-threeds" value={form.threeDS} onChange={(e) => setForm((p) => ({ ...p, threeDS: e.target.value as ThreeDSContingency }))} 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">
|
|
171
111
|
{THREE_DS_OPTIONS.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
|
|
172
112
|
</select>
|
|
173
113
|
{THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)?.hint
|
|
@@ -180,4 +120,4 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
180
120
|
</div>
|
|
181
121
|
</div>
|
|
182
122
|
)
|
|
183
|
-
}
|
|
123
|
+
}
|
|
@@ -11,36 +11,11 @@ import PayPalTabs from "../_components/Tabs"
|
|
|
11
11
|
|
|
12
12
|
export const config = defineRouteConfig({
|
|
13
13
|
label: "PayPal Connection",
|
|
14
|
-
hide: true,
|
|
15
14
|
})
|
|
16
15
|
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"
|
|
21
|
-
|
|
22
|
-
const existingPreload = document.head.querySelector(
|
|
23
|
-
`link[rel="preload"][href="${preloadHref}"]`
|
|
24
|
-
)
|
|
25
|
-
if (!existingPreload) {
|
|
26
|
-
const preloadLink = document.createElement("link")
|
|
27
|
-
preloadLink.rel = "preload"
|
|
28
|
-
preloadLink.href = preloadHref
|
|
29
|
-
preloadLink.as = "script"
|
|
30
|
-
document.head.appendChild(preloadLink)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const existingScript = document.getElementById(
|
|
34
|
-
"paypal-partner-js"
|
|
35
|
-
) as HTMLScriptElement | null
|
|
36
|
-
if (!existingScript) {
|
|
37
|
-
const ppScript = document.createElement("script")
|
|
38
|
-
ppScript.id = "paypal-partner-js"
|
|
39
|
-
ppScript.src = preloadHref
|
|
40
|
-
ppScript.async = true
|
|
41
|
-
document.head.appendChild(ppScript)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
17
|
+
const PARTNER_JS_URL =
|
|
18
|
+
"https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"
|
|
44
19
|
|
|
45
20
|
declare global {
|
|
46
21
|
interface Window {
|
|
@@ -59,7 +34,6 @@ declare global {
|
|
|
59
34
|
|
|
60
35
|
const SERVICE_URL = "/admin/paypal/onboarding-link"
|
|
61
36
|
const CACHE_KEY = "pp_onboard_cache"
|
|
62
|
-
const RELOAD_KEY = "pp_onboard_reloaded_once"
|
|
63
37
|
const CACHE_EXPIRY = 10 * 60 * 1000
|
|
64
38
|
|
|
65
39
|
const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete"
|
|
@@ -87,7 +61,7 @@ export default function PayPalConnectionPage() {
|
|
|
87
61
|
const [env, setEnv] = useState<"sandbox" | "live">("live")
|
|
88
62
|
|
|
89
63
|
useEffect(() => {
|
|
90
|
-
fetch("/admin/paypal/environment", { method: "GET" })
|
|
64
|
+
fetch("/admin/paypal/environment", { method: "GET", credentials: "include" })
|
|
91
65
|
.then((r) => r.json())
|
|
92
66
|
.then((d) => {
|
|
93
67
|
const v = d?.environment === "sandbox" ? "sandbox" : "live"
|
|
@@ -95,6 +69,44 @@ export default function PayPalConnectionPage() {
|
|
|
95
69
|
})
|
|
96
70
|
.catch(() => {})
|
|
97
71
|
}, [])
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
const preloadHref = PARTNER_JS_URL
|
|
75
|
+
|
|
76
|
+
let preloadLink: HTMLLinkElement | null = null
|
|
77
|
+
const existingPreload = document.head.querySelector(
|
|
78
|
+
`link[rel="preload"][href="${preloadHref}"]`
|
|
79
|
+
)
|
|
80
|
+
if (!existingPreload) {
|
|
81
|
+
preloadLink = document.createElement("link")
|
|
82
|
+
preloadLink.rel = "preload"
|
|
83
|
+
preloadLink.href = preloadHref
|
|
84
|
+
preloadLink.as = "script"
|
|
85
|
+
document.head.appendChild(preloadLink)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let ppScript: HTMLScriptElement | null = null
|
|
89
|
+
const existingScript = document.getElementById(
|
|
90
|
+
"paypal-partner-js"
|
|
91
|
+
) as HTMLScriptElement | null
|
|
92
|
+
if (!existingScript) {
|
|
93
|
+
ppScript = document.createElement("script")
|
|
94
|
+
ppScript.id = "paypal-partner-js"
|
|
95
|
+
ppScript.src = preloadHref
|
|
96
|
+
ppScript.async = true
|
|
97
|
+
document.head.appendChild(ppScript)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
if (preloadLink && preloadLink.parentNode) {
|
|
102
|
+
preloadLink.parentNode.removeChild(preloadLink)
|
|
103
|
+
}
|
|
104
|
+
if (ppScript && ppScript.parentNode) {
|
|
105
|
+
ppScript.parentNode.removeChild(ppScript)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}, [])
|
|
109
|
+
|
|
98
110
|
const [connState, setConnState] = useState<
|
|
99
111
|
"loading" | "ready" | "connected" | "error"
|
|
100
112
|
>("loading")
|
|
@@ -135,11 +147,15 @@ export default function PayPalConnectionPage() {
|
|
|
135
147
|
fetch(SERVICE_URL, {
|
|
136
148
|
method: "POST",
|
|
137
149
|
headers: { "content-type": "application/json" },
|
|
150
|
+
credentials: "include",
|
|
138
151
|
body: JSON.stringify({
|
|
139
152
|
products: ["PPCP"],
|
|
140
153
|
}),
|
|
141
154
|
})
|
|
142
|
-
.then((r) =>
|
|
155
|
+
.then((r) => {
|
|
156
|
+
if (!r.ok) throw new Error(`Service returned ${r.status}`)
|
|
157
|
+
return r.json()
|
|
158
|
+
})
|
|
143
159
|
.then((data) => {
|
|
144
160
|
if (runId !== currentRunId.current) return
|
|
145
161
|
|
|
@@ -160,12 +176,6 @@ export default function PayPalConnectionPage() {
|
|
|
160
176
|
})
|
|
161
177
|
)
|
|
162
178
|
|
|
163
|
-
if (!localStorage.getItem(RELOAD_KEY)) {
|
|
164
|
-
localStorage.setItem(RELOAD_KEY, "1")
|
|
165
|
-
window.location.reload()
|
|
166
|
-
return
|
|
167
|
-
}
|
|
168
|
-
|
|
169
179
|
activatePayPal(finalUrl, runId)
|
|
170
180
|
})
|
|
171
181
|
.catch(() => {
|
|
@@ -196,12 +206,20 @@ export default function PayPalConnectionPage() {
|
|
|
196
206
|
}
|
|
197
207
|
setFinalUrl(url)
|
|
198
208
|
|
|
209
|
+
let attempts = 0
|
|
210
|
+
const MAX_ATTEMPTS = 200
|
|
211
|
+
|
|
199
212
|
const tryInit = () => {
|
|
200
213
|
if (runId !== currentRunId.current) return
|
|
201
214
|
if (window.PAYPAL?.apps?.Signup) {
|
|
202
215
|
showUI()
|
|
203
216
|
return
|
|
204
217
|
}
|
|
218
|
+
attempts++
|
|
219
|
+
if (attempts >= MAX_ATTEMPTS) {
|
|
220
|
+
showError("PayPal partner script failed to load. Please refresh and try again.")
|
|
221
|
+
return
|
|
222
|
+
}
|
|
205
223
|
setTimeout(tryInit, 50)
|
|
206
224
|
}
|
|
207
225
|
|
|
@@ -224,6 +242,7 @@ export default function PayPalConnectionPage() {
|
|
|
224
242
|
try {
|
|
225
243
|
const r = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
|
|
226
244
|
method: "GET",
|
|
245
|
+
credentials: "include",
|
|
227
246
|
})
|
|
228
247
|
const st = await r.json().catch(() => ({}))
|
|
229
248
|
|
|
@@ -261,7 +280,7 @@ export default function PayPalConnectionPage() {
|
|
|
261
280
|
useLayoutEffect(() => {
|
|
262
281
|
window.onboardingCallback = async function (authCode: string, sharedId: string) {
|
|
263
282
|
try {
|
|
264
|
-
|
|
283
|
+
window.onbeforeunload = null
|
|
265
284
|
} catch {}
|
|
266
285
|
|
|
267
286
|
setOnboardingInProgress(true)
|
|
@@ -278,6 +297,7 @@ export default function PayPalConnectionPage() {
|
|
|
278
297
|
const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
|
|
279
298
|
method: "POST",
|
|
280
299
|
headers: { "content-type": "application/json" },
|
|
300
|
+
credentials: "include",
|
|
281
301
|
body: JSON.stringify(payload),
|
|
282
302
|
})
|
|
283
303
|
|
|
@@ -299,10 +319,22 @@ export default function PayPalConnectionPage() {
|
|
|
299
319
|
|
|
300
320
|
try {
|
|
301
321
|
localStorage.removeItem(CACHE_KEY)
|
|
302
|
-
localStorage.removeItem(RELOAD_KEY)
|
|
303
322
|
} catch {}
|
|
304
323
|
|
|
305
|
-
|
|
324
|
+
try {
|
|
325
|
+
const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
|
|
326
|
+
method: "GET",
|
|
327
|
+
credentials: "include",
|
|
328
|
+
})
|
|
329
|
+
const refreshedStatus = await statusRes.json().catch(() => ({}))
|
|
330
|
+
setStatusInfo(refreshedStatus || null)
|
|
331
|
+
setConnState("connected")
|
|
332
|
+
setShowManual(false)
|
|
333
|
+
} catch {
|
|
334
|
+
setConnState("connected")
|
|
335
|
+
setShowManual(false)
|
|
336
|
+
}
|
|
337
|
+
setOnboardingInProgress(false)
|
|
306
338
|
} catch (e: any) {
|
|
307
339
|
console.error(e)
|
|
308
340
|
setConnState("error")
|
|
@@ -357,6 +389,7 @@ export default function PayPalConnectionPage() {
|
|
|
357
389
|
const res = await fetch(SAVE_CREDENTIALS_ENDPOINT, {
|
|
358
390
|
method: "POST",
|
|
359
391
|
headers: { "content-type": "application/json" },
|
|
392
|
+
credentials: "include",
|
|
360
393
|
body: JSON.stringify({
|
|
361
394
|
clientId: clientId.trim(),
|
|
362
395
|
clientSecret: secret.trim(),
|
|
@@ -371,6 +404,7 @@ export default function PayPalConnectionPage() {
|
|
|
371
404
|
|
|
372
405
|
const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
|
|
373
406
|
method: "GET",
|
|
407
|
+
credentials: "include",
|
|
374
408
|
})
|
|
375
409
|
const refreshedStatus = await statusRes.json().catch(() => ({}))
|
|
376
410
|
|
|
@@ -380,7 +414,6 @@ export default function PayPalConnectionPage() {
|
|
|
380
414
|
|
|
381
415
|
try {
|
|
382
416
|
localStorage.removeItem(CACHE_KEY)
|
|
383
|
-
localStorage.removeItem(RELOAD_KEY)
|
|
384
417
|
} catch {}
|
|
385
418
|
} catch (e: any) {
|
|
386
419
|
console.error(e)
|
|
@@ -400,11 +433,13 @@ export default function PayPalConnectionPage() {
|
|
|
400
433
|
setError(null)
|
|
401
434
|
setFinalUrl("")
|
|
402
435
|
setShowManual(false)
|
|
436
|
+
setStatusInfo(null)
|
|
403
437
|
|
|
404
438
|
try {
|
|
405
439
|
const res = await fetch(DISCONNECT_ENDPOINT, {
|
|
406
440
|
method: "POST",
|
|
407
441
|
headers: { "content-type": "application/json" },
|
|
442
|
+
credentials: "include",
|
|
408
443
|
body: JSON.stringify({ environment: env }),
|
|
409
444
|
})
|
|
410
445
|
|
|
@@ -415,7 +450,7 @@ export default function PayPalConnectionPage() {
|
|
|
415
450
|
|
|
416
451
|
try {
|
|
417
452
|
localStorage.removeItem(CACHE_KEY)
|
|
418
|
-
|
|
453
|
+
|
|
419
454
|
} catch {}
|
|
420
455
|
|
|
421
456
|
currentRunId.current = ++runIdRef.current
|
|
@@ -439,20 +474,24 @@ export default function PayPalConnectionPage() {
|
|
|
439
474
|
await fetch("/admin/paypal/environment", {
|
|
440
475
|
method: "POST",
|
|
441
476
|
headers: { "content-type": "application/json" },
|
|
477
|
+
credentials: "include",
|
|
442
478
|
body: JSON.stringify({ environment: next }),
|
|
443
479
|
})
|
|
444
480
|
} catch {}
|
|
445
481
|
|
|
446
482
|
try {
|
|
447
483
|
localStorage.removeItem(CACHE_KEY)
|
|
448
|
-
localStorage.removeItem(RELOAD_KEY)
|
|
449
484
|
} catch {}
|
|
450
485
|
}
|
|
451
486
|
|
|
452
487
|
return (
|
|
453
488
|
<div className="p-6">
|
|
454
489
|
<div className="flex flex-col gap-6">
|
|
455
|
-
<
|
|
490
|
+
<div className="flex items-start justify-between gap-4">
|
|
491
|
+
<div>
|
|
492
|
+
<h1 className="text-xl font-semibold text-ui-fg-base">PayPal Gateway By Easy Payment</h1>
|
|
493
|
+
</div>
|
|
494
|
+
</div>
|
|
456
495
|
|
|
457
496
|
<PayPalTabs />
|
|
458
497
|
|
|
@@ -481,6 +520,7 @@ export default function PayPalConnectionPage() {
|
|
|
481
520
|
<div className="text-sm text-green-600 bg-green-50 p-3 rounded border border-green-200">
|
|
482
521
|
✅ Successfully connected to PayPal!
|
|
483
522
|
<a
|
|
523
|
+
target="PPFrame"
|
|
484
524
|
data-paypal-button="true"
|
|
485
525
|
data-paypal-onboard-complete="onboardingCallback"
|
|
486
526
|
href="#"
|
|
@@ -535,6 +575,7 @@ export default function PayPalConnectionPage() {
|
|
|
535
575
|
ppBtnMeasureRef.current = node
|
|
536
576
|
}}
|
|
537
577
|
id="paypal-button"
|
|
578
|
+
target="PPFrame"
|
|
538
579
|
data-paypal-button="true"
|
|
539
580
|
href={finalUrl || "#"}
|
|
540
581
|
data-paypal-onboard-complete="onboardingCallback"
|
|
@@ -604,8 +645,9 @@ export default function PayPalConnectionPage() {
|
|
|
604
645
|
<div className="md:col-span-2">
|
|
605
646
|
<div className="ml-[260px] max-w-xl mt-4 grid grid-cols-1 gap-3 md:grid-cols-2">
|
|
606
647
|
<div className="flex flex-col gap-1">
|
|
607
|
-
<label className="text-sm font-medium">Client ID</label>
|
|
648
|
+
<label htmlFor="pp-manual-client-id" className="text-sm font-medium">Client ID</label>
|
|
608
649
|
<input
|
|
650
|
+
id="pp-manual-client-id"
|
|
609
651
|
type="text"
|
|
610
652
|
value={clientId}
|
|
611
653
|
onChange={(e) => setClientId(e.target.value)}
|
|
@@ -618,8 +660,9 @@ export default function PayPalConnectionPage() {
|
|
|
618
660
|
</div>
|
|
619
661
|
|
|
620
662
|
<div className="flex flex-col gap-1">
|
|
621
|
-
<label className="text-sm font-medium">Secret</label>
|
|
663
|
+
<label htmlFor="pp-manual-secret" className="text-sm font-medium">Secret</label>
|
|
622
664
|
<input
|
|
665
|
+
id="pp-manual-secret"
|
|
623
666
|
type="password"
|
|
624
667
|
value={secret}
|
|
625
668
|
onChange={(e) => setSecret(e.target.value)}
|