المسار
- يحصّل نظام الفوترة لديك المبلغ من العميل عبر مزود الدفع لديك، وبقواعدك أنت.
- عند النجاح تستدعي مهمة في الخلفية
POST /invoicesمع معرّف الدفعة فيexternal_reference. - حين يصبح
zatca.deliverableمساويًاtrueأرسلpublic_urlمع بريد الإيصال. - للاسترداد أو تغيير الباقة بعد الإصدار، أنشئ إشعارًا دائنًا عبر
POST /invoices/{id}/notes(أو من لوحة تحكم وندرفاتورة).
ربط الاشتراك بحقول الفاتورة
| بياناتك | حقل الواجهة |
|---|---|
| معرّف الدفعة أو فترة الفوترة | external_reference |
| تاريخ الدفع | issue_date |
| الباقة والفترة | items[].description، مثل "باقة Pro، نوفمبر 2026" |
| السعر قبل الضريبة | items[].unit_price |
| قسيمة خصم على الطلب كله | document_discount_type وdocument_discount_value |
| العميل | customer.id (يُحفظ بعد أول فاتورة) أو بيانات مباشرة |
| العملة | currency وexchange_rate |
العملاء من المنشآت والمستهلكون
العميل المنشأة الذي له رقم ضريبي سعودي صالح يحصل على فاتورة ضريبية تعتمدها الهيئة قبل أن ترسلها. أنشئ هؤلاء العملاء مرة واحدة عبر POST /customers (أو من لوحة التحكم) بعنوانهم الوطني الكامل وأعد استخدام المعرّف. أما المستهلك، أو العميل بلا رقم ضريبي سعودي، فيحصل على فاتورة مبسطة ترسلها فورًا.
العملاء خارج المملكة
اجعل customer.country رمز دولتهم. الخدمات المقدمة لعميل خارج المملكة كثيرًا ما تخضع للنسبة الصفرية، وعندها أرسل vat_rate: 0 مع vat_exemption_code: "VATEX-SA-33". أما هل تنطبق النسبة الصفرية على توريدك فسؤال لمستشارك الضريبي.
{
"issue_date": "2026-11-03",
"currency": "USD",
"exchange_rate": 3.75,
"customer": { "name": "Example Inc.", "country": "US" },
"items": [{
"description": "Software licence (export of services)",
"quantity": 1,
"unit_price": 400,
"vat_rate": 0,
"vat_exemption_code": "VATEX-SA-33"
}]
}
لا تفوتر مرتين أبدًا
مهام الفوترة تعيد المحاولة. ومع external_reference يساوي معرّف الدفعة، تتلقى المهمة المعادة الفاتورة التي أنشأتها المحاولة الأولى (200، "replayed": true).
import os, time, uuid
import requests
API = "https://fatoora.tw.sa/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['WONDERFATOORA_API_KEY']}",
"Accept": "application/json"}
def create_invoice(order):
payload = {
# Your own order id: a retry after a timeout returns the invoice
# the first attempt created instead of issuing a second one.
"external_reference": order["id"],
"issue_date": order["date"],
"items": order["items"],
}
for attempt in range(5):
try:
res = requests.post(f"{API}/invoices", json=payload, headers=HEADERS, timeout=60)
except requests.RequestException:
time.sleep(2 ** attempt) # network error: same payload, try again
continue
if res.status_code == 429:
time.sleep(int(res.headers.get("Retry-After", "5")))
continue
if res.status_code >= 500:
time.sleep(2 ** attempt)
continue
return res.json() # 200/201, or a 4xx to fix, not retry
raise RuntimeError("WonderFatoora API unavailable, will retry later")
async function createInvoice(order) {
const payload = {
external_reference: order.id, // safe to retry
issue_date: order.date,
items: order.items,
};
for (let attempt = 0; attempt < 5; attempt++) {
let res;
try {
res = await fetch("https://fatoora.tw.sa/api/v1/invoices", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WONDERFATOORA_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(payload),
signal: AbortSignal.timeout(60_000),
});
} catch {
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
continue;
}
if (res.status === 429) {
await new Promise((r) => setTimeout(r, Number(res.headers.get("Retry-After") ?? 5) * 1000));
continue;
}
if (res.status >= 500) {
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
continue;
}
return res.json(); // 200/201, or a 4xx to fix rather than retry
}
throw new Error("WonderFatoora API unavailable, retry later");
}
صفحات ذات صلة
جاهز للبدء؟
الوصول إلى الواجهة البرمجية ضمن باقات: الأعمال والمؤسسات. أنشئ مفتاحًا من الإعدادات ← مفاتيح API وأرسل أول طلب خلال دقائق.