@porulle/core 0.10.2 → 0.10.4
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/hooks/checkout-completion.d.ts.map +1 -1
- package/dist/hooks/checkout-completion.js +12 -8
- package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/checkout.js +4 -1
- package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/orders.js +26 -1
- package/dist/interfaces/rest/schemas/orders.d.ts +207 -0
- package/dist/interfaces/rest/schemas/orders.d.ts.map +1 -1
- package/dist/interfaces/rest/schemas/orders.js +62 -0
- package/dist/modules/orders/quote.d.ts +47 -0
- package/dist/modules/orders/quote.d.ts.map +1 -0
- package/dist/modules/orders/quote.js +74 -0
- package/dist/modules/orders/service.d.ts.map +1 -1
- package/dist/modules/orders/service.js +10 -2
- package/dist/modules/tax/service.d.ts.map +1 -1
- package/dist/modules/tax/service.js +4 -1
- package/package.json +3 -3
- package/src/hooks/checkout-completion.ts +22 -18
- package/src/interfaces/rest/routes/checkout.ts +4 -1
- package/src/interfaces/rest/routes/orders.ts +36 -1
- package/src/interfaces/rest/schemas/orders.ts +68 -0
- package/src/modules/orders/quote.ts +131 -0
- package/src/modules/orders/service.ts +10 -3
- package/src/modules/tax/service.ts +9 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout-completion.d.ts","sourceRoot":"","sources":["../../src/hooks/checkout-completion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAgDlD;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,EAAE,IAAI,CACrC,YAAY,EACZ,oBAAoB,EAAE,CAyDvB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,IAAI,CACnC,YAAY,EACZ;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"checkout-completion.d.ts","sourceRoot":"","sources":["../../src/hooks/checkout-completion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAgDlD;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,EAAE,IAAI,CACrC,YAAY,EACZ,oBAAoB,EAAE,CAyDvB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,IAAI,CACnC,YAAY,EACZ;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA0D5C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,EAAE,IAAI,CACxC,YAAY,EACZ;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAwBpB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,IAAI,CACrC,YAAY,EACZ;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CA+ClB,CAAC"}
|
|
@@ -67,17 +67,21 @@ export const capturePaymentStep = {
|
|
|
67
67
|
return Err(new CommerceValidationError("No authorized payment intent to capture."));
|
|
68
68
|
}
|
|
69
69
|
const payments = ctx.hook.services.payments;
|
|
70
|
-
|
|
70
|
+
// Capture the full checkout total. Pass it explicitly — an adapter that
|
|
71
|
+
// returns 0 for an omitted amount (dev mock, some custom adapters) would
|
|
72
|
+
// otherwise cause a $0 capture to be recorded on every order.
|
|
73
|
+
const result = await payments.capture(data.paymentIntentId, data.total);
|
|
71
74
|
if (!result.ok) {
|
|
72
75
|
return Err(new CommerceValidationError(`Payment capture failed: ${result.error?.message ?? "unknown"}`));
|
|
73
76
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
const orders = ctx.hook.services.orders;
|
|
78
|
+
if (orders.updateOrder) {
|
|
79
|
+
// Record the adapter's positive figure, else the total we captured.
|
|
80
|
+
const reported = result.value?.amountCaptured;
|
|
81
|
+
const amountCaptured = reported && reported > 0 ? reported : data.total;
|
|
82
|
+
const updated = await orders.updateOrder(data.checkoutId, { amountCaptured }, ctx.hook.actor);
|
|
83
|
+
if (!updated.ok) {
|
|
84
|
+
return Err(new CommerceValidationError("Failed to persist captured payment amount."));
|
|
81
85
|
}
|
|
82
86
|
}
|
|
83
87
|
return Ok({ paymentIntentId: data.paymentIntentId, amount: data.total });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAoBzD,OAAO,EAAE,KAAK,MAAM,EAAwC,MAAM,aAAa,CAAC;AAKhF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,EAC1C,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmC7B;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAoBzD,OAAO,EAAE,KAAK,MAAM,EAAwC,MAAM,aAAa,CAAC;AAKhF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,EAC1C,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmC7B;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,gCA+S5C"}
|
|
@@ -130,8 +130,11 @@ export function checkoutRoutes(kernel) {
|
|
|
130
130
|
resolveCurrentPrices,
|
|
131
131
|
checkInventoryAvailability,
|
|
132
132
|
applyPromotionCodes,
|
|
133
|
-
calculateTax
|
|
133
|
+
// Shipping BEFORE tax: calculateTax reads data.shippingTotal for
|
|
134
|
+
// appliesToShipping rates, so shipping must be computed first or shipping
|
|
135
|
+
// tax is silently never collected (audit C1).
|
|
134
136
|
calculateShipping,
|
|
137
|
+
calculateTax,
|
|
135
138
|
...kernel.hooks.resolve("checkout.beforePayment"),
|
|
136
139
|
validatePaymentMethod,
|
|
137
140
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,KAAK,MAAM,EAAiE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,KAAK,MAAM,EAAiE,MAAM,aAAa,CAAC;AAKzG,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,gCAkTzC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
2
|
-
import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
|
|
2
|
+
import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, quoteOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
|
|
3
3
|
import { isUUID, mapErrorToResponse, mapErrorToStatus, parsePagination } from "../utils.js";
|
|
4
|
+
import { computeOrderPricing } from "../../../modules/orders/quote.js";
|
|
4
5
|
import { assertPermission } from "../../../auth/permissions.js";
|
|
5
6
|
export function orderRoutes(kernel) {
|
|
6
7
|
const router = new OpenAPIHono();
|
|
@@ -69,6 +70,30 @@ export function orderRoutes(kernel) {
|
|
|
69
70
|
return c.json({ data: result.value }, 201);
|
|
70
71
|
});
|
|
71
72
|
// @ts-expect-error -- openapi handler union return type
|
|
73
|
+
router.openapi(quoteOrderRoute, async (c) => {
|
|
74
|
+
try {
|
|
75
|
+
assertPermission(c.get("actor"), "orders:manage");
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
|
|
79
|
+
}
|
|
80
|
+
const body = c.req.valid("json");
|
|
81
|
+
try {
|
|
82
|
+
const breakdown = await computeOrderPricing(kernel, {
|
|
83
|
+
currency: body.currency,
|
|
84
|
+
lineItems: body.lineItems,
|
|
85
|
+
...(body.customerId !== undefined ? { customerId: body.customerId } : {}),
|
|
86
|
+
...(body.customerGroupIds !== undefined ? { customerGroupIds: body.customerGroupIds } : {}),
|
|
87
|
+
...(body.promotionCodes !== undefined ? { promotionCodes: body.promotionCodes } : {}),
|
|
88
|
+
...(body.shippingAddress !== undefined ? { shippingAddress: body.shippingAddress } : {}),
|
|
89
|
+
}, c.get("actor"));
|
|
90
|
+
return c.json({ data: breakdown });
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
// @ts-expect-error -- openapi handler union return type
|
|
72
97
|
router.openapi(refundOrderRoute, async (c) => {
|
|
73
98
|
const body = c.req.valid("json");
|
|
74
99
|
const result = await kernel.services.orders.refund(c.req.param("id"), c.get("actor"), body?.reason ?? "refunded", undefined, body?.amount !== undefined ? { amount: body.amount } : undefined);
|
|
@@ -36,6 +36,213 @@ export declare const CreateOrderBodySchema: z.ZodObject<{
|
|
|
36
36
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
37
37
|
}, z.core.$strip>>;
|
|
38
38
|
}, z.core.$strip>;
|
|
39
|
+
export declare const QuoteOrderBodySchema: z.ZodObject<{
|
|
40
|
+
currency: z.ZodString;
|
|
41
|
+
lineItems: z.ZodArray<z.ZodObject<{
|
|
42
|
+
entityId: z.ZodUUID;
|
|
43
|
+
entityType: z.ZodOptional<z.ZodString>;
|
|
44
|
+
variantId: z.ZodOptional<z.ZodUUID>;
|
|
45
|
+
quantity: z.ZodNumber;
|
|
46
|
+
title: z.ZodOptional<z.ZodString>;
|
|
47
|
+
}, z.core.$strip>>;
|
|
48
|
+
customerId: z.ZodOptional<z.ZodUUID>;
|
|
49
|
+
customerGroupIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
50
|
+
promotionCodes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
51
|
+
shippingAddress: z.ZodOptional<z.ZodObject<{
|
|
52
|
+
line1: z.ZodString;
|
|
53
|
+
line2: z.ZodOptional<z.ZodString>;
|
|
54
|
+
city: z.ZodString;
|
|
55
|
+
state: z.ZodOptional<z.ZodString>;
|
|
56
|
+
postalCode: z.ZodString;
|
|
57
|
+
country: z.ZodString;
|
|
58
|
+
}, z.core.$strip>>;
|
|
59
|
+
}, z.core.$strip>;
|
|
60
|
+
export declare const QuoteOrderResultSchema: z.ZodObject<{
|
|
61
|
+
data: z.ZodObject<{
|
|
62
|
+
currency: z.ZodString;
|
|
63
|
+
subtotal: z.ZodNumber;
|
|
64
|
+
discountTotal: z.ZodNumber;
|
|
65
|
+
shippingTotal: z.ZodNumber;
|
|
66
|
+
taxTotal: z.ZodNumber;
|
|
67
|
+
grandTotal: z.ZodNumber;
|
|
68
|
+
lineItems: z.ZodArray<z.ZodObject<{
|
|
69
|
+
entityId: z.ZodString;
|
|
70
|
+
variantId: z.ZodOptional<z.ZodString>;
|
|
71
|
+
quantity: z.ZodNumber;
|
|
72
|
+
unitPrice: z.ZodNumber;
|
|
73
|
+
totalPrice: z.ZodNumber;
|
|
74
|
+
discountAmount: z.ZodNumber;
|
|
75
|
+
taxAmount: z.ZodNumber;
|
|
76
|
+
}, z.core.$strip>>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
}, z.core.$strip>;
|
|
79
|
+
export declare const quoteOrderRoute: {
|
|
80
|
+
method: "post";
|
|
81
|
+
path: "/quote";
|
|
82
|
+
tags: string[];
|
|
83
|
+
summary: string;
|
|
84
|
+
description: string;
|
|
85
|
+
request: {
|
|
86
|
+
body: {
|
|
87
|
+
content: {
|
|
88
|
+
"application/json": {
|
|
89
|
+
schema: z.ZodObject<{
|
|
90
|
+
currency: z.ZodString;
|
|
91
|
+
lineItems: z.ZodArray<z.ZodObject<{
|
|
92
|
+
entityId: z.ZodUUID;
|
|
93
|
+
entityType: z.ZodOptional<z.ZodString>;
|
|
94
|
+
variantId: z.ZodOptional<z.ZodUUID>;
|
|
95
|
+
quantity: z.ZodNumber;
|
|
96
|
+
title: z.ZodOptional<z.ZodString>;
|
|
97
|
+
}, z.core.$strip>>;
|
|
98
|
+
customerId: z.ZodOptional<z.ZodUUID>;
|
|
99
|
+
customerGroupIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
100
|
+
promotionCodes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
101
|
+
shippingAddress: z.ZodOptional<z.ZodObject<{
|
|
102
|
+
line1: z.ZodString;
|
|
103
|
+
line2: z.ZodOptional<z.ZodString>;
|
|
104
|
+
city: z.ZodString;
|
|
105
|
+
state: z.ZodOptional<z.ZodString>;
|
|
106
|
+
postalCode: z.ZodString;
|
|
107
|
+
country: z.ZodString;
|
|
108
|
+
}, z.core.$strip>>;
|
|
109
|
+
}, z.core.$strip>;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
required: true;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
responses: {
|
|
116
|
+
400: {
|
|
117
|
+
readonly content: {
|
|
118
|
+
readonly "application/json": {
|
|
119
|
+
readonly schema: z.ZodObject<{
|
|
120
|
+
error: z.ZodObject<{
|
|
121
|
+
code: z.ZodString;
|
|
122
|
+
message: z.ZodString;
|
|
123
|
+
details: z.ZodOptional<z.ZodObject<{
|
|
124
|
+
issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
125
|
+
path: z.ZodString;
|
|
126
|
+
message: z.ZodString;
|
|
127
|
+
code: z.ZodString;
|
|
128
|
+
}, z.core.$strip>>>;
|
|
129
|
+
}, z.core.$strip>>;
|
|
130
|
+
}, z.core.$strip>;
|
|
131
|
+
}, z.core.$strip>;
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
readonly description: "Business logic error.";
|
|
135
|
+
};
|
|
136
|
+
401: {
|
|
137
|
+
readonly content: {
|
|
138
|
+
readonly "application/json": {
|
|
139
|
+
readonly schema: z.ZodObject<{
|
|
140
|
+
error: z.ZodObject<{
|
|
141
|
+
code: z.ZodString;
|
|
142
|
+
message: z.ZodString;
|
|
143
|
+
details: z.ZodOptional<z.ZodObject<{
|
|
144
|
+
issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
145
|
+
path: z.ZodString;
|
|
146
|
+
message: z.ZodString;
|
|
147
|
+
code: z.ZodString;
|
|
148
|
+
}, z.core.$strip>>>;
|
|
149
|
+
}, z.core.$strip>>;
|
|
150
|
+
}, z.core.$strip>;
|
|
151
|
+
}, z.core.$strip>;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
readonly description: "Authentication required.";
|
|
155
|
+
};
|
|
156
|
+
403: {
|
|
157
|
+
readonly content: {
|
|
158
|
+
readonly "application/json": {
|
|
159
|
+
readonly schema: z.ZodObject<{
|
|
160
|
+
error: z.ZodObject<{
|
|
161
|
+
code: z.ZodString;
|
|
162
|
+
message: z.ZodString;
|
|
163
|
+
details: z.ZodOptional<z.ZodObject<{
|
|
164
|
+
issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
165
|
+
path: z.ZodString;
|
|
166
|
+
message: z.ZodString;
|
|
167
|
+
code: z.ZodString;
|
|
168
|
+
}, z.core.$strip>>>;
|
|
169
|
+
}, z.core.$strip>>;
|
|
170
|
+
}, z.core.$strip>;
|
|
171
|
+
}, z.core.$strip>;
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
readonly description: "Insufficient permissions.";
|
|
175
|
+
};
|
|
176
|
+
404: {
|
|
177
|
+
readonly content: {
|
|
178
|
+
readonly "application/json": {
|
|
179
|
+
readonly schema: z.ZodObject<{
|
|
180
|
+
error: z.ZodObject<{
|
|
181
|
+
code: z.ZodString;
|
|
182
|
+
message: z.ZodString;
|
|
183
|
+
details: z.ZodOptional<z.ZodObject<{
|
|
184
|
+
issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
185
|
+
path: z.ZodString;
|
|
186
|
+
message: z.ZodString;
|
|
187
|
+
code: z.ZodString;
|
|
188
|
+
}, z.core.$strip>>>;
|
|
189
|
+
}, z.core.$strip>>;
|
|
190
|
+
}, z.core.$strip>;
|
|
191
|
+
}, z.core.$strip>;
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
readonly description: "Resource not found.";
|
|
195
|
+
};
|
|
196
|
+
422: {
|
|
197
|
+
readonly content: {
|
|
198
|
+
readonly "application/json": {
|
|
199
|
+
readonly schema: z.ZodObject<{
|
|
200
|
+
error: z.ZodObject<{
|
|
201
|
+
code: z.ZodString;
|
|
202
|
+
message: z.ZodString;
|
|
203
|
+
details: z.ZodOptional<z.ZodObject<{
|
|
204
|
+
issues: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
205
|
+
path: z.ZodString;
|
|
206
|
+
message: z.ZodString;
|
|
207
|
+
code: z.ZodString;
|
|
208
|
+
}, z.core.$strip>>>;
|
|
209
|
+
}, z.core.$strip>>;
|
|
210
|
+
}, z.core.$strip>;
|
|
211
|
+
}, z.core.$strip>;
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
readonly description: "Validation error.";
|
|
215
|
+
};
|
|
216
|
+
200: {
|
|
217
|
+
content: {
|
|
218
|
+
"application/json": {
|
|
219
|
+
schema: z.ZodObject<{
|
|
220
|
+
data: z.ZodObject<{
|
|
221
|
+
currency: z.ZodString;
|
|
222
|
+
subtotal: z.ZodNumber;
|
|
223
|
+
discountTotal: z.ZodNumber;
|
|
224
|
+
shippingTotal: z.ZodNumber;
|
|
225
|
+
taxTotal: z.ZodNumber;
|
|
226
|
+
grandTotal: z.ZodNumber;
|
|
227
|
+
lineItems: z.ZodArray<z.ZodObject<{
|
|
228
|
+
entityId: z.ZodString;
|
|
229
|
+
variantId: z.ZodOptional<z.ZodString>;
|
|
230
|
+
quantity: z.ZodNumber;
|
|
231
|
+
unitPrice: z.ZodNumber;
|
|
232
|
+
totalPrice: z.ZodNumber;
|
|
233
|
+
discountAmount: z.ZodNumber;
|
|
234
|
+
taxAmount: z.ZodNumber;
|
|
235
|
+
}, z.core.$strip>>;
|
|
236
|
+
}, z.core.$strip>;
|
|
237
|
+
}, z.core.$strip>;
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
description: string;
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
} & {
|
|
244
|
+
getRoutingPath(): "/quote";
|
|
245
|
+
};
|
|
39
246
|
export declare const OrderDataResponseSchema: z.ZodObject<{
|
|
40
247
|
data: import("drizzle-zod").BuildSchema<"select", {
|
|
41
248
|
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/schemas/orders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAe,MAAM,mBAAmB,CAAC;AAMnD,eAAO,MAAM,2BAA2B;;;iBAMF,CAAC;AAEvC,eAAO,MAAM,qBAAqB;;;iBAIF,CAAC;AAEjC,eAAO,MAAM,sBAAsB;;iBAGF,CAAC;AAgBlC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;iBAgBF,CAAC;
|
|
1
|
+
{"version":3,"file":"orders.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/schemas/orders.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBtC,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmB3B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB3B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoB5B,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;iBAYF,CAAC;AAEtC,eAAO,MAAM,6BAA6B;;iBAEF,CAAC;AAOzC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBhC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoBnC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgBnC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqBjC,CAAC;AAIH,eAAO,MAAM,qBAAqB;;;;;;iBAMF,CAAC;AASjC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAchC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU/B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUhC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAS/B,CAAC;AAIH,eAAO,MAAM,yBAAyB;;;iBAGF,CAAC;AASrC,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAa/B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU9B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU/B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAU7B,CAAC"}
|
|
@@ -48,6 +48,68 @@ export const CreateOrderBodySchema = z.object({
|
|
|
48
48
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
49
49
|
lineItems: z.array(CreateOrderLineItemSchema).min(1),
|
|
50
50
|
}).openapi("CreateOrderRequest");
|
|
51
|
+
const QuoteLineItemSchema = z.object({
|
|
52
|
+
entityId: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
|
|
53
|
+
entityType: z.string().optional().openapi({ example: "product" }),
|
|
54
|
+
variantId: z.uuid().optional(),
|
|
55
|
+
quantity: z.number().int().positive().openapi({ example: 2 }),
|
|
56
|
+
title: z.string().optional(),
|
|
57
|
+
});
|
|
58
|
+
const QuoteAddressSchema = z.object({
|
|
59
|
+
line1: z.string(),
|
|
60
|
+
line2: z.string().optional(),
|
|
61
|
+
city: z.string(),
|
|
62
|
+
state: z.string().optional(),
|
|
63
|
+
postalCode: z.string(),
|
|
64
|
+
country: z.string(),
|
|
65
|
+
});
|
|
66
|
+
export const QuoteOrderBodySchema = z.object({
|
|
67
|
+
currency: z.string().min(3).max(3).openapi({ example: "USD" }),
|
|
68
|
+
lineItems: z.array(QuoteLineItemSchema).min(1),
|
|
69
|
+
customerId: z.uuid().optional(),
|
|
70
|
+
customerGroupIds: z.array(z.string()).optional(),
|
|
71
|
+
promotionCodes: z.array(z.string()).optional(),
|
|
72
|
+
shippingAddress: QuoteAddressSchema.optional(),
|
|
73
|
+
}).openapi("QuoteOrderRequest");
|
|
74
|
+
export const QuoteOrderResultSchema = z.object({
|
|
75
|
+
data: z.object({
|
|
76
|
+
currency: z.string(),
|
|
77
|
+
subtotal: z.number().openapi({ description: "Minor units (e.g. cents)." }),
|
|
78
|
+
discountTotal: z.number(),
|
|
79
|
+
shippingTotal: z.number(),
|
|
80
|
+
taxTotal: z.number(),
|
|
81
|
+
grandTotal: z.number(),
|
|
82
|
+
lineItems: z.array(z.object({
|
|
83
|
+
entityId: z.string(),
|
|
84
|
+
variantId: z.string().optional(),
|
|
85
|
+
quantity: z.number(),
|
|
86
|
+
unitPrice: z.number(),
|
|
87
|
+
totalPrice: z.number(),
|
|
88
|
+
discountAmount: z.number(),
|
|
89
|
+
taxAmount: z.number(),
|
|
90
|
+
})),
|
|
91
|
+
}),
|
|
92
|
+
}).openapi("QuoteOrderResult");
|
|
93
|
+
export const quoteOrderRoute = createRoute({
|
|
94
|
+
method: "post",
|
|
95
|
+
path: "/quote",
|
|
96
|
+
tags: ["Orders"],
|
|
97
|
+
summary: "Quote an order's charges without creating it",
|
|
98
|
+
description: "Computes subtotal, discount, shipping, tax and grand total for the given line items (+ optional address, customer, promotion codes) using the SAME pricing pipeline checkout runs — so a manual/draft order previews exactly what it will be charged. Persists nothing.",
|
|
99
|
+
request: {
|
|
100
|
+
body: {
|
|
101
|
+
content: { "application/json": { schema: QuoteOrderBodySchema } },
|
|
102
|
+
required: true,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
responses: {
|
|
106
|
+
200: {
|
|
107
|
+
content: { "application/json": { schema: QuoteOrderResultSchema } },
|
|
108
|
+
description: "Computed order charges (authoritative — matches checkout).",
|
|
109
|
+
},
|
|
110
|
+
...errorResponses,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
51
113
|
// ─── Response Schemas ───────────────────────────────────────────────────────
|
|
52
114
|
export const OrderDataResponseSchema = OrderResponse;
|
|
53
115
|
// ─── Path Params ────────────────────────────────────────────────────────────
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Kernel } from "../../runtime/kernel.js";
|
|
2
|
+
import type { Actor } from "../../auth/types.js";
|
|
3
|
+
import type { ShippingAddress } from "../shipping/calculator.js";
|
|
4
|
+
export interface OrderPricingInput {
|
|
5
|
+
currency: string;
|
|
6
|
+
lineItems: Array<{
|
|
7
|
+
entityId: string;
|
|
8
|
+
entityType?: string;
|
|
9
|
+
variantId?: string;
|
|
10
|
+
quantity: number;
|
|
11
|
+
title?: string;
|
|
12
|
+
}>;
|
|
13
|
+
customerId?: string;
|
|
14
|
+
customerGroupIds?: string[];
|
|
15
|
+
promotionCodes?: string[];
|
|
16
|
+
shippingAddress?: ShippingAddress;
|
|
17
|
+
}
|
|
18
|
+
export interface OrderPricingBreakdown {
|
|
19
|
+
currency: string;
|
|
20
|
+
subtotal: number;
|
|
21
|
+
discountTotal: number;
|
|
22
|
+
shippingTotal: number;
|
|
23
|
+
taxTotal: number;
|
|
24
|
+
grandTotal: number;
|
|
25
|
+
lineItems: Array<{
|
|
26
|
+
entityId: string;
|
|
27
|
+
variantId?: string;
|
|
28
|
+
quantity: number;
|
|
29
|
+
unitPrice: number;
|
|
30
|
+
totalPrice: number;
|
|
31
|
+
discountAmount: number;
|
|
32
|
+
taxAmount: number;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The single pricing authority for an order. Runs the SAME hooks checkout runs —
|
|
37
|
+
* `resolveCurrentPrices → applyPromotionCodes → calculateShipping → calculateTax`
|
|
38
|
+
* (shipping before tax so `appliesToShipping` rates fire) — against a synthetic
|
|
39
|
+
* `CheckoutData`, with NO cart claim, inventory check, or payment side effects.
|
|
40
|
+
*
|
|
41
|
+
* Because it reuses the checkout hooks verbatim, a manual-order quote produced
|
|
42
|
+
* here equals what checkout will actually charge for the same inputs — there is
|
|
43
|
+
* no second pricing implementation to drift. Used by `POST /api/orders/quote`
|
|
44
|
+
* and available to any manual/draft-order flow.
|
|
45
|
+
*/
|
|
46
|
+
export declare function computeOrderPricing(kernel: Kernel, input: OrderPricingInput, actor: Actor | null, tx?: unknown): Promise<OrderPricingBreakdown>;
|
|
47
|
+
//# sourceMappingURL=quote.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/quote.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAGjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAGjE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,KAAK,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;CACJ;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,iBAAiB,EACxB,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,EAAE,CAAC,EAAE,OAAO,GACX,OAAO,CAAC,qBAAqB,CAAC,CAiEhC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createHookContext } from "../../kernel/hooks/create-context.js";
|
|
2
|
+
import { runBeforeHooks } from "../../kernel/hooks/executor.js";
|
|
3
|
+
import { resolveCurrentPrices, applyPromotionCodes, calculateShipping, calculateTax, } from "../../hooks/checkout.js";
|
|
4
|
+
import { makeId } from "../../utils/id.js";
|
|
5
|
+
/**
|
|
6
|
+
* The single pricing authority for an order. Runs the SAME hooks checkout runs —
|
|
7
|
+
* `resolveCurrentPrices → applyPromotionCodes → calculateShipping → calculateTax`
|
|
8
|
+
* (shipping before tax so `appliesToShipping` rates fire) — against a synthetic
|
|
9
|
+
* `CheckoutData`, with NO cart claim, inventory check, or payment side effects.
|
|
10
|
+
*
|
|
11
|
+
* Because it reuses the checkout hooks verbatim, a manual-order quote produced
|
|
12
|
+
* here equals what checkout will actually charge for the same inputs — there is
|
|
13
|
+
* no second pricing implementation to drift. Used by `POST /api/orders/quote`
|
|
14
|
+
* and available to any manual/draft-order flow.
|
|
15
|
+
*/
|
|
16
|
+
export async function computeOrderPricing(kernel, input, actor, tx) {
|
|
17
|
+
const data = {
|
|
18
|
+
checkoutId: makeId(),
|
|
19
|
+
cartId: "",
|
|
20
|
+
currency: input.currency,
|
|
21
|
+
paymentMethodId: "",
|
|
22
|
+
lineItems: input.lineItems.map((li) => ({
|
|
23
|
+
id: `${li.entityId}:${li.variantId ?? "_"}`,
|
|
24
|
+
entityId: li.entityId,
|
|
25
|
+
...(li.entityType !== undefined ? { entityType: li.entityType } : {}),
|
|
26
|
+
...(li.variantId !== undefined ? { variantId: li.variantId } : {}),
|
|
27
|
+
title: li.title ?? li.entityId,
|
|
28
|
+
quantity: li.quantity,
|
|
29
|
+
})),
|
|
30
|
+
subtotal: 0,
|
|
31
|
+
discountTotal: 0,
|
|
32
|
+
taxTotal: 0,
|
|
33
|
+
shippingTotal: 0,
|
|
34
|
+
total: 0,
|
|
35
|
+
...(input.customerId !== undefined ? { customerId: input.customerId } : {}),
|
|
36
|
+
...(input.customerGroupIds !== undefined
|
|
37
|
+
? { customerGroupIds: input.customerGroupIds }
|
|
38
|
+
: {}),
|
|
39
|
+
...(input.promotionCodes !== undefined
|
|
40
|
+
? { promotionCodes: input.promotionCodes }
|
|
41
|
+
: {}),
|
|
42
|
+
...(input.shippingAddress !== undefined
|
|
43
|
+
? { shippingAddress: input.shippingAddress }
|
|
44
|
+
: {}),
|
|
45
|
+
};
|
|
46
|
+
const context = createHookContext({
|
|
47
|
+
actor,
|
|
48
|
+
logger: kernel.logger,
|
|
49
|
+
services: kernel.services,
|
|
50
|
+
context: { moduleName: "orders" },
|
|
51
|
+
origin: "rest",
|
|
52
|
+
database: { db: kernel.database.db },
|
|
53
|
+
});
|
|
54
|
+
if (tx !== undefined)
|
|
55
|
+
context.tx = tx;
|
|
56
|
+
const priced = await runBeforeHooks([resolveCurrentPrices, applyPromotionCodes, calculateShipping, calculateTax], data, "create", context);
|
|
57
|
+
return {
|
|
58
|
+
currency: priced.currency,
|
|
59
|
+
subtotal: priced.subtotal,
|
|
60
|
+
discountTotal: priced.discountTotal,
|
|
61
|
+
shippingTotal: priced.shippingTotal,
|
|
62
|
+
taxTotal: priced.taxTotal,
|
|
63
|
+
grandTotal: priced.total,
|
|
64
|
+
lineItems: priced.lineItems.map((li) => ({
|
|
65
|
+
entityId: li.entityId,
|
|
66
|
+
...(li.variantId !== undefined ? { variantId: li.variantId } : {}),
|
|
67
|
+
quantity: li.quantity,
|
|
68
|
+
unitPrice: li.resolvedUnitPrice ?? 0,
|
|
69
|
+
totalPrice: li.resolvedTotal ?? 0,
|
|
70
|
+
discountAmount: li.discountAmount ?? 0,
|
|
71
|
+
taxAmount: li.taxAmount ?? 0,
|
|
72
|
+
})),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/service.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -850,11 +850,19 @@ export class OrderService {
|
|
|
850
850
|
return Err(new CommerceValidationError("No payment adapter configured for capture."));
|
|
851
851
|
}
|
|
852
852
|
const paymentMethodId = order.paymentMethodId;
|
|
853
|
-
|
|
853
|
+
// Capture the full authorized total when no partial amount is given, and
|
|
854
|
+
// pass it explicitly: Stripe captures the full authorized amount for an
|
|
855
|
+
// omitted value, but not every adapter does — an ambiguous "no amount"
|
|
856
|
+
// capture must never silently record 0.
|
|
857
|
+
const amountToCapture = opts?.amount ?? order.grandTotal;
|
|
858
|
+
const captureResult = await payments.capture(paymentIntentId, amountToCapture, paymentMethodId);
|
|
854
859
|
if (!captureResult.ok) {
|
|
855
860
|
return Err(toCommerceError(captureResult.error));
|
|
856
861
|
}
|
|
857
|
-
|
|
862
|
+
// Trust the adapter's reported figure only when it sends a positive one;
|
|
863
|
+
// otherwise record the amount we asked to capture (never a swallowed 0).
|
|
864
|
+
const reported = captureResult.value?.amountCaptured;
|
|
865
|
+
const amountCaptured = reported && reported > 0 ? reported : amountToCapture;
|
|
858
866
|
await this.repo.update(orderId, { amountCaptured }, ctx);
|
|
859
867
|
const refreshed = await this.repo.findById(orgId, orderId, ctx);
|
|
860
868
|
const hydrated = await this.hydrateOrder(refreshed ?? order, ctx);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/tax/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAErE,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACd,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEnF,UAAU,cAAc;IACtB,OAAO,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,UAAU,CAAiC;gBAEvC,IAAI,EAAE,cAAc;IAKhC;;;;;;;;;;;OAWG;IACG,SAAS,CACb,MAAM,EAAE,oBAAoB,EAC5B,KAAK,CAAC,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/tax/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAErE,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACd,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEnF,UAAU,cAAc;IACtB,OAAO,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,UAAU,CAAiC;gBAEvC,IAAI,EAAE,cAAc;IAKhC;;;;;;;;;;;OAWG;IACG,SAAS,CACb,MAAM,EAAE,oBAAoB,EAC5B,KAAK,CAAC,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAuDxC,0EAA0E;YAC5D,kBAAkB;IAoE1B,cAAc,CAClB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;KAAE,EACzG,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAqBtB,cAAc,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAOlF,cAAc,CAClB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;KAAE,EACnI,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAoBtB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;YAU7F,iBAAiB;IAoBzB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAKtF,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAKxF,iBAAiB,IAAI,MAAM,CAAC,UAAU,CAAC;IASvC,OAAO,CAAC,iBAAiB;IAOnB,aAAa,CACjB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACxC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAChC,EACD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAuBrB,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAO/E,aAAa,CACjB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;QAClC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACxC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAChC,EACD,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EACpB,GAAG,CAAC,EAAE,SAAS,GACd,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAwBrB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;CAS3G"}
|
|
@@ -30,7 +30,10 @@ export class TaxService {
|
|
|
30
30
|
if (this.repository && orgId && params.toAddress) {
|
|
31
31
|
const matched = await this.matchRuntimeRates(orgId, params.toAddress, ctx);
|
|
32
32
|
if (matched.length > 0) {
|
|
33
|
-
|
|
33
|
+
// Subtract the order-level discount from the taxable base too (audit
|
|
34
|
+
// C2a) — otherwise runtime rates tax the pre-discount subtotal and
|
|
35
|
+
// over-collect on every discounted order, unlike the class-based path.
|
|
36
|
+
const taxableAmount = Math.max(0, params.lineItems.reduce((sum, lineItem) => sum + lineItem.unitPrice * lineItem.quantity - (lineItem.discount ?? 0), 0) - (params.orderDiscount ?? 0));
|
|
34
37
|
let amountToCollect = 0;
|
|
35
38
|
const breakdown = {};
|
|
36
39
|
for (const rate of matched) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@porulle/core",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"eslint": "^9.39.1",
|
|
57
57
|
"typescript": "5.9.2",
|
|
58
58
|
"vitest": "^3.2.4",
|
|
59
|
-
"@porulle/
|
|
60
|
-
"@porulle/
|
|
59
|
+
"@porulle/typescript-config": "0.1.0",
|
|
60
|
+
"@porulle/eslint-config": "0.1.0"
|
|
61
61
|
},
|
|
62
62
|
"publishConfig": {
|
|
63
63
|
"access": "public"
|
|
@@ -155,7 +155,10 @@ export const capturePaymentStep: Step<
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
const payments = ctx.hook.services.payments as PaymentsServiceLike;
|
|
158
|
-
|
|
158
|
+
// Capture the full checkout total. Pass it explicitly — an adapter that
|
|
159
|
+
// returns 0 for an omitted amount (dev mock, some custom adapters) would
|
|
160
|
+
// otherwise cause a $0 capture to be recorded on every order.
|
|
161
|
+
const result = await payments.capture(data.paymentIntentId, data.total);
|
|
159
162
|
|
|
160
163
|
if (!result.ok) {
|
|
161
164
|
return Err(
|
|
@@ -164,23 +167,24 @@ export const capturePaymentStep: Step<
|
|
|
164
167
|
),
|
|
165
168
|
);
|
|
166
169
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
170
|
+
const orders = ctx.hook.services.orders as {
|
|
171
|
+
updateOrder?(
|
|
172
|
+
orderId: string,
|
|
173
|
+
data: { amountCaptured?: number },
|
|
174
|
+
actor?: unknown,
|
|
175
|
+
): Promise<{ ok: boolean }>;
|
|
176
|
+
};
|
|
177
|
+
if (orders.updateOrder) {
|
|
178
|
+
// Record the adapter's positive figure, else the total we captured.
|
|
179
|
+
const reported = result.value?.amountCaptured;
|
|
180
|
+
const amountCaptured = reported && reported > 0 ? reported : data.total;
|
|
181
|
+
const updated = await orders.updateOrder(
|
|
182
|
+
data.checkoutId,
|
|
183
|
+
{ amountCaptured },
|
|
184
|
+
ctx.hook.actor,
|
|
185
|
+
);
|
|
186
|
+
if (!updated.ok) {
|
|
187
|
+
return Err(new CommerceValidationError("Failed to persist captured payment amount."));
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
|
|
@@ -164,8 +164,11 @@ export function checkoutRoutes(kernel: Kernel) {
|
|
|
164
164
|
resolveCurrentPrices,
|
|
165
165
|
checkInventoryAvailability,
|
|
166
166
|
applyPromotionCodes,
|
|
167
|
-
calculateTax
|
|
167
|
+
// Shipping BEFORE tax: calculateTax reads data.shippingTotal for
|
|
168
|
+
// appliesToShipping rates, so shipping must be computed first or shipping
|
|
169
|
+
// tax is silently never collected (audit C1).
|
|
168
170
|
calculateShipping,
|
|
171
|
+
calculateTax,
|
|
169
172
|
...(kernel.hooks.resolve("checkout.beforePayment") as BeforeHook<CheckoutData>[]),
|
|
170
173
|
validatePaymentMethod,
|
|
171
174
|
];
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
2
2
|
import type { Kernel } from "../../../runtime/kernel.js";
|
|
3
|
-
import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
|
|
3
|
+
import { changeOrderStatusRoute, listOrdersRoute, orderLookupRoute, getOrderRoute, getOrderFulfillmentsRoute, createOrderRoute, quoteOrderRoute, refundOrderRoute, captureOrderRoute, createOrderFulfillmentRoute, addOrderLineItemRoute, updateOrderLineItemRoute, removeOrderLineItemRoute, refundOrderLinesRoute, undoOrderRefundRoute, listOrderRefundsRoute, refundCapStatusRoute, createOrderNoteRoute, listOrderNotesRoute, deleteOrderNoteRoute, orderTimelineRoute } from "../schemas/orders.js";
|
|
4
4
|
import { type AppEnv, isUUID, mapErrorToResponse, mapErrorToStatus, parsePagination } from "../utils.js";
|
|
5
5
|
import type { CreateOrderInput } from "../../../modules/orders/service.js";
|
|
6
|
+
import { computeOrderPricing } from "../../../modules/orders/quote.js";
|
|
6
7
|
import { assertPermission } from "../../../auth/permissions.js";
|
|
7
8
|
|
|
8
9
|
export function orderRoutes(kernel: Kernel) {
|
|
@@ -74,6 +75,40 @@ export function orderRoutes(kernel: Kernel) {
|
|
|
74
75
|
return c.json({ data: result.value }, 201);
|
|
75
76
|
});
|
|
76
77
|
|
|
78
|
+
// @ts-expect-error -- openapi handler union return type
|
|
79
|
+
router.openapi(quoteOrderRoute, async (c) => {
|
|
80
|
+
try {
|
|
81
|
+
assertPermission(c.get("actor"), "orders:manage");
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
|
|
84
|
+
}
|
|
85
|
+
const body = c.req.valid("json") as {
|
|
86
|
+
currency: string;
|
|
87
|
+
lineItems: Array<{ entityId: string; entityType?: string; variantId?: string; quantity: number; title?: string }>;
|
|
88
|
+
customerId?: string;
|
|
89
|
+
customerGroupIds?: string[];
|
|
90
|
+
promotionCodes?: string[];
|
|
91
|
+
shippingAddress?: import("../../../modules/shipping/calculator.js").ShippingAddress;
|
|
92
|
+
};
|
|
93
|
+
try {
|
|
94
|
+
const breakdown = await computeOrderPricing(
|
|
95
|
+
kernel,
|
|
96
|
+
{
|
|
97
|
+
currency: body.currency,
|
|
98
|
+
lineItems: body.lineItems,
|
|
99
|
+
...(body.customerId !== undefined ? { customerId: body.customerId } : {}),
|
|
100
|
+
...(body.customerGroupIds !== undefined ? { customerGroupIds: body.customerGroupIds } : {}),
|
|
101
|
+
...(body.promotionCodes !== undefined ? { promotionCodes: body.promotionCodes } : {}),
|
|
102
|
+
...(body.shippingAddress !== undefined ? { shippingAddress: body.shippingAddress } : {}),
|
|
103
|
+
},
|
|
104
|
+
c.get("actor"),
|
|
105
|
+
);
|
|
106
|
+
return c.json({ data: breakdown });
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return c.json(mapErrorToResponse(error), mapErrorToStatus(error));
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
77
112
|
// @ts-expect-error -- openapi handler union return type
|
|
78
113
|
router.openapi(refundOrderRoute, async (c) => {
|
|
79
114
|
const body = c.req.valid("json") as { amount?: number; reason?: string } | undefined;
|
|
@@ -55,6 +55,74 @@ export const CreateOrderBodySchema = z.object({
|
|
|
55
55
|
lineItems: z.array(CreateOrderLineItemSchema).min(1),
|
|
56
56
|
}).openapi("CreateOrderRequest");
|
|
57
57
|
|
|
58
|
+
const QuoteLineItemSchema = z.object({
|
|
59
|
+
entityId: z.uuid().openapi({ example: "550e8400-e29b-41d4-a716-446655440000" }),
|
|
60
|
+
entityType: z.string().optional().openapi({ example: "product" }),
|
|
61
|
+
variantId: z.uuid().optional(),
|
|
62
|
+
quantity: z.number().int().positive().openapi({ example: 2 }),
|
|
63
|
+
title: z.string().optional(),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const QuoteAddressSchema = z.object({
|
|
67
|
+
line1: z.string(),
|
|
68
|
+
line2: z.string().optional(),
|
|
69
|
+
city: z.string(),
|
|
70
|
+
state: z.string().optional(),
|
|
71
|
+
postalCode: z.string(),
|
|
72
|
+
country: z.string(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export const QuoteOrderBodySchema = z.object({
|
|
76
|
+
currency: z.string().min(3).max(3).openapi({ example: "USD" }),
|
|
77
|
+
lineItems: z.array(QuoteLineItemSchema).min(1),
|
|
78
|
+
customerId: z.uuid().optional(),
|
|
79
|
+
customerGroupIds: z.array(z.string()).optional(),
|
|
80
|
+
promotionCodes: z.array(z.string()).optional(),
|
|
81
|
+
shippingAddress: QuoteAddressSchema.optional(),
|
|
82
|
+
}).openapi("QuoteOrderRequest");
|
|
83
|
+
|
|
84
|
+
export const QuoteOrderResultSchema = z.object({
|
|
85
|
+
data: z.object({
|
|
86
|
+
currency: z.string(),
|
|
87
|
+
subtotal: z.number().openapi({ description: "Minor units (e.g. cents)." }),
|
|
88
|
+
discountTotal: z.number(),
|
|
89
|
+
shippingTotal: z.number(),
|
|
90
|
+
taxTotal: z.number(),
|
|
91
|
+
grandTotal: z.number(),
|
|
92
|
+
lineItems: z.array(z.object({
|
|
93
|
+
entityId: z.string(),
|
|
94
|
+
variantId: z.string().optional(),
|
|
95
|
+
quantity: z.number(),
|
|
96
|
+
unitPrice: z.number(),
|
|
97
|
+
totalPrice: z.number(),
|
|
98
|
+
discountAmount: z.number(),
|
|
99
|
+
taxAmount: z.number(),
|
|
100
|
+
})),
|
|
101
|
+
}),
|
|
102
|
+
}).openapi("QuoteOrderResult");
|
|
103
|
+
|
|
104
|
+
export const quoteOrderRoute = createRoute({
|
|
105
|
+
method: "post",
|
|
106
|
+
path: "/quote",
|
|
107
|
+
tags: ["Orders"],
|
|
108
|
+
summary: "Quote an order's charges without creating it",
|
|
109
|
+
description:
|
|
110
|
+
"Computes subtotal, discount, shipping, tax and grand total for the given line items (+ optional address, customer, promotion codes) using the SAME pricing pipeline checkout runs — so a manual/draft order previews exactly what it will be charged. Persists nothing.",
|
|
111
|
+
request: {
|
|
112
|
+
body: {
|
|
113
|
+
content: { "application/json": { schema: QuoteOrderBodySchema } },
|
|
114
|
+
required: true,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
responses: {
|
|
118
|
+
200: {
|
|
119
|
+
content: { "application/json": { schema: QuoteOrderResultSchema } },
|
|
120
|
+
description: "Computed order charges (authoritative — matches checkout).",
|
|
121
|
+
},
|
|
122
|
+
...errorResponses,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
58
126
|
// ─── Response Schemas ───────────────────────────────────────────────────────
|
|
59
127
|
|
|
60
128
|
export const OrderDataResponseSchema = OrderResponse;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { createHookContext } from "../../kernel/hooks/create-context.js";
|
|
2
|
+
import { runBeforeHooks } from "../../kernel/hooks/executor.js";
|
|
3
|
+
import {
|
|
4
|
+
resolveCurrentPrices,
|
|
5
|
+
applyPromotionCodes,
|
|
6
|
+
calculateShipping,
|
|
7
|
+
calculateTax,
|
|
8
|
+
type CheckoutData,
|
|
9
|
+
} from "../../hooks/checkout.js";
|
|
10
|
+
import type { Kernel } from "../../runtime/kernel.js";
|
|
11
|
+
import type { Actor } from "../../auth/types.js";
|
|
12
|
+
import type { ServiceContainer } from "../../kernel/hooks/types.js";
|
|
13
|
+
import type { PluginDb } from "../../kernel/database/plugin-types.js";
|
|
14
|
+
import type { ShippingAddress } from "../shipping/calculator.js";
|
|
15
|
+
import { makeId } from "../../utils/id.js";
|
|
16
|
+
|
|
17
|
+
export interface OrderPricingInput {
|
|
18
|
+
currency: string;
|
|
19
|
+
lineItems: Array<{
|
|
20
|
+
entityId: string;
|
|
21
|
+
entityType?: string;
|
|
22
|
+
variantId?: string;
|
|
23
|
+
quantity: number;
|
|
24
|
+
title?: string;
|
|
25
|
+
}>;
|
|
26
|
+
customerId?: string;
|
|
27
|
+
customerGroupIds?: string[];
|
|
28
|
+
promotionCodes?: string[];
|
|
29
|
+
shippingAddress?: ShippingAddress;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface OrderPricingBreakdown {
|
|
33
|
+
currency: string;
|
|
34
|
+
subtotal: number;
|
|
35
|
+
discountTotal: number;
|
|
36
|
+
shippingTotal: number;
|
|
37
|
+
taxTotal: number;
|
|
38
|
+
grandTotal: number;
|
|
39
|
+
lineItems: Array<{
|
|
40
|
+
entityId: string;
|
|
41
|
+
variantId?: string;
|
|
42
|
+
quantity: number;
|
|
43
|
+
unitPrice: number;
|
|
44
|
+
totalPrice: number;
|
|
45
|
+
discountAmount: number;
|
|
46
|
+
taxAmount: number;
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The single pricing authority for an order. Runs the SAME hooks checkout runs —
|
|
52
|
+
* `resolveCurrentPrices → applyPromotionCodes → calculateShipping → calculateTax`
|
|
53
|
+
* (shipping before tax so `appliesToShipping` rates fire) — against a synthetic
|
|
54
|
+
* `CheckoutData`, with NO cart claim, inventory check, or payment side effects.
|
|
55
|
+
*
|
|
56
|
+
* Because it reuses the checkout hooks verbatim, a manual-order quote produced
|
|
57
|
+
* here equals what checkout will actually charge for the same inputs — there is
|
|
58
|
+
* no second pricing implementation to drift. Used by `POST /api/orders/quote`
|
|
59
|
+
* and available to any manual/draft-order flow.
|
|
60
|
+
*/
|
|
61
|
+
export async function computeOrderPricing(
|
|
62
|
+
kernel: Kernel,
|
|
63
|
+
input: OrderPricingInput,
|
|
64
|
+
actor: Actor | null,
|
|
65
|
+
tx?: unknown,
|
|
66
|
+
): Promise<OrderPricingBreakdown> {
|
|
67
|
+
const data: CheckoutData = {
|
|
68
|
+
checkoutId: makeId(),
|
|
69
|
+
cartId: "",
|
|
70
|
+
currency: input.currency,
|
|
71
|
+
paymentMethodId: "",
|
|
72
|
+
lineItems: input.lineItems.map((li) => ({
|
|
73
|
+
id: `${li.entityId}:${li.variantId ?? "_"}`,
|
|
74
|
+
entityId: li.entityId,
|
|
75
|
+
...(li.entityType !== undefined ? { entityType: li.entityType } : {}),
|
|
76
|
+
...(li.variantId !== undefined ? { variantId: li.variantId } : {}),
|
|
77
|
+
title: li.title ?? li.entityId,
|
|
78
|
+
quantity: li.quantity,
|
|
79
|
+
})),
|
|
80
|
+
subtotal: 0,
|
|
81
|
+
discountTotal: 0,
|
|
82
|
+
taxTotal: 0,
|
|
83
|
+
shippingTotal: 0,
|
|
84
|
+
total: 0,
|
|
85
|
+
...(input.customerId !== undefined ? { customerId: input.customerId } : {}),
|
|
86
|
+
...(input.customerGroupIds !== undefined
|
|
87
|
+
? { customerGroupIds: input.customerGroupIds }
|
|
88
|
+
: {}),
|
|
89
|
+
...(input.promotionCodes !== undefined
|
|
90
|
+
? { promotionCodes: input.promotionCodes }
|
|
91
|
+
: {}),
|
|
92
|
+
...(input.shippingAddress !== undefined
|
|
93
|
+
? { shippingAddress: input.shippingAddress }
|
|
94
|
+
: {}),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const context = createHookContext({
|
|
98
|
+
actor,
|
|
99
|
+
logger: kernel.logger,
|
|
100
|
+
services: kernel.services as ServiceContainer,
|
|
101
|
+
context: { moduleName: "orders" },
|
|
102
|
+
origin: "rest",
|
|
103
|
+
database: { db: kernel.database.db as PluginDb },
|
|
104
|
+
});
|
|
105
|
+
if (tx !== undefined) context.tx = tx as typeof context.tx;
|
|
106
|
+
|
|
107
|
+
const priced = await runBeforeHooks(
|
|
108
|
+
[resolveCurrentPrices, applyPromotionCodes, calculateShipping, calculateTax],
|
|
109
|
+
data,
|
|
110
|
+
"create",
|
|
111
|
+
context,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
currency: priced.currency,
|
|
116
|
+
subtotal: priced.subtotal,
|
|
117
|
+
discountTotal: priced.discountTotal,
|
|
118
|
+
shippingTotal: priced.shippingTotal,
|
|
119
|
+
taxTotal: priced.taxTotal,
|
|
120
|
+
grandTotal: priced.total,
|
|
121
|
+
lineItems: priced.lineItems.map((li) => ({
|
|
122
|
+
entityId: li.entityId,
|
|
123
|
+
...(li.variantId !== undefined ? { variantId: li.variantId } : {}),
|
|
124
|
+
quantity: li.quantity,
|
|
125
|
+
unitPrice: li.resolvedUnitPrice ?? 0,
|
|
126
|
+
totalPrice: li.resolvedTotal ?? 0,
|
|
127
|
+
discountAmount: li.discountAmount ?? 0,
|
|
128
|
+
taxAmount: li.taxAmount ?? 0,
|
|
129
|
+
})),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -1482,17 +1482,24 @@ export class OrderService {
|
|
|
1482
1482
|
const paymentMethodId = (order as Record<string, unknown>).paymentMethodId as
|
|
1483
1483
|
| string
|
|
1484
1484
|
| undefined;
|
|
1485
|
+
// Capture the full authorized total when no partial amount is given, and
|
|
1486
|
+
// pass it explicitly: Stripe captures the full authorized amount for an
|
|
1487
|
+
// omitted value, but not every adapter does — an ambiguous "no amount"
|
|
1488
|
+
// capture must never silently record 0.
|
|
1489
|
+
const amountToCapture = opts?.amount ?? order.grandTotal;
|
|
1485
1490
|
const captureResult = await payments.capture(
|
|
1486
1491
|
paymentIntentId,
|
|
1487
|
-
|
|
1492
|
+
amountToCapture,
|
|
1488
1493
|
paymentMethodId,
|
|
1489
1494
|
);
|
|
1490
1495
|
if (!captureResult.ok) {
|
|
1491
1496
|
return Err(toCommerceError(captureResult.error));
|
|
1492
1497
|
}
|
|
1493
1498
|
|
|
1494
|
-
|
|
1495
|
-
|
|
1499
|
+
// Trust the adapter's reported figure only when it sends a positive one;
|
|
1500
|
+
// otherwise record the amount we asked to capture (never a swallowed 0).
|
|
1501
|
+
const reported = captureResult.value?.amountCaptured;
|
|
1502
|
+
const amountCaptured = reported && reported > 0 ? reported : amountToCapture;
|
|
1496
1503
|
await this.repo.update(orderId, { amountCaptured }, ctx);
|
|
1497
1504
|
|
|
1498
1505
|
const refreshed = await this.repo.findById(orgId, orderId, ctx);
|
|
@@ -53,10 +53,16 @@ export class TaxService {
|
|
|
53
53
|
if (this.repository && orgId && params.toAddress) {
|
|
54
54
|
const matched = await this.matchRuntimeRates(orgId, params.toAddress, ctx);
|
|
55
55
|
if (matched.length > 0) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
// Subtract the order-level discount from the taxable base too (audit
|
|
57
|
+
// C2a) — otherwise runtime rates tax the pre-discount subtotal and
|
|
58
|
+
// over-collect on every discounted order, unlike the class-based path.
|
|
59
|
+
const taxableAmount = Math.max(
|
|
59
60
|
0,
|
|
61
|
+
params.lineItems.reduce(
|
|
62
|
+
(sum, lineItem) =>
|
|
63
|
+
sum + lineItem.unitPrice * lineItem.quantity - (lineItem.discount ?? 0),
|
|
64
|
+
0,
|
|
65
|
+
) - (params.orderDiscount ?? 0),
|
|
60
66
|
);
|
|
61
67
|
let amountToCollect = 0;
|
|
62
68
|
const breakdown: Record<string, unknown> = {};
|