[REFACTOR-TAGIHAN-CONTROLLER-DEDUPLICATION] Refactoring Arsitektur Controller & Deduplikasi Alur Orkestrasi Single Billing (V2 & V3)
Modul: Single Billing | Tipe: Refactoring | Stage Saat Ini:
PRODUCTION| Tanggal Dokumen: 2026-09-07
PRODUCTION RELEASED
Dokumen ini telah berada dalam tahap PRODUCTION. Perubahan logika backend, migrasi skema/konfigurasi, dan rilis v1.9.2 telah diterapkan secara penuh di lingkungan Production via MR !232.
LIFECYCLE GUIDE — UPDATE BERTAHAP:
- Buat file ini saat selesai development. Isi semua section kecuali Staging & Production.
- Tanggal Dokumen (Header) & Riwayat Update Log: Gunakan tanggal hari ini (pembuatan dokumen).
- Baris Development (Lifecycle): Gunakan tanggal & jam commit terakhir terkait di branch development.
- Update baris Staging saat naik ke UAT. Ubah "Stage Saat Ini" di header menjadi STAGING.
- Update baris Production saat naik ke Prod. Ubah "Stage Saat Ini" di header menjadi PRODUCTION.
- Update entry di README.md setiap kali status berubah. -->
1. Informasi Tugas & Referensi Bisnis
Task ID / Nomor Referensi:
REFACTOR-TAGIHAN-CONTROLLER-DEDUPLICATIONSumber Instruksi: Refactoring Arsitektur Single Billing & Deduplikasi Kode Controller
Dokumen Referensi / Lampiran: Single Billing V3 - Multi Satker.postman_collection.json
Task / Dependensi Terkait (Cross-Service): FEAT-SIMPONI-V3-ISOLATION
Modul Terkait:
Single BillingService / Repository:
single-billing-BEPIC Pengembang: Hermansyah Efendi
Latar Belakang & Ringkasan Kebutuhan: Pasca penambahan integrasi Simponi V3 (FEAT-SIMPONI-V3-ISOLATION), repositori
single-billing-BEmengalami lonjakan metrik code duplication pada SonarQube Quality Gate hingga melanggar batas ambang batas toleransi (terdeteksi lebih dari 3.300 baris kode redundan). Hal ini terjadi karena alur logika bisnis penerimaan tagihan, validasi integritas payload Joi, pengelompokan satker (PNBP, BLU, BUP), kalkulasi nominal, pemanggilan gateway eksternal (Inaportnet, Sinkarkes), serta pencatatan audit trail disalin ulang antar-versi V2 dan V3 padatagihan_controller.jsdanbilling_controller.js. Kondisi ini memperbesar risiko divergent bug fixes, menurunkan maintainability score, dan memicu kegagalan pipeline SonarQube. Refactoring ini mengekstraksi seluruh alur orkestrasi ke dalam helper terisolasi (tagihan_helper.jsdanbilling_helper.js), mengisolasi controller V3 ke folder dedicatedapp/controllers/v3/, mereduksi controller menjadi lightweight wrapper, dan menyatukan skema validasi Joi tanpa mengubah kontrak API eksisting (zero-regression).Ruang Lingkup & Rincian Sub-Pekerjaan (Technical Scope & Milestones):
- [x] Ekstraksi orkestrasi rekap tagihan ke
processRekapTagihanOrchestratorpadaapp/helpers/tagihan_helper.js(-2.772 baris padatagihan_controller.js) - [x] Ekstraksi orkestrasi request ulang billing ke
processRequestBillingOrchestratorpadaapp/helpers/billing_helper.js(-532 baris padabilling_controller.js) - [x] Isolasi controller V3 ke direktori modular
app/controllers/v3/(tagihan_controller.jsdanbilling_controller.js) - [x] Unifikasi skema validasi Joi pada
app/models/tagihan_models.js(schemaHeaderItems,schemaDetailItemsCommon) guna mengeliminasi definisi aturan SQL injection guard yang berulang - [x] Eliminasi sub-fungsi duplikat (koleksi unique satker, pengelompokan data, penyiapan payload database, logging cetakan) ke helper murni
- [x] Verifikasi regresi fungsional untuk memastikan integrasi V2 dan V3 tetap berjalan normal tanpa perubahan endpoint contract
- [x] Ekstraksi orkestrasi rekap tagihan ke
2. Deployment Lifecycle
| # | Environment | Tanggal & Waktu | PIC | Status | Catatan |
|---|---|---|---|---|---|
| 1 | Development | 2026-06-25 15:40 | Hermansyah Efendi | DEPLOYED | Branch: development (Merged from refactor/tagihan-controller-duplication) |
| 2 | Staging (UAT) | 2026-10-14 13:00 | Hermansyah Efendi / QA | DEPLOYED | Release Candidate via MR Staging !229. Co-deployed: [FEAT-SIMPONI-V3-ISOLATION, TASK-SET-EXPIRED-BILLING-V3, FEAT-BILLING-REKAP-EXPIRED-DATE-PAYLOAD, REFACTOR-TAGIHAN-CONTROLLER-DEDUPLICATION, FIX-PAYMENT-WEBHOOK-RACE-CONDITION, FIX-TAGIHAN-PREVENT-PAID-INVOICE-REORDER, FEAT-REQUEST-LOGGER-POSTMAN, TEST-DRAGONFLY-VITEST-SUITE, FEAT-RUNTIME-ENV-VALIDATION, TASK-NON-BLOCKING-BILLING-DISPATCH-AND-RETRY-CONFIG] |
| 3 | Production | 2026-10-14 20:06 | achmad fauzi | DEPLOYED | Branch: prod via MR !232 (Release v1.9.2). Co-deployed: [FEAT-SIMPONI-V3-ISOLATION, TASK-SET-EXPIRED-BILLING-V3, FEAT-BILLING-REKAP-EXPIRED-DATE-PAYLOAD, REFACTOR-TAGIHAN-CONTROLLER-DEDUPLICATION, FIX-PAYMENT-WEBHOOK-RACE-CONDITION, FIX-TAGIHAN-PREVENT-PAID-INVOICE-REORDER, FEAT-REQUEST-LOGGER-POSTMAN, TEST-DRAGONFLY-VITEST-SUITE, FEAT-RUNTIME-ENV-VALIDATION, TASK-NON-BLOCKING-BILLING-DISPATCH-AND-RETRY-CONFIG] |
Riwayat Update Log
| Tanggal | Environment | Perubahan | PIC |
|---|---|---|---|
| 2026-10-14 | Production | Status diupdate ke PRODUCTION (DEPLOYED, CLOSED), rilis Release v1.9.2 via MR !232 | Hermansyah Efendi |
| 2026-09-07 | Development | Inisialisasi draft audit log refactoring controller deduplication (commit ceb304a) | Hermansyah Efendi |
3. Analisis Teknis & Rincian Perubahan
Alur Logika & Bisnis:
- Pola Controller Ringan (Thin Controller Pattern): Controller pada
app/controllers/tagihan_controller.js,app/controllers/billing_controller.js,app/controllers/v3/tagihan_controller.js, danapp/controllers/v3/billing_controller.jsdireduksi secara masif menjadi entry point tipis. Controller hanya memanggil orchestrator helper terkait dengan menyuplai context(req, res, next, version)('v2'atau'v3'). - Sentralisasi Orkestrasi Rekap Tagihan (
tagihan_helper.js): Seluruh alur orkestrasi penerimaan tagihan, validasi integritas payload, pemisahan variasi satker (PNBP, BLU, BUP, dan kombinasi multi-satker Simponi), kalkulasi total tarif/nominal, persistensi database ke tabel billing (t_billingdan relasinya), pemanggilan webhook/gateway eksternal (Inaportnet, Sinkarkes), serta pencatatan logging/tracking audit dipusatkan dalamprocessRekapTagihanOrchestrator. Parameter versi ('v2'vs'v3') digunakan secara deklaratif untuk menentukan endpoint Simponi dan mapping detail elemen (16 elemen untuk V2, 19 elemen untuk V3). - Sentralisasi Orkestrasi Request Ulang Billing (
billing_helper.js): Logika query pengecekan status transaksi billing gantung/pending ke database dan eksekusi request ulang ke Simponi dikonsolidasikan ke dalamprocessRequestBillingOrchestrator. - DRY Skema Validasi Joi (
tagihan_models.js): Elemen array Joi yang berulang diekstraksi ke variabel bersamaschemaHeaderItemsdanschemaDetailItemsCommon. Custom validatorjoiSqlTestuntuk mitigasi SQL injection pattern tidak lagi didefinisikan ganda pada masing-masing versi. Elemen pembeda spesifik V3 (indeks 16–18) ditambahkan secara deklaratif. - Net Code Reduction & Anti Divergent Bug Fixes: Pengurangan bersih kode sebanyak 1.792 baris kode (+1.566 baris penambahan helper terstruktur, -3.358 baris pembersihan kode duplikat). Refactoring ini mengeliminasi risiko perbaikan bug yang tidak sinkron (divergent bug fixes) antara jalur V2 dan V3.
- Pola Controller Ringan (Thin Controller Pattern): Controller pada
Perubahan Database (DDL / DML):
sql-- Tidak ada migration file yang terdeteksi. Murni refactoring arsitektur kode backend.Kontrak API & Response Payload:Tidak ada penambahan atau perubahan kontrak API. Seluruh kontrak payload, routing, status code, dan response format untuk endpoint berikut dipertahankan secara identik (zero-regression):
POST /api/v2/terima-rekap-tagihanPOST /api/v3/terima-rekap-tagihanPOST /api/v2/request_ulang_billingPOST /api/v3/request_ulang_billing
4. Bukti Teknis (Technical Proof & Audit Trail)
- Repository:
single-billing-BE - Branch Utama:
development (Merged from refactor/tagihan-controller-duplication) - Merge Request / PR: !232 (Release v1.9.2 — Merged to prod) | Staging: !229
Daftar Commit Terkait (Multi-Commit Trail)
| Commit ID | Branch | Author | Tanggal & Jam | Ringkasan Perubahan |
|---|---|---|---|---|
f81d793 | refactor/tagihan-controller-duplication | Hermansyah Efendi | 2026-06-25 13:55 | refactor(tagihan): resolve code duplication in rekap tagihan logic |
313b84a | refactor/tagihan-controller-duplication | Hermansyah Efendi | 2026-06-25 15:03 | refactor(billing): resolve code duplication in request billing logic |
a494f8b | refactor/tagihan-controller-duplication | Hermansyah Efendi | 2026-06-25 15:21 | refactor(tagihan): resolve code duplication in validation schemas |
ceb304a | refactor/tagihan-controller-duplication | Hermansyah Efendi | 2026-06-25 15:40 | refactor(tagihan): resolve code duplication in tagihan orchestrator |
Cuplikan Kode / Implementasi Inti
// app/controllers/v3/tagihan_controller.js (Controller menjadi sangat ringkas)
import { processRekapTagihanOrchestrator } from "../../helpers/tagihan_helper";
const rekapTagihanINSW_V3 = async (req, res, next) => {
return processRekapTagihanOrchestrator(req, res, next, 'v3');
};
export { rekapTagihanINSW_V3 };// app/models/tagihan_models.js (Unifikasi skema validasi Joi)
const schemaHeaderItems = [
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').required(),
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').allow("").required(),
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').allow("").required(),
Joi.string().regex(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/).required(), // Date format
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').required(),
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').required(),
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').required(),
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').required(),
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').required(),
Joi.number().required(),
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').required(),
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').required()
];
const schemaDetailItemsCommon = [
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').allow("").required(),
Joi.string().custom(joiSqlTest, 'Input mengandung SQL Injection pattern').required(),
Joi.string().regex(/^\d{4}-\d{2}-\d{2}$/).required(), // Date format
// ... elemen umum di-share antar V2 dan V3
];5. Bukti Verifikasi Pengujian (QA / Unit Test / API Verification)
A. Verifikasi Unit Test (Development Stage)
- Test Suite File:
tests/helpers/billing_helper.test.js,tests/helpers/tagihan_helper.test.js - Status Eksekusi CLI:
PASSED
// tests/helpers/tagihan_helper.test.js
describe("processRekapTagihanOrchestrator", () => {
it("should process v2 rekap tagihan successfully", async () => {
// Verifikasi eksekusi delegasi orchestrator pada mode v2
});
it("should process v3 rekap tagihan successfully", async () => {
// Verifikasi eksekusi delegasi orchestrator pada mode v3
});
}); RUN v4.1.11 D:/WORK/LNSW/single-billing-BE
✓ tests/helpers/billing_helper.test.js (7 tests) 19ms
✓ tests/helpers/tagihan_helper.test.js (13 tests) 26ms
Test Files 2 passed (2)
Tests 20 passed (20)
Start at 11:46:11
Duration 451ms (transform 200ms, setup 125ms, import 202ms, tests 44ms, environment 1ms)B. Panduan Pengujian QA & Verifikasi Staging (QA Playbook)
Disesuaikan dan sejalan dengan Postman Collection:
Single Billing V3 - Multi Satker.postman_collection.jsonpada task FEAT-SIMPONI-V3-ISOLATION.PRINSIP QA: Live Gateway Simponi (Tanpa Mock) & Zero-Regression Dual Alur (V2 + V3). Pengujian difokuskan untuk membuktikan bahwa deduplikasi kode (ekstraksi orkestrator helper dan unifikasi skema Joi guna memenuhi SonarQube Quality Gate) tidak menimbulkan degradasi atau perubahan perilaku pada alur produksi eksisting.
- Cakupan / Surface Pengujian:
API / Postman Only(Direct Single Billing API & Live Gateway Simponi Sandbox) - Kebutuhan Akses / Role Akun: Token Bearer valid (
), Postman Environment Dev/Staging dengan koneksi live ke Simponi Kemenkeu (MOCK_MODE=off). - Hasil Pengujian UAT:
PASSED - Catatan QA: Verifikasi deduplikasi controller dan zero-regression dual flow (V2 dan V3) berhasil lolos pengujian UAT.
Skenario Pengujian QA (Test Scenarios)
Bagian I: Verifikasi Regresi Alur V2 Legacy (Backward Compatibility)
Skenario 1 — Regression Test: Terima Rekap Tagihan V2 (16 Elemen Detail):
- Postman Request:
POST/api/v2/terima-rekap-tagihan - Pre-kondisi: Environment DEV/Staging aktif, payload V2 standar (detail 16 elemen, single satker PNBP).
- Trigger / Request: Kirim payload rekap tagihan V2 ke entry-point controller tipis
rekapTagihanINSW. - Ekspektasi Response: HTTP
200 OK,processRekapTagihanOrchestratordengan mode'v2'berhasil mengeksekusi mapping detail, kalkulasi tarif, serta penerbitan billing Simponi V2 tanpa deviasi logika. - Verifikasi Database / Log: Data tersimpan pada tabel
t_billingdan terdisposisi ke Inaportnet / Sinkarkes secara normal.
- Postman Request:
Skenario 2 — Regression Test: Request Ulang Billing V2:
- Postman Request:
POST/api/v2/request_ulang_billing - Pre-kondisi: Terdapat transaksi billing V2 dengan status pending di database.
- Trigger / Request: Kirim request dengan parameter
trx_id_insw. - Ekspektasi Response: HTTP
200 OK,processRequestBillingOrchestratorberhasil memproses request ulang ke Simponi V2. - Verifikasi Database / Log: Kolom status dan nomor billing ter-update sesuai respon Simponi V2.
- Postman Request:
Bagian II: Verifikasi Alur V3 Multi-Satker & Gateway Simponi (Sejalan dengan FEAT-SIMPONI-V3-ISOLATION)
Skenario 3 — Happy Path: Agregasi Multi-Satker Ekstrem (7 Satker Hubla Lintas Wilayah):
- Postman Request:
Uji Multisatker - Tagihan Kemenhub 1 - Pre-kondisi: Environment DEV/Staging terhubung langsung ke Live Gateway Simponi Sandbox (
MOCK_MODE=off), Token Bearer valid. - Trigger / Request:
POST/api/pengangkut/tagihan(atau/api/v3/terima-rekap-tagihan) membawa bundle 7 tagihan dari 7 Satker Hubla (KSOP & Disnav: Palembang, Pangkal Balam, Banjarmasin, Tanjung Emas, Teluk Palu, Ujung Pandang, Tanjung Priok). - Ekspektasi Response: HTTP
200 OK,code: "00"dari Gateway Simponi, mengembalikan kode billing konsolidasi (820xxxxxxx). - Verifikasi Database / Log: Seluruh 7 data tagihan tersimpan dengan asosiasi kode satker riil masing-masing dan status disposisi billing aktif.
- Postman Request:
Skenario 4 — Happy Path: Baseline Single Satker (1 Satker Tunggal):
- Postman Request:
Uji Multisatker - Tagihan Kemenhub 2&Uji Multisatker - Tagihan Kemenhub 4 - Pre-kondisi: Live Gateway Simponi Sandbox aktif, Token Bearer valid.
- Trigger / Request:
POST/api/pengangkut/tagihanmembawa tagihan 1 Satker (Disnav Samarinda287260atau KSOP Samarinda287211). - Ekspektasi Response: HTTP
200 OK,code: "00". Membuktikan alur V3 tetap kompatibel saat menangani tagihan satker tunggal.
- Postman Request:
Skenario 5 — Happy Path: Agregasi Multi-Satker Moderat (Kombinasi 2 Satker Berbeda):
- Postman Request:
Uji Multisatker - Tagihan Kemenhub 3 - Pre-kondisi: Live Gateway Simponi Sandbox aktif, Token Bearer valid.
- Trigger / Request:
POST/api/pengangkut/tagihanmenggabungkan tagihan Disnav Samarinda (287260) dan KSOP Samarinda (287211). - Ekspektasi Response: HTTP
200 OK,code: "00", kode billing konsolidasi berhasil diterbitkan.
- Postman Request:
Skenario 6 — Happy Path: Agregasi Multi-Satker Kompleks (9 Tagihan Lintas 7 Satker Hubla):
- Postman Request:
Uji Multisatker - Tagihan Kemenhub 5 - Pre-kondisi: Live Gateway Simponi Sandbox aktif, Token Bearer valid.
- Trigger / Request:
POST/api/pengangkut/tagihanmembawa 9 tagihan lintas 7 Satker Hubla (KSOP Kuala Tungkal, Disnav Semarang, KSOP Tanjung Emas, Disnav Palembang, KSOP Palembang, Disnav Samarinda, KSOP Balikpapan). - Ekspektasi Response: HTTP
200 OK,code: "00", gateway Simponi menerima dan memproses seluruh array detail 19 elemen dari 9 tagihan tersebut.
- Postman Request:
Skenario 7 — Negative Tests: Validasi Skema Joi Terpadu (Unifikasi Schema):
- Postman Request:
Uji Multisatker - Tagihan Kemenhub (Minus)Uji Multisatker - Tagihan Kemenhub (Satker Alhpanumeric - Header)Uji Multisatker - Tagihan Kemenhub (Satker Alhpanumeric - Header Detail)Uji Multisatker - Tagihan Kemenhub (Nominal Tidak Sesuai Detail)
- Pre-kondisi: Skema validasi terpadu pada
tagihan_models.jsaktif. - Trigger / Request: Kirim payload dengan kondisi: nominal negatif, karakter non-numerik pada satker/akun, atau mismatch total nominal header vs akumulasi detail.
- Ekspektasi Response: HTTP
400 Bad Request, validasi Joi menolak request sebelum diteruskan ke Simponi, membuktikan aturan validasi bersama tidak bocor pasca refactoring.
- Postman Request:
Skenario 8 — Integration: Penanganan Error Respon Gateway Simponi Riil:
- Pre-kondisi: Live Gateway Simponi Sandbox aktif, dikirimkan request dengan relasi satker/akun tidak valid.
- Trigger / Request:
POST/api/v3/terima-rekap-tagihan - Ekspektasi Response: Gateway Simponi mengembalikan kode penolakan resmi (
IX,IR,IM,IS), orchestrator meneruskan format response error secara terstruktur tanpa uncaught exception.
Skenario 9 — Regression Test: Request Ulang Billing V3:
- Postman Request:
POST/api/v3/request_ulang_billing - Pre-kondisi: Terdapat transaksi billing V3 pending / gantung di database.
- Trigger / Request: Kirim request dengan parameter
trx_id_insw. - Ekspektasi Response: HTTP
200 OK,processRequestBillingOrchestratormengeksekusi request ulang ke Simponi V3 dan memperbarui record database.
- Postman Request:
Skenario 10 — Integration: Pelunasan Pembayaran Billing DJA (
activeinquiry_v2):- Postman Request:
Uji Multisatker - Kirim Payment - Pre-kondisi: Kode billing aktif terbentuk di Single Billing dan terdaftar di Simponi.
- Trigger / Request:
POST/single-billing/dja/activeinquiry_v2membawa payload pelunasan DJA (nomor billing dan NTPN). - Ekspektasi Response: HTTP
200 OK, status pembayaran billing berhasil diperbarui menjadi lunas (fl_paid = true).
- Postman Request:
Bukti Response Verifikasi Staging (Diisi saat UAT)
{
"status": "PASSED",
"note": "Pengujian verifikasi UAT dan validasi integrasi berhasil lolos, rilis production v1.9.2 telah live."
}