@easypayment/medusa-payment-paypal 0.9.21 → 0.9.22
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.js +200 -326
- package/.medusa/server/src/admin/index.mjs +200 -326
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.d.ts.map +1 -1
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js +208 -379
- package/.medusa/server/src/admin/routes/settings/paypal/connection/page.js.map +1 -1
- package/package.json +1 -1
- package/src/admin/routes/settings/paypal/connection/page.tsx +234 -423
|
@@ -134,22 +134,20 @@ async function adminFetch(path, opts = {}) {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
const DEFAULT_FORM$1 = {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
paymentAction: "capture",
|
|
138
|
+
brandName: "PayPal",
|
|
139
|
+
landingPage: "no_preference",
|
|
140
|
+
requireInstantPayment: false,
|
|
141
|
+
sendItemDetails: true,
|
|
142
|
+
invoicePrefix: "PP-",
|
|
143
|
+
creditCardStatementName: "PayPal"
|
|
140
144
|
};
|
|
141
145
|
function mergeWithDefaults$1(saved) {
|
|
142
146
|
if (!saved) return { ...DEFAULT_FORM$1 };
|
|
143
147
|
const entries = Object.entries(saved).filter(([, value]) => value !== void 0);
|
|
144
148
|
return { ...DEFAULT_FORM$1, ...Object.fromEntries(entries) };
|
|
145
149
|
}
|
|
146
|
-
|
|
147
|
-
{ value: "when_required", label: "3D Secure when required", hint: "Triggers 3DS only when the card / issuer requires it." },
|
|
148
|
-
{ value: "sli", label: "3D Secure (SCA) / liability shift (recommended)", hint: "Attempts to optimize for liability shift while remaining compliant." },
|
|
149
|
-
{ value: "always", label: "Always request 3D Secure", hint: "Forces 3DS challenge whenever possible (may reduce conversion)." }
|
|
150
|
-
];
|
|
151
|
-
function AdvancedCardPaymentsTab() {
|
|
152
|
-
var _a, _b;
|
|
150
|
+
function AdditionalSettingsTab() {
|
|
153
151
|
const [form, setForm] = react.useState(() => ({ ...DEFAULT_FORM$1 }));
|
|
154
152
|
const [loading, setLoading] = react.useState(false);
|
|
155
153
|
const [saving, setSaving] = react.useState(false);
|
|
@@ -164,7 +162,7 @@ function AdvancedCardPaymentsTab() {
|
|
|
164
162
|
setLoading(true);
|
|
165
163
|
const json = await adminFetch("/admin/paypal/settings");
|
|
166
164
|
const payload = (json == null ? void 0 : json.data) ?? json;
|
|
167
|
-
const saved = payload == null ? void 0 : payload.
|
|
165
|
+
const saved = payload == null ? void 0 : payload.additional_settings;
|
|
168
166
|
if (saved && typeof saved === "object") setForm(mergeWithDefaults$1(saved));
|
|
169
167
|
} catch {
|
|
170
168
|
} finally {
|
|
@@ -175,16 +173,14 @@ function AdvancedCardPaymentsTab() {
|
|
|
175
173
|
async function onSave() {
|
|
176
174
|
try {
|
|
177
175
|
setSaving(true);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
body: { advanced_card_payments: form }
|
|
181
|
-
});
|
|
176
|
+
setToast(null);
|
|
177
|
+
const json = await adminFetch("/admin/paypal/settings", { method: "POST", body: { additional_settings: form } });
|
|
182
178
|
const payload = (json == null ? void 0 : json.data) ?? json;
|
|
183
|
-
const saved = payload == null ? void 0 : payload.
|
|
179
|
+
const saved = payload == null ? void 0 : payload.additional_settings;
|
|
184
180
|
if (saved && typeof saved === "object") setForm(mergeWithDefaults$1(saved));
|
|
185
181
|
setToast({ kind: "success", message: "Settings saved" });
|
|
186
182
|
} catch (e) {
|
|
187
|
-
setToast({ kind: "error", message:
|
|
183
|
+
setToast({ kind: "error", message: e instanceof Error ? e.message : "Failed to save settings" });
|
|
188
184
|
} finally {
|
|
189
185
|
setSaving(false);
|
|
190
186
|
}
|
|
@@ -196,42 +192,55 @@ function AdvancedCardPaymentsTab() {
|
|
|
196
192
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
197
193
|
SectionCard,
|
|
198
194
|
{
|
|
199
|
-
title: "
|
|
200
|
-
description: "
|
|
195
|
+
title: "Additional Settings",
|
|
196
|
+
description: "These settings control checkout behavior and PayPal experience.",
|
|
201
197
|
right: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
202
198
|
/* @__PURE__ */ jsxRuntime.jsx("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", children: saving ? "Saving..." : "Save settings" }),
|
|
203
199
|
loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-subtle", children: "Loading…" }) : null
|
|
204
200
|
] }),
|
|
205
201
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "divide-y divide-ui-border-base", children: [
|
|
206
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "
|
|
207
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
208
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Payment action", htmlFor: "as-payment-action", children: /* @__PURE__ */ jsxRuntime.jsxs("select", { id: "as-payment-action", value: form.paymentAction, onChange: (e) => setForm((p) => ({ ...p, paymentAction: 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", children: [
|
|
203
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "capture", children: "Capture" }),
|
|
204
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "authorize", children: "Authorize" })
|
|
209
205
|
] }) }),
|
|
210
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "
|
|
211
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "
|
|
212
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
213
|
-
|
|
214
|
-
|
|
206
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Brand Name", htmlFor: "as-brand-name", children: /* @__PURE__ */ jsxRuntime.jsx("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" }) }),
|
|
207
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Landing Page", htmlFor: "as-landing-page", children: /* @__PURE__ */ jsxRuntime.jsxs("select", { id: "as-landing-page", value: form.landingPage, onChange: (e) => setForm((p) => ({ ...p, landingPage: 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", children: [
|
|
208
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "no_preference", children: "No Preference" }),
|
|
209
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "login", children: "Login" }),
|
|
210
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "billing", children: "Billing" })
|
|
211
|
+
] }) }),
|
|
212
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Instant Payments", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2", children: [
|
|
213
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: form.requireInstantPayment, onChange: (e) => setForm((p) => ({ ...p, requireInstantPayment: e.target.checked })), className: "h-4 w-4 rounded border-ui-border-base" }),
|
|
214
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Require Instant Payment" })
|
|
215
|
+
] }) }),
|
|
216
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Send Item Details", hint: "Include all line item details in the payment request to PayPal so that they can be seen from the PayPal transaction details page.", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2", children: [
|
|
217
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: form.sendItemDetails, onChange: (e) => setForm((p) => ({ ...p, sendItemDetails: e.target.checked })), className: "h-4 w-4 rounded border-ui-border-base" }),
|
|
218
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Send line item details to PayPal" })
|
|
219
|
+
] }) }),
|
|
220
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Invoice prefix", htmlFor: "as-invoice-prefix", children: /* @__PURE__ */ jsxRuntime.jsx("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-" }) }),
|
|
221
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Credit Card Statement Name", htmlFor: "as-cc-statement", children: /* @__PURE__ */ jsxRuntime.jsx("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" }) })
|
|
215
222
|
] })
|
|
216
223
|
}
|
|
217
224
|
)
|
|
218
225
|
] }) });
|
|
219
226
|
}
|
|
220
227
|
const DEFAULT_FORM = {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
requireInstantPayment: false,
|
|
225
|
-
sendItemDetails: true,
|
|
226
|
-
invoicePrefix: "PP-",
|
|
227
|
-
creditCardStatementName: "PayPal"
|
|
228
|
+
enabled: true,
|
|
229
|
+
title: "Credit or Debit Card",
|
|
230
|
+
threeDS: "when_required"
|
|
228
231
|
};
|
|
229
232
|
function mergeWithDefaults(saved) {
|
|
230
233
|
if (!saved) return { ...DEFAULT_FORM };
|
|
231
234
|
const entries = Object.entries(saved).filter(([, value]) => value !== void 0);
|
|
232
235
|
return { ...DEFAULT_FORM, ...Object.fromEntries(entries) };
|
|
233
236
|
}
|
|
234
|
-
|
|
237
|
+
const THREE_DS_OPTIONS = [
|
|
238
|
+
{ value: "when_required", label: "3D Secure when required", hint: "Triggers 3DS only when the card / issuer requires it." },
|
|
239
|
+
{ value: "sli", label: "3D Secure (SCA) / liability shift (recommended)", hint: "Attempts to optimize for liability shift while remaining compliant." },
|
|
240
|
+
{ value: "always", label: "Always request 3D Secure", hint: "Forces 3DS challenge whenever possible (may reduce conversion)." }
|
|
241
|
+
];
|
|
242
|
+
function AdvancedCardPaymentsTab() {
|
|
243
|
+
var _a, _b;
|
|
235
244
|
const [form, setForm] = react.useState(() => ({ ...DEFAULT_FORM }));
|
|
236
245
|
const [loading, setLoading] = react.useState(false);
|
|
237
246
|
const [saving, setSaving] = react.useState(false);
|
|
@@ -246,7 +255,7 @@ function AdditionalSettingsTab() {
|
|
|
246
255
|
setLoading(true);
|
|
247
256
|
const json = await adminFetch("/admin/paypal/settings");
|
|
248
257
|
const payload = (json == null ? void 0 : json.data) ?? json;
|
|
249
|
-
const saved = payload == null ? void 0 : payload.
|
|
258
|
+
const saved = payload == null ? void 0 : payload.advanced_card_payments;
|
|
250
259
|
if (saved && typeof saved === "object") setForm(mergeWithDefaults(saved));
|
|
251
260
|
} catch {
|
|
252
261
|
} finally {
|
|
@@ -257,14 +266,16 @@ function AdditionalSettingsTab() {
|
|
|
257
266
|
async function onSave() {
|
|
258
267
|
try {
|
|
259
268
|
setSaving(true);
|
|
260
|
-
|
|
261
|
-
|
|
269
|
+
const json = await adminFetch("/admin/paypal/settings", {
|
|
270
|
+
method: "POST",
|
|
271
|
+
body: { advanced_card_payments: form }
|
|
272
|
+
});
|
|
262
273
|
const payload = (json == null ? void 0 : json.data) ?? json;
|
|
263
|
-
const saved = payload == null ? void 0 : payload.
|
|
274
|
+
const saved = payload == null ? void 0 : payload.advanced_card_payments;
|
|
264
275
|
if (saved && typeof saved === "object") setForm(mergeWithDefaults(saved));
|
|
265
276
|
setToast({ kind: "success", message: "Settings saved" });
|
|
266
277
|
} catch (e) {
|
|
267
|
-
setToast({ kind: "error", message: e instanceof Error ? e.message : "Failed to save settings" });
|
|
278
|
+
setToast({ kind: "error", message: (e instanceof Error ? e.message : "") || "Failed to save settings." });
|
|
268
279
|
} finally {
|
|
269
280
|
setSaving(false);
|
|
270
281
|
}
|
|
@@ -276,33 +287,22 @@ function AdditionalSettingsTab() {
|
|
|
276
287
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
277
288
|
SectionCard,
|
|
278
289
|
{
|
|
279
|
-
title: "
|
|
280
|
-
description: "
|
|
290
|
+
title: "Advanced Card Payments",
|
|
291
|
+
description: "Control card checkout settings and 3D Secure behavior.",
|
|
281
292
|
right: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
282
293
|
/* @__PURE__ */ jsxRuntime.jsx("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", children: saving ? "Saving..." : "Save settings" }),
|
|
283
294
|
loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-subtle", children: "Loading…" }) : null
|
|
284
295
|
] }),
|
|
285
296
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "divide-y divide-ui-border-base", children: [
|
|
286
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "
|
|
287
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
288
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
289
|
-
] }) }),
|
|
290
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Brand Name", htmlFor: "as-brand-name", children: /* @__PURE__ */ jsxRuntime.jsx("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" }) }),
|
|
291
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Landing Page", htmlFor: "as-landing-page", children: /* @__PURE__ */ jsxRuntime.jsxs("select", { id: "as-landing-page", value: form.landingPage, onChange: (e) => setForm((p) => ({ ...p, landingPage: 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", children: [
|
|
292
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "no_preference", children: "No Preference" }),
|
|
293
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "login", children: "Login" }),
|
|
294
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "billing", children: "Billing" })
|
|
295
|
-
] }) }),
|
|
296
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Instant Payments", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2", children: [
|
|
297
|
-
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: form.requireInstantPayment, onChange: (e) => setForm((p) => ({ ...p, requireInstantPayment: e.target.checked })), className: "h-4 w-4 rounded border-ui-border-base" }),
|
|
298
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Require Instant Payment" })
|
|
299
|
-
] }) }),
|
|
300
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Send Item Details", hint: "Include all line item details in the payment request to PayPal so that they can be seen from the PayPal transaction details page.", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2", children: [
|
|
301
|
-
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: form.sendItemDetails, onChange: (e) => setForm((p) => ({ ...p, sendItemDetails: e.target.checked })), className: "h-4 w-4 rounded border-ui-border-base" }),
|
|
302
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Send line item details to PayPal" })
|
|
297
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Enable/Disable", children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex items-center gap-2", children: [
|
|
298
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: form.enabled, onChange: (e) => setForm((p) => ({ ...p, enabled: e.target.checked })), className: "h-4 w-4 rounded border-ui-border-base" }),
|
|
299
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Enable Advanced Credit/Debit Card" })
|
|
303
300
|
] }) }),
|
|
304
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "
|
|
305
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "
|
|
301
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Title", htmlFor: "acp-title", children: /* @__PURE__ */ jsxRuntime.jsx("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" }) }),
|
|
302
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldRow, { label: "Contingency for 3D Secure", hint: "Choose when 3D Secure should be triggered during card payments.", htmlFor: "acp-threeds", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
303
|
+
/* @__PURE__ */ jsxRuntime.jsx("select", { id: "acp-threeds", value: form.threeDS, onChange: (e) => setForm((p) => ({ ...p, threeDS: 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", children: THREE_DS_OPTIONS.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value)) }),
|
|
304
|
+
((_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
|
|
305
|
+
] }) })
|
|
306
306
|
] })
|
|
307
307
|
}
|
|
308
308
|
)
|
|
@@ -324,7 +324,7 @@ const PARTNER_JS_URLS = {
|
|
|
324
324
|
const SERVICE_URL = "/admin/paypal/onboarding-link";
|
|
325
325
|
const POPUP_NAME = "PPFrame";
|
|
326
326
|
const CACHE_PREFIX = "pp_onboard_cache";
|
|
327
|
-
const CACHE_EXPIRY =
|
|
327
|
+
const CACHE_EXPIRY = 6 * 60 * 60 * 1e3;
|
|
328
328
|
const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete";
|
|
329
329
|
const STATUS_ENDPOINT = "/admin/paypal/status";
|
|
330
330
|
const SAVE_CREDENTIALS_ENDPOINT = "/admin/paypal/save-credentials";
|
|
@@ -347,7 +347,9 @@ function readCachedUrl(env) {
|
|
|
347
347
|
function writeCachedUrl(env, url) {
|
|
348
348
|
try {
|
|
349
349
|
localStorage.setItem(cacheKeyFor(env), JSON.stringify({ url, ts: Date.now() }));
|
|
350
|
+
return readCachedUrl(env) === url;
|
|
350
351
|
} catch {
|
|
352
|
+
return false;
|
|
351
353
|
}
|
|
352
354
|
}
|
|
353
355
|
function clearCachedUrl(env) {
|
|
@@ -361,58 +363,12 @@ function clearCachedUrl(env) {
|
|
|
361
363
|
} catch {
|
|
362
364
|
}
|
|
363
365
|
}
|
|
364
|
-
function isPayPalOrigin(origin) {
|
|
365
|
-
try {
|
|
366
|
-
const host = new URL(origin).hostname.toLowerCase();
|
|
367
|
-
return host === "www.paypal.com" || host === "www.sandbox.paypal.com" || host.endsWith(".paypal.com") || host.endsWith(".paypalobjects.com");
|
|
368
|
-
} catch {
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
function extractAuth(data) {
|
|
373
|
-
if (!data) return {};
|
|
374
|
-
if (typeof data === "object") {
|
|
375
|
-
const obj = data;
|
|
376
|
-
const authCode = obj.authCode ?? obj.auth_code ?? obj.authcode;
|
|
377
|
-
const sharedId = obj.sharedId ?? obj.shared_id ?? obj.sharedid;
|
|
378
|
-
if (authCode && sharedId) {
|
|
379
|
-
return { authCode: String(authCode), sharedId: String(sharedId) };
|
|
380
|
-
}
|
|
381
|
-
for (const key of ["data", "payload", "message", "detail", "body"]) {
|
|
382
|
-
if (obj[key] && typeof obj[key] === "object") {
|
|
383
|
-
const inner = extractAuth(obj[key]);
|
|
384
|
-
if (inner.authCode && inner.sharedId) return inner;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
return {};
|
|
388
|
-
}
|
|
389
|
-
if (typeof data === "string") {
|
|
390
|
-
const s = data.trim();
|
|
391
|
-
if (!s) return {};
|
|
392
|
-
if (s.startsWith("{")) {
|
|
393
|
-
try {
|
|
394
|
-
return extractAuth(JSON.parse(s));
|
|
395
|
-
} catch {
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
if (/auth_?code/i.test(s) && /shared_?id/i.test(s)) {
|
|
399
|
-
try {
|
|
400
|
-
const sp = new URLSearchParams(s.replace(/^[?#]/, ""));
|
|
401
|
-
const authCode = sp.get("authCode") || sp.get("auth_code") || void 0;
|
|
402
|
-
const sharedId = sp.get("sharedId") || sp.get("shared_id") || void 0;
|
|
403
|
-
if (authCode && sharedId) return { authCode, sharedId };
|
|
404
|
-
} catch {
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
return {};
|
|
409
|
-
}
|
|
410
366
|
function PayPalConnectionPage() {
|
|
411
367
|
const [env, setEnv] = react.useState("live");
|
|
412
368
|
const [envReady, setEnvReady] = react.useState(false);
|
|
413
369
|
const [connState, setConnState] = react.useState("loading");
|
|
370
|
+
const [partnerReady, setPartnerReady] = react.useState(false);
|
|
414
371
|
const [error, setError] = react.useState(null);
|
|
415
|
-
const [popupBlocked, setPopupBlocked] = react.useState(false);
|
|
416
372
|
const [finalUrl, setFinalUrl] = react.useState("");
|
|
417
373
|
const [showManual, setShowManual] = react.useState(false);
|
|
418
374
|
const [clientId, setClientId] = react.useState("");
|
|
@@ -420,20 +376,11 @@ function PayPalConnectionPage() {
|
|
|
420
376
|
const [statusInfo, setStatusInfo] = react.useState(null);
|
|
421
377
|
const [onboardingInProgress, setOnboardingInProgress] = react.useState(false);
|
|
422
378
|
const initLoaderRef = react.useRef(null);
|
|
423
|
-
const paypalButtonRef = react.useRef(null);
|
|
424
379
|
const errorLogRef = react.useRef(null);
|
|
425
380
|
const runIdRef = react.useRef(0);
|
|
426
381
|
const currentRunId = react.useRef(0);
|
|
427
|
-
const finalUrlRef = react.useRef(finalUrl);
|
|
428
|
-
finalUrlRef.current = finalUrl;
|
|
429
|
-
const connStateRef = react.useRef(connState);
|
|
430
|
-
connStateRef.current = connState;
|
|
431
|
-
const inProgressRef = react.useRef(onboardingInProgress);
|
|
432
|
-
inProgressRef.current = onboardingInProgress;
|
|
433
382
|
const envRef = react.useRef(env);
|
|
434
383
|
envRef.current = env;
|
|
435
|
-
const popupRef = react.useRef(null);
|
|
436
|
-
const popupPollRef = react.useRef(null);
|
|
437
384
|
const completedRef = react.useRef(false);
|
|
438
385
|
const ppBtnMeasureRef = react.useRef(null);
|
|
439
386
|
const [ppBtnWidth, setPpBtnWidth] = react.useState(null);
|
|
@@ -444,131 +391,23 @@ function PayPalConnectionPage() {
|
|
|
444
391
|
setConnState("error");
|
|
445
392
|
setError(msg);
|
|
446
393
|
}, []);
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const openOnboardingPopup = react.useCallback(
|
|
454
|
-
(url) => {
|
|
455
|
-
const w = 450;
|
|
456
|
-
const h = 600;
|
|
457
|
-
const baseLeft = typeof window.screenX === "number" ? window.screenX : window.screenLeft || 0;
|
|
458
|
-
const baseTop = typeof window.screenY === "number" ? window.screenY : window.screenTop || 0;
|
|
459
|
-
const outerW = window.outerWidth || document.documentElement.clientWidth || 1024;
|
|
460
|
-
const outerH = window.outerHeight || document.documentElement.clientHeight || 768;
|
|
461
|
-
const left = Math.round(baseLeft + Math.max(0, (outerW - w) / 2));
|
|
462
|
-
const top = Math.round(baseTop + Math.max(0, (outerH - h) / 2));
|
|
463
|
-
const features = `popup=yes,width=${w},height=${h},left=${left},top=${top},scrollbars=yes,resizable=yes`;
|
|
464
|
-
let popup = null;
|
|
465
|
-
try {
|
|
466
|
-
popup = window.open(url, POPUP_NAME, features);
|
|
467
|
-
} catch {
|
|
468
|
-
popup = null;
|
|
469
|
-
}
|
|
470
|
-
if (popup) {
|
|
471
|
-
popupRef.current = popup;
|
|
472
|
-
try {
|
|
473
|
-
popup.focus();
|
|
474
|
-
} catch {
|
|
394
|
+
const generateLinkAndReload = react.useCallback(
|
|
395
|
+
async (targetEnv, runId) => {
|
|
396
|
+
if (initLoaderRef.current) {
|
|
397
|
+
const loaderText = initLoaderRef.current.querySelector("#loader-text");
|
|
398
|
+
if (loaderText) {
|
|
399
|
+
loaderText.textContent = "Generating onboarding session...";
|
|
475
400
|
}
|
|
476
|
-
stopPopupPoll();
|
|
477
|
-
popupPollRef.current = setInterval(() => {
|
|
478
|
-
if (!popupRef.current || popupRef.current.closed) {
|
|
479
|
-
stopPopupPoll();
|
|
480
|
-
}
|
|
481
|
-
}, 1e3);
|
|
482
401
|
}
|
|
483
|
-
return popup;
|
|
484
|
-
},
|
|
485
|
-
[stopPopupPoll]
|
|
486
|
-
);
|
|
487
|
-
const completeOnboarding = react.useCallback(
|
|
488
|
-
async (authCode, sharedId) => {
|
|
489
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
490
|
-
if (!authCode || !sharedId) return;
|
|
491
|
-
if (completedRef.current) return;
|
|
492
|
-
completedRef.current = true;
|
|
493
402
|
try {
|
|
494
|
-
|
|
495
|
-
} catch {
|
|
496
|
-
}
|
|
497
|
-
const activeEnv = envRef.current === "sandbox" ? "sandbox" : "live";
|
|
498
|
-
setOnboardingInProgress(true);
|
|
499
|
-
setConnState("loading");
|
|
500
|
-
setError(null);
|
|
501
|
-
setPopupBlocked(false);
|
|
502
|
-
try {
|
|
503
|
-
const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
|
|
403
|
+
const res = await fetch(SERVICE_URL, {
|
|
504
404
|
method: "POST",
|
|
505
405
|
headers: { "content-type": "application/json" },
|
|
506
406
|
credentials: "include",
|
|
507
|
-
body: JSON.stringify({
|
|
407
|
+
body: JSON.stringify({ products: ["PPCP"], environment: targetEnv })
|
|
508
408
|
});
|
|
509
|
-
if (!res.ok) {
|
|
510
|
-
|
|
511
|
-
throw new Error(txt || `Onboarding exchange failed (${res.status})`);
|
|
512
|
-
}
|
|
513
|
-
try {
|
|
514
|
-
(_a = popupRef.current) == null ? void 0 : _a.close();
|
|
515
|
-
} catch {
|
|
516
|
-
}
|
|
517
|
-
stopPopupPoll();
|
|
518
|
-
try {
|
|
519
|
-
const close1 = (_e = (_d = (_c = (_b = window.PAYPAL) == null ? void 0 : _b.apps) == null ? void 0 : _c.Signup) == null ? void 0 : _d.MiniBrowser) == null ? void 0 : _e.closeFlow;
|
|
520
|
-
if (typeof close1 === "function") close1();
|
|
521
|
-
} catch {
|
|
522
|
-
}
|
|
523
|
-
try {
|
|
524
|
-
const close2 = ((_h = (_g = (_f = window.PAYPAL) == null ? void 0 : _f.apps) == null ? void 0 : _g.Signup) == null ? void 0 : _h.miniBrowser) && window.PAYPAL.apps.Signup.miniBrowser.closeFlow;
|
|
525
|
-
if (typeof close2 === "function") close2();
|
|
526
|
-
} catch {
|
|
527
|
-
}
|
|
528
|
-
clearCachedUrl(activeEnv);
|
|
529
|
-
try {
|
|
530
|
-
const statusRes = await fetch(
|
|
531
|
-
`${STATUS_ENDPOINT}?environment=${activeEnv}`,
|
|
532
|
-
{ method: "GET", credentials: "include" }
|
|
533
|
-
);
|
|
534
|
-
const refreshedStatus = await statusRes.json().catch(() => ({}));
|
|
535
|
-
setStatusInfo(refreshedStatus || null);
|
|
536
|
-
} catch {
|
|
537
|
-
}
|
|
538
|
-
setConnState("connected");
|
|
539
|
-
setShowManual(false);
|
|
540
|
-
} catch (e) {
|
|
541
|
-
completedRef.current = false;
|
|
542
|
-
console.error(e);
|
|
543
|
-
setConnState("error");
|
|
544
|
-
setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
|
|
545
|
-
} finally {
|
|
546
|
-
setOnboardingInProgress(false);
|
|
547
|
-
}
|
|
548
|
-
},
|
|
549
|
-
[stopPopupPoll]
|
|
550
|
-
);
|
|
551
|
-
const fetchFreshLink = react.useCallback(
|
|
552
|
-
(runId, targetEnv) => {
|
|
553
|
-
if (initLoaderRef.current) {
|
|
554
|
-
const loaderText = initLoaderRef.current.querySelector("#loader-text");
|
|
555
|
-
if (loaderText)
|
|
556
|
-
loaderText.textContent = "Generating onboarding session...";
|
|
557
|
-
}
|
|
558
|
-
fetch(SERVICE_URL, {
|
|
559
|
-
method: "POST",
|
|
560
|
-
headers: { "content-type": "application/json" },
|
|
561
|
-
credentials: "include",
|
|
562
|
-
body: JSON.stringify({
|
|
563
|
-
products: ["PPCP"],
|
|
564
|
-
// Generate the link for the explicitly selected environment so it can
|
|
565
|
-
// never depend on a racing POST /environment having landed yet.
|
|
566
|
-
environment: targetEnv
|
|
567
|
-
})
|
|
568
|
-
}).then((r) => {
|
|
569
|
-
if (!r.ok) throw new Error(`Service returned ${r.status}`);
|
|
570
|
-
return r.json();
|
|
571
|
-
}).then((data) => {
|
|
409
|
+
if (!res.ok) throw new Error(`Service returned ${res.status}`);
|
|
410
|
+
const data = await res.json();
|
|
572
411
|
if (runId !== currentRunId.current) return;
|
|
573
412
|
const href = data == null ? void 0 : data.onboarding_url;
|
|
574
413
|
if (!href) {
|
|
@@ -576,13 +415,17 @@ function PayPalConnectionPage() {
|
|
|
576
415
|
return;
|
|
577
416
|
}
|
|
578
417
|
const url = href + (href.includes("?") ? "&" : "?") + "displayMode=minibrowser";
|
|
579
|
-
writeCachedUrl(targetEnv, url);
|
|
418
|
+
const persisted = writeCachedUrl(targetEnv, url);
|
|
419
|
+
if (persisted) {
|
|
420
|
+
window.location.reload();
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
580
423
|
setFinalUrl(url);
|
|
581
424
|
setConnState("ready");
|
|
582
|
-
}
|
|
425
|
+
} catch {
|
|
583
426
|
if (runId !== currentRunId.current) return;
|
|
584
427
|
showError("Unable to connect to service.");
|
|
585
|
-
}
|
|
428
|
+
}
|
|
586
429
|
},
|
|
587
430
|
[showError]
|
|
588
431
|
);
|
|
@@ -599,12 +442,60 @@ function PayPalConnectionPage() {
|
|
|
599
442
|
alive = false;
|
|
600
443
|
};
|
|
601
444
|
}, []);
|
|
445
|
+
react.useEffect(() => {
|
|
446
|
+
if (!envReady) return;
|
|
447
|
+
currentRunId.current = ++runIdRef.current;
|
|
448
|
+
const runId = currentRunId.current;
|
|
449
|
+
let cancelled = false;
|
|
450
|
+
completedRef.current = false;
|
|
451
|
+
const run = async () => {
|
|
452
|
+
setConnState("loading");
|
|
453
|
+
setError(null);
|
|
454
|
+
setFinalUrl("");
|
|
455
|
+
try {
|
|
456
|
+
const r = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
|
|
457
|
+
method: "GET",
|
|
458
|
+
credentials: "include"
|
|
459
|
+
});
|
|
460
|
+
const st = await r.json().catch(() => ({}));
|
|
461
|
+
if (cancelled || runId !== currentRunId.current) return;
|
|
462
|
+
setStatusInfo(st);
|
|
463
|
+
const isConnected = (st == null ? void 0 : st.status) === "connected" && (st == null ? void 0 : st.seller_client_id_present) === true;
|
|
464
|
+
if (isConnected) {
|
|
465
|
+
setConnState("connected");
|
|
466
|
+
setShowManual(false);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
} catch (e) {
|
|
470
|
+
console.error(e);
|
|
471
|
+
}
|
|
472
|
+
if (cancelled || runId !== currentRunId.current) return;
|
|
473
|
+
const cached = readCachedUrl(env);
|
|
474
|
+
if (cached) {
|
|
475
|
+
setFinalUrl(cached);
|
|
476
|
+
setConnState("ready");
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
await generateLinkAndReload(env, runId);
|
|
480
|
+
};
|
|
481
|
+
run();
|
|
482
|
+
return () => {
|
|
483
|
+
cancelled = true;
|
|
484
|
+
};
|
|
485
|
+
}, [env, envReady, generateLinkAndReload]);
|
|
602
486
|
react.useEffect(() => {
|
|
603
487
|
var _a, _b, _c;
|
|
604
488
|
if (connState !== "ready" || !finalUrl) return;
|
|
605
489
|
const scriptUrl = PARTNER_JS_URLS[env];
|
|
606
490
|
let cancelled = false;
|
|
607
491
|
let pollTimer;
|
|
492
|
+
setPartnerReady(false);
|
|
493
|
+
const readyFallback = setTimeout(() => {
|
|
494
|
+
if (!cancelled) setPartnerReady(true);
|
|
495
|
+
}, 6e3);
|
|
496
|
+
const markReady = () => {
|
|
497
|
+
if (!cancelled) setPartnerReady(true);
|
|
498
|
+
};
|
|
608
499
|
const initPartner = (attempt = 0) => {
|
|
609
500
|
var _a2, _b2;
|
|
610
501
|
if (cancelled) return;
|
|
@@ -616,6 +507,7 @@ function PayPalConnectionPage() {
|
|
|
616
507
|
} catch (e) {
|
|
617
508
|
console.error("[paypal] partner.js init failed:", e);
|
|
618
509
|
}
|
|
510
|
+
markReady();
|
|
619
511
|
return;
|
|
620
512
|
}
|
|
621
513
|
if (typeof (signup == null ? void 0 : signup.render) === "function") {
|
|
@@ -624,6 +516,7 @@ function PayPalConnectionPage() {
|
|
|
624
516
|
} catch (e) {
|
|
625
517
|
console.error("[paypal] partner.js render failed:", e);
|
|
626
518
|
}
|
|
519
|
+
markReady();
|
|
627
520
|
return;
|
|
628
521
|
}
|
|
629
522
|
if (attempt < 40) {
|
|
@@ -634,6 +527,7 @@ function PayPalConnectionPage() {
|
|
|
634
527
|
document.dispatchEvent(new Event("DOMContentLoaded"));
|
|
635
528
|
} catch {
|
|
636
529
|
}
|
|
530
|
+
markReady();
|
|
637
531
|
};
|
|
638
532
|
const existingScript = document.getElementById("paypal-partner-js");
|
|
639
533
|
if (existingScript) {
|
|
@@ -650,9 +544,11 @@ function PayPalConnectionPage() {
|
|
|
650
544
|
ppScript.src = scriptUrl;
|
|
651
545
|
ppScript.async = true;
|
|
652
546
|
ppScript.onload = () => initPartner();
|
|
547
|
+
ppScript.onerror = () => markReady();
|
|
653
548
|
document.body.appendChild(ppScript);
|
|
654
549
|
return () => {
|
|
655
550
|
cancelled = true;
|
|
551
|
+
clearTimeout(readyFallback);
|
|
656
552
|
if (pollTimer) {
|
|
657
553
|
clearTimeout(pollTimer);
|
|
658
554
|
}
|
|
@@ -661,59 +557,61 @@ function PayPalConnectionPage() {
|
|
|
661
557
|
}
|
|
662
558
|
};
|
|
663
559
|
}, [connState, finalUrl, env]);
|
|
664
|
-
react.
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
560
|
+
const completeOnboarding = react.useCallback(async (authCode, sharedId) => {
|
|
561
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
562
|
+
if (!authCode || !sharedId) return;
|
|
563
|
+
if (completedRef.current) return;
|
|
564
|
+
completedRef.current = true;
|
|
565
|
+
try {
|
|
566
|
+
window.onbeforeunload = null;
|
|
567
|
+
} catch {
|
|
568
|
+
}
|
|
569
|
+
const activeEnv = envRef.current === "sandbox" ? "sandbox" : "live";
|
|
570
|
+
setOnboardingInProgress(true);
|
|
571
|
+
setConnState("loading");
|
|
572
|
+
setError(null);
|
|
573
|
+
try {
|
|
574
|
+
const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
|
|
575
|
+
method: "POST",
|
|
576
|
+
headers: { "content-type": "application/json" },
|
|
577
|
+
credentials: "include",
|
|
578
|
+
body: JSON.stringify({ authCode, sharedId, env: activeEnv })
|
|
579
|
+
});
|
|
580
|
+
if (!res.ok) {
|
|
581
|
+
const txt = await res.text().catch(() => "");
|
|
582
|
+
throw new Error(txt || `Onboarding exchange failed (${res.status})`);
|
|
670
583
|
}
|
|
671
|
-
};
|
|
672
|
-
window.addEventListener("message", onMessage);
|
|
673
|
-
return () => window.removeEventListener("message", onMessage);
|
|
674
|
-
}, [completeOnboarding]);
|
|
675
|
-
react.useEffect(() => {
|
|
676
|
-
if (!envReady) return;
|
|
677
|
-
currentRunId.current = ++runIdRef.current;
|
|
678
|
-
const runId = currentRunId.current;
|
|
679
|
-
let cancelled = false;
|
|
680
|
-
completedRef.current = false;
|
|
681
|
-
const run = async () => {
|
|
682
|
-
setConnState("loading");
|
|
683
|
-
setError(null);
|
|
684
|
-
setPopupBlocked(false);
|
|
685
|
-
setFinalUrl("");
|
|
686
584
|
try {
|
|
687
|
-
const
|
|
585
|
+
const close1 = (_d = (_c = (_b = (_a = window.PAYPAL) == null ? void 0 : _a.apps) == null ? void 0 : _b.Signup) == null ? void 0 : _c.MiniBrowser) == null ? void 0 : _d.closeFlow;
|
|
586
|
+
if (typeof close1 === "function") close1();
|
|
587
|
+
} catch {
|
|
588
|
+
}
|
|
589
|
+
try {
|
|
590
|
+
const close2 = ((_g = (_f = (_e = window.PAYPAL) == null ? void 0 : _e.apps) == null ? void 0 : _f.Signup) == null ? void 0 : _g.miniBrowser) && window.PAYPAL.apps.Signup.miniBrowser.closeFlow;
|
|
591
|
+
if (typeof close2 === "function") close2();
|
|
592
|
+
} catch {
|
|
593
|
+
}
|
|
594
|
+
clearCachedUrl(activeEnv);
|
|
595
|
+
try {
|
|
596
|
+
const statusRes = await fetch(`${STATUS_ENDPOINT}?environment=${activeEnv}`, {
|
|
688
597
|
method: "GET",
|
|
689
598
|
credentials: "include"
|
|
690
599
|
});
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const isConnected = (st == null ? void 0 : st.status) === "connected" && (st == null ? void 0 : st.seller_client_id_present) === true;
|
|
695
|
-
if (isConnected) {
|
|
696
|
-
setConnState("connected");
|
|
697
|
-
setShowManual(false);
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
} catch (e) {
|
|
701
|
-
console.error(e);
|
|
702
|
-
}
|
|
703
|
-
if (cancelled || runId !== currentRunId.current) return;
|
|
704
|
-
const cached = readCachedUrl(env);
|
|
705
|
-
if (cached) {
|
|
706
|
-
setFinalUrl(cached);
|
|
707
|
-
setConnState("ready");
|
|
708
|
-
} else {
|
|
709
|
-
fetchFreshLink(runId, env);
|
|
600
|
+
const refreshedStatus = await statusRes.json().catch(() => ({}));
|
|
601
|
+
setStatusInfo(refreshedStatus || null);
|
|
602
|
+
} catch {
|
|
710
603
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
604
|
+
setConnState("connected");
|
|
605
|
+
setShowManual(false);
|
|
606
|
+
} catch (e) {
|
|
607
|
+
completedRef.current = false;
|
|
608
|
+
console.error(e);
|
|
609
|
+
setConnState("error");
|
|
610
|
+
setError((e == null ? void 0 : e.message) || "Exchange failed while saving credentials.");
|
|
611
|
+
} finally {
|
|
612
|
+
setOnboardingInProgress(false);
|
|
613
|
+
}
|
|
614
|
+
}, []);
|
|
717
615
|
react.useLayoutEffect(() => {
|
|
718
616
|
window.onboardingCallback = (authCode, sharedId) => {
|
|
719
617
|
completeOnboarding(authCode, sharedId);
|
|
@@ -722,11 +620,6 @@ function PayPalConnectionPage() {
|
|
|
722
620
|
window.onboardingCallback = void 0;
|
|
723
621
|
};
|
|
724
622
|
}, [completeOnboarding]);
|
|
725
|
-
react.useEffect(() => {
|
|
726
|
-
return () => {
|
|
727
|
-
stopPopupPoll();
|
|
728
|
-
};
|
|
729
|
-
}, [stopPopupPoll]);
|
|
730
623
|
react.useLayoutEffect(() => {
|
|
731
624
|
const el = ppBtnMeasureRef.current;
|
|
732
625
|
if (!el) return;
|
|
@@ -747,26 +640,6 @@ function PayPalConnectionPage() {
|
|
|
747
640
|
else window.removeEventListener("resize", update);
|
|
748
641
|
};
|
|
749
642
|
}, [connState, env, finalUrl]);
|
|
750
|
-
react.useEffect(() => {
|
|
751
|
-
const onCaptureClick = (e) => {
|
|
752
|
-
const btn = paypalButtonRef.current;
|
|
753
|
-
if (!btn) return;
|
|
754
|
-
const target = e.target;
|
|
755
|
-
if (!target || !btn.contains(target)) return;
|
|
756
|
-
e.preventDefault();
|
|
757
|
-
if (connStateRef.current !== "ready" || !finalUrlRef.current || inProgressRef.current) {
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
|
-
completedRef.current = false;
|
|
761
|
-
const popup = openOnboardingPopup(finalUrlRef.current);
|
|
762
|
-
setPopupBlocked(!popup);
|
|
763
|
-
};
|
|
764
|
-
document.addEventListener("click", onCaptureClick, true);
|
|
765
|
-
return () => document.removeEventListener("click", onCaptureClick, true);
|
|
766
|
-
}, [openOnboardingPopup]);
|
|
767
|
-
const handleConnectClick = (e) => {
|
|
768
|
-
e.preventDefault();
|
|
769
|
-
};
|
|
770
643
|
const handleSaveManual = async () => {
|
|
771
644
|
if (!canSaveManual || onboardingInProgress) return;
|
|
772
645
|
setOnboardingInProgress(true);
|
|
@@ -827,8 +700,7 @@ function PayPalConnectionPage() {
|
|
|
827
700
|
clearCachedUrl(env);
|
|
828
701
|
completedRef.current = false;
|
|
829
702
|
currentRunId.current = ++runIdRef.current;
|
|
830
|
-
|
|
831
|
-
fetchFreshLink(runId, env);
|
|
703
|
+
await generateLinkAndReload(env, currentRunId.current);
|
|
832
704
|
} catch (e) {
|
|
833
705
|
console.error(e);
|
|
834
706
|
setConnState("error");
|
|
@@ -839,10 +711,10 @@ function PayPalConnectionPage() {
|
|
|
839
711
|
};
|
|
840
712
|
const handleEnvChange = async (e) => {
|
|
841
713
|
const next = e.target.value;
|
|
714
|
+
if (next === env || onboardingInProgress) return;
|
|
842
715
|
completedRef.current = false;
|
|
843
|
-
setPopupBlocked(false);
|
|
844
716
|
clearCachedUrl();
|
|
845
|
-
|
|
717
|
+
setConnState("loading");
|
|
846
718
|
try {
|
|
847
719
|
await fetch(ENVIRONMENT_ENDPOINT, {
|
|
848
720
|
method: "POST",
|
|
@@ -852,6 +724,7 @@ function PayPalConnectionPage() {
|
|
|
852
724
|
});
|
|
853
725
|
} catch {
|
|
854
726
|
}
|
|
727
|
+
setEnv(next);
|
|
855
728
|
};
|
|
856
729
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6", children: [
|
|
857
730
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
@@ -911,7 +784,6 @@ function PayPalConnectionPage() {
|
|
|
911
784
|
"a",
|
|
912
785
|
{
|
|
913
786
|
ref: (node) => {
|
|
914
|
-
paypalButtonRef.current = node;
|
|
915
787
|
ppBtnMeasureRef.current = node;
|
|
916
788
|
},
|
|
917
789
|
id: "paypal-button",
|
|
@@ -919,17 +791,19 @@ function PayPalConnectionPage() {
|
|
|
919
791
|
"data-paypal-button": "true",
|
|
920
792
|
href: finalUrl || "#",
|
|
921
793
|
"data-paypal-onboard-complete": "onboardingCallback",
|
|
922
|
-
|
|
794
|
+
"aria-disabled": onboardingInProgress || !partnerReady,
|
|
923
795
|
className: "transition-fg relative inline-flex w-fit items-center justify-center overflow-hidden rounded-md outline-none no-underline shadow-buttons-neutral text-ui-fg-base bg-ui-button-neutral after:transition-fg after:absolute after:inset-0 after:content-[''] after:button-neutral-gradient hover:bg-ui-button-neutral-hover hover:after:button-neutral-hover-gradient active:bg-ui-button-neutral-pressed active:after:button-neutral-pressed-gradient focus-visible:shadow-buttons-neutral-focus disabled:bg-ui-bg-disabled disabled:border-ui-border-base disabled:text-ui-fg-disabled disabled:shadow-buttons-neutral disabled:after:hidden txt-compact-small-plus px-3 py-1.5",
|
|
924
796
|
style: {
|
|
925
|
-
cursor: onboardingInProgress ? "not-allowed" : "pointer",
|
|
926
|
-
opacity: onboardingInProgress ? 0.6 : 1,
|
|
927
|
-
|
|
797
|
+
cursor: onboardingInProgress || !partnerReady ? "not-allowed" : "pointer",
|
|
798
|
+
opacity: onboardingInProgress || !partnerReady ? 0.6 : 1,
|
|
799
|
+
// Keep the anchor mounted (partner.js must find it to bind)
|
|
800
|
+
// but un-clickable until partner.js has taken over the
|
|
801
|
+
// click — so it can never open a tab via the native href.
|
|
802
|
+
pointerEvents: onboardingInProgress || !partnerReady ? "none" : "auto"
|
|
928
803
|
},
|
|
929
|
-
children: "Connect to PayPal"
|
|
804
|
+
children: partnerReady || onboardingInProgress ? "Connect to PayPal" : "Preparing PayPal…"
|
|
930
805
|
}
|
|
931
806
|
),
|
|
932
|
-
popupBlocked && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 text-left text-xs bg-amber-50 text-amber-700 p-3 border border-amber-200 rounded", children: "Your browser blocked the PayPal popup. Please allow popups for this site, then click “Connect to PayPal” again." }),
|
|
933
807
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
934
808
|
"div",
|
|
935
809
|
{
|
|
@@ -1302,14 +1176,14 @@ const routeModule = {
|
|
|
1302
1176
|
Component: PayPalSettingsIndexRoute,
|
|
1303
1177
|
path: "/settings/paypal"
|
|
1304
1178
|
},
|
|
1305
|
-
{
|
|
1306
|
-
Component: AdvancedCardPaymentsTab,
|
|
1307
|
-
path: "/settings/paypal/advanced-card-payments"
|
|
1308
|
-
},
|
|
1309
1179
|
{
|
|
1310
1180
|
Component: AdditionalSettingsTab,
|
|
1311
1181
|
path: "/settings/paypal/additional-settings"
|
|
1312
1182
|
},
|
|
1183
|
+
{
|
|
1184
|
+
Component: AdvancedCardPaymentsTab,
|
|
1185
|
+
path: "/settings/paypal/advanced-card-payments"
|
|
1186
|
+
},
|
|
1313
1187
|
{
|
|
1314
1188
|
Component: PayPalApplePayPage,
|
|
1315
1189
|
path: "/settings/paypal/apple-pay"
|