πŸ” CASHCARD SYSTEM β€” FULL IMPLEMENTATION (A–C) STACK CONFIRMED Laravel 11 (or latest LTS) Vue 3 + Inertia MySQL 8 Redis (queues) Sanctum Service–Repository pattern 🧱 A. DATABASE MIGRATIONS (FOUNDATION) 1️⃣ users Schema::create("users", function (Blueprint $table) { $table->id(); $table->uuid("uuid")->unique(); $table->string("name"); $table->string("email")->unique(); $table->string("password"); $table->boolean("active")->default(true); $table->timestamps(); }); 2️⃣ roles / role_user Schema::create("roles", function (Blueprint $table) { $table->id(); $table->string("name")->unique(); }); Schema::create("role_user", function (Blueprint $table) { $table->foreignId("user_id")->constrained(); $table->foreignId("role_id")->constrained(); }); 3️⃣ clients Schema::create("clients", function (Blueprint $table) { $table->id(); $table->uuid("uuid")->unique(); $table->enum("type", ["individual", "corporate"]); $table->string("full_name"); $table->string("kyc_level")->default("basic"); $table->string("status")->default("active"); $table->timestamps(); }); 4️⃣ accounts Schema::create("accounts", function (Blueprint $table) { $table->id(); $table->uuid("uuid")->unique(); $table->foreignId("client_id")->constrained(); $table->string("account_number")->unique(); $table->decimal("balance", 15, 2)->default(0); $table->string("status")->default("active"); $table->timestamps(); }); 5️⃣ cards Schema::create("cards", function (Blueprint $table) { $table->id(); $table->uuid("uuid")->unique(); $table->foreignId("account_id")->constrained(); $table->string("pan")->unique(); $table->string("pan_masked"); $table->string("cvv_hash"); $table->tinyInteger("expiry_month"); $table->smallInteger("expiry_year"); $table->string("status"); $table->timestamps(); $table->softDeletes(); }); 6️⃣ printers Schema::create("printers", function (Blueprint $table) { $table->id(); $table->string("name"); $table->string("model"); $table->string("status")->default("online"); $table->timestamps(); }); 7️⃣ card_print_jobs Schema::create("card_print_jobs", function (Blueprint $table) { $table->id(); $table->foreignId("card_id")->constrained(); $table->foreignId("printer_id")->constrained(); $table->string("status"); $table->timestamp("printed_at")->nullable(); $table->timestamps(); }); 8️⃣ audit_logs Schema::create("audit_logs", function (Blueprint $table) { $table->id(); $table->foreignId("user_id")->nullable(); $table->string("action"); $table->string("entity"); $table->string("entity_id"); $table->ipAddress("ip_address"); $table->timestamps(); }); 🧠 B. SERVICE + REPOSITORY LAYER (CORE LOGIC) Folder structure app/ β”œβ”€β”€ Services/ β”‚ β”œβ”€β”€ ClientService.php β”‚ β”œβ”€β”€ AccountService.php β”‚ β”œβ”€β”€ CardService.php β”‚ └── CardPrintService.php β”œβ”€β”€ Repositories/ β”‚ β”œβ”€β”€ ClientRepository.php β”‚ β”œβ”€β”€ AccountRepository.php β”‚ β”œβ”€β”€ CardRepository.php β”‚ └── CardPrintRepository.php ClientService.php class ClientService { public function create(array $data) { return Client::create($data); } } AccountService.php class AccountService { public function create(Client $client) { return Account::create([ "client_id" => $client->id, "account_number" => "AC" . now()->timestamp . rand(100,999) ]); } } πŸ” CardService.php (CRITICAL) class CardService { public function create(Account $account): Card { $pan = $this->generatePAN(); return Card::create([ "account_id" => $account->id, "pan" => $pan, "pan_masked" => $this->maskPAN($pan), "cvv_hash" => bcrypt(rand(100,999)), "expiry_month" => now()->month, "expiry_year" => now()->year + 5, "status" => "CREATED" ]); } private function generatePAN(): string { return "6299" . rand(100000000000,999999999999); } private function maskPAN(string $pan): string { return "**** **** **** " . substr($pan, -4); } } πŸ–¨οΈ C. CARD PRINTING WORKFLOW (ATM / EMV READY) CardPrintService.php class CardPrintService { public function queue(Card $card, Printer $printer) { if ($card->status !== "PERSONALIZED") { throw new Exception("Card not ready for printing"); } return CardPrintJob::create([ "card_id" => $card->id, "printer_id" => $printer->id, "status" => "QUEUED" ]); } public function markPrinted(CardPrintJob $job) { $job->update([ "status" => "PRINTED", "printed_at" => now() ]); $job->card->update(["status" => "PRINTED"]); } } 🧭 D. CONTROLLERS (THIN) CardController.php class CardController extends Controller { public function store(Account $account, CardService $service) { $card = $service->create($account); audit("CREATE_CARD", "cards", $card->uuid); return redirect()->back(); } } πŸ§ͺ E. AUDIT HELPER (MANDATORY) function audit(string $action, string $entity, string $entityId) { AuditLog::create([ "user_id" => auth()->id(), "action" => $action, "entity" => $entity, "entity_id" => $entityId, "ip_address" => request()->ip() ]); } 🌐 F. INERTIA FLOW (Vue) Controllers send data via Inertia::render Vue never calls DB All writes via Laravel routes 🚧 G. TRANSACTION PHASE (PREPARED, NOT IMPLEMENTED) You are now READY for: ISO8583 MTI parsing POS & ATM switching HSM key injection PIN verification Ledger posting Nothing needs refactor.