App Configuration
This comprehensive guide covers all aspects of application configuration in OpenDev, from basic setup to config package generation and distribution.
Overview
App Configuration in OpenDev includes:
- Basic application information
- Channel and publisher bindings
- Product configurations
- Payment settings
- Configuration package generation and distribution
Accessing App Configuration
- Log in to OpenDev Platform
- Go to Apps in the sidebar
- Click on your application
- Navigate using the configuration tabs
Configuration Tabs
1. Basic Info
The foundation of your application configuration.
Fields
| Field | Description |
|---|---|
| App Name | Display name |
| App ID | Unique identifier (read-only after creation) |
| App Logo | Visual identifier |
| Platforms | Target platforms (iOS, Android, Web, Desktop) |
| Description | Internal notes |
| App Secrets | Authentication keys per environment |
Managing App Secrets
Development: For local development
Testing: For QA/staging environments
Production: For live applications
Each environment has its own secret to prevent accidental production access during development.
2. Channels Tab
Configure which channels distribute your application.
Binding Channels
- Click Bind Channel
- Select from available channels
- Configure channel-specific settings
- Save bindings
Channel Settings
| Setting | Description |
|---|---|
| Enabled | Channel is active |
| OAuth Providers | Login methods for this channel |
| Payment Provider | Payment processor |
| Region | Geographic availability |
Example Channel Binding
{
"channelKey": "google_play_global",
"enabled": true,
"oauth": {
"providers": ["google", "facebook"]
},
"payment": {
"provider": "google_play"
}
}
3. Products Tab
Configure products available in your application.
Product Configuration
- Click Add Product
- Select product tier to include
- Configure app-specific settings
- Save configuration
Product Settings
| Setting | Description |
|---|---|
| Enabled | Product is purchasable |
| Display Order | Sort order in app |
| Featured | Highlighted in UI |
| Promotional Price | Temporary discounts |
4. Payment Tab
Configure payment processing for your application.
Payment Platforms
Enable and configure each platform:
Stripe (Web/Desktop)
{
"platform": "stripe",
"enabled": true,
"publishableKey": "pk_live_xxx",
"webhookSecret": "whsec_xxx"
}
Google Play (Android)
{
"platform": "google_play",
"enabled": true,
"packageName": "com.yourcompany.app"
}
Apple (iOS)
{
"platform": "apple",
"enabled": true,
"bundleId": "com.yourcompany.app"
}
WeChat Pay (China)
{
"platform": "wechat_pay",
"enabled": true,
"mchId": "1234567890"
}
5. Config Packages Tab
Generate and manage configuration packages for client distribution.
Configuration Packages
What's in a Config Package?
A config package contains all client-side configuration:
{
"app": {
"id": "app_xxxxxxxxxxxx",
"name": "My Application",
"version": "1.0.0"
},
"channel": {
"key": "google_play_global",
"name": "Google Play Global"
},
"oauth": {
"providers": [
{
"id": "google",
"clientId": "xxxxx.apps.googleusercontent.com",
"scopes": ["email", "profile"]
}
]
},
"payment": {
"enabled": true,
"provider": "google_play"
},
"products": [
{
"id": "pro_monthly",
"name": "Pro Monthly",
"platformId": "pro_monthly_sub"
}
],
"services": {
"api": "https://api.yourdomain.com",
"cdn": "https://cdn.yourdomain.com"
}
}
Generating Config Packages
- Go to Config Packages tab
- Click Generate Package
- Select configuration options:
| Option | Description |
|---|---|
| Channel | Target distribution channel |
| Environment | dev, test, or prod |
| Platform | ios, android, web, or desktop |
- Click Generate
- Download or copy the configuration
Package Distribution Methods
1. Direct Download
- Download JSON file
- Bundle with app at build time
- Simple but requires app update for changes
2. CDN Publish
- Publish to CDN endpoint
- App fetches on startup
- Update config without app release
// Client code
const configUrl = 'https://cdn.yourdomain.com/config/app_xxx/prod/android.json';
const config = await fetch(configUrl).then(r => r.json());
3. API Endpoint
- Fetch from API at runtime
- Most flexible
- Requires network on startup
// Client code
const config = await api.get('/config', {
headers: {
'X-App-ID': appId,
'X-Channel-Key': channelKey,
'X-Environment': 'production'
}
});
CDN Configuration
Setting Up CDN
- Go to App Details > Config Packages
- Click CDN Settings
- Configure CDN endpoint:
| Field | Description |
|---|---|
| CDN Provider | AWS S3, Cloudflare, Aliyun OSS, etc. |
| Bucket/Container | Storage location |
| Base URL | Public access URL |
| Credentials | Access keys |
Publishing to CDN
- Generate a config package
- Click Publish to CDN
- Select target environment
- Confirm publication
Published configs are available at:
https://your-cdn.com/config/{appId}/{env}/{platform}.json
Version Management
Config Versioning
Each generated config gets a version:
- Timestamp-based
- Incrementing number
- Can rollback if needed
Version History
- Go to Config Packages tab
- Click History
- View all generated configs
- Download or rollback to previous versions
Integration Guide
Android Integration
// Load config at startup
class ConfigManager {
private lateinit var config: AppConfig
suspend fun loadConfig() {
val channelKey = BuildConfig.CHANNEL_KEY
val environment = if (BuildConfig.DEBUG) "dev" else "prod"
// Option 1: Bundled config
config = loadBundledConfig()
// Option 2: Remote config
config = fetchRemoteConfig(channelKey, environment)
}
private fun loadBundledConfig(): AppConfig {
val inputStream = context.assets.open("config.json")
return Gson().fromJson(inputStream.reader(), AppConfig::class.java)
}
private suspend fun fetchRemoteConfig(
channelKey: String,
environment: String
): AppConfig {
val url = "$CDN_BASE/$APP_ID/$environment/android.json"
return httpClient.get(url).body()
}
}
iOS Integration
class ConfigManager {
static let shared = ConfigManager()
var config: AppConfig?
func loadConfig() async {
// Option 1: Bundled config
if let bundledConfig = loadBundledConfig() {
self.config = bundledConfig
}
// Option 2: Remote config (with fallback)
if let remoteConfig = await fetchRemoteConfig() {
self.config = remoteConfig
}
}
private func loadBundledConfig() -> AppConfig? {
guard let url = Bundle.main.url(forResource: "config", withExtension: "json"),
let data = try? Data(contentsOf: url) else {
return nil
}
return try? JSONDecoder().decode(AppConfig.self, from: data)
}
private func fetchRemoteConfig() async -> AppConfig? {
let channelKey = Bundle.main.infoDictionary?["ChannelKey"] as? String ?? ""
let environment = isDebug ? "dev" : "prod"
let url = URL(string: "\(cdnBase)/\(appId)/\(environment)/ios.json")!
do {
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(AppConfig.self, from: data)
} catch {
return nil
}
}
}
Web Integration
// config.js
class ConfigManager {
constructor() {
this.config = null;
}
async loadConfig() {
const channelKey = process.env.REACT_APP_CHANNEL_KEY;
const environment = process.env.NODE_ENV === 'production' ? 'prod' : 'dev';
// Try remote config first
try {
const url = `${CDN_BASE}/${APP_ID}/${environment}/web.json`;
const response = await fetch(url);
this.config = await response.json();
} catch (error) {
// Fallback to bundled config
this.config = require('./config.bundled.json');
}
return this.config;
}
get(key) {
return this.config?.[key];
}
}
export const configManager = new ConfigManager();
Best Practices
Configuration Strategy
- Environment Separation
- Never use production secrets in development
- Separate config files per environment
- Clear naming conventions
- Security
- Don't include sensitive secrets in client config
- Use server-side proxy for sensitive operations
- Validate config integrity
- Performance
- Cache config locally
- Implement refresh strategy
- Handle offline gracefully
- Maintainability
- Document config changes
- Version control config templates
- Automate config generation in CI/CD
Config Refresh Strategy
// Recommended refresh approach
class ConfigRefreshManager {
constructor(configManager) {
this.configManager = configManager;
this.refreshInterval = 3600000; // 1 hour
}
startAutoRefresh() {
setInterval(async () => {
try {
const newConfig = await this.configManager.fetchRemoteConfig();
if (this.hasChanged(newConfig)) {
this.configManager.config = newConfig;
this.notifyListeners();
}
} catch (error) {
console.log('Config refresh failed, using cached');
}
}, this.refreshInterval);
}
hasChanged(newConfig) {
return JSON.stringify(newConfig) !== JSON.stringify(this.configManager.config);
}
}
Troubleshooting
Config Not Loading
Possible causes:
- Network issues
- Incorrect URL/path
- Config not published
Solutions:
- Check CDN URL is correct
- Verify config was published
- Test with direct URL access
- Check app permissions
OAuth Not Working
Possible causes:
- Missing OAuth config
- Incorrect client IDs
- Channel mismatch
Solutions:
- Verify OAuth providers in config
- Check client IDs match platform
- Ensure channel key is correct
Payment Issues
Possible causes:
- Payment not enabled
- Wrong platform config
- Product IDs mismatch
Solutions:
- Verify payment enabled in config
- Check platform-specific settings
- Match product IDs with platform console