@absolutejs/commerce 0.0.1-beta.0 → 0.1.1-beta.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.
@@ -0,0 +1,17 @@
1
+ export type PricedLine = {
2
+ quantity: number;
3
+ /** Per-unit price (major units), excluding the one-time setup fee. */
4
+ unitPrice: number;
5
+ /** One-time fee charged once for the whole line (e.g. digitizing/setup). */
6
+ setupFee: number;
7
+ };
8
+ /** Total units across the cart. */
9
+ export declare const cartCount: <T extends {
10
+ quantity: number;
11
+ }>(lines: T[]) => number;
12
+ /** Cost of one line: unit × qty + the one-time setup fee. */
13
+ export declare const lineTotal: (line: PricedLine) => number;
14
+ /** Sum of every line's one-time setup fees. */
15
+ export declare const cartSetupTotal: <T extends PricedLine>(lines: T[]) => number;
16
+ /** Cart subtotal (all lines, including setup fees). */
17
+ export declare const cartSubtotal: <T extends PricedLine>(lines: T[]) => number;
@@ -0,0 +1,15 @@
1
+ export type Discount = {
2
+ code: string;
3
+ percentOff: number | null;
4
+ /** Fixed amount off in minor units (cents). */
5
+ amountOff: number | null;
6
+ active: boolean;
7
+ maxUses: number | null;
8
+ usedCount: number;
9
+ /** Expiry as epoch ms, or null for no expiry. */
10
+ expiresAt: number | null;
11
+ };
12
+ /** Whether a code can be redeemed right now. */
13
+ export declare const isDiscountValid: (discount: Discount | null | undefined, now?: number) => boolean;
14
+ /** The amount a code takes off a subtotal, in minor units (cents). */
15
+ export declare const discountAmountCents: (discount: Discount, subtotalCents: number) => number;
@@ -0,0 +1,8 @@
1
+ /** Round a major-unit amount to 2 decimals (avoids float drift in pricing). */
2
+ export declare const roundMoney: (value: number) => number;
3
+ /** Major units → integer minor units (dollars → cents). */
4
+ export declare const toCents: (value: number) => number;
5
+ /** Integer minor units → major units (cents → dollars). */
6
+ export declare const fromCents: (cents: number) => number;
7
+ /** Format a major-unit amount, USD by default. */
8
+ export declare const formatPrice: (value: number, currency?: string) => string;
@@ -0,0 +1,9 @@
1
+ export type OrderStatus = 'paid' | 'shipped' | 'rejected' | 'failed' | 'refunded';
2
+ export declare const PRODUCTION_STAGES: readonly ["queued", "digitizing", "production", "ready"];
3
+ export type ProductionStage = (typeof PRODUCTION_STAGES)[number];
4
+ /** Normalize an unknown stage to a valid one (defaults to the first). */
5
+ export declare const toProductionStage: (stage: string | null | undefined) => "queued" | "digitizing" | "production" | "ready";
6
+ /** The next stage forward, or null at the end. */
7
+ export declare const nextStage: (stage: string) => ProductionStage | null;
8
+ /** The previous stage, or null at the start. */
9
+ export declare const prevStage: (stage: string) => ProductionStage | null;
@@ -0,0 +1,78 @@
1
+ import type { Address } from './shipping';
2
+ export type CheckoutLineItem = {
3
+ name: string;
4
+ description?: string;
5
+ /** Unit price in minor units (cents). */
6
+ amountCents: number;
7
+ quantity: number;
8
+ taxBehavior?: 'inclusive' | 'exclusive';
9
+ };
10
+ export type CheckoutShipping = {
11
+ mode: 'none';
12
+ } | {
13
+ mode: 'collect';
14
+ countries: string[];
15
+ flatAmountCents?: number;
16
+ label?: string;
17
+ };
18
+ export type CreateCheckoutInput = {
19
+ uiMode: 'embedded' | 'hosted';
20
+ currency?: string;
21
+ lineItems: CheckoutLineItem[];
22
+ metadata?: Record<string, string>;
23
+ /** Embedded checkout return URL. */
24
+ returnUrl?: string;
25
+ /** Hosted checkout success/cancel URLs. */
26
+ successUrl?: string;
27
+ cancelUrl?: string;
28
+ shipping?: CheckoutShipping;
29
+ /** A coupon id from `createCoupon`, applied to the session. */
30
+ couponId?: string;
31
+ /** Calculate tax automatically when the provider supports it. */
32
+ automaticTax?: boolean;
33
+ };
34
+ export type CheckoutResult = {
35
+ id: string;
36
+ /** Set for embedded checkout. */
37
+ clientSecret: string | null;
38
+ /** Set for hosted checkout (a pay link). */
39
+ url: string | null;
40
+ };
41
+ export type CreateCouponInput = {
42
+ percentOff?: number;
43
+ amountOffCents?: number;
44
+ currency?: string;
45
+ };
46
+ export type CheckoutSession = {
47
+ id: string;
48
+ /** Session lifecycle status (e.g. 'complete' | 'open' | 'expired'). */
49
+ status: string | null;
50
+ paymentStatus: string | null;
51
+ amountTotalCents: number | null;
52
+ currency: string | null;
53
+ customerEmail: string | null;
54
+ customerName: string | null;
55
+ shippingAddress: Address | null;
56
+ metadata: Record<string, string>;
57
+ lineItems: {
58
+ name: string;
59
+ quantity: number;
60
+ amountTotalCents: number;
61
+ }[];
62
+ };
63
+ export type WebhookEvent = {
64
+ type: string;
65
+ /** A checkout that completed successfully (sync or async). */
66
+ isComplete: boolean;
67
+ /** A checkout that failed or expired. */
68
+ isFailed: boolean;
69
+ session: CheckoutSession;
70
+ };
71
+ export type PaymentProvider = {
72
+ createCheckout(input: CreateCheckoutInput): Promise<CheckoutResult>;
73
+ createCoupon(input: CreateCouponInput): Promise<string>;
74
+ /** Fetch a session's current state (for return pages). */
75
+ retrieveCheckout(sessionId: string): Promise<CheckoutSession>;
76
+ refundBySession(sessionId: string): Promise<void>;
77
+ verifyWebhook(payload: string, signature: string): Promise<WebhookEvent>;
78
+ };
@@ -0,0 +1,8 @@
1
+ export type QuantityBreak = {
2
+ min: number;
3
+ discount: number;
4
+ };
5
+ /** The fractional discount (0–1) that applies at a given quantity. */
6
+ export declare const quantityDiscount: (breaks: QuantityBreak[], quantity: number) => number;
7
+ /** The next break a buyer hasn't reached yet (for "order N+ to save X%" hints). */
8
+ export declare const nextQuantityBreak: (breaks: QuantityBreak[], quantity: number) => QuantityBreak | null;
package/dist/index.d.ts CHANGED
@@ -1 +1,7 @@
1
+ export * from './core/cart';
2
+ export * from './core/discounts';
3
+ export * from './core/money';
4
+ export * from './core/orders';
5
+ export * from './core/payment';
6
+ export * from './core/pricing';
1
7
  export * from './core/shipping';
package/dist/index.js CHANGED
@@ -1,4 +1,48 @@
1
1
  // @bun
2
+ // src/core/cart.ts
3
+ var cartCount = (lines) => lines.reduce((sum, line) => sum + line.quantity, 0);
4
+ var lineTotal = (line) => line.setupFee + line.unitPrice * line.quantity;
5
+ var cartSetupTotal = (lines) => lines.reduce((sum, line) => sum + line.setupFee, 0);
6
+ var cartSubtotal = (lines) => lines.reduce((sum, line) => sum + lineTotal(line), 0);
7
+ // src/core/discounts.ts
8
+ var isDiscountValid = (discount, now = 0) => {
9
+ if (!discount || !discount.active)
10
+ return false;
11
+ const at = now || nowMs();
12
+ if (discount.expiresAt !== null && discount.expiresAt < at)
13
+ return false;
14
+ if (discount.maxUses !== null && discount.usedCount >= discount.maxUses)
15
+ return false;
16
+ return true;
17
+ };
18
+ var discountAmountCents = (discount, subtotalCents) => discount.percentOff !== null ? Math.round(subtotalCents * discount.percentOff / 100) : Math.min(subtotalCents, discount.amountOff ?? 0);
19
+ var nowMs = () => Date.now();
20
+ // src/core/money.ts
21
+ var CENTS = 100;
22
+ var roundMoney = (value) => Math.round(value * CENTS) / CENTS;
23
+ var toCents = (value) => Math.round(value * CENTS);
24
+ var fromCents = (cents) => cents / CENTS;
25
+ var formatPrice = (value, currency = "USD") => {
26
+ const code = currency.toUpperCase();
27
+ return code === "USD" ? `$${value.toFixed(2)}` : `${value.toFixed(2)} ${code}`;
28
+ };
29
+ // src/core/orders.ts
30
+ var PRODUCTION_STAGES = [
31
+ "queued",
32
+ "digitizing",
33
+ "production",
34
+ "ready"
35
+ ];
36
+ var stageIndex = (stage) => PRODUCTION_STAGES.indexOf(stage);
37
+ var toProductionStage = (stage) => stage && stageIndex(stage) >= 0 ? stage : PRODUCTION_STAGES[0];
38
+ var nextStage = (stage) => PRODUCTION_STAGES[stageIndex(toProductionStage(stage)) + 1] ?? null;
39
+ var prevStage = (stage) => {
40
+ const index = stageIndex(toProductionStage(stage));
41
+ return index > 0 ? PRODUCTION_STAGES[index - 1] ?? null : null;
42
+ };
43
+ // src/core/pricing.ts
44
+ var quantityDiscount = (breaks, quantity) => breaks.reduce((best, brk) => quantity >= brk.min ? brk.discount : best, 0);
45
+ var nextQuantityBreak = (breaks, quantity) => breaks.find((brk) => brk.min > quantity) ?? null;
2
46
  // src/core/shipping.ts
3
47
  var DEFAULT_APPAREL_PARCEL = {
4
48
  heightIn: 2,
@@ -7,5 +51,21 @@ var DEFAULT_APPAREL_PARCEL = {
7
51
  widthIn: 9
8
52
  };
9
53
  export {
54
+ toProductionStage,
55
+ toCents,
56
+ roundMoney,
57
+ quantityDiscount,
58
+ prevStage,
59
+ nextStage,
60
+ nextQuantityBreak,
61
+ lineTotal,
62
+ isDiscountValid,
63
+ fromCents,
64
+ formatPrice,
65
+ discountAmountCents,
66
+ cartSubtotal,
67
+ cartSetupTotal,
68
+ cartCount,
69
+ PRODUCTION_STAGES,
10
70
  DEFAULT_APPAREL_PARCEL
11
71
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/commerce",
3
- "version": "0.0.1-beta.0",
3
+ "version": "0.1.1-beta.0",
4
4
  "description": "Provider-agnostic commerce primitives (cart, orders, fulfillment, shipping) for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",