@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.
Files changed (37) hide show
  1. package/dist/advanced.d.mts +2 -2
  2. package/dist/advanced.d.ts +2 -2
  3. package/dist/advanced.js +90 -2906
  4. package/dist/advanced.mjs +3 -2886
  5. package/dist/chunk-FLPI2VVJ.js +72 -0
  6. package/dist/chunk-G6KNFYJD.mjs +687 -0
  7. package/dist/chunk-GFKBH7R2.mjs +2759 -0
  8. package/dist/chunk-J65U5SED.js +714 -0
  9. package/dist/chunk-LM3CJ4L7.mjs +69 -0
  10. package/dist/chunk-OITG2SEN.js +692 -0
  11. package/dist/chunk-OVM5YWM5.mjs +674 -0
  12. package/dist/chunk-REPTMTQ4.js +2810 -0
  13. package/dist/chunk-S6TU55KP.js +170 -0
  14. package/dist/chunk-WDIMISRC.mjs +166 -0
  15. package/dist/{client-DTTtSSmQ.d.ts → client-BaolRdB4.d.mts} +3 -3
  16. package/dist/{client-Cb7r8u6t.d.mts → client-C59R79gx.d.ts} +3 -3
  17. package/dist/index.d.mts +4 -4
  18. package/dist/index.d.ts +4 -4
  19. package/dist/index.js +401 -4412
  20. package/dist/index.mjs +5 -4317
  21. package/dist/{payment-Dvd7nRmk.d.mts → payment-C6ULw3K-.d.mts} +1 -1
  22. package/dist/{payment-Dvd7nRmk.d.ts → payment-C6ULw3K-.d.ts} +1 -1
  23. package/dist/{price-DrfhwHpw.d.ts → price-Bd6JBV8W.d.ts} +1 -1
  24. package/dist/{price-BDZNXiLS.d.mts → price-CbR6DCt0.d.mts} +1 -1
  25. package/dist/react.d.mts +12 -7
  26. package/dist/react.d.ts +12 -7
  27. package/dist/react.js +377 -3961
  28. package/dist/react.mjs +326 -3913
  29. package/dist/utils.d.mts +2 -2
  30. package/dist/utils.d.ts +2 -2
  31. package/dist/utils.js +122 -720
  32. package/dist/utils.mjs +2 -694
  33. package/package.json +1 -1
  34. package/registry/add-on-selector.json +1 -1
  35. package/registry/checkout-page.json +1 -1
  36. package/registry/composite-selector.json +1 -1
  37. package/registry/product-grid.json +1 -1
@@ -0,0 +1,2759 @@
1
+ import { CimplifyError, currencyCode, ErrorCode, isPaymentStatusRequiresAction, normalizeStatusResponse, isPaymentStatusSuccess, isPaymentStatusFailure, getErrorHint, enrichError } from './chunk-OVM5YWM5.mjs';
2
+
3
+ // src/types/result.ts
4
+ function ok(value) {
5
+ return { ok: true, value };
6
+ }
7
+ function err(error) {
8
+ return { ok: false, error };
9
+ }
10
+ function isOk(result) {
11
+ return result.ok === true;
12
+ }
13
+ function isErr(result) {
14
+ return result.ok === false;
15
+ }
16
+ function mapResult(result, fn) {
17
+ return result.ok ? ok(fn(result.value)) : result;
18
+ }
19
+ function mapError(result, fn) {
20
+ return result.ok ? result : err(fn(result.error));
21
+ }
22
+ function flatMap(result, fn) {
23
+ return result.ok ? fn(result.value) : result;
24
+ }
25
+ function getOrElse(result, defaultFn) {
26
+ return result.ok ? result.value : defaultFn();
27
+ }
28
+ function unwrap(result) {
29
+ if (result.ok) {
30
+ return result.value;
31
+ }
32
+ throw result.error;
33
+ }
34
+ function toNullable(result) {
35
+ return result.ok ? result.value : void 0;
36
+ }
37
+ async function fromPromise(promise, mapError2) {
38
+ try {
39
+ const value = await promise;
40
+ return ok(value);
41
+ } catch (error) {
42
+ return err(mapError2(error));
43
+ }
44
+ }
45
+ function tryCatch(fn, mapError2) {
46
+ try {
47
+ return ok(fn());
48
+ } catch (error) {
49
+ return err(mapError2(error));
50
+ }
51
+ }
52
+ function combine(results) {
53
+ const values = [];
54
+ for (const result of results) {
55
+ if (!result.ok) {
56
+ return result;
57
+ }
58
+ values.push(result.value);
59
+ }
60
+ return ok(values);
61
+ }
62
+ function combineObject(results) {
63
+ const values = {};
64
+ for (const [key, result] of Object.entries(results)) {
65
+ if (!result.ok) {
66
+ return result;
67
+ }
68
+ values[key] = result.value;
69
+ }
70
+ return ok(values);
71
+ }
72
+
73
+ // src/internal/safe.ts
74
+ function toCimplifyError(error) {
75
+ if (error instanceof CimplifyError) return enrichError(error);
76
+ if (error instanceof Error) {
77
+ return enrichError(new CimplifyError(ErrorCode.UNKNOWN_ERROR, error.message, false));
78
+ }
79
+ return enrichError(new CimplifyError(ErrorCode.UNKNOWN_ERROR, String(error), false));
80
+ }
81
+ async function safe(promise) {
82
+ try {
83
+ return ok(await promise);
84
+ } catch (error) {
85
+ return err(toCimplifyError(error));
86
+ }
87
+ }
88
+
89
+ // src/catalogue.ts
90
+ function withQuery(path, params) {
91
+ const query = new URLSearchParams();
92
+ for (const [key, value] of Object.entries(params)) {
93
+ if (value === void 0) continue;
94
+ query.set(key, String(value));
95
+ }
96
+ const queryString = query.toString();
97
+ return queryString ? `${path}?${queryString}` : path;
98
+ }
99
+ function isRecord(value) {
100
+ return typeof value === "object" && value !== null;
101
+ }
102
+ function readFinalPrice(value) {
103
+ if (!isRecord(value)) return void 0;
104
+ const finalPrice = value.final_price;
105
+ if (typeof finalPrice === "string" || typeof finalPrice === "number") {
106
+ return finalPrice;
107
+ }
108
+ return void 0;
109
+ }
110
+ function needsDefaultPriceNormalization(value) {
111
+ return value === void 0 || value === null || value === "";
112
+ }
113
+ function optionNeedsNormalization(option) {
114
+ return isRecord(option) && needsDefaultPriceNormalization(option["default_price"]);
115
+ }
116
+ function addOnNeedsNormalization(addOn) {
117
+ if (!isRecord(addOn)) return false;
118
+ const options = addOn["options"];
119
+ return Array.isArray(options) && options.some((option) => optionNeedsNormalization(option));
120
+ }
121
+ function normalizeAddOnPayload(addOn) {
122
+ if (!isRecord(addOn)) return addOn;
123
+ const options = addOn["options"];
124
+ if (!Array.isArray(options)) return addOn;
125
+ if (!options.some((option) => optionNeedsNormalization(option))) return addOn;
126
+ const normalizedAddOn = { ...addOn };
127
+ normalizedAddOn["options"] = options.map((option) => {
128
+ if (!isRecord(option)) return option;
129
+ if (!optionNeedsNormalization(option)) return option;
130
+ const normalizedOption = { ...option };
131
+ const optionPrice = normalizedOption["default_price"];
132
+ if (needsDefaultPriceNormalization(optionPrice)) {
133
+ normalizedOption["default_price"] = readFinalPrice(normalizedOption["default_price_info"]) || readFinalPrice(normalizedOption["price_info"]) || "0";
134
+ }
135
+ return normalizedOption;
136
+ });
137
+ return normalizedAddOn;
138
+ }
139
+ function normalizeCatalogueProductPayload(product) {
140
+ const productRecord = product;
141
+ const defaultPrice = productRecord["default_price"];
142
+ const addOns = productRecord["add_ons"];
143
+ const needsPriceFix = needsDefaultPriceNormalization(defaultPrice);
144
+ const needsAddOnFix = Array.isArray(addOns) && addOns.some((addOn) => addOnNeedsNormalization(addOn));
145
+ if (!needsPriceFix && !needsAddOnFix) {
146
+ return product;
147
+ }
148
+ const normalized = { ...productRecord };
149
+ if (needsPriceFix) {
150
+ 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);
151
+ normalized["default_price"] = derivedDefaultPrice ?? "0";
152
+ }
153
+ if (needsAddOnFix && Array.isArray(addOns)) {
154
+ normalized["add_ons"] = addOns.map((addOn) => normalizeAddOnPayload(addOn));
155
+ }
156
+ return normalized;
157
+ }
158
+ function toFiniteNumber(value) {
159
+ if (typeof value === "number" && Number.isFinite(value)) {
160
+ return value;
161
+ }
162
+ if (typeof value === "string" && value.trim().length > 0) {
163
+ const parsed = Number(value);
164
+ if (Number.isFinite(parsed)) {
165
+ return parsed;
166
+ }
167
+ }
168
+ return void 0;
169
+ }
170
+ function normalizePagination(value) {
171
+ if (!isRecord(value)) {
172
+ return void 0;
173
+ }
174
+ const totalCount = toFiniteNumber(value.total_count);
175
+ const currentPage = toFiniteNumber(value.current_page);
176
+ const pageSize = toFiniteNumber(value.page_size);
177
+ const totalPages = toFiniteNumber(value.total_pages);
178
+ if (totalCount === void 0 || currentPage === void 0 || pageSize === void 0 || totalPages === void 0) {
179
+ return void 0;
180
+ }
181
+ return {
182
+ total_count: totalCount,
183
+ current_page: currentPage,
184
+ page_size: pageSize,
185
+ total_pages: totalPages,
186
+ has_more: value.has_more === true,
187
+ next_cursor: typeof value.next_cursor === "string" ? value.next_cursor : void 0
188
+ };
189
+ }
190
+ function normalizeCatalogueResult(payload) {
191
+ if (Array.isArray(payload)) {
192
+ return {
193
+ items: payload.map((product) => normalizeCatalogueProductPayload(product)),
194
+ is_complete: true
195
+ };
196
+ }
197
+ if (!isRecord(payload)) {
198
+ return {
199
+ items: [],
200
+ is_complete: true
201
+ };
202
+ }
203
+ const rawItems = Array.isArray(payload.products) ? payload.products : Array.isArray(payload.items) ? payload.items : [];
204
+ return {
205
+ items: rawItems.map((product) => normalizeCatalogueProductPayload(product)),
206
+ is_complete: typeof payload.is_complete === "boolean" ? payload.is_complete : true,
207
+ total_available: toFiniteNumber(payload.total_available),
208
+ pagination: normalizePagination(payload.pagination)
209
+ };
210
+ }
211
+ function normalizeCatalogueSnapshot(payload) {
212
+ if (Array.isArray(payload)) {
213
+ return {
214
+ categories: [],
215
+ products: payload.map((product) => normalizeCatalogueProductPayload(product)),
216
+ add_ons: [],
217
+ is_complete: true
218
+ };
219
+ }
220
+ if (!isRecord(payload)) {
221
+ return {
222
+ categories: [],
223
+ products: [],
224
+ add_ons: [],
225
+ is_complete: true
226
+ };
227
+ }
228
+ const rawProducts = Array.isArray(payload.products) ? payload.products : Array.isArray(payload.items) ? payload.items : [];
229
+ const rawCategories = Array.isArray(payload.categories) ? payload.categories : [];
230
+ const rawAddOns = Array.isArray(payload.add_ons) ? payload.add_ons : [];
231
+ return {
232
+ categories: rawCategories,
233
+ products: rawProducts.map((product) => normalizeCatalogueProductPayload(product)),
234
+ add_ons: rawAddOns.map((addOn) => normalizeAddOnPayload(addOn)),
235
+ is_complete: typeof payload.is_complete === "boolean" ? payload.is_complete : true,
236
+ total_available: toFiniteNumber(payload.total_available),
237
+ pagination: normalizePagination(payload.pagination)
238
+ };
239
+ }
240
+ var CatalogueQueries = class {
241
+ constructor(client) {
242
+ this.client = client;
243
+ }
244
+ async getCatalogue() {
245
+ const result = await safe(this.client.get("/api/v1/catalogue"));
246
+ if (!result.ok) return result;
247
+ return ok(normalizeCatalogueSnapshot(result.value));
248
+ }
249
+ async getProducts(options) {
250
+ const path = withQuery("/api/v1/catalogue/products", {
251
+ category_id: options?.category,
252
+ taxonomy_id: options?.taxonomy,
253
+ search: options?.search,
254
+ page: options?.page,
255
+ tags: options?.tags?.join(","),
256
+ featured: options?.featured,
257
+ in_stock: options?.in_stock,
258
+ min_price: options?.min_price,
259
+ max_price: options?.max_price,
260
+ sort_by: options?.sort_by,
261
+ sort_order: options?.sort_order,
262
+ limit: options?.limit,
263
+ offset: options?.offset,
264
+ cursor: options?.cursor
265
+ });
266
+ const result = await safe(this.client.get(path));
267
+ if (!result.ok) return result;
268
+ return ok(normalizeCatalogueResult(result.value));
269
+ }
270
+ async getProduct(id) {
271
+ const encodedId = encodeURIComponent(id);
272
+ const result = await safe(this.client.get(`/api/v1/catalogue/products/${encodedId}`));
273
+ if (!result.ok) return result;
274
+ return ok(normalizeCatalogueProductPayload(result.value));
275
+ }
276
+ async getProductBySlug(slug) {
277
+ const encodedSlug = encodeURIComponent(slug);
278
+ const result = await safe(this.client.get(`/api/v1/catalogue/products/slug/${encodedSlug}`));
279
+ if (!result.ok) return result;
280
+ return ok(normalizeCatalogueProductPayload(result.value));
281
+ }
282
+ async getVariants(productId) {
283
+ const encodedId = encodeURIComponent(productId);
284
+ return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/variants`));
285
+ }
286
+ async getVariantAxes(productId) {
287
+ const encodedId = encodeURIComponent(productId);
288
+ return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/variant-axes`));
289
+ }
290
+ async getVariantByAxisSelections(productId, selections) {
291
+ const encodedId = encodeURIComponent(productId);
292
+ return safe(
293
+ this.client.post(
294
+ `/api/v1/catalogue/products/${encodedId}/variants/find`,
295
+ { axis_selections: selections }
296
+ )
297
+ );
298
+ }
299
+ async getVariantById(productId, variantId) {
300
+ const encodedProductId = encodeURIComponent(productId);
301
+ const encodedVariantId = encodeURIComponent(variantId);
302
+ return safe(
303
+ this.client.get(
304
+ `/api/v1/catalogue/products/${encodedProductId}/variants/${encodedVariantId}`
305
+ )
306
+ );
307
+ }
308
+ async getAddOns(productId) {
309
+ const encodedId = encodeURIComponent(productId);
310
+ return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/add-ons`));
311
+ }
312
+ async getCategories() {
313
+ return safe(this.client.get("/api/v1/catalogue/categories"));
314
+ }
315
+ async getCategory(id) {
316
+ const encodedId = encodeURIComponent(id);
317
+ return safe(this.client.get(`/api/v1/catalogue/categories/${encodedId}`));
318
+ }
319
+ async getCategoryBySlug(slug) {
320
+ const encodedSlug = encodeURIComponent(slug);
321
+ return safe(this.client.get(`/api/v1/catalogue/categories/slug/${encodedSlug}`));
322
+ }
323
+ async getCategoryProducts(categoryId) {
324
+ const encodedId = encodeURIComponent(categoryId);
325
+ return safe(this.client.get(`/api/v1/catalogue/categories/${encodedId}/products`));
326
+ }
327
+ async getCollections() {
328
+ return safe(this.client.get("/api/v1/catalogue/collections"));
329
+ }
330
+ async getCollection(id) {
331
+ const encodedId = encodeURIComponent(id);
332
+ return safe(this.client.get(`/api/v1/catalogue/collections/${encodedId}`));
333
+ }
334
+ async getCollectionBySlug(slug) {
335
+ const encodedSlug = encodeURIComponent(slug);
336
+ return safe(this.client.get(`/api/v1/catalogue/collections/slug/${encodedSlug}`));
337
+ }
338
+ async getCollectionProducts(collectionId) {
339
+ const encodedId = encodeURIComponent(collectionId);
340
+ return safe(this.client.get(`/api/v1/catalogue/collections/${encodedId}/products`));
341
+ }
342
+ async searchCollections(query, limit = 20) {
343
+ const path = withQuery("/api/v1/catalogue/collections", { search: query, limit });
344
+ return safe(this.client.get(path));
345
+ }
346
+ async getBundles() {
347
+ return safe(this.client.get("/api/v1/catalogue/bundles"));
348
+ }
349
+ async searchBundles(query, limit = 20) {
350
+ const path = withQuery("/api/v1/catalogue/bundles", { search: query, limit });
351
+ return safe(this.client.get(path));
352
+ }
353
+ async getComposites(options) {
354
+ const path = withQuery("/api/v1/catalogue/composites", { limit: options?.limit });
355
+ return safe(this.client.get(path));
356
+ }
357
+ async calculateCompositePrice(compositeId, selections, locationId) {
358
+ const encodedId = encodeURIComponent(compositeId);
359
+ return safe(
360
+ this.client.post(
361
+ `/api/v1/catalogue/composites/${encodedId}/calculate-price`,
362
+ {
363
+ selections,
364
+ location_id: locationId
365
+ }
366
+ )
367
+ );
368
+ }
369
+ async fetchQuote(input) {
370
+ return safe(this.client.post("/api/v1/catalogue/quotes", input));
371
+ }
372
+ async getQuote(quoteId) {
373
+ const encodedQuoteId = encodeURIComponent(quoteId);
374
+ return safe(this.client.get(`/api/v1/catalogue/quotes/${encodedQuoteId}`));
375
+ }
376
+ async refreshQuote(input) {
377
+ const encodedQuoteId = encodeURIComponent(input.quote_id);
378
+ return safe(
379
+ this.client.post(
380
+ `/api/v1/catalogue/quotes/${encodedQuoteId}/refresh`,
381
+ input
382
+ )
383
+ );
384
+ }
385
+ async getTaxonomies(parentId) {
386
+ const path = withQuery("/api/v1/catalogue/taxonomies", { parent_id: parentId });
387
+ return safe(this.client.get(path));
388
+ }
389
+ async getTaxonomy(id) {
390
+ const encodedId = encodeURIComponent(id);
391
+ return safe(this.client.get(`/api/v1/catalogue/taxonomies/${encodedId}`));
392
+ }
393
+ async getTaxonomyPath(id) {
394
+ const encodedId = encodeURIComponent(id);
395
+ return safe(this.client.get(`/api/v1/catalogue/taxonomies/${encodedId}/path`));
396
+ }
397
+ async searchTaxonomies(query, limit) {
398
+ const path = withQuery("/api/v1/catalogue/taxonomies/search", { q: query, limit });
399
+ return safe(this.client.get(path));
400
+ }
401
+ async getProductSchedules(productId) {
402
+ const encodedId = encodeURIComponent(productId);
403
+ return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/schedules`));
404
+ }
405
+ async checkProductAvailableNow(productId, locationId) {
406
+ const encodedId = encodeURIComponent(productId);
407
+ const path = withQuery(`/api/v1/catalogue/products/${encodedId}/available-now`, { location_id: locationId });
408
+ return safe(this.client.get(path));
409
+ }
410
+ async getDeals(locationId) {
411
+ const path = withQuery("/api/v1/catalogue/deals", { location_id: locationId });
412
+ return safe(this.client.get(path));
413
+ }
414
+ async getProductsOnSale() {
415
+ return safe(this.client.get("/api/v1/catalogue/deals/on-sale"));
416
+ }
417
+ async getProductDeals(productId) {
418
+ const encodedId = encodeURIComponent(productId);
419
+ return safe(this.client.get(`/api/v1/catalogue/products/${encodedId}/deals`));
420
+ }
421
+ async getCategoryDeals(categoryId) {
422
+ const encodedId = encodeURIComponent(categoryId);
423
+ return safe(this.client.get(`/api/v1/catalogue/categories/${encodedId}/deals`));
424
+ }
425
+ async getCollectionDeals(collectionId) {
426
+ const encodedId = encodeURIComponent(collectionId);
427
+ return safe(this.client.get(`/api/v1/catalogue/collections/${encodedId}/deals`));
428
+ }
429
+ async validateDiscountCode(code, orderSubtotal, locationId) {
430
+ return safe(
431
+ this.client.post("/api/v1/catalogue/deals/validate", {
432
+ discount_code: code,
433
+ order_subtotal: orderSubtotal,
434
+ location_id: locationId
435
+ })
436
+ );
437
+ }
438
+ async search(query, options) {
439
+ const result = await this.getProducts({
440
+ search: query,
441
+ category: options?.category,
442
+ limit: options?.limit ?? 20
443
+ });
444
+ if (!result.ok) return result;
445
+ return ok(result.value.items);
446
+ }
447
+ async searchProducts(query, options) {
448
+ return this.search(query, options);
449
+ }
450
+ async getMenu(options) {
451
+ const path = withQuery("/api/v1/catalogue/menu", {
452
+ category_id: options?.category,
453
+ limit: options?.limit
454
+ });
455
+ return safe(this.client.get(path));
456
+ }
457
+ async getMenuCategory(categoryId) {
458
+ const encodedId = encodeURIComponent(categoryId);
459
+ return safe(this.client.get(`/api/v1/catalogue/menu/categories/${encodedId}`));
460
+ }
461
+ async getMenuItem(itemId) {
462
+ const encodedId = encodeURIComponent(itemId);
463
+ return safe(this.client.get(`/api/v1/catalogue/menu/items/${encodedId}`));
464
+ }
465
+ async getTags(options) {
466
+ const path = withQuery("/api/v1/catalogue/tags", {
467
+ tag_group: options?.tag_group,
468
+ search: options?.search,
469
+ min_usage: options?.min_usage,
470
+ limit: options?.limit,
471
+ offset: options?.offset
472
+ });
473
+ return safe(this.client.get(path));
474
+ }
475
+ };
476
+
477
+ // src/cart.ts
478
+ function isUICartResponse(value) {
479
+ return "cart" in value;
480
+ }
481
+ function unwrapEnrichedCart(value) {
482
+ return isUICartResponse(value) ? value.cart : value;
483
+ }
484
+ var CartOperations = class {
485
+ constructor(client) {
486
+ this.client = client;
487
+ }
488
+ async get() {
489
+ const result = await safe(this.client.get("/api/v1/cart"));
490
+ if (!result.ok) return result;
491
+ return ok(unwrapEnrichedCart(result.value));
492
+ }
493
+ async getRaw() {
494
+ return safe(this.client.get("/api/v1/cart"));
495
+ }
496
+ async getItems() {
497
+ return safe(this.client.get("/api/v1/cart/items"));
498
+ }
499
+ async getCount() {
500
+ return safe(this.client.get("/api/v1/cart/count"));
501
+ }
502
+ async getTotal() {
503
+ return safe(this.client.get("/api/v1/cart/total"));
504
+ }
505
+ async getSummary() {
506
+ const cartResult = await this.get();
507
+ if (!cartResult.ok) return cartResult;
508
+ const cart = cartResult.value;
509
+ return ok({
510
+ item_count: cart.items.length,
511
+ total_items: cart.items.reduce((sum, item) => sum + item.quantity, 0),
512
+ subtotal: cart.pricing.subtotal,
513
+ discount_amount: cart.pricing.total_discounts,
514
+ tax_amount: cart.pricing.tax_amount,
515
+ total: cart.pricing.total_price,
516
+ currency: cart.pricing.currency
517
+ });
518
+ }
519
+ async addItem(input) {
520
+ return safe(this.client.post("/api/v1/cart/items", input));
521
+ }
522
+ async updateItem(cartItemId, updates) {
523
+ if (typeof updates.quantity === "number") {
524
+ return this.updateQuantity(cartItemId, updates.quantity);
525
+ }
526
+ const encodedId = encodeURIComponent(cartItemId);
527
+ return safe(this.client.patch(`/api/v1/cart/items/${encodedId}`, updates));
528
+ }
529
+ async updateQuantity(cartItemId, quantity) {
530
+ const encodedId = encodeURIComponent(cartItemId);
531
+ return safe(
532
+ this.client.patch(`/api/v1/cart/items/${encodedId}`, {
533
+ quantity
534
+ })
535
+ );
536
+ }
537
+ async removeItem(cartItemId) {
538
+ const encodedId = encodeURIComponent(cartItemId);
539
+ return safe(this.client.delete(`/api/v1/cart/items/${encodedId}`));
540
+ }
541
+ async clear() {
542
+ return safe(this.client.delete("/api/v1/cart"));
543
+ }
544
+ async applyCoupon(code) {
545
+ return safe(
546
+ this.client.post("/api/v1/cart/coupons", {
547
+ coupon_code: code
548
+ })
549
+ );
550
+ }
551
+ async removeCoupon() {
552
+ return safe(this.client.delete("/api/v1/cart/coupons/current"));
553
+ }
554
+ async isEmpty() {
555
+ const countResult = await this.getCount();
556
+ if (!countResult.ok) return countResult;
557
+ return ok(countResult.value === 0);
558
+ }
559
+ async hasItem(productId, variantId) {
560
+ const itemsResult = await this.getItems();
561
+ if (!itemsResult.ok) return itemsResult;
562
+ const found = itemsResult.value.some((item) => {
563
+ const matchesProduct = item.item_id === productId;
564
+ if (!variantId) return matchesProduct;
565
+ const config = item.configuration;
566
+ if ("variant" in config && config.variant) {
567
+ return matchesProduct && config.variant.variant_id === variantId;
568
+ }
569
+ return matchesProduct;
570
+ });
571
+ return ok(found);
572
+ }
573
+ async findItem(productId, variantId) {
574
+ const itemsResult = await this.getItems();
575
+ if (!itemsResult.ok) return itemsResult;
576
+ const found = itemsResult.value.find((item) => {
577
+ const matchesProduct = item.item_id === productId;
578
+ if (!variantId) return matchesProduct;
579
+ const config = item.configuration;
580
+ if ("variant" in config && config.variant) {
581
+ return matchesProduct && config.variant.variant_id === variantId;
582
+ }
583
+ return matchesProduct;
584
+ });
585
+ return ok(found);
586
+ }
587
+ };
588
+
589
+ // src/constants.ts
590
+ var CHECKOUT_MODE = {
591
+ LINK: "link",
592
+ GUEST: "guest"
593
+ };
594
+ var ORDER_TYPE = {
595
+ DELIVERY: "delivery",
596
+ PICKUP: "pickup",
597
+ DINE_IN: "dine-in",
598
+ WALK_IN: "walk-in"
599
+ };
600
+ var PAYMENT_METHOD = {
601
+ MOBILE_MONEY: "mobile_money",
602
+ CARD: "card"
603
+ };
604
+ var CHECKOUT_STEP = {
605
+ AUTHENTICATION: "authentication",
606
+ ORDER_DETAILS: "order_details",
607
+ PAYMENT_METHOD: "payment_method",
608
+ PAYMENT: "payment",
609
+ CONFIRMATION: "confirmation"
610
+ };
611
+ var PAYMENT_STATE = {
612
+ INITIAL: "initial",
613
+ PREPARING: "preparing",
614
+ PROCESSING: "processing",
615
+ VERIFYING: "verifying",
616
+ AWAITING_AUTHORIZATION: "awaiting_authorization",
617
+ SUCCESS: "success",
618
+ ERROR: "error",
619
+ TIMEOUT: "timeout"
620
+ };
621
+ var PICKUP_TIME_TYPE = {
622
+ ASAP: "asap",
623
+ SCHEDULED: "scheduled"
624
+ };
625
+ var MOBILE_MONEY_PROVIDER = {
626
+ MTN: "mtn",
627
+ VODAFONE: "vodafone",
628
+ AIRTEL: "airtel"
629
+ };
630
+ var AUTHORIZATION_TYPE = {
631
+ OTP: "otp",
632
+ PIN: "pin",
633
+ PHONE: "phone",
634
+ BIRTHDAY: "birthday",
635
+ ADDRESS: "address"
636
+ };
637
+ var DEVICE_TYPE = {
638
+ MOBILE: "mobile",
639
+ DESKTOP: "desktop",
640
+ TABLET: "tablet"
641
+ };
642
+ var CONTACT_TYPE = {
643
+ PHONE: "phone",
644
+ EMAIL: "email"
645
+ };
646
+ var LINK_QUERY = {
647
+ DATA: "link.data",
648
+ ADDRESSES: "link.addresses",
649
+ MOBILE_MONEY: "link.mobile_money",
650
+ PREFERENCES: "link.preferences",
651
+ SESSIONS: "link.sessions"
652
+ };
653
+ var LINK_MUTATION = {
654
+ CHECK_STATUS: "link.check_status",
655
+ ENROLL: "link.enroll",
656
+ ENROLL_AND_LINK_ORDER: "link.enroll_and_link_order",
657
+ UPDATE_PREFERENCES: "link.update_preferences",
658
+ CREATE_ADDRESS: "link.create_address",
659
+ UPDATE_ADDRESS: "link.update_address",
660
+ DELETE_ADDRESS: "link.delete_address",
661
+ SET_DEFAULT_ADDRESS: "link.set_default_address",
662
+ TRACK_ADDRESS_USAGE: "link.track_address_usage",
663
+ CREATE_MOBILE_MONEY: "link.create_mobile_money",
664
+ DELETE_MOBILE_MONEY: "link.delete_mobile_money",
665
+ SET_DEFAULT_MOBILE_MONEY: "link.set_default_mobile_money",
666
+ TRACK_MOBILE_MONEY_USAGE: "link.track_mobile_money_usage",
667
+ VERIFY_MOBILE_MONEY: "link.verify_mobile_money",
668
+ REVOKE_SESSION: "link.revoke_session",
669
+ REVOKE_ALL_SESSIONS: "link.revoke_all_sessions"
670
+ };
671
+ var AUTH_MUTATION = {
672
+ REQUEST_OTP: "auth.request_otp",
673
+ VERIFY_OTP: "auth.verify_otp"
674
+ };
675
+ var CHECKOUT_MUTATION = {
676
+ PROCESS: "checkout.process"
677
+ };
678
+ var PAYMENT_MUTATION = {
679
+ SUBMIT_AUTHORIZATION: "payment.submit_authorization",
680
+ CHECK_STATUS: "order.poll_payment_status"
681
+ };
682
+ var ORDER_MUTATION = {
683
+ UPDATE_CUSTOMER: "order.update_order_customer"
684
+ };
685
+ var DEFAULT_CURRENCY = "GHS";
686
+ var DEFAULT_COUNTRY = "GHA";
687
+
688
+ // src/utils/paystack.ts
689
+ async function openPaystackPopup(options, signal) {
690
+ if (typeof window === "undefined") {
691
+ return { success: false, error: "PROVIDER_UNAVAILABLE" };
692
+ }
693
+ let PaystackPop;
694
+ try {
695
+ const imported = await import('@paystack/inline-js');
696
+ PaystackPop = imported.default;
697
+ } catch {
698
+ return { success: false, error: "PROVIDER_UNAVAILABLE" };
699
+ }
700
+ return new Promise((resolve) => {
701
+ let settled = false;
702
+ const resolveOnce = (result) => {
703
+ if (settled) {
704
+ return;
705
+ }
706
+ settled = true;
707
+ if (signal) {
708
+ signal.removeEventListener("abort", onAbort);
709
+ }
710
+ resolve(result);
711
+ };
712
+ const onAbort = () => {
713
+ resolveOnce({ success: false, error: "CANCELLED" });
714
+ };
715
+ if (signal?.aborted) {
716
+ resolveOnce({ success: false, error: "CANCELLED" });
717
+ return;
718
+ }
719
+ if (signal) {
720
+ signal.addEventListener("abort", onAbort, { once: true });
721
+ }
722
+ try {
723
+ const popup = new PaystackPop();
724
+ popup.newTransaction({
725
+ key: options.key,
726
+ email: options.email,
727
+ amount: options.amount,
728
+ currency: options.currency,
729
+ reference: options.reference,
730
+ accessCode: options.accessCode,
731
+ onSuccess: (transaction) => {
732
+ resolveOnce({
733
+ success: true,
734
+ reference: transaction.reference ?? options.reference
735
+ });
736
+ },
737
+ onCancel: () => {
738
+ resolveOnce({ success: false, error: "PAYMENT_CANCELLED" });
739
+ },
740
+ onError: (error) => {
741
+ resolveOnce({
742
+ success: false,
743
+ error: error?.message || "PAYMENT_FAILED"
744
+ });
745
+ }
746
+ });
747
+ } catch {
748
+ resolveOnce({ success: false, error: "POPUP_BLOCKED" });
749
+ }
750
+ });
751
+ }
752
+
753
+ // src/utils/checkout-resolver.ts
754
+ var DEFAULT_POLL_INTERVAL_MS = 3e3;
755
+ var DEFAULT_MAX_POLL_ATTEMPTS = 60;
756
+ var MAX_CONSECUTIVE_NETWORK_ERRORS = 5;
757
+ var CARD_PROVIDER_PAYSTACK = "paystack";
758
+ var NEXT_ACTION_NONE = "none";
759
+ var NEXT_ACTION_CARD_POPUP = "card_popup";
760
+ var NEXT_ACTION_REDIRECT = "redirect";
761
+ var NEXT_ACTION_AUTHORIZATION = "authorization";
762
+ function normalizeCardPopupError(error) {
763
+ if (!error || error === "PAYMENT_CANCELLED" || error === "CANCELLED") {
764
+ return "PAYMENT_CANCELLED";
765
+ }
766
+ if (error === "PROVIDER_UNAVAILABLE") {
767
+ return "PROVIDER_UNAVAILABLE";
768
+ }
769
+ if (error === "POPUP_BLOCKED") {
770
+ return "POPUP_BLOCKED";
771
+ }
772
+ return "PAYMENT_FAILED";
773
+ }
774
+ function normalizeCardProvider(value) {
775
+ const normalized = value?.trim().toLowerCase();
776
+ return normalized && normalized.length > 0 ? normalized : CARD_PROVIDER_PAYSTACK;
777
+ }
778
+ async function openCardPopup(provider, checkoutResult, email, currency, signal) {
779
+ if (typeof window === "undefined") {
780
+ return { success: false, error: "PROVIDER_UNAVAILABLE" };
781
+ }
782
+ if (provider === CARD_PROVIDER_PAYSTACK) {
783
+ return openPaystackPopup(
784
+ {
785
+ key: checkoutResult.public_key || "",
786
+ email,
787
+ accessCode: checkoutResult.client_secret,
788
+ reference: checkoutResult.payment_reference || checkoutResult.client_secret,
789
+ currency
790
+ },
791
+ signal
792
+ );
793
+ }
794
+ return { success: false, error: "PROVIDER_UNAVAILABLE" };
795
+ }
796
+ var VALID_MOBILE_MONEY_PROVIDERS = new Set(Object.values(MOBILE_MONEY_PROVIDER));
797
+ function isValidMobileMoneyProvider(value) {
798
+ return VALID_MOBILE_MONEY_PROVIDERS.has(value);
799
+ }
800
+ function normalizeAuthorizationType(value) {
801
+ if (value && typeof value === "object" && "address" in value) {
802
+ return "address";
803
+ }
804
+ if (typeof value !== "string") return void 0;
805
+ const normalized = value.trim().toLowerCase().replace(/[\s-]+/g, "_");
806
+ if (normalized === "otp" || normalized === "pin") return normalized;
807
+ if (normalized === "phone" || normalized === "birthday" || normalized === "address") {
808
+ return normalized;
809
+ }
810
+ return void 0;
811
+ }
812
+ function formatMoney(value) {
813
+ if (typeof value === "number" && Number.isFinite(value)) {
814
+ return value.toFixed(2);
815
+ }
816
+ if (typeof value === "string" && value.trim().length > 0) {
817
+ return value;
818
+ }
819
+ return "0.00";
820
+ }
821
+ function createAbortError() {
822
+ const error = new Error("CANCELLED");
823
+ error.name = "AbortError";
824
+ return error;
825
+ }
826
+ function isAbortError(error) {
827
+ if (error instanceof DOMException && error.name === "AbortError") {
828
+ return true;
829
+ }
830
+ return error instanceof Error && (error.name === "AbortError" || error.message === "CANCELLED");
831
+ }
832
+ var CheckoutResolver = class {
833
+ constructor(options) {
834
+ this.client = options.client;
835
+ this.checkoutData = options.checkoutData;
836
+ this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
837
+ this.maxPollAttempts = options.maxPollAttempts ?? DEFAULT_MAX_POLL_ATTEMPTS;
838
+ this.onStatusChange = options.onStatusChange;
839
+ this.onAuthorizationRequired = options.onAuthorizationRequired;
840
+ this.signal = options.signal;
841
+ this.returnUrl = options.returnUrl;
842
+ this.enrollInLink = options.enrollInLink !== false;
843
+ this.businessId = options.businessId;
844
+ this.addressData = options.addressData;
845
+ this.paymentData = options.paymentData;
846
+ this.orderType = options.orderType;
847
+ this.cartTotal = options.cartTotal;
848
+ this.cartCurrency = options.cartCurrency;
849
+ this.allowPopups = options.allowPopups ?? typeof window !== "undefined";
850
+ }
851
+ async resolve(checkoutResult) {
852
+ try {
853
+ this.ensureNotAborted();
854
+ if (!checkoutResult.order_id) {
855
+ return this.fail("CHECKOUT_FAILED", "Checkout did not return an order ID.", false);
856
+ }
857
+ if (checkoutResult.next_action) {
858
+ return this.resolveNextAction(checkoutResult);
859
+ }
860
+ let latestCheckoutResult = checkoutResult;
861
+ let authorizationType = normalizeAuthorizationType(checkoutResult.authorization_type);
862
+ let paymentReference = checkoutResult.payment_reference;
863
+ if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
864
+ return this.finalizeSuccess(checkoutResult);
865
+ }
866
+ if (checkoutResult.client_secret && checkoutResult.public_key) {
867
+ const provider = normalizeCardProvider(checkoutResult.provider);
868
+ this.emit("awaiting_authorization", {
869
+ display_text: "Complete payment in the card authorization popup.",
870
+ order_id: checkoutResult.order_id,
871
+ order_number: checkoutResult.order_number
872
+ });
873
+ if (!this.allowPopups) {
874
+ return this.fail(
875
+ "PROVIDER_UNAVAILABLE",
876
+ "Card payment popup is unavailable in this environment.",
877
+ false
878
+ );
879
+ }
880
+ const popupResult = await openCardPopup(
881
+ provider,
882
+ checkoutResult,
883
+ this.checkoutData.customer.email || "customer@cimplify.io",
884
+ this.getOrderCurrency(checkoutResult),
885
+ this.signal
886
+ );
887
+ if (!popupResult.success) {
888
+ const popupError = normalizeCardPopupError(popupResult.error);
889
+ if (popupError === "POPUP_BLOCKED") {
890
+ return this.fail(
891
+ "POPUP_BLOCKED",
892
+ "Unable to open card payment popup. Please allow popups and try again.",
893
+ true
894
+ );
895
+ }
896
+ if (popupError === "PROVIDER_UNAVAILABLE") {
897
+ return this.fail(
898
+ "PROVIDER_UNAVAILABLE",
899
+ "Card payment provider is unavailable.",
900
+ false
901
+ );
902
+ }
903
+ return this.fail(
904
+ "PAYMENT_CANCELLED",
905
+ "Card payment was cancelled before completion.",
906
+ true
907
+ );
908
+ }
909
+ paymentReference = popupResult.reference || paymentReference;
910
+ }
911
+ if (checkoutResult.authorization_url) {
912
+ if (typeof window !== "undefined" && this.returnUrl) {
913
+ window.location.assign(checkoutResult.authorization_url);
914
+ return this.fail(
915
+ "REDIRECT_REQUIRED",
916
+ "Redirecting to complete payment authorization.",
917
+ true
918
+ );
919
+ }
920
+ if (typeof window !== "undefined") {
921
+ const popup = window.open(
922
+ checkoutResult.authorization_url,
923
+ "cimplify-auth",
924
+ "width=520,height=760,noopener,noreferrer"
925
+ );
926
+ if (!popup) {
927
+ return this.fail(
928
+ "POPUP_BLOCKED",
929
+ "Authorization popup was blocked. Please allow popups and retry.",
930
+ true
931
+ );
932
+ }
933
+ }
934
+ }
935
+ if (checkoutResult.requires_authorization || isPaymentStatusRequiresAction(checkoutResult.payment_status)) {
936
+ const authorization = await this.handleAuthorization({
937
+ authorizationType,
938
+ paymentReference,
939
+ displayText: checkoutResult.display_text,
940
+ provider: checkoutResult.provider
941
+ });
942
+ if (!authorization.ok) {
943
+ return authorization.result;
944
+ }
945
+ if (authorization.value) {
946
+ latestCheckoutResult = authorization.value;
947
+ paymentReference = authorization.value.payment_reference || paymentReference;
948
+ authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
949
+ if (this.isSuccessfulStatus(authorization.value.payment_status)) {
950
+ return this.finalizeSuccess(authorization.value);
951
+ }
952
+ if (this.isFailureStatus(authorization.value.payment_status)) {
953
+ return this.fail(
954
+ "AUTHORIZATION_FAILED",
955
+ authorization.value.display_text || "Payment authorization failed.",
956
+ false
957
+ );
958
+ }
959
+ }
960
+ }
961
+ return this.pollUntilTerminal({
962
+ orderId: checkoutResult.order_id,
963
+ latestCheckoutResult,
964
+ paymentReference,
965
+ authorizationType
966
+ });
967
+ } catch (error) {
968
+ if (isAbortError(error)) {
969
+ return this.fail("CANCELLED", "Checkout was cancelled.", true);
970
+ }
971
+ const message = error instanceof Error ? error.message : "Checkout failed unexpectedly.";
972
+ return this.fail("CHECKOUT_FAILED", message, true);
973
+ }
974
+ }
975
+ async resolveNextAction(checkoutResult) {
976
+ const action = checkoutResult.next_action;
977
+ if (this.isSuccessfulStatus(checkoutResult.payment_status)) {
978
+ return this.finalizeSuccess(checkoutResult);
979
+ }
980
+ let latestCheckoutResult = checkoutResult;
981
+ let paymentReference = checkoutResult.payment_reference;
982
+ switch (action.type) {
983
+ case NEXT_ACTION_NONE:
984
+ return this.finalizeSuccess(checkoutResult);
985
+ case NEXT_ACTION_CARD_POPUP: {
986
+ const provider = normalizeCardProvider(action.provider);
987
+ this.emit("awaiting_authorization", {
988
+ display_text: "Complete payment in the card authorization popup.",
989
+ order_id: checkoutResult.order_id,
990
+ order_number: checkoutResult.order_number
991
+ });
992
+ if (!this.allowPopups) {
993
+ return this.fail(
994
+ "PROVIDER_UNAVAILABLE",
995
+ "Card payment popup is unavailable in this environment.",
996
+ false
997
+ );
998
+ }
999
+ const popupResult = await openCardPopup(
1000
+ provider,
1001
+ checkoutResult,
1002
+ this.checkoutData.customer.email || "customer@cimplify.io",
1003
+ this.getOrderCurrency(checkoutResult),
1004
+ this.signal
1005
+ );
1006
+ if (!popupResult.success) {
1007
+ const popupError = normalizeCardPopupError(popupResult.error);
1008
+ if (popupError === "POPUP_BLOCKED") {
1009
+ return this.fail(
1010
+ "POPUP_BLOCKED",
1011
+ "Unable to open card payment popup. Please allow popups and try again.",
1012
+ true
1013
+ );
1014
+ }
1015
+ if (popupError === "PROVIDER_UNAVAILABLE") {
1016
+ return this.fail("PROVIDER_UNAVAILABLE", "Card payment provider is unavailable.", false);
1017
+ }
1018
+ return this.fail("PAYMENT_CANCELLED", "Card payment was cancelled before completion.", true);
1019
+ }
1020
+ paymentReference = popupResult.reference || paymentReference;
1021
+ break;
1022
+ }
1023
+ case NEXT_ACTION_REDIRECT: {
1024
+ if (typeof window !== "undefined" && this.returnUrl) {
1025
+ window.location.assign(action.authorization_url);
1026
+ return this.fail("REDIRECT_REQUIRED", "Redirecting to complete payment authorization.", true);
1027
+ }
1028
+ if (typeof window !== "undefined") {
1029
+ const popup = window.open(
1030
+ action.authorization_url,
1031
+ "cimplify-auth",
1032
+ "width=520,height=760,noopener,noreferrer"
1033
+ );
1034
+ if (!popup) {
1035
+ return this.fail(
1036
+ "POPUP_BLOCKED",
1037
+ "Authorization popup was blocked. Please allow popups and retry.",
1038
+ true
1039
+ );
1040
+ }
1041
+ }
1042
+ break;
1043
+ }
1044
+ case NEXT_ACTION_AUTHORIZATION: {
1045
+ const authType = normalizeAuthorizationType(action.authorization_type);
1046
+ const authorization = await this.handleAuthorization({
1047
+ authorizationType: authType,
1048
+ paymentReference,
1049
+ displayText: action.display_text,
1050
+ provider: checkoutResult.provider
1051
+ });
1052
+ if (!authorization.ok) {
1053
+ return authorization.result;
1054
+ }
1055
+ if (authorization.value) {
1056
+ latestCheckoutResult = authorization.value;
1057
+ paymentReference = authorization.value.payment_reference || paymentReference;
1058
+ if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1059
+ return this.finalizeSuccess(authorization.value);
1060
+ }
1061
+ if (this.isFailureStatus(authorization.value.payment_status)) {
1062
+ return this.fail(
1063
+ "AUTHORIZATION_FAILED",
1064
+ authorization.value.display_text || "Payment authorization failed.",
1065
+ false
1066
+ );
1067
+ }
1068
+ }
1069
+ break;
1070
+ }
1071
+ }
1072
+ return this.pollUntilTerminal({
1073
+ orderId: checkoutResult.order_id,
1074
+ latestCheckoutResult,
1075
+ paymentReference,
1076
+ authorizationType: normalizeAuthorizationType(checkoutResult.authorization_type)
1077
+ });
1078
+ }
1079
+ async pollUntilTerminal(input) {
1080
+ let consecutiveErrors = 0;
1081
+ let latestCheckoutResult = input.latestCheckoutResult;
1082
+ let paymentReference = input.paymentReference;
1083
+ let authorizationType = input.authorizationType;
1084
+ for (let attempt = 0; attempt < this.maxPollAttempts; attempt++) {
1085
+ this.ensureNotAborted();
1086
+ this.emit("polling", {
1087
+ poll_attempt: attempt,
1088
+ max_poll_attempts: this.maxPollAttempts,
1089
+ order_id: input.orderId,
1090
+ order_number: latestCheckoutResult.order_number
1091
+ });
1092
+ const statusResult = await this.client.checkout.pollPaymentStatus(input.orderId);
1093
+ if (!statusResult.ok) {
1094
+ consecutiveErrors += 1;
1095
+ if (consecutiveErrors >= MAX_CONSECUTIVE_NETWORK_ERRORS) {
1096
+ return this.fail(
1097
+ "NETWORK_ERROR",
1098
+ "Unable to confirm payment due to repeated network errors.",
1099
+ true
1100
+ );
1101
+ }
1102
+ await this.wait(this.pollIntervalMs);
1103
+ continue;
1104
+ }
1105
+ consecutiveErrors = 0;
1106
+ if (statusResult.value.reference) {
1107
+ paymentReference = statusResult.value.reference;
1108
+ }
1109
+ const normalized = normalizeStatusResponse(statusResult.value);
1110
+ if (normalized.paid || isPaymentStatusSuccess(normalized.status)) {
1111
+ return this.finalizeSuccess(latestCheckoutResult);
1112
+ }
1113
+ if (isPaymentStatusFailure(normalized.status)) {
1114
+ return this.fail(
1115
+ normalized.status ? normalized.status.toUpperCase() : "PAYMENT_FAILED",
1116
+ normalized.message || "Payment failed during confirmation.",
1117
+ false
1118
+ );
1119
+ }
1120
+ if (isPaymentStatusRequiresAction(normalized.status)) {
1121
+ const authorization = await this.handleAuthorization({
1122
+ authorizationType,
1123
+ paymentReference,
1124
+ displayText: normalized.message || latestCheckoutResult.display_text,
1125
+ provider: latestCheckoutResult.provider
1126
+ });
1127
+ if (!authorization.ok) {
1128
+ return authorization.result;
1129
+ }
1130
+ if (authorization.value) {
1131
+ latestCheckoutResult = authorization.value;
1132
+ paymentReference = authorization.value.payment_reference || paymentReference;
1133
+ authorizationType = normalizeAuthorizationType(authorization.value.authorization_type) || authorizationType;
1134
+ if (this.isSuccessfulStatus(authorization.value.payment_status)) {
1135
+ return this.finalizeSuccess(authorization.value);
1136
+ }
1137
+ if (this.isFailureStatus(authorization.value.payment_status)) {
1138
+ return this.fail(
1139
+ "AUTHORIZATION_FAILED",
1140
+ authorization.value.display_text || "Payment authorization failed.",
1141
+ false
1142
+ );
1143
+ }
1144
+ }
1145
+ }
1146
+ await this.wait(this.pollIntervalMs);
1147
+ }
1148
+ try {
1149
+ const finalResult = await this.client.checkout.pollPaymentStatus(input.orderId);
1150
+ if (finalResult.ok) {
1151
+ const normalized = normalizeStatusResponse(finalResult.value);
1152
+ if (normalized.paid || isPaymentStatusSuccess(normalized.status)) {
1153
+ return this.finalizeSuccess(latestCheckoutResult);
1154
+ }
1155
+ }
1156
+ } catch {
1157
+ }
1158
+ return this.fail(
1159
+ "PAYMENT_TIMEOUT",
1160
+ "Payment confirmation timed out. Please retry checkout.",
1161
+ true
1162
+ );
1163
+ }
1164
+ async handleAuthorization(input) {
1165
+ const authorizationType = input.authorizationType;
1166
+ const paymentReference = input.paymentReference;
1167
+ if (!authorizationType || !paymentReference) {
1168
+ return { ok: true };
1169
+ }
1170
+ this.emit("awaiting_authorization", {
1171
+ authorization_type: authorizationType,
1172
+ display_text: input.displayText,
1173
+ provider: input.provider
1174
+ });
1175
+ let authorizationResult;
1176
+ const submit = async (code) => {
1177
+ this.ensureNotAborted();
1178
+ const trimmedCode = code.trim();
1179
+ if (!trimmedCode) {
1180
+ throw new Error("Authorization code is required.");
1181
+ }
1182
+ const submitResult = await this.client.checkout.submitAuthorization({
1183
+ reference: paymentReference,
1184
+ auth_type: authorizationType,
1185
+ value: trimmedCode
1186
+ });
1187
+ if (!submitResult.ok) {
1188
+ throw new Error(submitResult.error.message || "Authorization failed.");
1189
+ }
1190
+ authorizationResult = submitResult.value;
1191
+ };
1192
+ try {
1193
+ if (this.onAuthorizationRequired) {
1194
+ await this.onAuthorizationRequired(authorizationType, submit);
1195
+ } else {
1196
+ return {
1197
+ ok: false,
1198
+ result: this.fail(
1199
+ "AUTHORIZATION_REQUIRED",
1200
+ "Authorization callback is required in headless checkout mode.",
1201
+ false
1202
+ )
1203
+ };
1204
+ }
1205
+ } catch (error) {
1206
+ if (isAbortError(error)) {
1207
+ return {
1208
+ ok: false,
1209
+ result: this.fail("CANCELLED", "Checkout was cancelled.", true)
1210
+ };
1211
+ }
1212
+ const message = error instanceof Error ? error.message : "Payment authorization failed.";
1213
+ return {
1214
+ ok: false,
1215
+ result: this.fail("AUTHORIZATION_FAILED", message, true)
1216
+ };
1217
+ }
1218
+ return {
1219
+ ok: true,
1220
+ value: authorizationResult
1221
+ };
1222
+ }
1223
+ async finalizeSuccess(checkoutResult) {
1224
+ this.emit("finalizing", {
1225
+ order_id: checkoutResult.order_id,
1226
+ order_number: checkoutResult.order_number
1227
+ });
1228
+ const enrolledInLink = await this.maybeEnrollInLink(checkoutResult.order_id);
1229
+ this.emit("success", {
1230
+ order_id: checkoutResult.order_id,
1231
+ order_number: checkoutResult.order_number
1232
+ });
1233
+ return {
1234
+ success: true,
1235
+ order: {
1236
+ id: checkoutResult.order_id,
1237
+ order_number: checkoutResult.order_number || checkoutResult.order_id,
1238
+ status: checkoutResult.payment_status || "paid",
1239
+ total: this.getOrderTotal(checkoutResult),
1240
+ currency: this.getOrderCurrency(checkoutResult)
1241
+ },
1242
+ enrolled_in_link: enrolledInLink
1243
+ };
1244
+ }
1245
+ async maybeEnrollInLink(orderId) {
1246
+ if (!this.enrollInLink || !orderId) {
1247
+ return false;
1248
+ }
1249
+ let businessId = this.businessId;
1250
+ if (!businessId) {
1251
+ const businessResult = await this.client.business.getInfo();
1252
+ if (!businessResult.ok || !businessResult.value?.id) {
1253
+ return false;
1254
+ }
1255
+ businessId = businessResult.value.id;
1256
+ }
1257
+ const address = this.getEnrollmentAddress();
1258
+ const mobileMoney = this.getEnrollmentMobileMoney();
1259
+ try {
1260
+ const enrollment = await this.client.link.enrollAndLinkOrder({
1261
+ order_id: orderId,
1262
+ business_id: businessId,
1263
+ order_type: this.orderType || this.checkoutData.order_type,
1264
+ address,
1265
+ mobile_money: mobileMoney
1266
+ });
1267
+ return enrollment.ok && enrollment.value.success;
1268
+ } catch {
1269
+ return false;
1270
+ }
1271
+ }
1272
+ getEnrollmentAddress() {
1273
+ const source = this.addressData ?? this.checkoutData.address_info;
1274
+ if (!source?.street_address) {
1275
+ return void 0;
1276
+ }
1277
+ return {
1278
+ label: "Default",
1279
+ street_address: source.street_address,
1280
+ apartment: source.apartment || void 0,
1281
+ city: source.city || "",
1282
+ region: source.region || "",
1283
+ delivery_instructions: source.delivery_instructions || void 0,
1284
+ phone_for_delivery: source.phone_for_delivery || void 0
1285
+ };
1286
+ }
1287
+ getEnrollmentMobileMoney() {
1288
+ if (this.paymentData?.type === "mobile_money" && this.paymentData.phone_number && this.paymentData.provider && isValidMobileMoneyProvider(this.paymentData.provider)) {
1289
+ return {
1290
+ phone_number: this.paymentData.phone_number,
1291
+ provider: this.paymentData.provider,
1292
+ label: this.paymentData.label || "Mobile Money"
1293
+ };
1294
+ }
1295
+ if (this.checkoutData.mobile_money_details?.phone_number) {
1296
+ return {
1297
+ phone_number: this.checkoutData.mobile_money_details.phone_number,
1298
+ provider: this.checkoutData.mobile_money_details.provider,
1299
+ label: "Mobile Money"
1300
+ };
1301
+ }
1302
+ return void 0;
1303
+ }
1304
+ getOrderTotal(checkoutResult) {
1305
+ if (checkoutResult.fx?.pay_amount !== void 0) {
1306
+ return formatMoney(checkoutResult.fx.pay_amount);
1307
+ }
1308
+ if (this.cartTotal !== void 0) {
1309
+ return formatMoney(this.cartTotal);
1310
+ }
1311
+ return "0.00";
1312
+ }
1313
+ getOrderCurrency(checkoutResult) {
1314
+ if (checkoutResult.fx?.pay_currency) {
1315
+ return checkoutResult.fx.pay_currency;
1316
+ }
1317
+ if (this.cartCurrency) {
1318
+ return this.cartCurrency;
1319
+ }
1320
+ if (this.checkoutData.pay_currency) {
1321
+ return this.checkoutData.pay_currency;
1322
+ }
1323
+ return "GHS";
1324
+ }
1325
+ emit(status, context = {}) {
1326
+ if (!this.onStatusChange) {
1327
+ return;
1328
+ }
1329
+ try {
1330
+ this.onStatusChange(status, context);
1331
+ } catch {
1332
+ }
1333
+ }
1334
+ fail(code, message, recoverable) {
1335
+ this.emit("failed", {
1336
+ display_text: message
1337
+ });
1338
+ return {
1339
+ success: false,
1340
+ error: {
1341
+ code,
1342
+ message,
1343
+ recoverable
1344
+ }
1345
+ };
1346
+ }
1347
+ isSuccessfulStatus(status) {
1348
+ const normalized = normalizeStatusResponse({ status, paid: false });
1349
+ return normalized.paid || isPaymentStatusSuccess(normalized.status);
1350
+ }
1351
+ isFailureStatus(status) {
1352
+ const normalized = normalizeStatusResponse({ status, paid: false });
1353
+ return isPaymentStatusFailure(normalized.status);
1354
+ }
1355
+ ensureNotAborted() {
1356
+ if (this.signal?.aborted) {
1357
+ throw createAbortError();
1358
+ }
1359
+ }
1360
+ wait(ms) {
1361
+ this.ensureNotAborted();
1362
+ return new Promise((resolve, reject) => {
1363
+ const timer = setTimeout(() => {
1364
+ if (this.signal) {
1365
+ this.signal.removeEventListener("abort", onAbort);
1366
+ }
1367
+ resolve();
1368
+ }, ms);
1369
+ const onAbort = () => {
1370
+ clearTimeout(timer);
1371
+ reject(createAbortError());
1372
+ };
1373
+ if (this.signal) {
1374
+ this.signal.addEventListener("abort", onAbort, { once: true });
1375
+ }
1376
+ });
1377
+ }
1378
+ };
1379
+
1380
+ // src/checkout.ts
1381
+ function toTerminalFailure(code, message, recoverable) {
1382
+ return {
1383
+ success: false,
1384
+ error: {
1385
+ code,
1386
+ message,
1387
+ recoverable
1388
+ }
1389
+ };
1390
+ }
1391
+ function generateIdempotencyKey() {
1392
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
1393
+ return `idem_${crypto.randomUUID()}`;
1394
+ }
1395
+ const timestamp = Date.now().toString(36);
1396
+ const random = Math.random().toString(36).substring(2, 15);
1397
+ return `idem_${timestamp}_${random}`;
1398
+ }
1399
+ var CheckoutService = class {
1400
+ constructor(client) {
1401
+ this.client = client;
1402
+ }
1403
+ orderTokenParam(orderId) {
1404
+ const token = this.client.getOrderToken(orderId);
1405
+ return token ? `?token=${encodeURIComponent(token)}` : "";
1406
+ }
1407
+ async process(data) {
1408
+ const checkoutData = {
1409
+ ...data,
1410
+ idempotency_key: data.idempotency_key || generateIdempotencyKey()
1411
+ };
1412
+ const result = await safe(
1413
+ this.client.post("/api/v1/checkout", {
1414
+ checkout_data: checkoutData
1415
+ })
1416
+ );
1417
+ if (result.ok && result.value.bill_token) {
1418
+ this.client.setOrderToken(result.value.order_id, result.value.bill_token);
1419
+ }
1420
+ return result;
1421
+ }
1422
+ async initializePayment(_orderId, _method) {
1423
+ return err(
1424
+ new CimplifyError(
1425
+ "NOT_IMPLEMENTED",
1426
+ "initializePayment is not available in REST mode.",
1427
+ false
1428
+ )
1429
+ );
1430
+ }
1431
+ async submitAuthorization(input) {
1432
+ return safe(this.client.post("/api/v1/payments/authorization", input));
1433
+ }
1434
+ async pollPaymentStatus(orderId) {
1435
+ const encodedId = encodeURIComponent(orderId);
1436
+ const tokenParam = this.orderTokenParam(orderId);
1437
+ return safe(this.client.get(`/api/v1/orders/${encodedId}/payment-status${tokenParam}`));
1438
+ }
1439
+ async updateOrderCustomer(orderId, customer) {
1440
+ const encodedId = encodeURIComponent(orderId);
1441
+ const tokenParam = this.orderTokenParam(orderId);
1442
+ return safe(this.client.post(`/api/v1/orders/${encodedId}/customer${tokenParam}`, customer));
1443
+ }
1444
+ async verifyPayment(orderId) {
1445
+ const encodedId = encodeURIComponent(orderId);
1446
+ const tokenParam = this.orderTokenParam(orderId);
1447
+ return safe(this.client.post(`/api/v1/orders/${encodedId}/verify-payment${tokenParam}`));
1448
+ }
1449
+ async processAndResolve(data) {
1450
+ data.on_status_change?.("preparing", {});
1451
+ if (!data.cart_id) {
1452
+ return ok(
1453
+ toTerminalFailure(
1454
+ "INVALID_CART",
1455
+ "A valid cart is required before checkout can start.",
1456
+ false
1457
+ )
1458
+ );
1459
+ }
1460
+ const cartResult = await this.client.cart.get();
1461
+ if (!cartResult.ok || !cartResult.value?.id) {
1462
+ return ok(
1463
+ toTerminalFailure(
1464
+ "INVALID_CART",
1465
+ "Unable to load cart for checkout.",
1466
+ false
1467
+ )
1468
+ );
1469
+ }
1470
+ const cart = cartResult.value;
1471
+ if (cart.id !== data.cart_id || cart.items.length === 0) {
1472
+ return ok(
1473
+ toTerminalFailure(
1474
+ "INVALID_CART",
1475
+ "Cart is empty or no longer valid. Please refresh and try again.",
1476
+ false
1477
+ )
1478
+ );
1479
+ }
1480
+ const checkoutData = {
1481
+ cart_id: data.cart_id,
1482
+ location_id: data.location_id,
1483
+ customer: data.customer,
1484
+ order_type: data.order_type,
1485
+ address_info: data.address_info,
1486
+ payment_method: data.payment_method,
1487
+ mobile_money_details: data.mobile_money_details,
1488
+ special_instructions: data.special_instructions,
1489
+ link_address_id: data.link_address_id,
1490
+ link_payment_method_id: data.link_payment_method_id,
1491
+ idempotency_key: data.idempotency_key || generateIdempotencyKey(),
1492
+ metadata: data.metadata,
1493
+ pay_currency: data.pay_currency,
1494
+ fx_quote_id: data.fx_quote_id
1495
+ };
1496
+ const baseCurrency = currencyCode(
1497
+ (cart.pricing.currency || checkoutData.pay_currency || "GHS").toUpperCase()
1498
+ );
1499
+ const payCurrency = data.pay_currency?.trim().toUpperCase();
1500
+ const payCurrencyCode = payCurrency ? currencyCode(payCurrency) : void 0;
1501
+ const cartTotalAmount = typeof cart.pricing.total_price === "number" ? cart.pricing.total_price : Number.parseFloat(cart.pricing.total_price || "0");
1502
+ if (payCurrencyCode && payCurrencyCode !== baseCurrency && !checkoutData.fx_quote_id && Number.isFinite(cartTotalAmount) && cartTotalAmount > 0) {
1503
+ const fxQuoteResult = await this.client.fx.lockQuote({
1504
+ from: baseCurrency,
1505
+ to: payCurrencyCode,
1506
+ amount: cart.pricing.total_price
1507
+ });
1508
+ if (!fxQuoteResult.ok) {
1509
+ return ok(
1510
+ toTerminalFailure(
1511
+ "FX_QUOTE_FAILED",
1512
+ fxQuoteResult.error.message || "Unable to lock FX quote for checkout.",
1513
+ true
1514
+ )
1515
+ );
1516
+ }
1517
+ checkoutData.pay_currency = payCurrencyCode;
1518
+ checkoutData.fx_quote_id = fxQuoteResult.value.id;
1519
+ }
1520
+ data.on_status_change?.("processing", {});
1521
+ const processResult = await this.process(checkoutData);
1522
+ if (!processResult.ok) {
1523
+ return ok(
1524
+ toTerminalFailure(
1525
+ processResult.error.code || "CHECKOUT_FAILED",
1526
+ processResult.error.message || "Unable to process checkout.",
1527
+ processResult.error.retryable ?? true
1528
+ )
1529
+ );
1530
+ }
1531
+ const resolver = new CheckoutResolver({
1532
+ client: this.client,
1533
+ checkoutData,
1534
+ pollIntervalMs: data.poll_interval_ms,
1535
+ maxPollAttempts: data.max_poll_attempts,
1536
+ onStatusChange: data.on_status_change,
1537
+ onAuthorizationRequired: data.on_authorization_required,
1538
+ signal: data.signal,
1539
+ returnUrl: data.return_url,
1540
+ enrollInLink: data.enroll_in_link,
1541
+ cartTotal: cart.pricing.total_price,
1542
+ cartCurrency: checkoutData.pay_currency || cart.pricing.currency || "GHS",
1543
+ orderType: checkoutData.order_type,
1544
+ allowPopups: data.allow_popups
1545
+ });
1546
+ const resolved = await resolver.resolve(processResult.value);
1547
+ return ok(resolved);
1548
+ }
1549
+ };
1550
+
1551
+ // src/orders.ts
1552
+ var OrderQueries = class {
1553
+ constructor(client) {
1554
+ this.client = client;
1555
+ }
1556
+ orderTokenParam(orderId) {
1557
+ const token = this.client.getOrderToken(orderId);
1558
+ return token ? `?token=${encodeURIComponent(token)}` : "";
1559
+ }
1560
+ async list(options) {
1561
+ const params = new URLSearchParams();
1562
+ if (options?.status) params.set("status", options.status);
1563
+ if (options?.limit) params.set("limit", String(options.limit));
1564
+ if (options?.offset) params.set("offset", String(options.offset));
1565
+ const path = params.toString() ? `/api/v1/orders?${params.toString()}` : "/api/v1/orders";
1566
+ return safe(this.client.get(path));
1567
+ }
1568
+ async get(orderId) {
1569
+ const encodedId = encodeURIComponent(orderId);
1570
+ const tokenParam = this.orderTokenParam(orderId);
1571
+ return safe(this.client.get(`/api/v1/orders/${encodedId}${tokenParam}`));
1572
+ }
1573
+ async getRecent(limit = 5) {
1574
+ return this.list({ limit });
1575
+ }
1576
+ async getByStatus(status) {
1577
+ return this.list({ status });
1578
+ }
1579
+ async cancel(orderId, reason) {
1580
+ const encodedId = encodeURIComponent(orderId);
1581
+ const tokenParam = this.orderTokenParam(orderId);
1582
+ return safe(
1583
+ this.client.post(`/api/v1/orders/${encodedId}/cancel${tokenParam}`, {
1584
+ reason
1585
+ })
1586
+ );
1587
+ }
1588
+ };
1589
+
1590
+ // src/link.ts
1591
+ function encodePathSegment(value) {
1592
+ return encodeURIComponent(value);
1593
+ }
1594
+ function toCreateAddressPayload(input) {
1595
+ return {
1596
+ label: input.label,
1597
+ street_address: input.address_line1,
1598
+ apartment: input.address_line2,
1599
+ city: input.city,
1600
+ region: input.state ?? "",
1601
+ postal_code: input.postal_code,
1602
+ country: input.country
1603
+ };
1604
+ }
1605
+ function toUpdateAddressPayload(input) {
1606
+ return {
1607
+ label: input.label,
1608
+ street_address: input.address_line1,
1609
+ apartment: input.address_line2,
1610
+ city: input.city,
1611
+ region: input.state,
1612
+ postal_code: input.postal_code,
1613
+ country: input.country
1614
+ };
1615
+ }
1616
+ function toCreateMobileMoneyPayload(input) {
1617
+ return {
1618
+ phone_number: input.phone_number,
1619
+ provider: input.provider,
1620
+ label: input.account_name
1621
+ };
1622
+ }
1623
+ var LinkService = class {
1624
+ constructor(client) {
1625
+ this.client = client;
1626
+ }
1627
+ async getProfile() {
1628
+ return safe(this.client.linkGet("/v1/link/profile"));
1629
+ }
1630
+ async requestOtp(input) {
1631
+ return safe(this.client.linkPost("/v1/link/auth/request-otp", input));
1632
+ }
1633
+ async verifyOtp(input) {
1634
+ const result = await safe(
1635
+ this.client.linkPost("/v1/link/auth/verify-otp", input)
1636
+ );
1637
+ if (result.ok && result.value.session_token) {
1638
+ this.client.setAccessToken(result.value.session_token);
1639
+ }
1640
+ return result;
1641
+ }
1642
+ async logout() {
1643
+ const result = await safe(this.client.linkPost("/v1/link/auth/logout"));
1644
+ if (result.ok) {
1645
+ this.client.clearSession();
1646
+ }
1647
+ return result;
1648
+ }
1649
+ async checkStatus(contact) {
1650
+ return safe(this.client.linkPost("/v1/link/check-status", { contact }));
1651
+ }
1652
+ async getLinkData() {
1653
+ return safe(this.client.linkGet("/v1/link/data"));
1654
+ }
1655
+ async getAddresses() {
1656
+ return safe(this.client.linkGet("/v1/link/addresses"));
1657
+ }
1658
+ async getMobileMoney() {
1659
+ return safe(this.client.linkGet("/v1/link/mobile-money"));
1660
+ }
1661
+ async getPreferences() {
1662
+ return safe(this.client.linkGet("/v1/link/preferences"));
1663
+ }
1664
+ async enroll(data) {
1665
+ return safe(this.client.linkPost("/v1/link/enroll", data));
1666
+ }
1667
+ async enrollAndLinkOrder(data) {
1668
+ return safe(
1669
+ this.client.linkPost("/v1/link/enroll-and-link-order", data)
1670
+ );
1671
+ }
1672
+ async updatePreferences(preferences) {
1673
+ return safe(this.client.linkPost("/v1/link/preferences", preferences));
1674
+ }
1675
+ async createAddress(input) {
1676
+ return safe(
1677
+ this.client.linkPost("/v1/link/addresses", toCreateAddressPayload(input))
1678
+ );
1679
+ }
1680
+ async updateAddress(input) {
1681
+ return safe(
1682
+ this.client.linkPost(
1683
+ `/v1/link/addresses/${encodePathSegment(input.address_id)}`,
1684
+ toUpdateAddressPayload(input)
1685
+ )
1686
+ );
1687
+ }
1688
+ async deleteAddress(addressId) {
1689
+ return safe(
1690
+ this.client.linkDelete(`/v1/link/addresses/${encodePathSegment(addressId)}`)
1691
+ );
1692
+ }
1693
+ async setDefaultAddress(addressId) {
1694
+ return safe(
1695
+ this.client.linkPost(
1696
+ `/v1/link/addresses/${encodePathSegment(addressId)}/default`
1697
+ )
1698
+ );
1699
+ }
1700
+ async trackAddressUsage(addressId) {
1701
+ return safe(
1702
+ this.client.linkPost(
1703
+ `/v1/link/addresses/${encodePathSegment(addressId)}/track-usage`
1704
+ )
1705
+ );
1706
+ }
1707
+ async createMobileMoney(input) {
1708
+ return safe(
1709
+ this.client.linkPost(
1710
+ "/v1/link/mobile-money",
1711
+ toCreateMobileMoneyPayload(input)
1712
+ )
1713
+ );
1714
+ }
1715
+ async deleteMobileMoney(mobileMoneyId) {
1716
+ return safe(
1717
+ this.client.linkDelete(
1718
+ `/v1/link/mobile-money/${encodePathSegment(mobileMoneyId)}`
1719
+ )
1720
+ );
1721
+ }
1722
+ async setDefaultMobileMoney(mobileMoneyId) {
1723
+ return safe(
1724
+ this.client.linkPost(
1725
+ `/v1/link/mobile-money/${encodePathSegment(mobileMoneyId)}/default`
1726
+ )
1727
+ );
1728
+ }
1729
+ async trackMobileMoneyUsage(mobileMoneyId) {
1730
+ return safe(
1731
+ this.client.linkPost(
1732
+ `/v1/link/mobile-money/${encodePathSegment(mobileMoneyId)}/track-usage`
1733
+ )
1734
+ );
1735
+ }
1736
+ async verifyMobileMoney(mobileMoneyId) {
1737
+ return safe(
1738
+ this.client.linkPost(
1739
+ `/v1/link/mobile-money/${encodePathSegment(mobileMoneyId)}/verify`
1740
+ )
1741
+ );
1742
+ }
1743
+ async getSessions() {
1744
+ return safe(this.client.linkGet("/v1/link/sessions"));
1745
+ }
1746
+ async revokeSession(sessionId) {
1747
+ return safe(this.client.linkDelete(`/v1/link/sessions/${sessionId}`));
1748
+ }
1749
+ async revokeAllSessions() {
1750
+ return safe(this.client.linkDelete("/v1/link/sessions"));
1751
+ }
1752
+ };
1753
+
1754
+ // src/auth.ts
1755
+ var AuthService = class {
1756
+ constructor(client) {
1757
+ this.client = client;
1758
+ }
1759
+ async getStatus() {
1760
+ return safe(this.client.get("/api/v1/auth/status"));
1761
+ }
1762
+ async getCurrentUser() {
1763
+ const result = await this.getStatus();
1764
+ if (!result.ok) return result;
1765
+ return ok(result.value.customer || null);
1766
+ }
1767
+ async isAuthenticated() {
1768
+ const result = await this.getStatus();
1769
+ if (!result.ok) return result;
1770
+ return ok(result.value.is_authenticated);
1771
+ }
1772
+ async requestOtp(contact, contactType) {
1773
+ return safe(
1774
+ this.client.post("/api/v1/auth/request-otp", {
1775
+ contact,
1776
+ contact_type: contactType
1777
+ })
1778
+ );
1779
+ }
1780
+ async verifyOtp(code, contact) {
1781
+ return safe(
1782
+ this.client.post("/api/v1/auth/verify-otp", {
1783
+ otp_code: code,
1784
+ contact
1785
+ })
1786
+ );
1787
+ }
1788
+ async logout() {
1789
+ return safe(this.client.post("/api/v1/auth/logout"));
1790
+ }
1791
+ async updateProfile(input) {
1792
+ return safe(this.client.post("/api/v1/auth/profile", input));
1793
+ }
1794
+ };
1795
+
1796
+ // src/business.ts
1797
+ var BusinessService = class {
1798
+ constructor(client) {
1799
+ this.client = client;
1800
+ }
1801
+ async getInfo() {
1802
+ return safe(this.client.get("/api/v1/business"));
1803
+ }
1804
+ async getByHandle(handle) {
1805
+ const encodedHandle = encodeURIComponent(handle);
1806
+ return safe(this.client.get(`/api/v1/business/by-handle/${encodedHandle}`));
1807
+ }
1808
+ async getByDomain(domain) {
1809
+ const encodedDomain = encodeURIComponent(domain);
1810
+ return safe(this.client.get(`/api/v1/business/by-domain?domain=${encodedDomain}`));
1811
+ }
1812
+ async getSettings() {
1813
+ return safe(this.client.get("/api/v1/business/settings"));
1814
+ }
1815
+ async getLocations() {
1816
+ return safe(this.client.get("/api/v1/business/locations"));
1817
+ }
1818
+ async getLocation(locationId) {
1819
+ const result = await this.getLocations();
1820
+ if (!result.ok) return err(result.error);
1821
+ const location = result.value.find((item) => item.id === locationId);
1822
+ if (!location) {
1823
+ return err(new CimplifyError(ErrorCode.NOT_FOUND, `Location not found: ${locationId}`, false));
1824
+ }
1825
+ return ok(location);
1826
+ }
1827
+ async getHours() {
1828
+ return safe(this.client.get("/api/v1/business/hours"));
1829
+ }
1830
+ async getLocationHours(locationId) {
1831
+ const result = await this.getHours();
1832
+ if (!result.ok) return result;
1833
+ return ok(result.value.filter((hour) => hour.location_id === locationId));
1834
+ }
1835
+ async getBootstrap() {
1836
+ return safe(this.client.get("/api/v1/bootstrap"));
1837
+ }
1838
+ };
1839
+
1840
+ // src/inventory.ts
1841
+ var InventoryService = class {
1842
+ constructor(client) {
1843
+ this.client = client;
1844
+ }
1845
+ withQuery(path, params) {
1846
+ const searchParams = new URLSearchParams();
1847
+ for (const [key, value] of Object.entries(params)) {
1848
+ if (value === void 0) continue;
1849
+ searchParams.set(key, String(value));
1850
+ }
1851
+ const query = searchParams.toString();
1852
+ return query ? `${path}?${query}` : path;
1853
+ }
1854
+ async getProductStock(productId, locationId) {
1855
+ const encodedId = encodeURIComponent(productId);
1856
+ const path = this.withQuery(`/api/v1/inventory/products/${encodedId}/stock`, {
1857
+ location_id: locationId
1858
+ });
1859
+ return safe(this.client.get(path));
1860
+ }
1861
+ async getVariantStock(variantId, locationId) {
1862
+ const encodedId = encodeURIComponent(variantId);
1863
+ const path = this.withQuery(`/api/v1/inventory/variants/${encodedId}/stock`, {
1864
+ location_id: locationId
1865
+ });
1866
+ return safe(this.client.get(path));
1867
+ }
1868
+ async checkProductAvailability(productId, quantity, locationId) {
1869
+ const encodedId = encodeURIComponent(productId);
1870
+ const path = this.withQuery(`/api/v1/inventory/products/${encodedId}/availability`, {
1871
+ quantity,
1872
+ location_id: locationId
1873
+ });
1874
+ return safe(this.client.get(path));
1875
+ }
1876
+ async checkVariantAvailability(variantId, quantity, locationId) {
1877
+ const encodedId = encodeURIComponent(variantId);
1878
+ const path = this.withQuery(`/api/v1/inventory/variants/${encodedId}/availability`, {
1879
+ quantity,
1880
+ location_id: locationId
1881
+ });
1882
+ return safe(this.client.get(path));
1883
+ }
1884
+ async checkMultipleAvailability(items, locationId) {
1885
+ const results = await Promise.all(
1886
+ items.map(
1887
+ (item) => item.variant_id ? this.checkVariantAvailability(item.variant_id, item.quantity, locationId) : this.checkProductAvailability(item.product_id, item.quantity, locationId)
1888
+ )
1889
+ );
1890
+ for (const result of results) {
1891
+ if (!result.ok) return result;
1892
+ }
1893
+ return ok(results.map((r) => r.value));
1894
+ }
1895
+ async isInStock(productId, locationId) {
1896
+ const result = await this.checkProductAvailability(productId, 1, locationId);
1897
+ if (!result.ok) return result;
1898
+ return ok(result.value.is_available);
1899
+ }
1900
+ async getAvailableQuantity(productId, locationId) {
1901
+ const result = await this.getProductStock(productId, locationId);
1902
+ if (!result.ok) return result;
1903
+ return ok(result.value.available_quantity);
1904
+ }
1905
+ };
1906
+
1907
+ // src/scheduling.ts
1908
+ function withQuery2(path, params) {
1909
+ const searchParams = new URLSearchParams();
1910
+ for (const [key, value] of Object.entries(params)) {
1911
+ if (value === void 0) continue;
1912
+ searchParams.set(key, String(value));
1913
+ }
1914
+ const query = searchParams.toString();
1915
+ return query ? `${path}?${query}` : path;
1916
+ }
1917
+ function firstScheduledTime(booking) {
1918
+ return booking.service_items.find((item) => typeof item.scheduled_start === "string")?.scheduled_start || booking.created_at;
1919
+ }
1920
+ var SchedulingService = class {
1921
+ constructor(client) {
1922
+ this.client = client;
1923
+ }
1924
+ async getServices() {
1925
+ return safe(this.client.get("/api/v1/scheduling/services"));
1926
+ }
1927
+ async getService(serviceId) {
1928
+ const encodedId = encodeURIComponent(serviceId);
1929
+ return safe(this.client.get(`/api/v1/scheduling/services/${encodedId}`));
1930
+ }
1931
+ async getAvailableSlots(input) {
1932
+ const path = withQuery2("/api/v1/scheduling/slots", {
1933
+ service_id: input.service_id,
1934
+ date: input.date,
1935
+ participant_count: input.participant_count,
1936
+ duration_minutes: input.duration_minutes
1937
+ });
1938
+ return safe(this.client.get(path));
1939
+ }
1940
+ async checkSlotAvailability(input) {
1941
+ const path = withQuery2("/api/v1/scheduling/slots/check", {
1942
+ service_id: input.service_id,
1943
+ slot_time: input.slot_time,
1944
+ duration_minutes: input.duration_minutes,
1945
+ participant_count: input.participant_count
1946
+ });
1947
+ return safe(this.client.get(path));
1948
+ }
1949
+ async getServiceAvailability(params) {
1950
+ const path = withQuery2("/api/v1/scheduling/availability", {
1951
+ service_id: params.service_id,
1952
+ start_date: params.start_date,
1953
+ end_date: params.end_date,
1954
+ location_id: params.location_id,
1955
+ participant_count: params.participant_count
1956
+ });
1957
+ return safe(this.client.get(path));
1958
+ }
1959
+ async getBooking(bookingId) {
1960
+ const encodedId = encodeURIComponent(bookingId);
1961
+ return safe(this.client.get(`/api/v1/scheduling/bookings/${encodedId}`));
1962
+ }
1963
+ async getCustomerBookings(customerId) {
1964
+ const path = withQuery2("/api/v1/scheduling/bookings", {
1965
+ customer_id: customerId
1966
+ });
1967
+ return safe(this.client.get(path));
1968
+ }
1969
+ async getUpcomingBookings() {
1970
+ const result = await this.getCustomerBookings();
1971
+ if (!result.ok) return result;
1972
+ const upcoming = result.value.filter((booking) => {
1973
+ const status = String(booking.status).toLowerCase();
1974
+ return status !== "completed" && status !== "cancelled";
1975
+ }).sort((a, b) => firstScheduledTime(a).localeCompare(firstScheduledTime(b)));
1976
+ return ok(upcoming);
1977
+ }
1978
+ async getPastBookings(limit = 10) {
1979
+ const result = await this.getCustomerBookings();
1980
+ if (!result.ok) return result;
1981
+ 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));
1982
+ return ok(past);
1983
+ }
1984
+ async cancelBooking(input) {
1985
+ const encodedId = encodeURIComponent(input.booking_id);
1986
+ return safe(
1987
+ this.client.post(`/api/v1/scheduling/bookings/${encodedId}/cancel`, {
1988
+ reason: input.reason
1989
+ })
1990
+ );
1991
+ }
1992
+ async rescheduleBooking(input) {
1993
+ return safe(
1994
+ this.client.post("/api/v1/scheduling/bookings/reschedule", input)
1995
+ );
1996
+ }
1997
+ async getNextAvailableSlot(serviceId, fromDate) {
1998
+ const date = fromDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1999
+ const result = await this.getAvailableSlots({
2000
+ service_id: serviceId,
2001
+ date
2002
+ });
2003
+ if (!result.ok) return result;
2004
+ return ok(result.value.find((slot) => slot.is_available) || null);
2005
+ }
2006
+ async hasAvailabilityOn(serviceId, date) {
2007
+ const result = await this.getAvailableSlots({
2008
+ service_id: serviceId,
2009
+ date
2010
+ });
2011
+ if (!result.ok) return result;
2012
+ return ok(result.value.some((slot) => slot.is_available));
2013
+ }
2014
+ };
2015
+
2016
+ // src/lite.ts
2017
+ var LiteService = class {
2018
+ constructor(client) {
2019
+ this.client = client;
2020
+ }
2021
+ async getBootstrap() {
2022
+ return safe(this.client.get("/api/v1/lite/bootstrap"));
2023
+ }
2024
+ async getTable(tableId) {
2025
+ const encodedId = encodeURIComponent(tableId);
2026
+ return safe(this.client.get(`/api/v1/lite/tables/${encodedId}`));
2027
+ }
2028
+ async getMenu() {
2029
+ return safe(this.client.get("/api/v1/lite/menu"));
2030
+ }
2031
+ async getMenuByCategory(categoryId) {
2032
+ const encodedId = encodeURIComponent(categoryId);
2033
+ return safe(this.client.get(`/api/v1/lite/menu/categories/${encodedId}`));
2034
+ }
2035
+ };
2036
+
2037
+ // src/fx.ts
2038
+ var FxService = class {
2039
+ constructor(client) {
2040
+ this.client = client;
2041
+ }
2042
+ async getRate(from, to) {
2043
+ const path = `/api/v1/fx/rate?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`;
2044
+ return safe(this.client.get(path));
2045
+ }
2046
+ async lockQuote(request) {
2047
+ return safe(this.client.post("/api/v1/fx/quotes", request));
2048
+ }
2049
+ };
2050
+
2051
+ // src/types/elements.ts
2052
+ var ELEMENT_TYPES = {
2053
+ AUTH: "auth",
2054
+ ADDRESS: "address",
2055
+ PAYMENT: "payment",
2056
+ CHECKOUT: "checkout"
2057
+ };
2058
+ var MESSAGE_TYPES = {
2059
+ INIT: "init",
2060
+ SET_TOKEN: "set_token",
2061
+ SET_CART: "set_cart",
2062
+ GET_DATA: "get_data",
2063
+ REFRESH_TOKEN: "refresh_token",
2064
+ LOGOUT: "logout",
2065
+ PROCESS_CHECKOUT: "process_checkout",
2066
+ ABORT_CHECKOUT: "abort_checkout",
2067
+ READY: "ready",
2068
+ HEIGHT_CHANGE: "height_change",
2069
+ AUTHENTICATED: "authenticated",
2070
+ REQUIRES_OTP: "requires_otp",
2071
+ ERROR: "error",
2072
+ ADDRESS_CHANGED: "address_changed",
2073
+ ADDRESS_SELECTED: "address_selected",
2074
+ PAYMENT_METHOD_SELECTED: "payment_method_selected",
2075
+ TOKEN_REFRESHED: "token_refreshed",
2076
+ LOGOUT_COMPLETE: "logout_complete",
2077
+ CONTACT_PROVIDED: "contact_provided",
2078
+ CHECKOUT_STATUS: "checkout_status",
2079
+ CHECKOUT_COMPLETE: "checkout_complete",
2080
+ ORDER_TYPE_CHANGED: "order_type_changed",
2081
+ REQUEST_SUBMIT: "request_submit"
2082
+ };
2083
+ var EVENT_TYPES = {
2084
+ READY: "ready",
2085
+ AUTHENTICATED: "authenticated",
2086
+ REQUIRES_OTP: "requires_otp",
2087
+ ERROR: "error",
2088
+ CHANGE: "change",
2089
+ BLUR: "blur",
2090
+ FOCUS: "focus",
2091
+ ORDER_TYPE_CHANGED: "order_type_changed",
2092
+ REQUEST_SUBMIT: "request_submit"
2093
+ };
2094
+
2095
+ // src/elements.ts
2096
+ function toCheckoutError(code, message, recoverable) {
2097
+ const hint = getErrorHint(code);
2098
+ return {
2099
+ success: false,
2100
+ error: {
2101
+ code,
2102
+ message,
2103
+ recoverable,
2104
+ docs_url: hint?.docs_url,
2105
+ suggestion: hint?.suggestion
2106
+ }
2107
+ };
2108
+ }
2109
+ var DEFAULT_LINK_URL = "https://link.cimplify.io";
2110
+ function generateNonce() {
2111
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
2112
+ return crypto.randomUUID().replace(/-/g, "").slice(0, 16);
2113
+ }
2114
+ return Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
2115
+ }
2116
+ function isAllowedOrigin(origin) {
2117
+ try {
2118
+ const url = new URL(origin);
2119
+ const hostname = url.hostname;
2120
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
2121
+ return true;
2122
+ }
2123
+ if (url.protocol !== "https:") {
2124
+ return false;
2125
+ }
2126
+ return hostname === "cimplify.io" || hostname.endsWith(".cimplify.io");
2127
+ } catch {
2128
+ return false;
2129
+ }
2130
+ }
2131
+ function parseIframeMessage(data) {
2132
+ if (!data || typeof data !== "object") {
2133
+ return null;
2134
+ }
2135
+ const maybeType = data.type;
2136
+ if (typeof maybeType !== "string") {
2137
+ return null;
2138
+ }
2139
+ return data;
2140
+ }
2141
+ var CimplifyElements = class {
2142
+ constructor(client, businessId, options = {}) {
2143
+ this.elements = /* @__PURE__ */ new Map();
2144
+ this.accessToken = null;
2145
+ this.accountId = null;
2146
+ this.customerId = null;
2147
+ this.customerData = null;
2148
+ this.addressData = null;
2149
+ this.paymentData = null;
2150
+ this.checkoutInProgress = false;
2151
+ this.activeCheckoutAbort = null;
2152
+ this.businessIdResolvePromise = null;
2153
+ this.client = client;
2154
+ this.businessId = businessId ?? null;
2155
+ this.linkUrl = options.linkUrl || DEFAULT_LINK_URL;
2156
+ this.options = options;
2157
+ this.boundHandleMessage = this.handleMessage.bind(this);
2158
+ if (typeof window !== "undefined") {
2159
+ window.addEventListener("message", this.boundHandleMessage);
2160
+ }
2161
+ }
2162
+ debug(...args) {
2163
+ if (this.client.isTestMode()) {
2164
+ console.debug(...args);
2165
+ }
2166
+ }
2167
+ create(type, options = {}) {
2168
+ const existing = this.elements.get(type);
2169
+ if (existing) return existing;
2170
+ const element = new CimplifyElement(type, this.businessId, this.linkUrl, options, this);
2171
+ this.elements.set(type, element);
2172
+ return element;
2173
+ }
2174
+ getElement(type) {
2175
+ return this.elements.get(type);
2176
+ }
2177
+ _removeElement(type, instance) {
2178
+ const current = this.elements.get(type);
2179
+ if (current === instance) {
2180
+ this.elements.delete(type);
2181
+ }
2182
+ }
2183
+ destroy() {
2184
+ this.elements.forEach((element) => element.destroy());
2185
+ this.elements.clear();
2186
+ if (typeof window !== "undefined") {
2187
+ window.removeEventListener("message", this.boundHandleMessage);
2188
+ }
2189
+ }
2190
+ async submitCheckout(data) {
2191
+ const result = await this.processCheckout({
2192
+ cart_id: data.cart_id,
2193
+ order_type: data.order_type ?? "delivery",
2194
+ location_id: data.location_id,
2195
+ notes: data.notes,
2196
+ scheduled_time: data.scheduled_time,
2197
+ tip_amount: data.tip_amount,
2198
+ enroll_in_link: true
2199
+ });
2200
+ if (result.success) {
2201
+ return {
2202
+ success: true,
2203
+ order: {
2204
+ id: result.order?.id || "",
2205
+ status: result.order?.status || "unknown",
2206
+ total: result.order?.total || ""
2207
+ }
2208
+ };
2209
+ }
2210
+ return {
2211
+ success: false,
2212
+ error: {
2213
+ code: result.error?.code || "CHECKOUT_FAILED",
2214
+ message: result.error?.message || "Checkout failed"
2215
+ }
2216
+ };
2217
+ }
2218
+ processCheckout(options) {
2219
+ let abortFn = null;
2220
+ const task = (async () => {
2221
+ this.debug("[cimplify:checkout] processCheckout called", { cart_id: options.cart_id, order_type: options.order_type, location_id: options.location_id });
2222
+ if (this.checkoutInProgress) {
2223
+ this.debug("[cimplify:checkout] BLOCKED: already processing");
2224
+ return toCheckoutError(
2225
+ "ALREADY_PROCESSING",
2226
+ "Checkout is already in progress.",
2227
+ false
2228
+ );
2229
+ }
2230
+ this.checkoutInProgress = true;
2231
+ if (!options.cart_id) {
2232
+ this.debug("[cimplify:checkout] BLOCKED: no cart_id");
2233
+ this.checkoutInProgress = false;
2234
+ return toCheckoutError(
2235
+ "INVALID_CART",
2236
+ "A valid cart is required before checkout can start.",
2237
+ false
2238
+ );
2239
+ }
2240
+ if (!options.order_type) {
2241
+ this.debug("[cimplify:checkout] BLOCKED: no order_type");
2242
+ this.checkoutInProgress = false;
2243
+ return toCheckoutError(
2244
+ "ORDER_TYPE_REQUIRED",
2245
+ "Order type is required before checkout can start.",
2246
+ false
2247
+ );
2248
+ }
2249
+ const checkoutElement = this.elements.get(ELEMENT_TYPES.CHECKOUT) || this.elements.get(ELEMENT_TYPES.PAYMENT);
2250
+ if (!checkoutElement) {
2251
+ this.debug("[cimplify:checkout] BLOCKED: no checkout element");
2252
+ this.checkoutInProgress = false;
2253
+ return toCheckoutError(
2254
+ "NO_PAYMENT_ELEMENT",
2255
+ "Checkout element must be mounted before checkout.",
2256
+ false
2257
+ );
2258
+ }
2259
+ if (!checkoutElement.isMounted()) {
2260
+ this.debug("[cimplify:checkout] BLOCKED: checkout element not mounted");
2261
+ this.checkoutInProgress = false;
2262
+ return toCheckoutError(
2263
+ "PAYMENT_NOT_MOUNTED",
2264
+ "Checkout element must be mounted before checkout.",
2265
+ false
2266
+ );
2267
+ }
2268
+ const isUnifiedCheckout = this.elements.has(ELEMENT_TYPES.CHECKOUT);
2269
+ if (!isUnifiedCheckout) {
2270
+ const authElement = this.elements.get(ELEMENT_TYPES.AUTH);
2271
+ if (authElement && !this.accessToken) {
2272
+ this.debug("[cimplify:checkout] BLOCKED: auth incomplete");
2273
+ this.checkoutInProgress = false;
2274
+ return toCheckoutError(
2275
+ "AUTH_INCOMPLETE",
2276
+ "Authentication must complete before checkout can start.",
2277
+ true
2278
+ );
2279
+ }
2280
+ const addressElement = this.elements.get(ELEMENT_TYPES.ADDRESS);
2281
+ if (addressElement) {
2282
+ this.debug("[cimplify:checkout] getting address data...");
2283
+ await addressElement.getData();
2284
+ this.debug("[cimplify:checkout] address data resolved", this.addressData);
2285
+ }
2286
+ await this.hydrateCustomerData();
2287
+ }
2288
+ this.debug("[cimplify:checkout] customer data hydrated", { customerData: this.customerData, accountId: this.accountId, isUnifiedCheckout });
2289
+ const processMessage = {
2290
+ type: MESSAGE_TYPES.PROCESS_CHECKOUT,
2291
+ cart_id: options.cart_id,
2292
+ order_type: options.order_type,
2293
+ location_id: options.location_id,
2294
+ notes: options.notes,
2295
+ scheduled_time: options.scheduled_time,
2296
+ tip_amount: options.tip_amount,
2297
+ pay_currency: options.pay_currency,
2298
+ enroll_in_link: options.enroll_in_link ?? true,
2299
+ address: isUnifiedCheckout ? void 0 : this.addressData ?? void 0,
2300
+ customer: isUnifiedCheckout ? void 0 : this.customerData ? {
2301
+ name: this.customerData.name,
2302
+ email: this.customerData.email,
2303
+ phone: this.customerData.phone
2304
+ } : null,
2305
+ account_id: this.accountId ?? void 0,
2306
+ customer_id: this.customerId ?? void 0
2307
+ };
2308
+ const timeoutMs = options.timeout_ms ?? 18e4;
2309
+ const paymentWindow = checkoutElement.getContentWindow();
2310
+ return new Promise((resolve) => {
2311
+ let settled = false;
2312
+ const cleanup = () => {
2313
+ if (typeof window !== "undefined") {
2314
+ window.removeEventListener("message", handleCheckoutMessage);
2315
+ }
2316
+ clearTimeout(timeout);
2317
+ this.checkoutInProgress = false;
2318
+ this.activeCheckoutAbort = null;
2319
+ };
2320
+ const settle = (result) => {
2321
+ if (settled) {
2322
+ return;
2323
+ }
2324
+ settled = true;
2325
+ cleanup();
2326
+ resolve(result);
2327
+ };
2328
+ const timeout = setTimeout(() => {
2329
+ settle(
2330
+ toCheckoutError(
2331
+ "TIMEOUT",
2332
+ "Checkout timed out before receiving a terminal response.",
2333
+ true
2334
+ )
2335
+ );
2336
+ }, timeoutMs);
2337
+ const handleCheckoutMessage = (event) => {
2338
+ if (!isAllowedOrigin(event.origin)) {
2339
+ return;
2340
+ }
2341
+ const message = parseIframeMessage(event.data);
2342
+ if (!message) {
2343
+ return;
2344
+ }
2345
+ if (message.type === MESSAGE_TYPES.LOGOUT_COMPLETE) {
2346
+ checkoutElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
2347
+ settle(
2348
+ toCheckoutError(
2349
+ "AUTH_LOST",
2350
+ "Authentication was cleared during checkout.",
2351
+ true
2352
+ )
2353
+ );
2354
+ return;
2355
+ }
2356
+ if (paymentWindow && event.source && event.source !== paymentWindow) {
2357
+ return;
2358
+ }
2359
+ if (message.type === MESSAGE_TYPES.CHECKOUT_STATUS) {
2360
+ options.on_status_change?.(message.status, message.context);
2361
+ return;
2362
+ }
2363
+ if (message.type === MESSAGE_TYPES.CHECKOUT_COMPLETE) {
2364
+ this.debug("[cimplify:checkout] received checkout_complete", { success: message.success, error: message.error });
2365
+ settle({
2366
+ success: message.success,
2367
+ order: message.order,
2368
+ error: message.error,
2369
+ enrolled_in_link: message.enrolled_in_link
2370
+ });
2371
+ }
2372
+ };
2373
+ if (typeof window !== "undefined") {
2374
+ window.addEventListener("message", handleCheckoutMessage);
2375
+ }
2376
+ abortFn = () => {
2377
+ checkoutElement.sendMessage({ type: MESSAGE_TYPES.ABORT_CHECKOUT });
2378
+ settle(
2379
+ toCheckoutError(
2380
+ "CANCELLED",
2381
+ "Checkout was cancelled.",
2382
+ true
2383
+ )
2384
+ );
2385
+ };
2386
+ this.activeCheckoutAbort = abortFn;
2387
+ 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 });
2388
+ checkoutElement.sendMessage(processMessage);
2389
+ this.debug("[cimplify:checkout] postMessage sent, waiting for checkout_complete...");
2390
+ });
2391
+ })();
2392
+ const abortable = task;
2393
+ abortable.abort = () => {
2394
+ if (abortFn) {
2395
+ abortFn();
2396
+ }
2397
+ };
2398
+ return abortable;
2399
+ }
2400
+ isAuthenticated() {
2401
+ return this.accessToken !== null;
2402
+ }
2403
+ getAccessToken() {
2404
+ return this.accessToken;
2405
+ }
2406
+ getPublicKey() {
2407
+ return this.client.getPublicKey();
2408
+ }
2409
+ getBusinessId() {
2410
+ return this.businessId;
2411
+ }
2412
+ async resolveBusinessId() {
2413
+ if (this.businessId) {
2414
+ return this.businessId;
2415
+ }
2416
+ if (this.businessIdResolvePromise) {
2417
+ return this.businessIdResolvePromise;
2418
+ }
2419
+ this.businessIdResolvePromise = this.client.resolveBusinessId().then((resolvedBusinessId) => {
2420
+ this.businessId = resolvedBusinessId;
2421
+ return resolvedBusinessId;
2422
+ }).catch(() => null).finally(() => {
2423
+ this.businessIdResolvePromise = null;
2424
+ });
2425
+ return this.businessIdResolvePromise;
2426
+ }
2427
+ getAppearance() {
2428
+ return this.options.appearance;
2429
+ }
2430
+ async hydrateCustomerData() {
2431
+ if (!this.accessToken || this.customerData) {
2432
+ return;
2433
+ }
2434
+ if (!this.client.getAccessToken()) {
2435
+ this.client.setAccessToken(this.accessToken);
2436
+ }
2437
+ const linkDataResult = await this.client.link.getLinkData();
2438
+ if (!linkDataResult.ok || !linkDataResult.value?.customer) {
2439
+ return;
2440
+ }
2441
+ const customer = linkDataResult.value.customer;
2442
+ this.customerData = {
2443
+ name: customer.name || "",
2444
+ email: customer.email || null,
2445
+ phone: customer.phone || null
2446
+ };
2447
+ }
2448
+ handleMessage(event) {
2449
+ if (!isAllowedOrigin(event.origin)) {
2450
+ return;
2451
+ }
2452
+ const message = parseIframeMessage(event.data);
2453
+ if (!message) return;
2454
+ switch (message.type) {
2455
+ case MESSAGE_TYPES.AUTHENTICATED:
2456
+ const customer = message.customer ?? {
2457
+ name: "",
2458
+ email: null,
2459
+ phone: null
2460
+ };
2461
+ this.accessToken = message.token;
2462
+ this.accountId = message.accountId;
2463
+ this.customerId = message.customerId;
2464
+ this.customerData = customer;
2465
+ this.client.setAccessToken(message.token);
2466
+ this.elements.forEach((element, type) => {
2467
+ if (type !== ELEMENT_TYPES.AUTH && type !== ELEMENT_TYPES.CHECKOUT) {
2468
+ element.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token: message.token });
2469
+ }
2470
+ });
2471
+ break;
2472
+ case MESSAGE_TYPES.TOKEN_REFRESHED:
2473
+ this.accessToken = message.token;
2474
+ break;
2475
+ case MESSAGE_TYPES.ADDRESS_CHANGED:
2476
+ case MESSAGE_TYPES.ADDRESS_SELECTED:
2477
+ this.addressData = message.address;
2478
+ break;
2479
+ case MESSAGE_TYPES.PAYMENT_METHOD_SELECTED:
2480
+ this.paymentData = message.method;
2481
+ break;
2482
+ case MESSAGE_TYPES.CONTACT_PROVIDED:
2483
+ if (!this.customerData) {
2484
+ this.customerData = { name: "", email: null, phone: null };
2485
+ }
2486
+ if (message.contactType === "email") {
2487
+ this.customerData.email = message.contact;
2488
+ } else {
2489
+ this.customerData.phone = message.contact;
2490
+ }
2491
+ break;
2492
+ case MESSAGE_TYPES.LOGOUT_COMPLETE:
2493
+ if (this.checkoutInProgress && this.activeCheckoutAbort) {
2494
+ this.activeCheckoutAbort();
2495
+ }
2496
+ this.accessToken = null;
2497
+ this.accountId = null;
2498
+ this.customerId = null;
2499
+ this.customerData = null;
2500
+ this.addressData = null;
2501
+ this.paymentData = null;
2502
+ this.client.clearSession();
2503
+ break;
2504
+ }
2505
+ }
2506
+ _setAddressData(data) {
2507
+ this.addressData = data;
2508
+ }
2509
+ _setPaymentData(data) {
2510
+ this.paymentData = data;
2511
+ }
2512
+ _setGuestContact(contact, contactType) {
2513
+ if (!this.customerData) {
2514
+ this.customerData = { name: "", email: null, phone: null };
2515
+ }
2516
+ if (contactType === "email") {
2517
+ this.customerData.email = contact;
2518
+ } else {
2519
+ this.customerData.phone = contact;
2520
+ }
2521
+ }
2522
+ };
2523
+ var CimplifyElement = class {
2524
+ constructor(type, businessId, linkUrl, options, parent) {
2525
+ this.iframe = null;
2526
+ this.container = null;
2527
+ this.mounted = false;
2528
+ this.pendingInit = null;
2529
+ this.eventHandlers = /* @__PURE__ */ new Map();
2530
+ this.resolvers = /* @__PURE__ */ new Map();
2531
+ this.listening = false;
2532
+ this.type = type;
2533
+ this.businessId = businessId;
2534
+ this.linkUrl = linkUrl;
2535
+ this.options = options;
2536
+ this.parent = parent;
2537
+ this.nonce = generateNonce();
2538
+ this.boundHandleMessage = this.handleMessage.bind(this);
2539
+ if (typeof window !== "undefined") {
2540
+ window.addEventListener("message", this.boundHandleMessage);
2541
+ this.listening = true;
2542
+ }
2543
+ }
2544
+ mount(container) {
2545
+ if (this.mounted) {
2546
+ console.warn(`Element ${this.type} is already mounted`);
2547
+ return;
2548
+ }
2549
+ const target = typeof container === "string" ? document.querySelector(container) : container;
2550
+ if (!target) {
2551
+ console.error(`Container not found: ${container}`);
2552
+ return;
2553
+ }
2554
+ this.container = target;
2555
+ this.mounted = true;
2556
+ if (!this.listening && typeof window !== "undefined") {
2557
+ window.addEventListener("message", this.boundHandleMessage);
2558
+ this.listening = true;
2559
+ }
2560
+ void this.createIframe();
2561
+ }
2562
+ destroy() {
2563
+ if (this.iframe) {
2564
+ this.iframe.remove();
2565
+ this.iframe = null;
2566
+ }
2567
+ this.container = null;
2568
+ this.mounted = false;
2569
+ this.eventHandlers.clear();
2570
+ this.resolvers.forEach((entry) => clearTimeout(entry.timeoutId));
2571
+ this.resolvers.clear();
2572
+ if (this.listening && typeof window !== "undefined") {
2573
+ window.removeEventListener("message", this.boundHandleMessage);
2574
+ this.listening = false;
2575
+ }
2576
+ this.parent._removeElement(this.type, this);
2577
+ }
2578
+ on(event, handler) {
2579
+ if (!this.eventHandlers.has(event)) {
2580
+ this.eventHandlers.set(event, /* @__PURE__ */ new Set());
2581
+ }
2582
+ this.eventHandlers.get(event).add(handler);
2583
+ }
2584
+ off(event, handler) {
2585
+ this.eventHandlers.get(event)?.delete(handler);
2586
+ }
2587
+ async getData() {
2588
+ if (!this.isMounted()) {
2589
+ return null;
2590
+ }
2591
+ return new Promise((resolve) => {
2592
+ const id = Math.random().toString(36).slice(2);
2593
+ const timeoutId = setTimeout(() => {
2594
+ const entry = this.resolvers.get(id);
2595
+ if (!entry) {
2596
+ return;
2597
+ }
2598
+ this.resolvers.delete(id);
2599
+ entry.resolve(null);
2600
+ }, 5e3);
2601
+ this.resolvers.set(id, { resolve, timeoutId });
2602
+ this.sendMessage({ type: MESSAGE_TYPES.GET_DATA });
2603
+ });
2604
+ }
2605
+ setCart(cart) {
2606
+ this.sendMessage({ type: MESSAGE_TYPES.SET_CART, cart });
2607
+ }
2608
+ sendMessage(message) {
2609
+ if (this.iframe?.contentWindow) {
2610
+ this.iframe.contentWindow.postMessage(message, this.linkUrl);
2611
+ } else {
2612
+ console.warn("[cimplify:checkout] sendMessage DROPPED \u2014 no contentWindow", { type: message.type, hasIframe: !!this.iframe, isConnected: this.iframe?.isConnected });
2613
+ }
2614
+ }
2615
+ getContentWindow() {
2616
+ return this.iframe?.contentWindow ?? null;
2617
+ }
2618
+ isMounted() {
2619
+ return this.mounted && Boolean(this.iframe) && this.iframe?.isConnected === true;
2620
+ }
2621
+ async createIframe() {
2622
+ if (!this.container) return;
2623
+ const resolvedBusinessId = this.businessId ?? await this.parent.resolveBusinessId();
2624
+ if (!resolvedBusinessId || !this.container || !this.mounted) {
2625
+ console.error("Unable to mount element without a resolved business ID");
2626
+ this.emit(EVENT_TYPES.ERROR, {
2627
+ code: "BUSINESS_ID_REQUIRED",
2628
+ message: "Unable to initialize checkout without a business ID."
2629
+ });
2630
+ return;
2631
+ }
2632
+ this.businessId = resolvedBusinessId;
2633
+ const iframe = document.createElement("iframe");
2634
+ const url = new URL(`${this.linkUrl}/elements/${this.type}`);
2635
+ url.searchParams.set("businessId", resolvedBusinessId);
2636
+ url.searchParams.set("nonce", this.nonce);
2637
+ if (this.options.prefillEmail) url.searchParams.set("email", this.options.prefillEmail);
2638
+ if (this.options.mode) url.searchParams.set("mode", this.options.mode);
2639
+ iframe.src = url.toString();
2640
+ iframe.style.border = "none";
2641
+ iframe.style.width = "100%";
2642
+ iframe.style.minHeight = "200px";
2643
+ iframe.style.display = "block";
2644
+ iframe.style.overflow = "hidden";
2645
+ iframe.style.background = "transparent";
2646
+ iframe.setAttribute("allowtransparency", "true");
2647
+ iframe.setAttribute("frameborder", "0");
2648
+ iframe.setAttribute("allow", "geolocation");
2649
+ iframe.setAttribute(
2650
+ "sandbox",
2651
+ "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
2652
+ );
2653
+ this.iframe = iframe;
2654
+ this.container.appendChild(iframe);
2655
+ this.pendingInit = {
2656
+ businessId: resolvedBusinessId,
2657
+ publicKey: this.parent.getPublicKey()
2658
+ };
2659
+ }
2660
+ handleMessage(event) {
2661
+ if (!isAllowedOrigin(event.origin)) {
2662
+ return;
2663
+ }
2664
+ const data = event.data;
2665
+ if (data?.nonce) {
2666
+ if (data.nonce !== this.nonce) return;
2667
+ } else {
2668
+ const iframeWindow = this.iframe?.contentWindow;
2669
+ if (iframeWindow && event.source && event.source !== iframeWindow) {
2670
+ return;
2671
+ }
2672
+ }
2673
+ const message = parseIframeMessage(event.data);
2674
+ if (!message) return;
2675
+ switch (message.type) {
2676
+ case MESSAGE_TYPES.READY:
2677
+ if (this.iframe && message.height) this.iframe.style.height = `${message.height}px`;
2678
+ if (this.pendingInit) {
2679
+ const { publicKey } = this.pendingInit;
2680
+ this.sendMessage({
2681
+ type: MESSAGE_TYPES.INIT,
2682
+ businessId: this.pendingInit.businessId,
2683
+ publicKey,
2684
+ demoMode: publicKey.length === 0,
2685
+ prefillEmail: this.options.prefillEmail,
2686
+ appearance: this.parent.getAppearance(),
2687
+ orderTypes: this.options.orderTypes,
2688
+ defaultOrderType: this.options.defaultOrderType,
2689
+ renderSubmitButton: true,
2690
+ submitLabel: this.options.submitLabel
2691
+ });
2692
+ const token = this.parent.getAccessToken();
2693
+ if (token && this.type !== ELEMENT_TYPES.AUTH) {
2694
+ this.sendMessage({ type: MESSAGE_TYPES.SET_TOKEN, token });
2695
+ }
2696
+ this.pendingInit = null;
2697
+ }
2698
+ this.emit(EVENT_TYPES.READY, { height: message.height });
2699
+ break;
2700
+ case MESSAGE_TYPES.HEIGHT_CHANGE:
2701
+ if (this.iframe) this.iframe.style.height = `${message.height}px`;
2702
+ break;
2703
+ case MESSAGE_TYPES.AUTHENTICATED:
2704
+ const customer = message.customer ?? {
2705
+ name: "",
2706
+ email: null,
2707
+ phone: null
2708
+ };
2709
+ this.emit(EVENT_TYPES.AUTHENTICATED, {
2710
+ accountId: message.accountId,
2711
+ customerId: message.customerId,
2712
+ token: message.token,
2713
+ customer
2714
+ });
2715
+ break;
2716
+ case MESSAGE_TYPES.REQUIRES_OTP:
2717
+ this.emit(EVENT_TYPES.REQUIRES_OTP, { contactMasked: message.contactMasked });
2718
+ break;
2719
+ case MESSAGE_TYPES.ERROR:
2720
+ this.emit(EVENT_TYPES.ERROR, { code: message.code, message: message.message });
2721
+ break;
2722
+ case MESSAGE_TYPES.ADDRESS_CHANGED:
2723
+ case MESSAGE_TYPES.ADDRESS_SELECTED:
2724
+ this.parent._setAddressData(message.address);
2725
+ this.emit(EVENT_TYPES.CHANGE, { address: message.address });
2726
+ this.resolveData(message.address);
2727
+ break;
2728
+ case MESSAGE_TYPES.PAYMENT_METHOD_SELECTED:
2729
+ this.parent._setPaymentData(message.method);
2730
+ this.emit(EVENT_TYPES.CHANGE, { paymentMethod: message.method });
2731
+ this.resolveData(message.method);
2732
+ break;
2733
+ case MESSAGE_TYPES.CONTACT_PROVIDED:
2734
+ this.parent._setGuestContact(message.contact, message.contactType);
2735
+ break;
2736
+ case MESSAGE_TYPES.ORDER_TYPE_CHANGED:
2737
+ this.emit(EVENT_TYPES.ORDER_TYPE_CHANGED, { orderType: message.orderType });
2738
+ break;
2739
+ case MESSAGE_TYPES.REQUEST_SUBMIT:
2740
+ this.emit(EVENT_TYPES.REQUEST_SUBMIT, {});
2741
+ break;
2742
+ }
2743
+ }
2744
+ emit(event, data) {
2745
+ this.eventHandlers.get(event)?.forEach((handler) => handler(data));
2746
+ }
2747
+ resolveData(data) {
2748
+ this.resolvers.forEach((entry) => {
2749
+ clearTimeout(entry.timeoutId);
2750
+ entry.resolve(data);
2751
+ });
2752
+ this.resolvers.clear();
2753
+ }
2754
+ };
2755
+ function createElements(client, businessId, options) {
2756
+ return new CimplifyElements(client, businessId, options);
2757
+ }
2758
+
2759
+ export { AUTHORIZATION_TYPE, AUTH_MUTATION, AuthService, BusinessService, CHECKOUT_MODE, CHECKOUT_MUTATION, CHECKOUT_STEP, CONTACT_TYPE, CartOperations, CatalogueQueries, CheckoutService, CimplifyElement, CimplifyElements, DEFAULT_COUNTRY, DEFAULT_CURRENCY, DEVICE_TYPE, ELEMENT_TYPES, EVENT_TYPES, FxService, InventoryService, LINK_MUTATION, LINK_QUERY, LinkService, LiteService, MESSAGE_TYPES, MOBILE_MONEY_PROVIDER, ORDER_MUTATION, ORDER_TYPE, OrderQueries, PAYMENT_METHOD, PAYMENT_MUTATION, PAYMENT_STATE, PICKUP_TIME_TYPE, SchedulingService, combine, combineObject, createElements, err, flatMap, fromPromise, generateIdempotencyKey, getOrElse, isErr, isOk, mapError, mapResult, ok, toNullable, tryCatch, unwrap };