Files
apskel-pos-backend/docs/enakgame-prd.md
T
efrilmandClaude Opus 5.5 3ebc09f818 docs(enakgame): PRD, RFC, and task breakdown
EnakGame: pay EnakCoin to play, earn EnakCoin from the result, exchange
into EnakPoint, and redeem EnakPoint for vouchers only.

- enakgame-prd.md: economy and business rules, including entry cost and
  automatic refund, monthly global budget with a separate budget per
  event (event = campaign), and EnakPoint being voucher-only.
- rfc-enakgame.md: built on the existing wallet, ledger and lots. Game
  sessions with a state machine, versioned reward configs, Economy Guard
  counters, vouchers with internal codes and external providers, and
  realized cost attributed to budgets by tracing the lots spent.
- tasks-enakgame.md: EG-001 to EG-1003 in eleven phases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:48:17 +07:00

1623 lines
39 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.