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