first commit
This commit is contained in:
586
CouponGuide.md
Normal file
586
CouponGuide.md
Normal file
@@ -0,0 +1,586 @@
|
||||
# 🎟️ 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 ✅
|
||||
Reference in New Issue
Block a user