Panduan Pasukan Lucee MY

Panduan lengkap integrasi MyInvois e-Invois LHDN dengan Lucee/CFML

Panduan lengkap integrasi MyInvois e-Invois LHDN dengan Lucee dan CFML: aliran OAuth client_credentials, hantar dokumen, semak status dan ralat validator.

Panduan lengkap integrasi MyInvois e-Invois LHDN dengan Lucee/CFML
0
Kandungan

Panduan lengkap integrasi MyInvois e-Invois LHDN dengan Lucee/CFML

MyInvois ialah sistem e-Invois LHDN yang memerlukan invois digital dihantar dan disahkan melalui API, dan artikel ini menunjukkan cara menyambungkan aplikasi Lucee/CFML anda kepadanya. Anda akan belajar mendapatkan token OAuth, menghantar dokumen, dan menyemak status penghantaran dengan contoh kod cfhttp yang boleh disalin terus. Dua perkara paling penting untuk difahami sebelum mula: token OAuth diperoleh dengan client_id dan client_secret, manakala sijil digital digunakan untuk menandatangani dokumen, bukan untuk mendapatkan token.

Apa itu MyInvois dan siapa perlu integrasi

MyInvois ialah platform e-Invois rasmi LHDN untuk penjanaan, penghantaran dan pengesahan invois digital. Invois boleh dihantar melalui dua saluran: Portal MyInvois secara manual, atau API untuk integrasi sistem. Jika aplikasi anda menjana invois daripada pangkalan data dan mahu menghantarnya secara automatik, laluan API ialah pilihan yang betul. Dokumen dihantar dalam format UBL 2.1, sama ada XML atau JSON, dan setiap dokumen yang dihantar melalui API mesti ditandatangani secara digital sebelum disahkan oleh LHDN.

Integrasi API membolehkan anda menghantar invois terus daripada aliran kerja sedia ada tanpa log masuk ke portal secara manual. Ini penting untuk perniagaan yang mengeluarkan invois dalam jumlah besar atau yang mahu status invois dikemas kini secara automatik dalam sistem sendiri. Jika anda belum biasa membina endpoint dalam Lucee, mulakan dengan panduan REST API dengan Lucee.

Prasyarat: TIN, Client ID dan Client Secret

Sebelum menulis sebarang kod, anda memerlukan beberapa perkara. Pertama, syarikat anda mesti berdaftar dengan LHDN dan mempunyai TIN. Kedua, anda perlu menyediakan Client ID dan Client Secret melalui Portal MyInvois, di bawah panduan View and Register ERP. Proses ini dipanggil self-provision dan boleh dilakukan sendiri oleh pembayar cukai.

Satu perkara penting: anda akan menerima dua Client Secret sebagai redundansi, dan kedua-duanya tamat tempoh serentak. Client ID dan Client Secret juga berbeza antara persekitaran production dan sandbox, jadi pastikan anda menggunakan pasangan yang betul untuk persekitaran yang anda uji. Simpan Client ID dan Client Secret dalam pemboleh ubah persekitaran dan jangan sekali-kali menulisnya ke dalam kod sumber. Amalan ini dibincangkan lanjut dalam panduan rahsia dan pemboleh ubah persekitaran.

Persekitaran Production dan Sandbox

LHDN menyediakan dua persekitaran: production untuk invois sebenar dan sandbox untuk ujian. Jadual berikut menyenaraikan URL rasmi bagi setiap persekitaran.

PersekitaranPortal InvoisSystem APIIdentity Service
Productionmyinvois.hasil.gov.myapi.myinvois.hasil.gov.myapi.myinvois.hasil.gov.my
Sandboxpreprod.myinvois.hasil.gov.mypreprod-api.myinvois.hasil.gov.mypreprod-api.myinvois.hasil.gov.my

Sandbox wujud dan boleh diakses dengan mendaftar syarikat di portal pra-pengeluaran, kemudian mendaftar sistem ERP untuk mendapatkan Client ID dan Client Secret. Perhatikan bahawa sandbox menggunakan had kadar API yang lebih rendah berbanding production, berkuat kuasa 28 April 2025, dan data sandbox disimpan maksimum tiga bulan sebelum dipadam secara kekal. Sentiasa uji di sandbox dahulu sebelum menyentuh production.

Langkah 1: Dapatkan token OAuth

Pengesahan sistem menggunakan OAuth 2.0 client credentials flow, bukan kaedah lain. Ini bermakna anda menghantar client_id dan client_secret untuk mendapatkan access token, dan token inilah yang digunakan pada panggilan API berikutnya. Sijil digital tidak terlibat dalam langkah ini.

Token diperoleh daripada Identity Service melalui endpoint POST /connect/token. Nilai grant_type mesti tepat client_credentials, dan parameter scope seperti InvoicingAPI adalah pilihan. Contoh permintaan menggunakan cfhttp:

<cfhttp method="post" url="https://preprod-api.myinvois.hasil.gov.my/connect/token" result="tokenResp">
    <cfhttpparam type="header" name="Content-Type" value="application/x-www-form-urlencoded" />
    <cfhttpparam type="formfield" name="client_id" value="CLIENT_ID_ANDA" />
    <cfhttpparam type="formfield" name="client_secret" value="CLIENT_SECRET_ANDA" />
    <cfhttpparam type="formfield" name="grant_type" value="client_credentials" />
    <cfhttpparam type="formfield" name="scope" value="InvoicingAPI" />
</cfhttp>

<cfif tokenResp.statuscode EQ 200>
    <cfset tokenData = deserializeJSON(tokenResp.filecontent) />
    <cfset application.myInvoisToken = tokenData.access_token />
    <cfset application.myInvoisTokenExpiry = dateAdd("h", 1, now()) />
<cfelse>
    <cfset ralat = deserializeJSON(tokenResp.filecontent) />
    <!--- Periksa ralat.error dan ralat.error_description untuk punca kegagalan --->
</cfif>

Balasan yang berjaya memulangkan access_token berbentuk JWT, token_type bernilai Bearer, expires_in bernilai 3600, dan scope. Nilai expires_in 3600 bermakna token sah selama satu jam. Jika permintaan gagal dengan kod 400, medan error akan mengandungi salah satu nilai seperti invalid_client, invalid_grant atau unsupported_grant_type. Kegagalan biasanya berpunca daripada Client ID atau Secret yang salah, atau pengguna sistem yang disekat atau tamat tempoh. Untuk perantara, tambah header onbehalfof dengan TIN pembayar cukai atau format TIN:ROB.

Langkah 2: Simpan dan guna semula token

Token sistem sah selama 60 minit. Tiada refresh token, jadi cara memperbaharui ialah meminta token baharu melalui login semula sebelum token semasa tamat. Jangan minta token baharu untuk setiap panggilan API, ini anti-pattern yang didokumenkan secara rasmi dan akan menyebabkan anda terkena had kadar login sebanyak 12 permintaan seminit untuk setiap Client ID.

Simpan token dalam skop application bersama masa tamatnya, dan semak sebelum setiap panggilan. Ini selari dengan amalan yang dibincangkan dalam panduan caching dan prestasi CFML.

<cfset fungsiAmbilToken = function() {
    if (not structKeyExists(application, "myInvoisToken") or now() gte application.myInvoisTokenExpiry) {
        // panggil semula /connect/token dan simpan token baharu
    }
    return application.myInvoisToken;
} />

Logik ini mudah tetapi penting: satu token digunakan merentasi banyak panggilan sehingga ia hampir tamat, kemudian token baharu diminta sekali. Pendekatan ini menjimatkan permintaan dan mematuhi had kadar login.

Langkah 3: Hantar dokumen

Dokumen dihantar ke endpoint POST /api/v1.0/documentsubmissions/ pada System API. Setiap permintaan membawa tatasusunan documents, dan setiap dokumen mengandungi format (XML atau JSON), document sebagai base64, documentHash sebagai hash SHA-256 dokumen, dan codeNumber sebagai nombor rujukan dalaman anda.

<cfset docXml = fileRead(expandPath("./invois/inv-0001.xml")) />
<cfset docHash = hash(docXml, "SHA-256") />
<cfset docB64 = toBase64(docXml) />

<cfset payload = {
    documents = [
        {
            format = "XML",
            document = docB64,
            documentHash = docHash,
            codeNumber = "INV-0001"
        }
    ]
} />

<cfhttp method="post" url="https://preprod-api.myinvois.hasil.gov.my/api/v1.0/documentsubmissions/" result="subResp">
    <cfhttpparam type="header" name="Authorization" value="Bearer #application.myInvoisToken#" />
    <cfhttpparam type="header" name="Content-Type" value="application/json" />
    <cfhttpparam type="body" value="#serializeJSON(payload)#" />
</cfhttp>

Endpoint ini memulangkan kod 202 Accepted, bukan 200. Ini bermakna penghantaran diterima tetapi pengesahan belum selesai pada masa respons dikembalikan. Balasan mengandungi submissionUID sepanjang 26 aksara alfanumerik yang anda perlukan untuk menyemak status kemudian, serta senarai acceptedDocuments dan rejectedDocuments. Setiap dokumen yang diterima mendapat uuid 26 aksara dan invoiceCodeNumber.

Had saiz yang perlu dipatuhi: maksimum 5 MB setiap submission, maksimum 100 e-Invois setiap submission, dan maksimum 300 KB setiap e-Invois. Ralat khusus penghantaran termasuk BadStructure dan MaximumSizeExceeded dengan kod 400, IncorrectSubmitter dengan kod 403, dan DuplicateSubmission dengan kod 422 yang disertakan header Retry-After. Pengesanan duplikat berdasarkan hash payload dalam tempoh 10 minit yang lepas, jadi jangan hantar semula submission yang sama.

Langkah 4: Semak status dan ambil dokumen

Selepas menghantar, anda menyemak status melalui endpoint Get Submission, bukan melalui Get Recent, Search atau Get Document. Ini anti-pattern rasmi yang perlu dielakkan kerana endpoint lain tidak memulangkan status pengesahan yang tepat.

Guna GET /api/v1.0/documentsubmissions/{submissionUid} dengan parameter pageNo dan pageSize, di mana pageSize maksimum ialah 100. Medan overallStatus memulangkan salah satu daripada nilai in progress, valid, partially valid atau invalid. Polling disyorkan pada selang 3 hingga 5 saat, dan endpoint ini mempunyai had kadar 300 permintaan seminit.

Apabila dokumen sudah valid, anda boleh mengambil dokumen mentah melalui GET /api/v1.0/documents/{uuid}/raw. Medan longId hanya tersedia untuk dokumen berstatus valid, dan ia digunakan untuk membina URL pengesahan dokumen dalam bentuk {envbaseurl}/uuid-of-document/share/longid. URL inilah yang menjadi asas kod QR yang diberikan kepada pembeli untuk menyemak invois.

Batalkan, tolak dan kawal kadar

Dokumen yang sah boleh dibatalkan atau ditolak dalam tetingkap 72 jam dari masa dokumen ditandai valid. Selepas tempoh itu, anda mesti menggunakan nota kredit, debit atau bayaran balik. Pembatalan menggunakan PUT /api/v1.0/documents/state/{UUID}/state dengan status cancelled dan reason tidak melebihi 300 aksara. Penolakan menggunakan laluan yang sama dengan status rejected, dan hanya boleh dilakukan sekali.

Setiap respons API membawa header correlationId untuk jejak audit, serta header X-Rate-Limit-Limit, X-Rate-Limit-Remaining dan X-Rate-Limit-Reset untuk kawalan kadar. Apabila anda menerima kod 429 Too Many Requests, hormati header Retry-After sebelum mencuba semula. Struktur ralat standard mengandungi medan propertyName, propertyPath, errorCode, error dalam Bahasa Inggeris, errorMS dalam Bahasa Melayu, target dan innerError. Untuk panduan lanjut tentang penggunaan cfhttp secara umum, rujuk panduan cfhttp dan API luar.

Soalan Lazim (FAQ)

Adakah saya perlu sijil digital untuk mendapatkan token OAuth?

Tidak. Token diperoleh dengan client_id dan client_secret melalui aliran client_credentials. Sijil digital digunakan untuk menandatangani dokumen, bukan untuk mendapatkan token.

Berapa lama token sah dan bagaimana ia diperbaharui?

Token sah selama 60 minit. Tiada refresh token, jadi anda mesti meminta token baharu melalui login semula sebelum token semasa tamat.

Di mana saya boleh dapatkan Client ID dan Client Secret?

Melalui Portal MyInvois di bawah panduan View and Register ERP. Anda akan menerima dua Client Secret sebagai redundansi, dan kedua-duanya tamat serentak.

Mengapa saya menerima 202 dan bukannya 200 semasa menghantar dokumen?

Kod 202 Accepted bermakna penghantaran diterima tetapi pengesahan belum selesai. Semak status melalui Get Submission selepas itu.

Mengapa saya mendapat ralat DuplicateSubmission?

Kerana payload yang sama dihantar semula dalam tempoh 10 minit. Semak header Retry-After dan jangan hantar semula submission yang sama.

Sumber

0 komen

Log masuk untuk menjawab atau memberi komen.

Log masuk