@cimplify/sdk 0.104.0 → 0.104.1

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