@godaddy/gd-commerce-server 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -1
- package/dist/index.d.ts +107 -7
- package/dist/index.js +661 -477
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router } from "express";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
|
|
4
4
|
//#region src/lib/commerce/checkout-config.ts
|
|
5
5
|
const DEFAULT_CHECKOUT_CONFIGURATION = {
|
|
@@ -35,6 +35,68 @@ function parseCommerceCheckoutConfiguration(raw) {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/lib/commerce/errors.ts
|
|
40
|
+
var CommerceError = class extends Error {
|
|
41
|
+
httpStatus;
|
|
42
|
+
code;
|
|
43
|
+
publicMessage;
|
|
44
|
+
details;
|
|
45
|
+
constructor(httpStatus, code, message, options = {}) {
|
|
46
|
+
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
47
|
+
this.name = "CommerceError";
|
|
48
|
+
this.httpStatus = httpStatus;
|
|
49
|
+
this.code = code;
|
|
50
|
+
this.publicMessage = options.publicMessage;
|
|
51
|
+
this.details = options.details;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var InvalidRequestError = class extends CommerceError {
|
|
55
|
+
constructor(publicMessage, { message = publicMessage,...options } = {}) {
|
|
56
|
+
super(400, "invalid_request", message, {
|
|
57
|
+
...options,
|
|
58
|
+
publicMessage
|
|
59
|
+
});
|
|
60
|
+
this.name = "InvalidRequestError";
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var ScopeMismatchError = class extends CommerceError {
|
|
64
|
+
constructor() {
|
|
65
|
+
const publicMessage = "The connected store changed. Reload the page before continuing.";
|
|
66
|
+
super(409, "scope_mismatch", publicMessage, { publicMessage });
|
|
67
|
+
this.name = "ScopeMismatchError";
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var NotFoundError = class extends CommerceError {
|
|
71
|
+
constructor(publicMessage, { message = publicMessage,...options } = {}) {
|
|
72
|
+
super(404, "not_found", message, {
|
|
73
|
+
...options,
|
|
74
|
+
publicMessage
|
|
75
|
+
});
|
|
76
|
+
this.name = "NotFoundError";
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var CommerceNotConfiguredError = class extends CommerceError {
|
|
80
|
+
constructor(message, { publicMessage = "Commerce configuration is unavailable. Complete the store connection before continuing.",...options } = {}) {
|
|
81
|
+
super(503, "not_configured", message, {
|
|
82
|
+
...options,
|
|
83
|
+
publicMessage
|
|
84
|
+
});
|
|
85
|
+
this.name = "CommerceNotConfiguredError";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Commerce returned a failure, an unexpected response, or could not be reached. Always 502:
|
|
90
|
+
* even an upstream 401/403 concerns this server's client credentials, not the caller, and
|
|
91
|
+
* answering 401 would collide with the host's own authentication handling.
|
|
92
|
+
*/
|
|
93
|
+
var UpstreamError = class extends CommerceError {
|
|
94
|
+
constructor(message, { unauthorized = false,...options } = {}) {
|
|
95
|
+
super(502, unauthorized ? "upstream_unauthorized" : "upstream_error", message, options);
|
|
96
|
+
this.name = "UpstreamError";
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
38
100
|
//#endregion
|
|
39
101
|
//#region src/lib/commerce/config.ts
|
|
40
102
|
const DEFAULT_API_BASE_URL = "https://api.godaddy.com";
|
|
@@ -43,14 +105,14 @@ function normalizeApiBaseUrl(raw) {
|
|
|
43
105
|
try {
|
|
44
106
|
url = new URL(raw);
|
|
45
107
|
} catch {
|
|
46
|
-
throw new
|
|
108
|
+
throw new CommerceNotConfiguredError("Commerce config: apiBaseUrl must be a valid HTTPS origin.");
|
|
47
109
|
}
|
|
48
|
-
if (url.protocol !== "https:" || url.username || url.password || url.pathname !== "/" || url.search || url.hash) throw new
|
|
110
|
+
if (url.protocol !== "https:" || url.username || url.password || url.pathname !== "/" || url.search || url.hash) throw new CommerceNotConfiguredError("Commerce config: apiBaseUrl must be an HTTPS origin without credentials, path, query, or fragment.");
|
|
49
111
|
return url.origin;
|
|
50
112
|
}
|
|
51
113
|
function requireValue(environment, key) {
|
|
52
114
|
const value = environment[key]?.trim();
|
|
53
|
-
if (!value) throw new
|
|
115
|
+
if (!value) throw new CommerceNotConfiguredError(`Commerce config: ${key} is missing. Configure it in the server environment.`);
|
|
54
116
|
return value;
|
|
55
117
|
}
|
|
56
118
|
/** Read server environment values on each call. Loading files or secret stores belongs to the host. */
|
|
@@ -73,13 +135,81 @@ function createRuntimeCommerceConfiguration(options = {}) {
|
|
|
73
135
|
readCheckout: () => parseCommerceCheckoutConfiguration((options.environment ?? process.env).GODADDY_CHECKOUT_CONFIGURATION)
|
|
74
136
|
};
|
|
75
137
|
}
|
|
138
|
+
function fromHost(read) {
|
|
139
|
+
try {
|
|
140
|
+
return read();
|
|
141
|
+
} catch (cause) {
|
|
142
|
+
if (cause instanceof CommerceError) throw cause;
|
|
143
|
+
throw new CommerceNotConfiguredError("Commerce configuration could not be read", { cause });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const CONNECTION_STATES = new Set([
|
|
147
|
+
"unbound",
|
|
148
|
+
"connecting",
|
|
149
|
+
"ready"
|
|
150
|
+
]);
|
|
151
|
+
/** The host-reported connection state, or `undefined` when the host does not report one. */
|
|
152
|
+
function readCommerceConnectionState(configuration) {
|
|
153
|
+
const state = fromHost(() => configuration.readConnectionState?.());
|
|
154
|
+
if (state !== void 0 && !CONNECTION_STATES.has(state)) throw new CommerceNotConfiguredError("Invalid Commerce connection state.");
|
|
155
|
+
return state;
|
|
156
|
+
}
|
|
157
|
+
/** Nonready connection states must never use leftover credentials. */
|
|
158
|
+
function assertCommerceConnectionReady(configuration) {
|
|
159
|
+
const state = readCommerceConnectionState(configuration);
|
|
160
|
+
if (state === "unbound" || state === "connecting") throw new CommerceNotConfiguredError("Commerce store connection is not ready.", { details: { connectionState: state } });
|
|
161
|
+
}
|
|
162
|
+
function readReadyCommerceConfig(configuration) {
|
|
163
|
+
assertCommerceConnectionReady(configuration);
|
|
164
|
+
return fromHost(() => configuration.read());
|
|
165
|
+
}
|
|
166
|
+
function readReadyCheckoutConfig(configuration) {
|
|
167
|
+
assertCommerceConnectionReady(configuration);
|
|
168
|
+
return fromHost(() => configuration.readCheckout());
|
|
169
|
+
}
|
|
76
170
|
function commerceConfigurationForResponse(res) {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
return createRuntimeCommerceConfiguration();
|
|
171
|
+
const supplied = res.locals.commerceConfiguration;
|
|
172
|
+
return supplied && typeof supplied === "object" && "read" in supplied && typeof supplied.read === "function" && "readCheckout" in supplied && typeof supplied.readCheckout === "function" ? supplied : createRuntimeCommerceConfiguration();
|
|
80
173
|
}
|
|
81
174
|
function readCommerceConfigForResponse(res) {
|
|
82
|
-
return commerceConfigurationForResponse(res)
|
|
175
|
+
return readReadyCommerceConfig(commerceConfigurationForResponse(res));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/lib/commerce/upstream-http.ts
|
|
180
|
+
async function fetchUpstream(input, init, { operation, details }, fetchImplementation = fetch) {
|
|
181
|
+
try {
|
|
182
|
+
return await fetchImplementation(input, init);
|
|
183
|
+
} catch (cause) {
|
|
184
|
+
throw new UpstreamError(`${operation} could not reach Commerce`, {
|
|
185
|
+
cause,
|
|
186
|
+
details
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function readUpstreamJson(response, { operation, details }) {
|
|
191
|
+
try {
|
|
192
|
+
return await response.json();
|
|
193
|
+
} catch (cause) {
|
|
194
|
+
throw new UpstreamError(`${operation} returned a non-JSON response`, {
|
|
195
|
+
cause,
|
|
196
|
+
details: {
|
|
197
|
+
...details,
|
|
198
|
+
upstreamStatus: response.status
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/** Builds the error for a non-2xx response. 401 and 403 always mean this server's credentials were rejected. */
|
|
204
|
+
async function upstreamStatusError(response, { operation, details }, { unauthorized = false } = {}) {
|
|
205
|
+
if (!response.bodyUsed) await response.body?.cancel();
|
|
206
|
+
return new UpstreamError(`${operation} failed: upstream returned ${response.status}`, {
|
|
207
|
+
unauthorized: unauthorized || response.status === 401 || response.status === 403,
|
|
208
|
+
details: {
|
|
209
|
+
...details,
|
|
210
|
+
upstreamStatus: response.status
|
|
211
|
+
}
|
|
212
|
+
});
|
|
83
213
|
}
|
|
84
214
|
|
|
85
215
|
//#endregion
|
|
@@ -121,22 +251,52 @@ function checkoutGraphqlEndpoint({ apiBaseUrl }) {
|
|
|
121
251
|
function authorizationHeaders({ accessToken }) {
|
|
122
252
|
return { Authorization: `Bearer ${accessToken}` };
|
|
123
253
|
}
|
|
254
|
+
const UNAUTHORIZED_OAUTH_ERRORS = new Set([
|
|
255
|
+
"invalid_client",
|
|
256
|
+
"invalid_grant",
|
|
257
|
+
"unauthorized_client",
|
|
258
|
+
"invalid_scope"
|
|
259
|
+
]);
|
|
260
|
+
async function readOAuthError(response) {
|
|
261
|
+
try {
|
|
262
|
+
const body = await response.json();
|
|
263
|
+
return body && typeof body === "object" && "error" in body && typeof body.error === "string" ? body.error : void 0;
|
|
264
|
+
} catch {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
124
268
|
async function getOAuthAccessToken({ clientId, clientSecret, apiBaseUrl, scope = "commerce.product:read", fetch: fetchImplementation }) {
|
|
125
|
-
if (!clientId || !clientSecret) throw new
|
|
126
|
-
const requestFetch = fetchImplementation ?? fetch;
|
|
269
|
+
if (!clientId || !clientSecret) throw new CommerceNotConfiguredError("clientId and clientSecret are required");
|
|
127
270
|
const body = new URLSearchParams();
|
|
128
271
|
body.append("grant_type", "client_credentials");
|
|
129
272
|
body.append("client_id", clientId);
|
|
130
273
|
body.append("client_secret", clientSecret);
|
|
131
274
|
body.append("scope", scope);
|
|
132
|
-
const
|
|
275
|
+
const endpoint = new URL("/v2/oauth2/token", apiBaseUrl);
|
|
276
|
+
const request = {
|
|
277
|
+
operation: "Access token request",
|
|
278
|
+
details: {
|
|
279
|
+
endpoint: endpoint.href,
|
|
280
|
+
scope
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
const response = await fetchUpstream(endpoint, {
|
|
133
284
|
method: "POST",
|
|
134
285
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
135
286
|
body: body.toString(),
|
|
136
287
|
cache: "no-store"
|
|
137
|
-
});
|
|
138
|
-
if (!response.ok)
|
|
139
|
-
|
|
288
|
+
}, request, fetchImplementation);
|
|
289
|
+
if (!response.ok) {
|
|
290
|
+
const oauthError = response.status === 400 ? await readOAuthError(response) : void 0;
|
|
291
|
+
throw await upstreamStatusError(response, {
|
|
292
|
+
...request,
|
|
293
|
+
details: {
|
|
294
|
+
...request.details,
|
|
295
|
+
oauthError
|
|
296
|
+
}
|
|
297
|
+
}, { unauthorized: oauthError !== void 0 && UNAUTHORIZED_OAUTH_ERRORS.has(oauthError) });
|
|
298
|
+
}
|
|
299
|
+
return readUpstreamJson(response, request);
|
|
140
300
|
}
|
|
141
301
|
function buildBuyNowCheckoutInput({ storeId, skuId, quantity = 1, returnUrl, successUrl, channelId }, overrides = {}) {
|
|
142
302
|
return applyCheckoutDefaults({
|
|
@@ -174,10 +334,18 @@ function buildNonCatalogCheckoutInput({ storeId, lineItemData, quantity = 1, ret
|
|
|
174
334
|
|
|
175
335
|
//#endregion
|
|
176
336
|
//#region src/lib/commerce/gql.ts
|
|
177
|
-
|
|
337
|
+
/** `status` is the upstream HTTP status; the route responds with `httpStatus` (502) unless mapped. */
|
|
338
|
+
var GraphQLErrorWithCodes = class extends UpstreamError {
|
|
178
339
|
constructor(errors, status) {
|
|
179
340
|
const errorMessage = errors.length === 1 ? `${errors[0]?.message || "Unknown error"}` : errors.map((error) => error.message).filter(Boolean).join("; ");
|
|
180
|
-
super(errorMessage
|
|
341
|
+
super(errorMessage, {
|
|
342
|
+
unauthorized: [status, ...errors.map((error) => error.status)].some((value) => value === 401 || value === 403),
|
|
343
|
+
details: {
|
|
344
|
+
upstreamStatus: status,
|
|
345
|
+
upstreamCodes: errors.map((error) => error.code).filter(Boolean),
|
|
346
|
+
upstreamStatuses: errors.map((error) => error.status).filter((value) => typeof value === "number")
|
|
347
|
+
}
|
|
348
|
+
});
|
|
181
349
|
this.errors = errors;
|
|
182
350
|
this.status = status;
|
|
183
351
|
this.name = "GraphQLErrorWithCodes";
|
|
@@ -202,7 +370,7 @@ async function gqlRequest({ endpoint, query, variables, headers: headersInit, fe
|
|
|
202
370
|
const requestHeaders = new Headers(headersInit);
|
|
203
371
|
requestHeaders.set("Accept", "application/json");
|
|
204
372
|
requestHeaders.set("Content-Type", "application/json");
|
|
205
|
-
const response = await (
|
|
373
|
+
const response = await fetchUpstream(endpoint, {
|
|
206
374
|
method: "POST",
|
|
207
375
|
headers: requestHeaders,
|
|
208
376
|
body: JSON.stringify({
|
|
@@ -210,7 +378,10 @@ async function gqlRequest({ endpoint, query, variables, headers: headersInit, fe
|
|
|
210
378
|
variables: variables ?? {}
|
|
211
379
|
}),
|
|
212
380
|
cache: "no-store"
|
|
213
|
-
}
|
|
381
|
+
}, {
|
|
382
|
+
operation: "GraphQL request",
|
|
383
|
+
details: { endpoint }
|
|
384
|
+
}, fetchImplementation);
|
|
214
385
|
let result;
|
|
215
386
|
try {
|
|
216
387
|
result = await response.json();
|
|
@@ -229,7 +400,7 @@ async function gqlRequest({ endpoint, query, variables, headers: headersInit, fe
|
|
|
229
400
|
message: `GraphQL request failed: ${response.status} ${response.statusText}`,
|
|
230
401
|
status: response.status
|
|
231
402
|
}], response.status);
|
|
232
|
-
if (result.data === void 0) throw new
|
|
403
|
+
if (result.data === void 0) throw new UpstreamError("GraphQL response did not include data", { details: { endpoint } });
|
|
233
404
|
return result.data;
|
|
234
405
|
}
|
|
235
406
|
|
|
@@ -324,14 +495,14 @@ function promotionCodesEnabled(configuration) {
|
|
|
324
495
|
}
|
|
325
496
|
async function createCheckoutSession(params, configuration = createRuntimeCommerceConfiguration()) {
|
|
326
497
|
const { draftOrderId, skuId, quantity, lineItemData, returnUrl, successUrl } = params;
|
|
327
|
-
if (!returnUrl || !successUrl) throw new
|
|
498
|
+
if (!returnUrl || !successUrl) throw new InvalidRequestError("createCheckoutSession: returnUrl and successUrl are required");
|
|
328
499
|
if ([
|
|
329
500
|
draftOrderId,
|
|
330
501
|
skuId,
|
|
331
502
|
lineItemData
|
|
332
|
-
].filter(Boolean).length !== 1) throw new
|
|
333
|
-
const { storeId, channelId, clientId, clientSecret, apiBaseUrl, sourceApp, owner, currencyCode: configCurrencyCode } = configuration
|
|
334
|
-
const checkoutConfiguration = configuration
|
|
503
|
+
].filter(Boolean).length !== 1) throw new InvalidRequestError("createCheckoutSession: exactly one of draftOrderId, skuId, or lineItemData is required");
|
|
504
|
+
const { storeId, channelId, clientId, clientSecret, apiBaseUrl, sourceApp, owner, currencyCode: configCurrencyCode } = readReadyCommerceConfig(configuration);
|
|
505
|
+
const checkoutConfiguration = readReadyCheckoutConfig(configuration);
|
|
335
506
|
const enablePromotionCodes = promotionCodesEnabled(checkoutConfiguration);
|
|
336
507
|
const catalogShippingEnabled = lineItemData === void 0 && checkoutConfiguration.enableShipping;
|
|
337
508
|
const token = await getOAuthAccessToken({
|
|
@@ -394,16 +565,20 @@ async function createCheckoutSession(params, configuration = createRuntimeCommer
|
|
|
394
565
|
headers: authorizationHeaders({ accessToken: token.access_token })
|
|
395
566
|
});
|
|
396
567
|
} catch (error) {
|
|
397
|
-
throw new
|
|
568
|
+
throw new UpstreamError("Commerce checkout session could not be created", {
|
|
569
|
+
cause: error,
|
|
570
|
+
unauthorized: error instanceof UpstreamError && error.code === "upstream_unauthorized",
|
|
571
|
+
details: error instanceof UpstreamError ? error.details : void 0
|
|
572
|
+
});
|
|
398
573
|
}
|
|
399
574
|
const session = result.createCheckoutSession;
|
|
400
|
-
if (!session?.url || !session.id) throw new
|
|
401
|
-
if (session.storeId !== storeId || session.channelId !== channelId) throw new
|
|
575
|
+
if (!session?.url || !session.id) throw new UpstreamError("Checkout session was not created");
|
|
576
|
+
if (session.storeId !== storeId || session.channelId !== channelId) throw new UpstreamError(`Checkout session binding mismatch: expected store ${storeId} and channel ${channelId}, received store ${session.storeId ?? "missing"} and channel ${session.channelId ?? "missing"}`);
|
|
402
577
|
const expectedShipping = lineItemData === void 0 && checkoutConfiguration.enableShipping;
|
|
403
|
-
if (lineItemData === void 0 && enablePromotionCodes && session.enablePromotionCodes !== true) throw new
|
|
404
|
-
if (checkoutConfiguration.enableTaxCollection && session.enableTaxCollection !== true) throw new
|
|
405
|
-
if (expectedShipping && (session.enableShipping !== true || session.enableShippingAddressCollection !== true)) throw new
|
|
406
|
-
if (!session.paymentMethods?.card?.processor) throw new
|
|
578
|
+
if (lineItemData === void 0 && enablePromotionCodes && session.enablePromotionCodes !== true) throw new UpstreamError("Checkout session did not enable configured promotion codes");
|
|
579
|
+
if (checkoutConfiguration.enableTaxCollection && session.enableTaxCollection !== true) throw new UpstreamError("Checkout session did not enable configured tax collection");
|
|
580
|
+
if (expectedShipping && (session.enableShipping !== true || session.enableShippingAddressCollection !== true)) throw new UpstreamError("Checkout session did not enable configured shipping and address collection");
|
|
581
|
+
if (!session.paymentMethods?.card?.processor) throw new UpstreamError("Checkout session did not configure payment methods.");
|
|
407
582
|
return {
|
|
408
583
|
url: session.url,
|
|
409
584
|
id: session.id,
|
|
@@ -418,31 +593,73 @@ async function createCheckoutSession(params, configuration = createRuntimeCommer
|
|
|
418
593
|
|
|
419
594
|
//#endregion
|
|
420
595
|
//#region src/lib/commerce/get-order-status.ts
|
|
596
|
+
/** `CommerceOrderStatus.status` when the Orders API reports no payment status. */
|
|
597
|
+
const ORDER_STATUS_UNKNOWN = "unknown";
|
|
598
|
+
var InvalidOrderIdError = class extends InvalidRequestError {
|
|
599
|
+
constructor() {
|
|
600
|
+
super("missing or invalid orderId query parameter", { message: "getOrderStatus: a valid orderId is required" });
|
|
601
|
+
this.name = "InvalidOrderIdError";
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
var OrderNotFoundError = class extends NotFoundError {
|
|
605
|
+
constructor() {
|
|
606
|
+
super("Order not found");
|
|
607
|
+
this.name = "OrderNotFoundError";
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
function isBindingId(value) {
|
|
611
|
+
return typeof value === "string" && value !== "";
|
|
612
|
+
}
|
|
613
|
+
async function readErrorCode(response) {
|
|
614
|
+
try {
|
|
615
|
+
const body = await response.json();
|
|
616
|
+
return body && typeof body === "object" && "code" in body && typeof body.code === "string" ? body.code : null;
|
|
617
|
+
} catch {
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
421
621
|
async function getOrderStatus(orderId, configuration = createRuntimeCommerceConfiguration()) {
|
|
422
|
-
if (typeof orderId !== "string" || !orderId.trim() || orderId === "." || orderId === "..") throw new
|
|
423
|
-
const { storeId, channelId, clientId, clientSecret, apiBaseUrl, currencyCode } = configuration
|
|
622
|
+
if (typeof orderId !== "string" || !orderId || orderId !== orderId.trim() || orderId === "." || orderId === "..") throw new InvalidOrderIdError();
|
|
623
|
+
const { storeId, channelId, clientId, clientSecret, apiBaseUrl, currencyCode } = readReadyCommerceConfig(configuration);
|
|
424
624
|
const token = await getOAuthAccessToken({
|
|
425
625
|
clientId,
|
|
426
626
|
clientSecret,
|
|
427
627
|
apiBaseUrl,
|
|
428
628
|
scope: "commerce.order:read"
|
|
429
629
|
});
|
|
430
|
-
const
|
|
630
|
+
const endpoint = new URL(`/v1/commerce/stores/${encodeURIComponent(storeId)}/orders/${encodeURIComponent(orderId)}`, apiBaseUrl);
|
|
631
|
+
const request = {
|
|
632
|
+
operation: "Order lookup",
|
|
633
|
+
details: { endpoint: endpoint.href }
|
|
634
|
+
};
|
|
635
|
+
const response = await fetchUpstream(endpoint, {
|
|
431
636
|
method: "GET",
|
|
432
637
|
headers: {
|
|
433
638
|
...authorizationHeaders({ accessToken: token.access_token }),
|
|
434
639
|
Accept: "application/json"
|
|
435
640
|
},
|
|
436
641
|
cache: "no-store"
|
|
437
|
-
});
|
|
438
|
-
if (!response.ok)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
642
|
+
}, request);
|
|
643
|
+
if (!response.ok) {
|
|
644
|
+
const code = response.status === 404 || response.status === 422 ? await readErrorCode(response) : void 0;
|
|
645
|
+
if (response.status === 404 && code === "NOT_FOUND") throw new OrderNotFoundError();
|
|
646
|
+
if (response.status === 422 && code === "VALIDATION_FAILED") throw new InvalidOrderIdError();
|
|
647
|
+
throw await upstreamStatusError(response, {
|
|
648
|
+
...request,
|
|
649
|
+
details: {
|
|
650
|
+
...request.details,
|
|
651
|
+
upstreamCode: code ?? void 0
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
const order = (await readUpstreamJson(response, request))?.order;
|
|
656
|
+
const context = order?.context;
|
|
657
|
+
if (!isBindingId(order?.id) || order.id !== orderId || !isBindingId(context?.storeId) || !isBindingId(context?.channelId)) throw new UpstreamError("Order lookup did not return the requested order", { details: { endpoint: endpoint.href } });
|
|
658
|
+
if (context.storeId !== storeId || context.channelId !== channelId) throw new OrderNotFoundError();
|
|
442
659
|
const total = order.totals?.total;
|
|
443
660
|
return {
|
|
444
661
|
id: order.id,
|
|
445
|
-
status: order.statuses?.paymentStatus ??
|
|
662
|
+
status: order.statuses?.paymentStatus ?? ORDER_STATUS_UNKNOWN,
|
|
446
663
|
amount: total?.value ?? 0,
|
|
447
664
|
currency: total?.currencyCode ?? currencyCode,
|
|
448
665
|
createdAt: order.createdAt,
|
|
@@ -496,6 +713,75 @@ function createCheckoutReturnUrlValidator(policy) {
|
|
|
496
713
|
};
|
|
497
714
|
}
|
|
498
715
|
|
|
716
|
+
//#endregion
|
|
717
|
+
//#region src/lib/commerce/commerce-route.ts
|
|
718
|
+
const consoleCommerceLogger = { error: (message, context) => console.error(message, context) };
|
|
719
|
+
const REQUEST_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
720
|
+
function observabilityLocals(res) {
|
|
721
|
+
return res.locals;
|
|
722
|
+
}
|
|
723
|
+
function resolveRequestId(req, resolver) {
|
|
724
|
+
let supplied;
|
|
725
|
+
try {
|
|
726
|
+
supplied = resolver?.(req);
|
|
727
|
+
} catch {
|
|
728
|
+
supplied = void 0;
|
|
729
|
+
}
|
|
730
|
+
return typeof supplied === "string" && REQUEST_ID_PATTERN.test(supplied) ? supplied : randomUUID();
|
|
731
|
+
}
|
|
732
|
+
function requestIdFor(req, res) {
|
|
733
|
+
const locals = observabilityLocals(res);
|
|
734
|
+
locals.commerceRequestId ??= resolveRequestId(req, locals.commerceRequestIdResolver);
|
|
735
|
+
return locals.commerceRequestId;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* The single place Commerce failures become HTTP responses. Typed errors keep their status and
|
|
739
|
+
* public message; unmapped upstream failures are 502 and anything else is 500. 5xx detail goes
|
|
740
|
+
* to the configured logger, never into the body.
|
|
741
|
+
*/
|
|
742
|
+
function sendCommerceError(req, res, label, error, { failureFields = {}, classifyUpstreamError } = {}) {
|
|
743
|
+
const resolved = error instanceof UpstreamError ? classifyUpstreamError?.(error) ?? error : error instanceof CommerceError ? error : void 0;
|
|
744
|
+
const httpStatus = resolved?.httpStatus ?? 500;
|
|
745
|
+
const code = resolved?.code ?? "internal_error";
|
|
746
|
+
const requestId = requestIdFor(req, res);
|
|
747
|
+
if (httpStatus >= 500) {
|
|
748
|
+
const context = {
|
|
749
|
+
requestId,
|
|
750
|
+
method: req.method,
|
|
751
|
+
path: req.path,
|
|
752
|
+
httpStatus,
|
|
753
|
+
code,
|
|
754
|
+
...resolved?.details ? { details: resolved.details } : {},
|
|
755
|
+
error
|
|
756
|
+
};
|
|
757
|
+
try {
|
|
758
|
+
(observabilityLocals(res).commerceLogger ?? consoleCommerceLogger).error(`commerce-server: ${label}`, context);
|
|
759
|
+
} catch (loggerError) {
|
|
760
|
+
console.error(`commerce-server: ${label} (host logger failed)`, {
|
|
761
|
+
...context,
|
|
762
|
+
loggerError
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
if (res.headersSent) return;
|
|
767
|
+
res.status(httpStatus).json({
|
|
768
|
+
...failureFields,
|
|
769
|
+
error: resolved?.publicMessage ?? label,
|
|
770
|
+
code,
|
|
771
|
+
requestId
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
/** Wraps a route so thrown errors are answered by `sendCommerceError` with the route's label. */
|
|
775
|
+
function commerceRoute(label, handler, options = {}) {
|
|
776
|
+
return async (req, res) => {
|
|
777
|
+
try {
|
|
778
|
+
await handler(req, res);
|
|
779
|
+
} catch (error) {
|
|
780
|
+
sendCommerceError(req, res, label, error, options);
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
|
|
499
785
|
//#endregion
|
|
500
786
|
//#region src/lib/commerce/cart-scope.ts
|
|
501
787
|
/** Public cache/storage scope, not an authorization credential. */
|
|
@@ -508,11 +794,10 @@ function getCommerceCartScope(config) {
|
|
|
508
794
|
])).digest("hex").slice(0, 32);
|
|
509
795
|
}
|
|
510
796
|
/** Existing custom clients may omit the header; managed components always send it. */
|
|
511
|
-
function
|
|
797
|
+
function assertCommerceCartScope(req, config) {
|
|
512
798
|
const suppliedScope = req.headers?.["x-commerce-scope"];
|
|
513
|
-
if (suppliedScope === void 0 || suppliedScope === getCommerceCartScope(config)) return
|
|
514
|
-
|
|
515
|
-
return false;
|
|
799
|
+
if (suppliedScope === void 0 || suppliedScope === getCommerceCartScope(config)) return;
|
|
800
|
+
throw new ScopeMismatchError();
|
|
516
801
|
}
|
|
517
802
|
|
|
518
803
|
//#endregion
|
|
@@ -722,61 +1007,7 @@ const getCartOrderQuery = `
|
|
|
722
1007
|
`;
|
|
723
1008
|
|
|
724
1009
|
//#endregion
|
|
725
|
-
//#region src/
|
|
726
|
-
const applyDiscountCodesMutation = `
|
|
727
|
-
mutation ApplyDiscountCodes($input: ApplyDiscountCodesInput!) {
|
|
728
|
-
applyDiscountCodes(input: $input) {
|
|
729
|
-
id
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
`;
|
|
733
|
-
async function handler(req, res) {
|
|
734
|
-
try {
|
|
735
|
-
const cartId = req.params.id;
|
|
736
|
-
if (typeof cartId !== "string" || !cartId) {
|
|
737
|
-
res.status(400).json({ error: "Missing cart id" });
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
const body = req.body ?? {};
|
|
741
|
-
const codes = Array.isArray(body.discountCodes) ? body.discountCodes.filter((code) => typeof code === "string" && code.length > 0) : [];
|
|
742
|
-
if (codes.length === 0) {
|
|
743
|
-
res.status(400).json({ error: "discountCodes must be a non-empty string array" });
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
const config = readCommerceConfigForResponse(res);
|
|
747
|
-
if (!validateCommerceCartScope(req, res, config)) return;
|
|
748
|
-
const { storeId, clientId, apiBaseUrl } = config;
|
|
749
|
-
const endpoint = orderStorefrontEndpoint({ apiBaseUrl });
|
|
750
|
-
const headers = storefrontHeaders({
|
|
751
|
-
storeId,
|
|
752
|
-
clientId
|
|
753
|
-
});
|
|
754
|
-
await gqlRequest({
|
|
755
|
-
endpoint,
|
|
756
|
-
query: applyDiscountCodesMutation,
|
|
757
|
-
variables: { input: {
|
|
758
|
-
orderId: cartId,
|
|
759
|
-
discountCodes: codes
|
|
760
|
-
} },
|
|
761
|
-
headers
|
|
762
|
-
});
|
|
763
|
-
const hydrated = await gqlRequest({
|
|
764
|
-
endpoint,
|
|
765
|
-
query: getCartOrderQuery,
|
|
766
|
-
variables: { id: cartId },
|
|
767
|
-
headers
|
|
768
|
-
});
|
|
769
|
-
res.json({ cart: hydrated.orderById ?? null });
|
|
770
|
-
} catch (error) {
|
|
771
|
-
res.status(500).json({
|
|
772
|
-
error: "Failed to apply discount codes",
|
|
773
|
-
message: error instanceof Error ? error.message : String(error)
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
//#endregion
|
|
779
|
-
//#region src/server/api/commerce/cart/[id]/GET.ts
|
|
1010
|
+
//#region src/lib/commerce/upstream-errors.ts
|
|
780
1011
|
function isCartNotFoundError(error) {
|
|
781
1012
|
if (!(error instanceof GraphQLErrorWithCodes)) return false;
|
|
782
1013
|
const isFailure = (status) => status !== void 0 && status >= 400 && status !== 404 && status !== 410;
|
|
@@ -789,17 +1020,68 @@ function isCartNotFoundError(error) {
|
|
|
789
1020
|
return /\b(?:cart|(?:draft[ -])?order)\s+(?:(?:is|was|has)\s+)?(?:not found|expired)\b/i.test(message ?? "");
|
|
790
1021
|
});
|
|
791
1022
|
}
|
|
792
|
-
|
|
1023
|
+
const CART_UPSTREAM_ERROR_RULES = [{
|
|
1024
|
+
matches: isCartNotFoundError,
|
|
1025
|
+
toError: (error) => new NotFoundError("Cart not found", { cause: error })
|
|
1026
|
+
}];
|
|
1027
|
+
/** For routes acting on an existing saved cart (`/cart/:id`); creating a cart has none to miss. */
|
|
1028
|
+
function classifyCartUpstreamError(error) {
|
|
1029
|
+
return CART_UPSTREAM_ERROR_RULES.find((rule) => rule.matches(error))?.toError(error) ?? error;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
//#endregion
|
|
1033
|
+
//#region src/server/api/commerce/cart/[id]/discounts/POST.ts
|
|
1034
|
+
const applyDiscountCodesMutation = `
|
|
1035
|
+
mutation ApplyDiscountCodes($input: ApplyDiscountCodesInput!) {
|
|
1036
|
+
applyDiscountCodes(input: $input) {
|
|
1037
|
+
id
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
`;
|
|
1041
|
+
async function applyDiscountCodes(req, res) {
|
|
1042
|
+
const cartId = req.params.id;
|
|
1043
|
+
if (typeof cartId !== "string" || !cartId) throw new InvalidRequestError("Missing cart id");
|
|
1044
|
+
const body = req.body ?? {};
|
|
1045
|
+
const codes = Array.isArray(body.discountCodes) ? body.discountCodes.filter((code) => typeof code === "string" && code.length > 0) : [];
|
|
1046
|
+
if (codes.length === 0) throw new InvalidRequestError("discountCodes must be a non-empty string array");
|
|
1047
|
+
const config = readCommerceConfigForResponse(res);
|
|
1048
|
+
assertCommerceCartScope(req, config);
|
|
1049
|
+
const { storeId, clientId, apiBaseUrl } = config;
|
|
1050
|
+
const endpoint = orderStorefrontEndpoint({ apiBaseUrl });
|
|
1051
|
+
const headers = storefrontHeaders({
|
|
1052
|
+
storeId,
|
|
1053
|
+
clientId
|
|
1054
|
+
});
|
|
1055
|
+
await gqlRequest({
|
|
1056
|
+
endpoint,
|
|
1057
|
+
query: applyDiscountCodesMutation,
|
|
1058
|
+
variables: { input: {
|
|
1059
|
+
orderId: cartId,
|
|
1060
|
+
discountCodes: codes
|
|
1061
|
+
} },
|
|
1062
|
+
headers
|
|
1063
|
+
});
|
|
1064
|
+
const hydrated = await gqlRequest({
|
|
1065
|
+
endpoint,
|
|
1066
|
+
query: getCartOrderQuery,
|
|
1067
|
+
variables: { id: cartId },
|
|
1068
|
+
headers
|
|
1069
|
+
});
|
|
1070
|
+
res.json({ cart: hydrated.orderById ?? null });
|
|
1071
|
+
}
|
|
1072
|
+
var POST_default = commerceRoute("Failed to apply discount codes", applyDiscountCodes, { classifyUpstreamError: classifyCartUpstreamError });
|
|
1073
|
+
|
|
1074
|
+
//#endregion
|
|
1075
|
+
//#region src/server/api/commerce/cart/[id]/GET.ts
|
|
1076
|
+
async function readCart(req, res) {
|
|
1077
|
+
const cartId = req.params.id;
|
|
1078
|
+
if (typeof cartId !== "string" || !cartId) throw new InvalidRequestError("Missing cart id");
|
|
1079
|
+
const config = readCommerceConfigForResponse(res);
|
|
1080
|
+
assertCommerceCartScope(req, config);
|
|
1081
|
+
const { storeId, clientId, apiBaseUrl } = config;
|
|
1082
|
+
let data;
|
|
793
1083
|
try {
|
|
794
|
-
|
|
795
|
-
if (typeof cartId !== "string" || !cartId) {
|
|
796
|
-
res.status(400).json({ error: "Missing cart id" });
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
const config = readCommerceConfigForResponse(res);
|
|
800
|
-
if (!validateCommerceCartScope(req, res, config)) return;
|
|
801
|
-
const { storeId, clientId, apiBaseUrl } = config;
|
|
802
|
-
const data = await gqlRequest({
|
|
1084
|
+
data = await gqlRequest({
|
|
803
1085
|
endpoint: orderStorefrontEndpoint({ apiBaseUrl }),
|
|
804
1086
|
query: getCartOrderQuery,
|
|
805
1087
|
variables: { id: cartId },
|
|
@@ -808,18 +1090,16 @@ async function handler$1(req, res) {
|
|
|
808
1090
|
clientId
|
|
809
1091
|
})
|
|
810
1092
|
});
|
|
811
|
-
res.json({ cart: data.orderById ?? null });
|
|
812
1093
|
} catch (error) {
|
|
813
1094
|
if (isCartNotFoundError(error)) {
|
|
814
1095
|
res.status(200).json({ cart: null });
|
|
815
1096
|
return;
|
|
816
1097
|
}
|
|
817
|
-
|
|
818
|
-
error: "Failed to load cart",
|
|
819
|
-
message: error instanceof Error ? error.message : String(error)
|
|
820
|
-
});
|
|
1098
|
+
throw error;
|
|
821
1099
|
}
|
|
1100
|
+
res.json({ cart: data.orderById ?? null });
|
|
822
1101
|
}
|
|
1102
|
+
var GET_default = commerceRoute("Failed to load cart", readCart, { classifyUpstreamError: classifyCartUpstreamError });
|
|
823
1103
|
|
|
824
1104
|
//#endregion
|
|
825
1105
|
//#region src/server/api/commerce/cart/[id]/items/[itemId]/DELETE.ts
|
|
@@ -828,45 +1108,36 @@ const deleteLineItemByIdMutation = `
|
|
|
828
1108
|
deleteLineItemById(id: $id, orderId: $orderId)
|
|
829
1109
|
}
|
|
830
1110
|
`;
|
|
831
|
-
async function
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
variables: { id: cartId },
|
|
860
|
-
headers
|
|
861
|
-
});
|
|
862
|
-
res.json({ cart: hydrated.orderById ?? null });
|
|
863
|
-
} catch (error) {
|
|
864
|
-
res.status(500).json({
|
|
865
|
-
error: "Failed to delete line item",
|
|
866
|
-
message: error instanceof Error ? error.message : String(error)
|
|
867
|
-
});
|
|
868
|
-
}
|
|
1111
|
+
async function deleteLineItem(req, res) {
|
|
1112
|
+
const cartId = req.params.id;
|
|
1113
|
+
const itemId = req.params.itemId;
|
|
1114
|
+
if (typeof cartId !== "string" || !cartId || typeof itemId !== "string" || !itemId) throw new InvalidRequestError("Missing cart id or item id");
|
|
1115
|
+
const config = readCommerceConfigForResponse(res);
|
|
1116
|
+
assertCommerceCartScope(req, config);
|
|
1117
|
+
const { storeId, clientId, apiBaseUrl } = config;
|
|
1118
|
+
const endpoint = orderStorefrontEndpoint({ apiBaseUrl });
|
|
1119
|
+
const headers = storefrontHeaders({
|
|
1120
|
+
storeId,
|
|
1121
|
+
clientId
|
|
1122
|
+
});
|
|
1123
|
+
await gqlRequest({
|
|
1124
|
+
endpoint,
|
|
1125
|
+
query: deleteLineItemByIdMutation,
|
|
1126
|
+
variables: {
|
|
1127
|
+
id: itemId,
|
|
1128
|
+
orderId: cartId
|
|
1129
|
+
},
|
|
1130
|
+
headers
|
|
1131
|
+
});
|
|
1132
|
+
const hydrated = await gqlRequest({
|
|
1133
|
+
endpoint,
|
|
1134
|
+
query: getCartOrderQuery,
|
|
1135
|
+
variables: { id: cartId },
|
|
1136
|
+
headers
|
|
1137
|
+
});
|
|
1138
|
+
res.json({ cart: hydrated.orderById ?? null });
|
|
869
1139
|
}
|
|
1140
|
+
var DELETE_default = commerceRoute("Failed to delete line item", deleteLineItem, { classifyUpstreamError: classifyCartUpstreamError });
|
|
870
1141
|
|
|
871
1142
|
//#endregion
|
|
872
1143
|
//#region src/server/api/commerce/cart/[id]/items/[itemId]/PATCH.ts
|
|
@@ -877,52 +1148,43 @@ const updateLineItemByIdMutation = `
|
|
|
877
1148
|
}
|
|
878
1149
|
}
|
|
879
1150
|
`;
|
|
880
|
-
async function
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
variables: { id: cartId },
|
|
916
|
-
headers
|
|
917
|
-
});
|
|
918
|
-
res.json({ cart: hydrated.orderById ?? null });
|
|
919
|
-
} catch (error) {
|
|
920
|
-
res.status(500).json({
|
|
921
|
-
error: "Failed to update line item",
|
|
922
|
-
message: error instanceof Error ? error.message : String(error)
|
|
923
|
-
});
|
|
924
|
-
}
|
|
1151
|
+
async function updateLineItem(req, res) {
|
|
1152
|
+
const cartId = req.params.id;
|
|
1153
|
+
const itemId = req.params.itemId;
|
|
1154
|
+
if (typeof cartId !== "string" || !cartId || typeof itemId !== "string" || !itemId) throw new InvalidRequestError("Missing cart id or item id");
|
|
1155
|
+
const body = req.body ?? {};
|
|
1156
|
+
const config = readCommerceConfigForResponse(res);
|
|
1157
|
+
assertCommerceCartScope(req, config);
|
|
1158
|
+
const { storeId, clientId, apiBaseUrl } = config;
|
|
1159
|
+
const endpoint = orderStorefrontEndpoint({ apiBaseUrl });
|
|
1160
|
+
const headers = storefrontHeaders({
|
|
1161
|
+
storeId,
|
|
1162
|
+
clientId
|
|
1163
|
+
});
|
|
1164
|
+
await gqlRequest({
|
|
1165
|
+
endpoint,
|
|
1166
|
+
query: updateLineItemByIdMutation,
|
|
1167
|
+
variables: { input: {
|
|
1168
|
+
id: itemId,
|
|
1169
|
+
orderId: cartId,
|
|
1170
|
+
name: body.name,
|
|
1171
|
+
quantity: body.quantity,
|
|
1172
|
+
fulfillmentMode: body.fulfillmentMode,
|
|
1173
|
+
status: body.status,
|
|
1174
|
+
type: body.type,
|
|
1175
|
+
details: body.details
|
|
1176
|
+
} },
|
|
1177
|
+
headers
|
|
1178
|
+
});
|
|
1179
|
+
const hydrated = await gqlRequest({
|
|
1180
|
+
endpoint,
|
|
1181
|
+
query: getCartOrderQuery,
|
|
1182
|
+
variables: { id: cartId },
|
|
1183
|
+
headers
|
|
1184
|
+
});
|
|
1185
|
+
res.json({ cart: hydrated.orderById ?? null });
|
|
925
1186
|
}
|
|
1187
|
+
var PATCH_default = commerceRoute("Failed to update line item", updateLineItem, { classifyUpstreamError: classifyCartUpstreamError });
|
|
926
1188
|
|
|
927
1189
|
//#endregion
|
|
928
1190
|
//#region src/server/api/commerce/cart/[id]/items/POST.ts
|
|
@@ -933,50 +1195,38 @@ const addLineItemBySkuIdMutation$1 = `
|
|
|
933
1195
|
}
|
|
934
1196
|
}
|
|
935
1197
|
`;
|
|
936
|
-
async function
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
const hydrated = await gqlRequest({
|
|
967
|
-
endpoint,
|
|
968
|
-
query: getCartOrderQuery,
|
|
969
|
-
variables: { id: cartId },
|
|
970
|
-
headers
|
|
971
|
-
});
|
|
972
|
-
res.status(201).json({ cart: hydrated.orderById ?? null });
|
|
973
|
-
} catch (error) {
|
|
974
|
-
res.status(500).json({
|
|
975
|
-
error: "Failed to add line item",
|
|
976
|
-
message: error instanceof Error ? error.message : String(error)
|
|
977
|
-
});
|
|
978
|
-
}
|
|
1198
|
+
async function addLineItem(req, res) {
|
|
1199
|
+
const cartId = req.params.id;
|
|
1200
|
+
if (typeof cartId !== "string" || !cartId) throw new InvalidRequestError("Missing cart id");
|
|
1201
|
+
const body = req.body ?? {};
|
|
1202
|
+
if (!body.skuId || !body.name || typeof body.quantity !== "number") throw new InvalidRequestError("Missing required fields: skuId, name, quantity");
|
|
1203
|
+
const config = readCommerceConfigForResponse(res);
|
|
1204
|
+
assertCommerceCartScope(req, config);
|
|
1205
|
+
const { storeId, clientId, apiBaseUrl } = config;
|
|
1206
|
+
const endpoint = orderStorefrontEndpoint({ apiBaseUrl });
|
|
1207
|
+
const headers = storefrontHeaders({
|
|
1208
|
+
storeId,
|
|
1209
|
+
clientId
|
|
1210
|
+
});
|
|
1211
|
+
await gqlRequest({
|
|
1212
|
+
endpoint,
|
|
1213
|
+
query: addLineItemBySkuIdMutation$1,
|
|
1214
|
+
variables: { input: buildAddLineItemBySkuIdInput(cartId, {
|
|
1215
|
+
skuId: body.skuId,
|
|
1216
|
+
name: body.name,
|
|
1217
|
+
quantity: body.quantity
|
|
1218
|
+
}) },
|
|
1219
|
+
headers
|
|
1220
|
+
});
|
|
1221
|
+
const hydrated = await gqlRequest({
|
|
1222
|
+
endpoint,
|
|
1223
|
+
query: getCartOrderQuery,
|
|
1224
|
+
variables: { id: cartId },
|
|
1225
|
+
headers
|
|
1226
|
+
});
|
|
1227
|
+
res.status(201).json({ cart: hydrated.orderById ?? null });
|
|
979
1228
|
}
|
|
1229
|
+
var POST_default$1 = commerceRoute("Failed to add line item", addLineItem, { classifyUpstreamError: classifyCartUpstreamError });
|
|
980
1230
|
|
|
981
1231
|
//#endregion
|
|
982
1232
|
//#region src/server/api/commerce/cart/POST.ts
|
|
@@ -994,147 +1244,100 @@ const addLineItemBySkuIdMutation = `
|
|
|
994
1244
|
}
|
|
995
1245
|
}
|
|
996
1246
|
`;
|
|
997
|
-
async function
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1247
|
+
async function createCart(req, res) {
|
|
1248
|
+
const body = req.body ?? {};
|
|
1249
|
+
const initialItems = Array.isArray(body.lineItems) ? body.lineItems : [];
|
|
1250
|
+
for (const item of initialItems) if (!item.skuId || !item.name || typeof item.quantity !== "number") throw new InvalidRequestError("Each lineItem must have skuId, a non-empty name, and a numeric quantity");
|
|
1251
|
+
const config = readCommerceConfigForResponse(res);
|
|
1252
|
+
assertCommerceCartScope(req, config);
|
|
1253
|
+
const { storeId, channelId, clientId, apiBaseUrl, currencyCode } = config;
|
|
1254
|
+
const endpoint = orderStorefrontEndpoint({ apiBaseUrl });
|
|
1255
|
+
const headers = storefrontHeaders({
|
|
1256
|
+
storeId,
|
|
1257
|
+
clientId
|
|
1258
|
+
});
|
|
1259
|
+
const newCartId = (await gqlRequest({
|
|
1260
|
+
endpoint,
|
|
1261
|
+
query: addCartOrderMutation,
|
|
1262
|
+
variables: { input: buildEmptyCartOrderInput({
|
|
1010
1263
|
storeId,
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
variables: { input: buildAddLineItemBySkuIdInput(newCartId, item) },
|
|
1032
|
-
headers
|
|
1033
|
-
});
|
|
1034
|
-
const hydrated = await gqlRequest({
|
|
1035
|
-
endpoint,
|
|
1036
|
-
query: getCartOrderQuery,
|
|
1037
|
-
variables: { id: newCartId },
|
|
1038
|
-
headers
|
|
1039
|
-
});
|
|
1040
|
-
res.status(201).json({ cart: hydrated.orderById ?? null });
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
res.status(500).json({
|
|
1043
|
-
error: "Failed to create cart",
|
|
1044
|
-
message: error instanceof Error ? error.message : String(error)
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1264
|
+
channelId,
|
|
1265
|
+
owner: config.owner,
|
|
1266
|
+
currencyCode: body.currencyCode ?? currencyCode
|
|
1267
|
+
}) },
|
|
1268
|
+
headers
|
|
1269
|
+
})).addDraftOrder?.id;
|
|
1270
|
+
if (!newCartId) throw new UpstreamError("addDraftOrder response did not include a cart id");
|
|
1271
|
+
for (const item of initialItems) await gqlRequest({
|
|
1272
|
+
endpoint,
|
|
1273
|
+
query: addLineItemBySkuIdMutation,
|
|
1274
|
+
variables: { input: buildAddLineItemBySkuIdInput(newCartId, item) },
|
|
1275
|
+
headers
|
|
1276
|
+
});
|
|
1277
|
+
const hydrated = await gqlRequest({
|
|
1278
|
+
endpoint,
|
|
1279
|
+
query: getCartOrderQuery,
|
|
1280
|
+
variables: { id: newCartId },
|
|
1281
|
+
headers
|
|
1282
|
+
});
|
|
1283
|
+
res.status(201).json({ cart: hydrated.orderById ?? null });
|
|
1047
1284
|
}
|
|
1285
|
+
var POST_default$2 = commerceRoute("Failed to create cart", createCart);
|
|
1048
1286
|
|
|
1049
1287
|
//#endregion
|
|
1050
1288
|
//#region src/server/api/commerce/checkout/POST.ts
|
|
1051
|
-
async function
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
}
|
|
1070
|
-
const validateReturnUrls = res.locals.commerceCheckoutReturnUrlValidator;
|
|
1071
|
-
if (!validateReturnUrls) {
|
|
1072
|
-
res.status(503).json({ error: "Checkout return destinations are not configured." });
|
|
1073
|
-
return;
|
|
1074
|
-
}
|
|
1075
|
-
const destinations = validateReturnUrls(returnUrl, successUrl);
|
|
1076
|
-
if (!destinations) {
|
|
1077
|
-
res.status(400).json({ error: "Checkout return destinations are not allowed." });
|
|
1078
|
-
return;
|
|
1079
|
-
}
|
|
1080
|
-
const session = await createCheckoutSession({
|
|
1081
|
-
draftOrderId,
|
|
1082
|
-
skuId,
|
|
1083
|
-
quantity,
|
|
1084
|
-
...destinations
|
|
1085
|
-
}, configuration);
|
|
1086
|
-
res.status(200).json(session);
|
|
1087
|
-
} catch (error) {
|
|
1088
|
-
res.status(500).json({
|
|
1089
|
-
error: "Failed to create checkout session",
|
|
1090
|
-
message: error instanceof Error ? error.message : String(error)
|
|
1091
|
-
});
|
|
1092
|
-
}
|
|
1289
|
+
async function createCheckout(req, res) {
|
|
1290
|
+
const configuration = commerceConfigurationForResponse(res);
|
|
1291
|
+
const { draftOrderId, skuId, quantity, lineItemData, returnUrl, successUrl } = req.body ?? {};
|
|
1292
|
+
if (lineItemData !== void 0) throw new InvalidRequestError("Non-catalog checkout must be created by the server.");
|
|
1293
|
+
if ([draftOrderId, skuId].filter(Boolean).length !== 1 || [draftOrderId, skuId].some((value) => value !== void 0 && (typeof value !== "string" || !value.trim()))) throw new InvalidRequestError("exactly one non-empty draftOrderId or skuId is required");
|
|
1294
|
+
if (quantity !== void 0 && (!Number.isSafeInteger(quantity) || quantity < 1)) throw new InvalidRequestError("quantity must be a positive whole number");
|
|
1295
|
+
if (req.headers?.["x-commerce-scope"] !== void 0) assertCommerceCartScope(req, readReadyCommerceConfig(configuration));
|
|
1296
|
+
const validateReturnUrls = res.locals.commerceCheckoutReturnUrlValidator;
|
|
1297
|
+
if (!validateReturnUrls) throw new CommerceNotConfiguredError("Checkout return URLs are not configured on the router.", { publicMessage: "Checkout return destinations are not configured." });
|
|
1298
|
+
const destinations = validateReturnUrls(returnUrl, successUrl);
|
|
1299
|
+
if (!destinations) throw new InvalidRequestError("Checkout return destinations are not allowed.");
|
|
1300
|
+
const session = await createCheckoutSession({
|
|
1301
|
+
draftOrderId,
|
|
1302
|
+
skuId,
|
|
1303
|
+
quantity,
|
|
1304
|
+
...destinations
|
|
1305
|
+
}, configuration);
|
|
1306
|
+
res.status(200).json(session);
|
|
1093
1307
|
}
|
|
1308
|
+
var POST_default$3 = commerceRoute("Failed to create checkout session", createCheckout);
|
|
1094
1309
|
|
|
1095
1310
|
//#endregion
|
|
1096
1311
|
//#region src/server/api/commerce/config/GET.ts
|
|
1097
|
-
async function
|
|
1312
|
+
async function readPublicConfig(_req, res) {
|
|
1098
1313
|
res.setHeader("Cache-Control", "no-store");
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
});
|
|
1105
|
-
} catch (cause) {
|
|
1106
|
-
res.status(503).json({
|
|
1107
|
-
error: "Commerce configuration is unavailable. Complete the store connection before continuing.",
|
|
1108
|
-
message: cause instanceof Error ? cause.message : "Invalid Commerce configuration."
|
|
1109
|
-
});
|
|
1314
|
+
const configuration = commerceConfigurationForResponse(res);
|
|
1315
|
+
const state = readCommerceConnectionState(configuration);
|
|
1316
|
+
if (state === "unbound" || state === "connecting") {
|
|
1317
|
+
res.json({ state });
|
|
1318
|
+
return;
|
|
1110
1319
|
}
|
|
1320
|
+
const config = readReadyCommerceConfig(configuration);
|
|
1321
|
+
res.json({
|
|
1322
|
+
...state === "ready" ? { state } : {},
|
|
1323
|
+
cartScope: getCommerceCartScope(config),
|
|
1324
|
+
currencyCode: config.currencyCode
|
|
1325
|
+
});
|
|
1111
1326
|
}
|
|
1327
|
+
var GET_default$1 = commerceRoute("Failed to load Commerce configuration", readPublicConfig);
|
|
1112
1328
|
|
|
1113
1329
|
//#endregion
|
|
1114
1330
|
//#region src/server/api/commerce/order-status/GET.ts
|
|
1115
|
-
async function
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
return;
|
|
1124
|
-
}
|
|
1125
|
-
const order = await getOrderStatus(orderId, commerceConfigurationForResponse(res));
|
|
1126
|
-
res.status(200).json({
|
|
1127
|
-
success: true,
|
|
1128
|
-
order
|
|
1129
|
-
});
|
|
1130
|
-
} catch (error) {
|
|
1131
|
-
res.status(500).json({
|
|
1132
|
-
success: false,
|
|
1133
|
-
error: "Failed to get order status",
|
|
1134
|
-
message: error instanceof Error ? error.message : String(error)
|
|
1135
|
-
});
|
|
1136
|
-
}
|
|
1331
|
+
async function readOrderStatus(req, res) {
|
|
1332
|
+
const { orderId } = req.query;
|
|
1333
|
+
if (typeof orderId !== "string") throw new InvalidOrderIdError();
|
|
1334
|
+
const order = await getOrderStatus(orderId, commerceConfigurationForResponse(res));
|
|
1335
|
+
res.status(200).json({
|
|
1336
|
+
success: true,
|
|
1337
|
+
order
|
|
1338
|
+
});
|
|
1137
1339
|
}
|
|
1340
|
+
var GET_default$2 = commerceRoute("Failed to get order status", readOrderStatus, { failureFields: { success: false } });
|
|
1138
1341
|
|
|
1139
1342
|
//#endregion
|
|
1140
1343
|
//#region src/lib/commerce/catalog-subgraph.ts
|
|
@@ -1257,45 +1460,33 @@ function asNumber$1(value) {
|
|
|
1257
1460
|
const n = Number(value);
|
|
1258
1461
|
return Number.isFinite(n) ? n : void 0;
|
|
1259
1462
|
}
|
|
1260
|
-
async function
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
})
|
|
1286
|
-
});
|
|
1287
|
-
if (!data.skuGroup || !data.activeSkuGroups?.edges?.some((edge) => edge?.node?.id === productId)) {
|
|
1288
|
-
res.status(404).json({ error: "Product not found" });
|
|
1289
|
-
return;
|
|
1290
|
-
}
|
|
1291
|
-
res.json({ skuGroup: data.skuGroup });
|
|
1292
|
-
} catch (error) {
|
|
1293
|
-
res.status(500).json({
|
|
1294
|
-
error: "Failed to load product",
|
|
1295
|
-
message: error instanceof Error ? error.message : String(error)
|
|
1296
|
-
});
|
|
1297
|
-
}
|
|
1463
|
+
async function readProduct(req, res) {
|
|
1464
|
+
const productId = req.params.id;
|
|
1465
|
+
if (typeof productId !== "string" || !productId) throw new InvalidRequestError("Missing product id");
|
|
1466
|
+
const config = readCommerceConfigForResponse(res);
|
|
1467
|
+
assertCommerceCartScope(req, config);
|
|
1468
|
+
const { storeId, clientId, apiBaseUrl } = config;
|
|
1469
|
+
const variables = buildSkuGroupVariables({
|
|
1470
|
+
productId,
|
|
1471
|
+
selectedAttributeValues: asStringArray$1(req.query.attributeValues),
|
|
1472
|
+
skuGroupFirst: asNumber$1(req.query.skuGroupFirst)
|
|
1473
|
+
});
|
|
1474
|
+
const data = await gqlRequest({
|
|
1475
|
+
endpoint: catalogStorefrontEndpoint({
|
|
1476
|
+
storeId,
|
|
1477
|
+
apiBaseUrl
|
|
1478
|
+
}),
|
|
1479
|
+
query: skuGroupQuery,
|
|
1480
|
+
variables,
|
|
1481
|
+
headers: storefrontHeaders({
|
|
1482
|
+
storeId,
|
|
1483
|
+
clientId
|
|
1484
|
+
})
|
|
1485
|
+
});
|
|
1486
|
+
if (!data.skuGroup || !data.activeSkuGroups?.edges?.some((edge) => edge?.node?.id === productId)) throw new NotFoundError("Product not found");
|
|
1487
|
+
res.json({ skuGroup: data.skuGroup });
|
|
1298
1488
|
}
|
|
1489
|
+
var GET_default$3 = commerceRoute("Failed to load product", readProduct);
|
|
1299
1490
|
|
|
1300
1491
|
//#endregion
|
|
1301
1492
|
//#region src/server/api/commerce/products/GET.ts
|
|
@@ -1396,38 +1587,32 @@ function asNumber(value) {
|
|
|
1396
1587
|
const n = Number(value);
|
|
1397
1588
|
return Number.isFinite(n) ? n : void 0;
|
|
1398
1589
|
}
|
|
1399
|
-
async function
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
res.json(data);
|
|
1424
|
-
} catch (error) {
|
|
1425
|
-
res.status(500).json({
|
|
1426
|
-
error: "Failed to load products",
|
|
1427
|
-
message: error instanceof Error ? error.message : String(error)
|
|
1428
|
-
});
|
|
1429
|
-
}
|
|
1590
|
+
async function listProducts(req, res) {
|
|
1591
|
+
const config = readCommerceConfigForResponse(res);
|
|
1592
|
+
assertCommerceCartScope(req, config);
|
|
1593
|
+
const { storeId, clientId, apiBaseUrl } = config;
|
|
1594
|
+
const variables = buildSkuGroupsVariables({
|
|
1595
|
+
first: asNumber(req.query.first) ?? 24,
|
|
1596
|
+
after: typeof req.query.after === "string" ? req.query.after : void 0,
|
|
1597
|
+
searchQuery: typeof req.query.searchQuery === "string" ? req.query.searchQuery : void 0,
|
|
1598
|
+
productIds: asStringArray(req.query.productIds),
|
|
1599
|
+
categoryIds: asStringArray(req.query.categoryIds)
|
|
1600
|
+
});
|
|
1601
|
+
const data = await gqlRequest({
|
|
1602
|
+
endpoint: catalogStorefrontEndpoint({
|
|
1603
|
+
storeId,
|
|
1604
|
+
apiBaseUrl
|
|
1605
|
+
}),
|
|
1606
|
+
query: skuGroupsQuery,
|
|
1607
|
+
variables,
|
|
1608
|
+
headers: storefrontHeaders({
|
|
1609
|
+
storeId,
|
|
1610
|
+
clientId
|
|
1611
|
+
})
|
|
1612
|
+
});
|
|
1613
|
+
res.json(data);
|
|
1430
1614
|
}
|
|
1615
|
+
var GET_default$4 = commerceRoute("Failed to load products", listProducts);
|
|
1431
1616
|
|
|
1432
1617
|
//#endregion
|
|
1433
1618
|
//#region src/server/api/commerce/skus/[id]/GET.ts
|
|
@@ -1487,36 +1672,27 @@ const skuQuery = `
|
|
|
1487
1672
|
}
|
|
1488
1673
|
}
|
|
1489
1674
|
`;
|
|
1490
|
-
async function
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
clientId
|
|
1510
|
-
})
|
|
1511
|
-
});
|
|
1512
|
-
res.json(data);
|
|
1513
|
-
} catch (error) {
|
|
1514
|
-
res.status(500).json({
|
|
1515
|
-
error: "Failed to load sku",
|
|
1516
|
-
message: error instanceof Error ? error.message : String(error)
|
|
1517
|
-
});
|
|
1518
|
-
}
|
|
1675
|
+
async function readSku(req, res) {
|
|
1676
|
+
const skuId = req.params.id;
|
|
1677
|
+
if (typeof skuId !== "string" || !skuId) throw new InvalidRequestError("Missing sku id");
|
|
1678
|
+
const config = readCommerceConfigForResponse(res);
|
|
1679
|
+
assertCommerceCartScope(req, config);
|
|
1680
|
+
const { storeId, clientId, apiBaseUrl } = config;
|
|
1681
|
+
const data = await gqlRequest({
|
|
1682
|
+
endpoint: catalogStorefrontEndpoint({
|
|
1683
|
+
storeId,
|
|
1684
|
+
apiBaseUrl
|
|
1685
|
+
}),
|
|
1686
|
+
query: skuQuery,
|
|
1687
|
+
variables: { id: skuId },
|
|
1688
|
+
headers: storefrontHeaders({
|
|
1689
|
+
storeId,
|
|
1690
|
+
clientId
|
|
1691
|
+
})
|
|
1692
|
+
});
|
|
1693
|
+
res.json(data);
|
|
1519
1694
|
}
|
|
1695
|
+
var GET_default$5 = commerceRoute("Failed to load sku", readSku);
|
|
1520
1696
|
|
|
1521
1697
|
//#endregion
|
|
1522
1698
|
//#region src/router.ts
|
|
@@ -1525,32 +1701,39 @@ function createCommerceRouter(options = {}) {
|
|
|
1525
1701
|
const configuration = options.configuration ?? createRuntimeCommerceConfiguration();
|
|
1526
1702
|
const catalogEnabled = options.features?.catalog ?? true;
|
|
1527
1703
|
const paymentsEnabled = options.features?.payments ?? true;
|
|
1704
|
+
const logger = options.logger ?? consoleCommerceLogger;
|
|
1528
1705
|
const validateCheckoutReturnUrls = options.checkoutReturnUrls ? createCheckoutReturnUrlValidator(options.checkoutReturnUrls) : void 0;
|
|
1529
1706
|
router.use((_req, res, next) => {
|
|
1707
|
+
const observability = {
|
|
1708
|
+
commerceLogger: logger,
|
|
1709
|
+
commerceRequestIdResolver: options.getRequestId
|
|
1710
|
+
};
|
|
1711
|
+
Object.assign(res.locals, observability);
|
|
1530
1712
|
res.locals.commerceConfiguration = configuration;
|
|
1531
1713
|
res.locals.commerceCheckoutReturnUrlValidator = validateCheckoutReturnUrls;
|
|
1532
1714
|
next();
|
|
1533
1715
|
});
|
|
1534
1716
|
if (catalogEnabled) {
|
|
1535
|
-
router.get("/config",
|
|
1536
|
-
router.get("/products",
|
|
1537
|
-
router.get("/products/:id",
|
|
1538
|
-
router.get("/skus/:id",
|
|
1539
|
-
router.post("/cart",
|
|
1540
|
-
router.get("/cart/:id",
|
|
1541
|
-
router.post("/cart/:id/items",
|
|
1542
|
-
router.patch("/cart/:id/items/:itemId",
|
|
1543
|
-
router.delete("/cart/:id/items/:itemId",
|
|
1544
|
-
router.post("/cart/:id/discounts",
|
|
1717
|
+
router.get("/config", GET_default$1);
|
|
1718
|
+
router.get("/products", GET_default$4);
|
|
1719
|
+
router.get("/products/:id", GET_default$3);
|
|
1720
|
+
router.get("/skus/:id", GET_default$5);
|
|
1721
|
+
router.post("/cart", POST_default$2);
|
|
1722
|
+
router.get("/cart/:id", GET_default);
|
|
1723
|
+
router.post("/cart/:id/items", POST_default$1);
|
|
1724
|
+
router.patch("/cart/:id/items/:itemId", PATCH_default);
|
|
1725
|
+
router.delete("/cart/:id/items/:itemId", DELETE_default);
|
|
1726
|
+
router.post("/cart/:id/discounts", POST_default);
|
|
1545
1727
|
}
|
|
1546
1728
|
if (paymentsEnabled) {
|
|
1547
|
-
router.post("/checkout",
|
|
1548
|
-
router.get("/order-status",
|
|
1729
|
+
router.post("/checkout", POST_default$3);
|
|
1730
|
+
router.get("/order-status", GET_default$2);
|
|
1549
1731
|
}
|
|
1550
1732
|
return router;
|
|
1551
1733
|
}
|
|
1552
|
-
function createCommerceCatalogRouter(configuration) {
|
|
1734
|
+
function createCommerceCatalogRouter(configuration, observability = {}) {
|
|
1553
1735
|
return createCommerceRouter({
|
|
1736
|
+
...observability,
|
|
1554
1737
|
configuration,
|
|
1555
1738
|
features: {
|
|
1556
1739
|
catalog: true,
|
|
@@ -1558,8 +1741,9 @@ function createCommerceCatalogRouter(configuration) {
|
|
|
1558
1741
|
}
|
|
1559
1742
|
});
|
|
1560
1743
|
}
|
|
1561
|
-
function createGoDaddyPaymentsRouter(configuration, checkoutReturnUrls) {
|
|
1744
|
+
function createGoDaddyPaymentsRouter(configuration, checkoutReturnUrls, observability = {}) {
|
|
1562
1745
|
return createCommerceRouter({
|
|
1746
|
+
...observability,
|
|
1563
1747
|
configuration,
|
|
1564
1748
|
checkoutReturnUrls,
|
|
1565
1749
|
features: {
|
|
@@ -1570,4 +1754,4 @@ function createGoDaddyPaymentsRouter(configuration, checkoutReturnUrls) {
|
|
|
1570
1754
|
}
|
|
1571
1755
|
|
|
1572
1756
|
//#endregion
|
|
1573
|
-
export { createCheckoutSession, createCommerceCatalogRouter, createCommerceRouter, createGoDaddyPaymentsRouter, createRuntimeCommerceConfiguration, getOrderStatus, parseCommerceCheckoutConfiguration, readCommerceConfig };
|
|
1757
|
+
export { CommerceError, CommerceNotConfiguredError, InvalidOrderIdError, InvalidRequestError, NotFoundError, ORDER_STATUS_UNKNOWN, OrderNotFoundError, ScopeMismatchError, UpstreamError, createCheckoutSession, createCommerceCatalogRouter, createCommerceRouter, createGoDaddyPaymentsRouter, createRuntimeCommerceConfiguration, getOrderStatus, parseCommerceCheckoutConfiguration, readCommerceConfig };
|