Quick start & license key
Create your buyer account, purchase a license, and find the key used by the /install wizard.
What you need
- An active Regular or Extended license from this store
- A Supabase account (the free tier works to begin)
- A Vercel account and a domain name
- Node.js 20+ and pnpm installed locally
Your license key
After payment is confirmed, the key appears in Dashboard → Licenses. It is also emailed to the receipt address when email delivery is configured. Keep it private: the key activates one installation and binds your domain slots.
- Buy a Regular or Extended license.
- Copy the key from the dashboard.
- Create your Supabase project and run
schema.sql. - Deploy the release to Vercel.
- Open
/install, paste the key and activate the domain.
What is included
The release zip contains the complete Next.js application, the consolidated schema.sql, and a buyer README. Env files, backups, git history and node_modules are never included.
Need help?
Open a support ticket from the dashboard with your domain, license key prefix and the exact error message. Extended licenses receive priority support.
STH-LMS-REG-XXXX-XXXX
STH-LMS-EXT-XXXX-XXXXSupabase schema.sql setup
Create the database, run the consolidated schema, and connect Auth redirects.
Create the project
- Create a project at supabase.com in the region closest to your students.
- Save the database password in a private vault.
- Open Project Settings → API and copy the Project URL, publishable key and secret key.
- Open Authentication → URL Configuration and set the Site URL to your production domain.
- Add
https://your-domain.com/auth/callbackto the allowed redirect URLs.
Run the schema
Open SQL Editor, paste the complete schema.sql from the release, and run it once. The file is idempotent, so it is safe to run again after an update.
Expected core tables include profiles, courses, payments, certificates, licenses and the license cache used by the install wizard.
Auth email
Supabase's default mailer is rate-limited. For production volume, configure your own SMTP provider under Authentication → SMTP and send a test message before launch.
Never expose the service-role or secret key in browser code. Only the publishable key belongs in NEXT_PUBLIC_* variables.
-- Supabase Dashboard → SQL Editor → paste schema.sql → Run
-- or from the project folder
supabase link --project-ref YOUR_PROJECT_REF
supabase db pushVercel deployment & /install activation
Import the release into Vercel, add environment variables, deploy, and bind your domain through /install.
Deploy the code
- Unzip the release and push it to a private repository you own.
- Import the repository into Vercel.
- Use the Next.js framework preset and
pnpm buildas the build command. - Add every required environment variable to Production and Preview.
- Deploy.
Activate the license
The first visit redirects to /install. The wizard checks database connectivity, activates the key and binds the current hostname to a free domain slot.
- Database check verifies the Supabase URL and secret key can read the schema.
- License activation verifies the key with this store and returns a signed 24-hour status.
- Domain binding normalises the hostname and binds it automatically when a slot is free.
Domain normalisation
Protocol, www. and ports are removed. www.example.com and example.com count as the same domain. Vercel preview domains can consume a slot, so activate the production domain.
If activation returns domain_limit_reached, unbind an unused domain in the buyer dashboard and retry.
unzip nahiyan-lms-vX.Y.Z.zip
cd nahiyan-lms
pnpm install
git init && git add . && git commit -m "Initial Nahiyan LMS"
gh repo create my-academy --private --source=. --push
vercel link
vercel deploy --prodOne-click .zip / .sql system updater
Run the guided updater with a transactional SQL migration, one GitHub commit and a Vercel production build.
How the updater works
The guided updater lives at /admin/system. Download the release package from your buyer dashboard, then upload the matching .zip and .sql files.
- SQL first, in a transaction. A failed statement rolls back before any code is deployed.
- Atomic GitHub commit. The package is committed as one commit, so the whole release can be reviewed or reverted.
- Vercel production build. Vercel rebuilds from the commit. Env vars, domain bindings and the license stay untouched.
Connect once
You need a GitHub fine-grained token with repository contents read/write, a Vercel access token with project and deployment access, and the Supabase database URL. Credentials are encrypted with AES-256-GCM and masked after saving.
Before the first update
Take a Supabase backup and test the release on a staging project. Use least-privilege tokens and rotate them if a device is lost.
Prefer the terminal? Replace the code while keeping .env.local, your Supabase project and the license cache, run any new SQL first, then pnpm install && pnpm build && vercel deploy --prod.
# What the updater does on each release
1. Upload nahiyan-lms-vX.Y.Z.zip and update.sql
2. Run update.sql in a transaction (rollback on error)
3. Commit the extracted package to your branch
4. Trigger a Vercel production deployment
5. Show the result in Update historyAndroid app & Google Play .jks signing guide
Build the Capacitor shell, create a release keystore, sign an .aab and publish safely to Google Play.
Prerequisites
- Node.js 20+ and pnpm
- JDK 21 with
JAVA_HOMEconfigured - Android SDK 36, build-tools and platform-tools
- Android Studio or VS Code with the Gradle extension
Point the shell at your domain
Edit capacitor.config.ts and set server.url to your production origin. Keep the branded offline fallback in capacitor/www so the app shows a useful screen when the site is unreachable.
Create the signing key
In Android Studio choose Build → Generate Signed Bundle / APK, then Android App Bundle, and create a new keystore. Or use keytool from the terminal.
Add *.jks, *.keystore and keystore.properties to .gitignore. Back the keystore and both passwords up in a private vault. Losing them means future builds cannot install as an update over the current app.
Build and publish
Google Play requires an Android App Bundle (.aab) for new apps. Use a direct APK only for sideloading.
- Upload the
.aabto Internal testing and install it on a physical device. - Complete the store listing, screenshots, content rating, data safety and privacy policy.
- Increment
versionCodeandversionNamefor every upload. - Keep
public/version.jsonin sync for the in-app update prompt. - Promote to closed, open or production testing after device checks pass.
For native Google sign-in, add nahiyanacademy://auth/callback to Supabase redirect URLs.
keytool -genkeypair -v \
-keystore android/keystore/release.jks \
-alias release -keyalg RSA -keysize 2048 -validity 10000
# android/keystore.properties (never commit)
storeFile=keystore/release.jks
storePassword=YOUR_STORE_PASSWORD
keyAlias=release
keyPassword=YOUR_KEY_PASSWORD
cd android
./gradlew bundleRelease
./gradlew assembleRelease
apksigner verify --verbose --print-certs \
app/build/outputs/apk/release/app-release.apkEnvironment variables
Every required Vercel and local environment variable, with the purpose of each key.
Required variables
Add these in Vercel under Project → Settings → Environment Variables for Production and Preview, and mirror them in .env.local locally.
NEXT_PUBLIC_SUPABASE_URL— your Supabase project URLNEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY— browser-safe publishable/anon keySUPABASE_SERVICE_ROLE_KEY— server-only key for payments and license checksNEXT_PUBLIC_SITE_URL— your public https originNEXT_PUBLIC_LICENSE_REQUIRED—truefor production installsNEXT_PUBLIC_LICENSE_SERVER_URL— this store URLCRON_SECRET— optional scheduled-route protection
Restart or redeploy after changing environment variables. Never commit .env.local or paste the service-role key into a support ticket.
NEXT_PUBLIC_SUPABASE_URL=https://YOUR-PROJECT.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxx
SUPABASE_SERVICE_ROLE_KEY=sb_secret_xxx
NEXT_PUBLIC_SITE_URL=https://your-domain.com
NEXT_PUBLIC_LICENSE_REQUIRED=true
NEXT_PUBLIC_LICENSE_SERVER_URL=https://lms.nahiyanacademy.com
CRON_SECRET=generate-a-long-random-valueDomain management & license API
Move a license between domains and validate installs with the signed license API.
Manage domains
Open the buyer dashboard to see every bound domain and slot usage.
- Unbind the old domain. The slot frees immediately.
- Deploy the same code to the new domain.
- Open
/installand activate again. The new hostname binds automatically.
Regular includes one live domain; Extended includes five. If every slot is used, activation returns domain_limit_reached until one is unbound.
License API
The install wizard calls POST /api/license/verify. A successful response is signed with HMAC-SHA256 over licenseKey|tier|domain|expiresAt. Cache a successful response for up to 24 hours, then revalidate.
A failed domain check returns valid: false with a reason. Do not cache failures longer than a few minutes.
curl -X POST https://lms.nahiyanacademy.com/api/license/verify \
-H "Content-Type: application/json" \
-d '{"licenseKey":"STH-LMS-REG-XXXX-XXXX","domain":"academy.example.com"}'
# success
{
"valid": true,
"tier": "REGULAR",
"maxDomains": 1,
"expiresAt": "2026-11-16T10:00:00.000Z",
"signature": "<hmac-sha256>"
}Troubleshooting & support
Fix the most common install, domain, email, payment and build problems.
Common fixes
The app keeps redirecting to /install
The cached license is missing or expired, or the hostname does not match. Re-run activation and confirm NEXT_PUBLIC_LICENSE_REQUIRED=true.
domain_limit_reached
Every slot is bound. Unbind an unused domain in the buyer dashboard, then retry.
Database check fails
Verify the Supabase URL and secret key in Vercel, redeploy, and confirm schema.sql ran completely.
Auth emails do not arrive
Supabase's default mailer is rate-limited. Configure custom SMTP in Authentication → SMTP.
bKash checkout is unavailable
The store's bKash credentials are incomplete or the gateway is disabled. Use manual payment and wait for admin approval.
Build fails on Vercel
Use Node 20+, confirm the lockfile locally with pnpm install, and make sure all required env vars exist.
Still stuck?
Open a support ticket from the dashboard and include your domain, license key prefix and exact error. Extended licenses get priority.
Still stuck?
Open a ticket from your dashboard and include your domain, license key prefix and the exact error. Extended licenses get priority.
Open a support ticket