@tokenoftrust/storefront-runner 2.4.91 → 2.5.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/apps/storefront/migrations-apps/0017_commerce_durable_projection.sql +28 -0
- package/apps/storefront/migrations-apps/0018_commerce_provider_cart_identity.sql +13 -0
- package/apps/storefront/src/config/declaredTenantConfig.ts +5 -0
- package/apps/storefront/src/config/devTenantSeed.ts +3 -2
- package/apps/storefront/src/lib/checkout/commerce/commerceAdmission.ts +108 -0
- package/apps/storefront/src/lib/checkout/commerce/finalCartAuthorization.ts +1 -1
- package/apps/storefront/src/lib/checkout/shared/checkoutCommerce.ts +6 -4
- package/apps/storefront/src/lib/fulfillment/admin/commerceDesk.ts +121 -0
- package/apps/storefront/src/lib/fulfillment/admin/commerceDeskRouting.ts +45 -0
- package/apps/storefront/src/lib/fulfillment/admin/viewmodels/fulfillment.ts +1 -1
- package/apps/storefront/src/lib/fulfillment/commerceProjection.ts +146 -0
- package/apps/storefront/src/lib/fulfillment/commerceReadback.ts +60 -0
- package/apps/storefront/src/lib/fulfillment/commerceRecovery.ts +65 -0
- package/apps/storefront/src/lib/fulfillment/commerceRuntime.ts +13 -0
- package/apps/storefront/src/lib/fulfillment/fulfillmentService.ts +50 -4
- package/apps/storefront/src/lib/fulfillment/readiness.ts +1 -0
- package/apps/storefront/src/lib/fulfillment/store.ts +15 -0
- package/apps/storefront/src/lib/integrations/webhooks/deliveryEngine.ts +6 -3
- package/apps/storefront/src/lib/integrations/webhooks/deliveryStore.ts +6 -5
- package/apps/storefront/src/lib/inventory/allocations.ts +58 -9
- package/apps/storefront/src/lib/inventory/mirrorStock.ts +121 -0
- package/apps/storefront/src/lib/orders/admin/services/orders.ts +8 -8
- package/apps/storefront/src/lib/orders/data/orderMapper.ts +22 -1
- package/apps/storefront/src/lib/orders/data/ordersStore.ts +12 -4
- package/apps/storefront/src/pages/api/admin/commerce-readiness.ts +56 -26
- package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +34 -2
- package/apps/storefront/src/pages/api/commerce/final-cart-authorization.ts +158 -306
- package/apps/storefront/src/pages/products/[handle].astro +13 -8
- package/apps/storefront/test/pipeline/harness.ts +6 -0
- package/apps/storefront/test/pipeline/pipeline-host.ts +29 -0
- package/package.json +1 -1
- package/packages/public-runtime/src/checkout-orders.ts +45 -7
- package/packages/public-runtime/src/checkout.ts +37 -9
- package/packages/public-runtime/src/tenant.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
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",
|
|
@@ -223,7 +223,7 @@ export const LABEL_STATES = [
|
|
|
223
223
|
] as const;
|
|
224
224
|
export type LabelState = (typeof LABEL_STATES)[number];
|
|
225
225
|
|
|
226
|
-
export const EVIDENCE_STATES = ["fresh", "UNKNOWN", "stale", "unreadable", "absent"] as const;
|
|
226
|
+
export const EVIDENCE_STATES = ["fresh", "not-required", "UNKNOWN", "stale", "unreadable", "absent"] as const;
|
|
227
227
|
export type EvidenceState = (typeof EVIDENCE_STATES)[number];
|
|
228
228
|
|
|
229
229
|
export const PROVIDER_OUTCOMES = ["intent", "accepted", "unknown", "voided", "refunded"] as const;
|
|
@@ -331,6 +331,48 @@ export interface FinalCartBinding {
|
|
|
331
331
|
taxExpiresAt: string;
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
/** Provider-authenticated cart facts; authority evidence is resolved by the receiving server. */
|
|
335
|
+
export interface CommerceProviderCartSnapshot {
|
|
336
|
+
provider: "foxycart";
|
|
337
|
+
providerCartId: string;
|
|
338
|
+
providerCartDigest: string;
|
|
339
|
+
/** Current PRIVATE provider-cart reference, verified by the provider integration. */
|
|
340
|
+
expectedAuthorizationId?: string;
|
|
341
|
+
tenantId: string;
|
|
342
|
+
stage: CommerceStage;
|
|
343
|
+
currency: string;
|
|
344
|
+
lines: readonly FinalCartLine[];
|
|
345
|
+
totals: MinorTotals;
|
|
346
|
+
destinationDigest: string;
|
|
347
|
+
destination: { country: string; region: string };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Safe source references explaining each server-owned authority decision. */
|
|
351
|
+
export interface CommerceAuthoritySource {
|
|
352
|
+
requirement: "required" | "not-required";
|
|
353
|
+
state: "satisfied" | "not-required";
|
|
354
|
+
source: string;
|
|
355
|
+
policyRevision: string;
|
|
356
|
+
expiresAt: string;
|
|
357
|
+
}
|
|
358
|
+
export type CommerceEvidenceProvenance = Record<keyof CommerceEvidence, CommerceAuthoritySource>;
|
|
359
|
+
|
|
360
|
+
export interface SyntheticCommerceAuthorityPolicy {
|
|
361
|
+
purpose: "synthetic-nonbillable";
|
|
362
|
+
revision: string;
|
|
363
|
+
expiresAt: string;
|
|
364
|
+
requirements: Record<"verification" | "tax" | "risk", "required" | "not-required">;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export interface CommerceAdmissionSnapshot extends CommerceProviderCartSnapshot {
|
|
368
|
+
authorizationId: string;
|
|
369
|
+
cartDigest: string;
|
|
370
|
+
evidence: CommerceEvidence;
|
|
371
|
+
provenance: CommerceEvidenceProvenance;
|
|
372
|
+
expiresAt: string;
|
|
373
|
+
now: string;
|
|
374
|
+
}
|
|
375
|
+
|
|
334
376
|
export interface AsymmetricCallerToken {
|
|
335
377
|
scheme: "asymmetric-jwks";
|
|
336
378
|
signatureValid: boolean;
|
|
@@ -358,12 +400,8 @@ export type CallerTokenRefusal =
|
|
|
358
400
|
| "stage";
|
|
359
401
|
|
|
360
402
|
export function evidencePermitsRelease(evidence: CommerceEvidence): boolean {
|
|
361
|
-
return (
|
|
362
|
-
|
|
363
|
-
evidence.tax === "fresh" &&
|
|
364
|
-
evidence.risk === "fresh" &&
|
|
365
|
-
evidence.stock === "fresh"
|
|
366
|
-
);
|
|
403
|
+
return evidence.stock === "fresh" && (["verification", "tax", "risk"] as const)
|
|
404
|
+
.every(field => evidence[field] === "fresh" || evidence[field] === "not-required");
|
|
367
405
|
}
|
|
368
406
|
|
|
369
407
|
export function paymentIsCaptured(payment: PaymentState): boolean {
|
|
@@ -25,13 +25,32 @@ import type {
|
|
|
25
25
|
SubscriptionPurchaseOption,
|
|
26
26
|
} from "./product.js";
|
|
27
27
|
import { DEFAULT_ONE_TIME_PURCHASE_OPTION } from "./product.js";
|
|
28
|
+
import type { TenantConfig } from "./tenant.js";
|
|
29
|
+
|
|
30
|
+
/** Stable commerce scope shared by signed carts, provider callers and operator authorization. */
|
|
31
|
+
export function commerceTenantId(tenant: Pick<TenantConfig, "appDomain">): string {
|
|
32
|
+
return tenant.appDomain;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Public API destination on the declared nonproduction wildcard Worker route. */
|
|
36
|
+
export const SAMPLE_CART_HANDOFF_URL =
|
|
37
|
+
"https://synthetic-vendor-b.tokenoftrust.store/api/commerce/checkout-handoff";
|
|
38
|
+
|
|
39
|
+
/** Identifier-only local sample form. Product facts are resolved by the hosted catalog. */
|
|
40
|
+
export function sampleVariantCartPayload(handle: string, variantId: string): VariantCartPayload {
|
|
41
|
+
return {
|
|
42
|
+
hidden: [
|
|
43
|
+
{ name: "sample_product", value: handle },
|
|
44
|
+
{ name: "sample_variant", value: variantId },
|
|
45
|
+
],
|
|
46
|
+
quantityName: "quantity",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
28
49
|
|
|
29
50
|
/**
|
|
30
51
|
* Per-tenant managed-checkout wiring. Public config only — the signing SECRET is never
|
|
31
|
-
* stored here (it's an env-injected Worker binding, resolved per request).
|
|
32
|
-
* `unsigned-demo`
|
|
33
|
-
* has cart validation turned OFF, so a developer can run the full cart locally
|
|
34
|
-
* (and on our shared preview worker) with zero secrets.
|
|
52
|
+
* stored here (it's an env-injected Worker binding, resolved per request).
|
|
53
|
+
* `unsigned-demo` sends plain fields and reads no secret.
|
|
35
54
|
*/
|
|
36
55
|
export interface ManagedCheckoutConfig {
|
|
37
56
|
/** Checkout store subdomain, e.g. "tot-demo". */
|
|
@@ -42,8 +61,8 @@ export interface ManagedCheckoutConfig {
|
|
|
42
61
|
*/
|
|
43
62
|
storeDomain?: string;
|
|
44
63
|
/**
|
|
45
|
-
* "signed" (
|
|
46
|
-
* "unsigned-demo" (
|
|
64
|
+
* "signed" (cart validation ON, HMAC every field; requires FOXY_CART_SECRET) or
|
|
65
|
+
* "unsigned-demo" (plain fields, reads no secret).
|
|
47
66
|
*/
|
|
48
67
|
mode: "signed" | "unsigned-demo";
|
|
49
68
|
/**
|
|
@@ -59,9 +78,15 @@ export interface ManagedCheckoutConfig {
|
|
|
59
78
|
cartDrawerCss?: string;
|
|
60
79
|
}
|
|
61
80
|
|
|
81
|
+
/** Runtime tenant records must name a supported mode before they can build or admit a cart. */
|
|
82
|
+
export function isManagedCheckoutMode(value: unknown): value is ManagedCheckoutConfig["mode"] {
|
|
83
|
+
return value === "signed" || value === "unsigned-demo";
|
|
84
|
+
}
|
|
85
|
+
|
|
62
86
|
/**
|
|
63
|
-
* The ONE shared ToT-owned demo checkout store (
|
|
64
|
-
*
|
|
87
|
+
* The ONE shared ToT-owned demo checkout store (`tot-preview`). Carts are
|
|
88
|
+
* signed: HMAC cart validation is required, and FOXY_CART_SECRET must be set
|
|
89
|
+
* before they are served. The local dev loop (`synthesizeDevTenant`) wires every
|
|
65
90
|
* local commerce checkout to this exact config; the hosted QA/test tier's
|
|
66
91
|
* demo-checkout fallback (`resolveTenantCommerce`) reuses this same constant
|
|
67
92
|
* rather than a second copy of the literal, so "the same as `tot dev`" stays
|
|
@@ -69,7 +94,7 @@ export interface ManagedCheckoutConfig {
|
|
|
69
94
|
*/
|
|
70
95
|
export const SHARED_DEMO_CHECKOUT: ManagedCheckoutConfig = {
|
|
71
96
|
storeSubDomain: "tot-preview",
|
|
72
|
-
mode: "
|
|
97
|
+
mode: "signed",
|
|
73
98
|
};
|
|
74
99
|
|
|
75
100
|
/** Resolved, request-scoped commerce facts stashed on `Astro.locals`. */
|
|
@@ -244,6 +269,7 @@ export function quantityAllowed(value: string): boolean {
|
|
|
244
269
|
export function resolveManagedCheckout(
|
|
245
270
|
config: ManagedCheckoutConfig,
|
|
246
271
|
): ResolvedManagedCheckout {
|
|
272
|
+
if (!isManagedCheckoutMode(config.mode)) throw new CartSigningUnavailableError();
|
|
247
273
|
const storeDomain =
|
|
248
274
|
config.storeDomain ??
|
|
249
275
|
`${config.storeSubDomain}${DEFAULT_CHECKOUT_HOST_SUFFIX}`;
|
|
@@ -463,6 +489,7 @@ export async function buildVariantCartPayload(
|
|
|
463
489
|
variant: CatalogVariant,
|
|
464
490
|
opts: BuildVariantCartPayloadOptions,
|
|
465
491
|
): Promise<VariantCartPayload> {
|
|
492
|
+
if (!isManagedCheckoutMode(opts.mode)) throw new CartSigningUnavailableError();
|
|
466
493
|
// Hide-at-launch guard: refuse to SIGN a subscription option while
|
|
467
494
|
// Autopilot is disabled. `visiblePurchaseOptions` removes subscription options
|
|
468
495
|
// for such tenants, so one reaching the signer means the visibility filter was
|
|
@@ -586,6 +613,7 @@ export async function enforceCartSubmission(input: {
|
|
|
586
613
|
secret?: string;
|
|
587
614
|
trustedTenantId: string;
|
|
588
615
|
}): Promise<CartEnforcement> {
|
|
616
|
+
if (!isManagedCheckoutMode(input.mode)) return denial("signing-unavailable");
|
|
589
617
|
if (input.mode === "signed" && !input.secret) return denial("signing-unavailable");
|
|
590
618
|
if (!input.trustedTenantId) return denial("tenant");
|
|
591
619
|
|
|
@@ -12,6 +12,7 @@ import type { IntegrationDeclarationInput } from "./integration/resolve.js";
|
|
|
12
12
|
import type { EuUkVisitorPolicy } from "./integration/plan.js";
|
|
13
13
|
import type { PublicationSpaceManifestV1 } from "./publication-space.js";
|
|
14
14
|
import type { HtmlCachePolicy } from "./html-cache-policy.js";
|
|
15
|
+
import type { SyntheticCommerceAuthorityPolicy } from "./checkout-orders.js";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* The shared semantic COLOR contract — the widget library's public API.
|
|
@@ -609,6 +610,8 @@ export interface TenantConfig {
|
|
|
609
610
|
*/
|
|
610
611
|
commerce?: {
|
|
611
612
|
checkout?: ManagedCheckoutConfig;
|
|
613
|
+
/** Server-owned applicability for nonbillable synthetic fixtures; never waives regulated rules. */
|
|
614
|
+
authorityPolicy?: SyntheticCommerceAuthorityPolicy;
|
|
612
615
|
/**
|
|
613
616
|
* Autopilot (subscription) program visibility. Absent → subscription
|
|
614
617
|
* purchase options render wherever a product authors them (framework/dev
|