Për zhvilluesit
SDK për sistemin e faturave elektronike të UJP-së. Nënshkrim, kontroll, llogaritje e totaleve dhe dërgim nga softueri juaj, i njëjti kod që përdor aplikacioni.
Fillim i shpejtë
- Siguroni qasje në mjedisin e testimit të e-Faktura të UJP-së: EDB, eUJP ID dhe certifikatë e kualifikuar.
- Shtoni SDK-në për gjuhën tuaj nga repo-ja, nga shtegu në tabelën më poshtë.
- Hapni një nënshkrues (token USB, skedar .p12 ose macOS Keychain) dhe krijoni një klient për mjedisin e testimit.
- Dërgoni një dokument dhe kontrolloni EUID-in në përgjigje.
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);SDK sipas gjuhës
SDK-të janë me pagesë, ndaj nuk janë në regjistrat publikë (crates.io, npm, PyPI e të tjerë). Instalohen nga repo-ja, që e merrni kur i blini.
| Gjuha | Paketa | Instalimi | Statusi |
|---|---|---|---|
| Rust | efakt-sdk, efakt-signer | nga repo-ja (crates/efakt-sdk, crates/efakt-signer) | I disponueshëm |
| TypeScript | @rid/efakt | nga repo-ja (sdks/typescript) | I disponueshëm |
| Python | efakt | nga repo-ja (sdks/python) | I disponueshëm |
| PHP | rid/efakt | nga repo-ja (sdks/php) | I disponueshëm |
| .NET | Rid.Efakt | nga repo-ja (sdks/dotnet) | I disponueshëm |
| Java | mk.rid:efakt | nga repo-ja (sdks/java) | I disponueshëm |
| Go | github.com/rid/efakt-go | nga repo-ja (sdks/go) | I disponueshëm |
Nënshkrimi
Çdo kërkesë drejt UJP-së që dërgon ose ndryshon një dokument nënshkruhet si JWS kompakt me RS256 (RSASSA-PKCS1-v1_5 me SHA-256). Koka është saktësisht {"alg":"RS256","typ":"JWT"}, si në shembujt e UJP-së, ndërsa payload-i është JSON-i i dokumentit së bashku me requestTimestamp.
Çelësi mund të jetë në tri vende:
- PKCS#11: token USB ose kartë inteligjente. SDK-ja i njeh shtigjet e zakonshme të moduleve (SafeNet/Thales, OpenSC, SoftHSM). Tokenët dhe certifikatat mund të listohen pa PIN, ndërsa certifikata zgjidhet sipas slotit dhe
CKA_ID. - PKCS#12: skedar
.p12ose.pfx, nga disku ose nga memoria. - macOS Keychain: vetëm në macOS, përfshirë tokenët përmes CryptoTokenKit. PIN-in atëherë e kërkon vetë macOS.
Çelësi privat nuk e lë kurrë tokenin ose skedarin. PIN-i mbahet vetëm në memorie dhe fshihet pas përdorimit. Baza lokale (efakt.db, SQLite, e përbashkët për aplikacionin dhe CLI-në) nuk përmban sekrete.
Me kërkesën shkojnë edhe kokat X-EUJP-ID, X-EDB dhe X-SERIAL-NUMBER. Numri serik i certifikatës mund të dërgohet në heksadecimal, heksadecimal me shkronja të vogla ose decimal, ose të vendoset me dorë nëse UJP nuk e njeh.
requestTimestamp është në kohën lokale të Shkupit, në formatin yyyy-MM-ddTHH:mm:ss. UJP pranon devijim prej ±5 minutash, prandaj SDK-ja para dërgimit e sinkronizon orën me kohën e serverit të UJP-së.
Para dërgimit të parë mund të bëni një nënshkrim provë: nënshkrim dhe verifikim lokal, pa rrjet. SDK-ja e kontrollon certifikatën edhe lokalisht ashtu si UJP: nëse ka skaduar ose nuk vlen ende (E5002, E5004), nëse ka çelës RSA prej të paktën 2048 bitësh (E5004, E5005), nëse është certifikatë e lëshuesit (CA) në vend të asaj personale, dhe nëse EDB-ja në certifikatë përputhet me atë të konfiguruar (E1013, E4023). Revokimin e certifikatës (OCSP/CRL) e kontrollon vetëm UJP.
Kontrolli sipas strukturës së UJP-së
Para dërgimit dokumenti kontrollohet sipas strukturës zyrtare JSON të UJP-së dhe faqeve për çdo lloj dokumenti. Rregullat janë gjeneruar nga wiki: formati i çdo fushe (gjatësia e tekstit, data YYYY-MM-DD, decimale, numër i plotë, vlerë logjike) dhe fushat që janë të detyrueshme pa kusht për çdo lloj dokumenti.
Fushat e detyrueshme me kusht (të shënuara me З* në wiki, për shembull ato që varen nga TVSH-ja, rezidenca ose referencat) nuk kërkohen lokalisht. Ato i kontrollon UJP gjatë dërgimit. Fushat në artikuj, referenca dhe adresë kontrollohen kur ajo pjesë ekziston.
Kërkesat për një lloj dokumenti mund t’i lexoni edhe drejtpërdrejt nga UJP (document_type_requirements).
Llogaritja e totaleve
SDK-ja llogarit artikujt, totalet dhe rekapitulimin e TVSH-së sipas rregullave të UJP-së:
- artikujt llogariten me 4 decimale dhe nuk rrumbullakohen në 2;
- totalet mblidhen pa rrumbullakim, pastaj rrumbullakohen në 2 decimale (gjysma lart);
docGrossAmountRështë numër i plotë në denarë (ose pa valutë), ndërsa në valutë të huaj mbetet me 2 decimale (rregull i UJP-së nga 19.10.2026);- në denarë
docAvansAmount,docFinalAmount,docGrossAmountR,docAmountIncreasedhedocAmountDecreaseduhet të jenë numra të plotë, përndryshe kontrolli kthen gabimin UJP08-08 deri UJP08-12; docTotalsdhevatTotalsplotësohen nga artikujt dhe treguesit tatimorë.
Shumat e avansit, të rritjes dhe të zvogëlimit janë vetëm hyrje dhe nuk llogariten. Pas llogaritjes, kontrolli konfirmon që shuma e vatTaxableAmount dhe docNetAmountDisc + docVatAmount përputhen me totalet brenda tolerancës së UJP-së prej 0,1.
Dërgimi
Dokumenti dërgohet në JSONReceiver/api/v1/sales-invoices/send, me llojin e dokumentit në kokën X-DOC-TYPE-CODE. E gjithë rrjedha është një thirrje (send_checked): llogaritje, kontroll, sinkronizim i orës, requestTimestamp i ri, nënshkrim dhe dërgim. Nëse kontrolli gjen gabim, asgjë nuk dërgohet.
Pas dërgimit të suksesshëm, përgjigjja përmban EUID-in (numrin që UJP i cakton dokumentit), mesazhin dhe statusin, nëse blerësi e përdor sistemin, kohën e pranimit dhe lidhjen për kodin QR.
Për stornim dhe korrigjim SDK-ja e përgatit dokumentin e ri nga origjinali: stornim me sasi negative, docStorno = 1 dhe referencë „ST“, korrigjim me docStorno = 2 dhe referencë „KR“.
UJP lejon një kërkesë në sekondë për përdorues. SDK-ja pret të paktën 1050 ms mes kërkesave, dhe te përgjigjja 429 provon përsëri sipas Retry-After, më së shumti 3 herë.
Statusi dhe kodet e gabimeve
Përveç dërgimit, SDK-ja mbulon edhe shërbimet e tjera të UJP-së:
- statusin aktual dhe historikun e statuseve të dokumentit, ndryshimet sipas ditës, listat e EUID-ve dhe statuseve për një periudhë;
- PDF-në, JSON-in e nënshkruar dhe arkivin ASiC të dokumentit;
- pranimin dhe refuzimin e dokumenteve hyrëse (refuzimi kërkon kod arsyeje) dhe totalet e TVSH-së për dokumentet hyrëse;
- tatimpaguesin dhe degët sipas EDB-së, shtetet, valutat dhe kursin mesatar të BPRMV-së;
- shifrarët: arsyet për korrigjim, stornim dhe refuzim, mënyrat e pagesës, statuset, llojet e dokumenteve, grupet tatimore dhe treguesit.
UJP lejon periudhë prej më së shumti një muaji, prandaj SDK-ja periudhat më të gjata i ndan në pjesë prej 28 ditësh.
Kodet e gabimeve janë nga lista zyrtare e UJP-së, në tri pjesë: eInvoiceApi, JSON Receiver dhe aplikacioni web. Disa kode kanë kuptim të ndryshëm në secilën pjesë (për shembull E4023), prandaj kodi interpretohet sipas shërbimit që e ktheu. Grupet janë:
| Grupi | Kuptimi |
|---|---|
| E1xxx | gabime biznesi: tatimpaguesi, certifikata, statusi i dokumentit |
| E4xxx | gabime në kërkesë: parametra, koka, format |
| E5xxx | gabime gjatë përpunimit: certifikata, JWS, serveri |
Për çdo kod SDK-ja jep tekstin e UJP-së, nëse lidhet me certifikatën, nëse ia vlen të provohet përsëri dhe një këshillë në maqedonisht. Gabimi nga UJP kthehet me statusin HTTP, kodin, mesazhin dhe përgjigjen e plotë.
Testimi dhe prodhimi
| Mjedisi | eInvoiceApi | JSON Receiver |
|---|---|---|
| Testimi (parazgjedhje) | https://efakturatest.ujp.gov.mk/einvoice_api |
https://efakturatest.ujp.gov.mk/JSONReceiver |
| Prodhimi | https://efaktura.ujp.gov.mk/einvoice_api |
https://efaktura.ujp.gov.mk/JSONReceiver |
Dokumentet e dërguara në mjedisin e testimit nuk kanë efekt juridik. Adresat e prodhimit nuk janë publikuar në dokumentacionin e UJP-së dhe ende nuk janë konfirmuar. Nëse UJP ju jep adresa të tjera, i vendosni si adresa të veta (Custom).
Burimet
- Struktura JSON në wiki-n e UJP-së.
- Wiki i e-Faktura-s së UJP-së.
Nuk e gjetët përgjigjen? Na shkruani