Add Ladill POS v1 — register, Pay checkout, and commerce links.
Deploy Ladill Mini / deploy (push) Successful in 23s
Deploy Ladill Mini / deploy (push) Successful in 23s
Staff-facing counter register at pos.ladill.com with catalog cart, cash and MoMo/card checkout via Ladill Pay, CRM timeline/import, invoice prefill, and Merchant catalog import. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\AuthController;
|
||||
use App\Http\Controllers\Api\MeController;
|
||||
use App\Http\Controllers\Api\Mini\AccountController as MiniAccountController;
|
||||
use App\Http\Controllers\Api\Mini\AfiaController as MiniAfiaController;
|
||||
use App\Http\Controllers\Api\Mini\NotificationController as MiniNotificationController;
|
||||
use App\Http\Controllers\Api\Mini\OverviewController as MiniOverviewController;
|
||||
use App\Http\Controllers\Api\Mini\PushTokenController as MiniPushTokenController;
|
||||
use App\Http\Controllers\Api\Mini\PaymentQrController as MiniPaymentQrController;
|
||||
use App\Http\Controllers\Api\Mini\PaymentsController as MiniPaymentsController;
|
||||
use App\Http\Controllers\Api\Mini\PayoutsController as MiniPayoutsController;
|
||||
use App\Http\Controllers\Api\Mini\SupportController as MiniSupportController;
|
||||
use App\Http\Controllers\Api\Mini\WalletController as MiniWalletController;
|
||||
use App\Http\Controllers\Api\QrCodeController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::prefix('v1')->group(function () {
|
||||
// Mobile token auth (Ladill Mini Android). Public — proxies to the central
|
||||
// Ladill identity API and issues a Sanctum token.
|
||||
Route::post('/auth/login', [AuthController::class, 'login'])->middleware('throttle:10,1')->name('api.auth.login');
|
||||
Route::post('/auth/register', [AuthController::class, 'register'])->middleware('throttle:10,1')->name('api.auth.register');
|
||||
|
||||
Route::middleware(['auth:sanctum', \App\Http\Middleware\SetActingAccount::class])->group(function () {
|
||||
Route::post('/auth/logout', [AuthController::class, 'logout'])->name('api.auth.logout');
|
||||
Route::get('/me', MeController::class);
|
||||
|
||||
Route::get('/qr-codes', [QrCodeController::class, 'index']);
|
||||
Route::post('/qr-codes', [QrCodeController::class, 'store']);
|
||||
Route::get('/qr-codes/{qrCode}', [QrCodeController::class, 'show'])->whereNumber('qrCode');
|
||||
Route::patch('/qr-codes/{qrCode}', [QrCodeController::class, 'update'])->whereNumber('qrCode');
|
||||
Route::get('/qr-codes/{qrCode}/analytics', [QrCodeController::class, 'analytics'])->whereNumber('qrCode');
|
||||
|
||||
// Ladill Mini — payments product.
|
||||
Route::get('/mini/overview', MiniOverviewController::class)->name('api.mini.overview');
|
||||
|
||||
Route::get('/mini/notifications', [MiniNotificationController::class, 'index'])->name('api.mini.notifications.index');
|
||||
Route::get('/mini/notifications/unread-count', [MiniNotificationController::class, 'unreadCount'])->name('api.mini.notifications.unread-count');
|
||||
Route::post('/mini/notifications/{id}/read', [MiniNotificationController::class, 'markAsRead'])->name('api.mini.notifications.read');
|
||||
Route::post('/mini/notifications/mark-all-read', [MiniNotificationController::class, 'markAllAsRead'])->name('api.mini.notifications.mark-all-read');
|
||||
|
||||
Route::post('/mini/push-token', [MiniPushTokenController::class, 'store'])->name('api.mini.push-token.store');
|
||||
Route::delete('/mini/push-token', [MiniPushTokenController::class, 'destroy'])->name('api.mini.push-token.destroy');
|
||||
Route::post('/mini/afia/chat', [MiniAfiaController::class, 'chat'])->middleware('throttle:30,1')->name('api.mini.afia.chat');
|
||||
Route::get('/mini/payment-qrs', [MiniPaymentQrController::class, 'index'])->name('api.mini.payment-qrs.index');
|
||||
Route::post('/mini/payment-qrs', [MiniPaymentQrController::class, 'store'])->name('api.mini.payment-qrs.store');
|
||||
Route::get('/mini/payment-qrs/{paymentQr}', [MiniPaymentQrController::class, 'show'])->whereNumber('paymentQr')->name('api.mini.payment-qrs.show');
|
||||
Route::patch('/mini/payment-qrs/{paymentQr}', [MiniPaymentQrController::class, 'update'])->whereNumber('paymentQr')->name('api.mini.payment-qrs.update');
|
||||
Route::delete('/mini/payment-qrs/{paymentQr}', [MiniPaymentQrController::class, 'destroy'])->whereNumber('paymentQr')->name('api.mini.payment-qrs.destroy');
|
||||
Route::get('/mini/payment-qrs/{paymentQr}/preview.png', [MiniPaymentQrController::class, 'preview'])->whereNumber('paymentQr')->name('api.mini.payment-qrs.preview');
|
||||
|
||||
Route::get('/mini/payments', [MiniPaymentsController::class, 'index'])->name('api.mini.payments.index');
|
||||
Route::get('/mini/payouts', MiniPayoutsController::class)->name('api.mini.payouts');
|
||||
|
||||
// Account self-service (Settings, Wallet, Support) — keeps Mini usable
|
||||
// entirely in-app without the website.
|
||||
Route::get('/mini/account/settings', [MiniAccountController::class, 'settings'])->name('api.mini.account.settings');
|
||||
Route::put('/mini/account/settings', [MiniAccountController::class, 'updateSettings'])->name('api.mini.account.settings.update');
|
||||
Route::put('/mini/account/profile', [MiniAccountController::class, 'updateProfile'])->name('api.mini.account.profile');
|
||||
Route::post('/mini/account/avatar', [MiniAccountController::class, 'uploadAvatar'])->name('api.mini.account.avatar');
|
||||
Route::post('/mini/account/change-password', [MiniAccountController::class, 'changePassword'])->name('api.mini.account.change-password');
|
||||
|
||||
Route::get('/mini/wallet', [MiniWalletController::class, 'show'])->name('api.mini.wallet');
|
||||
Route::post('/mini/wallet/topup', [MiniWalletController::class, 'topup'])->name('api.mini.wallet.topup');
|
||||
Route::get('/mini/wallet/banks', [MiniWalletController::class, 'banks'])->name('api.mini.wallet.banks');
|
||||
Route::get('/mini/wallet/payout-account', [MiniWalletController::class, 'payoutAccount'])->name('api.mini.wallet.payout-account');
|
||||
Route::put('/mini/wallet/payout-account', [MiniWalletController::class, 'updatePayoutAccount'])->name('api.mini.wallet.payout-account.update');
|
||||
Route::get('/mini/wallet/withdrawals', [MiniWalletController::class, 'withdrawals'])->name('api.mini.wallet.withdrawals');
|
||||
Route::post('/mini/wallet/withdraw', [MiniWalletController::class, 'withdraw'])->name('api.mini.wallet.withdraw');
|
||||
Route::match(['put', 'post'], '/mini/wallet/auto-withdraw', [MiniWalletController::class, 'updateAutoWithdraw'])->name('api.mini.wallet.auto-withdraw');
|
||||
|
||||
Route::get('/mini/support/tickets', [MiniSupportController::class, 'tickets'])->name('api.mini.support.tickets');
|
||||
Route::post('/mini/support/tickets', [MiniSupportController::class, 'store'])->name('api.mini.support.tickets.store');
|
||||
Route::get('/mini/support/tickets/{ticket}', [MiniSupportController::class, 'ticket'])->whereNumber('ticket')->name('api.mini.support.ticket');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Schedule;
|
||||
|
||||
Artisan::command('inspire', function () {
|
||||
$this->comment(Inspiring::quote());
|
||||
})->purpose('Display an inspiring quote');
|
||||
|
||||
Schedule::command('mini:cancel-stale-payments')->hourly()->withoutOverlapping();
|
||||
Schedule::command('mini:process-auto-withdrawals')->everyFiveMinutes()->withoutOverlapping();
|
||||
|
||||
Schedule::command('hosting:check-node-health --all')->everyFiveMinutes()->withoutOverlapping();
|
||||
Schedule::command('hosting:sync-account-usage')->hourly()->withoutOverlapping();
|
||||
Schedule::command('hosting:recalculate-node-capacity --shared-only')->daily()->withoutOverlapping();
|
||||
Schedule::command('hosting:process-expired-accounts')->daily()->withoutOverlapping();
|
||||
Schedule::command('hosting:notify-expiring')->daily()->withoutOverlapping();
|
||||
Schedule::command('hosting:retry-pending-fulfillment')->everyFifteenMinutes()->withoutOverlapping();
|
||||
Schedule::command('ssl:renew')->daily()->withoutOverlapping();
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Auth\SsoLoginController;
|
||||
use App\Http\Controllers\Pos\DashboardController;
|
||||
use App\Http\Controllers\Pos\ProductController;
|
||||
use App\Http\Controllers\Pos\RegisterController;
|
||||
use App\Http\Controllers\Pos\SaleController;
|
||||
use App\Http\Controllers\Pos\SettingsController;
|
||||
use App\Http\Controllers\NotificationController;
|
||||
use App\Http\Controllers\WalletBalanceController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', fn () => auth()->check()
|
||||
? redirect()->route('pos.dashboard')
|
||||
: redirect()->route('sso.connect'))->name('pos.root');
|
||||
|
||||
Route::get('/login', [SsoLoginController::class, 'connect'])->name('login');
|
||||
Route::get('/sso/connect', [SsoLoginController::class, 'connect'])->name('sso.connect');
|
||||
Route::get('/sso/callback', [SsoLoginController::class, 'callback'])->name('sso.callback');
|
||||
Route::post('/logout', [SsoLoginController::class, 'logout'])->name('logout');
|
||||
Route::get('/sso/logout-bridge', [SsoLoginController::class, 'logoutBridge'])->name('sso.logout-bridge');
|
||||
Route::get('/sso/logout-frontchannel', [SsoLoginController::class, 'frontchannelLogout'])->name('sso.logout-frontchannel');
|
||||
Route::get('/sso/platform-signed-out', [SsoLoginController::class, 'platformSignedOut'])->name('sso.platform-signed-out');
|
||||
Route::get('/signed-out', fn () => auth()->check() ? redirect()->route('pos.dashboard') : view('auth.signed-out'))->name('pos.signed-out');
|
||||
|
||||
Route::get('/sales/{sale}/callback', [SaleController::class, 'callback'])->name('pos.sales.callback');
|
||||
|
||||
Route::middleware(['auth', 'platform.session'])->group(function () {
|
||||
Route::get('/wallet/balance', [WalletBalanceController::class, 'balance'])->name('wallet.balance');
|
||||
|
||||
Route::get('/notifications', [NotificationController::class, 'index'])->name('notifications.index');
|
||||
Route::get('/notifications/unread', [NotificationController::class, 'unread'])->name('notifications.unread');
|
||||
Route::post('/notifications/{id}/read', [NotificationController::class, 'markAsRead'])->name('notifications.mark-read');
|
||||
Route::post('/notifications/mark-all-read', [NotificationController::class, 'markAllAsRead'])->name('notifications.mark-all-read');
|
||||
|
||||
Route::get('/dashboard', [DashboardController::class, 'index'])->name('pos.dashboard');
|
||||
|
||||
Route::get('/register', [RegisterController::class, 'index'])->name('pos.register');
|
||||
Route::post('/register/charge', [RegisterController::class, 'charge'])->name('pos.register.charge');
|
||||
|
||||
Route::get('/sales', [SaleController::class, 'index'])->name('pos.sales.index');
|
||||
Route::get('/sales/{sale}', [SaleController::class, 'show'])->name('pos.sales.show');
|
||||
|
||||
Route::get('/products', [ProductController::class, 'index'])->name('pos.products.index');
|
||||
Route::get('/products/create', [ProductController::class, 'create'])->name('pos.products.create');
|
||||
Route::post('/products', [ProductController::class, 'store'])->name('pos.products.store');
|
||||
Route::get('/products/{product}/edit', [ProductController::class, 'edit'])->name('pos.products.edit');
|
||||
Route::put('/products/{product}', [ProductController::class, 'update'])->name('pos.products.update');
|
||||
Route::delete('/products/{product}', [ProductController::class, 'destroy'])->name('pos.products.destroy');
|
||||
|
||||
Route::get('/settings', [SettingsController::class, 'index'])->name('pos.settings');
|
||||
Route::put('/settings', [SettingsController::class, 'update'])->name('pos.settings.update');
|
||||
Route::post('/settings/import-crm', [SettingsController::class, 'importCrm'])->name('pos.settings.import-crm');
|
||||
Route::post('/settings/import-merchant', [SettingsController::class, 'importMerchant'])->name('pos.settings.import-merchant');
|
||||
|
||||
Route::get('/wallet', fn () => redirect()->away(ladill_account_url('/wallet')))->name('pos.wallet');
|
||||
});
|
||||
Reference in New Issue
Block a user