Prerequisites
- Android Studio Hedgehog (2023.1.1) or newer
- JDK 11
- Android SDK with API 26 (min) and API 36 (target) platforms installed
- Android NDK
27.2.12479018— required to build the OpenVPN native module - A Firebase project for Analytics and Crashlytics
- A Google Play Console developer account (required to publish and to test in-app billing)
- A deployed copy of the StartMyVPN backend reachable over HTTPS
- The Android app source code (from your purchase)
Project Layout
Step 1 — Open the Project
Open the project root folder in Android Studio. Let Gradle sync complete. The initial sync will fail until you complete Steps 2 and 3 below — that is expected.Make sure the NDK version matches the one declared in
app/build.gradle.kts. You can install it via Android Studio → SDK Manager → SDK Tools → NDK (Side by side).Step 2 — Add the WireGuard Library
The WireGuard tunnel library (wireguard-tunnel.aar) is a native dependency that is not bundled with the release package. You can either:
- Build it from source: wireguard-android — follow its build instructions to produce
tunnel-release.aarand rename it towireguard-tunnel.aar. - Or obtain a compatible prebuilt copy.
com.wireguard.android:tunnel.
Step 3 — Configure Firebase
- Go to the Firebase Console and create a new project.
- Add an Android app with the package name you plan to use (e.g.
com.yourcompany.vpn). - Download
google-services.json. - Replace the placeholder file at
app/google-services.jsonwith your download.
google-services.json the project will not compile — this is enforced by the Google Services Gradle plugin.
Step 4 — Set the Package Name
Pick a unique Android application ID (reverse-DNS, all lowercase, e.g.com.yourcompany.vpn). Update it in all of the following places:
app/build.gradle.kts
app/google-services.json
The client[].client_info.android_client_info.package_name field must match your applicationId exactly. If it does not, the Google Services plugin will throw an error at build time.
app/src/main/AndroidManifest.xml
Update the deep-link host (see Step 7) and any tools:replace references that include a package path.
Step 5 — Configure the Backend URL
Open:Step 6 — Certificate Pinning
The app pins your backend’s TLS certificate via Android’s network security config to prevent MITM attacks. Openapp/src/main/res/xml/network_security_config.xml and:
-
Replace
yourdomain.comwith your actual API domain. -
Generate the SPKI SHA-256 hashes for your leaf certificate and its intermediate CA:
-
Replace the placeholder
YOUR_LEAF_CERT_PIN_HASHandYOUR_CA_CERT_PIN_HASHvalues with your computed hashes.
You should always pin two hashes: the current leaf (or its SPKI) and the issuing intermediate. When your certificate rotates, the intermediate will usually still match, preventing a hard outage.
Step 7 — Deep Links & Universal Links
The app handles password-reset and email-verification deep links of the form:app/src/main/AndroidManifest.xml, find the <intent-filter> with <data android:host="..."/> and set the host to your domain:
Step 8 — AdMob (optional)
If you want to display ads:- Create an AdMob account and register your Android app.
-
Update the AdMob App ID in
app/src/main/AndroidManifest.xml: -
Update the individual ad unit IDs in:
app/src/main/java/com/lunecore/vpn/ads/AdConfig.ktapp/src/main/java/com/lunecore/vpn/ads/InterstitialAdManager.ktapp/src/main/java/com/lunecore/vpn/ads/AppOpenAdManager.kt
<meta-data> entry — or simply toggle ads_enabled to false on your backend’s /api/v1/app-config endpoint to hide the ad UI server-side without any code changes.
Step 9 — Google Play Billing
In-app purchases are mapped to your backend’s plan catalog. To wire them up:- In Google Play Console, create your in-app products or subscriptions.
- Use product IDs that match the ones you configure on your backend’s plans (or map them in the admin panel).
- Upload a signed release build to an internal test track before testing billing — Google Play Billing does not work on debug builds distributed outside Play.
Step 10 — App Branding
App Name
Change theapp_name string resource in:
values-* folders if you want a different translated name.
App Icon
Replace the launcher icons underapp/src/main/res/mipmap-*/ with your own. The easiest way is to use Android Studio’s File → New → Image Asset wizard with a 1024×1024 master icon.
Theme Colors
The Material 3 color scheme lives at:Color.kt and Theme.kt to match your brand palette.
Splash / Hero imagery
Brand imagery used on the home screen and onboarding is underapp/src/main/res/drawable/. Replace PNGs and vector assets as needed.
Step 11 — Build & Sign a Release
1. Create a keystore
2. Configure signing
In Android Studio: Build → Generate Signed Bundle / APK → Android App Bundle, then either paste the keystore path each time or create asigningConfig in app/build.gradle.kts that reads from a local keystore.properties file (never committed).
3. Build an AAB
app/build/outputs/bundle/release/app-release.aab.
4. Upload to Google Play
- Create your app in the Play Console.
- Upload your first AAB to an internal test track.
- Fill out the store listing, content rating, data safety form, and privacy policy URL.
- Once internal testing is stable, promote to closed / open testing, then production.
Step 12 — Backend Requirements
The Android client consumes the REST API exposed by the StartMyVPN backend. The key endpoints it calls are:| Endpoint | Purpose |
|---|---|
POST /api/v1/auth/login | Email/password login |
POST /api/v1/auth/register | Registration |
POST /api/v1/auth/guest | Guest / anonymous device login |
POST /api/v1/auth/claim | Convert a guest account to a full account |
POST /api/v1/auth/forgot-password | Send password-reset email |
POST /api/v1/auth/reset-password | Consume password-reset token |
POST /api/v1/auth/resend-verification | Resend the email-verification mail |
PUT /api/v1/auth/change-email | Change email address |
GET /api/v1/user | Current user + active service |
PUT /api/v1/account/password | Change password |
GET /api/v1/servers | Full server list |
GET /api/v1/free-servers | Free-tier servers |
GET /api/v1/premium-servers | Premium-tier servers |
GET /api/v1/servers/{id}/wireguard | On-demand WireGuard config |
GET /api/v1/user/openvpn-credentials | OpenVPN username/password |
GET /api/v1/plans | Plan catalog |
POST /api/v1/iap/verify | Play Billing purchase verification |
POST /api/v1/iap/guest-verify | Play Billing verification for guests |
POST /api/v1/iap/restore | Restore a previous purchase |
GET /api/v1/app-config | Runtime feature flags (ads, maintenance, force-update) |
Troubleshooting
Gradle sync fails with 'Could not find com.wireguard.android:tunnel'
Gradle sync fails with 'Could not find com.wireguard.android:tunnel'
You have not placed
wireguard-tunnel.aar at app/libs/wireguard-tunnel.aar. See Step 2.'File google-services.json is missing' at build time
'File google-services.json is missing' at build time
You have not replaced the placeholder
app/google-services.json with a real file from your Firebase project. See Step 3.OpenVPN native build fails ('NDK not configured')
OpenVPN native build fails ('NDK not configured')
Install NDK
27.2.12479018 via Android Studio → SDK Manager → SDK Tools → NDK (Side by side). The OpenVPN module uses CMake and requires this exact NDK version or newer.The VPN connects but no traffic flows
The VPN connects but no traffic flows
Check that your server’s WireGuard or OpenVPN config is valid by testing it on desktop first. Also verify that the API returns a
config string in the expected format on GET /api/v1/servers/{id}/config.Play Billing returns 'Item unavailable'
Play Billing returns 'Item unavailable'
Certificate pinning failures in production
Certificate pinning failures in production
Your pins in
network_security_config.xml do not match the actual certificate chain served by your domain. Regenerate with openssl and verify by running the app in debug with OkHttp logging enabled.Quick Checklist
Package name
Update
namespace and applicationId in app/build.gradle.kts and in google-services.json.