@lacspace/coupon 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +66 -43
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,19 +1,35 @@
1
+ <div align="center">
2
+
1
3
  # @lacspace/coupon
2
4
 
3
- A small, dependable **discount / coupon engine** — percent, fixed and free-shipping codes with validity windows, minimum-subtotal thresholds, discount caps and usage limits. All money is in **integer minor units** (e.g. cents), so there is no floating-point drift.
5
+ **A discount / coupon engine — percent, fixed & free-shipping codes with validity windows, minimum-subtotal thresholds, discount caps and usage limits.**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@lacspace/coupon?color=%2316a34a&label=npm)](https://www.npmjs.com/package/@lacspace/coupon)
8
+ [![install size](https://packagephobia.com/badge?p=@lacspace/coupon)](https://packagephobia.com/result?p=@lacspace/coupon)
9
+ [![minzipped](https://img.shields.io/bundlephobia/minzip/@lacspace/coupon?label=minzip)](https://bundlephobia.com/package/@lacspace/coupon)
10
+ [![types](https://img.shields.io/badge/types-included-blue)](https://www.npmjs.com/package/@lacspace/coupon)
11
+ [![license](https://img.shields.io/npm/l/@lacspace/coupon?color=green)](https://github.com/lacspace/npm-packages/blob/main/LICENSE)
12
+
13
+ </div>
4
14
 
5
- - **Zero runtime dependencies**
6
- - **Isomorphic** — Node, edge runtimes and browsers
7
- - **TypeScript-first**, strict types
15
+ > The discount logic every checkout re-implements badly: is this code valid right now, and what does it actually take off the total? A tiny pair of **pure functions** over a plain `Coupon` object — validate the window / threshold / usage limit, then compute `percent`, `fixed` or `free-shipping` discounts. Integer **minor units** throughout, so there's no floating-point drift.
16
+
17
+ - 🏷️ **Three code kinds** — `percent`, `fixed` (flat amount) and `free-shipping`
18
+ - ⏱️ **Validity window** — `startsAt` / `endsAt` (ISO-8601), plus `minSubtotal` threshold and `usageLimit`
19
+ - 🧢 **Discount caps** — `maxDiscount`, and the discount is never more than the subtotal
20
+ - 🪙 **Exact money** — integer **minor units** (cents / paisa) everywhere, never a float
21
+ - ⚡ Isomorphic — Node, edge runtimes & browsers · 📦 ESM + CJS · fully typed · zero deps
22
+
23
+ ## Install
8
24
 
9
25
  ```bash
10
- npm install @lacspace/coupon
26
+ npm install @lacspace/coupon # or pnpm add / yarn add / bun add
11
27
  ```
12
28
 
13
- ## Quick start
29
+ ## Apply a coupon
14
30
 
15
31
  ```ts
16
- import { applyCoupon, validateCoupon, type Coupon } from "@lacspace/coupon";
32
+ import { applyCoupon, type Coupon } from "@lacspace/coupon";
17
33
 
18
34
  const coupon: Coupon = {
19
35
  code: "SAVE20",
@@ -24,58 +40,65 @@ const coupon: Coupon = {
24
40
  };
25
41
 
26
42
  applyCoupon(coupon, { subtotal: 3000, shipping: 400 });
27
- // { valid: true, discount: 500, shippingDiscount: 0, total: 2900 }
43
+ // { valid: true, discount: 500, shippingDiscount: 0, total: 2900 }
44
+ // 20% of 3000 is 600, but maxDiscount caps it at 500
45
+ // total = max(0, subtotal - discount + shipping - shippingDiscount)
28
46
  ```
29
47
 
30
- ## API
31
-
32
- ### `validateCoupon(coupon, { subtotal, now? }): { valid, reason? }`
48
+ `percent` is `round(subtotal * value / 100)`, `fixed` is `min(value, subtotal)` — both capped by `maxDiscount` and clamped to the subtotal:
33
49
 
34
- Checks the validity window (`not-yet-started` / `expired`), `minSubtotal` (`below-min-subtotal`) and `usageLimit` vs `used` (`usage-limit-reached`). Does not compute a discount.
50
+ ```ts
51
+ // flat $5.00 off (value is in minor units)
52
+ applyCoupon({ code: "FIVER", type: "fixed", value: 500 }, { subtotal: 1200 });
53
+ // → { valid: true, discount: 500, shippingDiscount: 0, total: 700 }
35
54
 
36
- ### `applyCoupon(coupon, { subtotal, shipping?, now? }): CouponResult`
55
+ // free shipping zeroes the shipping line only
56
+ applyCoupon({ code: "FREESHIP", type: "free-shipping" }, { subtotal: 1000, shipping: 300 });
57
+ // → { valid: true, discount: 0, shippingDiscount: 300, total: 1000 }
58
+ ```
37
59
 
38
- Validates, then computes the discount:
60
+ ## Validate before applying
39
61
 
40
- | type | discount |
41
- | --------------- | ---------------------------------------------------- |
42
- | `percent` | `round(subtotal * value / 100)`, capped by `maxDiscount` and the subtotal |
43
- | `fixed` | `min(value, subtotal)`, capped by `maxDiscount` |
44
- | `free-shipping` | `shippingDiscount = shipping` |
62
+ ```ts
63
+ import { validateCoupon } from "@lacspace/coupon";
45
64
 
46
- ```
47
- total = max(0, subtotal - discount + shipping - shippingDiscount)
65
+ validateCoupon(
66
+ { code: "XMAS", type: "percent", value: 10, endsAt: "2025-12-26T00:00:00Z" },
67
+ { subtotal: 5000, now: new Date("2026-01-01") },
68
+ );
69
+ // → { valid: false, reason: "expired" }
48
70
  ```
49
71
 
50
- When the coupon is invalid, discounts are `0` and the total is the untouched `subtotal + shipping`.
72
+ `validateCoupon` checks the window (`not-yet-started` / `expired`), `minSubtotal` (`below-min-subtotal`) and `usageLimit` vs `used` (`usage-limit-reached`) — it does **not** compute a discount. `applyCoupon` runs it first; when a coupon is invalid it returns zero discounts and the untouched `subtotal + shipping`.
51
73
 
52
- ```ts
53
- applyCoupon({ code: "FREESHIP", type: "free-shipping" }, { subtotal: 1000, shipping: 300 });
54
- // { valid: true, discount: 0, shippingDiscount: 300, total: 1000 }
55
- ```
74
+ ## API
56
75
 
57
- ## Types
76
+ | Function | Description |
77
+ | --- | --- |
78
+ | `validateCoupon(coupon, { subtotal, now? })` | `{ valid, reason? }` — checks window, `minSubtotal`, `usageLimit`; no discount computed |
79
+ | `applyCoupon(coupon, { subtotal, shipping?, now? })` | `{ valid, reason?, discount, shippingDiscount, total }` in minor units |
58
80
 
59
- ```ts
60
- interface Coupon {
61
- code: string;
62
- type: "percent" | "fixed" | "free-shipping";
63
- value?: number; // percent 0..100, or minor units for fixed
64
- minSubtotal?: number;
65
- maxDiscount?: number;
66
- startsAt?: string; // ISO-8601
67
- endsAt?: string; // ISO-8601
68
- usageLimit?: number;
69
- used?: number;
70
- currency?: string;
71
- }
72
- ```
81
+ | `type` | discount |
82
+ | --- | --- |
83
+ | `percent` | `round(subtotal * value / 100)`, capped by `maxDiscount` and the subtotal |
84
+ | `fixed` | `min(value, subtotal)`, capped by `maxDiscount` |
85
+ | `free-shipping` | `shippingDiscount = shipping` |
86
+
87
+ `total = max(0, subtotal - discount + shipping - shippingDiscount)`. `value` is a whole percent `0..100` for `percent`, or an amount in minor units for `fixed` (ignored for `free-shipping`). Types exported: `Coupon`, `CouponValidation`, `CouponResult`.
88
+
89
+ ## Licensing
90
+
91
+ This package is **free** under the **[Lacspace Free Licence](https://developer.lacspace.com/licenses/lacspace-free-1.0)** — permissive freedoms. Use it in personal and commercial projects at no cost; just keep the notice.
92
+
93
+ Not every Lacspace package is free. We also offer **Commercial** (paid), **Client-specific**, and **Private** (proprietary) packages under separate terms. See the full **[Lacspace Licence Centre](https://developer.lacspace.com/licenses)**.
94
+
95
+ <!-- LACSPACE-DEV-PLATFORM -->
73
96
 
74
97
  ---
75
98
 
76
99
  ## The Lacspace Developer Platform
77
100
 
78
- `@lacspace/coupon` is part of **63+ zero-dependency, isomorphic TypeScript packages**. Explore the ecosystem:
101
+ `@lacspace/coupon` is part of **80+ zero-dependency, isomorphic TypeScript packages**. Explore the ecosystem:
79
102
 
80
103
  - 🗂️ **All packages** — https://developer.lacspace.com/packages
81
104
  - 🧭 **Developer handbook** — https://developer.lacspace.com/handbook
@@ -83,4 +106,4 @@ interface Coupon {
83
106
  - 🖥️ **Finished app templates** — https://templates.lacspace.com
84
107
  - 🚀 **Scaffold a full app** — `npm create lacspace-app@latest`
85
108
 
86
- Free under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** — a permissive, free-to-use licence.
109
+ Free under the **[Lacspace Free Licence](https://developer.lacspace.com/licenses/lacspace-free-1.0)** — a permissive, free-to-use licence.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lacspace/coupon",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Discount & coupon engine — percent, fixed and free-shipping codes with validity windows, minimum-subtotal, discount caps and usage limits. Integer minor units, no float drift. Zero-dependency, isomorphic.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",