Order Ly
Cafe POS + QR self-ordering: customers order & pay from the table QR, cashier PC runs offline (Flutter, SQLite, Firebase)
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Live at https://runam.app
Runam is a web app for errands inside the estate: residents ask for food, groceries, gas refills, parcel pick-ups or laundry, a vetted runner does it, and the shop is paid directly by the platform so runners never handle residents' money.
It's a mobile-first web app (installable on the home screen). Everyone signs in at /login with their phone number, and the app opens the right part for them:
| Who | Where | What they do |
|---|---|---|
| Residents | /home | Start an errand, pay with Paystack or wallet, track it live, approve extra money, give the delivery code, get change back in their wallet, rate the runner |
| Runners | /runner | Go online, get errands (with sound + vibration), accept, confirm the purchase with a receipt photo, the shop is paid automatically, deliver, enter the resident's code |
| Shop staff | /shop | Chat with customers, see riders on their way |
| You (admin) | /admin | Live dashboard, errands, runners, shops & their items, payout approvals, runner payouts, residents (incl. "gone quiet" watch), support chats, fees and settings |
Who gets in where. Everyone signs in with their email and a 6-digit code we email them. Only the emails in ADMIN_EMAILS can open /admin. An email becomes a runner when you add it in Admin → Runners, and shop staff when you add it to a shop. Everyone else is a resident. If someone opens a page that isn't theirs (a runner trying /admin, say), they're sent back to their own home. Admins can also use the resident app, and there's an "Open admin console" button on their Account page.
"Runam" is a working name. Change it in one place: set
NEXT_PUBLIC_APP_NAME(andNEXT_PUBLIC_ESTATE_NAME) in your environment.
Chat with the shop. Residents tap a shop and chat with it in the app: ask what's in stock, agree the price, and pay the shop directly (the shop can send its bank details in one tap). Once they've paid, they tap Send rider, enter the name the order is under, and pay the delivery fee in the app. The shop is then asked in the chat (and on its "Riders coming" tab) whether the payment reached them: only after Payment received is a rider sent. Not yet tells the resident to check their transfer or cancel. (Shops with nobody on chat skip this step; an admin can also confirm on the shop's behalf from the errand page.) The shop sees in the same chat when the rider arrives. At the door the rider enters the resident's delivery code and takes a photo of the handover as proof of delivery. Cancelling before the rider reaches the shop refunds the fee to the wallet.
Phone numbers and WhatsApp links typed into chats are hidden, so orders stay on the platform.
Let a runner buy it. The resident pays everything up front and the app pays the shop, as described below. This needs Paystack Transfers, so it's off by default: switch it on in Admin → Settings once your Paystack account can send transfers. While it's off, the Food, Groceries and Gas tiles show those shops, and Pick up / Laundry errands (no buying involved) still work.
Residents can call a shop from its chat, and residents and runners can call each other during an errand, using the call button. Calls go over the internet (WebRTC), so phone numbers are never shared, and they aren't recorded; the chat or errand timeline gets a line like "Voice call · 2:31" or "Missed call".
Calls connect directly between phones. On some mobile networks that isn't possible without a relay, so for reliable calls add a TURN service (for example Cloudflare Realtime TURN or Metered) and set TURN_URLS, TURN_USERNAME and TURN_CREDENTIAL. Microphone access needs HTTPS, which the live site has.
Residents, runners and shop staff can tap Chat with support (Account, Profile or the shop inbox, and "Get help" on any errand). Messages arrive in Admin → Support with a badge and a push notification to every admin; replies go straight back to them. Next to the chat there are Call and WhatsApp buttons for the support phone set in Admin → Settings (also shown on the landing page, the login screen, and in the Terms and Privacy Policy).
In /admin/vendors, add each shop's staff under Shop staff on chat (email + name). They sign in with that email like everyone else and land in the shop inbox at /shop: chats with customers, and riders on their way. Residents never see it. Removing someone signs them out.
This is what keeps everyone in the app: residents are covered only for in-app payments, runners never hold cash, runners are assigned automatically (so nobody "owns" a household), phone numbers stay hidden behind in-app chat, and the admin "gone quiet" list flags regulars who suddenly stop ordering.
Everything in this section is for a test copy on your own computer. The live site has no demo data and no demo mode.
Requirements: Node 20.9+ and a Postgres database (local, or a free one from Neon).
npm install
cp .env.example .env # then set DATABASE_URL, and DEMO_MODE=true
npm run setup # creates tables + demo data
npm run dev # http://localhost:3000
With DEMO_MODE=true and no Paystack key, payments go through a pretend checkout and shop transfers "settle" after a few seconds, and the login code is shown on screen (without it, codes are only printed in the terminal).
⚠️
DEMO_MODE=truelets anyone sign in as anyone. Never set it on the live site. Without it and without a Paystack key, the app refuses payments with "Online payments aren't switched on yet".
Demo accounts (all made up, local only; the seed script refuses to run against anything but a local database):
| Role | |
|---|---|
| Admin | [email protected] (whatever you put first in ADMIN_EMAILS) |
| Resident | [email protected] |
| Runner | [email protected], [email protected], [email protected] |
| Shop staff | [email protected] (Village Mart) |
Start an errand as Amaka in one browser and accept it as Adaeze in another (or a private window) to see the whole flow.
To wipe everything and start again: npm run db:seed -- --reset (deletes all data).
Create a Postgres database (Neon free tier is plenty to start). Use the pooled connection string as DATABASE_URL.
.env.example (see the table below).vercel-build script runs database migrations automatically before each build./login with your admin email and add your real shops and runners in /admin. Don't set DEMO_MODE and don't run the demo seed on the live database.Create a Paystack business account and complete business activation. A Starter (unregistered) business can take payments but can't send transfers; a Registered business (CAC) can do both.
Put your secret key in PAYSTACK_SECRET_KEY (sk_test_… first, sk_live_… at launch). On a Starter business also set PAYSTACK_TRANSFERS=off: bank account names are then typed in, and runner payouts and wallet withdrawals appear in Admin → Payouts for you to pay from your bank app and tap Mark as paid.
In Paystack → Settings → API Keys & Webhooks, set:
https://YOUR-DOMAIN/api/paystack/webhookhttps://YOUR-DOMAIN/pay/callback(Both are shown with a copy button in /admin/settings.)
In Paystack → Settings → Preferences, turn off OTP for transfers, otherwise automatic shop payments will stop and ask for an OTP.
Keep your Paystack balance topped up so transfers to shops and runners go through.
Paystack charges a fee on every payment; build that into your service fees in /admin/settings.
Create a Resend account, add your domain (it gives you a few DNS records to add), and set RESEND_API_KEY and EMAIL_FROM (e.g. Runam <[email protected]>). Nobody can sign in on the live site until this is done: without it, codes are only written to the server logs (Vercel → your project → Logs, search for email:console). Resend's free plan covers 3,000 emails a month (100 a day).
SMS (Termii) is optional and only used for a few errand alerts. A Nigerian SMS sender ID needs CAC documents.
npx web-push generate-vapid-keys
Put the keys in NEXT_PUBLIC_VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY. Runners and residents can then switch on notifications from their profile. On iPhone this needs the app added to the home screen first.
The Terms of Service (/terms) and Privacy Policy (/privacy) are written for a Nigerian business under the Nigeria Data Protection Act 2023. Set NEXT_PUBLIC_LEGAL_NAME to your registered business name and the support phone in /admin/settings, and have a lawyer look over both before launch. Depending on how many people's data you handle, you may also need to register with the Nigeria Data Protection Commission.
| Variable | Required | What it's for |
|---|---|---|
DATABASE_URL | ✅ | Postgres connection string |
APP_URL | ✅ | Your public URL, e.g. https://runam.app (no trailing slash) |
SESSION_SECRET | ✅ | Long random string — openssl rand -base64 48 |
ADMIN_EMAILS | ✅ | Comma-separated admin emails |
RESEND_API_KEY, EMAIL_FROM | ✅ | Sends sign-in codes by email |
PAYSTACK_SECRET_KEY | for payments | Empty = no payments (pretend checkout only with DEMO_MODE=true) |
PAYSTACK_TRANSFERS | off for a Starter Paystack business (no transfers): payouts are paid by hand | |
PAYSTACK_EMAIL_DOMAIN | Used to build an email for residents who haven't added one | |
SMS_PROVIDER, TERMII_* | Optional SMS alerts | |
DEMO_MODE | Local demo only: on-screen login codes and a pretend checkout. Never set on the live site. | |
NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT | for push | Web push notifications |
NEXT_PUBLIC_APP_NAME, NEXT_PUBLIC_ESTATE_NAME | Rename the app / estate | |
NEXT_PUBLIC_LEGAL_NAME | Your registered business name, used in the Terms and Privacy Policy | |
TURN_URLS, TURN_USERNAME, TURN_CREDENTIAL | for calls on mobile data | TURN relay for in-app calls |
/admin/runners with their phone, NIN, guarantor and bank account. They sign in with that phone number./admin/vendors (Shops) with a photo, the staff who reply to chats, their bank account and popular items. Partner shops are paid automatically./admin/payouts (one-off shops and wallet withdrawals), and pay runners their earnings weekly with one tap.Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS 4 · Motion · Drizzle ORM + Postgres · Paystack · Termii · Web Push.
src/app/(auth) sign-in and onboarding
src/app/(resident) resident app
src/app/runner runner app
src/app/admin admin console
src/app/api JSON endpoints, Paystack webhook, uploads
src/lib/orders order state machine, dispatch, queries
src/lib/payouts.ts transfers to shops/runners
src/lib/db schema (drizzle) — migrations live in /drizzle
Useful commands: npm run typecheck, npm run db:generate (after editing the schema), npm run db:migrate.