202 lines
7.2 KiB
PHP
202 lines
7.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Controllers\Subscription;
|
|
|
|
use Hypervel\Http\Request;
|
|
use Hypervel\Support\Facades\Auth;
|
|
use Hypervel\Support\Facades\Response;
|
|
use App\Models\User;
|
|
use App\Models\Subscription\SubscriptionPlan;
|
|
use App\Models\Subscription\Subscription;
|
|
use App\Models\Subscription\SubscriptionInvoice;
|
|
use App\Enums\UserTypes;
|
|
use Carbon\Carbon;
|
|
|
|
class SubscriptionController
|
|
{
|
|
// ── User: list available plans (active only) ───────────────────────────
|
|
public function listAvailablePlans()
|
|
{
|
|
$plans = SubscriptionPlan::where('active', true)
|
|
->orderBy('price')
|
|
->get()
|
|
->map(fn($p) => [
|
|
'hashkey' => $p->hashkey,
|
|
'name' => $p->name,
|
|
'description' => $p->description,
|
|
'price' => $p->price,
|
|
'duration_days' => $p->duration_days,
|
|
'expiry_action' => $p->expiry_action,
|
|
]);
|
|
|
|
return Response::json($plans);
|
|
}
|
|
|
|
// ── User: get my current subscription ─────────────────────────────────
|
|
public function mySubscription()
|
|
{
|
|
$user = User::findOrFail(Auth::id());
|
|
$sub = self::getActiveSubscription($user->id);
|
|
|
|
if (!$sub) {
|
|
return Response::json([
|
|
'has_subscription' => false,
|
|
'balance' => $user->total_balance,
|
|
]);
|
|
}
|
|
|
|
return Response::json([
|
|
'has_subscription' => true,
|
|
'subscription' => self::formatUserSubscription($sub),
|
|
'balance' => $user->total_balance,
|
|
]);
|
|
}
|
|
|
|
// ── User: pay for a subscription via wallet ────────────────────────────
|
|
public function payWithWallet(Request $request)
|
|
{
|
|
$planHashkey = $request->input('plan_hashkey');
|
|
if (!$planHashkey) {
|
|
return Response::json('Plan is required.', 422);
|
|
}
|
|
|
|
$plan = SubscriptionPlan::where('hashkey', $planHashkey)
|
|
->where('active', true)
|
|
->first();
|
|
|
|
if (!$plan) {
|
|
return Response::json('Plan not found or no longer available.', 404);
|
|
}
|
|
|
|
$user = User::findOrFail(Auth::id());
|
|
|
|
if ($user->total_balance < $plan->price) {
|
|
return Response::json('Insufficient wallet balance.', 402);
|
|
}
|
|
|
|
$admin = User::where('acct_type', UserTypes::ULTIMATE->value)->first();
|
|
if (!$admin) {
|
|
return Response::json('Payment recipient not configured.', 500);
|
|
}
|
|
|
|
try {
|
|
// Deduct from user, credit admin
|
|
$user->total_balance -= $plan->price;
|
|
$user->save();
|
|
|
|
$admin->total_balance += $plan->price;
|
|
$admin->save();
|
|
|
|
// Create or extend subscription
|
|
$now = Carbon::now();
|
|
$expiry = $now->copy()->addDays($plan->duration_days);
|
|
|
|
$existing = self::getActiveSubscription($user->id);
|
|
|
|
if ($existing) {
|
|
// Extend from current expiry if still active, otherwise from now
|
|
$base = $existing->expires_at && $existing->expires_at->isFuture()
|
|
? $existing->expires_at
|
|
: $now;
|
|
$expiry = $base->copy()->addDays($plan->duration_days);
|
|
|
|
$existing->expires_at = $expiry;
|
|
$existing->status = 'active';
|
|
$existing->payment_method = 'wallet';
|
|
$existing->save();
|
|
|
|
$subscription = $existing;
|
|
} else {
|
|
$subscription = Subscription::create([
|
|
'user_id' => $user->id,
|
|
'plan_id' => $plan->id,
|
|
'status' => 'active',
|
|
'starts_at' => $now,
|
|
'expires_at' => $expiry,
|
|
'payment_method' => 'wallet',
|
|
]);
|
|
}
|
|
|
|
// Record invoice
|
|
SubscriptionInvoice::create([
|
|
'subscription_id' => $subscription->id,
|
|
'user_id' => $user->id,
|
|
'amount' => $plan->price,
|
|
'status' => 'paid',
|
|
'paid_at' => $now,
|
|
'payment_method' => 'wallet',
|
|
'payment_reference' => null,
|
|
'additional_details' => [
|
|
'plan_name' => $plan->name,
|
|
'plan_hashkey' => $plan->hashkey,
|
|
'admin_id' => $admin->id,
|
|
],
|
|
]);
|
|
|
|
return Response::json([
|
|
'success' => true,
|
|
'expires_at' => $expiry,
|
|
'balance' => $user->total_balance,
|
|
]);
|
|
} catch (\Throwable $th) {
|
|
return Response::json($th->getMessage(), 500);
|
|
}
|
|
}
|
|
|
|
// ── User: my invoice history ───────────────────────────────────────────
|
|
public function myInvoices()
|
|
{
|
|
$invoices = SubscriptionInvoice::where('user_id', Auth::id())
|
|
->orderByDesc('created_at')
|
|
->get()
|
|
->map(fn($inv) => [
|
|
'hashkey' => $inv->hashkey,
|
|
'amount' => $inv->amount,
|
|
'status' => $inv->status,
|
|
'payment_method' => $inv->payment_method,
|
|
'payment_reference' => $inv->payment_reference,
|
|
'paid_at' => $inv->paid_at,
|
|
'plan_name' => $inv->additional_details['plan_name'] ?? '',
|
|
'created_at' => $inv->created_at,
|
|
]);
|
|
|
|
return Response::json($invoices);
|
|
}
|
|
|
|
// ── Helper: get user's latest active subscription ─────────────────────
|
|
private static function getActiveSubscription(int $userId): ?Subscription
|
|
{
|
|
return Subscription::where('user_id', $userId)
|
|
->where('status', 'active')
|
|
->where('expires_at', '>', Carbon::now())
|
|
->with('plan')
|
|
->orderByDesc('expires_at')
|
|
->first();
|
|
}
|
|
|
|
private static function formatUserSubscription(Subscription $sub): array
|
|
{
|
|
$plan = $sub->plan;
|
|
$expiresAt = $sub->expires_at;
|
|
$daysRemaining = $expiresAt ? (int) now()->diffInDays($expiresAt, false) : 0;
|
|
|
|
return [
|
|
'hashkey' => $sub->hashkey,
|
|
'status' => $sub->status,
|
|
'starts_at' => $sub->starts_at,
|
|
'expires_at' => $expiresAt,
|
|
'days_remaining' => max(0, $daysRemaining),
|
|
'payment_method' => $sub->payment_method,
|
|
'plan' => $plan ? [
|
|
'hashkey' => $plan->hashkey,
|
|
'name' => $plan->name,
|
|
'price' => $plan->price,
|
|
'duration_days' => $plan->duration_days,
|
|
'expiry_action' => $plan->expiry_action,
|
|
] : null,
|
|
];
|
|
}
|
|
}
|