diff --git a/README.md b/README.md index a3a4618..1ab5481 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Wowngeon +# Wownerogue -Wowngeon is a browser-based roguelike that synchronizes dungeon runs with Monero (XMR) and Wownero (WOW) block timing. The backend is a Node.js/Express server with Socket.IO for realtime play, optional payment enforcement, and automated payouts. +Wownerogue is a browser-based roguelike that synchronizes dungeon runs with Monero (XMR) and Wownero (WOW) block timing. The backend is a Node.js/Express server with Socket.IO for realtime play, optional payment enforcement, and automated payouts. ## Overview @@ -68,7 +68,7 @@ Only the payment routes require wallet RPC access; all other gameplay functions 1. Clone and enter the server directory. ```bash git clone - cd wowngeon/src + cd wownerogue/src ``` 2. Install dependencies. ```bash @@ -79,9 +79,13 @@ Only the payment routes require wallet RPC access; all other gameplay functions cp .env.example .env # edit .env to match your database and wallet settings ``` -4. Provision the database (migrations run automatically at startup). +4. Provision the database. ```bash - createdb wowngeon + # Create database and user + npm run db:create + + # (Optional) To reset the database later: + # npm run db:reset ``` 5. Start the development server. ```bash @@ -152,7 +156,7 @@ RPC_POLL_INTERVAL=2000 # Database DB_HOST=localhost DB_PORT=5432 -DB_NAME=wowgue +DB_NAME=wownerogue DB_USER=jw DB_PASSWORD=jw diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..5c5d929 --- /dev/null +++ b/TODO.md @@ -0,0 +1,17 @@ +# Wownerogue Development Todo + +## High Priority +- [ ] **Verify Server Startup**: Ensure `npm run dev` starts cleanly without errors after recent refactors. +- [ ] **Test Payment Flow**: Verify the full payment lifecycle (Request -> QR -> Payment -> Confirmation -> Game Start). +- [ ] **Test Payouts**: Confirm payouts are triggered correctly upon winning in paid modes. +- [ ] **Address Persistence**: Verify that payout addresses persist across server restarts via session tokens. + +## Improvements / Refactoring +- [ ] **Error Handling**: Extend `normalizeError` usage to remaining network modules for consistent error reporting. +- [ ] **Configuration Persistence**: Implement persistence for payment configuration updates to support hot reloads. +- [ ] **Monitoring Dashboard**: Build a lightweight dashboard for live metrics (active games, queue length, rate limits). +- [ ] **Frontend Cleanup**: Remove any remaining dead code or unused assets from the `html/` directory. + +## Documentation +- [ ] **API Documentation**: Expand API documentation in `README.md` with request/response examples. +- [ ] **Deployment Guide**: Create a detailed deployment guide including reverse proxy setup (Nginx/Caddy). diff --git a/UnifyPayments.md b/UnifyPayments.md deleted file mode 100644 index d495325..0000000 --- a/UnifyPayments.md +++ /dev/null @@ -1,812 +0,0 @@ -I'll first check if we've completed #4 (database indexes), then provide a comprehensive plan for the unified payment mode. - -## ๐Ÿ“Š Checking Database Indexes Status - -Let me check what indexes have been applied: - -```bash -# Check the migration file we created -cat src/migrations/004_security_improvements.sql | grep -E "CREATE INDEX|ALTER TABLE.*CONSTRAINT" -``` - -Looking at the migration file, we have: -- โœ… Created indexes for: games(user_id), games(status), payments(status), payments(created_at), users(last_seen) -- โœ… Added foreign key constraints for referential integrity -- โœ… Added audit_log and credit_transactions tables -- โš ๏ธ The migration had issues with CONCURRENTLY keyword (fixed by removing it) - -**Status: #4 is PARTIALLY complete** - The indexes exist in the migration but we need to verify they were actually applied to the database. - -## ๐ŸŽฏ Unified Payment Mode - Comprehensive Design Plan - -After deep review of the codebase, here's my detailed plan for merging PAID_SINGLE and PAID_CREDITS into a unified, flexible payment system: - -### ๐Ÿ“‹ Current State Analysis - -**PAID_SINGLE Mode:** -- User pays per game -- Payment required for each game -- Payouts: 2x for escape, 3x for escape with treasure -- No credit system -- Simple flow: Pay โ†’ Play โ†’ Win/Lose โ†’ Payout - -**PAID_CREDITS Mode:** -- User buys credit packages (10 credits for X WOW) -- Each game costs 1 credit -- Currently: Optional payouts (CREDITS_PAYOUT_ENABLED flag) -- Complex flow: Buy Credits โ†’ Play Multiple Games โ†’ Optional Payouts - -**Code Locations:** -- Mode logic: gameModeManager.js -- Payment handling: paymentHandlers.js -- Queue management: queueManager.js -- Configuration: `.env` file with GAME_MODE, CREDITS_PAYOUT_ENABLED - -### ๐Ÿ—๏ธ Unified Payment Architecture - -```javascript -// New Unified Payment Configuration Structure -const UNIFIED_PAYMENT_CONFIG = { - // Global payment system toggle - paymentsEnabled: true, - - // Currency configuration - currency: { - symbol: 'WOW', - decimals: 11, - minPayment: BigInt('1e10'), // 0.01 WOW minimum - maxPayment: BigInt('1e14') // 1000 WOW maximum - }, - - // Payment modes (both can be enabled simultaneously) - modes: { - // Direct pay-per-game mode - direct: { - enabled: true, - price: BigInt('1e11'), // 0.1 WOW per game - requiresAddress: true, - allowGuestPlay: false - }, - - // Credit package mode - credits: { - enabled: true, - creditsPerGame: 1, - requiresAddress: true, - packages: [ - { id: 'small', credits: 10, price: BigInt('5e11'), bonus: 0 }, - { id: 'medium', credits: 25, price: BigInt('10e11'), bonus: 2 }, - { id: 'large', credits: 100, price: BigInt('35e11'), bonus: 15 } - ], - // Allow mixing - can buy credits even in direct mode - allowMixedMode: true - } - }, - - // Payout configuration (applies to both modes) - payouts: { - enabled: true, - requiresKYC: false, // Future: regulatory compliance - - // Payout rules by mode - rules: { - direct: { - enabled: true, - multipliers: { - escape: 2.0, - escapeWithTreasure: 3.0, - perfectRun: 5.0 // Future: no damage, fast time - }, - minPayout: BigInt('1e10'), // 0.01 WOW minimum - maxPayout: BigInt('1e13') // 10 WOW maximum per game - }, - credits: { - enabled: true, - // Different multipliers for credit mode (operator choice) - multipliers: { - escape: 1.5, - escapeWithTreasure: 2.0, - perfectRun: 3.0 - }, - // Credits mode uses base price for calculations - baseValue: BigInt('5e10'), // 0.05 WOW per credit - minPayout: BigInt('1e10'), - maxPayout: BigInt('5e12') // 5 WOW maximum per game - } - }, - - // Payout processing - processing: { - automatic: true, - batchingEnabled: true, - batchInterval: 300, // 5 minutes - confirmations: 1, - maxRetries: 3 - } - }, - - // Promotional features - promotions: { - enabled: true, - freeCredits: { - onSignup: 0, - onFirstPurchase: 2, - referralBonus: 5 - }, - discounts: { - enabled: false, - bulkDiscount: 0.1, // 10% off large packages - happyHour: null // Future: time-based discounts - } - }, - - // Anti-abuse measures - limits: { - maxGamesPerHour: 60, - maxPayoutsPerDay: 100, - maxCreditPurchasePerDay: BigInt('1e14'), // 1000 WOW - cooldownBetweenGames: 5 // seconds - } -}; -``` - -### ๐Ÿ“ File Structure Refactoring - -``` -src/ -โ”œโ”€โ”€ payments/ -โ”‚ โ”œโ”€โ”€ UnifiedPaymentManager.js # NEW: Main payment orchestrator -โ”‚ โ”œโ”€โ”€ PaymentModeManager.js # NEW: Handles mode logic -โ”‚ โ”œโ”€โ”€ CreditManager.js # NEW: Credit operations -โ”‚ โ”œโ”€โ”€ PayoutManager.js # NEW: Payout processing -โ”‚ โ”œโ”€โ”€ PaymentValidator.js # NEW: Input validation -โ”‚ โ”œโ”€โ”€ moneroPayService.js # EXISTING: Keep -โ”‚ โ”œโ”€โ”€ qrService.js # EXISTING: Keep -โ”‚ โ””โ”€โ”€ walletRPCService.js # EXISTING: Keep -โ”œโ”€โ”€ game/ -โ”‚ โ”œโ”€โ”€ gameModeManager.js # REFACTOR: Simplify to game logic only -โ”‚ โ””โ”€โ”€ ... -โ””โ”€โ”€ config/ - โ”œโ”€โ”€ paymentConfig.js # NEW: Centralized config - โ””โ”€โ”€ environmentValidator.js # NEW: Validate .env settings -``` - -### ๐Ÿ”„ Migration Strategy - -#### Phase 1: Database Schema Updates (Day 1) -```sql --- New migrations/005_unified_payment_system.sql - --- Add payment mode tracking -ALTER TABLE payments ADD COLUMN payment_mode VARCHAR(20) DEFAULT 'direct'; -ALTER TABLE payments ADD COLUMN credit_package_id VARCHAR(20); -ALTER TABLE payments ADD COLUMN credits_purchased INTEGER DEFAULT 0; - --- Add user preferences -ALTER TABLE users ADD COLUMN preferred_payment_mode VARCHAR(20) DEFAULT 'direct'; -ALTER TABLE users ADD COLUMN total_credits_purchased INTEGER DEFAULT 0; -ALTER TABLE users ADD COLUMN total_games_played INTEGER DEFAULT 0; -ALTER TABLE users ADD COLUMN total_payouts_received BIGINT DEFAULT 0; - --- Add payout tracking improvements -CREATE TABLE IF NOT EXISTS payouts ( - id SERIAL PRIMARY KEY, - user_id INTEGER REFERENCES users(id), - game_id INTEGER REFERENCES games(id), - amount BIGINT NOT NULL, - multiplier DECIMAL(3,1) NOT NULL, - reason VARCHAR(50) NOT NULL, - status VARCHAR(20) DEFAULT 'pending', - tx_hash VARCHAR(64), - created_at TIMESTAMP DEFAULT NOW(), - processed_at TIMESTAMP, - error_message TEXT -); - --- Add credit transaction types -ALTER TABLE credit_transactions ADD COLUMN transaction_type VARCHAR(20); --- Types: 'purchase', 'game_fee', 'payout', 'bonus', 'refund', 'adjustment' - --- Indexes for performance -CREATE INDEX idx_payouts_status ON payouts(status); -CREATE INDEX idx_payouts_user_id ON payouts(user_id); -CREATE INDEX idx_payments_payment_mode ON payments(payment_mode); -``` - -#### Phase 2: Configuration System (Day 1-2) - -```javascript -// src/config/paymentConfig.js -class PaymentConfigManager { - constructor() { - this.config = this.loadConfig(); - this.validateConfig(); - } - - loadConfig() { - // Priority: Environment > Database > Defaults - const config = { ...DEFAULT_CONFIG }; - - // Load from environment - if (process.env.PAYMENT_MODES) { - const modes = process.env.PAYMENT_MODES.split(','); - config.modes.direct.enabled = modes.includes('direct'); - config.modes.credits.enabled = modes.includes('credits'); - } - - // Payouts configuration - config.payouts.enabled = process.env.PAYOUTS_ENABLED === 'true'; - config.payouts.rules.direct.enabled = - process.env.DIRECT_PAYOUTS_ENABLED !== 'false'; - config.payouts.rules.credits.enabled = - process.env.CREDIT_PAYOUTS_ENABLED === 'true'; - - // Price configuration - if (process.env.GAME_PRICE) { - config.modes.direct.price = BigInt(process.env.GAME_PRICE); - } - - return config; - } - - validateConfig() { - // Ensure at least one payment mode is enabled - if (!this.config.modes.direct.enabled && - !this.config.modes.credits.enabled) { - throw new Error('At least one payment mode must be enabled'); - } - - // Validate payout configuration - if (this.config.payouts.enabled) { - if (!this.config.modes.direct.enabled && - !this.config.payouts.rules.credits.enabled) { - console.warn('Payouts enabled but no payout rules active'); - } - } - - // Validate prices - this.validatePrices(); - - return true; - } - - validatePrices() { - const { direct, credits } = this.config.modes; - - if (direct.enabled && direct.price <= 0n) { - throw new Error('Direct game price must be positive'); - } - - if (credits.enabled) { - for (const pkg of credits.packages) { - if (pkg.price <= 0n) { - throw new Error(`Credit package ${pkg.id} has invalid price`); - } - if (pkg.credits <= 0) { - throw new Error(`Credit package ${pkg.id} has invalid credit count`); - } - } - } - } - - // Dynamic configuration updates - async updateConfig(updates) { - // Validate updates - const newConfig = { ...this.config, ...updates }; - - // Test configuration - const testManager = new PaymentConfigManager(); - testManager.config = newConfig; - testManager.validateConfig(); - - // Apply updates - this.config = newConfig; - - // Persist to database for next restart - await this.persistConfig(); - - // Notify all connected clients - this.broadcastConfigUpdate(); - } -} -``` - -#### Phase 3: Unified Payment Manager (Day 2-3) - -```javascript -// src/payments/UnifiedPaymentManager.js -class UnifiedPaymentManager { - constructor(config, walletService, db) { - this.config = config; - this.wallet = walletService; - this.db = db; - - this.creditManager = new CreditManager(db, config); - this.payoutManager = new PayoutManager(wallet, db, config); - this.validator = new PaymentValidator(config); - - this.pendingPayments = new Map(); - this.activeGames = new Map(); - } - - // Unified game start flow - async requestGameStart(userId, socketId) { - const user = await this.db.getUser(userId); - - // Check if user has credits - if (user.credits > 0 && this.config.modes.credits.enabled) { - return this.startGameWithCredits(user, socketId); - } - - // Check if direct payment is enabled - if (this.config.modes.direct.enabled) { - return this.startGameWithDirectPayment(user, socketId); - } - - // Offer credit purchase if only credits mode - if (this.config.modes.credits.enabled) { - return { - action: 'purchase_credits', - packages: this.config.modes.credits.packages - }; - } - - throw new Error('No payment method available'); - } - - async startGameWithCredits(user, socketId) { - // Validate credit balance - if (user.credits < this.config.modes.credits.creditsPerGame) { - return { - action: 'insufficient_credits', - required: this.config.modes.credits.creditsPerGame, - balance: user.credits, - packages: this.config.modes.credits.packages - }; - } - - // Deduct credits - await this.creditManager.deductCredits( - user.id, - this.config.modes.credits.creditsPerGame, - 'game_start' - ); - - // Start game - return { - action: 'game_start', - mode: 'credits', - creditsRemaining: user.credits - this.config.modes.credits.creditsPerGame - }; - } - - async startGameWithDirectPayment(user, socketId) { - // Check if user needs to set payout address - if (this.config.payouts.enabled && !user.payout_address) { - return { - action: 'require_payout_address', - message: 'Please set your payout address first' - }; - } - - // Generate payment request - const payment = await this.createPaymentRequest( - user.id, - this.config.modes.direct.price, - 'direct_game' - ); - - return { - action: 'payment_required', - payment: { - address: payment.address, - amount: payment.amount, - qr: payment.qr, - expires: payment.expires - } - }; - } - - // Handle game completion and payouts - async processGameCompletion(gameId, outcome) { - const game = await this.db.getGame(gameId); - const user = await this.db.getUser(game.user_id); - - // Determine if payout is due - const payoutConfig = this.getPayoutConfig(game.payment_mode); - if (!payoutConfig.enabled) { - return { payout: false }; - } - - // Calculate payout - const payout = this.calculatePayout(game, outcome, payoutConfig); - if (payout.amount <= 0n) { - return { payout: false }; - } - - // Process payout - const result = await this.payoutManager.processPayout( - user, - game, - payout - ); - - return { - payout: true, - amount: payout.amount, - multiplier: payout.multiplier, - status: result.status, - txHash: result.txHash - }; - } - - calculatePayout(game, outcome, config) { - let multiplier = 0; - let baseAmount = 0n; - - // Determine base amount - if (game.payment_mode === 'direct') { - baseAmount = this.config.modes.direct.price; - } else if (game.payment_mode === 'credits') { - baseAmount = this.config.payouts.rules.credits.baseValue; - } - - // Determine multiplier - if (outcome.reason === 'escaped') { - if (outcome.treasure) { - multiplier = config.multipliers.escapeWithTreasure; - } else { - multiplier = config.multipliers.escape; - } - - // Future: Perfect run bonus - if (outcome.perfectRun) { - multiplier = config.multipliers.perfectRun; - } - } - - // Calculate final amount - const amount = BigInt(Math.floor(Number(baseAmount) * multiplier)); - - // Apply limits - const minPayout = config.minPayout; - const maxPayout = config.maxPayout; - - if (amount < minPayout) return { amount: 0n, multiplier: 0 }; - if (amount > maxPayout) return { amount: maxPayout, multiplier }; - - return { amount, multiplier }; - } -} -``` - -#### Phase 4: Credit Management System (Day 3) - -```javascript -// src/payments/CreditManager.js -class CreditManager { - constructor(db, config) { - this.db = db; - this.config = config; - } - - async purchaseCredits(userId, packageId, paymentId) { - const pkg = this.config.modes.credits.packages.find(p => p.id === packageId); - if (!pkg) { - throw new Error('Invalid credit package'); - } - - const client = await this.db.pool.connect(); - try { - await client.query('BEGIN'); - - // Add credits to user account - const totalCredits = pkg.credits + pkg.bonus; - await client.query( - `UPDATE users - SET credits = credits + $1, - total_credits_purchased = total_credits_purchased + $2 - WHERE id = $3`, - [totalCredits, pkg.credits, userId] - ); - - // Log transaction - await client.query( - `INSERT INTO credit_transactions - (user_id, amount, transaction_type, reason, payment_id) - VALUES ($1, $2, $3, $4, $5)`, - [userId, totalCredits, 'purchase', `Package: ${packageId}`, paymentId] - ); - - // Add bonus credits if applicable - if (pkg.bonus > 0) { - await client.query( - `INSERT INTO credit_transactions - (user_id, amount, transaction_type, reason) - VALUES ($1, $2, $3, $4)`, - [userId, pkg.bonus, 'bonus', `Package ${packageId} bonus`] - ); - } - - await client.query('COMMIT'); - - return { - success: true, - creditsAdded: totalCredits, - newBalance: await this.getBalance(userId) - }; - - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - async deductCredits(userId, amount, reason) { - const client = await this.db.pool.connect(); - try { - await client.query('BEGIN'); - - // Check balance - const balance = await this.getBalance(userId); - if (balance < amount) { - throw new Error('Insufficient credits'); - } - - // Deduct credits - await client.query( - `UPDATE users - SET credits = credits - $1, - total_games_played = total_games_played + 1 - WHERE id = $2`, - [amount, userId] - ); - - // Log transaction - await client.query( - `INSERT INTO credit_transactions - (user_id, amount, transaction_type, reason) - VALUES ($1, $2, $3, $4)`, - [userId, -amount, 'game_fee', reason] - ); - - await client.query('COMMIT'); - - return { - success: true, - newBalance: balance - amount - }; - - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - async refundCredits(userId, amount, reason) { - // Similar to purchaseCredits but for refunds - } - - async getBalance(userId) { - const result = await this.db.query( - 'SELECT credits FROM users WHERE id = $1', - [userId] - ); - return result.rows[0]?.credits || 0; - } - - async getCreditHistory(userId, limit = 50) { - const result = await this.db.query( - `SELECT * FROM credit_transactions - WHERE user_id = $1 - ORDER BY created_at DESC - LIMIT $2`, - [userId, limit] - ); - return result.rows; - } -} -``` - -#### Phase 5: UI/UX Updates (Day 4) - -```javascript -// Frontend changes needed -// html/js/network/socketHandlers.js additions - -SocketHandlers.onPaymentModeSelection = function(data) { - // Show available payment options - if (data.modes.length === 1) { - // Single mode - proceed directly - this.proceedWithPaymentMode(data.modes[0]); - } else { - // Multiple modes - show selection UI - UI.showPaymentModeSelection(data.modes); - } -}; - -SocketHandlers.onCreditPackageSelection = function(data) { - // Show credit packages - const packages = data.packages.map(pkg => ({ - id: pkg.id, - credits: pkg.credits + (pkg.bonus || 0), - price: UI.formatCurrency(pkg.price), - bonus: pkg.bonus > 0 ? `+${pkg.bonus} bonus!` : '', - savings: this.calculateSavings(pkg) - })); - - UI.showCreditPackages(packages); -}; - -SocketHandlers.onPayoutProcessed = function(data) { - // Show payout notification - const message = `Payout processed: ${UI.formatCurrency(data.amount)} (${data.multiplier}x)`; - - UI.showNotification(message, 'success'); - - // Play appropriate sound - if (data.multiplier >= 3) { - AudioAlerts.playFile('big_win'); - } else { - AudioAlerts.playFile('payout_received'); - } -}; - -// UI additions -UI.showPaymentModeSelection = function(modes) { - const html = ` -
-

Select Payment Method

- ${modes.map(mode => ` -
-

${mode.name}

-

${mode.description}

- ${mode.price ? `

${UI.formatCurrency(mode.price)}

` : ''} - ${mode.creditsAvailable ? `

Credits: ${mode.creditsAvailable}

` : ''} - -
- `).join('')} -
- `; - - this.showModal(html); -}; -``` - -### ๐Ÿ”ง Environment Configuration - -```bash -# New .env configuration -# Payment System Configuration -PAYMENTS_ENABLED=true - -# Payment Modes (comma-separated: direct,credits) -PAYMENT_MODES=direct,credits - -# Direct Payment Configuration -DIRECT_PAYMENT_ENABLED=true -DIRECT_GAME_PRICE=100000000000 # 0.1 WOW -DIRECT_PAYOUTS_ENABLED=true -DIRECT_PAYOUT_ESCAPE=2.0 -DIRECT_PAYOUT_TREASURE=3.0 - -# Credit System Configuration -CREDITS_ENABLED=true -CREDITS_PER_GAME=1 -CREDITS_PAYOUTS_ENABLED=false # Operator choice -CREDITS_PAYOUT_BASE=50000000000 # 0.05 WOW per credit -CREDITS_PAYOUT_ESCAPE=1.5 -CREDITS_PAYOUT_TREASURE=2.0 - -# Credit Packages (JSON format) -CREDITS_PACKAGES='[ - {"id":"small","credits":10,"price":"500000000000","bonus":0}, - {"id":"medium","credits":25,"price":"1000000000000","bonus":2}, - {"id":"large","credits":100,"price":"3500000000000","bonus":15} -]' - -# Mixed Mode Settings -ALLOW_MIXED_MODE=true # Can use credits even in direct mode -PREFER_CREDITS_FIRST=true # Use credits before requesting payment - -# Payout Processing -PAYOUT_BATCH_ENABLED=true -PAYOUT_BATCH_INTERVAL=300 # 5 minutes -PAYOUT_MIN_AMOUNT=10000000000 # 0.01 WOW minimum -PAYOUT_MAX_PER_GAME=10000000000000 # 10 WOW maximum - -# Anti-Abuse Limits -MAX_GAMES_PER_HOUR=60 -MAX_PAYOUTS_PER_DAY=100 -GAME_COOLDOWN_SECONDS=5 -``` - -### ๐Ÿ“ˆ Migration Timeline - -**Week 1:** -- Day 1: Database schema updates, configuration system -- Day 2: UnifiedPaymentManager core implementation -- Day 3: CreditManager and PayoutManager -- Day 4: Frontend UI updates -- Day 5: Integration testing - -**Week 2:** -- Day 1-2: Migrate existing data -- Day 3: Staging deployment and testing -- Day 4: Performance optimization -- Day 5: Production deployment - -### โœ… Testing Strategy - -```javascript -// Test scenarios to cover -describe('Unified Payment System', () => { - test('Direct payment only mode', async () => { - const config = { modes: { direct: { enabled: true }, credits: { enabled: false }}}; - // Test direct payment flow - }); - - test('Credits only mode', async () => { - const config = { modes: { direct: { enabled: false }, credits: { enabled: true }}}; - // Test credit purchase and usage - }); - - test('Mixed mode - prefer credits', async () => { - const config = { - modes: { direct: { enabled: true }, credits: { enabled: true }}, - preferCreditsFirst: true - }; - // Test credits used before direct payment - }); - - test('Payout calculations - direct mode', async () => { - // Test 2x and 3x multipliers - }); - - test('Payout calculations - credits mode', async () => { - // Test different multipliers for credits - }); - - test('Anti-abuse limits', async () => { - // Test rate limiting and max limits - }); -}); -``` - -### ๐ŸŽฏ Benefits of This Approach - -1. **Maximum Flexibility**: Operators can enable/disable any combination -2. **Backward Compatible**: Existing configurations continue to work -3. **Future Proof**: Easy to add new payment methods or payout rules -4. **Granular Control**: Different payout rates for different modes -5. **Player Choice**: Users can choose their preferred payment method -6. **Revenue Optimization**: Credit packages encourage bulk purchases -7. **Risk Management**: Configurable limits and anti-abuse measures - -### ๐Ÿš€ Implementation Priority - -1. **High Priority (Days 1-3)**: - - UnifiedPaymentManager - - Configuration system - - Database migrations - - Basic mode switching - -2. **Medium Priority (Days 4-5)**: - - Credit management - - Payout processing - - UI updates - - Testing - -3. **Low Priority (Future)**: - - Advanced analytics - - A/B testing framework - - Dynamic pricing - - Loyalty rewards - -This plan provides a robust, flexible payment system that can adapt to different business models while maintaining code simplicity and maintainability. The operator gains complete control over payment modes, pricing, and payout rules through simple configuration changes. \ No newline at end of file diff --git a/html/audio/game1.mid b/html/audio/game1.mid new file mode 100644 index 0000000..cfebddc Binary files /dev/null and b/html/audio/game1.mid differ diff --git a/html/index.html b/html/index.html index 2e66ade..994ce5a 100644 --- a/html/index.html +++ b/html/index.html @@ -1,7 +1,7 @@ - Wowgue - A Wownero Roguelike + Wownerogue - A Wownero Roguelike @@ -13,7 +13,7 @@ (function(){ let opts = {}; try { - const existing = localStorage.getItem('wowngeon_token'); + const existing = localStorage.getItem('wownerogue_token'); if (existing) { opts.query = { resumeToken: existing }; } @@ -38,6 +38,7 @@ + @@ -59,6 +60,9 @@ + @@ -71,21 +75,21 @@ -