@tokenoftrust/storefront-runner 2.4.60 → 2.4.62
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/apps/storefront/src/lib/admin/services/orders.ts +31 -23
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +11 -9
- package/apps/storefront/src/pages/api/admin/orders.ts +9 -2
- package/apps/storefront/src/pages/products/[handle].astro +3 -9
- package/package.json +1 -1
- package/packages/public-runtime/src/membership.ts +3 -0
|
@@ -204,7 +204,7 @@ function readSourcedEvidence(
|
|
|
204
204
|
const asOfValue = verification[`${key}AsOf`];
|
|
205
205
|
return {
|
|
206
206
|
state,
|
|
207
|
-
source: typeof sourceValue === "string" && sourceValue.length > 0 ? sourceValue : "
|
|
207
|
+
source: typeof sourceValue === "string" && sourceValue.length > 0 ? sourceValue : "absent",
|
|
208
208
|
asOf: typeof asOfValue === "string" ? asOfValue : null,
|
|
209
209
|
};
|
|
210
210
|
}
|
|
@@ -266,13 +266,14 @@ export function releaseOf(order: ScrappyOrderRecord): OrderRelease {
|
|
|
266
266
|
return { state: ready ? "ready" : "held", ready, blocks };
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
function checkoutRecord(order: CheckoutOrderSummary): ScrappyOrderRecord {
|
|
269
|
+
function checkoutRecord(order: CheckoutOrderSummary, scope: CommerceScope): ScrappyOrderRecord {
|
|
270
|
+
if (scope.tenantId.length === 0) throw new Error("checkout scope has no tenant");
|
|
270
271
|
const payment: PaymentState = order.payment;
|
|
271
272
|
return {
|
|
272
273
|
id: order.id,
|
|
273
274
|
placedAt: order.placedAt,
|
|
274
|
-
tenantId:
|
|
275
|
-
stage:
|
|
275
|
+
tenantId: scope.tenantId,
|
|
276
|
+
stage: scope.stage,
|
|
276
277
|
currency: order.currency,
|
|
277
278
|
payment: { state: payment, source: "checkout-order", asOf: order.placedAt },
|
|
278
279
|
tax: readSourcedEvidence(order.verification, "tax"),
|
|
@@ -294,24 +295,16 @@ function checkoutRecord(order: CheckoutOrderSummary): ScrappyOrderRecord {
|
|
|
294
295
|
}
|
|
295
296
|
|
|
296
297
|
/** Checkout DTO assessment. `verification: null` reports UNKNOWN and held. */
|
|
297
|
-
export function assessCheckoutOrder(order: CheckoutOrderSummary): CheckoutReadiness {
|
|
298
|
-
const record = checkoutRecord(order);
|
|
299
|
-
const release = order.verification == null
|
|
300
|
-
? heldRelease(releaseOf(record))
|
|
301
|
-
: releaseOf(record);
|
|
298
|
+
export function assessCheckoutOrder(order: CheckoutOrderSummary, scope: CommerceScope): CheckoutReadiness {
|
|
299
|
+
const record = checkoutRecord(order, scope);
|
|
302
300
|
return {
|
|
303
301
|
columns: readinessColumns(record),
|
|
304
302
|
evidence: evidenceOf(record),
|
|
305
|
-
release,
|
|
303
|
+
release: releaseOf(record),
|
|
306
304
|
riskLevel: deriveRisk(order.verification, order.blocker),
|
|
307
305
|
};
|
|
308
306
|
}
|
|
309
307
|
|
|
310
|
-
function heldRelease(release: OrderRelease): OrderRelease {
|
|
311
|
-
if (release.state === "held") return release;
|
|
312
|
-
return { state: "held", ready: false, blocks: [...release.blocks, "evidence"] };
|
|
313
|
-
}
|
|
314
|
-
|
|
315
308
|
export function mapSummaryToRow(s: CheckoutOrderSummary): OrderRow {
|
|
316
309
|
const verificationMissing = s.verification == null;
|
|
317
310
|
const shipState: OrderShipState = verificationMissing ? "hold_review_required" : s.fulfillment;
|
|
@@ -346,8 +339,8 @@ function columnCheck(
|
|
|
346
339
|
return { key: column.key, value: column.state, ...copy };
|
|
347
340
|
}
|
|
348
341
|
|
|
349
|
-
function deriveReadinessChecks(d: CheckoutOrderDetail): OrderReadinessCheck[] {
|
|
350
|
-
const report = assessCheckoutOrder(d);
|
|
342
|
+
function deriveReadinessChecks(d: CheckoutOrderDetail, scope: CommerceScope): OrderReadinessCheck[] {
|
|
343
|
+
const report = assessCheckoutOrder(d, scope);
|
|
351
344
|
const column = (key: OrderReadinessColumn["key"]) => report.columns.find((item) => item.key === key)!;
|
|
352
345
|
const payment = column("payment");
|
|
353
346
|
const tax = column("tax");
|
|
@@ -444,8 +437,8 @@ function deriveEvidenceTimeline(d: CheckoutOrderDetail): OrderEvidenceEvent[] {
|
|
|
444
437
|
return events;
|
|
445
438
|
}
|
|
446
439
|
|
|
447
|
-
export function mapDetailToDetail(d: CheckoutOrderDetail, row: OrderRow): OrderDetail {
|
|
448
|
-
const report = assessCheckoutOrder(d);
|
|
440
|
+
export function mapDetailToDetail(d: CheckoutOrderDetail, row: OrderRow, scope: CommerceScope): OrderDetail {
|
|
441
|
+
const report = assessCheckoutOrder(d, scope);
|
|
449
442
|
return {
|
|
450
443
|
...row,
|
|
451
444
|
lineItems: d.lineItems.map((li) => ({
|
|
@@ -457,7 +450,7 @@ export function mapDetailToDetail(d: CheckoutOrderDetail, row: OrderRow): OrderD
|
|
|
457
450
|
shipping: d.totals.shipping,
|
|
458
451
|
estimatedTax: d.totals.estimatedTax,
|
|
459
452
|
taxesAndFees: d.totals.taxesAndFees,
|
|
460
|
-
readinessChecks: deriveReadinessChecks(d),
|
|
453
|
+
readinessChecks: deriveReadinessChecks(d, scope),
|
|
461
454
|
evidenceTimeline: deriveEvidenceTimeline(d),
|
|
462
455
|
columns: report.columns,
|
|
463
456
|
release: report.release,
|
|
@@ -481,13 +474,14 @@ export function mapOrdersToViewModel(
|
|
|
481
474
|
targetTenant: string,
|
|
482
475
|
summaries: CheckoutOrderSummary[],
|
|
483
476
|
details: Record<string, CheckoutOrderDetail>,
|
|
477
|
+
scope: CommerceScope,
|
|
484
478
|
): OrdersViewModel {
|
|
485
479
|
const rows = summaries.map(mapSummaryToRow);
|
|
486
480
|
const detailsById: Record<string, OrderDetail> = {};
|
|
487
481
|
for (const row of rows) {
|
|
488
482
|
const d = details[row.id];
|
|
489
483
|
detailsById[row.id] = d
|
|
490
|
-
? mapDetailToDetail(d, row)
|
|
484
|
+
? mapDetailToDetail(d, row, scope)
|
|
491
485
|
: {
|
|
492
486
|
...row,
|
|
493
487
|
lineItems: [],
|
|
@@ -520,11 +514,25 @@ export function mapOrdersToViewModel(
|
|
|
520
514
|
}
|
|
521
515
|
|
|
522
516
|
export function emptyOrdersViewModel(targetTenant: string): OrdersViewModel {
|
|
523
|
-
|
|
517
|
+
const rows: OrderRow[] = [];
|
|
518
|
+
return {
|
|
519
|
+
targetTenant,
|
|
520
|
+
queues: buildQueues(rows),
|
|
521
|
+
rows,
|
|
522
|
+
detailsById: {},
|
|
523
|
+
today: {
|
|
524
|
+
grossSales: 0,
|
|
525
|
+
paidOrders: 0,
|
|
526
|
+
averageOrder: 0,
|
|
527
|
+
refunds: 0,
|
|
528
|
+
oldestOpenAt: null,
|
|
529
|
+
},
|
|
530
|
+
};
|
|
524
531
|
}
|
|
525
532
|
|
|
526
533
|
export interface GetOrdersViewModelOptions {
|
|
527
534
|
targetTenant: string;
|
|
535
|
+
scope: CommerceScope;
|
|
528
536
|
client: CheckoutOrderClient;
|
|
529
537
|
filters?: Parameters<CheckoutOrderClient["listOrders"]>[0];
|
|
530
538
|
}
|
|
@@ -569,7 +577,7 @@ export async function getOrdersViewModelResult(opts: GetOrdersViewModelOptions):
|
|
|
569
577
|
);
|
|
570
578
|
return {
|
|
571
579
|
status: orders.length ? "loaded" : "empty",
|
|
572
|
-
vm: mapOrdersToViewModel(opts.targetTenant, orders, details),
|
|
580
|
+
vm: mapOrdersToViewModel(opts.targetTenant, orders, details, opts.scope),
|
|
573
581
|
page,
|
|
574
582
|
};
|
|
575
583
|
} catch {
|
|
@@ -11,13 +11,13 @@ import { readEnv } from "@/lib/env";
|
|
|
11
11
|
import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
|
|
12
12
|
import {
|
|
13
13
|
commerceActorForAdmin,
|
|
14
|
-
commerceStageForCustomization,
|
|
15
14
|
createCheckoutOrderClient,
|
|
16
15
|
orderApiBaseUrl,
|
|
17
16
|
OrderApiError,
|
|
18
17
|
projectCheckoutOrder,
|
|
19
18
|
} from "@/lib/admin/orderClient";
|
|
20
19
|
import { assessCheckoutOrder } from "@/lib/admin/services/orders";
|
|
20
|
+
import { commerceStageForEnv } from "@/lib/fulfillment/fulfillmentService";
|
|
21
21
|
|
|
22
22
|
export const prerender = false;
|
|
23
23
|
|
|
@@ -46,25 +46,27 @@ export async function GET(context: APIContext): Promise<Response> {
|
|
|
46
46
|
const bearer = await readEnv("FOXY_ORDER_API_KEY");
|
|
47
47
|
if (!bearer) return jsonError("not_provisioned", 503);
|
|
48
48
|
|
|
49
|
+
const stage = commerceStageForEnv(customizationEnv);
|
|
50
|
+
if (!stage) return jsonError("stage-unknown", 409);
|
|
51
|
+
const tenantId = tenant.appDomain.trim();
|
|
52
|
+
if (!tenantId) return jsonError("not_provisioned", 503);
|
|
53
|
+
|
|
49
54
|
const client = createCheckoutOrderClient({
|
|
50
55
|
baseUrl: orderApiBaseUrl(customizationEnv),
|
|
51
|
-
appDomain:
|
|
56
|
+
appDomain: tenantId,
|
|
52
57
|
bearer,
|
|
53
58
|
});
|
|
54
59
|
|
|
55
60
|
try {
|
|
56
|
-
const
|
|
61
|
+
const scope = { tenantId, stage };
|
|
57
62
|
const actor = commerceActorForAdmin({
|
|
58
|
-
tenantId
|
|
63
|
+
tenantId,
|
|
59
64
|
stage,
|
|
60
65
|
owner: access.entry.isOwnerViewer,
|
|
61
66
|
admitted: access.entry.admitted,
|
|
62
67
|
});
|
|
63
|
-
const order = projectCheckoutOrder(await client.getOrder(orderId), actor,
|
|
64
|
-
|
|
65
|
-
stage,
|
|
66
|
-
});
|
|
67
|
-
const readiness = assessCheckoutOrder(order);
|
|
68
|
+
const order = projectCheckoutOrder(await client.getOrder(orderId), actor, scope);
|
|
69
|
+
const readiness = assessCheckoutOrder(order, scope);
|
|
68
70
|
return new Response(JSON.stringify({ order, readiness }), { headers: JSON_HEADERS });
|
|
69
71
|
} catch (err) {
|
|
70
72
|
if (err instanceof OrderApiError) return jsonError(err.code, err.status);
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
orderApiBaseUrl,
|
|
18
18
|
OrderApiError,
|
|
19
19
|
} from "@/lib/admin/orderClient";
|
|
20
|
+
import { commerceStageForEnv } from "@/lib/fulfillment/fulfillmentService";
|
|
20
21
|
import { assessCheckoutOrder, parseOrderFilters } from "@/lib/admin/services/orders";
|
|
21
22
|
|
|
22
23
|
export const prerender = false;
|
|
@@ -49,21 +50,27 @@ export async function GET(context: APIContext): Promise<Response> {
|
|
|
49
50
|
return jsonError("not_provisioned", 503);
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
const stage = commerceStageForEnv(customizationEnv);
|
|
54
|
+
if (!stage) return jsonError("stage-unknown", 409);
|
|
55
|
+
const tenantId = tenant.appDomain.trim();
|
|
56
|
+
if (!tenantId) return jsonError("not_provisioned", 503);
|
|
57
|
+
|
|
52
58
|
const url = new URL(context.request.url);
|
|
53
59
|
// Tenant comes from the host, never from a query parameter.
|
|
54
60
|
const filters = parseOrderFilters(url.searchParams);
|
|
55
61
|
|
|
56
62
|
const client = createCheckoutOrderClient({
|
|
57
63
|
baseUrl: orderApiBaseUrl(customizationEnv),
|
|
58
|
-
appDomain:
|
|
64
|
+
appDomain: tenantId,
|
|
59
65
|
bearer,
|
|
60
66
|
});
|
|
61
67
|
|
|
62
68
|
try {
|
|
63
69
|
const data = await client.listOrders(filters);
|
|
70
|
+
const scope = { tenantId, stage };
|
|
64
71
|
const orders = data.orders.map((order) => ({
|
|
65
72
|
...order,
|
|
66
|
-
readiness: assessCheckoutOrder(order),
|
|
73
|
+
readiness: assessCheckoutOrder(order, scope),
|
|
67
74
|
}));
|
|
68
75
|
return new Response(JSON.stringify({ ...data, orders }), { headers: JSON_HEADERS });
|
|
69
76
|
} catch (err) {
|
|
@@ -21,8 +21,7 @@ import ReviewSummary from "@/components/commerce/ReviewSummary.astro";
|
|
|
21
21
|
import ReviewList from "@/components/commerce/ReviewList.astro";
|
|
22
22
|
import { isOnSale, isNew, isAvailable, fromPrice } from "@/lib/tot/query";
|
|
23
23
|
import {
|
|
24
|
-
|
|
25
|
-
priceForPurchaseOption,
|
|
24
|
+
buildMemberSignedCartPayload,
|
|
26
25
|
workerCartApiPath,
|
|
27
26
|
normalizePurchaseOptions,
|
|
28
27
|
visiblePurchaseOptions,
|
|
@@ -243,18 +242,13 @@ if (commerce) {
|
|
|
243
242
|
async (v) => {
|
|
244
243
|
const payloads = await Promise.all(
|
|
245
244
|
purchaseOptions.map(async (purchaseOption) => {
|
|
246
|
-
const effective = priceForPurchaseOption(
|
|
247
|
-
v.price,
|
|
248
|
-
purchaseOption,
|
|
249
|
-
pricingCtx,
|
|
250
|
-
);
|
|
251
245
|
return [
|
|
252
246
|
purchaseOption.id,
|
|
253
|
-
await
|
|
247
|
+
await buildMemberSignedCartPayload(product, v, {
|
|
254
248
|
mode: commerce.mode,
|
|
255
249
|
secret: signing.secret,
|
|
256
250
|
purchaseOption,
|
|
257
|
-
|
|
251
|
+
pricing: pricingCtx,
|
|
258
252
|
autopilotEnabled,
|
|
259
253
|
tenantId: tenant.tenant_id,
|
|
260
254
|
}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.62",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -276,6 +276,8 @@ export interface MemberSignedCartPayloadOptions {
|
|
|
276
276
|
* is off. Callers should still pre-filter with {@link visiblePurchaseOptions}.
|
|
277
277
|
*/
|
|
278
278
|
autopilotEnabled: boolean;
|
|
279
|
+
/** Tenant id written onto the signed `tenant` field when present. */
|
|
280
|
+
tenantId?: string;
|
|
279
281
|
}
|
|
280
282
|
|
|
281
283
|
/**
|
|
@@ -309,6 +311,7 @@ export async function buildMemberSignedCartPayload(
|
|
|
309
311
|
purchaseOption: opts.purchaseOption,
|
|
310
312
|
signedPrice: effective.price,
|
|
311
313
|
autopilotEnabled: opts.autopilotEnabled,
|
|
314
|
+
tenantId: opts.tenantId,
|
|
312
315
|
});
|
|
313
316
|
}
|
|
314
317
|
|