Skip to main content

Setup Guide

This walks you from a fresh clone to a running stack. Tested on macOS and Linux. Windows users should use WSL2.

Prerequisites

ToolVersionNeeded for
PHP≥ 8.2 (with pdo_mysql, mbstring, bcmath, openssl, curl, xml, tokenizer)Backend
Composer≥ 2.5Backend
Node.js≥ 18Frontend + docs
npm≥ 9Frontend + docs
MySQL≥ 8 (or SQLite for quick local runs)Backend
Flutter SDK≥ 3.19 (stable channel)Mobile apps (optional)
Android Studio / XcodelatestiOS/Android simulators for Flutter (optional)

1. Clone and install backend

cd backend
cp .env.example .env
composer install
php artisan key:generate

Edit .env to point at your database:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=vetty_saas
DB_USERNAME=root
DB_PASSWORD=

(For a zero-setup run, leave the SQLite fallback and point DB_CONNECTION=sqlite at database/database.sqlite.)

Create schema and sample data:

php artisan migrate --seed

The seeder (database/seeders/DatabaseSeeder.php) creates:

  • Tenant: Paws & Claws Veterinary (premium plan, active)
  • Users: Ava Thompson (clinic_admin), Dr. Sarah Lee (veterinarian), Emily Johnson (owner)
  • Client: Emily Johnson (paired with the owner user)
  • Pet: Luna (golden retriever)
  • Appointment: vaccination booked two days from now
  • Invoice: INV-2026-0001 — partial (balance due)
  • Payment: $100 card payment

Generate the OpenAPI spec once so /docs/openapi.json has something to serve:

php artisan openapi:generate

Run the server:

php artisan serve

By default Laravel binds to http://127.0.0.1:8000.

2. Install the frontend

cd ../ui
npm install

Optional — point the SPA at your API:

# ui/.env.local
REACT_APP_API_BASE_URL=http://127.0.0.1:8000/api/v1
REACT_APP_API_MODE=http

Or skip this and run against the built-in mock adapter:

# ui/.env.local
REACT_APP_API_MODE=mock

Start the dev server:

npm start

The SPA opens on http://localhost:3000. Log in as:

  • sarah.lee@pawsclaws.com / password password (staff)
  • emily@example.com / password password (pet owner)

3. Filament admin panel

Visit http://127.0.0.1:8000/systemadmin. The seeder creates a system admin you can log in as (check database/seeders/DatabaseSeeder.php for the latest credentials).

4. Flutter apps (optional)

Both Flutter apps live alongside the backend and SPA at the repository root.

vetty — pet-owner app (vetyy/)

cd vetyy
flutter pub get
flutter run \
--dart-define=VETTY_API_BASE=http://10.0.2.2:8000/api/v1 \
--dart-define=VETTY_WEB_BASE=http://localhost:3000

vetdoctor — vet/clinic app (vetdoctor/)

cd vetdoctor
flutter pub get
flutter run \
--dart-define=VETDOCTOR_API_BASE=http://10.0.2.2:8000/api/v1 \
--dart-define=VETDOCTOR_WEB_BASE=http://localhost:3000

Notes on API_BASE

TargetHost to use
Android emulatorhttp://10.0.2.2:8000/api/v1 (emulator loopback)
iOS simulatorhttp://127.0.0.1:8000/api/v1
Physical device on same Wi-Fihttp://<host-LAN-IP>:8000/api/v1
Staging / prode.g. https://api.vetty.example/api/v1

WEB_BASE is the URL the backend should put into password-reset emails — it should point at the running React SPA.

Default credentials

  • vetty — register any new account via the app's Create account screen, or use the seeded owner emily@example.com / password.
  • vetdoctor — clinic users are provisioned through Filament or the SPA. Use the seeded vet sarah.lee@pawsclaws.com / password.

5. Running the docs site

cd ../docs
npm install
npm start

Opens http://localhost:3000 — if that collides with the SPA, run with a different port: npm start -- --port 3100.

6. Tests

Backend unit/feature tests use SQLite and live in backend/tests/:

cd backend
php artisan test

The test runner writes to database/verify.sqlite (already present in the repo) to keep production data untouched.

Frontend tests live in ui/src/setupTests.js and can be run with:

cd ui
npm test

Flutter tests (test/widget_test.dart in each app) are compile smoke tests that pump the app's theme and assert it builds:

cd vetyy && flutter test
cd ../vetdoctor && flutter test

7. Razorpay (sandbox payments)

The vetty pet-owner app can pay clinic invoices with Razorpay. Test-mode keys from your Razorpay dashboard are enough for local development — no real money moves.

  1. Pull the SDK (only needed once, after a fresh clone or after pulling a branch that upgrades composer.json):

    cd backend
    composer require razorpay/razorpay:^2.9
  2. Add sandbox keys to backend/.env:

    RAZORPAY_KEY=rzp_test_xxxxxxxxxxxxxx
    RAZORPAY_SECRET=your_test_secret
    RAZORPAY_WEBHOOK_SECRET=any_string_you_set_in_the_dashboard
    RAZORPAY_CURRENCY=INR

    The same publishable key also lives on the Flutter side — the app fetches it from the create-order endpoint, so you don't need to duplicate it anywhere.

  3. Run the migration that adds gateway columns to the payments table:

    php artisan migrate
  4. (Optional) Expose your local API to Razorpay's webhook poller with ngrok / cloudflared, then register the tunnel URL in the Razorpay dashboard and point it at:

    POST /api/v1/webhooks/razorpay

    Events to subscribe: payment.captured, payment.failed. The handler is idempotent (unique index on provider_payment_id) so replays are safe.

  5. In the vetty app, open any unpaid invoice and tap Pay. Use Razorpay's test cards (test card reference) — e.g. 4111 1111 1111 1111, CVV 123, any future expiry.

If RAZORPAY_KEY or RAZORPAY_SECRET is missing, the create-order endpoint returns 503 Payments unavailable instead of a cryptic 500 — handy when you've forgotten to drop your keys in.

8. Useful artisan commands

php artisan tinker # REPL
php artisan openapi:generate # Regenerate API spec
php artisan filament:upgrade # After Filament updates
php artisan migrate:fresh --seed # Wipe + reseed local DB

9. Common pitfalls

SymptomFix
SQLSTATE[HY000] [2002]MySQL isn't running, or wrong host/port in .env
Target class [openapi:generate] does not existThe console route lives in routes/console.php — don't remove it
SPA gets CORS blockedAdd your SPA origin to SANCTUM_STATEFUL_DOMAINS in backend .env
SPA sees old dataClear localStorage (vetty.session, vetty.currentTenant, access_token)
Filament admin 404Make sure APP_URL is set; the panel provider reads it
Flutter emulator can't reach APIUse 10.0.2.2 (Android) or 127.0.0.1 (iOS) — not localhost
Flutter app stuck on login screen after correct credsInspect the API response; AuthStore clears itself on 401 — verify Sanctum is issuing tokens
mobile_scanner black screenRun on a physical device — most emulators don't expose the camera