name('api.v1.health'); Route::prefix('auth')->name('api.v1.auth.')->group(function () { Route::middleware('throttle:10,1')->group(function () { Route::post('/register', RegisterController::class)->name('register'); Route::post('/login', LoginController::class)->name('login'); Route::post('/forgot-password', ForgotPasswordController::class)->name('forgot-password'); Route::post('/reset-password', ResetPasswordController::class)->name('reset-password'); Route::post('/otp/verify', VerifyOtpController::class)->name('otp.verify'); Route::post('/otp/resend', ResendOtpController::class)->name('otp.resend'); }); Route::middleware('auth:sanctum')->group(function () { Route::post('/logout', LogoutController::class)->name('logout'); Route::post('/refresh', RefreshTokenController::class)->name('refresh'); Route::get('/me', MeController::class)->name('me'); Route::post('/email/resend', [VerifyEmailController::class, 'resend'])->name('email.resend'); }); // Public verification link from the email itself. URL is signed by // Laravel's URL signer (see VerifyEmail::verificationUrl) — the // `signed` middleware rejects tampered links. Route::get('/email/verify/{id}/{hash}', [VerifyEmailController::class, 'verify']) ->middleware('signed') ->name('verification.verify'); }); Route::middleware('auth:sanctum')->prefix('me')->name('api.v1.me.')->group(function () { Route::patch('/', UpdateSelfController::class)->name('update'); Route::get('/profile', [SelfProfileController::class, 'show'])->name('profile.show'); Route::patch('/profile', [SelfProfileController::class, 'update'])->name('profile.update'); Route::get('/notification-preferences', [NotificationPreferencesController::class, 'show'])->name('notification-preferences.show'); Route::patch('/notification-preferences', [NotificationPreferencesController::class, 'update'])->name('notification-preferences.update'); Route::post('/password', ChangePasswordController::class)->name('password.change'); Route::post('/payments/code-purchase', [PaymentController::class, 'initiateResidentPurchase'])->name('payments.code-purchase'); Route::get('/payments/{payment}', [PaymentController::class, 'show'])->name('payments.show'); Route::get('/household', [MyHouseholdController::class, 'show'])->name('household.show'); Route::get('/collections', [MyCollectionsController::class, 'index'])->name('collections'); Route::get('/upcoming-pickups', [UpcomingPickupsController::class, 'index'])->name('upcoming-pickups'); Route::get('/notifications', [MyNotificationsController::class, 'index'])->name('notifications.index'); Route::get('/notifications/unread-count', [MyNotificationsController::class, 'unreadCount'])->name('notifications.unread-count'); Route::post('/notifications/mark-all-read', [MyNotificationsController::class, 'markAllRead'])->name('notifications.mark-all-read'); Route::post('/notifications/{id}/read', [MyNotificationsController::class, 'markRead'])->name('notifications.mark-read'); Route::get('/sales', [\App\Http\Controllers\Api\V1\Me\MySalesController::class, 'index'])->name('sales.index'); Route::get('/sales/{sale}/receipt', [\App\Http\Controllers\Api\V1\Me\MySalesController::class, 'downloadReceipt'])->name('sales.receipt'); Route::get('/live/trucks', [MyLiveTrucksController::class, 'trucks'])->name('live.trucks'); }); // Public partner stores (for residents browsing where to buy codes) Route::prefix('partner-stores')->name('api.v1.partner-stores.')->group(function () { Route::get('/nearby', [PartnerStorePublicController::class, 'nearby'])->name('nearby'); Route::get('/{store}', [PartnerStorePublicController::class, 'show'])->name('show'); }); // PayMongo webhook — no auth, signature verified by driver Route::post('/webhooks/paymongo', [PaymentController::class, 'paymongoWebhook']) ->name('api.v1.webhooks.paymongo'); // Public tenant lookup — used by every app's pre-login LGU-code screen Route::get('/tenants/lookup', [TenantLookupController::class, 'lookup'])->name('api.v1.tenants.lookup'); // Driver telemetry Route::middleware(['auth:sanctum', 'role:driver'])->prefix('driver')->name('api.v1.driver.')->group(function () { Route::post('/trucks/{truck}/location', [DriverLocationController::class, 'store'])->name('trucks.location'); Route::post('/trips/{trip}/detour', [\App\Http\Controllers\Api\V1\Driver\TripDetourController::class, 'start'])->name('trips.detour.start'); Route::post('/trips/{trip}/resume', [\App\Http\Controllers\Api\V1\Driver\TripDetourController::class, 'resume'])->name('trips.detour.resume'); }); // Store Portal Route::middleware(['auth:sanctum', 'role:store_partner'])->prefix('store')->name('api.v1.store.')->group(function () { Route::get('/dashboard', [StorePortalController::class, 'dashboard'])->name('dashboard'); Route::get('/analytics', [StorePortalController::class, 'analytics'])->name('analytics'); Route::post('/sales', [StorePortalController::class, 'recordSale'])->name('sales.store'); Route::get('/sales', [StorePortalController::class, 'salesHistory'])->name('sales.index'); Route::get('/sales/{sale}', [StorePortalController::class, 'downloadReceipt'])->name('sales.receipt'); Route::get('/inventory', [StorePortalController::class, 'inventoryHistory'])->name('inventory.index'); Route::get('/households', [StorePortalController::class, 'searchHouseholds'])->name('households.search'); Route::get('/settings', [StorePortalController::class, 'getSettings'])->name('settings'); }); // Admin live tracking Route::middleware(['auth:sanctum', 'role:admin'])->prefix('admin/live')->name('api.v1.admin.live.')->group(function () { Route::get('/trucks', [AdminLiveTrackingController::class, 'trucks'])->name('trucks'); Route::get('/trucks/{truck}/trail', [AdminLiveTrackingController::class, 'trail'])->name('trucks.trail'); Route::get('/trucks/{truck}/active-trip', [AdminLiveTrackingController::class, 'activeTrip'])->name('trucks.active-trip'); }); // Admin payments Route::middleware(['auth:sanctum', 'role:admin'])->prefix('admin/payments')->name('api.v1.admin.payments.')->group(function () { Route::get('/', [PaymentController::class, 'adminIndex'])->name('index'); Route::post('/{payment}/mark-paid', [PaymentController::class, 'adminMarkPaid'])->name('mark-paid'); }); Route::prefix('admin/users') ->name('api.v1.admin.users.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminUserController::class, 'index'])->name('index'); Route::get('/{user:uuid}', [AdminUserController::class, 'show'])->name('show'); Route::patch('/{user:uuid}', [AdminUserController::class, 'update'])->name('update'); Route::delete('/{user:uuid}', [AdminUserController::class, 'destroy'])->name('destroy'); Route::post('/{user:uuid}/suspend', [AdminUserController::class, 'suspend'])->name('suspend'); Route::post('/{user:uuid}/activate', [AdminUserController::class, 'activate'])->name('activate'); Route::post('/{user:uuid}/approve-profile', [AdminUserController::class, 'approveProfile'])->name('approve-profile'); Route::post('/{user:uuid}/reject-profile', [AdminUserController::class, 'rejectProfile'])->name('reject-profile'); Route::post('/{user:uuid}/group-into-household', [AdminUserController::class, 'groupIntoHousehold'])->name('group-into-household'); }); Route::prefix('households') ->name('api.v1.households.') ->middleware(['auth:sanctum']) ->group(function () { Route::post('/', [HouseholdController::class, 'store'])->name('store'); Route::get('/{household}', [HouseholdController::class, 'show'])->name('show'); Route::patch('/{household}', [HouseholdController::class, 'update'])->name('update'); Route::post('/{household}/proof', [HouseholdController::class, 'uploadProof'])->name('proof.upload'); Route::post('/{household}/members', [HouseholdController::class, 'addMember'])->name('members.add'); Route::delete('/{household}/members/{member}', [HouseholdController::class, 'removeMember'])->name('members.remove'); }); Route::prefix('admin/households') ->name('api.v1.admin.households.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminHouseholdController::class, 'index'])->name('index'); Route::post('/', [AdminHouseholdController::class, 'store'])->name('store'); Route::post('/bulk-approve', [BulkActionController::class, 'approveHouseholds'])->name('bulk-approve'); Route::get('/{household}', [AdminHouseholdController::class, 'show'])->name('show'); Route::patch('/{household}', [AdminHouseholdController::class, 'update'])->name('update'); Route::get('/{household}/proof', [AdminHouseholdController::class, 'proof'])->name('proof'); Route::patch('/{household}/drop-off', [AdminHouseholdController::class, 'overrideDop'])->name('drop-off'); Route::post('/{household}/approve', [AdminHouseholdController::class, 'approve'])->name('approve'); Route::post('/{household}/reject', [AdminHouseholdController::class, 'reject'])->name('reject'); Route::post('/{household}/members', [AdminHouseholdController::class, 'addMember'])->name('members.store'); Route::patch('/{household}/members/{member}', [AdminHouseholdController::class, 'updateMember'])->name('members.update'); Route::delete('/{household}/members/{member}', [AdminHouseholdController::class, 'removeMember'])->name('members.destroy'); }); Route::prefix('admin/bulk') ->name('api.v1.admin.bulk.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::post('/users/suspend', [BulkActionController::class, 'suspendUsers'])->name('users.suspend'); Route::post('/users/activate', [BulkActionController::class, 'activateUsers'])->name('users.activate'); Route::post('/qr-codes/void', [BulkActionController::class, 'voidQrCodes'])->name('qr-codes.void'); }); Route::prefix('drop-off-points')->name('api.v1.drop-off-points.')->group(function () { Route::get('/nearby', [DropOffPointController::class, 'nearby'])->name('nearby'); Route::get('/{drop_off_point}', [DropOffPointController::class, 'show'])->name('show'); }); Route::prefix('admin/drop-off-points') ->name('api.v1.admin.drop-off-points.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminDropOffPointController::class, 'index'])->name('index'); Route::post('/', [AdminDropOffPointController::class, 'store'])->name('store'); Route::get('/{drop_off_point}', [AdminDropOffPointController::class, 'show'])->name('show'); Route::patch('/{drop_off_point}', [AdminDropOffPointController::class, 'update'])->name('update'); Route::delete('/{drop_off_point}', [AdminDropOffPointController::class, 'destroy'])->name('destroy'); Route::post('/{drop_off_point}/capacity', [AdminDropOffPointController::class, 'logCapacity'])->name('capacity.store'); }); Route::prefix('admin/dumpsites') ->name('api.v1.admin.dumpsites.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminDumpsiteController::class, 'index'])->name('index'); Route::post('/', [AdminDumpsiteController::class, 'store'])->name('store'); Route::get('/{dumpsite}', [AdminDumpsiteController::class, 'show'])->name('show'); Route::patch('/{dumpsite}', [AdminDumpsiteController::class, 'update'])->name('update'); Route::delete('/{dumpsite}', [AdminDumpsiteController::class, 'destroy'])->name('destroy'); }); Route::prefix('admin/qr-batches') ->name('api.v1.admin.qr-batches.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminQrBatchController::class, 'index'])->name('index'); Route::post('/', [AdminQrBatchController::class, 'store'])->name('store'); Route::get('/{qr_code_batch}', [AdminQrBatchController::class, 'show'])->name('show'); Route::post('/{qr_code_batch}/mark-printed', [AdminQrBatchController::class, 'markPrinted'])->name('mark-printed'); Route::post('/{qr_code_batch}/void', [AdminQrBatchController::class, 'voidBatch'])->name('void'); Route::delete('/{qr_code_batch}', [AdminQrBatchController::class, 'destroy'])->name('destroy'); }); Route::get('admin/qr-batches/{qr_code_batch}/print.pdf', [AdminQrBatchController::class, 'printPdf']) ->middleware([ 'auth:sanctum', 'role:admin,super_admin', ]) ->name('api.v1.admin.qr-batches.print-pdf'); Route::prefix('admin/qr-codes') ->name('api.v1.admin.qr-codes.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/{serial}', [AdminQrCodeController::class, 'show'])->name('show'); Route::post('/{qr_code}/void', [AdminQrCodeController::class, 'void'])->name('void'); }); Route::middleware('auth:sanctum')->prefix('me/qr-codes')->name('api.v1.me.qr-codes.')->group(function () { Route::get('/', [MyQrCodeController::class, 'index'])->name('index'); Route::get('/balance', [MyQrCodeController::class, 'balance'])->name('balance'); Route::post('/{qr_code}/activate', [MyQrCodeController::class, 'activate'])->name('activate'); }); Route::prefix('admin/routes') ->name('api.v1.admin.routes.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminRouteController::class, 'index'])->name('index'); Route::post('/', [AdminRouteController::class, 'store'])->name('store'); Route::get('/{route}', [AdminRouteController::class, 'show'])->name('show'); Route::patch('/{route}', [AdminRouteController::class, 'update'])->name('update'); Route::delete('/{route}', [AdminRouteController::class, 'destroy'])->name('destroy'); Route::post('/{route}/clone', [AdminRouteController::class, 'clone'])->name('clone'); }); Route::prefix('admin/trucks') ->name('api.v1.admin.trucks.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminTruckController::class, 'index'])->name('index'); Route::post('/', [AdminTruckController::class, 'store'])->name('store'); Route::get('/{truck}', [AdminTruckController::class, 'show'])->name('show'); Route::patch('/{truck}', [AdminTruckController::class, 'update'])->name('update'); Route::delete('/{truck}', [AdminTruckController::class, 'destroy'])->name('destroy'); }); Route::prefix('admin/teams') ->name('api.v1.admin.teams.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminTeamController::class, 'index'])->name('index'); Route::post('/', [AdminTeamController::class, 'store'])->name('store'); Route::get('/{team}', [AdminTeamController::class, 'show'])->name('show'); Route::patch('/{team}', [AdminTeamController::class, 'update'])->name('update'); Route::delete('/{team}', [AdminTeamController::class, 'destroy'])->name('destroy'); }); Route::prefix('admin/partner-stores') ->name('api.v1.admin.partner-stores.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminPartnerStoreController::class, 'index'])->name('index'); Route::post('/', [AdminPartnerStoreController::class, 'store'])->name('store'); Route::patch('/{store}', [AdminPartnerStoreController::class, 'update'])->name('update'); Route::post('/{store}/issue-inventory', [AdminPartnerStoreController::class, 'issueInventory'])->name('issue-inventory'); Route::post('/{store}/sales', [AdminPartnerStoreController::class, 'recordSale'])->name('sales'); Route::get('/{store}/purchases', [AdminPartnerStoreController::class, 'purchases'])->name('purchases'); Route::get('/{store}/sales-history', [AdminPartnerStoreController::class, 'salesHistory'])->name('sales-history'); Route::get('/{store}/inventory-history', [AdminPartnerStoreController::class, 'inventoryLog'])->name('inventory-history'); Route::get('/{store}/analytics', [AdminPartnerStoreController::class, 'analytics'])->name('analytics'); Route::post('/{store}/report-issue', [AdminPartnerStoreController::class, 'reportIssue'])->name('report-issue'); Route::post('/{store}/adjust-stock', [AdminPartnerStoreController::class, 'adjustInventory'])->name('adjust-stock'); Route::post('/{store}/settle', [AdminPartnerStoreController::class, 'recordPayment'])->name('settle'); Route::get('/{store}/settlements', [AdminPartnerStoreController::class, 'settlementHistory'])->name('settlements'); Route::get('/{store}/qr-codes/{qrCode:serial}/print', [AdminPartnerStoreController::class, 'printReplacement'])->name('print-replacement'); Route::get('/{store}', [AdminPartnerStoreController::class, 'show'])->name('show'); }); Route::prefix('admin/reports') ->name('api.v1.admin.reports.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/daily-collection', [AdminReportController::class, 'dailyCollection'])->name('daily-collection'); Route::get('/trip-performance', [AdminReportController::class, 'tripPerformance'])->name('trip-performance'); Route::get('/store-sales', [AdminReportController::class, 'storeSales'])->name('store-sales'); Route::get('/compliance.csv', [AdminReportController::class, 'complianceCsv'])->name('compliance-csv'); Route::post('/rebuild', [AdminReportController::class, 'rebuild'])->name('rebuild'); }); Route::prefix('admin/trips') ->name('api.v1.admin.trips.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [AdminTripController::class, 'index'])->name('index'); Route::post('/', [AdminTripController::class, 'store'])->name('store'); Route::get('/{trip}', [AdminTripController::class, 'show'])->name('show'); Route::patch('/{trip}', [AdminTripController::class, 'update'])->name('update'); Route::delete('/{trip}', [AdminTripController::class, 'destroy'])->name('destroy'); Route::post('/{trip}/cancel', [AdminTripController::class, 'cancel'])->name('cancel'); }); Route::prefix('scanner') ->name('api.v1.scanner.') ->middleware(['auth:sanctum', 'role:scanner,admin']) ->group(function () { Route::get('/team-details', [ScannerController::class, 'teamDetails'])->name('team-details'); Route::post('/scan', [ScannerController::class, 'scan'])->name('scan'); Route::post('/scan/bulk', [ScannerController::class, 'bulk'])->name('scan.bulk'); }); Route::prefix('driver/trips') ->name('api.v1.driver.trips.') ->middleware(['auth:sanctum', 'role:driver']) ->group(function () { Route::get('/', [DriverTripController::class, 'index'])->name('index'); Route::get('/{trip}', [DriverTripController::class, 'show'])->name('show'); Route::post('/{trip}/start', [DriverTripController::class, 'start'])->name('start'); Route::post('/{trip}/stops/{stop}/arrive', [DriverTripController::class, 'arrive'])->name('arrive'); Route::post('/{trip}/stops/{stop}/depart', [DriverTripController::class, 'depart'])->name('depart'); Route::post('/{trip}/stops/{stop}/skip', [DriverTripController::class, 'skip'])->name('skip'); Route::post('/{trip}/incident', [DriverTripController::class, 'reportIncident'])->name('incident'); Route::post('/{trip}/arrive-dumpsite', [DriverTripController::class, 'arriveDumpsite'])->name('arrive-dumpsite'); Route::post('/{trip}/release-load', [DriverTripController::class, 'releaseLoad'])->name('release-load'); Route::post('/{trip}/complete', [DriverTripController::class, 'complete'])->name('complete'); }); Route::prefix('helper') ->name('api.v1.helper.') ->middleware(['auth:sanctum', 'role:helper']) ->group(function () { Route::get('/assignment', [HelperAssignmentController::class, 'assignment'])->name('assignment'); Route::get('/active-trip', [HelperAssignmentController::class, 'activeTrip'])->name('active-trip'); Route::post('/assignment/accept', [HelperAssignmentController::class, 'accept'])->name('accept'); Route::post('/assignment/reject', [HelperAssignmentController::class, 'reject'])->name('reject'); Route::get('/history', [HelperAssignmentController::class, 'history'])->name('history'); }); Route::middleware('auth:sanctum')->prefix('households')->name('api.v1.households.')->group(function () { Route::post('/{household}/reassign-drop-off', [HouseholdController::class, 'reassignDropOff']) ->name('reassign-drop-off'); }); Route::middleware('auth:sanctum')->group(function () { Route::get('/me', MeController::class)->name('api.v1.me'); }); Route::prefix('geo')->name('api.v1.geo.')->group(function () { Route::get('/regions', RegionController::class)->name('regions.index'); Route::get('/provinces', ProvinceController::class)->name('provinces.index'); Route::get('/cities', CityMunicipalityController::class)->name('cities.index'); Route::get('/barangays', BarangayController::class)->name('barangays.index'); Route::post('/resolve', ResolveLocationController::class)->name('resolve'); }); Route::prefix('service-areas') ->name('api.v1.service-areas.') ->middleware(['auth:sanctum', 'role:admin']) ->group(function () { Route::get('/', [ServiceAreaController::class, 'index'])->name('index'); Route::post('/', [ServiceAreaController::class, 'store'])->name('store'); Route::get('/{service_area}', [ServiceAreaController::class, 'show'])->name('show'); Route::patch('/{service_area}', [ServiceAreaController::class, 'update'])->name('update'); Route::delete('/{service_area}', [ServiceAreaController::class, 'destroy'])->name('destroy'); Route::post('/{service_area}/barangays', [ServiceAreaController::class, 'attachBarangays'])->name('barangays.attach'); Route::delete('/{service_area}/barangays/{barangay}', [ServiceAreaController::class, 'detachBarangay'])->name('barangays.detach'); }); Route::prefix('super-admin') ->name('api.v1.super-admin.') ->middleware(['auth:sanctum', 'role:super_admin']) ->group(function () { Route::apiResource('tenants', SuperAdminTenantController::class); Route::apiResource('users', SuperAdminUserController::class)->scoped(['user' => 'uuid']); Route::apiResource('barangays', SuperAdminBarangayController::class); });