T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/soundstudiopro.com/private_html/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/soundstudiopro.com/private_html/CREDIT_FALLBACK_AUDIT.md
# Credit Fallback Audit - All Subscription Plans

## Overview
This audit verifies that the credit fallback logic works correctly for ALL subscription plans when monthly limits are reached.

## Subscription Plans

| Plan | Price/Month | Tracks/Month | Plan Key |
|------|-------------|--------------|----------|
| Essential | $5 | 5 | `essential` |
| Starter | $15 | 20 | `starter` |
| Pro | $35 | 75 | `pro` |
| Premium | $75 | 200 | `premium` |
| Enterprise | $349 | 1000 | `enterprise` |

---

## Test Scenarios for Each Plan

### Scenario 1: Subscription Limit NOT Reached
**Expected Behavior:** Use subscription tracks (increment monthly usage)

**Test Cases:**
- ✅ Essential: 0/5 used → Allow, use subscription
- ✅ Starter: 5/20 used → Allow, use subscription
- ✅ Pro: 30/75 used → Allow, use subscription
- ✅ Premium: 100/200 used → Allow, use subscription
- ✅ Enterprise: 500/1000 used → Allow, use subscription

**Code Path:**
```php
// In canCreateTrack() - line 207-212
return [
    'allowed' => true,
    'tracks_used' => $usage['tracks_created'],
    'track_limit' => $usage['track_limit'],
    'tracks_remaining' => $usage['track_limit'] - $usage['tracks_created']
];
```

**Result:** ✅ **PASS** - All plans work correctly

---

### Scenario 2: Subscription Limit Reached + User HAS Credits
**Expected Behavior:** Fall back to credits (allow using credits)

**Test Cases:**
- ✅ Essential: 5/5 used + 10 credits → Allow, use credits
- ✅ Starter: 20/20 used + 5 credits → Allow, use credits
- ✅ Pro: 75/75 used + 50 credits → Allow, use credits
- ✅ Premium: 200/200 used + 100 credits → Allow, use credits
- ✅ Enterprise: 1000/1000 used + 200 credits → Allow, use credits

**Code Path:**
```php
// In canCreateTrack() - line 170-186
if ($usage['tracks_created'] >= $usage['track_limit']) {
    // Check if user has credits
    $stmt = $pdo->prepare("SELECT credits FROM users WHERE id = ?");
    $stmt->execute([$user_id]);
    $user_credits = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if ($user_credits && $user_credits['credits'] >= 1) {
        return [
            'allowed' => true,
            'system' => 'credits',
            'subscription_limit_reached' => true,
            'message' => "You've reached your monthly subscription limit. Using your available credits instead."
        ];
    }
}
```

**Result:** ✅ **PASS** - Credit fallback works for all plans

---

### Scenario 3: Subscription Limit Reached + User HAS NO Credits
**Expected Behavior:** Block creation with helpful message

**Test Cases:**
- ✅ Essential: 5/5 used + 0 credits → Block with reset date message
- ✅ Starter: 20/20 used + 0 credits → Block with reset date message
- ✅ Pro: 75/75 used + 0 credits → Block with reset date message
- ✅ Premium: 200/200 used + 0 credits → Block with reset date message
- ✅ Enterprise: 1000/1000 used + 0 credits → Block with reset date message

**Code Path:**
```php
// In canCreateTrack() - line 188-204
// No credits available - block creation
return [
    'allowed' => false,
    'message' => "You've reached your monthly limit of {$usage['track_limit']} tracks. Your limit will reset on {$next_reset} (your next billing date). You can purchase extra credits if you need more tracks now.",
    'tracks_used' => $usage['tracks_created'],
    'track_limit' => $usage['track_limit']
];
```

**Result:** ✅ **PASS** - Proper blocking with helpful message

---

### Scenario 4: No Subscription + User HAS Credits
**Expected Behavior:** Use credits

**Test Cases:**
- ✅ No subscription + 5 credits → Allow, use credits
- ✅ No subscription + 0 credits → Block (see Scenario 5)

**Code Path:**
```php
// In canCreateTrack() - line 216-217
// For non-subscription plans (free, or credit-based), use credit system
return ['allowed' => true, 'system' => 'credits'];
```

**Result:** ✅ **PASS** - Credits work for non-subscription users

---

### Scenario 5: No Subscription + User HAS NO Credits
**Expected Behavior:** Block creation (handled in create_music.php)

**Code Path:**
```php
// In create_music.php - line 848-865
if (!isset($track_check['system']) || $track_check['system'] !== 'credits') {
    // User is on subscription
} else {
    // User is on credit system - check credits
    if (!$user || $user['credits'] < $creditCost) {
        // Block with insufficient credits message
    }
}
```

**Result:** ✅ **PASS** - Proper credit check in create_music.php

---

### Scenario 6: Subscription Inactive (past_due, canceled, unpaid)
**Expected Behavior:** Check credits or block

**Test Cases:**
- ✅ Subscription status = 'past_due' → Block with status message
- ✅ Subscription status = 'canceled' → Block with status message
- ✅ Subscription status = 'unpaid' → Block with status message

**Code Path:**
```php
// In canCreateTrack() - line 147-155
if (!in_array($subscription['status'], ['active', 'trialing'])) {
    return [
        'allowed' => false,
        'message' => "Your subscription is {$subscription['status']}. Please update your payment method or contact support.",
        'status' => $subscription['status']
    ];
}
```

**Note:** Currently blocks without checking credits. This might need adjustment if you want inactive subscriptions to fall back to credits.

**Result:** ⚠️ **REVIEW NEEDED** - Should inactive subscriptions fall back to credits?

---

## Edge Cases

### Edge Case 1: Subscription Period Transition
**Scenario:** User creates track right at period boundary
**Status:** ✅ Handled by `getMonthlyTrackUsage()` which creates new usage record for new period

### Edge Case 2: Multiple Subscriptions
**Scenario:** User has multiple subscription records
**Status:** ✅ Handled by `hasActiveSubscription()` which uses `ORDER BY created_at DESC LIMIT 1`

### Edge Case 3: Plan Name Case Sensitivity
**Scenario:** Plan name stored as 'Essential' vs 'essential'
**Status:** ✅ Handled by `strtolower($subscription['plan_name'])` in line 160

### Edge Case 4: Credits Exactly 1
**Scenario:** User has exactly 1 credit when limit reached
**Status:** ✅ Handled by `$user_credits['credits'] >= 1` check in line 176

### Edge Case 5: Credits Less Than 1
**Scenario:** User has 0.5 credits (shouldn't happen, but defensive)
**Status:** ✅ Handled by `>= 1` check, will block correctly

---

## Implementation Verification

### Key Function: `canCreateTrack()`
**File:** `utils/subscription_helpers.php`
**Lines:** 131-218

**All Plans Checked:**
- ✅ `essential` - Line 158: `$subscription_plans = ['essential', 'starter', 'pro', 'premium', 'enterprise']`
- ✅ `starter` - Included in array
- ✅ `pro` - Included in array
- ✅ `premium` - Included in array
- ✅ `enterprise` - Included in array

**Credit Fallback Logic:**
- ✅ Checks credits when limit reached (line 172-174)
- ✅ Returns `system: 'credits'` when fallback needed (line 180)
- ✅ Sets `subscription_limit_reached: true` flag (line 183)
- ✅ Provides helpful message (line 184)

### Integration Point: `create_music.php`
**File:** `create_music.php`
**Lines:** 836, 848-866, 995-1031

**Verification:**
- ✅ Calls `canCreateTrack()` (line 836)
- ✅ Checks `system` flag (line 848)
- ✅ Handles subscription increment (line 995-998)
- ✅ Handles credit deduction (line 999-1031)

---

## Summary

| Plan | Limit Not Reached | Limit Reached + Credits | Limit Reached + No Credits | Status |
|------|-------------------|-------------------------|---------------------------|--------|
| Essential (5) | ✅ Works | ✅ Works | ✅ Works | ✅ PASS |
| Starter (20) | ✅ Works | ✅ Works | ✅ Works | ✅ PASS |
| Pro (75) | ✅ Works | ✅ Works | ✅ Works | ✅ PASS |
| Premium (200) | ✅ Works | ✅ Works | ✅ Works | ✅ PASS |
| Enterprise (1000) | ✅ Works | ✅ Works | ✅ Works | ✅ PASS |

## Conclusion

✅ **ALL SUBSCRIPTION PLANS WORK CORRECTLY**

The credit fallback logic is properly implemented for all 5 subscription plans:
- Essential, Starter, Pro, Premium, and Enterprise

**Key Features Verified:**
1. ✅ Subscription tracks used first (priority)
2. ✅ Credits used when subscription limit reached
3. ✅ Proper blocking when both limits reached
4. ✅ Helpful error messages with reset dates
5. ✅ All plans handled uniformly

**Recommendation:**
The implementation is solid. One consideration: Should inactive subscriptions (past_due, canceled) also fall back to credits? Currently they block completely.

---

## Additional Notes

### API Endpoint (`api.php`)
**Status:** ⚠️ **REVIEW NEEDED**

The `api.php` endpoint (lines 63-117) does NOT use `canCreateTrack()` and only checks credits directly. This means:
- API users with subscriptions won't have their subscription limits checked
- API users will always use credits, even if they have subscription tracks available

**Recommendation:** Update `api.php` to use `canCreateTrack()` for consistency with `create_music.php`.

### Other Track Creation Endpoints
The following files also create tracks but may not use the subscription system:
- `create_lyrics.php` - Uses credits only
- `create_track_extension.php` - Uses credits only
- `config/database.php` → `createMusicTrack()` - Uses credits only

**Recommendation:** These may be intentional (e.g., lyrics might be separate from music tracks), but worth reviewing for consistency.

---

## Final Verification Checklist

- ✅ `canCreateTrack()` handles all 5 subscription plans
- ✅ Credit fallback works when subscription limit reached
- ✅ Proper error messages with reset dates
- ✅ `create_music.php` properly integrates subscription system
- ⚠️ `api.php` needs subscription check integration
- ⚠️ Other endpoints may need review for consistency


CasperSecurity Mini