@shoppexio/storefront 1.0.60 → 1.0.61
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/CHANGELOG.md +4 -0
- package/dist/index.cjs +135 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -9
- package/dist/index.d.ts +28 -9
- package/dist/index.js +134 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.61
|
|
4
|
+
|
|
5
|
+
- Add `mountCheckoutChallenge()` so custom storefront domains can complete the hosted checkout verification broker and retry invoice creation.
|
|
6
|
+
|
|
3
7
|
## 1.0.60
|
|
4
8
|
|
|
5
9
|
- Reject post-purchase `redirectUrl` values that still contain unresolved template placeholders.
|
package/dist/index.cjs
CHANGED
|
@@ -71,6 +71,7 @@ __export(src_exports, {
|
|
|
71
71
|
loyalty: () => loyalty,
|
|
72
72
|
me: () => me,
|
|
73
73
|
mergeSettings: () => mergeSettings,
|
|
74
|
+
mountCheckoutChallenge: () => mountCheckoutChallenge,
|
|
74
75
|
normalizeSearchQuery: () => normalizeSearchQuery,
|
|
75
76
|
normalizeStorefrontCustomFields: () => normalizeStorefrontCustomFields,
|
|
76
77
|
order: () => order,
|
|
@@ -15638,7 +15639,7 @@ var PaymentGatewayStateSchema = object({
|
|
|
15638
15639
|
});
|
|
15639
15640
|
|
|
15640
15641
|
// ../contracts/src/style-center.ts
|
|
15641
|
-
var CHECKOUT_PLATFORM_BRAND_COLOR = "#
|
|
15642
|
+
var CHECKOUT_PLATFORM_BRAND_COLOR = "#7c3aed";
|
|
15642
15643
|
var CHECKOUT_PLATFORM_FONT_FAMILY = "Geist";
|
|
15643
15644
|
var checkoutStyleSurfaceValues = ["checkout", "payment_link", "embed"];
|
|
15644
15645
|
var checkoutStyleDensityValues = ["comfortable", "compact"];
|
|
@@ -15665,17 +15666,28 @@ var checkoutStyleTokenDefinitions = [
|
|
|
15665
15666
|
// background. Setting an explicit hex would clobber the ambient gradient and
|
|
15666
15667
|
// the right-panel `--surface-2` accent for every default-theme checkout.
|
|
15667
15668
|
{ key: "color.background", cssVar: "--spx-checkout-bg", group: "color", type: "color", default: "" },
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
//
|
|
15673
|
-
//
|
|
15674
|
-
//
|
|
15675
|
-
//
|
|
15676
|
-
//
|
|
15677
|
-
//
|
|
15678
|
-
|
|
15669
|
+
// The four neutrals below are the DARK appearance of the hosted checkout's
|
|
15670
|
+
// tonal ladder (`apps/checkout/app/globals.css`); the light values sit in
|
|
15671
|
+
// CHECKOUT_PLATFORM_BASELINE_LIGHT_DEFAULTS. They are materialised into the
|
|
15672
|
+
// live checkout root, so they are what a shop with no Style Center theme
|
|
15673
|
+
// actually renders — which is why each one has to name the SAME rung the
|
|
15674
|
+
// component paints, not a palette of its own.
|
|
15675
|
+
// surface → `--surface-0`, the working panel the buyer pays in
|
|
15676
|
+
// surfaceRaised → `--card`, a panel raised above that panel
|
|
15677
|
+
// (the provider widget frame and its loading skeleton)
|
|
15678
|
+
// text → `--foreground`
|
|
15679
|
+
// textMuted → `--muted-foreground`
|
|
15680
|
+
// border → `--border`
|
|
15681
|
+
{ key: "color.surface", cssVar: "--spx-checkout-surface", group: "color", type: "color", default: "#1b1b1b" },
|
|
15682
|
+
{ key: "color.surfaceRaised", cssVar: "--spx-checkout-surface-raised", group: "color", type: "color", default: "#222222" },
|
|
15683
|
+
{ key: "color.text", cssVar: "--spx-checkout-text", group: "color", type: "color", default: "#f5f5f5", protected: true },
|
|
15684
|
+
{ key: "color.textMuted", cssVar: "--spx-checkout-text-muted", group: "color", type: "color", default: "#a1a1a1" },
|
|
15685
|
+
// Never '': the border token feeds runtime-style fallbacks for inputs,
|
|
15686
|
+
// provider widgets and buttons, and an unset value turns those slots'
|
|
15687
|
+
// `var(--spx-checkout-border, …)` chains loose. The hosted checkout draws
|
|
15688
|
+
// almost no hairlines any more — the slots that still do read this, the rest
|
|
15689
|
+
// resolve their border to `transparent` in the system CSS.
|
|
15690
|
+
{ key: "color.border", cssVar: "--spx-checkout-border", group: "color", type: "color", default: "#2c2c2c" },
|
|
15679
15691
|
{ key: "color.focus", cssVar: "--spx-checkout-focus", group: "color", type: "color", default: CHECKOUT_PLATFORM_BRAND_COLOR, protected: true },
|
|
15680
15692
|
{ key: "color.success", cssVar: "--spx-checkout-success", group: "color", type: "color", default: "#22c55e", protected: true },
|
|
15681
15693
|
{ key: "color.warning", cssVar: "--spx-checkout-warning", group: "color", type: "color", default: "#f59e0b", protected: true },
|
|
@@ -15687,9 +15699,16 @@ var checkoutStyleTokenDefinitions = [
|
|
|
15687
15699
|
// actually loaded.
|
|
15688
15700
|
{ key: "typography.googleFontFamily", cssVar: "--spx-checkout-google-font", group: "typography", type: "font", default: "" },
|
|
15689
15701
|
{ key: "typography.baseSize", cssVar: "--spx-checkout-font-size", group: "typography", type: "number", default: 14, min: 12, max: 18, step: 1, unit: "px" },
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
|
|
15702
|
+
// 12px, not 8, for the same reason as the input below: the hosted pay CTA is
|
|
15703
|
+
// `rounded-xl` and this token is materialised over every slotted button. At 8
|
|
15704
|
+
// the store-credit and manual actions rendered one step tighter than the
|
|
15705
|
+
// field directly above them and than the CTA they stand in for.
|
|
15706
|
+
{ key: "shape.buttonRadius", cssVar: "--spx-checkout-button-radius", group: "shape", type: "number", default: 10, min: 0, max: 24, step: 1, unit: "px" },
|
|
15707
|
+
// 12px, not 8: the hosted `Input` primitive is `rounded-lg`, and this token is
|
|
15708
|
+
// materialised over it. At 8 the system CSS rounded every field one step
|
|
15709
|
+
// tighter than the component that drew it.
|
|
15710
|
+
{ key: "shape.inputRadius", cssVar: "--spx-checkout-input-radius", group: "shape", type: "number", default: 10, min: 0, max: 24, step: 1, unit: "px" },
|
|
15711
|
+
{ key: "shape.cardRadius", cssVar: "--spx-checkout-card-radius", group: "shape", type: "number", default: 10, min: 0, max: 28, step: 1, unit: "px" },
|
|
15693
15712
|
{
|
|
15694
15713
|
key: "spacing.density",
|
|
15695
15714
|
cssVar: "--spx-checkout-density",
|
|
@@ -15701,8 +15720,8 @@ var checkoutStyleTokenDefinitions = [
|
|
|
15701
15720
|
{ key: "spacing.controlHeight", cssVar: "--spx-checkout-control-height", group: "spacing", type: "number", default: 48, min: 36, max: 56, step: 2, unit: "px" },
|
|
15702
15721
|
{ key: "component.primaryButton.background", cssVar: "--spx-checkout-button-bg", group: "component", type: "color", default: "", protected: true },
|
|
15703
15722
|
{ key: "component.primaryButton.text", cssVar: "--spx-checkout-button-text", group: "component", type: "color", default: "#ffffff", protected: true },
|
|
15704
|
-
{ key: "component.input.background", cssVar: "--spx-checkout-input-bg", group: "component", type: "color", default: "#
|
|
15705
|
-
{ key: "component.input.border", cssVar: "--spx-checkout-input-border", group: "component", type: "color", default: "#
|
|
15723
|
+
{ key: "component.input.background", cssVar: "--spx-checkout-input-bg", group: "component", type: "color", default: "#292929" },
|
|
15724
|
+
{ key: "component.input.border", cssVar: "--spx-checkout-input-border", group: "component", type: "color", default: "#2c2c2c" },
|
|
15706
15725
|
{ key: "component.input.focusRing", cssVar: "--spx-checkout-input-focus-ring", group: "component", type: "color", default: "" },
|
|
15707
15726
|
// Default '' is filtered out by the preview-iframe normaliser, so the
|
|
15708
15727
|
// CSS fallback chain on [data-spx-slot="summary.panel"] resolves to
|
|
@@ -15727,14 +15746,14 @@ var checkoutStyleTokenDefinitions = [
|
|
|
15727
15746
|
{ key: "component.brandAvatar.border", cssVar: "--spx-checkout-brand-avatar-border", group: "component", type: "color", default: "rgba(255,255,255,0.1)" },
|
|
15728
15747
|
{ key: "component.pill.background", cssVar: "--spx-checkout-pill-bg", group: "component", type: "color", default: "rgba(255,255,255,0.04)" },
|
|
15729
15748
|
{ key: "component.pill.border", cssVar: "--spx-checkout-pill-border", group: "component", type: "color", default: "rgba(255,255,255,0.08)" },
|
|
15730
|
-
{ key: "component.pill.text", cssVar: "--spx-checkout-pill-text", group: "component", type: "color", default: "#
|
|
15749
|
+
{ key: "component.pill.text", cssVar: "--spx-checkout-pill-text", group: "component", type: "color", default: "#a1a1a1" },
|
|
15731
15750
|
{ key: "component.errorBanner.background", cssVar: "--spx-checkout-error-bg", group: "component", type: "color", default: "rgba(239,68,68,0.1)" },
|
|
15732
15751
|
{ key: "component.errorBanner.border", cssVar: "--spx-checkout-error-border", group: "component", type: "color", default: "rgba(239,68,68,0.2)" },
|
|
15733
15752
|
{ key: "component.errorBanner.text", cssVar: "--spx-checkout-error-text", group: "component", type: "color", default: "#ef4444" },
|
|
15734
15753
|
{ key: "component.warningBanner.background", cssVar: "--spx-checkout-warning-bg", group: "component", type: "color", default: "" },
|
|
15735
15754
|
{ key: "component.warningBanner.border", cssVar: "--spx-checkout-warning-border", group: "component", type: "color", default: "" },
|
|
15736
15755
|
{ key: "component.warningBanner.text", cssVar: "--spx-checkout-warning-text", group: "component", type: "color", default: "" },
|
|
15737
|
-
{ key: "component.radio.idleRing", cssVar: "--spx-checkout-radio-idle-ring", group: "component", type: "color", default: "rgba(161,161,
|
|
15756
|
+
{ key: "component.radio.idleRing", cssVar: "--spx-checkout-radio-idle-ring", group: "component", type: "color", default: "rgba(161,161,161,0.5)" },
|
|
15738
15757
|
{ key: "component.radio.checkedFill", cssVar: "--spx-checkout-radio-checked-fill", group: "component", type: "color", default: "" },
|
|
15739
15758
|
{ key: "component.radio.checkedIcon", cssVar: "--spx-checkout-radio-checked-icon", group: "component", type: "color", default: "#ffffff" },
|
|
15740
15759
|
{ key: "component.divider.color", cssVar: "--spx-checkout-divider", group: "component", type: "color", default: "rgba(255,255,255,0.06)" }
|
|
@@ -16536,6 +16555,32 @@ var externalPaymentAdapterConformanceResponseSchema = externalPaymentAdapterBase
|
|
|
16536
16555
|
});
|
|
16537
16556
|
|
|
16538
16557
|
// ../contracts/src/redirect-link-template.ts
|
|
16558
|
+
var REMAINING_PLACEHOLDER_PATTERNS = [
|
|
16559
|
+
/\{\{[\s\S]*?\}\}/g,
|
|
16560
|
+
/(?<!\{)\{[^{}]+\}(?!\})/g
|
|
16561
|
+
];
|
|
16562
|
+
function findDanglingBraceFragments(template) {
|
|
16563
|
+
let stripped = template;
|
|
16564
|
+
for (const pattern of REMAINING_PLACEHOLDER_PATTERNS) {
|
|
16565
|
+
stripped = stripped.replace(pattern, "");
|
|
16566
|
+
}
|
|
16567
|
+
return /[{}]/.test(stripped) ? [...new Set(stripped.match(/[{}]+/g) ?? [])] : [];
|
|
16568
|
+
}
|
|
16569
|
+
function findRemainingProductRedirectPlaceholders(template) {
|
|
16570
|
+
if (!template.trim()) {
|
|
16571
|
+
return [];
|
|
16572
|
+
}
|
|
16573
|
+
const remaining = /* @__PURE__ */ new Set();
|
|
16574
|
+
for (const pattern of REMAINING_PLACEHOLDER_PATTERNS) {
|
|
16575
|
+
for (const match of template.matchAll(pattern)) {
|
|
16576
|
+
remaining.add(match[0]);
|
|
16577
|
+
}
|
|
16578
|
+
}
|
|
16579
|
+
for (const fragment of findDanglingBraceFragments(template)) {
|
|
16580
|
+
remaining.add(fragment);
|
|
16581
|
+
}
|
|
16582
|
+
return [...remaining];
|
|
16583
|
+
}
|
|
16539
16584
|
function isSafeHttpsRedirectTemplateUrl(url2) {
|
|
16540
16585
|
const trimmed = url2.trim();
|
|
16541
16586
|
if (!trimmed) {
|
|
@@ -16597,6 +16642,11 @@ function requirePriceDisplayUnlessPayWhatYouWant(product, ctx) {
|
|
|
16597
16642
|
}
|
|
16598
16643
|
var publicInvoiceWireProductSchema = external_exports.object({
|
|
16599
16644
|
addons: external_exports.array(publicInvoiceWireProductAddonSchema).optional(),
|
|
16645
|
+
/** Per-line cart-edit capability from the enricher; the checkout renders no
|
|
16646
|
+
* control without an explicit true. */
|
|
16647
|
+
quantity_editable: external_exports.boolean().optional(),
|
|
16648
|
+
addons_editable: external_exports.boolean().optional(),
|
|
16649
|
+
removable: external_exports.boolean().optional(),
|
|
16600
16650
|
available_addons: external_exports.array(external_exports.unknown()).optional(),
|
|
16601
16651
|
cloudflare_image_id: external_exports.string().nullable(),
|
|
16602
16652
|
created_at: external_exports.string().optional(),
|
|
@@ -19916,6 +19966,9 @@ function resolvePostPurchaseRedirectUrl(redirectUrl) {
|
|
|
19916
19966
|
if (!isSafeHttpsRedirectTemplateUrl(normalized)) {
|
|
19917
19967
|
return { value: null, error: "redirectUrl must be an absolute HTTPS URL." };
|
|
19918
19968
|
}
|
|
19969
|
+
if (findRemainingProductRedirectPlaceholders(normalized).length > 0) {
|
|
19970
|
+
return { value: null, error: "redirectUrl must not contain template placeholders." };
|
|
19971
|
+
}
|
|
19919
19972
|
return { value: normalized, error: null };
|
|
19920
19973
|
}
|
|
19921
19974
|
var pendingCheckoutCreates = /* @__PURE__ */ new Map();
|
|
@@ -20328,6 +20381,67 @@ function buildCheckoutUrlSync() {
|
|
|
20328
20381
|
throw new Error("buildCheckoutUrlSync is deprecated. Use buildCheckoutUrl (async) instead.");
|
|
20329
20382
|
}
|
|
20330
20383
|
|
|
20384
|
+
// ../sdk/src/modules/checkout-challenge.ts
|
|
20385
|
+
var TURNSTILE_FRAME_MESSAGE_SOURCE = "shoppex-turnstile";
|
|
20386
|
+
var TURNSTILE_FRAME_MESSAGE_VERSION = 1;
|
|
20387
|
+
function readFrameMessage(value, nonce) {
|
|
20388
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
20389
|
+
const record2 = value;
|
|
20390
|
+
if (record2.source !== TURNSTILE_FRAME_MESSAGE_SOURCE || record2.version !== TURNSTILE_FRAME_MESSAGE_VERSION || record2.nonce !== nonce || !["success", "expired", "timeout", "error"].includes(String(record2.type))) return null;
|
|
20391
|
+
if (record2.type === "success" && (typeof record2.token !== "string" || !record2.token.trim())) {
|
|
20392
|
+
return null;
|
|
20393
|
+
}
|
|
20394
|
+
return {
|
|
20395
|
+
type: record2.type,
|
|
20396
|
+
...typeof record2.token === "string" ? { token: record2.token.trim() } : {}
|
|
20397
|
+
};
|
|
20398
|
+
}
|
|
20399
|
+
function mountCheckoutChallenge(container, challenge, callbacks) {
|
|
20400
|
+
if (challenge.provider !== "turnstile" || !challenge.siteKey.trim()) {
|
|
20401
|
+
throw new Error("Checkout challenge is invalid.");
|
|
20402
|
+
}
|
|
20403
|
+
const win = container.ownerDocument.defaultView;
|
|
20404
|
+
if (!win) throw new Error("Checkout challenge requires a browser document.");
|
|
20405
|
+
const checkoutBaseUrl = getConfig().checkoutBaseUrl;
|
|
20406
|
+
const frameUrl = new URL("/turnstile", checkoutBaseUrl);
|
|
20407
|
+
if (frameUrl.protocol !== "https:" && frameUrl.protocol !== "http:") {
|
|
20408
|
+
throw new Error("Checkout base URL must use http or https.");
|
|
20409
|
+
}
|
|
20410
|
+
const nonce = win.crypto.randomUUID();
|
|
20411
|
+
frameUrl.searchParams.set("site_key", challenge.siteKey.trim());
|
|
20412
|
+
frameUrl.searchParams.set("nonce", nonce);
|
|
20413
|
+
const frame = container.ownerDocument.createElement("iframe");
|
|
20414
|
+
frame.src = frameUrl.toString();
|
|
20415
|
+
frame.title = "Checkout verification";
|
|
20416
|
+
frame.referrerPolicy = "no-referrer";
|
|
20417
|
+
frame.style.border = "0";
|
|
20418
|
+
frame.style.width = "100%";
|
|
20419
|
+
frame.style.height = "72px";
|
|
20420
|
+
let disposed = false;
|
|
20421
|
+
const onMessage = (event) => {
|
|
20422
|
+
if (disposed || event.origin !== frameUrl.origin || event.source !== frame.contentWindow) return;
|
|
20423
|
+
const message = readFrameMessage(event.data, nonce);
|
|
20424
|
+
if (!message) return;
|
|
20425
|
+
if (message.type === "success") callbacks.onSuccess(message.token);
|
|
20426
|
+
if (message.type === "expired" || message.type === "timeout") callbacks.onExpired?.();
|
|
20427
|
+
if (message.type === "error") callbacks.onUnavailable?.();
|
|
20428
|
+
};
|
|
20429
|
+
const onFrameError = () => callbacks.onUnavailable?.();
|
|
20430
|
+
win.addEventListener("message", onMessage);
|
|
20431
|
+
frame.addEventListener("error", onFrameError, { once: true });
|
|
20432
|
+
container.appendChild(frame);
|
|
20433
|
+
return {
|
|
20434
|
+
element: frame,
|
|
20435
|
+
dispose() {
|
|
20436
|
+
if (disposed) return;
|
|
20437
|
+
disposed = true;
|
|
20438
|
+
win.removeEventListener("message", onMessage);
|
|
20439
|
+
frame.removeEventListener("error", onFrameError);
|
|
20440
|
+
frame.remove();
|
|
20441
|
+
}
|
|
20442
|
+
};
|
|
20443
|
+
}
|
|
20444
|
+
|
|
20331
20445
|
// ../sdk/src/modules/coupons.ts
|
|
20332
20446
|
async function resolveShopId() {
|
|
20333
20447
|
const cachedShopId2 = getShopId();
|
|
@@ -21337,6 +21451,7 @@ var shoppex = {
|
|
|
21337
21451
|
checkout,
|
|
21338
21452
|
buildCheckoutUrl,
|
|
21339
21453
|
buildCheckoutUrlSync,
|
|
21454
|
+
mountCheckoutChallenge,
|
|
21340
21455
|
// Affiliates
|
|
21341
21456
|
captureAffiliateFromUrl,
|
|
21342
21457
|
validateAffiliateCode,
|
|
@@ -21485,6 +21600,7 @@ if (typeof window !== "undefined") {
|
|
|
21485
21600
|
loyalty,
|
|
21486
21601
|
me,
|
|
21487
21602
|
mergeSettings,
|
|
21603
|
+
mountCheckoutChallenge,
|
|
21488
21604
|
normalizeSearchQuery,
|
|
21489
21605
|
normalizeStorefrontCustomFields,
|
|
21490
21606
|
order,
|