npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

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

Readme

ZATCA Phase 2 QR Code Generator 🇸🇦

npm version TypeScript Node.js License

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

🚀 Quick Start

# Install the package
npm install zatca-qr-generator
import { 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-generator

Requirements

  • 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

  1. Register with ZATCA

  2. 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 | base64

Step 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 | base64

Step 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.pem

Step 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

  1. Never hardcode credentials - Always use environment variables
  2. Rotate certificates regularly - Follow ZATCA's certificate lifecycle
  3. Use HSM for production - Consider Hardware Security Modules for storing private keys
  4. Limit access - Only authorized services should access credentials
  5. 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:

  1. TLV Encoding: All data is encoded using Tag-Length-Value format
  2. Required Fields: All 9 tags (1-9) are included as per ZATCA specifications
  3. Base64 Encoding: Final QR data is Base64 encoded
  4. Digital Signature: Support for ECDSA cryptographic signatures with secp256k1 curve
  5. 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 example

Project 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.md

Scripts

# 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.ts

The examples will output:

  • Generated QR code Data URLs
  • Sample usage for different scenarios
  • Error handling demonstrations

Testing with ZATCA Validator

  1. Generate a QR code using this library
  2. Visit ZATCA QR Code Validator
  3. Scan or upload your generated QR code
  4. Verify that all fields are correctly parsed

Validate with ZATCA

  1. Generate a QR code using this library
  2. Visit ZATCA QR Code Validator
  3. Scan or upload your generated QR code
  4. Verify that all fields are correctly parsed

🤝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. 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:

  1. Obtain Real Certificates: Complete ZATCA onboarding and get production certificates
  2. Secure Credential Storage: Use environment variables or secret management systems
  3. Use HTTPS: Always transmit certificates and keys over secure connections
  4. Implement Rate Limiting: Protect your QR generation endpoints
  5. Log Generations: Maintain audit logs for compliance
  6. Test Thoroughly: Validate with ZATCA's official tools before going live
  7. 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" >> .gitignore

Using 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 0

Issue: "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 short

Issue: "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 -dates

Issue: 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:

📞 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 URL
  • generateTLVString() - 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.