A VPN client looks simple from the outside: a big toggle. Under the hood it touches the most sensitive APIs on the platform, and Google Play treats VPN apps with extra scrutiny. I built and shipped a cross-platform WireGuard client — Zerova / Zystra — through the full lifecycle, including a rebrand. Here's what actually matters.

Start with the native tunnel, not the UI

The tunnelling must live in the platform layer. On Android that means implementing a VpnService; on iOS, a NetworkExtension packet-tunnel provider. WireGuard gives you the crypto and the protocol, but you still own the service lifecycle: start, stop, reconnect, and the foreground notification.

In Flutter I keep the UI thin and push all tunnel control across a platform channel. The UI should never assume the tunnel is up — it renders the state the native layer reports, including transitions.

// Conceptual platform channel shape
setState(method: "connect", server: "sg-01", config: wgConfig)
// native side: create TUN, apply routes, start WireGuard, post notification
onStateChanged: { status: "connecting" | "connected" | "disconnected" }

The VPN service declaration is mandatory

Google Play requires any app using VpnService to complete a VPN service declaration and use the app as its core functionality. This is a policy gate, not a formality: misdeclaring gets the app removed. You must explain how the VPN works, that you use the platform's VpnService, and that you follow the secure-handling requirements.

Non-negotiable: declare the VPN service, use the Android VpnService API, and never redirect or inspect traffic you don't need. Play reviewers verify all three.

Data safety is where VPN apps get rejected

A VPN sees traffic metadata by nature, so the data-safety form must be precise. For a client that only tunnels and stores no logs, the declaration is minimal — but it has to match your actual behaviour exactly. Common rejection triggers I've seen:

Audit your dependency tree before you fill the form. The plugins you inherited count as your data collection.

Signing, rebranding, and versioning

If you're shipping under an organisation, you need an organisation account, a D-U-N-S number, and a verified developer identity — get this in place early because verification takes time. For the rebrand, the important detail is app signing continuity: keep the same signing key so Play treats the new build as an update, not a new app. Bundle IDs and signing keys are for life.

Store assets you'll need to produce for each release:

Handle the review feedback loop gracefully

Even a clean VPN submission often gets one round of questions. The winning approach is a precise, factual reply: quote the policy, point to where your app satisfies it, and attach evidence when asked. Vague responses slow review to a crawl. I keep a short "compliance dossier" per app — declarations, policy excerpts, and screenshots — so answers take minutes, not days.

Reliability is the real feature

A VPN that disconnects when the screen locks is worthless. After shipping, the work that keeps users is unglamorous: reconnect-on-network-change, kill-switch behaviour, reliable foreground notification, and battery-friendly keep-alive. Users judge a VPN on whether the toggle stays green, not on the crypto.

Takeaways

Do this and a VPN app is very shippable. Skip it and you'll meet the rejection queue — repeatedly.