# EnakGame — Economy & Business Rules v1 | Item | Value | | --------------------------- | ----------------------------------------------------------------------------------- | | **Status** | Draft v1 | | **Purpose** | Source of truth untuk perencanaan dan implementasi EnakGame menggunakan Claude Code | | **Backend** | Go | | **Database** | PostgreSQL | | **Game Client** | Phaser | | **Frontend/Backoffice** | Next.js | | **Cache/Temporary Session** | Redis (opsional, sesuai kebutuhan implementasi) | --- ## 1. Product Overview EnakGame adalah game portal dalam ekosistem Enaklo/F&B. - User membayar **Coin** untuk memainkan mini-game (entry cost), dan dapat memperoleh **Coin** sebagai virtual reward dari hasil permainan. - Coin dapat dikonversi/digunakan sebagai **Point**, kemudian Point **hanya** dapat ditukar ke voucher yang disediakan Enaklo. - Point **tidak** dapat digunakan sebagai alat pembayaran dan **tidak** dapat diuangkan. - EnakGame **bukan** platform gambling dan **tidak** menggunakan uang tunai sebagai hadiah dari game. ### Core Flow ```text User ↓ Game Lobby ↓ Start Game ↓ Pay Entry Cost (Coin) + Create Game Session ↓ Play Game ↓ Submit Result ↓ Result Validation ↓ Reward Engine ↓ Economy Guard ↓ Coin Wallet + Ledger ↓ Coin → Point ↓ Voucher Redemption ↓ Realized Voucher Cost ↓ Budget Controller ``` --- ## 2. Core Business Principles ### 2.1 Server Is Authoritative Client/game (Phaser) **tidak boleh** menentukan reward final. Client hanya mengirim hasil permainan yang diperlukan, misalnya: - `session_id` - `score` - `outcome` - game-specific result data Backend menghitung reward berdasarkan configuration yang aktif. #### ❌ Tidak boleh ```text Phaser: score = 800 reward = 1000 Coin → backend menerima 1000 Coin ``` #### ✅ Yang benar ```text Phaser: score = 800 Backend: score 800 → load active reward configuration → calculate reward → validate limits → issue approved Coin ``` --- ## 3. Currency Model EnakGame memiliki dua konsep utama: **Coin** dan **Point**. ### 3.1 Coin Coin adalah **virtual game/reward currency**. Sumber utama: - Game reward - Event reward - Mission reward - Bonus/promo yang diizinkan Penggunaan: - Membayar entry cost untuk memainkan game (lihat [Section 10.1](#101-game-entry-cost)) - Dikonversi ke Point Coin dapat memiliki expiration (lihat [Section 4](#4-coin-expiration)). ### 3.2 Point Point adalah **redemption currency**. Current business rule: ```text 1 Coin = 1 Point = Rp1 ``` Penggunaan Point dibatasi: - Point **hanya** dapat ditukar ke voucher (lihat [Section 26](#26-redemption-flow)). - Point **tidak** dapat digunakan sebagai alat pembayaran (misalnya membayar order). - Point **tidak** dapat diuangkan (cash out). Nilai `Rp1` di atas adalah nilai acuan untuk perhitungan voucher dan budget, **bukan** nilai tukar ke uang tunai. Konversi dan expiration mengikuti sistem existing ([prd-point-coin.md](prd-point-coin.md)), tidak didesain ulang: - **Coin → Point** menggunakan fitur exchange existing (K3, F4): dilakukan manual oleh customer, satu arah, kurs di-configure di level organisasi (default 1 : 1). - **Point expiration** menggunakan sistem expiration existing per lot (F12). > **Catatan perubahan dari sistem existing:** Berdasarkan keputusan sebelumnya ([prd-point-coin.md](prd-point-coin.md)), backend saat ini masih mengizinkan EnakPoint dipakai untuk **membayar order** (point payment, ledger `PAYMENT` / `PAYMENT_REFUND`). Aturan EnakGame di atas menggantikan keputusan tersebut, sehingga fitur point payment perlu di-update/dinonaktifkan sebelum EnakGame berjalan. Belum ada order yang dibayar dengan Point, jadi tidak ada data lama yang perlu dimigrasi. Saldo Point user tetap sebagai Point; yang berubah hanya penggunaannya (redeem voucher saja). Namun Coin dan Point tetap diperlakukan sebagai **konsep/domain yang berbeda** agar sistem tidak terlalu tightly coupled. Tujuannya agar business rule di masa depan dapat berubah tanpa membongkar Game/Reward Engine. --- ## 4. Coin Expiration Sistem Coin sudah memiliki konsep expiration. Expiration harus tetap menjadi bagian dari economy. Coin yang expired: ```text Wallet usable balance ↓ berkurang Ledger ↓ mencatat expiration transaction ``` > **Penting:** Expiration **tidak boleh** dianggap sebagai voucher redemption. Kategori transaksi harus dapat dibedakan: ```text Wallet usable balance ↓ berkurang Ledger ↓ mencatat expiration transaction ``` Implementasi detail expiration mengikuti sistem existing dan tidak perlu didesain ulang kecuali diperlukan. --- ## 5. Budget Model ### 5.1 Budget Is Shared Semua game menggunakan **budget pool yang sama**. Tidak ada kewajiban setiap game memiliki budget terpisah. Contoh: ```text EnakGame Global Budget Rp100.000.000 │ ├── Runner ├── Spin ├── Memory └── Puzzle ``` Semua reward normal game berasal dari economy/budget pool yang sama. Pengecualian: **Event/Campaign** memiliki budget sendiri (lihat [Section 7](#7-budget-hierarchy)). ### 5.2 Budget Period - Global budget menggunakan periode **bulanan** secara default. - Periode budget dapat di-**configure**. - Setiap Event/Campaign memiliki budget sendiri, terpisah dari global budget bulanan. --- ## 6. Budget vs Realized Cost ### 6.1 Budget Is Based on Actual Voucher Redemption Budget Controller menggunakan **voucher yang benar-benar diredeem** sebagai dasar actual cost. Coin yang baru diterbitkan **bukan** otomatis dianggap sebagai biaya voucher. Contoh: | Metric | Jumlah | | ------------------- | ---------- | | Coin Generated | 20.000.000 | | Coin Outstanding | 12.000.000 | | Coin Expired | 3.000.000 | | Coin Used/Converted | 5.000.000 | ```text Actual Voucher Cost = Rp5.000.000 ``` Karena `1 Point = Rp1` dan Point digunakan untuk redemption. ### 6.1.1 Hanya Point dari EnakGame yang Dihitung ke Budget Satu voucher dapat dibayar dengan Point dari berbagai asal: reward game, belanja (earning order), transfer, atau adjustment. Realized cost yang dihitung ke budget EnakGame (global maupun event) **hanya** bagian voucher yang dibayar dengan Point yang berasal dari **reward EnakGame**. Bagian yang dibayar dengan Point dari belanja tetap dicatat untuk reporting Finance, tetapi **tidak** mengurangi budget mana pun. ```text Voucher face value = Rp10.000, point cost = 8.000 Point dipakai = 6.000 (dari reward game) + 2.000 (dari belanja) Dihitung ke budget = Rp7.500 Tidak ke budget = Rp2.500 ``` ### 6.2 Important Distinction Sistem harus membedakan: | Konsep | Definisi | | ----------------- | ------------------------------------------------------------ | | **Issuance** | Berapa Coin yang dikeluarkan oleh Reward Engine | | **Outstanding** | Berapa Coin masih berada di user/economy | | **Redemption** | Berapa Point benar-benar digunakan untuk mendapatkan voucher | | **Realized Cost** | Nilai voucher yang benar-benar menjadi cost bisnis | Budget Controller terutama menggunakan **realized redemption/cost**, bukan sekadar total Coin issuance. --- ## 7. Budget Hierarchy Secara konsep: ```text GLOBAL BUDGET (bulanan, configurable) │ └── Regular Game Activity (reward normal semua game) EVENT/CAMPAIGN BUDGET (per event, terpisah) │ └── Tambahan reward dari event (multiplier + bonus) ``` **Event dan Campaign adalah hal yang sama.** Setiap event memiliki **budget sendiri**, terpisah dari global budget. Pembagian biaya saat event berjalan: | Komponen reward | Dibiayai oleh | | ------------------------------------ | --------------- | | Reward normal game (base reward) | Global budget | | Tambahan dari event (multiplier/bonus) | Budget event | Contoh: ```text Normal Reward 10 Coin → Global budget Ramadan 2x +10 Coin → Budget event Ramadan ────────────────────────── Final Reward 20 Coin ``` Realized cost dari Point yang berasal dari tambahan event dihitung ke budget event tersebut, **kapan pun** Point itu ditukar ke voucher, termasuk setelah event berakhir. --- ## 8. Budget Metrics Minimum metric yang harus tersedia: - Total Budget - Allocated Budget - Realized Cost - Remaining Budget - Budget Utilization - Forecasted Cost - Forecasted Remaining Budget - Budget Status Contoh status: - `HEALTHY` - `WARNING` - `CRITICAL` - `EXHAUSTED` Threshold harus **configurable**. --- ## 9. Game Management Game Management mengelola semua game yang tersedia di EnakGame. Minimum information: - `id` - `name` - `slug` - `description` - `thumbnail` - game URL/path - `version` - `status` - `configuration` - `created_at` - `updated_at` Game status minimal: - `DRAFT` - `ACTIVE` - `INACTIVE` - `ARCHIVED` Game harus dapat memiliki reward configuration. --- ## 10. Game Session Setiap permainan yang dapat menghasilkan reward **harus** memiliki session. Flow: ```text User ↓ Start Game ↓ Debit Entry Cost (Coin) + Create Game Session ↓ Phaser Game ↓ Submit Result ↓ Validate Session ↓ Calculate Reward ↓ Issue Reward ``` Session harus dapat mencegah: - duplicate completion - duplicate reward - expired session - invalid session - user mismatch - game mismatch Recommended concept: `game_session_id` menjadi **idempotency key** untuk reward completion. > **Penting:** Satu session tidak boleh menghasilkan reward berkali-kali. ### 10.1 Game Entry Cost Semua game **wajib berbayar** menggunakan **Coin**. Tidak ada game gratis. - Entry cost di-configure per game sebagai bagian dari game configuration, dengan nilai minimal **1 Coin**. - Entry cost dibayar dengan **Coin**, bukan Point. - Coin dipotong saat **Start Game**, yaitu saat session dibuat — bukan saat submit result. - Debit Coin dan pembuatan session terjadi dalam **satu database transaction**. Jika salah satu gagal, tidak ada Coin yang terpotong dan tidak ada session yang terbuat. - Jika usable balance Coin tidak cukup, session **tidak** dibuat dan user tidak dapat bermain. - Entry cost yang berlaku dicatat di session (snapshot), sehingga perubahan configuration tidak mengubah session yang sudah berjalan. - Debit dicatat di ledger sebagai `SPENT` dengan reference `game_session_id` dan `game_id`. - Satu session hanya boleh didebit **satu kali** (`game_session_id` menjadi idempotency key untuk debit). ```text Start Game → load game configuration (entry cost) → check Coin usable balance → [1 transaction] debit Coin (SPENT) + create session → return session_id ke Phaser ``` #### Entry Cost & Budget Entry cost **tidak** mengurangi atau meng-offset perhitungan budget. Budget Controller dan Budget Metrics hanya memperhitungkan **reward** (Coin yang diterbitkan) dan realized voucher cost. Coin yang dibayar untuk bermain tidak dianggap sebagai pemasukan yang menambah budget. ```text Reward issued = 1.000.000 Coin Entry cost paid = 400.000 Coin Budget memakai → 1.000.000 Coin (reward), bukan 600.000 (net) ``` ### 10.2 Entry Cost Refund Entry cost dapat dikembalikan (refund) ke user untuk session yang tidak dapat diselesaikan. - Refund mengembalikan **jumlah Coin yang sama** dengan entry cost yang tercatat di session. - Refund dicatat di ledger sebagai `REFUND` dengan reference `game_session_id` dan reference ke transaksi `SPENT` asalnya. - Refund **wajib idempotent**: satu session maksimal satu kali refund. - Session yang sudah di-refund **tidak** boleh menghasilkan reward, dan session yang sudah rewarded **tidak** boleh di-refund. - Refund bukan reward: tidak melewati Reward Engine, tidak dihitung sebagai Coin issued, dan tidak dihitung di budget. - Refund harus auditable (siapa/sistem apa yang memicu dan alasannya). Kondisi refund: | Kondisi | Refund | | -------------------------------------------------------- | -------------------------- | | System error (session gagal diselesaikan karena sistem) | **Ya, otomatis** oleh sistem | | Game di-nonaktifkan saat session sedang berjalan | **Ya, otomatis** oleh sistem | | Session expired/abandoned oleh user | **Tidak** | - Refund dijalankan **otomatis** oleh sistem, tanpa perlu request user atau approval admin. - Session yang ditinggalkan user tidak di-refund, agar user tidak dapat memulai game lalu meninggalkannya saat hasilnya tidak menguntungkan. --- ## 11. Reward Engine Reward Engine adalah komponen yang menentukan berapa reward yang **secara teoritis** berhak diterima user berdasarkan result dan configuration. Reward Engine **tidak** bertanggung jawab sendirian untuk memutuskan apakah reward boleh diterbitkan. Flow: ```text Game Result ↓ Reward Engine ↓ Base Reward ↓ Event Modifier / Bonus ↓ Final Calculated Reward ↓ Economy Guard ``` --- ## 12. Supported Reward Types Reward Engine dirancang agar **extensible**. Jenis reward minimum yang dapat didukung: ### `FIXED` ```text Play completed → 5 Coin ``` ### `SCORE_BASED` | Score | Reward | | -------- | ------- | | 0–100 | 1 Coin | | 101–500 | 5 Coin | | 501–1000 | 10 Coin | | 1001+ | 20 Coin | ### `OUTCOME_BASED` | Outcome | Reward | | --------- | ------- | | `PERFECT` | 20 Coin | | `GOOD` | 10 Coin | | `NORMAL` | 5 Coin | | `FAIL` | 0 Coin | ### `PROBABILITY` Reward berdasarkan probability yang **dihitung server**. Contoh: | Probability | Reward | | ----------- | --------- | | 0.1% | 1000 Coin | | 1% | 100 Coin | | 10% | 10 Coin | | 88.9% | 0 Coin | Probability harus tervalidasi. ### `MULTIPLIER` Base reward dikalikan modifier. ```text Base = 10 Coin Multiplier = 2x Final = 20 Coin ``` ### `TIERED` Reward berdasarkan tier/user/game progression. --- Jenis reward dapat ditambah di masa depan tanpa mengubah seluruh engine. --- ## 13. Reward Configuration Reward configuration harus memiliki **lifecycle/version**. Contoh: | Config | Reward | | ----------------------- | ------- | | Runner Reward Config v1 | 10 Coin | | Runner Reward Config v2 | 8 Coin | > **Penting:** Jangan overwrite configuration lama jika configuration tersebut sudah pernah digunakan dalam transaksi. Tujuan: - audit - debugging - historical accuracy - mengetahui rule yang berlaku saat user bermain Setiap reward transaction idealnya dapat dilacak ke configuration/version yang digunakan. --- ## 14. Normal Reward + Event Reward Normal reward dan event modifier **boleh aktif bersamaan**. Contoh: ```text Normal Reward 10 Coin Ramadan Event 2x multiplier Mission Bonus +5 Coin ───────────────────────── Final Reward 25 Coin ``` Event **tidak boleh** mengubah permanent/base configuration game. Event bekerja sebagai **layer/override/modifier**. Setelah event selesai: ```text Normal Reward 10 Coin ``` --- ## 15. Event Management Event digunakan untuk seasonal/campaign activity. Contoh: - Ramadan - Christmas - Independence Day - Anniversary - F&B campaign Minimum event data: - `id` - `name` - `slug` - `description` - `banner` - `start_at` - `end_at` - `timezone` - `status` - `priority` - reward configuration/modifier - budget sendiri (lihat [Section 7](#7-budget-hierarchy)) - rules Event dapat menentukan: - participating games - reward multiplier - bonus reward - mission - daily limit - user limit - leaderboard - event-specific campaign rules --- ## 16. Multiple Event Handling Karena event dapat overlap, sistem harus memiliki **priority/stacking rule**. Contoh: ```text Normal Reward + Event A + Event B ``` Sistem harus memiliki aturan jelas apakah: - hanya event priority tertinggi yang berlaku - modifier dapat ditumpuk - bonus dapat ditumpuk - ada maximum multiplier Default recommendation: | Komponen | Rule | | ------------------ | --------------------- | | Reward Modifier | Controlled stacking | | Bonus | Independently tracked | | Maximum Reward Cap | Always enforced | > **Penting:** Final stacking rules harus ditentukan sebelum production. --- ## 17. Economy Guard Economy Guard menentukan apakah calculated reward **benar-benar boleh diterbitkan**. Minimum checks: - session valid - session belum rewarded - user valid - game valid - reward configuration active - user daily limit - game daily limit - event limit - global limit - maximum reward - budget/economy status - fraud/risk checks - idempotency Contoh: ```text Reward Engine → 20 Coin Economy Guard → daily limit OK → event limit OK → duplicate NO → budget status OK Approved → issue 20 Coin ``` --- ## 18. Coin Wallet Wallet menyimpan **current usable balance**. Namun wallet **bukan** satu-satunya source of truth. Source of truth untuk audit adalah **ledger**. | Komponen | Digunakan untuk | | ---------- | ---------------------------------------------------------------- | | **Wallet** | fast balance lookup, current balance | | **Ledger** | transaction history, audit, reconciliation, debugging, reporting | --- ## 19. Coin Ledger Setiap perubahan balance **harus** menghasilkan ledger transaction. Minimum transaction concepts: - `EARNED` - `BONUS` - `SPENT` - `EXPIRED` - `ADJUSTMENT` - `REVERSAL` - `CONVERSION` - `REFUND` `SPENT` mencakup pembayaran entry cost game. `REFUND` adalah pengembalian entry cost (lihat [Section 10.2](#102-entry-cost-refund)). Transaction harus dapat memiliki reference: - `game_session_id` - `game_id` - `event_id` - `voucher_redemption_id` - `mission_id` - admin adjustment reference Idealnya setiap transaction memiliki: - `balance_before` - `amount` - `balance_after` --- ## 20. Idempotency Reward transaction **wajib idempotent**. Contoh: ```text POST /game-session/complete session_id = ABC ``` Request pertama: ```text Reward = 10 Coin Status = SUCCESS ``` Request kedua dengan session yang sama: ```text Tidak membuat reward baru. Return existing reward/result. ``` Database harus memiliki **unique constraint/strategy** yang menjamin satu session tidak bisa menghasilkan duplicate reward. Hal yang sama berlaku untuk entry cost: satu session maksimal satu debit `SPENT` dan satu `REFUND`. --- ## 21. Voucher Management Voucher Management adalah **core module tersendiri**. Voucher adalah benefit yang dapat ditukar menggunakan Point. Minimum voucher data: - `id` - `name` - `description` - `image` - provider/merchant - voucher value - point cost - stock - validity - terms - status - redemption rules Sumber voucher mendukung **keduanya**: - **Internal voucher codes** — kode dikelola sendiri (lihat [Section 25](#25-voucher-stock)). - **External API** — kode/voucher diterbitkan oleh provider eksternal saat redemption. --- ## 22. Voucher Value vs Point Cost Jangan menganggap `Voucher Value = Point Cost` selamanya. Current conversion: ```text 1 Coin = 1 Point = Rp1 ``` Tetapi sebuah voucher dapat memiliki: | Voucher Value | Point Cost | | ------------- | ---------- | | Rp10.000 | 8.000 | | Rp10.000 | 12.000 | Hal ini memungkinkan Product/Finance mengatur **subsidy, promotion, atau margin**. --- ## 23. Voucher Types Voucher Management harus **extensible**. Contoh: | Type | Contoh | | ----------------------- | ---------------------------------------------------------- | | **Fixed Value** | Rp5.000, Rp10.000, Rp20.000 | | **Percentage Discount** | 10%, 20%, 50% | | **Free Item** | Free Coffee, Free Food | | **Merchant Benefit** | Benefit yang hanya berlaku pada merchant/location tertentu | Jenis voucher dapat ditambah sesuai kebutuhan. --- ## 24. Voucher Inventory Jika voucher menggunakan unique code, sistem harus memiliki inventory. Status minimal: - `AVAILABLE` - `RESERVED` - `REDEEMED` - `EXPIRED` - `CANCELLED` Flow normal: ```text AVAILABLE → RESERVED → REDEEMED ``` Jika redemption gagal/timeout: ```text RESERVED → AVAILABLE ``` Jika voucher expired: ```text AVAILABLE → EXPIRED ``` --- ## 25. Voucher Stock Voucher dapat menggunakan salah satu dari: ### Static Stock Admin memasukkan jumlah stock. ```text Stock = 1.000 ``` ### Code Pool Admin/provider memasukkan unique voucher codes. ```text CODE-001 CODE-002 CODE-003 ... ``` System harus mengetahui stock available secara **reliable**. --- ## 26. Redemption Flow Recommended flow: ```text User ↓ Select Voucher ↓ Check Point Balance ↓ Check Voucher Availability ↓ Reserve Voucher ↓ Deduct Point ↓ Issue Voucher ↓ Mark Voucher Redeemed ↓ Create Redemption Record ↓ Record Realized Cost ``` Transaction harus **atomic**. > **Penting:** Jangan sampai Point sudah dikurangi **+** voucher gagal diberikan tanpa recovery/rollback. --- ## 27. Redemption Idempotency Redemption juga **harus idempotent**. Satu redemption request tidak boleh menghasilkan: - dua voucher - dua Point deduction - dua cost records Gunakan idempotency/reference key. --- ## 28. Realized Cost Setiap successful voucher redemption menghasilkan **realized cost**. Contoh: ```text Voucher Value = Rp10.000 Point Used = 10.000 Realized Cost = Rp10.000 ``` Realized cost menggunakan **voucher face value**. Sistem tetap menyimpan field berikut secara terpisah: - `voucher_value` — face value, menjadi dasar realized cost dan budget - `point_cost` — Point yang dibayar user - `business_cost` — opsional, untuk reporting Finance jika berbeda dari face value; **tidak** dipakai untuk budget --- ## 29. Budget Controller Budget Controller bertugas mengontrol reward economy berdasarkan **actual redemption dan forecast**. ### Input Minimum input: - global budget - realized voucher cost - remaining budget - remaining days - historical redemption - coin issuance - active users - plays - average reward - event status - redemption rate - target utilization ### Output Budget Controller dapat menghasilkan: - budget status - forecast cost - recommended reward multiplier - recommended reward rate - warning - automatic adjustment - stop reward recommendation --- ## 30. Redemption-Based Forecast Contoh kondisi: | Metric | Nilai | | -------------- | ------ | | Global Budget | Rp100M | | Realized Cost | Rp60M | | Remaining | Rp40M | | Remaining Days | 10 | Jika current burn rate terlalu tinggi: ```text Forecast = Rp115M ``` System dapat menghitung adjustment. Contoh: | Item | Nilai | | ---------------------- | -------- | | Current Reward | 10 Coin | | Recommended Multiplier | 0.85x | | New Effective Reward | 8.5 Coin | > **Penting:** Rounding rule harus ditentukan agar reward final tidak menghasilkan pecahan Coin jika Coin integer. --- ## 31. Dynamic Rate Safety Reward rate **tidak boleh** berubah secara liar setiap request. Gunakan: - configuration version - `effective_at` - cooldown - min/max multiplier - adjustment step - forecast window - audit log Contoh: ```text Current = 10 Coin Allowed adjustment step = 10% Next possible: 9 Coin atau 11 Coin ``` ❌ Jangan: ```text 10 → 7 → 12 → 5 → 14 (dalam waktu singkat) ``` Tujuannya menjaga **predictability** dan **user trust**. --- ## 32. Budget Status Recommended: | Status | Arti | | ----------- | ------------------------------------- | | `HEALTHY` | Budget aman | | `WARNING` | Burn rate mulai tinggi | | `CRITICAL` | Forecast berpotensi melewati budget | | `EXHAUSTED` | Budget sudah mencapai limit | Behavior setiap status harus **configurable**. --- ## 33. Automatic vs Approval Sistem sebaiknya mendukung dua mode: ### `RECOMMENDATION MODE` ```text System calculates recommendation ↓ Admin/Product/Finance approves ↓ Publish ``` ### `AUTOMATIC MODE` ```text System calculates ↓ Guardrails ↓ Auto publish ``` Automatic mode hanya boleh berjalan dengan: - min reward - max reward - min multiplier - max multiplier - maximum daily adjustment - budget safety threshold - audit log - rollback capability > **Default recommendation untuk production awal:** `RECOMMENDATION MODE` > > Setelah economy memiliki data historis yang cukup, automatic mode dapat diaktifkan untuk rule tertentu. --- ## 34. Budget Exhaustion Jika budget exhausted, system harus memiliki policy. Possible policy: 1. Stop rewards 2. Reduce rewards to minimum 3. Allow non-budget rewards only 4. Disable affected event 5. Continue game but no reward Policy harus **configurable**. Game tetap dapat dimainkan meskipun reward sementara tidak tersedia, kecuali Product menentukan game harus ikut disabled. --- ## 35. Limits Minimum limit concepts: | Limit | Definisi | | -------------------- | ----------------------------------------------------- | | **User Daily Limit** | Jumlah Coin maksimal yang dapat diperoleh user per hari | | **Game Daily Limit** | Total reward game per hari | | **Event Limit** | Reward maksimal dari event | | **Global Limit** | Global economy protection | | **Session Limit** | Satu session hanya dapat rewarded sekali | Semua limit harus dapat di-**configure**. Jika reward melewati limit, reward **dipotong ke sisa limit** (bukan dibatalkan seluruhnya). Jika sisa limit 0, reward menjadi 0. --- ## 36. Reporting & Analytics Dashboard minimal: ### Game - DAU - total plays - completed games - average score - total Coin issued - average reward - reward per play - total Coin spent for entry cost - total Coin refunded ### Economy - Coin generated - Coin spent - Coin expired - Coin outstanding - Point balance - Point redeemed ### Voucher - redemption count - voucher value - point spent - realized cost - stock - redemption rate ### Budget - total budget - realized cost - remaining budget - utilization - forecast - burn rate ### Event - participants - plays - Coin issued - redemption - event cost - event performance --- ## 37. Audit Log Admin changes harus dapat **diaudit**. Minimal audit: - who - what - before - after - timestamp - reason - source Contoh: ```text Who : Admin A Change : Reward 10 Coin → 8 Coin Reason : Budget optimization Timestamp : 2027-03-10 10:00 ``` Audit terutama **wajib** untuk: - reward configuration - event configuration - budget - voucher - Point/Coin adjustment - automatic controller changes --- ## 38. Security & Anti-Abuse Minimum protection: - server-side reward calculation - session validation - idempotency - rate limiting - duplicate detection - suspicious score detection - impossible score detection - concurrent request protection - atomic wallet update - atomic redemption - audit trail > **Penting:** Jangan mempercayai reward amount dari client. --- ## 39. Recommended System Architecture ```text ┌──────────────────┐ │ Next.js Web │ │ Game Portal │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Go API │ └────────┬─────────┘ │ ┌────────────────────┼────────────────────┐ │ │ │ ▼ ▼ ▼ Game Management Game Session User/Economy │ │ │ └────────────────────┼────────────────────┘ ▼ Result Validator │ ▼ Reward Engine │ ▼ Economy Guard │ ┌──────┴──────┐ ▼ ▼ Budget Controller Event │ │ └──────┬──────┘ ▼ Coin Wallet │ ▼ Coin Ledger │ ▼ Point Layer │ ▼ Voucher Management │ ▼ Redemption Engine │ ▼ Realized Cost │ ▼ Budget Controller ``` --- ## 40. Recommended Development Order Jangan langsung membuat semua modul sekaligus. ### Phase 1 — Business Rules Lock: - Coin - Point - Voucher - Budget - Redemption - Reward - Event - Limits - Expiration ### Phase 2 — Domain & Database Design: - `games` - `game_sessions` - `reward_configs` - `events` - `event_reward_configs` - `wallets` - `coin_transactions` - `budgets` - `budget_transactions` - `vouchers` - `voucher_inventory` - `redemptions` - `audit_logs` Exact schema harus ditentukan setelah business rules final. ### Phase 3 — Game Management Build: - Game CRUD - Game status - Game configuration - Game version ### Phase 4 — Game Session Build: - create session - entry cost debit (Coin) saat create session - entry cost refund - validate session - complete session - idempotency ### Phase 5 — Reward Engine Build: - fixed reward - score based - outcome based - probability - multiplier - tiered - reward configuration versioning ### Phase 6 — Coin Economy Build: - wallet - ledger - earning - expiration integration - adjustment - transaction history ### Phase 7 — Voucher Management Build: - voucher CRUD - voucher type - voucher value - point cost - stock - code pool - expiration - status ### Phase 8 — Redemption Build: - voucher reservation - Point deduction - voucher issue - rollback - idempotency - realized cost ### Phase 9 — Event Management Build: - event CRUD - participating games - reward modifiers - missions - event limits - event leaderboard ### Phase 10 — Budget Controller Build: - budget pool - realized cost tracking - burn rate - forecast - recommendation - automatic mode - guardrails ### Phase 11 — Analytics Build: - game analytics - economy analytics - voucher analytics - budget dashboard - event analytics ### Phase 12 — Phaser Integration Integrate real games with: - game session - result submission - reward result - leaderboard - missions --- ## 41. Important Implementation Rules for Claude Code Claude Code **must** follow these principles: 1. Do not invent business rules that are not defined. 2. Do not let Phaser/client determine reward amount. 3. Do not mutate historical reward configuration that has already been used in transactions. 4. Every reward completion must be idempotent. 5. Every wallet mutation must have a ledger record. 6. Voucher redemption must be atomic/recoverable. 7. Budget actual cost is based on successful voucher redemption, according to the current business rule. 8. All games use the same global/shared budget pool. 9. Normal reward and event reward can be active simultaneously. 10. Event configuration must not permanently mutate normal game reward configuration. 11. Automatic reward adjustment must have guardrails. 12. All important admin/economy changes must be auditable. 13. Use database transactions for wallet, redemption, and other financial/economic mutations. 14. Avoid premature microservices. Start with a modular Go backend unless scale requires otherwise. 15. Prefer clear domain boundaries inside the existing backend. 16. Entry cost is debited in Coin when the session is created, in the same transaction as the session. 17. Entry cost and its refund never offset the budget; budget only counts reward and realized voucher cost. 18. Point can only be redeemed for vouchers. It must never be usable as payment or cashed out. --- ## 42. Current Decisions These decisions are **confirmed for v1**: | Decision | Rule | | ----------------------- | -------------------------------- | | Backend | Go | | Database | PostgreSQL | | Game engine | Phaser | | Temporary session | Redis optional | | Coin | Virtual game currency | | Point | Redemption currency | | Coin : Point | 1 : 1 | | Coin : Rupiah | 1 Coin = Rp1 (nilai acuan, bukan cash out) | | Point usage | Voucher redemption only | | Point as payment | Not allowed | | Point cash out | Not allowed | | Game entry cost | Paid in Coin | | Entry cost debit | At session creation (Start Game) | | Entry cost refund | Automatic, idempotent; only for system error / game deactivated mid-session | | Abandoned/expired session | No refund | | Entry cost vs budget | Not counted; budget = reward only | | Coin expiration | Existing system | | Point expiration | Existing system | | Coin → Point | Existing exchange (manual, configurable rate) | | Budget model | Shared/global pool | | Budget period | Monthly (default), configurable | | Event = Campaign | Same concept | | Budget counts | Only Point originating from EnakGame rewards | | Budget actual cost | Successful voucher redemption | | Realized cost basis | Voucher face value | | Voucher source | Internal codes + external API | | Free game | Not allowed (entry cost ≥ 1 Coin) | | Event budget | Own budget per event, separate from global; funds the event's extra reward | | Over limit | Reward capped to remaining limit | | Game reward Coin transfer | Allowed | | Voucher redemption PIN | Required | | Legacy games (spin, ferris wheel) | Removed; spin rebuilt as EnakGame game | | Game budget | Shared pool, not isolated per game | | Normal + Event reward | Can run together | | Reward authority | Backend | | Reward idempotency | Required | | Wallet | Required | | Ledger | Required | | Voucher Management | Core module | | Redemption | Atomic/idempotent | | Budget Controller | Required | | Dynamic reward | Supported | | Auto adjustment | Supported with guardrails | | Default adjustment mode | Recommendation/approval | | Event | Layer over normal game reward | | Historical configs | Must be versioned | | Audit | Required | --- ## 43. Open Decisions Before Database Design The following should remain **explicitly unresolved** until Product/Finance decides them: 1. **Reward rounding** - integer Coin only - allow fractional internal calculation but round final reward 2. **Event stacking** - priority only - controlled stacking - maximum multiplier 3. **Budget exhaustion policy** (global dan event) 4. **Automatic Budget Controller thresholds** 5. **Voucher reservation timeout** Sudah diputuskan (lihat [Section 42](#42-current-decisions)): budget period, event/campaign budget, budget attribution, redemption cost model, Coin → Point conversion, Point expiration, voucher provider, free game, entry cost refund conditions. These decisions should be finalized before production schema/API is considered final. --- ## 44. Definition of Done for Economy v1 Economy v1 is considered technically ready when: - [ ] Game session cannot be rewarded twice. - [ ] Entry cost is debited exactly once per session, atomically with session creation. - [ ] Session cannot start when Coin balance is insufficient. - [ ] Entry cost refund is idempotent, and a session cannot be both refunded and rewarded. - [ ] Sessions failed by system error or game deactivation are refunded automatically; abandoned/expired sessions are not. - [ ] Entry cost does not affect budget calculation. - [ ] Point cannot be used as payment or cashed out. - [ ] Client cannot directly choose reward amount. - [ ] Reward configuration is versioned. - [ ] Reward Engine calculates reward correctly. - [ ] Economy Guard enforces limits. - [ ] Wallet balance is consistent with ledger. - [ ] Coin expiration is correctly represented. - [ ] Voucher stock cannot go negative. - [ ] Redemption is atomic. - [ ] Redemption is idempotent. - [ ] Successful redemption records realized cost. - [ ] Budget Controller can calculate current utilization. - [ ] Budget Controller can forecast future cost. - [ ] Reward adjustment has guardrails. - [ ] Event reward does not corrupt normal reward configuration. - [ ] Admin changes are auditable. - [ ] Finance can reconcile voucher cost against redemption records. --- ## 45. Guiding Principle | Component | Responsibility | | ---------------------- | ------------------------------------ | | **Product/Finance** | Defines the budget | | **Game Management** | Defines the game | | **Reward Engine** | Calculates the reward | | **Economy Guard** | Protects the economy | | **Budget Controller** | Protects the budget | | **Voucher Management** | Defines what users can redeem | | **Redemption** | Records the actual business cost | | **Ledger** | Provides the audit trail | The goal is not simply to build a collection of games.