Files
apskel-pos-backend/docs/enakgame-spin.md
T
efrilmandClaude Opus 5.5 18e87398bb feat(enakgame): spin as an EnakGame game; remove the old game flow
EnakGame phase 10 of docs/tasks-enakgame.md (EG-1001 to EG-1003).

Spin (EG-1001)
- PROBABILITY entries take an optional label (a wheel segment). The customer game
  list shows a PROBABILITY game's prizes (entry, label, amount, never weights), and
  completing returns the drawn prize, so the client can draw the wheel and stop it
  on the server's draw.
- docs/enakgame-spin.md: the admin steps to set up spin per organization (no
  seeder) and the customer app flow. An HTTP test plays it end to end.

Old game flow removed (EG-1002)
- Routes POST /customer/spin, GET /customer/games, GET /customer/ferris-wheel, and
  admin /marketing/games, /marketing/game-prizes, /marketing/rewards, with their
  handlers, services, processors, repositories, validators, models, contracts,
  mappers and tests (GamePlayProcessor, SpinGameService, rewards, ...). This also
  closes RFC §15 findings 1 and 2 (double charge, spinning another org's game).
- Tables games, game_prizes, game_plays and rewards stay for ledger history.
  entities.StringSlice moves to its own file; the omset tracker (unrouted) keeps
  game_id but no longer embeds the old game response.

games.is_active dropped (EG-1003)
- Migration 000115; nothing reads metadata.coin_cost any more.

The EnakPoint integration docs now point at /customer/enakgame. The Postgres tests
were not run: no test database here. Migration 000115 has not been run anywhere.
The customer app must stop calling the removed endpoints before this is deployed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:31:56 +07:00

87 lines
3.0 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.
# Spin sebagai Game EnakGame
**Sumber:** [RFC EnakGame](rfc-enakgame.md) §14, task EG-1001
**Pembaca:** admin organisasi dan tim backoffice / aplikasi customer
Spin lama (`POST /customer/spin`) diganti dengan game EnakGame biasa: tipe `SPIN`,
reward `PROBABILITY`, hadiah berupa EnakCoin. Spin dimainkan lewat endpoint session
yang sama dengan game lain, sehingga ikut mendapat idempotency, refund otomatis, budget,
event, dan Economy Guard.
Tidak ada seeder: setiap organisasi membuat spin-nya sendiri lewat API admin di bawah.
Semua langkah memakai token admin organisasi; langkah 2–4 butuh loyalty manager.
## Langkah Admin
### 1. Buat game
`POST /api/v1/marketing/enakgame/games`
```json
{
"name": "Spin Harian",
"slug": "spin",
"type": "SPIN",
"entry_cost": 5,
"status": "ACTIVE",
"thumbnail_url": "https://…/spin.png",
"game_url": "https://…/spin/index.html"
}
```
`entry_cost` adalah EnakCoin yang dipotong setiap kali spin (minimal 1).
### 2. Buat reward config `PROBABILITY`
`POST /api/v1/marketing/enakgame/games/:id/reward-configs`
```json
{
"reward_type": "PROBABILITY",
"max_reward": 50,
"rules": {
"table": [
{ "weight": 50, "amount": 0, "label": "Zonk" },
{ "weight": 30, "amount": 3, "label": "3 Coin" },
{ "weight": 15, "amount": 10, "label": "10 Coin" },
{ "weight": 5, "amount": 50, "label": "Jackpot" }
]
},
"reason": "Spin pertama"
}
```
- Satu baris `table` = satu segmen roda, urut searah gambar roda.
- `weight` bilangan bulat ≥ 1. Peluang segmen = `weight` ÷ total weight (di atas: 50%,
30%, 15%, 5%). Weight tidak pernah dikirim ke customer.
- `amount` EnakCoin yang didapat (boleh 0). `label` opsional, maksimal 100 karakter,
ditampilkan di roda.
- `max_reward` minimal sebesar `amount` terbesar, kalau tidak hadiah besar terpotong.
### 3. Aktifkan config
`POST /api/v1/marketing/enakgame/reward-configs/:id/activate`
Mengganti hadiah nanti berarti membuat versi config baru lalu mengaktifkannya; session
yang sedang berjalan tetap memakai versi saat dimulai.
### 4. Pastikan ada budget global bulan berjalan
`POST /api/v1/marketing/enakgame/budgets` dengan `scope: "GLOBAL"`, bila belum ada. Tanpa
budget global, customer tidak bisa memulai game apa pun.
## Alur di Aplikasi Customer
1. `GET /api/v1/customer/enakgame/games`: game spin punya `prizes`, yaitu segmen roda
(`entry`, `label`, `amount`) dalam urutan config. Gambar roda dari sini.
2. `POST /api/v1/customer/enakgame/sessions` dengan `{"game_id": "…"}` dan header
`Idempotency-Key`. Entry cost dipotong di sini.
3. `POST /api/v1/customer/enakgame/sessions/:id/complete` dengan body `{}`. Server yang
mengundi. Response berisi `prize` (`entry`, `label`, `amount`): putar roda sampai
berhenti di segmen `entry` itu. `reward_total` adalah Coin yang benar-benar masuk,
bisa lebih besar dari `prize.amount` karena event, atau lebih kecil karena limit
harian (`limited_by`).
Aplikasi tidak boleh mengundi sendiri atau mengirim hadiah: apa pun yang dikirim selain
data hasil diabaikan.