143 lines
5.9 KiB
Markdown
143 lines
5.9 KiB
Markdown
# Integrasi POS: EnakPoint, EnakCoin & Voucher
|
||||
|
|
|
|||
|
|
**Untuk:** tim aplikasi POS (kasir) · **Base URL:** `/api/v1` · **Per:** 8 Okt 2026
|
|||
|
|
|
|||
|
|
Kamu mengerjakan aplikasi **POS** yang dipakai kasir di outlet. Dokumen ini menjelaskan
|
|||
|
|
bagian program loyalitas yang menyentuh POS: mengaitkan customer ke order, menampilkan
|
|||
|
|
EnakPoint dan EnakCoin yang didapat, void/refund, dan voucher. Jangan mengarang
|
|||
|
|
endpoint, field, atau aturan yang tidak tertulis di sini; kalau ada yang kurang jelas,
|
|||
|
|
tanyakan ke tim backend.
|
|||
|
|
|
|||
|
|
Dokumen ini menggantikan bagian POS di `integration-enakpoint.md` dan `api-enakpoint.md`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Yang perlu diketahui kasir
|
|||
|
|
|
|||
|
|
| | EnakPoint (`POINT`) | EnakCoin (`COIN`) |
|
|||
|
|
|---|---|---|
|
|||
|
|
| Didapat dari | Belanja (order lunas), hasil tukar EnakCoin, koreksi admin | Belanja, hadiah game, koreksi admin |
|
|||
|
|
| Dipakai untuk | **Ditukar ke voucher** di aplikasi customer | Main game, ditukar ke EnakPoint |
|
|||
|
|
| Bisa membayar order | **Tidak** | **Tidak** |
|
|||
|
|
|
|||
|
|
- **EnakPoint bukan alat bayar.** Tidak ada payment method EnakPoint di POS, dan saldo
|
|||
|
|
tidak bisa dicairkan. Customer menukar EnakPoint ke voucher di aplikasinya sendiri.
|
|||
|
|
- Saldo berlaku di **semua outlet** organisasi. Berapa yang didapat per order diatur
|
|||
|
|
**per outlet** oleh owner di backoffice.
|
|||
|
|
- Semua jumlah bilangan bulat.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Mengaitkan customer ke order
|
|||
|
|
|
|||
|
|
Earning hanya terjadi bila order dikaitkan ke customer terdaftar. Order tanpa customer,
|
|||
|
|
dengan **customer default (walk-in)**, atau dengan customer nonaktif tidak mendapat
|
|||
|
|
apa-apa.
|
|||
|
|
|
|||
|
|
1. **Cari customer:** `GET /api/v1/customers?search=0812…&page=1&limit=20`
|
|||
|
|
(cocok dengan nama, email, atau nomor HP). Abaikan customer dengan `is_default: true`.
|
|||
|
|
2. **Kaitkan** dengan salah satu cara:
|
|||
|
|
- saat membuat order: `POST /api/v1/orders` dengan `"customer_id": "…"`, atau
|
|||
|
|
- setelah order dibuat: `PUT /api/v1/orders/:id/customer` dengan
|
|||
|
|
`{ "customer_id": "…" }`.
|
|||
|
|
|
|||
|
|
**Kaitkan sebelum order lunas.** Earning dihitung saat order menjadi lunas penuh.
|
|||
|
|
Customer yang dikaitkan setelah lunas tetap mendapat earning lewat job susulan yang
|
|||
|
|
berjalan tiap 30 menit untuk order lunas 72 jam terakhir, tapi tidak langsung, sehingga
|
|||
|
|
struk akan menulis 0.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Earning: yang didapat dari order
|
|||
|
|
|
|||
|
|
Earning berjalan otomatis di backend saat order lunas lewat jalur pembayaran mana pun
|
|||
|
|
(`POST /payments`, update order, split bill). POS tidak memanggil apa-apa.
|
|||
|
|
|
|||
|
|
- **Basis** = `subtotal − discount_amount`, **sebelum pajak** dan biaya lain.
|
|||
|
|
- Rumus per outlet (diatur owner): mode `PER_AMOUNT`
|
|||
|
|
`floor(basis ÷ earn_per_amount) × earn_value`, atau mode `PERCENTAGE`
|
|||
|
|
`floor(basis × earn_percent ÷ 100)`, dengan minimal belanja dan batas per order.
|
|||
|
|
- Contoh: basis Rp 87.500, outlet memberi 1 EnakPoint per Rp 100 dan 1 EnakCoin per
|
|||
|
|
Rp 25.000 → **875 EnakPoint** dan **3 EnakCoin**.
|
|||
|
|
|
|||
|
|
Response order (`GET /api/v1/orders/:id` dan response order lainnya) membawa:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{ "points_earned": 875, "coins_earned": 3 }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Keduanya 0 bila order tidak mendapat apa-apa. **Cetak di struk**, mis. "Kamu mendapat
|
|||
|
|
875 EnakPoint & 3 EnakCoin". Ambil nilainya setelah pembayaran terakhir berhasil; bila
|
|||
|
|
masih 0 padahal customer sudah dikaitkan, earning akan menyusul (§2).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Void dan refund
|
|||
|
|
|
|||
|
|
Tidak ada langkah tambahan di POS. Saat order di-void atau direfund, backend menarik
|
|||
|
|
kembali yang didapat dari order itu (mutasi `EARN_REVERSAL` di riwayat customer):
|
|||
|
|
|
|||
|
|
| Kejadian | Yang ditarik |
|
|||
|
|
|---|---|
|
|||
|
|
| Void | Semua EnakPoint dan EnakCoin dari order itu |
|
|||
|
|
| Refund (sebagian atau penuh) | `floor(earned × total_refund ÷ basis)`, tidak pernah lebih dari yang didapat; refund berikutnya hanya menarik sisanya |
|
|||
|
|
|
|||
|
|
Bila saldo customer sudah terpakai, yang ditarik sebanyak yang ada. **Refund tidak
|
|||
|
|
pernah diblokir** karena ini.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Voucher dari EnakPoint
|
|||
|
|
|
|||
|
|
Customer menukar EnakPoint ke voucher di aplikasi customer. Voucher yang didapat tampil
|
|||
|
|
di menu "Voucher saya" di aplikasi itu, dengan nama, nilai (`face_value`), jenis, dan
|
|||
|
|
bila ada, **kode** serta tanggal berlakunya.
|
|||
|
|
|
|||
|
|
> **Belum tersedia:** POS belum punya endpoint untuk **mengecek** atau **menandai
|
|||
|
|
> voucher sudah dipakai**. Ini pekerjaan lanjutan di backend.
|
|||
|
|
|
|||
|
|
Sampai endpoint itu ada:
|
|||
|
|
|
|||
|
|
1. Kasir melihat voucher di layar aplikasi customer (nama, nilai, kode, masa berlaku).
|
|||
|
|
2. Kasir memasukkan potongannya sebagai **diskon biasa** di order, sesuai jenisnya:
|
|||
|
|
|
|||
|
|
| `voucher_type` | Cara memasukkan |
|
|||
|
|
|---|---|
|
|||
|
|
| `FIXED_VALUE` | Diskon nominal sebesar `face_value` |
|
|||
|
|
| `PERCENTAGE` | Diskon persen sesuai syarat voucher |
|
|||
|
|
| `FREE_ITEM` | Item gratis sesuai syarat voucher |
|
|||
|
|
| `MERCHANT_BENEFIT` | Sesuai syarat voucher |
|
|||
|
|
|
|||
|
|
3. Karena backend belum mencatat voucher terpakai, outlet perlu mencatat kode yang
|
|||
|
|
sudah dipakai secara manual supaya voucher yang sama tidak dipakai dua kali.
|
|||
|
|
|
|||
|
|
Diskon dari voucher mengurangi basis earning seperti diskon lain (§3).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Yang sudah dihapus
|
|||
|
|
|
|||
|
|
Bayar dengan EnakPoint dihapus pada 7 Okt 2026. Jangan dipanggil atau ditampilkan lagi;
|
|||
|
|
tidak ada penggantinya.
|
|||
|
|
|
|||
|
|
| Dihapus | Catatan |
|
|||
|
|
|---|---|
|
|||
|
|
| Payment method tipe `point` ("EnakPoint") | Tidak ada di daftar payment method |
|
|||
|
|
| Field `points` dan `payment_code` di `POST /payments` | `amount` wajib seperti pembayaran lain |
|
|||
|
|
| `GET /orders/:id/point-payment/preview` | – |
|
|||
|
|
| Kode bayar dari aplikasi customer | – |
|
|||
|
|
| `points_used`, `point_value` di response pembayaran | – |
|
|||
|
|
| `point_amount`, `points_used`, `total_with_points`, `counts_as_cash_in` di laporan payment method | `summary.total_amount` adalah total semua method |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Checklist
|
|||
|
|
|
|||
|
|
- [ ] Kasir bisa mencari dan mengaitkan customer ke order sebelum pembayaran.
|
|||
|
|
- [ ] Customer default (walk-in) tidak ditawarkan sebagai pemilik earning.
|
|||
|
|
- [ ] Struk mencetak `points_earned` dan `coins_earned`.
|
|||
|
|
- [ ] Tidak ada payment method EnakPoint dan tidak ada field pembayaran EnakPoint di
|
|||
|
|
request.
|
|||
|
|
- [ ] Void/refund tidak menampilkan langkah tambahan untuk EnakPoint/EnakCoin.
|
|||
|
|
- [ ] SOP outlet untuk voucher manual (§5) sudah disepakati sampai endpoint POS tersedia.
|