# Discount Codes: Implement Promo System

# Discount Codes: Promo System Implementation

## Problem

E-commerce platforms need robust discount code systems that:
- Validate codes before applying discounts
- Enforce expiry dates and usage limits
- Prevent abuse and fraud
- Track redemptions accurately
- Support multiple discount types (percentage, fixed, tiered)

## Solution Architecture

### Core Components

1. **Promo Code Model** - Store code metadata
2. **Validation Engine** - Check eligibility and constraints
3. **Application Logic** - Calculate and apply discounts
4. **Audit Trail** - Track usage and redemptions

### Key Features

- **Expiry Management**: Date-based and usage-based expiration
- **Constraint Validation**: Minimum purchase, user limits, category restrictions
- **Discount Types**: Percentage, fixed amount, BOGO, tiered
- **Redemption Tracking**: Per-user and global limits
- **Real-time Verification**: Instant validation during checkout

---

## Code Implementation

### 1. Database Schema

```sql
-- Promo codes table
CREATE TABLE promo_codes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    code VARCHAR(50) UNIQUE NOT NULL,
    discount_type ENUM('percentage', 'fixed', 'bogo', 'tiered') NOT NULL,
    discount_value DECIMAL(10, 2) NOT NULL,
    max_discount DECIMAL(10, 2),
    min_purchase DECIMAL(10, 2) DEFAULT 0,
    max_uses INT,
    max_uses_per_user INT DEFAULT 1,
    valid_from TIMESTAMP NOT NULL,
    valid_until TIMESTAMP NOT NULL,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Redemption history
CREATE TABLE redemptions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    promo_code_id UUID NOT NULL REFERENCES promo_codes(id),
    user_id UUID NOT NULL,
    order_id UUID NOT NULL,
    discount_amount DECIMAL(10, 2) NOT NULL,
    redeemed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(order_id)
);

-- Promo code restrictions
CREATE TABLE promo_restrictions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    promo_code_id UUID NOT NULL REFERENCES promo_codes(id),
    restriction_type ENUM('category', 'product', 'user_segment') NOT NULL,
    restriction_value VARCHAR(255) NOT NULL,
    is_inclusive BOOLEAN DEFAULT TRUE
);

CREATE INDEX idx_promo_code ON promo_codes(code);
CREATE INDEX idx_redemptions_user ON redemptions(user_id);
CREATE INDEX idx_redemptions_promo ON redemptions(promo_code_id);
```

### 2. TypeScript Models & Types

```typescript
// types/promo.ts
export enum DiscountType {
  PERCENTAGE = 'percentage',
  FIXED = 'fixed',
  BOGO = 'bogo',
  TIERED = 'tiered'
}

export enum RestrictionType {
  CATEGORY = 'category',
  PRODUCT = 'product',
  USER_SEGMENT = 'user_segment'
}

export interface PromoCode {
  id: string;
  code: string;
  discountType: DiscountType;
  discountValue: number;
  maxDiscount?: number;
  minPurchase: number;
  maxUses?: number;
  maxUsesPerUser: number;
  validFrom: Date;
  validUntil: Date;
  isActive: boolean;
  createdAt: Date;
  updatedAt: Date;
}

export interface Redemption {
  id: string;
  promoCodeId: string;
  userId: string;
  orderId: string;
  discountAmount: number;
  redeemedAt: Date;
}

export interface PromoRestriction {
  id: string;
  promoCodeId: string;
  restrictionType: RestrictionType;
  restrictionValue: string;
  isInclusive: boolean;
}

export interface ValidationResult {
  isValid: boolean;
  error?: string;
  discountAmount?: number;
  promoCode?: PromoCode;
}

export interface CartItem {
  productId: string;
  category: string;
  quantity: number;
  price: number;
}

export interface Cart {
  items: CartItem[];
  subtotal: number;
  userId: string;
}
```

### 3. Validation Engine

```typescript
// services/promoValidation.ts
import { Pool } from 'pg';
import { PromoCode, ValidationResult, Cart, Redemption } from '../types/promo';

export class PromoValidationService {
  constructor(private db: Pool) {}

  /**
   * Comprehensive promo code validation
   */
  async validatePromoCode(
    code: string,
    cart: Cart,
    userId: string
  ): Promise<ValidationResult> {
    try {
      // Step 1: Fetch promo code
      const promoCode = await this.getPromoCode(code);
      if (!promoCode) {
        return { isValid: false, error: 'Promo code not found' };
      }

      // Step 2: Check if active
      if (!promoCode.isActive) {
        return { isValid: false, error: 'Promo code is inactive' };
      }

      // Step 3: Check expiry
      const expiryCheck = this.checkExpiry(promoCode);
      if (!expiryCheck.isValid) {
        return expiryCheck;
      }

      // Step 4: Check usage limits
      const usageCheck = await this.checkUsageLimits(
        promoCode.id,
        userId
      );
      if (!usageCheck.isValid) {
        return usageCheck;
      }

      // Step 5: Check minimum purchase
      if (cart.subtotal < promoCode.minPurchase) {
        return {
          isValid: false,
          error: `Minimum purchase of $${promoCode.minPurchase} required`
        };
      }

      // Step 6: Check restrictions
      const restrictionCheck = await this.checkRestrictions(
        promoCode.id,
        cart
      );
      if (!restrictionCheck.isValid) {
        return restrictionCheck;
      }

      // Step 7: Calculate discount
      const discountAmount = await this.calculateDiscount(
        promoCode,
        cart
      );

      return {
        isValid: true,
        discountAmount,
        promoCode
      };
    } catch (error) {
      console.error('Promo validation error:', error);
      return { isValid: false, error: 'Validation failed' };
    }
  }

  /**
   * Fetch promo code from database
   */
  private async getPromoCode(code: string): Promise<PromoCode | null> {
    const result = await this.db.query(
      `SELECT * FROM promo_codes WHERE code = $1`,
      [code.toUpperCase()]
    );
    return result.rows[0] || null;
  }

  /**
   * Check if promo code has expired
   */
  private checkExpiry(promoCode: PromoCode): ValidationResult {
    const now = new Date();

    if (now < promoCode.validFrom) {
      return {
        isValid: false,
        error: `Promo code not yet active (starts ${promoCode.validFrom.toLocaleDateString()})`
      };
    }

    if (now > promoCode.validUntil) {
      return {
        isValid: false,
        error: 'Promo code has expired'
      };
    }

    return { isValid: true };
  }

  /**
   * Check global and per-user usage limits
   */
  private async checkUsageLimits(
    promoCodeId: string,
    userId: string
  ): Promise<ValidationResult> {
    const result = await this.db.query(
      `SELECT 
        COUNT(*) as total_uses,
        COUNT(CASE WHEN user_id = $2 THEN 1 END) as user_uses
       FROM redemptions 
       WHERE promo_code_id = $1`,
      [promoCodeId, userId]
    );

    const { total_uses, user_uses } = result.rows[0];
    const promoCode = await this.db.query(
      `SELECT max_uses, max_uses_per_user FROM promo_codes WHERE id = $1`,
      [promoCodeId]
    );

    const { max_uses, max_uses_per_user } = promoCode.rows[0];

    if (max_uses && total_uses >= max_uses) {
      return { isValid: false, error: 'Promo code usage limit reached' };
    }

    if (max_uses_per_user && user_uses >= max_uses_per_user) {
      return {
        isValid: false,
        error: 'You have already used this promo code'
      };
    }

    return { isValid: true };
  }

  /**
   * Check category and product restrictions
   */
  private async checkRestrictions(
    promoCodeId: string,
    cart: Cart
  ): Promise<ValidationResult> {
    const restrictions = await this.db.query(
      `SELECT * FROM promo_restrictions WHERE promo_code_id = $1`,
      [promoCodeId]
    );

    if (restrictions.rows.length === 0) {
      return { isValid: true }; // No restrictions
    }

    const categoryRestrictions = restrictions.rows.filter(
      r => r.restriction_type === 'category'
    );
    const productRestrictions = restrictions.rows.filter(
      r => r.restriction_type === 'product'
    );

    // Check category restrictions
    if (categoryRestrictions.length > 0) {
      const allowedCategories = categoryRestrictions
        .filter(r => r.is_inclusive)
        .map(r => r.restriction_value);
      const excludedCategories = categoryRestrictions
        .filter(r => !r.is_inclusive)
        .map(r => r.restriction_value);

      const cartHasValidCategory = cart.items.some(item => {
        const isAllowed = allowedCategories.length === 0 ||
          allowedCategories.includes(item.category);
        const isNotExcluded = !excludedCategories.includes(item.category);
        return isAllowed && isNotExcluded;
      });

      if (!cartHasValidCategory && allowedCategories.length > 0) {
        return {
          isValid: false,
          error: `Promo code only applies to: ${allowedCategories.join(', ')}`
        };
      }
    }

    return { isValid: true };
  }

  /**
   * Calculate discount amount based on type
   */
  private async calculateDiscount(
    promoCode: PromoCode,
    cart: Cart
  ): Promise<number> {
    let discount = 0;

    switch (promoCode.discountType) {
      case 'percentage':
        discount = (cart.subtotal * promoCode.discountValue) / 100;
        break;

      case 'fixed':
        discount = promoCode.discountValue;
        break;

      case 'tiered':
        discount = this.calculateTieredDiscount(promoCode, cart.subtotal);
        break;

      case 'bogo':
        discount = this.calculateBogoDiscount(promoCode, cart);
        break;
    }

    // Apply max discount cap if set
    if (promoCode.maxDiscount) {
      discount = Math.min(discount, promoCode.maxDiscount);
    }

    // Ensure discount doesn't exceed subtotal
    discount = Math.min(discount, cart.subtotal);

    return Math.round(discount * 100) / 100;
  }

  /**
   * Calculate tiered discount
   */
  private calculateTieredDiscount(
    promoCode: PromoCode,
    subtotal: number
  ): number {
    // Example: $0-50: 5%, $50-100: 10%, $100+: 15%
    const tiers = [
      { min: 0, max: 50, discount: 5 },
      { min: 50, max: 100, discount: 10 },
      { min: 100, max: Infinity, discount: 15 }
    ];

    const tier = tiers.find(t => subtotal >= t.min && subtotal < t.max);
    return tier ? (subtotal * tier.discount) / 100 : 0;
  }

  /**
   * Calculate BOGO (Buy One Get One) discount
   */
  private calculateBogoDiscount(
    promoCode: PromoCode,
    cart: Cart
  ): number {
    const totalItems = cart.items.reduce((sum, item) => sum + item.quantity, 0);
    const freeItems = Math.floor(totalItems / 2);
    const avgPrice = cart.subtotal / totalItems;
    return freeItems * avgPrice;
  }
}
```

### 4. Promo Code Service

```typescript
// services/promoService.ts
export class PromoCodeService {
  constructor(private db: Pool) {}

  /**
   * Create new promo code
   */
  async createPromoCode(data: {
    code: string;
    discountType: DiscountType;
    discountValue: number;
    maxDiscount?: number;
    minPurchase?: number;
    maxUses?: number;
    maxUsesPerUser?: number;
    validFrom: Date;
    validUntil: Date;
  }): Promise<PromoCode> {
    const result = await this.db.query(
      `INSERT INTO promo_codes 
       (code, discount_type, discount_value, max_discount, min_purchase, 
        max_uses, max_uses_per_user, valid_from, valid_until)
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
       RETURNING *`,
      [
        data.code.toUpperCase(),
        data.discountType,
        data.discountValue,
        data.maxDiscount || null,
        data.minPurchase || 0,
        data.maxUses || null,
        data.maxUsesPerUser || 1,
        data.validFrom,
        data.validUntil
      ]
    );

    return result.rows[0];
  }

  /**
   * Add restriction to promo code
   */
  async addRestriction(
    promoCodeId: string,
    restrictionType: RestrictionType,
    restrictionValue: string,
    isInclusive: boolean = true
  ): Promise<void> {
    await this.db.query(
      `INSERT INTO promo_restrictions 
       (promo_code_id, restriction_type, restriction_value, is_inclusive)
       VALUES ($1, $2, $3, $4)`,
      [promoCodeId, restrictionType, restrictionValue, isInclusive]
    );
  }

  /**
   * Record redemption
   */
  async recordRedemption(
    promoCodeId: string,
    userId: string,
    orderId: string,
    discountAmount: number
  ): Promise<Redemption> {
    const result = await this.db.query(
      `INSERT INTO redemptions 
       (promo_code_id, user_id, order_id, discount_amount)
       VALUES ($1, $2, $3, $4)
       RETURNING *`,
      [promoCodeId, userId, orderId, discountAmount]
    );

    return result.rows[0];
  }

  /**
   * Get promo code statistics
   */
  async getPromoStats(promoCodeId: string): Promise<{
    totalRedemptions: number;
    totalDiscountGiven: number;
    uniqueUsers: number;
    averageDiscount: number;
  }> {
    const result = await this.db.query(
      `SELECT 
        COUNT(*) as total_redemptions,
        SUM(discount_amount) as total_discount,
        COUNT(DISTINCT user_id) as unique_users,
        AVG(discount_amount) as avg_discount
       FROM redemptions 
       WHERE promo_code_id = $1`,
      [promoCodeId]
    );

    const row = result.rows[0];
    return {
      totalRedemptions: parseInt(row.total_redemptions),
      totalDiscountGiven: parseFloat(row.total_discount || 0),
      uniqueUsers: parseInt(row.unique_users),
      averageDiscount: parseFloat(row.avg_discount || 0)
    };
  }

  /**
   * Deactivate expired promo codes
   */
  async deactivateExpiredCodes(): Promise<number> {
    const result = await this.db.query(
      `UPDATE promo_codes 
       SET is_active = FALSE 
       WHERE valid_until < NOW() AND is_active = TRUE
       RETURNING id`
    );

    return result.rowCount;
  }
}
```

### 5. API Endpoints

```typescript
// routes/promo.ts
import express from 'express';
import { PromoValidationService } from '../services/promoValidation';
import { PromoCodeService } from '../services/promoService';

const router = express.Router();

/**
 * Validate promo code
 * POST /api/promo/validate
 */
router.post('/validate', async (req, res) => {
  try {
    const { code, cart, userId } = req.body;

    const validationService = new PromoValidationService(req.db);
    const result = await validationService.validatePromoCode(
      code,
      cart,
      userId
    );

    res.json(result);
  } catch (error) {
    res.status(500).json({ error: 'Validation failed' });
  }
});

/**
 * Apply promo code to order
 * POST /api/promo/apply
 */
router.post('/apply', async (req, res) => {
  try {
    const { code, orderId, userId, cart } = req.body;

    const validationService = new PromoValidationService(req.db);
    const validation = await validationService.validatePromoCode(
      code,
      cart,
      userId
    );

    if (!validation.isValid) {
      return res.status(400).json({ error: validation.error });
    }

    const promoService = new PromoCodeService(req.db);
    const redemption = await promoService.recordRedemption(
      validation.promoCode!.id,
      userId,
      orderId,
      validation.discountAmount!
    );

    res.json({
      success: true,
      discountAmount: validation.discountAmount,
      redemption
    });
  } catch (error) {
    res.status(500).json({ error: 'Failed to apply promo code' });
  }
});

/**
 * Get promo code statistics
 * GET /api/promo/:promoCodeId/stats
 */
router.get('/:promoCodeId/stats', async (req, res) => {
  try {
    const promoService = new PromoCodeService(req.db);
    const stats = await promoService.getPromoStats(req.params.promoCodeId);
    res.json(stats);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch stats' });
  }
});

/**
 * Create
