resolveTenant($request); return response()->json([ 'balance' => $tenant->event_credits_balance, 'free_event_granted_at' => $tenant->free_event_granted_at, ]); } public function ledger(Request $request) { $tenant = $this->resolveTenant($request); $ledgers = EventCreditsLedger::where('tenant_id', $tenant->id) ->orderByDesc('created_at') ->paginate(20); return CreditLedgerResource::collection($ledgers); } public function history(Request $request) { $tenant = $this->resolveTenant($request); $purchases = EventPurchase::where('tenant_id', $tenant->id) ->orderByDesc('purchased_at') ->paginate(20); return EventPurchaseResource::collection($purchases); } public function purchase(Request $request): JsonResponse { $tenant = $this->resolveTenant($request); $data = $request->validate([ 'package_id' => ['required', 'string', 'max:255'], 'credits_added' => ['required', 'integer', 'min:1'], 'platform' => ['nullable', 'string', 'max:32'], 'transaction_id' => ['nullable', 'string', 'max:255'], 'subscription_active' => ['sometimes', 'boolean'], ]); $purchase = EventPurchase::create([ 'tenant_id' => $tenant->id, 'events_purchased' => $data['credits_added'], 'amount' => 0, 'currency' => 'EUR', 'provider' => $data['platform'] ?? 'tenant-app', 'external_receipt_id' => $data['transaction_id'] ?? null, 'status' => 'completed', 'purchased_at' => now(), ]); $note = 'Package: '.$data['package_id']; $incremented = $tenant->incrementCredits($data['credits_added'], 'purchase', $note, $purchase->id); if (! $incremented) { throw new HttpException(500, 'Unable to record credit purchase'); } if (array_key_exists('subscription_active', $data)) { $tenant->update([ 'subscription_tier' => $data['subscription_active'] ? 'pro' : $tenant->subscription_tier, ]); } $tenant->refresh(); return response()->json([ 'message' => 'Purchase recorded', 'balance' => $tenant->event_credits_balance, 'subscription_active' => (bool) ($data['subscription_active'] ?? false), ], 201); } public function sync(Request $request): JsonResponse { $tenant = $this->resolveTenant($request); $data = $request->validate([ 'balance' => ['nullable', 'integer', 'min:0'], 'subscription_active' => ['sometimes', 'boolean'], 'last_sync' => ['nullable', 'date'], ]); if (array_key_exists('subscription_active', $data)) { $tenant->update([ 'subscription_tier' => $data['subscription_active'] ? 'pro' : ($tenant->subscription_tier ?? 'free'), ]); } // Server remains source of truth for balance; echo current state back to client return response()->json([ 'balance' => $tenant->event_credits_balance, 'subscription_active' => (bool) ($tenant->active_subscription ?? false), 'server_time' => now()->toIso8601String(), ]); } private function resolveTenant(Request $request): Tenant { $user = $request->user(); if ($user && isset($user->tenant) && $user->tenant instanceof Tenant) { return $user->tenant; } $tenantId = $request->attributes->get('tenant_id'); if (! $tenantId && $user && isset($user->tenant_id)) { $tenantId = $user->tenant_id; } if (! $tenantId) { throw new HttpException(401, 'Unauthenticated'); } return Tenant::findOrFail($tenantId); } }