eFakt™
Shqip

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ë

  1. Siguroni qasje në mjedisin e testimit të e-Faktura të UJP-së: EDB, eUJP ID dhe certifikatë e kualifikuar.
  2. Shtoni SDK-në për gjuhën tuaj nga repo-ja, nga shtegu në tabelën më poshtë.
  3. Hapni një nënshkrues (token USB, skedar .p12 ose macOS Keychain) dhe krijoni një klient për mjedisin e testimit.
  4. Dërgoni një dokument dhe kontrolloni EUID-in në përgjigje.
Dërgimi i faturës me token USB
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.

GjuhaPaketaInstalimiStatusi
Rustefakt-sdk, efakt-signernga repo-ja (crates/efakt-sdk, crates/efakt-signer)I disponueshëm
TypeScript@rid/efaktnga repo-ja (sdks/typescript)I disponueshëm
Pythonefaktnga repo-ja (sdks/python)I disponueshëm
PHPrid/efaktnga repo-ja (sdks/php)I disponueshëm
.NETRid.Efaktnga repo-ja (sdks/dotnet)I disponueshëm
Javamk.rid:efaktnga repo-ja (sdks/java)I disponueshëm
Gogithub.com/rid/efakt-gonga 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 .p12 ose .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, docAmountIncrease dhe docAmountDecrease duhet të jenë numra të plotë, përndryshe kontrolli kthen gabimin UJP08-08 deri UJP08-12;
  • docTotals dhe vatTotals plotë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

Nuk e gjetët përgjigjen? Na shkruani