Files
eCommerce-backend/CouponGuide.md
2026-02-19 17:25:38 +05:30

12 KiB

🎟️ 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:

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

// 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

GET /api/coupons/admin?page=1&limit=20&isActive=true&type=PERCENTAGE&search=SAVE
Authorization: Bearer <admin_token>

Response:

{
  "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

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

PUT /api/coupons/admin/:id
Authorization: Bearer <admin_token>

{
  "value": 25,
  "maxUses": 150
}

4. Toggle Coupon Status

PATCH /api/coupons/admin/:id/toggle
Authorization: Bearer <admin_token>

5. Get Coupon Statistics

GET /api/coupons/admin/stats
Authorization: Bearer <admin_token>

Response:

{
  "status": true,
  "data": {
    "totalCoupons": 50,
    "activeCoupons": 35,
    "expiredCoupons": 10,
    "totalRedemptions": 2450,
    "mostUsedCoupons": [...]
  }
}

User Endpoints

1. Get Available Coupons

GET /api/coupons/available?orderAmount=2599

Response:

{
  "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

POST /api/coupons/validate
Content-Type: application/json

{
  "code": "SAVE20",
  "orderAmount": 2599
}

Response:

{
  "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

POST /api/coupons/apply
Authorization: Bearer <token>

{
  "couponCode": "SAVE20",
  "orderId": "order_123"
}

4. Remove Coupon

POST /api/coupons/remove
Authorization: Bearer <token>

{
  "orderId": "order_123"
}

💡 Usage Examples

Example 1: Admin Creates Percentage Coupon

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

{
  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

{
  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

// 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:

model Coupon {
  // ... existing fields
  userId    String?  // Optional: restrict to specific user
  user      User?    @relation(fields: [userId], references: [id])
}

Feature 2: Product-Specific Coupons

model Coupon {
  // ... existing fields
  applicableCategories String[]  // Only for these categories
  applicableProducts   String[]  // Only for these products
}

Feature 3: First-Time User Coupons

// 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

{
  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

{
  code: 'BUY1000GET200',
  type: 'FIXED_AMOUNT',
  value: 200,
  minOrderAmount: 1000,
}

Use Case 3: New Customer Welcome

{
  code: 'WELCOME',
  type: 'PERCENTAGE',
  value: 15,
  description: 'Welcome! Get 15% off on your first order',
  maxUses: null, // Unlimited
}

📈 Analytics & Reporting

Track coupon performance:

// 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