@cimplify/sdk 0.103.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.
- package/dist/advanced.d.mts +3 -7
- package/dist/advanced.d.ts +3 -7
- package/dist/advanced.js +23 -23
- package/dist/advanced.mjs +4 -4
- package/dist/{client-DHBwvh7U.d.ts → catalogue-BlQ85Uma.d.mts} +492 -748
- package/dist/{client-BfpNjJ_a.d.mts → catalogue-C8TqOj9K.d.ts} +492 -748
- package/dist/{payment-H5LoIvXU.d.mts → checkout-BqK9XUz5.d.mts} +4338 -1495
- package/dist/{payment-H5LoIvXU.d.ts → checkout-BqK9XUz5.d.ts} +4338 -1495
- package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
- package/dist/{chunk-C2JSNJUC.mjs → chunk-6G3ANKQA.mjs} +1080 -369
- package/dist/{chunk-QOWPGS7Z.js → chunk-BWEIMKLF.js} +572 -17
- package/dist/{chunk-JSWJ4XDE.js → chunk-DQEXY542.js} +1630 -824
- package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
- package/dist/{chunk-VIL6IQCG.js → chunk-HYFA67A7.js} +1084 -373
- package/dist/{chunk-H7ACMHM3.mjs → chunk-L5SNEU2O.mjs} +553 -16
- package/dist/{chunk-JG5CQNCB.js → chunk-RBNBSQI7.mjs} +221 -19
- package/dist/{chunk-VNPEYLB2.mjs → chunk-SFUJMS5E.js} +265 -2
- package/dist/{chunk-M4URVKHE.mjs → chunk-U54CWUUT.mjs} +11 -2
- package/dist/{chunk-GERXZQSX.mjs → chunk-UFOWPN3D.mjs} +967 -582
- package/dist/{chunk-J6PNHDHL.mjs → chunk-V4457OKB.mjs} +1580 -785
- package/dist/{chunk-R6OLQ33I.js → chunk-WXREIBFW.js} +1041 -654
- package/dist/{chunk-NNG6IFFX.js → chunk-XAUSDMMZ.js} +13 -3
- package/dist/{client-DpqRobY9.d.mts → client-DhXeiUrf.d.ts} +2 -2
- package/dist/{client-Dqg2mc-7.d.ts → client-mXiUrV4X.d.mts} +2 -2
- package/dist/{index-C8uDqLwJ.d.ts → index-B0AdZvrM.d.mts} +9 -4
- package/dist/{index-e2y6AK70.d.mts → index-CK8Nk5uQ.d.ts} +9 -4
- package/dist/index-DPWI5XcU.d.ts +8 -0
- package/dist/index-DVfUh-ci.d.mts +8 -0
- package/dist/index.d.mts +34 -15
- package/dist/index.d.ts +34 -15
- package/dist/index.js +306 -133
- package/dist/index.mjs +7 -6
- package/dist/mock/cli.mjs +1494 -395
- package/dist/mock/library.js +1220 -361
- package/dist/mock/library.mjs +1220 -361
- package/dist/mock/msw.js +1220 -361
- package/dist/mock/msw.mjs +1220 -361
- package/dist/{price-C2RqscMY.d.ts → price-6hQIIac9.d.ts} +1 -1
- package/dist/{price-BvePgDXe.d.mts → price-BUDv1rI7.d.mts} +1 -1
- package/dist/react.d.mts +10 -5
- package/dist/react.d.ts +10 -5
- package/dist/react.js +279 -225
- package/dist/react.mjs +140 -86
- package/dist/{server-DoXIpSd7.d.ts → server-DJ0mSs9i.d.mts} +33 -57
- package/dist/{server-C7FyLdrz.d.mts → server-DSTX76Ki.d.ts} +33 -57
- package/dist/server.d.mts +3 -4
- package/dist/server.d.ts +3 -4
- package/dist/server.js +12 -11
- package/dist/server.mjs +6 -5
- package/dist/styles.css +2 -2
- package/dist/testing/msw.d.mts +2 -2
- package/dist/testing/msw.d.ts +2 -2
- package/dist/testing/msw.js +4 -4
- package/dist/testing/msw.mjs +3 -3
- package/dist/testing/suite.d.mts +5 -6
- package/dist/testing/suite.d.ts +5 -6
- package/dist/testing/suite.js +29 -28
- package/dist/testing/suite.mjs +10 -9
- package/dist/testing.d.mts +2217 -14
- package/dist/testing.d.ts +2217 -14
- package/dist/testing.js +173 -83
- package/dist/testing.mjs +28 -10
- package/dist/utils.d.mts +3 -4
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +35 -31
- package/dist/utils.mjs +2 -2
- package/package.json +9 -9
- package/registry/chat-widget.json +1 -1
- package/registry/order-history.json +1 -1
- package/registry/order-summary.json +1 -1
- package/dist/link-cuNVYc2s.d.mts +0 -297
- package/dist/link-cuNVYc2s.d.ts +0 -297
package/dist/mock/library.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHmac } from 'crypto';
|
|
2
2
|
|
|
3
|
-
// ../node_modules/.bun/hono@4.
|
|
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.
|
|
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.
|
|
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
|
|
65
|
+
const headers = isRawRequest(request) ? request.headers : request.raw.headers;
|
|
54
66
|
const contentType = headers.get("Content-Type");
|
|
55
|
-
|
|
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
|
-
|
|
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.
|
|
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 (
|
|
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.
|
|
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
|
|
273
|
+
return tryDecodeURIComponent(value);
|
|
251
274
|
};
|
|
252
275
|
var _getQueryParam = (url, key, multiple) => {
|
|
253
276
|
let encoded;
|
|
254
|
-
if (!multiple && 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.
|
|
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 &&
|
|
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] =
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
912
|
-
if (typeof arg === "object" &&
|
|
913
|
-
|
|
914
|
-
for (const [key, value] of
|
|
915
|
-
if (key
|
|
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
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
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, {
|
|
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.
|
|
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.
|
|
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.
|
|
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}
|
|
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.
|
|
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.
|
|
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,
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
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
|
-
|
|
1516
|
-
|
|
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
|
-
|
|
1521
|
-
|
|
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;
|
|
1555
|
+
nextNode.#varIndex ??= context.varIndex++;
|
|
1556
|
+
paramMap.push([name, nextNode.#varIndex]);
|
|
1533
1557
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
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
|
|
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
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
1592
|
-
|
|
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.
|
|
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
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
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
|
-
|
|
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
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
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
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
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
|
+
}
|
|
1791
1809
|
}
|
|
1810
|
+
const handlerMap = [];
|
|
1811
|
+
for (const i in indexReplacementMap) {
|
|
1812
|
+
handlerMap[i] = handlerData[indexReplacementMap[i]];
|
|
1813
|
+
}
|
|
1814
|
+
return [regexp, handlerMap, staticMap];
|
|
1792
1815
|
}
|
|
1793
1816
|
};
|
|
1794
1817
|
|
|
1795
|
-
// ../node_modules/.bun/hono@4.
|
|
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.
|
|
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.
|
|
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.
|
|
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, "-")}`;
|
|
@@ -2100,6 +2198,8 @@ var ERROR_SUGGESTIONS = {
|
|
|
2100
2198
|
CARD_DECLINED: "Card was declined. Ask shopper to retry or switch payment method.",
|
|
2101
2199
|
INVALID_OTP: "Authorization code is invalid. Let shopper re-enter OTP/PIN and retry.",
|
|
2102
2200
|
OTP_EXPIRED: "Authorization code expired. Request a new OTP and re-submit authorization.",
|
|
2201
|
+
OTP_CHALLENGE_EXHAUSTED: "This verification challenge has no attempts left. Continue checkout without saved details or request a fresh challenge.",
|
|
2202
|
+
OTP_CHALLENGE_CONSUMED: "This verification challenge has already been used. Continue with the established session or request a fresh challenge.",
|
|
2103
2203
|
AUTHORIZATION_FAILED: "Additional payment authorization failed. Retry authorization or change payment method.",
|
|
2104
2204
|
PAYMENT_ACTION_NOT_COMPLETED: "Required payment action was not completed. Resume provider flow and poll for status.",
|
|
2105
2205
|
SLOT_UNAVAILABLE: "Selected schedule slot is unavailable. Refresh available slots and ask shopper to reselect.",
|
|
@@ -2118,6 +2218,10 @@ var ERROR_SUGGESTIONS = {
|
|
|
2118
2218
|
CHECKOUT_NOT_READY: "Checkout elements are still initializing. Wait for readiness before submit.",
|
|
2119
2219
|
CANCELLED: "Checkout was cancelled. Preserve cart state and allow shopper to retry.",
|
|
2120
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.",
|
|
2121
2225
|
POPUP_BLOCKED: "Browser blocked provider popup. Ask shopper to enable popups and retry.",
|
|
2122
2226
|
FX_QUOTE_FAILED: "Failed to lock FX quote. Retry currency quote or fallback to base currency."
|
|
2123
2227
|
};
|
|
@@ -2161,7 +2265,7 @@ function authMiddleware(deps) {
|
|
|
2161
2265
|
await next();
|
|
2162
2266
|
};
|
|
2163
2267
|
}
|
|
2164
|
-
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))(\/|$)/;
|
|
2165
2269
|
var IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2166
2270
|
function idempotencyMiddleware(deps) {
|
|
2167
2271
|
return async (c, next) => {
|
|
@@ -2171,7 +2275,8 @@ function idempotencyMiddleware(deps) {
|
|
|
2171
2275
|
const key = c.req.header("idempotency-key") ?? c.req.header("Idempotency-Key");
|
|
2172
2276
|
if (!key || key.length > 200) return next();
|
|
2173
2277
|
const session = c.var.session;
|
|
2174
|
-
const
|
|
2278
|
+
const routeScope = c.req.path.includes("/purchase-intents") ? `:${c.req.path}` : "";
|
|
2279
|
+
const recordId = `${session.id}${routeScope}:${key}`;
|
|
2175
2280
|
const existing = deps.registry.idempotency.get(recordId);
|
|
2176
2281
|
if (existing) {
|
|
2177
2282
|
const expiresAt = Date.parse(existing.expires_at);
|
|
@@ -2249,8 +2354,89 @@ var PAYMENT_METHOD = {
|
|
|
2249
2354
|
MOBILE_MONEY: "mobile_money",
|
|
2250
2355
|
CARD: "card"
|
|
2251
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
|
+
};
|
|
2252
2423
|
|
|
2253
2424
|
// src/mock/routes/v1.ts
|
|
2425
|
+
var PURCHASE_INTENT_ROUTE = {
|
|
2426
|
+
COLLECTION: "/purchase-intents",
|
|
2427
|
+
MEMBER: "/purchase-intents/:intent_id",
|
|
2428
|
+
REFRESH: "/purchase-intents/:intent_id/refresh",
|
|
2429
|
+
CONFIRM: "/purchase-intents/:intent_id/confirm",
|
|
2430
|
+
ACTION: "/purchase-intents/:intent_id/attempts/:attempt_id/actions"
|
|
2431
|
+
};
|
|
2432
|
+
var PURCHASE_INTENT_ROUTE_PARAM = {
|
|
2433
|
+
INTENT_ID: "intent_id",
|
|
2434
|
+
ATTEMPT_ID: "attempt_id"
|
|
2435
|
+
};
|
|
2436
|
+
var PAYMENT_ROUTE = {
|
|
2437
|
+
COLLECTION_OPTIONS: "/payments/collection-options",
|
|
2438
|
+
AUTHORIZATION: "/payments/authorization"
|
|
2439
|
+
};
|
|
2254
2440
|
function buildV1(deps) {
|
|
2255
2441
|
const app = new Hono2();
|
|
2256
2442
|
app.use("*", chaosMiddleware(deps));
|
|
@@ -2512,6 +2698,36 @@ function buildV1(deps) {
|
|
|
2512
2698
|
"/catalogue/quotes/:quote_id/refresh",
|
|
2513
2699
|
(c) => tryRoute(c, () => deps.catalogue.refreshQuote(c.req.param("quote_id")))
|
|
2514
2700
|
);
|
|
2701
|
+
app.get("/reviews/summary", (c) => {
|
|
2702
|
+
const productId = c.req.query("product_id");
|
|
2703
|
+
if (productId !== void 0 && (productId.length === 0 || productId.trim() !== productId)) {
|
|
2704
|
+
return fail(c, validation("product_id must be a non-empty canonical identifier"));
|
|
2705
|
+
}
|
|
2706
|
+
const response = {
|
|
2707
|
+
data: {
|
|
2708
|
+
ratings: {
|
|
2709
|
+
product_id: productId ?? null,
|
|
2710
|
+
review_count: 0,
|
|
2711
|
+
rating_sum: 0,
|
|
2712
|
+
average_rating: null,
|
|
2713
|
+
distribution: { one: 0, two: 0, three: 0, four: 0, five: 0 }
|
|
2714
|
+
},
|
|
2715
|
+
trust_chips: {
|
|
2716
|
+
on_time_delivery_percent: null,
|
|
2717
|
+
delivery_sample_size: 0,
|
|
2718
|
+
average_first_response_seconds: null,
|
|
2719
|
+
support_sample_size: 0
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
};
|
|
2723
|
+
return c.json(response);
|
|
2724
|
+
});
|
|
2725
|
+
app.get("/reviews", (c) => {
|
|
2726
|
+
const response = {
|
|
2727
|
+
data: { items: [], next_cursor: null }
|
|
2728
|
+
};
|
|
2729
|
+
return c.json(response);
|
|
2730
|
+
});
|
|
2515
2731
|
app.get("/cart", (c) => tryRoute(c, () => deps.cart.toUICart(deps.cart.get(c.var.session))));
|
|
2516
2732
|
app.delete("/cart", (c) => tryRoute(c, () => deps.cart.clear(c.var.session)));
|
|
2517
2733
|
app.get("/cart/items", (c) => tryRoute(c, () => deps.cart.getItems(c.var.session)));
|
|
@@ -2593,28 +2809,81 @@ function buildV1(deps) {
|
|
|
2593
2809
|
"/subscriptions/:subscription_id/skip",
|
|
2594
2810
|
(c) => tryRoute(c, () => deps.subscriptions.skipNext(c.req.param("subscription_id")))
|
|
2595
2811
|
);
|
|
2596
|
-
app.get(
|
|
2812
|
+
app.get(PAYMENT_ROUTE.COLLECTION_OPTIONS, async (c) => {
|
|
2597
2813
|
const currency = c.req.query("currency")?.trim().toUpperCase();
|
|
2598
2814
|
if (!currency || !/^[A-Z]{3}$/.test(currency)) {
|
|
2599
2815
|
return fail(c, validation("currency must be a canonical three-letter code"));
|
|
2600
2816
|
}
|
|
2601
2817
|
return tryRoute(c, () => ({
|
|
2602
2818
|
presentment_currency: currency,
|
|
2603
|
-
options: currency ===
|
|
2819
|
+
options: currency === DEFAULT_CURRENCY ? [
|
|
2604
2820
|
{ method: PAYMENT_METHOD.CARD, network: null },
|
|
2605
|
-
{
|
|
2606
|
-
|
|
2607
|
-
|
|
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
|
+
}
|
|
2608
2833
|
] : currency === "USD" ? [{ method: PAYMENT_METHOD.CARD, network: null }] : []
|
|
2609
2834
|
}));
|
|
2610
2835
|
});
|
|
2611
|
-
app.post(
|
|
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) => {
|
|
2851
|
+
const body = await c.req.json().catch(() => ({}));
|
|
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
|
+
);
|
|
2860
|
+
});
|
|
2861
|
+
app.post(PURCHASE_INTENT_ROUTE.CONFIRM, async (c) => {
|
|
2862
|
+
const body = await c.req.json().catch(() => ({}));
|
|
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) => {
|
|
2612
2873
|
const body = await c.req.json().catch(() => ({}));
|
|
2613
|
-
return tryRoute(
|
|
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
|
+
);
|
|
2614
2883
|
});
|
|
2615
|
-
app.post(
|
|
2884
|
+
app.post(PAYMENT_ROUTE.AUTHORIZATION, async (c) => {
|
|
2616
2885
|
const body = await c.req.json().catch(() => ({}));
|
|
2617
|
-
return tryRoute(c, () => deps.
|
|
2886
|
+
return tryRoute(c, () => deps.payments.authorize(body));
|
|
2618
2887
|
});
|
|
2619
2888
|
app.get("/auth/status", (c) => tryRoute(c, () => deps.auth.status(c.var.session.token)));
|
|
2620
2889
|
app.post("/auth/request-otp", async (c) => {
|
|
@@ -2920,6 +3189,7 @@ function createStateRegistry() {
|
|
|
2920
3189
|
collections: createMemoryStore(),
|
|
2921
3190
|
carts: createMemoryStore(),
|
|
2922
3191
|
orders: createMemoryStore(),
|
|
3192
|
+
purchaseIntents: createMemoryStore(),
|
|
2923
3193
|
subscriptions: createMemoryStore(),
|
|
2924
3194
|
bookings: createMemoryStore(),
|
|
2925
3195
|
services: createMemoryStore(),
|
|
@@ -3069,9 +3339,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3069
3339
|
// injected into the HTML page served by the mock. Edit this file directly \u2014
|
|
3070
3340
|
// type-check via tsgo + the IframeToParentMessage shape from the SDK.
|
|
3071
3341
|
(function () {
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3342
|
+
const TYPE = window.__CIMPLIFY_MOCK_ELEMENT__;
|
|
3343
|
+
const DEFAULT_BUSINESS = window.__CIMPLIFY_MOCK_BUSINESS__;
|
|
3344
|
+
const MSG = {
|
|
3075
3345
|
INIT: "init", SET_TOKEN: "set_token", SET_CART: "set_cart", GET_DATA: "get_data",
|
|
3076
3346
|
REFRESH_TOKEN: "refresh_token", LOGOUT: "logout",
|
|
3077
3347
|
PROCESS_CHECKOUT: "process_checkout", ABORT_CHECKOUT: "abort_checkout",
|
|
@@ -3084,12 +3354,36 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3084
3354
|
CHECKOUT_STATUS: "checkout_status", CHECKOUT_COMPLETE: "checkout_complete",
|
|
3085
3355
|
ORDER_TYPE_CHANGED: "order_type_changed", REQUEST_SUBMIT: "request_submit"
|
|
3086
3356
|
};
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
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 = {
|
|
3093
3387
|
businessId: DEFAULT_BUSINESS,
|
|
3094
3388
|
token: null,
|
|
3095
3389
|
cart: null,
|
|
@@ -3100,15 +3394,33 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3100
3394
|
contact: null,
|
|
3101
3395
|
pendingOtp: null
|
|
3102
3396
|
};
|
|
3103
|
-
|
|
3104
|
-
|
|
3397
|
+
const content = document.getElementById("content");
|
|
3398
|
+
const title = document.getElementById("title");
|
|
3105
3399
|
|
|
3106
3400
|
function send(msg) {
|
|
3107
3401
|
if (NONCE) msg.nonce = NONCE;
|
|
3108
3402
|
parent.postMessage(msg, ORIGIN);
|
|
3109
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
|
+
}
|
|
3110
3409
|
function reportHeight() { send({ type: MSG.HEIGHT_CHANGE, height: document.documentElement.scrollHeight }); }
|
|
3111
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
|
+
}
|
|
3112
3424
|
|
|
3113
3425
|
function defaultAddress() {
|
|
3114
3426
|
return {
|
|
@@ -3118,9 +3430,20 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3118
3430
|
};
|
|
3119
3431
|
}
|
|
3120
3432
|
function defaultPayment(type) {
|
|
3121
|
-
if (type ===
|
|
3122
|
-
|
|
3123
|
-
|
|
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
|
+
};
|
|
3124
3447
|
}
|
|
3125
3448
|
|
|
3126
3449
|
function renderAuth() {
|
|
@@ -3145,12 +3468,12 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3145
3468
|
' <div class="option" data-key="lagos">5 Allen Ave, Ikeja, Lagos</div>' +
|
|
3146
3469
|
'</div>' +
|
|
3147
3470
|
'<button id="confirm">Use this address</button>';
|
|
3148
|
-
|
|
3471
|
+
const options = {
|
|
3149
3472
|
"default": defaultAddress(),
|
|
3150
3473
|
"east-legon": { street_address: "8 Ring Road", city: "Accra", region: "Greater Accra", country: "GH", latitude: 5.65, longitude: -0.16 },
|
|
3151
3474
|
"lagos": { street_address: "5 Allen Avenue", city: "Lagos", region: "Lagos", country: "NG", latitude: 6.5244, longitude: 3.3792 }
|
|
3152
3475
|
};
|
|
3153
|
-
|
|
3476
|
+
let picked = "default";
|
|
3154
3477
|
Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
|
|
3155
3478
|
el.onclick = function () {
|
|
3156
3479
|
picked = el.getAttribute("data-key");
|
|
@@ -3173,10 +3496,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3173
3496
|
'<div class="options" id="pay-options">' +
|
|
3174
3497
|
' <div class="option selected" data-method="mobile_money">\u{1F4F1} MTN Mobile Money</div>' +
|
|
3175
3498
|
' <div class="option" data-method="card">\u{1F4B3} Card (Paystack)</div>' +
|
|
3176
|
-
' <div class="option" data-method="cash">\u{1F4B5} Cash on delivery</div>' +
|
|
3177
3499
|
'</div>' +
|
|
3178
3500
|
'<button id="confirm">Use this method</button>';
|
|
3179
|
-
|
|
3501
|
+
let picked = TENDER.MOBILE_MONEY;
|
|
3180
3502
|
Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
|
|
3181
3503
|
el.onclick = function () {
|
|
3182
3504
|
picked = el.getAttribute("data-method");
|
|
@@ -3220,7 +3542,6 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3220
3542
|
'</div>' +
|
|
3221
3543
|
'<label>Payment method</label>' +
|
|
3222
3544
|
'<select id="pay-method">' +
|
|
3223
|
-
' <option value="cash">Cash</option>' +
|
|
3224
3545
|
' <option value="mobile_money">Mobile Money (MTN)</option>' +
|
|
3225
3546
|
' <option value="card">Card (Paystack)</option>' +
|
|
3226
3547
|
'</select>' +
|
|
@@ -3237,65 +3558,139 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3237
3558
|
}
|
|
3238
3559
|
|
|
3239
3560
|
function runCheckout() {
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3561
|
+
const statusEl = document.getElementById("status");
|
|
3562
|
+
const errEl = document.getElementById("err");
|
|
3563
|
+
const resultEl = document.getElementById("result");
|
|
3243
3564
|
if (errEl) errEl.textContent = "";
|
|
3244
3565
|
if (!state.cart) { errEl.textContent = "No cart provided"; return; }
|
|
3245
|
-
|
|
3566
|
+
const customer = {
|
|
3246
3567
|
name: document.getElementById("cust-name").value.trim() || "Customer",
|
|
3247
3568
|
email: document.getElementById("cust-email").value.trim() || "noemail@example.com",
|
|
3248
3569
|
phone: document.getElementById("cust-phone").value.trim() || "+233000000000",
|
|
3249
3570
|
save_details: false
|
|
3250
3571
|
};
|
|
3251
|
-
|
|
3252
|
-
|
|
3572
|
+
const paymentMethod = document.getElementById("pay-method").value;
|
|
3573
|
+
const orderType = document.getElementById("order-type").value;
|
|
3253
3574
|
statusEl.textContent = "Processing\u2026";
|
|
3254
|
-
send({ type: MSG.CHECKOUT_STATUS, status:
|
|
3575
|
+
send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.PROCESSING, context: {} });
|
|
3255
3576
|
|
|
3256
|
-
|
|
3577
|
+
const headers = { "content-type": "application/json" };
|
|
3257
3578
|
if (state.token) headers["x-session-token"] = state.token;
|
|
3258
3579
|
|
|
3259
|
-
|
|
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, {
|
|
3260
3592
|
method: "POST",
|
|
3261
3593
|
headers: headers,
|
|
3262
3594
|
body: JSON.stringify({
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
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
|
+
}
|
|
3268
3603
|
})
|
|
3269
3604
|
})
|
|
3270
|
-
.then(
|
|
3605
|
+
.then(responseWithBody)
|
|
3271
3606
|
.then(function (resp) {
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
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");
|
|
3614
|
+
}
|
|
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, " "));
|
|
3278
3666
|
}
|
|
3279
|
-
|
|
3280
|
-
|
|
3667
|
+
send({
|
|
3668
|
+
type: MSG.CHECKOUT_STATUS,
|
|
3669
|
+
status: CHECKOUT_STATUS.SUCCESS,
|
|
3670
|
+
context: { order_id: outcome.order_id }
|
|
3671
|
+
});
|
|
3281
3672
|
send({
|
|
3282
3673
|
type: MSG.CHECKOUT_COMPLETE,
|
|
3283
3674
|
success: true,
|
|
3284
3675
|
order: {
|
|
3285
|
-
id:
|
|
3286
|
-
order_number:
|
|
3287
|
-
status:
|
|
3676
|
+
id: outcome.order_id,
|
|
3677
|
+
order_number: outcome.order_number || outcome.order_id,
|
|
3678
|
+
status: ORDER_STATUS.CONFIRMED,
|
|
3288
3679
|
total: (state.cart && state.cart.total_price) || "0.00",
|
|
3289
3680
|
currency: (state.cart && state.cart.currency) || "GHS"
|
|
3290
3681
|
}
|
|
3291
3682
|
});
|
|
3292
|
-
statusEl.textContent = "Success \u2014 order " +
|
|
3683
|
+
statusEl.textContent = "Success \u2014 order " + outcome.order_id;
|
|
3293
3684
|
resultEl.style.display = "block";
|
|
3294
|
-
resultEl.textContent = JSON.stringify(
|
|
3685
|
+
resultEl.textContent = JSON.stringify(outcome, null, 2);
|
|
3295
3686
|
})
|
|
3296
3687
|
.catch(function (e) {
|
|
3297
|
-
send({
|
|
3298
|
-
|
|
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";
|
|
3299
3694
|
errEl.textContent = String(e);
|
|
3300
3695
|
});
|
|
3301
3696
|
}
|
|
@@ -3312,7 +3707,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3312
3707
|
break;
|
|
3313
3708
|
case MSG.SET_CART:
|
|
3314
3709
|
state.cart = msg.cart;
|
|
3315
|
-
|
|
3710
|
+
const s = document.getElementById("status");
|
|
3316
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");
|
|
3317
3712
|
reportHeight();
|
|
3318
3713
|
break;
|
|
@@ -3336,7 +3731,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3336
3731
|
}
|
|
3337
3732
|
break;
|
|
3338
3733
|
case MSG.ABORT_CHECKOUT:
|
|
3339
|
-
send({ type: MSG.CHECKOUT_STATUS, status:
|
|
3734
|
+
send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.FAILED, context: {} });
|
|
3340
3735
|
break;
|
|
3341
3736
|
}
|
|
3342
3737
|
}
|
|
@@ -3407,9 +3802,10 @@ function buildLink(deps) {
|
|
|
3407
3802
|
app.post("/auth/request-otp", async (c) => {
|
|
3408
3803
|
const body = await c.req.json().catch(() => ({}));
|
|
3409
3804
|
return tryRoute(c, () => {
|
|
3410
|
-
deps.auth.requestOtp(body.contact ?? "");
|
|
3805
|
+
const challenge = deps.auth.requestOtp(body.contact ?? "");
|
|
3411
3806
|
return {
|
|
3412
3807
|
success: true,
|
|
3808
|
+
challenge_id: challenge.otp_id,
|
|
3413
3809
|
message: `OTP sent to ${body.contact ?? "contact"}`,
|
|
3414
3810
|
expires_in: 300,
|
|
3415
3811
|
is_new_account: false
|
|
@@ -3419,7 +3815,11 @@ function buildLink(deps) {
|
|
|
3419
3815
|
app.post("/auth/verify-otp", async (c) => {
|
|
3420
3816
|
const body = await c.req.json().catch(() => ({}));
|
|
3421
3817
|
return tryRoute(c, () => {
|
|
3422
|
-
const session = deps.auth.verifyOtp(
|
|
3818
|
+
const session = deps.auth.verifyOtp(
|
|
3819
|
+
body.contact ?? "",
|
|
3820
|
+
body.otp_code ?? "",
|
|
3821
|
+
body.challenge_id
|
|
3822
|
+
);
|
|
3423
3823
|
c.header("x-session-token", session.token);
|
|
3424
3824
|
const customer = deps.link.ensureCustomer(session.customer_id ?? session.id, {
|
|
3425
3825
|
email: body.contact?.includes("@") ? body.contact : void 0,
|
|
@@ -3513,10 +3913,7 @@ function buildLink(deps) {
|
|
|
3513
3913
|
};
|
|
3514
3914
|
});
|
|
3515
3915
|
});
|
|
3516
|
-
app.get(
|
|
3517
|
-
"/preferences",
|
|
3518
|
-
(c) => tryRoute(c, () => deps.link.getPreferences(customerId(c)))
|
|
3519
|
-
);
|
|
3916
|
+
app.get("/preferences", (c) => tryRoute(c, () => deps.link.getPreferences(customerId(c))));
|
|
3520
3917
|
app.post("/preferences", async (c) => {
|
|
3521
3918
|
const body = await c.req.json().catch(() => ({}));
|
|
3522
3919
|
return tryRoute(c, () => {
|
|
@@ -3948,6 +4345,7 @@ function createAuthService(opts) {
|
|
|
3948
4345
|
token: id,
|
|
3949
4346
|
business_id: options.defaultBusinessId,
|
|
3950
4347
|
is_authenticated: authenticated,
|
|
4348
|
+
account_id: authenticated ? ids.raw("account") : void 0,
|
|
3951
4349
|
customer_id: authenticated ? ids.customer() : void 0,
|
|
3952
4350
|
customer_name: profile?.name,
|
|
3953
4351
|
customer_email: profile?.email,
|
|
@@ -3993,9 +4391,16 @@ function createAuthService(opts) {
|
|
|
3993
4391
|
code_hint: options.mode === "permissive" ? code : void 0
|
|
3994
4392
|
};
|
|
3995
4393
|
},
|
|
3996
|
-
verifyOtp(contact, code) {
|
|
3997
|
-
|
|
3998
|
-
|
|
4394
|
+
verifyOtp(contact, code, challengeId) {
|
|
4395
|
+
let otp = challengeId ? registry.otps.get(challengeId) : void 0;
|
|
4396
|
+
if (!challengeId) {
|
|
4397
|
+
const matching = registry.otps.list((o) => o.contact === contact);
|
|
4398
|
+
otp = matching[matching.length - 1];
|
|
4399
|
+
}
|
|
4400
|
+
if (challengeId) {
|
|
4401
|
+
if (!otp) throw new Error("No OTP challenge found");
|
|
4402
|
+
if (otp.contact !== contact) throw new Error("OTP challenge does not match contact");
|
|
4403
|
+
}
|
|
3999
4404
|
if (options.mode === "strict") {
|
|
4000
4405
|
if (!otp) throw new Error("No OTP requested");
|
|
4001
4406
|
if (otp.code !== code && code !== options.defaultOtp) throw new Error("Invalid OTP");
|
|
@@ -4023,7 +4428,7 @@ function createAuthService(opts) {
|
|
|
4023
4428
|
if (!s) return { is_authenticated: false, user_type: "guest" };
|
|
4024
4429
|
return {
|
|
4025
4430
|
is_authenticated: s.is_authenticated,
|
|
4026
|
-
account_id: s.
|
|
4431
|
+
account_id: s.account_id,
|
|
4027
4432
|
user_type: s.is_authenticated ? "customer" : "guest",
|
|
4028
4433
|
session_expires_at: s.expires_at ? Math.floor(new Date(s.expires_at).getTime() / 1e3) : void 0,
|
|
4029
4434
|
session: s
|
|
@@ -5321,88 +5726,10 @@ function createCartService(opts) {
|
|
|
5321
5726
|
};
|
|
5322
5727
|
}
|
|
5323
5728
|
|
|
5324
|
-
// src/mock/domain/
|
|
5325
|
-
function
|
|
5326
|
-
const { registry, clock,
|
|
5729
|
+
// src/mock/domain/payment.ts
|
|
5730
|
+
function createPaymentService(options) {
|
|
5731
|
+
const { registry, clock, bus } = options;
|
|
5327
5732
|
return {
|
|
5328
|
-
process(session, body) {
|
|
5329
|
-
if (!body.cart_id) throw validation("cart_id required");
|
|
5330
|
-
const c = registry.carts.get(body.cart_id);
|
|
5331
|
-
if (!c) throw notFound(`Cart ${body.cart_id} not found`);
|
|
5332
|
-
if (c.items.length === 0) throw validation("Cart is empty", { code: "CART_EMPTY" });
|
|
5333
|
-
const orderId = ids.order();
|
|
5334
|
-
const orderNumber = ids.orderNumber();
|
|
5335
|
-
const paymentId = ids.payment();
|
|
5336
|
-
const reference = ids.payment();
|
|
5337
|
-
const requiresAuth = body.payment_method === "mobile_money";
|
|
5338
|
-
const isCard = body.payment_method === "card" || body.payment_method === "stripe";
|
|
5339
|
-
const cardProvider = body.payment_method === "stripe" || body.pay_currency?.toUpperCase() === "USD" ? "stripe" : "paystack";
|
|
5340
|
-
const order = {
|
|
5341
|
-
id: orderId,
|
|
5342
|
-
business_id: c.business_id,
|
|
5343
|
-
user_friendly_id: orderNumber,
|
|
5344
|
-
channel: c.channel,
|
|
5345
|
-
status: requiresAuth ? "pending" : "confirmed",
|
|
5346
|
-
payment_state: requiresAuth ? "not_paid" : "paid",
|
|
5347
|
-
order_type: body.order_type,
|
|
5348
|
-
customer_id: session.customer_id,
|
|
5349
|
-
customer_name: body.customer.name,
|
|
5350
|
-
customer_email: body.customer.email,
|
|
5351
|
-
customer_phone: body.customer.phone,
|
|
5352
|
-
delivery_address: body.address_info?.street_address,
|
|
5353
|
-
delivery_fee: c.delivery_fee,
|
|
5354
|
-
subtotal: c.subtotal,
|
|
5355
|
-
tax_amount: c.tax_amount,
|
|
5356
|
-
service_charge: c.service_charge,
|
|
5357
|
-
total_discounts: c.total_discounts,
|
|
5358
|
-
total_price: c.total_price,
|
|
5359
|
-
currency: c.currency,
|
|
5360
|
-
items: c.items,
|
|
5361
|
-
applied_discount_ids: c.applied_discount_ids,
|
|
5362
|
-
applied_discount_codes: c.applied_discount_codes,
|
|
5363
|
-
payment_id: paymentId,
|
|
5364
|
-
payment_reference: reference,
|
|
5365
|
-
metadata: body.metadata,
|
|
5366
|
-
created_at: clock.iso(),
|
|
5367
|
-
updated_at: clock.iso()
|
|
5368
|
-
};
|
|
5369
|
-
registry.orders.put(orderId, order);
|
|
5370
|
-
registry.carts.put(c.id, { ...c, status: "converted", updated_at: clock.iso() });
|
|
5371
|
-
bus.emit("order.created", { order_id: orderId, order_number: orderNumber }, c.business_id);
|
|
5372
|
-
if (!requiresAuth) {
|
|
5373
|
-
bus.emit("order.payment.captured", { order_id: orderId, reference }, c.business_id);
|
|
5374
|
-
bus.emit("order.confirmed", { order_id: orderId }, c.business_id);
|
|
5375
|
-
}
|
|
5376
|
-
const billToken = `bt_${reference.slice(-12)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
5377
|
-
const result = {
|
|
5378
|
-
order_id: orderId,
|
|
5379
|
-
order_number: orderNumber,
|
|
5380
|
-
payment_id: paymentId,
|
|
5381
|
-
payment_status: requiresAuth || isCard ? "pending" : "succeeded",
|
|
5382
|
-
payment_reference: reference,
|
|
5383
|
-
requires_authorization: requiresAuth,
|
|
5384
|
-
authorization_type: requiresAuth ? "otp" : void 0,
|
|
5385
|
-
next_action: requiresAuth ? {
|
|
5386
|
-
type: "authorization",
|
|
5387
|
-
authorization_type: "otp",
|
|
5388
|
-
display_text: "Approve on your mobile money app"
|
|
5389
|
-
} : { type: "none" },
|
|
5390
|
-
provider: isCard ? cardProvider : body.payment_method,
|
|
5391
|
-
bill_token: billToken
|
|
5392
|
-
};
|
|
5393
|
-
if (isCard) {
|
|
5394
|
-
const proc = globalThis.process;
|
|
5395
|
-
result.client_secret = cardProvider === "stripe" ? `pi_${orderId.slice(-12)}_secret_${Math.random().toString(36).slice(2, 18)}` : `access_${reference}`;
|
|
5396
|
-
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";
|
|
5397
|
-
result.next_action = {
|
|
5398
|
-
type: "card_popup",
|
|
5399
|
-
provider: cardProvider,
|
|
5400
|
-
client_secret: result.client_secret,
|
|
5401
|
-
public_key: result.public_key
|
|
5402
|
-
};
|
|
5403
|
-
}
|
|
5404
|
-
return result;
|
|
5405
|
-
},
|
|
5406
5733
|
authorize(body) {
|
|
5407
5734
|
if (!body.payment_id?.trim()) throw validation("payment_id required");
|
|
5408
5735
|
if (!body.authorization) throw validation("authorization required");
|
|
@@ -5413,7 +5740,7 @@ function createCheckoutService(opts) {
|
|
|
5413
5740
|
const updated = {
|
|
5414
5741
|
...order,
|
|
5415
5742
|
payment_state: "paid",
|
|
5416
|
-
status:
|
|
5743
|
+
status: CHECKOUT_ORDER_RESULT_STATUS.CONFIRMED,
|
|
5417
5744
|
updated_at: clock.iso()
|
|
5418
5745
|
};
|
|
5419
5746
|
registry.orders.put(order.id, updated);
|
|
@@ -5424,10 +5751,538 @@ function createCheckoutService(opts) {
|
|
|
5424
5751
|
order_number: order.user_friendly_id,
|
|
5425
5752
|
payment_id: body.payment_id,
|
|
5426
5753
|
payment_reference: order.payment_reference,
|
|
5427
|
-
payment_status:
|
|
5754
|
+
payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.SUCCEEDED,
|
|
5428
5755
|
requires_authorization: false,
|
|
5429
|
-
next_action: { type:
|
|
5756
|
+
next_action: { type: CHECKOUT_NEXT_ACTION.NONE }
|
|
5757
|
+
};
|
|
5758
|
+
}
|
|
5759
|
+
};
|
|
5760
|
+
}
|
|
5761
|
+
|
|
5762
|
+
// src/mock/domain/purchase-intent.ts
|
|
5763
|
+
var INTENT_LIFETIME_MS = 15 * 60 * 1e3;
|
|
5764
|
+
var ZERO_MONEY = "0.00";
|
|
5765
|
+
var 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
|
|
5430
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);
|
|
5431
6286
|
}
|
|
5432
6287
|
};
|
|
5433
6288
|
}
|
|
@@ -5810,6 +6665,7 @@ function createDeliveryService() {
|
|
|
5810
6665
|
rate_id: "dlvrt_mock_standard",
|
|
5811
6666
|
name: "Standard",
|
|
5812
6667
|
fee: standardFree ? money(0) : money(15),
|
|
6668
|
+
fee_status: "priced",
|
|
5813
6669
|
currency: "GHS",
|
|
5814
6670
|
free_delivery_applied: standardFree,
|
|
5815
6671
|
free_over_amount: money(200),
|
|
@@ -5823,6 +6679,7 @@ function createDeliveryService() {
|
|
|
5823
6679
|
name: "Express",
|
|
5824
6680
|
description: "Delivered within the hour",
|
|
5825
6681
|
fee: money(25),
|
|
6682
|
+
fee_status: "priced",
|
|
5826
6683
|
currency: "GHS",
|
|
5827
6684
|
free_delivery_applied: false,
|
|
5828
6685
|
eta_min_minutes: 30,
|
|
@@ -10741,7 +11598,7 @@ var ID_PREFIX = {
|
|
|
10741
11598
|
product: "prod_",
|
|
10742
11599
|
service: "svc_"
|
|
10743
11600
|
};
|
|
10744
|
-
var
|
|
11601
|
+
var DEFAULT_CURRENCY2 = "GHS";
|
|
10745
11602
|
function slugify(value) {
|
|
10746
11603
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
10747
11604
|
}
|
|
@@ -10776,7 +11633,7 @@ function makeSeedContext(registry) {
|
|
|
10776
11633
|
const business = {
|
|
10777
11634
|
business_type: "other",
|
|
10778
11635
|
email: `hello@${handle}.test`,
|
|
10779
|
-
default_currency:
|
|
11636
|
+
default_currency: DEFAULT_CURRENCY2,
|
|
10780
11637
|
default_offers_table_service: false,
|
|
10781
11638
|
default_accepts_online_orders: true,
|
|
10782
11639
|
status: "active",
|
|
@@ -10958,7 +11815,8 @@ function createDeps(options = {}) {
|
|
|
10958
11815
|
const inventory = createInventoryService({ registry });
|
|
10959
11816
|
const cart = createCartService({ registry, clock, ids, bus, inventory });
|
|
10960
11817
|
const orders = createOrderService({ registry, clock, bus });
|
|
10961
|
-
const
|
|
11818
|
+
const payments = createPaymentService({ registry, clock, bus });
|
|
11819
|
+
const purchaseIntents = createPurchaseIntentMockService({ registry, clock, ids, bus });
|
|
10962
11820
|
const subscriptions = createSubscriptionService({ registry, clock, bus });
|
|
10963
11821
|
const scheduling = createSchedulingService({ registry, clock, bus });
|
|
10964
11822
|
const fx = createFxService({ registry, clock, ids });
|
|
@@ -10979,7 +11837,8 @@ function createDeps(options = {}) {
|
|
|
10979
11837
|
business,
|
|
10980
11838
|
catalogue,
|
|
10981
11839
|
cart,
|
|
10982
|
-
|
|
11840
|
+
payments,
|
|
11841
|
+
purchaseIntents,
|
|
10983
11842
|
orders,
|
|
10984
11843
|
subscriptions,
|
|
10985
11844
|
scheduling,
|