@ripwords/myinvois-client
v0.6.0
Published
TypeScript client for Malaysia's MyInvois (LHDN) e-invoicing API — document building, XAdES signing, submission and rate limiting.
Maintainers
Readme
MyInvois Client
A comprehensive Node.js client for Malaysia's MyInvois e-invoicing system with full digital signature support.
Installation
bun installEnvironment Variables
Configuration lives in a git-ignored .env file — copy .env.example and
fill it in. The PKCS#12 bundle is the single source of the signing
certificate and private key.
# MyInvois API credentials (from the MyInvois portal)
MYINVOIS_CLIENT_ID=your_client_id
MYINVOIS_CLIENT_SECRET=your_client_secret
# PKCS#12 signing certificate
MYINVOIS_P12_PATH=path/to/certificate.p12
MYINVOIS_P12_PASSPHRASE=your_passphrase
# Supplier identity (must match the certificate holder)
MYINVOIS_SUPPLIER_NAME=My Company Sdn Bhd
MYINVOIS_SUPPLIER_TIN=C12345678900
MYINVOIS_SUPPLIER_ID_TYPE=BRN # BRN for companies, NRIC for individuals
MYINVOIS_SUPPLIER_ID_VALUE=202301234567⚠️ Important: Never commit your actual TIN, certificates, or API credentials to version control.
Testing
Unit tests are deterministic and need no environment — certificates are generated on the fly:
bun run test:run # unit tests (test/unit)The live sandbox integration suite (test/integration) signs and submits a
real invoice to the LHDN preprod API. It requires the .env variables above
and is gated behind an explicit flag:
bun run test:integration # sets RUN_INTEGRATION=trueGenerating Test Certificates for Development
For testing purposes, you can generate self-signed certificates with MyInvois-required fields:
1. Create Certificate Configuration
Create myinvois-cert.conf:
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = MY
ST = YOUR_STATE
L = YOUR_CITY
O = YOUR_COMPANY_NAME
CN = YOUR_COMPANY_NAME
emailAddress = YOUR_EMAIL
serialNumber = YOUR_BUSINESS_REGISTRATION_NUMBER
[v3_req]
keyUsage = keyEncipherment, dataEncipherment, digitalSignature, nonRepudiation
extendedKeyUsage = clientAuth, emailProtection
subjectAltName = @alt_names
[alt_names]
email.1 = YOUR_EMAIL
DNS.1 = YOUR_DOMAIN2. Generate Private Key
openssl genrsa -out myinvois-test-key.pem 20483. Generate Certificate with Required MyInvois Fields
# Basic certificate with SERIALNUMBER
openssl req -new -x509 -key myinvois-test-key.pem -out myinvois-test-cert.pem -days 365 -config myinvois-cert.conf
# Enhanced certificate with organizationIdentifier (OI) field for TIN
openssl req -new -x509 -key myinvois-test-key.pem -out myinvois-enhanced-cert.pem -days 365 \
-subj "/C=MY/ST=YOUR_STATE/L=YOUR_CITY/O=YOUR_COMPANY_NAME/CN=YOUR_COMPANY_NAME/serialNumber=YOUR_BUSINESS_REG/2.5.4.97=YOUR_TIN/emailAddress=YOUR_EMAIL"4. Verify Certificate Fields
# Check certificate details
openssl x509 -in myinvois-enhanced-cert.pem -text -noout | grep -A 2 -B 2 "Subject:"
# Should show both:
# serialNumber=YOUR_BUSINESS_REG
# organizationIdentifier=YOUR_TINExample Values
Replace the placeholders with your actual values:
YOUR_STATE:Kuala LumpurYOUR_CITY:Kuala LumpurYOUR_COMPANY_NAME:My Company Sdn BhdYOUR_EMAIL:[email protected]YOUR_BUSINESS_REG:202301234567(SSM registration)YOUR_TIN:IG12345678901(MyInvois TIN)YOUR_DOMAIN:mycompany.com
Rate Limiting
The client includes a built-in, per-clientId request queue that proactively
keeps each API category within the MyInvois-documented limits (e.g. submit
documents 100/min, get submission 300/min, cancel/reject 12/min). Requests that
would exceed a category's limit are queued and released as the sliding window
frees up — you can fire calls freely and the queue paces them.
Known limitations
These are intentional trade-offs for a dependency-free, in-process limiter. Plan around them for production deployments:
- In-process only. Limits are tracked in memory per Node.js process. If you run multiple instances/workers (clustered NestJS, PM2 cluster, serverless, multiple pods), each process tracks its own counts, so the combined traffic can exceed the MyInvois limit. For horizontally-scaled deployments, throttle upstream or route MyInvois calls through a single worker.
- Bursts within the cap. The limiter allows up to
maxrequests in the same instant (as long as the 60s window allows it). This is within the documented limit, but if you observe429s under heavy concurrency, reduce upstream concurrency. - No automatic
429/Retry-Afterback-off. The limiter is proactive only; it does not currently inspect rate-limit response headers and retry. - Login is not queued. Token requests (
/connect/token) bypass the queue. Cache and reuse the client so token refreshes stay infrequent.
Production Certificates
⚠️ For production use, obtain official business certificates from MyInvois-approved Certificate Authorities:
- MSC Trustgate: https://www.msctrustgate.com/
- Digicert Sdn Bhd
- Cybersign Asia
The certificate must include:
organizationIdentifierfield with your TINSERIALNUMBERfield with your business registration number- Issued by a MyInvois-trusted CA
This project was created using bun init in bun v1.2.10. Bun is a fast all-in-one JavaScript runtime.
License
MIT — see LICENSE.
