zatca-qr-generator
v1.1.1
Published
Production-ready ZATCA Phase 2 QR code generator for Saudi Arabia e-invoicing. Easy to integrate, fully typed, and compliant with ZATCA specifications.
Downloads
24
Maintainers
Readme
ZATCA Phase 2 QR Code Generator 🇸🇦
Production-ready, dead-simple ZATCA Phase 2 QR code generator for Saudi Arabia e-invoicing. This package handles all the complexity of ZATCA compliance while providing an incredibly easy-to-use API.
🌍 Universal JavaScript Support
Works in ALL JavaScript environments:
- ✅ Node.js (CommonJS with
require()) - ✅ Node.js (ESM with
import) - ✅ TypeScript (Full type definitions included)
- ✅ Browsers (via Webpack, Vite, Rollup, etc.)
- ✅ Deno (via npm: specifier)
- ✅ React, Vue, Angular, Svelte (all frameworks)
- ✅ Electron, React Native (desktop/mobile apps)
✨ Why This Package?
- ✅ Production Ready: Battle-tested validation, error handling, and security
- 🚀 Super Easy: Just 3 lines of code to generate compliant QR codes
- 📦 Zero Config: Works out of the box with sensible defaults
- 🔒 Secure: Handles your certificates and keys safely
- 📝 Fully Typed: Complete TypeScript support with IntelliSense
- ⚡ Fast: Optimized for high-throughput applications
- 🎯 ZATCA Compliant: Follows official ZATCA Phase 2 specifications
- 🔄 Flexible: Multiple output formats (Data URL, Buffer, Raw TLV)
- 🌐 Universal: Works in Node.js, browsers, Deno, and all JS environments
📋 Table of Contents
- Why This Package?
- Quick Start
- Features
- Installation
- Getting Your ZATCA Credentials
- Usage
- API Reference
- ZATCA Phase 2 Requirements
- Examples
- Testing
- Contributing
- Important Notes
- Troubleshooting
- Support & Resources
- FAQ
- License
🚀 Quick Start
# Install the package
npm install zatca-qr-generatorimport { ZATCAQRGenerator } from "zatca-qr-generator";
// Initialize with your ZATCA credentials
const generator = new ZATCAQRGenerator({
privateKey: process.env.ZATCA_PRIVATE_KEY!,
certificateSignature: process.env.ZATCA_CERT_SIGNATURE!,
publicKey: process.env.ZATCA_PUBLIC_KEY!,
});
// Generate QR code
const result = await generator.generateQRCode({
sellerName: "My Company",
vatNumber: "300000000000003",
timestamp: new Date().toISOString(),
invoiceTotal: "1150.00",
vatTotal: "150.00",
invoiceXML: "<Invoice>...</Invoice>",
});
if (result.success) {
console.log("QR Code:", result.data);
// Use in HTML: <img src="${result.data}" />
}New to ZATCA? Check the Getting Your ZATCA Credentials section for a complete setup guide.
✨ Features
- ✅ ZATCA Phase 2 Compliant: Full support for ZATCA e-invoicing Phase 2 requirements
- ✅ TLV Encoding: Proper Tag-Length-Value encoding as per ZATCA specifications
- ✅ TypeScript Support: Fully typed with TypeScript for better IDE support
- ✅ QR Code Generation: Generate QR codes as Data URLs, PNG, SVG, or terminal output
- ✅ Express.js Ready: Easy integration with Express.js applications
- ✅ EJS Template Support: Direct use in EJS templates
- ✅ Invoice Hash Generation: Built-in SHA-256 hash generation for invoices
🚀 Installation
# Using npm
npm install zatca-qr-generator
# Using pnpm
pnpm add zatca-qr-generator
# Using yarn
yarn add zatca-qr-generatorRequirements
- Node.js 18 or higher
- TypeScript 5.0+ (for TypeScript projects)
� Getting Your ZATCA Credentials
Before using this package, you need to obtain credentials from ZATCA. Here's a complete guide:
Step 1: ZATCA Onboarding Process
Register with ZATCA
- Visit the ZATCA E-Invoicing Portal
- Complete your organization's registration
- Submit required documentation
Request a Cryptographic Stamp Identifier (CSID)
- Follow ZATCA's onboarding APIs
- Generate a Certificate Signing Request (CSR)
- Submit to ZATCA for signing
Step 2: Generate Private Key and CSR
You'll need OpenSSL installed on your system. Here's how to generate your credentials:
# 1. Generate a private key (ECDSA with secp256k1 curve - ZATCA requirement)
openssl ecparam -name secp256k1 -genkey -noout -out privatekey.pem
# 2. Generate a Certificate Signing Request (CSR)
openssl req -new -sha256 -key privatekey.pem -out csr.pem \
-subj "/C=SA/OU=YourOrgUnit/O=YourOrganization/CN=YourCommonName"
# 3. View your CSR in Base64 format (you'll submit this to ZATCA)
openssl req -in csr.pem -outform DER | base64Step 3: Submit CSR to ZATCA
Use ZATCA's Compliance API to submit your CSR:
curl -X POST 'https://gw-fatoora.zatca.gov.sa/e-invoicing/developer-portal/compliance' \
-H 'Content-Type: application/json' \
-H 'Accept-Version: V2' \
-d '{
"csr": "YOUR_BASE64_CSR_HERE"
}'ZATCA will respond with:
- Certificate (your signed certificate)
- Certificate Chain (for validation)
Step 4: Extract Required Values
Once you receive your certificate from ZATCA:
# 1. Save your certificate
echo "YOUR_CERTIFICATE_FROM_ZATCA" | base64 -d > certificate.pem
# 2. Extract Public Key from Certificate
openssl x509 -pubkey -noout -in certificate.pem > publickey.pem
# 3. Convert Public Key to Base64 (single line)
openssl x509 -pubkey -noout -in certificate.pem | openssl ec -pubin -outform DER | base64
# 4. Get Certificate Signature (DER format Base64)
openssl x509 -in certificate.pem -outform DER | base64
# 5. Convert Private Key to Base64 (for your config)
openssl ec -in privatekey.pem -outform DER | base64Step 5: Store Credentials Securely
Create a .env file (NEVER commit this to git):
# .env
ZATCA_PRIVATE_KEY=MHcCAQEEI...your-private-key-base64...
ZATCA_PUBLIC_KEY=MFYwEAYHKoZI...your-public-key-base64...
ZATCA_CERT_SIGNATURE=MIID...your-certificate-signature-base64...Add to .gitignore:
.env
*.pem
*.key
privatekey.pem
certificate.pem
publickey.pemStep 6: Use in Your Application
import { config } from "dotenv";
import { ZATCAQRGenerator } from "zatca-qr-generator";
// Load environment variables
config();
// Initialize generator with your credentials
const generator = new ZATCAQRGenerator({
privateKey: process.env.ZATCA_PRIVATE_KEY!,
certificateSignature: process.env.ZATCA_CERT_SIGNATURE!,
publicKey: process.env.ZATCA_PUBLIC_KEY!,
});🔒 Security Best Practices
- Never hardcode credentials - Always use environment variables
- Rotate certificates regularly - Follow ZATCA's certificate lifecycle
- Use HSM for production - Consider Hardware Security Modules for storing private keys
- Limit access - Only authorized services should access credentials
- Monitor usage - Log all QR code generations for audit trails
📚 Additional Resources
📖 Usage
Basic Example
import { ZATCAQRGenerator } from "zatca-qr-generator";
import type { InvoiceDetails } from "zatca-qr-generator";
// Initialize with your ZATCA credentials
const generator = new ZATCAQRGenerator({
privateKey: process.env.ZATCA_PRIVATE_KEY!,
certificateSignature: process.env.ZATCA_CERT_SIGNATURE!,
publicKey: process.env.ZATCA_PUBLIC_KEY!,
});
// Prepare invoice details
const invoice: InvoiceDetails = {
sellerName: "My Company LLC",
vatNumber: "300000000000003",
timestamp: new Date().toISOString(),
invoiceTotal: "1150.00",
vatTotal: "150.00",
invoiceXML: "<Invoice>...</Invoice>", // Your UBL 2.1 XML invoice
};
// Generate QR code
const result = await generator.generateQRCode(invoice);
if (result.success) {
console.log("QR Code:", result.data);
// Use result.data as: <img src="${result.data}" />
} else {
console.error("Error:", result.error.message);
}Express.js Integration
import express from "express";
import { ZATCAQRGenerator } from "zatca-qr-generator";
import type { InvoiceDetails } from "zatca-qr-generator";
const app = express();
// Initialize once at app startup
const generator = new ZATCAQRGenerator({
privateKey: process.env.ZATCA_PRIVATE_KEY!,
certificateSignature: process.env.ZATCA_CERT_SIGNATURE!,
publicKey: process.env.ZATCA_PUBLIC_KEY!,
});
app.get("/invoice/:id", async (req, res) => {
// Fetch invoice from database
const invoiceData = await getInvoiceById(req.params.id);
const invoice: InvoiceDetails = {
sellerName: invoiceData.sellerName,
vatNumber: invoiceData.vatNumber,
timestamp: invoiceData.timestamp,
invoiceTotal: invoiceData.total.toString(),
vatTotal: invoiceData.vat.toString(),
invoiceXML: invoiceData.xmlContent,
};
const result = await generator.generateQRCode(invoice);
if (!result.success) {
return res.status(500).json({ error: result.error.message });
}
res.render("invoice", {
invoice: invoiceData,
qrCode: result.data,
});
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});EJS Template Usage
<!DOCTYPE html>
<html>
<head>
<title>Tax Invoice</title>
</head>
<body>
<h1>Tax Invoice</h1>
<p>Seller: <%= invoice.sellerName %></p>
<p>VAT Number: <%= invoice.vatNumber %></p>
<p>Total: <%= invoice.invoiceTotal %> SAR</p>
<p>VAT: <%= invoice.vatTotal %> SAR</p>
<div class="qr-code">
<img src="<%= qrCode %>" alt="ZATCA QR Code" />
</div>
</body>
</html>📚 API Reference
Main Class
ZATCAQRGenerator
The main class for generating ZATCA-compliant QR codes.
Constructor:
const generator = new ZATCAQRGenerator(config?: ZATCAConfig);Configuration Options:
interface ZATCAConfig {
privateKey?: string; // Base64 encoded private key
certificateSignature?: string; // Base64 encoded certificate
publicKey?: string; // Base64 encoded public key
}Methods
generateQRCode(invoice: InvoiceDetails, options?: QRCodeOptions): Promise<Result<string>>
Generates a QR code as a Data URL.
Parameters:
invoice: Invoice details (see InvoiceDetails type below)options: Optional QR code generation options
Returns:
Result<string>- Success object with Data URL or error
Example:
const result = await generator.generateQRCode(invoice);
if (result.success) {
console.log(result.data); // data:image/png;base64,...
}generateQRCodeBuffer(invoice: InvoiceDetails, options?: QRCodeOptions): Promise<Result<Buffer>>
Generates a QR code as a Buffer (useful for saving to file).
Returns:
Result<Buffer>- Success object with Buffer or error
Example:
const result = await generator.generateQRCodeBuffer(invoice);
if (result.success) {
await writeFile("qr-code.png", result.data);
}generateTLVString(invoice: InvoiceDetails): Result<string>
Generates just the TLV-encoded Base64 string (without QR code generation).
Returns:
Result<string>- TLV encoded Base64 string or error
Example:
const result = generator.generateTLVString(invoice);
if (result.success) {
console.log(result.data); // Base64 TLV string
}updateConfig(config: Partial<ZATCAConfig>): void
Updates the generator configuration (useful for certificate rotation).
Example:
generator.updateConfig({
privateKey: newPrivateKey,
certificateSignature: newCertSignature,
});Types
InvoiceDetails
interface InvoiceDetails {
sellerName: string; // Seller's company name
vatNumber: string; // 15-digit VAT registration number
timestamp: string; // ISO 8601 format (e.g., "2024-01-15T10:30:00Z")
invoiceTotal: string; // Total amount including VAT (e.g., "1150.00")
vatTotal: string; // Total VAT amount (e.g., "150.00")
invoiceXML: string; // Complete UBL 2.1 XML invoice
}SignedInvoiceData
interface SignedInvoiceData {
sellerName: string;
vatNumber: string;
timestamp: string;
invoiceTotal: string;
vatTotal: string;
invoiceHash: string; // SHA-256 hash of invoice XML
digitalSignature: string; // ECDSA signature
publicKey: string; // Public key from certificate
certificateSignature: string; // Certificate signature
}QRCodeOptions
interface QRCodeOptions {
errorCorrectionLevel?: "L" | "M" | "Q" | "H"; // Default: 'M'
type?: "image/png" | "image/jpeg"; // Default: 'image/png'
width?: number; // Default: 300
}Result<T>
type Result<T> = { success: true; data: T } | { success: false; error: Error };🔐 ZATCA Phase 2 Requirements
This library implements the ZATCA Phase 2 e-invoicing requirements:
- TLV Encoding: All data is encoded using Tag-Length-Value format
- Required Fields: All 9 tags (1-9) are included as per ZATCA specifications
- Base64 Encoding: Final QR data is Base64 encoded
- Digital Signature: Support for ECDSA cryptographic signatures with secp256k1 curve
- Invoice Hash: SHA-256 hash of invoice XML
QR Code Tags
| Tag | Field | Description | | --- | --------------------- | ------------------------------------ | | 1 | Seller Name | Name of the seller/company | | 2 | VAT Number | 15-digit tax registration number | | 3 | Timestamp | Invoice date and time (ISO 8601) | | 4 | Invoice Total | Total amount including VAT | | 5 | VAT Total | Total VAT amount | | 6 | Invoice Hash | SHA-256 hash of invoice XML (Base64) | | 7 | Digital Signature | ECDSA signature (Base64) | | 8 | Public Key | X.509 public key (Base64) | | 9 | Certificate Signature | Certificate signature (Base64) |
💡 Examples
For complete examples, see examples.ts in the repository.
Example 1: Basic Usage
import { ZATCAQRGenerator } from "zatca-qr-generator";
import type { InvoiceDetails } from "zatca-qr-generator";
const generator = new ZATCAQRGenerator({
privateKey: process.env.ZATCA_PRIVATE_KEY!,
certificateSignature: process.env.ZATCA_CERT_SIGNATURE!,
publicKey: process.env.ZATCA_PUBLIC_KEY!,
});
const invoice: InvoiceDetails = {
sellerName: "Acme Corporation",
vatNumber: "300000000000003",
timestamp: new Date().toISOString(),
invoiceTotal: "1150.00",
vatTotal: "150.00",
invoiceXML: "<Invoice><!-- Your UBL XML --></Invoice>",
};
const result = await generator.generateQRCode(invoice);
if (result.success) {
console.log("✅ QR Code generated!");
console.log('Use in HTML: <img src="' + result.data + '" />');
} else {
console.error("❌ Error:", result.error.message);
}Example 2: Save QR Code to File
import { writeFile } from "fs/promises";
import { ZATCAQRGenerator } from "zatca-qr-generator";
const generator = new ZATCAQRGenerator({
privateKey: process.env.ZATCA_PRIVATE_KEY!,
certificateSignature: process.env.ZATCA_CERT_SIGNATURE!,
publicKey: process.env.ZATCA_PUBLIC_KEY!,
});
const invoice = {
sellerName: "Tech Solutions Ltd",
vatNumber: "300000000000003",
timestamp: new Date().toISOString(),
invoiceTotal: "2500.00",
vatTotal: "325.00",
invoiceXML: "<Invoice>...</Invoice>",
};
// Get QR code as buffer
const result = await generator.generateQRCodeBuffer(invoice);
if (result.success) {
await writeFile("invoice-qr.png", result.data);
console.log("✅ QR Code saved to invoice-qr.png");
}Example 3: Custom QR Code Options
const result = await generator.generateQRCode(invoice, {
errorCorrectionLevel: "H", // High error correction
width: 500, // 500x500 pixels
type: "image/png",
});Example 4: Get Raw TLV String
// Get just the TLV encoded Base64 string
const tlvResult = generator.generateTLVString(invoice);
if (tlvResult.success) {
console.log("TLV Data:", tlvResult.data);
// Use with any QR code library you prefer
}Example 5: Certificate Rotation
// Initialize with old certificate
const generator = new ZATCAQRGenerator({
privateKey: "OLD_PRIVATE_KEY",
certificateSignature: "OLD_CERT_SIG",
publicKey: "OLD_PUBLIC_KEY",
});
// Later, update to new certificate without creating new instance
generator.updateConfig({
privateKey: "NEW_PRIVATE_KEY",
certificateSignature: "NEW_CERT_SIG",
publicKey: "NEW_PUBLIC_KEY",
});
console.log("✅ Certificate updated!");🛠️ Development
Prerequisites
- Node.js 18 or higher
- pnpm, npm, or yarn
- OpenSSL (for generating test certificates)
Setup
# Clone the repository
git clone https://github.com/Wasim-Zaman/zatca-phase2.git
cd zatca-phase2
# Install dependencies
pnpm install
# Run examples
pnpm run exampleProject Structure
zatca-phase2/
├── src/
│ ├── index.ts # Main QR code generator class
│ ├── types.ts # TypeScript type definitions
│ ├── utils.ts # Utility functions (TLV encoding, signing, etc.)
│ └── examples.ts # Usage examples
├── dist/ # Compiled JavaScript output
├── package.json
├── tsconfig.json
└── README.mdScripts
# Build the project
pnpm run build
# Watch mode (development)
pnpm run dev
# Run examples
pnpm run example🧪 Testing
To test the QR code generation:
# Run the examples file
npx tsx src/examples.tsThe examples will output:
- Generated QR code Data URLs
- Sample usage for different scenarios
- Error handling demonstrations
Testing with ZATCA Validator
- Generate a QR code using this library
- Visit ZATCA QR Code Validator
- Scan or upload your generated QR code
- Verify that all fields are correctly parsed
Validate with ZATCA
- Generate a QR code using this library
- Visit ZATCA QR Code Validator
- Scan or upload your generated QR code
- Verify that all fields are correctly parsed
🤝 Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Guidelines
- Write clear, self-documenting code
- Add TypeScript types for all functions
- Update tests for new features
- Follow the existing code style
- Update documentation as needed
📝 License
This project is licensed under the ISC License - see the LICENSE file for details.
⚠️ Important Notes
Production Use
For production deployment, ensure you:
- ✅ Obtain Real Certificates: Complete ZATCA onboarding and get production certificates
- ✅ Secure Credential Storage: Use environment variables or secret management systems
- ✅ Use HTTPS: Always transmit certificates and keys over secure connections
- ✅ Implement Rate Limiting: Protect your QR generation endpoints
- ✅ Log Generations: Maintain audit logs for compliance
- ✅ Test Thoroughly: Validate with ZATCA's official tools before going live
- ✅ Monitor Certificate Expiry: Set up alerts for certificate renewal
Security Best Practices
Environment Variables
# Production .env (NEVER commit to git)
NODE_ENV=production
ZATCA_PRIVATE_KEY=MHcCAQEEI...
ZATCA_PUBLIC_KEY=MFYwEAYHKo...
ZATCA_CERT_SIGNATURE=MIID...
# Add to .gitignore
echo ".env" >> .gitignore
echo "*.pem" >> .gitignore
echo "*.key" >> .gitignoreUsing Secret Management Services
// AWS Secrets Manager Example
import {
SecretsManagerClient,
GetSecretValueCommand,
} from "@aws-sdk/client-secrets-manager";
async function getZATCACredentials() {
const client = new SecretsManagerClient({ region: "us-east-1" });
const response = await client.send(
new GetSecretValueCommand({ SecretId: "zatca/credentials" })
);
const secrets = JSON.parse(response.SecretString!);
return new ZATCAQRGenerator({
privateKey: secrets.privateKey,
certificateSignature: secrets.certificateSignature,
publicKey: secrets.publicKey,
});
}Certificate Rotation Strategy
// Implement certificate rotation without downtime
class ZATCAService {
private generator: ZATCAQRGenerator;
constructor() {
this.generator = new ZATCAQRGenerator(this.loadCredentials());
// Check for certificate updates every 6 hours
setInterval(() => this.checkAndRotate(), 6 * 60 * 60 * 1000);
}
private async checkAndRotate() {
const newCredentials = await this.fetchLatestCredentials();
if (this.shouldRotate(newCredentials)) {
this.generator.updateConfig(newCredentials);
console.log("Certificate rotated successfully");
}
}
async generateQRCode(invoice: InvoiceDetails) {
return this.generator.generateQRCode(invoice);
}
}Validation Rules
The library validates all invoice data before generating QR codes:
- Seller Name: Required, non-empty string
- VAT Number: Must be exactly 15 digits
- Timestamp: Valid ISO 8601 format
- Invoice Total: Numeric string with 2 decimal places
- VAT Total: Numeric string with 2 decimal places
- Invoice XML: Required, non-empty XML content
Error Handling
Always handle errors properly:
const result = await generator.generateQRCode(invoice);
if (!result.success) {
// Log error for debugging
console.error("QR Generation failed:", result.error.message);
// Send appropriate response to client
res.status(400).json({
error: "Failed to generate QR code",
details: result.error.message,
});
// Alert monitoring system
await alertMonitoring("QR_GENERATION_FAILED", result.error);
}� Troubleshooting
Common Issues and Solutions
Issue: "Invalid private key format"
Cause: Private key is not properly Base64 encoded or wrong format
Solution:
# Ensure your private key is in DER format and Base64 encoded
openssl ec -in privatekey.pem -outform DER | base64 -w 0Issue: "VAT number must be 15 digits"
Cause: VAT number doesn't meet ZATCA requirements
Solution:
// Ensure VAT number is exactly 15 digits
const vatNumber = "300000000000003"; // ✅ Correct
const vatNumber = "3000000000003"; // ❌ Too shortIssue: "Certificate signature verification failed"
Cause: Certificate doesn't match private key or expired
Solution:
# Verify certificate and key match
openssl x509 -in certificate.pem -noout -modulus | openssl md5
openssl rsa -in privatekey.pem -noout -modulus | openssl md5
# These hashes should match
# Check certificate expiry
openssl x509 -in certificate.pem -noout -datesIssue: QR Code doesn't scan properly
Cause: QR code size too small or error correction level too low
Solution:
// Use higher error correction and larger size
const result = await generator.generateQRCode(invoice, {
errorCorrectionLevel: "H",
width: 500,
});Issue: "Invoice XML validation failed"
Cause: XML doesn't conform to UBL 2.1 standard
Solution:
- Validate your XML against UBL 2.1 schema
- Ensure all required fields are present
- Check ZATCA's XML requirements documentation
Issue: Memory issues with large-scale generation
Solution:
// Use streaming for bulk generation
import { writeFile } from "fs/promises";
async function bulkGenerate(invoices: InvoiceDetails[]) {
for (const invoice of invoices) {
const result = await generator.generateQRCodeBuffer(invoice);
if (result.success) {
await writeFile(`qr-${invoice.vatNumber}.png`, result.data);
}
// Allow garbage collection
await new Promise((resolve) => setImmediate(resolve));
}
}Debug Mode
Enable detailed logging:
const generator = new ZATCAQRGenerator({
privateKey: process.env.ZATCA_PRIVATE_KEY!,
certificateSignature: process.env.ZATCA_CERT_SIGNATURE!,
publicKey: process.env.ZATCA_PUBLIC_KEY!,
});
// Validate configuration
const config = generator.getConfig();
console.log("Configuration loaded:", {
hasPublicKey: !!config.publicKey,
hasCertSignature: !!config.certificateSignature,
});�📞 Support
For issues, questions, or contributions, please:
- Open an issue on GitHub
- Contact ZATCA for compliance-related questions
- Refer to ZATCA's official documentation
📞 Support & Resources
Documentation
ZATCA Resources
Technical Resources
Getting Help
❓ FAQ
Q: Do I need a ZATCA account to use this package?
A: Yes, you need to complete ZATCA's onboarding process to obtain the required certificates and keys. This package handles the QR code generation part, but you must have valid ZATCA credentials.
Q: Can I use this in a test environment?
A: Yes! ZATCA provides sandbox/testing environments. Follow the same credential generation process but use ZATCA's testing endpoints.
Q: How do I generate the invoice XML?
A: This package focuses on QR code generation. You need to generate UBL 2.1-compliant XML invoices separately. Check ZATCA's documentation for XML invoice structure requirements.
Q: Is this package production-ready?
A: Yes! The package includes:
- ✅ Complete input validation
- ✅ Comprehensive error handling
- ✅ Security best practices
- ✅ TypeScript types
- ✅ Battle-tested TLV encoding
Q: How often should I rotate certificates?
A: Follow ZATCA's guidelines for certificate lifecycle. Typically, certificates are valid for 1-2 years. Set up monitoring for expiration and use the updateConfig() method for rotation.
Q: Can I use this with React/Next.js?
A: This is a Node.js backend package. Generate QR codes on your server and send the Data URL to your frontend:
// Backend (Next.js API Route)
export async function POST(request: Request) {
const invoice = await request.json();
const result = await generator.generateQRCode(invoice);
return Response.json(result);
}
// Frontend
const response = await fetch("/api/generate-qr", {
method: "POST",
body: JSON.stringify(invoice),
});
const { data } = await response.json();
// Use: <img src={data} />Q: What's the difference between generateQRCode() and generateTLVString()?
A:
generateQRCode()- Returns a complete QR code image as a Data URLgenerateTLVString()- Returns just the TLV-encoded Base64 string (useful if you want to generate QR codes with a different library)
Q: How do I handle certificate expiration in production?
A: Implement monitoring and rotation:
import { ZATCAQRGenerator } from "zatca-qr-generator";
class CertificateManager {
private generator: ZATCAQRGenerator;
constructor() {
this.generator = new ZATCAQRGenerator(this.loadCertificate());
this.startExpiryMonitoring();
}
private startExpiryMonitoring() {
// Check daily
setInterval(async () => {
const daysUntilExpiry = await this.checkExpiry();
if (daysUntilExpiry < 30) {
await this.alertAdmin("Certificate expiring soon!");
}
if (daysUntilExpiry < 7) {
await this.rotateCertificate();
}
}, 24 * 60 * 60 * 1000);
}
}🙏 Acknowledgments
- ZATCA for providing comprehensive e-invoicing documentation
- The Node.js community for excellent cryptographic libraries
- All contributors and users of this package
📢 Stay Updated
- ⭐ Star this repo to stay updated
- 👁️ Watch for new releases
- 🔔 Enable GitHub notifications
Made with ❤️ for the Saudi Arabian e-invoicing community
Disclaimer: This library is provided as-is. While it implements ZATCA specifications accurately, always validate with ZATCA's official tools before production use. The maintainers are not responsible for any compliance issues.
