Wire seller push notifications with FCM, inbox API, and payment milestones.
Deploy Ladill Mini / deploy (push) Successful in 52s

Sellers get instant payment alerts on Android; withdrawals respect payout prefs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-11 19:04:44 +00:00
co-authored by Cursor
parent ad96754099
commit 136bfbea47
15 changed files with 626 additions and 5 deletions
@@ -158,6 +158,7 @@ class AuthController extends Controller
private function issueToken(User $user, ?string $deviceName): JsonResponse
{
QrTeamMember::linkPendingInvitesFor($user);
$user->update(['last_app_active_at' => now()]);
$token = $user->createToken($deviceName ?: 'Ladill Mini Android', ['mini:read', 'mini:write']);
@@ -0,0 +1,76 @@
<?php
namespace App\Http\Controllers\Api\Mini;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class NotificationController extends Controller
{
public function index(Request $request): JsonResponse
{
$account = ladill_account();
$notifications = $account->notifications()
->latest()
->limit(100)
->get()
->map(fn ($n) => $this->present($n));
$unread = $account->unreadNotifications()->count();
return response()->json([
'data' => $notifications,
'unread_count' => $unread,
]);
}
public function unreadCount(Request $request): JsonResponse
{
$account = ladill_account();
return response()->json([
'data' => [
'unread_count' => $account->unreadNotifications()->count(),
],
]);
}
public function markAsRead(Request $request, string $id): JsonResponse
{
$notification = ladill_account()
->notifications()
->where('id', $id)
->first();
if ($notification) {
$notification->markAsRead();
}
return response()->json(['data' => ['success' => true]]);
}
public function markAllAsRead(Request $request): JsonResponse
{
ladill_account()->unreadNotifications->markAsRead();
return response()->json(['data' => ['success' => true]]);
}
/** @return array<string, mixed> */
private function present(mixed $notification): array
{
return [
'id' => $notification->id,
'type' => class_basename($notification->type),
'title' => $notification->data['title'] ?? 'Notification',
'message' => $notification->data['message'] ?? '',
'icon' => $notification->data['icon'] ?? 'bell',
'milestone' => $notification->data['milestone'] ?? null,
'url' => $notification->data['url'] ?? null,
'read_at' => $notification->read_at?->toIso8601String(),
'created_at' => $notification->created_at?->toIso8601String(),
];
}
}
@@ -0,0 +1,51 @@
<?php
namespace App\Http\Controllers\Api\Mini;
use App\Http\Controllers\Controller;
use App\Models\UserPushToken;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class PushTokenController extends Controller
{
public function store(Request $request): JsonResponse
{
$data = $request->validate([
'token' => ['required', 'string', 'max:512'],
'platform' => ['nullable', 'string', 'max:32'],
'device_name' => ['nullable', 'string', 'max:120'],
]);
$user = $request->user();
$now = now();
UserPushToken::updateOrCreate(
['token' => $data['token']],
[
'user_id' => $user->id,
'platform' => $data['platform'] ?? 'android',
'device_name' => $data['device_name'] ?? $user->currentAccessToken()?->name,
'last_seen_at' => $now,
],
);
$user->update(['last_app_active_at' => $now]);
return response()->json(['data' => ['registered' => true]]);
}
public function destroy(Request $request): JsonResponse
{
$data = $request->validate([
'token' => ['required', 'string', 'max:512'],
]);
$request->user()
->pushTokens()
->where('token', $data['token'])
->delete();
return response()->json(['data' => ['removed' => true]]);
}
}
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\Mini;
use App\Http\Controllers\Api\Concerns\CallsIdentityApi;
use App\Http\Controllers\Controller;
use App\Services\Billing\BillingClient;
use App\Services\Notifications\MiniNotificationService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
@@ -14,7 +15,10 @@ class WalletController extends Controller
{
use CallsIdentityApi;
public function __construct(private BillingClient $billing) {}
public function __construct(
private BillingClient $billing,
private MiniNotificationService $notifications,
) {}
public function show(): JsonResponse
{
@@ -118,6 +122,11 @@ class WalletController extends Controller
]);
$this->rethrowValidation($response, 'amount');
$this->notifications->withdrawalSubmitted(
ladill_account(),
(float) $data['amount'],
);
return response()->json(['data' => $response->json('data', [])]);
}
}