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