Geliştiriciler için
UJP elektronik fatura sistemi için SDK. Kendi yazılımınızdan imzalama, doğrulama, toplam hesaplama ve gönderme; uygulamanın kullandığı kodun aynısı.
Hızlı başlangıç
- UJP e-Faktura test ortamına erişim edinin: EDB, eUJP kimliği ve nitelikli sertifika.
- Dilinize uygun SDK'yı depodan, aşağıdaki tablodaki yoldan ekleyin.
- Bir imzalayıcı açın (USB token, .p12 dosyası veya macOS Keychain) ve test ortamı için bir istemci oluşturun.
- Bir belge gönderin ve yanıttaki EUID'yi kontrol edin.
import { UjpClient, Pkcs11Signer } from "@rid/efakt";
const signer = await Pkcs11Signer.open({
module: "/usr/lib/opensc-pkcs11.so",
pin,
});
const ujp = new UjpClient({
environment: "test",
edb, eujpId,
signer,
});
const sent = await ujp.sendInvoice(invoice);
console.log(sent.euid, sent.qrLink);from efakt import Environment, Pkcs11Signer, UjpClient
signer = Pkcs11Signer.open(
module="/usr/lib/opensc-pkcs11.so",
pin=pin,
)
ujp = UjpClient(
environment=Environment.TEST,
edb=edb, eujp_id=eujp_id,
signer=signer,
)
sent = ujp.send_invoice(invoice)
print(sent.euid, sent.qr_link)<?php
use Rid\Efakt\{Environment, Pkcs11Signer, UjpClient};
$signer = Pkcs11Signer::open(
module: '/usr/lib/opensc-pkcs11.so',
pin: $pin,
);
$ujp = new UjpClient(
environment: Environment::Test,
edb: $edb, eujpId: $eujpId,
signer: $signer,
);
$sent = $ujp->sendInvoice($invoice);
echo $sent->euid, ' ', $sent->qrLink;using Rid.Efakt;
using Rid.Efakt.Signing;
using var signer = Pkcs11Signer.Open(
module: "/usr/lib/opensc-pkcs11.so",
pin: pin);
var ujp = new UjpClient(new ClientConfig
{
Environment = UjpEnvironment.Test,
Edb = edb, EujpId = eujpId,
Signer = signer,
});
var sent = await ujp.SendInvoiceAsync(invoice);
Console.WriteLine($"{sent.Euid} {sent.QrLink}");import mk.rid.efakt.*;
var signer = Pkcs11Signer.open(
"/usr/lib/opensc-pkcs11.so", pin);
var ujp = UjpClient.builder()
.environment(Environment.TEST)
.edb(edb).eujpId(eujpId)
.signer(signer)
.build();
var sent = ujp.sendInvoice(invoice);
System.out.println(sent.euid() + " " + sent.qrLink());signer, err := efakt.OpenPKCS11(efakt.PKCS11{
Module: "/usr/lib/opensc-pkcs11.so",
PIN: pin,
})
if err != nil {
return err
}
ujp := efakt.NewClient(efakt.Config{
Environment: efakt.Test,
EDB: edb,
EUJPID: eujpID,
Signer: signer,
})
sent, err := ujp.SendInvoice(ctx, invoice)
if err != nil {
return err
}
fmt.Println(sent.EUID, sent.QRLink)let signer = SignerSource::Pkcs11 {
module: "/usr/lib/opensc-pkcs11.so".into(),
slot_id: None,
object_id_hex: None,
}.open(&pin)?;
let ujp = UjpClient::new(ClientConfig {
environment: Environment::Test,
edb, eujp_id,
..Default::default()
})?.with_signer(signer)?;
let sent = ujp.send_invoice(invoice).await?;
println!("{:?} {:?}", sent.euid, sent.qr_link);Dile göre SDK'lar
SDK'lar ücretlidir, bu yüzden genel kayıtlarda (crates.io, npm, PyPI ve diğerleri) yer almaz. Satın alırken verilen depodan kurulur.
| Dil | Paket | Kurulum | Durum |
|---|---|---|---|
| Rust | efakt-sdk, efakt-signer | depodan (crates/efakt-sdk, crates/efakt-signer) | Hazır |
| TypeScript | @rid/efakt | depodan (sdks/typescript) | Hazır |
| Python | efakt | depodan (sdks/python) | Hazır |
| PHP | rid/efakt | depodan (sdks/php) | Hazır |
| .NET | Rid.Efakt | depodan (sdks/dotnet) | Hazır |
| Java | mk.rid:efakt | depodan (sdks/java) | Hazır |
| Go | github.com/rid/efakt-go | depodan (sdks/go) | Hazır |
İmzalama
UJP’ye belge gönderen veya belgeyi değiştiren her istek, RS256 (SHA-256 ile RSASSA-PKCS1-v1_5) kullanan kompakt bir JWS olarak imzalanır. Başlık, UJP örneklerinde olduğu gibi tam olarak {"alg":"RS256","typ":"JWT"} şeklindedir; payload ise belgenin JSON’u ile requestTimestamp alanıdır.
Anahtar üç yerde bulunabilir:
- PKCS#11: USB token veya akıllı kart. SDK, yaygın modül yollarını bilir (SafeNet/Thales, OpenSC, SoftHSM). Token’lar ve sertifikalar PIN olmadan listelenebilir; sertifika, yuva ve
CKA_IDile seçilir. - PKCS#12: diskten veya bellekten bir
.p12ya da.pfxdosyası. - macOS Keychain: yalnızca macOS’ta, CryptoTokenKit üzerinden token’lar dahil. Bu durumda PIN’i macOS kendisi ister.
Özel anahtar token’dan veya dosyadan hiçbir zaman çıkmaz. PIN yalnızca bellekte tutulur ve kullanımdan sonra silinir. Yerel veritabanı (efakt.db, SQLite, uygulama ve CLI için ortak) gizli bilgi içermez.
Her istekle birlikte X-EUJP-ID, X-EDB ve X-SERIAL-NUMBER başlıkları da gönderilir. Sertifikanın seri numarası onaltılık, küçük harfli onaltılık veya ondalık olarak gönderilebilir ya da UJP tanımazsa elle girilebilir.
requestTimestamp, Üsküp yerel saatine göre ve yyyy-MM-ddTHH:mm:ss biçimindedir. UJP ±5 dakikalık sapmayı kabul eder; bu yüzden SDK göndermeden önce saatini UJP sunucusunun saatiyle eşitler.
İlk gönderimden önce bir test imzası yapabilirsiniz: ağ olmadan yerel imzalama ve doğrulama. SDK sertifikayı yerel olarak da UJP’nin yaptığı gibi kontrol eder: süresi dolmuş mu veya henüz geçerli değil mi (E5002, E5004), en az 2048 bitlik bir RSA anahtarı var mı (E5004, E5005), kişisel sertifika yerine bir yayıncı (CA) sertifikası mı, ve sertifikadaki EDB ayarlanan EDB ile eşleşiyor mu (E1013, E4023). Sertifika iptalini (OCSP/CRL) yalnızca UJP kontrol eder.
UJP yapısına göre doğrulama
Göndermeden önce belge, UJP’nin resmi JSON yapısına ve her belge türünün sayfasına göre kontrol edilir. Kurallar wiki’den üretilmiştir: her alanın biçimi (metin uzunluğu, YYYY-MM-DD tarihi, ondalık, tam sayı, mantıksal değer) ve her belge türü için koşulsuz zorunlu olan alanlar.
Koşullu zorunlu alanlar (wiki’de З* ile işaretlenenler, örneğin KDV’ye, yerleşikliğe veya referanslara bağlı olanlar) yerel olarak aranmaz. Bunları gönderim sırasında UJP kontrol eder. Kalemlerdeki, referanslardaki ve adresteki alanlar, o bölüm mevcutsa kontrol edilir.
Bir belge türünün gereksinimlerini doğrudan UJP’den de okuyabilirsiniz (document_type_requirements).
Toplamların hesaplanması
SDK kalemleri, toplamları ve KDV özetini UJP kurallarına göre hesaplar:
- kalemler 4 ondalık basamakla hesaplanır ve 2 basamağa yuvarlanmaz;
- toplamlar yuvarlanmadan toplanır, ardından 2 ondalık basamağa yuvarlanır (yarım yukarı);
docGrossAmountRdenar cinsinden (veya para birimi yoksa) tam sayıdır, yabancı para biriminde 2 ondalık basamakta kalır (19.10.2026 tarihli UJP kuralı);- denar cinsinden
docAvansAmount,docFinalAmount,docGrossAmountR,docAmountIncreasevedocAmountDecreasetam sayı olmalıdır, aksi halde doğrulama UJP08-08 ile UJP08-12 arası hata döndürür; docTotalsvevatTotals, kalemlerden ve vergi göstergelerinden doldurulur.
Avans, artış ve azalış tutarları yalnızca girdidir ve hesaplanmaz. Hesaplamadan sonra doğrulama, vatTaxableAmount toplamının ve docNetAmountDisc + docVatAmount değerinin, UJP’nin 0,1’lik toleransı içinde toplamlarla eşleştiğini teyit eder.
Gönderme
Belge, belge türü X-DOC-TYPE-CODE başlığında olacak şekilde JSONReceiver/api/v1/sales-invoices/send adresine gönderilir. Tüm süreç tek bir çağrıdır (send_checked): hesaplama, doğrulama, saat eşitleme, yeni bir requestTimestamp, imzalama ve gönderme. Doğrulama bir hata bulursa hiçbir şey gönderilmez.
Başarılı bir gönderimden sonra yanıt; EUID’yi (UJP’nin belgeye verdiği numara), mesajı ve durumu, alıcının sistemi kullanıp kullanmadığını, alınma zamanını ve QR kodu bağlantısını içerir.
İptal ve düzeltme için SDK yeni belgeyi orijinalden hazırlar: iptal negatif miktarlarla, docStorno = 1 ve „ST“ referansıyla; düzeltme docStorno = 2 ve „KR“ referansıyla.
UJP, kullanıcı başına saniyede bir isteğe izin verir. SDK istekler arasında en az 1050 ms bekler; 429 yanıtında Retry-After değerine göre en fazla 3 kez yeniden dener.
Durum ve hata kodları
SDK, göndermenin yanı sıra UJP’nin diğer hizmetlerini de kapsar:
- belgenin güncel durumu ve durum geçmişi, güne göre değişiklikler, bir dönem için EUID ve durum listeleri;
- belgenin PDF’i, imzalı JSON’u ve ASiC arşivi;
- gelen belgelerin kabulü ve reddi (ret için neden kodu gerekir) ve gelen belgeler için KDV toplamları;
- EDB ile mükellef ve şubeleri, ülkeler, para birimleri ve NBRM orta kuru;
- kod listeleri: düzeltme, iptal ve ret nedenleri, ödeme yöntemleri, durumlar, belge türleri, vergi grupları ve göstergeler.
UJP en fazla bir aylık döneme izin verir; bu yüzden SDK daha uzun dönemleri 28 günlük parçalara böler.
Hata kodları UJP’nin resmi listesinden alınmıştır ve üç bölümdedir: eInvoiceApi, JSON Receiver ve web uygulaması. Bazı kodlar her bölümde farklı anlama gelir (örneğin E4023); bu yüzden bir kod, onu döndüren hizmete göre yorumlanır. Gruplar şunlardır:
| Grup | Anlamı |
|---|---|
| E1xxx | iş hataları: mükellef, sertifika, belge durumu |
| E4xxx | istek hataları: parametreler, başlıklar, biçim |
| E5xxx | işleme hataları: sertifika, JWS, sunucu |
SDK her kod için UJP metnini, kodun sertifikayla ilgili olup olmadığını, yeniden denemeye değip değmediğini ve Makedonca bir ipucunu verir. UJP’den gelen bir hata; HTTP durumu, kod, mesaj ve yanıtın tamamıyla döner.
Test ve canlı ortam
| Ortam | eInvoiceApi | JSON Receiver |
|---|---|---|
| Test (varsayılan) | https://efakturatest.ujp.gov.mk/einvoice_api |
https://efakturatest.ujp.gov.mk/JSONReceiver |
| Canlı | https://efaktura.ujp.gov.mk/einvoice_api |
https://efaktura.ujp.gov.mk/JSONReceiver |
Test ortamına gönderilen belgelerin hukuki etkisi yoktur. Canlı ortam adresleri UJP belgelerinde yayımlanmamıştır ve henüz doğrulanmamıştır. UJP size başka adresler verirse bunları özel adresler olarak (Custom) ayarlarsınız.
Kaynaklar
- UJP wiki’sinde JSON yapısı.
- UJP e-Faktura wiki’si.
Cevap bulamadınız mı? Bize yazın