✨ CodeCanyon Package • v4.1.4

Welcome to WitVPN

Full source code for a production-ready VPN app — Android client, admin dashboard, Firebase backend. Built with Kotlin, React, TypeScript. Ready to reskin, rebrand, and ship.

🚀 The fast path
Run bash setup.sh (macOS/Linux) or double-click setup.bat (Windows) at the package root. Answer the prompts. That's it.
🐛 Known issue — Android 14+ VPN stall
Apply the 3-line Manifest fix (in FAQ) before your first build if you plan to target Android 14+. Android 13 and below unaffected.

What you get

Android app — 01-android-app/

  • Kotlin, MVVM, Hilt, Coroutines, Retrofit, Room, Glide
  • Bundled OpenVPN (GPL-2.0)
  • Google Sign-In + Firebase Auth + Firestore
  • Google AdMob (banner + interstitial, premium-gated, frequency-capped)
  • Google Play Billing Library 7.x (subscriptions + one-time)
  • reCAPTCHA Enterprise + R8 minification

VPN Server — 03-vpn-server/

  • Production-grade OpenVPN install script (angristan/openvpn-install, MIT)
  • Works on Debian/Ubuntu/CentOS/Fedora/Rocky/Alma/Arch
  • 5-minute setup on a $4/mo Linux VPS (DigitalOcean, Hetzner, Vultr…)
  • Generates a ready-to-upload .ovpn file the Admin Dashboard accepts
  • Add/revoke users via interactive menu on re-runs

Admin Dashboard — 02-admin-dashboard/

  • Vite + React + TypeScript + Tailwind
  • Firebase Auth (Google login) + Firestore CRUD
  • Manage servers, configs, users, admin roles
  • Cloud Function: auto-assigns admin custom claims
  • Deploys to Firebase Hosting in one command

How long & how much?

StepTimeCost
Install prerequisites (Node, JDK, Android Studio)30–60 minFree
Create Firebase project + enable services15–20 minFree (Spark plan)
Run setup.sh / setup.bat5–10 minFree
First Android build5–15 minFree
Google Play Console account1–14 days (review)$25 one-time
AdMob account1–7 days (review)Free

1. Prerequisites

Install these first. The setup script will verify them and guide you if anything is missing.

# Install Homebrew if you don't have it
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Node.js 20
brew install node@20

# JDK 17
brew install --cask temurin@17

# Android Studio (~8GB)
brew install --cask android-studio

# (Optional) Firebase CLI — only for deploy
npm install -g firebase-tools

Download installers:

After Node is installed, open PowerShell and run:

npm install -g firebase-tools   # optional, for deploy
# Node.js 20 (Debian/Ubuntu)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# JDK 17
sudo apt install -y openjdk-17-jdk

# Android Studio — download from https://developer.android.com/studio
# Extract to ~/android-studio and run bin/studio.sh

# (Optional) Firebase CLI
sudo npm install -g firebase-tools

Verify installation

node -v      # expect v18+ (v20+ preferred)
npm -v       # expect 9+
java -version # expect 17
# Android Studio: launch it once to finish SDK setup

2. Create your Firebase project

One Firebase project is shared by both apps. Go to console.firebase.google.com and follow the steps.

1

Create a new project

Click Add project → name it (e.g., witvpn-prod) → disable Analytics if you don't need it → Create project.

2

Enable authentication providers

Authentication (left sidebar) → Get startedSign-in method tab. Enable all three providers the Android app uses:

  • Google — set support email → Save
  • Email/Password — toggle EnableSave
  • Anonymous — toggle EnableSave

The Admin Dashboard only uses Google; the other two are for the Android app's login flow.

3

Create Firestore database

Firestore DatabaseCreate databaseStart in production mode → choose region (pick one close to your users).

4

Register the Android app

Project Settings (gear icon) → General tab → Your apps → Android icon.

  • Package name: com.wit.witvpn (or your own — remember what you entered)
  • App nickname: anything
  • SHA-1: you can add this later after generating your keystore

Click Register appDownload google-services.json. Save it somewhere you'll find later.

console.firebase.google.com/.../settings/general
Firebase Console — Add Android app, Register step

After clicking Register, you'll be prompted to download google-services.json — drop it into 01-android-app/app/:

console.firebase.google.com/.../settings/general
Firebase Console — Download google-services.json
5

Register the Web app (for the Admin Dashboard)

Same screen → Add appWeb (</> icon) → nickname it → Register app.

Firebase shows you a config object with 7 values:

const firebaseConfig = {
  apiKey: "AIza...",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project",
  storageBucket: "your-project.appspot.com",
  messagingSenderId: "123...",
  appId: "1:123:web:abc...",
  measurementId: "G-..."      // optional
};

Keep this tab open — you'll paste these values into the Admin setup script.

console.firebase.google.com/.../settings/general
Firebase Console — Add Web app, Register step

After registering, the SDK config values appear:

console.firebase.google.com/.../settings/general
Firebase Console — Web SDK config values
6

Generate a service-account key

Project SettingsService accounts tab → Generate new private key → confirm the scary warning → a JSON file downloads.

Rename it to serviceAccountKey.json. You'll place it at 02-admin-dashboard/serviceAccountKey.json during setup.

🔒 Keep this file secret
Anyone with serviceAccountKey.json has full admin access to your Firebase project. Never commit it to git. Never share it. The package's .gitignore already blocks it.

3. Spin up an OpenVPN server

Your Android app needs at least one OpenVPN server to connect to. The bundled angristan/openvpn-install script (MIT, in 03-vpn-server/) takes a fresh Linux VPS and turns it into a working server in 5 minutes.

1

Rent a Linux VPS

Cheapest tier is plenty for ~30 concurrent users. Pick the region you want the exit IP in.

ProviderTierPriceNotes
HetznerCX11€4.51/moBest price/perf, EU only
DigitalOceanBasic Droplet$4/moEasiest UX, global
VultrCloud Compute$5/moMany regions
Oracle CloudAlways Free$04 vCPU + 24GB RAM, signup is strict

Distribution: Debian 12 or Ubuntu 24.04. KVM virtualization (avoid OpenVZ — TUN module won't work).

2

Upload & run the script

# from your laptop
scp 03-vpn-server/openvpn-install.sh root@YOUR_VPS_IP:/root/

# then SSH in
ssh root@YOUR_VPS_IP
chmod +x openvpn-install.sh
./openvpn-install.sh

Answer ~10 prompts. Defaults are good. Recommended overrides:

  • Protocol: UDP (faster, what the Android app uses)
  • DNS: Cloudflare or Google
  • Compression: no (security best practice — VORACLE attacks)
  • Password-protect client: no (the Android app needs to auto-connect)
3

Grab the .ovpn file

The script writes the client config to /root/<name>.ovpn. Download it to your laptop:

scp root@YOUR_VPS_IP:/root/default.ovpn .

Keep this file — you'll upload it via the Admin Dashboard in Step 7 (Populate Firestore).

🔁 Repeat for each server region
Want servers in US + EU + Asia? Spin up 3 VPSs and run the script on each. You'll end up with 3 .ovpn files → 3 docs in the Servers Firestore collection.

Full reference + troubleshooting: 03-vpn-server/SETUP.md.

4. Set up the Android app

Open a terminal in the extracted package root.

cd 01-android-app
bash setup.sh --build
# Windows: double-click setup.bat, or run `setup.bat` from cmd

The script will:

  1. Check Node, JDK 17, Android SDK — and guide you to install anything missing.
  2. Ask you to place google-services.json at app/google-services.json (from Firebase Step 4).
  3. Prompt for: Firebase Android config, Google Web Client ID, AdMob App ID + ad unit IDs, Play License Key, Envato purchase code (optional).
  4. Generate a release keystore (3 short questions — see below).
  5. Run ./gradlew bundleRelease — output: app/build/outputs/bundle/release/app-release.aab.
What is a keystore and what do I type?

A keystore is a file Android uses to digitally sign your app. The script runs keytool and asks:

  • Full name — your name or your company name
  • Organization — your company (or your own name again)
  • Country code — 2 letters, e.g. US, VN, GB
  • Passwords — pick something and remember it. Min 6 chars.

None of this is shown to end users. The keystore file is saved to app/key/release.jks.

⚠️ Back up the keystore file
If you lose release.jks or forget the password, you can never again push updates to the same app on Play Store. Store a copy somewhere safe (1Password, encrypted drive, etc.).
Where do I get Firebase Android values?

Open Firebase Console → Project Settings → General → Your apps → Android app. The script asks for:

  • Project ID — e.g. witvpn-prod
  • Web Client ID (for Google Sign-In) — same screen, under the Google provider settings. Looks like 123-abc.apps.googleusercontent.com.
Where do I get AdMob ad-unit IDs?

admob.google.com → Apps → your app (create one if needed) → Ad units. Create one Banner and one Interstitial. IDs look like ca-app-pub-XXXXXXXXXXXXXXXX/YYYYYYYYYY.

The App ID (different from unit ID) looks like ca-app-pub-XXXXXXXXXXXXXXXX~YYYYYYYYYY (tilde, not slash).

Where do I get the Play Billing License Key?

Google Play Console → your app (create a draft if you haven't) → Monetize → Licensing. Copy the Base64 key (starts with MII...).

If your app is still in draft and you haven't published yet, you can leave this blank — the setup script accepts an empty value and you can fill it later in buildSrc/src/main/kotlin/Config.kt.

5. Set up the Admin Dashboard

cd 02-admin-dashboard
bash setup.sh --dev
# Windows: setup.bat

The script will:

  1. Check Node & npm.
  2. Prompt for your Firebase Web SDK config (from Firebase Step 5 — not the Android one!).
  3. Write .env and .firebaserc.
  4. Ask you to place serviceAccountKey.json at the project root (from Firebase Step 6).
  5. Run npm install.
  6. Start the Vite dev server at http://localhost:5173.
💡 Using the right Firebase config
The Admin Dashboard wants the Web app's SDK config (7 values). The Android app wants a separate google-services.json file. If you paste Android values into Admin, the dashboard will show a blank page.

6. Create your first admin user

After you've signed into the dashboard once with Google (so Firebase Auth has your UID), run:

cd 02-admin-dashboard
npx tsx scripts/setAdminRole.ts your@email.com

This sets a admin: true custom claim on your user. Sign out of the dashboard and back in — admin-only routes unlock.

Requires serviceAccountKey.json
The script uses the service account to set custom claims. Make sure 02-admin-dashboard/serviceAccountKey.json exists first.

7. Populate Firestore

The Android app reads Servers and configs from Firestore. Easiest path: use the Admin Dashboard UI — open ServersAdd Server, fill in country/IP/premium flag, then upload the .ovpn file you got from Step 3. The dashboard base64-encodes it for you.

Below is what the same data looks like inside Firebase Console, in case you want to seed it manually before the dashboard is up.

console.firebase.google.com/.../firestore/data
Firestore — collections list overview

Create the configs collection

Click Start collection at the root of the database, set Collection ID to configs:

console.firebase.google.com/.../firestore
Firestore — Start a collection dialog

Inside the document, add an ads array of maps. Each map has a google sub-map with your AdMob IDs:

console.firebase.google.com/.../firestore/configs
Firestore — configs document with ads field

Then add the iap map with your subscription product IDs from Play Console:

console.firebase.google.com/.../firestore/configs
Firestore — configs document with iap field (yearly/monthly)

Composite index for Servers

The Android app filters Servers by status + premium simultaneously, which Firestore needs an index for. Go to Indexes tab → Add index:

console.firebase.google.com/.../firestore/indexes
Firestore — Create composite index for Servers (status + premium)

Collection ID: Servers · Fields: status Ascending, premium Ascending · Query scope: Collection. Save and wait ~1 minute for the index to build.

Required fields — Servers collection

FieldTypeExample
countrystringUnited States
countryCodestring (2-letter lowercase)us
statestringCalifornia
ipAddressstring192.168.1.1
ovpnstring (base64-encoded .ovpn config)(long string)
ovpnNamestringus-california.ovpn
premiumbooleantrue / false
recommendbooleantrue / false
statusbooleantrue (enabled) / false

8. Deploy

Android app → Google Play Store

  1. Log in to play.google.com/console ($25 one-time if first time).
  2. Create a new app → choose defaults → fill required policy questionnaires.
  3. Release → ProductionCreate new release → upload app-release.aab.
  4. Fill store listing (icon, screenshots, description, privacy policy URL).
  5. Submit for review. First review is 1–7 days.
VPN apps: extra declaration required
Play Console → App content → VPN apps — declare your app as a VPN service and describe its primary purpose. Skipping this causes immediate rejection.

Admin Dashboard → Firebase Hosting

cd 02-admin-dashboard
firebase login              # first time only
npm run build
firebase deploy --only hosting
# optional — only needed for the onAdminCreated trigger
firebase deploy --only functions

The firebase.json shipped in 02-admin-dashboard/ already wires up build/ as the public directory and rewrites all routes to index.html (SPA mode):

02-admin-dashboard/firebase.json
firebase.json — hosting config with SPA rewrites

You normally don't need to edit it. If you do, change "public" to match your build output folder, or add custom "headers" for caching.

FAQ

For the full FAQ see FAQ.md. Quick answers below.

Do I need to be a developer?

Yes — basic developer skills (terminal, Firebase Console, signing apps). If not, hire a freelancer on Fiverr (~$30–80) and share the zip.

One Firebase project or two?

One. Android and Admin share the same Firestore collections (Servers, configs).

Can I change the package name / app ID?

Yes — edit buildSrc/src/main/kotlin/Config.ktapplicationId. You'll also need to register a new Android app in Firebase with the new package name and download a fresh google-services.json.

Billing Library version?

7.1.1. Meets Play's 7.0+ requirement (enforced since August 2025).

What's the OpenVPN license situation?

The bundled ovpn/ module is GPL-2.0. You can ship it in your closed-source app (this is how every commercial VPN on Play works). If you modify files inside ovpn/, those modifications must be open-sourced. Don't touch ovpn/ and you have nothing to worry about.

Android 14+ VPN stalls at "Connecting…" — known bug, 3-line fix

The bundled OpenVPN service predates Android 14's mandatory foregroundServiceType declaration, so startForeground() throws on API 34+ and the tunnel never finishes connecting. Android 13 and below unaffected.

Open 01-android-app/ovpn/src/main/AndroidManifest.xml and:

  1. Add <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/> near the other permissions.
  2. On the <service android:name=".core.OpenVPNService" ...> element add attribute android:foregroundServiceType="specialUse".
  3. Inside that service element, add a <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="VPN tunnel connection" />.

Rebuild. Connects normally afterwards. Full snippet in FAQ.md.

Troubleshooting

ProblemFix
Android: SDK location not foundRe-run bash setup.sh in 01-android-app/, or create local.properties manually with sdk.dir=/path/to/Android/sdk.
keytool: command not foundInstall JDK 17: brew install --cask temurin@17 (macOS) or adoptium.net.
Admin: blank page / auth errorsCheck .env uses Web app Firebase config, not Android.
setAdminRole.ts: Could not load default credentialsserviceAccountKey.json missing at 02-admin-dashboard/ root.
Windows: bash: command not foundInstall Git for Windows, or double-click setup.bat.
Gradle download very slowFirst build downloads ~500MB of dependencies. Subsequent builds are <1 min.
VPN stalls on Android 14+Bundled OpenVPN needs a foregroundServiceType declaration. 3-line fix in FAQ.
Play Console: "Disruptive Ads" rejectionAlready fixed in this package (7-min frequency cap + premium gating). Submit an appeal citing those fixes.

Licensing

Android app + Admin Dashboard — your CodeCanyon Regular License (single end-product) or Extended License (reusable across multiple end-products). Full terms at themeforest.net/licenses.

Bundled OpenVPN (01-android-app/ovpn/) — GPL-2.0. Mere aggregation in a closed-source app is allowed (this is standard for commercial VPNs). If you modify files inside the ovpn/ folder, those modifications must be available under GPL-2.0 on request. If you don't modify ovpn/, you have nothing extra to do.

Full license text: see 01-android-app/LICENSE and 02-admin-dashboard/LICENSE.

Support

Best channel is the CodeCanyon item comments page — replies within 24h on business days.

Please include:

  • Your item order number (from the Envato receipt email)
  • A screenshot of the error
  • Which step you were on (link to this page's section, e.g. #android)
  • Your OS + node -v + java -version output

Need custom work? Contact via the Envato profile on the item listing.

WitVPN CodeCanyon Package v4.1.4 · README · INSTALL · FAQ