saudivatpro-ecommerce-sdk
v1.0.0
Published
Official Node.js SDK for Saudi VAT Pro — turn e-commerce orders into ZATCA Phase 2 compliant invoices in a few lines of code. No XML, digital signing, or CSID handling required; the hosted platform does it for you.
Downloads
153
Maintainers
Readme
saudivatpro-ecommerce-sdk
The official Node.js SDK for Saudi VAT Pro — turn e-commerce orders into ZATCA Phase 2 compliant invoices in a few lines of code.
You never touch ZATCA crypto or onboarding. Every other "ZATCA Phase 2" package on npm (zatca-sdk, zatca-phase2, @talha7k/zatca, and similar) hands you raw UBL XML generation and XAdES signing — you still have to run your own CSID onboarding, manage signing keys, and submit to ZATCA's Fatoora portal yourself. Real compliance risk sits with you.
This SDK is different: it's a thin, typed client for Saudi VAT Pro's already-live, production-tested compliance engine. You send order data over HTTPS with your API key; Saudi VAT Pro generates the UBL 2.1 XML, signs it with your ZATCA-issued certificate, submits it for clearance or reporting, and hands back the invoice, its QR code, and a PDF. No XML, no CSID, no crypto in your process.
Today WooCommerce and Magento stores already get this through Saudi VAT Pro's PHP plugins. This SDK is the same hosted compliance engine, for any Node.js backend — a Shopify custom app, a Salla/Zid integration, or a fully custom cart.
Install
npm install saudivatpro-ecommerce-sdkRequires Node.js 18+ (uses the global fetch and node:crypto).
Quick start
import { SaudiVatProClient } from "saudivatpro-ecommerce-sdk";
const client = new SaudiVatProClient({
apiKey: process.env.SAUDI_VAT_PRO_API_KEY!, // from saudivatpro.com/developers
});
// Order placed in your store -> ZATCA-compliant invoice out.
const invoice = await client.submitOrder({
orderReference: order.id, // your own ID; also the idempotency key
buyerName: order.customerName,
buyerVatNumber: order.customerVatNumber, // omit for B2C / simplified invoices
lines: order.items.map((item) => ({
name: item.title,
quantity: item.quantity,
unitPrice: item.unitPrice, // SAR, excluding VAT
vatRate: 15,
})),
});
console.log(invoice.status); // "pending" | "generated" | "signed" | "reported" | "cleared" | "failed" | "needs_data"
console.log(invoice.qrCode); // base64 ZATCA QR code, once generatedThat's the whole integration. Ten lines, no ZATCA knowledge required.
Get your API key
Create a Saudi VAT Pro account and generate an API key at saudivatpro.com/developers. Use a live key in production; the same account also lets you create test/demo keys for development.
Checking invoice status
submitOrder returns immediately with the invoice in whatever state the pipeline has reached so far; clearance/reporting can take a moment. Poll or use webhooks:
const invoice = await client.getInvoice(invoiceId);
if (invoice.status === "cleared" || invoice.status === "reported") {
// done — invoice.qrCode is ready
} else if (invoice.status === "failed") {
console.error(invoice.zatcaMessage);
await client.retryInvoice(invoiceId); // fix the underlying issue first if it's a data problem
}List and filter
const { invoices, total } = await client.listInvoices({
status: "failed",
page: 1,
pageSize: 20,
});Webhooks (recommended over polling)
Register an HTTPS endpoint in the Saudi VAT Pro dashboard (Developers → Webhooks). Saudi VAT Pro POSTs to it on invoice.cleared, invoice.reported, and invoice.failed, signing every request with HMAC-SHA256 over the raw body.
import express from "express";
import { verifyWebhookSignature, parseWebhookEvent, WEBHOOK_SIGNATURE_HEADER } from "saudivatpro-ecommerce-sdk";
const app = express();
// IMPORTANT: verify against the raw body, before any JSON-parsing middleware runs.
app.post("/webhooks/saudi-vat-pro", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.header(WEBHOOK_SIGNATURE_HEADER);
const valid = verifyWebhookSignature({
payload: req.body, // Buffer, from express.raw()
signature,
secret: process.env.SAUDI_VAT_PRO_WEBHOOK_SECRET!, // shown once when the endpoint was created
});
if (!valid) return res.status(401).end();
const event = parseWebhookEvent(req.body);
// event.event: "invoice.cleared" | "invoice.reported" | "invoice.failed"
// event.invoiceId / event.invoiceNumber / event.status / event.zatcaUuid?
res.status(200).end();
});Credit notes (full and partial refunds)
// Full refund — omit `lines`, every original line is credited.
await client.issueCreditNote(invoiceId, {
reasonCode: "cancellation",
note: "Customer cancelled the order",
idempotencyKey: `refund-${order.id}-full`, // always send one; repeats are safe
});
// Partial refund — only the positive-amount lines being refunded.
await client.issueCreditNote(invoiceId, {
reasonCode: "return",
note: "One item returned",
idempotencyKey: `refund-${order.id}-${returnId}`,
lines: [{ name: "Premium Hoodie", quantity: 1, unitPrice: 149, vatRate: 15 }],
});Cumulative credit notes can never exceed the original invoice's gross total. A rejected request throws a SaudiVatProApiError with body.remainingRefundable set to the amount still available.
XML and PDF
const xml = await client.getInvoiceXml(invoiceId); // signed UBL 2.1 XML
const pdfBuffer = await client.getInvoicePdf(invoiceId); // ArrayBuffer, QR code embeddedError handling
Every non-2xx response throws a SaudiVatProApiError:
import { SaudiVatProApiError } from "saudivatpro-ecommerce-sdk";
try {
await client.submitOrder(order);
} catch (err) {
if (err instanceof SaudiVatProApiError) {
console.error(err.status, err.code, err.message, err.body);
} else {
throw err;
}
}What this SDK is not
It is not a standalone ZATCA compliance engine — it has no UBL XML generation, no XAdES signing, and no CSID/certificate handling. All of that runs inside Saudi VAT Pro. If you need a bare crypto/XML library instead of a hosted service, this package is not for you.
API reference
| Method | Endpoint | Purpose |
|---|---|---|
| submitOrder(order) | POST /v1/orders | Submit a new order for automatic invoicing (idempotent on orderReference) |
| listInvoices(params?) | GET /invoices | List/filter/paginate invoices |
| getInvoice(id) | GET /invoices/{id} | Full invoice detail: lines, event timeline, linked credit/debit notes |
| createInvoice(body) | POST /invoices | Manually create an invoice outside order intake |
| updateInvoice(id, body) | PATCH /invoices/{id} | Fix buyer data on a failed/needs-data invoice |
| getInvoiceXml(id) | GET /invoices/{id}/xml | Signed UBL 2.1 XML |
| getInvoicePdf(id) | GET /invoices/{id}/pdf | PDF with embedded QR code |
| retryInvoice(id) | POST /invoices/{id}/retry | Re-run the pipeline / resubmit to ZATCA |
| issueCreditNote(id, body?) | POST /invoices/{id}/credit-note | Full or partial refund (type 381) |
| issueDebitNote(id, body) | POST /invoices/{id}/debit-note | Additional charge against a cleared/reported invoice (type 383) |
| verifyWebhookSignature(opts) | — | Verify the X-SaudiVatPro-Signature header on inbound webhooks |
| parseWebhookEvent(payload) | — | Type-narrow a verified webhook body |
Full request/response types are exported from the package — see Invoice, InvoiceDetail, OrderIntakeRequest, CreditNoteRequest, WebhookPayload, and others.
Links
- Platform: saudivatpro.com
- API docs: saudivatpro.com/developers
- WooCommerce plugin, Magento extension, ZATCA QR toolkit, MCP server: github.com/Morouna1/SaudiVatPro
License
MIT
