Buyer documentation

From license key to live academy, in plain English.

Nahiyan LMS is self-hosted: your code, your Supabase project and your domain. These guides walk you through purchase, installation, updates and mobile publishing.

Licensed buyers receive an active license key and access to the private release vault in their dashboard. Release downloads are never public.

Getting started

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.

  1. Buy a Regular or Extended license.
  2. Copy the key from the dashboard.
  3. Create your Supabase project and run schema.sql.
  4. Deploy the release to Vercel.
  5. 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.

code / command
STH-LMS-REG-XXXX-XXXX
STH-LMS-EXT-XXXX-XXXX
Database

Supabase schema.sql setup

Create the database, run the consolidated schema, and connect Auth redirects.

Create the project

  1. Create a project at supabase.com in the region closest to your students.
  2. Save the database password in a private vault.
  3. Open Project Settings → API and copy the Project URL, publishable key and secret key.
  4. Open Authentication → URL Configuration and set the Site URL to your production domain.
  5. Add https://your-domain.com/auth/callback to 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.

code / command
-- Supabase Dashboard → SQL Editor → paste schema.sql → Run

-- or from the project folder
supabase link --project-ref YOUR_PROJECT_REF
supabase db push
Deployment

Vercel deployment & /install activation

Import the release into Vercel, add environment variables, deploy, and bind your domain through /install.

Deploy the code

  1. Unzip the release and push it to a private repository you own.
  2. Import the repository into Vercel.
  3. Use the Next.js framework preset and pnpm build as the build command.
  4. Add every required environment variable to Production and Preview.
  5. 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.

code / command
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 --prod
Updates

One-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.

  1. SQL first, in a transaction. A failed statement rolls back before any code is deployed.
  2. Atomic GitHub commit. The package is committed as one commit, so the whole release can be reviewed or reverted.
  3. 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.

code / command
# 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 history
Mobile

Android 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_HOME configured
  • 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.

  1. Upload the .aab to Internal testing and install it on a physical device.
  2. Complete the store listing, screenshots, content rating, data safety and privacy policy.
  3. Increment versionCode and versionName for every upload.
  4. Keep public/version.json in sync for the in-app update prompt.
  5. Promote to closed, open or production testing after device checks pass.

For native Google sign-in, add nahiyanacademy://auth/callback to Supabase redirect URLs.

code / command
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.apk
Configuration

Environment 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 URL
  • NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY — browser-safe publishable/anon key
  • SUPABASE_SERVICE_ROLE_KEY — server-only key for payments and license checks
  • NEXT_PUBLIC_SITE_URL — your public https origin
  • NEXT_PUBLIC_LICENSE_REQUIRED — true for production installs
  • NEXT_PUBLIC_LICENSE_SERVER_URL — this store URL
  • CRON_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.

code / command
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-value
Licensing

Domain 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.

  1. Unbind the old domain. The slot frees immediately.
  2. Deploy the same code to the new domain.
  3. Open /install and 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.

code / command
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>"
}
Support

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