Deployment
hosting, Android, iOS.
Put the admin panel and the web wallet online, and build signed Android and iOS releases for Google Play, the App Store and TestFlight.
01Before any release
- Rebrand done (checklist).
crypto_wallet/.envfilled with your keys.- Version raised in
crypto_wallet/pubspec.yaml:version: 1.0.0+9means version 1.0.0, build 9. Every store upload needs a higher build number. - Checks pass:
cd crypto_wallet
flutter analyze
flutter test
02Admin panel on Firebase Hosting
crypto_wallet_admin/firebase.json has a hosting target admin. Its pre-deploy step builds the panel with PRODUCTION_MODE=true and the demo login off, and sends /config.json to the getRuntimeConfigJson function.
cd crypto_wallet_admin
firebase hosting:sites:create <admin-site>
firebase target:apply hosting admin <admin-site>
firebase deploy --only hosting:admin
- Add
<admin-site>.web.app(and any custom domain) to Authentication → Settings → Authorized domains, or sign-in fails. - Set
kAdminConfigUrltohttps://<admin-site>.web.app/config.jsonand rebuild the app. - Custom domain: Hosting → your site → Add custom domain, add the DNS records at your registrar, wait for the certificate.
The admin-demo target builds a showroom with a demo login and sample data. Use it only on a separate site, and only if you want to show the panel to prospects.
03Web wallet on Firebase Hosting
cd crypto_wallet
flutter build web --release --dart-define-from-file=.env
firebase hosting:sites:create <wallet-site>
firebase target:apply hosting app <wallet-site>
firebase deploy --only hosting:app
crypto_wallet/firebase.json already has the app target with single-page routing, caching and security headers. Add the domain to Authorized domains. For web push, fill the firebaseConfig block in web/firebase-messaging-sw.js and add --dart-define=WEB_PUSH_VAPID_KEY=<key> to the build.
04Web wallet or admin on Vercel
Both builds are static sites and run on any HTTPS host. With Vercel, build first, add a vercel.json to the build folder, then deploy that folder. flutter build web empties build/web, so copy vercel.json in after every build.
Wallet
cd crypto_wallet
flutter build web --release --dart-define-from-file=.env
cat > build/web/vercel.json <<'EOF'
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
EOF
npx vercel deploy build/web --prod
Admin panel
The admin must be built with the production flags, and /config.json has to reach your function:
cd crypto_wallet_admin
flutter build web --release --dart-define=PRODUCTION_MODE=true --dart-define=ADMIN_CLIENT_DEMO_LOGIN_ENABLED=false
cat > build/web/vercel.json <<'EOF'
{
"rewrites": [
{ "source": "/config.json",
"destination": "https://europe-west1-<your-project-id>.cloudfunctions.net/getRuntimeConfigJson" },
{ "source": "/(.*)", "destination": "/index.html" }
]
}
EOF
npx vercel deploy build/web --prod
Add the Vercel domain to Firebase Authorized domains. The security headers in firebase.json (frame, content-type, referrer and permissions policies, CSP) do not travel automatically; set them in Vercel under headers if you want the same protection.
05Backend updates
cd crypto_wallet
firebase deploy --only firestore:rules,firestore:indexes # rules or indexes changed
firebase deploy --only functions # functions or functions/.env changed
firebase deploy --only functions:recordFeeEvent # one function
06Android: APK and App Bundle
- Create an upload keystore onceBack up the file and the passwords; without them you cannot update your app.
keytool -genkey -v -keystore ~/upload-keystore.jks \ -keyalg RSA -keysize 2048 -validity 10000 -alias upload - Create android/key.propertiesNext to
android/build.gradle.kts. Never commit it. On a CI server you can useANDROID_KEYSTORE_PATH,ANDROID_KEYSTORE_PASSWORD,ANDROID_KEY_ALIAS,ANDROID_KEY_PASSWORDinstead.storePassword=<your store password> keyPassword=<your key password> keyAlias=upload storeFile=/Users/<you>/upload-keystore.jks - Build
cd crypto_wallet # App Bundle for Google Play flutter build appbundle --release --dart-define-from-file=.env # APK to install on a test phone flutter build apk --release --dart-define-from-file=.env - UploadPlay Console → your app → Testing or Production → Create new release → upload
build/app/outputs/bundle/release/app-release.aab. Use Play App Signing.
Without signing values the release build falls back to the debug key so that local release runs work. Google Play rejects debug-signed bundles.
07iOS: App Store and TestFlight
- Install pods
cd crypto_wallet/ios pod install --repo-update - SigningOpen
ios/Runner.xcworkspacein Xcode → Runner → Signing & Capabilities. Select your Team, check the bundle ID, keep Push Notifications, In-App Purchase and Keychain Sharing. - Push keyApple Developer → Keys → create an APNs key, then upload it in Firebase → Project settings → Cloud Messaging → Apple app configuration.
- Build the archive
cd crypto_wallet flutter build ipa --release --dart-define-from-file=.env - Upload to TestFlightXcode → Window → Organizer → Distribute App → App Store Connect, or drag the
.ipafrombuild/ios/ipa/into Apple's Transporter app. After processing it appears under TestFlight; add testers there. - Submit for reviewRewrite the permission texts in
ios/Runner/Info.plist(camera, Face ID, local network) with your app's name — reviewers read them.
08Store review notes
- Apple 3.1.5: crypto wallets must come from a developer enrolled as an organisation.
- Apple 3.1.1: sell PRO with In-App Purchase inside the iOS app; do not link to outside payment on iOS.
- Google Play: some countries require wallet providers to be registered or licensed. Check the policy pages for the countries you publish in.
- Fill App Privacy (Apple) and Data safety (Google) truthfully; Firebase Analytics, Crashlytics and Messaging collect data.
- Tell reviewers the wallet is non-custodial, how to create a test wallet, and give them PRO (Admin → Users → Grant PRO).
These are practical pointers. You are responsible for the licences and disclosures your business needs.
09After deploying: is it live?
- The admin URL shows the Sign In screen, with no "Preview" or "Client demo" badge.
/config.jsonon your admin returns JSON with your values.- A change saved and published in the admin appears in the app after a restart.
- A test push from Admin → Notifications arrives on a test phone.
- Firebase console → Functions → Logs shows no repeated errors.