@easypayment/medusa-paypal 0.3.7 → 0.3.9
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 +15 -43
- package/.medusa/server/src/admin/index.mjs +15 -43
- package/.medusa/server/src/api/store/paypal/config/route.d.ts.map +1 -1
- package/.medusa/server/src/api/store/paypal/config/route.js +8 -3
- package/.medusa/server/src/api/store/paypal/config/route.js.map +1 -1
- package/package.json +1 -1
- package/src/admin/routes/settings/paypal/advanced-card-payments/page.tsx +17 -53
- package/src/api/store/paypal/config/route.ts +12 -4
|
@@ -217,32 +217,21 @@ async function adminFetch$1(path, opts = {}) {
|
|
|
217
217
|
return {};
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
-
const DEFAULT_FORM = {
|
|
220
|
+
const DEFAULT_FORM = {
|
|
221
|
+
enabled: true,
|
|
222
|
+
title: "Credit or Debit Card",
|
|
223
|
+
threeDS: "when_required"
|
|
224
|
+
};
|
|
221
225
|
function mergeWithDefaults(saved) {
|
|
222
226
|
if (!saved) return { ...DEFAULT_FORM };
|
|
223
227
|
const entries = Object.entries(saved).filter(([, value]) => value !== void 0);
|
|
224
228
|
return { ...DEFAULT_FORM, ...Object.fromEntries(entries) };
|
|
225
229
|
}
|
|
226
|
-
const CARD_BRANDS = [
|
|
227
|
-
{ value: "visa", label: "Visa" },
|
|
228
|
-
{ value: "mastercard", label: "Mastercard" },
|
|
229
|
-
{ value: "amex", label: "American Express" },
|
|
230
|
-
{ value: "discover", label: "Discover" },
|
|
231
|
-
{ value: "diners", label: "Diners Club" },
|
|
232
|
-
{ value: "jcb", label: "JCB" },
|
|
233
|
-
{ value: "unionpay", label: "UnionPay" }
|
|
234
|
-
];
|
|
235
230
|
const THREE_DS_OPTIONS = [
|
|
236
231
|
{ value: "when_required", label: "3D Secure when required", hint: "Triggers 3DS only when the card / issuer requires it." },
|
|
237
232
|
{ value: "sli", label: "3D Secure (SCA) / liability shift (recommended)", hint: "Attempts to optimize for liability shift while remaining compliant." },
|
|
238
233
|
{ value: "always", label: "Always request 3D Secure", hint: "Forces 3DS challenge whenever possible (may reduce conversion)." }
|
|
239
234
|
];
|
|
240
|
-
function Pill$1({ children, onRemove }) {
|
|
241
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-ui-border-base bg-ui-bg-base px-2 py-1 text-sm text-ui-fg-base", children: [
|
|
242
|
-
children,
|
|
243
|
-
onRemove ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onRemove, className: "ml-1 rounded px-1 text-ui-fg-subtle hover:text-ui-fg-base", "aria-label": "Remove", children: "x" }) : null
|
|
244
|
-
] });
|
|
245
|
-
}
|
|
246
235
|
function SectionCard$1({ title, description, right, children }) {
|
|
247
236
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm", children: [
|
|
248
237
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 border-b border-ui-border-base p-4", children: [
|
|
@@ -290,7 +279,10 @@ function AdvancedCardPaymentsTab() {
|
|
|
290
279
|
async function onSave() {
|
|
291
280
|
try {
|
|
292
281
|
setSaving(true);
|
|
293
|
-
const json = await adminFetch$1("/admin/paypal/settings", {
|
|
282
|
+
const json = await adminFetch$1("/admin/paypal/settings", {
|
|
283
|
+
method: "POST",
|
|
284
|
+
body: { advanced_card_payments: form }
|
|
285
|
+
});
|
|
294
286
|
const payload = (json == null ? void 0 : json.data) ?? json;
|
|
295
287
|
const saved = payload == null ? void 0 : payload.advanced_card_payments;
|
|
296
288
|
if (saved && typeof saved === "object") setForm(mergeWithDefaults(saved));
|
|
@@ -303,16 +295,6 @@ function AdvancedCardPaymentsTab() {
|
|
|
303
295
|
setSaving(false);
|
|
304
296
|
}
|
|
305
297
|
}
|
|
306
|
-
const disabledSet = react.useMemo(() => new Set(form.disabledCards), [form.disabledCards]);
|
|
307
|
-
function toggleDisabledCard(value) {
|
|
308
|
-
setForm((prev) => {
|
|
309
|
-
const exists = prev.disabledCards.includes(value);
|
|
310
|
-
return { ...prev, disabledCards: exists ? prev.disabledCards.filter((v) => v !== value) : [...prev.disabledCards, value] };
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
function removeDisabledCard(value) {
|
|
314
|
-
setForm((prev) => ({ ...prev, disabledCards: prev.disabledCards.filter((v) => v !== value) }));
|
|
315
|
-
}
|
|
316
298
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
317
299
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between gap-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-ui-fg-base", children: "PayPal Gateway By Easy Payment" }) }) }),
|
|
318
300
|
/* @__PURE__ */ jsxRuntime.jsx(PayPalTabs, {}),
|
|
@@ -332,16 +314,6 @@ function AdvancedCardPaymentsTab() {
|
|
|
332
314
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: "Enable Advanced Credit/Debit Card" })
|
|
333
315
|
] }) }),
|
|
334
316
|
/* @__PURE__ */ jsxRuntime.jsx(FieldRow$1, { label: "Title", children: /* @__PURE__ */ jsxRuntime.jsx("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" }) }),
|
|
335
|
-
/* @__PURE__ */ jsxRuntime.jsx(FieldRow$1, { label: "Disable specific credit cards", hint: "Select card brands to hide from the card form.", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
336
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: form.disabledCards.length ? form.disabledCards.map((v) => {
|
|
337
|
-
var _a2;
|
|
338
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Pill$1, { onRemove: () => removeDisabledCard(v), children: ((_a2 = CARD_BRANDS.find((b) => b.value === v)) == null ? void 0 : _a2.label) ?? v }, v);
|
|
339
|
-
}) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-subtle", children: "No card brands disabled." }) }),
|
|
340
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-ui-border-base p-3", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-2 md:grid-cols-2", children: CARD_BRANDS.map((b) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 rounded-md p-2 hover:bg-ui-bg-subtle", children: [
|
|
341
|
-
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: disabledSet.has(b.value), onChange: () => toggleDisabledCard(b.value), className: "h-4 w-4 rounded border-ui-border-base" }),
|
|
342
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-ui-fg-base", children: b.label })
|
|
343
|
-
] }, b.value)) }) })
|
|
344
|
-
] }) }),
|
|
345
317
|
/* @__PURE__ */ jsxRuntime.jsx(FieldRow$1, { label: "Contingency for 3D Secure", hint: "Choose when 3D Secure should be triggered during card payments.", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
346
318
|
/* @__PURE__ */ jsxRuntime.jsx("select", { 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)) }),
|
|
347
319
|
((_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
|
|
@@ -935,10 +907,10 @@ function PayPalConnectionPage() {
|
|
|
935
907
|
` })
|
|
936
908
|
] });
|
|
937
909
|
}
|
|
938
|
-
function
|
|
910
|
+
function PayPalApplePayPage() {
|
|
939
911
|
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
940
912
|
}
|
|
941
|
-
function
|
|
913
|
+
function PayPalGooglePayPage() {
|
|
942
914
|
return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
943
915
|
}
|
|
944
916
|
function PayPalPayLaterMessagingPage() {
|
|
@@ -1355,14 +1327,14 @@ const routeModule = {
|
|
|
1355
1327
|
Component: PayPalConnectionPage,
|
|
1356
1328
|
path: "/settings/paypal/connection"
|
|
1357
1329
|
},
|
|
1358
|
-
{
|
|
1359
|
-
Component: PayPalGooglePayPage,
|
|
1360
|
-
path: "/settings/paypal/google-pay"
|
|
1361
|
-
},
|
|
1362
1330
|
{
|
|
1363
1331
|
Component: PayPalApplePayPage,
|
|
1364
1332
|
path: "/settings/paypal/apple-pay"
|
|
1365
1333
|
},
|
|
1334
|
+
{
|
|
1335
|
+
Component: PayPalGooglePayPage,
|
|
1336
|
+
path: "/settings/paypal/google-pay"
|
|
1337
|
+
},
|
|
1366
1338
|
{
|
|
1367
1339
|
Component: PayPalPayLaterMessagingPage,
|
|
1368
1340
|
path: "/settings/paypal/pay-later-messaging"
|
|
@@ -216,32 +216,21 @@ async function adminFetch$1(path, opts = {}) {
|
|
|
216
216
|
return {};
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
|
-
const DEFAULT_FORM = {
|
|
219
|
+
const DEFAULT_FORM = {
|
|
220
|
+
enabled: true,
|
|
221
|
+
title: "Credit or Debit Card",
|
|
222
|
+
threeDS: "when_required"
|
|
223
|
+
};
|
|
220
224
|
function mergeWithDefaults(saved) {
|
|
221
225
|
if (!saved) return { ...DEFAULT_FORM };
|
|
222
226
|
const entries = Object.entries(saved).filter(([, value]) => value !== void 0);
|
|
223
227
|
return { ...DEFAULT_FORM, ...Object.fromEntries(entries) };
|
|
224
228
|
}
|
|
225
|
-
const CARD_BRANDS = [
|
|
226
|
-
{ value: "visa", label: "Visa" },
|
|
227
|
-
{ value: "mastercard", label: "Mastercard" },
|
|
228
|
-
{ value: "amex", label: "American Express" },
|
|
229
|
-
{ value: "discover", label: "Discover" },
|
|
230
|
-
{ value: "diners", label: "Diners Club" },
|
|
231
|
-
{ value: "jcb", label: "JCB" },
|
|
232
|
-
{ value: "unionpay", label: "UnionPay" }
|
|
233
|
-
];
|
|
234
229
|
const THREE_DS_OPTIONS = [
|
|
235
230
|
{ value: "when_required", label: "3D Secure when required", hint: "Triggers 3DS only when the card / issuer requires it." },
|
|
236
231
|
{ value: "sli", label: "3D Secure (SCA) / liability shift (recommended)", hint: "Attempts to optimize for liability shift while remaining compliant." },
|
|
237
232
|
{ value: "always", label: "Always request 3D Secure", hint: "Forces 3DS challenge whenever possible (may reduce conversion)." }
|
|
238
233
|
];
|
|
239
|
-
function Pill$1({ children, onRemove }) {
|
|
240
|
-
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-ui-border-base bg-ui-bg-base px-2 py-1 text-sm text-ui-fg-base", children: [
|
|
241
|
-
children,
|
|
242
|
-
onRemove ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onRemove, className: "ml-1 rounded px-1 text-ui-fg-subtle hover:text-ui-fg-base", "aria-label": "Remove", children: "x" }) : null
|
|
243
|
-
] });
|
|
244
|
-
}
|
|
245
234
|
function SectionCard$1({ title, description, right, children }) {
|
|
246
235
|
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm", children: [
|
|
247
236
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4 border-b border-ui-border-base p-4", children: [
|
|
@@ -289,7 +278,10 @@ function AdvancedCardPaymentsTab() {
|
|
|
289
278
|
async function onSave() {
|
|
290
279
|
try {
|
|
291
280
|
setSaving(true);
|
|
292
|
-
const json = await adminFetch$1("/admin/paypal/settings", {
|
|
281
|
+
const json = await adminFetch$1("/admin/paypal/settings", {
|
|
282
|
+
method: "POST",
|
|
283
|
+
body: { advanced_card_payments: form }
|
|
284
|
+
});
|
|
293
285
|
const payload = (json == null ? void 0 : json.data) ?? json;
|
|
294
286
|
const saved = payload == null ? void 0 : payload.advanced_card_payments;
|
|
295
287
|
if (saved && typeof saved === "object") setForm(mergeWithDefaults(saved));
|
|
@@ -302,16 +294,6 @@ function AdvancedCardPaymentsTab() {
|
|
|
302
294
|
setSaving(false);
|
|
303
295
|
}
|
|
304
296
|
}
|
|
305
|
-
const disabledSet = useMemo(() => new Set(form.disabledCards), [form.disabledCards]);
|
|
306
|
-
function toggleDisabledCard(value) {
|
|
307
|
-
setForm((prev) => {
|
|
308
|
-
const exists = prev.disabledCards.includes(value);
|
|
309
|
-
return { ...prev, disabledCards: exists ? prev.disabledCards.filter((v) => v !== value) : [...prev.disabledCards, value] };
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
function removeDisabledCard(value) {
|
|
313
|
-
setForm((prev) => ({ ...prev, disabledCards: prev.disabledCards.filter((v) => v !== value) }));
|
|
314
|
-
}
|
|
315
297
|
return /* @__PURE__ */ jsx("div", { className: "p-6", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
316
298
|
/* @__PURE__ */ jsx("div", { className: "flex items-start justify-between gap-4", children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-ui-fg-base", children: "PayPal Gateway By Easy Payment" }) }) }),
|
|
317
299
|
/* @__PURE__ */ jsx(PayPalTabs, {}),
|
|
@@ -331,16 +313,6 @@ function AdvancedCardPaymentsTab() {
|
|
|
331
313
|
/* @__PURE__ */ jsx("span", { className: "text-sm text-ui-fg-base", children: "Enable Advanced Credit/Debit Card" })
|
|
332
314
|
] }) }),
|
|
333
315
|
/* @__PURE__ */ jsx(FieldRow$1, { label: "Title", children: /* @__PURE__ */ jsx("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" }) }),
|
|
334
|
-
/* @__PURE__ */ jsx(FieldRow$1, { label: "Disable specific credit cards", hint: "Select card brands to hide from the card form.", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
335
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: form.disabledCards.length ? form.disabledCards.map((v) => {
|
|
336
|
-
var _a2;
|
|
337
|
-
return /* @__PURE__ */ jsx(Pill$1, { onRemove: () => removeDisabledCard(v), children: ((_a2 = CARD_BRANDS.find((b) => b.value === v)) == null ? void 0 : _a2.label) ?? v }, v);
|
|
338
|
-
}) : /* @__PURE__ */ jsx("span", { className: "text-sm text-ui-fg-subtle", children: "No card brands disabled." }) }),
|
|
339
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-md border border-ui-border-base p-3", children: /* @__PURE__ */ jsx("div", { className: "grid gap-2 md:grid-cols-2", children: CARD_BRANDS.map((b) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 rounded-md p-2 hover:bg-ui-bg-subtle", children: [
|
|
340
|
-
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: disabledSet.has(b.value), onChange: () => toggleDisabledCard(b.value), className: "h-4 w-4 rounded border-ui-border-base" }),
|
|
341
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-ui-fg-base", children: b.label })
|
|
342
|
-
] }, b.value)) }) })
|
|
343
|
-
] }) }),
|
|
344
316
|
/* @__PURE__ */ jsx(FieldRow$1, { label: "Contingency for 3D Secure", hint: "Choose when 3D Secure should be triggered during card payments.", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
345
317
|
/* @__PURE__ */ jsx("select", { 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__ */ jsx("option", { value: o.value, children: o.label }, o.value)) }),
|
|
346
318
|
((_a = THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)) == null ? void 0 : _a.hint) ? /* @__PURE__ */ 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
|
|
@@ -934,10 +906,10 @@ function PayPalConnectionPage() {
|
|
|
934
906
|
` })
|
|
935
907
|
] });
|
|
936
908
|
}
|
|
937
|
-
function
|
|
909
|
+
function PayPalApplePayPage() {
|
|
938
910
|
return /* @__PURE__ */ jsx(Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
939
911
|
}
|
|
940
|
-
function
|
|
912
|
+
function PayPalGooglePayPage() {
|
|
941
913
|
return /* @__PURE__ */ jsx(Navigate, { to: "/settings/paypal/connection", replace: true });
|
|
942
914
|
}
|
|
943
915
|
function PayPalPayLaterMessagingPage() {
|
|
@@ -1354,14 +1326,14 @@ const routeModule = {
|
|
|
1354
1326
|
Component: PayPalConnectionPage,
|
|
1355
1327
|
path: "/settings/paypal/connection"
|
|
1356
1328
|
},
|
|
1357
|
-
{
|
|
1358
|
-
Component: PayPalGooglePayPage,
|
|
1359
|
-
path: "/settings/paypal/google-pay"
|
|
1360
|
-
},
|
|
1361
1329
|
{
|
|
1362
1330
|
Component: PayPalApplePayPage,
|
|
1363
1331
|
path: "/settings/paypal/apple-pay"
|
|
1364
1332
|
},
|
|
1333
|
+
{
|
|
1334
|
+
Component: PayPalGooglePayPage,
|
|
1335
|
+
path: "/settings/paypal/google-pay"
|
|
1336
|
+
},
|
|
1365
1337
|
{
|
|
1366
1338
|
Component: PayPalPayLaterMessagingPage,
|
|
1367
1339
|
path: "/settings/paypal/pay-later-messaging"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/api/store/paypal/config/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAQ7E,wBAAsB,GAAG,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/api/store/paypal/config/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAQ7E,wBAAsB,GAAG,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,cAAc,2BAkGhE"}
|
|
@@ -47,11 +47,14 @@ async function GET(req, res) {
|
|
|
47
47
|
const disableButtons = Array.isArray(paypalSettings.disableButtons)
|
|
48
48
|
? paypalSettings.disableButtons.filter((value) => typeof value === "string")
|
|
49
49
|
: [];
|
|
50
|
-
// P2 — read advanced card payments
|
|
50
|
+
// P2 — read advanced card payments settings
|
|
51
51
|
const advancedCardSettings = data && typeof data === "object"
|
|
52
52
|
? (data.advanced_card_payments || {})
|
|
53
53
|
: {};
|
|
54
54
|
const cardEnabled = advancedCardSettings.enabled !== false;
|
|
55
|
+
const cardThreeDS = typeof advancedCardSettings.threeDS === "string"
|
|
56
|
+
? advancedCardSettings.threeDS
|
|
57
|
+
: "when_required";
|
|
55
58
|
return res.json({
|
|
56
59
|
environment: creds.environment,
|
|
57
60
|
client_id: creds.client_id,
|
|
@@ -63,10 +66,12 @@ async function GET(req, res) {
|
|
|
63
66
|
intent: paymentAction,
|
|
64
67
|
paypal_enabled: paypalSettings.enabled ?? true,
|
|
65
68
|
paypal_title: paypalSettings.title || "PayPal",
|
|
66
|
-
card_enabled: cardEnabled,
|
|
69
|
+
card_enabled: cardEnabled,
|
|
70
|
+
card_title: advancedCardSettings.title || "Credit or Debit Card",
|
|
71
|
+
card_three_ds: cardThreeDS, // ← added
|
|
67
72
|
button_color: paypalSettings.buttonColor || "gold",
|
|
68
73
|
button_shape: paypalSettings.buttonShape || "rect",
|
|
69
|
-
button_width: paypalSettings.buttonWidth || "
|
|
74
|
+
button_width: paypalSettings.buttonWidth || "responsive",
|
|
70
75
|
button_height: paypalSettings.buttonHeight ?? 45,
|
|
71
76
|
button_label: paypalSettings.buttonLabel || "paypal",
|
|
72
77
|
disable_buttons: disableButtons,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../../src/api/store/paypal/config/route.ts"],"names":[],"mappings":";;AAQA,
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../../../../src/api/store/paypal/config/route.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react"
|
|
2
2
|
import PayPalTabs from "../_components/Tabs"
|
|
3
3
|
|
|
4
4
|
type AdminFetchOptions = {
|
|
@@ -34,17 +34,19 @@ async function adminFetch<T = unknown>(path: string, opts: AdminFetchOptions = {
|
|
|
34
34
|
try { return JSON.parse(text) as T } catch { return {} as T }
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
type DisabledCardBrand = "visa" | "mastercard" | "amex" | "discover" | "diners" | "jcb" | "unionpay"
|
|
38
37
|
type ThreeDSContingency = "sli" | "when_required" | "always"
|
|
39
38
|
|
|
40
39
|
type AdvancedCardPaymentsForm = {
|
|
41
40
|
enabled: boolean
|
|
42
41
|
title: string
|
|
43
|
-
disabledCards: DisabledCardBrand[]
|
|
44
42
|
threeDS: ThreeDSContingency
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
const DEFAULT_FORM: AdvancedCardPaymentsForm = {
|
|
45
|
+
const DEFAULT_FORM: AdvancedCardPaymentsForm = {
|
|
46
|
+
enabled: true,
|
|
47
|
+
title: "Credit or Debit Card",
|
|
48
|
+
threeDS: "when_required",
|
|
49
|
+
}
|
|
48
50
|
|
|
49
51
|
function mergeWithDefaults(saved?: Partial<AdvancedCardPaymentsForm> | null) {
|
|
50
52
|
if (!saved) return { ...DEFAULT_FORM }
|
|
@@ -52,27 +54,12 @@ function mergeWithDefaults(saved?: Partial<AdvancedCardPaymentsForm> | null) {
|
|
|
52
54
|
return { ...DEFAULT_FORM, ...(Object.fromEntries(entries) as Partial<AdvancedCardPaymentsForm>) }
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
const CARD_BRANDS: { value: DisabledCardBrand; label: string }[] = [
|
|
56
|
-
{ value: "visa", label: "Visa" }, { value: "mastercard", label: "Mastercard" },
|
|
57
|
-
{ value: "amex", label: "American Express" }, { value: "discover", label: "Discover" },
|
|
58
|
-
{ value: "diners", label: "Diners Club" }, { value: "jcb", label: "JCB" }, { value: "unionpay", label: "UnionPay" },
|
|
59
|
-
]
|
|
60
|
-
|
|
61
57
|
const THREE_DS_OPTIONS: { value: ThreeDSContingency; label: string; hint?: string }[] = [
|
|
62
58
|
{ value: "when_required", label: "3D Secure when required", hint: "Triggers 3DS only when the card / issuer requires it." },
|
|
63
59
|
{ value: "sli", label: "3D Secure (SCA) / liability shift (recommended)", hint: "Attempts to optimize for liability shift while remaining compliant." },
|
|
64
60
|
{ value: "always", label: "Always request 3D Secure", hint: "Forces 3DS challenge whenever possible (may reduce conversion)." },
|
|
65
61
|
]
|
|
66
62
|
|
|
67
|
-
function Pill({ children, onRemove }: { children: React.ReactNode; onRemove?: () => void }) {
|
|
68
|
-
return (
|
|
69
|
-
<span className="inline-flex items-center gap-1 rounded-md border border-ui-border-base bg-ui-bg-base px-2 py-1 text-sm text-ui-fg-base">
|
|
70
|
-
{children}
|
|
71
|
-
{onRemove ? <button type="button" onClick={onRemove} className="ml-1 rounded px-1 text-ui-fg-subtle hover:text-ui-fg-base" aria-label="Remove">x</button> : null}
|
|
72
|
-
</span>
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
63
|
function SectionCard({ title, description, right, children }: { title: string; description?: string; right?: React.ReactNode; children: React.ReactNode }) {
|
|
77
64
|
return (
|
|
78
65
|
<div className="rounded-xl border border-ui-border-base bg-ui-bg-base shadow-sm">
|
|
@@ -128,7 +115,10 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
128
115
|
async function onSave() {
|
|
129
116
|
try {
|
|
130
117
|
setSaving(true)
|
|
131
|
-
const json = await adminFetch<any>("/admin/paypal/settings", {
|
|
118
|
+
const json = await adminFetch<any>("/admin/paypal/settings", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
body: { advanced_card_payments: form as unknown as Record<string, unknown> },
|
|
121
|
+
})
|
|
132
122
|
const payload = json?.data ?? json
|
|
133
123
|
const saved = payload?.advanced_card_payments
|
|
134
124
|
if (saved && typeof saved === "object") setForm(mergeWithDefaults(saved))
|
|
@@ -142,19 +132,6 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
142
132
|
}
|
|
143
133
|
}
|
|
144
134
|
|
|
145
|
-
const disabledSet = useMemo(() => new Set(form.disabledCards), [form.disabledCards])
|
|
146
|
-
|
|
147
|
-
function toggleDisabledCard(value: DisabledCardBrand) {
|
|
148
|
-
setForm((prev) => {
|
|
149
|
-
const exists = prev.disabledCards.includes(value)
|
|
150
|
-
return { ...prev, disabledCards: exists ? prev.disabledCards.filter((v) => v !== value) : [...prev.disabledCards, value] }
|
|
151
|
-
})
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function removeDisabledCard(value: DisabledCardBrand) {
|
|
155
|
-
setForm((prev) => ({ ...prev, disabledCards: prev.disabledCards.filter((v) => v !== value) }))
|
|
156
|
-
}
|
|
157
|
-
|
|
158
135
|
return (
|
|
159
136
|
<div className="p-6">
|
|
160
137
|
<div className="flex flex-col gap-6">
|
|
@@ -167,7 +144,9 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
167
144
|
<span className={toast.type === "success" ? "text-ui-fg-base" : "text-ui-fg-error"}>{toast.message}</span>
|
|
168
145
|
</div>
|
|
169
146
|
) : null}
|
|
170
|
-
<SectionCard
|
|
147
|
+
<SectionCard
|
|
148
|
+
title="Advanced Card Payments"
|
|
149
|
+
description="Control card checkout settings and 3D Secure behavior."
|
|
171
150
|
right={(
|
|
172
151
|
<div className="flex items-center gap-3">
|
|
173
152
|
<button type="button" onClick={onSave} disabled={saving || loading} className="rounded-md bg-ui-button-neutral px-4 py-2 text-sm font-medium text-ui-fg-on-color shadow-sm hover:opacity-90 disabled:opacity-60">
|
|
@@ -187,29 +166,14 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
187
166
|
<FieldRow label="Title">
|
|
188
167
|
<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" />
|
|
189
168
|
</FieldRow>
|
|
190
|
-
<FieldRow label="Disable specific credit cards" hint="Select card brands to hide from the card form.">
|
|
191
|
-
<div className="flex flex-col gap-2">
|
|
192
|
-
<div className="flex flex-wrap gap-2">
|
|
193
|
-
{form.disabledCards.length ? form.disabledCards.map((v) => <Pill key={v} onRemove={() => removeDisabledCard(v)}>{CARD_BRANDS.find((b) => b.value === v)?.label ?? v}</Pill>) : <span className="text-sm text-ui-fg-subtle">No card brands disabled.</span>}
|
|
194
|
-
</div>
|
|
195
|
-
<div className="rounded-md border border-ui-border-base p-3">
|
|
196
|
-
<div className="grid gap-2 md:grid-cols-2">
|
|
197
|
-
{CARD_BRANDS.map((b) => (
|
|
198
|
-
<label key={b.value} className="flex items-center gap-2 rounded-md p-2 hover:bg-ui-bg-subtle">
|
|
199
|
-
<input type="checkbox" checked={disabledSet.has(b.value)} onChange={() => toggleDisabledCard(b.value)} className="h-4 w-4 rounded border-ui-border-base" />
|
|
200
|
-
<span className="text-sm text-ui-fg-base">{b.label}</span>
|
|
201
|
-
</label>
|
|
202
|
-
))}
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
</div>
|
|
206
|
-
</FieldRow>
|
|
207
169
|
<FieldRow label="Contingency for 3D Secure" hint="Choose when 3D Secure should be triggered during card payments.">
|
|
208
170
|
<div className="flex flex-col gap-2">
|
|
209
171
|
<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">
|
|
210
172
|
{THREE_DS_OPTIONS.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
|
|
211
173
|
</select>
|
|
212
|
-
{THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)?.hint
|
|
174
|
+
{THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)?.hint
|
|
175
|
+
? <div className="text-xs text-ui-fg-subtle">{THREE_DS_OPTIONS.find((o) => o.value === form.threeDS)?.hint}</div>
|
|
176
|
+
: null}
|
|
213
177
|
</div>
|
|
214
178
|
</FieldRow>
|
|
215
179
|
</div>
|
|
@@ -217,4 +181,4 @@ export default function AdvancedCardPaymentsTab() {
|
|
|
217
181
|
</div>
|
|
218
182
|
</div>
|
|
219
183
|
)
|
|
220
|
-
}
|
|
184
|
+
}
|
|
@@ -67,13 +67,19 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
|
|
67
67
|
? paypalSettings.disableButtons.filter((value: unknown): value is string => typeof value === "string")
|
|
68
68
|
: []
|
|
69
69
|
|
|
70
|
-
// P2 — read advanced card payments
|
|
70
|
+
// P2 — read advanced card payments settings
|
|
71
71
|
const advancedCardSettings =
|
|
72
72
|
data && typeof data === "object"
|
|
73
73
|
? ((data as Record<string, any>).advanced_card_payments || {})
|
|
74
74
|
: {}
|
|
75
|
+
|
|
75
76
|
const cardEnabled: boolean = advancedCardSettings.enabled !== false
|
|
76
77
|
|
|
78
|
+
const cardThreeDS =
|
|
79
|
+
typeof advancedCardSettings.threeDS === "string"
|
|
80
|
+
? advancedCardSettings.threeDS
|
|
81
|
+
: "when_required"
|
|
82
|
+
|
|
77
83
|
return res.json({
|
|
78
84
|
environment: creds.environment,
|
|
79
85
|
client_id: creds.client_id,
|
|
@@ -85,10 +91,12 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
|
|
85
91
|
intent: paymentAction,
|
|
86
92
|
paypal_enabled: paypalSettings.enabled ?? true,
|
|
87
93
|
paypal_title: paypalSettings.title || "PayPal",
|
|
88
|
-
card_enabled: cardEnabled,
|
|
94
|
+
card_enabled: cardEnabled,
|
|
95
|
+
card_title: advancedCardSettings.title || "Credit or Debit Card",
|
|
96
|
+
card_three_ds: cardThreeDS, // ← added
|
|
89
97
|
button_color: paypalSettings.buttonColor || "gold",
|
|
90
98
|
button_shape: paypalSettings.buttonShape || "rect",
|
|
91
|
-
button_width: paypalSettings.buttonWidth || "
|
|
99
|
+
button_width: paypalSettings.buttonWidth || "responsive",
|
|
92
100
|
button_height: paypalSettings.buttonHeight ?? 45,
|
|
93
101
|
button_label: paypalSettings.buttonLabel || "paypal",
|
|
94
102
|
disable_buttons: disableButtons,
|
|
@@ -96,4 +104,4 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
|
|
96
104
|
} catch (e: any) {
|
|
97
105
|
return res.status(500).json({ message: e?.message || "Failed to load PayPal config" })
|
|
98
106
|
}
|
|
99
|
-
}
|
|
107
|
+
}
|