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