@wocart/storefront 1.10.0 → 1.11.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/CHANGELOG.md +25 -0
- package/USAGE.md +38 -1
- package/dist/actions.d.ts +4 -1
- package/dist/actions.js +5 -0
- package/dist/client/cart.d.ts +16 -7
- package/dist/client/cart.js +8 -3
- package/dist/coupon.d.ts +18 -0
- package/dist/coupon.js +19 -0
- package/dist/domain.d.ts +3 -0
- package/dist/domain.js +1 -0
- package/dist/endpoints/storefront-routes.d.ts +4 -3
- package/dist/endpoints/storefront-routes.js +15 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/schemas.d.ts +33 -0
- package/dist/schemas.js +17 -1
- package/dist/types.d.ts +33 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ follows [semantic versioning](https://semver.org/): breaking changes ship only
|
|
|
5
5
|
in a new major version, and an export is marked `@deprecated` for at least one
|
|
6
6
|
minor release before a major removes it.
|
|
7
7
|
|
|
8
|
+
## 1.11.0 — 2026-10-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Coupons.** `cart.quote` / `cart.validate` take `couponCode` and `phone`
|
|
13
|
+
(`CartQuoteOptions`) and answer `coupon` (`CouponOutcome`,
|
|
14
|
+
`CouponOutcomeSchema`): applied — its `discount` already out of `subtotal`
|
|
15
|
+
and of each line's `lineTotal`, `CartValidationLine.discount` being the
|
|
16
|
+
line's share, `freeShipping` making the delivery fee 0 — or refused with a
|
|
17
|
+
`reason` (`CouponRefusalReason`, open), `shortBy` or `startsAt` when it has
|
|
18
|
+
one. Null without a code; absent from an older server.
|
|
19
|
+
- `CreateOrderInput.couponCode` and `placeOrderInput.couponCode` (an empty
|
|
20
|
+
box sends none); `CreatedOrder.couponDiscount`. An order whose code does not
|
|
21
|
+
apply is refused whole with `COUPON_NOT_APPLICABLE`: `couponRefusalOf(result)`
|
|
22
|
+
reads the refusal, and `executePlaceOrder`'s upstream failure carries it as
|
|
23
|
+
`couponRefusal`.
|
|
24
|
+
- Shareable coupon links: `COUPON_URL_PARAM` (`?coupon=CODE`) and
|
|
25
|
+
`couponFromUrl(url)`.
|
|
26
|
+
- `CheckoutDraft.couponCode`.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- `CartValidationLine.offerId` names the offer applied to the line, whatever
|
|
31
|
+
its kind — a coupon never covers that line.
|
|
32
|
+
|
|
8
33
|
## 1.10.0 — 2026-10-17
|
|
9
34
|
|
|
10
35
|
### Added
|
package/USAGE.md
CHANGED
|
@@ -1059,7 +1059,44 @@ variant details. The displayed price can be included for the request shape,
|
|
|
1059
1059
|
but the server is authoritative and recalculates the actual amount.
|
|
1060
1060
|
|
|
1061
1061
|
CreatedOrder contains the order ID, customer-facing order number, product
|
|
1062
|
-
price, delivery fee, and total, all in whole DZD
|
|
1062
|
+
price, delivery fee, and total, all in whole DZD — and `couponDiscount`, what
|
|
1063
|
+
the order's coupon took off.
|
|
1064
|
+
|
|
1065
|
+
### Coupons
|
|
1066
|
+
|
|
1067
|
+
A coupon is a code the shopper types. It applies after offers, only to the
|
|
1068
|
+
products no offer applied to, and the server alone decides whether it applies
|
|
1069
|
+
and what it takes off. A theme asks, then shows the answer.
|
|
1070
|
+
|
|
1071
|
+
Ask the cart quote with the code — and the phone once the shopper has typed
|
|
1072
|
+
one, so a coupon for selected customers or one use per customer is judged
|
|
1073
|
+
before the order:
|
|
1074
|
+
|
|
1075
|
+
~~~ts
|
|
1076
|
+
const quoted = await store.cart.quote(form, { destination, couponCode, phone });
|
|
1077
|
+
if (quoted.success && quoted.data.coupon) {
|
|
1078
|
+
const coupon = quoted.data.coupon;
|
|
1079
|
+
// applied: coupon.discount is already out of quoted.data.subtotal and of each
|
|
1080
|
+
// line's lineTotal (line.discount is its share); coupon.freeShipping
|
|
1081
|
+
// means delivery.fee is 0.
|
|
1082
|
+
// refused: coupon.reason — not_found, not_started (startsAt), expired,
|
|
1083
|
+
// used_up, identify_first, not_eligible, already_used,
|
|
1084
|
+
// no_eligible_products, below_minimum_subtotal / _quantity (shortBy),
|
|
1085
|
+
// delivery_already_free. An unknown reason gets the theme's general sentence.
|
|
1086
|
+
}
|
|
1087
|
+
~~~
|
|
1088
|
+
|
|
1089
|
+
`CartQuoteOptions` names what a quote may be asked besides its lines.
|
|
1090
|
+
|
|
1091
|
+
Send the code with the order as `couponCode` (`placeOrderInput` keeps it; an
|
|
1092
|
+
empty box sends none). An order whose code does not apply is refused whole —
|
|
1093
|
+
never placed without the discount the shopper expected — with the code
|
|
1094
|
+
`COUPON_NOT_APPLICABLE`; `couponRefusalOf(result)` reads its refusal, and
|
|
1095
|
+
`executePlaceOrder` hands it back as `couponRefusal` so the form can show it on
|
|
1096
|
+
the code's field.
|
|
1097
|
+
|
|
1098
|
+
A shareable link carries a code in the query (`COUPON_URL_PARAM`, `?coupon=RAMADAN10`);
|
|
1099
|
+
`couponFromUrl(url)` reads it so the theme can fill the code box.
|
|
1063
1100
|
|
|
1064
1101
|
### OTP
|
|
1065
1102
|
|
package/dist/actions.d.ts
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
import { z } from "zod";
|
|
37
37
|
import type { StoreClient } from "./client/index.js";
|
|
38
38
|
import type { DomainError } from "./domain.js";
|
|
39
|
-
import type { CreatedOrder } from "./types.js";
|
|
39
|
+
import type { CouponRefusal, CreatedOrder } from "./types.js";
|
|
40
40
|
/**
|
|
41
41
|
* The order form's input whitelist.
|
|
42
42
|
*
|
|
@@ -85,6 +85,7 @@ export declare const placeOrderInput: z.ZodObject<{
|
|
|
85
85
|
offerId: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
|
|
86
86
|
}, z.core.$strip>>>, unknown>;
|
|
87
87
|
landingPageSlug: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
|
|
88
|
+
couponCode: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
|
|
88
89
|
}, z.core.$strip>;
|
|
89
90
|
export type PlaceOrderInput = z.infer<typeof placeOrderInput>;
|
|
90
91
|
/**
|
|
@@ -106,6 +107,8 @@ export type PlaceOrderResult = {
|
|
|
106
107
|
error: string;
|
|
107
108
|
errorKind: "upstream";
|
|
108
109
|
domainError: DomainError;
|
|
110
|
+
/** Set when the order was refused for its code: the form shows it on the code's field. */
|
|
111
|
+
couponRefusal: CouponRefusal | null;
|
|
109
112
|
};
|
|
110
113
|
/**
|
|
111
114
|
* Execute the placeOrder action's handler body: enforce the
|
package/dist/actions.js
CHANGED
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
* without Astro.
|
|
35
35
|
*/
|
|
36
36
|
import { z } from "zod";
|
|
37
|
+
import { couponRefusalOf } from "./coupon.js";
|
|
37
38
|
import { toLocalAlgerianMobile } from "./phone.js";
|
|
38
39
|
function parseOptionalArrayInput(value) {
|
|
39
40
|
if (!value || value === "[]")
|
|
@@ -149,6 +150,9 @@ export const placeOrderInput = z.object({
|
|
|
149
150
|
// Landing page attribution — best-effort: unknown/draft slug leaves
|
|
150
151
|
// the order unattributed, never blocked (platform extension, LP feature).
|
|
151
152
|
landingPageSlug: z.preprocess((v) => (v === "" || v == null ? undefined : v), z.string().min(1).max(60).optional()),
|
|
153
|
+
// The code the shopper typed; an empty box sends none. Whether it applies
|
|
154
|
+
// is the server's to say — a refusal comes back as `couponRefusal`.
|
|
155
|
+
couponCode: z.preprocess((v) => (typeof v === "string" && v.trim() === "") || v == null ? undefined : v, z.string().max(64).optional()),
|
|
152
156
|
});
|
|
153
157
|
/**
|
|
154
158
|
* Collect the shopper's attribution headers so the server records the
|
|
@@ -195,6 +199,7 @@ export async function executePlaceOrder(input, request, client) {
|
|
|
195
199
|
error: result.error,
|
|
196
200
|
errorKind: "upstream",
|
|
197
201
|
domainError: result.domainError,
|
|
202
|
+
couponRefusal: couponRefusalOf(result),
|
|
198
203
|
};
|
|
199
204
|
}
|
|
200
205
|
return { success: true, data: result.data };
|
package/dist/client/cart.d.ts
CHANGED
|
@@ -24,19 +24,28 @@ export interface CartNamespace {
|
|
|
24
24
|
*
|
|
25
25
|
* With a `destination`, the answer also carries `delivery`: the fee and
|
|
26
26
|
* total the order would charge there. A theme shows that, never a fee or
|
|
27
|
-
* total of its own making.
|
|
27
|
+
* total of its own making. With a `couponCode`, it carries `coupon`: applied,
|
|
28
|
+
* or refused with the reason the order would be refused for.
|
|
28
29
|
*/
|
|
29
|
-
validate(items: readonly BasketLineInput[], options?:
|
|
30
|
-
destination?: DeliveryDestination;
|
|
31
|
-
}): Promise<ClientResult<CartValidation>>;
|
|
30
|
+
validate(items: readonly BasketLineInput[], options?: CartQuoteOptions): Promise<ClientResult<CartValidation>>;
|
|
32
31
|
/**
|
|
33
32
|
* Quote what an order would cost, for a basket or for one product exactly as
|
|
34
33
|
* its order form posts it (quantity, variants per unit, offer tier). The lines
|
|
35
34
|
* are built the way the order builds them, so the quote and the order agree.
|
|
36
35
|
*/
|
|
37
|
-
quote(lines: CartQuoteLines, options?:
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
quote(lines: CartQuoteLines, options?: CartQuoteOptions): Promise<ClientResult<CartValidation>>;
|
|
37
|
+
}
|
|
38
|
+
/** What a quote is asked besides its lines. */
|
|
39
|
+
export interface CartQuoteOptions {
|
|
40
|
+
/** Where it would be delivered: the answer then carries `delivery`. */
|
|
41
|
+
destination?: DeliveryDestination;
|
|
42
|
+
/** The code the shopper typed: the answer then carries `coupon`. */
|
|
43
|
+
couponCode?: string;
|
|
44
|
+
/**
|
|
45
|
+
* The phone the shopper typed, once there is one: a coupon for selected
|
|
46
|
+
* customers, or one use per customer, is then judged before the order.
|
|
47
|
+
*/
|
|
48
|
+
phone?: string;
|
|
40
49
|
}
|
|
41
50
|
/**
|
|
42
51
|
* Create cart namespace
|
package/dist/client/cart.js
CHANGED
|
@@ -19,9 +19,14 @@ import { localizedPath } from "./paths.js";
|
|
|
19
19
|
*/
|
|
20
20
|
export function createCartNamespace(core) {
|
|
21
21
|
// A closure rather than `this`: a theme may destructure `store.cart`.
|
|
22
|
-
const quote = async (lines, options) => {
|
|
23
|
-
const destination = options
|
|
24
|
-
return core.post(localizedPath(core.config, "/store/cart/validate"),
|
|
22
|
+
const quote = async (lines, options = {}) => {
|
|
23
|
+
const { destination, couponCode, phone } = options;
|
|
24
|
+
return core.post(localizedPath(core.config, "/store/cart/validate"), {
|
|
25
|
+
...lines,
|
|
26
|
+
...(destination ? { destination } : {}),
|
|
27
|
+
...(couponCode?.trim() ? { couponCode } : {}),
|
|
28
|
+
...(phone?.trim() ? { phone } : {}),
|
|
29
|
+
}, { schema: CartValidationSchema, operation: STOREFRONT_OPERATIONS.cartValidate });
|
|
25
30
|
};
|
|
26
31
|
return {
|
|
27
32
|
validate: (items, options) => quote({ items: [...items] }, options),
|
package/dist/coupon.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coupons on the storefront: the code a shareable link brings, and the
|
|
3
|
+
* refusal an order comes back with.
|
|
4
|
+
*
|
|
5
|
+
* What a code does is the server's to say — the cart quote answers `coupon`
|
|
6
|
+
* (applied, or refused with a reason) and the order is refused whole when its
|
|
7
|
+
* code does not apply. A theme shows those answers; it never decides one.
|
|
8
|
+
*/
|
|
9
|
+
import type { ClientResult } from "./domain.js";
|
|
10
|
+
import type { CouponRefusal } from "./types.js";
|
|
11
|
+
/** The error code of an order refused for its code. */
|
|
12
|
+
export declare const COUPON_NOT_APPLICABLE = "COUPON_NOT_APPLICABLE";
|
|
13
|
+
/** The query parameter a shareable coupon link carries: `/products/robe?coupon=RAMADAN10`. */
|
|
14
|
+
export declare const COUPON_URL_PARAM = "coupon";
|
|
15
|
+
/** The code a URL brings, trimmed; null when it brings none. */
|
|
16
|
+
export declare function couponFromUrl(url: URL): string | null;
|
|
17
|
+
/** Why an order was refused for its code, or null when it was refused — or placed — for anything else. */
|
|
18
|
+
export declare function couponRefusalOf(result: ClientResult<unknown>): CouponRefusal | null;
|
package/dist/coupon.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** The error code of an order refused for its code. */
|
|
2
|
+
export const COUPON_NOT_APPLICABLE = "COUPON_NOT_APPLICABLE";
|
|
3
|
+
/** The query parameter a shareable coupon link carries: `/products/robe?coupon=RAMADAN10`. */
|
|
4
|
+
export const COUPON_URL_PARAM = "coupon";
|
|
5
|
+
/** The code a URL brings, trimmed; null when it brings none. */
|
|
6
|
+
export function couponFromUrl(url) {
|
|
7
|
+
const code = url.searchParams.get(COUPON_URL_PARAM)?.trim();
|
|
8
|
+
return code ? code.slice(0, 64) : null;
|
|
9
|
+
}
|
|
10
|
+
/** Why an order was refused for its code, or null when it was refused — or placed — for anything else. */
|
|
11
|
+
export function couponRefusalOf(result) {
|
|
12
|
+
if (result.success || result.domainError.code !== COUPON_NOT_APPLICABLE || typeof result.reason !== "string")
|
|
13
|
+
return null;
|
|
14
|
+
return {
|
|
15
|
+
reason: result.reason,
|
|
16
|
+
...(typeof result.shortBy === "number" ? { shortBy: result.shortBy } : {}),
|
|
17
|
+
...(typeof result.startsAt === "string" ? { startsAt: result.startsAt } : {}),
|
|
18
|
+
};
|
|
19
|
+
}
|
package/dist/domain.d.ts
CHANGED
|
@@ -272,6 +272,8 @@ export interface CheckoutDraft {
|
|
|
272
272
|
readonly customFieldResponses: readonly CheckoutFieldAnswer[];
|
|
273
273
|
readonly otpProof: OtpProof | null;
|
|
274
274
|
readonly attribution: CheckoutAttribution;
|
|
275
|
+
/** The code the shopper typed, if any; the server decides whether it applies. */
|
|
276
|
+
readonly couponCode?: string | null;
|
|
275
277
|
}
|
|
276
278
|
/** Proof that the checkout phone was verified or that the provider failed open. */
|
|
277
279
|
export declare const OtpProofSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -292,6 +294,7 @@ export declare const OrderSchema: z.ZodObject<{
|
|
|
292
294
|
price: z.ZodNumber;
|
|
293
295
|
deliveryFee: z.ZodNumber;
|
|
294
296
|
total: z.ZodNumber;
|
|
297
|
+
couponDiscount: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
295
298
|
}, z.core.$strip>;
|
|
296
299
|
export type Order = z.infer<typeof OrderSchema>;
|
|
297
300
|
/** Server-owned decision for the thank-you page's conversion event. */
|
package/dist/domain.js
CHANGED
|
@@ -189,6 +189,7 @@ export const OrderSchema = z.object({
|
|
|
189
189
|
price: z.number().finite().nonnegative(),
|
|
190
190
|
deliveryFee: z.number().finite().nonnegative(),
|
|
191
191
|
total: z.number().finite().nonnegative(),
|
|
192
|
+
couponDiscount: z.number().finite().nonnegative().nullish(),
|
|
192
193
|
});
|
|
193
194
|
/** Server-owned decision for the thank-you page's conversion event. */
|
|
194
195
|
export const TrackingDecisionSchema = z.object({
|
|
@@ -35,9 +35,10 @@ export interface StorefrontRoutes<C extends AstroAPIContext> {
|
|
|
35
35
|
readonly otpVerify: StorefrontApiRoute<C>;
|
|
36
36
|
/**
|
|
37
37
|
* POST a basket `{ items }` or one product `{ productId, productName, quantity,
|
|
38
|
-
* variantSelections, offerId, … }`, each with an optional `destination
|
|
39
|
-
* `{ success: true, data: CartValidation }`.
|
|
40
|
-
* With a destination, `data.delivery` is the fee and total the order will
|
|
38
|
+
* variantSelections, offerId, … }`, each with an optional `destination`,
|
|
39
|
+
* `couponCode` and `phone` → `{ success: true, data: CartValidation }`.
|
|
40
|
+
* With a destination, `data.delivery` is the fee and total the order will
|
|
41
|
+
* charge; with a code, `data.coupon` is what it does.
|
|
41
42
|
*/
|
|
42
43
|
readonly cartValidate: StorefrontApiRoute<C>;
|
|
43
44
|
/** POST an `AbandonedCheckoutInput` → 204. */
|
|
@@ -32,16 +32,23 @@ const otpVerifyInput = z.object({
|
|
|
32
32
|
requestId: z.string().min(1).max(200),
|
|
33
33
|
code: z.string().min(1).max(16),
|
|
34
34
|
});
|
|
35
|
+
/** What a quote is asked besides its lines (`CartQuoteOptions`). */
|
|
36
|
+
const quoteAsks = {
|
|
37
|
+
destination: DeliveryDestinationSchema.optional(),
|
|
38
|
+
couponCode: z.string().max(64).optional(),
|
|
39
|
+
phone: z.string().max(32).optional(),
|
|
40
|
+
};
|
|
35
41
|
/**
|
|
36
42
|
* A basket, or one product as its order form posts it — the same two shapes the
|
|
37
|
-
* server's quote takes — each with an optional destination to quote to
|
|
43
|
+
* server's quote takes — each with an optional destination to quote to, the
|
|
44
|
+
* code typed and the phone typed.
|
|
38
45
|
*/
|
|
39
46
|
const cartValidateInput = z.union([
|
|
40
47
|
z.object({
|
|
41
48
|
items: z.array(BasketLineInputSchema).min(1).max(MAX_BASKET_LINES),
|
|
42
|
-
|
|
49
|
+
...quoteAsks,
|
|
43
50
|
}),
|
|
44
|
-
ProductQuoteInputSchema.extend(
|
|
51
|
+
ProductQuoteInputSchema.extend(quoteAsks),
|
|
45
52
|
]);
|
|
46
53
|
const abandonedConvertInput = z.object({
|
|
47
54
|
sessionId: z.string().uuid(),
|
|
@@ -125,7 +132,11 @@ export function createStorefrontRoutes(options) {
|
|
|
125
132
|
method: "POST",
|
|
126
133
|
inputSchema: cartValidateInput,
|
|
127
134
|
successBody: (data) => ({ success: true, data }),
|
|
128
|
-
call: (client, { destination, ...lines }) => client.cart.quote(lines,
|
|
135
|
+
call: (client, { destination, couponCode, phone, ...lines }) => client.cart.quote(lines, {
|
|
136
|
+
...(destination ? { destination } : {}),
|
|
137
|
+
...(couponCode !== undefined ? { couponCode } : {}),
|
|
138
|
+
...(phone !== undefined ? { phone } : {}),
|
|
139
|
+
}),
|
|
129
140
|
}),
|
|
130
141
|
abandonedUpsert: route({
|
|
131
142
|
method: "POST",
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export * from "./schemas.js";
|
|
|
35
35
|
export * from "./domain.js";
|
|
36
36
|
export * from "./product-view.js";
|
|
37
37
|
export { getOfferTierView, getQuantityTierViews } from "./offer-view.js";
|
|
38
|
+
export { COUPON_NOT_APPLICABLE, COUPON_URL_PARAM, couponFromUrl, couponRefusalOf } from "./coupon.js";
|
|
39
|
+
export type { CartQuoteOptions } from "./client/cart.js";
|
|
38
40
|
export type { OfferTierInput, OfferTierView, QuantityTierView } from "./offer-view.js";
|
|
39
41
|
export { createAbandonedCheckoutTracker } from "./abandoned-checkout.js";
|
|
40
42
|
export type { AbandonedCheckoutPorts, AbandonedCheckoutSnapshot, AbandonedCheckoutStorage, AbandonedCheckoutTracker, } from "./abandoned-checkout.js";
|
package/dist/index.js
CHANGED
|
@@ -26,5 +26,6 @@ export * from "./schemas.js";
|
|
|
26
26
|
export * from "./domain.js";
|
|
27
27
|
export * from "./product-view.js";
|
|
28
28
|
export { getOfferTierView, getQuantityTierViews } from "./offer-view.js";
|
|
29
|
+
export { COUPON_NOT_APPLICABLE, COUPON_URL_PARAM, couponFromUrl, couponRefusalOf } from "./coupon.js";
|
|
29
30
|
export { createAbandonedCheckoutTracker } from "./abandoned-checkout.js";
|
|
30
31
|
export { META_CURRENCY, buildMetaAddToCartPayload, buildMetaInitiateCheckoutPayload, buildMetaProductPayload, buildMetaViewContentPayload, createMetaPixelTracker, } from "./meta-tracking.js";
|
package/dist/schemas.d.ts
CHANGED
|
@@ -713,6 +713,7 @@ export declare const CartValidationLineSchema: z.ZodObject<{
|
|
|
713
713
|
unitPrice: z.ZodNumber;
|
|
714
714
|
lineTotal: z.ZodNumber;
|
|
715
715
|
offerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
716
|
+
discount: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
716
717
|
maxQuantity: z.ZodNullable<z.ZodNumber>;
|
|
717
718
|
blocker: z.ZodNullable<z.ZodEnum<{
|
|
718
719
|
missing: "missing";
|
|
@@ -726,6 +727,24 @@ export declare const CartRewardSchema: z.ZodObject<{
|
|
|
726
727
|
productName: z.ZodNullable<z.ZodString>;
|
|
727
728
|
quantity: z.ZodNumber;
|
|
728
729
|
}, z.core.$strip>;
|
|
730
|
+
/**
|
|
731
|
+
* What the code the quote was asked with did: applied, or refused with the
|
|
732
|
+
* reason the order would be refused for. `reason` is an open string — a reason
|
|
733
|
+
* this SDK has not seen yet still validates — and `shortBy` / `startsAt` come
|
|
734
|
+
* with the reasons that have them.
|
|
735
|
+
*/
|
|
736
|
+
export declare const CouponOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
737
|
+
code: z.ZodString;
|
|
738
|
+
applied: z.ZodLiteral<true>;
|
|
739
|
+
discount: z.ZodNumber;
|
|
740
|
+
freeShipping: z.ZodBoolean;
|
|
741
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
742
|
+
code: z.ZodString;
|
|
743
|
+
applied: z.ZodLiteral<false>;
|
|
744
|
+
reason: z.ZodString;
|
|
745
|
+
shortBy: z.ZodOptional<z.ZodNumber>;
|
|
746
|
+
startsAt: z.ZodOptional<z.ZodString>;
|
|
747
|
+
}, z.core.$strip>], "applied">;
|
|
729
748
|
export declare const CartValidationSchema: z.ZodObject<{
|
|
730
749
|
lines: z.ZodArray<z.ZodObject<{
|
|
731
750
|
productId: z.ZodString;
|
|
@@ -736,6 +755,7 @@ export declare const CartValidationSchema: z.ZodObject<{
|
|
|
736
755
|
unitPrice: z.ZodNumber;
|
|
737
756
|
lineTotal: z.ZodNumber;
|
|
738
757
|
offerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
758
|
+
discount: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
739
759
|
maxQuantity: z.ZodNullable<z.ZodNumber>;
|
|
740
760
|
blocker: z.ZodNullable<z.ZodEnum<{
|
|
741
761
|
missing: "missing";
|
|
@@ -765,6 +785,18 @@ export declare const CartValidationSchema: z.ZodObject<{
|
|
|
765
785
|
fee: z.ZodNull;
|
|
766
786
|
total: z.ZodNull;
|
|
767
787
|
}, z.core.$strip>], "available">>;
|
|
788
|
+
coupon: z.ZodOptional<z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
789
|
+
code: z.ZodString;
|
|
790
|
+
applied: z.ZodLiteral<true>;
|
|
791
|
+
discount: z.ZodNumber;
|
|
792
|
+
freeShipping: z.ZodBoolean;
|
|
793
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
794
|
+
code: z.ZodString;
|
|
795
|
+
applied: z.ZodLiteral<false>;
|
|
796
|
+
reason: z.ZodString;
|
|
797
|
+
shortBy: z.ZodOptional<z.ZodNumber>;
|
|
798
|
+
startsAt: z.ZodOptional<z.ZodString>;
|
|
799
|
+
}, z.core.$strip>], "applied">>>;
|
|
768
800
|
}, z.core.$strip>;
|
|
769
801
|
export declare const CreatedOrderSchema: z.ZodObject<{
|
|
770
802
|
orderId: z.ZodString;
|
|
@@ -772,6 +804,7 @@ export declare const CreatedOrderSchema: z.ZodObject<{
|
|
|
772
804
|
price: z.ZodNumber;
|
|
773
805
|
deliveryFee: z.ZodNumber;
|
|
774
806
|
total: z.ZodNumber;
|
|
807
|
+
couponDiscount: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
775
808
|
}, z.core.$strip>;
|
|
776
809
|
export declare const OrderTrackingSchema: z.ZodObject<{
|
|
777
810
|
pixelId: z.ZodNullable<z.ZodString>;
|
package/dist/schemas.js
CHANGED
|
@@ -351,8 +351,10 @@ export const CartValidationLineSchema = z.object({
|
|
|
351
351
|
quantity: z.number().int(),
|
|
352
352
|
unitPrice: z.number(),
|
|
353
353
|
lineTotal: z.number(),
|
|
354
|
-
/** The
|
|
354
|
+
/** The offer applied to the line; absent from a server that predates offers on lines. */
|
|
355
355
|
offerId: z.string().nullish(),
|
|
356
|
+
/** The line's share of the coupon's discount, already out of `lineTotal`; absent from a server that predates coupons. */
|
|
357
|
+
discount: z.number().nullish(),
|
|
356
358
|
maxQuantity: z.number().nullable(),
|
|
357
359
|
blocker: z.enum(["missing", "unlisted", "out_of_stock"]).nullable(),
|
|
358
360
|
});
|
|
@@ -362,6 +364,16 @@ export const CartRewardSchema = z.object({
|
|
|
362
364
|
productName: z.string().nullable(),
|
|
363
365
|
quantity: z.number().int(),
|
|
364
366
|
});
|
|
367
|
+
/**
|
|
368
|
+
* What the code the quote was asked with did: applied, or refused with the
|
|
369
|
+
* reason the order would be refused for. `reason` is an open string — a reason
|
|
370
|
+
* this SDK has not seen yet still validates — and `shortBy` / `startsAt` come
|
|
371
|
+
* with the reasons that have them.
|
|
372
|
+
*/
|
|
373
|
+
export const CouponOutcomeSchema = z.discriminatedUnion("applied", [
|
|
374
|
+
z.object({ code: z.string(), applied: z.literal(true), discount: z.number(), freeShipping: z.boolean() }),
|
|
375
|
+
z.object({ code: z.string(), applied: z.literal(false), reason: z.string(), shortBy: z.number().optional(), startsAt: z.string().optional() }),
|
|
376
|
+
]);
|
|
365
377
|
export const CartValidationSchema = z.object({
|
|
366
378
|
lines: z.array(CartValidationLineSchema),
|
|
367
379
|
subtotal: z.number(),
|
|
@@ -378,6 +390,8 @@ export const CartValidationSchema = z.object({
|
|
|
378
390
|
z.object({ available: z.literal(false), fee: z.null(), total: z.null() }),
|
|
379
391
|
])
|
|
380
392
|
.optional(),
|
|
393
|
+
/** The code's outcome: null without one; absent from a server that predates coupons. */
|
|
394
|
+
coupon: CouponOutcomeSchema.nullish(),
|
|
381
395
|
});
|
|
382
396
|
export const CreatedOrderSchema = z.object({
|
|
383
397
|
orderId: z.string(),
|
|
@@ -385,6 +399,8 @@ export const CreatedOrderSchema = z.object({
|
|
|
385
399
|
price: z.number(),
|
|
386
400
|
deliveryFee: z.number(),
|
|
387
401
|
total: z.number(),
|
|
402
|
+
/** What the coupon took off; absent from a server that predates coupons. */
|
|
403
|
+
couponDiscount: z.number().nullish(),
|
|
388
404
|
});
|
|
389
405
|
export const OrderTrackingSchema = z.object({
|
|
390
406
|
pixelId: z.string().nullable(),
|
package/dist/types.d.ts
CHANGED
|
@@ -310,10 +310,12 @@ export interface CartValidationLine {
|
|
|
310
310
|
quantity: number;
|
|
311
311
|
/** CURRENT catalog price — the cart's optimistic price is display-only. */
|
|
312
312
|
unitPrice: number;
|
|
313
|
-
/** What the line costs
|
|
313
|
+
/** What the line costs, offers and the coupon applied. */
|
|
314
314
|
lineTotal: number;
|
|
315
|
-
/** The
|
|
315
|
+
/** The offer applied to the line; null or absent when none. */
|
|
316
316
|
offerId?: string | null | undefined;
|
|
317
|
+
/** The line's share of the coupon's discount, already out of `lineTotal`; null or absent without one. */
|
|
318
|
+
discount?: number | null | undefined;
|
|
317
319
|
/** Units still available; null when the product is not stock-tracked. */
|
|
318
320
|
maxQuantity: number | null;
|
|
319
321
|
/**
|
|
@@ -349,7 +351,31 @@ export interface CartValidation {
|
|
|
349
351
|
* deliver there, and an order to it is refused.
|
|
350
352
|
*/
|
|
351
353
|
delivery?: CartDeliveryQuote | undefined;
|
|
354
|
+
/** What the code the quote was asked with did; null without one, absent from a server that predates coupons. */
|
|
355
|
+
coupon?: CouponOutcome | null | undefined;
|
|
352
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* Why a code does not apply. Open: a server may name a reason this SDK has not
|
|
359
|
+
* seen yet, and a theme then shows its general sentence.
|
|
360
|
+
*/
|
|
361
|
+
export type CouponRefusalReason = "not_found" | "not_started" | "expired" | "used_up" | "identify_first" | "not_eligible" | "already_used" | "no_eligible_products" | "below_minimum_subtotal" | "below_minimum_quantity" | "delivery_already_free" | (string & {});
|
|
362
|
+
export interface CouponRefusal {
|
|
363
|
+
reason: CouponRefusalReason;
|
|
364
|
+
/** The dinars (`below_minimum_subtotal`) or items (`below_minimum_quantity`) still missing. */
|
|
365
|
+
shortBy?: number | undefined;
|
|
366
|
+
/** When a `not_started` code begins. */
|
|
367
|
+
startsAt?: string | undefined;
|
|
368
|
+
}
|
|
369
|
+
/** A code applied — `discount` already out of the lines, delivery free with `freeShipping` — or refused. */
|
|
370
|
+
export type CouponOutcome = {
|
|
371
|
+
code: string;
|
|
372
|
+
applied: true;
|
|
373
|
+
discount: number;
|
|
374
|
+
freeShipping: boolean;
|
|
375
|
+
} | ({
|
|
376
|
+
code: string;
|
|
377
|
+
applied: false;
|
|
378
|
+
} & CouponRefusal);
|
|
353
379
|
export type CartDeliveryQuote = {
|
|
354
380
|
available: true;
|
|
355
381
|
fee: number;
|
|
@@ -365,8 +391,10 @@ export interface CreatedOrder {
|
|
|
365
391
|
orderNumber: string;
|
|
366
392
|
price: number;
|
|
367
393
|
deliveryFee: number;
|
|
368
|
-
/** price + deliveryFee,
|
|
394
|
+
/** price + deliveryFee, offers and the coupon applied. */
|
|
369
395
|
total: number;
|
|
396
|
+
/** What the coupon took off; null or absent without one. */
|
|
397
|
+
couponDiscount?: number | null | undefined;
|
|
370
398
|
}
|
|
371
399
|
/** One basket line in a create-order body. */
|
|
372
400
|
export interface CreateOrderItemInput {
|
|
@@ -431,6 +459,8 @@ export interface CreateOrderInput {
|
|
|
431
459
|
items?: CreateOrderItemInput[] | undefined;
|
|
432
460
|
/** Landing-page attribution — best-effort; an unknown slug never blocks. */
|
|
433
461
|
landingPageSlug?: string | undefined;
|
|
462
|
+
/** The code the shopper typed. An order whose code does not apply is refused whole (`couponRefusalOf`). */
|
|
463
|
+
couponCode?: string | undefined;
|
|
434
464
|
}
|
|
435
465
|
/**
|
|
436
466
|
* The order's tracking destination — which Meta pixel it belongs to and
|
package/package.json
CHANGED