Coupon Management Endpoints Index

Coupon Management

GET /api/coupons

List all coupons with optional filtering (requires administrator access)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Query Parameters:

  • sort - JSON array with interface name and direction ["interfaceName","ASC|DESC"]
    Example: sort=["name","ASC"] - Sort by name in ascending order
  • page - Page number (1-based)
    Example: page=1 - Get the first page
  • perPage - Number of items per page
    Example: perPage=10 - Show 10 items per page
  • filter - JSON object with interface name/value pairs for filtering {"interfaceName1":"value1","interfaceName2":value2}
    Example: filter={"name":"Summer"} - Filter coupons with name containing "Summer"

    Special filter: Global Search
    Using the key q in the filter will search across all text columns:
    Example: filter={"q":"discount","isActive":true} - Find active coupons with "discount" in any text field
  • active - Filter by active status (true/false)
  • syncWithStripe - Whether to sync with Stripe before fetching data (default: true)

Example Requests:

GET /api/coupons?page=1&perPage=10&sort=["id","ASC"]&filter={"name":"Summer"}&syncWithStripe=true
GET /api/coupons?page=2&perPage=20&filter={"q":"discount","isActive":true}

Response (200 OK):

{
    "data": [
        {
            "id": 1,
            "stripeCouponId": "Q9Wpvk4o",
            "name": "Summer Discount",
            "couponTypeCode": "DISC",
            "couponTypeName": "Discount Percentage",
            "discountPercentage": 20,
            "trialDays": null,
            "couponDurationTypeCode": "ONCE",
            "couponDurationTypeName": "Once",
            "durationInMonths": null,
            "isActive": true,
            "createdAt": "2023-07-15T14:25:00Z",
            "updatedAt": null,
            "timesRedeemed": 45,
            "promotions": [
                {
                    "id": 1,
                    "name": "Summer Sale",
                    "description": "20% off summer products",
                    "stripePromotionCodeId": "promo_1NcL5GJ8oAJtHC9rnvmaJ4Hl",
                    "code": "SUMMER20",
                    "couponId": 1,
                    "maxRedemptions": 100,
                    "redemptionCount": 45,
                    "validUntil": "2023-09-30T23:59:59Z",
                    "isActive": true,
                    "createdAt": "2023-07-15T14:25:00Z",
                    "updatedAt": null
                }
            ]
        }
        // Additional coupon objects...
    ],
    "total": 10
}

Headers:

Content-Range: coupons 0-9/10
Accept-Range: coupons
Access-Control-Expose-Headers: Content-Range
X-Total-Count: 10

Error Responses:

{
    "message": "Administrator access required"
}

Notes:

  • The syncWithStripe parameter ensures coupon data is synchronized with Stripe before filtering
  • The response includes pagination metadata in headers
  • Promotions array includes all promotion codes associated with each coupon
  • The timesRedeemed field comes from Stripe data
GET /api/coupons/:id

Get details of a specific coupon by ID (requires administrator access)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Response (200 OK):

{
    "id": 1,
    "stripeCouponId": "Q9Wpvk4o",
    "name": "Welcome Discount",
    "couponTypeCode": "DISC",
    "couponTypeName": "Discount Percentage",
    "discountPercentage": 20,
    "trialDays": null,
    "couponDurationTypeCode": "ONCE",
    "couponDurationTypeName": "Once",
    "durationInMonths": null,
    "isActive": true,
    "createdAt": "2023-01-15T08:30:00Z",
    "updatedAt": null,
    "timesRedeemed": 45,
    "promotions": [
        {
            "id": 1,
            "name": "Summer Promo",
            "description": "Summer promotion code",
            "stripePromotionCodeId": "promo_1NcL5GJ8oAJtHC9rnvmaJ4Hl",
            "code": "SUMMER20",
            "couponId": 1,
            "maxRedemptions": 100,
            "redemptionCount": 45,
            "validUntil": "2023-09-30T23:59:59Z",
            "isActive": true,
            "createdAt": "2023-07-15T14:25:00Z",
            "updatedAt": null
        }
    ]
}

Error Responses:

{
    "message": "Administrator access required"
}

{
    "message": "Invalid coupon ID"
}

{
    "message": "Coupon not found"
}

Notes:

  • This endpoint syncs the specific coupon with Stripe before returning the data
  • If the coupon doesn't exist in Stripe, it will be removed from the database
  • The timesRedeemed field reflects the current usage from Stripe
GET /api/coupons/metadata

Get metadata about coupon types and duration types (requires administrator access)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Response (200 OK):

{
    "couponTypes": [
        {
            "code": "DISC",
            "name": "Discount Percentage"
        },
        {
            "code": "TRIA",
            "name": "Free Trial"
        }
    ],
    "durationTypes": [
        {
            "code": "ONCE",
            "name": "Once",
            "value": "once"
        },
        {
            "code": "REPE",
            "name": "Repeating",
            "value": "repeating"
        },
        {
            "code": "FORE", 
            "name": "Forever",
            "value": "forever"
        }
    ]
}

Error Responses:

{
    "message": "Administrator access required"
}

Notes:

  • This endpoint syncs with Stripe before returning metadata to ensure data consistency
  • The value field in duration types corresponds to Stripe's duration values
POST /api/coupons

Create a new coupon (requires administrator access and CSRF token)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...
x-csrf-token: a8d7f9c6e5b4a3c2d1e0f9a8d7f6c5b4a3

Request Body (Percentage Discount Coupon):

{
    "name": "Back to School",
    "couponType": "DISC",
    "percentOff": 25,
    "duration": "once"
}

Request Body (Free Trial Coupon):

{
    "name": "Extended Trial",
    "couponType": "TRIA",
    "trialDays": 30,
    "duration": "once"
}

Request Body (Repeating Discount):

{
    "name": "Three Month Discount",
    "couponType": "DISC",
    "percentOff": 10,
    "duration": "repeating",
    "durationInMonths": 3
}

Response (201 Created):

{
    "message": "Coupon created successfully",
    "coupon": {
        "id": 3,
        "stripeCouponId": "coupon_1234567890",
        "name": "Back to School",
        "couponTypeCode": "DISC",
        "couponTypeName": "Discount Percentage",
        "discountPercentage": 25,
        "trialDays": null,
        "couponDurationTypeCode": "ONCE",
        "couponDurationTypeName": "Once",
        "durationInMonths": null,
        "isActive": true,
        "createdAt": "2023-07-15T14:25:00Z",
        "updatedAt": null
    }
}

Error Responses:

{
    "message": "Administrator access required"
}

{
    "message": "Missing required fields: name, couponType, and duration are required"
}

{
    "message": "Invalid coupon type. Must be either DISC (discount percentage) or TRIA (free trial)"
}

{
    "message": "Percentage discount coupons require percentOff value"
}

{
    "message": "Free trial coupons require trialDays value"
}

{
    "message": "Invalid duration. Must be once, repeating, or forever"
}

{
    "message": "durationInMonths is required for repeating coupons"
}

{
    "message": "Failed to create coupon",
    "error": "Error creating coupon in Stripe"
}

Notes:

  • The coupon is created in both Stripe and the local database
  • System syncs with Stripe before creating to ensure data consistency
  • Auto-generated code is used internally; promotion codes are created separately
POST /api/coupons/validate

Validate a coupon code (requires authentication)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...

Request Body:

{
    "couponCode": "SUMMER20"
}

Response (200 OK) - Valid Coupon:

{
    "valid": true,
    "couponDetails": {
        "type": "DISC",
        "name": "Summer Discount",
        "discountPercentage": 20,
        "trialDays": null
    }
}

Response (200 OK) - Valid Trial Coupon:

{
    "valid": true,
    "couponDetails": {
        "type": "TRIA",
        "name": "Extended Trial",
        "discountPercentage": null,
        "trialDays": 30
    }
}

Response (200 OK) - Invalid Coupon:

{
    "valid": false,
    "message": "Coupon code is invalid or expired"
}

Response (200 OK) - Maximum Uses Reached:

{
    "valid": false,
    "message": "This coupon code has reached its maximum number of uses"
}

Error Responses:

{
    "message": "Authentication required"
}

{
    "message": "Coupon code is required"
}

{
    "message": "Failed to validate coupon",
    "error": "Error message from service"
}

Notes:

  • This endpoint validates promotion codes, not coupon IDs directly
  • Checks both coupon and promotion validity, expiration, and redemption limits
  • Syncs with Stripe before validation to ensure up-to-date data
PUT /api/coupons/:id

Update a coupon (currently only supports toggling active status) (requires administrator access and CSRF token)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...
x-csrf-token: a8d7f9c6e5b4a3c2d1e0f9a8d7f6c5b4a3

Request Body:

{
    "isActive": false
}

Response (200 OK):

{
    "message": "Coupon updated successfully",
    "coupon": {
        "id": 1,
        "stripeCouponId": "Q9Wpvk4o",
        "name": "Welcome Discount",
        "couponTypeCode": "DISC",
        "couponTypeName": "Discount Percentage",
        "discountPercentage": 20,
        "trialDays": null,
        "couponDurationTypeCode": "ONCE",
        "couponDurationTypeName": "Once",
        "durationInMonths": null,
        "isActive": false,
        "createdAt": "2023-01-15T08:30:00Z",
        "updatedAt": "2023-07-15T16:45:00Z"
    }
}

Error Responses:

{
    "message": "Administrator access required"
}

{
    "message": "Invalid coupon ID"
}

{
    "message": "No update parameters provided"
}

{
    "message": "Coupon not found"
}

Notes:

  • Updates are synchronized with Stripe before and after the operation
  • Only the isActive field can currently be modified
  • Setting isActive to false effectively disables the coupon
DELETE /api/coupons/:id

Delete a coupon (requires administrator access and CSRF token)

Headers:

Authorization: Bearer ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr...
x-csrf-token: a8d7f9c6e5b4a3c2d1e0f9a8d7f6c5b4a3

Response (200 OK):

{
    "message": "Coupon deleted successfully"
}

Error Responses:

{
    "message": "Administrator access required"
}

{
    "message": "Invalid coupon ID"
}

{
    "message": "Coupon not found"
}

{
    "message": "Cannot delete coupon with linked promotions. Delete the promotions first."
}

Notes:

  • Deletion is synchronized with Stripe - the coupon is removed from both systems
  • Cannot delete coupons that have associated promotions - those must be deleted first
  • This is a permanent operation and cannot be undone