# Coupon API Documentation

## Overview

The Coupon API allows customers to apply discount codes to their cart before checkout.

**Base URL:** `https://missio.dev/api/shop`

**Endpoint:** `POST /apply-coupon`

---

## Complete Checkout Flow with Coupon

The full checkout process with optional coupon:

```
1. Create/Get Cart
   GET /api/catalog/cart
   Returns: cart_id, cart_token

2. Add Items to Cart
   POST /api/catalog/cart/items
   Body: product_id, quantity, attributes

3. [OPTIONAL] Apply Coupon
   POST /api/shop/apply-coupon?cart_token=abc123
   Body: coupon_code
   Returns: discount_amount, new_total

4. Save Checkout Data
   POST /api/catalog/checkout?cart_token=abc123
   Body: billing_*, shipping_*

5. Create Payment Intent
   POST /api/shop/payment-intent?cart_token=abc123
   Returns: client_secret, payment_intent_id

6. Confirm Payment (on client after Stripe confirms)
   POST /api/shop/payment-confirm
   Body: payment_intent_id, cart_token
   Returns: order_id, order_number
```

---

## Apply Coupon Endpoint

### Request

**Method:** `POST`

**URL:** `/api/shop/apply-coupon`

**Required Headers:**

```
X-Company-Hash: {company_identifier}
Authorization: Bearer {token}              (optional)
Content-Type: application/json
```

**Request Body:**

```json
{
  "coupon_code": "SAVE10",
  "cart_token": "cart_abc123xyz789"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `coupon_code` | string | Yes | Coupon code to apply (case-insensitive) |
| `cart_token` | string | Yes | Cart token from cart creation |

### Response - Success

**Status Code:** `200 OK`

```json
{
  "status": "success",
  "message": "Coupon applied successfully",
  "data": {
    "coupon_code": "SAVE10",
    "discount_type": "percentage",
    "discount_value": 10,
    "discount_amount": "12.50",
    "subtotal": "125.00",
    "new_total": "112.50"
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `coupon_code` | string | Applied coupon code (uppercase) |
| `discount_type` | string | Type of discount: 'percentage' or 'fixed' |
| `discount_value` | number | Discount percentage (e.g., 10) or fixed amount |
| `discount_amount` | string | Actual discount amount in currency |
| `subtotal` | string | Original cart subtotal |
| `new_total` | string | Total after discount |

### Response - Error

**Status Code:** `400`

```json
{
  "status": "error",
  "message": "Invalid or expired coupon code"
}
```

**Possible Error Messages:**
- `"Invalid or expired coupon code"` - Coupon doesn't exist or expired
- `"Coupon already applied to this cart"` - Only one coupon per cart
- `"Cart not found"` - Invalid cart_token
- `"Cart is empty"` - No items in cart

---

## Coupon Types

The API supports two discount types:

### Percentage Discount
```json
{
  "discount_type": "percentage",
  "discount_value": 10
}
```
- 10% off the subtotal
- Discount = subtotal × 0.10

### Fixed Amount Discount
```json
{
  "discount_type": "fixed",
  "discount_value": 15
}
```
- $15 off the subtotal
- Discount = $15 (capped at subtotal)

---

## Coupon Validation Rules

1. **Code Match** - Case-insensitive match in database
2. **Company Scope** - Coupon must belong to requesting company
3. **Status** - Coupon status must be 'active'
4. **Expiry** - Current date must be before `expiry_date`
5. **Uniqueness** - Only one coupon per cart
6. **Amount Cap** - Discount cannot exceed subtotal

---

## Database Schema

Coupons table structure:

```sql
CREATE TABLE coupons (
    id INT PRIMARY KEY,
    company_id INT NOT NULL,
    code VARCHAR(50) UNIQUE NOT NULL,
    discount_type ENUM('percentage', 'fixed'),
    discount_value DECIMAL(10, 2),
    expiry_date DATE NOT NULL,
    status ENUM('active', 'inactive', 'archived'),
    usage_limit INT,
    times_used INT DEFAULT 0,
    created_at TIMESTAMP,
    updated_at TIMESTAMP
);
```

---

## Cart Storage

When a coupon is applied, it's stored as a discount item in the `order_carts` table:

```json
{
    "id": 6,
    "cart_id": "cart_abc123",
    "company_id": 5,
    "customer_id": 1,
    "product_id": 0,
    "quantity": 1,
    "unit_price": -12.50,
    "amount": -12.50,
    "type": "discount",
    "coupon_code": "SAVE10",
    "attributes": {
        "discount_type": "percentage",
        "discount_value": 10,
        "coupon_id": 42
    }
}
```

The discount is stored as a negative amount:
- Retrieved with: `OrderCart::where('type', 'discount')`
- Applied as: `total = subtotal - abs(discount_amount)`

---

## Example: Complete Flow

### Step 1: Create Cart
```bash
curl -X POST https://missio.dev/api/catalog/cart \
  -H "X-Company-Hash: abc123" \
  -H "Content-Type: application/json" \
  -d '{}'

# Response:
# {
#   "data": {
#     "id": 5,
#     "cart_token": "cart_abc123xyz789"
#   }
# }
```

### Step 2: Add Items
```bash
curl -X POST https://missio.dev/api/catalog/cart/items \
  -H "X-Company-Hash: abc123" \
  -d '{
    "product_id": 1,
    "quantity": 5,
    "attributes": {"color": "red"}
  }?cart_token=cart_abc123xyz789'

# Response: Cart with $125.00 subtotal
```

### Step 3: Apply Coupon
```bash
curl -X POST https://missio.dev/api/shop/apply-coupon \
  -H "X-Company-Hash: abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "coupon_code": "SAVE10",
    "cart_token": "cart_abc123xyz789"
  }'

# Response:
# {
#   "data": {
#     "coupon_code": "SAVE10",
#     "discount_type": "percentage",
#     "discount_value": 10,
#     "discount_amount": "12.50",
#     "subtotal": "125.00",
#     "new_total": "112.50"
#   }
# }
```

### Step 4: Proceed to Checkout
```bash
# Discount is already in cart, proceed with normal checkout
curl -X POST https://missio.dev/api/catalog/checkout \
  -H "X-Company-Hash: abc123" \
  -d '{
    "billing_first_name": "John",
    ...billing fields
  }?cart_token=cart_abc123xyz789'
```

### Step 5: Create Payment Intent
```bash
curl -X POST https://missio.dev/api/shop/payment-intent \
  -H "X-Company-Hash: abc123" \
  -d '{}?cart_token=cart_abc123xyz789'

# Returns client_secret with discount already applied
```

---

## Important Notes

1. **Order of Operations**
   - Add items first
   - Apply coupon (optional, before checkout)
   - Save checkout data
   - Create payment intent (includes discount)
   - Confirm payment

2. **Discount Persistence**
   - Discount stays in cart until payment is confirmed
   - If payment fails, coupon is still applied to cart
   - Customer can remove cart and start over if needed

3. **Multiple Attempts**
   - One coupon per cart
   - To change coupon, customer must remove cart and start over
   - Or manually delete discount record from order_carts

4. **Amount Calculation**
   - PaymentIntent automatically includes discount
   - Stripe receives correct final amount
   - No need to pass discount to payment step

---

## Error Handling

### Invalid Coupon
```json
{
  "status": "error",
  "message": "Invalid or expired coupon code"
}
```
Check:
- Spelling of coupon code
- Coupon hasn't expired
- Coupon is active (not archived/deleted)

### Coupon Already Applied
```json
{
  "status": "error",
  "message": "Coupon already applied to this cart"
}
```
Action: Create new cart to use different coupon, or proceed with current discount

### Cart Empty
```json
{
  "status": "error",
  "message": "Cart is empty"
}
```
Action: Add items before applying coupon

---

## Testing Coupons

For development, create test coupons in the database:

```sql
INSERT INTO coupons (company_id, code, discount_type, discount_value, expiry_date, status)
VALUES 
  (5, 'SAVE10', 'percentage', 10, '2025-12-31', 'active'),
  (5, 'SAVE20', 'percentage', 20, '2025-12-31', 'active'),
  (5, 'FIXED5', 'fixed', 5.00, '2025-12-31', 'active');
```

Use in API testing:
- `coupon_code=SAVE10` - 10% off
- `coupon_code=SAVE20` - 20% off
- `coupon_code=FIXED5` - $5 off

---

## Security Considerations

1. **Server-side Validation**
   - All coupon validation happens on server
   - Client cannot override discount

2. **Company Scoping**
   - Coupons are company-specific
   - X-Company-Hash header validates ownership

3. **One Per Cart**
   - Prevents stacking coupons
   - Simplifies discount logic

4. **Expiry Enforcement**
   - Server checks expiry date
   - Expired coupons rejected immediately

---

## Support

For issues with:
- **Coupon API:** See this documentation
- **Discount Calculation:** Check order_carts.type='discount' records
- **Payment Integration:** See PAYMENT_API.md
- **Cart Issues:** See CART_API.md
