docs: EnakGame game list, API list and in-game login

integration-enakgame.md was only the flow of a play. It now also has:

- §2 the games: Spin is the only one; what each reward_type needs from the
  client at complete; what to agree on to register a new game.
- §3 the endpoints the client calls, in one table.
- §5 tokens: with no token, or one the backend refuses, the game shows a
  customer login (POST /customer-auth/login) and repeats the request once.
  Standalone mode for a browser without the app finds its game_id by slug. The
  login errors (304, 429 with locked_until), the attempt limit and the phone
  formats accepted. A JS helper for all of it.

The bridge loses token_expired and token: the game logs the customer in
itself. Sections are renumbered.

integration-mobile-customer.md: customer phone numbers are 62… (§2), a login
section with its errors and the change from 900 to 304 (§2.4), 62… examples,
and init carrying the access token. integration-backoffice.md: example
responses are the data field, so a list is data.data in a raw response.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
efrilm
2026-10-09 22:59:58 +07:00
co-authored by Claude Opus 5.5
parent a01e651709
commit c43baa53e1
3 changed files with 384 additions and 61 deletions
+42 -8
View File
@@ -52,13 +52,19 @@ Tidak ada lagi "token". Semua yang dulu token sekarang EnakCoin.
- Semua jumlah di request dan response berupa integer.
- Belum ada endpoint refresh token: bila token ditolak (§2.2, `entity` `auth_handler`),
customer login ulang.
- **Nomor HP customer selalu disimpan dan dikembalikan dalam format `62…`** (mis.
`6281234561234`). Di request (registrasi, login, kirim ulang OTP, cek nomor, penerima
transfer) nomor boleh ditulis `0812…`, `+62 812…`, `62812…`, atau `812…`; backend
mengubahnya ke `62…`. Hanya nomor HP Indonesia (`628…`) yang diterima; selain itu
ditolak `304` `invalid phone number format`. Pengecualian: nomor penerima transfer
yang disamarkan tetap ditampilkan dalam format lokal, `08**-****-1234` (§7.2).
### 2.1 Registrasi customer
`POST /api/v1/customer-auth/register/start` menerima `organization_id` (opsional):
```json
{ "phone_number": "0812…", "name": "Budi", "birth_date": "2000-01-31", "organization_id": "648b96a0-1d1d-414e-baee-37e9d6317b4e" }
{ "phone_number": "6281234561234", "name": "Budi", "birth_date": "2000-01-31", "organization_id": "648b96a0-1d1d-414e-baee-37e9d6317b4e" }
```
- Customer terdaftar di satu organisasi, dan saldonya berlaku di semua outlet organisasi itu.
@@ -78,6 +84,10 @@ Sukses:
{ "success": true, "data": { … }, "errors": null }
```
Semua contoh response di dokumen ini adalah isi `data`. Untuk daftar berhalaman, isi
itu sendiri berbentuk `{ "data": [ … ], "pagination": { … } }`, jadi array-nya ada di
`data.data` pada response mentah.
Gagal:
```json
@@ -89,7 +99,7 @@ Gagal:
| `303`, `310` | 400 | Request tidak lengkap / salah format | Bug di app; tampilkan pesan umum |
| `304` | 400 | Ditolak aturan bisnis, **atau token tidak berlaku** bila `entity` = `auth_handler` | Pesan yang ramah per fitur; `cause` berbahasa Inggris, jangan tampilkan mentah. Token: login ulang |
| `404` | 404 | Tidak ditemukan, juga untuk data milik customer lain | Tampilkan "tidak ditemukan" |
| `429` | 429 | Minta OTP terlalu cepat | Hitung mundur sebelum boleh minta lagi |
| `429` | 429 | Minta OTP terlalu cepat, atau terlalu banyak percobaan login (§2.4) | Hitung mundur sebelum boleh minta lagi |
| `PIN_NOT_SET` | 403 | Belum punya PIN | Buka alur buat PIN (§6.2) |
| `PIN_INVALID` | 400 | PIN salah | §6.5 |
| `PIN_LOCKED` | 423 | PIN terkunci | §6.5 |
@@ -107,6 +117,25 @@ Endpoint **tukar**, **transfer**, dan **tukar voucher** wajib header `Idempotenc
dua kali.
- Jangan pakai ulang key untuk transaksi yang berbeda; server menolaknya (`304`).
### 2.4 Login — `POST /api/v1/customer-auth/login`
`{ "phone_number": "…", "password": "…" }` → token di `data.data.access_token`.
| `errors[0].code` | HTTP | Arti | Tampilan |
|---|---|---|---|
| `304`, `entity` `customer_auth_service` | 400 | Nomor HP tidak terdaftar, password salah, atau pendaftaran belum selesai | "Nomor HP atau password salah." |
| `304`, `entity` `request` | 400 | Format nomor HP salah, field kosong | "Periksa nomor HP dan password." |
| `429` | 429 | Terlalu banyak percobaan; `data.locked_until` (RFC3339 UTC) | "Terlalu banyak percobaan. Coba lagi pukul {jam}." |
| `900` | 500 | Error server | "Terjadi kesalahan, coba lagi" |
> **Berubah per 9 Okt 2026.** Sebelumnya nomor HP atau password salah dijawab `900`
> (HTTP 500) dengan `cause` `invalid password` / `customer not found`. Bila app
> menangani login gagal dari HTTP 500 atau teks `cause` itu, ganti ke tabel di atas.
Setiap nomor HP hanya boleh mencoba login 5 kali dalam 15 menit; login yang berhasil
memulai hitungan dari nol. Percobaan ke-6 ditolak `429` sampai 15 menit itu habis, juga
bila password-nya benar.
---
## 3. Layar yang perlu dibuat
@@ -495,7 +524,7 @@ Jumlah yang salah ditolak sebelum PIN dicek, jadi tidak memakan jatah percobaan
1. Pilih mata uang (EnakPoint / EnakCoin), isi nomor HP penerima dan jumlah.
2. Cek penerima:
`GET /api/v1/customer/wallet/transfer/recipient?phone=081234561234`
`GET /api/v1/customer/wallet/transfer/recipient?phone=6281234561234`
```json
{ "name": "Bu*** Sa***", "phone_number": "08**-****-1234" }
@@ -512,7 +541,7 @@ Jumlah yang salah ditolak sebelum PIN dicek, jadi tidak memakan jatah percobaan
`POST /api/v1/customer/wallet/transfer` + header `Idempotency-Key`
```json
{ "currency": "POINT", "amount": 120, "recipient_phone": "081234561234", "pin": "482913" }
{ "currency": "POINT", "amount": 120, "recipient_phone": "6281234561234", "pin": "482913" }
```
```json
@@ -597,7 +626,7 @@ log server game dan riwayat webview.
### 8.3 Bridge (sisi aplikasi)
> **Usulan.** Kontrak ini sama dengan [`integration-enakgame.md`](./integration-enakgame.md)
> §2 dan belum diimplementasikan. Sepakati dengan tim EnakGame sebelum mulai.
> §4 dan belum diimplementasikan. Sepakati dengan tim EnakGame sebelum mulai.
- Game → aplikasi: JavaScript channel webview bernama **`EnakGameHost`**; setiap pesan
berupa JSON string.
@@ -605,11 +634,14 @@ log server game dan riwayat webview.
| Pesan masuk dari game | Yang dilakukan aplikasi |
|---|---|
| `{ "type": "ready" }` | Kirim `{ "type": "init", "api_base_url": "<base URL>/api/v1", "token": "<token customer>", "game_id": "<id game yang dibuka>" }` |
| `{ "type": "token_expired" }` | Login ulang customer (tidak ada refresh token), lalu kirim `{ "type": "token", "token": "<token baru>" }` |
| `{ "type": "ready" }` | Kirim `{ "type": "init", "api_base_url": "<base URL>/api/v1", "token": "<access token customer>", "game_id": "<id game yang dibuka>" }`. Jawab setiap `ready`, termasuk setelah halaman game dimuat ulang. Access token, bukan refresh token (ditolak backend) |
| `{ "type": "balance_changed", "coin_balance": 15 }` | Perbarui saldo EnakCoin yang ditampilkan aplikasi |
| `{ "type": "close" }` | Tutup webview, muat ulang beranda |
Bila token kosong atau ditolak backend, game menampilkan layar login akun customer
sendiri; aplikasi tidak perlu menangani apa pun, dan token hasil login di game tidak
dikirim balik ke aplikasi.
Abaikan pesan dengan `type` lain. Tombol back Android jangan langsung menutup webview:
tampilkan konfirmasi "Keluar dari game? EnakCoin yang sudah dipakai untuk main tidak
kembali", lalu tutup. Tidak perlu mengirim pesan ke game.
@@ -731,7 +763,9 @@ minta PIN → kirim dengan header `Idempotency-Key`:
### 9.3 Voucher saya — `GET /customer/vouchers/redemptions?page=1&limit=20`
Daftar penukaran customer, terbaru di atas, dengan bentuk item sama seperti response
§9.2 (tanpa `point_balance` dan `replayed`), dibungkus `data` + `pagination`.
§9.2 (tanpa `point_balance` dan `replayed`). Seperti semua daftar berhalaman di dokumen
ini, array-nya ada di `data.data` dan pagination di `data.pagination` di dalam amplop
response (§2.2); daftar kosong berupa `[]`.
| `status` | Tampilan |
|---|---|