@shoppexio/storefront 1.0.63 → 1.0.65
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 +8 -0
- package/README.md +40 -0
- package/dist/index.cjs +169 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -8
- package/dist/index.d.ts +2 -8
- package/dist/index.js +169 -74
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1549,19 +1549,13 @@ interface CheckoutChallengeCallbacks {
|
|
|
1549
1549
|
onSuccess(token: string): void;
|
|
1550
1550
|
onExpired?(): void;
|
|
1551
1551
|
onUnavailable?(): void;
|
|
1552
|
+
/** Fired when the widget switches between the invisible run and an interactive challenge. */
|
|
1553
|
+
onVisibilityChange?(visible: boolean): void;
|
|
1552
1554
|
}
|
|
1553
1555
|
interface CheckoutChallengeFrame {
|
|
1554
1556
|
element: HTMLIFrameElement;
|
|
1555
1557
|
dispose(): void;
|
|
1556
1558
|
}
|
|
1557
|
-
/**
|
|
1558
|
-
* Mount the Shoppex-hosted Turnstile broker requested by a checkout refusal.
|
|
1559
|
-
*
|
|
1560
|
-
* The challenge always executes on the configured hosted-checkout origin, so
|
|
1561
|
-
* this works on arbitrary custom storefront domains without adding each one
|
|
1562
|
-
* to Cloudflare's widget hostname allowlist. Retry `checkout()` or
|
|
1563
|
-
* `buildCheckoutUrl()` with the token delivered to `onSuccess`.
|
|
1564
|
-
*/
|
|
1565
1559
|
declare function mountCheckoutChallenge(container: HTMLElement, challenge: ApiChallenge, callbacks: CheckoutChallengeCallbacks): CheckoutChallengeFrame;
|
|
1566
1560
|
|
|
1567
1561
|
interface SearchOptions {
|
package/dist/index.d.ts
CHANGED
|
@@ -1549,19 +1549,13 @@ interface CheckoutChallengeCallbacks {
|
|
|
1549
1549
|
onSuccess(token: string): void;
|
|
1550
1550
|
onExpired?(): void;
|
|
1551
1551
|
onUnavailable?(): void;
|
|
1552
|
+
/** Fired when the widget switches between the invisible run and an interactive challenge. */
|
|
1553
|
+
onVisibilityChange?(visible: boolean): void;
|
|
1552
1554
|
}
|
|
1553
1555
|
interface CheckoutChallengeFrame {
|
|
1554
1556
|
element: HTMLIFrameElement;
|
|
1555
1557
|
dispose(): void;
|
|
1556
1558
|
}
|
|
1557
|
-
/**
|
|
1558
|
-
* Mount the Shoppex-hosted Turnstile broker requested by a checkout refusal.
|
|
1559
|
-
*
|
|
1560
|
-
* The challenge always executes on the configured hosted-checkout origin, so
|
|
1561
|
-
* this works on arbitrary custom storefront domains without adding each one
|
|
1562
|
-
* to Cloudflare's widget hostname allowlist. Retry `checkout()` or
|
|
1563
|
-
* `buildCheckoutUrl()` with the token delivered to `onSuccess`.
|
|
1564
|
-
*/
|
|
1565
1559
|
declare function mountCheckoutChallenge(container: HTMLElement, challenge: ApiChallenge, callbacks: CheckoutChallengeCallbacks): CheckoutChallengeFrame;
|
|
1566
1560
|
|
|
1567
1561
|
interface SearchOptions {
|
package/dist/index.js
CHANGED
|
@@ -4539,6 +4539,152 @@ async function quoteCart(coupon, currency) {
|
|
|
4539
4539
|
return response;
|
|
4540
4540
|
}
|
|
4541
4541
|
|
|
4542
|
+
// ../sdk/src/modules/checkout-challenge.ts
|
|
4543
|
+
var TURNSTILE_FRAME_MESSAGE_SOURCE = "shoppex-turnstile";
|
|
4544
|
+
var TURNSTILE_FRAME_MESSAGE_VERSION = 1;
|
|
4545
|
+
var TURNSTILE_FRAME_READY_TIMEOUT_MS = 1e4;
|
|
4546
|
+
function readFrameMessage(value, nonce) {
|
|
4547
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
4548
|
+
const record2 = value;
|
|
4549
|
+
if (record2.source !== TURNSTILE_FRAME_MESSAGE_SOURCE || record2.version !== TURNSTILE_FRAME_MESSAGE_VERSION || record2.nonce !== nonce || !["ready", "visible", "hidden", "success", "expired", "timeout", "error"].includes(String(record2.type))) return null;
|
|
4550
|
+
if (record2.type === "success" && (typeof record2.token !== "string" || !record2.token.trim())) {
|
|
4551
|
+
return null;
|
|
4552
|
+
}
|
|
4553
|
+
return {
|
|
4554
|
+
type: record2.type,
|
|
4555
|
+
...typeof record2.token === "string" ? { token: record2.token.trim() } : {}
|
|
4556
|
+
};
|
|
4557
|
+
}
|
|
4558
|
+
var AUTO_CHALLENGE_INVISIBLE_TIMEOUT_MS = 3e4;
|
|
4559
|
+
async function resolveCheckoutChallengeProof(challenge) {
|
|
4560
|
+
if (typeof document === "undefined" || challenge.provider !== "turnstile" || !challenge.siteKey.trim()) {
|
|
4561
|
+
return null;
|
|
4562
|
+
}
|
|
4563
|
+
const host = document.createElement("div");
|
|
4564
|
+
host.setAttribute("data-shoppex-checkout-challenge", "");
|
|
4565
|
+
host.style.position = "fixed";
|
|
4566
|
+
host.style.inset = "0";
|
|
4567
|
+
host.style.display = "none";
|
|
4568
|
+
host.style.alignItems = "center";
|
|
4569
|
+
host.style.justifyContent = "center";
|
|
4570
|
+
host.style.background = "rgba(0, 0, 0, 0.55)";
|
|
4571
|
+
host.style.zIndex = "2147483646";
|
|
4572
|
+
const card = document.createElement("div");
|
|
4573
|
+
card.style.width = "min(340px, 90vw)";
|
|
4574
|
+
card.style.background = "#ffffff";
|
|
4575
|
+
card.style.borderRadius = "12px";
|
|
4576
|
+
card.style.padding = "16px";
|
|
4577
|
+
card.style.boxShadow = "0 12px 40px rgba(0, 0, 0, 0.35)";
|
|
4578
|
+
host.appendChild(card);
|
|
4579
|
+
document.body.appendChild(host);
|
|
4580
|
+
return new Promise((resolve) => {
|
|
4581
|
+
let settled = false;
|
|
4582
|
+
let timeoutId = null;
|
|
4583
|
+
let frame = null;
|
|
4584
|
+
const finish = (token) => {
|
|
4585
|
+
if (settled) return;
|
|
4586
|
+
settled = true;
|
|
4587
|
+
if (timeoutId !== null) window.clearTimeout(timeoutId);
|
|
4588
|
+
frame?.dispose();
|
|
4589
|
+
host.remove();
|
|
4590
|
+
resolve(token);
|
|
4591
|
+
};
|
|
4592
|
+
const armTimeout = () => {
|
|
4593
|
+
timeoutId = window.setTimeout(() => finish(null), AUTO_CHALLENGE_INVISIBLE_TIMEOUT_MS);
|
|
4594
|
+
};
|
|
4595
|
+
try {
|
|
4596
|
+
frame = mountCheckoutChallenge(card, challenge, {
|
|
4597
|
+
onSuccess: (token) => finish(token),
|
|
4598
|
+
// `refresh-expired: auto` renews expired runs on its own; the
|
|
4599
|
+
// invisible-run timeout stays the bound.
|
|
4600
|
+
onExpired: () => {
|
|
4601
|
+
},
|
|
4602
|
+
onUnavailable: () => finish(null),
|
|
4603
|
+
onVisibilityChange: (visible) => {
|
|
4604
|
+
host.style.display = visible ? "flex" : "none";
|
|
4605
|
+
if (visible) {
|
|
4606
|
+
if (timeoutId !== null) {
|
|
4607
|
+
window.clearTimeout(timeoutId);
|
|
4608
|
+
timeoutId = null;
|
|
4609
|
+
}
|
|
4610
|
+
} else if (timeoutId === null && !settled) {
|
|
4611
|
+
armTimeout();
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
});
|
|
4615
|
+
} catch {
|
|
4616
|
+
host.remove();
|
|
4617
|
+
resolve(null);
|
|
4618
|
+
return;
|
|
4619
|
+
}
|
|
4620
|
+
armTimeout();
|
|
4621
|
+
});
|
|
4622
|
+
}
|
|
4623
|
+
function mountCheckoutChallenge(container, challenge, callbacks) {
|
|
4624
|
+
if (challenge.provider !== "turnstile" || !challenge.siteKey.trim()) {
|
|
4625
|
+
throw new Error("Checkout challenge is invalid.");
|
|
4626
|
+
}
|
|
4627
|
+
const win = container.ownerDocument.defaultView;
|
|
4628
|
+
if (!win) throw new Error("Checkout challenge requires a browser document.");
|
|
4629
|
+
const checkoutBaseUrl = getConfig().checkoutBaseUrl;
|
|
4630
|
+
const frameUrl = new URL("/turnstile", checkoutBaseUrl);
|
|
4631
|
+
if (frameUrl.protocol !== "https:" && frameUrl.protocol !== "http:") {
|
|
4632
|
+
throw new Error("Checkout base URL must use http or https.");
|
|
4633
|
+
}
|
|
4634
|
+
const nonce = win.crypto.randomUUID();
|
|
4635
|
+
frameUrl.searchParams.set("site_key", challenge.siteKey.trim());
|
|
4636
|
+
frameUrl.searchParams.set("nonce", nonce);
|
|
4637
|
+
const frame = container.ownerDocument.createElement("iframe");
|
|
4638
|
+
frame.src = frameUrl.toString();
|
|
4639
|
+
frame.title = "Checkout verification";
|
|
4640
|
+
frame.referrerPolicy = "no-referrer";
|
|
4641
|
+
frame.style.border = "0";
|
|
4642
|
+
frame.style.width = "100%";
|
|
4643
|
+
frame.style.height = "0";
|
|
4644
|
+
let disposed = false;
|
|
4645
|
+
let ready = false;
|
|
4646
|
+
const readyTimeout = win.setTimeout(() => {
|
|
4647
|
+
if (!disposed && !ready) callbacks.onUnavailable?.();
|
|
4648
|
+
}, TURNSTILE_FRAME_READY_TIMEOUT_MS);
|
|
4649
|
+
const onMessage = (event) => {
|
|
4650
|
+
if (disposed || event.origin !== frameUrl.origin || event.source !== frame.contentWindow) return;
|
|
4651
|
+
const message = readFrameMessage(event.data, nonce);
|
|
4652
|
+
if (!message) return;
|
|
4653
|
+
ready = true;
|
|
4654
|
+
win.clearTimeout(readyTimeout);
|
|
4655
|
+
if (message.type === "visible") {
|
|
4656
|
+
frame.style.height = "72px";
|
|
4657
|
+
callbacks.onVisibilityChange?.(true);
|
|
4658
|
+
}
|
|
4659
|
+
if (message.type === "hidden") {
|
|
4660
|
+
frame.style.height = "0";
|
|
4661
|
+
callbacks.onVisibilityChange?.(false);
|
|
4662
|
+
}
|
|
4663
|
+
if (message.type === "success") {
|
|
4664
|
+
frame.style.height = "0";
|
|
4665
|
+
callbacks.onVisibilityChange?.(false);
|
|
4666
|
+
callbacks.onSuccess(message.token);
|
|
4667
|
+
}
|
|
4668
|
+
if (message.type === "expired" || message.type === "timeout") callbacks.onExpired?.();
|
|
4669
|
+
if (message.type === "error") callbacks.onUnavailable?.();
|
|
4670
|
+
};
|
|
4671
|
+
const onFrameError = () => callbacks.onUnavailable?.();
|
|
4672
|
+
win.addEventListener("message", onMessage);
|
|
4673
|
+
frame.addEventListener("error", onFrameError, { once: true });
|
|
4674
|
+
container.appendChild(frame);
|
|
4675
|
+
return {
|
|
4676
|
+
element: frame,
|
|
4677
|
+
dispose() {
|
|
4678
|
+
if (disposed) return;
|
|
4679
|
+
disposed = true;
|
|
4680
|
+
win.clearTimeout(readyTimeout);
|
|
4681
|
+
win.removeEventListener("message", onMessage);
|
|
4682
|
+
frame.removeEventListener("error", onFrameError);
|
|
4683
|
+
frame.remove();
|
|
4684
|
+
}
|
|
4685
|
+
};
|
|
4686
|
+
}
|
|
4687
|
+
|
|
4542
4688
|
// ../sdk/src/modules/checkout.ts
|
|
4543
4689
|
var CheckoutCreateError = class _CheckoutCreateError extends Error {
|
|
4544
4690
|
constructor(message, options = {}) {
|
|
@@ -4781,6 +4927,16 @@ function mapCartItemsForApi(items) {
|
|
|
4781
4927
|
}
|
|
4782
4928
|
async function checkout(couponOrOptions, options) {
|
|
4783
4929
|
const resolvedOptions = resolveCheckoutOptions(couponOrOptions, options);
|
|
4930
|
+
const firstAttempt = await performCheckout(resolvedOptions);
|
|
4931
|
+
if (!firstAttempt.success && firstAttempt.challenge?.provider === "turnstile" && !resolvedOptions.turnstileToken && typeof document !== "undefined") {
|
|
4932
|
+
const proof = await resolveCheckoutChallengeProof(firstAttempt.challenge);
|
|
4933
|
+
if (proof) {
|
|
4934
|
+
return performCheckout({ ...resolvedOptions, turnstileToken: proof });
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
return firstAttempt;
|
|
4938
|
+
}
|
|
4939
|
+
async function performCheckout(resolvedOptions) {
|
|
4784
4940
|
const { autoRedirect = true, email: email2 } = resolvedOptions;
|
|
4785
4941
|
const checkoutCodes = resolveCheckoutCodes(resolvedOptions);
|
|
4786
4942
|
const normalizedCoupon = checkoutCodes.coupon;
|
|
@@ -4898,6 +5054,19 @@ async function checkout(couponOrOptions, options) {
|
|
|
4898
5054
|
}
|
|
4899
5055
|
async function buildCheckoutUrl(couponOrOptions, options) {
|
|
4900
5056
|
const resolvedOptions = resolveCheckoutOptions(couponOrOptions, options);
|
|
5057
|
+
try {
|
|
5058
|
+
return await performBuildCheckoutUrl(resolvedOptions);
|
|
5059
|
+
} catch (error) {
|
|
5060
|
+
if (error instanceof CheckoutCreateError && error.challenge?.provider === "turnstile" && !resolvedOptions.turnstileToken && typeof document !== "undefined") {
|
|
5061
|
+
const proof = await resolveCheckoutChallengeProof(error.challenge);
|
|
5062
|
+
if (proof) {
|
|
5063
|
+
return performBuildCheckoutUrl({ ...resolvedOptions, turnstileToken: proof });
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
throw error;
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
async function performBuildCheckoutUrl(resolvedOptions) {
|
|
4901
5070
|
const { email: email2 } = resolvedOptions;
|
|
4902
5071
|
const checkoutCodes = resolveCheckoutCodes(resolvedOptions);
|
|
4903
5072
|
const normalizedCoupon = checkoutCodes.coupon;
|
|
@@ -4986,80 +5155,6 @@ function buildCheckoutUrlSync() {
|
|
|
4986
5155
|
throw new Error("buildCheckoutUrlSync is deprecated. Use buildCheckoutUrl (async) instead.");
|
|
4987
5156
|
}
|
|
4988
5157
|
|
|
4989
|
-
// ../sdk/src/modules/checkout-challenge.ts
|
|
4990
|
-
var TURNSTILE_FRAME_MESSAGE_SOURCE = "shoppex-turnstile";
|
|
4991
|
-
var TURNSTILE_FRAME_MESSAGE_VERSION = 1;
|
|
4992
|
-
var TURNSTILE_FRAME_READY_TIMEOUT_MS = 1e4;
|
|
4993
|
-
function readFrameMessage(value, nonce) {
|
|
4994
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
4995
|
-
const record2 = value;
|
|
4996
|
-
if (record2.source !== TURNSTILE_FRAME_MESSAGE_SOURCE || record2.version !== TURNSTILE_FRAME_MESSAGE_VERSION || record2.nonce !== nonce || !["ready", "visible", "hidden", "success", "expired", "timeout", "error"].includes(String(record2.type))) return null;
|
|
4997
|
-
if (record2.type === "success" && (typeof record2.token !== "string" || !record2.token.trim())) {
|
|
4998
|
-
return null;
|
|
4999
|
-
}
|
|
5000
|
-
return {
|
|
5001
|
-
type: record2.type,
|
|
5002
|
-
...typeof record2.token === "string" ? { token: record2.token.trim() } : {}
|
|
5003
|
-
};
|
|
5004
|
-
}
|
|
5005
|
-
function mountCheckoutChallenge(container, challenge, callbacks) {
|
|
5006
|
-
if (challenge.provider !== "turnstile" || !challenge.siteKey.trim()) {
|
|
5007
|
-
throw new Error("Checkout challenge is invalid.");
|
|
5008
|
-
}
|
|
5009
|
-
const win = container.ownerDocument.defaultView;
|
|
5010
|
-
if (!win) throw new Error("Checkout challenge requires a browser document.");
|
|
5011
|
-
const checkoutBaseUrl = getConfig().checkoutBaseUrl;
|
|
5012
|
-
const frameUrl = new URL("/turnstile", checkoutBaseUrl);
|
|
5013
|
-
if (frameUrl.protocol !== "https:" && frameUrl.protocol !== "http:") {
|
|
5014
|
-
throw new Error("Checkout base URL must use http or https.");
|
|
5015
|
-
}
|
|
5016
|
-
const nonce = win.crypto.randomUUID();
|
|
5017
|
-
frameUrl.searchParams.set("site_key", challenge.siteKey.trim());
|
|
5018
|
-
frameUrl.searchParams.set("nonce", nonce);
|
|
5019
|
-
const frame = container.ownerDocument.createElement("iframe");
|
|
5020
|
-
frame.src = frameUrl.toString();
|
|
5021
|
-
frame.title = "Checkout verification";
|
|
5022
|
-
frame.referrerPolicy = "no-referrer";
|
|
5023
|
-
frame.style.border = "0";
|
|
5024
|
-
frame.style.width = "100%";
|
|
5025
|
-
frame.style.height = "0";
|
|
5026
|
-
let disposed = false;
|
|
5027
|
-
let ready = false;
|
|
5028
|
-
const readyTimeout = win.setTimeout(() => {
|
|
5029
|
-
if (!disposed && !ready) callbacks.onUnavailable?.();
|
|
5030
|
-
}, TURNSTILE_FRAME_READY_TIMEOUT_MS);
|
|
5031
|
-
const onMessage = (event) => {
|
|
5032
|
-
if (disposed || event.origin !== frameUrl.origin || event.source !== frame.contentWindow) return;
|
|
5033
|
-
const message = readFrameMessage(event.data, nonce);
|
|
5034
|
-
if (!message) return;
|
|
5035
|
-
ready = true;
|
|
5036
|
-
win.clearTimeout(readyTimeout);
|
|
5037
|
-
if (message.type === "visible") frame.style.height = "72px";
|
|
5038
|
-
if (message.type === "hidden") frame.style.height = "0";
|
|
5039
|
-
if (message.type === "success") {
|
|
5040
|
-
frame.style.height = "0";
|
|
5041
|
-
callbacks.onSuccess(message.token);
|
|
5042
|
-
}
|
|
5043
|
-
if (message.type === "expired" || message.type === "timeout") callbacks.onExpired?.();
|
|
5044
|
-
if (message.type === "error") callbacks.onUnavailable?.();
|
|
5045
|
-
};
|
|
5046
|
-
const onFrameError = () => callbacks.onUnavailable?.();
|
|
5047
|
-
win.addEventListener("message", onMessage);
|
|
5048
|
-
frame.addEventListener("error", onFrameError, { once: true });
|
|
5049
|
-
container.appendChild(frame);
|
|
5050
|
-
return {
|
|
5051
|
-
element: frame,
|
|
5052
|
-
dispose() {
|
|
5053
|
-
if (disposed) return;
|
|
5054
|
-
disposed = true;
|
|
5055
|
-
win.clearTimeout(readyTimeout);
|
|
5056
|
-
win.removeEventListener("message", onMessage);
|
|
5057
|
-
frame.removeEventListener("error", onFrameError);
|
|
5058
|
-
frame.remove();
|
|
5059
|
-
}
|
|
5060
|
-
};
|
|
5061
|
-
}
|
|
5062
|
-
|
|
5063
5158
|
// ../sdk/src/modules/coupons.ts
|
|
5064
5159
|
async function resolveShopId() {
|
|
5065
5160
|
const cachedShopId2 = getShopId();
|