586 lines
11 KiB
Markdown
586 lines
11 KiB
Markdown
# 🎟️ Complete Coupon System Guide
|
|
|
|
## 📦 What's Included
|
|
|
|
### Backend
|
|
1. **Admin Coupon Controller** - Full CRUD + Statistics
|
|
2. **User Coupon Controller** - Validation & Application
|
|
3. **Coupon Routes** - Both admin and user endpoints
|
|
|
|
### Frontend
|
|
1. **CouponApply Component** - For checkout page
|
|
2. **Available Coupons Modal** - Browse & select coupons
|
|
|
|
---
|
|
|
|
## 🚀 Setup Instructions
|
|
|
|
### Step 1: Backend Setup
|
|
|
|
#### 1.1 Add Controllers
|
|
|
|
Create two controller files:
|
|
|
|
**File 1:** `src/controllers/admin/couponController.js`
|
|
(Use the `couponController_Admin.js` file)
|
|
|
|
**File 2:** `src/controllers/couponController.js`
|
|
(Use the `couponController_User.js` file)
|
|
|
|
#### 1.2 Add Routes
|
|
|
|
Create: `src/routes/couponRoutes.js`
|
|
(Use the `couponRoutes.js` file)
|
|
|
|
#### 1.3 Register Routes in App
|
|
|
|
In your `server.js` or `app.js`:
|
|
|
|
```javascript
|
|
const couponRoutes = require('./routes/couponRoutes');
|
|
|
|
app.use('/api/coupons', couponRoutes);
|
|
```
|
|
|
|
---
|
|
|
|
### Step 2: Frontend Setup
|
|
|
|
#### 2.1 Create Component
|
|
|
|
Create: `src/components/CouponApply.jsx`
|
|
(Use the `CouponApply.jsx` file)
|
|
|
|
#### 2.2 Use in Checkout Page
|
|
|
|
```javascript
|
|
// pages/Checkout.jsx
|
|
|
|
import CouponApply from '../components/CouponApply';
|
|
import { useState } from 'react';
|
|
|
|
const Checkout = () => {
|
|
const [appliedCoupon, setAppliedCoupon] = useState(null);
|
|
const [orderTotal, setOrderTotal] = useState(2599);
|
|
|
|
const handleCouponApplied = (couponData) => {
|
|
setAppliedCoupon(couponData);
|
|
setOrderTotal(couponData.finalAmount);
|
|
};
|
|
|
|
const handleCouponRemoved = () => {
|
|
setAppliedCoupon(null);
|
|
setOrderTotal(2599); // Reset to original amount
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-4xl mx-auto p-6">
|
|
{/* Cart Items */}
|
|
|
|
{/* Coupon Section */}
|
|
<CouponApply
|
|
orderAmount={orderTotal}
|
|
onCouponApplied={handleCouponApplied}
|
|
onCouponRemoved={handleCouponRemoved}
|
|
/>
|
|
|
|
{/* Order Summary */}
|
|
<div className="mt-6 bg-white p-6 rounded-lg">
|
|
<div className="flex justify-between mb-2">
|
|
<span>Subtotal:</span>
|
|
<span>₹2,599</span>
|
|
</div>
|
|
{appliedCoupon && (
|
|
<div className="flex justify-between mb-2 text-green-600">
|
|
<span>Discount ({appliedCoupon.couponCode}):</span>
|
|
<span>-₹{appliedCoupon.discountAmount}</span>
|
|
</div>
|
|
)}
|
|
<div className="flex justify-between text-xl font-bold border-t pt-2">
|
|
<span>Total:</span>
|
|
<span>₹{orderTotal.toLocaleString()}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 📡 API Endpoints
|
|
|
|
### Admin Endpoints
|
|
|
|
#### 1. Get All Coupons
|
|
```http
|
|
GET /api/coupons/admin?page=1&limit=20&isActive=true&type=PERCENTAGE&search=SAVE
|
|
Authorization: Bearer <admin_token>
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status": true,
|
|
"message": "Coupons fetched successfully",
|
|
"data": {
|
|
"coupons": [
|
|
{
|
|
"id": "...",
|
|
"code": "SAVE20",
|
|
"description": "20% off on all items",
|
|
"type": "PERCENTAGE",
|
|
"value": 20,
|
|
"minOrderAmount": 1000,
|
|
"maxUses": 100,
|
|
"usedCount": 25,
|
|
"usagePercentage": 25,
|
|
"isExpired": false,
|
|
"remainingUses": 75,
|
|
"isActive": true,
|
|
"validFrom": "2024-01-01",
|
|
"validUntil": "2024-12-31"
|
|
}
|
|
],
|
|
"pagination": {
|
|
"total": 50,
|
|
"page": 1,
|
|
"limit": 20,
|
|
"pages": 3
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 2. Create Coupon
|
|
```http
|
|
POST /api/coupons/admin
|
|
Authorization: Bearer <admin_token>
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"code": "SAVE20",
|
|
"description": "Get 20% off on orders above ₹1000",
|
|
"type": "PERCENTAGE",
|
|
"value": 20,
|
|
"minOrderAmount": 1000,
|
|
"maxUses": 100,
|
|
"validFrom": "2024-01-01T00:00:00Z",
|
|
"validUntil": "2024-12-31T23:59:59Z",
|
|
"isActive": true
|
|
}
|
|
```
|
|
|
|
**Coupon Types:**
|
|
- `PERCENTAGE` - Percentage discount (e.g., 20%)
|
|
- `FIXED_AMOUNT` - Fixed amount off (e.g., ₹500)
|
|
- `FREE_SHIPPING` - Free shipping
|
|
|
|
#### 3. Update Coupon
|
|
```http
|
|
PUT /api/coupons/admin/:id
|
|
Authorization: Bearer <admin_token>
|
|
|
|
{
|
|
"value": 25,
|
|
"maxUses": 150
|
|
}
|
|
```
|
|
|
|
#### 4. Toggle Coupon Status
|
|
```http
|
|
PATCH /api/coupons/admin/:id/toggle
|
|
Authorization: Bearer <admin_token>
|
|
```
|
|
|
|
#### 5. Get Coupon Statistics
|
|
```http
|
|
GET /api/coupons/admin/stats
|
|
Authorization: Bearer <admin_token>
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status": true,
|
|
"data": {
|
|
"totalCoupons": 50,
|
|
"activeCoupons": 35,
|
|
"expiredCoupons": 10,
|
|
"totalRedemptions": 2450,
|
|
"mostUsedCoupons": [...]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### User Endpoints
|
|
|
|
#### 1. Get Available Coupons
|
|
```http
|
|
GET /api/coupons/available?orderAmount=2599
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status": true,
|
|
"data": [
|
|
{
|
|
"code": "SAVE20",
|
|
"description": "20% off on orders above ₹1000",
|
|
"type": "PERCENTAGE",
|
|
"value": 20,
|
|
"minOrderAmount": 1000,
|
|
"discountPreview": "20% OFF",
|
|
"remainingUses": 75,
|
|
"validUntil": "2024-12-31"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### 2. Validate Coupon
|
|
```http
|
|
POST /api/coupons/validate
|
|
Content-Type: application/json
|
|
|
|
{
|
|
"code": "SAVE20",
|
|
"orderAmount": 2599
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status": true,
|
|
"message": "Coupon applied successfully",
|
|
"data": {
|
|
"couponCode": "SAVE20",
|
|
"couponType": "PERCENTAGE",
|
|
"discountAmount": 519.80,
|
|
"originalAmount": 2599,
|
|
"finalAmount": 2079.20,
|
|
"savings": 519.80,
|
|
"savingsPercentage": 20
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 3. Apply Coupon to Order
|
|
```http
|
|
POST /api/coupons/apply
|
|
Authorization: Bearer <token>
|
|
|
|
{
|
|
"couponCode": "SAVE20",
|
|
"orderId": "order_123"
|
|
}
|
|
```
|
|
|
|
#### 4. Remove Coupon
|
|
```http
|
|
POST /api/coupons/remove
|
|
Authorization: Bearer <token>
|
|
|
|
{
|
|
"orderId": "order_123"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 💡 Usage Examples
|
|
|
|
### Example 1: Admin Creates Percentage Coupon
|
|
```javascript
|
|
const createCoupon = async () => {
|
|
const response = await axios.post(
|
|
'http://localhost:3000/api/coupons/admin',
|
|
{
|
|
code: 'WELCOME10',
|
|
description: 'Get 10% off on your first order',
|
|
type: 'PERCENTAGE',
|
|
value: 10,
|
|
minOrderAmount: 500,
|
|
maxUses: 1000,
|
|
validFrom: new Date(),
|
|
validUntil: new Date('2024-12-31'),
|
|
isActive: true,
|
|
},
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${adminToken}`,
|
|
},
|
|
}
|
|
);
|
|
};
|
|
```
|
|
|
|
### Example 2: Admin Creates Fixed Amount Coupon
|
|
```javascript
|
|
{
|
|
code: 'FLAT500',
|
|
description: 'Flat ₹500 off on orders above ₹2000',
|
|
type: 'FIXED_AMOUNT',
|
|
value: 500,
|
|
minOrderAmount: 2000,
|
|
maxUses: 500,
|
|
validFrom: new Date(),
|
|
validUntil: new Date('2024-12-31'),
|
|
}
|
|
```
|
|
|
|
### Example 3: Admin Creates Free Shipping Coupon
|
|
```javascript
|
|
{
|
|
code: 'FREESHIP',
|
|
description: 'Free shipping on all orders',
|
|
type: 'FREE_SHIPPING',
|
|
value: 0,
|
|
minOrderAmount: null,
|
|
maxUses: null,
|
|
validFrom: new Date(),
|
|
validUntil: new Date('2024-12-31'),
|
|
}
|
|
```
|
|
|
|
### Example 4: Customer Applies Coupon
|
|
```javascript
|
|
// In your checkout component
|
|
const [discount, setDiscount] = useState(0);
|
|
|
|
<CouponApply
|
|
orderAmount={2599}
|
|
onCouponApplied={(data) => {
|
|
setDiscount(data.discountAmount);
|
|
console.log('Saved:', data.savings);
|
|
}}
|
|
onCouponRemoved={() => {
|
|
setDiscount(0);
|
|
}}
|
|
/>
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 Coupon Validation Rules
|
|
|
|
### Automatic Validations
|
|
|
|
1. **Active Status** - Must be active
|
|
2. **Date Range** - Must be within validFrom and validUntil
|
|
3. **Usage Limit** - Must have remaining uses
|
|
4. **Minimum Order** - Order amount must meet minimum requirement
|
|
5. **Code Uniqueness** - Code must be unique (enforced on creation)
|
|
|
|
### Error Messages
|
|
|
|
```
|
|
❌ "This coupon is no longer active"
|
|
❌ "This coupon is not yet valid"
|
|
❌ "This coupon has expired"
|
|
❌ "This coupon has reached its usage limit"
|
|
❌ "Minimum order amount of ₹1000 required"
|
|
❌ "Invalid coupon code"
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Coupon Types Explained
|
|
|
|
### 1. PERCENTAGE Discount
|
|
```
|
|
Order Amount: ₹2,599
|
|
Coupon: 20% OFF
|
|
Discount: ₹519.80
|
|
Final Amount: ₹2,079.20
|
|
```
|
|
|
|
### 2. FIXED_AMOUNT Discount
|
|
```
|
|
Order Amount: ₹2,599
|
|
Coupon: ₹500 OFF
|
|
Discount: ₹500
|
|
Final Amount: ₹2,099
|
|
```
|
|
|
|
### 3. FREE_SHIPPING
|
|
```
|
|
Order Amount: ₹2,599
|
|
Shipping: ₹50
|
|
Coupon: FREE SHIPPING
|
|
Discount: ₹50
|
|
Final Amount: ₹2,549
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Advanced Features
|
|
|
|
### Feature 1: User-Specific Coupons
|
|
|
|
Add a `userId` field to restrict coupons:
|
|
|
|
```prisma
|
|
model Coupon {
|
|
// ... existing fields
|
|
userId String? // Optional: restrict to specific user
|
|
user User? @relation(fields: [userId], references: [id])
|
|
}
|
|
```
|
|
|
|
### Feature 2: Product-Specific Coupons
|
|
|
|
```prisma
|
|
model Coupon {
|
|
// ... existing fields
|
|
applicableCategories String[] // Only for these categories
|
|
applicableProducts String[] // Only for these products
|
|
}
|
|
```
|
|
|
|
### Feature 3: First-Time User Coupons
|
|
|
|
```javascript
|
|
// In validation logic
|
|
const userOrders = await prisma.order.count({
|
|
where: { userId, status: 'DELIVERED' }
|
|
});
|
|
|
|
if (coupon.firstTimeOnly && userOrders > 0) {
|
|
return { isValid: false, message: 'Coupon only for first-time users' };
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ Testing Checklist
|
|
|
|
### Backend Testing
|
|
|
|
- [ ] Create percentage coupon
|
|
- [ ] Create fixed amount coupon
|
|
- [ ] Create free shipping coupon
|
|
- [ ] Validate active coupon
|
|
- [ ] Validate expired coupon
|
|
- [ ] Validate coupon below min order
|
|
- [ ] Validate coupon at usage limit
|
|
- [ ] Apply coupon to order
|
|
- [ ] Remove coupon from order
|
|
- [ ] Toggle coupon status
|
|
- [ ] Get coupon statistics
|
|
|
|
### Frontend Testing
|
|
|
|
- [ ] Apply valid coupon
|
|
- [ ] Apply invalid coupon
|
|
- [ ] View available coupons
|
|
- [ ] Remove applied coupon
|
|
- [ ] See discount in order summary
|
|
- [ ] Responsive design
|
|
- [ ] Loading states
|
|
- [ ] Error handling
|
|
|
|
---
|
|
|
|
## 🎯 Common Use Cases
|
|
|
|
### Use Case 1: Flash Sale
|
|
```javascript
|
|
{
|
|
code: 'FLASH50',
|
|
type: 'PERCENTAGE',
|
|
value: 50,
|
|
validFrom: new Date('2024-12-25T00:00:00'),
|
|
validUntil: new Date('2024-12-25T23:59:59'),
|
|
maxUses: 100
|
|
}
|
|
```
|
|
|
|
### Use Case 2: Minimum Purchase Promotion
|
|
```javascript
|
|
{
|
|
code: 'BUY1000GET200',
|
|
type: 'FIXED_AMOUNT',
|
|
value: 200,
|
|
minOrderAmount: 1000,
|
|
}
|
|
```
|
|
|
|
### Use Case 3: New Customer Welcome
|
|
```javascript
|
|
{
|
|
code: 'WELCOME',
|
|
type: 'PERCENTAGE',
|
|
value: 15,
|
|
description: 'Welcome! Get 15% off on your first order',
|
|
maxUses: null, // Unlimited
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📈 Analytics & Reporting
|
|
|
|
Track coupon performance:
|
|
|
|
```javascript
|
|
// Get most successful coupons
|
|
const topCoupons = await prisma.coupon.findMany({
|
|
orderBy: { usedCount: 'desc' },
|
|
take: 10,
|
|
});
|
|
|
|
// Calculate total savings given
|
|
const totalSavings = await calculateTotalDiscounts();
|
|
|
|
// Conversion rate
|
|
const conversionRate = (usedCount / impressions) * 100;
|
|
```
|
|
|
|
---
|
|
|
|
**Your coupon system is ready! 🎉**
|
|
|
|
Full features:
|
|
✅ Admin CRUD operations
|
|
✅ Smart validation
|
|
✅ Multiple coupon types
|
|
✅ Usage tracking
|
|
✅ Statistics & analytics
|
|
✅ User-friendly UI
|
|
✅ Mobile responsive
|
|
|
|
|
|
|
|
|
|
1. User applies coupon "WELCOME10"
|
|
↓
|
|
2. CouponApply validates coupon
|
|
↓
|
|
3. Coupon data stored in state
|
|
{
|
|
couponCode: "WELCOME10",
|
|
discountAmount: 259.90,
|
|
...
|
|
}
|
|
↓
|
|
4. User clicks "Place Order"
|
|
↓
|
|
5. Frontend sends:
|
|
{
|
|
items: [...],
|
|
couponCode: "WELCOME10" ✅
|
|
}
|
|
↓
|
|
6. Backend validates coupon again
|
|
↓
|
|
7. Creates order in transaction:
|
|
- Creates order
|
|
- Increments coupon.usedCount ✅
|
|
↓
|
|
8. Admin panel shows:
|
|
"usedCount": 1 ✅ |