[ND-256/LNSW.34/2026] Sanitasi Karakter dan Truncate 50 Karakter Nama Wajib Bayar & Organization Name Single Billing
Modul: Pengangkut | Tipe: Change Request | Stage Saat Ini:
STAGING| Tanggal Dokumen: 2026-08-24
1. Informasi Tugas & Referensi Bisnis
- Task ID / Nomor Referensi:
ND-256/LNSW.34/2026 - Sumber Instruksi: Nota Dinas Resmi ND-256/LNSW.34/2026
- Dokumen Referensi / Lampiran: ND-256-LNSW.34-2026.pdf, ND-256_sanitize_nama_wajib_bayar_postman_collection.json
- Modul Terkait:
Pengangkut - Service / Repository:
pengangkut-backend - PIC Pengembang: Hermansyah Efendi
- Latar Belakang & Ringkasan Kebutuhan: Berdasarkan arahan integrasi SIMPONI Single Billing pada Nota Dinas ND-256/LNSW.34/2026, field nama wajib bayar dan nama organisasi wajib memenuhi batasan alfanumerik serta panjang maksimal 50 karakter (
C(50)). Perubahan dilakukan pada layer helper utilitas, validasi Zod, dan builder payload Single Billing agar request tagihan valid dan tidak mengalami penolakan (rejection) oleh gateway SIMPONI akibat karakter spesial atau string overflow.
2. Deployment Lifecycle
| # | Environment | Tanggal & Waktu | PIC | Status | Catatan |
|---|---|---|---|---|---|
| 1 | Development | 2026-08-20 14:30 | Hermansyah Efendi | DEPLOYED | Branch: development |
| 2 | Staging (UAT) | 2026-09-09 11:34 | achmad fauzi | DEPLOYED | Release Candidate v1.18.2 via MR !2080. Co-deployed: [TASK-DRAGONFLYDB-MIGRATION, TASK-SPLIT-BILLING-202608, FEAT-MANUAL-BILLING-REQUEST, MAINT-SUPERSET-SUMMARY-SHIP-SYNC-SECURITY, REFACTOR-DEPRECATE-POST-STATUS-SERVICE-MIGRATION, TASK-PINO-LOGGER-REFACTOR, ND-304-LNSW.34-2026, ND-256-LNSW.34-2026, ND-183-LNSW.34-2026] |
| 3 | Production | — | — | PENDING | — |
Riwayat Update Log
| Tanggal | Environment | Perubahan | PIC |
|---|---|---|---|
| 2026-09-09 | Staging | Promosi ke STAGING via MR !2080 (Release Candidate v1.18.2) | Hermansyah Efendi |
| 2026-08-24 | Development | Inisialisasi draft audit log dari context block backend (commit 401a4dcb) | Hermansyah Efendi |
3. Analisis Teknis & Rincian Perubahan
Alur Logika & Bisnis:
- Penambahan helper
normalizeNpwpuntuk standardisasi format NPWP dengan membersihkan karakter selain angka (spasi, titik, tanda minus). - Penambahan helper
sanitizeAlphanumericdengan regex untuk membersihkan karakter non-alfanumerik, menormalkan spasi ganda menjadi spasi tunggal, serta melakukan pemotongan string otomatis maksimal 50 karakter. - Refactor skema validasi NPWP di
app/helpers/zod.jsmenggunakan helpernormalizeNpwp. - Integrasi sanitasi & truncate 50 karakter di
app/models/billing_models.jspada methodgenerateBillingPayloadV3,setRekapTagihanV2, danpayloadSingleBillingV2untuk fieldorganization_name(header) dannama_wajib_bayar(detail). - Penambahan test suite komprehensif di
tests/helpers/utils.test.jsdantests/models/billing_models.test.js.
- Penambahan helper
Perubahan Database (DDL / DML):
sql-- Tidak ada perubahan skema database (DDL/DML) untuk task ini. -- Perubahan murni pada layer data formatting dan outbound API payload builder.Kontrak API & Response Payload: Penyesuaian outbound payload menuju Single Billing SIMPONI: field
organization_namedannama_wajib_bayardijamin alfanumerik dengan panjang $\le 50$ karakter.json{ "organization_name": "PT PELAYARAN NASIONAL INDONESIA", "details": [ { "nama_wajib_bayar": "PT PELAYARAN NASIONAL INDONESIA", "npwp": "012345678901234" } ] }
4. Bukti Teknis (Technical Proof & Audit Trail)
- Repository:
pengangkut-backend - Branch:
development - Commit ID / Hash:
401a4dcb68b77cacbf3e6eda741bca565ca1c8fd - Merge Request / PR: MR !2080 (Release Candidate v1.18.2)
Cuplikan Kode / Implementasi Inti
javascript
// app/helpers/utils.js
const sanitizeAlphanumeric = (str, maxLength = 50) => {
if (!str || typeof str !== 'string') return '';
return str
.replace(/[^a-zA-Z0-9\s]/g, '')
.replace(/\s+/g, ' ')
.trim()
.slice(0, maxLength);
};
const normalizeNpwp = (npwp) => {
if (!npwp || typeof npwp !== 'string') return '';
return npwp.replace(/[^0-9]/g, '');
};5. Bukti Verifikasi Pengujian (QA / API Verification)
Diisi saat naik ke Staging atau Production.
A. Hasil Test Runner Lokal (Unit / Integration Test)
json
{
"test_suites": [
"tests/helpers/utils.test.js (PASS)",
"tests/models/billing_models.test.js (PASS)"
],
"verification_status": "DEV_PASSED"
}- Hasil Pengujian Unit Test:
PASSED(Lokal Dev) - Status Verifikasi Staging:
PENDING(Menunggu pengujian integrasi UAT Staging) - Catatan QA: Validasi sanitasi karakter string dan normalisasi NPWP telah diuji dengan berbagai variasi input edge-case.
B. Panduan Pengujian QA & Verifikasi Staging (QA Playbook)
Panduan eksekusi pengujian tim QA untuk memvalidasi fungsi sanitasi nama wajib bayar pada endpoint penerimaan tagihan.
- Cakupan Pengujian:
Backend API (Injeksi Tagihan / Webhook Simponi) - Endpoint Target:
POST /tagihan - Postman Collection File: ND-256_sanitize_nama_wajib_bayar_postman_collection.json
- Aturan Sanitasi:
- Karakter diperbolehkan: hanya alfanumerik (
a-z,A-Z,0-9) dan spasi. - Karakter spesial/simbol/tanda baca dihapus/dibersihkan (
[^a-zA-Z0-9\s]dibuang). - Spasi berulang dipadatkan menjadi satu spasi dan di-trim.
- Panjang maksimum dibatasi tepat 50 karakter (
slice(0, 50)).
- Karakter diperbolehkan: hanya alfanumerik (
1. Konfigurasi Variabel Postman Collection
| Variable Name | Default Value / Template | Fungsi & Deskripsi |
|---|---|---|
baseUrl | https://demo-backend.insw.go.id/pengangkut/api | Base URL API pengangkut |
billingKemenhubKey | xxx | Key otentikasi tagihan Kemenhub (KL 022) pada simponi_req.key |
inswKemenhubKey | xxx | Key otentikasi header insw-kemenhub-key respons warta kapal |
bearerToken | KZ6IlAA4NxocmCozKC71xaYymS7sMAgM_94h0gftFnG | Bearer token otentikasi user |
serviceNumber | 102019B83694C | Nomor aju / service number kapal target uji |
2. Simulasi Data Prasyarat (Data Seeding)
Sebelum menjalankan pengujian variasi nama wajib bayar, pastikan data prasyarat berikut diinjeksi ke environment Staging:
A. Simulasi Approval LK3 Kemenhub (POST /warta-kapal/send/putResponse)
bash
curl --location 'https://demo-backend.insw.go.id/pengangkut/api/warta-kapal/send/putResponse' \
--header 'Content-Type: application/json' \
--header 'insw-kemenhub-key: xxx' \
--data '{
"no_aju": "{{serviceNumber}}",
"tipe": "LK3",
"nomor_layanan": "BDN.IDMAK.2609.000140",
"status": 2,
"message": "",
"nomor_produk": "SL003.IDMAK.0926.000142",
"waktu_approve": "2026-09-09 11:10:22",
"link_produk": "https://simpadu-inaportnet.dephub.go.id/index.php/document/pdfs/lk3_nakhoda/SL003.DN.IDMAK.2609.000139",
"kode_dermaga": ""
}'B. Simulasi Tagihan Inaportnet Baseline Kemenhub KL 022 (POST /tagihan)
bash
curl --location 'https://demo-backend.insw.go.id/pengangkut/api/tagihan' \
--header 'Content-Type: application/json' \
--data '{
"simponi_req": {
"method": "billingcode",
"key": "{{billingKemenhubKey}}",
"data": [
{
"header": [
"{{serviceNumber}}",
"INV.IDMAK.2609.000003",
"",
null,
"022",
"04",
"413439",
"F",
"1",
834660,
"Labuh-PU-PT. JELAJAH LAUT NUSANTARA",
"413439",
"014844831042001"
],
"detail": [
[
"Labuh-PU-PT. JELAJAH LAUT NUSANTARA",
"000401",
"201615C",
"425513",
90,
9274,
"Per GT/15 Hari",
834660,
"19",
"51",
0
]
]
}
]
}
}'3. Skenario Pengujian Sanitasi Nama Wajib Bayar
Skenario 1: Nama Wajib Bayar Melebihi 50 Karakter (Length Truncation)
- Tujuan: Memastikan nilai
nama_wajib_bayaryang panjangnya melebihi 50 karakter dipotong (truncated) menjadi tepat 50 karakter tanpa menimbulkan error pada saat diproses atau dikirim ke DJA Simponi. - Input Nama Wajib Bayar (70 Karakter):
"PT JELAJAH LAUT NUSANTARA LOGISTIK MARITIM INDONESIA JAYA MAKMUR ABADI" - Nilai Ekspektasi Hasil Sanitasi (Tepat 50 Karakter):
"PT JELAJAH LAUT NUSANTARA LOGISTIK MARITIM INDONESI" - Eksekusi cURL:bash
curl --location 'https://demo-backend.insw.go.id/pengangkut/api/tagihan' \ --header 'Content-Type: application/json' \ --data '{ "simponi_req": { "method": "billingcode", "key": "{{billingKemenhubKey}}", "data": [ { "header": [ "{{serviceNumber}}", "INV.IDMAK.2609.000003", "", null, "022", "04", "413439", "F", "1", 834660, "PT JELAJAH LAUT NUSANTARA LOGISTIK MARITIM INDONESIA JAYA MAKMUR ABADI", "413439", "014844831042001" ], "detail": [ [ "PT JELAJAH LAUT NUSANTARA LOGISTIK MARITIM INDONESIA JAYA MAKMUR ABADI", "000401", "201615C", "425513", 90, 9274, "Per GT/15 Hari", 834660, "19", "51", 0 ] ] } ] } }' - Kriteria Penerimaan (Acceptance Criteria):
- API merespon dengan status
200 OK. - Kolom
nama_wajib_bayarpada tabelpengangkut.tagihan_hdrtersimpan dengan panjang maksimal 50 karakter:"PT JELAJAH LAUT NUSANTARA LOGISTIK MARITIM INDONESI".
- API merespon dengan status
Skenario 2: Nama Wajib Bayar Mengandung Karakter Spesial (Special Character Stripping)
- Tujuan: Memastikan semua karakter spesial (titik, koma, tanda hubung, simbol
@,#,&,*,(,),!,_) dibersihkan secara bersih, sehingga payload yang keluar ke DJA Simponi tidak memicu error validasi XML/JSON. - Input Nama Wajib Bayar (Mengandung Karakter Spesial):
"Labuh-PU-PT. @JELAJAH# & LAUT* (NUSANTARA)!_123" - Nilai Ekspektasi Hasil Sanitasi:
"Labuh PU PT JELAJAH LAUT NUSANTARA 123" - Eksekusi cURL:bash
curl --location 'https://demo-backend.insw.go.id/pengangkut/api/tagihan' \ --header 'Content-Type: application/json' \ --data '{ "simponi_req": { "method": "billingcode", "key": "{{billingKemenhubKey}}", "data": [ { "header": [ "{{serviceNumber}}", "INV.IDMAK.2609.000003", "", null, "022", "04", "413439", "F", "1", 834660, "Labuh-PU-PT. @JELAJAH# & LAUT* (NUSANTARA)!_123", "413439", "014844831042001" ], "detail": [ [ "Labuh-PU-PT. @JELAJAH# & LAUT* (NUSANTARA)!_123", "000401", "201615C", "425513", 90, 9274, "Per GT/15 Hari", 834660, "19", "51", 0 ] ] } ] } }' - Kriteria Penerimaan (Acceptance Criteria):
- API merespon dengan status
200 OK. - Kolom
nama_wajib_bayarpada database tersimpan menjadi"Labuh PU PT JELAJAH LAUT NUSANTARA 123"tanpa karakter khusus apa pun.
- API merespon dengan status
Skenario 3: Nama Wajib Bayar Melebihi 50 Karakter DAN Mengandung Karakter Spesial
- Tujuan: Memastikan urutan sanitasi berjalan benar saat input berupa teks panjang yang mengandung banyak karakter spesial (karakter ilegal dibuang terlebih dahulu, spasi dipadatkan, lalu dipotong maksimal 50 karakter).
- Input Nama Wajib Bayar (102 Karakter dengan Karakter Spesial):
"PT. JELAJAH LAUT @NUSANTARA #LOGISTIK (MARITIM) - CABANG UTAMA PELABUHAN TANJUNG PRIOK JAKARTA UTARA" - Proses Evaluasi:
- Pembersihan karakter khusus
[^a-zA-Z0-9\s]:"PT JELAJAH LAUT NUSANTARA LOGISTIK MARITIM CABANG UTAMA PELABUHAN TANJUNG PRIOK JAKARTA UTARA"(93 karakter). - Pemotongan
slice(0, 50):"PT JELAJAH LAUT NUSANTARA LOGISTIK MARITIM CABANG "-> trim menjadi"PT JELAJAH LAUT NUSANTARA LOGISTIK MARITIM CABANG"(50 karakter).
- Pembersihan karakter khusus
- Nilai Ekspektasi Hasil Sanitasi (Tepat 50 Karakter Bersih):
"PT JELAJAH LAUT NUSANTARA LOGISTIK MARITIM CABANG" - Eksekusi cURL:bash
curl --location 'https://demo-backend.insw.go.id/pengangkut/api/tagihan' \ --header 'Content-Type: application/json' \ --data '{ "simponi_req": { "method": "billingcode", "key": "{{billingKemenhubKey}}", "data": [ { "header": [ "{{serviceNumber}}", "INV.IDMAK.2609.000003", "", null, "022", "04", "413439", "F", "1", 834660, "PT. JELAJAH LAUT @NUSANTARA #LOGISTIK (MARITIM) - CABANG UTAMA PELABUHAN TANJUNG PRIOK JAKARTA UTARA", "413439", "014844831042001" ], "detail": [ [ "PT. JELAJAH LAUT @NUSANTARA #LOGISTIK (MARITIM) - CABANG UTAMA PELABUHAN TANJUNG PRIOK JAKARTA UTARA", "000401", "201615C", "425513", 90, 9274, "Per GT/15 Hari", 834660, "19", "51", 0 ] ] } ] } }' - Kriteria Penerimaan (Acceptance Criteria):
- API merespon dengan status
200 OK. - Nilai
nama_wajib_bayaryang tersimpan dan dikirimkan ke outbound DJA Simponi tidak memiliki karakter spesial dan panjangnya $\le 50$ karakter.
- API merespon dengan status
Verifikasi Database (SQL Query)
Untuk memverifikasi hasil penyimpanan pada database Staging:
sql
SELECT
no_aju_layanan,
nama_wajib_bayar,
LENGTH(nama_wajib_bayar) AS panjang_karakter,
create_date,
kode_billing_induk
FROM billing.subbiller_hdr
WHERE no_aju_layanan = '{{serviceNumber}}' and create_date is not null
ORDER BY create_date DESC
LIMIT 5;