@cimplify/sdk 0.16.2 → 0.17.0
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/dist/advanced.d.mts +2 -2
- package/dist/advanced.d.ts +2 -2
- package/dist/advanced.js +90 -2906
- package/dist/advanced.mjs +3 -2886
- package/dist/chunk-FLPI2VVJ.js +72 -0
- package/dist/chunk-G6KNFYJD.mjs +687 -0
- package/dist/chunk-GFKBH7R2.mjs +2759 -0
- package/dist/chunk-J65U5SED.js +714 -0
- package/dist/chunk-LM3CJ4L7.mjs +69 -0
- package/dist/chunk-OITG2SEN.js +692 -0
- package/dist/chunk-OVM5YWM5.mjs +674 -0
- package/dist/chunk-REPTMTQ4.js +2810 -0
- package/dist/chunk-S6TU55KP.js +170 -0
- package/dist/chunk-WDIMISRC.mjs +166 -0
- package/dist/{client-DTTtSSmQ.d.ts → client-BaolRdB4.d.mts} +3 -3
- package/dist/{client-Cb7r8u6t.d.mts → client-C59R79gx.d.ts} +3 -3
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +401 -4412
- package/dist/index.mjs +5 -4317
- package/dist/{payment-Dvd7nRmk.d.mts → payment-C6ULw3K-.d.mts} +1 -1
- package/dist/{payment-Dvd7nRmk.d.ts → payment-C6ULw3K-.d.ts} +1 -1
- package/dist/{price-DrfhwHpw.d.ts → price-Bd6JBV8W.d.ts} +1 -1
- package/dist/{price-BDZNXiLS.d.mts → price-CbR6DCt0.d.mts} +1 -1
- package/dist/react.d.mts +12 -7
- package/dist/react.d.ts +12 -7
- package/dist/react.js +377 -3961
- package/dist/react.mjs +326 -3913
- package/dist/utils.d.mts +2 -2
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +122 -720
- package/dist/utils.mjs +2 -694
- package/package.json +1 -1
- package/registry/add-on-selector.json +1 -1
- package/registry/checkout-page.json +1 -1
- package/registry/composite-selector.json +1 -1
- package/registry/product-grid.json +1 -1
package/dist/react.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
var chunkOITG2SEN_js = require('./chunk-OITG2SEN.js');
|
|
5
|
+
var chunkREPTMTQ4_js = require('./chunk-REPTMTQ4.js');
|
|
6
|
+
var chunkJ65U5SED_js = require('./chunk-J65U5SED.js');
|
|
4
7
|
var React3 = require('react');
|
|
5
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
9
|
var numberField = require('@base-ui/react/number-field');
|
|
@@ -19,8 +22,6 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
19
22
|
|
|
20
23
|
var React3__default = /*#__PURE__*/_interopDefault(React3);
|
|
21
24
|
|
|
22
|
-
// src/ads/index.tsx
|
|
23
|
-
|
|
24
25
|
// src/ads/identity.ts
|
|
25
26
|
var COOKIE_NAME = "_cimplify_uid";
|
|
26
27
|
var COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
@@ -284,3752 +285,98 @@ function Ad({
|
|
|
284
285
|
onImpression?.(ad.id);
|
|
285
286
|
}
|
|
286
287
|
},
|
|
287
|
-
{ threshold: 0.5 }
|
|
288
|
-
);
|
|
289
|
-
if (containerRef.current) {
|
|
290
|
-
observer.observe(containerRef.current);
|
|
291
|
-
}
|
|
292
|
-
return () => observer.disconnect();
|
|
293
|
-
}, [ad, siteId, slot, onImpression, identity, apiBase]);
|
|
294
|
-
if (isLoading) {
|
|
295
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
296
|
-
"div",
|
|
297
|
-
{
|
|
298
|
-
className: `cimplify-ad cimplify-ad--loading ${className || ""}`,
|
|
299
|
-
style,
|
|
300
|
-
"aria-hidden": "true"
|
|
301
|
-
}
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
if (!config?.enabled) return null;
|
|
305
|
-
if (error) return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
|
|
306
|
-
if (!ad) return null;
|
|
307
|
-
const handleClick = () => {
|
|
308
|
-
onClick?.(ad.id);
|
|
309
|
-
};
|
|
310
|
-
const positionStyles = position === "fixed" ? { position: "fixed", bottom: 0, left: 0, right: 0, zIndex: 9999 } : position === "sticky" ? { position: "sticky", top: 20 } : {};
|
|
311
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
312
|
-
"div",
|
|
313
|
-
{
|
|
314
|
-
ref: containerRef,
|
|
315
|
-
id: `cimplify-ad-${ad.id}`,
|
|
316
|
-
className: `cimplify-ad cimplify-ad--${slot} cimplify-ad--${position} cimplify-ad--${config.theme} ${className || ""}`,
|
|
317
|
-
style: { ...positionStyles, ...style },
|
|
318
|
-
onClick: handleClick,
|
|
319
|
-
dangerouslySetInnerHTML: { __html: ad.html }
|
|
320
|
-
}
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// src/utils/cart-transform.ts
|
|
325
|
-
function getSelections(item) {
|
|
326
|
-
if (item.bundle_resolved?.selections?.length) {
|
|
327
|
-
return item.bundle_resolved.selections.map((s) => ({
|
|
328
|
-
name: s.product_name || s.component_id,
|
|
329
|
-
quantity: s.quantity,
|
|
330
|
-
variant_name: s.variant_name
|
|
331
|
-
}));
|
|
332
|
-
}
|
|
333
|
-
if (item.composite_resolved?.selections?.length) {
|
|
334
|
-
return item.composite_resolved.selections.map((s) => ({
|
|
335
|
-
name: s.component_name || s.component_id,
|
|
336
|
-
quantity: s.quantity
|
|
337
|
-
}));
|
|
338
|
-
}
|
|
339
|
-
return void 0;
|
|
340
|
-
}
|
|
341
|
-
function mapItem(item, fx) {
|
|
342
|
-
const amt = (value) => fx ? String(fx.convertPrice(value)) : String(value);
|
|
343
|
-
const result = {
|
|
344
|
-
name: item.name,
|
|
345
|
-
quantity: item.quantity,
|
|
346
|
-
unit_price: amt(item.base_price),
|
|
347
|
-
total_price: amt(item.total_price),
|
|
348
|
-
line_type: item.line_type
|
|
349
|
-
};
|
|
350
|
-
if (item.image_url) result.image_url = item.image_url;
|
|
351
|
-
const variantName = item.variant_info?.name || item.variant_name || void 0;
|
|
352
|
-
if (variantName) result.variant_name = variantName;
|
|
353
|
-
if (item.scheduled_start) result.scheduled_start = item.scheduled_start;
|
|
354
|
-
if (item.scheduled_end) result.scheduled_end = item.scheduled_end;
|
|
355
|
-
const selections = getSelections(item);
|
|
356
|
-
if (selections) result.selections = selections;
|
|
357
|
-
if (item.add_on_options?.length) {
|
|
358
|
-
result.add_ons = item.add_on_options.map((opt) => ({
|
|
359
|
-
name: opt.name,
|
|
360
|
-
price: amt(opt.price ?? "0")
|
|
361
|
-
}));
|
|
362
|
-
}
|
|
363
|
-
if (item.special_instructions) {
|
|
364
|
-
result.special_instructions = item.special_instructions;
|
|
365
|
-
}
|
|
366
|
-
return result;
|
|
367
|
-
}
|
|
368
|
-
function transformToCheckoutCart(cart, fx) {
|
|
369
|
-
const amt = (value) => fx ? String(fx.convertPrice(value)) : String(value);
|
|
370
|
-
return {
|
|
371
|
-
items: cart.items.map((item) => mapItem(item, fx)),
|
|
372
|
-
subtotal: amt(cart.pricing.subtotal),
|
|
373
|
-
tax_amount: amt(cart.pricing.tax_amount),
|
|
374
|
-
total_discounts: amt(cart.pricing.total_discounts),
|
|
375
|
-
service_charge: amt(cart.pricing.service_charge),
|
|
376
|
-
total: amt(cart.pricing.total_price),
|
|
377
|
-
currency: fx?.displayCurrency ?? cart.pricing.currency
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// src/types/common.ts
|
|
382
|
-
function money(value) {
|
|
383
|
-
return typeof value === "string" ? parseFloat(value) || 0 : value;
|
|
384
|
-
}
|
|
385
|
-
function moneyFromNumber(value) {
|
|
386
|
-
return value;
|
|
387
|
-
}
|
|
388
|
-
var SUPPORTED_CURRENCY_CODES = /* @__PURE__ */ new Set([
|
|
389
|
-
"USD",
|
|
390
|
-
"EUR",
|
|
391
|
-
"GBP",
|
|
392
|
-
"JPY",
|
|
393
|
-
"CNY",
|
|
394
|
-
"CHF",
|
|
395
|
-
"CAD",
|
|
396
|
-
"AUD",
|
|
397
|
-
"GHS",
|
|
398
|
-
"NGN",
|
|
399
|
-
"KES",
|
|
400
|
-
"ZAR",
|
|
401
|
-
"XOF",
|
|
402
|
-
"XAF",
|
|
403
|
-
"EGP",
|
|
404
|
-
"TZS",
|
|
405
|
-
"UGX",
|
|
406
|
-
"RWF",
|
|
407
|
-
"ETB",
|
|
408
|
-
"ZMW",
|
|
409
|
-
"BWP",
|
|
410
|
-
"MUR",
|
|
411
|
-
"NAD",
|
|
412
|
-
"MWK",
|
|
413
|
-
"AOA",
|
|
414
|
-
"CDF",
|
|
415
|
-
"GMD",
|
|
416
|
-
"GNF",
|
|
417
|
-
"LRD",
|
|
418
|
-
"SLL",
|
|
419
|
-
"MZN",
|
|
420
|
-
"BIF",
|
|
421
|
-
"INR",
|
|
422
|
-
"BRL",
|
|
423
|
-
"MXN",
|
|
424
|
-
"KRW",
|
|
425
|
-
"TRY",
|
|
426
|
-
"THB",
|
|
427
|
-
"MYR",
|
|
428
|
-
"PHP",
|
|
429
|
-
"IDR",
|
|
430
|
-
"VND",
|
|
431
|
-
"SGD",
|
|
432
|
-
"HKD",
|
|
433
|
-
"TWD",
|
|
434
|
-
"AED",
|
|
435
|
-
"SAR",
|
|
436
|
-
"ILS"
|
|
437
|
-
]);
|
|
438
|
-
function isSupportedCurrency(code) {
|
|
439
|
-
return SUPPORTED_CURRENCY_CODES.has(code);
|
|
440
|
-
}
|
|
441
|
-
function currencyCode(value) {
|
|
442
|
-
return value;
|
|
443
|
-
}
|
|
444
|
-
var ErrorCode = {
|
|
445
|
-
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
|
446
|
-
NETWORK_ERROR: "NETWORK_ERROR",
|
|
447
|
-
TIMEOUT: "TIMEOUT",
|
|
448
|
-
UNAUTHORIZED: "UNAUTHORIZED",
|
|
449
|
-
NOT_FOUND: "NOT_FOUND"};
|
|
450
|
-
var DOCS_ERROR_BASE_URL = "https://docs.cimplify.io/reference/error-codes";
|
|
451
|
-
function docsUrlForCode(code) {
|
|
452
|
-
return `${DOCS_ERROR_BASE_URL}#${code.toLowerCase().replace(/_/g, "-")}`;
|
|
453
|
-
}
|
|
454
|
-
var ERROR_SUGGESTIONS = {
|
|
455
|
-
UNKNOWN_ERROR: "An unexpected error occurred. Capture the request/response payload and retry with exponential backoff.",
|
|
456
|
-
NETWORK_ERROR: "Check the shopper's connection and retry. If this persists, inspect CORS, DNS, and API reachability.",
|
|
457
|
-
TIMEOUT: "The request exceeded the timeout. Retry once, then poll order status before charging again.",
|
|
458
|
-
UNAUTHORIZED: "Authentication is missing or expired. Ensure a valid access token is set and refresh the session if needed.",
|
|
459
|
-
FORBIDDEN: "The key/session lacks permission for this resource. Verify business ownership and API key scope.",
|
|
460
|
-
NOT_FOUND: "The requested resource does not exist or is not visible in this environment.",
|
|
461
|
-
VALIDATION_ERROR: "One or more fields are invalid. Validate required fields and enum values before retrying.",
|
|
462
|
-
CART_EMPTY: "The cart has no items. Redirect back to menu/catalogue and require at least one line item.",
|
|
463
|
-
CART_EXPIRED: "This cart is no longer active. Recreate a new cart and re-add shopper selections.",
|
|
464
|
-
CART_NOT_FOUND: "Cart could not be located. It may have expired or belongs to a different key/location.",
|
|
465
|
-
ITEM_UNAVAILABLE: "The selected item is unavailable at this location/time. Prompt the shopper to pick an alternative.",
|
|
466
|
-
VARIANT_NOT_FOUND: "The requested variant no longer exists. Refresh product data and require re-selection.",
|
|
467
|
-
VARIANT_OUT_OF_STOCK: "The selected variant is out of stock. Show in-stock variants and block checkout for this line.",
|
|
468
|
-
ADDON_REQUIRED: "A required add-on is missing. Ensure required modifier groups are completed before add-to-cart.",
|
|
469
|
-
ADDON_MAX_EXCEEDED: "Too many add-ons were selected. Enforce max selections client-side before submission.",
|
|
470
|
-
CHECKOUT_VALIDATION_FAILED: "Checkout payload failed validation. Verify customer, order type, and address fields are complete.",
|
|
471
|
-
DELIVERY_ADDRESS_REQUIRED: "Delivery orders require an address. Collect and pass address info before processing checkout.",
|
|
472
|
-
CUSTOMER_INFO_REQUIRED: "Customer details are required. Ensure name/email/phone are available before checkout.",
|
|
473
|
-
QUOTE_NOT_FOUND: "Quote could not be found. Refresh pricing and create a new quote before checkout.",
|
|
474
|
-
QUOTE_EXPIRED: "Quote has expired. Re-fetch pricing to generate a new quote with a valid expiry window.",
|
|
475
|
-
QUOTE_CONSUMED: "Quote has already been used. Request a fresh quote to prevent duplicate checkout attempts.",
|
|
476
|
-
QUOTE_STORAGE_UNAVAILABLE: "Quote storage is temporarily unavailable. Retry shortly and avoid charging until quote fetch succeeds.",
|
|
477
|
-
PAYMENT_FAILED: "Payment provider rejected or failed processing. Show retry/change-method options to the shopper.",
|
|
478
|
-
PAYMENT_CANCELLED: "Payment was cancelled by the shopper or provider flow. Allow a safe retry path.",
|
|
479
|
-
INSUFFICIENT_FUNDS: "Payment method has insufficient funds. Prompt shopper to use another method.",
|
|
480
|
-
CARD_DECLINED: "Card was declined. Ask shopper to retry or switch payment method.",
|
|
481
|
-
INVALID_OTP: "Authorization code is invalid. Let shopper re-enter OTP/PIN and retry.",
|
|
482
|
-
OTP_EXPIRED: "Authorization code expired. Request a new OTP and re-submit authorization.",
|
|
483
|
-
AUTHORIZATION_FAILED: "Additional payment authorization failed. Retry authorization or change payment method.",
|
|
484
|
-
PAYMENT_ACTION_NOT_COMPLETED: "Required payment action was not completed. Resume provider flow and poll for status.",
|
|
485
|
-
SLOT_UNAVAILABLE: "Selected schedule slot is unavailable. Refresh available slots and ask shopper to reselect.",
|
|
486
|
-
BOOKING_CONFLICT: "The requested booking conflicts with an existing reservation. Pick another slot/resource.",
|
|
487
|
-
SERVICE_NOT_FOUND: "Requested service no longer exists. Refresh service catalogue and retry selection.",
|
|
488
|
-
OUT_OF_STOCK: "Inventory is depleted for this item. Remove it or reduce quantity before checkout.",
|
|
489
|
-
INSUFFICIENT_QUANTITY: "Requested quantity exceeds available inventory. Reduce quantity and retry.",
|
|
490
|
-
BUSINESS_ID_REQUIRED: "Business context could not be resolved. Verify the public key and business bootstrap call.",
|
|
491
|
-
INVALID_CART: "Cart is invalid for checkout. Sync cart state, ensure items exist, then retry.",
|
|
492
|
-
ORDER_TYPE_REQUIRED: "Order type is required. Provide one of delivery, pickup, or dine_in before checkout.",
|
|
493
|
-
NO_PAYMENT_ELEMENT: "PaymentElement is required for processCheckout(). Mount it before triggering checkout.",
|
|
494
|
-
PAYMENT_NOT_MOUNTED: "PaymentElement iframe is not mounted. Mount it in the DOM before processCheckout().",
|
|
495
|
-
AUTH_INCOMPLETE: "AuthElement has not completed authentication. Wait for AUTHENTICATED before checkout.",
|
|
496
|
-
AUTH_LOST: "Session was cleared during checkout. Re-authenticate and restart checkout safely.",
|
|
497
|
-
ALREADY_PROCESSING: "Checkout is already in progress. Disable duplicate submits until completion.",
|
|
498
|
-
CHECKOUT_NOT_READY: "Checkout elements are still initializing. Wait for readiness before submit.",
|
|
499
|
-
CANCELLED: "Checkout was cancelled. Preserve cart state and allow shopper to retry.",
|
|
500
|
-
REQUEST_TIMEOUT: "Provider call timed out. Poll payment/order status before issuing another charge attempt.",
|
|
501
|
-
POPUP_BLOCKED: "Browser blocked provider popup. Ask shopper to enable popups and retry.",
|
|
502
|
-
FX_QUOTE_FAILED: "Failed to lock FX quote. Retry currency quote or fallback to base currency."
|
|
503
|
-
};
|
|
504
|
-
var ERROR_HINTS = Object.fromEntries(
|
|
505
|
-
Object.entries(ERROR_SUGGESTIONS).map(([code, suggestion]) => [
|
|
506
|
-
code,
|
|
507
|
-
{
|
|
508
|
-
docs_url: docsUrlForCode(code),
|
|
509
|
-
suggestion
|
|
510
|
-
}
|
|
511
|
-
])
|
|
512
|
-
);
|
|
513
|
-
var CimplifyError = class extends Error {
|
|
514
|
-
constructor(code, message, retryable = false, docs_url, suggestion) {
|
|
515
|
-
super(message);
|
|
516
|
-
this.code = code;
|
|
517
|
-
this.retryable = retryable;
|
|
518
|
-
this.docs_url = docs_url;
|
|
519
|
-
this.suggestion = suggestion;
|
|
520
|
-
this.name = "CimplifyError";
|
|
521
|
-
}
|
|
522
|
-
get userMessage() {
|
|
523
|
-
return this.message;
|
|
524
|
-
}
|
|
525
|
-
};
|
|
526
|
-
function getErrorHint(code) {
|
|
527
|
-
return ERROR_HINTS[code];
|
|
528
|
-
}
|
|
529
|
-
function enrichError(error, options = {}) {
|
|
530
|
-
const hint = getErrorHint(error.code);
|
|
531
|
-
if (hint) {
|
|
532
|
-
if (!error.docs_url) {
|
|
533
|
-
error.docs_url = hint.docs_url;
|
|
534
|
-
}
|
|
535
|
-
if (!error.suggestion) {
|
|
536
|
-
error.suggestion = hint.suggestion;
|
|
537
|
-
}
|
|
538
|
-
} else if (!error.docs_url) {
|
|
539
|
-
error.docs_url = docsUrlForCode(error.code || ErrorCode.UNKNOWN_ERROR);
|
|
540
|
-
}
|
|
541
|
-
if (options.isTestMode && !error.message.includes("pk_test_")) {
|
|
542
|
-
error.message = `${error.message}
|
|
543
|
-
|
|
544
|
-
\u2139 Your API key is a test-mode key (pk_test_...). Verify test data/session before retrying.`;
|
|
545
|
-
}
|
|
546
|
-
return error;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// src/types/result.ts
|
|
550
|
-
function ok(value) {
|
|
551
|
-
return { ok: true, value };
|
|
552
|
-
}
|
|
553
|
-
function err(error) {
|
|
554
|
-
return { ok: false, error };
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// src/internal/safe.ts
|
|
558
|
-
function toCimplifyError(error) {
|
|
559
|
-
if (error instanceof CimplifyError) return enrichError(error);
|
|
560
|
-
if (error instanceof Error) {
|
|
561
|
-
return enrichError(new CimplifyError(ErrorCode.UNKNOWN_ERROR, error.message, false));
|
|
562
|
-
}
|
|
563
|
-
return enrichError(new CimplifyError(ErrorCode.UNKNOWN_ERROR, String(error), false));
|
|
564
|
-
}
|
|
565
|
-
async function safe(promise) {
|
|
566
|
-
try {
|
|
567
|
-
return ok(await promise);
|
|
568
|
-
} catch (error) {
|
|
569
|
-
return err(toCimplifyError(error));
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// src/catalogue.ts
|
|
574
|
-
function withQuery(path, params) {
|
|
575
|
-
const query = new URLSearchParams();
|
|
576
|
-
for (const [key, value] of Object.entries(params)) {
|
|
577
|
-
if (value === void 0) continue;
|
|
578
|
-
query.set(key, String(value));
|
|
579
|
-
}
|
|
580
|
-
const queryString = query.toString();
|
|
581
|
-
return queryString ? `${path}?${queryString}` : path;
|
|
582
|
-
}
|
|
583
|
-
function isRecord(value) {
|
|
584
|
-
return typeof value === "object" && value !== null;
|
|
585
|
-
}
|
|
586
|
-
function readFinalPrice(value) {
|
|
587
|
-
if (!isRecord(value)) return void 0;
|
|
588
|
-
const finalPrice = value.final_price;
|
|
589
|
-
if (typeof finalPrice === "string" || typeof finalPrice === "number") {
|
|
590
|
-
return finalPrice;
|
|
591
|
-
}
|
|
592
|
-
return void 0;
|
|
593
|
-
}
|
|
594
|
-
function normalizeAddOnPayload(addOn) {
|
|
595
|
-
if (!isRecord(addOn)) return addOn;
|
|
596
|
-
const normalizedAddOn = { ...addOn };
|
|
597
|
-
const options = normalizedAddOn["options"];
|
|
598
|
-
if (!Array.isArray(options)) return normalizedAddOn;
|
|
599
|
-
normalizedAddOn["options"] = options.map((option) => {
|
|
600
|
-
if (!isRecord(option)) return option;
|
|
601
|
-
const normalizedOption = { ...option };
|
|
602
|
-
const optionPrice = normalizedOption["default_price"];
|
|
603
|
-
if (optionPrice === void 0 || optionPrice === null || optionPrice === "") {
|
|
604
|
-
normalizedOption["default_price"] = readFinalPrice(normalizedOption["default_price_info"]) || readFinalPrice(normalizedOption["price_info"]) || "0";
|
|
605
|
-
}
|
|
606
|
-
return normalizedOption;
|
|
607
|
-
});
|
|
608
|
-
return normalizedAddOn;
|
|
609
|
-
}
|
|
610
|
-
function normalizeCatalogueProductPayload(product) {
|
|
611
|
-
const normalized = { ...product };
|
|
612
|
-
const defaultPrice = normalized["default_price"];
|
|
613
|
-
if (defaultPrice === void 0 || defaultPrice === null || defaultPrice === "") {
|
|
614
|
-
const derivedDefaultPrice = readFinalPrice(normalized["default_price_info"]) || readFinalPrice(normalized["price_info"]) || (typeof normalized["final_price"] === "string" || typeof normalized["final_price"] === "number" ? normalized["final_price"] : void 0);
|
|
615
|
-
normalized["default_price"] = derivedDefaultPrice ?? "0";
|
|
616
|
-
}
|
|
617
|
-
const addOns = normalized["add_ons"];
|
|
618
|
-
if (Array.isArray(addOns)) {
|
|
619
|
-
normalized["add_ons"] = addOns.map((addOn) => normalizeAddOnPayload(addOn));
|
|
620
|
-
}
|
|
621
|
-
return normalized;
|
|
622
|
-
}
|
|
623
|
-
function toFiniteNumber(value) {
|
|
624
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
625
|
-
return value;
|
|
626
|
-
}
|
|
627
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
628
|
-
const parsed = Number(value);
|
|
629
|
-
if (Number.isFinite(parsed)) {
|
|
630
|
-
return parsed;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
return void 0;
|
|
634
|
-
}
|
|
635
|
-
function normalizePagination(value) {
|
|
636
|
-
if (!isRecord(value)) {
|
|
637
|
-
return void 0;
|
|
638
|
-
}
|
|
639
|
-
const totalCount = toFiniteNumber(value.total_count);
|
|
640
|
-
const currentPage = toFiniteNumber(value.current_page);
|
|
641
|
-
const pageSize = toFiniteNumber(value.page_size);
|
|
642
|
-
const totalPages = toFiniteNumber(value.total_pages);
|
|
643
|
-
if (totalCount === void 0 || currentPage === void 0 || pageSize === void 0 || totalPages === void 0) {
|
|
644
|
-
return void 0;
|
|
645
|
-
}
|
|
646
|
-
return {
|
|
647
|
-
total_count: totalCount,
|
|
648
|
-
current_page: currentPage,
|
|
649
|
-
page_size: pageSize,
|
|
650
|
-
total_pages: totalPages,
|
|
651
|
-
has_more: value.has_more === true,
|
|
652
|
-
next_cursor: typeof value.next_cursor === "string" ? value.next_cursor : void 0
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
function normalizeCatalogueResult(payload) {
|
|
656
|
-
if (Array.isArray(payload)) {
|
|
657
|
-
return {
|
|
658
|
-
items: payload.map((product) => normalizeCatalogueProductPayload(product)),
|
|
659
|
-
is_complete: true
|
|
660
|
-
};
|
|
661
|
-
}
|
|
662
|
-
if (!isRecord(payload)) {
|
|
663
|
-
return {
|
|
664
|
-
items: [],
|
|
665
|
-
is_complete: true
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
|
-
const rawItems = Array.isArray(payload.products) ? payload.products : Array.isArray(payload.items) ? payload.items : [];
|
|
669
|
-
return {
|
|
670
|
-
items: rawItems.map((product) => normalizeCatalogueProductPayload(product)),
|
|
671
|
-
is_complete: typeof payload.is_complete === "boolean" ? payload.is_complete : true,
|
|
672
|
-
total_available: toFiniteNumber(payload.total_available),
|
|
673
|
-
pagination: normalizePagination(payload.pagination)
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
function normalizeCatalogueSnapshot(payload) {
|
|
677
|
-
if (Array.isArray(payload)) {
|
|
678
|
-
return {
|
|
679
|
-
categories: [],
|
|
680
|
-
products: payload.map((product) => normalizeCatalogueProductPayload(product)),
|
|
681
|
-
add_ons: [],
|
|
682
|
-
is_complete: true
|
|
683
|
-
};
|
|
684
|
-
}
|
|
685
|
-
if (!isRecord(payload)) {
|
|
686
|
-
return {
|
|
687
|
-
categories: [],
|
|
688
|
-
products: [],
|
|
689
|
-
add_ons: [],
|
|
690
|
-
is_complete: true
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
const rawProducts = Array.isArray(payload.products) ? payload.products : Array.isArray(payload.items) ? payload.items : [];
|
|
694
|
-
const rawCategories = Array.isArray(payload.categories) ? payload.categories : [];
|
|
695
|
-
const rawAddOns = Array.isArray(payload.add_ons) ? payload.add_ons : [];
|
|
696
|
-
return {
|
|
697
|
-
categories: rawCategories,
|
|
698
|
-
products: rawProducts.map((product) => normalizeCatalogueProductPayload(product)),
|
|
699
|
-
add_ons: rawAddOns.map((addOn) => normalizeAddOnPayload(addOn)),
|
|
700
|
-
is_complete: typeof payload.is_complete === "boolean" ? payload.is_complete : true,
|
|
701
|
-
total_available: toFiniteNumber(payload.total_available),
|
|
702
|
-
pagination: normalizePagination(payload.pagination)
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
var CatalogueQueries = class {
|
|
706
|
-
constructor(client) {
|
|
707
|
-
this.client = client;
|
|
708
|
-
}
|
|
709
|
-
async getCatalogue() {
|
|
710
|
-
const result = await safe(this.client.get("/api/v1/catalogue"));
|
|
711
|
-
if (!result.ok) return result;
|
|
712
|
-
return ok(normalizeCatalogueSnapshot(result.value));
|
|
713
|
-
}
|
|
714
|
-
async getProducts(options) {
|
|
715
|
-
const path = withQuery("/api/v1/catalogue/products", {
|
|
716
|
-
category_id: options?.category,
|
|
717
|
-
taxonomy_id: options?.taxonomy,
|
|
718
|
-
search: options?.search,
|
|
719
|
-
page: options?.page,
|
|
720
|
-
tags: options?.tags?.join(","),
|
|
721
|
-
featured: options?.featured,
|
|
722
|
-
in_stock: options?.in_stock,
|
|
723
|
-
min_price: options?.min_price,
|
|
724
|
-
max_price: options?.max_price,
|
|
725
|
-
sort_by: options?.sort_by,
|
|
726
|
-
sort_order: options?.sort_order,
|
|
727
|
-
limit: options?.limit,
|
|
728
|
-
offset: options?.offset,
|
|
729
|
-
cursor: options?.cursor
|
|
730
|
-
});
|
|
731
|
-
const result = await safe(this.client.get(path));
|
|
732
|
-
if (!result.ok) return result;
|
|
733
|
-
return ok(normalizeCatalogueResult(result.value));
|
|
734
|
-
}
|
|
735
|
-
async getProduct(id) {
|
|
736
|
-
const encodedId = encodeURIComponent(id);
|
|
737
|
-
const result = await safe(this.client.get(`/api/v1/catalogue/products/${encodedId}`));
|
|
738
|
-
if (!result.ok) return result;
|
|
739
|
-
return ok(normalizeCatalogueProductPayload(result.value));
|
|
740
|
-
}
|
|
741
|
-
async getProductBySlug(slug) {
|
|
742
|
-
const encodedSlug = encodeURIComponent(slug);
|
|
743
|
-
const result = await safe(this.client.get(`/api/v1/catalogue/products/slug/${encodedSlug}`));
|
|
744
|
-
if (!result.ok) return result;
|
|
745
|
-
return ok(normalizeCatalogueProductPayload(result.value));
|
|
746
|
-
}
|
|
747
|
-
async getVariants(productId) {
|
|
748
|
-
const encodedId = encodeURIComponent(productId);
|
|
749
|
-
return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/variants`));
|
|
750
|
-
}
|
|
751
|
-
async getVariantAxes(productId) {
|
|
752
|
-
const encodedId = encodeURIComponent(productId);
|
|
753
|
-
return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/variant-axes`));
|
|
754
|
-
}
|
|
755
|
-
async getVariantByAxisSelections(productId, selections) {
|
|
756
|
-
const encodedId = encodeURIComponent(productId);
|
|
757
|
-
return safe(
|
|
758
|
-
this.client.post(
|
|
759
|
-
`/api/v1/catalogue/products/${encodedId}/variants/find`,
|
|
760
|
-
{ axis_selections: selections }
|
|
761
|
-
)
|
|
762
|
-
);
|
|
763
|
-
}
|
|
764
|
-
async getVariantById(productId, variantId) {
|
|
765
|
-
const encodedProductId = encodeURIComponent(productId);
|
|
766
|
-
const encodedVariantId = encodeURIComponent(variantId);
|
|
767
|
-
return safe(
|
|
768
|
-
this.client.get(
|
|
769
|
-
`/api/v1/catalogue/products/${encodedProductId}/variants/${encodedVariantId}`
|
|
770
|
-
)
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
async getAddOns(productId) {
|
|
774
|
-
const encodedId = encodeURIComponent(productId);
|
|
775
|
-
return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/add-ons`));
|
|
776
|
-
}
|
|
777
|
-
async getCategories() {
|
|
778
|
-
return safe(this.client.get("/api/v1/catalogue/categories"));
|
|
779
|
-
}
|
|
780
|
-
async getCategory(id) {
|
|
781
|
-
const encodedId = encodeURIComponent(id);
|
|
782
|
-
return safe(this.client.get(`/api/v1/catalogue/categories/${encodedId}`));
|
|
783
|
-
}
|
|
784
|
-
async getCategoryBySlug(slug) {
|
|
785
|
-
const encodedSlug = encodeURIComponent(slug);
|
|
786
|
-
return safe(this.client.get(`/api/v1/catalogue/categories/slug/${encodedSlug}`));
|
|
787
|
-
}
|
|
788
|
-
async getCategoryProducts(categoryId) {
|
|
789
|
-
const encodedId = encodeURIComponent(categoryId);
|
|
790
|
-
return safe(this.client.get(`/api/v1/catalogue/categories/${encodedId}/products`));
|
|
791
|
-
}
|
|
792
|
-
async getCollections() {
|
|
793
|
-
return safe(this.client.get("/api/v1/catalogue/collections"));
|
|
794
|
-
}
|
|
795
|
-
async getCollection(id) {
|
|
796
|
-
const encodedId = encodeURIComponent(id);
|
|
797
|
-
return safe(this.client.get(`/api/v1/catalogue/collections/${encodedId}`));
|
|
798
|
-
}
|
|
799
|
-
async getCollectionBySlug(slug) {
|
|
800
|
-
const encodedSlug = encodeURIComponent(slug);
|
|
801
|
-
return safe(this.client.get(`/api/v1/catalogue/collections/slug/${encodedSlug}`));
|
|
802
|
-
}
|
|
803
|
-
async getCollectionProducts(collectionId) {
|
|
804
|
-
const encodedId = encodeURIComponent(collectionId);
|
|
805
|
-
return safe(this.client.get(`/api/v1/catalogue/collections/${encodedId}/products`));
|
|
806
|
-
}
|
|
807
|
-
async searchCollections(query, limit = 20) {
|
|
808
|
-
const path = withQuery("/api/v1/catalogue/collections", { search: query, limit });
|
|
809
|
-
return safe(this.client.get(path));
|
|
810
|
-
}
|
|
811
|
-
async getBundles() {
|
|
812
|
-
return safe(this.client.get("/api/v1/catalogue/bundles"));
|
|
813
|
-
}
|
|
814
|
-
async searchBundles(query, limit = 20) {
|
|
815
|
-
const path = withQuery("/api/v1/catalogue/bundles", { search: query, limit });
|
|
816
|
-
return safe(this.client.get(path));
|
|
817
|
-
}
|
|
818
|
-
async getComposites(options) {
|
|
819
|
-
const path = withQuery("/api/v1/catalogue/composites", { limit: options?.limit });
|
|
820
|
-
return safe(this.client.get(path));
|
|
821
|
-
}
|
|
822
|
-
async calculateCompositePrice(compositeId, selections, locationId) {
|
|
823
|
-
const encodedId = encodeURIComponent(compositeId);
|
|
824
|
-
return safe(
|
|
825
|
-
this.client.post(
|
|
826
|
-
`/api/v1/catalogue/composites/${encodedId}/calculate-price`,
|
|
827
|
-
{
|
|
828
|
-
selections,
|
|
829
|
-
location_id: locationId
|
|
830
|
-
}
|
|
831
|
-
)
|
|
832
|
-
);
|
|
833
|
-
}
|
|
834
|
-
async fetchQuote(input) {
|
|
835
|
-
return safe(this.client.post("/api/v1/catalogue/quotes", input));
|
|
836
|
-
}
|
|
837
|
-
async getQuote(quoteId) {
|
|
838
|
-
const encodedQuoteId = encodeURIComponent(quoteId);
|
|
839
|
-
return safe(this.client.get(`/api/v1/catalogue/quotes/${encodedQuoteId}`));
|
|
840
|
-
}
|
|
841
|
-
async refreshQuote(input) {
|
|
842
|
-
const encodedQuoteId = encodeURIComponent(input.quote_id);
|
|
843
|
-
return safe(
|
|
844
|
-
this.client.post(
|
|
845
|
-
`/api/v1/catalogue/quotes/${encodedQuoteId}/refresh`,
|
|
846
|
-
input
|
|
847
|
-
)
|
|
848
|
-
);
|
|
849
|
-
}
|
|
850
|
-
async getTaxonomies(parentId) {
|
|
851
|
-
const path = withQuery("/api/v1/catalogue/taxonomies", { parent_id: parentId });
|
|
852
|
-
return safe(this.client.get(path));
|
|
853
|
-
}
|
|
854
|
-
async getTaxonomy(id) {
|
|
855
|
-
const encodedId = encodeURIComponent(id);
|
|
856
|
-
return safe(this.client.get(`/api/v1/catalogue/taxonomies/${encodedId}`));
|
|
857
|
-
}
|
|
858
|
-
async getTaxonomyPath(id) {
|
|
859
|
-
const encodedId = encodeURIComponent(id);
|
|
860
|
-
return safe(this.client.get(`/api/v1/catalogue/taxonomies/${encodedId}/path`));
|
|
861
|
-
}
|
|
862
|
-
async searchTaxonomies(query, limit) {
|
|
863
|
-
const path = withQuery("/api/v1/catalogue/taxonomies/search", { q: query, limit });
|
|
864
|
-
return safe(this.client.get(path));
|
|
865
|
-
}
|
|
866
|
-
async getProductSchedules(productId) {
|
|
867
|
-
const encodedId = encodeURIComponent(productId);
|
|
868
|
-
return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/schedules`));
|
|
869
|
-
}
|
|
870
|
-
async checkProductAvailableNow(productId, locationId) {
|
|
871
|
-
const encodedId = encodeURIComponent(productId);
|
|
872
|
-
const path = withQuery(`/api/v1/catalogue/products/${encodedId}/available-now`, { location_id: locationId });
|
|
873
|
-
return safe(this.client.get(path));
|
|
874
|
-
}
|
|
875
|
-
async getDeals(locationId) {
|
|
876
|
-
const path = withQuery("/api/v1/catalogue/deals", { location_id: locationId });
|
|
877
|
-
return safe(this.client.get(path));
|
|
878
|
-
}
|
|
879
|
-
async getProductsOnSale() {
|
|
880
|
-
return safe(this.client.get("/api/v1/catalogue/deals/on-sale"));
|
|
881
|
-
}
|
|
882
|
-
async getProductDeals(productId) {
|
|
883
|
-
const encodedId = encodeURIComponent(productId);
|
|
884
|
-
return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/deals`));
|
|
885
|
-
}
|
|
886
|
-
async getCategoryDeals(categoryId) {
|
|
887
|
-
const encodedId = encodeURIComponent(categoryId);
|
|
888
|
-
return safe(this.client.get(`/api/v1/catalogue/categories/${encodedId}/deals`));
|
|
889
|
-
}
|
|
890
|
-
async getCollectionDeals(collectionId) {
|
|
891
|
-
const encodedId = encodeURIComponent(collectionId);
|
|
892
|
-
return safe(this.client.get(`/api/v1/catalogue/collections/${encodedId}/deals`));
|
|
893
|
-
}
|
|
894
|
-
async validateDiscountCode(code, orderSubtotal, locationId) {
|
|
895
|
-
return safe(
|
|
896
|
-
this.client.post("/api/v1/catalogue/deals/validate", {
|
|
897
|
-
discount_code: code,
|
|
898
|
-
order_subtotal: orderSubtotal,
|
|
899
|
-
location_id: locationId
|
|
900
|
-
})
|
|
901
|
-
);
|
|
902
|
-
}
|
|
903
|
-
async search(query, options) {
|
|
904
|
-
const result = await this.getProducts({
|
|
905
|
-
search: query,
|
|
906
|
-
category: options?.category,
|
|
907
|
-
limit: options?.limit ?? 20
|
|
908
|
-
});
|
|
909
|
-
if (!result.ok) return result;
|
|
910
|
-
return ok(result.value.items);
|
|
911
|
-
}
|
|
912
|
-
async searchProducts(query, options) {
|
|
913
|
-
return this.search(query, options);
|
|
914
|
-
}
|
|
915
|
-
async getMenu(options) {
|
|
916
|
-
const path = withQuery("/api/v1/catalogue/menu", {
|
|
917
|
-
category_id: options?.category,
|
|
918
|
-
limit: options?.limit
|
|
919
|
-
});
|
|
920
|
-
return safe(this.client.get(path));
|
|
921
|
-
}
|
|
922
|
-
async getMenuCategory(categoryId) {
|
|
923
|
-
const encodedId = encodeURIComponent(categoryId);
|
|
924
|
-
return safe(this.client.get(`/api/v1/catalogue/menu/categories/${encodedId}`));
|
|
925
|
-
}
|
|
926
|
-
async getMenuItem(itemId) {
|
|
927
|
-
const encodedId = encodeURIComponent(itemId);
|
|
928
|
-
return safe(this.client.get(`/api/v1/catalogue/menu/items/${encodedId}`));
|
|
929
|
-
}
|
|
930
|
-
async getTags(options) {
|
|
931
|
-
const path = withQuery("/api/v1/catalogue/tags", {
|
|
932
|
-
tag_group: options?.tag_group,
|
|
933
|
-
search: options?.search,
|
|
934
|
-
min_usage: options?.min_usage,
|
|
935
|
-
limit: options?.limit,
|
|
936
|
-
offset: options?.offset
|
|
937
|
-
});
|
|
938
|
-
return safe(this.client.get(path));
|
|
939
|
-
}
|
|
940
|
-
};
|
|
941
|
-
|
|
942
|
-
// src/cart.ts
|
|
943
|
-
function isUICartResponse(value) {
|
|
944
|
-
return "cart" in value;
|
|
945
|
-
}
|
|
946
|
-
function unwrapEnrichedCart(value) {
|
|
947
|
-
return isUICartResponse(value) ? value.cart : value;
|
|
948
|
-
}
|
|
949
|
-
var CartOperations = class {
|
|
950
|
-
constructor(client) {
|
|
951
|
-
this.client = client;
|
|
952
|
-
}
|
|
953
|
-
async get() {
|
|
954
|
-
const result = await safe(this.client.get("/api/v1/cart"));
|
|
955
|
-
if (!result.ok) return result;
|
|
956
|
-
return ok(unwrapEnrichedCart(result.value));
|
|
957
|
-
}
|
|
958
|
-
async getRaw() {
|
|
959
|
-
return safe(this.client.get("/api/v1/cart"));
|
|
960
|
-
}
|
|
961
|
-
async getItems() {
|
|
962
|
-
return safe(this.client.get("/api/v1/cart/items"));
|
|
963
|
-
}
|
|
964
|
-
async getCount() {
|
|
965
|
-
return safe(this.client.get("/api/v1/cart/count"));
|
|
966
|
-
}
|
|
967
|
-
async getTotal() {
|
|
968
|
-
return safe(this.client.get("/api/v1/cart/total"));
|
|
969
|
-
}
|
|
970
|
-
async getSummary() {
|
|
971
|
-
const cartResult = await this.get();
|
|
972
|
-
if (!cartResult.ok) return cartResult;
|
|
973
|
-
const cart = cartResult.value;
|
|
974
|
-
return ok({
|
|
975
|
-
item_count: cart.items.length,
|
|
976
|
-
total_items: cart.items.reduce((sum, item) => sum + item.quantity, 0),
|
|
977
|
-
subtotal: cart.pricing.subtotal,
|
|
978
|
-
discount_amount: cart.pricing.total_discounts,
|
|
979
|
-
tax_amount: cart.pricing.tax_amount,
|
|
980
|
-
total: cart.pricing.total_price,
|
|
981
|
-
currency: cart.pricing.currency
|
|
982
|
-
});
|
|
983
|
-
}
|
|
984
|
-
async addItem(input) {
|
|
985
|
-
return safe(this.client.post("/api/v1/cart/items", input));
|
|
986
|
-
}
|
|
987
|
-
async updateItem(cartItemId, updates) {
|
|
988
|
-
if (typeof updates.quantity === "number") {
|
|
989
|
-
return this.updateQuantity(cartItemId, updates.quantity);
|
|
990
|
-
}
|
|
991
|
-
const encodedId = encodeURIComponent(cartItemId);
|
|
992
|
-
return safe(this.client.patch(`/api/v1/cart/items/${encodedId}`, updates));
|
|
993
|
-
}
|
|
994
|
-
async updateQuantity(cartItemId, quantity) {
|
|
995
|
-
const encodedId = encodeURIComponent(cartItemId);
|
|
996
|
-
return safe(
|
|
997
|
-
this.client.patch(`/api/v1/cart/items/${encodedId}`, {
|
|
998
|
-
quantity
|
|
999
|
-
})
|
|
1000
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
async removeItem(cartItemId) {
|
|
1003
|
-
const encodedId = encodeURIComponent(cartItemId);
|
|
1004
|
-
return safe(this.client.delete(`/api/v1/cart/items/${encodedId}`));
|
|
1005
|
-
}
|
|
1006
|
-
async clear() {
|
|
1007
|
-
return safe(this.client.delete("/api/v1/cart"));
|
|
1008
|
-
}
|
|
1009
|
-
async applyCoupon(code) {
|
|
1010
|
-
return safe(
|
|
1011
|
-
this.client.post("/api/v1/cart/coupons", {
|
|
1012
|
-
coupon_code: code
|
|
1013
|
-
})
|
|
1014
|
-
);
|
|
1015
|
-
}
|
|
1016
|
-
async removeCoupon() {
|
|
1017
|
-
return safe(this.client.delete("/api/v1/cart/coupons/current"));
|
|
1018
|
-
}
|
|
1019
|
-
async isEmpty() {
|
|
1020
|
-
const countResult = await this.getCount();
|
|
1021
|
-
if (!countResult.ok) return countResult;
|
|
1022
|
-
return ok(countResult.value === 0);
|
|
1023
|
-
}
|
|
1024
|
-
async hasItem(productId, variantId) {
|
|
1025
|
-
const itemsResult = await this.getItems();
|
|
1026
|
-
if (!itemsResult.ok) return itemsResult;
|
|
1027
|
-
const found = itemsResult.value.some((item) => {
|
|
1028
|
-
const matchesProduct = item.item_id === productId;
|
|
1029
|
-
if (!variantId) return matchesProduct;
|
|
1030
|
-
const config = item.configuration;
|
|
1031
|
-
if ("variant" in config && config.variant) {
|
|
1032
|
-
return matchesProduct && config.variant.variant_id === variantId;
|
|
1033
|
-
}
|
|
1034
|
-
return matchesProduct;
|
|
1035
|
-
});
|
|
1036
|
-
return ok(found);
|
|
1037
|
-
}
|
|
1038
|
-
async findItem(productId, variantId) {
|
|
1039
|
-
const itemsResult = await this.getItems();
|
|
1040
|
-
if (!itemsResult.ok) return itemsResult;
|
|
1041
|
-
const found = itemsResult.value.find((item) => {
|
|
1042
|
-
const matchesProduct = item.item_id === productId;
|
|
1043
|
-
if (!variantId) return matchesProduct;
|
|
1044
|
-
const config = item.configuration;
|
|
1045
|
-
if ("variant" in config && config.variant) {
|
|
1046
|
-
return matchesProduct && config.variant.variant_id === variantId;
|
|
1047
|
-
}
|
|
1048
|
-
return matchesProduct;
|
|
1049
|
-
});
|
|
1050
|
-
return ok(found);
|
|
1051
|
-
}
|
|
1052
|
-
};
|
|
1053
|
-
|
|
1054
|
-
// src/constants.ts
|
|
1055
|
-
var MOBILE_MONEY_PROVIDER = {
|
|
1056
|
-
MTN: "mtn",
|
|
1057
|
-
VODAFONE: "vodafone",
|
|
1058
|
-
AIRTEL: "airtel"
|
|
1059
|
-
};
|
|
1060
|
-
|
|
1061
|
-
// src/utils/price.ts
|
|
1062
|
-
var CURRENCY_SYMBOLS = {
|
|
1063
|
-
USD: "$",
|
|
1064
|
-
EUR: "\u20AC",
|
|
1065
|
-
GBP: "\xA3",
|
|
1066
|
-
JPY: "\xA5",
|
|
1067
|
-
CNY: "\xA5",
|
|
1068
|
-
CHF: "CHF",
|
|
1069
|
-
CAD: "C$",
|
|
1070
|
-
AUD: "A$",
|
|
1071
|
-
NZD: "NZ$",
|
|
1072
|
-
HKD: "HK$",
|
|
1073
|
-
SGD: "S$",
|
|
1074
|
-
INR: "\u20B9",
|
|
1075
|
-
BRL: "R$",
|
|
1076
|
-
MXN: "MX$",
|
|
1077
|
-
KRW: "\u20A9",
|
|
1078
|
-
RUB: "\u20BD",
|
|
1079
|
-
TRY: "\u20BA",
|
|
1080
|
-
THB: "\u0E3F",
|
|
1081
|
-
PLN: "z\u0142",
|
|
1082
|
-
SEK: "kr",
|
|
1083
|
-
NOK: "kr",
|
|
1084
|
-
DKK: "kr",
|
|
1085
|
-
CZK: "K\u010D",
|
|
1086
|
-
HUF: "Ft",
|
|
1087
|
-
ILS: "\u20AA",
|
|
1088
|
-
AED: "\u062F.\u0625",
|
|
1089
|
-
SAR: "\uFDFC",
|
|
1090
|
-
MYR: "RM",
|
|
1091
|
-
PHP: "\u20B1",
|
|
1092
|
-
IDR: "Rp",
|
|
1093
|
-
VND: "\u20AB",
|
|
1094
|
-
TWD: "NT$",
|
|
1095
|
-
GHS: "GH\u20B5",
|
|
1096
|
-
NGN: "\u20A6",
|
|
1097
|
-
KES: "KSh",
|
|
1098
|
-
ZAR: "R",
|
|
1099
|
-
XOF: "CFA",
|
|
1100
|
-
XAF: "FCFA",
|
|
1101
|
-
EGP: "E\xA3",
|
|
1102
|
-
MAD: "MAD",
|
|
1103
|
-
TZS: "TSh",
|
|
1104
|
-
UGX: "USh",
|
|
1105
|
-
RWF: "FRw",
|
|
1106
|
-
ETB: "Br",
|
|
1107
|
-
ZMW: "ZK",
|
|
1108
|
-
BWP: "P",
|
|
1109
|
-
MUR: "\u20A8",
|
|
1110
|
-
SCR: "\u20A8",
|
|
1111
|
-
NAD: "N$",
|
|
1112
|
-
SZL: "E",
|
|
1113
|
-
LSL: "L",
|
|
1114
|
-
MWK: "MK",
|
|
1115
|
-
AOA: "Kz",
|
|
1116
|
-
CDF: "FC",
|
|
1117
|
-
GMD: "D",
|
|
1118
|
-
GNF: "FG",
|
|
1119
|
-
LRD: "L$",
|
|
1120
|
-
SLL: "Le",
|
|
1121
|
-
MZN: "MT",
|
|
1122
|
-
SDG: "SDG",
|
|
1123
|
-
SSP: "SSP",
|
|
1124
|
-
SOS: "Sh.So.",
|
|
1125
|
-
DJF: "Fdj",
|
|
1126
|
-
ERN: "Nfk",
|
|
1127
|
-
CVE: "$",
|
|
1128
|
-
STN: "Db",
|
|
1129
|
-
KMF: "CF",
|
|
1130
|
-
BIF: "FBu"
|
|
1131
|
-
};
|
|
1132
|
-
function getCurrencySymbol(currencyCode3) {
|
|
1133
|
-
return CURRENCY_SYMBOLS[currencyCode3.toUpperCase()] || currencyCode3;
|
|
1134
|
-
}
|
|
1135
|
-
function formatPrice(amount, currency = "GHS", locale = "en-US") {
|
|
1136
|
-
const numAmount = typeof amount === "string" ? parseFloat(amount) : amount;
|
|
1137
|
-
if (isNaN(numAmount)) {
|
|
1138
|
-
return `${getCurrencySymbol(currency)}0.00`;
|
|
1139
|
-
}
|
|
1140
|
-
try {
|
|
1141
|
-
return new Intl.NumberFormat(locale, {
|
|
1142
|
-
style: "currency",
|
|
1143
|
-
currency: currency.toUpperCase(),
|
|
1144
|
-
minimumFractionDigits: 2,
|
|
1145
|
-
maximumFractionDigits: 2
|
|
1146
|
-
}).format(numAmount);
|
|
1147
|
-
} catch {
|
|
1148
|
-
return `${getCurrencySymbol(currency)}${numAmount.toFixed(2)}`;
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
function parsePrice(value) {
|
|
1152
|
-
if (value === void 0 || value === null) {
|
|
1153
|
-
return 0;
|
|
1154
|
-
}
|
|
1155
|
-
if (typeof value === "number") {
|
|
1156
|
-
return isNaN(value) ? 0 : value;
|
|
1157
|
-
}
|
|
1158
|
-
const cleaned = value.replace(/[^\d.-]/g, "");
|
|
1159
|
-
const parsed = parseFloat(cleaned);
|
|
1160
|
-
return isNaN(parsed) ? 0 : parsed;
|
|
1161
|
-
}
|
|
1162
|
-
function getDisplayPrice(product) {
|
|
1163
|
-
if (product.price_info) {
|
|
1164
|
-
return parsePrice(product.price_info.final_price);
|
|
1165
|
-
}
|
|
1166
|
-
if (product.final_price !== void 0 && product.final_price !== null) {
|
|
1167
|
-
return parsePrice(product.final_price);
|
|
1168
|
-
}
|
|
1169
|
-
if (product.default_price !== void 0 && product.default_price !== null) {
|
|
1170
|
-
return parsePrice(product.default_price);
|
|
1171
|
-
}
|
|
1172
|
-
return 0;
|
|
1173
|
-
}
|
|
1174
|
-
function getBasePrice(product) {
|
|
1175
|
-
if (product.price_info) {
|
|
1176
|
-
return parsePrice(product.price_info.base_price);
|
|
1177
|
-
}
|
|
1178
|
-
if (product.base_price !== void 0 && product.base_price !== null) {
|
|
1179
|
-
return parsePrice(product.base_price);
|
|
1180
|
-
}
|
|
1181
|
-
if (product.default_price !== void 0 && product.default_price !== null) {
|
|
1182
|
-
return parsePrice(product.default_price);
|
|
1183
|
-
}
|
|
1184
|
-
return 0;
|
|
1185
|
-
}
|
|
1186
|
-
function isOnSale(product) {
|
|
1187
|
-
const basePrice = getBasePrice(product);
|
|
1188
|
-
const finalPrice = getDisplayPrice(product);
|
|
1189
|
-
return basePrice > finalPrice && basePrice > 0;
|
|
1190
|
-
}
|
|
1191
|
-
function getDiscountPercentage(product) {
|
|
1192
|
-
const basePrice = getBasePrice(product);
|
|
1193
|
-
const finalPrice = getDisplayPrice(product);
|
|
1194
|
-
if (basePrice > finalPrice && basePrice > 0) {
|
|
1195
|
-
return Math.round((basePrice - finalPrice) / basePrice * 100);
|
|
1196
|
-
}
|
|
1197
|
-
return 0;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
// src/utils/payment.ts
|
|
1201
|
-
var PAYMENT_SUCCESS_STATUSES = /* @__PURE__ */ new Set([
|
|
1202
|
-
"success",
|
|
1203
|
-
"succeeded",
|
|
1204
|
-
"paid",
|
|
1205
|
-
"captured",
|
|
1206
|
-
"completed",
|
|
1207
|
-
"authorized"
|
|
1208
|
-
]);
|
|
1209
|
-
var PAYMENT_FAILURE_STATUSES = /* @__PURE__ */ new Set([
|
|
1210
|
-
"failed",
|
|
1211
|
-
"declined",
|
|
1212
|
-
"cancelled",
|
|
1213
|
-
"voided",
|
|
1214
|
-
"error",
|
|
1215
|
-
"abandoned"
|
|
1216
|
-
]);
|
|
1217
|
-
var PAYMENT_REQUIRES_ACTION_STATUSES = /* @__PURE__ */ new Set([
|
|
1218
|
-
"requires_action",
|
|
1219
|
-
"requires_payment_method",
|
|
1220
|
-
"requires_capture"
|
|
1221
|
-
]);
|
|
1222
|
-
var PAYMENT_STATUS_ALIAS_MAP = {
|
|
1223
|
-
ok: "success",
|
|
1224
|
-
done: "success",
|
|
1225
|
-
paid: "paid",
|
|
1226
|
-
paid_in_full: "paid",
|
|
1227
|
-
paid_successfully: "paid",
|
|
1228
|
-
succeeded: "success",
|
|
1229
|
-
captured: "captured",
|
|
1230
|
-
completed: "completed",
|
|
1231
|
-
pending_confirmation: "pending_confirmation",
|
|
1232
|
-
requires_authorization: "requires_action",
|
|
1233
|
-
requires_action: "requires_action",
|
|
1234
|
-
requires_payment_method: "requires_payment_method",
|
|
1235
|
-
requires_capture: "requires_capture",
|
|
1236
|
-
partially_paid: "partially_paid",
|
|
1237
|
-
partially_refunded: "partially_refunded",
|
|
1238
|
-
card_declined: "declined",
|
|
1239
|
-
canceled: "cancelled",
|
|
1240
|
-
authorized: "authorized",
|
|
1241
|
-
cancelled: "cancelled",
|
|
1242
|
-
unresolved: "pending"
|
|
1243
|
-
};
|
|
1244
|
-
var KNOWN_PAYMENT_STATUSES = /* @__PURE__ */ new Set([
|
|
1245
|
-
"initialized",
|
|
1246
|
-
"pending",
|
|
1247
|
-
"processing",
|
|
1248
|
-
"authorized",
|
|
1249
|
-
"captured",
|
|
1250
|
-
"failed",
|
|
1251
|
-
"cancelled",
|
|
1252
|
-
"refunded",
|
|
1253
|
-
"unknown",
|
|
1254
|
-
"voided",
|
|
1255
|
-
"requires_action",
|
|
1256
|
-
"abandoned",
|
|
1257
|
-
"reversed",
|
|
1258
|
-
"disputed",
|
|
1259
|
-
"refund_pending",
|
|
1260
|
-
"created",
|
|
1261
|
-
"pending_confirmation",
|
|
1262
|
-
"success",
|
|
1263
|
-
"succeeded",
|
|
1264
|
-
"declined",
|
|
1265
|
-
"partially_refunded",
|
|
1266
|
-
"partially_paid",
|
|
1267
|
-
"paid",
|
|
1268
|
-
"unpaid",
|
|
1269
|
-
"requires_payment_method",
|
|
1270
|
-
"requires_capture",
|
|
1271
|
-
"completed",
|
|
1272
|
-
"error"
|
|
1273
|
-
]);
|
|
1274
|
-
function normalizeStatusToken(status) {
|
|
1275
|
-
return status?.trim().toLowerCase().replace(/[\s-]+/g, "_") ?? "";
|
|
1276
|
-
}
|
|
1277
|
-
function normalizePaymentStatusValue(status) {
|
|
1278
|
-
const normalized = normalizeStatusToken(status);
|
|
1279
|
-
if (Object.prototype.hasOwnProperty.call(PAYMENT_STATUS_ALIAS_MAP, normalized)) {
|
|
1280
|
-
return PAYMENT_STATUS_ALIAS_MAP[normalized];
|
|
1281
|
-
}
|
|
1282
|
-
return KNOWN_PAYMENT_STATUSES.has(normalized) ? normalized : "unknown";
|
|
1283
|
-
}
|
|
1284
|
-
function isPaymentStatusSuccess(status) {
|
|
1285
|
-
const normalizedStatus = normalizePaymentStatusValue(status);
|
|
1286
|
-
return PAYMENT_SUCCESS_STATUSES.has(normalizedStatus);
|
|
1287
|
-
}
|
|
1288
|
-
function isPaymentStatusFailure(status) {
|
|
1289
|
-
const normalizedStatus = normalizePaymentStatusValue(status);
|
|
1290
|
-
return PAYMENT_FAILURE_STATUSES.has(normalizedStatus);
|
|
1291
|
-
}
|
|
1292
|
-
function isPaymentStatusRequiresAction(status) {
|
|
1293
|
-
const normalizedStatus = normalizePaymentStatusValue(status);
|
|
1294
|
-
return PAYMENT_REQUIRES_ACTION_STATUSES.has(normalizedStatus);
|
|
1295
|
-
}
|
|
1296
|
-
function normalizeStatusResponse(response) {
|
|
1297
|
-
if (!response || typeof response !== "object") {
|
|
1298
|
-
return {
|
|
1299
|
-
status: "pending",
|
|
1300
|
-
paid: false,
|
|
1301
|
-
amount: moneyFromNumber(0),
|
|
1302
|
-
currency: "",
|
|
1303
|
-
message: "No status available"
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
const res = response;
|
|
1307
|
-
const normalizedStatus = normalizePaymentStatusValue(res.status ?? void 0);
|
|
1308
|
-
const normalizedAmount = typeof res.amount === "string" ? money(res.amount) : typeof res.amount === "number" && Number.isFinite(res.amount) ? moneyFromNumber(res.amount) : moneyFromNumber(0);
|
|
1309
|
-
const normalizedCurrency = typeof res.currency === "string" && res.currency.trim().length > 0 ? res.currency : "";
|
|
1310
|
-
const paidValue = res.paid === true;
|
|
1311
|
-
const derivedPaid = paidValue || [
|
|
1312
|
-
"success",
|
|
1313
|
-
"succeeded",
|
|
1314
|
-
"paid",
|
|
1315
|
-
"captured",
|
|
1316
|
-
"authorized",
|
|
1317
|
-
"completed"
|
|
1318
|
-
].includes(normalizedStatus);
|
|
1319
|
-
return {
|
|
1320
|
-
status: normalizedStatus,
|
|
1321
|
-
paid: derivedPaid,
|
|
1322
|
-
amount: normalizedAmount,
|
|
1323
|
-
currency: normalizedCurrency,
|
|
1324
|
-
reference: res.reference,
|
|
1325
|
-
message: res.message || ""
|
|
1326
|
-
};
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
// src/utils/paystack.ts
|
|
1330
|
-
async function openPaystackPopup(options, signal) {
|
|
1331
|
-
if (typeof window === "undefined") {
|
|
1332
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
1333
|
-
}
|
|
1334
|
-
let PaystackPop;
|
|
1335
|
-
try {
|
|
1336
|
-
const imported = await import('@paystack/inline-js');
|
|
1337
|
-
PaystackPop = imported.default;
|
|
1338
|
-
} catch {
|
|
1339
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
1340
|
-
}
|
|
1341
|
-
return new Promise((resolve) => {
|
|
1342
|
-
let settled = false;
|
|
1343
|
-
const resolveOnce = (result) => {
|
|
1344
|
-
if (settled) {
|
|
1345
|
-
return;
|
|
1346
|
-
}
|
|
1347
|
-
settled = true;
|
|
1348
|
-
if (signal) {
|
|
1349
|
-
signal.removeEventListener("abort", onAbort);
|
|
1350
|
-
}
|
|
1351
|
-
resolve(result);
|
|
1352
|
-
};
|
|
1353
|
-
const onAbort = () => {
|
|
1354
|
-
resolveOnce({ success: false, error: "CANCELLED" });
|
|
1355
|
-
};
|
|
1356
|
-
if (signal?.aborted) {
|
|
1357
|
-
resolveOnce({ success: false, error: "CANCELLED" });
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
|
-
if (signal) {
|
|
1361
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
1362
|
-
}
|
|
1363
|
-
try {
|
|
1364
|
-
const popup = new PaystackPop();
|
|
1365
|
-
popup.newTransaction({
|
|
1366
|
-
key: options.key,
|
|
1367
|
-
email: options.email,
|
|
1368
|
-
amount: options.amount,
|
|
1369
|
-
currency: options.currency,
|
|
1370
|
-
reference: options.reference,
|
|
1371
|
-
accessCode: options.accessCode,
|
|
1372
|
-
onSuccess: (transaction) => {
|
|
1373
|
-
resolveOnce({
|
|
1374
|
-
success: true,
|
|
1375
|
-
reference: transaction.reference ?? options.reference
|
|
1376
|
-
});
|
|
1377
|
-
},
|
|
1378
|
-
onCancel: () => {
|
|
1379
|
-
resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
|
|
1380
|
-
},
|
|
1381
|
-
onError: (error) => {
|
|
1382
|
-
resolveOnce({
|
|
1383
|
-
success: false,
|
|
1384
|
-
error: error?.message || "PAYMENT_FAILED"
|
|
1385
|
-
});
|
|
1386
|
-
}
|
|
1387
|
-
});
|
|
1388
|
-
} catch {
|
|
1389
|
-
resolveOnce({ success: false, error: "POPUP_BLOCKED" });
|
|
1390
|
-
}
|
|
1391
|
-
});
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
// src/utils/checkout-resolver.ts
|
|
1395
|
-
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
1396
|
-
var DEFAULT_MAX_POLL_ATTEMPTS = 60;
|
|
1397
|
-
var MAX_CONSECUTIVE_NETWORK_ERRORS = 5;
|
|
1398
|
-
var CARD_PROVIDER_PAYSTACK = "paystack";
|
|
1399
|
-
var NEXT_ACTION_NONE = "none";
|
|
1400
|
-
var NEXT_ACTION_CARD_POPUP = "card_popup";
|
|
1401
|
-
var NEXT_ACTION_REDIRECT = "redirect";
|
|
1402
|
-
var NEXT_ACTION_AUTHORIZATION = "authorization";
|
|
1403
|
-
function normalizeCardPopupError(error) {
|
|
1404
|
-
if (!error || error === "PAYMENT_CANCELLED" || error === "CANCELLED") {
|
|
1405
|
-
return "PAYMENT_CANCELLED";
|
|
1406
|
-
}
|
|
1407
|
-
if (error === "PROVIDER_UNAVAILABLE") {
|
|
1408
|
-
return "PROVIDER_UNAVAILABLE";
|
|
1409
|
-
}
|
|
1410
|
-
if (error === "POPUP_BLOCKED") {
|
|
1411
|
-
return "POPUP_BLOCKED";
|
|
1412
|
-
}
|
|
1413
|
-
return "PAYMENT_FAILED";
|
|
1414
|
-
}
|
|
1415
|
-
function normalizeCardProvider(value) {
|
|
1416
|
-
const normalized = value?.trim().toLowerCase();
|
|
1417
|
-
return normalized && normalized.length > 0 ? normalized : CARD_PROVIDER_PAYSTACK;
|
|
1418
|
-
}
|
|
1419
|
-
async function openCardPopup(provider, checkoutResult, email, currency, signal) {
|
|
1420
|
-
if (typeof window === "undefined") {
|
|
1421
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
1422
|
-
}
|
|
1423
|
-
if (provider === CARD_PROVIDER_PAYSTACK) {
|
|
1424
|
-
return openPaystackPopup(
|
|
1425
|
-
{
|
|
1426
|
-
key: checkoutResult.public_key || "",
|
|
1427
|
-
email,
|
|
1428
|
-
accessCode: checkoutResult.client_secret,
|
|
1429
|
-
reference: checkoutResult.payment_reference || checkoutResult.client_secret,
|
|
1430
|
-
currency
|
|
1431
|
-
},
|
|
1432
|
-
signal
|
|
1433
|
-
);
|
|
1434
|
-
}
|
|
1435
|
-
return { success: false, error: "PROVIDER_UNAVAILABLE" };
|
|
1436
|
-
}
|
|
1437
|
-
var VALID_MOBILE_MONEY_PROVIDERS = new Set(Object.values(MOBILE_MONEY_PROVIDER));
|
|
1438
|
-
function isValidMobileMoneyProvider(value) {
|
|
1439
|
-
return VALID_MOBILE_MONEY_PROVIDERS.has(value);
|
|
1440
|
-
}
|
|
1441
|
-
function normalizeAuthorizationType(value) {
|
|
1442
|
-
if (typeof value !== "string") return void 0;
|
|
1443
|
-
const lower = value.toLowerCase();
|
|
1444
|
-
return lower === "otp" || lower === "pin" ? lower : void 0;
|
|
1445
|
-
}
|
|
1446
|
-
function formatMoney2(value) {
|
|
1447
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1448
|
-
return value.toFixed(2);
|
|
1449
|
-
}
|
|
1450
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
1451
|
-
return value;
|
|
1452
|
-
}
|
|
1453
|
-
return "0.00";
|
|
1454
|
-
}
|
|
1455
|
-
function createAbortError() {
|
|
1456
|
-
const error = new Error("CANCELLED");
|
|
1457
|
-
error.name = "AbortError";
|
|
1458
|
-
return error;
|
|
1459
|
-
}
|
|
1460
|
-
function isAbortError(error) {
|
|
1461
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1462
|
-
return true;
|
|
1463
|
-
}
|
|
1464
|
-
return error instanceof Error && (error.name === "AbortError" || error.message === "CANCELLED");
|
|
1465
|
-
}
|
|
1466
|
-
var CheckoutResolver = class {
|
|
1467
|
-
constructor(options) {
|
|
1468
|
-
this.client = options.client;
|
|
1469
|
-
this.checkoutData = options.checkoutData;
|
|
1470
|
-
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
1471
|
-
this.maxPollAttempts = options.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS;
|
|
1472
|
-
this.onStatusChange = options.onStatusChange;
|
|
1473
|
-
this.onAuthorizationRequired = options.onAuthorizationRequired;
|
|
1474
|
-
this.signal = options.signal;
|
|
1475
|
-
this.returnUrl = options.returnUrl;
|
|
1476
|
-
this.enrollInLink = options.enrollInLink !== false;
|
|
1477
|
-
this.businessId = options.businessId;
|
|
1478
|
-
this.addressData = options.addressData;
|
|
1479
|
-
this.paymentData = options.paymentData;
|
|
1480
|
-
this.orderType = options.orderType;
|
|
1481
|
-
this.cartTotal = options.cartTotal;
|
|
1482
|
-
this.cartCurrency = options.cartCurrency;
|
|
1483
|
-
this.allowPopups = options.allowPopups ?? typeof window !== "undefined";
|
|
1484
|
-
}
|
|
1485
|
-
async resolve(checkoutResult) {
|
|
1486
|
-
try {
|
|
1487
|
-
this.ensureNotAborted();
|
|
1488
|
-
if (!checkoutResult.order_id) {
|
|
1489
|
-
return this.fail("CHECKOUT_FAILED", "Checkout did not return an order ID.", false);
|
|
1490
|
-
}
|
|
1491
|
-
if (checkoutResult.next_action) {
|
|
1492
|
-
return this.resolveNextAction(checkoutResult);
|
|
1493
|
-
}
|
|
1494
|
-
let latestCheckoutResult = checkoutResult;
|
|
1495
|
-
let authorizationType = normalizeAuthorizationType(checkoutResult.authorization_type);
|
|
1496
|
-
let paymentReference = checkoutResult.payment_reference;
|
|
1497
|
-
if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
|
|
1498
|
-
return this.finalizeSuccess(checkoutResult);
|
|
1499
|
-
}
|
|
1500
|
-
if (checkoutResult.client_secret && checkoutResult.public_key) {
|
|
1501
|
-
const provider = normalizeCardProvider(checkoutResult.provider);
|
|
1502
|
-
this.emit("awaiting_authorization", {
|
|
1503
|
-
display_text: "Complete payment in the card authorization popup.",
|
|
1504
|
-
order_id: checkoutResult.order_id,
|
|
1505
|
-
order_number: checkoutResult.order_number
|
|
1506
|
-
});
|
|
1507
|
-
if (!this.allowPopups) {
|
|
1508
|
-
return this.fail(
|
|
1509
|
-
"PROVIDER_UNAVAILABLE",
|
|
1510
|
-
"Card payment popup is unavailable in this environment.",
|
|
1511
|
-
false
|
|
1512
|
-
);
|
|
1513
|
-
}
|
|
1514
|
-
const popupResult = await openCardPopup(
|
|
1515
|
-
provider,
|
|
1516
|
-
checkoutResult,
|
|
1517
|
-
this.checkoutData.customer.email || "customer@cimplify.io",
|
|
1518
|
-
this.getOrderCurrency(checkoutResult),
|
|
1519
|
-
this.signal
|
|
1520
|
-
);
|
|
1521
|
-
if (!popupResult.success) {
|
|
1522
|
-
const popupError = normalizeCardPopupError(popupResult.error);
|
|
1523
|
-
if (popupError === "POPUP_BLOCKED") {
|
|
1524
|
-
return this.fail(
|
|
1525
|
-
"POPUP_BLOCKED",
|
|
1526
|
-
"Unable to open card payment popup. Please allow popups and try again.",
|
|
1527
|
-
true
|
|
1528
|
-
);
|
|
1529
|
-
}
|
|
1530
|
-
if (popupError === "PROVIDER_UNAVAILABLE") {
|
|
1531
|
-
return this.fail(
|
|
1532
|
-
"PROVIDER_UNAVAILABLE",
|
|
1533
|
-
"Card payment provider is unavailable.",
|
|
1534
|
-
false
|
|
1535
|
-
);
|
|
1536
|
-
}
|
|
1537
|
-
return this.fail(
|
|
1538
|
-
"PAYMENT_CANCELLED",
|
|
1539
|
-
"Card payment was cancelled before completion.",
|
|
1540
|
-
true
|
|
1541
|
-
);
|
|
1542
|
-
}
|
|
1543
|
-
paymentReference = popupResult.reference || paymentReference;
|
|
1544
|
-
}
|
|
1545
|
-
if (checkoutResult.authorization_url) {
|
|
1546
|
-
if (typeof window !== "undefined" && this.returnUrl) {
|
|
1547
|
-
window.location.assign(checkoutResult.authorization_url);
|
|
1548
|
-
return this.fail(
|
|
1549
|
-
"REDIRECT_REQUIRED",
|
|
1550
|
-
"Redirecting to complete payment authorization.",
|
|
1551
|
-
true
|
|
1552
|
-
);
|
|
1553
|
-
}
|
|
1554
|
-
if (typeof window !== "undefined") {
|
|
1555
|
-
const popup = window.open(
|
|
1556
|
-
checkoutResult.authorization_url,
|
|
1557
|
-
"cimplify-auth",
|
|
1558
|
-
"width=520,height=760,noopener,noreferrer"
|
|
1559
|
-
);
|
|
1560
|
-
if (!popup) {
|
|
1561
|
-
return this.fail(
|
|
1562
|
-
"POPUP_BLOCKED",
|
|
1563
|
-
"Authorization popup was blocked. Please allow popups and retry.",
|
|
1564
|
-
true
|
|
1565
|
-
);
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
if (checkoutResult.requires_authorization || isPaymentStatusRequiresAction(checkoutResult.payment_status)) {
|
|
1570
|
-
const authorization = await this.handleAuthorization({
|
|
1571
|
-
authorizationType,
|
|
1572
|
-
paymentReference,
|
|
1573
|
-
displayText: checkoutResult.display_text,
|
|
1574
|
-
provider: checkoutResult.provider
|
|
1575
|
-
});
|
|
1576
|
-
if (!authorization.ok) {
|
|
1577
|
-
return authorization.result;
|
|
1578
|
-
}
|
|
1579
|
-
if (authorization.value) {
|
|
1580
|
-
latestCheckoutResult = authorization.value;
|
|
1581
|
-
paymentReference = authorization.value.payment_reference || paymentReference;
|
|
1582
|
-
authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
|
|
1583
|
-
if (this.isSuccessfulStatus(authorization.value.payment_status)) {
|
|
1584
|
-
return this.finalizeSuccess(authorization.value);
|
|
1585
|
-
}
|
|
1586
|
-
if (this.isFailureStatus(authorization.value.payment_status)) {
|
|
1587
|
-
return this.fail(
|
|
1588
|
-
"AUTHORIZATION_FAILED",
|
|
1589
|
-
authorization.value.display_text || "Payment authorization failed.",
|
|
1590
|
-
false
|
|
1591
|
-
);
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
return this.pollUntilTerminal({
|
|
1596
|
-
orderId: checkoutResult.order_id,
|
|
1597
|
-
latestCheckoutResult,
|
|
1598
|
-
paymentReference,
|
|
1599
|
-
authorizationType
|
|
1600
|
-
});
|
|
1601
|
-
} catch (error) {
|
|
1602
|
-
if (isAbortError(error)) {
|
|
1603
|
-
return this.fail("CANCELLED", "Checkout was cancelled.", true);
|
|
1604
|
-
}
|
|
1605
|
-
const message = error instanceof Error ? error.message : "Checkout failed unexpectedly.";
|
|
1606
|
-
return this.fail("CHECKOUT_FAILED", message, true);
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
async resolveNextAction(checkoutResult) {
|
|
1610
|
-
const action = checkoutResult.next_action;
|
|
1611
|
-
if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
|
|
1612
|
-
return this.finalizeSuccess(checkoutResult);
|
|
1613
|
-
}
|
|
1614
|
-
let latestCheckoutResult = checkoutResult;
|
|
1615
|
-
let paymentReference = checkoutResult.payment_reference;
|
|
1616
|
-
switch (action.type) {
|
|
1617
|
-
case NEXT_ACTION_NONE:
|
|
1618
|
-
return this.finalizeSuccess(checkoutResult);
|
|
1619
|
-
case NEXT_ACTION_CARD_POPUP: {
|
|
1620
|
-
const provider = normalizeCardProvider(action.provider);
|
|
1621
|
-
this.emit("awaiting_authorization", {
|
|
1622
|
-
display_text: "Complete payment in the card authorization popup.",
|
|
1623
|
-
order_id: checkoutResult.order_id,
|
|
1624
|
-
order_number: checkoutResult.order_number
|
|
1625
|
-
});
|
|
1626
|
-
if (!this.allowPopups) {
|
|
1627
|
-
return this.fail(
|
|
1628
|
-
"PROVIDER_UNAVAILABLE",
|
|
1629
|
-
"Card payment popup is unavailable in this environment.",
|
|
1630
|
-
false
|
|
1631
|
-
);
|
|
1632
|
-
}
|
|
1633
|
-
const popupResult = await openCardPopup(
|
|
1634
|
-
provider,
|
|
1635
|
-
checkoutResult,
|
|
1636
|
-
this.checkoutData.customer.email || "customer@cimplify.io",
|
|
1637
|
-
this.getOrderCurrency(checkoutResult),
|
|
1638
|
-
this.signal
|
|
1639
|
-
);
|
|
1640
|
-
if (!popupResult.success) {
|
|
1641
|
-
const popupError = normalizeCardPopupError(popupResult.error);
|
|
1642
|
-
if (popupError === "POPUP_BLOCKED") {
|
|
1643
|
-
return this.fail(
|
|
1644
|
-
"POPUP_BLOCKED",
|
|
1645
|
-
"Unable to open card payment popup. Please allow popups and try again.",
|
|
1646
|
-
true
|
|
1647
|
-
);
|
|
1648
|
-
}
|
|
1649
|
-
if (popupError === "PROVIDER_UNAVAILABLE") {
|
|
1650
|
-
return this.fail("PROVIDER_UNAVAILABLE", "Card payment provider is unavailable.", false);
|
|
1651
|
-
}
|
|
1652
|
-
return this.fail("PAYMENT_CANCELLED", "Card payment was cancelled before completion.", true);
|
|
1653
|
-
}
|
|
1654
|
-
paymentReference = popupResult.reference || paymentReference;
|
|
1655
|
-
break;
|
|
1656
|
-
}
|
|
1657
|
-
case NEXT_ACTION_REDIRECT: {
|
|
1658
|
-
if (typeof window !== "undefined" && this.returnUrl) {
|
|
1659
|
-
window.location.assign(action.authorization_url);
|
|
1660
|
-
return this.fail("REDIRECT_REQUIRED", "Redirecting to complete payment authorization.", true);
|
|
1661
|
-
}
|
|
1662
|
-
if (typeof window !== "undefined") {
|
|
1663
|
-
const popup = window.open(
|
|
1664
|
-
action.authorization_url,
|
|
1665
|
-
"cimplify-auth",
|
|
1666
|
-
"width=520,height=760,noopener,noreferrer"
|
|
1667
|
-
);
|
|
1668
|
-
if (!popup) {
|
|
1669
|
-
return this.fail(
|
|
1670
|
-
"POPUP_BLOCKED",
|
|
1671
|
-
"Authorization popup was blocked. Please allow popups and retry.",
|
|
1672
|
-
true
|
|
1673
|
-
);
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
break;
|
|
1677
|
-
}
|
|
1678
|
-
case NEXT_ACTION_AUTHORIZATION: {
|
|
1679
|
-
const authType = normalizeAuthorizationType(action.authorization_type);
|
|
1680
|
-
const authorization = await this.handleAuthorization({
|
|
1681
|
-
authorizationType: authType,
|
|
1682
|
-
paymentReference,
|
|
1683
|
-
displayText: action.display_text,
|
|
1684
|
-
provider: checkoutResult.provider
|
|
1685
|
-
});
|
|
1686
|
-
if (!authorization.ok) {
|
|
1687
|
-
return authorization.result;
|
|
1688
|
-
}
|
|
1689
|
-
if (authorization.value) {
|
|
1690
|
-
latestCheckoutResult = authorization.value;
|
|
1691
|
-
paymentReference = authorization.value.payment_reference || paymentReference;
|
|
1692
|
-
if (this.isSuccessfulStatus(authorization.value.payment_status)) {
|
|
1693
|
-
return this.finalizeSuccess(authorization.value);
|
|
1694
|
-
}
|
|
1695
|
-
if (this.isFailureStatus(authorization.value.payment_status)) {
|
|
1696
|
-
return this.fail(
|
|
1697
|
-
"AUTHORIZATION_FAILED",
|
|
1698
|
-
authorization.value.display_text || "Payment authorization failed.",
|
|
1699
|
-
false
|
|
1700
|
-
);
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
break;
|
|
1704
|
-
}
|
|
1705
|
-
}
|
|
1706
|
-
return this.pollUntilTerminal({
|
|
1707
|
-
orderId: checkoutResult.order_id,
|
|
1708
|
-
latestCheckoutResult,
|
|
1709
|
-
paymentReference,
|
|
1710
|
-
authorizationType: normalizeAuthorizationType(checkoutResult.authorization_type)
|
|
1711
|
-
});
|
|
1712
|
-
}
|
|
1713
|
-
async pollUntilTerminal(input) {
|
|
1714
|
-
let consecutiveErrors = 0;
|
|
1715
|
-
let latestCheckoutResult = input.latestCheckoutResult;
|
|
1716
|
-
let paymentReference = input.paymentReference;
|
|
1717
|
-
let authorizationType = input.authorizationType;
|
|
1718
|
-
for (let attempt = 0; attempt < this.maxPollAttempts; attempt++) {
|
|
1719
|
-
this.ensureNotAborted();
|
|
1720
|
-
this.emit("polling", {
|
|
1721
|
-
poll_attempt: attempt,
|
|
1722
|
-
max_poll_attempts: this.maxPollAttempts,
|
|
1723
|
-
order_id: input.orderId,
|
|
1724
|
-
order_number: latestCheckoutResult.order_number
|
|
1725
|
-
});
|
|
1726
|
-
const statusResult = await this.client.checkout.pollPaymentStatus(input.orderId);
|
|
1727
|
-
if (!statusResult.ok) {
|
|
1728
|
-
consecutiveErrors += 1;
|
|
1729
|
-
if (consecutiveErrors >= MAX_CONSECUTIVE_NETWORK_ERRORS) {
|
|
1730
|
-
return this.fail(
|
|
1731
|
-
"NETWORK_ERROR",
|
|
1732
|
-
"Unable to confirm payment due to repeated network errors.",
|
|
1733
|
-
true
|
|
1734
|
-
);
|
|
1735
|
-
}
|
|
1736
|
-
await this.wait(this.pollIntervalMs);
|
|
1737
|
-
continue;
|
|
1738
|
-
}
|
|
1739
|
-
consecutiveErrors = 0;
|
|
1740
|
-
if (statusResult.value.reference) {
|
|
1741
|
-
paymentReference = statusResult.value.reference;
|
|
1742
|
-
}
|
|
1743
|
-
const normalized = normalizeStatusResponse(statusResult.value);
|
|
1744
|
-
if (normalized.paid || isPaymentStatusSuccess(normalized.status)) {
|
|
1745
|
-
return this.finalizeSuccess(latestCheckoutResult);
|
|
1746
|
-
}
|
|
1747
|
-
if (isPaymentStatusFailure(normalized.status)) {
|
|
1748
|
-
return this.fail(
|
|
1749
|
-
normalized.status ? normalized.status.toUpperCase() : "PAYMENT_FAILED",
|
|
1750
|
-
normalized.message || "Payment failed during confirmation.",
|
|
1751
|
-
false
|
|
1752
|
-
);
|
|
1753
|
-
}
|
|
1754
|
-
if (isPaymentStatusRequiresAction(normalized.status)) {
|
|
1755
|
-
const authorization = await this.handleAuthorization({
|
|
1756
|
-
authorizationType,
|
|
1757
|
-
paymentReference,
|
|
1758
|
-
displayText: normalized.message || latestCheckoutResult.display_text,
|
|
1759
|
-
provider: latestCheckoutResult.provider
|
|
1760
|
-
});
|
|
1761
|
-
if (!authorization.ok) {
|
|
1762
|
-
return authorization.result;
|
|
1763
|
-
}
|
|
1764
|
-
if (authorization.value) {
|
|
1765
|
-
latestCheckoutResult = authorization.value;
|
|
1766
|
-
paymentReference = authorization.value.payment_reference || paymentReference;
|
|
1767
|
-
authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
|
|
1768
|
-
if (this.isSuccessfulStatus(authorization.value.payment_status)) {
|
|
1769
|
-
return this.finalizeSuccess(authorization.value);
|
|
1770
|
-
}
|
|
1771
|
-
if (this.isFailureStatus(authorization.value.payment_status)) {
|
|
1772
|
-
return this.fail(
|
|
1773
|
-
"AUTHORIZATION_FAILED",
|
|
1774
|
-
authorization.value.display_text || "Payment authorization failed.",
|
|
1775
|
-
false
|
|
1776
|
-
);
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
await this.wait(this.pollIntervalMs);
|
|
1781
|
-
}
|
|
1782
|
-
try {
|
|
1783
|
-
const finalResult = await this.client.checkout.pollPaymentStatus(input.orderId);
|
|
1784
|
-
if (finalResult.ok) {
|
|
1785
|
-
const normalized = normalizeStatusResponse(finalResult.value);
|
|
1786
|
-
if (normalized.paid || isPaymentStatusSuccess(normalized.status)) {
|
|
1787
|
-
return this.finalizeSuccess(latestCheckoutResult);
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
} catch {
|
|
1791
|
-
}
|
|
1792
|
-
return this.fail(
|
|
1793
|
-
"PAYMENT_TIMEOUT",
|
|
1794
|
-
"Payment confirmation timed out. Please retry checkout.",
|
|
1795
|
-
true
|
|
1796
|
-
);
|
|
1797
|
-
}
|
|
1798
|
-
async handleAuthorization(input) {
|
|
1799
|
-
const authorizationType = input.authorizationType;
|
|
1800
|
-
const paymentReference = input.paymentReference;
|
|
1801
|
-
if (!authorizationType || !paymentReference) {
|
|
1802
|
-
return { ok: true };
|
|
1803
|
-
}
|
|
1804
|
-
this.emit("awaiting_authorization", {
|
|
1805
|
-
authorization_type: authorizationType,
|
|
1806
|
-
display_text: input.displayText,
|
|
1807
|
-
provider: input.provider
|
|
1808
|
-
});
|
|
1809
|
-
let authorizationResult;
|
|
1810
|
-
const submit = async (code) => {
|
|
1811
|
-
this.ensureNotAborted();
|
|
1812
|
-
const trimmedCode = code.trim();
|
|
1813
|
-
if (!trimmedCode) {
|
|
1814
|
-
throw new Error("Authorization code is required.");
|
|
1815
|
-
}
|
|
1816
|
-
const submitResult = await this.client.checkout.submitAuthorization({
|
|
1817
|
-
reference: paymentReference,
|
|
1818
|
-
auth_type: authorizationType,
|
|
1819
|
-
value: trimmedCode
|
|
1820
|
-
});
|
|
1821
|
-
if (!submitResult.ok) {
|
|
1822
|
-
throw new Error(submitResult.error.message || "Authorization failed.");
|
|
1823
|
-
}
|
|
1824
|
-
authorizationResult = submitResult.value;
|
|
1825
|
-
};
|
|
1826
|
-
try {
|
|
1827
|
-
if (this.onAuthorizationRequired) {
|
|
1828
|
-
await this.onAuthorizationRequired(authorizationType, submit);
|
|
1829
|
-
} else {
|
|
1830
|
-
return {
|
|
1831
|
-
ok: false,
|
|
1832
|
-
result: this.fail(
|
|
1833
|
-
"AUTHORIZATION_REQUIRED",
|
|
1834
|
-
"Authorization callback is required in headless checkout mode.",
|
|
1835
|
-
false
|
|
1836
|
-
)
|
|
1837
|
-
};
|
|
1838
|
-
}
|
|
1839
|
-
} catch (error) {
|
|
1840
|
-
if (isAbortError(error)) {
|
|
1841
|
-
return {
|
|
1842
|
-
ok: false,
|
|
1843
|
-
result: this.fail("CANCELLED", "Checkout was cancelled.", true)
|
|
1844
|
-
};
|
|
1845
|
-
}
|
|
1846
|
-
const message = error instanceof Error ? error.message : "Payment authorization failed.";
|
|
1847
|
-
return {
|
|
1848
|
-
ok: false,
|
|
1849
|
-
result: this.fail("AUTHORIZATION_FAILED", message, true)
|
|
1850
|
-
};
|
|
1851
|
-
}
|
|
1852
|
-
return {
|
|
1853
|
-
ok: true,
|
|
1854
|
-
value: authorizationResult
|
|
1855
|
-
};
|
|
1856
|
-
}
|
|
1857
|
-
async finalizeSuccess(checkoutResult) {
|
|
1858
|
-
this.emit("finalizing", {
|
|
1859
|
-
order_id: checkoutResult.order_id,
|
|
1860
|
-
order_number: checkoutResult.order_number
|
|
1861
|
-
});
|
|
1862
|
-
const enrolledInLink = await this.maybeEnrollInLink(checkoutResult.order_id);
|
|
1863
|
-
this.emit("success", {
|
|
1864
|
-
order_id: checkoutResult.order_id,
|
|
1865
|
-
order_number: checkoutResult.order_number
|
|
1866
|
-
});
|
|
1867
|
-
return {
|
|
1868
|
-
success: true,
|
|
1869
|
-
order: {
|
|
1870
|
-
id: checkoutResult.order_id,
|
|
1871
|
-
order_number: checkoutResult.order_number || checkoutResult.order_id,
|
|
1872
|
-
status: checkoutResult.payment_status || "paid",
|
|
1873
|
-
total: this.getOrderTotal(checkoutResult),
|
|
1874
|
-
currency: this.getOrderCurrency(checkoutResult)
|
|
1875
|
-
},
|
|
1876
|
-
enrolled_in_link: enrolledInLink
|
|
1877
|
-
};
|
|
1878
|
-
}
|
|
1879
|
-
async maybeEnrollInLink(orderId) {
|
|
1880
|
-
if (!this.enrollInLink || !orderId) {
|
|
1881
|
-
return false;
|
|
1882
|
-
}
|
|
1883
|
-
let businessId = this.businessId;
|
|
1884
|
-
if (!businessId) {
|
|
1885
|
-
const businessResult = await this.client.business.getInfo();
|
|
1886
|
-
if (!businessResult.ok || !businessResult.value?.id) {
|
|
1887
|
-
return false;
|
|
1888
|
-
}
|
|
1889
|
-
businessId = businessResult.value.id;
|
|
1890
|
-
}
|
|
1891
|
-
const address = this.getEnrollmentAddress();
|
|
1892
|
-
const mobileMoney = this.getEnrollmentMobileMoney();
|
|
1893
|
-
try {
|
|
1894
|
-
const enrollment = await this.client.link.enrollAndLinkOrder({
|
|
1895
|
-
order_id: orderId,
|
|
1896
|
-
business_id: businessId,
|
|
1897
|
-
order_type: this.orderType || this.checkoutData.order_type,
|
|
1898
|
-
address,
|
|
1899
|
-
mobile_money: mobileMoney
|
|
1900
|
-
});
|
|
1901
|
-
return enrollment.ok && enrollment.value.success;
|
|
1902
|
-
} catch {
|
|
1903
|
-
return false;
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
getEnrollmentAddress() {
|
|
1907
|
-
const source = this.addressData ?? this.checkoutData.address_info;
|
|
1908
|
-
if (!source?.street_address) {
|
|
1909
|
-
return void 0;
|
|
1910
|
-
}
|
|
1911
|
-
return {
|
|
1912
|
-
label: "Default",
|
|
1913
|
-
street_address: source.street_address,
|
|
1914
|
-
apartment: source.apartment || void 0,
|
|
1915
|
-
city: source.city || "",
|
|
1916
|
-
region: source.region || "",
|
|
1917
|
-
delivery_instructions: source.delivery_instructions || void 0,
|
|
1918
|
-
phone_for_delivery: source.phone_for_delivery || void 0
|
|
1919
|
-
};
|
|
1920
|
-
}
|
|
1921
|
-
getEnrollmentMobileMoney() {
|
|
1922
|
-
if (this.paymentData?.type === "mobile_money" && this.paymentData.phone_number && this.paymentData.provider && isValidMobileMoneyProvider(this.paymentData.provider)) {
|
|
1923
|
-
return {
|
|
1924
|
-
phone_number: this.paymentData.phone_number,
|
|
1925
|
-
provider: this.paymentData.provider,
|
|
1926
|
-
label: this.paymentData.label || "Mobile Money"
|
|
1927
|
-
};
|
|
1928
|
-
}
|
|
1929
|
-
if (this.checkoutData.mobile_money_details?.phone_number) {
|
|
1930
|
-
return {
|
|
1931
|
-
phone_number: this.checkoutData.mobile_money_details.phone_number,
|
|
1932
|
-
provider: this.checkoutData.mobile_money_details.provider,
|
|
1933
|
-
label: "Mobile Money"
|
|
1934
|
-
};
|
|
1935
|
-
}
|
|
1936
|
-
return void 0;
|
|
1937
|
-
}
|
|
1938
|
-
getOrderTotal(checkoutResult) {
|
|
1939
|
-
if (checkoutResult.fx?.pay_amount !== void 0) {
|
|
1940
|
-
return formatMoney2(checkoutResult.fx.pay_amount);
|
|
1941
|
-
}
|
|
1942
|
-
if (this.cartTotal !== void 0) {
|
|
1943
|
-
return formatMoney2(this.cartTotal);
|
|
1944
|
-
}
|
|
1945
|
-
return "0.00";
|
|
1946
|
-
}
|
|
1947
|
-
getOrderCurrency(checkoutResult) {
|
|
1948
|
-
if (checkoutResult.fx?.pay_currency) {
|
|
1949
|
-
return checkoutResult.fx.pay_currency;
|
|
1950
|
-
}
|
|
1951
|
-
if (this.cartCurrency) {
|
|
1952
|
-
return this.cartCurrency;
|
|
1953
|
-
}
|
|
1954
|
-
if (this.checkoutData.pay_currency) {
|
|
1955
|
-
return this.checkoutData.pay_currency;
|
|
1956
|
-
}
|
|
1957
|
-
return "GHS";
|
|
1958
|
-
}
|
|
1959
|
-
emit(status, context = {}) {
|
|
1960
|
-
if (!this.onStatusChange) {
|
|
1961
|
-
return;
|
|
1962
|
-
}
|
|
1963
|
-
try {
|
|
1964
|
-
this.onStatusChange(status, context);
|
|
1965
|
-
} catch {
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
fail(code, message, recoverable) {
|
|
1969
|
-
this.emit("failed", {
|
|
1970
|
-
display_text: message
|
|
1971
|
-
});
|
|
1972
|
-
return {
|
|
1973
|
-
success: false,
|
|
1974
|
-
error: {
|
|
1975
|
-
code,
|
|
1976
|
-
message,
|
|
1977
|
-
recoverable
|
|
1978
|
-
}
|
|
1979
|
-
};
|
|
1980
|
-
}
|
|
1981
|
-
isSuccessfulStatus(status) {
|
|
1982
|
-
const normalized = normalizeStatusResponse({ status, paid: false });
|
|
1983
|
-
return normalized.paid || isPaymentStatusSuccess(normalized.status);
|
|
1984
|
-
}
|
|
1985
|
-
isFailureStatus(status) {
|
|
1986
|
-
const normalized = normalizeStatusResponse({ status, paid: false });
|
|
1987
|
-
return isPaymentStatusFailure(normalized.status);
|
|
1988
|
-
}
|
|
1989
|
-
ensureNotAborted() {
|
|
1990
|
-
if (this.signal?.aborted) {
|
|
1991
|
-
throw createAbortError();
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1994
|
-
wait(ms) {
|
|
1995
|
-
this.ensureNotAborted();
|
|
1996
|
-
return new Promise((resolve, reject) => {
|
|
1997
|
-
const timer = setTimeout(() => {
|
|
1998
|
-
if (this.signal) {
|
|
1999
|
-
this.signal.removeEventListener("abort", onAbort);
|
|
2000
|
-
}
|
|
2001
|
-
resolve();
|
|
2002
|
-
}, ms);
|
|
2003
|
-
const onAbort = () => {
|
|
2004
|
-
clearTimeout(timer);
|
|
2005
|
-
reject(createAbortError());
|
|
2006
|
-
};
|
|
2007
|
-
if (this.signal) {
|
|
2008
|
-
this.signal.addEventListener("abort", onAbort, { once: true });
|
|
2009
|
-
}
|
|
2010
|
-
});
|
|
2011
|
-
}
|
|
2012
|
-
};
|
|
2013
|
-
|
|
2014
|
-
// src/checkout.ts
|
|
2015
|
-
function toTerminalFailure(code, message, recoverable) {
|
|
2016
|
-
return {
|
|
2017
|
-
success: false,
|
|
2018
|
-
error: {
|
|
2019
|
-
code,
|
|
2020
|
-
message,
|
|
2021
|
-
recoverable
|
|
2022
|
-
}
|
|
2023
|
-
};
|
|
2024
|
-
}
|
|
2025
|
-
function generateIdempotencyKey() {
|
|
2026
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
2027
|
-
return `idem_${crypto.randomUUID()}`;
|
|
2028
|
-
}
|
|
2029
|
-
const timestamp = Date.now().toString(36);
|
|
2030
|
-
const random = Math.random().toString(36).substring(2, 15);
|
|
2031
|
-
return `idem_${timestamp}_${random}`;
|
|
2032
|
-
}
|
|
2033
|
-
var CheckoutService = class {
|
|
2034
|
-
constructor(client) {
|
|
2035
|
-
this.client = client;
|
|
2036
|
-
}
|
|
2037
|
-
orderTokenParam(orderId) {
|
|
2038
|
-
const token = this.client.getOrderToken(orderId);
|
|
2039
|
-
return token ? `?token=${encodeURIComponent(token)}` : "";
|
|
2040
|
-
}
|
|
2041
|
-
async process(data) {
|
|
2042
|
-
const checkoutData = {
|
|
2043
|
-
...data,
|
|
2044
|
-
idempotency_key: data.idempotency_key || generateIdempotencyKey()
|
|
2045
|
-
};
|
|
2046
|
-
const result = await safe(
|
|
2047
|
-
this.client.post("/api/v1/checkout", {
|
|
2048
|
-
checkout_data: checkoutData
|
|
2049
|
-
})
|
|
2050
|
-
);
|
|
2051
|
-
if (result.ok && result.value.bill_token) {
|
|
2052
|
-
this.client.setOrderToken(result.value.order_id, result.value.bill_token);
|
|
2053
|
-
}
|
|
2054
|
-
return result;
|
|
2055
|
-
}
|
|
2056
|
-
async initializePayment(_orderId, _method) {
|
|
2057
|
-
return err(
|
|
2058
|
-
new CimplifyError(
|
|
2059
|
-
"NOT_IMPLEMENTED",
|
|
2060
|
-
"initializePayment is not available in REST mode.",
|
|
2061
|
-
false
|
|
2062
|
-
)
|
|
2063
|
-
);
|
|
2064
|
-
}
|
|
2065
|
-
async submitAuthorization(input) {
|
|
2066
|
-
return safe(this.client.post("/api/v1/payments/authorization", input));
|
|
2067
|
-
}
|
|
2068
|
-
async pollPaymentStatus(orderId) {
|
|
2069
|
-
const encodedId = encodeURIComponent(orderId);
|
|
2070
|
-
const tokenParam = this.orderTokenParam(orderId);
|
|
2071
|
-
return safe(this.client.get(`/api/v1/orders/${encodedId}/payment-status${tokenParam}`));
|
|
2072
|
-
}
|
|
2073
|
-
async updateOrderCustomer(orderId, customer) {
|
|
2074
|
-
const encodedId = encodeURIComponent(orderId);
|
|
2075
|
-
const tokenParam = this.orderTokenParam(orderId);
|
|
2076
|
-
return safe(this.client.post(`/api/v1/orders/${encodedId}/customer${tokenParam}`, customer));
|
|
2077
|
-
}
|
|
2078
|
-
async verifyPayment(orderId) {
|
|
2079
|
-
const encodedId = encodeURIComponent(orderId);
|
|
2080
|
-
const tokenParam = this.orderTokenParam(orderId);
|
|
2081
|
-
return safe(this.client.post(`/api/v1/orders/${encodedId}/verify-payment${tokenParam}`));
|
|
2082
|
-
}
|
|
2083
|
-
async processAndResolve(data) {
|
|
2084
|
-
data.on_status_change?.("preparing", {});
|
|
2085
|
-
if (!data.cart_id) {
|
|
2086
|
-
return ok(
|
|
2087
|
-
toTerminalFailure(
|
|
2088
|
-
"INVALID_CART",
|
|
2089
|
-
"A valid cart is required before checkout can start.",
|
|
2090
|
-
false
|
|
2091
|
-
)
|
|
2092
|
-
);
|
|
2093
|
-
}
|
|
2094
|
-
const cartResult = await this.client.cart.get();
|
|
2095
|
-
if (!cartResult.ok || !cartResult.value?.id) {
|
|
2096
|
-
return ok(
|
|
2097
|
-
toTerminalFailure(
|
|
2098
|
-
"INVALID_CART",
|
|
2099
|
-
"Unable to load cart for checkout.",
|
|
2100
|
-
false
|
|
2101
|
-
)
|
|
2102
|
-
);
|
|
2103
|
-
}
|
|
2104
|
-
const cart = cartResult.value;
|
|
2105
|
-
if (cart.id !== data.cart_id || cart.items.length === 0) {
|
|
2106
|
-
return ok(
|
|
2107
|
-
toTerminalFailure(
|
|
2108
|
-
"INVALID_CART",
|
|
2109
|
-
"Cart is empty or no longer valid. Please refresh and try again.",
|
|
2110
|
-
false
|
|
2111
|
-
)
|
|
2112
|
-
);
|
|
2113
|
-
}
|
|
2114
|
-
const checkoutData = {
|
|
2115
|
-
cart_id: data.cart_id,
|
|
2116
|
-
location_id: data.location_id,
|
|
2117
|
-
customer: data.customer,
|
|
2118
|
-
order_type: data.order_type,
|
|
2119
|
-
address_info: data.address_info,
|
|
2120
|
-
payment_method: data.payment_method,
|
|
2121
|
-
mobile_money_details: data.mobile_money_details,
|
|
2122
|
-
special_instructions: data.special_instructions,
|
|
2123
|
-
link_address_id: data.link_address_id,
|
|
2124
|
-
link_payment_method_id: data.link_payment_method_id,
|
|
2125
|
-
idempotency_key: data.idempotency_key || generateIdempotencyKey(),
|
|
2126
|
-
metadata: data.metadata,
|
|
2127
|
-
pay_currency: data.pay_currency,
|
|
2128
|
-
fx_quote_id: data.fx_quote_id
|
|
2129
|
-
};
|
|
2130
|
-
const baseCurrency = currencyCode(
|
|
2131
|
-
(cart.pricing.currency || checkoutData.pay_currency || "GHS").toUpperCase()
|
|
2132
|
-
);
|
|
2133
|
-
const payCurrency = data.pay_currency?.trim().toUpperCase();
|
|
2134
|
-
const payCurrencyCode = payCurrency ? currencyCode(payCurrency) : void 0;
|
|
2135
|
-
const cartTotalAmount = typeof cart.pricing.total_price === "number" ? cart.pricing.total_price : Number.parseFloat(cart.pricing.total_price || "0");
|
|
2136
|
-
if (payCurrencyCode && payCurrencyCode !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
|
|
2137
|
-
const fxQuoteResult = await this.client.fx.lockQuote({
|
|
2138
|
-
from: baseCurrency,
|
|
2139
|
-
to: payCurrencyCode,
|
|
2140
|
-
amount: cart.pricing.total_price
|
|
2141
|
-
});
|
|
2142
|
-
if (!fxQuoteResult.ok) {
|
|
2143
|
-
return ok(
|
|
2144
|
-
toTerminalFailure(
|
|
2145
|
-
"FX_QUOTE_FAILED",
|
|
2146
|
-
fxQuoteResult.error.message || "Unable to lock FX quote for checkout.",
|
|
2147
|
-
true
|
|
2148
|
-
)
|
|
2149
|
-
);
|
|
2150
|
-
}
|
|
2151
|
-
checkoutData.pay_currency = payCurrencyCode;
|
|
2152
|
-
checkoutData.fx_quote_id = fxQuoteResult.value.id;
|
|
2153
|
-
}
|
|
2154
|
-
data.on_status_change?.("processing", {});
|
|
2155
|
-
const processResult = await this.process(checkoutData);
|
|
2156
|
-
if (!processResult.ok) {
|
|
2157
|
-
return ok(
|
|
2158
|
-
toTerminalFailure(
|
|
2159
|
-
processResult.error.code || "CHECKOUT_FAILED",
|
|
2160
|
-
processResult.error.message || "Unable to process checkout.",
|
|
2161
|
-
processResult.error.retryable ?? true
|
|
2162
|
-
)
|
|
2163
|
-
);
|
|
2164
|
-
}
|
|
2165
|
-
const resolver = new CheckoutResolver({
|
|
2166
|
-
client: this.client,
|
|
2167
|
-
checkoutData,
|
|
2168
|
-
pollIntervalMs: data.poll_interval_ms,
|
|
2169
|
-
maxPollAttempts: data.max_poll_attempts,
|
|
2170
|
-
onStatusChange: data.on_status_change,
|
|
2171
|
-
onAuthorizationRequired: data.on_authorization_required,
|
|
2172
|
-
signal: data.signal,
|
|
2173
|
-
returnUrl: data.return_url,
|
|
2174
|
-
enrollInLink: data.enroll_in_link,
|
|
2175
|
-
cartTotal: cart.pricing.total_price,
|
|
2176
|
-
cartCurrency: checkoutData.pay_currency || cart.pricing.currency || "GHS",
|
|
2177
|
-
orderType: checkoutData.order_type,
|
|
2178
|
-
allowPopups: data.allow_popups
|
|
2179
|
-
});
|
|
2180
|
-
const resolved = await resolver.resolve(processResult.value);
|
|
2181
|
-
return ok(resolved);
|
|
2182
|
-
}
|
|
2183
|
-
};
|
|
2184
|
-
|
|
2185
|
-
// src/orders.ts
|
|
2186
|
-
var OrderQueries = class {
|
|
2187
|
-
constructor(client) {
|
|
2188
|
-
this.client = client;
|
|
2189
|
-
}
|
|
2190
|
-
orderTokenParam(orderId) {
|
|
2191
|
-
const token = this.client.getOrderToken(orderId);
|
|
2192
|
-
return token ? `?token=${encodeURIComponent(token)}` : "";
|
|
2193
|
-
}
|
|
2194
|
-
async list(options) {
|
|
2195
|
-
const params = new URLSearchParams();
|
|
2196
|
-
if (options?.status) params.set("status", options.status);
|
|
2197
|
-
if (options?.limit) params.set("limit", String(options.limit));
|
|
2198
|
-
if (options?.offset) params.set("offset", String(options.offset));
|
|
2199
|
-
const path = params.toString() ? `/api/v1/orders?${params.toString()}` : "/api/v1/orders";
|
|
2200
|
-
return safe(this.client.get(path));
|
|
2201
|
-
}
|
|
2202
|
-
async get(orderId) {
|
|
2203
|
-
const encodedId = encodeURIComponent(orderId);
|
|
2204
|
-
const tokenParam = this.orderTokenParam(orderId);
|
|
2205
|
-
return safe(this.client.get(`/api/v1/orders/${encodedId}${tokenParam}`));
|
|
2206
|
-
}
|
|
2207
|
-
async getRecent(limit = 5) {
|
|
2208
|
-
return this.list({ limit });
|
|
2209
|
-
}
|
|
2210
|
-
async getByStatus(status) {
|
|
2211
|
-
return this.list({ status });
|
|
2212
|
-
}
|
|
2213
|
-
async cancel(orderId, reason) {
|
|
2214
|
-
const encodedId = encodeURIComponent(orderId);
|
|
2215
|
-
const tokenParam = this.orderTokenParam(orderId);
|
|
2216
|
-
return safe(
|
|
2217
|
-
this.client.post(`/api/v1/orders/${encodedId}/cancel${tokenParam}`, {
|
|
2218
|
-
reason
|
|
2219
|
-
})
|
|
2220
|
-
);
|
|
2221
|
-
}
|
|
2222
|
-
};
|
|
2223
|
-
|
|
2224
|
-
// src/link.ts
|
|
2225
|
-
function encodePathSegment(value) {
|
|
2226
|
-
return encodeURIComponent(value);
|
|
2227
|
-
}
|
|
2228
|
-
function toCreateAddressPayload(input) {
|
|
2229
|
-
return {
|
|
2230
|
-
label: input.label,
|
|
2231
|
-
street_address: input.address_line1,
|
|
2232
|
-
apartment: input.address_line2,
|
|
2233
|
-
city: input.city,
|
|
2234
|
-
region: input.state ?? "",
|
|
2235
|
-
postal_code: input.postal_code,
|
|
2236
|
-
country: input.country
|
|
2237
|
-
};
|
|
2238
|
-
}
|
|
2239
|
-
function toUpdateAddressPayload(input) {
|
|
2240
|
-
return {
|
|
2241
|
-
label: input.label,
|
|
2242
|
-
street_address: input.address_line1,
|
|
2243
|
-
apartment: input.address_line2,
|
|
2244
|
-
city: input.city,
|
|
2245
|
-
region: input.state,
|
|
2246
|
-
postal_code: input.postal_code,
|
|
2247
|
-
country: input.country
|
|
2248
|
-
};
|
|
2249
|
-
}
|
|
2250
|
-
function toCreateMobileMoneyPayload(input) {
|
|
2251
|
-
return {
|
|
2252
|
-
phone_number: input.phone_number,
|
|
2253
|
-
provider: input.provider,
|
|
2254
|
-
label: input.account_name
|
|
2255
|
-
};
|
|
2256
|
-
}
|
|
2257
|
-
var LinkService = class {
|
|
2258
|
-
constructor(client) {
|
|
2259
|
-
this.client = client;
|
|
2260
|
-
}
|
|
2261
|
-
async getProfile() {
|
|
2262
|
-
return safe(this.client.linkGet("/v1/link/profile"));
|
|
2263
|
-
}
|
|
2264
|
-
async requestOtp(input) {
|
|
2265
|
-
return safe(this.client.linkPost("/v1/link/auth/request-otp", input));
|
|
2266
|
-
}
|
|
2267
|
-
async verifyOtp(input) {
|
|
2268
|
-
const result = await safe(
|
|
2269
|
-
this.client.linkPost("/v1/link/auth/verify-otp", input)
|
|
2270
|
-
);
|
|
2271
|
-
if (result.ok && result.value.session_token) {
|
|
2272
|
-
this.client.setAccessToken(result.value.session_token);
|
|
2273
|
-
}
|
|
2274
|
-
return result;
|
|
2275
|
-
}
|
|
2276
|
-
async logout() {
|
|
2277
|
-
const result = await safe(this.client.linkPost("/v1/link/auth/logout"));
|
|
2278
|
-
if (result.ok) {
|
|
2279
|
-
this.client.clearSession();
|
|
2280
|
-
}
|
|
2281
|
-
return result;
|
|
2282
|
-
}
|
|
2283
|
-
async checkStatus(contact) {
|
|
2284
|
-
return safe(this.client.linkPost("/v1/link/check-status", { contact }));
|
|
2285
|
-
}
|
|
2286
|
-
async getLinkData() {
|
|
2287
|
-
return safe(this.client.linkGet("/v1/link/data"));
|
|
2288
|
-
}
|
|
2289
|
-
async getAddresses() {
|
|
2290
|
-
return safe(this.client.linkGet("/v1/link/addresses"));
|
|
2291
|
-
}
|
|
2292
|
-
async getMobileMoney() {
|
|
2293
|
-
return safe(this.client.linkGet("/v1/link/mobile-money"));
|
|
2294
|
-
}
|
|
2295
|
-
async getPreferences() {
|
|
2296
|
-
return safe(this.client.linkGet("/v1/link/preferences"));
|
|
2297
|
-
}
|
|
2298
|
-
async enroll(data) {
|
|
2299
|
-
return safe(this.client.linkPost("/v1/link/enroll", data));
|
|
2300
|
-
}
|
|
2301
|
-
async enrollAndLinkOrder(data) {
|
|
2302
|
-
return safe(
|
|
2303
|
-
this.client.linkPost("/v1/link/enroll-and-link-order", data)
|
|
2304
|
-
);
|
|
2305
|
-
}
|
|
2306
|
-
async updatePreferences(preferences) {
|
|
2307
|
-
return safe(this.client.linkPost("/v1/link/preferences", preferences));
|
|
2308
|
-
}
|
|
2309
|
-
async createAddress(input) {
|
|
2310
|
-
return safe(
|
|
2311
|
-
this.client.linkPost("/v1/link/addresses", toCreateAddressPayload(input))
|
|
2312
|
-
);
|
|
2313
|
-
}
|
|
2314
|
-
async updateAddress(input) {
|
|
2315
|
-
return safe(
|
|
2316
|
-
this.client.linkPost(
|
|
2317
|
-
`/v1/link/addresses/${encodePathSegment(input.address_id)}`,
|
|
2318
|
-
toUpdateAddressPayload(input)
|
|
2319
|
-
)
|
|
2320
|
-
);
|
|
2321
|
-
}
|
|
2322
|
-
async deleteAddress(addressId) {
|
|
2323
|
-
return safe(
|
|
2324
|
-
this.client.linkDelete(`/v1/link/addresses/${encodePathSegment(addressId)}`)
|
|
2325
|
-
);
|
|
2326
|
-
}
|
|
2327
|
-
async setDefaultAddress(addressId) {
|
|
2328
|
-
return safe(
|
|
2329
|
-
this.client.linkPost(
|
|
2330
|
-
`/v1/link/addresses/${encodePathSegment(addressId)}/default`
|
|
2331
|
-
)
|
|
2332
|
-
);
|
|
2333
|
-
}
|
|
2334
|
-
async trackAddressUsage(addressId) {
|
|
2335
|
-
return safe(
|
|
2336
|
-
this.client.linkPost(
|
|
2337
|
-
`/v1/link/addresses/${encodePathSegment(addressId)}/track-usage`
|
|
2338
|
-
)
|
|
2339
|
-
);
|
|
2340
|
-
}
|
|
2341
|
-
async createMobileMoney(input) {
|
|
2342
|
-
return safe(
|
|
2343
|
-
this.client.linkPost(
|
|
2344
|
-
"/v1/link/mobile-money",
|
|
2345
|
-
toCreateMobileMoneyPayload(input)
|
|
2346
|
-
)
|
|
2347
|
-
);
|
|
2348
|
-
}
|
|
2349
|
-
async deleteMobileMoney(mobileMoneyId) {
|
|
2350
|
-
return safe(
|
|
2351
|
-
this.client.linkDelete(
|
|
2352
|
-
`/v1/link/mobile-money/${encodePathSegment(mobileMoneyId)}`
|
|
2353
|
-
)
|
|
2354
|
-
);
|
|
2355
|
-
}
|
|
2356
|
-
async setDefaultMobileMoney(mobileMoneyId) {
|
|
2357
|
-
return safe(
|
|
2358
|
-
this.client.linkPost(
|
|
2359
|
-
`/v1/link/mobile-money/${encodePathSegment(mobileMoneyId)}/default`
|
|
2360
|
-
)
|
|
2361
|
-
);
|
|
2362
|
-
}
|
|
2363
|
-
async trackMobileMoneyUsage(mobileMoneyId) {
|
|
2364
|
-
return safe(
|
|
2365
|
-
this.client.linkPost(
|
|
2366
|
-
`/v1/link/mobile-money/${encodePathSegment(mobileMoneyId)}/track-usage`
|
|
2367
|
-
)
|
|
2368
|
-
);
|
|
2369
|
-
}
|
|
2370
|
-
async verifyMobileMoney(mobileMoneyId) {
|
|
2371
|
-
return safe(
|
|
2372
|
-
this.client.linkPost(
|
|
2373
|
-
`/v1/link/mobile-money/${encodePathSegment(mobileMoneyId)}/verify`
|
|
2374
|
-
)
|
|
2375
|
-
);
|
|
2376
|
-
}
|
|
2377
|
-
async getSessions() {
|
|
2378
|
-
return safe(this.client.linkGet("/v1/link/sessions"));
|
|
2379
|
-
}
|
|
2380
|
-
async revokeSession(sessionId) {
|
|
2381
|
-
return safe(this.client.linkDelete(`/v1/link/sessions/${sessionId}`));
|
|
2382
|
-
}
|
|
2383
|
-
async revokeAllSessions() {
|
|
2384
|
-
return safe(this.client.linkDelete("/v1/link/sessions"));
|
|
2385
|
-
}
|
|
2386
|
-
};
|
|
2387
|
-
|
|
2388
|
-
// src/auth.ts
|
|
2389
|
-
var AuthService = class {
|
|
2390
|
-
constructor(client) {
|
|
2391
|
-
this.client = client;
|
|
2392
|
-
}
|
|
2393
|
-
async getStatus() {
|
|
2394
|
-
return safe(this.client.get("/api/v1/auth/status"));
|
|
2395
|
-
}
|
|
2396
|
-
async getCurrentUser() {
|
|
2397
|
-
const result = await this.getStatus();
|
|
2398
|
-
if (!result.ok) return result;
|
|
2399
|
-
return ok(result.value.customer || null);
|
|
2400
|
-
}
|
|
2401
|
-
async isAuthenticated() {
|
|
2402
|
-
const result = await this.getStatus();
|
|
2403
|
-
if (!result.ok) return result;
|
|
2404
|
-
return ok(result.value.is_authenticated);
|
|
2405
|
-
}
|
|
2406
|
-
async requestOtp(contact, contactType) {
|
|
2407
|
-
return safe(
|
|
2408
|
-
this.client.post("/api/v1/auth/request-otp", {
|
|
2409
|
-
contact,
|
|
2410
|
-
contact_type: contactType
|
|
2411
|
-
})
|
|
2412
|
-
);
|
|
2413
|
-
}
|
|
2414
|
-
async verifyOtp(code, contact) {
|
|
2415
|
-
return safe(
|
|
2416
|
-
this.client.post("/api/v1/auth/verify-otp", {
|
|
2417
|
-
otp_code: code,
|
|
2418
|
-
contact
|
|
2419
|
-
})
|
|
2420
|
-
);
|
|
2421
|
-
}
|
|
2422
|
-
async logout() {
|
|
2423
|
-
return safe(this.client.post("/api/v1/auth/logout"));
|
|
2424
|
-
}
|
|
2425
|
-
async updateProfile(input) {
|
|
2426
|
-
return safe(this.client.post("/api/v1/auth/profile", input));
|
|
2427
|
-
}
|
|
2428
|
-
};
|
|
2429
|
-
|
|
2430
|
-
// src/business.ts
|
|
2431
|
-
var BusinessService = class {
|
|
2432
|
-
constructor(client) {
|
|
2433
|
-
this.client = client;
|
|
2434
|
-
}
|
|
2435
|
-
async getInfo() {
|
|
2436
|
-
return safe(this.client.get("/api/v1/business"));
|
|
2437
|
-
}
|
|
2438
|
-
async getByHandle(handle) {
|
|
2439
|
-
const encodedHandle = encodeURIComponent(handle);
|
|
2440
|
-
return safe(this.client.get(`/api/v1/business/by-handle/${encodedHandle}`));
|
|
2441
|
-
}
|
|
2442
|
-
async getByDomain(domain) {
|
|
2443
|
-
const encodedDomain = encodeURIComponent(domain);
|
|
2444
|
-
return safe(this.client.get(`/api/v1/business/by-domain?domain=${encodedDomain}`));
|
|
2445
|
-
}
|
|
2446
|
-
async getSettings() {
|
|
2447
|
-
return safe(this.client.get("/api/v1/business/settings"));
|
|
2448
|
-
}
|
|
2449
|
-
async getLocations() {
|
|
2450
|
-
return safe(this.client.get("/api/v1/business/locations"));
|
|
2451
|
-
}
|
|
2452
|
-
async getLocation(locationId) {
|
|
2453
|
-
const result = await this.getLocations();
|
|
2454
|
-
if (!result.ok) return err(result.error);
|
|
2455
|
-
const location = result.value.find((item) => item.id === locationId);
|
|
2456
|
-
if (!location) {
|
|
2457
|
-
return err(new CimplifyError(ErrorCode.NOT_FOUND, `Location not found: ${locationId}`, false));
|
|
2458
|
-
}
|
|
2459
|
-
return ok(location);
|
|
2460
|
-
}
|
|
2461
|
-
async getHours() {
|
|
2462
|
-
return safe(this.client.get("/api/v1/business/hours"));
|
|
2463
|
-
}
|
|
2464
|
-
async getLocationHours(locationId) {
|
|
2465
|
-
const result = await this.getHours();
|
|
2466
|
-
if (!result.ok) return result;
|
|
2467
|
-
return ok(result.value.filter((hour) => hour.location_id === locationId));
|
|
2468
|
-
}
|
|
2469
|
-
async getBootstrap() {
|
|
2470
|
-
return safe(this.client.get("/api/v1/bootstrap"));
|
|
2471
|
-
}
|
|
2472
|
-
};
|
|
2473
|
-
|
|
2474
|
-
// src/inventory.ts
|
|
2475
|
-
var InventoryService = class {
|
|
2476
|
-
constructor(client) {
|
|
2477
|
-
this.client = client;
|
|
2478
|
-
}
|
|
2479
|
-
withQuery(path, params) {
|
|
2480
|
-
const searchParams = new URLSearchParams();
|
|
2481
|
-
for (const [key, value] of Object.entries(params)) {
|
|
2482
|
-
if (value === void 0) continue;
|
|
2483
|
-
searchParams.set(key, String(value));
|
|
2484
|
-
}
|
|
2485
|
-
const query = searchParams.toString();
|
|
2486
|
-
return query ? `${path}?${query}` : path;
|
|
2487
|
-
}
|
|
2488
|
-
async getProductStock(productId, locationId) {
|
|
2489
|
-
const encodedId = encodeURIComponent(productId);
|
|
2490
|
-
const path = this.withQuery(`/api/v1/inventory/products/${encodedId}/stock`, {
|
|
2491
|
-
location_id: locationId
|
|
2492
|
-
});
|
|
2493
|
-
return safe(this.client.get(path));
|
|
2494
|
-
}
|
|
2495
|
-
async getVariantStock(variantId, locationId) {
|
|
2496
|
-
const encodedId = encodeURIComponent(variantId);
|
|
2497
|
-
const path = this.withQuery(`/api/v1/inventory/variants/${encodedId}/stock`, {
|
|
2498
|
-
location_id: locationId
|
|
2499
|
-
});
|
|
2500
|
-
return safe(this.client.get(path));
|
|
2501
|
-
}
|
|
2502
|
-
async checkProductAvailability(productId, quantity, locationId) {
|
|
2503
|
-
const encodedId = encodeURIComponent(productId);
|
|
2504
|
-
const path = this.withQuery(`/api/v1/inventory/products/${encodedId}/availability`, {
|
|
2505
|
-
quantity,
|
|
2506
|
-
location_id: locationId
|
|
2507
|
-
});
|
|
2508
|
-
return safe(this.client.get(path));
|
|
2509
|
-
}
|
|
2510
|
-
async checkVariantAvailability(variantId, quantity, locationId) {
|
|
2511
|
-
const encodedId = encodeURIComponent(variantId);
|
|
2512
|
-
const path = this.withQuery(`/api/v1/inventory/variants/${encodedId}/availability`, {
|
|
2513
|
-
quantity,
|
|
2514
|
-
location_id: locationId
|
|
2515
|
-
});
|
|
2516
|
-
return safe(this.client.get(path));
|
|
2517
|
-
}
|
|
2518
|
-
async checkMultipleAvailability(items, locationId) {
|
|
2519
|
-
const results = await Promise.all(
|
|
2520
|
-
items.map(
|
|
2521
|
-
(item) => item.variant_id ? this.checkVariantAvailability(item.variant_id, item.quantity, locationId) : this.checkProductAvailability(item.product_id, item.quantity, locationId)
|
|
2522
|
-
)
|
|
2523
|
-
);
|
|
2524
|
-
for (const result of results) {
|
|
2525
|
-
if (!result.ok) return result;
|
|
2526
|
-
}
|
|
2527
|
-
return ok(results.map((r) => r.value));
|
|
2528
|
-
}
|
|
2529
|
-
async isInStock(productId, locationId) {
|
|
2530
|
-
const result = await this.checkProductAvailability(productId, 1, locationId);
|
|
2531
|
-
if (!result.ok) return result;
|
|
2532
|
-
return ok(result.value.is_available);
|
|
2533
|
-
}
|
|
2534
|
-
async getAvailableQuantity(productId, locationId) {
|
|
2535
|
-
const result = await this.getProductStock(productId, locationId);
|
|
2536
|
-
if (!result.ok) return result;
|
|
2537
|
-
return ok(result.value.available_quantity);
|
|
2538
|
-
}
|
|
2539
|
-
};
|
|
2540
|
-
|
|
2541
|
-
// src/scheduling.ts
|
|
2542
|
-
function withQuery2(path, params) {
|
|
2543
|
-
const searchParams = new URLSearchParams();
|
|
2544
|
-
for (const [key, value] of Object.entries(params)) {
|
|
2545
|
-
if (value === void 0) continue;
|
|
2546
|
-
searchParams.set(key, String(value));
|
|
2547
|
-
}
|
|
2548
|
-
const query = searchParams.toString();
|
|
2549
|
-
return query ? `${path}?${query}` : path;
|
|
2550
|
-
}
|
|
2551
|
-
function firstScheduledTime(booking) {
|
|
2552
|
-
return booking.service_items.find((item) => typeof item.scheduled_start === "string")?.scheduled_start || booking.created_at;
|
|
2553
|
-
}
|
|
2554
|
-
var SchedulingService = class {
|
|
2555
|
-
constructor(client) {
|
|
2556
|
-
this.client = client;
|
|
2557
|
-
}
|
|
2558
|
-
async getServices() {
|
|
2559
|
-
return safe(this.client.get("/api/v1/scheduling/services"));
|
|
2560
|
-
}
|
|
2561
|
-
async getService(serviceId) {
|
|
2562
|
-
const encodedId = encodeURIComponent(serviceId);
|
|
2563
|
-
return safe(this.client.get(`/api/v1/scheduling/services/${encodedId}`));
|
|
2564
|
-
}
|
|
2565
|
-
async getAvailableSlots(input) {
|
|
2566
|
-
const path = withQuery2("/api/v1/scheduling/slots", {
|
|
2567
|
-
service_id: input.service_id,
|
|
2568
|
-
date: input.date,
|
|
2569
|
-
participant_count: input.participant_count,
|
|
2570
|
-
duration_minutes: input.duration_minutes
|
|
2571
|
-
});
|
|
2572
|
-
return safe(this.client.get(path));
|
|
2573
|
-
}
|
|
2574
|
-
async checkSlotAvailability(input) {
|
|
2575
|
-
const path = withQuery2("/api/v1/scheduling/slots/check", {
|
|
2576
|
-
service_id: input.service_id,
|
|
2577
|
-
slot_time: input.slot_time,
|
|
2578
|
-
duration_minutes: input.duration_minutes,
|
|
2579
|
-
participant_count: input.participant_count
|
|
2580
|
-
});
|
|
2581
|
-
return safe(this.client.get(path));
|
|
2582
|
-
}
|
|
2583
|
-
async getServiceAvailability(params) {
|
|
2584
|
-
const path = withQuery2("/api/v1/scheduling/availability", {
|
|
2585
|
-
service_id: params.service_id,
|
|
2586
|
-
start_date: params.start_date,
|
|
2587
|
-
end_date: params.end_date,
|
|
2588
|
-
location_id: params.location_id,
|
|
2589
|
-
participant_count: params.participant_count
|
|
2590
|
-
});
|
|
2591
|
-
return safe(this.client.get(path));
|
|
2592
|
-
}
|
|
2593
|
-
async getBooking(bookingId) {
|
|
2594
|
-
const encodedId = encodeURIComponent(bookingId);
|
|
2595
|
-
return safe(this.client.get(`/api/v1/scheduling/bookings/${encodedId}`));
|
|
2596
|
-
}
|
|
2597
|
-
async getCustomerBookings(customerId) {
|
|
2598
|
-
const path = withQuery2("/api/v1/scheduling/bookings", {
|
|
2599
|
-
customer_id: customerId
|
|
2600
|
-
});
|
|
2601
|
-
return safe(this.client.get(path));
|
|
2602
|
-
}
|
|
2603
|
-
async getUpcomingBookings() {
|
|
2604
|
-
const result = await this.getCustomerBookings();
|
|
2605
|
-
if (!result.ok) return result;
|
|
2606
|
-
const upcoming = result.value.filter((booking) => {
|
|
2607
|
-
const status = String(booking.status).toLowerCase();
|
|
2608
|
-
return status !== "completed" && status !== "cancelled";
|
|
2609
|
-
}).sort((a, b) => firstScheduledTime(a).localeCompare(firstScheduledTime(b)));
|
|
2610
|
-
return ok(upcoming);
|
|
2611
|
-
}
|
|
2612
|
-
async getPastBookings(limit = 10) {
|
|
2613
|
-
const result = await this.getCustomerBookings();
|
|
2614
|
-
if (!result.ok) return result;
|
|
2615
|
-
const past = result.value.filter((booking) => String(booking.status).toLowerCase() === "completed").sort((a, b) => firstScheduledTime(b).localeCompare(firstScheduledTime(a))).slice(0, Math.max(0, limit));
|
|
2616
|
-
return ok(past);
|
|
2617
|
-
}
|
|
2618
|
-
async cancelBooking(input) {
|
|
2619
|
-
const encodedId = encodeURIComponent(input.booking_id);
|
|
2620
|
-
return safe(
|
|
2621
|
-
this.client.post(`/api/v1/scheduling/bookings/${encodedId}/cancel`, {
|
|
2622
|
-
reason: input.reason
|
|
2623
|
-
})
|
|
2624
|
-
);
|
|
2625
|
-
}
|
|
2626
|
-
async rescheduleBooking(input) {
|
|
2627
|
-
return safe(
|
|
2628
|
-
this.client.post("/api/v1/scheduling/bookings/reschedule", input)
|
|
2629
|
-
);
|
|
2630
|
-
}
|
|
2631
|
-
async getNextAvailableSlot(serviceId, fromDate) {
|
|
2632
|
-
const date = fromDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2633
|
-
const result = await this.getAvailableSlots({
|
|
2634
|
-
service_id: serviceId,
|
|
2635
|
-
date
|
|
2636
|
-
});
|
|
2637
|
-
if (!result.ok) return result;
|
|
2638
|
-
return ok(result.value.find((slot) => slot.is_available) || null);
|
|
2639
|
-
}
|
|
2640
|
-
async hasAvailabilityOn(serviceId, date) {
|
|
2641
|
-
const result = await this.getAvailableSlots({
|
|
2642
|
-
service_id: serviceId,
|
|
2643
|
-
date
|
|
2644
|
-
});
|
|
2645
|
-
if (!result.ok) return result;
|
|
2646
|
-
return ok(result.value.some((slot) => slot.is_available));
|
|
2647
|
-
}
|
|
2648
|
-
};
|
|
2649
|
-
|
|
2650
|
-
// src/lite.ts
|
|
2651
|
-
var LiteService = class {
|
|
2652
|
-
constructor(client) {
|
|
2653
|
-
this.client = client;
|
|
2654
|
-
}
|
|
2655
|
-
async getBootstrap() {
|
|
2656
|
-
return safe(this.client.get("/api/v1/lite/bootstrap"));
|
|
2657
|
-
}
|
|
2658
|
-
async getTable(tableId) {
|
|
2659
|
-
const encodedId = encodeURIComponent(tableId);
|
|
2660
|
-
return safe(this.client.get(`/api/v1/lite/tables/${encodedId}`));
|
|
2661
|
-
}
|
|
2662
|
-
async getMenu() {
|
|
2663
|
-
return safe(this.client.get("/api/v1/lite/menu"));
|
|
2664
|
-
}
|
|
2665
|
-
async getMenuByCategory(categoryId) {
|
|
2666
|
-
const encodedId = encodeURIComponent(categoryId);
|
|
2667
|
-
return safe(this.client.get(`/api/v1/lite/menu/categories/${encodedId}`));
|
|
2668
|
-
}
|
|
2669
|
-
};
|
|
2670
|
-
|
|
2671
|
-
// src/fx.ts
|
|
2672
|
-
var FxService = class {
|
|
2673
|
-
constructor(client) {
|
|
2674
|
-
this.client = client;
|
|
2675
|
-
}
|
|
2676
|
-
async getRate(from, to) {
|
|
2677
|
-
const path = `/api/v1/fx/rate?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`;
|
|
2678
|
-
return safe(this.client.get(path));
|
|
2679
|
-
}
|
|
2680
|
-
async lockQuote(request) {
|
|
2681
|
-
return safe(this.client.post("/api/v1/fx/quotes", request));
|
|
2682
|
-
}
|
|
2683
|
-
};
|
|
2684
|
-
|
|
2685
|
-
// src/activity.ts
|
|
2686
|
-
var ActivityService = class {
|
|
2687
|
-
constructor(client) {
|
|
2688
|
-
this.client = client;
|
|
2689
|
-
}
|
|
2690
|
-
trackProductView(productId, options = {}) {
|
|
2691
|
-
const event = {
|
|
2692
|
-
type: "product_view",
|
|
2693
|
-
product_id: productId,
|
|
2694
|
-
product_name: options.productName,
|
|
2695
|
-
category_id: options.categoryId,
|
|
2696
|
-
price: options.price,
|
|
2697
|
-
page_path: options.pagePath ?? currentPath()
|
|
2698
|
-
};
|
|
2699
|
-
this.sendEvents([event]);
|
|
2700
|
-
}
|
|
2701
|
-
trackCategoryView(categoryId, options = {}) {
|
|
2702
|
-
const event = {
|
|
2703
|
-
type: "category_view",
|
|
2704
|
-
category_id: categoryId,
|
|
2705
|
-
category_name: options.categoryName,
|
|
2706
|
-
page_path: options.pagePath ?? currentPath()
|
|
2707
|
-
};
|
|
2708
|
-
this.sendEvents([event]);
|
|
2709
|
-
}
|
|
2710
|
-
sendEvents(events) {
|
|
2711
|
-
try {
|
|
2712
|
-
this.client.post("/api/v1/activity/events", { events }).catch(() => {
|
|
2713
|
-
});
|
|
2714
|
-
} catch {
|
|
2715
|
-
}
|
|
2716
|
-
}
|
|
2717
|
-
};
|
|
2718
|
-
function currentPath() {
|
|
2719
|
-
if (typeof window !== "undefined") {
|
|
2720
|
-
return window.location.pathname;
|
|
2721
|
-
}
|
|
2722
|
-
return void 0;
|
|
2723
|
-
}
|
|
2724
|
-
|
|
2725
|
-
// src/types/elements.ts
|
|
2726
|
-
var ELEMENT_TYPES = {
|
|
2727
|
-
AUTH: "auth",
|
|
2728
|
-
ADDRESS: "address",
|
|
2729
|
-
PAYMENT: "payment",
|
|
2730
|
-
CHECKOUT: "checkout"
|
|
2731
|
-
};
|
|
2732
|
-
var MESSAGE_TYPES = {
|
|
2733
|
-
INIT: "init",
|
|
2734
|
-
SET_TOKEN: "set_token",
|
|
2735
|
-
SET_CART: "set_cart",
|
|
2736
|
-
GET_DATA: "get_data",
|
|
2737
|
-
PROCESS_CHECKOUT: "process_checkout",
|
|
2738
|
-
ABORT_CHECKOUT: "abort_checkout",
|
|
2739
|
-
READY: "ready",
|
|
2740
|
-
HEIGHT_CHANGE: "height_change",
|
|
2741
|
-
AUTHENTICATED: "authenticated",
|
|
2742
|
-
REQUIRES_OTP: "requires_otp",
|
|
2743
|
-
ERROR: "error",
|
|
2744
|
-
ADDRESS_CHANGED: "address_changed",
|
|
2745
|
-
ADDRESS_SELECTED: "address_selected",
|
|
2746
|
-
PAYMENT_METHOD_SELECTED: "payment_method_selected",
|
|
2747
|
-
TOKEN_REFRESHED: "token_refreshed",
|
|
2748
|
-
LOGOUT_COMPLETE: "logout_complete",
|
|
2749
|
-
CONTACT_PROVIDED: "contact_provided",
|
|
2750
|
-
CHECKOUT_STATUS: "checkout_status",
|
|
2751
|
-
CHECKOUT_COMPLETE: "checkout_complete",
|
|
2752
|
-
ORDER_TYPE_CHANGED: "order_type_changed",
|
|
2753
|
-
REQUEST_SUBMIT: "request_submit"
|
|
2754
|
-
};
|
|
2755
|
-
var EVENT_TYPES = {
|
|
2756
|
-
READY: "ready",
|
|
2757
|
-
AUTHENTICATED: "authenticated",
|
|
2758
|
-
REQUIRES_OTP: "requires_otp",
|
|
2759
|
-
ERROR: "error",
|
|
2760
|
-
CHANGE: "change",
|
|
2761
|
-
ORDER_TYPE_CHANGED: "order_type_changed",
|
|
2762
|
-
REQUEST_SUBMIT: "request_submit"
|
|
2763
|
-
};
|
|
2764
|
-
|
|
2765
|
-
// src/elements.ts
|
|
2766
|
-
function toCheckoutError(code, message, recoverable) {
|
|
2767
|
-
const hint = getErrorHint(code);
|
|
2768
|
-
return {
|
|
2769
|
-
success: false,
|
|
2770
|
-
error: {
|
|
2771
|
-
code,
|
|
2772
|
-
message,
|
|
2773
|
-
recoverable,
|
|
2774
|
-
docs_url: hint?.docs_url,
|
|
2775
|
-
suggestion: hint?.suggestion
|
|
2776
|
-
}
|
|
2777
|
-
};
|
|
2778
|
-
}
|
|
2779
|
-
var DEFAULT_LINK_URL = "https://link.cimplify.io";
|
|
2780
|
-
function generateNonce() {
|
|
2781
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
2782
|
-
return crypto.randomUUID().replace(/-/g, "").slice(0, 16);
|
|
2783
|
-
}
|
|
2784
|
-
return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
|
|
2785
|
-
}
|
|
2786
|
-
function isAllowedOrigin(origin) {
|
|
2787
|
-
try {
|
|
2788
|
-
const url = new URL(origin);
|
|
2789
|
-
const hostname = url.hostname;
|
|
2790
|
-
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
2791
|
-
return true;
|
|
2792
|
-
}
|
|
2793
|
-
if (url.protocol !== "https:") {
|
|
2794
|
-
return false;
|
|
2795
|
-
}
|
|
2796
|
-
return hostname === "cimplify.io" || hostname.endsWith(".cimplify.io");
|
|
2797
|
-
} catch {
|
|
2798
|
-
return false;
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
function parseIframeMessage(data) {
|
|
2802
|
-
if (!data || typeof data !== "object") {
|
|
2803
|
-
return null;
|
|
2804
|
-
}
|
|
2805
|
-
const maybeType = data.type;
|
|
2806
|
-
if (typeof maybeType !== "string") {
|
|
2807
|
-
return null;
|
|
2808
|
-
}
|
|
2809
|
-
return data;
|
|
2810
|
-
}
|
|
2811
|
-
var CimplifyElements = class {
|
|
2812
|
-
constructor(client, businessId, options = {}) {
|
|
2813
|
-
this.elements = /* @__PURE__ */ new Map();
|
|
2814
|
-
this.accessToken = null;
|
|
2815
|
-
this.accountId = null;
|
|
2816
|
-
this.customerId = null;
|
|
2817
|
-
this.customerData = null;
|
|
2818
|
-
this.addressData = null;
|
|
2819
|
-
this.paymentData = null;
|
|
2820
|
-
this.checkoutInProgress = false;
|
|
2821
|
-
this.activeCheckoutAbort = null;
|
|
2822
|
-
this.businessIdResolvePromise = null;
|
|
2823
|
-
this.client = client;
|
|
2824
|
-
this.businessId = businessId ?? null;
|
|
2825
|
-
this.linkUrl = options.linkUrl || DEFAULT_LINK_URL;
|
|
2826
|
-
this.options = options;
|
|
2827
|
-
this.boundHandleMessage = this.handleMessage.bind(this);
|
|
2828
|
-
if (typeof window !== "undefined") {
|
|
2829
|
-
window.addEventListener("message", this.boundHandleMessage);
|
|
2830
|
-
}
|
|
2831
|
-
}
|
|
2832
|
-
debug(...args) {
|
|
2833
|
-
if (this.client.isTestMode()) {
|
|
2834
|
-
console.debug(...args);
|
|
2835
|
-
}
|
|
2836
|
-
}
|
|
2837
|
-
create(type, options = {}) {
|
|
2838
|
-
const existing = this.elements.get(type);
|
|
2839
|
-
if (existing) return existing;
|
|
2840
|
-
const element = new CimplifyElement(type, this.businessId, this.linkUrl, options, this);
|
|
2841
|
-
this.elements.set(type, element);
|
|
2842
|
-
return element;
|
|
2843
|
-
}
|
|
2844
|
-
getElement(type) {
|
|
2845
|
-
return this.elements.get(type);
|
|
2846
|
-
}
|
|
2847
|
-
_removeElement(type, instance) {
|
|
2848
|
-
const current = this.elements.get(type);
|
|
2849
|
-
if (current === instance) {
|
|
2850
|
-
this.elements.delete(type);
|
|
2851
|
-
}
|
|
2852
|
-
}
|
|
2853
|
-
destroy() {
|
|
2854
|
-
this.elements.forEach((element) => element.destroy());
|
|
2855
|
-
this.elements.clear();
|
|
2856
|
-
if (typeof window !== "undefined") {
|
|
2857
|
-
window.removeEventListener("message", this.boundHandleMessage);
|
|
2858
|
-
}
|
|
2859
|
-
}
|
|
2860
|
-
async submitCheckout(data) {
|
|
2861
|
-
const result = await this.processCheckout({
|
|
2862
|
-
cart_id: data.cart_id,
|
|
2863
|
-
order_type: data.order_type ?? "delivery",
|
|
2864
|
-
location_id: data.location_id,
|
|
2865
|
-
notes: data.notes,
|
|
2866
|
-
scheduled_time: data.scheduled_time,
|
|
2867
|
-
tip_amount: data.tip_amount,
|
|
2868
|
-
enroll_in_link: true
|
|
2869
|
-
});
|
|
2870
|
-
if (result.success) {
|
|
2871
|
-
return {
|
|
2872
|
-
success: true,
|
|
2873
|
-
order: {
|
|
2874
|
-
id: result.order?.id || "",
|
|
2875
|
-
status: result.order?.status || "unknown",
|
|
2876
|
-
total: result.order?.total || ""
|
|
2877
|
-
}
|
|
2878
|
-
};
|
|
2879
|
-
}
|
|
2880
|
-
return {
|
|
2881
|
-
success: false,
|
|
2882
|
-
error: {
|
|
2883
|
-
code: result.error?.code || "CHECKOUT_FAILED",
|
|
2884
|
-
message: result.error?.message || "Checkout failed"
|
|
2885
|
-
}
|
|
2886
|
-
};
|
|
2887
|
-
}
|
|
2888
|
-
processCheckout(options) {
|
|
2889
|
-
let abortFn = null;
|
|
2890
|
-
const task = (async () => {
|
|
2891
|
-
this.debug("[cimplify:checkout] processCheckout called", { cart_id: options.cart_id, order_type: options.order_type, location_id: options.location_id });
|
|
2892
|
-
if (this.checkoutInProgress) {
|
|
2893
|
-
this.debug("[cimplify:checkout] BLOCKED: already processing");
|
|
2894
|
-
return toCheckoutError(
|
|
2895
|
-
"ALREADY_PROCESSING",
|
|
2896
|
-
"Checkout is already in progress.",
|
|
2897
|
-
false
|
|
2898
|
-
);
|
|
2899
|
-
}
|
|
2900
|
-
this.checkoutInProgress = true;
|
|
2901
|
-
if (!options.cart_id) {
|
|
2902
|
-
this.debug("[cimplify:checkout] BLOCKED: no cart_id");
|
|
2903
|
-
this.checkoutInProgress = false;
|
|
2904
|
-
return toCheckoutError(
|
|
2905
|
-
"INVALID_CART",
|
|
2906
|
-
"A valid cart is required before checkout can start.",
|
|
2907
|
-
false
|
|
2908
|
-
);
|
|
2909
|
-
}
|
|
2910
|
-
if (!options.order_type) {
|
|
2911
|
-
this.debug("[cimplify:checkout] BLOCKED: no order_type");
|
|
2912
|
-
this.checkoutInProgress = false;
|
|
2913
|
-
return toCheckoutError(
|
|
2914
|
-
"ORDER_TYPE_REQUIRED",
|
|
2915
|
-
"Order type is required before checkout can start.",
|
|
2916
|
-
false
|
|
2917
|
-
);
|
|
2918
|
-
}
|
|
2919
|
-
const checkoutElement = this.elements.get(ELEMENT_TYPES.CHECKOUT) || this.elements.get(ELEMENT_TYPES.PAYMENT);
|
|
2920
|
-
if (!checkoutElement) {
|
|
2921
|
-
this.debug("[cimplify:checkout] BLOCKED: no checkout element");
|
|
2922
|
-
this.checkoutInProgress = false;
|
|
2923
|
-
return toCheckoutError(
|
|
2924
|
-
"NO_PAYMENT_ELEMENT",
|
|
2925
|
-
"Checkout element must be mounted before checkout.",
|
|
2926
|
-
false
|
|
2927
|
-
);
|
|
2928
|
-
}
|
|
2929
|
-
if (!checkoutElement.isMounted()) {
|
|
2930
|
-
this.debug("[cimplify:checkout] BLOCKED: checkout element not mounted");
|
|
2931
|
-
this.checkoutInProgress = false;
|
|
2932
|
-
return toCheckoutError(
|
|
2933
|
-
"PAYMENT_NOT_MOUNTED",
|
|
2934
|
-
"Checkout element must be mounted before checkout.",
|
|
2935
|
-
false
|
|
2936
|
-
);
|
|
2937
|
-
}
|
|
2938
|
-
const isUnifiedCheckout = this.elements.has(ELEMENT_TYPES.CHECKOUT);
|
|
2939
|
-
if (!isUnifiedCheckout) {
|
|
2940
|
-
const authElement = this.elements.get(ELEMENT_TYPES.AUTH);
|
|
2941
|
-
if (authElement && !this.accessToken) {
|
|
2942
|
-
this.debug("[cimplify:checkout] BLOCKED: auth incomplete");
|
|
2943
|
-
this.checkoutInProgress = false;
|
|
2944
|
-
return toCheckoutError(
|
|
2945
|
-
"AUTH_INCOMPLETE",
|
|
2946
|
-
"Authentication must complete before checkout can start.",
|
|
2947
|
-
true
|
|
2948
|
-
);
|
|
2949
|
-
}
|
|
2950
|
-
const addressElement = this.elements.get(ELEMENT_TYPES.ADDRESS);
|
|
2951
|
-
if (addressElement) {
|
|
2952
|
-
this.debug("[cimplify:checkout] getting address data...");
|
|
2953
|
-
await addressElement.getData();
|
|
2954
|
-
this.debug("[cimplify:checkout] address data resolved", this.addressData);
|
|
2955
|
-
}
|
|
2956
|
-
await this.hydrateCustomerData();
|
|
2957
|
-
}
|
|
2958
|
-
this.debug("[cimplify:checkout] customer data hydrated", { customerData: this.customerData, accountId: this.accountId, isUnifiedCheckout });
|
|
2959
|
-
const processMessage = {
|
|
2960
|
-
type: MESSAGE_TYPES.PROCESS_CHECKOUT,
|
|
2961
|
-
cart_id: options.cart_id,
|
|
2962
|
-
order_type: options.order_type,
|
|
2963
|
-
location_id: options.location_id,
|
|
2964
|
-
notes: options.notes,
|
|
2965
|
-
scheduled_time: options.scheduled_time,
|
|
2966
|
-
tip_amount: options.tip_amount,
|
|
2967
|
-
pay_currency: options.pay_currency,
|
|
2968
|
-
enroll_in_link: options.enroll_in_link ?? true,
|
|
2969
|
-
address: isUnifiedCheckout ? void 0 : this.addressData ?? void 0,
|
|
2970
|
-
customer: isUnifiedCheckout ? void 0 : this.customerData ? {
|
|
2971
|
-
name: this.customerData.name,
|
|
2972
|
-
email: this.customerData.email,
|
|
2973
|
-
phone: this.customerData.phone
|
|
2974
|
-
} : null,
|
|
2975
|
-
account_id: this.accountId ?? void 0,
|
|
2976
|
-
customer_id: this.customerId ?? void 0
|
|
2977
|
-
};
|
|
2978
|
-
const timeoutMs = options.timeout_ms ?? 18e4;
|
|
2979
|
-
const paymentWindow = checkoutElement.getContentWindow();
|
|
2980
|
-
return new Promise((resolve) => {
|
|
2981
|
-
let settled = false;
|
|
2982
|
-
const cleanup = () => {
|
|
2983
|
-
if (typeof window !== "undefined") {
|
|
2984
|
-
window.removeEventListener("message", handleCheckoutMessage);
|
|
2985
|
-
}
|
|
2986
|
-
clearTimeout(timeout);
|
|
2987
|
-
this.checkoutInProgress = false;
|
|
2988
|
-
this.activeCheckoutAbort = null;
|
|
2989
|
-
};
|
|
2990
|
-
const settle = (result) => {
|
|
2991
|
-
if (settled) {
|
|
2992
|
-
return;
|
|
2993
|
-
}
|
|
2994
|
-
settled = true;
|
|
2995
|
-
cleanup();
|
|
2996
|
-
resolve(result);
|
|
2997
|
-
};
|
|
2998
|
-
const timeout = setTimeout(() => {
|
|
2999
|
-
settle(
|
|
3000
|
-
toCheckoutError(
|
|
3001
|
-
"TIMEOUT",
|
|
3002
|
-
"Checkout timed out before receiving a terminal response.",
|
|
3003
|
-
true
|
|
3004
|
-
)
|
|
3005
|
-
);
|
|
3006
|
-
}, timeoutMs);
|
|
3007
|
-
const handleCheckoutMessage = (event) => {
|
|
3008
|
-
if (!isAllowedOrigin(event.origin)) {
|
|
3009
|
-
return;
|
|
3010
|
-
}
|
|
3011
|
-
const message = parseIframeMessage(event.data);
|
|
3012
|
-
if (!message) {
|
|
3013
|
-
return;
|
|
3014
|
-
}
|
|
3015
|
-
if (message.type === MESSAGE_TYPES.LOGOUT_COMPLETE) {
|
|
3016
|
-
checkoutElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
|
|
3017
|
-
settle(
|
|
3018
|
-
toCheckoutError(
|
|
3019
|
-
"AUTH_LOST",
|
|
3020
|
-
"Authentication was cleared during checkout.",
|
|
3021
|
-
true
|
|
3022
|
-
)
|
|
3023
|
-
);
|
|
3024
|
-
return;
|
|
3025
|
-
}
|
|
3026
|
-
if (paymentWindow && event.source && event.source !== paymentWindow) {
|
|
3027
|
-
return;
|
|
3028
|
-
}
|
|
3029
|
-
if (message.type === MESSAGE_TYPES.CHECKOUT_STATUS) {
|
|
3030
|
-
options.on_status_change?.(message.status, message.context);
|
|
3031
|
-
return;
|
|
3032
|
-
}
|
|
3033
|
-
if (message.type === MESSAGE_TYPES.CHECKOUT_COMPLETE) {
|
|
3034
|
-
this.debug("[cimplify:checkout] received checkout_complete", { success: message.success, error: message.error });
|
|
3035
|
-
settle({
|
|
3036
|
-
success: message.success,
|
|
3037
|
-
order: message.order,
|
|
3038
|
-
error: message.error,
|
|
3039
|
-
enrolled_in_link: message.enrolled_in_link
|
|
3040
|
-
});
|
|
3041
|
-
}
|
|
3042
|
-
};
|
|
3043
|
-
if (typeof window !== "undefined") {
|
|
3044
|
-
window.addEventListener("message", handleCheckoutMessage);
|
|
3045
|
-
}
|
|
3046
|
-
abortFn = () => {
|
|
3047
|
-
checkoutElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
|
|
3048
|
-
settle(
|
|
3049
|
-
toCheckoutError(
|
|
3050
|
-
"CANCELLED",
|
|
3051
|
-
"Checkout was cancelled.",
|
|
3052
|
-
true
|
|
3053
|
-
)
|
|
3054
|
-
);
|
|
3055
|
-
};
|
|
3056
|
-
this.activeCheckoutAbort = abortFn;
|
|
3057
|
-
this.debug("[cimplify:checkout] sending process_checkout to checkout iframe", { cart_id: processMessage.cart_id, order_type: processMessage.order_type, hasCustomer: !!processMessage.customer, hasAddress: !!processMessage.address, isUnifiedCheckout });
|
|
3058
|
-
checkoutElement.sendMessage(processMessage);
|
|
3059
|
-
this.debug("[cimplify:checkout] postMessage sent, waiting for checkout_complete...");
|
|
3060
|
-
});
|
|
3061
|
-
})();
|
|
3062
|
-
const abortable = task;
|
|
3063
|
-
abortable.abort = () => {
|
|
3064
|
-
if (abortFn) {
|
|
3065
|
-
abortFn();
|
|
3066
|
-
}
|
|
3067
|
-
};
|
|
3068
|
-
return abortable;
|
|
3069
|
-
}
|
|
3070
|
-
isAuthenticated() {
|
|
3071
|
-
return this.accessToken !== null;
|
|
3072
|
-
}
|
|
3073
|
-
getAccessToken() {
|
|
3074
|
-
return this.accessToken;
|
|
3075
|
-
}
|
|
3076
|
-
getPublicKey() {
|
|
3077
|
-
return this.client.getPublicKey();
|
|
3078
|
-
}
|
|
3079
|
-
getBusinessId() {
|
|
3080
|
-
return this.businessId;
|
|
3081
|
-
}
|
|
3082
|
-
async resolveBusinessId() {
|
|
3083
|
-
if (this.businessId) {
|
|
3084
|
-
return this.businessId;
|
|
3085
|
-
}
|
|
3086
|
-
if (this.businessIdResolvePromise) {
|
|
3087
|
-
return this.businessIdResolvePromise;
|
|
3088
|
-
}
|
|
3089
|
-
this.businessIdResolvePromise = this.client.resolveBusinessId().then((resolvedBusinessId) => {
|
|
3090
|
-
this.businessId = resolvedBusinessId;
|
|
3091
|
-
return resolvedBusinessId;
|
|
3092
|
-
}).catch(() => null).finally(() => {
|
|
3093
|
-
this.businessIdResolvePromise = null;
|
|
3094
|
-
});
|
|
3095
|
-
return this.businessIdResolvePromise;
|
|
3096
|
-
}
|
|
3097
|
-
getAppearance() {
|
|
3098
|
-
return this.options.appearance;
|
|
3099
|
-
}
|
|
3100
|
-
async hydrateCustomerData() {
|
|
3101
|
-
if (!this.accessToken || this.customerData) {
|
|
3102
|
-
return;
|
|
3103
|
-
}
|
|
3104
|
-
if (!this.client.getAccessToken()) {
|
|
3105
|
-
this.client.setAccessToken(this.accessToken);
|
|
3106
|
-
}
|
|
3107
|
-
const linkDataResult = await this.client.link.getLinkData();
|
|
3108
|
-
if (!linkDataResult.ok || !linkDataResult.value?.customer) {
|
|
3109
|
-
return;
|
|
3110
|
-
}
|
|
3111
|
-
const customer = linkDataResult.value.customer;
|
|
3112
|
-
this.customerData = {
|
|
3113
|
-
name: customer.name || "",
|
|
3114
|
-
email: customer.email || null,
|
|
3115
|
-
phone: customer.phone || null
|
|
3116
|
-
};
|
|
3117
|
-
}
|
|
3118
|
-
handleMessage(event) {
|
|
3119
|
-
if (!isAllowedOrigin(event.origin)) {
|
|
3120
|
-
return;
|
|
3121
|
-
}
|
|
3122
|
-
const message = parseIframeMessage(event.data);
|
|
3123
|
-
if (!message) return;
|
|
3124
|
-
switch (message.type) {
|
|
3125
|
-
case MESSAGE_TYPES.AUTHENTICATED:
|
|
3126
|
-
const customer = message.customer ?? {
|
|
3127
|
-
name: "",
|
|
3128
|
-
email: null,
|
|
3129
|
-
phone: null
|
|
3130
|
-
};
|
|
3131
|
-
this.accessToken = message.token;
|
|
3132
|
-
this.accountId = message.accountId;
|
|
3133
|
-
this.customerId = message.customerId;
|
|
3134
|
-
this.customerData = customer;
|
|
3135
|
-
this.client.setAccessToken(message.token);
|
|
3136
|
-
this.elements.forEach((element, type) => {
|
|
3137
|
-
if (type !== ELEMENT_TYPES.AUTH && type !== ELEMENT_TYPES.CHECKOUT) {
|
|
3138
|
-
element.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token: message.token });
|
|
3139
|
-
}
|
|
3140
|
-
});
|
|
3141
|
-
break;
|
|
3142
|
-
case MESSAGE_TYPES.TOKEN_REFRESHED:
|
|
3143
|
-
this.accessToken = message.token;
|
|
3144
|
-
break;
|
|
3145
|
-
case MESSAGE_TYPES.ADDRESS_CHANGED:
|
|
3146
|
-
case MESSAGE_TYPES.ADDRESS_SELECTED:
|
|
3147
|
-
this.addressData = message.address;
|
|
3148
|
-
break;
|
|
3149
|
-
case MESSAGE_TYPES.PAYMENT_METHOD_SELECTED:
|
|
3150
|
-
this.paymentData = message.method;
|
|
3151
|
-
break;
|
|
3152
|
-
case MESSAGE_TYPES.CONTACT_PROVIDED:
|
|
3153
|
-
if (!this.customerData) {
|
|
3154
|
-
this.customerData = { name: "", email: null, phone: null };
|
|
3155
|
-
}
|
|
3156
|
-
if (message.contactType === "email") {
|
|
3157
|
-
this.customerData.email = message.contact;
|
|
3158
|
-
} else {
|
|
3159
|
-
this.customerData.phone = message.contact;
|
|
3160
|
-
}
|
|
3161
|
-
break;
|
|
3162
|
-
case MESSAGE_TYPES.LOGOUT_COMPLETE:
|
|
3163
|
-
if (this.checkoutInProgress && this.activeCheckoutAbort) {
|
|
3164
|
-
this.activeCheckoutAbort();
|
|
3165
|
-
}
|
|
3166
|
-
this.accessToken = null;
|
|
3167
|
-
this.accountId = null;
|
|
3168
|
-
this.customerId = null;
|
|
3169
|
-
this.customerData = null;
|
|
3170
|
-
this.addressData = null;
|
|
3171
|
-
this.paymentData = null;
|
|
3172
|
-
this.client.clearSession();
|
|
3173
|
-
break;
|
|
3174
|
-
}
|
|
3175
|
-
}
|
|
3176
|
-
_setAddressData(data) {
|
|
3177
|
-
this.addressData = data;
|
|
3178
|
-
}
|
|
3179
|
-
_setPaymentData(data) {
|
|
3180
|
-
this.paymentData = data;
|
|
3181
|
-
}
|
|
3182
|
-
_setGuestContact(contact, contactType) {
|
|
3183
|
-
if (!this.customerData) {
|
|
3184
|
-
this.customerData = { name: "", email: null, phone: null };
|
|
3185
|
-
}
|
|
3186
|
-
if (contactType === "email") {
|
|
3187
|
-
this.customerData.email = contact;
|
|
3188
|
-
} else {
|
|
3189
|
-
this.customerData.phone = contact;
|
|
3190
|
-
}
|
|
3191
|
-
}
|
|
3192
|
-
};
|
|
3193
|
-
var CimplifyElement = class {
|
|
3194
|
-
constructor(type, businessId, linkUrl, options, parent) {
|
|
3195
|
-
this.iframe = null;
|
|
3196
|
-
this.container = null;
|
|
3197
|
-
this.mounted = false;
|
|
3198
|
-
this.pendingInit = null;
|
|
3199
|
-
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
3200
|
-
this.resolvers = /* @__PURE__ */ new Map();
|
|
3201
|
-
this.listening = false;
|
|
3202
|
-
this.type = type;
|
|
3203
|
-
this.businessId = businessId;
|
|
3204
|
-
this.linkUrl = linkUrl;
|
|
3205
|
-
this.options = options;
|
|
3206
|
-
this.parent = parent;
|
|
3207
|
-
this.nonce = generateNonce();
|
|
3208
|
-
this.boundHandleMessage = this.handleMessage.bind(this);
|
|
3209
|
-
if (typeof window !== "undefined") {
|
|
3210
|
-
window.addEventListener("message", this.boundHandleMessage);
|
|
3211
|
-
this.listening = true;
|
|
3212
|
-
}
|
|
3213
|
-
}
|
|
3214
|
-
mount(container) {
|
|
3215
|
-
if (this.mounted) {
|
|
3216
|
-
console.warn(`Element ${this.type} is already mounted`);
|
|
3217
|
-
return;
|
|
3218
|
-
}
|
|
3219
|
-
const target = typeof container === "string" ? document.querySelector(container) : container;
|
|
3220
|
-
if (!target) {
|
|
3221
|
-
console.error(`Container not found: ${container}`);
|
|
3222
|
-
return;
|
|
3223
|
-
}
|
|
3224
|
-
this.container = target;
|
|
3225
|
-
this.mounted = true;
|
|
3226
|
-
if (!this.listening && typeof window !== "undefined") {
|
|
3227
|
-
window.addEventListener("message", this.boundHandleMessage);
|
|
3228
|
-
this.listening = true;
|
|
3229
|
-
}
|
|
3230
|
-
void this.createIframe();
|
|
3231
|
-
}
|
|
3232
|
-
destroy() {
|
|
3233
|
-
if (this.iframe) {
|
|
3234
|
-
this.iframe.remove();
|
|
3235
|
-
this.iframe = null;
|
|
3236
|
-
}
|
|
3237
|
-
this.container = null;
|
|
3238
|
-
this.mounted = false;
|
|
3239
|
-
this.eventHandlers.clear();
|
|
3240
|
-
this.resolvers.forEach((entry) => clearTimeout(entry.timeoutId));
|
|
3241
|
-
this.resolvers.clear();
|
|
3242
|
-
if (this.listening && typeof window !== "undefined") {
|
|
3243
|
-
window.removeEventListener("message", this.boundHandleMessage);
|
|
3244
|
-
this.listening = false;
|
|
3245
|
-
}
|
|
3246
|
-
this.parent._removeElement(this.type, this);
|
|
3247
|
-
}
|
|
3248
|
-
on(event, handler) {
|
|
3249
|
-
if (!this.eventHandlers.has(event)) {
|
|
3250
|
-
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
3251
|
-
}
|
|
3252
|
-
this.eventHandlers.get(event).add(handler);
|
|
3253
|
-
}
|
|
3254
|
-
off(event, handler) {
|
|
3255
|
-
this.eventHandlers.get(event)?.delete(handler);
|
|
3256
|
-
}
|
|
3257
|
-
async getData() {
|
|
3258
|
-
if (!this.isMounted()) {
|
|
3259
|
-
return null;
|
|
3260
|
-
}
|
|
3261
|
-
return new Promise((resolve) => {
|
|
3262
|
-
const id = Math.random().toString(36).slice(2);
|
|
3263
|
-
const timeoutId = setTimeout(() => {
|
|
3264
|
-
const entry = this.resolvers.get(id);
|
|
3265
|
-
if (!entry) {
|
|
3266
|
-
return;
|
|
3267
|
-
}
|
|
3268
|
-
this.resolvers.delete(id);
|
|
3269
|
-
entry.resolve(null);
|
|
3270
|
-
}, 5e3);
|
|
3271
|
-
this.resolvers.set(id, { resolve, timeoutId });
|
|
3272
|
-
this.sendMessage({ type: MESSAGE_TYPES.GET_DATA });
|
|
3273
|
-
});
|
|
3274
|
-
}
|
|
3275
|
-
setCart(cart) {
|
|
3276
|
-
this.sendMessage({ type: MESSAGE_TYPES.SET_CART, cart });
|
|
3277
|
-
}
|
|
3278
|
-
sendMessage(message) {
|
|
3279
|
-
if (this.iframe?.contentWindow) {
|
|
3280
|
-
this.iframe.contentWindow.postMessage(message, this.linkUrl);
|
|
3281
|
-
} else {
|
|
3282
|
-
console.warn("[cimplify:checkout] sendMessage DROPPED \u2014 no contentWindow", { type: message.type, hasIframe: !!this.iframe, isConnected: this.iframe?.isConnected });
|
|
3283
|
-
}
|
|
3284
|
-
}
|
|
3285
|
-
getContentWindow() {
|
|
3286
|
-
return this.iframe?.contentWindow ?? null;
|
|
3287
|
-
}
|
|
3288
|
-
isMounted() {
|
|
3289
|
-
return this.mounted && Boolean(this.iframe) && this.iframe?.isConnected === true;
|
|
3290
|
-
}
|
|
3291
|
-
async createIframe() {
|
|
3292
|
-
if (!this.container) return;
|
|
3293
|
-
const resolvedBusinessId = this.businessId ?? await this.parent.resolveBusinessId();
|
|
3294
|
-
if (!resolvedBusinessId || !this.container || !this.mounted) {
|
|
3295
|
-
console.error("Unable to mount element without a resolved business ID");
|
|
3296
|
-
this.emit(EVENT_TYPES.ERROR, {
|
|
3297
|
-
code: "BUSINESS_ID_REQUIRED",
|
|
3298
|
-
message: "Unable to initialize checkout without a business ID."
|
|
3299
|
-
});
|
|
3300
|
-
return;
|
|
3301
|
-
}
|
|
3302
|
-
this.businessId = resolvedBusinessId;
|
|
3303
|
-
const iframe = document.createElement("iframe");
|
|
3304
|
-
const url = new URL(`${this.linkUrl}/elements/${this.type}`);
|
|
3305
|
-
url.searchParams.set("businessId", resolvedBusinessId);
|
|
3306
|
-
url.searchParams.set("nonce", this.nonce);
|
|
3307
|
-
if (this.options.prefillEmail) url.searchParams.set("email", this.options.prefillEmail);
|
|
3308
|
-
if (this.options.mode) url.searchParams.set("mode", this.options.mode);
|
|
3309
|
-
iframe.src = url.toString();
|
|
3310
|
-
iframe.style.border = "none";
|
|
3311
|
-
iframe.style.width = "100%";
|
|
3312
|
-
iframe.style.minHeight = "200px";
|
|
3313
|
-
iframe.style.display = "block";
|
|
3314
|
-
iframe.style.overflow = "hidden";
|
|
3315
|
-
iframe.style.background = "transparent";
|
|
3316
|
-
iframe.setAttribute("allowtransparency", "true");
|
|
3317
|
-
iframe.setAttribute("frameborder", "0");
|
|
3318
|
-
iframe.setAttribute("allow", "geolocation");
|
|
3319
|
-
iframe.setAttribute(
|
|
3320
|
-
"sandbox",
|
|
3321
|
-
"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
|
|
3322
|
-
);
|
|
3323
|
-
this.iframe = iframe;
|
|
3324
|
-
this.container.appendChild(iframe);
|
|
3325
|
-
this.pendingInit = {
|
|
3326
|
-
businessId: resolvedBusinessId,
|
|
3327
|
-
publicKey: this.parent.getPublicKey()
|
|
3328
|
-
};
|
|
3329
|
-
}
|
|
3330
|
-
handleMessage(event) {
|
|
3331
|
-
if (!isAllowedOrigin(event.origin)) {
|
|
3332
|
-
return;
|
|
3333
|
-
}
|
|
3334
|
-
const data = event.data;
|
|
3335
|
-
if (data?.nonce) {
|
|
3336
|
-
if (data.nonce !== this.nonce) return;
|
|
3337
|
-
} else {
|
|
3338
|
-
const iframeWindow = this.iframe?.contentWindow;
|
|
3339
|
-
if (iframeWindow && event.source && event.source !== iframeWindow) {
|
|
3340
|
-
return;
|
|
3341
|
-
}
|
|
3342
|
-
}
|
|
3343
|
-
const message = parseIframeMessage(event.data);
|
|
3344
|
-
if (!message) return;
|
|
3345
|
-
switch (message.type) {
|
|
3346
|
-
case MESSAGE_TYPES.READY:
|
|
3347
|
-
if (this.iframe && message.height) this.iframe.style.height = `${message.height}px`;
|
|
3348
|
-
if (this.pendingInit) {
|
|
3349
|
-
const { publicKey } = this.pendingInit;
|
|
3350
|
-
this.sendMessage({
|
|
3351
|
-
type: MESSAGE_TYPES.INIT,
|
|
3352
|
-
businessId: this.pendingInit.businessId,
|
|
3353
|
-
publicKey,
|
|
3354
|
-
demoMode: publicKey.length === 0,
|
|
3355
|
-
prefillEmail: this.options.prefillEmail,
|
|
3356
|
-
appearance: this.parent.getAppearance(),
|
|
3357
|
-
orderTypes: this.options.orderTypes,
|
|
3358
|
-
defaultOrderType: this.options.defaultOrderType,
|
|
3359
|
-
renderSubmitButton: true,
|
|
3360
|
-
submitLabel: this.options.submitLabel
|
|
3361
|
-
});
|
|
3362
|
-
const token = this.parent.getAccessToken();
|
|
3363
|
-
if (token && this.type !== ELEMENT_TYPES.AUTH) {
|
|
3364
|
-
this.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
|
|
3365
|
-
}
|
|
3366
|
-
this.pendingInit = null;
|
|
3367
|
-
}
|
|
3368
|
-
this.emit(EVENT_TYPES.READY, { height: message.height });
|
|
3369
|
-
break;
|
|
3370
|
-
case MESSAGE_TYPES.HEIGHT_CHANGE:
|
|
3371
|
-
if (this.iframe) this.iframe.style.height = `${message.height}px`;
|
|
3372
|
-
break;
|
|
3373
|
-
case MESSAGE_TYPES.AUTHENTICATED:
|
|
3374
|
-
const customer = message.customer ?? {
|
|
3375
|
-
name: "",
|
|
3376
|
-
email: null,
|
|
3377
|
-
phone: null
|
|
3378
|
-
};
|
|
3379
|
-
this.emit(EVENT_TYPES.AUTHENTICATED, {
|
|
3380
|
-
accountId: message.accountId,
|
|
3381
|
-
customerId: message.customerId,
|
|
3382
|
-
token: message.token,
|
|
3383
|
-
customer
|
|
3384
|
-
});
|
|
3385
|
-
break;
|
|
3386
|
-
case MESSAGE_TYPES.REQUIRES_OTP:
|
|
3387
|
-
this.emit(EVENT_TYPES.REQUIRES_OTP, { contactMasked: message.contactMasked });
|
|
3388
|
-
break;
|
|
3389
|
-
case MESSAGE_TYPES.ERROR:
|
|
3390
|
-
this.emit(EVENT_TYPES.ERROR, { code: message.code, message: message.message });
|
|
3391
|
-
break;
|
|
3392
|
-
case MESSAGE_TYPES.ADDRESS_CHANGED:
|
|
3393
|
-
case MESSAGE_TYPES.ADDRESS_SELECTED:
|
|
3394
|
-
this.parent._setAddressData(message.address);
|
|
3395
|
-
this.emit(EVENT_TYPES.CHANGE, { address: message.address });
|
|
3396
|
-
this.resolveData(message.address);
|
|
3397
|
-
break;
|
|
3398
|
-
case MESSAGE_TYPES.PAYMENT_METHOD_SELECTED:
|
|
3399
|
-
this.parent._setPaymentData(message.method);
|
|
3400
|
-
this.emit(EVENT_TYPES.CHANGE, { paymentMethod: message.method });
|
|
3401
|
-
this.resolveData(message.method);
|
|
3402
|
-
break;
|
|
3403
|
-
case MESSAGE_TYPES.CONTACT_PROVIDED:
|
|
3404
|
-
this.parent._setGuestContact(message.contact, message.contactType);
|
|
3405
|
-
break;
|
|
3406
|
-
case MESSAGE_TYPES.ORDER_TYPE_CHANGED:
|
|
3407
|
-
this.emit(EVENT_TYPES.ORDER_TYPE_CHANGED, { orderType: message.orderType });
|
|
3408
|
-
break;
|
|
3409
|
-
case MESSAGE_TYPES.REQUEST_SUBMIT:
|
|
3410
|
-
this.emit(EVENT_TYPES.REQUEST_SUBMIT, {});
|
|
3411
|
-
break;
|
|
3412
|
-
}
|
|
3413
|
-
}
|
|
3414
|
-
emit(event, data) {
|
|
3415
|
-
this.eventHandlers.get(event)?.forEach((handler) => handler(data));
|
|
3416
|
-
}
|
|
3417
|
-
resolveData(data) {
|
|
3418
|
-
this.resolvers.forEach((entry) => {
|
|
3419
|
-
clearTimeout(entry.timeoutId);
|
|
3420
|
-
entry.resolve(data);
|
|
3421
|
-
});
|
|
3422
|
-
this.resolvers.clear();
|
|
3423
|
-
}
|
|
3424
|
-
};
|
|
3425
|
-
function createElements(client, businessId, options) {
|
|
3426
|
-
return new CimplifyElements(client, businessId, options);
|
|
3427
|
-
}
|
|
3428
|
-
|
|
3429
|
-
// src/client.ts
|
|
3430
|
-
var ACCESS_TOKEN_STORAGE_KEY = "cimplify_access_token";
|
|
3431
|
-
var SESSION_TOKEN_STORAGE_KEY = "cimplify_session_token";
|
|
3432
|
-
var ORDER_TOKEN_PREFIX = "cimplify_ot_";
|
|
3433
|
-
var SESSION_TOKEN_HEADER = "x-session-token";
|
|
3434
|
-
var ORDER_TOKEN_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
3435
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
3436
|
-
var DEFAULT_MAX_RETRIES = 3;
|
|
3437
|
-
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
3438
|
-
function sleep(ms) {
|
|
3439
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3440
|
-
}
|
|
3441
|
-
function isRetryable(error) {
|
|
3442
|
-
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
3443
|
-
return true;
|
|
3444
|
-
}
|
|
3445
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
3446
|
-
return true;
|
|
3447
|
-
}
|
|
3448
|
-
if (error instanceof CimplifyError) {
|
|
3449
|
-
return error.retryable;
|
|
3450
|
-
}
|
|
3451
|
-
return false;
|
|
3452
|
-
}
|
|
3453
|
-
function toNetworkError(error, isTestMode) {
|
|
3454
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
3455
|
-
return enrichError(
|
|
3456
|
-
new CimplifyError(
|
|
3457
|
-
ErrorCode.TIMEOUT,
|
|
3458
|
-
"Request timed out. Please check your connection and try again.",
|
|
3459
|
-
true
|
|
3460
|
-
),
|
|
3461
|
-
{ isTestMode }
|
|
3462
|
-
);
|
|
3463
|
-
}
|
|
3464
|
-
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
3465
|
-
return enrichError(
|
|
3466
|
-
new CimplifyError(
|
|
3467
|
-
ErrorCode.NETWORK_ERROR,
|
|
3468
|
-
"Network error. Please check your internet connection.",
|
|
3469
|
-
true
|
|
3470
|
-
),
|
|
3471
|
-
{ isTestMode }
|
|
3472
|
-
);
|
|
3473
|
-
}
|
|
3474
|
-
if (error instanceof CimplifyError) {
|
|
3475
|
-
return enrichError(error, { isTestMode });
|
|
3476
|
-
}
|
|
3477
|
-
return enrichError(
|
|
3478
|
-
new CimplifyError(
|
|
3479
|
-
ErrorCode.UNKNOWN_ERROR,
|
|
3480
|
-
error instanceof Error ? error.message : "An unknown error occurred",
|
|
3481
|
-
false
|
|
3482
|
-
),
|
|
3483
|
-
{ isTestMode }
|
|
3484
|
-
);
|
|
3485
|
-
}
|
|
3486
|
-
function deriveUrls() {
|
|
3487
|
-
const hostname = typeof window !== "undefined" ? window.location.hostname : "";
|
|
3488
|
-
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
3489
|
-
const protocol = window.location.protocol;
|
|
3490
|
-
return {
|
|
3491
|
-
baseUrl: `${protocol}//${hostname}:8082`,
|
|
3492
|
-
linkApiUrl: `${protocol}//${hostname}:8080`
|
|
3493
|
-
};
|
|
3494
|
-
}
|
|
3495
|
-
return {
|
|
3496
|
-
baseUrl: "https://storefronts.cimplify.io",
|
|
3497
|
-
linkApiUrl: "https://api.cimplify.io"
|
|
3498
|
-
};
|
|
3499
|
-
}
|
|
3500
|
-
function normalizeUrl(url) {
|
|
3501
|
-
const trimmed = url.trim();
|
|
3502
|
-
if (!trimmed) return trimmed;
|
|
3503
|
-
return trimmed.replace(/\/+$/, "");
|
|
3504
|
-
}
|
|
3505
|
-
function getEnvPublicKey() {
|
|
3506
|
-
try {
|
|
3507
|
-
const env = globalThis.process;
|
|
3508
|
-
return env?.env.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || void 0;
|
|
3509
|
-
} catch {
|
|
3510
|
-
return void 0;
|
|
3511
|
-
}
|
|
3512
|
-
}
|
|
3513
|
-
var CimplifyClient = class {
|
|
3514
|
-
constructor(config = {}) {
|
|
3515
|
-
this.accessToken = null;
|
|
3516
|
-
this.sessionToken = null;
|
|
3517
|
-
this.context = {};
|
|
3518
|
-
this.businessId = null;
|
|
3519
|
-
this.businessIdResolvePromise = null;
|
|
3520
|
-
this.inflightRequests = /* @__PURE__ */ new Map();
|
|
3521
|
-
this.publicKey = config.publicKey || getEnvPublicKey() || "";
|
|
3522
|
-
const urls = deriveUrls();
|
|
3523
|
-
this.baseUrl = config.baseUrl ? normalizeUrl(config.baseUrl) : urls.baseUrl;
|
|
3524
|
-
this.linkApiUrl = config.linkApiUrl ? normalizeUrl(config.linkApiUrl) : urls.linkApiUrl;
|
|
3525
|
-
this.credentials = config.credentials || "include";
|
|
3526
|
-
this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
3527
|
-
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
3528
|
-
this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
|
|
3529
|
-
this.hooks = config.hooks ?? {};
|
|
3530
|
-
this.accessToken = this.loadAccessToken();
|
|
3531
|
-
this.sessionToken = this.loadSessionToken();
|
|
3532
|
-
if (!this.publicKey && !config.suppressPublicKeyWarning) {
|
|
3533
|
-
console.warn(
|
|
3534
|
-
'[Cimplify] No public key found. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in your environment, or pass { publicKey: "pk_..." } to createCimplifyClient().'
|
|
3535
|
-
);
|
|
3536
|
-
}
|
|
3537
|
-
}
|
|
3538
|
-
buildUrl(base, path) {
|
|
3539
|
-
const url = `${base}${path}`;
|
|
3540
|
-
const locationId = this.context.location_id;
|
|
3541
|
-
if (!locationId) return url;
|
|
3542
|
-
const separator = url.includes("?") ? "&" : "?";
|
|
3543
|
-
return `${url}${separator}location_id=${encodeURIComponent(locationId)}`;
|
|
3544
|
-
}
|
|
3545
|
-
getAccessToken() {
|
|
3546
|
-
return this.accessToken;
|
|
3547
|
-
}
|
|
3548
|
-
getPublicKey() {
|
|
3549
|
-
return this.publicKey;
|
|
3550
|
-
}
|
|
3551
|
-
isTestMode() {
|
|
3552
|
-
return this.publicKey.trim().startsWith("pk_test_");
|
|
3553
|
-
}
|
|
3554
|
-
setAccessToken(token) {
|
|
3555
|
-
const previous = this.accessToken;
|
|
3556
|
-
this.accessToken = token;
|
|
3557
|
-
this.saveAccessToken(token);
|
|
3558
|
-
this.hooks.onSessionChange?.({
|
|
3559
|
-
previousToken: previous,
|
|
3560
|
-
newToken: token,
|
|
3561
|
-
source: "manual"
|
|
3562
|
-
});
|
|
3563
|
-
}
|
|
3564
|
-
clearSession() {
|
|
3565
|
-
const previous = this.accessToken;
|
|
3566
|
-
this.accessToken = null;
|
|
3567
|
-
this.sessionToken = null;
|
|
3568
|
-
this.saveAccessToken(null);
|
|
3569
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
3570
|
-
localStorage.removeItem(SESSION_TOKEN_STORAGE_KEY);
|
|
3571
|
-
}
|
|
3572
|
-
this.hooks.onSessionChange?.({
|
|
3573
|
-
previousToken: previous,
|
|
3574
|
-
newToken: null,
|
|
3575
|
-
source: "clear"
|
|
3576
|
-
});
|
|
3577
|
-
}
|
|
3578
|
-
setOrderToken(orderId, token) {
|
|
3579
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
3580
|
-
try {
|
|
3581
|
-
const entry = JSON.stringify({ token, storedAt: Date.now() });
|
|
3582
|
-
localStorage.setItem(`${ORDER_TOKEN_PREFIX}${orderId}`, entry);
|
|
3583
|
-
} catch {
|
|
3584
|
-
}
|
|
3585
|
-
}
|
|
3586
|
-
}
|
|
3587
|
-
getOrderToken(orderId) {
|
|
3588
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
3589
|
-
try {
|
|
3590
|
-
const raw = localStorage.getItem(`${ORDER_TOKEN_PREFIX}${orderId}`);
|
|
3591
|
-
if (!raw) return null;
|
|
3592
|
-
const entry = JSON.parse(raw);
|
|
3593
|
-
if (Date.now() - entry.storedAt > ORDER_TOKEN_TTL_MS) {
|
|
3594
|
-
localStorage.removeItem(`${ORDER_TOKEN_PREFIX}${orderId}`);
|
|
3595
|
-
return null;
|
|
3596
|
-
}
|
|
3597
|
-
return entry.token;
|
|
3598
|
-
} catch {
|
|
3599
|
-
return null;
|
|
3600
|
-
}
|
|
3601
|
-
}
|
|
3602
|
-
return null;
|
|
3603
|
-
}
|
|
3604
|
-
clearOrderTokens() {
|
|
3605
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
3606
|
-
const keysToRemove = [];
|
|
3607
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
3608
|
-
const key = localStorage.key(i);
|
|
3609
|
-
if (key?.startsWith(ORDER_TOKEN_PREFIX)) {
|
|
3610
|
-
keysToRemove.push(key);
|
|
3611
|
-
}
|
|
3612
|
-
}
|
|
3613
|
-
keysToRemove.forEach((k) => localStorage.removeItem(k));
|
|
3614
|
-
}
|
|
3615
|
-
}
|
|
3616
|
-
setLocationId(locationId) {
|
|
3617
|
-
if (locationId) {
|
|
3618
|
-
this.context.location_id = locationId;
|
|
3619
|
-
} else {
|
|
3620
|
-
delete this.context.location_id;
|
|
3621
|
-
}
|
|
3622
|
-
}
|
|
3623
|
-
getLocationId() {
|
|
3624
|
-
return this.context.location_id ?? null;
|
|
3625
|
-
}
|
|
3626
|
-
setBusinessId(businessId) {
|
|
3627
|
-
const normalized = businessId.trim();
|
|
3628
|
-
if (!normalized) {
|
|
3629
|
-
return;
|
|
3630
|
-
}
|
|
3631
|
-
this.businessId = normalized;
|
|
3632
|
-
}
|
|
3633
|
-
getBusinessId() {
|
|
3634
|
-
return this.businessId;
|
|
3635
|
-
}
|
|
3636
|
-
async resolveBusinessId() {
|
|
3637
|
-
if (this.businessId) {
|
|
3638
|
-
return this.businessId;
|
|
3639
|
-
}
|
|
3640
|
-
if (this.businessIdResolvePromise) {
|
|
3641
|
-
return this.businessIdResolvePromise;
|
|
3642
|
-
}
|
|
3643
|
-
this.businessIdResolvePromise = (async () => {
|
|
3644
|
-
const result = await this.business.getInfo();
|
|
3645
|
-
if (!result.ok || !result.value?.id) {
|
|
3646
|
-
throw new CimplifyError(
|
|
3647
|
-
ErrorCode.NOT_FOUND,
|
|
3648
|
-
"Unable to resolve business ID from the current public key.",
|
|
3649
|
-
true
|
|
3650
|
-
);
|
|
3651
|
-
}
|
|
3652
|
-
this.businessId = result.value.id;
|
|
3653
|
-
return result.value.id;
|
|
3654
|
-
})();
|
|
3655
|
-
try {
|
|
3656
|
-
return await this.businessIdResolvePromise;
|
|
3657
|
-
} finally {
|
|
3658
|
-
this.businessIdResolvePromise = null;
|
|
3659
|
-
}
|
|
3660
|
-
}
|
|
3661
|
-
loadAccessToken() {
|
|
3662
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
3663
|
-
return localStorage.getItem(ACCESS_TOKEN_STORAGE_KEY);
|
|
3664
|
-
}
|
|
3665
|
-
return null;
|
|
3666
|
-
}
|
|
3667
|
-
saveAccessToken(token) {
|
|
3668
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
3669
|
-
try {
|
|
3670
|
-
if (token) {
|
|
3671
|
-
localStorage.setItem(ACCESS_TOKEN_STORAGE_KEY, token);
|
|
3672
|
-
} else {
|
|
3673
|
-
localStorage.removeItem(ACCESS_TOKEN_STORAGE_KEY);
|
|
3674
|
-
}
|
|
3675
|
-
} catch {
|
|
3676
|
-
}
|
|
3677
|
-
}
|
|
3678
|
-
}
|
|
3679
|
-
loadSessionToken() {
|
|
3680
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
3681
|
-
return localStorage.getItem(SESSION_TOKEN_STORAGE_KEY);
|
|
3682
|
-
}
|
|
3683
|
-
return null;
|
|
3684
|
-
}
|
|
3685
|
-
saveSessionToken(token) {
|
|
3686
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
3687
|
-
try {
|
|
3688
|
-
localStorage.setItem(SESSION_TOKEN_STORAGE_KEY, token);
|
|
3689
|
-
} catch {
|
|
3690
|
-
}
|
|
3691
|
-
}
|
|
3692
|
-
}
|
|
3693
|
-
captureSessionToken(response) {
|
|
3694
|
-
const token = response.headers.get(SESSION_TOKEN_HEADER);
|
|
3695
|
-
if (token && token !== this.sessionToken) {
|
|
3696
|
-
this.sessionToken = token;
|
|
3697
|
-
this.saveSessionToken(token);
|
|
3698
|
-
}
|
|
3699
|
-
}
|
|
3700
|
-
getHeaders() {
|
|
3701
|
-
const headers = {
|
|
3702
|
-
"Content-Type": "application/json"
|
|
3703
|
-
};
|
|
3704
|
-
const trimmedPublicKey = this.publicKey.trim();
|
|
3705
|
-
if (trimmedPublicKey) {
|
|
3706
|
-
headers["X-API-Key"] = trimmedPublicKey;
|
|
3707
|
-
}
|
|
3708
|
-
if (this.accessToken) {
|
|
3709
|
-
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
3710
|
-
}
|
|
3711
|
-
if (this.sessionToken) {
|
|
3712
|
-
headers[SESSION_TOKEN_HEADER] = this.sessionToken;
|
|
3713
|
-
}
|
|
3714
|
-
return headers;
|
|
3715
|
-
}
|
|
3716
|
-
async resilientFetch(url, options) {
|
|
3717
|
-
const method = options.method || "GET";
|
|
3718
|
-
const path = url.replace(this.baseUrl, "").replace(this.linkApiUrl, "");
|
|
3719
|
-
const startTime = Date.now();
|
|
3720
|
-
let retryCount = 0;
|
|
3721
|
-
const context = {
|
|
3722
|
-
method,
|
|
3723
|
-
path,
|
|
3724
|
-
url,
|
|
3725
|
-
body: options.body ? JSON.parse(options.body) : void 0,
|
|
3726
|
-
startTime
|
|
3727
|
-
};
|
|
3728
|
-
this.hooks.onRequestStart?.(context);
|
|
3729
|
-
let lastError;
|
|
3730
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
3731
|
-
try {
|
|
3732
|
-
const controller = new AbortController();
|
|
3733
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
3734
|
-
const response = await fetch(url, {
|
|
3735
|
-
...options,
|
|
3736
|
-
signal: controller.signal
|
|
3737
|
-
});
|
|
3738
|
-
clearTimeout(timeoutId);
|
|
3739
|
-
this.captureSessionToken(response);
|
|
3740
|
-
if (response.ok) {
|
|
3741
|
-
this.hooks.onRequestSuccess?.({
|
|
3742
|
-
...context,
|
|
3743
|
-
status: response.status,
|
|
3744
|
-
durationMs: Date.now() - startTime
|
|
3745
|
-
});
|
|
3746
|
-
return response;
|
|
3747
|
-
}
|
|
3748
|
-
if (response.status === 429 && attempt < this.maxRetries) {
|
|
3749
|
-
retryCount++;
|
|
3750
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
3751
|
-
const delay = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3, 3e4) || this.retryDelay * Math.pow(2, attempt) : this.retryDelay * Math.pow(2, attempt);
|
|
3752
|
-
this.hooks.onRetry?.({
|
|
3753
|
-
...context,
|
|
3754
|
-
attempt: retryCount,
|
|
3755
|
-
delayMs: delay,
|
|
3756
|
-
error: new Error(`Rate limited: ${response.status}`)
|
|
3757
|
-
});
|
|
3758
|
-
await sleep(delay);
|
|
3759
|
-
continue;
|
|
3760
|
-
}
|
|
3761
|
-
if (response.status >= 400 && response.status < 500) {
|
|
3762
|
-
this.hooks.onRequestError?.({
|
|
3763
|
-
...context,
|
|
3764
|
-
error: new CimplifyError(
|
|
3765
|
-
`HTTP_${response.status}`,
|
|
3766
|
-
`Request failed with status ${response.status}`,
|
|
3767
|
-
false
|
|
3768
|
-
),
|
|
3769
|
-
status: response.status,
|
|
3770
|
-
durationMs: Date.now() - startTime,
|
|
3771
|
-
retryCount,
|
|
3772
|
-
retryable: false
|
|
3773
|
-
});
|
|
3774
|
-
return response;
|
|
3775
|
-
}
|
|
3776
|
-
if (response.status >= 500 && attempt < this.maxRetries) {
|
|
3777
|
-
retryCount++;
|
|
3778
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
3779
|
-
this.hooks.onRetry?.({
|
|
3780
|
-
...context,
|
|
3781
|
-
attempt: retryCount,
|
|
3782
|
-
delayMs: delay,
|
|
3783
|
-
error: new Error(`Server error: ${response.status}`)
|
|
3784
|
-
});
|
|
3785
|
-
await sleep(delay);
|
|
3786
|
-
continue;
|
|
3787
|
-
}
|
|
3788
|
-
this.hooks.onRequestError?.({
|
|
3789
|
-
...context,
|
|
3790
|
-
error: new CimplifyError(
|
|
3791
|
-
"SERVER_ERROR",
|
|
3792
|
-
`Server error ${response.status} after ${retryCount} retries`,
|
|
3793
|
-
false
|
|
3794
|
-
),
|
|
3795
|
-
status: response.status,
|
|
3796
|
-
durationMs: Date.now() - startTime,
|
|
3797
|
-
retryCount,
|
|
3798
|
-
retryable: false
|
|
3799
|
-
});
|
|
3800
|
-
return response;
|
|
3801
|
-
} catch (error) {
|
|
3802
|
-
lastError = error;
|
|
3803
|
-
const networkError = toNetworkError(error, this.isTestMode());
|
|
3804
|
-
const errorRetryable = isRetryable(error);
|
|
3805
|
-
if (!errorRetryable || attempt >= this.maxRetries) {
|
|
3806
|
-
this.hooks.onRequestError?.({
|
|
3807
|
-
...context,
|
|
3808
|
-
error: networkError,
|
|
3809
|
-
durationMs: Date.now() - startTime,
|
|
3810
|
-
retryCount,
|
|
3811
|
-
retryable: errorRetryable
|
|
3812
|
-
});
|
|
3813
|
-
throw networkError;
|
|
3814
|
-
}
|
|
3815
|
-
retryCount++;
|
|
3816
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
3817
|
-
this.hooks.onRetry?.({
|
|
3818
|
-
...context,
|
|
3819
|
-
attempt: retryCount,
|
|
3820
|
-
delayMs: delay,
|
|
3821
|
-
error: networkError
|
|
3822
|
-
});
|
|
3823
|
-
await sleep(delay);
|
|
3824
|
-
}
|
|
3825
|
-
}
|
|
3826
|
-
const finalError = toNetworkError(lastError, this.isTestMode());
|
|
3827
|
-
this.hooks.onRequestError?.({
|
|
3828
|
-
...context,
|
|
3829
|
-
error: finalError,
|
|
3830
|
-
durationMs: Date.now() - startTime,
|
|
3831
|
-
retryCount,
|
|
3832
|
-
retryable: false
|
|
3833
|
-
});
|
|
3834
|
-
throw finalError;
|
|
3835
|
-
}
|
|
3836
|
-
getDedupeKey(type, payload) {
|
|
3837
|
-
return `${type}:${JSON.stringify(payload)}`;
|
|
3838
|
-
}
|
|
3839
|
-
async deduplicatedRequest(key, requestFn) {
|
|
3840
|
-
const existing = this.inflightRequests.get(key);
|
|
3841
|
-
if (existing) {
|
|
3842
|
-
return existing;
|
|
288
|
+
{ threshold: 0.5 }
|
|
289
|
+
);
|
|
290
|
+
if (containerRef.current) {
|
|
291
|
+
observer.observe(containerRef.current);
|
|
3843
292
|
}
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
|
|
3854
|
-
method: "GET",
|
|
3855
|
-
credentials: this.credentials,
|
|
3856
|
-
headers: this.getHeaders()
|
|
3857
|
-
});
|
|
3858
|
-
return this.handleRestResponse(response);
|
|
3859
|
-
});
|
|
3860
|
-
}
|
|
3861
|
-
async post(path, body) {
|
|
3862
|
-
const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
|
|
3863
|
-
method: "POST",
|
|
3864
|
-
credentials: this.credentials,
|
|
3865
|
-
headers: this.getHeaders(),
|
|
3866
|
-
body: body ? JSON.stringify(body) : void 0
|
|
3867
|
-
});
|
|
3868
|
-
return this.handleRestResponse(response);
|
|
3869
|
-
}
|
|
3870
|
-
async patch(path, body) {
|
|
3871
|
-
const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
|
|
3872
|
-
method: "PATCH",
|
|
3873
|
-
credentials: this.credentials,
|
|
3874
|
-
headers: this.getHeaders(),
|
|
3875
|
-
body: body ? JSON.stringify(body) : void 0
|
|
3876
|
-
});
|
|
3877
|
-
return this.handleRestResponse(response);
|
|
3878
|
-
}
|
|
3879
|
-
async delete(path) {
|
|
3880
|
-
const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
|
|
3881
|
-
method: "DELETE",
|
|
3882
|
-
credentials: this.credentials,
|
|
3883
|
-
headers: this.getHeaders()
|
|
3884
|
-
});
|
|
3885
|
-
return this.handleRestResponse(response);
|
|
3886
|
-
}
|
|
3887
|
-
async linkGet(path) {
|
|
3888
|
-
const key = this.getDedupeKey("linkGet", path);
|
|
3889
|
-
return this.deduplicatedRequest(key, async () => {
|
|
3890
|
-
const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
|
|
3891
|
-
method: "GET",
|
|
3892
|
-
credentials: this.credentials,
|
|
3893
|
-
headers: this.getHeaders()
|
|
3894
|
-
});
|
|
3895
|
-
return this.handleRestResponse(response);
|
|
3896
|
-
});
|
|
3897
|
-
}
|
|
3898
|
-
async linkPost(path, body) {
|
|
3899
|
-
const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
|
|
3900
|
-
method: "POST",
|
|
3901
|
-
credentials: this.credentials,
|
|
3902
|
-
headers: this.getHeaders(),
|
|
3903
|
-
body: body ? JSON.stringify(body) : void 0
|
|
3904
|
-
});
|
|
3905
|
-
return this.handleRestResponse(response);
|
|
3906
|
-
}
|
|
3907
|
-
async linkDelete(path) {
|
|
3908
|
-
const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
|
|
3909
|
-
method: "DELETE",
|
|
3910
|
-
credentials: this.credentials,
|
|
3911
|
-
headers: this.getHeaders()
|
|
3912
|
-
});
|
|
3913
|
-
return this.handleRestResponse(response);
|
|
3914
|
-
}
|
|
3915
|
-
async handleRestResponse(response) {
|
|
3916
|
-
const json = await response.json();
|
|
3917
|
-
if (!response.ok) {
|
|
3918
|
-
const errorCode = typeof json.error === "object" && json.error?.error_code || typeof json.error === "object" && json.error?.code || "API_ERROR";
|
|
3919
|
-
const errorMessage = typeof json.error === "object" && json.error?.error_message || typeof json.error === "object" && json.error?.message || typeof json.error === "string" && json.error || typeof json.message === "string" && json.message || "An error occurred";
|
|
3920
|
-
const retryable = typeof json.error === "object" && typeof json.error?.retryable === "boolean" ? json.error.retryable : false;
|
|
3921
|
-
const error = enrichError(
|
|
3922
|
-
new CimplifyError(
|
|
3923
|
-
errorCode,
|
|
3924
|
-
errorMessage,
|
|
3925
|
-
retryable
|
|
3926
|
-
),
|
|
3927
|
-
{ isTestMode: this.isTestMode() }
|
|
3928
|
-
);
|
|
3929
|
-
if (response.status === 401 || error.code === ErrorCode.UNAUTHORIZED) {
|
|
3930
|
-
console.warn(
|
|
3931
|
-
"[Cimplify] Received 401 Unauthorized. Access token may be missing/expired. Refresh authentication and retry the request."
|
|
3932
|
-
);
|
|
293
|
+
return () => observer.disconnect();
|
|
294
|
+
}, [ad, siteId, slot, onImpression, identity, apiBase]);
|
|
295
|
+
if (isLoading) {
|
|
296
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
297
|
+
"div",
|
|
298
|
+
{
|
|
299
|
+
className: `cimplify-ad cimplify-ad--loading ${className || ""}`,
|
|
300
|
+
style,
|
|
301
|
+
"aria-hidden": "true"
|
|
3933
302
|
}
|
|
3934
|
-
|
|
3935
|
-
}
|
|
3936
|
-
if (json?.success === false || json?.error?.code || json?.error?.message) {
|
|
3937
|
-
const error = enrichError(
|
|
3938
|
-
new CimplifyError(
|
|
3939
|
-
json.error?.code || "API_ERROR",
|
|
3940
|
-
json.error?.message || "An error occurred",
|
|
3941
|
-
json.error?.retryable || false
|
|
3942
|
-
),
|
|
3943
|
-
{ isTestMode: this.isTestMode() }
|
|
3944
|
-
);
|
|
3945
|
-
throw error;
|
|
3946
|
-
}
|
|
3947
|
-
if (json?.data !== void 0) {
|
|
3948
|
-
return json.data;
|
|
3949
|
-
}
|
|
3950
|
-
return json;
|
|
3951
|
-
}
|
|
3952
|
-
get catalogue() {
|
|
3953
|
-
if (!this._catalogue) {
|
|
3954
|
-
this._catalogue = new CatalogueQueries(this);
|
|
3955
|
-
}
|
|
3956
|
-
return this._catalogue;
|
|
3957
|
-
}
|
|
3958
|
-
get cart() {
|
|
3959
|
-
if (!this._cart) {
|
|
3960
|
-
this._cart = new CartOperations(this);
|
|
3961
|
-
}
|
|
3962
|
-
return this._cart;
|
|
3963
|
-
}
|
|
3964
|
-
get checkout() {
|
|
3965
|
-
if (!this._checkout) {
|
|
3966
|
-
this._checkout = new CheckoutService(this);
|
|
3967
|
-
}
|
|
3968
|
-
return this._checkout;
|
|
3969
|
-
}
|
|
3970
|
-
get orders() {
|
|
3971
|
-
if (!this._orders) {
|
|
3972
|
-
this._orders = new OrderQueries(this);
|
|
3973
|
-
}
|
|
3974
|
-
return this._orders;
|
|
3975
|
-
}
|
|
3976
|
-
get link() {
|
|
3977
|
-
if (!this._link) {
|
|
3978
|
-
this._link = new LinkService(this);
|
|
3979
|
-
}
|
|
3980
|
-
return this._link;
|
|
3981
|
-
}
|
|
3982
|
-
get auth() {
|
|
3983
|
-
if (!this._auth) {
|
|
3984
|
-
this._auth = new AuthService(this);
|
|
3985
|
-
}
|
|
3986
|
-
return this._auth;
|
|
3987
|
-
}
|
|
3988
|
-
get business() {
|
|
3989
|
-
if (!this._business) {
|
|
3990
|
-
this._business = new BusinessService(this);
|
|
3991
|
-
}
|
|
3992
|
-
return this._business;
|
|
3993
|
-
}
|
|
3994
|
-
get inventory() {
|
|
3995
|
-
if (!this._inventory) {
|
|
3996
|
-
this._inventory = new InventoryService(this);
|
|
3997
|
-
}
|
|
3998
|
-
return this._inventory;
|
|
3999
|
-
}
|
|
4000
|
-
get scheduling() {
|
|
4001
|
-
if (!this._scheduling) {
|
|
4002
|
-
this._scheduling = new SchedulingService(this);
|
|
4003
|
-
}
|
|
4004
|
-
return this._scheduling;
|
|
303
|
+
);
|
|
4005
304
|
}
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
305
|
+
if (!config?.enabled) return null;
|
|
306
|
+
if (error) return fallback ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : null;
|
|
307
|
+
if (!ad) return null;
|
|
308
|
+
const handleClick = () => {
|
|
309
|
+
onClick?.(ad.id);
|
|
310
|
+
};
|
|
311
|
+
const positionStyles = position === "fixed" ? { position: "fixed", bottom: 0, left: 0, right: 0, zIndex: 9999 } : position === "sticky" ? { position: "sticky", top: 20 } : {};
|
|
312
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
313
|
+
"div",
|
|
314
|
+
{
|
|
315
|
+
ref: containerRef,
|
|
316
|
+
id: `cimplify-ad-${ad.id}`,
|
|
317
|
+
className: `cimplify-ad cimplify-ad--${slot} cimplify-ad--${position} cimplify-ad--${config.theme} ${className || ""}`,
|
|
318
|
+
style: { ...positionStyles, ...style },
|
|
319
|
+
onClick: handleClick,
|
|
320
|
+
dangerouslySetInnerHTML: { __html: ad.html }
|
|
4009
321
|
}
|
|
4010
|
-
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/utils/cart-transform.ts
|
|
326
|
+
function getSelections(item) {
|
|
327
|
+
if (item.bundle_resolved?.selections?.length) {
|
|
328
|
+
return item.bundle_resolved.selections.map((s) => ({
|
|
329
|
+
name: s.product_name || s.component_id,
|
|
330
|
+
quantity: s.quantity,
|
|
331
|
+
variant_name: s.variant_name
|
|
332
|
+
}));
|
|
4011
333
|
}
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
334
|
+
if (item.composite_resolved?.selections?.length) {
|
|
335
|
+
return item.composite_resolved.selections.map((s) => ({
|
|
336
|
+
name: s.component_name || s.component_id,
|
|
337
|
+
quantity: s.quantity
|
|
338
|
+
}));
|
|
4017
339
|
}
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
340
|
+
return void 0;
|
|
341
|
+
}
|
|
342
|
+
function mapItem(item, fx) {
|
|
343
|
+
const amt = (value) => fx ? String(fx.convertPrice(value)) : String(value);
|
|
344
|
+
const result = {
|
|
345
|
+
name: item.name,
|
|
346
|
+
quantity: item.quantity,
|
|
347
|
+
unit_price: amt(item.base_price),
|
|
348
|
+
total_price: amt(item.total_price),
|
|
349
|
+
line_type: item.line_type
|
|
350
|
+
};
|
|
351
|
+
if (item.image_url) result.image_url = item.image_url;
|
|
352
|
+
const variantName = item.variant_info?.name || item.variant_name || void 0;
|
|
353
|
+
if (variantName) result.variant_name = variantName;
|
|
354
|
+
if (item.scheduled_start) result.scheduled_start = item.scheduled_start;
|
|
355
|
+
if (item.scheduled_end) result.scheduled_end = item.scheduled_end;
|
|
356
|
+
const selections = getSelections(item);
|
|
357
|
+
if (selections) result.selections = selections;
|
|
358
|
+
if (item.add_on_options?.length) {
|
|
359
|
+
result.add_ons = item.add_on_options.map((opt) => ({
|
|
360
|
+
name: opt.name,
|
|
361
|
+
price: amt(opt.price ?? "0")
|
|
362
|
+
}));
|
|
4023
363
|
}
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
this.setBusinessId(businessId);
|
|
4027
|
-
}
|
|
4028
|
-
return createElements(this, businessId ?? this.businessId ?? void 0, options);
|
|
364
|
+
if (item.special_instructions) {
|
|
365
|
+
result.special_instructions = item.special_instructions;
|
|
4029
366
|
}
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
369
|
+
function transformToCheckoutCart(cart, fx) {
|
|
370
|
+
const amt = (value) => fx ? String(fx.convertPrice(value)) : String(value);
|
|
371
|
+
return {
|
|
372
|
+
items: cart.items.map((item) => mapItem(item, fx)),
|
|
373
|
+
subtotal: amt(cart.pricing.subtotal),
|
|
374
|
+
tax_amount: amt(cart.pricing.tax_amount),
|
|
375
|
+
total_discounts: amt(cart.pricing.total_discounts),
|
|
376
|
+
service_charge: amt(cart.pricing.service_charge),
|
|
377
|
+
total: amt(cart.pricing.total_price),
|
|
378
|
+
currency: fx?.displayCurrency ?? cart.pricing.currency
|
|
379
|
+
};
|
|
4033
380
|
}
|
|
4034
381
|
var CART_STORAGE_PREFIX = "cimplify:cart:v2";
|
|
4035
382
|
var DEFAULT_TAX_RATE = 0.08875;
|
|
@@ -4100,13 +447,13 @@ function buildLineKey(productId, options) {
|
|
|
4100
447
|
return parts.join("|");
|
|
4101
448
|
}
|
|
4102
449
|
function calculateLineSubtotal(item) {
|
|
4103
|
-
let unitPrice = parsePrice(item.product.default_price);
|
|
450
|
+
let unitPrice = chunkJ65U5SED_js.parsePrice(item.product.default_price);
|
|
4104
451
|
if (item.variant?.price_adjustment) {
|
|
4105
|
-
unitPrice += parsePrice(item.variant.price_adjustment);
|
|
452
|
+
unitPrice += chunkJ65U5SED_js.parsePrice(item.variant.price_adjustment);
|
|
4106
453
|
}
|
|
4107
454
|
for (const option of item.addOnOptions || []) {
|
|
4108
455
|
if (option.default_price) {
|
|
4109
|
-
unitPrice += parsePrice(option.default_price);
|
|
456
|
+
unitPrice += chunkJ65U5SED_js.parsePrice(option.default_price);
|
|
4110
457
|
}
|
|
4111
458
|
}
|
|
4112
459
|
return roundMoney(unitPrice * item.quantity);
|
|
@@ -4120,6 +467,28 @@ function calculateSummary(items) {
|
|
|
4120
467
|
total: roundMoney(subtotal + tax)
|
|
4121
468
|
};
|
|
4122
469
|
}
|
|
470
|
+
function lineKeyFromItem(item) {
|
|
471
|
+
return buildLineKey(item.product.id, {
|
|
472
|
+
locationId: item.locationId,
|
|
473
|
+
variantId: item.variantId,
|
|
474
|
+
quoteId: item.quoteId,
|
|
475
|
+
addOnOptionIds: item.addOnOptionIds,
|
|
476
|
+
bundleSelections: item.bundleSelections,
|
|
477
|
+
compositeSelections: item.compositeSelections,
|
|
478
|
+
scheduledStart: item.scheduledStart,
|
|
479
|
+
staffId: item.staffId
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
function attachInternalItemMetadata(item) {
|
|
483
|
+
return {
|
|
484
|
+
...item,
|
|
485
|
+
_lineKey: lineKeyFromItem(item)
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function stripInternalItemMetadata(item) {
|
|
489
|
+
const { _lineKey, ...rest } = item;
|
|
490
|
+
return rest;
|
|
491
|
+
}
|
|
4123
492
|
function toProductFromServerItem(item, businessId) {
|
|
4124
493
|
return {
|
|
4125
494
|
id: item.item_id,
|
|
@@ -4149,7 +518,7 @@ function mapServerCart(serverCart) {
|
|
|
4149
518
|
price_adjustment: item.variant_info?.price_adjustment
|
|
4150
519
|
} : void 0;
|
|
4151
520
|
const quoteId = typeof item.metadata?.quote_id === "string" ? item.metadata.quote_id : void 0;
|
|
4152
|
-
return {
|
|
521
|
+
return attachInternalItemMetadata({
|
|
4153
522
|
id: item.id,
|
|
4154
523
|
product: toProductFromServerItem(item, serverCart.business_id),
|
|
4155
524
|
quantity: clampQuantity(item.quantity),
|
|
@@ -4157,7 +526,7 @@ function mapServerCart(serverCart) {
|
|
|
4157
526
|
variantId: item.variant_id,
|
|
4158
527
|
variant,
|
|
4159
528
|
quoteId,
|
|
4160
|
-
addOnOptionIds: item.add_on_option_ids || void 0,
|
|
529
|
+
addOnOptionIds: normalizeOptionIds(item.add_on_option_ids || void 0),
|
|
4161
530
|
addOnOptions: (item.add_on_options || []).map((option) => ({
|
|
4162
531
|
id: option.id,
|
|
4163
532
|
name: option.name,
|
|
@@ -4169,7 +538,7 @@ function mapServerCart(serverCart) {
|
|
|
4169
538
|
scheduledStart: item.scheduled_start || void 0,
|
|
4170
539
|
scheduledEnd: item.scheduled_end || void 0,
|
|
4171
540
|
staffId: item.staff_id || void 0
|
|
4172
|
-
};
|
|
541
|
+
});
|
|
4173
542
|
});
|
|
4174
543
|
return {
|
|
4175
544
|
items,
|
|
@@ -4199,6 +568,7 @@ function createCartStore(params) {
|
|
|
4199
568
|
let snapshot = createEmptySnapshot(currency);
|
|
4200
569
|
let initialized = false;
|
|
4201
570
|
let initializePromise = null;
|
|
571
|
+
let persistTimer = null;
|
|
4202
572
|
function emit() {
|
|
4203
573
|
listeners.forEach((listener) => {
|
|
4204
574
|
listener();
|
|
@@ -4206,7 +576,7 @@ function createCartStore(params) {
|
|
|
4206
576
|
}
|
|
4207
577
|
function setSnapshot(nextSnapshot) {
|
|
4208
578
|
snapshot = nextSnapshot;
|
|
4209
|
-
|
|
579
|
+
schedulePersist();
|
|
4210
580
|
emit();
|
|
4211
581
|
}
|
|
4212
582
|
function updateSnapshot(updater) {
|
|
@@ -4220,7 +590,7 @@ function createCartStore(params) {
|
|
|
4220
590
|
window.localStorage.setItem(
|
|
4221
591
|
storageKey,
|
|
4222
592
|
JSON.stringify({
|
|
4223
|
-
items: snapshot.items,
|
|
593
|
+
items: snapshot.items.map((item) => stripInternalItemMetadata(item)),
|
|
4224
594
|
subtotal: snapshot.subtotal,
|
|
4225
595
|
tax: snapshot.tax,
|
|
4226
596
|
total: snapshot.total,
|
|
@@ -4230,6 +600,15 @@ function createCartStore(params) {
|
|
|
4230
600
|
} catch {
|
|
4231
601
|
}
|
|
4232
602
|
}
|
|
603
|
+
function schedulePersist() {
|
|
604
|
+
if (persistTimer) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
persistTimer = setTimeout(() => {
|
|
608
|
+
persistTimer = null;
|
|
609
|
+
persistSnapshot();
|
|
610
|
+
}, 100);
|
|
611
|
+
}
|
|
4233
612
|
function hydrateFromStorage() {
|
|
4234
613
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
4235
614
|
return;
|
|
@@ -4244,7 +623,7 @@ function createCartStore(params) {
|
|
|
4244
623
|
return;
|
|
4245
624
|
}
|
|
4246
625
|
snapshot = {
|
|
4247
|
-
items: parsed.items,
|
|
626
|
+
items: parsed.items.map((item) => attachInternalItemMetadata(item)),
|
|
4248
627
|
subtotal: toNumber(parsed.subtotal),
|
|
4249
628
|
tax: toNumber(parsed.tax),
|
|
4250
629
|
total: toNumber(parsed.total),
|
|
@@ -4321,7 +700,7 @@ function createCartStore(params) {
|
|
|
4321
700
|
}
|
|
4322
701
|
const previousSnapshot = snapshot;
|
|
4323
702
|
const lineKey = buildLineKey(product.id, normalizedOptions);
|
|
4324
|
-
const optimisticItem = {
|
|
703
|
+
const optimisticItem = attachInternalItemMetadata({
|
|
4325
704
|
id: `tmp_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
|
|
4326
705
|
product,
|
|
4327
706
|
quantity: resolvedQuantity,
|
|
@@ -4337,21 +716,11 @@ function createCartStore(params) {
|
|
|
4337
716
|
scheduledStart: normalizedOptions.scheduledStart,
|
|
4338
717
|
scheduledEnd: normalizedOptions.scheduledEnd,
|
|
4339
718
|
staffId: normalizedOptions.staffId
|
|
4340
|
-
};
|
|
719
|
+
});
|
|
4341
720
|
updateSnapshot((current) => {
|
|
4342
|
-
const existingIndex = current.items.findIndex(
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
variantId: item.variantId,
|
|
4346
|
-
quoteId: item.quoteId,
|
|
4347
|
-
addOnOptionIds: item.addOnOptionIds,
|
|
4348
|
-
bundleSelections: item.bundleSelections,
|
|
4349
|
-
compositeSelections: item.compositeSelections,
|
|
4350
|
-
scheduledStart: item.scheduledStart,
|
|
4351
|
-
staffId: item.staffId
|
|
4352
|
-
});
|
|
4353
|
-
return itemKey === lineKey;
|
|
4354
|
-
});
|
|
721
|
+
const existingIndex = current.items.findIndex(
|
|
722
|
+
(item) => (item._lineKey ?? lineKeyFromItem(item)) === lineKey
|
|
723
|
+
);
|
|
4355
724
|
const nextItems = [...current.items];
|
|
4356
725
|
if (existingIndex >= 0) {
|
|
4357
726
|
const existing = nextItems[existingIndex];
|
|
@@ -4561,7 +930,7 @@ function getOrCreateStore(params) {
|
|
|
4561
930
|
return created;
|
|
4562
931
|
}
|
|
4563
932
|
function useCart(options = {}) {
|
|
4564
|
-
const context =
|
|
933
|
+
const context = useOptionalCimplifyClient();
|
|
4565
934
|
const client = options.client ?? context?.client;
|
|
4566
935
|
if (!client) {
|
|
4567
936
|
throw new Error("useCart must be used within CimplifyProvider or passed { client }.");
|
|
@@ -4642,7 +1011,7 @@ var DEFAULT_COUNTRY = "US";
|
|
|
4642
1011
|
function createDefaultClient() {
|
|
4643
1012
|
const processRef = globalThis.process;
|
|
4644
1013
|
const envPublicKey = processRef?.env?.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || "";
|
|
4645
|
-
return createCimplifyClient({ publicKey: envPublicKey });
|
|
1014
|
+
return chunkOITG2SEN_js.createCimplifyClient({ publicKey: envPublicKey });
|
|
4646
1015
|
}
|
|
4647
1016
|
function getStoredLocationId() {
|
|
4648
1017
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
@@ -4699,7 +1068,8 @@ function resolveInitialLocation(locations) {
|
|
|
4699
1068
|
}
|
|
4700
1069
|
return locations[0];
|
|
4701
1070
|
}
|
|
4702
|
-
var
|
|
1071
|
+
var CimplifyClientContext = React3.createContext(null);
|
|
1072
|
+
var CimplifyStateContext = React3.createContext(null);
|
|
4703
1073
|
function CimplifyProvider({
|
|
4704
1074
|
client,
|
|
4705
1075
|
children,
|
|
@@ -4724,7 +1094,7 @@ function CimplifyProvider({
|
|
|
4724
1094
|
const setDisplayCurrency = React3.useCallback(
|
|
4725
1095
|
(currency) => {
|
|
4726
1096
|
const normalized = currency?.trim().toUpperCase() || null;
|
|
4727
|
-
if (normalized && !isSupportedCurrency(normalized)) {
|
|
1097
|
+
if (normalized && !chunkJ65U5SED_js.isSupportedCurrency(normalized)) {
|
|
4728
1098
|
return;
|
|
4729
1099
|
}
|
|
4730
1100
|
setDisplayCurrencyOverride(normalized);
|
|
@@ -4838,49 +1208,76 @@ function CimplifyProvider({
|
|
|
4838
1208
|
cancelled = true;
|
|
4839
1209
|
};
|
|
4840
1210
|
}, [resolvedClient, isDemoMode]);
|
|
4841
|
-
const
|
|
1211
|
+
const clientContextValue = React3.useMemo(
|
|
4842
1212
|
() => ({
|
|
4843
1213
|
client: resolvedClient,
|
|
4844
1214
|
business,
|
|
4845
1215
|
currency: baseCurrency,
|
|
4846
1216
|
country: business?.country_code || DEFAULT_COUNTRY,
|
|
4847
1217
|
locations,
|
|
4848
|
-
currentLocation,
|
|
4849
|
-
setCurrentLocation,
|
|
4850
1218
|
isReady,
|
|
4851
1219
|
isDemoMode,
|
|
1220
|
+
baseCurrency
|
|
1221
|
+
}),
|
|
1222
|
+
[
|
|
1223
|
+
resolvedClient,
|
|
1224
|
+
business,
|
|
4852
1225
|
baseCurrency,
|
|
1226
|
+
locations,
|
|
1227
|
+
isReady,
|
|
1228
|
+
isDemoMode
|
|
1229
|
+
]
|
|
1230
|
+
);
|
|
1231
|
+
const stateContextValue = React3.useMemo(
|
|
1232
|
+
() => ({
|
|
1233
|
+
currentLocation,
|
|
1234
|
+
setCurrentLocation,
|
|
4853
1235
|
displayCurrency,
|
|
4854
1236
|
setDisplayCurrency,
|
|
4855
1237
|
convertPrice,
|
|
4856
1238
|
fxRate
|
|
4857
1239
|
}),
|
|
4858
1240
|
[
|
|
4859
|
-
resolvedClient,
|
|
4860
|
-
business,
|
|
4861
|
-
baseCurrency,
|
|
4862
|
-
locations,
|
|
4863
1241
|
currentLocation,
|
|
4864
1242
|
setCurrentLocation,
|
|
4865
|
-
isReady,
|
|
4866
|
-
isDemoMode,
|
|
4867
1243
|
displayCurrency,
|
|
4868
1244
|
setDisplayCurrency,
|
|
4869
1245
|
convertPrice,
|
|
4870
1246
|
fxRate
|
|
4871
1247
|
]
|
|
4872
1248
|
);
|
|
4873
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1249
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CimplifyClientContext.Provider, { value: clientContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(CimplifyStateContext.Provider, { value: stateContextValue, children }) });
|
|
4874
1250
|
}
|
|
4875
1251
|
function useCimplify() {
|
|
4876
|
-
const
|
|
4877
|
-
|
|
1252
|
+
const clientContext = React3.useContext(CimplifyClientContext);
|
|
1253
|
+
const stateContext = React3.useContext(CimplifyStateContext);
|
|
1254
|
+
if (!clientContext || !stateContext) {
|
|
4878
1255
|
throw new Error("useCimplify must be used within CimplifyProvider");
|
|
4879
1256
|
}
|
|
4880
|
-
return
|
|
1257
|
+
return React3.useMemo(
|
|
1258
|
+
() => ({ ...clientContext, ...stateContext }),
|
|
1259
|
+
[clientContext, stateContext]
|
|
1260
|
+
);
|
|
4881
1261
|
}
|
|
4882
1262
|
function useOptionalCimplify() {
|
|
4883
|
-
|
|
1263
|
+
const clientContext = React3.useContext(CimplifyClientContext);
|
|
1264
|
+
const stateContext = React3.useContext(CimplifyStateContext);
|
|
1265
|
+
return React3.useMemo(() => {
|
|
1266
|
+
if (!clientContext || !stateContext) {
|
|
1267
|
+
return null;
|
|
1268
|
+
}
|
|
1269
|
+
return { ...clientContext, ...stateContext };
|
|
1270
|
+
}, [clientContext, stateContext]);
|
|
1271
|
+
}
|
|
1272
|
+
function useCimplifyClient() {
|
|
1273
|
+
const context = React3.useContext(CimplifyClientContext);
|
|
1274
|
+
if (!context) {
|
|
1275
|
+
throw new Error("useCimplifyClient must be used within CimplifyProvider");
|
|
1276
|
+
}
|
|
1277
|
+
return context;
|
|
1278
|
+
}
|
|
1279
|
+
function useOptionalCimplifyClient() {
|
|
1280
|
+
return React3.useContext(CimplifyClientContext);
|
|
4884
1281
|
}
|
|
4885
1282
|
var SPACE = { sm: 8};
|
|
4886
1283
|
function shellColors(isDark, primaryColor) {
|
|
@@ -5259,20 +1656,32 @@ function Price({ amount, className, prefix }) {
|
|
|
5259
1656
|
const { displayCurrency, convertPrice } = useCimplify();
|
|
5260
1657
|
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className, children: [
|
|
5261
1658
|
prefix,
|
|
5262
|
-
formatPrice(convertPrice(amount), displayCurrency)
|
|
1659
|
+
chunkJ65U5SED_js.formatPrice(convertPrice(amount), displayCurrency)
|
|
5263
1660
|
] });
|
|
5264
1661
|
}
|
|
5265
1662
|
var productsCache = /* @__PURE__ */ new Map();
|
|
5266
1663
|
var productsInflight = /* @__PURE__ */ new Map();
|
|
5267
1664
|
function buildProductsCacheKey(client, locationId, options) {
|
|
5268
|
-
return
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
options
|
|
5272
|
-
|
|
1665
|
+
return [
|
|
1666
|
+
client.getPublicKey(),
|
|
1667
|
+
locationId ?? "",
|
|
1668
|
+
options.category ?? "",
|
|
1669
|
+
options.collection ?? "",
|
|
1670
|
+
options.taxonomy ?? "",
|
|
1671
|
+
options.search ?? "",
|
|
1672
|
+
options.featured ?? "",
|
|
1673
|
+
options.limit ?? "",
|
|
1674
|
+
options.sort_by ?? "",
|
|
1675
|
+
options.sort_order ?? "",
|
|
1676
|
+
options.tags?.join(",") ?? "",
|
|
1677
|
+
options.in_stock ?? "",
|
|
1678
|
+
options.min_price ?? "",
|
|
1679
|
+
options.max_price ?? "",
|
|
1680
|
+
options.page ?? ""
|
|
1681
|
+
].join("|");
|
|
5273
1682
|
}
|
|
5274
1683
|
function useProducts(options = {}) {
|
|
5275
|
-
const context =
|
|
1684
|
+
const context = useOptionalCimplifyClient();
|
|
5276
1685
|
const client = options.client ?? context?.client;
|
|
5277
1686
|
if (!client) {
|
|
5278
1687
|
throw new Error("useProducts must be used within CimplifyProvider or passed { client }.");
|
|
@@ -5281,6 +1690,7 @@ function useProducts(options = {}) {
|
|
|
5281
1690
|
const locationId = client.getLocationId();
|
|
5282
1691
|
const previousLocationIdRef = React3.useRef(locationId);
|
|
5283
1692
|
const requestIdRef = React3.useRef(0);
|
|
1693
|
+
const tagsSignature = options.tags?.join(",") ?? "";
|
|
5284
1694
|
const queryOptions = React3.useMemo(
|
|
5285
1695
|
() => ({
|
|
5286
1696
|
category: options.category,
|
|
@@ -5306,7 +1716,7 @@ function useProducts(options = {}) {
|
|
|
5306
1716
|
options.limit,
|
|
5307
1717
|
options.sort_by,
|
|
5308
1718
|
options.sort_order,
|
|
5309
|
-
|
|
1719
|
+
tagsSignature,
|
|
5310
1720
|
options.in_stock,
|
|
5311
1721
|
options.min_price,
|
|
5312
1722
|
options.max_price,
|
|
@@ -5424,7 +1834,7 @@ function buildProductCacheKey(client, locationId, slugOrId) {
|
|
|
5424
1834
|
});
|
|
5425
1835
|
}
|
|
5426
1836
|
function useProduct(slugOrId, options = {}) {
|
|
5427
|
-
const context =
|
|
1837
|
+
const context = useOptionalCimplifyClient();
|
|
5428
1838
|
const client = options.client ?? context?.client;
|
|
5429
1839
|
if (!client) {
|
|
5430
1840
|
throw new Error("useProduct must be used within CimplifyProvider or passed { client }.");
|
|
@@ -5528,7 +1938,7 @@ function buildCategoriesCacheKey(client) {
|
|
|
5528
1938
|
return client.getPublicKey() || "__demo__";
|
|
5529
1939
|
}
|
|
5530
1940
|
function useCategories(options = {}) {
|
|
5531
|
-
const context =
|
|
1941
|
+
const context = useOptionalCimplifyClient();
|
|
5532
1942
|
const client = options.client ?? context?.client;
|
|
5533
1943
|
if (!client) {
|
|
5534
1944
|
throw new Error("useCategories must be used within CimplifyProvider or passed { client }.");
|
|
@@ -5598,7 +2008,7 @@ function buildOrderCacheKey(client, orderId) {
|
|
|
5598
2008
|
return `${client.getPublicKey() || "__demo__"}:${orderId}`;
|
|
5599
2009
|
}
|
|
5600
2010
|
function useOrder(orderId, options = {}) {
|
|
5601
|
-
const context =
|
|
2011
|
+
const context = useOptionalCimplifyClient();
|
|
5602
2012
|
const client = options.client ?? context?.client;
|
|
5603
2013
|
if (!client) {
|
|
5604
2014
|
throw new Error("useOrder must be used within CimplifyProvider or passed { client }.");
|
|
@@ -5800,7 +2210,7 @@ function buildCollectionsCacheKey(client, locationId) {
|
|
|
5800
2210
|
});
|
|
5801
2211
|
}
|
|
5802
2212
|
function useCollections(options = {}) {
|
|
5803
|
-
const context =
|
|
2213
|
+
const context = useOptionalCimplifyClient();
|
|
5804
2214
|
const client = options.client ?? context?.client;
|
|
5805
2215
|
if (!client) {
|
|
5806
2216
|
throw new Error("useCollections must be used within CimplifyProvider or passed { client }.");
|
|
@@ -5896,7 +2306,7 @@ function buildCollectionCacheKey(client, locationId, idOrSlug) {
|
|
|
5896
2306
|
});
|
|
5897
2307
|
}
|
|
5898
2308
|
function useCollection(idOrSlug, options = {}) {
|
|
5899
|
-
const context =
|
|
2309
|
+
const context = useOptionalCimplifyClient();
|
|
5900
2310
|
const client = options.client ?? context?.client;
|
|
5901
2311
|
if (!client) {
|
|
5902
2312
|
throw new Error("useCollection must be used within CimplifyProvider or passed { client }.");
|
|
@@ -5997,7 +2407,7 @@ function useCollection(idOrSlug, options = {}) {
|
|
|
5997
2407
|
return { collection, products, isLoading, error, refetch };
|
|
5998
2408
|
}
|
|
5999
2409
|
function useSearch(options = {}) {
|
|
6000
|
-
const context =
|
|
2410
|
+
const context = useOptionalCimplifyClient();
|
|
6001
2411
|
const client = options.client ?? context?.client;
|
|
6002
2412
|
if (!client) {
|
|
6003
2413
|
throw new Error("useSearch must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6077,11 +2487,46 @@ function useSearch(options = {}) {
|
|
|
6077
2487
|
var quoteCache = /* @__PURE__ */ new Map();
|
|
6078
2488
|
var quoteInflight = /* @__PURE__ */ new Map();
|
|
6079
2489
|
function buildQuoteCacheKey(client, locationId, inputSignature) {
|
|
6080
|
-
return
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
2490
|
+
return [client.getPublicKey(), locationId ?? "", inputSignature].join("|");
|
|
2491
|
+
}
|
|
2492
|
+
function serializeQuoteBundleSelections(selections) {
|
|
2493
|
+
if (!selections || selections.length === 0) {
|
|
2494
|
+
return "";
|
|
2495
|
+
}
|
|
2496
|
+
return selections.map((selection) => {
|
|
2497
|
+
return [
|
|
2498
|
+
selection.component_id,
|
|
2499
|
+
selection.quantity,
|
|
2500
|
+
selection.variant_id ?? ""
|
|
2501
|
+
].join(":");
|
|
2502
|
+
}).join(";");
|
|
2503
|
+
}
|
|
2504
|
+
function serializeQuoteCompositeSelections(selections) {
|
|
2505
|
+
if (!selections || selections.length === 0) {
|
|
2506
|
+
return "";
|
|
2507
|
+
}
|
|
2508
|
+
return selections.map((selection) => {
|
|
2509
|
+
return [
|
|
2510
|
+
selection.component_id,
|
|
2511
|
+
selection.quantity,
|
|
2512
|
+
selection.variant_id ?? "",
|
|
2513
|
+
selection.add_on_option_id ?? ""
|
|
2514
|
+
].join(":");
|
|
2515
|
+
}).join(";");
|
|
2516
|
+
}
|
|
2517
|
+
function buildQuoteInputSignature(input) {
|
|
2518
|
+
if (!input) {
|
|
2519
|
+
return "";
|
|
2520
|
+
}
|
|
2521
|
+
return [
|
|
2522
|
+
input.productId,
|
|
2523
|
+
input.variantId ?? "",
|
|
2524
|
+
input.locationId ?? "",
|
|
2525
|
+
input.quantity ?? "",
|
|
2526
|
+
input.addOnOptionIds?.join(",") ?? "",
|
|
2527
|
+
serializeQuoteBundleSelections(input.bundleSelections),
|
|
2528
|
+
serializeQuoteCompositeSelections(input.compositeSelections)
|
|
2529
|
+
].join("|");
|
|
6085
2530
|
}
|
|
6086
2531
|
function isQuoteExpired(quote) {
|
|
6087
2532
|
if (!quote?.expires_at) {
|
|
@@ -6108,7 +2553,7 @@ function normalizeInput(input, fallbackLocationId) {
|
|
|
6108
2553
|
};
|
|
6109
2554
|
}
|
|
6110
2555
|
function useQuote(input, options = {}) {
|
|
6111
|
-
const context =
|
|
2556
|
+
const context = useOptionalCimplifyClient();
|
|
6112
2557
|
const client = options.client ?? context?.client;
|
|
6113
2558
|
if (!client) {
|
|
6114
2559
|
throw new Error("useQuote must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6118,10 +2563,8 @@ function useQuote(input, options = {}) {
|
|
|
6118
2563
|
const refreshBeforeExpiryMs = Math.max(0, options.refreshBeforeExpiryMs ?? 3e4);
|
|
6119
2564
|
const locationId = client.getLocationId();
|
|
6120
2565
|
const requestIdRef = React3.useRef(0);
|
|
6121
|
-
const refreshTimerRef = React3.useRef(null);
|
|
6122
|
-
const expiryTimerRef = React3.useRef(null);
|
|
6123
2566
|
const debounceTimerRef = React3.useRef(null);
|
|
6124
|
-
const inputSignature =
|
|
2567
|
+
const inputSignature = buildQuoteInputSignature(input);
|
|
6125
2568
|
const normalizedInput = React3.useMemo(() => {
|
|
6126
2569
|
if (!input) {
|
|
6127
2570
|
return null;
|
|
@@ -6139,6 +2582,13 @@ function useQuote(input, options = {}) {
|
|
|
6139
2582
|
const [error, setError] = React3.useState(null);
|
|
6140
2583
|
const [isExpired, setIsExpired] = React3.useState(isQuoteExpired(cached?.quote ?? null));
|
|
6141
2584
|
const [messages, setMessages] = React3.useState(cached?.quote?.ui_messages ?? []);
|
|
2585
|
+
const quoteExpiryTimestamp = React3.useMemo(() => {
|
|
2586
|
+
if (!quote?.expires_at) {
|
|
2587
|
+
return Number.NaN;
|
|
2588
|
+
}
|
|
2589
|
+
const expiresAt = Date.parse(quote.expires_at);
|
|
2590
|
+
return Number.isFinite(expiresAt) ? expiresAt : Number.NaN;
|
|
2591
|
+
}, [quote?.expires_at]);
|
|
6142
2592
|
const load = React3.useCallback(
|
|
6143
2593
|
async (force = false) => {
|
|
6144
2594
|
if (!enabled || !normalizedInput) {
|
|
@@ -6256,52 +2706,34 @@ function useQuote(input, options = {}) {
|
|
|
6256
2706
|
}
|
|
6257
2707
|
}, [cacheKey, client, enabled, load, normalizedInput, quote]);
|
|
6258
2708
|
React3.useEffect(() => {
|
|
6259
|
-
if (
|
|
6260
|
-
clearTimeout(expiryTimerRef.current);
|
|
6261
|
-
expiryTimerRef.current = null;
|
|
6262
|
-
}
|
|
6263
|
-
const expiresAt = quote?.expires_at ? Date.parse(quote.expires_at) : NaN;
|
|
6264
|
-
if (!Number.isFinite(expiresAt)) {
|
|
2709
|
+
if (!Number.isFinite(quoteExpiryTimestamp)) {
|
|
6265
2710
|
setIsExpired(false);
|
|
6266
2711
|
return;
|
|
6267
2712
|
}
|
|
6268
|
-
const
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
expiryTimerRef.current = null;
|
|
6279
|
-
}
|
|
6280
|
-
};
|
|
6281
|
-
}, [quote?.expires_at, quote?.quote_id]);
|
|
6282
|
-
React3.useEffect(() => {
|
|
6283
|
-
if (refreshTimerRef.current) {
|
|
6284
|
-
clearTimeout(refreshTimerRef.current);
|
|
6285
|
-
refreshTimerRef.current = null;
|
|
6286
|
-
}
|
|
6287
|
-
if (!autoRefresh || !enabled || !quote?.expires_at) {
|
|
6288
|
-
return;
|
|
2713
|
+
const now = Date.now();
|
|
2714
|
+
const isCurrentlyExpired = quoteExpiryTimestamp <= now;
|
|
2715
|
+
const timers = [];
|
|
2716
|
+
setIsExpired(isCurrentlyExpired);
|
|
2717
|
+
if (!isCurrentlyExpired) {
|
|
2718
|
+
timers.push(
|
|
2719
|
+
setTimeout(() => {
|
|
2720
|
+
setIsExpired(true);
|
|
2721
|
+
}, Math.max(0, quoteExpiryTimestamp - now))
|
|
2722
|
+
);
|
|
6289
2723
|
}
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
2724
|
+
if (autoRefresh && enabled && !isCurrentlyExpired) {
|
|
2725
|
+
timers.push(
|
|
2726
|
+
setTimeout(() => {
|
|
2727
|
+
void refresh();
|
|
2728
|
+
}, Math.max(0, quoteExpiryTimestamp - now - refreshBeforeExpiryMs))
|
|
2729
|
+
);
|
|
6293
2730
|
}
|
|
6294
|
-
const delay = Math.max(0, expiresAt - Date.now() - refreshBeforeExpiryMs);
|
|
6295
|
-
refreshTimerRef.current = setTimeout(() => {
|
|
6296
|
-
void refresh();
|
|
6297
|
-
}, delay);
|
|
6298
2731
|
return () => {
|
|
6299
|
-
|
|
6300
|
-
clearTimeout(
|
|
6301
|
-
refreshTimerRef.current = null;
|
|
2732
|
+
for (const timer of timers) {
|
|
2733
|
+
clearTimeout(timer);
|
|
6302
2734
|
}
|
|
6303
2735
|
};
|
|
6304
|
-
}, [autoRefresh, enabled,
|
|
2736
|
+
}, [autoRefresh, enabled, quoteExpiryTimestamp, refresh, refreshBeforeExpiryMs]);
|
|
6305
2737
|
return { quote, isLoading, error, refresh, isExpired, messages };
|
|
6306
2738
|
}
|
|
6307
2739
|
var taxonomiesCache = /* @__PURE__ */ new Map();
|
|
@@ -6313,7 +2745,7 @@ function buildTaxonomiesCacheKey(client, parentId) {
|
|
|
6313
2745
|
});
|
|
6314
2746
|
}
|
|
6315
2747
|
function useTaxonomies(options = {}) {
|
|
6316
|
-
const context =
|
|
2748
|
+
const context = useOptionalCimplifyClient();
|
|
6317
2749
|
const client = options.client ?? context?.client;
|
|
6318
2750
|
if (!client) {
|
|
6319
2751
|
throw new Error("useTaxonomies must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6390,7 +2822,7 @@ function buildTaxonomyCacheKey(client, id) {
|
|
|
6390
2822
|
});
|
|
6391
2823
|
}
|
|
6392
2824
|
function useTaxonomy(id, options = {}) {
|
|
6393
|
-
const context =
|
|
2825
|
+
const context = useOptionalCimplifyClient();
|
|
6394
2826
|
const client = options.client ?? context?.client;
|
|
6395
2827
|
if (!client) {
|
|
6396
2828
|
throw new Error("useTaxonomy must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6470,7 +2902,7 @@ function buildTaxonomyPathCacheKey(client, id) {
|
|
|
6470
2902
|
});
|
|
6471
2903
|
}
|
|
6472
2904
|
function useTaxonomyPath(id, options = {}) {
|
|
6473
|
-
const context =
|
|
2905
|
+
const context = useOptionalCimplifyClient();
|
|
6474
2906
|
const client = options.client ?? context?.client;
|
|
6475
2907
|
if (!client) {
|
|
6476
2908
|
throw new Error("useTaxonomyPath must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6543,7 +2975,7 @@ function buildSchedulesCacheKey(client, productId) {
|
|
|
6543
2975
|
});
|
|
6544
2976
|
}
|
|
6545
2977
|
function useProductSchedules(productId, options = {}) {
|
|
6546
|
-
const context =
|
|
2978
|
+
const context = useOptionalCimplifyClient();
|
|
6547
2979
|
const client = options.client ?? context?.client;
|
|
6548
2980
|
if (!client) {
|
|
6549
2981
|
throw new Error("useProductSchedules must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6617,7 +3049,7 @@ function buildAvailabilityCacheKey(client, productId, locationId) {
|
|
|
6617
3049
|
});
|
|
6618
3050
|
}
|
|
6619
3051
|
function useProductAvailability(productId, options = {}) {
|
|
6620
|
-
const context =
|
|
3052
|
+
const context = useOptionalCimplifyClient();
|
|
6621
3053
|
const client = options.client ?? context?.client;
|
|
6622
3054
|
if (!client) {
|
|
6623
3055
|
throw new Error("useProductAvailability must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6706,7 +3138,7 @@ function buildDealsCacheKey(client, locationId) {
|
|
|
6706
3138
|
return JSON.stringify({ key: client.getPublicKey(), location_id: locationId });
|
|
6707
3139
|
}
|
|
6708
3140
|
function useDeals(options = {}) {
|
|
6709
|
-
const context =
|
|
3141
|
+
const context = useOptionalCimplifyClient();
|
|
6710
3142
|
const client = options.client ?? context?.client;
|
|
6711
3143
|
if (!client) {
|
|
6712
3144
|
throw new Error("useDeals must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6785,7 +3217,7 @@ function buildOnSaleCacheKey(client) {
|
|
|
6785
3217
|
return client.getPublicKey() || "__demo__";
|
|
6786
3218
|
}
|
|
6787
3219
|
function useProductsOnSale(options = {}) {
|
|
6788
|
-
const context =
|
|
3220
|
+
const context = useOptionalCimplifyClient();
|
|
6789
3221
|
const client = options.client ?? context?.client;
|
|
6790
3222
|
if (!client) {
|
|
6791
3223
|
throw new Error("useProductsOnSale must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6855,7 +3287,7 @@ function buildProductDealsCacheKey(client, productId) {
|
|
|
6855
3287
|
return JSON.stringify({ key: client.getPublicKey(), product_id: productId });
|
|
6856
3288
|
}
|
|
6857
3289
|
function useProductDeals(productId, options = {}) {
|
|
6858
|
-
const context =
|
|
3290
|
+
const context = useOptionalCimplifyClient();
|
|
6859
3291
|
const client = options.client ?? context?.client;
|
|
6860
3292
|
if (!client) {
|
|
6861
3293
|
throw new Error("useProductDeals must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6923,7 +3355,7 @@ function useProductDeals(productId, options = {}) {
|
|
|
6923
3355
|
return { deals, isLoading, error, refetch };
|
|
6924
3356
|
}
|
|
6925
3357
|
function useValidateDiscount(options = {}) {
|
|
6926
|
-
const context =
|
|
3358
|
+
const context = useOptionalCimplifyClient();
|
|
6927
3359
|
const client = options.client ?? context?.client;
|
|
6928
3360
|
if (!client) {
|
|
6929
3361
|
throw new Error("useValidateDiscount must be used within CimplifyProvider or passed { client }.");
|
|
@@ -6961,7 +3393,7 @@ function buildBundlesCacheKey(client, locationId) {
|
|
|
6961
3393
|
});
|
|
6962
3394
|
}
|
|
6963
3395
|
function useBundles(options = {}) {
|
|
6964
|
-
const context =
|
|
3396
|
+
const context = useOptionalCimplifyClient();
|
|
6965
3397
|
const client = options.client ?? context?.client;
|
|
6966
3398
|
if (!client) {
|
|
6967
3399
|
throw new Error("useBundles must be used within CimplifyProvider or passed { client }.");
|
|
@@ -7058,16 +3490,16 @@ function useProductPrice(input) {
|
|
|
7058
3490
|
if (bundleTotalPrice != null) {
|
|
7059
3491
|
unitPrice = bundleTotalPrice;
|
|
7060
3492
|
} else if (compositePrice) {
|
|
7061
|
-
unitPrice = parsePrice(compositePrice.final_price);
|
|
3493
|
+
unitPrice = chunkJ65U5SED_js.parsePrice(compositePrice.final_price);
|
|
7062
3494
|
} else {
|
|
7063
|
-
unitPrice = parsePrice(product.default_price);
|
|
3495
|
+
unitPrice = chunkJ65U5SED_js.parsePrice(product.default_price);
|
|
7064
3496
|
if (variant?.price_adjustment) {
|
|
7065
|
-
unitPrice += parsePrice(variant.price_adjustment);
|
|
3497
|
+
unitPrice += chunkJ65U5SED_js.parsePrice(variant.price_adjustment);
|
|
7066
3498
|
}
|
|
7067
3499
|
if (addOnOptions) {
|
|
7068
3500
|
for (const option of addOnOptions) {
|
|
7069
3501
|
if (option.default_price) {
|
|
7070
|
-
unitPrice += parsePrice(option.default_price);
|
|
3502
|
+
unitPrice += chunkJ65U5SED_js.parsePrice(option.default_price);
|
|
7071
3503
|
}
|
|
7072
3504
|
}
|
|
7073
3505
|
}
|
|
@@ -7087,7 +3519,7 @@ function buildOrdersCacheKey(client, status, limit) {
|
|
|
7087
3519
|
return JSON.stringify({ key: client.getPublicKey(), status, limit });
|
|
7088
3520
|
}
|
|
7089
3521
|
function useOrders(options = {}) {
|
|
7090
|
-
const context =
|
|
3522
|
+
const context = useOptionalCimplifyClient();
|
|
7091
3523
|
const client = options.client ?? context?.client;
|
|
7092
3524
|
if (!client) {
|
|
7093
3525
|
throw new Error("useOrders must be used within CimplifyProvider or passed { client }.");
|
|
@@ -7172,7 +3604,7 @@ function buildServicesCacheKey(client) {
|
|
|
7172
3604
|
return JSON.stringify({ key: client.getPublicKey() });
|
|
7173
3605
|
}
|
|
7174
3606
|
function useServices(options = {}) {
|
|
7175
|
-
const context =
|
|
3607
|
+
const context = useOptionalCimplifyClient();
|
|
7176
3608
|
const client = options.client ?? context?.client;
|
|
7177
3609
|
if (!client) {
|
|
7178
3610
|
throw new Error("useServices must be used within CimplifyProvider or passed { client }.");
|
|
@@ -7256,7 +3688,7 @@ function buildSlotsCacheKey(client, serviceId, date, participantCount) {
|
|
|
7256
3688
|
});
|
|
7257
3689
|
}
|
|
7258
3690
|
function useAvailableSlots(serviceId, date, options = {}) {
|
|
7259
|
-
const context =
|
|
3691
|
+
const context = useOptionalCimplifyClient();
|
|
7260
3692
|
const client = options.client ?? context?.client;
|
|
7261
3693
|
if (!client) {
|
|
7262
3694
|
throw new Error("useAvailableSlots must be used within CimplifyProvider or passed { client }.");
|
|
@@ -7350,7 +3782,7 @@ function buildAvailabilityCacheKey2(client, serviceId, startDate, endDate, parti
|
|
|
7350
3782
|
});
|
|
7351
3783
|
}
|
|
7352
3784
|
function useServiceAvailability(serviceId, startDate, endDate, options = {}) {
|
|
7353
|
-
const context =
|
|
3785
|
+
const context = useOptionalCimplifyClient();
|
|
7354
3786
|
const client = options.client ?? context?.client;
|
|
7355
3787
|
if (!client) {
|
|
7356
3788
|
throw new Error(
|
|
@@ -7448,7 +3880,7 @@ function buildBookingsCacheKey(client, filter) {
|
|
|
7448
3880
|
return JSON.stringify({ key: client.getPublicKey(), filter });
|
|
7449
3881
|
}
|
|
7450
3882
|
function useBookings(options = {}) {
|
|
7451
|
-
const context =
|
|
3883
|
+
const context = useOptionalCimplifyClient();
|
|
7452
3884
|
const client = options.client ?? context?.client;
|
|
7453
3885
|
if (!client) {
|
|
7454
3886
|
throw new Error("useBookings must be used within CimplifyProvider or passed { client }.");
|
|
@@ -7628,19 +4060,19 @@ function AuthElement({
|
|
|
7628
4060
|
}, [onReady, onAuthenticated, onRequiresOtp, onError]);
|
|
7629
4061
|
React3.useEffect(() => {
|
|
7630
4062
|
if (!elements || !containerRef.current) return;
|
|
7631
|
-
const element = elements.create(ELEMENT_TYPES.AUTH, { prefillEmail });
|
|
4063
|
+
const element = elements.create(chunkREPTMTQ4_js.ELEMENT_TYPES.AUTH, { prefillEmail });
|
|
7632
4064
|
elementRef.current = element;
|
|
7633
|
-
element.on(EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
4065
|
+
element.on(chunkREPTMTQ4_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
7634
4066
|
element.on(
|
|
7635
|
-
EVENT_TYPES.AUTHENTICATED,
|
|
4067
|
+
chunkREPTMTQ4_js.EVENT_TYPES.AUTHENTICATED,
|
|
7636
4068
|
(data) => onAuthenticatedRef.current?.(data)
|
|
7637
4069
|
);
|
|
7638
4070
|
element.on(
|
|
7639
|
-
EVENT_TYPES.REQUIRES_OTP,
|
|
4071
|
+
chunkREPTMTQ4_js.EVENT_TYPES.REQUIRES_OTP,
|
|
7640
4072
|
(data) => onRequiresOtpRef.current?.(data)
|
|
7641
4073
|
);
|
|
7642
4074
|
element.on(
|
|
7643
|
-
EVENT_TYPES.ERROR,
|
|
4075
|
+
chunkREPTMTQ4_js.EVENT_TYPES.ERROR,
|
|
7644
4076
|
(data) => onErrorRef.current?.(data)
|
|
7645
4077
|
);
|
|
7646
4078
|
element.mount(containerRef.current);
|
|
@@ -7669,15 +4101,15 @@ function AddressElement({
|
|
|
7669
4101
|
}, [onReady, onChange, onError]);
|
|
7670
4102
|
React3.useEffect(() => {
|
|
7671
4103
|
if (!elements || !containerRef.current) return;
|
|
7672
|
-
const element = elements.create(ELEMENT_TYPES.ADDRESS, { mode });
|
|
4104
|
+
const element = elements.create(chunkREPTMTQ4_js.ELEMENT_TYPES.ADDRESS, { mode });
|
|
7673
4105
|
elementRef.current = element;
|
|
7674
|
-
element.on(EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
4106
|
+
element.on(chunkREPTMTQ4_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
7675
4107
|
element.on(
|
|
7676
|
-
EVENT_TYPES.CHANGE,
|
|
4108
|
+
chunkREPTMTQ4_js.EVENT_TYPES.CHANGE,
|
|
7677
4109
|
(data) => onChangeRef.current?.(data)
|
|
7678
4110
|
);
|
|
7679
4111
|
element.on(
|
|
7680
|
-
EVENT_TYPES.ERROR,
|
|
4112
|
+
chunkREPTMTQ4_js.EVENT_TYPES.ERROR,
|
|
7681
4113
|
(data) => onErrorRef.current?.(data)
|
|
7682
4114
|
);
|
|
7683
4115
|
element.mount(containerRef.current);
|
|
@@ -7707,15 +4139,15 @@ function PaymentElement({
|
|
|
7707
4139
|
}, [onReady, onChange, onError]);
|
|
7708
4140
|
React3.useEffect(() => {
|
|
7709
4141
|
if (!elements || !containerRef.current) return;
|
|
7710
|
-
const element = elements.create(ELEMENT_TYPES.PAYMENT, { amount, currency });
|
|
4142
|
+
const element = elements.create(chunkREPTMTQ4_js.ELEMENT_TYPES.PAYMENT, { amount, currency });
|
|
7711
4143
|
elementRef.current = element;
|
|
7712
|
-
element.on(EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
4144
|
+
element.on(chunkREPTMTQ4_js.EVENT_TYPES.READY, () => onReadyRef.current?.());
|
|
7713
4145
|
element.on(
|
|
7714
|
-
EVENT_TYPES.CHANGE,
|
|
4146
|
+
chunkREPTMTQ4_js.EVENT_TYPES.CHANGE,
|
|
7715
4147
|
(data) => onChangeRef.current?.(data)
|
|
7716
4148
|
);
|
|
7717
4149
|
element.on(
|
|
7718
|
-
EVENT_TYPES.ERROR,
|
|
4150
|
+
chunkREPTMTQ4_js.EVENT_TYPES.ERROR,
|
|
7719
4151
|
(data) => onErrorRef.current?.(data)
|
|
7720
4152
|
);
|
|
7721
4153
|
element.mount(containerRef.current);
|
|
@@ -7827,43 +4259,6 @@ function QuantitySelector({
|
|
|
7827
4259
|
}
|
|
7828
4260
|
);
|
|
7829
4261
|
}
|
|
7830
|
-
|
|
7831
|
-
// src/utils/variant.ts
|
|
7832
|
-
function stripProductPrefix(variantName, productName) {
|
|
7833
|
-
if (!variantName || !productName) return variantName || productName;
|
|
7834
|
-
const lower = variantName.toLowerCase().trim();
|
|
7835
|
-
if (lower === "default" || lower === "standard") return productName;
|
|
7836
|
-
if (lower.endsWith("-default") || lower.endsWith("-standard")) return productName;
|
|
7837
|
-
if (variantName.startsWith(productName)) {
|
|
7838
|
-
const rest = variantName.slice(productName.length);
|
|
7839
|
-
const match = rest.match(/^\s*[-–—]\s*(.*)/);
|
|
7840
|
-
if (match && match[1].length > 0) return match[1];
|
|
7841
|
-
}
|
|
7842
|
-
const lowerProduct = productName.toLowerCase().trim();
|
|
7843
|
-
if (lower.startsWith(lowerProduct)) {
|
|
7844
|
-
const rest = variantName.slice(productName.length);
|
|
7845
|
-
const match = rest.match(/^\s*[-–—]\s*(.*)/);
|
|
7846
|
-
if (match && match[1].length > 0) return match[1];
|
|
7847
|
-
}
|
|
7848
|
-
const lastDash = variantName.lastIndexOf("-");
|
|
7849
|
-
if (lastDash > 0 && lastDash < variantName.length - 1) {
|
|
7850
|
-
const prefix = variantName.slice(0, lastDash).trim();
|
|
7851
|
-
const suffix = variantName.slice(lastDash + 1).trim();
|
|
7852
|
-
if (suffix.length > 0 && prefix.length > suffix.length * 2) {
|
|
7853
|
-
return suffix;
|
|
7854
|
-
}
|
|
7855
|
-
}
|
|
7856
|
-
return variantName;
|
|
7857
|
-
}
|
|
7858
|
-
function getVariantDisplayName(variant, productName) {
|
|
7859
|
-
if (variant.display_attributes?.length) {
|
|
7860
|
-
return variant.display_attributes.map((a) => a.value_name).join(" / ");
|
|
7861
|
-
}
|
|
7862
|
-
if (variant.name) {
|
|
7863
|
-
return productName ? stripProductPrefix(variant.name, productName) : variant.name;
|
|
7864
|
-
}
|
|
7865
|
-
return productName || (variant.is_default ? "Default" : "Option");
|
|
7866
|
-
}
|
|
7867
4262
|
function VariantSelector({
|
|
7868
4263
|
variants,
|
|
7869
4264
|
variantAxes,
|
|
@@ -7911,7 +4306,7 @@ function VariantSelector({
|
|
|
7911
4306
|
if (!variants || variants.length <= 1) {
|
|
7912
4307
|
return null;
|
|
7913
4308
|
}
|
|
7914
|
-
basePrice != null ? parsePrice(basePrice) : 0;
|
|
4309
|
+
basePrice != null ? chunkJ65U5SED_js.parsePrice(basePrice) : 0;
|
|
7915
4310
|
if (variantAxes && variantAxes.length > 0) {
|
|
7916
4311
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-variant-selector": true, className: cn("space-y-5", className, classNames?.root), children: variantAxes.map((axis) => {
|
|
7917
4312
|
const labelId = `${idPrefix}-axis-${axis.id}`;
|
|
@@ -7987,7 +4382,7 @@ function VariantSelector({
|
|
|
7987
4382
|
className: cn("divide-y divide-border", classNames?.list),
|
|
7988
4383
|
children: variants.map((variant) => {
|
|
7989
4384
|
const isSelected = selectedVariantId === variant.id;
|
|
7990
|
-
const adjustment = parsePrice(variant.price_adjustment);
|
|
4385
|
+
const adjustment = chunkJ65U5SED_js.parsePrice(variant.price_adjustment);
|
|
7991
4386
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7992
4387
|
radio.Radio.Root,
|
|
7993
4388
|
{
|
|
@@ -8015,7 +4410,7 @@ function VariantSelector({
|
|
|
8015
4410
|
{
|
|
8016
4411
|
"data-cimplify-variant-name": true,
|
|
8017
4412
|
className: cn("flex-1 min-w-0 text-sm", classNames?.name),
|
|
8018
|
-
children: getVariantDisplayName(variant, productName)
|
|
4413
|
+
children: chunkOITG2SEN_js.getVariantDisplayName(variant, productName)
|
|
8019
4414
|
}
|
|
8020
4415
|
),
|
|
8021
4416
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-variant-pricing": true, className: cn("text-sm text-muted-foreground", classNames?.pricing), children: [
|
|
@@ -8038,13 +4433,14 @@ function AddOnSelector({
|
|
|
8038
4433
|
className,
|
|
8039
4434
|
classNames
|
|
8040
4435
|
}) {
|
|
4436
|
+
const selectedSet = React3.useMemo(() => new Set(selectedOptions), [selectedOptions]);
|
|
8041
4437
|
const isOptionSelected = React3.useCallback(
|
|
8042
|
-
(optionId) =>
|
|
8043
|
-
[
|
|
4438
|
+
(optionId) => selectedSet.has(optionId),
|
|
4439
|
+
[selectedSet]
|
|
8044
4440
|
);
|
|
8045
4441
|
const handleCheckedChange = React3.useCallback(
|
|
8046
4442
|
(addOn, optionId, checked) => {
|
|
8047
|
-
const isSelected =
|
|
4443
|
+
const isSelected = selectedSet.has(optionId);
|
|
8048
4444
|
if (addOn.is_mutually_exclusive || !addOn.is_multiple_allowed) {
|
|
8049
4445
|
const groupOptionIds = new Set(addOn.options.map((o) => o.id));
|
|
8050
4446
|
const withoutGroup = selectedOptions.filter((id) => !groupOptionIds.has(id));
|
|
@@ -8059,9 +4455,12 @@ function AddOnSelector({
|
|
|
8059
4455
|
if (isSelected) {
|
|
8060
4456
|
onOptionsChange(selectedOptions.filter((id) => id !== optionId));
|
|
8061
4457
|
} else {
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
4458
|
+
let currentCount = 0;
|
|
4459
|
+
for (const option of addOn.options) {
|
|
4460
|
+
if (selectedSet.has(option.id)) {
|
|
4461
|
+
currentCount += 1;
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
8065
4464
|
if (addOn.max_selections && currentCount >= addOn.max_selections) {
|
|
8066
4465
|
return;
|
|
8067
4466
|
}
|
|
@@ -8069,15 +4468,18 @@ function AddOnSelector({
|
|
|
8069
4468
|
}
|
|
8070
4469
|
}
|
|
8071
4470
|
},
|
|
8072
|
-
[selectedOptions, onOptionsChange]
|
|
4471
|
+
[selectedOptions, selectedSet, onOptionsChange]
|
|
8073
4472
|
);
|
|
8074
4473
|
if (!addOns || addOns.length === 0) {
|
|
8075
4474
|
return null;
|
|
8076
4475
|
}
|
|
8077
4476
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-addon-selector": true, className: cn("space-y-6", className, classNames?.root), children: addOns.map((addOn) => {
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
4477
|
+
let currentSelections = 0;
|
|
4478
|
+
for (const option of addOn.options) {
|
|
4479
|
+
if (selectedSet.has(option.id)) {
|
|
4480
|
+
currentSelections += 1;
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
8081
4483
|
const minMet = !addOn.min_selections || currentSelections >= addOn.min_selections;
|
|
8082
4484
|
const isSingleSelect = addOn.is_mutually_exclusive || !addOn.is_multiple_allowed;
|
|
8083
4485
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -8246,16 +4648,16 @@ function BundleSelector({
|
|
|
8246
4648
|
}, [components, selections, onReady]);
|
|
8247
4649
|
const totalPrice = React3.useMemo(() => {
|
|
8248
4650
|
if (pricingType === "fixed" && bundlePrice) {
|
|
8249
|
-
return parsePrice(bundlePrice);
|
|
4651
|
+
return chunkJ65U5SED_js.parsePrice(bundlePrice);
|
|
8250
4652
|
}
|
|
8251
4653
|
const componentsTotal = components.reduce((sum, comp) => {
|
|
8252
4654
|
return sum + getComponentPrice(comp, variantChoices[comp.id]) * comp.quantity;
|
|
8253
4655
|
}, 0);
|
|
8254
4656
|
if (pricingType === "percentage_discount" && discountValue) {
|
|
8255
|
-
return componentsTotal * (1 - parsePrice(discountValue) / 100);
|
|
4657
|
+
return componentsTotal * (1 - chunkJ65U5SED_js.parsePrice(discountValue) / 100);
|
|
8256
4658
|
}
|
|
8257
4659
|
if (pricingType === "fixed_discount" && discountValue) {
|
|
8258
|
-
return componentsTotal - parsePrice(discountValue);
|
|
4660
|
+
return componentsTotal - chunkJ65U5SED_js.parsePrice(discountValue);
|
|
8259
4661
|
}
|
|
8260
4662
|
return componentsTotal;
|
|
8261
4663
|
}, [components, variantChoices, pricingType, bundlePrice, discountValue]);
|
|
@@ -8316,15 +4718,15 @@ function BundleSelector({
|
|
|
8316
4718
|
}
|
|
8317
4719
|
function getComponentPrice(component, selectedVariantId) {
|
|
8318
4720
|
if (!selectedVariantId || component.available_variants.length === 0) {
|
|
8319
|
-
return parsePrice(component.effective_price);
|
|
4721
|
+
return chunkJ65U5SED_js.parsePrice(component.effective_price);
|
|
8320
4722
|
}
|
|
8321
4723
|
if (selectedVariantId === component.variant_id) {
|
|
8322
|
-
return parsePrice(component.effective_price);
|
|
4724
|
+
return chunkJ65U5SED_js.parsePrice(component.effective_price);
|
|
8323
4725
|
}
|
|
8324
4726
|
const bakedAdj = component.variant_id ? component.available_variants.find((v) => v.id === component.variant_id) : void 0;
|
|
8325
4727
|
const selectedAdj = component.available_variants.find((v) => v.id === selectedVariantId);
|
|
8326
|
-
if (!selectedAdj) return parsePrice(component.effective_price);
|
|
8327
|
-
return parsePrice(component.effective_price) - parsePrice(bakedAdj?.price_adjustment ?? "0") + parsePrice(selectedAdj.price_adjustment);
|
|
4728
|
+
if (!selectedAdj) return chunkJ65U5SED_js.parsePrice(component.effective_price);
|
|
4729
|
+
return chunkJ65U5SED_js.parsePrice(component.effective_price) - chunkJ65U5SED_js.parsePrice(bakedAdj?.price_adjustment ?? "0") + chunkJ65U5SED_js.parsePrice(selectedAdj.price_adjustment);
|
|
8328
4730
|
}
|
|
8329
4731
|
function BundleComponentCard({
|
|
8330
4732
|
component,
|
|
@@ -8389,7 +4791,7 @@ function BundleComponentCard({
|
|
|
8389
4791
|
className: cn("mt-3 divide-y divide-border", classNames?.variantPicker),
|
|
8390
4792
|
children: component.available_variants.map((variant) => {
|
|
8391
4793
|
const isSelected = selectedVariantId === variant.id;
|
|
8392
|
-
const adjustment = parsePrice(variant.price_adjustment);
|
|
4794
|
+
const adjustment = chunkJ65U5SED_js.parsePrice(variant.price_adjustment);
|
|
8393
4795
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8394
4796
|
radio.Radio.Root,
|
|
8395
4797
|
{
|
|
@@ -8444,7 +4846,7 @@ function CompositeSelector({
|
|
|
8444
4846
|
className,
|
|
8445
4847
|
classNames
|
|
8446
4848
|
}) {
|
|
8447
|
-
const { client } =
|
|
4849
|
+
const { client } = useCimplifyClient();
|
|
8448
4850
|
const [groupSelections, setGroupSelections] = React3.useState({});
|
|
8449
4851
|
const [priceResult, setPriceResult] = React3.useState(null);
|
|
8450
4852
|
const [isPriceLoading, setIsPriceLoading] = React3.useState(false);
|
|
@@ -8474,6 +4876,13 @@ function CompositeSelector({
|
|
|
8474
4876
|
}
|
|
8475
4877
|
return true;
|
|
8476
4878
|
}, [groups, groupSelections]);
|
|
4879
|
+
const sortedGroups = React3.useMemo(
|
|
4880
|
+
() => [...groups].sort((a, b) => a.display_order - b.display_order).map((group) => ({
|
|
4881
|
+
...group,
|
|
4882
|
+
_sortedComponents: group.components.filter((component) => component.is_available && !component.is_archived).sort((a, b) => a.display_order - b.display_order)
|
|
4883
|
+
})),
|
|
4884
|
+
[groups]
|
|
4885
|
+
);
|
|
8477
4886
|
React3.useEffect(() => {
|
|
8478
4887
|
onReady?.(allGroupsSatisfied);
|
|
8479
4888
|
}, [allGroupsSatisfied, onReady]);
|
|
@@ -8564,7 +4973,7 @@ function CompositeSelector({
|
|
|
8564
4973
|
return null;
|
|
8565
4974
|
}
|
|
8566
4975
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-composite-selector": true, className: cn("space-y-6", className, classNames?.root), children: [
|
|
8567
|
-
|
|
4976
|
+
sortedGroups.map((group) => {
|
|
8568
4977
|
const groupSels = groupSelections[group.id] || {};
|
|
8569
4978
|
const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
|
|
8570
4979
|
const minMet = totalSelected >= group.min_selections;
|
|
@@ -8629,7 +5038,7 @@ function CompositeSelector({
|
|
|
8629
5038
|
role: isSingleSelect ? "radiogroup" : "group",
|
|
8630
5039
|
"aria-label": group.name,
|
|
8631
5040
|
className: cn("divide-y divide-border", classNames?.components),
|
|
8632
|
-
children: group.
|
|
5041
|
+
children: group._sortedComponents.map((component) => {
|
|
8633
5042
|
const qty = groupSels[component.id] || 0;
|
|
8634
5043
|
const isSelected = qty > 0;
|
|
8635
5044
|
const displayName = component.display_name || component.id;
|
|
@@ -8795,7 +5204,7 @@ function CompositeSelector({
|
|
|
8795
5204
|
"data-cimplify-composite-summary": true,
|
|
8796
5205
|
className: cn("border-t border-border pt-4 space-y-1 text-sm", classNames?.summary),
|
|
8797
5206
|
children: [
|
|
8798
|
-
parsePrice(priceResult.base_price) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5207
|
+
chunkJ65U5SED_js.parsePrice(priceResult.base_price) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8799
5208
|
"div",
|
|
8800
5209
|
{
|
|
8801
5210
|
"data-cimplify-composite-summary-line": true,
|
|
@@ -8806,7 +5215,7 @@ function CompositeSelector({
|
|
|
8806
5215
|
]
|
|
8807
5216
|
}
|
|
8808
5217
|
),
|
|
8809
|
-
parsePrice(priceResult.components_total) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5218
|
+
chunkJ65U5SED_js.parsePrice(priceResult.components_total) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8810
5219
|
"div",
|
|
8811
5220
|
{
|
|
8812
5221
|
"data-cimplify-composite-summary-line": true,
|
|
@@ -8906,18 +5315,18 @@ function ProductCustomizer({
|
|
|
8906
5315
|
}, [selectedAddOnOptionIds]);
|
|
8907
5316
|
const localTotalPrice = React3.useMemo(() => {
|
|
8908
5317
|
if (isComposite && compositePrice) {
|
|
8909
|
-
return parsePrice(compositePrice.final_price) * quantity;
|
|
5318
|
+
return chunkJ65U5SED_js.parsePrice(compositePrice.final_price) * quantity;
|
|
8910
5319
|
}
|
|
8911
5320
|
if (isBundle && bundleTotalPrice != null) {
|
|
8912
5321
|
return bundleTotalPrice * quantity;
|
|
8913
5322
|
}
|
|
8914
|
-
let price = parsePrice(product.default_price);
|
|
5323
|
+
let price = chunkJ65U5SED_js.parsePrice(product.default_price);
|
|
8915
5324
|
if (selectedVariant?.price_adjustment) {
|
|
8916
|
-
price += parsePrice(selectedVariant.price_adjustment);
|
|
5325
|
+
price += chunkJ65U5SED_js.parsePrice(selectedVariant.price_adjustment);
|
|
8917
5326
|
}
|
|
8918
5327
|
for (const option of selectedAddOnOptions) {
|
|
8919
5328
|
if (option.default_price) {
|
|
8920
|
-
price += parsePrice(option.default_price);
|
|
5329
|
+
price += chunkJ65U5SED_js.parsePrice(option.default_price);
|
|
8921
5330
|
}
|
|
8922
5331
|
}
|
|
8923
5332
|
return price * quantity;
|
|
@@ -8958,7 +5367,7 @@ function ProductCustomizer({
|
|
|
8958
5367
|
const priceInfo = quote.quoted_total_price_info ?? quote.final_price_info;
|
|
8959
5368
|
const perUnit = priceInfo.pre_tax_price;
|
|
8960
5369
|
if (perUnit === void 0 || perUnit === null) return void 0;
|
|
8961
|
-
return parsePrice(perUnit) * quantity;
|
|
5370
|
+
return chunkJ65U5SED_js.parsePrice(perUnit) * quantity;
|
|
8962
5371
|
}, [quote, quantity]);
|
|
8963
5372
|
const displayTotalPrice = quotedTotalPrice ?? localTotalPrice;
|
|
8964
5373
|
const handleVariantChange = React3.useCallback(
|
|
@@ -9190,7 +5599,7 @@ function CartLineItemRow({
|
|
|
9190
5599
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-item": true, children: [
|
|
9191
5600
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-cart-item-info": true, children: [
|
|
9192
5601
|
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-item-name": true, children: item.product.name }),
|
|
9193
|
-
item.variant && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-item-variant": true, children: getVariantDisplayName(item.variant, item.product.name) }),
|
|
5602
|
+
item.variant && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-cart-item-variant": true, children: chunkOITG2SEN_js.getVariantDisplayName(item.variant, item.product.name) }),
|
|
9194
5603
|
item.addOnOptions && item.addOnOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-cart-item-addons": true, children: [
|
|
9195
5604
|
"+ ",
|
|
9196
5605
|
item.addOnOptions.map((opt) => opt.name).join(", ")
|
|
@@ -9331,16 +5740,16 @@ function SaleBadge({
|
|
|
9331
5740
|
className,
|
|
9332
5741
|
classNames
|
|
9333
5742
|
}) {
|
|
9334
|
-
const onSale = isOnSale(product);
|
|
5743
|
+
const onSale = chunkJ65U5SED_js.isOnSale(product);
|
|
9335
5744
|
const hasDeal = dealInfo !== void 0;
|
|
9336
5745
|
if (!hasDeal && !onSale && !label) {
|
|
9337
5746
|
return null;
|
|
9338
5747
|
}
|
|
9339
5748
|
let percentage = null;
|
|
9340
5749
|
if (hasDeal && dealInfo.benefit_type === "percentage") {
|
|
9341
|
-
percentage = parsePrice(dealInfo.value);
|
|
5750
|
+
percentage = chunkJ65U5SED_js.parsePrice(dealInfo.value);
|
|
9342
5751
|
} else if (onSale) {
|
|
9343
|
-
percentage = getDiscountPercentage(product);
|
|
5752
|
+
percentage = chunkJ65U5SED_js.getDiscountPercentage(product);
|
|
9344
5753
|
}
|
|
9345
5754
|
const badgeText = label ?? dealInfo?.label ?? (percentage != null && percentage > 0 ? `${percentage}% off` : null);
|
|
9346
5755
|
if (!badgeText) {
|
|
@@ -9359,7 +5768,7 @@ function SaleBadge({
|
|
|
9359
5768
|
"%"
|
|
9360
5769
|
] }),
|
|
9361
5770
|
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-sale-label": true, className: classNames?.label, children: badgeText }),
|
|
9362
|
-
showOriginalPrice && onSale && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-sale-original-price": true, className: classNames?.originalPrice, children: /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: getBasePrice(product) }) })
|
|
5771
|
+
showOriginalPrice && onSale && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-sale-original-price": true, className: classNames?.originalPrice, children: /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: chunkJ65U5SED_js.getBasePrice(product) }) })
|
|
9363
5772
|
]
|
|
9364
5773
|
}
|
|
9365
5774
|
);
|
|
@@ -9713,6 +6122,15 @@ function ProductGrid({
|
|
|
9713
6122
|
const md = columns?.md ?? 2;
|
|
9714
6123
|
const lg = columns?.lg ?? 3;
|
|
9715
6124
|
const xl = columns?.xl ?? 4;
|
|
6125
|
+
const css = React3__default.default.useMemo(
|
|
6126
|
+
() => [
|
|
6127
|
+
`#${gridId}{display:grid;grid-template-columns:repeat(${sm},1fr);gap:1rem}`,
|
|
6128
|
+
`@media(min-width:768px){#${gridId}{grid-template-columns:repeat(${md},1fr)}}`,
|
|
6129
|
+
`@media(min-width:1024px){#${gridId}{grid-template-columns:repeat(${lg},1fr)}}`,
|
|
6130
|
+
`@media(min-width:1280px){#${gridId}{grid-template-columns:repeat(${xl},1fr)}}`
|
|
6131
|
+
].join(""),
|
|
6132
|
+
[gridId, sm, md, lg, xl]
|
|
6133
|
+
);
|
|
9716
6134
|
if (products.length === 0) {
|
|
9717
6135
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9718
6136
|
"div",
|
|
@@ -9724,12 +6142,6 @@ function ProductGrid({
|
|
|
9724
6142
|
}
|
|
9725
6143
|
);
|
|
9726
6144
|
}
|
|
9727
|
-
const css = [
|
|
9728
|
-
`#${gridId}{display:grid;grid-template-columns:repeat(${sm},1fr);gap:1rem}`,
|
|
9729
|
-
`@media(min-width:768px){#${gridId}{grid-template-columns:repeat(${md},1fr)}}`,
|
|
9730
|
-
`@media(min-width:1024px){#${gridId}{grid-template-columns:repeat(${lg},1fr)}}`,
|
|
9731
|
-
`@media(min-width:1280px){#${gridId}{grid-template-columns:repeat(${xl},1fr)}}`
|
|
9732
|
-
].join("");
|
|
9733
6145
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9734
6146
|
/* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: css } }),
|
|
9735
6147
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -10255,15 +6667,15 @@ function OrderSummary({
|
|
|
10255
6667
|
] }, item.id)
|
|
10256
6668
|
) }),
|
|
10257
6669
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-order-totals": true, className: classNames?.totals, children: [
|
|
10258
|
-
order.total_discount != null && parsePrice(order.total_discount) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-order-discount": true, children: [
|
|
6670
|
+
order.total_discount != null && chunkJ65U5SED_js.parsePrice(order.total_discount) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-order-discount": true, children: [
|
|
10259
6671
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Discount" }),
|
|
10260
6672
|
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: order.total_discount, prefix: "-" })
|
|
10261
6673
|
] }),
|
|
10262
|
-
order.service_charge != null && parsePrice(order.service_charge) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-order-service-charge": true, children: [
|
|
6674
|
+
order.service_charge != null && chunkJ65U5SED_js.parsePrice(order.service_charge) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-order-service-charge": true, children: [
|
|
10263
6675
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Service charge" }),
|
|
10264
6676
|
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: order.service_charge })
|
|
10265
6677
|
] }),
|
|
10266
|
-
order.tax != null && parsePrice(order.tax) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-order-tax": true, children: [
|
|
6678
|
+
order.tax != null && chunkJ65U5SED_js.parsePrice(order.tax) !== 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-order-tax": true, children: [
|
|
10267
6679
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Tax" }),
|
|
10268
6680
|
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: order.tax })
|
|
10269
6681
|
] }),
|
|
@@ -11334,7 +7746,7 @@ function CheckoutPage({
|
|
|
11334
7746
|
className,
|
|
11335
7747
|
classNames
|
|
11336
7748
|
}) {
|
|
11337
|
-
const { client } =
|
|
7749
|
+
const { client } = useCimplifyClient();
|
|
11338
7750
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-checkout-page": true, className: cn(className, classNames?.root), children: [
|
|
11339
7751
|
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-checkout-header": true, className: classNames?.header, children: /* @__PURE__ */ jsxRuntime.jsx("h1", { "data-cimplify-checkout-title": true, className: classNames?.title, children: title }) }),
|
|
11340
7752
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -11708,8 +8120,8 @@ function BookingPage({
|
|
|
11708
8120
|
staffId: selectedStaffId || void 0
|
|
11709
8121
|
});
|
|
11710
8122
|
onBooked?.(selectedSlot, selectedStaffId);
|
|
11711
|
-
} catch (
|
|
11712
|
-
setError(
|
|
8123
|
+
} catch (err) {
|
|
8124
|
+
setError(err instanceof Error ? err.message : "Failed to add booking to cart");
|
|
11713
8125
|
} finally {
|
|
11714
8126
|
setIsSubmitting(false);
|
|
11715
8127
|
}
|
|
@@ -11906,6 +8318,10 @@ function BookingsPage({
|
|
|
11906
8318
|
] });
|
|
11907
8319
|
}
|
|
11908
8320
|
|
|
8321
|
+
Object.defineProperty(exports, "getVariantDisplayName", {
|
|
8322
|
+
enumerable: true,
|
|
8323
|
+
get: function () { return chunkOITG2SEN_js.getVariantDisplayName; }
|
|
8324
|
+
});
|
|
11909
8325
|
exports.Ad = Ad;
|
|
11910
8326
|
exports.AdProvider = AdProvider;
|
|
11911
8327
|
exports.AddOnSelector = AddOnSelector;
|
|
@@ -11955,7 +8371,6 @@ exports.StaffPicker = StaffPicker;
|
|
|
11955
8371
|
exports.StoreNav = StoreNav;
|
|
11956
8372
|
exports.VariantSelector = VariantSelector;
|
|
11957
8373
|
exports.cn = cn;
|
|
11958
|
-
exports.getVariantDisplayName = getVariantDisplayName;
|
|
11959
8374
|
exports.useAds = useAds;
|
|
11960
8375
|
exports.useAvailableSlots = useAvailableSlots;
|
|
11961
8376
|
exports.useBookings = useBookings;
|
|
@@ -11964,6 +8379,7 @@ exports.useCart = useCart;
|
|
|
11964
8379
|
exports.useCategories = useCategories;
|
|
11965
8380
|
exports.useCheckout = useCheckout;
|
|
11966
8381
|
exports.useCimplify = useCimplify;
|
|
8382
|
+
exports.useCimplifyClient = useCimplifyClient;
|
|
11967
8383
|
exports.useCollection = useCollection;
|
|
11968
8384
|
exports.useCollections = useCollections;
|
|
11969
8385
|
exports.useDeals = useDeals;
|