e-invoicing2026-07-08

Cara Menyiasat Ralat API LHDN MyInvois Tanpa Meneka

Panduan berasaskan kod sebenar GetPay untuk pengesahan MyInvois, pembinaan muatan UBL 2.1, kawalan hantaran, semakan status dan bukti ralat.

Pasukan Kejuruteraan GetPay
Dikemas kini: 2026-07-28

Ringkasan Utama (TL;DR)

  • Kenal pasti operasi yang gagal, status HTTP dan petikan jawapan sebenar sebelum mengubah muatan; GetPay tidak mereka-reka senarai kod ralat MyInvois.
  • GetPay menghasilkan rentetan JSON UBL sekali sahaja, mengira SHA-256 daripada bait UTF-8 yang sama, kemudian mengekod rentetan itu kepada Base64.
  • Permintaan token dan semakan status boleh dicuba semula kerana tidak memfailkan dokumen; hantaran e-Invois serta perubahan status hanya mempunyai had masa tanpa cubaan semula automatik.
  • TIN awam Malaysia digunakan sebagai sandaran bagi pembeli B2C tempatan tanpa TIN, manakala TIN am luar negara hanya digunakan dalam aliran e-Invois bil kendiri untuk pembekal individu asing yang layak.

Mulakan dengan operasi yang gagal

Siasatan ralat e-Invois perlu bermula daripada bukti, bukan nama kod yang diandaikan:

  1. Tentukan sama ada kegagalan berlaku semasa mendapatkan token, menghantar dokumen, membaca status atau membatalkan/menolak dokumen.
  2. Simpan status HTTP dan teks jawapan sebenar.
  3. Bandingkan dokumen UBL yang dikira hash dengan bait yang dienkodkan kepada Base64.
  4. Baiki data atau peraturan pemetaan hanya selepas punca operasi dikenal pasti.

GetPay tidak memadankan setiap kegagalan kepada senarai kod ciptaan sendiri. Pelanggan HTTP asal mengekalkan bukti yang dipulangkan oleh LHDN, tetapi mengehadkan panjang teks supaya catatan operasi kekal terkawal.

Operasi GetPayKaedah dan laluanDasar cubaan semulaBukti kegagalan
Token aksesPOST /connect/tokenCubaan semula dengan sela masaStatus dan 200 aksara pertama
Hantar dokumenPOST /api/v1.0/documentsubmissionsHad masa sahaja; tiada cubaan semulaStatus dan 500 aksara pertama
Baca butiran dokumenGET /api/v1.0/documents/{uuid}/detailsCubaan semula dengan sela masaStatus dan 300 aksara pertama
Batal atau tolakPUT /api/v1.0/documents/state/{uuid}/stateHad masa sahaja; tiada cubaan semulaStatus dan 300 aksara pertama

Pengesahan: gunakan nilai expires_in

GetPay memohon token kelayakan pelanggan dengan skop InvoicingAPI. Tempoh sah token tidak diandaikan sebagai nombor tetap. Sistem menggunakan nilai expires_in daripada LHDN dan menyimpan token itu dalam proses pelayan.

Token simpanan hanya digunakan apabila baki tempohnya melebihi 60 saat:

const cached = tokenCache.get(key)
if (cached && cached.exp > Date.now() + 60_000) return cached.token

tokenCache.set(key, {
  token: json.access_token,
  exp: Date.now() + json.expires_in * 1000,
})

Permintaan token boleh dicuba semula kerana ia tidak memfailkan dokumen perakaunan. Jika permintaan token gagal, semak status dan teks jawapan sebelum menukar kelayakan atau skop.

Satu rentetan untuk hash dan Base64

Fungsi buildSubmissionDocument() menghasilkan satu rentetan JSON. Rentetan yang sama digunakan untuk hash SHA-256 dan pengekodan Base64:

const canonical = JSON.stringify(ublDoc)
const bytes = new TextEncoder().encode(canonical)
const hashBuf = await crypto.subtle.digest("SHA-256", bytes)
const documentHash = [...new Uint8Array(hashBuf)]
  .map((byte) => byte.toString(16).padStart(2, "0"))
  .join("")
const document = Buffer.from(canonical, "utf8").toString("base64")

Jangan kira hash daripada satu versi JSON tetapi menghantar versi yang telah diubah. Perbezaan satu bait pun menyebabkan documentHash tidak lagi menerangkan dokumen yang dihantar.

Sampul hantaran GetPay mengandungi format: "JSON", document, documentHash dan nombor invois syarikat sebagai codeNumber. Jawapan berjaya dijangka membawa submissionUid, senarai dokumen diterima berserta uuid, serta senarai dokumen ditolak dengan objek ralat sebenar daripada LHDN.

Semak pemetaan UBL sebelum menyalahkan rangkaian

Bagi invois biasa, GetPay membina dokumen jenis 01 versi 1.1 dalam mata wang MYR. Maklumat pihak pembekal merangkumi TIN dan nombor pendaftaran perniagaan. Bagi pembeli B2C tempatan tanpa TIN, pemeta boleh menggunakan EI00000000010.

EI00000000030 bukan TIN sandaran umum bagi semua pembeli. Dalam GetPay, nilai itu hanya digunakan dalam aliran bil kendiri apabila pembekal individu berada di luar Malaysia dan tidak memberikan TIN Malaysia.

Aliran bil kendiri turut mengesahkan:

  • jenis dan nilai pengenalan pembekal;
  • kod negara, negeri, pos dan MSIC;
  • nombor telefon dalam format E.164;
  • amaun positif dengan ketepatan sen; dan
  • jumlah baris yang sama tepat dengan jumlah baucar bayaran.

Agihan cukai yang tepat hingga sen

GetPay menggunakan kaedah baki terbesar Hamilton supaya setiap bahagian cukai baris tidak negatif dan jumlahnya sama tepat dengan cukai dokumen:

const exact = weights.map((weight) => (totalCents * weight) / totalWeight)
exact.forEach((share, index) => {
  cents[index] = Math.floor(share)
})
let remainder = totalCents - cents.reduce((sum, value) => sum + value, 0)

Kaedah ini mengelakkan pembundaran berasingan pada setiap baris daripada terlebih mengagihkan cukai kecil lalu menghasilkan nilai negatif pada baris terakhir.

Tamat masa bermaksud hasil belum diketahui

Hantaran dokumen menggunakan fetchWithTimeout, bukan fungsi cubaan semula. Sebelum panggilan keluar, aliran GetPay membuat tuntutan pangkalan data yang atomik dengan menetapkan status LHDN invois kepada SUBMITTING. Tuntutan itu menghalang dua pemanggil serentak, tetapi tidak membuktikan sama ada LHDN menerima dokumen apabila jawapan rangkaian hilang.

Selepas tamat masa:

  • jangan hantar dokumen yang sama sekali lagi secara membuta tuli;
  • kekalkan status tempatan dan bukti muatan;
  • jika uuid telah diterima, baca status melalui titik akhir butiran dokumen; dan
  • jika uuid belum diterima, selaraskan rekod operasi tanpa menganggap hantaran berjaya atau gagal.

Perubahan status untuk pembatalan dan penolakan menggunakan prinsip konservatif yang sama: ada had masa, tetapi tiada ulangan automatik.

Senarai semak penyiasatan

  • Pastikan persekitaran dan kelayakan diambil daripada rekod syarikat penyewa yang betul.
  • Kenal pasti operasi, status HTTP dan petikan jawapan sebenar.
  • Pastikan hash SHA-256 dan Base64 berasal daripada rentetan JSON yang sama.
  • Semak jenis dokumen, versi, peranan TIN, skim pendaftaran, MSIC, alamat, telefon dan ketepatan sen.
  • Pastikan jumlah cukai semua baris sama dengan cukai dokumen.
  • Jangan tafsir kehilangan jawapan rangkaian sebagai bukti bahawa permintaan perubahan gagal.

Soalan Lazim (FAQ)

Mengapakah GetPay tidak menghantar semula dokumen secara automatik selepas sambungan tamat masa?

LHDN mungkin telah menerima dokumen walaupun jawapannya tidak sampai kepada GetPay. Hantaran POST kali kedua boleh memfailkan dokumen pendua. Oleh itu, GetPay menggabungkan tuntutan pangkalan data SUBMITTING yang atomik dengan permintaan hantaran berhad masa tanpa cubaan semula.

Adakah TIN sandaran yang sama digunakan untuk semua pembeli dan pembekal?

Tidak. Invois B2C biasa boleh menggunakan EI00000000010 apabila pembeli tempatan tiada TIN. EI00000000030 hanya digunakan oleh pemeta bil kendiri GetPay bagi pembekal individu asing yang layak dan tidak mempunyai TIN Malaysia.

Apakah maklumat ralat MyInvois yang disimpan oleh GetPay?

Bagi jawapan yang tidak berjaya, GetPay menyimpan nama operasi, status HTTP dan petikan terhad daripada badan jawapan. Jawapan hantaran yang berjaya pula diwakili sebagai acceptedDocuments dan rejectedDocuments; dokumen yang ditolak boleh membawa kod dan mesej sebenar daripada LHDN.

Sumber & Rujukan Rasmi

Direct LHDN MyInvois Submission Engine

Ready to automate your Malaysian e-invoicing & bookkeeping?

GetPay handles 100% compliant e-invoices, multi-bank reconciliation, and statutory payroll out of the box.

Get Started Free

Artikel Berkaitan