@wtree/payload-ecommerce-coupon 3.70.7 → 3.72.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/README.md +68 -35
- package/dist/collections/createReferralProgramsCollection.d.ts.map +1 -1
- package/dist/endpoints/applyCoupon.d.ts.map +1 -1
- package/dist/endpoints/partnerStats.d.ts.map +1 -1
- package/dist/endpoints/validateCoupon.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +302 -210
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +301 -211
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utilities/getCartTotalWithDiscounts.d.ts +15 -0
- package/dist/utilities/getCartTotalWithDiscounts.d.ts.map +1 -0
- package/dist/utilities/roundTo2.d.ts +5 -0
- package/dist/utilities/roundTo2.d.ts.map +1 -0
- package/dist/utilities/sanitizePluginConfig.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,14 +14,15 @@ Production-ready coupon and referral system plugin for **Payload CMS** with seam
|
|
|
14
14
|
- **Hybrid Mode** (`enableReferrals: true` + `referralConfig.allowBothSystems: true`) – Both systems active
|
|
15
15
|
|
|
16
16
|
### **Coupon Mode Features**
|
|
17
|
-
- ✅ **Flexible Discounts** – Percentage or fixed amount discounts
|
|
18
|
-
- ✅ **Usage Controls** –
|
|
17
|
+
- ✅ **Flexible Discounts** – Percentage or fixed amount discounts (all amounts rounded to 2 decimals)
|
|
18
|
+
- ✅ **Usage Controls** – Global usage limit; usage is counted when an **order is placed** (not on apply)
|
|
19
|
+
- ✅ **Per-customer limit** – Optional limit per customer (requires `customerEmail` when applying)
|
|
19
20
|
- ✅ **Conditions** – Minimum/maximum order values (top-level fields), product restrictions
|
|
20
|
-
- ✅ **Auto-Application** – Seamless cart integration
|
|
21
|
+
- ✅ **Auto-Application** – Seamless cart integration; cart total is reduced when a code is applied
|
|
21
22
|
|
|
22
23
|
### **Referral Mode Features**
|
|
23
|
-
- ✅ **Commission Rules** – **Required
|
|
24
|
-
- ✅ **Referrer/Referee
|
|
24
|
+
- ✅ **Commission Rules** – **Required.** At least one rule per program. Each rule has **Referrer Reward** (partner commission) and **Referee Reward** (customer discount) inside it, plus appliesTo (all products / categories / products).
|
|
25
|
+
- ✅ **Referrer/Referee inside each rule** – Partner gets commission, customer gets discount; type (percentage/fixed), value, and optional max cap per rule.
|
|
25
26
|
- ✅ **Partner Tracking** – Commission earnings and referral performance (credited when order is placed)
|
|
26
27
|
- ✅ **Auto-Generated Codes** – Unique referral codes for each partner
|
|
27
28
|
- ✅ **Partner Dashboard** – Ready-to-use React components for partner stats
|
|
@@ -32,6 +33,7 @@ Production-ready coupon and referral system plugin for **Payload CMS** with seam
|
|
|
32
33
|
- ✅ **Frontend Hooks** – `useCouponCode()`, `usePartnerStats()`, `validateCouponCode()` for React/Next.js
|
|
33
34
|
- ✅ **Auto-Integration** – Extends carts/orders automatically
|
|
34
35
|
- ✅ **Usage on Order** – Coupon/referral usage and partner earnings are recorded when an order is placed (not when code is applied)
|
|
36
|
+
- ✅ **Cart total helper** – `getCartTotalWithDiscounts(cart)` for host app cart hooks so totals respect discounts
|
|
35
37
|
- ✅ **Type-Safe** – Full TypeScript support
|
|
36
38
|
- ✅ **Access Control** – Role-based permissions with partner role support
|
|
37
39
|
- ✅ **Custom Admin Groups** – Separate "Coupons" and "Referrals" categories
|
|
@@ -100,6 +102,14 @@ export default buildConfig({
|
|
|
100
102
|
isAdmin: ({ req }) => req.user?.role === 'admin',
|
|
101
103
|
isPartner: ({ req }) => req.user?.role === 'partner',
|
|
102
104
|
},
|
|
105
|
+
|
|
106
|
+
// Optional: for per-customer coupon limit (defaults shown)
|
|
107
|
+
// orderIntegration: {
|
|
108
|
+
// ordersSlug: 'orders',
|
|
109
|
+
// orderCustomerEmailField: 'customerEmail',
|
|
110
|
+
// orderPaymentStatusField: 'paymentStatus',
|
|
111
|
+
// orderPaidStatusValue: 'paid',
|
|
112
|
+
// },
|
|
103
113
|
}),
|
|
104
114
|
],
|
|
105
115
|
})
|
|
@@ -183,6 +193,32 @@ if (doc.paymentStatus === 'paid' && (doc.appliedCoupon || doc.appliedReferralCod
|
|
|
183
193
|
- **Coupon:** increments the coupon’s `usageCount`.
|
|
184
194
|
- **Referral:** increments the referral code’s `usageCount` and `successfulReferralsCount`, and adds `order.partnerCommission` to the referral code’s `totalEarnings` and `pendingEarnings` (referrer gets commission; referee discount is already on the order).
|
|
185
195
|
|
|
196
|
+
### 4.5 Coupon usage rules and cart total
|
|
197
|
+
|
|
198
|
+
**Usage rule**
|
|
199
|
+
- A customer can use a coupon until the coupon’s **global usage limit** or **expiry date** (usage is counted when the order is placed, not when the code is applied).
|
|
200
|
+
- **Optional per-customer limit:** If you set **Per customer limit** on a coupon, the customer must provide their email when applying (e.g. `customerEmail` in the apply request). The coupon is rejected once they have that many **paid** orders with that coupon. You can pass `customerEmail` when validating so the UI can show “limit reached” before apply.
|
|
201
|
+
|
|
202
|
+
**Monetary values**
|
|
203
|
+
- All discount, commission, and total values are rounded to **2 decimal places**.
|
|
204
|
+
|
|
205
|
+
**Cart total in your app**
|
|
206
|
+
- The plugin writes the reduced `total` when a code is applied. If your host app recalculates the cart total (e.g. in a `beforeChange` hook when items change), use the formula **total = subtotal − discountAmount − customerDiscount** so the discount is not overwritten. Use the provided helper in your Carts collection:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { getCartTotalWithDiscounts } from '@wtree/payload-ecommerce-coupon'
|
|
210
|
+
|
|
211
|
+
// In your Carts collection beforeChange hook, after setting items/subtotal:
|
|
212
|
+
data.total = getCartTotalWithDiscounts(data)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- **Optional config** for per-customer limit: `orderIntegration` with `ordersSlug`, `orderCustomerEmailField`, `orderPaymentStatusField`, `orderPaidStatusValue` (defaults: `'orders'`, `'customerEmail'`, `'paymentStatus'`, `'paid'`).
|
|
216
|
+
|
|
217
|
+
**Server utilities (for host app)**
|
|
218
|
+
|
|
219
|
+
- `getCartTotalWithDiscounts(cart)` – Returns `roundTo2(subtotal - discountAmount - customerDiscount)`. Use in your Carts `beforeChange` (or wherever you compute total) so the displayed total always reflects coupon/referral discounts.
|
|
220
|
+
- `recordCouponUsageForOrder(payload, order, pluginConfig)` – Call when an order is paid to increment coupon/referral usage and credit partner earnings (see step 4 above).
|
|
221
|
+
|
|
186
222
|
### 5. Frontend Integration
|
|
187
223
|
|
|
188
224
|
#### Apply Coupon/Referral Code
|
|
@@ -198,6 +234,8 @@ function CheckoutComponent() {
|
|
|
198
234
|
const result = await useCouponCode({
|
|
199
235
|
code,
|
|
200
236
|
cartID: cartId,
|
|
237
|
+
// When a coupon has per-customer limit, pass customerEmail so the limit can be enforced
|
|
238
|
+
// customerEmail: customerEmailFromAuthOrForm,
|
|
201
239
|
})
|
|
202
240
|
|
|
203
241
|
if (result.success) {
|
|
@@ -297,30 +335,13 @@ Best when you need both traditional coupons AND partner referrals, but want to e
|
|
|
297
335
|
### **Setting Up Referral Mode**
|
|
298
336
|
|
|
299
337
|
1. **Navigate to Admin Panel** → Go to "Referral Programs" (under "Referrals" group)
|
|
300
|
-
2. **Create Referral Program
|
|
301
|
-
- **Name**: "
|
|
302
|
-
- **
|
|
303
|
-
- **
|
|
304
|
-
- **
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
```json
|
|
308
|
-
{
|
|
309
|
-
"name": "Electronics Category",
|
|
310
|
-
"appliesTo": "categories",
|
|
311
|
-
"categories": ["electronics"],
|
|
312
|
-
"totalCommission": {
|
|
313
|
-
"type": "percentage",
|
|
314
|
-
"value": 15
|
|
315
|
-
},
|
|
316
|
-
"split": {
|
|
317
|
-
"partnerPercentage": 70,
|
|
318
|
-
"customerPercentage": 30
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
```
|
|
322
|
-
- **Referrer (partner)** receives the **commission** share (`partnerPercentage`).
|
|
323
|
-
- **Referee (customer)** receives the **discount** share (`customerPercentage`).
|
|
338
|
+
2. **Create Referral Program** with **Commission Rules** (required – at least one). Each rule has:
|
|
339
|
+
- **Name**: e.g. "Default" or "Electronics"
|
|
340
|
+
- **Applies To**: All Products, Specific Categories, or Specific Products
|
|
341
|
+
- **Referrer Reward** (inside the rule): Commission for the partner. Type: Percentage of Order or Fixed Amount. Value: e.g. 65 = 65% of order value. Optional Max Reward.
|
|
342
|
+
- **Referee Reward** (inside the rule): Discount for the customer. Type: Percentage Discount or Fixed Amount. Value: e.g. 30 = 30% off. Optional Max Reward.
|
|
343
|
+
|
|
344
|
+
There are no outer Referrer Reward / Referee Reward fields – only **Commission Rules**, and each rule contains its own Referrer Reward and Referee Reward.
|
|
324
345
|
|
|
325
346
|
### **Commission and Discount (Referrer / Referee)**
|
|
326
347
|
|
|
@@ -345,21 +366,27 @@ Best when you need both traditional coupons AND partner referrals, but want to e
|
|
|
345
366
|
### **Coupon/Referral Endpoints**
|
|
346
367
|
|
|
347
368
|
#### POST /api/coupons/validate
|
|
348
|
-
Validate a code without applying it.
|
|
369
|
+
Validate a code without applying it. Optionally pass `customerEmail` to check per-customer limit for coupons that have one.
|
|
349
370
|
|
|
350
371
|
```bash
|
|
351
372
|
curl -X POST http://localhost:3000/api/coupons/validate \
|
|
352
373
|
-H "Content-Type: application/json" \
|
|
353
374
|
-d '{"code": "WELCOME10", "cartValue": 5000}'
|
|
375
|
+
|
|
376
|
+
# With per-customer limit check:
|
|
377
|
+
# -d '{"code": "WELCOME10", "cartValue": 5000, "customerEmail": "user@example.com"}'
|
|
354
378
|
```
|
|
355
379
|
|
|
356
380
|
#### POST /api/coupons/apply
|
|
357
|
-
Apply a code to a cart. **Does not** increment usage; usage is recorded when you call the record-order-usage endpoint for a placed order.
|
|
381
|
+
Apply a code to a cart. **Does not** increment usage; usage is recorded when you call the record-order-usage endpoint for a placed order. For coupons with **per-customer limit**, include `customerEmail` so the limit can be enforced.
|
|
358
382
|
|
|
359
383
|
```bash
|
|
360
384
|
curl -X POST http://localhost:3000/api/coupons/apply \
|
|
361
385
|
-H "Content-Type: application/json" \
|
|
362
386
|
-d '{"code": "WELCOME10", "cartID": "cart-123"}'
|
|
387
|
+
|
|
388
|
+
# With per-customer limit (required when coupon has per-customer limit):
|
|
389
|
+
# -d '{"code": "WELCOME10", "cartID": "cart-123", "customerEmail": "user@example.com"}'
|
|
363
390
|
```
|
|
364
391
|
|
|
365
392
|
#### POST /api/coupons/record-order-usage
|
|
@@ -471,6 +498,14 @@ export type CouponPluginOptions = {
|
|
|
471
498
|
showRecentReferrals?: boolean // Show recent referrals (default: true)
|
|
472
499
|
showCommissionBreakdown?: boolean // Show breakdown (default: true)
|
|
473
500
|
}
|
|
501
|
+
|
|
502
|
+
/** Optional: for per-customer coupon limit (query paid orders by customer) */
|
|
503
|
+
orderIntegration?: {
|
|
504
|
+
ordersSlug?: string // Default: 'orders'
|
|
505
|
+
orderCustomerEmailField?: string // Default: 'customerEmail'
|
|
506
|
+
orderPaymentStatusField?: string // Default: 'paymentStatus'
|
|
507
|
+
orderPaidStatusValue?: string // Default: 'paid'
|
|
508
|
+
}
|
|
474
509
|
}
|
|
475
510
|
```
|
|
476
511
|
|
|
@@ -634,10 +669,8 @@ This occurs when `singleCodePerCart: true` and a code is already applied.
|
|
|
634
669
|
- Usage is **not** incremented when a code is applied to the cart. Call **record-order-usage** (or `recordCouponUsageForOrder`) when an order is placed/paid. See [Record usage when order is placed](#4-record-usage-when-order-is-placed).
|
|
635
670
|
|
|
636
671
|
#### **Commission not calculating correctly**
|
|
637
|
-
-
|
|
638
|
-
- Verify
|
|
639
|
-
- Check that products have correct category assignments
|
|
640
|
-
- Ensure cart has valid `subtotal` or `total` field
|
|
672
|
+
- Ensure at least one **Commission Rule** exists and each rule has **Referrer Reward** and **Referee Reward** set
|
|
673
|
+
- Verify cart has valid `subtotal` or `total` and items match rule’s appliesTo (all / categories / products)
|
|
641
674
|
|
|
642
675
|
## 📋 Future Features (Roadmap)
|
|
643
676
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createReferralProgramsCollection.d.ts","sourceRoot":"","sources":["../../src/collections/createReferralProgramsCollection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAE5D,eAAO,MAAM,gCAAgC,GAC3C,cAAc,4BAA4B,KACzC,
|
|
1
|
+
{"version":3,"file":"createReferralProgramsCollection.d.ts","sourceRoot":"","sources":["../../src/collections/createReferralProgramsCollection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAE5D,eAAO,MAAM,gCAAgC,GAC3C,cAAc,4BAA4B,KACzC,gBA4PF,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"applyCoupon.d.ts","sourceRoot":"","sources":["../../src/endpoints/applyCoupon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"applyCoupon.d.ts","sourceRoot":"","sources":["../../src/endpoints/applyCoupon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAG5D,KAAK,IAAI,GAAG;IACV,YAAY,EAAE,4BAA4B,CAAA;CAC3C,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC5B,kBAAkB,IAAI,KAAG,cAsFzB,CAAA;AA6XH,eAAO,MAAM,mBAAmB,GAAI,kBAAkB,IAAI,KAAG,QAI3D,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partnerStats.d.ts","sourceRoot":"","sources":["../../src/endpoints/partnerStats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,KAAK,EAAsC,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAEhG,KAAK,IAAI,GAAG;IACV,YAAY,EAAE,4BAA4B,CAAA;CAC3C,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC7B,kBAAkB,IAAI,KAAG,
|
|
1
|
+
{"version":3,"file":"partnerStats.d.ts","sourceRoot":"","sources":["../../src/endpoints/partnerStats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,KAAK,EAAsC,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAEhG,KAAK,IAAI,GAAG;IACV,YAAY,EAAE,4BAA4B,CAAA;CAC3C,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC7B,kBAAkB,IAAI,KAAG,cAqKzB,CAAA;AAEH,eAAO,MAAM,oBAAoB,GAAI,kBAAkB,IAAI,KAAG,QAI5D,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateCoupon.d.ts","sourceRoot":"","sources":["../../src/endpoints/validateCoupon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"validateCoupon.d.ts","sourceRoot":"","sources":["../../src/endpoints/validateCoupon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAG5D,KAAK,IAAI,GAAG;IACV,YAAY,EAAE,4BAA4B,CAAA;CAC3C,CAAA;AAED,eAAO,MAAM,qBAAqB,GAC/B,kBAAkB,IAAI,KAAG,cAiCzB,CAAA;AAkRH,eAAO,MAAM,sBAAsB,GAAI,kBAAkB,IAAI,KAAG,QAI9D,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,5 +4,8 @@ import { createReferralProgramsCollection } from './collections/createReferralPr
|
|
|
4
4
|
import { payloadEcommerceCouponPlugin } from './plugin';
|
|
5
5
|
export { useCouponCode, usePartnerStats, validateCouponCode } from './client/hooks';
|
|
6
6
|
export { createCouponsCollection, createReferralCodesCollection, createReferralProgramsCollection, payloadEcommerceCouponPlugin as payloadEcommerceCoupon, };
|
|
7
|
-
export
|
|
7
|
+
export { getCartTotalWithDiscounts } from './utilities/getCartTotalWithDiscounts';
|
|
8
|
+
export { recordCouponUsageForOrder } from './utilities/recordCouponUsageForOrder';
|
|
9
|
+
export type { AdminGroupConfig, ApplyCouponHook, ApplyCouponResponse, CouponPluginAccess, CouponPluginCollections, CouponPluginOptions, OrderIntegrationConfig, PartnerDashboardConfig, PartnerDashboardData, PartnerStats, ReferralProgramConfig, } from './types';
|
|
10
|
+
export type { CartLike } from './utilities/getCartTotalWithDiscounts';
|
|
8
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAC/E,OAAO,EAAE,6BAA6B,EAAE,MAAM,6CAA6C,CAAA;AAC3F,OAAO,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAA;AACjG,OAAO,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACnF,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,gCAAgC,EAChC,4BAA4B,IAAI,sBAAsB,GACvD,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAC/E,OAAO,EAAE,6BAA6B,EAAE,MAAM,6CAA6C,CAAA;AAC3F,OAAO,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAA;AACjG,OAAO,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACnF,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,gCAAgC,EAChC,4BAA4B,IAAI,sBAAsB,GACvD,CAAA;AACD,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAA;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAA;AAEjF,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,GACtB,MAAM,SAAS,CAAA;AAChB,YAAY,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAA"}
|