# Endpoint Matrix PyPOS vs ERP Target (UAT Contract)

Tanggal: 2026-04-15  
Workspace: `W:\POS_DUMMY`  
Scope: endpoint yang dipakai PyPOS untuk integrasi ke `existing_production_project_for_pypos`.

## 1) Ringkasan

- Mapping startpoint PyPOS ke endpoint ERP target sudah cocok secara kode (static check).
- Endpoint tambahan PyPOS yang sudah tersedia di target: JWT issue/refresh, branch lookup publik, UI assets, preorder get/use, check free produk alias, server check update alias.
- Endpoint legacy WEB_POS tetap dipertahankan (tidak diganti), termasuk `setUploadStream()`.

Catatan penting:
- Ini validasi kontrak berbasis code review, belum eksekusi end-to-end jaringan/database di dokumen ini.
- UAT checklist pada bagian 3 dipakai untuk verifikasi runtime.

## 2) Matriks Startpoint vs Endpoint (Dikelompokkan per File Endpoint)

Catatan urutan:
- Di setiap file endpoint, jalur shared/legacy diletakkan lebih dulu.
- Endpoint yang dipakai khusus PyPOS diletakkan di bagian paling bawah pada kelompok file tersebut.

### A. `application/controllers/eusvc/NonRest.php`

| No | Startpoint PyPOS (config key) | Caller PyPOS | HTTP | Endpoint ERP Target | Handler ERP Target | Auth | Kontrak Request Minimal | Kontrak Response Minimal |
|---|---|---|---|---|---|---|---|---|
| A1 | `ep_upload_stream` | `ExportUploadApiService.upload_file` | POST multipart | `/eusvc/NonRest/setUploadStream` | `NonRest::setUploadStream` | Legacy path (tanpa JWT guard) | multipart `file_contents` + metadata (`id_machine`,`db_table`,...) | `status=1`, `file_name` |
| A2 | `ep_device` | `device_utils.post_device_registration` | POST JSON | `/eusvc/NonRest/postDeviceRegistrasi` | `NonRest::postDeviceRegistrasi` | No JWT | JSON berisi `machine_id`, `cabang_id`, plus konteks device | JSON dengan `status` truthy untuk sukses |
| A3 | `ep_device_cek` | `device_utils.cek_device_ke_server` | GET | `/eusvc/NonRest/checkDevRegisV2` | `NonRest::checkDevRegisV2` | No JWT | query `machine_id` | `status=200` (approved), `202` (pending), `404` (belum terdaftar) |
| A4 | `ep_jwt_issue` | `JwtAuthService.issue_token` | POST | `/eusvc/NonRest/issueJwt` | `NonRest::issueJwt` | No JWT (init token) | `username`, `password`, `machine_id` | `status=1`, `data.access_token`, `data.refresh_token`, `data.token_type`, `data.expires_in` |
| A5 | `ep_jwt_refresh` | `JwtAuthService.refresh_token` | POST | `/eusvc/NonRest/refreshJwt` | `NonRest::refreshJwt` | Refresh token | `refresh_token` | `status=1`, bundle token baru pada `data` |
| A6 | `ep_update_cek` | `SyncApiService.check_update_server` | POST form | `/eusvc/NonRest/server_CheckUpdate` | `NonRest::server_CheckUpdate` | JWT required | `machine_id`, `cabang_id`, `date_last[table][dtime]`, `date_last[table][lastID]` | JSON object valid; minimal `connection`, `row`, `data` |
| A7 | `ep_upload_compile_status` | `ExportUploadApiService.fetch_compile_status` | GET | `/eusvc/NonRest/getUploadCompileStatus` | `NonRest::getUploadCompileStatus` | JWT required | query `idempotency_key`, optional `machine_id` | `status=1`, blok `idempotency` + `legacy_log` |
| A8 | `ep_preorder_get` | `PreorderApiService.get_preorder_list` | POST form | `/eusvc/NonRest/get_preorder` | `NonRest::get_preorder` | JWT required | `kasir_id`,`kasir_nama`,`dev_alias`,`cabang_id` | envelope `status`,`reason`,`message`,`data`; sukses jika `status=1` |
| A9 | `ep_preorder_use` | `PreorderApiService.use_preorder` | POST form | `/eusvc/NonRest/use_preorder` | `NonRest::use_preorder` | JWT required | `preorder_id`,`otp`,`kasir_id`,`kasir_nama`,`dev_alias`,`cabang_id` | envelope `status`,`reason`,`message`,`data`; sukses jika `status=1` |

### B. `application/controllers/eusvc/DataSync.php`

| No | Startpoint PyPOS (config key) | Caller PyPOS | HTTP | Endpoint ERP Target | Handler ERP Target | Auth | Kontrak Request Minimal | Kontrak Response Minimal |
|---|---|---|---|---|---|---|---|---|
| B1 | `ep_datas` | `cabang_utils` fallback + legacy sync | POST form | `/eusvc/DataSync/doSync_auto` | `DataSync::doSync_auto_post` -> `doSync_autoV2_post` | Config auth DataSync | `date_last[...]`, `machine_id`, `cabang_id` | JSON object valid; minimal `connection`, `data` |
| B2 | `ep_server_sync` | `SyncApiService.sync_data_server` | POST form | `/eusvc/DataSync/serverSync` | `DataSync::serverSync_post` -> `doSync_autoV2_post` | Config auth DataSync (opsional), token JWT boleh dikirim | `machine_id`, `cabang_id`, `date_last[...]`, `auth_required` | JSON object valid; minimal `connection`, `data` |
| B3 | `ep_branch_lookup` | `cabang_utils.get_branch_rows` | POST form | `/eusvc/DataSync/publicBranchLookup` | `DataSync::publicBranchLookup_post` | No JWT | `machine_id`, `toko_id` | `status=1`, `data.per_cabang[]` (juga mirror `per_cabang[]`) |
| B4 | `ep_ui_assets` | `DashboardAssetService.fetch_ui_assets_payload` | GET | `/eusvc/DataSync/uiAssets` | `DataSync::uiAssets_get` | No JWT | query `machine_id`, `cabang_id` | JSON object berisi `logo_header_url` |

### C. `application/controllers/eusvc/ProDiskon.php`

| No | Startpoint PyPOS (config key) | Caller PyPOS | HTTP | Endpoint ERP Target | Handler ERP Target | Auth | Kontrak Request Minimal | Kontrak Response Minimal |
|---|---|---|---|---|---|---|---|---|
| C1 | `ep_diskon_check_free_produk` | `TransaksiService.cek_kuota_free_produk` | POST form | `/eusvc/ProDiskon/checkFreeProdukQuota` | `ProDiskon::checkFreeProdukQuota_post` -> `getQuotaFreeProduk_post` | No JWT | field diskon/free-produk + `toko_id` | `status=1` untuk lolos kontrak PyPOS |
| C2 | `ep_diskon_save_free_produk` | `TransaksiService.update_quota_free_produk` | POST multipart | `/eusvc/ProDiskon/saveFreeProduk` | `ProDiskon::saveFreeProduk_post` | No JWT | multipart `arr[0][...]`, `arr[1][...]` | `status=1` untuk sukses simpan |

### D. `application/controllers/penjualan/ActivityReportApi.php`

| No | Startpoint PyPOS (config key) | Caller PyPOS | HTTP | Endpoint ERP Target | Handler ERP Target | Auth | Kontrak Request Minimal | Kontrak Response Minimal |
|---|---|---|---|---|---|---|---|---|
| D1 | `ep_settlement_direct` (opsional) | `SettlementDirectService.send_settlement` | POST JSON | `/penjualan/ActivityReportApi/settlement` | `ActivityReportApi::settlement` | Sesuai konfigurasi endpoint | JSON payload settlement + `X-Idempotency-Key` | sukses jika status/reason memenuhi classifier service |

## 3) Checklist UAT Startpoint vs Endpoint

Gunakan checklist ini per pasangan startpoint-endpoint.

### A. Prasyarat

- [ ] Backup DB ERP target dan DB WEB_POS.
- [ ] `api_base_url` PyPOS menunjuk environment UAT target.
- [ ] Endpoint config PyPOS sesuai matriks di atas.
- [ ] Akun uji kasir aktif tersedia.
- [ ] Device uji punya `machine_id` tetap.
- [ ] Legacy WEB_POS smoke test (10 menit) sudah PASS.

### B. Checklist Kontrak Per Endpoint

#### JWT
- [ ] `ep_jwt_issue`: request valid menghasilkan HTTP 200 dan `status=1`, token bundle lengkap.
- [ ] `ep_jwt_issue`: kredensial salah menghasilkan `status=0` dengan reason `invalid_credentials`.
- [ ] `ep_jwt_refresh`: refresh valid menghasilkan token baru (`status=1`).
- [ ] `ep_jwt_refresh`: refresh invalid menghasilkan `status=0` reason `invalid_refresh_token`.

#### Device Registration
- [ ] `ep_device`: submit registrasi sukses memberi `status` truthy.
- [ ] `ep_device_cek`: device approved memberi `status=200`.
- [ ] `ep_device_cek`: device pending memberi `status=202`.
- [ ] `ep_device_cek`: device belum terdaftar memberi `status=404`.

#### Sync Master
- [ ] `ep_update_cek`: dengan JWT valid, response JSON object (bukan HTML/error page).
- [ ] `ep_update_cek`: tanpa JWT/invalid JWT ditolak sesuai guard auth.
- [ ] `ep_server_sync`: request `date_last[...]` valid mengembalikan `connection=1` dan block `data`.
- [ ] `ep_datas`: fallback sync lama tetap mengembalikan kontrak JSON kompatibel.

#### Branch + UI Assets
- [ ] `ep_branch_lookup`: `toko_id` valid memberi `status=1` dan `data.per_cabang` array.
- [ ] `ep_branch_lookup`: `toko_id` invalid memberi `status=0` reason `invalid_toko_id`.
- [ ] `ep_ui_assets`: GET memberi URL logo valid (`logo_header_url` non-empty).

#### Upload Stream
- [ ] `ep_upload_stream`: upload file valid memberi `status=1` dan `file_name`.
- [ ] `ep_upload_stream`: upload file tidak valid memberi `status=0` dengan `reason`.
- [ ] `ep_upload_compile_status`: dengan JWT valid memberi `status=1` dan block `idempotency`.
- [ ] `ep_upload_compile_status`: filter `idempotency_key` mengisi `idempotency.per_key`.

#### Diskon Free Produk
- [ ] `ep_diskon_check_free_produk`: payload valid memberi `status=1`.
- [ ] `ep_diskon_check_free_produk`: payload invalid tetap memberi envelope JSON (bukan 500 HTML).
- [ ] `ep_diskon_save_free_produk`: multipart valid memberi `status=1`.
- [ ] `ep_diskon_save_free_produk`: row invalid ditangani tanpa memutus transaksi utama.

#### Preorder
- [ ] `ep_preorder_get`: JWT valid + identitas kasir valid memberi `status=1`, `data` list.
- [ ] `ep_preorder_get`: cabang invalid memberi `status=0`, reason `missing_cabang_id`/validasi setara.
- [ ] `ep_preorder_use`: `preorder_id+otp` valid memberi `status=1` dan payload transaksi preload.
- [ ] `ep_preorder_use`: OTP salah/data tidak ada memberi `status=0` dengan reason terkontrol (`preorder_not_found`/setara).

#### Settlement Direct (jika dipakai)
- [ ] `ep_settlement_direct`: payload valid memberi hasil classifier sukses di sisi PyPOS.
- [ ] `ep_settlement_direct`: skenario duplicate/idempotency conflict ditangani sesuai contract (bukan crash).

### C. Checklist Backward Compatibility WEB_POS

- [ ] Login kasir WEB_POS lama tetap normal.
- [ ] Sinkron master WEB_POS lama tetap normal.
- [ ] Upload transaksi WEB_POS lama lewat `setUploadStream()` tetap normal.
- [ ] Tidak ada error baru di log PHP/Apache setelah trafik campuran WEB_POS + PyPOS.

## 4) Endpoint yang Memang Belum Scope Saat Ini

- `ep_seed_per_employee_bootstrap`: kosong pada config.
- `ep_ppn_settings`: kosong pada config.

Keduanya tidak menghalangi checklist endpoint inti PyPOS di atas.

## 5) Evidence UAT (Isi saat eksekusi)

| Endpoint | Waktu Uji | Tester | Hasil | Catatan/Error Code |
|---|---|---|---|---|
| `ep_jwt_issue` |  |  | PASS/FAIL |  |
| `ep_jwt_refresh` |  |  | PASS/FAIL |  |
| `ep_device` |  |  | PASS/FAIL |  |
| `ep_device_cek` |  |  | PASS/FAIL |  |
| `ep_update_cek` |  |  | PASS/FAIL |  |
| `ep_server_sync` |  |  | PASS/FAIL |  |
| `ep_datas` |  |  | PASS/FAIL |  |
| `ep_branch_lookup` |  |  | PASS/FAIL |  |
| `ep_ui_assets` |  |  | PASS/FAIL |  |
| `ep_upload_stream` |  |  | PASS/FAIL |  |
| `ep_upload_compile_status` |  |  | PASS/FAIL |  |
| `ep_diskon_check_free_produk` |  |  | PASS/FAIL |  |
| `ep_diskon_save_free_produk` |  |  | PASS/FAIL |  |
| `ep_preorder_get` |  |  | PASS/FAIL |  |
| `ep_preorder_use` |  |  | PASS/FAIL |  |
| `ep_settlement_direct` |  |  | PASS/FAIL |  |
