# Coupon System + Bonus Transfer — Implementation Report

**Date:** 2026-05-31  
**Status:** COMPLETE — migrations ran, all routes verified

---

## What Was Built

### Database
| Migration | Table | Status |
|---|---|---|
| `2024_01_09_000001_create_coupons_table` | `coupons` | DONE |
| `2024_01_09_000002_create_coupon_usages_table` | `coupon_usages` | DONE |

`coupons`: id, code (unique), amount, max_uses, used_count, is_active, expires_at, description, timestamps  
`coupon_usages`: id, coupon_id, user_id, amount_credited, used_at — unique(coupon_id, user_id) prevents double-claim

---

### New Files Created
| File | Purpose |
|---|---|
| `app/Models/Coupon.php` | Coupon model with `isValid()` check |
| `app/Models/CouponUsage.php` | Usage record model |
| `app/Http/Controllers/API/V1/CouponController.php` | `POST /api/v1/coupons/claim` |
| `app/Http/Controllers/Admin/AdminCouponController.php` | Admin CRUD for coupons |
| `resources/views/admin/coupons/index.blade.php` | Admin coupon management UI |

### Files Modified
| File | Change |
|---|---|
| `app/Http/Controllers/Admin/AdminDepositController.php` | `approve()` now wraps in DB::transaction; credits deposit, then transfers min(deposit, bonus) to real and zeroes bonus |
| `app/Http/Controllers/API/V1/WalletController.php` | `withdrawRequest()` gates on 3× turnover; added `withdrawalStatus()` endpoint |
| `routes/api.php` | Added `POST /api/v1/coupons/claim` and `GET /api/v1/wallet/withdrawal-status` |
| `routes/web.php` | Added admin coupon routes under `/ponir/coupons` |
| `resources/views/admin/layout.blade.php` | Coupons link in sidebar |
| `resources/js/Components/screens/ProfileScreen.jsx` | Coupon redeem UI (input + Apply button + feedback message) |
| `resources/js/lib/api.js` | Added `api.coupons.claim()` and `api.wallet.withdrawalStatus()` |

---

## API Endpoints

### `POST /api/v1/coupons/claim` (auth required)
```json
Request:  { "code": "WELCOME10" }
Success:  { "success": true, "message": "Coupon applied! $10.00 added to your bonus account.", "amount": 10 }
Errors:   422 — Invalid code / expired / already used
```

### `GET /api/v1/wallet/withdrawal-status` (auth required)
```json
{
  "eligible": false,
  "real_balance": 25.00,
  "bonus_balance": 0.00,
  "total_volume": 20.00,
  "required_turnover": 75.00,
  "remaining": 55.00,
  "progress_percent": 27
}
```

### Admin Routes
| Method | URL | Action |
|---|---|---|
| GET | `/ponir/coupons` | List all coupons |
| POST | `/ponir/coupons` | Create coupon |
| PUT | `/ponir/coupons/{id}/toggle` | Enable/disable |
| DELETE | `/ponir/coupons/{id}` | Delete |

---

## Business Logic

### Coupon Claim
1. Look up code (case-insensitive)
2. Check `isValid()`: active + not used up + not expired
3. Check unique `(coupon_id, user_id)` — one per user
4. DB transaction: `bonus_balance += amount`, insert `coupon_usages`, `used_count++`

### Bonus → Real Transfer on Deposit Approve
```
deposit $20, bonus_balance $50
→ real_balance += $20   (deposit)
→ transfer = min($20, $50) = $20
→ real_balance += $20   (bonus transfer)
→ bonus_balance = $0    (always zeroed)
net: real gained $40, bonus zeroed
```

### 3× Turnover Gate on Withdrawal
```
real_balance = $100  →  required_volume = $300
If sum(trade.amount where status IN won/lost/cancelled) < $300 → blocked
```

---

## Test Plan

1. **Admin → `/ponir/coupons`** — create coupon `BONUS10`, amount $10, max_uses 100
2. **User → Profile screen** — enter `BONUS10`, click Apply → bonus_balance shows $10
3. **Try same coupon again** → "You have already used this coupon"
4. **Admin → Deposits → approve a $5 deposit** → real_balance += $5 (deposit) + $5 (bonus) = $10 total; bonus_balance = $0
5. **User tries withdraw before 3× volume** → locked message with remaining amount
6. **After enough trades settle** → withdrawal unlocks

---

## Run Commands (if starting fresh)
```bash
php artisan migrate
composer dump-autoload
php artisan route:clear
php artisan config:clear
npm run build
```
