18 KiB
VUKeyServerIntegration Component
Overview
The VUKeyServerIntegration component handles license key retrieval and syncing for VoiceUwu in an offline-first architecture. The app functions completely offline, with the keyserver only used for purchasing and retrieving keys associated with the user's account.
Architecture (Offline-First)
┌─────────────────────────────────┐ ┌─────────────────┐ ┌─────────────────────────────────┐
│ iOS App │ │ Your Server │ │ Android App │
│ (Offline-First) │ │ (KeyServer) │ │ (Offline-First) │
│ │ │ │ │ │
│ ┌─────────────────────────────┐ │ │ │ │ ┌─────────────────────────────┐ │
│ │ Local Key Storage │ │ │ │ │ │ Local Key Storage │ │
│ │ - Encrypted in Keychain │ │ │ │ │ │ - Encrypted locally │ │
│ │ - Full offline validation │ │ │ │ │ │ - Full offline validation │ │
│ │ - No network required │ │ │ │ │ │ - No network required │ │
│ └─────────────────────────────┘ │ │ │ │ └─────────────────────────────┘ │
│ │ │ │ │ │
│ ┌─────────────────────────────┐ │ │ │ │ ┌─────────────────────────────┐ │
│ │ Keys for All Feature │ │ │ │ │ │ Keys for All Feature │ │
│ │ - Auto-check on open │ │ │ │ │ │ - Auto-check on open │ │
│ │ - Manual sync button │ │ │ │ │ │ - Manual sync button │ │
│ │ - Shows available keys │ │ │ │ │ │ - Shows available keys │ │
│ └─────────────────────────────┘ │ │ │ │ └─────────────────────────────┘ │
└─────────────────────────────────┘ └─────────────────┘ └─────────────────────────────────┘
│ │ │
│ Sync: Get user's keys │ │
│──────────────────────────────→│ │
│ │ │
│ Response: [key1, key2, ...] │ │
│←──────────────────────────────│ │
│ │ │
│ │ Sync: Get user's keys │
│ │←──────────────────────────────│
│ │ │
│ │ Response: [key1, key2, ...] │
│ │──────────────────────────────→│
Components
1. VUKeyServerClient
Purpose: Client for keyserver communication (purchase, wallet management, feature unlocking) Platform: Cross-platform interface supporting multiple apps
protocol VUKeyServerClient {
// Multi-platform key pack purchasing
func purchaseKeyPack(pack: KeyPack, via platform: Platform) async throws -> KeyPurchaseResult
// Wallet management
func getWalletBalance(userId: String) async throws -> Int
func spendKeys(amount: Int, feature: String, app: String, userId: String) async throws -> SpendResult
func syncWallet(userId: String) async throws -> WalletState
// App-specific features
func getAppFeatures(appId: String) async throws -> [AppFeature]
func unlockFeature(featureId: String, appId: String, userId: String) async throws -> UnlockResult
func getUserFeatures(userId: String, appId: String) async throws -> [String]
// Community features
func donateKeys(amount: Int, userId: String) async throws -> DonationResult
func shareKeys(amount: Int, recipientEmail: String, userId: String) async throws -> ShareResult
}
enum Platform {
case apple, google, stripe, paypal, steam
}
struct KeyPurchaseResult {
let keysAdded: Int
let bonusKeys: Int
let communityDonated: Int
let transactionId: String
}
struct SpendResult {
let remainingBalance: Int
let featureUnlocked: Bool
let transactionId: String
}
2. Platform-Specific Purchase Managers
VUAppleStoreKitManager
Purpose: Handle Apple App Store purchases (iOS/macOS) Platform: iOS/macOS
class VUAppleStoreKitManager {
func purchaseProduct(_ productId: String) async throws -> StoreKit.Transaction
func restorePurchases() async throws -> [StoreKit.Transaction]
func sendTransactionToKeyServer(_ transaction: StoreKit.Transaction) async throws -> [VULicenseKey]
}
VUGooglePlayManager
Purpose: Handle Google Play purchases (Android) Platform: Android
class VUGooglePlayManager {
func purchaseProduct(_ productId: String) async throws -> GooglePlayPurchase
func sendPurchaseToKeyServer(_ purchase: GooglePlayPurchase) async throws -> [VULicenseKey]
}
VUStripeManager
Purpose: Handle Stripe payments (Web/Direct) Platform: Web/Cross-platform
class VUStripeManager {
func createPaymentIntent(amount: Int, email: String) async throws -> String
func confirmPaymentAndGetKeys(_ paymentIntentId: String) async throws -> [VULicenseKey]
}
VUPayPalManager
Purpose: Handle PayPal payments (Web/Direct) Platform: Web/Cross-platform
class VUPayPalManager {
func createPayment(amount: Double, email: String) async throws -> String
func executePaymentAndGetKeys(_ paymentId: String) async throws -> [VULicenseKey]
}
VUSteamManager
Purpose: Handle Steam purchases (PC Gaming) Platform: PC/Steam
class VUSteamManager {
func initiateSteamPurchase(_ productId: String) async throws -> String
func validateSteamPurchaseAndGetKeys(_ transactionId: String) async throws -> [VULicenseKey]
}
3. VUKeysForAllSync
Purpose: Handles the "Keys for All" feature sync functionality Platform: iOS/Android
class VUKeysForAllSync {
private let keyServerClient: VUKeyServerClient
private let keyStorage: VUKeyStorage
// Auto-sync when feature is opened
func autoSyncOnOpen() async throws -> [VULicenseKey]
// Manual sync via button
func manualSync() async throws -> [VULicenseKey]
// Check if user has account/email for syncing
func canSync() -> Bool
// Store retrieved keys locally
func storeRetrievedKeys(_ keys: [VULicenseKey]) async throws
}
Data Models
VULicenseKey
struct VULicenseKey: Codable {
let keyValue: String // The actual license key (e.g., "VUUW-ABCD-1234-WXYZ-L2")
let planType: VUPlanType // Level 1, Level 2, etc.
let purchaseDate: Date // When it was purchased
let serverMetadata: [String: Any]? // Optional server data
}
VUUserKeySync
struct VUUserKeySync: Codable {
let userEmail: String
let availableKeys: [VULicenseKey]
let syncTimestamp: Date
}
Integration Flows
Purchase Flow (Apple - iOS/macOS)
- User initiates purchase in iOS/macOS app
- StoreKit 2 handles payment with Apple
- App receives JWS-signed transaction
- App sends transaction to keyserver via
purchaseWithApple() - Keyserver validates JWS signature and generates keys
- Keyserver returns generated keys to app
- App stores keys locally using KeyStorage
Purchase Flow (Google Play - Android)
- User initiates purchase in Android app
- Google Play Billing handles payment
- App receives purchase token
- App sends purchase token to keyserver via
purchaseWithGoogle() - Keyserver validates with Google Play API and generates keys
- Keyserver returns generated keys to app
- App stores keys locally
Purchase Flow (Stripe - Web/Direct)
- User completes payment on website/web app
- Stripe processes payment and returns payment intent ID
- Client calls
purchaseWithStripe()with payment intent and email - Keyserver validates with Stripe API and generates keys
- Keyserver returns generated keys
- Keys stored locally in app
Purchase Flow (PayPal - Web/Direct)
- User completes payment via PayPal
- PayPal processes payment and returns payment ID
- Client calls
purchaseWithPayPal()with payment ID and email - Keyserver validates with PayPal API and generates keys
- Keyserver returns generated keys
- Keys stored locally in app
Purchase Flow (Steam - PC Gaming)
- User purchases through Steam store
- Steam processes payment and provides transaction ID
- Steam client calls
purchaseWithSteam()with Steam ID and transaction - Keyserver validates with Steam Web API and generates keys
- Keyserver returns generated keys
- Keys stored locally in Steam client/game
Keys for All Feature Flow
- User opens "Keys for All" feature
- App calls
autoSyncOnOpen()which:- Checks if user has email/account configured
- Calls
syncUserKeys()if available - Compares server keys with local keys
- Stores any new keys locally
- User can tap "Sync" button to trigger
manualSync() - All keys stored locally work offline immediately
Server-Side Integration
Endpoints
# Key pack purchase endpoints
POST /api/v1/keys/purchase/apple # Process Apple key pack purchase
POST /api/v1/keys/purchase/google # Process Google Play key pack purchase
POST /api/v1/keys/purchase/stripe # Process Stripe key pack payment
POST /api/v1/keys/purchase/paypal # Process PayPal key pack payment
POST /api/v1/keys/purchase/steam # Process Steam key pack purchase
# Wallet management endpoints
GET /api/v1/wallet/:userId # Get user's key wallet balance
POST /api/v1/wallet/spend # Spend keys on app feature
POST /api/v1/wallet/sync # Sync wallet across devices
GET /api/v1/wallet/history/:userId # Get spending transaction history
# App feature endpoints
GET /api/v1/apps/:appId/features # Get app's feature definitions
POST /api/v1/apps/:appId/unlock # Unlock feature in specific app
GET /api/v1/apps/:appId/unlocked/:userId # Get user's unlocked features
# Community endpoints
POST /api/v1/community/donate # Donate keys to community pool
POST /api/v1/community/share # Share keys with another user
GET /api/v1/community/pool # Get community pool status
Platform Validation Examples
// Apple JWS Validation
const jwt = require('jsonwebtoken');
const appleRootCert = loadAppleRootCertificate();
function validateAppleTransaction(jws) {
try {
const decoded = jwt.verify(jws, appleRootCert, {
algorithms: ['ES256'],
issuer: 'https://appleid.apple.com'
});
return decoded;
} catch (error) {
throw new Error('Invalid Apple transaction');
}
}
// Google Play Validation
async function validateGooglePlayPurchase(purchaseToken, productId) {
const response = await googlePlayAPI.purchases.products.get({
packageName: 'com.uwuapps.voiceuwu',
productId: productId,
token: purchaseToken
});
return response.data;
}
// Stripe Validation
async function validateStripePayment(paymentIntentId) {
const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId);
if (paymentIntent.status !== 'succeeded') {
throw new Error('Payment not completed');
}
return paymentIntent;
}
// PayPal Validation
async function validatePayPalPayment(paymentId) {
const payment = await paypal.payment.get(paymentId);
if (payment.state !== 'approved') {
throw new Error('Payment not approved');
}
return payment;
}
// Steam Validation
async function validateSteamPurchase(steamId, transactionId) {
const response = await steamWebAPI.GetUserInfo({
steamid: steamId,
transactionid: transactionId
});
return response;
}
// Purchase endpoints
app.post('/api/v1/purchase/apple', async (req, res) => {
const { transaction, userEmail } = req.body;
const validated = validateAppleTransaction(transaction);
const keys = generateKeysForPurchase(validated.productId, userEmail);
await storeUserKeys(userEmail, keys);
res.json({ keys });
});
app.post('/api/v1/purchase/google', async (req, res) => {
const { purchaseToken, productId, userEmail } = req.body;
const validated = await validateGooglePlayPurchase(purchaseToken, productId);
const keys = generateKeysForPurchase(productId, userEmail);
await storeUserKeys(userEmail, keys);
res.json({ keys });
});
app.post('/api/v1/purchase/stripe', async (req, res) => {
const { paymentIntentId, userEmail } = req.body;
const validated = await validateStripePayment(paymentIntentId);
const keys = generateKeysForPurchase(validated.metadata.productId, userEmail);
await storeUserKeys(userEmail, keys);
res.json({ keys });
});
app.post('/api/v1/purchase/paypal', async (req, res) => {
const { paymentId, userEmail } = req.body;
const validated = await validatePayPalPayment(paymentId);
const keys = generateKeysForPurchase(validated.transactions[0].item_list.items[0].sku, userEmail);
await storeUserKeys(userEmail, keys);
res.json({ keys });
});
app.post('/api/v1/purchase/steam', async (req, res) => {
const { steamId, transactionId } = req.body;
const validated = await validateSteamPurchase(steamId, transactionId);
const keys = generateKeysForPurchase(validated.productId, steamId);
await storeUserKeys(steamId, keys, 'steam');
res.json({ keys });
});
// Key sync endpoints
app.get('/api/v1/keys/email/:email', async (req, res) => {
const { email } = req.params;
const keys = await getUserKeys(email);
res.json({ keys });
});
app.get('/api/v1/keys/steam/:steamId', async (req, res) => {
const { steamId } = req.params;
const keys = await getUserKeys(steamId, 'steam');
res.json({ keys });
});
Security Features
Transaction Integrity
- Apple JWS signature validation
- HTTPS for all API calls
- Rate limiting on sync endpoints
- Input validation and sanitization
Key Protection
- Hardware-backed keystore (iOS Keychain)
- Encrypted local storage
- Local-only key validation
- No device binding required (offline-first)
Error Handling
Network Errors (Sync Only)
- Graceful failure - app continues working offline
- Retry logic with exponential backoff
- User feedback on sync failures
- Cached keys always work
Sync Errors
- Invalid email/token handling
- Server unavailable scenarios
- Network timeout handling
- Malformed response handling
Configuration
Server Configuration
{
"keyserver": {
"baseUrl": "https://api.voiceuwu.com",
"timeout": 15000,
"retryAttempts": 2
},
"apple": {
"bundleId": "com.uwuapps.VoiceUwu",
"environment": "production"
}
}
Client Configuration
struct VUKeyServerConfig {
let baseUrl: URL
let timeout: TimeInterval = 15.0 // Shorter timeout for sync
let retryAttempts: Int = 2 // Fewer retries for sync
let autoSyncOnOpen: Bool = true // Auto-sync when Keys for All opens
let syncCooldown: TimeInterval = 300 // 5 minutes between auto-syncs
}
Testing Strategy
Unit Tests
- Mock keyserver responses for sync operations
- Apple transaction validation
- Offline-first key validation logic
- Error handling scenarios (network failures)
Integration Tests
- End-to-end purchase flow
- Keys for All sync functionality
- Network failure scenarios (app continues working)
- Cross-platform key sharing
Implementation Notes
- Offline-First: App must work completely offline - network only for sync/purchase
- Multi-Platform Support: Single keyserver supports Apple, Google, Stripe, PayPal, and Steam
- Platform Requirements:
- Apple: StoreKit 2 for iOS/macOS
- Google: Play Billing API for Android
- Stripe: Payment Intents API for web/direct
- PayPal: REST API for web/direct
- Steam: Web API for PC gaming
- Server Validation: Each platform has its own validation method
- Minimal Network: 8 endpoints total (5 purchase + 3 sync)
- User Experience: Auto-sync on feature open + manual sync button
- Cross-Platform Keys: Same keys work across all platforms once synced
- No Session Management: Stateless key retrieval by email/token/steamId
- Security: Local key validation, HTTPS for sync, encrypted storage
- Web Portal Integration: Keys purchased through web portal immediately sync to all platforms
Related Documentation
- VU Key Web Portal: User-facing web application for key management, sharing, and purchasing