@cimplify/sdk 0.104.0 → 0.105.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 (71) hide show
  1. package/dist/advanced.d.mts +3 -7
  2. package/dist/advanced.d.ts +3 -7
  3. package/dist/advanced.js +23 -23
  4. package/dist/advanced.mjs +4 -4
  5. package/dist/{client-BuLcOWLv.d.ts → catalogue-D1jkpGcr.d.ts} +415 -763
  6. package/dist/{client-D5AoobFc.d.mts → catalogue-DjutZ0FC.d.mts} +415 -763
  7. package/dist/{payment-g0HFkEDU.d.mts → checkout-XoV4HNg2.d.mts} +4156 -1498
  8. package/dist/{payment-g0HFkEDU.d.ts → checkout-XoV4HNg2.d.ts} +4156 -1498
  9. package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
  10. package/dist/{chunk-OKADBZAI.mjs → chunk-37ZZCE7P.mjs} +808 -574
  11. package/dist/{chunk-5BDYDZ4U.js → chunk-3IMPJEUI.js} +892 -657
  12. package/dist/{chunk-YVAEY3VC.js → chunk-7PMMV5X4.js} +1040 -364
  13. package/dist/{chunk-H3RATH4B.mjs → chunk-AVXA4F6G.js} +293 -2
  14. package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
  15. package/dist/{chunk-4BKOKHE3.js → chunk-NJYR6GGM.mjs} +244 -19
  16. package/dist/{chunk-DYIAX3WI.mjs → chunk-NYDVVNZI.mjs} +11 -2
  17. package/dist/{chunk-E4K2VQDD.js → chunk-PDTXSUHZ.js} +559 -16
  18. package/dist/{chunk-2WPVUIHX.mjs → chunk-Q5HX7LVE.mjs} +1036 -360
  19. package/dist/{chunk-5RT4HYMP.mjs → chunk-TINYV5E2.mjs} +542 -15
  20. package/dist/{chunk-7PDEX54K.js → chunk-UYFTOIFW.js} +13 -3
  21. package/dist/{chunk-OCCNG6MO.mjs → chunk-X7RPMALQ.mjs} +1310 -722
  22. package/dist/{chunk-IQFFSKE2.js → chunk-XHVQDEVO.js} +1345 -746
  23. package/dist/{client-BVNWjk6W.d.ts → client-BBZj15JJ.d.ts} +2 -2
  24. package/dist/{client-fM716k7G.d.mts → client-CZ8MjJ_o.d.mts} +2 -2
  25. package/dist/index-CZjNWbQf.d.ts +8 -0
  26. package/dist/index-DNxWfRHF.d.mts +8 -0
  27. package/dist/{index-BtcoqrY1.d.ts → index-DRMBo26v.d.mts} +9 -4
  28. package/dist/{index-D7Tf3v7c.d.mts → index-DweR60Cv.d.ts} +9 -4
  29. package/dist/index.d.mts +31 -14
  30. package/dist/index.d.ts +31 -14
  31. package/dist/index.js +325 -133
  32. package/dist/index.mjs +6 -6
  33. package/dist/mock/cli.mjs +1450 -386
  34. package/dist/mock/library.js +1176 -352
  35. package/dist/mock/library.mjs +1176 -352
  36. package/dist/mock/msw.js +1176 -352
  37. package/dist/mock/msw.mjs +1176 -352
  38. package/dist/{price-BLIeAb8K.d.ts → price-CUGIYRtD.d.mts} +1 -1
  39. package/dist/{price-Czq_cTC_.d.mts → price-De-sxcuj.d.ts} +1 -1
  40. package/dist/react.d.mts +10 -5
  41. package/dist/react.d.ts +10 -5
  42. package/dist/react.js +227 -197
  43. package/dist/react.mjs +90 -60
  44. package/dist/{server-DKK_D-PP.d.ts → server-Byr4d0hL.d.mts} +31 -57
  45. package/dist/{server-BkVhOIwn.d.mts → server-C_RIDnHU.d.ts} +31 -57
  46. package/dist/server.d.mts +3 -4
  47. package/dist/server.d.ts +3 -4
  48. package/dist/server.js +11 -11
  49. package/dist/server.mjs +5 -5
  50. package/dist/styles.css +2 -2
  51. package/dist/testing/msw.d.mts +2 -2
  52. package/dist/testing/msw.d.ts +2 -2
  53. package/dist/testing/msw.js +4 -4
  54. package/dist/testing/msw.mjs +3 -3
  55. package/dist/testing/suite.d.mts +5 -6
  56. package/dist/testing/suite.d.ts +5 -6
  57. package/dist/testing/suite.js +29 -28
  58. package/dist/testing/suite.mjs +10 -9
  59. package/dist/testing.d.mts +2478 -14
  60. package/dist/testing.d.ts +2478 -14
  61. package/dist/testing.js +167 -89
  62. package/dist/testing.mjs +24 -10
  63. package/dist/utils.d.mts +3 -4
  64. package/dist/utils.d.ts +3 -4
  65. package/dist/utils.js +35 -31
  66. package/dist/utils.mjs +2 -2
  67. package/package.json +9 -9
  68. package/registry/order-history.json +1 -1
  69. package/registry/order-summary.json +1 -1
  70. package/dist/link-D9iSVxt5.d.mts +0 -308
  71. package/dist/link-D9iSVxt5.d.ts +0 -308
package/dist/mock/msw.mjs CHANGED
@@ -1,4 +1,4 @@
1
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/compose.js
1
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/compose.js
2
2
  var compose = (middleware, onError, onNotFound) => {
3
3
  return (context, next) => {
4
4
  let index = -1;
@@ -42,21 +42,46 @@ var compose = (middleware, onError, onNotFound) => {
42
42
  };
43
43
  };
44
44
 
45
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/request/constants.js
45
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/request/constants.js
46
46
  var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
47
47
 
48
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/body.js
48
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/buffer.js
49
+ var bufferToFormData = (arrayBuffer, contentType) => {
50
+ const response = new Response(arrayBuffer, {
51
+ headers: {
52
+ // Normalize the media type (case-insensitive) while keeping parameters like the boundary
53
+ "Content-Type": contentType.replace(/^[^;]+/, (mediaType) => mediaType.toLowerCase())
54
+ }
55
+ });
56
+ return response.formData();
57
+ };
58
+
59
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/body.js
60
+ var isRawRequest = (request) => "headers" in request;
49
61
  var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
50
62
  const { all = false, dot = false } = options;
51
- const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
63
+ const headers = isRawRequest(request) ? request.headers : request.raw.headers;
52
64
  const contentType = headers.get("Content-Type");
53
- if (contentType?.startsWith("multipart/form-data") || contentType?.startsWith("application/x-www-form-urlencoded")) {
65
+ const mediaType = contentType?.split(";")[0].trim().toLowerCase();
66
+ if (mediaType === "multipart/form-data" || mediaType === "application/x-www-form-urlencoded") {
54
67
  return parseFormData(request, { all, dot });
55
68
  }
56
69
  return {};
57
70
  };
58
71
  async function parseFormData(request, options) {
59
- const formData = await request.formData();
72
+ if (!isRawRequest(request) && request.bodyCache.formData) {
73
+ return convertFormDataToBodyData(
74
+ await request.bodyCache.formData,
75
+ options
76
+ );
77
+ }
78
+ const headers = isRawRequest(request) ? request.headers : request.raw.headers;
79
+ const arrayBuffer = await request.arrayBuffer();
80
+ const formDataPromise = bufferToFormData(arrayBuffer, headers.get("Content-Type") || "");
81
+ if (!isRawRequest(request)) {
82
+ request.bodyCache.formData = formDataPromise;
83
+ }
84
+ const formData = await formDataPromise;
60
85
  if (formData) {
61
86
  return convertFormDataToBodyData(formData, options);
62
87
  }
@@ -116,7 +141,7 @@ var handleParsingNestedValues = (form, key, value) => {
116
141
  });
117
142
  };
118
143
 
119
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/url.js
144
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/url.js
120
145
  var splitPath = (path) => {
121
146
  const paths = path.split("/");
122
147
  if (paths[0] === "") {
@@ -222,13 +247,13 @@ var checkOptionalParameter = (path) => {
222
247
  if (segment !== "" && !/\:/.test(segment)) {
223
248
  basePath += "/" + segment;
224
249
  } else if (/\:/.test(segment)) {
225
- if (/\?/.test(segment)) {
250
+ if (segment.charCodeAt(segment.length - 1) === 63) {
226
251
  if (results.length === 0 && basePath === "") {
227
252
  results.push("/");
228
253
  } else {
229
254
  results.push(basePath);
230
255
  }
231
- const optionalSegment = segment.replace("?", "");
256
+ const optionalSegment = segment.slice(0, -1);
232
257
  basePath += "/" + optionalSegment;
233
258
  results.push(basePath);
234
259
  } else {
@@ -238,18 +263,16 @@ var checkOptionalParameter = (path) => {
238
263
  });
239
264
  return results.filter((v, i, a) => a.indexOf(v) === i);
240
265
  };
266
+ var tryDecodeURIComponent = (str) => str.indexOf("%") !== -1 ? tryDecode(str, decodeURIComponent_) : str;
241
267
  var _decodeURI = (value) => {
242
- if (!/[%+]/.test(value)) {
243
- return value;
244
- }
245
268
  if (value.indexOf("+") !== -1) {
246
269
  value = value.replace(/\+/g, " ");
247
270
  }
248
- return value.indexOf("%") !== -1 ? tryDecode(value, decodeURIComponent_) : value;
271
+ return tryDecodeURIComponent(value);
249
272
  };
250
273
  var _getQueryParam = (url, key, multiple) => {
251
274
  let encoded;
252
- if (!multiple && key && !/[%+]/.test(key)) {
275
+ if (!multiple && key && key.indexOf("%") === -1 && key.indexOf("+") === -1) {
253
276
  let keyIndex2 = url.indexOf("?", 8);
254
277
  if (keyIndex2 === -1) {
255
278
  return void 0;
@@ -273,7 +296,7 @@ var _getQueryParam = (url, key, multiple) => {
273
296
  return void 0;
274
297
  }
275
298
  }
276
- const results = {};
299
+ const results = /* @__PURE__ */ Object.create(null);
277
300
  encoded ??= /[%+]/.test(url);
278
301
  let keyIndex = url.indexOf("?", 8);
279
302
  while (keyIndex !== -1) {
@@ -319,8 +342,7 @@ var getQueryParams = (url, key) => {
319
342
  };
320
343
  var decodeURIComponent_ = decodeURIComponent;
321
344
 
322
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/request.js
323
- var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
345
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/request.js
324
346
  var HonoRequest = class {
325
347
  /**
326
348
  * `.raw` can get the raw Request object.
@@ -359,7 +381,6 @@ var HonoRequest = class {
359
381
  this.raw = request;
360
382
  this.path = path;
361
383
  this.#matchResult = matchResult;
362
- this.#validatedData = {};
363
384
  }
364
385
  param(key) {
365
386
  return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
@@ -367,7 +388,7 @@ var HonoRequest = class {
367
388
  #getDecodedParam(key) {
368
389
  const paramKey = this.#matchResult[0][this.routeIndex][1][key];
369
390
  const param = this.#getParamValue(paramKey);
370
- return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
391
+ return param && tryDecodeURIComponent(param);
371
392
  }
372
393
  #getAllDecodedParams() {
373
394
  const decoded = {};
@@ -375,7 +396,7 @@ var HonoRequest = class {
375
396
  for (const key of keys) {
376
397
  const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
377
398
  if (value !== void 0) {
378
- decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
399
+ decoded[key] = tryDecodeURIComponent(value);
379
400
  }
380
401
  }
381
402
  return decoded;
@@ -393,7 +414,7 @@ var HonoRequest = class {
393
414
  if (name) {
394
415
  return this.raw.headers.get(name) ?? void 0;
395
416
  }
396
- const headerData = {};
417
+ const headerData = /* @__PURE__ */ Object.create(null);
397
418
  this.raw.headers.forEach((value, key) => {
398
419
  headerData[key] = value;
399
420
  });
@@ -408,8 +429,7 @@ var HonoRequest = class {
408
429
  if (cachedBody) {
409
430
  return cachedBody;
410
431
  }
411
- const anyCachedKey = Object.keys(bodyCache)[0];
412
- if (anyCachedKey) {
432
+ for (const anyCachedKey in bodyCache) {
413
433
  return bodyCache[anyCachedKey].then((body) => {
414
434
  if (anyCachedKey === "json") {
415
435
  body = JSON.stringify(body);
@@ -512,10 +532,10 @@ var HonoRequest = class {
512
532
  * @param data - The validated data to add.
513
533
  */
514
534
  addValidatedData(target, data) {
515
- this.#validatedData[target] = data;
535
+ (this.#validatedData ??= {})[target] = data;
516
536
  }
517
537
  valid(target) {
518
- return this.#validatedData[target];
538
+ return this.#validatedData?.[target];
519
539
  }
520
540
  /**
521
541
  * `.url()` can get the request url strings.
@@ -602,7 +622,7 @@ var HonoRequest = class {
602
622
  }
603
623
  };
604
624
 
605
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/html.js
625
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/html.js
606
626
  var HtmlEscapedCallbackPhase = {
607
627
  Stringify: 1};
608
628
  var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
@@ -633,7 +653,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
633
653
  }
634
654
  };
635
655
 
636
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/context.js
656
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/context.js
637
657
  var TEXT_PLAIN = "text/plain; charset=UTF-8";
638
658
  var setDefaultContentType = (contentType, headers) => {
639
659
  return {
@@ -906,11 +926,11 @@ var Context = class {
906
926
  return Object.fromEntries(this.#var);
907
927
  }
908
928
  #newResponse(data, arg, headers) {
909
- const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers();
910
- if (typeof arg === "object" && "headers" in arg) {
911
- const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers);
912
- for (const [key, value] of argHeaders) {
913
- if (key.toLowerCase() === "set-cookie") {
929
+ let responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders;
930
+ if (typeof arg === "object" && arg.headers) {
931
+ responseHeaders ??= new Headers();
932
+ for (const [key, value] of new Headers(arg.headers)) {
933
+ if (key === "set-cookie") {
914
934
  responseHeaders.append(key, value);
915
935
  } else {
916
936
  responseHeaders.set(key, value);
@@ -918,19 +938,34 @@ var Context = class {
918
938
  }
919
939
  }
920
940
  if (headers) {
921
- for (const [k, v] of Object.entries(headers)) {
922
- if (typeof v === "string") {
923
- responseHeaders.set(k, v);
924
- } else {
925
- responseHeaders.delete(k);
926
- for (const v2 of v) {
927
- responseHeaders.append(k, v2);
941
+ if (!responseHeaders) {
942
+ let count = 0;
943
+ for (const k in headers) {
944
+ if (++count > 1 || typeof headers[k] !== "string") {
945
+ responseHeaders = new Headers();
946
+ break;
947
+ }
948
+ }
949
+ }
950
+ if (responseHeaders) {
951
+ for (const k in headers) {
952
+ const v = headers[k];
953
+ if (typeof v === "string") {
954
+ responseHeaders.set(k, v);
955
+ } else {
956
+ responseHeaders.delete(k);
957
+ for (const v2 of v) {
958
+ responseHeaders.append(k, v2);
959
+ }
928
960
  }
929
961
  }
930
962
  }
931
963
  }
932
964
  const status = typeof arg === "number" ? arg : arg?.status ?? this.#status;
933
- return createResponseInstance(data, { status, headers: responseHeaders });
965
+ return createResponseInstance(data, {
966
+ status,
967
+ headers: responseHeaders ?? headers
968
+ });
934
969
  }
935
970
  newResponse = (...args) => this.#newResponse(...args);
936
971
  /**
@@ -1040,18 +1075,18 @@ var Context = class {
1040
1075
  };
1041
1076
  };
1042
1077
 
1043
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router.js
1078
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router.js
1044
1079
  var METHOD_NAME_ALL = "ALL";
1045
1080
  var METHOD_NAME_ALL_LOWERCASE = "all";
1046
- var METHODS = ["get", "post", "put", "delete", "options", "patch"];
1081
+ var METHODS = ["get", "post", "put", "delete", "options", "patch", "query"];
1047
1082
  var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built.";
1048
1083
  var UnsupportedPathError = class extends Error {
1049
1084
  };
1050
1085
 
1051
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/constants.js
1086
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/constants.js
1052
1087
  var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
1053
1088
 
1054
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/hono-base.js
1089
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/hono-base.js
1055
1090
  var notFoundHandler = (c) => {
1056
1091
  return c.text("404 Not Found", 404);
1057
1092
  };
@@ -1070,6 +1105,7 @@ var Hono = class _Hono {
1070
1105
  delete;
1071
1106
  options;
1072
1107
  patch;
1108
+ query;
1073
1109
  all;
1074
1110
  on;
1075
1111
  use;
@@ -1369,8 +1405,8 @@ var Hono = class _Hono {
1369
1405
  * @see {@link https://hono.dev/docs/api/hono#fetch}
1370
1406
  *
1371
1407
  * @param {Request} request - request Object of request
1372
- * @param {Env} Env - env Object
1373
- * @param {ExecutionContext} - context of execution
1408
+ * @param {Env} env - env Object
1409
+ * @param {ExecutionContext} executionCtx - context of execution
1374
1410
  * @returns {Response | Promise<Response>} response of request
1375
1411
  *
1376
1412
  */
@@ -1427,7 +1463,7 @@ var Hono = class _Hono {
1427
1463
  };
1428
1464
  };
1429
1465
 
1430
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/matcher.js
1466
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/matcher.js
1431
1467
  var emptyParam = [];
1432
1468
  function match(method, path) {
1433
1469
  const matchers = this.buildAllMatchers();
@@ -1448,7 +1484,7 @@ function match(method, path) {
1448
1484
  return match2(method, path);
1449
1485
  }
1450
1486
 
1451
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/node.js
1487
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/node.js
1452
1488
  var LABEL_REG_EXP_STR = "[^/]+";
1453
1489
  var ONLY_WILDCARD_REG_EXP_STR = ".*";
1454
1490
  var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
@@ -1462,7 +1498,7 @@ function compareKey(a, b) {
1462
1498
  return 1;
1463
1499
  }
1464
1500
  if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
1465
- return 1;
1501
+ return b === TAIL_WILDCARD_REG_EXP_STR ? -1 : 1;
1466
1502
  } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
1467
1503
  return -1;
1468
1504
  }
@@ -1474,76 +1510,75 @@ function compareKey(a, b) {
1474
1510
  return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
1475
1511
  }
1476
1512
  var Node = class _Node {
1513
+ // handler index of a dynamic path, or -1 for a static path terminal
1477
1514
  #index;
1478
1515
  #varIndex;
1479
1516
  #children = /* @__PURE__ */ Object.create(null);
1480
- insert(tokens, index, paramMap, context, pathErrorCheckOnly) {
1481
- if (tokens.length === 0) {
1482
- if (this.#index !== void 0) {
1483
- throw PATH_ERROR;
1484
- }
1485
- if (pathErrorCheckOnly) {
1486
- return;
1487
- }
1488
- this.#index = index;
1489
- return;
1490
- }
1491
- const [token, ...restTokens] = tokens;
1492
- const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
1493
- let node;
1494
- if (pattern) {
1495
- const name = pattern[1];
1496
- let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
1497
- if (name && pattern[2]) {
1498
- if (regexpStr === ".*") {
1499
- throw PATH_ERROR;
1500
- }
1501
- regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
1502
- if (/\((?!\?:)/.test(regexpStr)) {
1503
- throw PATH_ERROR;
1504
- }
1505
- }
1506
- node = this.#children[regexpStr];
1507
- if (!node) {
1508
- if (Object.keys(this.#children).some(
1509
- (k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
1510
- )) {
1511
- throw PATH_ERROR;
1517
+ insert(tokens, index, paramMap, context, isStatic) {
1518
+ let node = this;
1519
+ for (let i = 0, len = tokens.length; i < len; i++) {
1520
+ const token = tokens[i];
1521
+ const pattern = token.length === 1 ? token === "*" ? i === len - 1 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : null : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
1522
+ let nextNode;
1523
+ if (pattern) {
1524
+ const name = pattern[1];
1525
+ let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
1526
+ if (name && pattern[2]) {
1527
+ if (regexpStr === ".*") {
1528
+ throw PATH_ERROR;
1529
+ }
1530
+ regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
1531
+ if (/\((?!\?:)/.test(regexpStr)) {
1532
+ throw PATH_ERROR;
1533
+ }
1534
+ if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) {
1535
+ throw PATH_ERROR;
1536
+ }
1512
1537
  }
1513
- if (pathErrorCheckOnly) {
1514
- return;
1538
+ nextNode = node.#children[regexpStr];
1539
+ if (!nextNode) {
1540
+ if (regexpStr !== ONLY_WILDCARD_REG_EXP_STR && regexpStr !== TAIL_WILDCARD_REG_EXP_STR) {
1541
+ for (const k in node.#children) {
1542
+ if (
1543
+ // a single-char pattern coexists with single-char literals as a literal does
1544
+ (regexpStr.length > 1 || k.length > 1) && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
1545
+ ) {
1546
+ throw PATH_ERROR;
1547
+ }
1548
+ }
1549
+ }
1550
+ nextNode = node.#children[regexpStr] = new _Node();
1515
1551
  }
1516
- node = this.#children[regexpStr] = new _Node();
1517
1552
  if (name !== "") {
1518
- node.#varIndex = context.varIndex++;
1553
+ nextNode.#varIndex ??= context.varIndex++;
1554
+ paramMap.push([name, nextNode.#varIndex]);
1519
1555
  }
1520
- }
1521
- if (!pathErrorCheckOnly && name !== "") {
1522
- paramMap.push([name, node.#varIndex]);
1523
- }
1524
- } else {
1525
- node = this.#children[token];
1526
- if (!node) {
1527
- if (Object.keys(this.#children).some(
1528
- (k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
1529
- )) {
1530
- throw PATH_ERROR;
1531
- }
1532
- if (pathErrorCheckOnly) {
1533
- return;
1556
+ } else {
1557
+ nextNode = node.#children[token];
1558
+ if (!nextNode) {
1559
+ for (const k in node.#children) {
1560
+ if (k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR) {
1561
+ throw PATH_ERROR;
1562
+ }
1563
+ }
1564
+ nextNode = node.#children[token] = new _Node();
1534
1565
  }
1535
- node = this.#children[token] = new _Node();
1536
1566
  }
1567
+ node = nextNode;
1568
+ }
1569
+ if (node.#index !== void 0) {
1570
+ throw PATH_ERROR;
1537
1571
  }
1538
- node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
1572
+ node.#index = isStatic ? -1 : index;
1539
1573
  }
1540
1574
  buildRegExpStr() {
1541
1575
  const childKeys = Object.keys(this.#children).sort(compareKey);
1542
1576
  const strList = childKeys.map((k) => {
1543
1577
  const c = this.#children[k];
1544
- return (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr();
1545
- });
1546
- if (typeof this.#index === "number") {
1578
+ const childStr = c.buildRegExpStr();
1579
+ return childStr === "" ? "" : (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + childStr;
1580
+ }).filter(Boolean);
1581
+ if (typeof this.#index === "number" && this.#index !== -1) {
1547
1582
  strList.unshift(`#${this.#index}`);
1548
1583
  }
1549
1584
  if (strList.length === 0) {
@@ -1556,16 +1591,24 @@ var Node = class _Node {
1556
1591
  }
1557
1592
  };
1558
1593
 
1559
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/trie.js
1594
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/trie.js
1560
1595
  var Trie = class {
1561
1596
  #context = { varIndex: 0 };
1562
1597
  #root = new Node();
1563
- insert(path, index, pathErrorCheckOnly) {
1598
+ #index = 0;
1599
+ // dynamic path -> [handler index, param assoc]; static paths are not registered
1600
+ paths = /* @__PURE__ */ Object.create(null);
1601
+ insert(path, isStatic) {
1602
+ if (isStatic) {
1603
+ this.#root.insert(path.split(""), 0, [], this.#context, true);
1604
+ return;
1605
+ }
1564
1606
  const paramAssoc = [];
1565
1607
  const groups = [];
1608
+ let markedPath = path;
1566
1609
  for (let i = 0; ; ) {
1567
1610
  let replaced = false;
1568
- path = path.replace(/\{[^}]+\}/g, (m) => {
1611
+ markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => {
1569
1612
  const mark = `@\\${i}`;
1570
1613
  groups[i] = [mark, m];
1571
1614
  i++;
@@ -1576,7 +1619,7 @@ var Trie = class {
1576
1619
  break;
1577
1620
  }
1578
1621
  }
1579
- const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
1622
+ const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
1580
1623
  for (let i = groups.length - 1; i >= 0; i--) {
1581
1624
  const [mark] = groups[i];
1582
1625
  for (let j = tokens.length - 1; j >= 0; j--) {
@@ -1586,8 +1629,8 @@ var Trie = class {
1586
1629
  }
1587
1630
  }
1588
1631
  }
1589
- this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly);
1590
- return paramAssoc;
1632
+ this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false);
1633
+ this.paths[path] = [this.#index++, paramAssoc];
1591
1634
  }
1592
1635
  buildRegExp() {
1593
1636
  let regexp = this.#root.buildRegExpStr();
@@ -1612,8 +1655,7 @@ var Trie = class {
1612
1655
  }
1613
1656
  };
1614
1657
 
1615
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/router.js
1616
- var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
1658
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/router.js
1617
1659
  var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
1618
1660
  function buildWildcardRegExp(path) {
1619
1661
  return wildcardRegExpCache[path] ??= new RegExp(
@@ -1626,63 +1668,6 @@ function buildWildcardRegExp(path) {
1626
1668
  function clearWildcardRegExpCache() {
1627
1669
  wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
1628
1670
  }
1629
- function buildMatcherFromPreprocessedRoutes(routes) {
1630
- const trie = new Trie();
1631
- const handlerData = [];
1632
- if (routes.length === 0) {
1633
- return nullMatcher;
1634
- }
1635
- const routesWithStaticPathFlag = routes.map(
1636
- (route) => [!/\*|\/:/.test(route[0]), ...route]
1637
- ).sort(
1638
- ([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length
1639
- );
1640
- const staticMap = /* @__PURE__ */ Object.create(null);
1641
- for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
1642
- const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
1643
- if (pathErrorCheckOnly) {
1644
- staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
1645
- } else {
1646
- j++;
1647
- }
1648
- let paramAssoc;
1649
- try {
1650
- paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
1651
- } catch (e) {
1652
- throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
1653
- }
1654
- if (pathErrorCheckOnly) {
1655
- continue;
1656
- }
1657
- handlerData[j] = handlers.map(([h, paramCount]) => {
1658
- const paramIndexMap = /* @__PURE__ */ Object.create(null);
1659
- paramCount -= 1;
1660
- for (; paramCount >= 0; paramCount--) {
1661
- const [key, value] = paramAssoc[paramCount];
1662
- paramIndexMap[key] = value;
1663
- }
1664
- return [h, paramIndexMap];
1665
- });
1666
- }
1667
- const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
1668
- for (let i = 0, len = handlerData.length; i < len; i++) {
1669
- for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
1670
- const map = handlerData[i][j]?.[1];
1671
- if (!map) {
1672
- continue;
1673
- }
1674
- const keys = Object.keys(map);
1675
- for (let k = 0, len3 = keys.length; k < len3; k++) {
1676
- map[keys[k]] = paramReplacementMap[map[keys[k]]];
1677
- }
1678
- }
1679
- }
1680
- const handlerMap = [];
1681
- for (const i in indexReplacementMap) {
1682
- handlerMap[i] = handlerData[indexReplacementMap[i]];
1683
- }
1684
- return [regexp, handlerMap, staticMap];
1685
- }
1686
1671
  function findMiddleware(middleware, path) {
1687
1672
  if (!middleware) {
1688
1673
  return void 0;
@@ -1698,9 +1683,18 @@ var RegExpRouter = class {
1698
1683
  name = "RegExpRouter";
1699
1684
  #middleware;
1700
1685
  #routes;
1686
+ #tries;
1701
1687
  constructor() {
1702
1688
  this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
1703
1689
  this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
1690
+ this.#tries = { [METHOD_NAME_ALL]: new Trie() };
1691
+ }
1692
+ #insertPath(method, path) {
1693
+ try {
1694
+ this.#tries[method].insert(path, !/\*|\/:/.test(path));
1695
+ } catch (e) {
1696
+ throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
1697
+ }
1704
1698
  }
1705
1699
  add(method, path, handler) {
1706
1700
  const middleware = this.#middleware;
@@ -1709,10 +1703,12 @@ var RegExpRouter = class {
1709
1703
  throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
1710
1704
  }
1711
1705
  if (!middleware[method]) {
1706
+ this.#tries[method] = new Trie();
1712
1707
  [middleware, routes].forEach((handlerMap) => {
1713
1708
  handlerMap[method] = /* @__PURE__ */ Object.create(null);
1714
1709
  Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
1715
1710
  handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
1711
+ this.#insertPath(method, p);
1716
1712
  });
1717
1713
  });
1718
1714
  }
@@ -1722,13 +1718,12 @@ var RegExpRouter = class {
1722
1718
  const paramCount = (path.match(/\/:/g) || []).length;
1723
1719
  if (/\*$/.test(path)) {
1724
1720
  const re = buildWildcardRegExp(path);
1725
- if (method === METHOD_NAME_ALL) {
1726
- Object.keys(middleware).forEach((m) => {
1727
- middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
1728
- });
1729
- } else {
1730
- middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
1731
- }
1721
+ Object.keys(middleware).forEach((m) => {
1722
+ if ((method === METHOD_NAME_ALL || method === m) && !middleware[m][path]) {
1723
+ this.#insertPath(m, path);
1724
+ middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
1725
+ }
1726
+ });
1732
1727
  Object.keys(middleware).forEach((m) => {
1733
1728
  if (method === METHOD_NAME_ALL || method === m) {
1734
1729
  Object.keys(middleware[m]).forEach((p) => {
@@ -1750,9 +1745,12 @@ var RegExpRouter = class {
1750
1745
  const path2 = paths[i];
1751
1746
  Object.keys(routes).forEach((m) => {
1752
1747
  if (method === METHOD_NAME_ALL || method === m) {
1753
- routes[m][path2] ||= [
1754
- ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
1755
- ];
1748
+ if (!routes[m][path2]) {
1749
+ this.#insertPath(m, path2);
1750
+ routes[m][path2] = [
1751
+ ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
1752
+ ];
1753
+ }
1756
1754
  routes[m][path2].push([handler, paramCount - len + i + 1]);
1757
1755
  }
1758
1756
  });
@@ -1764,33 +1762,58 @@ var RegExpRouter = class {
1764
1762
  Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {
1765
1763
  matchers[method] ||= this.#buildMatcher(method);
1766
1764
  });
1767
- this.#middleware = this.#routes = void 0;
1765
+ this.#middleware = this.#routes = this.#tries = void 0;
1768
1766
  clearWildcardRegExpCache();
1769
1767
  return matchers;
1770
1768
  }
1771
1769
  #buildMatcher(method) {
1772
- const routes = [];
1773
- let hasOwnRoute = method === METHOD_NAME_ALL;
1774
- [this.#middleware, this.#routes].forEach((r) => {
1775
- const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];
1776
- if (ownRoute.length !== 0) {
1777
- hasOwnRoute ||= true;
1778
- routes.push(...ownRoute);
1779
- } else if (method !== METHOD_NAME_ALL) {
1780
- routes.push(
1781
- ...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]])
1782
- );
1770
+ const middleware = this.#middleware[method];
1771
+ const routes = this.#routes[method];
1772
+ const trie = this.#tries[method];
1773
+ const staticMap = /* @__PURE__ */ Object.create(null);
1774
+ const handlerData = [];
1775
+ [middleware, routes].forEach((r) => {
1776
+ for (const path in r) {
1777
+ const handlers = r[path];
1778
+ const pathData = trie.paths[path];
1779
+ if (!pathData) {
1780
+ staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
1781
+ continue;
1782
+ }
1783
+ const paramAssoc = pathData[1];
1784
+ handlerData[pathData[0]] = handlers.map(([h, paramCount]) => {
1785
+ const paramIndexMap = /* @__PURE__ */ Object.create(null);
1786
+ paramCount -= 1;
1787
+ for (; paramCount >= 0; paramCount--) {
1788
+ const [key, value] = paramAssoc[paramCount];
1789
+ paramIndexMap[key] = value;
1790
+ }
1791
+ return [h, paramIndexMap];
1792
+ });
1783
1793
  }
1784
1794
  });
1785
- if (!hasOwnRoute) {
1786
- return null;
1787
- } else {
1788
- return buildMatcherFromPreprocessedRoutes(routes);
1795
+ const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
1796
+ for (let i = 0, len = handlerData.length; i < len; i++) {
1797
+ for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
1798
+ const map = handlerData[i][j]?.[1];
1799
+ if (!map) {
1800
+ continue;
1801
+ }
1802
+ const keys = Object.keys(map);
1803
+ for (let k = 0, len3 = keys.length; k < len3; k++) {
1804
+ map[keys[k]] = paramReplacementMap[map[keys[k]]];
1805
+ }
1806
+ }
1807
+ }
1808
+ const handlerMap = [];
1809
+ for (const i in indexReplacementMap) {
1810
+ handlerMap[i] = handlerData[indexReplacementMap[i]];
1789
1811
  }
1812
+ return [regexp, handlerMap, staticMap];
1790
1813
  }
1791
1814
  };
1792
1815
 
1793
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/smart-router/router.js
1816
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/smart-router/router.js
1794
1817
  var SmartRouter = class {
1795
1818
  name = "SmartRouter";
1796
1819
  #routers = [];
@@ -1845,7 +1868,7 @@ var SmartRouter = class {
1845
1868
  }
1846
1869
  };
1847
1870
 
1848
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/trie-router/node.js
1871
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/trie-router/node.js
1849
1872
  var emptyParams = /* @__PURE__ */ Object.create(null);
1850
1873
  var hasChildren = (children) => {
1851
1874
  for (const _ in children) {
@@ -1979,9 +2002,18 @@ var Node2 = class _Node2 {
1979
2002
  if (m) {
1980
2003
  params[name] = m[0];
1981
2004
  this.#pushHandlerSets(handlerSets, child, method, node.#params, params);
2005
+ if (m[0].length === restPathString.length && child.#children["*"]) {
2006
+ this.#pushHandlerSets(
2007
+ handlerSets,
2008
+ child.#children["*"],
2009
+ method,
2010
+ node.#params,
2011
+ params
2012
+ );
2013
+ }
1982
2014
  if (hasChildren(child.#children)) {
1983
2015
  child.#params = params;
1984
- const componentCount = m[0].match(/\//)?.length ?? 0;
2016
+ const componentCount = m[0].match(/\//g)?.length ?? 0;
1985
2017
  const targetCurNodes = curNodesQueue[componentCount] ||= [];
1986
2018
  targetCurNodes.push(child);
1987
2019
  }
@@ -2020,7 +2052,7 @@ var Node2 = class _Node2 {
2020
2052
  }
2021
2053
  };
2022
2054
 
2023
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/trie-router/router.js
2055
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/trie-router/router.js
2024
2056
  var TrieRouter = class {
2025
2057
  name = "TrieRouter";
2026
2058
  #node;
@@ -2042,7 +2074,7 @@ var TrieRouter = class {
2042
2074
  }
2043
2075
  };
2044
2076
 
2045
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/hono.js
2077
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/hono.js
2046
2078
  var Hono2 = class extends Hono {
2047
2079
  /**
2048
2080
  * Creates an instance of the Hono class.
@@ -2058,9 +2090,75 @@ var Hono2 = class extends Hono {
2058
2090
  };
2059
2091
 
2060
2092
  // src/types/common.ts
2093
+ var SUPPORTED_CURRENCY_CODES = /* @__PURE__ */ new Set([
2094
+ "USD",
2095
+ "EUR",
2096
+ "GBP",
2097
+ "JPY",
2098
+ "CNY",
2099
+ "CHF",
2100
+ "CAD",
2101
+ "AUD",
2102
+ "GHS",
2103
+ "NGN",
2104
+ "KES",
2105
+ "ZAR",
2106
+ "XOF",
2107
+ "XAF",
2108
+ "EGP",
2109
+ "TZS",
2110
+ "UGX",
2111
+ "RWF",
2112
+ "ETB",
2113
+ "ZMW",
2114
+ "BWP",
2115
+ "MUR",
2116
+ "NAD",
2117
+ "MWK",
2118
+ "AOA",
2119
+ "CDF",
2120
+ "GMD",
2121
+ "GNF",
2122
+ "LRD",
2123
+ "SLL",
2124
+ "MZN",
2125
+ "BIF",
2126
+ "INR",
2127
+ "BRL",
2128
+ "COP",
2129
+ "MXN",
2130
+ "KRW",
2131
+ "TRY",
2132
+ "THB",
2133
+ "MYR",
2134
+ "PHP",
2135
+ "IDR",
2136
+ "VND",
2137
+ "SGD",
2138
+ "HKD",
2139
+ "TWD",
2140
+ "AED",
2141
+ "SAR",
2142
+ "ILS"
2143
+ ]);
2144
+ function isSupportedCurrency(code) {
2145
+ return SUPPORTED_CURRENCY_CODES.has(code);
2146
+ }
2147
+ var warnedCurrencies = /* @__PURE__ */ new Set();
2148
+ function currencyCode(value) {
2149
+ const normalized = value.trim().toUpperCase();
2150
+ if (!isSupportedCurrency(normalized) && !warnedCurrencies.has(normalized)) {
2151
+ warnedCurrencies.add(normalized);
2152
+ console.warn(
2153
+ `[Cimplify] Unrecognized currency code "${normalized}". It may not format correctly.`
2154
+ );
2155
+ }
2156
+ return normalized;
2157
+ }
2061
2158
  var ErrorCode = {
2062
2159
  NOT_FOUND: "NOT_FOUND",
2063
- VALIDATION_ERROR: "VALIDATION_ERROR"};
2160
+ VALIDATION_ERROR: "VALIDATION_ERROR",
2161
+ CART_EMPTY: "CART_EMPTY"};
2064
2162
  var DOCS_ERROR_BASE_URL = "https://docs.cimplify.io/reference/error-codes";
2065
2163
  function docsUrlForCode(code) {
2066
2164
  return `${DOCS_ERROR_BASE_URL}#${code.toLowerCase().replace(/_/g, "-")}`;
@@ -2118,6 +2216,10 @@ var ERROR_SUGGESTIONS = {
2118
2216
  CHECKOUT_NOT_READY: "Checkout elements are still initializing. Wait for readiness before submit.",
2119
2217
  CANCELLED: "Checkout was cancelled. Preserve cart state and allow shopper to retry.",
2120
2218
  REQUEST_TIMEOUT: "Provider call timed out. Poll payment/order status before issuing another charge attempt.",
2219
+ CHECKOUT_AUTHORITY_REQUIRED: "Provide an exact cart or purchase-intent authority before starting checkout.",
2220
+ AUTH_CONFIG_REQUIRED: "Configure the OAuth client and redirect URI before mounting embedded authentication.",
2221
+ AUTH_CALLBACK_MISSING_TOKEN: "The authentication callback returned no access token. Restart sign-in before checkout.",
2222
+ AUTH_START_FAILED: "Embedded authentication could not start. Verify the OAuth configuration and retry.",
2121
2223
  POPUP_BLOCKED: "Browser blocked provider popup. Ask shopper to enable popups and retry.",
2122
2224
  FX_QUOTE_FAILED: "Failed to lock FX quote. Retry currency quote or fallback to base currency."
2123
2225
  };
@@ -2161,7 +2263,7 @@ function authMiddleware(deps) {
2161
2263
  await next();
2162
2264
  };
2163
2265
  }
2164
- var IDEMPOTENT_PATH_RE = /\/(cart\/items|cart\/coupons|checkout|payments\/authorization|auth\/(request-otp|verify-otp|profile|logout))(\/|$)/;
2266
+ var IDEMPOTENT_PATH_RE = /\/(cart\/items|cart\/coupons|checkout|purchase-intents|payments\/authorization|auth\/(request-otp|verify-otp|profile|logout))(\/|$)/;
2165
2267
  var IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
2166
2268
  function idempotencyMiddleware(deps) {
2167
2269
  return async (c, next) => {
@@ -2171,7 +2273,8 @@ function idempotencyMiddleware(deps) {
2171
2273
  const key = c.req.header("idempotency-key") ?? c.req.header("Idempotency-Key");
2172
2274
  if (!key || key.length > 200) return next();
2173
2275
  const session = c.var.session;
2174
- const recordId = `${session.id}:${key}`;
2276
+ const routeScope = c.req.path.includes("/purchase-intents") ? `:${c.req.path}` : "";
2277
+ const recordId = `${session.id}${routeScope}:${key}`;
2175
2278
  const existing = deps.registry.idempotency.get(recordId);
2176
2279
  if (existing) {
2177
2280
  const expiresAt = Date.parse(existing.expires_at);
@@ -2249,8 +2352,91 @@ var PAYMENT_METHOD = {
2249
2352
  MOBILE_MONEY: "mobile_money",
2250
2353
  CARD: "card"
2251
2354
  };
2355
+ var CHECKOUT_NEXT_ACTION = {
2356
+ NONE: "none"};
2357
+ var CHECKOUT_ORDER_RESULT_STATUS = {
2358
+ CONFIRMED: "confirmed"};
2359
+ var AUTHORIZATION_TYPE = {
2360
+ OTP: "otp"};
2361
+ var DEFAULT_CURRENCY = "GHS";
2362
+
2363
+ // src/types/purchase-intent.ts
2364
+ var PURCHASE_INTENT_SOURCE_KIND = {
2365
+ CART: "cart"};
2366
+ var PURCHASING_PRINCIPAL_KIND = {
2367
+ ACCOUNT: "account",
2368
+ BUSINESS: "business",
2369
+ GUEST: "guest"};
2370
+ var PURCHASE_INTENT_STATUS = {
2371
+ OPEN: "open",
2372
+ CONVERTED: "converted"};
2373
+ var PURCHASE_INTENT_SELLABLE_KIND = {
2374
+ PRODUCT: "product",
2375
+ VARIANT: "variant"
2376
+ };
2377
+ var PURCHASE_INTENT_LINE_CONFIGURATION_KIND = {
2378
+ SIMPLE: "simple"};
2379
+ var MOBILE_MONEY_TENDER_AUTHORITY_KIND = {
2380
+ TRANSIENT: "transient"};
2381
+ var PURCHASE_INTENT_MOBILE_MONEY_NETWORK = {
2382
+ MTN: "mtn",
2383
+ TELECEL: "telecel",
2384
+ AIRTEL: "airtel"
2385
+ };
2386
+ var CHECKOUT_COMPLIANCE_LANE = {
2387
+ NATIVE: "native"};
2388
+ var CHECKOUT_COMPLIANCE_REASON = {
2389
+ PHYSICAL_GOODS_NATIVE: "physical_goods_native"};
2390
+ var PURCHASE_INTENT_PAYMENT_METHOD = {
2391
+ CARD: "card",
2392
+ MOBILE_MONEY: "mobile_money"};
2393
+ var PAYMENT_COLLECTION_MODE = {
2394
+ REQUEST_TO_PAY: "request_to_pay",
2395
+ CREDENTIAL_CHARGE: "credential_charge"};
2396
+ var PURCHASE_INTENT_PAYMENT_PROVIDER = {
2397
+ PAYSTACK: "paystack"};
2398
+ var PAYMENT_OPTION_STATUS = {
2399
+ ELIGIBLE: "eligible"};
2400
+ var PAYMENT_OPTION_NEXT_ACTION = {
2401
+ PROVIDER_APPROVAL: "provider_approval"};
2402
+ var LEGACY_CHECKOUT_PAYMENT_STATUS = {
2403
+ SUCCEEDED: "succeeded"};
2404
+ var CHECKOUT_PROTECTION_STATUS = {
2405
+ PROTECTED: "protected"};
2406
+ var CHECKOUT_TERMS_COMPONENT = {
2407
+ DUE_NOW: "due_now"};
2408
+ var CHECKOUT_TERMS_DELTA_KIND = {
2409
+ MONEY: "money",
2410
+ PAYMENT_OPTION: "payment_option"
2411
+ };
2412
+ var CHECKOUT_INVALIDATION_REASON = {
2413
+ PAYMENT_OPTION_CHANGED: "payment_option_changed"
2414
+ };
2415
+ var PURCHASE_INTENT_CONFIRM_STATUS = {
2416
+ ORDER_CREATED: "order_created",
2417
+ CARD_POPUP_REQUIRED: "card_popup_required",
2418
+ AUTHORIZATION_REQUIRED: "authorization_required",
2419
+ TERMS_CHANGED: "terms_changed",
2420
+ SELECTION_UNAVAILABLE: "selection_unavailable",
2421
+ SOURCE_UNAVAILABLE: "source_unavailable"
2422
+ };
2252
2423
 
2253
2424
  // src/mock/routes/v1.ts
2425
+ var PURCHASE_INTENT_ROUTE = {
2426
+ COLLECTION: "/purchase-intents",
2427
+ MEMBER: "/purchase-intents/:intent_id",
2428
+ REFRESH: "/purchase-intents/:intent_id/refresh",
2429
+ CONFIRM: "/purchase-intents/:intent_id/confirm",
2430
+ ACTION: "/purchase-intents/:intent_id/attempts/:attempt_id/actions"
2431
+ };
2432
+ var PURCHASE_INTENT_ROUTE_PARAM = {
2433
+ INTENT_ID: "intent_id",
2434
+ ATTEMPT_ID: "attempt_id"
2435
+ };
2436
+ var PAYMENT_ROUTE = {
2437
+ COLLECTION_OPTIONS: "/payments/collection-options",
2438
+ AUTHORIZATION: "/payments/authorization"
2439
+ };
2254
2440
  function buildV1(deps) {
2255
2441
  const app = new Hono2();
2256
2442
  app.use("*", chaosMiddleware(deps));
@@ -2623,28 +2809,81 @@ function buildV1(deps) {
2623
2809
  "/subscriptions/:subscription_id/skip",
2624
2810
  (c) => tryRoute(c, () => deps.subscriptions.skipNext(c.req.param("subscription_id")))
2625
2811
  );
2626
- app.get("/payments/collection-options", (c) => {
2812
+ app.get(PAYMENT_ROUTE.COLLECTION_OPTIONS, async (c) => {
2627
2813
  const currency = c.req.query("currency")?.trim().toUpperCase();
2628
2814
  if (!currency || !/^[A-Z]{3}$/.test(currency)) {
2629
2815
  return fail(c, validation("currency must be a canonical three-letter code"));
2630
2816
  }
2631
2817
  return tryRoute(c, () => ({
2632
2818
  presentment_currency: currency,
2633
- options: currency === "GHS" ? [
2819
+ options: currency === DEFAULT_CURRENCY ? [
2634
2820
  { method: PAYMENT_METHOD.CARD, network: null },
2635
- { method: PAYMENT_METHOD.MOBILE_MONEY, network: "mtn" },
2636
- { method: PAYMENT_METHOD.MOBILE_MONEY, network: "telecel" },
2637
- { method: PAYMENT_METHOD.MOBILE_MONEY, network: "airteltigo" }
2821
+ {
2822
+ method: PAYMENT_METHOD.MOBILE_MONEY,
2823
+ network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.MTN
2824
+ },
2825
+ {
2826
+ method: PAYMENT_METHOD.MOBILE_MONEY,
2827
+ network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.TELECEL
2828
+ },
2829
+ {
2830
+ method: PAYMENT_METHOD.MOBILE_MONEY,
2831
+ network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.AIRTEL
2832
+ }
2638
2833
  ] : currency === "USD" ? [{ method: PAYMENT_METHOD.CARD, network: null }] : []
2639
2834
  }));
2640
2835
  });
2641
- app.post("/checkout", async (c) => {
2836
+ app.post(PURCHASE_INTENT_ROUTE.COLLECTION, async (c) => {
2837
+ const body = await c.req.json().catch(() => ({}));
2838
+ return tryRoute(c, () => deps.purchaseIntents.mint(c.var.session, body));
2839
+ });
2840
+ app.get(
2841
+ PURCHASE_INTENT_ROUTE.MEMBER,
2842
+ (c) => tryRoute(
2843
+ c,
2844
+ () => deps.purchaseIntents.get(
2845
+ c.var.session,
2846
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID)
2847
+ )
2848
+ )
2849
+ );
2850
+ app.post(PURCHASE_INTENT_ROUTE.REFRESH, async (c) => {
2642
2851
  const body = await c.req.json().catch(() => ({}));
2643
- return tryRoute(c, () => deps.checkout.process(c.var.session, body));
2852
+ return tryRoute(
2853
+ c,
2854
+ () => deps.purchaseIntents.refresh(
2855
+ c.var.session,
2856
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
2857
+ body.expected_intent_revision
2858
+ )
2859
+ );
2644
2860
  });
2645
- app.post("/payments/authorization", async (c) => {
2861
+ app.post(PURCHASE_INTENT_ROUTE.CONFIRM, async (c) => {
2646
2862
  const body = await c.req.json().catch(() => ({}));
2647
- return tryRoute(c, () => deps.checkout.authorize(body));
2863
+ return tryRoute(
2864
+ c,
2865
+ () => deps.purchaseIntents.confirm(
2866
+ c.var.session,
2867
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
2868
+ body
2869
+ )
2870
+ );
2871
+ });
2872
+ app.post(PURCHASE_INTENT_ROUTE.ACTION, async (c) => {
2873
+ const body = await c.req.json().catch(() => ({}));
2874
+ return tryRoute(
2875
+ c,
2876
+ () => deps.purchaseIntents.submitAction(
2877
+ c.var.session,
2878
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
2879
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.ATTEMPT_ID),
2880
+ body
2881
+ )
2882
+ );
2883
+ });
2884
+ app.post(PAYMENT_ROUTE.AUTHORIZATION, async (c) => {
2885
+ const body = await c.req.json().catch(() => ({}));
2886
+ return tryRoute(c, () => deps.payments.authorize(body));
2648
2887
  });
2649
2888
  app.get("/auth/status", (c) => tryRoute(c, () => deps.auth.status(c.var.session.token)));
2650
2889
  app.post("/auth/request-otp", async (c) => {
@@ -2950,6 +3189,7 @@ function createStateRegistry() {
2950
3189
  collections: createMemoryStore(),
2951
3190
  carts: createMemoryStore(),
2952
3191
  orders: createMemoryStore(),
3192
+ purchaseIntents: createMemoryStore(),
2953
3193
  subscriptions: createMemoryStore(),
2954
3194
  bookings: createMemoryStore(),
2955
3195
  services: createMemoryStore(),
@@ -3099,9 +3339,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3099
3339
  // injected into the HTML page served by the mock. Edit this file directly \u2014
3100
3340
  // type-check via tsgo + the IframeToParentMessage shape from the SDK.
3101
3341
  (function () {
3102
- var TYPE = window.__CIMPLIFY_MOCK_ELEMENT__;
3103
- var DEFAULT_BUSINESS = window.__CIMPLIFY_MOCK_BUSINESS__;
3104
- var MSG = {
3342
+ const TYPE = window.__CIMPLIFY_MOCK_ELEMENT__;
3343
+ const DEFAULT_BUSINESS = window.__CIMPLIFY_MOCK_BUSINESS__;
3344
+ const MSG = {
3105
3345
  INIT: "init", SET_TOKEN: "set_token", SET_CART: "set_cart", GET_DATA: "get_data",
3106
3346
  REFRESH_TOKEN: "refresh_token", LOGOUT: "logout",
3107
3347
  PROCESS_CHECKOUT: "process_checkout", ABORT_CHECKOUT: "abort_checkout",
@@ -3114,12 +3354,36 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3114
3354
  CHECKOUT_STATUS: "checkout_status", CHECKOUT_COMPLETE: "checkout_complete",
3115
3355
  ORDER_TYPE_CHANGED: "order_type_changed", REQUEST_SUBMIT: "request_submit"
3116
3356
  };
3117
- var qs = new URLSearchParams(location.search);
3118
- var AUTO = qs.get("auto") === "true";
3119
- var DEMO_OTP = qs.get("otp") || "123456";
3120
- var ORIGIN = qs.get("origin") || "*";
3121
- var NONCE = qs.get("nonce");
3122
- var state = {
3357
+ const PURCHASE_INTENT = {
3358
+ PATH: "/api/v1/purchase-intents",
3359
+ ORDER_CREATED: "order_created",
3360
+ CARD_POPUP_REQUIRED: "card_popup_required",
3361
+ AUTHORIZATION_REQUIRED: "authorization_required",
3362
+ ELIGIBLE: "eligible"
3363
+ };
3364
+ const CHECKOUT_STATUS = {
3365
+ PROCESSING: "processing",
3366
+ SUCCESS: "success",
3367
+ FAILED: "failed"
3368
+ };
3369
+ const CHECKOUT_ERROR = { FAILED: "CHECKOUT_FAILED" };
3370
+ const ORDER_STATUS = { CONFIRMED: "confirmed" };
3371
+ const TENDER = {
3372
+ CARD: "card",
3373
+ MOBILE_MONEY: "mobile_money",
3374
+ AUTHORITY_TRANSIENT: "transient",
3375
+ MTN: "mtn"
3376
+ };
3377
+ const PAYMENT_PROVIDER = { PAYSTACK: "paystack" };
3378
+ const SOURCE_KIND = { CART: "cart" };
3379
+ const AUTHORIZATION = { OTP: "otp" };
3380
+ const EXACT_ZERO_MONEY = /^[+-]?0+(?:\\.0+)?(?:[eE][+-]?\\d+)?$/;
3381
+ const qs = new URLSearchParams(location.search);
3382
+ const AUTO = qs.get("auto") === "true";
3383
+ const DEMO_OTP = qs.get("otp") || "123456";
3384
+ const ORIGIN = qs.get("origin") || "*";
3385
+ const NONCE = qs.get("nonce");
3386
+ const state = {
3123
3387
  businessId: DEFAULT_BUSINESS,
3124
3388
  token: null,
3125
3389
  cart: null,
@@ -3130,15 +3394,33 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3130
3394
  contact: null,
3131
3395
  pendingOtp: null
3132
3396
  };
3133
- var content = document.getElementById("content");
3134
- var title = document.getElementById("title");
3397
+ const content = document.getElementById("content");
3398
+ const title = document.getElementById("title");
3135
3399
 
3136
3400
  function send(msg) {
3137
3401
  if (NONCE) msg.nonce = NONCE;
3138
3402
  parent.postMessage(msg, ORIGIN);
3139
3403
  }
3404
+ function isExactlyZeroMoney(value) {
3405
+ return typeof value === "number"
3406
+ ? Object.is(value, 0) || Object.is(value, -0)
3407
+ : EXACT_ZERO_MONEY.test(String(value).trim());
3408
+ }
3140
3409
  function reportHeight() { send({ type: MSG.HEIGHT_CHANGE, height: document.documentElement.scrollHeight }); }
3141
3410
  function ready() { send({ type: MSG.READY, height: document.documentElement.scrollHeight }); }
3411
+ function responseWithBody(response) {
3412
+ return response.json().then(function (body) {
3413
+ return { status: response.status, body: body };
3414
+ });
3415
+ }
3416
+ function assertSuccessfulResponse(response, fallbackMessage) {
3417
+ if (response.status >= 400) {
3418
+ throw new Error(
3419
+ (response.body && response.body.error && response.body.error.message) || fallbackMessage
3420
+ );
3421
+ }
3422
+ return response.body;
3423
+ }
3142
3424
 
3143
3425
  function defaultAddress() {
3144
3426
  return {
@@ -3148,9 +3430,20 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3148
3430
  };
3149
3431
  }
3150
3432
  function defaultPayment(type) {
3151
- if (type === "card") return { type: "card", provider: "paystack", last_four: "4242", label: "Visa \u2022\u2022\u2022\u2022 4242" };
3152
- if (type === "cash") return { type: "cash", label: "Cash on delivery" };
3153
- return { type: "mobile_money", provider: "mtn", phone_number: "+233244000000", label: "MTN Mobile Money" };
3433
+ if (type === TENDER.CARD) {
3434
+ return {
3435
+ type: TENDER.CARD,
3436
+ provider: PAYMENT_PROVIDER.PAYSTACK,
3437
+ last_four: "4242",
3438
+ label: "Visa \u2022\u2022\u2022\u2022 4242"
3439
+ };
3440
+ }
3441
+ return {
3442
+ type: TENDER.MOBILE_MONEY,
3443
+ provider: TENDER.MTN,
3444
+ phone_number: "+233244000000",
3445
+ label: "MTN Mobile Money"
3446
+ };
3154
3447
  }
3155
3448
 
3156
3449
  function renderAuth() {
@@ -3175,12 +3468,12 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3175
3468
  ' <div class="option" data-key="lagos">5 Allen Ave, Ikeja, Lagos</div>' +
3176
3469
  '</div>' +
3177
3470
  '<button id="confirm">Use this address</button>';
3178
- var options = {
3471
+ const options = {
3179
3472
  "default": defaultAddress(),
3180
3473
  "east-legon": { street_address: "8 Ring Road", city: "Accra", region: "Greater Accra", country: "GH", latitude: 5.65, longitude: -0.16 },
3181
3474
  "lagos": { street_address: "5 Allen Avenue", city: "Lagos", region: "Lagos", country: "NG", latitude: 6.5244, longitude: 3.3792 }
3182
3475
  };
3183
- var picked = "default";
3476
+ let picked = "default";
3184
3477
  Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
3185
3478
  el.onclick = function () {
3186
3479
  picked = el.getAttribute("data-key");
@@ -3203,10 +3496,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3203
3496
  '<div class="options" id="pay-options">' +
3204
3497
  ' <div class="option selected" data-method="mobile_money">\u{1F4F1} MTN Mobile Money</div>' +
3205
3498
  ' <div class="option" data-method="card">\u{1F4B3} Card (Paystack)</div>' +
3206
- ' <div class="option" data-method="cash">\u{1F4B5} Cash on delivery</div>' +
3207
3499
  '</div>' +
3208
3500
  '<button id="confirm">Use this method</button>';
3209
- var picked = "mobile_money";
3501
+ let picked = TENDER.MOBILE_MONEY;
3210
3502
  Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
3211
3503
  el.onclick = function () {
3212
3504
  picked = el.getAttribute("data-method");
@@ -3250,7 +3542,6 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3250
3542
  '</div>' +
3251
3543
  '<label>Payment method</label>' +
3252
3544
  '<select id="pay-method">' +
3253
- ' <option value="cash">Cash</option>' +
3254
3545
  ' <option value="mobile_money">Mobile Money (MTN)</option>' +
3255
3546
  ' <option value="card">Card (Paystack)</option>' +
3256
3547
  '</select>' +
@@ -3267,65 +3558,139 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3267
3558
  }
3268
3559
 
3269
3560
  function runCheckout() {
3270
- var statusEl = document.getElementById("status");
3271
- var errEl = document.getElementById("err");
3272
- var resultEl = document.getElementById("result");
3561
+ const statusEl = document.getElementById("status");
3562
+ const errEl = document.getElementById("err");
3563
+ const resultEl = document.getElementById("result");
3273
3564
  if (errEl) errEl.textContent = "";
3274
3565
  if (!state.cart) { errEl.textContent = "No cart provided"; return; }
3275
- var customer = {
3566
+ const customer = {
3276
3567
  name: document.getElementById("cust-name").value.trim() || "Customer",
3277
3568
  email: document.getElementById("cust-email").value.trim() || "noemail@example.com",
3278
3569
  phone: document.getElementById("cust-phone").value.trim() || "+233000000000",
3279
3570
  save_details: false
3280
3571
  };
3281
- var paymentMethod = document.getElementById("pay-method").value;
3282
- var orderType = document.getElementById("order-type").value;
3572
+ const paymentMethod = document.getElementById("pay-method").value;
3573
+ const orderType = document.getElementById("order-type").value;
3283
3574
  statusEl.textContent = "Processing\u2026";
3284
- send({ type: MSG.CHECKOUT_STATUS, status: "processing", context: {} });
3575
+ send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.PROCESSING, context: {} });
3285
3576
 
3286
- var headers = { "content-type": "application/json" };
3577
+ const headers = { "content-type": "application/json" };
3287
3578
  if (state.token) headers["x-session-token"] = state.token;
3288
3579
 
3289
- fetch("/api/v1/checkout", {
3580
+ const preference = paymentMethod === TENDER.MOBILE_MONEY
3581
+ ? {
3582
+ method_kind: TENDER.MOBILE_MONEY,
3583
+ authority: {
3584
+ authority_kind: TENDER.AUTHORITY_TRANSIENT,
3585
+ phone_number: customer.phone,
3586
+ network: TENDER.MTN
3587
+ }
3588
+ }
3589
+ : { method_kind: TENDER.CARD };
3590
+ const intentKey = "mock-element:" + state.cart.id;
3591
+ fetch(PURCHASE_INTENT.PATH, {
3290
3592
  method: "POST",
3291
3593
  headers: headers,
3292
3594
  body: JSON.stringify({
3293
- cart_id: state.cart.id,
3294
- customer: customer,
3295
- order_type: orderType,
3296
- payment_method: paymentMethod,
3297
- address_info: state.address || undefined
3595
+ client_intent_key: intentKey,
3596
+ source: { kind: SOURCE_KIND.CART, cart_id: state.cart.id },
3597
+ checkout: {
3598
+ customer: customer,
3599
+ order_type: orderType,
3600
+ address_info: state.address || {},
3601
+ tender_preference: preference
3602
+ }
3298
3603
  })
3299
3604
  })
3300
- .then(function (r) { return r.json().then(function (b) { return { status: r.status, body: b }; }); })
3605
+ .then(responseWithBody)
3301
3606
  .then(function (resp) {
3302
- if (resp.status >= 400) {
3303
- var msg = (resp.body && resp.body.error && resp.body.error.message) || "Checkout failed";
3304
- send({ type: MSG.CHECKOUT_COMPLETE, success: false, error: { code: (resp.body.error && resp.body.error.code) || "CHECKOUT_FAILED", message: msg, recoverable: true } });
3305
- statusEl.textContent = "Failed";
3306
- errEl.textContent = msg;
3307
- return;
3607
+ const intent = assertSuccessfulResponse(resp, "Intent creation failed");
3608
+ const terms = intent.current_terms;
3609
+ const option = terms && terms.payment_options && terms.payment_options.filter(function (candidate) {
3610
+ return candidate.status === PURCHASE_INTENT.ELIGIBLE && candidate.method_kind === preference.method_kind;
3611
+ })[0];
3612
+ if (!terms || (!option && !isExactlyZeroMoney(terms.money.rail_amount))) {
3613
+ throw new Error("No eligible payment option");
3308
3614
  }
3309
- var b = resp.body;
3310
- send({ type: MSG.CHECKOUT_STATUS, status: "success", context: { order_id: b.order_id, order_number: b.order_number, provider: b.provider } });
3615
+ const confirmationPath = PURCHASE_INTENT.PATH + "/" + encodeURIComponent(intent.id) + "/confirm";
3616
+ const confirmationBody = JSON.stringify({
3617
+ accepted_intent_revision: terms.intent_revision,
3618
+ accepted_terms_version: terms.terms_version,
3619
+ accepted_due_now: terms.money.due_now,
3620
+ accepted_customer_debit_amount: option
3621
+ ? option.customer_debit_amount
3622
+ : terms.money.rail_amount,
3623
+ currency: terms.money.currency,
3624
+ payment_option_id: option ? option.id : null,
3625
+ idempotency_key: "mock-confirm:" + intent.id
3626
+ });
3627
+ function confirmIntent() {
3628
+ return fetch(confirmationPath, {
3629
+ method: "POST",
3630
+ headers: headers,
3631
+ body: confirmationBody
3632
+ })
3633
+ .then(responseWithBody)
3634
+ .then(function (response) {
3635
+ return assertSuccessfulResponse(response, "Intent confirmation failed");
3636
+ });
3637
+ }
3638
+
3639
+ return confirmIntent().then(function (outcome) {
3640
+ // This mock has no external provider window. Replaying the exact
3641
+ // idempotent confirmation models the provider-approved callback.
3642
+ return outcome.status === PURCHASE_INTENT.CARD_POPUP_REQUIRED
3643
+ ? confirmIntent()
3644
+ : outcome;
3645
+ });
3646
+ })
3647
+ .then(function (outcome) {
3648
+ if (outcome.status !== PURCHASE_INTENT.AUTHORIZATION_REQUIRED) return outcome;
3649
+ return fetch(
3650
+ PURCHASE_INTENT.PATH + "/" + encodeURIComponent(outcome.intent_id) +
3651
+ "/attempts/" + encodeURIComponent(outcome.attempt_id) + "/actions",
3652
+ {
3653
+ method: "POST",
3654
+ headers: headers,
3655
+ body: JSON.stringify({ authorization: { type: AUTHORIZATION.OTP, value: DEMO_OTP } })
3656
+ }
3657
+ )
3658
+ .then(responseWithBody)
3659
+ .then(function (response) {
3660
+ return assertSuccessfulResponse(response, "Authorization failed");
3661
+ });
3662
+ })
3663
+ .then(function (outcome) {
3664
+ if (outcome.status !== PURCHASE_INTENT.ORDER_CREATED) {
3665
+ throw new Error("Checkout requires " + outcome.status.replace(/_/g, " "));
3666
+ }
3667
+ send({
3668
+ type: MSG.CHECKOUT_STATUS,
3669
+ status: CHECKOUT_STATUS.SUCCESS,
3670
+ context: { order_id: outcome.order_id }
3671
+ });
3311
3672
  send({
3312
3673
  type: MSG.CHECKOUT_COMPLETE,
3313
3674
  success: true,
3314
3675
  order: {
3315
- id: b.order_id,
3316
- order_number: b.order_number,
3317
- status: b.requires_authorization ? "pending" : "confirmed",
3676
+ id: outcome.order_id,
3677
+ order_number: outcome.order_number || outcome.order_id,
3678
+ status: ORDER_STATUS.CONFIRMED,
3318
3679
  total: (state.cart && state.cart.total_price) || "0.00",
3319
3680
  currency: (state.cart && state.cart.currency) || "GHS"
3320
3681
  }
3321
3682
  });
3322
- statusEl.textContent = "Success \u2014 order " + b.order_number;
3683
+ statusEl.textContent = "Success \u2014 order " + outcome.order_id;
3323
3684
  resultEl.style.display = "block";
3324
- resultEl.textContent = JSON.stringify(b, null, 2);
3685
+ resultEl.textContent = JSON.stringify(outcome, null, 2);
3325
3686
  })
3326
3687
  .catch(function (e) {
3327
- send({ type: MSG.CHECKOUT_COMPLETE, success: false, error: { code: "NETWORK", message: String(e), recoverable: true } });
3328
- statusEl.textContent = "Network error";
3688
+ send({
3689
+ type: MSG.CHECKOUT_COMPLETE,
3690
+ success: false,
3691
+ error: { code: CHECKOUT_ERROR.FAILED, message: String(e), recoverable: true }
3692
+ });
3693
+ statusEl.textContent = "Failed";
3329
3694
  errEl.textContent = String(e);
3330
3695
  });
3331
3696
  }
@@ -3342,7 +3707,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3342
3707
  break;
3343
3708
  case MSG.SET_CART:
3344
3709
  state.cart = msg.cart;
3345
- var s = document.getElementById("status");
3710
+ const s = document.getElementById("status");
3346
3711
  if (s) s.textContent = "Cart loaded \u2014 " + (msg.cart && msg.cart.items ? msg.cart.items.length : 0) + " item(s), " + ((msg.cart && msg.cart.currency) || "GHS") + " " + ((msg.cart && msg.cart.total_price) || "0.00");
3347
3712
  reportHeight();
3348
3713
  break;
@@ -3366,7 +3731,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
3366
3731
  }
3367
3732
  break;
3368
3733
  case MSG.ABORT_CHECKOUT:
3369
- send({ type: MSG.CHECKOUT_STATUS, status: "failed", context: {} });
3734
+ send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.FAILED, context: {} });
3370
3735
  break;
3371
3736
  }
3372
3737
  }
@@ -3980,6 +4345,7 @@ function createAuthService(opts) {
3980
4345
  token: id,
3981
4346
  business_id: options.defaultBusinessId,
3982
4347
  is_authenticated: authenticated,
4348
+ account_id: authenticated ? ids.raw("account") : void 0,
3983
4349
  customer_id: authenticated ? ids.customer() : void 0,
3984
4350
  customer_name: profile?.name,
3985
4351
  customer_email: profile?.email,
@@ -4062,7 +4428,7 @@ function createAuthService(opts) {
4062
4428
  if (!s) return { is_authenticated: false, user_type: "guest" };
4063
4429
  return {
4064
4430
  is_authenticated: s.is_authenticated,
4065
- account_id: s.customer_id,
4431
+ account_id: s.account_id,
4066
4432
  user_type: s.is_authenticated ? "customer" : "guest",
4067
4433
  session_expires_at: s.expires_at ? Math.floor(new Date(s.expires_at).getTime() / 1e3) : void 0,
4068
4434
  session: s
@@ -5360,88 +5726,10 @@ function createCartService(opts) {
5360
5726
  };
5361
5727
  }
5362
5728
 
5363
- // src/mock/domain/checkout.ts
5364
- function createCheckoutService(opts) {
5365
- const { registry, clock, ids, bus } = opts;
5729
+ // src/mock/domain/payment.ts
5730
+ function createPaymentService(options) {
5731
+ const { registry, clock, bus } = options;
5366
5732
  return {
5367
- process(session, body) {
5368
- if (!body.cart_id) throw validation("cart_id required");
5369
- const c = registry.carts.get(body.cart_id);
5370
- if (!c) throw notFound(`Cart ${body.cart_id} not found`);
5371
- if (c.items.length === 0) throw validation("Cart is empty", { code: "CART_EMPTY" });
5372
- const orderId = ids.order();
5373
- const orderNumber = ids.orderNumber();
5374
- const paymentId = ids.payment();
5375
- const reference = ids.payment();
5376
- const requiresAuth = body.payment_method === "mobile_money";
5377
- const isCard = body.payment_method === "card" || body.payment_method === "stripe";
5378
- const cardProvider = body.payment_method === "stripe" || body.pay_currency?.toUpperCase() === "USD" ? "stripe" : "paystack";
5379
- const order = {
5380
- id: orderId,
5381
- business_id: c.business_id,
5382
- user_friendly_id: orderNumber,
5383
- channel: c.channel,
5384
- status: requiresAuth ? "pending" : "confirmed",
5385
- payment_state: requiresAuth ? "not_paid" : "paid",
5386
- order_type: body.order_type,
5387
- customer_id: session.customer_id,
5388
- customer_name: body.customer.name,
5389
- customer_email: body.customer.email,
5390
- customer_phone: body.customer.phone,
5391
- delivery_address: body.address_info?.street_address,
5392
- delivery_fee: c.delivery_fee,
5393
- subtotal: c.subtotal,
5394
- tax_amount: c.tax_amount,
5395
- service_charge: c.service_charge,
5396
- total_discounts: c.total_discounts,
5397
- total_price: c.total_price,
5398
- currency: c.currency,
5399
- items: c.items,
5400
- applied_discount_ids: c.applied_discount_ids,
5401
- applied_discount_codes: c.applied_discount_codes,
5402
- payment_id: paymentId,
5403
- payment_reference: reference,
5404
- metadata: body.metadata,
5405
- created_at: clock.iso(),
5406
- updated_at: clock.iso()
5407
- };
5408
- registry.orders.put(orderId, order);
5409
- registry.carts.put(c.id, { ...c, status: "converted", updated_at: clock.iso() });
5410
- bus.emit("order.created", { order_id: orderId, order_number: orderNumber }, c.business_id);
5411
- if (!requiresAuth) {
5412
- bus.emit("order.payment.captured", { order_id: orderId, reference }, c.business_id);
5413
- bus.emit("order.confirmed", { order_id: orderId }, c.business_id);
5414
- }
5415
- const billToken = `bt_${reference.slice(-12)}_${Math.random().toString(36).slice(2, 10)}`;
5416
- const result = {
5417
- order_id: orderId,
5418
- order_number: orderNumber,
5419
- payment_id: paymentId,
5420
- payment_status: requiresAuth || isCard ? "pending" : "succeeded",
5421
- payment_reference: reference,
5422
- requires_authorization: requiresAuth,
5423
- authorization_type: requiresAuth ? "otp" : void 0,
5424
- next_action: requiresAuth ? {
5425
- type: "authorization",
5426
- authorization_type: "otp",
5427
- display_text: "Approve on your mobile money app"
5428
- } : { type: "none" },
5429
- provider: isCard ? cardProvider : body.payment_method,
5430
- bill_token: billToken
5431
- };
5432
- if (isCard) {
5433
- const proc = globalThis.process;
5434
- result.client_secret = cardProvider === "stripe" ? `pi_${orderId.slice(-12)}_secret_${Math.random().toString(36).slice(2, 18)}` : `access_${reference}`;
5435
- result.public_key = cardProvider === "stripe" ? proc?.env?.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ?? "pk_test_mock_stripe" : proc?.env?.NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY ?? "pk_test_mock_paystack";
5436
- result.next_action = {
5437
- type: "card_popup",
5438
- provider: cardProvider,
5439
- client_secret: result.client_secret,
5440
- public_key: result.public_key
5441
- };
5442
- }
5443
- return result;
5444
- },
5445
5733
  authorize(body) {
5446
5734
  if (!body.payment_id?.trim()) throw validation("payment_id required");
5447
5735
  if (!body.authorization) throw validation("authorization required");
@@ -5452,7 +5740,7 @@ function createCheckoutService(opts) {
5452
5740
  const updated = {
5453
5741
  ...order,
5454
5742
  payment_state: "paid",
5455
- status: "confirmed",
5743
+ status: CHECKOUT_ORDER_RESULT_STATUS.CONFIRMED,
5456
5744
  updated_at: clock.iso()
5457
5745
  };
5458
5746
  registry.orders.put(order.id, updated);
@@ -5463,10 +5751,544 @@ function createCheckoutService(opts) {
5463
5751
  order_number: order.user_friendly_id,
5464
5752
  payment_id: body.payment_id,
5465
5753
  payment_reference: order.payment_reference,
5466
- payment_status: "succeeded",
5754
+ payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.SUCCEEDED,
5467
5755
  requires_authorization: false,
5468
- next_action: { type: "none" }
5756
+ next_action: { type: CHECKOUT_NEXT_ACTION.NONE }
5757
+ };
5758
+ }
5759
+ };
5760
+ }
5761
+
5762
+ // src/mock/domain/purchase-intent.ts
5763
+ var INTENT_LIFETIME_MS = 15 * 60 * 1e3;
5764
+ var ZERO_MONEY = "0.00";
5765
+ var MOCK_PROTECTION = {
5766
+ status: CHECKOUT_PROTECTION_STATUS.PROTECTED,
5767
+ message: "Covered by Cimplify Protection"
5768
+ };
5769
+ var PURCHASE_INTENT_MOBILE_MONEY_NETWORKS = new Set(
5770
+ Object.values(PURCHASE_INTENT_MOBILE_MONEY_NETWORK)
5771
+ );
5772
+ var MOCK_PURCHASE_INTENT = {
5773
+ OWNER_KIND: { ACCOUNT: "account", STOREFRONT_SESSION: "storefront_session" },
5774
+ CART_STATUS: { ACTIVE: "active", CONVERTED: "converted" },
5775
+ ORDER_STATUS: { PENDING: "pending", CONFIRMED: "confirmed" },
5776
+ PAYMENT_STATE: { NOT_PAID: "not_paid", PAID: "paid" },
5777
+ EVENT: {
5778
+ ORDER_CREATED: "order.created",
5779
+ PAYMENT_CAPTURED: "order.payment.captured",
5780
+ ORDER_CONFIRMED: "order.confirmed"
5781
+ },
5782
+ ID_PREFIX: { INTENT: "pi", ATTEMPT: "attempt" },
5783
+ DEFAULT_LOCATION_ID: "mock-location",
5784
+ MOCK_PAYSTACK_PUBLIC_KEY: "pk_test_mock_paystack",
5785
+ COMPLIANCE: {
5786
+ POLICY_NAME: "mock-storefront-commerce"
5787
+ }
5788
+ };
5789
+ var DECIMAL_WIRE_VALUE = /^([+-]?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/;
5790
+ function isExactlyZero(value) {
5791
+ if (typeof value === "number") return Object.is(value, 0) || Object.is(value, -0);
5792
+ const match2 = DECIMAL_WIRE_VALUE.exec(value.trim());
5793
+ return Boolean(match2 && [...match2[2], ...match2[3] ?? ""].every((digit) => digit === "0"));
5794
+ }
5795
+ function sameDecimal(left, right) {
5796
+ const canonical = (value) => {
5797
+ const wire = typeof value === "number" ? String(value) : value.trim();
5798
+ const match2 = DECIMAL_WIRE_VALUE.exec(wire);
5799
+ if (!match2) return null;
5800
+ let digits = `${match2[2]}${match2[3] ?? ""}`.replace(/^0+/, "");
5801
+ if (!digits) return "0";
5802
+ let scale = BigInt((match2[3] ?? "").length) - BigInt(match2[4] ?? "0");
5803
+ while (digits.endsWith("0")) {
5804
+ digits = digits.slice(0, -1);
5805
+ scale -= 1n;
5806
+ }
5807
+ const sign = match2[1] === "-" ? "-" : "";
5808
+ return `${sign}${digits}:${scale}`;
5809
+ };
5810
+ const normalizedLeft = canonical(left);
5811
+ return normalizedLeft !== null && normalizedLeft === canonical(right);
5812
+ }
5813
+ function wireMoney(value) {
5814
+ return typeof value === "string" ? value.trim() : String(value);
5815
+ }
5816
+ function decimal(value) {
5817
+ return wireMoney(value.toFixed(2));
5818
+ }
5819
+ function paymentOptions(intentId, currency, amount, expiresAt) {
5820
+ const normalizedCurrency = currencyCode(currency);
5821
+ const normalizedAmount = wireMoney(amount);
5822
+ return [
5823
+ {
5824
+ id: `${intentId}:${PURCHASE_INTENT_PAYMENT_METHOD.CARD}`,
5825
+ rank: 0,
5826
+ method_kind: PURCHASE_INTENT_PAYMENT_METHOD.CARD,
5827
+ collection_mode: PAYMENT_COLLECTION_MODE.CREDENTIAL_CHARGE,
5828
+ provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
5829
+ principal_amount: normalizedAmount,
5830
+ fee_amount: wireMoney(ZERO_MONEY),
5831
+ customer_debit_amount: normalizedAmount,
5832
+ currency: normalizedCurrency,
5833
+ protection: MOCK_PROTECTION,
5834
+ status: PAYMENT_OPTION_STATUS.ELIGIBLE,
5835
+ expected_next_action: PAYMENT_OPTION_NEXT_ACTION.PROVIDER_APPROVAL,
5836
+ expires_at: expiresAt
5837
+ },
5838
+ {
5839
+ id: `${intentId}:${PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY}`,
5840
+ rank: 1,
5841
+ method_kind: PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY,
5842
+ collection_mode: PAYMENT_COLLECTION_MODE.REQUEST_TO_PAY,
5843
+ provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
5844
+ principal_amount: normalizedAmount,
5845
+ fee_amount: wireMoney(ZERO_MONEY),
5846
+ customer_debit_amount: normalizedAmount,
5847
+ currency: normalizedCurrency,
5848
+ protection: MOCK_PROTECTION,
5849
+ status: PAYMENT_OPTION_STATUS.ELIGIBLE,
5850
+ expected_next_action: PAYMENT_OPTION_NEXT_ACTION.PROVIDER_APPROVAL,
5851
+ expires_at: expiresAt
5852
+ }
5853
+ ];
5854
+ }
5855
+ function termsForCart(input) {
5856
+ const total = wireMoney(input.total);
5857
+ return {
5858
+ id: `${input.intentId}:terms:${input.version}`,
5859
+ intent_revision: input.revision,
5860
+ terms_version: input.version,
5861
+ terms_digest: `mock:${input.intentId}:${input.revision}:${input.version}`,
5862
+ money: {
5863
+ currency: currencyCode(input.currency),
5864
+ subtotal: wireMoney(input.subtotal),
5865
+ discount_total: wireMoney(input.discount),
5866
+ tax_total: wireMoney(input.tax),
5867
+ delivery_total: wireMoney(input.delivery),
5868
+ statutory_total: wireMoney(ZERO_MONEY),
5869
+ total_obligation: total,
5870
+ due_now: total,
5871
+ rail_amount: total,
5872
+ store_credit_consumed: wireMoney(ZERO_MONEY),
5873
+ voucher_credit_consumed: wireMoney(ZERO_MONEY)
5874
+ },
5875
+ material: {
5876
+ line_digest: `mock-lines:${input.intentId}`,
5877
+ destination_digest: `mock-destination:${input.intentId}`,
5878
+ delivery_promise_digest: `mock-delivery:${input.intentId}`,
5879
+ entitlement_digest: `mock-entitlement:${input.intentId}`,
5880
+ cancellation_terms_digest: `mock-cancellation:${input.intentId}`
5881
+ },
5882
+ compliance: {
5883
+ policy_name: MOCK_PURCHASE_INTENT.COMPLIANCE.POLICY_NAME,
5884
+ policy_version: 1,
5885
+ lane: CHECKOUT_COMPLIANCE_LANE.NATIVE,
5886
+ reason_code: CHECKOUT_COMPLIANCE_REASON.PHYSICAL_GOODS_NATIVE,
5887
+ evidence_digest: `mock-compliance:${input.intentId}`
5888
+ },
5889
+ payment_options: isExactlyZero(input.total) ? [] : paymentOptions(input.intentId, input.currency, input.total, input.validUntil),
5890
+ valid_until: input.validUntil,
5891
+ created_at: input.now
5892
+ };
5893
+ }
5894
+ function intentView(intent) {
5895
+ const {
5896
+ intent_owner: _owner,
5897
+ mint_request: _request,
5898
+ last_confirmation: _confirmation,
5899
+ ...view
5900
+ } = intent;
5901
+ return view;
5902
+ }
5903
+ function purchasingPrincipal(session) {
5904
+ if (!session.is_authenticated) {
5905
+ return { kind: PURCHASING_PRINCIPAL_KIND.GUEST };
5906
+ }
5907
+ if (!session.account_id) throw validation("Authenticated mock session has no account authority");
5908
+ return {
5909
+ kind: PURCHASING_PRINCIPAL_KIND.ACCOUNT,
5910
+ account_id: session.account_id
5911
+ };
5912
+ }
5913
+ function assertSupportedTenderPreference(request) {
5914
+ if (request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) return;
5915
+ const checkout = request.checkout;
5916
+ if (!checkout) throw validation("Cart checkout input is required");
5917
+ const preference = checkout.tender_preference;
5918
+ if (preference?.method_kind !== PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY || preference.authority.authority_kind !== MOBILE_MONEY_TENDER_AUTHORITY_KIND.TRANSIENT) {
5919
+ return;
5920
+ }
5921
+ if (!PURCHASE_INTENT_MOBILE_MONEY_NETWORKS.has(preference.authority.network)) {
5922
+ throw validation("Mobile Money network is not available for PurchaseIntent checkout");
5923
+ }
5924
+ }
5925
+ function intentOwner(session) {
5926
+ if (!session.is_authenticated) {
5927
+ return { kind: MOCK_PURCHASE_INTENT.OWNER_KIND.STOREFRONT_SESSION, id: session.id };
5928
+ }
5929
+ if (!session.account_id) throw validation("Authenticated mock session has no account authority");
5930
+ return { kind: MOCK_PURCHASE_INTENT.OWNER_KIND.ACCOUNT, id: session.account_id };
5931
+ }
5932
+ function createPurchaseIntentMockService(options) {
5933
+ const { registry, clock, ids, bus } = options;
5934
+ function stored(session, intentId) {
5935
+ const intent = registry.purchaseIntents.get(intentId);
5936
+ if (!intent) throw notFound(`Purchase intent ${intentId} not found`);
5937
+ const owner = intentOwner(session);
5938
+ if (intent.intent_owner.kind !== owner.kind || intent.intent_owner.id !== owner.id) {
5939
+ throw notFound(`Purchase intent ${intentId} not found`);
5940
+ }
5941
+ return intent;
5942
+ }
5943
+ function completeProviderOrder(intent, order, attemptId) {
5944
+ const updated = {
5945
+ ...order,
5946
+ payment_state: MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID,
5947
+ status: MOCK_PURCHASE_INTENT.ORDER_STATUS.CONFIRMED,
5948
+ updated_at: clock.iso()
5949
+ };
5950
+ registry.orders.put(order.id, updated);
5951
+ bus.emit(
5952
+ MOCK_PURCHASE_INTENT.EVENT.PAYMENT_CAPTURED,
5953
+ { order_id: order.id, reference: order.payment_reference },
5954
+ order.business_id
5955
+ );
5956
+ bus.emit(MOCK_PURCHASE_INTENT.EVENT.ORDER_CONFIRMED, { order_id: order.id }, order.business_id);
5957
+ const completed = {
5958
+ status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
5959
+ intent_id: intent.id,
5960
+ attempt_id: attemptId,
5961
+ order_id: order.id,
5962
+ order_number: order.user_friendly_id,
5963
+ payment_id: order.payment_id
5964
+ };
5965
+ registry.purchaseIntents.put(intent.id, {
5966
+ ...intent,
5967
+ updated_at: clock.iso(),
5968
+ last_confirmation: completed
5969
+ });
5970
+ return completed;
5971
+ }
5972
+ return {
5973
+ mint(session, request) {
5974
+ if (!request.client_intent_key.trim()) throw validation("client_intent_key required");
5975
+ if (request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) {
5976
+ throw validation("The mock currently prepares cart purchase intents only");
5977
+ }
5978
+ const checkoutInput = request.checkout;
5979
+ if (!checkoutInput) throw validation("Cart checkout input is required");
5980
+ assertSupportedTenderPreference(request);
5981
+ const cart = registry.carts.get(request.source.cart_id);
5982
+ if (!cart) throw notFound(`Cart ${request.source.cart_id} not found`);
5983
+ if (cart.items.length === 0) {
5984
+ throw validation("Cart is empty", { code: ErrorCode.CART_EMPTY });
5985
+ }
5986
+ const principal = purchasingPrincipal(session);
5987
+ const owner = intentOwner(session);
5988
+ const existing = registry.purchaseIntents.list(
5989
+ (candidate) => candidate.mint_request.client_intent_key === request.client_intent_key && candidate.intent_owner.kind === owner.kind && candidate.intent_owner.id === owner.id
5990
+ )[0];
5991
+ if (existing) return intentView(existing);
5992
+ const now = clock.iso();
5993
+ const expiresAt = new Date(clock.nowMs() + INTENT_LIFETIME_MS).toISOString();
5994
+ const intentId = ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.INTENT);
5995
+ const terms = termsForCart({
5996
+ intentId,
5997
+ revision: 1,
5998
+ version: 1,
5999
+ currency: cart.currency,
6000
+ subtotal: cart.subtotal,
6001
+ discount: cart.total_discounts,
6002
+ tax: cart.tax_amount,
6003
+ delivery: cart.delivery_fee,
6004
+ total: cart.total_price,
6005
+ now,
6006
+ validUntil: expiresAt
6007
+ });
6008
+ const intent = {
6009
+ id: intentId,
6010
+ seller_business_id: cart.business_id,
6011
+ principal,
6012
+ revision: 1,
6013
+ status: PURCHASE_INTENT_STATUS.OPEN,
6014
+ source: {
6015
+ kind: PURCHASE_INTENT_SOURCE_KIND.CART,
6016
+ source_id: cart.id,
6017
+ source_version: cart.updated_at,
6018
+ seller_business_id: cart.business_id,
6019
+ subject: {
6020
+ kind: PURCHASING_PRINCIPAL_KIND.BUSINESS,
6021
+ business_id: cart.business_id
6022
+ },
6023
+ binding_digest: `mock-cart:${cart.id}:${cart.updated_at}`
6024
+ },
6025
+ lines: cart.items.map((line, ordinal) => ({
6026
+ id: line.id,
6027
+ ordinal,
6028
+ sellable: {
6029
+ kind: line.variant_id ? PURCHASE_INTENT_SELLABLE_KIND.VARIANT : PURCHASE_INTENT_SELLABLE_KIND.PRODUCT,
6030
+ product_id: line.item_id,
6031
+ variant_id: line.variant_id,
6032
+ location_id: checkoutInput.location_id || MOCK_PURCHASE_INTENT.DEFAULT_LOCATION_ID
6033
+ },
6034
+ quantity: line.quantity,
6035
+ configuration: {
6036
+ kind: PURCHASE_INTENT_LINE_CONFIGURATION_KIND.SIMPLE,
6037
+ variant_id: line.variant_id,
6038
+ customer_inputs: line.customer_inputs
6039
+ }
6040
+ })),
6041
+ line_pricing: cart.items.map((line) => {
6042
+ const unitPrice = Number(line.price) + Number(line.add_ons_price);
6043
+ const lineSubtotal = unitPrice * line.quantity - Number(line.item_discount_amount);
6044
+ return {
6045
+ intent_line_id: line.id,
6046
+ quantity: line.quantity,
6047
+ unit_price: decimal(unitPrice),
6048
+ line_subtotal: decimal(lineSubtotal),
6049
+ currency: currencyCode(cart.currency)
6050
+ };
6051
+ }),
6052
+ current_terms: terms,
6053
+ expires_at: expiresAt,
6054
+ created_at: now,
6055
+ updated_at: now,
6056
+ intent_owner: owner,
6057
+ mint_request: request
6058
+ };
6059
+ registry.purchaseIntents.put(intentId, intent);
6060
+ return intentView(intent);
6061
+ },
6062
+ get(session, intentId) {
6063
+ return intentView(stored(session, intentId));
6064
+ },
6065
+ refresh(session, intentId, expectedRevision) {
6066
+ const intent = stored(session, intentId);
6067
+ if (intent.revision !== expectedRevision) {
6068
+ throw validation("expected_intent_revision does not match current intent revision");
6069
+ }
6070
+ if (!intent.current_terms) throw validation("Purchase intent has no current terms");
6071
+ const previous = intent.current_terms;
6072
+ const nextDelivery = Number(previous.money.delivery_total) + 1;
6073
+ const nextTotal = Number(previous.money.total_obligation) + 1;
6074
+ const now = clock.iso();
6075
+ const replacement = termsForCart({
6076
+ intentId,
6077
+ revision: intent.revision,
6078
+ version: previous.terms_version + 1,
6079
+ currency: previous.money.currency,
6080
+ subtotal: String(previous.money.subtotal),
6081
+ discount: String(previous.money.discount_total),
6082
+ tax: String(previous.money.tax_total),
6083
+ delivery: nextDelivery.toFixed(2),
6084
+ total: nextTotal.toFixed(2),
6085
+ now,
6086
+ validUntil: intent.expires_at
6087
+ });
6088
+ const updated = { ...intent, current_terms: replacement, updated_at: now };
6089
+ registry.purchaseIntents.put(intentId, updated);
6090
+ return intentView(updated);
6091
+ },
6092
+ confirm(session, intentId, request) {
6093
+ const intent = stored(session, intentId);
6094
+ if (intent.status === PURCHASE_INTENT_STATUS.CONVERTED && intent.last_confirmation) {
6095
+ const orderId2 = "order_id" in intent.last_confirmation ? intent.last_confirmation.order_id : void 0;
6096
+ const order2 = orderId2 ? registry.orders.get(orderId2) : void 0;
6097
+ if (order2?.payment_state === MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID) {
6098
+ return {
6099
+ status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
6100
+ intent_id: intent.id,
6101
+ attempt_id: "attempt_id" in intent.last_confirmation ? intent.last_confirmation.attempt_id : ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.ATTEMPT),
6102
+ order_id: order2.id
6103
+ };
6104
+ }
6105
+ if (order2?.payment_id && intent.last_confirmation.status === PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED) {
6106
+ return completeProviderOrder(intent, order2, intent.last_confirmation.attempt_id);
6107
+ }
6108
+ return intent.last_confirmation;
6109
+ }
6110
+ const terms = intent.current_terms;
6111
+ if (!terms) throw validation("Purchase intent has no current terms");
6112
+ const zeroRail = isExactlyZero(terms.money.rail_amount);
6113
+ const option = request.payment_option_id ? terms.payment_options.find(
6114
+ (candidate) => candidate.id === request.payment_option_id && candidate.status === PAYMENT_OPTION_STATUS.ELIGIBLE
6115
+ ) : void 0;
6116
+ const acceptedBaseTermsChanged = !sameDecimal(request.accepted_due_now, terms.money.due_now) || request.currency !== terms.money.currency;
6117
+ if (request.accepted_intent_revision !== intent.revision || request.accepted_terms_version !== terms.terms_version || acceptedBaseTermsChanged) {
6118
+ const deltas = acceptedBaseTermsChanged ? [
6119
+ {
6120
+ kind: CHECKOUT_TERMS_DELTA_KIND.MONEY,
6121
+ component: CHECKOUT_TERMS_COMPONENT.DUE_NOW,
6122
+ accepted: request.accepted_due_now,
6123
+ replacement: terms.money.due_now
6124
+ }
6125
+ ] : [
6126
+ {
6127
+ kind: CHECKOUT_TERMS_DELTA_KIND.PAYMENT_OPTION,
6128
+ accepted_option_id: request.payment_option_id ?? null,
6129
+ accepted_customer_debit_amount: request.accepted_customer_debit_amount,
6130
+ replacement_option_id: option?.id ?? null,
6131
+ replacement_customer_debit_amount: option?.customer_debit_amount ?? null
6132
+ }
6133
+ ];
6134
+ return {
6135
+ status: PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED,
6136
+ intent_id: intent.id,
6137
+ replacement: terms,
6138
+ deltas
6139
+ };
6140
+ }
6141
+ if (!zeroRail && !option || zeroRail && request.payment_option_id != null) {
6142
+ return {
6143
+ status: PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE,
6144
+ intent_id: intent.id,
6145
+ lines: intent.lines.map((line) => ({
6146
+ line_id: line.id,
6147
+ reason: CHECKOUT_INVALIDATION_REASON.PAYMENT_OPTION_CHANGED
6148
+ }))
6149
+ };
6150
+ }
6151
+ const acceptedCustomerDebitMatches = zeroRail ? isExactlyZero(request.accepted_customer_debit_amount) : Boolean(
6152
+ option && sameDecimal(request.accepted_customer_debit_amount, option.customer_debit_amount)
6153
+ );
6154
+ if (!acceptedCustomerDebitMatches) {
6155
+ return {
6156
+ status: PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED,
6157
+ intent_id: intent.id,
6158
+ replacement: terms,
6159
+ deltas: [
6160
+ {
6161
+ kind: CHECKOUT_TERMS_DELTA_KIND.PAYMENT_OPTION,
6162
+ accepted_option_id: request.payment_option_id ?? null,
6163
+ accepted_customer_debit_amount: request.accepted_customer_debit_amount,
6164
+ replacement_option_id: option?.id ?? null,
6165
+ replacement_customer_debit_amount: option?.customer_debit_amount ?? null
6166
+ }
6167
+ ]
6168
+ };
6169
+ }
6170
+ if (intent.mint_request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) {
6171
+ throw validation("The mock currently confirms cart purchase intents only");
6172
+ }
6173
+ const checkoutInput = intent.mint_request.checkout;
6174
+ if (!checkoutInput) throw validation("Cart checkout input is required");
6175
+ const cart = registry.carts.get(intent.mint_request.source.cart_id);
6176
+ if (!cart || cart.status !== MOCK_PURCHASE_INTENT.CART_STATUS.ACTIVE) {
6177
+ return { status: PURCHASE_INTENT_CONFIRM_STATUS.SOURCE_UNAVAILABLE, intent_id: intent.id };
6178
+ }
6179
+ const attemptId = ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.ATTEMPT);
6180
+ const orderId = ids.order();
6181
+ const orderNumber = ids.orderNumber();
6182
+ const paymentId = zeroRail ? void 0 : ids.payment();
6183
+ const paymentReference = zeroRail ? void 0 : ids.payment();
6184
+ const needsProvider = option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.CARD || option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY;
6185
+ const paid = zeroRail;
6186
+ const order = {
6187
+ id: orderId,
6188
+ business_id: cart.business_id,
6189
+ user_friendly_id: orderNumber,
6190
+ channel: cart.channel,
6191
+ status: paid ? MOCK_PURCHASE_INTENT.ORDER_STATUS.CONFIRMED : MOCK_PURCHASE_INTENT.ORDER_STATUS.PENDING,
6192
+ payment_state: paid ? MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID : MOCK_PURCHASE_INTENT.PAYMENT_STATE.NOT_PAID,
6193
+ order_type: checkoutInput.order_type,
6194
+ customer_id: session.customer_id,
6195
+ customer_name: checkoutInput.customer.name,
6196
+ customer_email: checkoutInput.customer.email,
6197
+ customer_phone: checkoutInput.customer.phone,
6198
+ delivery_address: checkoutInput.address_info.street_address,
6199
+ delivery_fee: cart.delivery_fee,
6200
+ subtotal: cart.subtotal,
6201
+ tax_amount: cart.tax_amount,
6202
+ service_charge: cart.service_charge,
6203
+ total_discounts: cart.total_discounts,
6204
+ total_price: cart.total_price,
6205
+ currency: cart.currency,
6206
+ items: cart.items,
6207
+ applied_discount_ids: cart.applied_discount_ids,
6208
+ applied_discount_codes: cart.applied_discount_codes,
6209
+ payment_id: paymentId,
6210
+ payment_reference: paymentReference,
6211
+ metadata: checkoutInput.metadata,
6212
+ created_at: clock.iso(),
6213
+ updated_at: clock.iso()
5469
6214
  };
6215
+ registry.orders.put(orderId, order);
6216
+ registry.carts.put(cart.id, {
6217
+ ...cart,
6218
+ status: MOCK_PURCHASE_INTENT.CART_STATUS.CONVERTED,
6219
+ updated_at: clock.iso()
6220
+ });
6221
+ bus.emit(
6222
+ MOCK_PURCHASE_INTENT.EVENT.ORDER_CREATED,
6223
+ { order_id: orderId, order_number: orderNumber },
6224
+ cart.business_id
6225
+ );
6226
+ if (paid) {
6227
+ if (paymentReference) {
6228
+ bus.emit(
6229
+ MOCK_PURCHASE_INTENT.EVENT.PAYMENT_CAPTURED,
6230
+ { order_id: orderId, reference: paymentReference },
6231
+ cart.business_id
6232
+ );
6233
+ }
6234
+ bus.emit(
6235
+ MOCK_PURCHASE_INTENT.EVENT.ORDER_CONFIRMED,
6236
+ { order_id: orderId },
6237
+ cart.business_id
6238
+ );
6239
+ }
6240
+ let confirmation;
6241
+ if (option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY) {
6242
+ confirmation = {
6243
+ status: PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED,
6244
+ intent_id: intent.id,
6245
+ attempt_id: attemptId,
6246
+ order_id: orderId,
6247
+ authorization_type: AUTHORIZATION_TYPE.OTP,
6248
+ display_text: "Approve on your mobile money app"
6249
+ };
6250
+ } else if (option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.CARD) {
6251
+ confirmation = {
6252
+ status: PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED,
6253
+ intent_id: intent.id,
6254
+ attempt_id: attemptId,
6255
+ order_id: orderId,
6256
+ provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
6257
+ client_secret: `access_${paymentReference}`,
6258
+ public_key: MOCK_PURCHASE_INTENT.MOCK_PAYSTACK_PUBLIC_KEY
6259
+ };
6260
+ } else {
6261
+ confirmation = {
6262
+ status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
6263
+ intent_id: intent.id,
6264
+ attempt_id: attemptId,
6265
+ order_id: orderId
6266
+ };
6267
+ }
6268
+ if (needsProvider && !paymentId) {
6269
+ throw validation("Provider-backed purchase intent has no payment authority");
6270
+ }
6271
+ registry.purchaseIntents.put(intent.id, {
6272
+ ...intent,
6273
+ status: PURCHASE_INTENT_STATUS.CONVERTED,
6274
+ converted_order_id: orderId,
6275
+ updated_at: clock.iso(),
6276
+ last_confirmation: confirmation
6277
+ });
6278
+ return confirmation;
6279
+ },
6280
+ submitAction(session, intentId, attemptId, request) {
6281
+ const intent = stored(session, intentId);
6282
+ const previous = intent.last_confirmation;
6283
+ if (!previous || previous.status !== PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED || previous.attempt_id !== attemptId) {
6284
+ throw validation("Purchase-intent attempt is not awaiting authorization");
6285
+ }
6286
+ if (!request.authorization) throw validation("authorization required");
6287
+ const order = registry.orders.get(previous.order_id);
6288
+ if (!order || !order.payment_id) {
6289
+ throw notFound(`Purchase-intent attempt ${attemptId} not found`);
6290
+ }
6291
+ return completeProviderOrder(intent, order, attemptId);
5470
6292
  }
5471
6293
  };
5472
6294
  }
@@ -10782,7 +11604,7 @@ var ID_PREFIX = {
10782
11604
  product: "prod_",
10783
11605
  service: "svc_"
10784
11606
  };
10785
- var DEFAULT_CURRENCY = "GHS";
11607
+ var DEFAULT_CURRENCY2 = "GHS";
10786
11608
  function slugify(value) {
10787
11609
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
10788
11610
  }
@@ -10817,7 +11639,7 @@ function makeSeedContext(registry) {
10817
11639
  const business = {
10818
11640
  business_type: "other",
10819
11641
  email: `hello@${handle}.test`,
10820
- default_currency: DEFAULT_CURRENCY,
11642
+ default_currency: DEFAULT_CURRENCY2,
10821
11643
  default_offers_table_service: false,
10822
11644
  default_accepts_online_orders: true,
10823
11645
  status: "active",
@@ -10996,7 +11818,8 @@ function createDeps(options = {}) {
10996
11818
  const inventory = createInventoryService({ registry });
10997
11819
  const cart = createCartService({ registry, clock, ids, bus, inventory });
10998
11820
  const orders = createOrderService({ registry, clock, bus });
10999
- const checkout = createCheckoutService({ registry, clock, ids, bus});
11821
+ const payments = createPaymentService({ registry, clock, bus });
11822
+ const purchaseIntents = createPurchaseIntentMockService({ registry, clock, ids, bus });
11000
11823
  const subscriptions = createSubscriptionService({ registry, clock, bus });
11001
11824
  const scheduling = createSchedulingService({ registry, clock, bus });
11002
11825
  const fx = createFxService({ registry, clock, ids });
@@ -11017,7 +11840,8 @@ function createDeps(options = {}) {
11017
11840
  business,
11018
11841
  catalogue,
11019
11842
  cart,
11020
- checkout,
11843
+ payments,
11844
+ purchaseIntents,
11021
11845
  orders,
11022
11846
  subscriptions,
11023
11847
  scheduling,