@wtree/payload-ecommerce-coupon 3.70.3 → 3.70.5
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 +434 -683
- package/dist/client/hooks.d.ts +13 -1
- package/dist/client/hooks.d.ts.map +1 -1
- package/dist/collections/createCouponsCollection.d.ts.map +1 -1
- package/dist/collections/createReferralCodesCollection.d.ts.map +1 -1
- package/dist/collections/createReferralProgramsCollection.d.ts.map +1 -1
- package/dist/endpoints/applyCoupon.d.ts.map +1 -1
- package/dist/endpoints/partnerStats.d.ts +9 -0
- package/dist/endpoints/partnerStats.d.ts.map +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +537 -281
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +534 -282
- package/dist/index.mjs.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/types.d.ts +91 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utilities/sanitizePluginConfig.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ Production-ready coupon and referral system plugin for **Payload CMS** with seam
|
|
|
11
11
|
### **System Modes**
|
|
12
12
|
- **Coupon Mode** (`enableReferrals: false`) – Traditional discount codes
|
|
13
13
|
- **Referral Mode** (`enableReferrals: true`) – Partner commissions + customer discounts
|
|
14
|
+
- **Hybrid Mode** (`enableReferrals: true` + `referralConfig.allowBothSystems: true`) – Both systems active
|
|
14
15
|
|
|
15
16
|
### **Coupon Mode Features**
|
|
16
17
|
- ✅ **Flexible Discounts** – Percentage or fixed amount discounts
|
|
@@ -22,14 +23,17 @@ Production-ready coupon and referral system plugin for **Payload CMS** with seam
|
|
|
22
23
|
- ✅ **Commission Rules** – Per-product/category commission rates
|
|
23
24
|
- ✅ **Split Configuration** – Configurable partner/customer share ratios
|
|
24
25
|
- ✅ **Partner Tracking** – Commission earnings and referral performance
|
|
25
|
-
- ✅ **Auto-Generated Codes** – Unique referral codes for each
|
|
26
|
+
- ✅ **Auto-Generated Codes** – Unique referral codes for each partner
|
|
27
|
+
- ✅ **Partner Dashboard** – Ready-to-use React components for partner stats
|
|
28
|
+
- ✅ **Single Code Per Cart** – Enforce one code (coupon or referral) per order
|
|
26
29
|
|
|
27
30
|
### **Core Features**
|
|
28
31
|
- ✅ **REST API** – Validate, apply, and track codes
|
|
29
|
-
- ✅ **Frontend Hooks** – `useCouponCode()` for React/Next.js
|
|
32
|
+
- ✅ **Frontend Hooks** – `useCouponCode()`, `usePartnerStats()` for React/Next.js
|
|
30
33
|
- ✅ **Auto-Integration** – Extends carts/orders automatically
|
|
31
34
|
- ✅ **Type-Safe** – Full TypeScript support
|
|
32
|
-
- ✅ **Access Control** – Role-based permissions
|
|
35
|
+
- ✅ **Access Control** – Role-based permissions with partner role support
|
|
36
|
+
- ✅ **Custom Admin Groups** – Separate "Coupons" and "Referrals" categories
|
|
33
37
|
- ✅ **Production-Ready** – Comprehensive testing and error handling
|
|
34
38
|
|
|
35
39
|
## 📦 Installation
|
|
@@ -62,14 +66,38 @@ export default buildConfig({
|
|
|
62
66
|
}),
|
|
63
67
|
payloadEcommerceCoupon({
|
|
64
68
|
enabled: true,
|
|
65
|
-
enableReferrals:
|
|
69
|
+
enableReferrals: true, // Enable referral system
|
|
66
70
|
defaultCurrency: 'USD',
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
|
|
72
|
+
// Referral-specific configuration
|
|
73
|
+
referralConfig: {
|
|
74
|
+
allowBothSystems: false, // Set true to allow both coupons and referrals
|
|
75
|
+
singleCodePerCart: true, // Only one code per order
|
|
76
|
+
defaultPartnerSplit: 70, // 70% to partner
|
|
77
|
+
defaultCustomerSplit: 30, // 30% discount to customer
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// Custom admin panel groups
|
|
81
|
+
adminGroups: {
|
|
82
|
+
couponsGroup: 'Coupons',
|
|
83
|
+
referralsGroup: 'Referrals',
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// Partner dashboard configuration
|
|
87
|
+
partnerDashboard: {
|
|
88
|
+
enabled: true,
|
|
89
|
+
showEarningsSummary: true,
|
|
90
|
+
showReferralPerformance: true,
|
|
91
|
+
showRecentReferrals: true,
|
|
92
|
+
showCommissionBreakdown: true,
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// Access control
|
|
69
96
|
access: {
|
|
70
97
|
canUseCoupons: () => true,
|
|
71
98
|
canUseReferrals: () => true,
|
|
72
|
-
isAdmin: ({ req }) =>
|
|
99
|
+
isAdmin: ({ req }) => req.user?.role === 'admin',
|
|
100
|
+
isPartner: ({ req }) => req.user?.role === 'partner',
|
|
73
101
|
},
|
|
74
102
|
}),
|
|
75
103
|
],
|
|
@@ -85,43 +113,140 @@ npm run payload migrate
|
|
|
85
113
|
```
|
|
86
114
|
|
|
87
115
|
This will create collections for:
|
|
88
|
-
- **Coupons** – Manage discount codes
|
|
89
|
-
- **Referral Programs** – Set up partner commission structures
|
|
90
|
-
- **Referral Codes** – Track generated referral links
|
|
116
|
+
- **Coupons** – Manage discount codes (in "Coupons" group)
|
|
117
|
+
- **Referral Programs** – Set up partner commission structures (in "Referrals" group)
|
|
118
|
+
- **Referral Codes** – Track generated referral links (in "Referrals" group)
|
|
119
|
+
|
|
120
|
+
### 3. Setting Up Partner Role
|
|
121
|
+
|
|
122
|
+
To enable the partner dashboard and role-based access, add a `role` field to your Users collection:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// collections/Users.ts
|
|
126
|
+
import type { CollectionConfig } from 'payload'
|
|
127
|
+
|
|
128
|
+
export const Users: CollectionConfig = {
|
|
129
|
+
slug: 'users',
|
|
130
|
+
auth: true,
|
|
131
|
+
fields: [
|
|
132
|
+
{
|
|
133
|
+
name: 'role',
|
|
134
|
+
type: 'select',
|
|
135
|
+
options: [
|
|
136
|
+
{ label: 'Admin', value: 'admin' },
|
|
137
|
+
{ label: 'Partner', value: 'partner' },
|
|
138
|
+
{ label: 'Customer', value: 'customer' },
|
|
139
|
+
],
|
|
140
|
+
defaultValue: 'customer',
|
|
141
|
+
required: true,
|
|
142
|
+
},
|
|
143
|
+
// Or use multiple roles
|
|
144
|
+
{
|
|
145
|
+
name: 'roles',
|
|
146
|
+
type: 'select',
|
|
147
|
+
hasMany: true,
|
|
148
|
+
options: [
|
|
149
|
+
{ label: 'Admin', value: 'admin' },
|
|
150
|
+
{ label: 'Partner', value: 'partner' },
|
|
151
|
+
{ label: 'Customer', value: 'customer' },
|
|
152
|
+
],
|
|
153
|
+
defaultValue: ['customer'],
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
}
|
|
157
|
+
```
|
|
91
158
|
|
|
92
|
-
|
|
159
|
+
### 4. Frontend Integration
|
|
93
160
|
|
|
94
|
-
|
|
161
|
+
#### Apply Coupon/Referral Code
|
|
95
162
|
|
|
96
163
|
```typescript
|
|
97
164
|
import { useCouponCode } from '@wtree/payload-ecommerce-coupon'
|
|
98
165
|
|
|
99
166
|
function CheckoutComponent() {
|
|
100
|
-
const [
|
|
167
|
+
const [code, setCode] = useState('')
|
|
101
168
|
const [cartId, setCartId] = useState('your-cart-id')
|
|
102
169
|
|
|
103
|
-
const
|
|
170
|
+
const applyCode = async () => {
|
|
104
171
|
const result = await useCouponCode({
|
|
105
|
-
code
|
|
172
|
+
code,
|
|
106
173
|
cartID: cartId,
|
|
107
174
|
})
|
|
108
175
|
|
|
109
176
|
if (result.success) {
|
|
110
|
-
|
|
111
|
-
|
|
177
|
+
if (result.coupon) {
|
|
178
|
+
console.log('Coupon applied! Discount:', result.discount)
|
|
179
|
+
} else if (result.referralCode) {
|
|
180
|
+
console.log('Referral applied!')
|
|
181
|
+
console.log('Your discount:', result.customerDiscount)
|
|
182
|
+
console.log('Partner commission:', result.partnerCommission)
|
|
183
|
+
}
|
|
112
184
|
} else {
|
|
113
|
-
console.error('
|
|
185
|
+
console.error('Error:', result.error)
|
|
114
186
|
}
|
|
115
187
|
}
|
|
116
188
|
|
|
117
189
|
return (
|
|
118
190
|
<div>
|
|
119
191
|
<input
|
|
120
|
-
value={
|
|
121
|
-
onChange={(e) =>
|
|
122
|
-
placeholder="Enter coupon code"
|
|
192
|
+
value={code}
|
|
193
|
+
onChange={(e) => setCode(e.target.value)}
|
|
194
|
+
placeholder="Enter coupon or referral code"
|
|
123
195
|
/>
|
|
124
|
-
<button onClick={
|
|
196
|
+
<button onClick={applyCode}>Apply Code</button>
|
|
197
|
+
</div>
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### Partner Dashboard
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { PartnerDashboard, usePartnerStats } from '@wtree/payload-ecommerce-coupon'
|
|
206
|
+
|
|
207
|
+
// Option 1: Use the pre-built dashboard component
|
|
208
|
+
function PartnerPage() {
|
|
209
|
+
return (
|
|
210
|
+
<PartnerDashboard
|
|
211
|
+
showEarningsSummary={true}
|
|
212
|
+
showReferralPerformance={true}
|
|
213
|
+
showRecentReferrals={true}
|
|
214
|
+
showReferralCodes={true}
|
|
215
|
+
apiEndpoint="/api/referrals/partner-stats"
|
|
216
|
+
/>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Option 2: Build custom dashboard with the hook
|
|
221
|
+
function CustomPartnerDashboard() {
|
|
222
|
+
const [data, setData] = useState(null)
|
|
223
|
+
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
const fetchStats = async () => {
|
|
226
|
+
const result = await usePartnerStats()
|
|
227
|
+
if (result.success) {
|
|
228
|
+
setData(result.data)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
fetchStats()
|
|
232
|
+
}, [])
|
|
233
|
+
|
|
234
|
+
if (!data) return <div>Loading...</div>
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div>
|
|
238
|
+
<h2>Your Earnings</h2>
|
|
239
|
+
<p>Total: ${data.stats.totalEarnings}</p>
|
|
240
|
+
<p>Pending: ${data.stats.pendingEarnings}</p>
|
|
241
|
+
<p>Paid: ${data.stats.paidEarnings}</p>
|
|
242
|
+
|
|
243
|
+
<h2>Your Referral Codes</h2>
|
|
244
|
+
{data.referralCodes.map(code => (
|
|
245
|
+
<div key={code.id}>
|
|
246
|
+
<span>{code.code}</span>
|
|
247
|
+
<span>Uses: {code.usageCount}</span>
|
|
248
|
+
</div>
|
|
249
|
+
))}
|
|
125
250
|
</div>
|
|
126
251
|
)
|
|
127
252
|
}
|
|
@@ -137,9 +262,12 @@ Best for traditional discount campaigns, seasonal sales, and customer loyalty pr
|
|
|
137
262
|
#### **Referral Mode** (`enableReferrals: true`)
|
|
138
263
|
Best for affiliate marketing, partner programs, and customer acquisition through referrals.
|
|
139
264
|
|
|
265
|
+
#### **Hybrid Mode** (`enableReferrals: true` + `allowBothSystems: true`)
|
|
266
|
+
Best when you need both traditional coupons AND partner referrals, but want to enforce only one code per order.
|
|
267
|
+
|
|
140
268
|
### **Setting Up Coupon Mode**
|
|
141
269
|
|
|
142
|
-
1. **Navigate to Admin Panel** → Go to "Coupons" collection
|
|
270
|
+
1. **Navigate to Admin Panel** → Go to "Coupons" collection (under "Coupons" group)
|
|
143
271
|
2. **Create New Coupon**:
|
|
144
272
|
- **Code**: `WELCOME10` (unique identifier)
|
|
145
273
|
- **Type**: `Percentage` or `Fixed Amount`
|
|
@@ -148,26 +276,19 @@ Best for affiliate marketing, partner programs, and customer acquisition through
|
|
|
148
276
|
- **Active From/Until**: Set validity period
|
|
149
277
|
- **Usage Limit**: Maximum uses (optional)
|
|
150
278
|
- **Per Customer Limit**: Uses per customer (optional)
|
|
151
|
-
- **
|
|
152
|
-
|
|
153
|
-
3. **Advanced Conditions**:
|
|
154
|
-
```json
|
|
155
|
-
{
|
|
156
|
-
"minOrderValue": 5000, // $50 minimum
|
|
157
|
-
"maxOrderValue": 100000 // $1000 maximum
|
|
158
|
-
}
|
|
159
|
-
```
|
|
279
|
+
- **Min/Max Order Value**: Order value constraints
|
|
160
280
|
|
|
161
281
|
### **Setting Up Referral Mode**
|
|
162
282
|
|
|
163
|
-
1. **Navigate to Admin Panel** → Go to "Referral Programs"
|
|
283
|
+
1. **Navigate to Admin Panel** → Go to "Referral Programs" (under "Referrals" group)
|
|
164
284
|
2. **Create Referral Program**:
|
|
165
285
|
- **Name**: "Partner Affiliate Program"
|
|
166
286
|
- **Description**: "Earn commissions by referring customers"
|
|
167
287
|
- **Is Active**: Enable/disable program
|
|
168
|
-
- **
|
|
288
|
+
- **Referrer Reward**: Commission for the partner (e.g., 10% of order)
|
|
289
|
+
- **Referee Reward**: Discount for the customer (e.g., 5% off)
|
|
169
290
|
|
|
170
|
-
3. **Configure Commission Rules
|
|
291
|
+
3. **Configure Commission Rules** (Optional - for product-specific rates):
|
|
171
292
|
```json
|
|
172
293
|
{
|
|
173
294
|
"name": "Electronics Category",
|
|
@@ -175,102 +296,47 @@ Best for affiliate marketing, partner programs, and customer acquisition through
|
|
|
175
296
|
"categories": ["electronics"],
|
|
176
297
|
"totalCommission": {
|
|
177
298
|
"type": "percentage",
|
|
178
|
-
"value": 15
|
|
299
|
+
"value": 15
|
|
179
300
|
},
|
|
180
301
|
"split": {
|
|
181
|
-
"partnerPercentage": 70,
|
|
182
|
-
"customerPercentage": 30
|
|
302
|
+
"partnerPercentage": 70,
|
|
303
|
+
"customerPercentage": 30
|
|
183
304
|
}
|
|
184
305
|
}
|
|
185
306
|
```
|
|
186
307
|
|
|
187
|
-
|
|
188
|
-
- **Min Order Value**: Minimum purchase required
|
|
189
|
-
- **Max Referrals Per User**: Limit referrals per user
|
|
190
|
-
- **Referral Code Prefix**: Custom prefix for codes
|
|
191
|
-
|
|
192
|
-
### **Commission Rule Examples**
|
|
193
|
-
|
|
194
|
-
#### **Example 1: Electronics Category**
|
|
195
|
-
- **Total Commission**: 15% of product price
|
|
196
|
-
- **Partner Share**: 70% = 10.5% commission
|
|
197
|
-
- **Customer Discount**: 30% = 4.5% discount
|
|
198
|
-
- **Result**: $100 product = $10.50 partner commission + $4.50 customer discount
|
|
199
|
-
|
|
200
|
-
#### **Example 2: Fixed Commission**
|
|
201
|
-
- **Total Commission**: $25 per product
|
|
202
|
-
- **Partner Share**: 80% = $20 commission
|
|
203
|
-
- **Customer Discount**: 20% = $5 discount
|
|
204
|
-
|
|
205
|
-
#### **Example 3: All Products**
|
|
206
|
-
- **Total Commission**: 10% of order total
|
|
207
|
-
- **Partner Share**: 60% = 6% commission
|
|
208
|
-
- **Customer Discount**: 40% = 4% discount
|
|
209
|
-
|
|
210
|
-
### **Managing Referral Codes**
|
|
211
|
-
|
|
212
|
-
1. **Auto-Generation**: Codes are created automatically when users join
|
|
213
|
-
2. **Manual Creation**: Admin can create codes for specific partners
|
|
214
|
-
3. **Tracking**: Monitor usage, successful referrals, and commission payouts
|
|
215
|
-
|
|
216
|
-
### **Monitoring & Analytics**
|
|
217
|
-
|
|
218
|
-
#### **Coupon Analytics**
|
|
219
|
-
- Total redemptions
|
|
220
|
-
- Revenue impact
|
|
221
|
-
- Customer usage patterns
|
|
222
|
-
- Expiration tracking
|
|
308
|
+
### **Commission Calculation Examples**
|
|
223
309
|
|
|
224
|
-
#### **
|
|
225
|
-
- Total
|
|
226
|
-
-
|
|
227
|
-
-
|
|
228
|
-
- Partner
|
|
229
|
-
|
|
230
|
-
### **Access Control Setup**
|
|
231
|
-
|
|
232
|
-
```typescript
|
|
233
|
-
payloadEcommerceCoupon({
|
|
234
|
-
access: {
|
|
235
|
-
// Who can use coupons/referrals
|
|
236
|
-
canUseCoupons: ({ req }) => Boolean(req.user),
|
|
237
|
-
canUseReferrals: ({ req }) => req.user?.subscription === 'premium',
|
|
238
|
-
|
|
239
|
-
// Who can create/manage
|
|
240
|
-
isAdmin: ({ req }) => req.user?.role === 'admin',
|
|
241
|
-
},
|
|
242
|
-
})
|
|
243
|
-
```
|
|
310
|
+
#### **Example 1: Simple Percentage Split**
|
|
311
|
+
- **Order Total**: $100
|
|
312
|
+
- **Referrer Reward**: 10% (percentage)
|
|
313
|
+
- **Referee Reward**: 5% (percentage)
|
|
314
|
+
- **Result**: Partner earns $10, Customer saves $5
|
|
244
315
|
|
|
245
|
-
|
|
316
|
+
#### **Example 2: Commission Rules with Split**
|
|
317
|
+
- **Order Total**: $100 (Electronics category)
|
|
318
|
+
- **Total Commission**: 15% = $15
|
|
319
|
+
- **Partner Share**: 70% of $15 = $10.50
|
|
320
|
+
- **Customer Discount**: 30% of $15 = $4.50
|
|
246
321
|
|
|
247
|
-
#### **
|
|
248
|
-
-
|
|
249
|
-
-
|
|
250
|
-
-
|
|
251
|
-
- Use per-customer limits to prevent abuse
|
|
322
|
+
#### **Example 3: Fixed Commission**
|
|
323
|
+
- **Referrer Reward**: $20 (fixed)
|
|
324
|
+
- **Referee Reward**: $10 (fixed)
|
|
325
|
+
- **Result**: Partner earns $20, Customer saves $10 (regardless of order value)
|
|
252
326
|
|
|
253
|
-
|
|
254
|
-
- Start with generous splits to attract partners
|
|
255
|
-
- Set clear program rules and conditions
|
|
256
|
-
- Monitor partner performance regularly
|
|
257
|
-
- Provide transparent commission tracking
|
|
327
|
+
### **Managing Partners**
|
|
258
328
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
- Document your specific business rules
|
|
329
|
+
1. **Create Partner Account**: Set user role to "partner"
|
|
330
|
+
2. **Generate Referral Code**: Partners can create codes in "Referral Codes" collection
|
|
331
|
+
3. **Track Performance**: View usage count, earnings, and successful referrals
|
|
332
|
+
4. **Payout Management**: Track pending vs paid earnings
|
|
264
333
|
|
|
265
334
|
## 🌐 REST API Endpoints
|
|
266
335
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
### **Coupon Mode Endpoints**
|
|
336
|
+
### **Coupon/Referral Endpoints**
|
|
270
337
|
|
|
271
338
|
#### POST /api/coupons/validate
|
|
272
|
-
|
|
273
|
-
Validate a coupon code without applying it.
|
|
339
|
+
Validate a code without applying it.
|
|
274
340
|
|
|
275
341
|
```bash
|
|
276
342
|
curl -X POST http://localhost:3000/api/coupons/validate \
|
|
@@ -278,24 +344,8 @@ curl -X POST http://localhost:3000/api/coupons/validate \
|
|
|
278
344
|
-d '{"code": "WELCOME10", "cartValue": 5000}'
|
|
279
345
|
```
|
|
280
346
|
|
|
281
|
-
**Response:**
|
|
282
|
-
```json
|
|
283
|
-
{
|
|
284
|
-
"success": true,
|
|
285
|
-
"coupon": {
|
|
286
|
-
"code": "WELCOME10",
|
|
287
|
-
"type": "percentage",
|
|
288
|
-
"value": 10,
|
|
289
|
-
"description": "Welcome discount"
|
|
290
|
-
},
|
|
291
|
-
"discount": 500,
|
|
292
|
-
"currency": "USD"
|
|
293
|
-
}
|
|
294
|
-
```
|
|
295
|
-
|
|
296
347
|
#### POST /api/coupons/apply
|
|
297
|
-
|
|
298
|
-
Apply a coupon to a cart.
|
|
348
|
+
Apply a code to a cart.
|
|
299
349
|
|
|
300
350
|
```bash
|
|
301
351
|
curl -X POST http://localhost:3000/api/coupons/apply \
|
|
@@ -303,652 +353,352 @@ curl -X POST http://localhost:3000/api/coupons/apply \
|
|
|
303
353
|
-d '{"code": "WELCOME10", "cartID": "cart-123"}'
|
|
304
354
|
```
|
|
305
355
|
|
|
306
|
-
**
|
|
307
|
-
```json
|
|
308
|
-
{
|
|
309
|
-
"success": true,
|
|
310
|
-
"message": "Coupon applied successfully",
|
|
311
|
-
"coupon": {
|
|
312
|
-
"code": "WELCOME10",
|
|
313
|
-
"type": "percentage",
|
|
314
|
-
"value": 10
|
|
315
|
-
},
|
|
316
|
-
"discount": 500,
|
|
317
|
-
"currency": "USD"
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### **Referral Mode Endpoints**
|
|
356
|
+
### **Partner Stats Endpoint**
|
|
322
357
|
|
|
323
|
-
####
|
|
324
|
-
|
|
325
|
-
Validate a referral code and preview commission/discount.
|
|
358
|
+
#### GET /api/referrals/partner-stats
|
|
359
|
+
Get partner dashboard data (requires authentication).
|
|
326
360
|
|
|
327
361
|
```bash
|
|
328
|
-
curl -X
|
|
329
|
-
-H "
|
|
330
|
-
-d '{"code": "REF-ABC123", "cartID": "cart-123"}'
|
|
362
|
+
curl -X GET http://localhost:3000/api/referrals/partner-stats \
|
|
363
|
+
-H "Cookie: payload-token=your-auth-token"
|
|
331
364
|
```
|
|
332
365
|
|
|
333
366
|
**Response:**
|
|
334
367
|
```json
|
|
335
368
|
{
|
|
336
369
|
"success": true,
|
|
337
|
-
"
|
|
338
|
-
"
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
-d '{"code": "REF-ABC123", "cartID": "cart-123"}'
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
**Response:**
|
|
358
|
-
```json
|
|
359
|
-
{
|
|
360
|
-
"success": true,
|
|
361
|
-
"message": "Referral code applied successfully",
|
|
362
|
-
"referralCode": {
|
|
363
|
-
"code": "REF-ABC123"
|
|
370
|
+
"data": {
|
|
371
|
+
"stats": {
|
|
372
|
+
"totalEarnings": 1250.50,
|
|
373
|
+
"pendingEarnings": 350.00,
|
|
374
|
+
"paidEarnings": 900.50,
|
|
375
|
+
"totalReferrals": 45,
|
|
376
|
+
"successfulReferrals": 38,
|
|
377
|
+
"conversionRate": 84.44,
|
|
378
|
+
"recentReferrals": [...],
|
|
379
|
+
"monthlyEarnings": [...]
|
|
380
|
+
},
|
|
381
|
+
"referralCodes": [...],
|
|
382
|
+
"program": {
|
|
383
|
+
"name": "Partner Program",
|
|
384
|
+
"commissionRate": 10,
|
|
385
|
+
"customerDiscount": 5
|
|
386
|
+
}
|
|
364
387
|
},
|
|
365
|
-
"partnerCommission": 36.75,
|
|
366
|
-
"customerDiscount": 15.50,
|
|
367
388
|
"currency": "USD"
|
|
368
389
|
}
|
|
369
390
|
```
|
|
370
391
|
|
|
371
|
-
### **Error Responses**
|
|
372
|
-
|
|
373
|
-
All endpoints return consistent error formats:
|
|
374
|
-
|
|
375
|
-
```json
|
|
376
|
-
{
|
|
377
|
-
"success": false,
|
|
378
|
-
"error": "Invalid coupon code"
|
|
379
|
-
}
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
```json
|
|
383
|
-
{
|
|
384
|
-
"success": false,
|
|
385
|
-
"error": "Referral code has expired"
|
|
386
|
-
}
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
```json
|
|
390
|
-
{
|
|
391
|
-
"success": false,
|
|
392
|
-
"error": "Coupon already applied to this cart"
|
|
393
|
-
}
|
|
394
|
-
```
|
|
395
|
-
|
|
396
392
|
## ⚙️ Configuration
|
|
397
393
|
|
|
398
394
|
### **Core Options**
|
|
399
395
|
|
|
400
396
|
```typescript
|
|
401
397
|
export type CouponPluginOptions = {
|
|
402
|
-
enabled?: boolean // Enable/disable the
|
|
403
|
-
enableReferrals?: boolean //
|
|
404
|
-
allowStackWithOtherCoupons?: boolean // Allow multiple coupons (
|
|
405
|
-
defaultCurrency?: string // Currency
|
|
406
|
-
autoIntegrate?: boolean // Auto-extend carts/orders
|
|
398
|
+
enabled?: boolean // Enable/disable the plugin (default: true)
|
|
399
|
+
enableReferrals?: boolean // Enable referral system (default: false)
|
|
400
|
+
allowStackWithOtherCoupons?: boolean // Allow multiple coupons (default: false)
|
|
401
|
+
defaultCurrency?: string // Currency code (default: 'USD')
|
|
402
|
+
autoIntegrate?: boolean // Auto-extend carts/orders (default: true)
|
|
403
|
+
|
|
407
404
|
collections?: {
|
|
408
|
-
couponsSlug?: string //
|
|
409
|
-
referralProgramsSlug?: string //
|
|
410
|
-
referralCodesSlug?: string //
|
|
411
|
-
|
|
405
|
+
couponsSlug?: string // Default: 'coupons'
|
|
406
|
+
referralProgramsSlug?: string // Default: 'referral-programs'
|
|
407
|
+
referralCodesSlug?: string // Default: 'referral-codes'
|
|
408
|
+
|
|
409
|
+
/** Override the default coupons collection configuration */
|
|
410
|
+
couponsCollectionOverride?: (params: { defaultCollection: any }) => any | Promise<any>
|
|
411
|
+
|
|
412
|
+
/** Override the default referral programs collection configuration */
|
|
413
|
+
referralProgramsCollectionOverride?: (params: { defaultCollection: any }) => any | Promise<any>
|
|
414
|
+
|
|
415
|
+
/** Override the default referral codes collection configuration */
|
|
416
|
+
referralCodesCollectionOverride?: (params: { defaultCollection: any }) => any | Promise<any>
|
|
412
417
|
}
|
|
418
|
+
|
|
419
|
+
endpoints?: {
|
|
420
|
+
applyCoupon?: string // Default: '/coupons/apply'
|
|
421
|
+
validateCoupon?: string // Default: '/coupons/validate'
|
|
422
|
+
partnerStats?: string // Default: '/referrals/partner-stats'
|
|
423
|
+
}
|
|
424
|
+
|
|
413
425
|
access?: {
|
|
414
|
-
canUseCoupons?: Access // Who can use coupons
|
|
415
|
-
canUseReferrals?: Access // Who can use referrals
|
|
416
|
-
isAdmin?: Access // Who can manage codes/programs
|
|
426
|
+
canUseCoupons?: Access // Who can use coupons
|
|
427
|
+
canUseReferrals?: Access // Who can use referrals
|
|
428
|
+
isAdmin?: Access // Who can manage codes/programs
|
|
429
|
+
isPartner?: Access // Who has partner access
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
referralConfig?: {
|
|
433
|
+
allowBothSystems?: boolean // Allow coupons + referrals (default: false)
|
|
434
|
+
singleCodePerCart?: boolean // One code per order (default: true)
|
|
435
|
+
defaultPartnerSplit?: number // Default partner % (default: 70)
|
|
436
|
+
defaultCustomerSplit?: number // Default customer % (default: 30)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
adminGroups?: {
|
|
440
|
+
couponsGroup?: string // Admin group for coupons (default: 'Coupons')
|
|
441
|
+
referralsGroup?: string // Admin group for referrals (default: 'Referrals')
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
partnerDashboard?: {
|
|
445
|
+
enabled?: boolean // Enable dashboard (default: true)
|
|
446
|
+
showEarningsSummary?: boolean // Show earnings widget (default: true)
|
|
447
|
+
showReferralPerformance?: boolean // Show performance widget (default: true)
|
|
448
|
+
showRecentReferrals?: boolean // Show recent referrals (default: true)
|
|
449
|
+
showCommissionBreakdown?: boolean // Show breakdown (default: true)
|
|
417
450
|
}
|
|
418
451
|
}
|
|
419
452
|
```
|
|
420
453
|
|
|
421
|
-
### **
|
|
422
|
-
|
|
423
|
-
#### **Coupon Mode** (`enableReferrals: false`)
|
|
424
|
-
```typescript
|
|
425
|
-
payloadEcommerceCoupon({
|
|
426
|
-
enableReferrals: false, // Traditional coupon system
|
|
427
|
-
// Creates: coupons collection
|
|
428
|
-
// Features: percentage/fixed discounts, usage limits, conditions
|
|
429
|
-
})
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
#### **Referral Mode** (`enableReferrals: true`)
|
|
433
|
-
```typescript
|
|
434
|
-
payloadEcommerceCoupon({
|
|
435
|
-
enableReferrals: true, // Partner referral system
|
|
436
|
-
// Creates: referral-programs, referral-codes collections
|
|
437
|
-
// Features: commission rules, partner/customer splits, referral tracking
|
|
438
|
-
})
|
|
439
|
-
```
|
|
454
|
+
### **Collection Overrides**
|
|
440
455
|
|
|
441
|
-
|
|
456
|
+
You can override the default collection configurations to customize fields, hooks, or other collection settings. This allows you to extend or modify the plugin's behavior without forking the code.
|
|
442
457
|
|
|
443
|
-
#### **Basic Authentication**
|
|
444
458
|
```typescript
|
|
445
459
|
payloadEcommerceCoupon({
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
460
|
+
collections: {
|
|
461
|
+
// Override coupons collection
|
|
462
|
+
couponsCollectionOverride: async ({ defaultCollection }) => {
|
|
463
|
+
return {
|
|
464
|
+
...defaultCollection,
|
|
465
|
+
fields: [
|
|
466
|
+
...defaultCollection.fields,
|
|
467
|
+
// Add custom field to coupons
|
|
468
|
+
{
|
|
469
|
+
name: 'customField',
|
|
470
|
+
type: 'text',
|
|
471
|
+
label: 'Custom Field',
|
|
472
|
+
},
|
|
473
|
+
],
|
|
474
|
+
hooks: {
|
|
475
|
+
...defaultCollection.hooks,
|
|
476
|
+
// Add custom hook
|
|
477
|
+
beforeChange: [
|
|
478
|
+
...(defaultCollection.hooks?.beforeChange || []),
|
|
479
|
+
async ({ data, req, operation }) => {
|
|
480
|
+
// Custom beforeChange logic
|
|
481
|
+
return data
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
},
|
|
485
|
+
}
|
|
472
486
|
},
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
return
|
|
487
|
+
|
|
488
|
+
// Override referral programs collection
|
|
489
|
+
referralProgramsCollectionOverride: ({ defaultCollection }) => {
|
|
490
|
+
return {
|
|
491
|
+
...defaultCollection,
|
|
492
|
+
admin: {
|
|
493
|
+
...defaultCollection.admin,
|
|
494
|
+
defaultColumns: ['name', 'isActive', 'totalReferrals'],
|
|
495
|
+
},
|
|
496
|
+
}
|
|
477
497
|
},
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
498
|
+
|
|
499
|
+
// Override referral codes collection
|
|
500
|
+
referralCodesCollectionOverride: async ({ defaultCollection }) => {
|
|
501
|
+
return {
|
|
502
|
+
...defaultCollection,
|
|
503
|
+
fields: [
|
|
504
|
+
...defaultCollection.fields,
|
|
505
|
+
{
|
|
506
|
+
name: 'customCodeField',
|
|
507
|
+
type: 'select',
|
|
508
|
+
label: 'Custom Code Type',
|
|
509
|
+
options: ['standard', 'premium'],
|
|
510
|
+
defaultValue: 'standard',
|
|
511
|
+
},
|
|
512
|
+
],
|
|
513
|
+
}
|
|
481
514
|
},
|
|
482
515
|
},
|
|
483
516
|
})
|
|
484
517
|
```
|
|
485
518
|
|
|
486
|
-
### **
|
|
487
|
-
|
|
488
|
-
Avoid slug conflicts with existing collections:
|
|
489
|
-
|
|
490
|
-
```typescript
|
|
491
|
-
payloadEcommerceCoupon({
|
|
492
|
-
collections: {
|
|
493
|
-
couponsSlug: 'discount-codes', // Instead of 'coupons'
|
|
494
|
-
referralProgramsSlug: 'affiliate-programs', // Instead of 'referral-programs'
|
|
495
|
-
referralCodesSlug: 'promo-codes', // Instead of 'referral-codes'
|
|
496
|
-
},
|
|
497
|
-
})
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
### **Advanced Configuration Examples**
|
|
501
|
-
|
|
502
|
-
#### **Multi-Tenant Setup**
|
|
503
|
-
```typescript
|
|
504
|
-
payloadEcommerceCoupon({
|
|
505
|
-
collections: {
|
|
506
|
-
couponsSlug: 'tenant-a-coupons',
|
|
507
|
-
referralProgramsSlug: 'tenant-a-referrals',
|
|
508
|
-
},
|
|
509
|
-
access: {
|
|
510
|
-
canUseCoupons: ({ req }) => req.user?.tenantId === 'tenant-a',
|
|
511
|
-
canUseReferrals: ({ req }) => req.user?.tenantId === 'tenant-a',
|
|
512
|
-
isAdmin: ({ req }) => req.user?.role === 'admin' && req.user?.tenantId === 'tenant-a',
|
|
513
|
-
},
|
|
514
|
-
})
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
#### **Development vs Production**
|
|
518
|
-
```typescript
|
|
519
|
-
const isProduction = process.env.NODE_ENV === 'production'
|
|
520
|
-
|
|
521
|
-
payloadEcommerceCoupon({
|
|
522
|
-
enabled: isProduction, // Disable in development
|
|
523
|
-
access: {
|
|
524
|
-
isAdmin: ({ req }) => isProduction ? req.user?.role === 'admin' : true, // Allow all in dev
|
|
525
|
-
},
|
|
526
|
-
})
|
|
527
|
-
```
|
|
519
|
+
### **Access Control Examples**
|
|
528
520
|
|
|
529
521
|
```typescript
|
|
530
522
|
payloadEcommerceCoupon({
|
|
531
523
|
access: {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
canUseReferrals: ({ req }) =>
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
},
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
524
|
+
// Anyone can use coupons
|
|
525
|
+
canUseCoupons: () => true,
|
|
526
|
+
|
|
527
|
+
// Only authenticated users can use referrals
|
|
528
|
+
canUseReferrals: ({ req }) => Boolean(req.user),
|
|
529
|
+
|
|
530
|
+
// Only admins can manage
|
|
531
|
+
isAdmin: ({ req }) => req.user?.role === 'admin',
|
|
532
|
+
|
|
533
|
+
// Partner role check (supports both single role and array)
|
|
534
|
+
isPartner: ({ req }) => {
|
|
535
|
+
const user = req.user
|
|
536
|
+
if (!user) return false
|
|
537
|
+
if (user.role === 'partner') return true
|
|
538
|
+
if (Array.isArray(user.roles) && user.roles.includes('partner')) return true
|
|
539
|
+
return false
|
|
543
540
|
},
|
|
544
541
|
},
|
|
545
542
|
})
|
|
546
543
|
```
|
|
547
544
|
|
|
548
|
-
|
|
545
|
+
## 📦 API Reference
|
|
549
546
|
|
|
550
|
-
|
|
547
|
+
### **Exported Functions**
|
|
551
548
|
|
|
552
549
|
```typescript
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
550
|
+
import {
|
|
551
|
+
payloadEcommerceCoupon,
|
|
552
|
+
|
|
553
|
+
// Collection creation functions
|
|
554
|
+
createCouponsCollection,
|
|
555
|
+
createReferralCodesCollection,
|
|
556
|
+
createReferralProgramsCollection,
|
|
557
|
+
|
|
558
|
+
// Frontend hooks
|
|
559
|
+
useCouponCode,
|
|
560
|
+
validateCouponCode,
|
|
561
|
+
usePartnerStats,
|
|
562
|
+
|
|
563
|
+
// Dashboard components
|
|
564
|
+
PartnerDashboard,
|
|
565
|
+
EarningsSummary,
|
|
566
|
+
ReferralPerformance,
|
|
567
|
+
RecentReferrals,
|
|
568
|
+
ReferralCodes,
|
|
569
|
+
} from '@wtree/payload-ecommerce-coupon'
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### **Collection Creation Functions**
|
|
573
|
+
|
|
574
|
+
You can use the collection creation functions directly in your Payload config to customize collections before they're added to the config.
|
|
565
575
|
|
|
566
|
-
#### **Basic Coupon Store**
|
|
567
576
|
```typescript
|
|
568
|
-
|
|
569
|
-
import {
|
|
577
|
+
import { buildConfig } from 'payload'
|
|
578
|
+
import { ecommercePlugin } from '@payloadcms/plugin-ecommerce'
|
|
579
|
+
import { payloadEcommerceCoupon, createCouponsCollection } from '@wtree/payload-ecommerce-coupon'
|
|
570
580
|
|
|
571
581
|
export default buildConfig({
|
|
572
|
-
collections: [/* your collections */],
|
|
573
582
|
plugins: [
|
|
574
|
-
ecommercePlugin({
|
|
583
|
+
ecommercePlugin({
|
|
584
|
+
// your ecommerce configuration
|
|
585
|
+
}),
|
|
575
586
|
payloadEcommerceCoupon({
|
|
576
|
-
|
|
577
|
-
defaultCurrency: 'USD',
|
|
578
|
-
access: {
|
|
579
|
-
canUseCoupons: ({ req }) => Boolean(req.user),
|
|
580
|
-
isAdmin: ({ req }) => req.user?.role === 'admin',
|
|
581
|
-
},
|
|
587
|
+
// plugin configuration
|
|
582
588
|
}),
|
|
583
589
|
],
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
```typescript
|
|
589
|
-
// payload.config.ts
|
|
590
|
-
export default buildConfig({
|
|
591
|
-
collections: [/* your collections */],
|
|
592
|
-
plugins: [
|
|
593
|
-
ecommercePlugin({ /* config */ }),
|
|
594
|
-
payloadEcommerceCoupon({
|
|
595
|
-
enableReferrals: true, // Referral mode
|
|
590
|
+
collections: [
|
|
591
|
+
// You can also create and customize collections directly
|
|
592
|
+
createCouponsCollection({
|
|
593
|
+
enabled: true,
|
|
596
594
|
defaultCurrency: 'USD',
|
|
597
|
-
access: {
|
|
598
|
-
canUseReferrals: ({ req }) => req.user?.subscription === 'premium',
|
|
599
|
-
isAdmin: ({ req }) => req.user?.role === 'admin',
|
|
600
|
-
},
|
|
601
595
|
}),
|
|
602
596
|
],
|
|
603
597
|
})
|
|
604
598
|
```
|
|
605
599
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
#### **Tiered Commission Structure**
|
|
609
|
-
```json
|
|
610
|
-
// Referral Program Commission Rules
|
|
611
|
-
[
|
|
612
|
-
{
|
|
613
|
-
"name": "High-Value Electronics",
|
|
614
|
-
"appliesTo": "categories",
|
|
615
|
-
"categories": ["laptops", "smartphones"],
|
|
616
|
-
"totalCommission": { "type": "percentage", "value": 20 },
|
|
617
|
-
"split": { "partnerPercentage": 80, "customerPercentage": 20 }
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
"name": "Accessories",
|
|
621
|
-
"appliesTo": "categories",
|
|
622
|
-
"categories": ["cases", "chargers"],
|
|
623
|
-
"totalCommission": { "type": "percentage", "value": 10 },
|
|
624
|
-
"split": { "partnerPercentage": 70, "customerPercentage": 30 }
|
|
625
|
-
},
|
|
626
|
-
{
|
|
627
|
-
"name": "Default Rate",
|
|
628
|
-
"appliesTo": "all",
|
|
629
|
-
"totalCommission": { "type": "percentage", "value": 5 },
|
|
630
|
-
"split": { "partnerPercentage": 60, "customerPercentage": 40 }
|
|
631
|
-
}
|
|
632
|
-
]
|
|
633
|
-
```
|
|
600
|
+
## 🎨 Partner Dashboard Components
|
|
634
601
|
|
|
635
|
-
|
|
636
|
-
```json
|
|
637
|
-
[
|
|
638
|
-
{
|
|
639
|
-
"name": "Premium Products",
|
|
640
|
-
"appliesTo": "products",
|
|
641
|
-
"products": ["premium-laptop", "gaming-pc"],
|
|
642
|
-
"totalCommission": { "type": "fixed", "value": 50 },
|
|
643
|
-
"split": { "partnerPercentage": 75, "customerPercentage": 25 }
|
|
644
|
-
}
|
|
645
|
-
]
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
### **Frontend Integration Examples**
|
|
649
|
-
|
|
650
|
-
#### **React Checkout Component**
|
|
651
|
-
```tsx
|
|
652
|
-
import { useCouponCode } from '@wtree/payload-ecommerce-coupon'
|
|
653
|
-
import { useState } from 'react'
|
|
654
|
-
|
|
655
|
-
function Checkout({ cartId, total }: { cartId: string, total: number }) {
|
|
656
|
-
const [code, setCode] = useState('')
|
|
657
|
-
const [discount, setDiscount] = useState(0)
|
|
658
|
-
const [loading, setLoading] = useState(false)
|
|
659
|
-
|
|
660
|
-
const applyCode = async () => {
|
|
661
|
-
setLoading(true)
|
|
662
|
-
try {
|
|
663
|
-
const result = await useCouponCode({
|
|
664
|
-
code,
|
|
665
|
-
cartID: cartId,
|
|
666
|
-
})
|
|
667
|
-
|
|
668
|
-
if (result.success) {
|
|
669
|
-
setDiscount(result.discount || 0)
|
|
670
|
-
alert(`Applied successfully! Discount: $${result.discount}`)
|
|
671
|
-
} else {
|
|
672
|
-
alert(`Error: ${result.error}`)
|
|
673
|
-
}
|
|
674
|
-
} catch (error) {
|
|
675
|
-
alert('Failed to apply code')
|
|
676
|
-
} finally {
|
|
677
|
-
setLoading(false)
|
|
678
|
-
}
|
|
679
|
-
}
|
|
602
|
+
The plugin provides ready-to-use React components for building partner dashboards:
|
|
680
603
|
|
|
604
|
+
```typescript
|
|
605
|
+
import {
|
|
606
|
+
PartnerDashboard, // Complete dashboard
|
|
607
|
+
EarningsSummary, // Earnings widget
|
|
608
|
+
ReferralPerformance, // Performance metrics
|
|
609
|
+
RecentReferrals, // Recent referrals table
|
|
610
|
+
ReferralCodes, // Referral codes list
|
|
611
|
+
} from '@wtree/payload-ecommerce-coupon'
|
|
612
|
+
|
|
613
|
+
// Use individual components
|
|
614
|
+
function CustomDashboard({ stats, currency }) {
|
|
681
615
|
return (
|
|
682
|
-
<div
|
|
683
|
-
<
|
|
684
|
-
|
|
685
|
-
value={code}
|
|
686
|
-
onChange={(e) => setCode(e.target.value)}
|
|
687
|
-
placeholder="Enter coupon or referral code"
|
|
688
|
-
disabled={loading}
|
|
689
|
-
/>
|
|
690
|
-
<button onClick={applyCode} disabled={loading}>
|
|
691
|
-
{loading ? 'Applying...' : 'Apply'}
|
|
692
|
-
</button>
|
|
693
|
-
</div>
|
|
694
|
-
|
|
695
|
-
<div className="totals">
|
|
696
|
-
<div>Subtotal: ${total}</div>
|
|
697
|
-
<div>Discount: -${discount}</div>
|
|
698
|
-
<div>Total: ${total - discount}</div>
|
|
699
|
-
</div>
|
|
616
|
+
<div>
|
|
617
|
+
<EarningsSummary stats={stats} currency={currency} />
|
|
618
|
+
<ReferralPerformance stats={stats} />
|
|
700
619
|
</div>
|
|
701
620
|
)
|
|
702
621
|
}
|
|
703
622
|
```
|
|
704
623
|
|
|
705
|
-
|
|
706
|
-
```typescript
|
|
707
|
-
// pages/api/apply-code.ts
|
|
708
|
-
import { useCouponCode } from '@wtree/payload-ecommerce-coupon'
|
|
624
|
+
### **Styling**
|
|
709
625
|
|
|
710
|
-
|
|
711
|
-
if (req.method !== 'POST') {
|
|
712
|
-
return res.status(405).json({ error: 'Method not allowed' })
|
|
713
|
-
}
|
|
626
|
+
Import the default styles or customize:
|
|
714
627
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
const result = await useCouponCode({
|
|
719
|
-
code,
|
|
720
|
-
cartID: cartId,
|
|
721
|
-
})
|
|
628
|
+
```css
|
|
629
|
+
/* Import default styles */
|
|
630
|
+
@import '@wtree/payload-ecommerce-coupon/styles.css';
|
|
722
631
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
} catch (error) {
|
|
729
|
-
return res.status(500).json({ error: 'Internal server error' })
|
|
730
|
-
}
|
|
632
|
+
/* Or customize with CSS variables */
|
|
633
|
+
.partner-dashboard {
|
|
634
|
+
--primary-color: #3b82f6;
|
|
635
|
+
--success-color: #059669;
|
|
636
|
+
--warning-color: #d97706;
|
|
731
637
|
}
|
|
732
638
|
```
|
|
733
639
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
#### **Bulk Coupon Creation**
|
|
737
|
-
```typescript
|
|
738
|
-
// Admin script to create multiple coupons
|
|
739
|
-
const coupons = [
|
|
740
|
-
{ code: 'WELCOME10', type: 'percentage', value: 10 },
|
|
741
|
-
{ code: 'SAVE20', type: 'percentage', value: 20 },
|
|
742
|
-
{ code: 'FLAT50', type: 'fixed', value: 50 },
|
|
743
|
-
]
|
|
744
|
-
|
|
745
|
-
for (const coupon of coupons) {
|
|
746
|
-
await payload.create({
|
|
747
|
-
collection: 'coupons',
|
|
748
|
-
data: {
|
|
749
|
-
...coupon,
|
|
750
|
-
activeFrom: new Date(),
|
|
751
|
-
activeUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days
|
|
752
|
-
},
|
|
753
|
-
})
|
|
754
|
-
}
|
|
755
|
-
```
|
|
756
|
-
|
|
757
|
-
#### **Referral Program Setup**
|
|
758
|
-
```typescript
|
|
759
|
-
// Create a complete referral program
|
|
760
|
-
const program = await payload.create({
|
|
761
|
-
collection: 'referral-programs',
|
|
762
|
-
data: {
|
|
763
|
-
name: 'Partner Program 2024',
|
|
764
|
-
description: 'Earn commissions by referring customers',
|
|
765
|
-
isActive: true,
|
|
766
|
-
commissionRules: [
|
|
767
|
-
{
|
|
768
|
-
name: 'Electronics',
|
|
769
|
-
appliesTo: 'categories',
|
|
770
|
-
categories: ['electronics'],
|
|
771
|
-
totalCommission: { type: 'percentage', value: 15 },
|
|
772
|
-
split: { partnerPercentage: 70, customerPercentage: 30 },
|
|
773
|
-
},
|
|
774
|
-
],
|
|
775
|
-
},
|
|
776
|
-
})
|
|
777
|
-
```
|
|
778
|
-
|
|
779
|
-
## �🔧 Troubleshooting
|
|
640
|
+
## 🔧 Troubleshooting
|
|
780
641
|
|
|
781
642
|
### **Common Issues**
|
|
782
643
|
|
|
783
|
-
#### **"
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
#### **Cart Integration Not Working**
|
|
826
|
-
**Problem**: Applied coupons/referrals not showing in cart
|
|
827
|
-
**Solution**: Check auto-integration settings
|
|
828
|
-
```typescript
|
|
829
|
-
payloadEcommerceCoupon({
|
|
830
|
-
autoIntegrate: true, // Ensure this is enabled (default)
|
|
831
|
-
})
|
|
832
|
-
```
|
|
833
|
-
|
|
834
|
-
### **Debugging Tips**
|
|
835
|
-
|
|
836
|
-
#### **Enable Debug Logging**
|
|
837
|
-
```typescript
|
|
838
|
-
// Add to your payload.config.ts for debugging
|
|
839
|
-
logger: {
|
|
840
|
-
level: 'debug',
|
|
841
|
-
},
|
|
842
|
-
```
|
|
843
|
-
|
|
844
|
-
#### **Test API Endpoints**
|
|
845
|
-
```bash
|
|
846
|
-
# Test coupon validation
|
|
847
|
-
curl -X POST http://localhost:3000/api/coupons/validate \
|
|
848
|
-
-H "Content-Type: application/json" \
|
|
849
|
-
-d '{"code": "TEST123"}'
|
|
850
|
-
|
|
851
|
-
# Test referral validation
|
|
852
|
-
curl -X POST http://localhost:3000/api/coupons/validate \
|
|
853
|
-
-H "Content-Type: application/json" \
|
|
854
|
-
-d '{"code": "REF-ABC123", "cartID": "cart-123"}'
|
|
855
|
-
```
|
|
856
|
-
|
|
857
|
-
#### **Check Database Collections**
|
|
858
|
-
Verify collections are created correctly:
|
|
859
|
-
- **Coupon Mode**: `coupons` collection
|
|
860
|
-
- **Referral Mode**: `referral-programs`, `referral-codes` collections
|
|
861
|
-
|
|
862
|
-
#### **Validate Configuration**
|
|
863
|
-
```typescript
|
|
864
|
-
// Add console.log to verify config
|
|
865
|
-
const couponConfig = payloadEcommerceCoupon({
|
|
866
|
-
enableReferrals: true,
|
|
867
|
-
// ... other options
|
|
868
|
-
})
|
|
869
|
-
console.log('Coupon plugin config:', couponConfig)
|
|
870
|
-
```
|
|
871
|
-
|
|
872
|
-
### **Performance Considerations**
|
|
873
|
-
|
|
874
|
-
#### **Database Indexes**
|
|
875
|
-
For high-traffic sites, add indexes on frequently queried fields:
|
|
876
|
-
- Coupon codes: `code` field
|
|
877
|
-
- Referral codes: `code` field
|
|
878
|
-
- Usage counts: `usageCount` field
|
|
879
|
-
|
|
880
|
-
#### **Caching Strategy**
|
|
881
|
-
Consider caching for:
|
|
882
|
-
- Frequently used coupon validation
|
|
883
|
-
- Commission rule lookups
|
|
884
|
-
- Product category mappings
|
|
885
|
-
|
|
886
|
-
#### **Rate Limiting**
|
|
887
|
-
Implement rate limiting for API endpoints to prevent abuse:
|
|
888
|
-
```typescript
|
|
889
|
-
// Example: Limit to 10 requests per minute per IP
|
|
890
|
-
const rateLimit = require('express-rate-limit')
|
|
891
|
-
app.use('/api/coupons', rateLimit({
|
|
892
|
-
windowMs: 60 * 1000, // 1 minute
|
|
893
|
-
max: 10
|
|
894
|
-
}))
|
|
895
|
-
```
|
|
644
|
+
#### **"A code has already been applied to this cart"**
|
|
645
|
+
This occurs when `singleCodePerCart: true` and a code is already applied.
|
|
646
|
+
- Solution: Remove the existing code before applying a new one, or set `singleCodePerCart: false`
|
|
647
|
+
|
|
648
|
+
#### **Partner can't see their referral codes**
|
|
649
|
+
- Ensure the user has `role: 'partner'` or `roles: ['partner']`
|
|
650
|
+
- Check the `isPartner` access control function
|
|
651
|
+
|
|
652
|
+
#### **Commission not calculating correctly**
|
|
653
|
+
- Verify commission rules are properly configured
|
|
654
|
+
- Check that products have correct category assignments
|
|
655
|
+
- Ensure cart has valid `subtotal` or `total` field
|
|
656
|
+
|
|
657
|
+
## 📋 Future Features (Roadmap)
|
|
658
|
+
|
|
659
|
+
The following features are planned for future releases:
|
|
660
|
+
|
|
661
|
+
| Feature | Status | Description |
|
|
662
|
+
|---------|--------|-------------|
|
|
663
|
+
| Multi-tier commissions | 🔜 Planned | Support for tiered commission rates based on performance |
|
|
664
|
+
| Automatic payouts | 🔜 Planned | Integration with payment providers for automatic partner payouts |
|
|
665
|
+
| Referral analytics | 🔜 Planned | Advanced analytics and reporting dashboard |
|
|
666
|
+
| Email notifications | 🔜 Planned | Automated emails for referral events |
|
|
667
|
+
| Custom code generation | 🔜 Planned | Allow partners to create custom branded codes |
|
|
668
|
+
| Fraud detection | 🔜 Planned | Automatic detection of suspicious referral patterns |
|
|
669
|
+
| Bulk code import | 🔜 Planned | Import coupons/codes from CSV |
|
|
670
|
+
| A/B testing | 🔜 Planned | Test different commission structures |
|
|
671
|
+
|
|
672
|
+
### **Comparison with Other Solutions**
|
|
673
|
+
|
|
674
|
+
| Feature | This Plugin | ReferralCandy | Refersion | Custom Build |
|
|
675
|
+
|---------|-------------|---------------|-----------|--------------|
|
|
676
|
+
| Payload CMS Integration | ✅ Native | ❌ | ❌ | ⚠️ Manual |
|
|
677
|
+
| Coupon System | ✅ | ❌ | ❌ | ⚠️ Manual |
|
|
678
|
+
| Referral System | ✅ | ✅ | ✅ | ⚠️ Manual |
|
|
679
|
+
| Partner Dashboard | ✅ | ✅ | ✅ | ⚠️ Manual |
|
|
680
|
+
| Commission Rules | ✅ | ⚠️ Limited | ✅ | ⚠️ Manual |
|
|
681
|
+
| Single Code Enforcement | ✅ | ❌ | ❌ | ⚠️ Manual |
|
|
682
|
+
| TypeScript Support | ✅ | ❌ | ❌ | ⚠️ Varies |
|
|
683
|
+
| Self-Hosted | ✅ | ❌ | ❌ | ✅ |
|
|
684
|
+
| Monthly Cost | Free | $49+ | $89+ | Dev Time |
|
|
896
685
|
|
|
897
686
|
## 🧪 Testing
|
|
898
687
|
|
|
899
|
-
### **Running Tests**
|
|
900
|
-
|
|
901
688
|
```bash
|
|
902
689
|
# Run all tests
|
|
903
690
|
npm test
|
|
904
691
|
|
|
905
|
-
# Watch mode
|
|
692
|
+
# Watch mode
|
|
906
693
|
npm run test:watch
|
|
907
694
|
|
|
908
|
-
#
|
|
695
|
+
# Coverage report
|
|
909
696
|
npm run test:coverage
|
|
910
|
-
|
|
911
|
-
# Run specific test file
|
|
912
|
-
npm test -- tests/plugin.test.ts
|
|
913
697
|
```
|
|
914
698
|
|
|
915
|
-
### **Test Coverage**
|
|
916
|
-
|
|
917
|
-
The plugin maintains 80%+ test coverage including:
|
|
918
|
-
- ✅ Plugin initialization and configuration
|
|
919
|
-
- ✅ Collection creation (conditional based on mode)
|
|
920
|
-
- ✅ API endpoint functionality
|
|
921
|
-
- ✅ Access control validation
|
|
922
|
-
- ✅ Commission calculation logic
|
|
923
|
-
- ✅ Error handling scenarios
|
|
924
|
-
|
|
925
|
-
### **Manual Testing Checklist**
|
|
926
|
-
|
|
927
|
-
#### **Coupon Mode Testing**
|
|
928
|
-
- [ ] Create coupon in admin panel
|
|
929
|
-
- [ ] Validate coupon via API
|
|
930
|
-
- [ ] Apply coupon to cart
|
|
931
|
-
- [ ] Verify discount calculation
|
|
932
|
-
- [ ] Test usage limits
|
|
933
|
-
- [ ] Test expiration dates
|
|
934
|
-
|
|
935
|
-
#### **Referral Mode Testing**
|
|
936
|
-
- [ ] Create referral program with commission rules
|
|
937
|
-
- [ ] Generate referral codes
|
|
938
|
-
- [ ] Validate referral codes via API
|
|
939
|
-
- [ ] Apply referral codes to cart
|
|
940
|
-
- [ ] Verify commission and discount split
|
|
941
|
-
- [ ] Test referral tracking
|
|
942
|
-
|
|
943
|
-
#### **Integration Testing**
|
|
944
|
-
- [ ] Cart total updates correctly
|
|
945
|
-
- [ ] Order creation includes applied discounts
|
|
946
|
-
- [ ] Frontend hooks work properly
|
|
947
|
-
- [ ] Access control restrictions work
|
|
948
|
-
|
|
949
699
|
## 📚 Documentation
|
|
950
700
|
|
|
951
|
-
|
|
701
|
+
- [API Reference](./docs/api.md)
|
|
952
702
|
- [Compatibility Matrix](./COMPATIBILITY.md)
|
|
953
703
|
- [Contributing Guide](./CONTRIBUTING.md)
|
|
954
704
|
|
|
@@ -957,6 +707,7 @@ For detailed usage examples and advanced configurations, see the sections above
|
|
|
957
707
|
- **GitHub**: https://github.com/technewwings/payload-ecommerce-coupon
|
|
958
708
|
- **NPM**: https://npmjs.com/package/@wtree/payload-ecommerce-coupon
|
|
959
709
|
- **Payload CMS**: https://payloadcms.com
|
|
710
|
+
- **Payload Dashboard Docs**: https://payloadcms.com/docs/custom-components/dashboard
|
|
960
711
|
|
|
961
712
|
## 📄 License
|
|
962
713
|
|