Konfiguracija
Detaljna podešavanja za optimalan rad sa eFiskalizacija API-jem.
Podešavanje API kredencijala
API kredencijali se koriste za autentifikaciju svakog zahteva prema eFiskalizacija servisu. Preporučujemo čuvanje u environment varijablama.
Environment varijable (preporučeno)
# .env fajl vašeg web šopa
EFISK_API_KEY=vaš_api_ključ
EFISK_API_SECRET=vaš_tajni_ključ
EFISK_API_URL=https://efiskalizacija.cloud/api/multitenant.php
Konfiguracija po jeziku
Izaberite programski jezik za primer konfiguracije:
<?php
// config/efiskalizacija.php
return [
'api_key' => getenv('EFISK_API_KEY'),
'api_secret' => getenv('EFISK_API_SECRET'),
'api_url' => getenv('EFISK_API_URL') ?: 'https://efiskalizacija.cloud/api/multitenant.php',
'timeout' => 30, // sekundi
'retries' => 3,
];
// Korišćenje
$config = require 'config/efiskalizacija.php';
$client = new EfiskalizacijaClient(
$config['api_key'],
$config['api_secret'],
$config['api_url']
);
# config/efiskalizacija.py
import os
from dataclasses import dataclass
@dataclass
class EfiskConfig:
api_key: str
api_secret: str
api_url: str = 'https://efiskalizacija.cloud/api/multitenant.php'
timeout: int = 30
retries: int = 3
@classmethod
def from_env(cls):
return cls(
api_key=os.environ['EFISK_API_KEY'],
api_secret=os.environ['EFISK_API_SECRET'],
api_url=os.environ.get('EFISK_API_URL', cls.api_url),
)
# Korišćenje
config = EfiskConfig.from_env()
client = EfiskalizacijaClient(config.api_key, config.api_secret, config.api_url)
// config/efiskalizacija.js
require('dotenv').config(); // npm install dotenv
const config = {
apiKey: process.env.EFISK_API_KEY,
apiSecret: process.env.EFISK_API_SECRET,
apiUrl: process.env.EFISK_API_URL || 'https://efiskalizacija.cloud/api/multitenant.php',
timeout: 30000, // milisekundi
retries: 3,
};
// Validacija
if (!config.apiKey || !config.apiSecret) {
throw new Error('EFISK_API_KEY i EFISK_API_SECRET su obavezni');
}
module.exports = config;
// Korišćenje
const config = require('./config/efiskalizacija');
const client = new EfiskalizacijaClient(config.apiKey, config.apiSecret, config.apiUrl);
// appsettings.json
{
"Efiskalizacija": {
"ApiKey": "", // Ili koristi User Secrets / Environment
"ApiSecret": "",
"ApiUrl": "https://efiskalizacija.cloud/api/multitenant.php",
"Timeout": 30,
"Retries": 3
}
}
// EfiskOptions.cs
public class EfiskOptions
{
public string ApiKey { get; set; } = "";
public string ApiSecret { get; set; } = "";
public string ApiUrl { get; set; } = "https://efiskalizacija.cloud/api/multitenant.php";
public int Timeout { get; set; } = 30;
public int Retries { get; set; } = 3;
}
// Program.cs / Startup.cs
builder.Services.Configure<EfiskOptions>(
builder.Configuration.GetSection("Efiskalizacija"));
// Ili iz environment varijabli
builder.Services.AddSingleton(sp => new EfiskalizacijaClient(
Environment.GetEnvironmentVariable("EFISK_API_KEY") ?? "",
Environment.GetEnvironmentVariable("EFISK_API_SECRET") ?? "",
Environment.GetEnvironmentVariable("EFISK_API_URL")
?? "https://efiskalizacija.cloud/api/multitenant.php"
));
// application.yml
efiskalizacija:
api-key: ${EFISK_API_KEY}
api-secret: ${EFISK_API_SECRET}
api-url: https://efiskalizacija.cloud/api/multitenant.php
timeout: 30
retries: 3
// EfiskProperties.java
@Configuration
@ConfigurationProperties(prefix = "efiskalizacija")
public class EfiskProperties {
private String apiKey;
private String apiSecret;
private String apiUrl = "https://efiskalizacija.cloud/api/multitenant.php";
private int timeout = 30;
private int retries = 3;
// Getters i setters
}
// EfiskConfig.java
@Configuration
public class EfiskConfig {
@Bean
public EfiskalizacijaClient efiskClient(EfiskProperties props) {
return new EfiskalizacijaClient(
props.getApiKey(),
props.getApiSecret(),
props.getApiUrl()
);
}
}
// Korišćenje u servisu
@Service
public class FiskalizacijaService {
private final EfiskalizacijaClient client;
public FiskalizacijaService(EfiskalizacijaClient client) {
this.client = client;
}
}
Timeout podešavanja
Preporučeni timeout za API zahteve:
| Operacija | Preporučeni timeout | Obrazloženje |
|---|---|---|
| Fiskalizacija | 30 sekundi | VSDC može biti spor u vršnim časovima |
| Status provera | 10 sekundi | Brz odgovor, nema VSDC poziva |
| PDF preuzimanje | 15 sekundi | Generisanje PDF-a zahteva vreme |
Retry logika
Za povećanje pouzdanosti implementirajte exponential backoff. Izaberite programski jezik:
<?php
function fiskalizujSaRetry(EfiskalizacijaClient $client, array $podaci, int $maxRetries = 3): array
{
$attempt = 0;
$lastException = null;
while ($attempt < $maxRetries) {
try {
return $client->fiskalizuj($podaci);
} catch (RuntimeException $e) {
$lastException = $e;
$httpCode = $e->getCode();
// Ne pokušavaj ponovo za klijentske greške (4xx)
if ($httpCode >= 400 && $httpCode < 500) {
throw $e;
}
$attempt++;
if ($attempt < $maxRetries) {
$delay = pow(2, $attempt); // 2s, 4s, 8s
sleep($delay);
}
}
}
throw $lastException;
}
import time
from requests.exceptions import HTTPError
def fiskalizuj_sa_retry(client, podaci, max_retries=3):
last_exception = None
for attempt in range(max_retries):
try:
return client.fiskalizuj(podaci)
except HTTPError as e:
last_exception = e
status_code = e.response.status_code
# Ne pokušavaj ponovo za klijentske greške (4xx)
if 400 <= status_code < 500:
raise
if attempt < max_retries - 1:
delay = 2 ** (attempt + 1) # 2s, 4s, 8s
time.sleep(delay)
raise last_exception
# Ili koristi tenacity biblioteku
# pip install tenacity
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=8),
retry=retry_if_exception(lambda e: getattr(e, 'status_code', 0) >= 500)
)
def fiskalizuj_sa_tenacity(client, podaci):
return client.fiskalizuj(podaci)
async function fiskalizujSaRetry(client, podaci, maxRetries = 3) {
let lastError = null;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await client.fiskalizuj(podaci);
} catch (error) {
lastError = error;
const statusCode = error.statusCode || error.response?.status || 0;
// Ne pokušavaj ponovo za klijentske greške (4xx)
if (statusCode >= 400 && statusCode < 500) {
throw error;
}
if (attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt + 1) * 1000; // 2s, 4s, 8s
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError;
}
// Korišćenje
try {
const result = await fiskalizujSaRetry(client, podaci);
console.log('PFR:', result.data.rezultat.pfr_broj);
} catch (error) {
console.error('Fiskalizacija neuspešna nakon svih pokušaja:', error.message);
}
// Koristi Polly NuGet paket: dotnet add package Polly
using Polly;
using Polly.Retry;
public class FiskalizacijaService
{
private readonly EfiskalizacijaClient _client;
private readonly AsyncRetryPolicy _retryPolicy;
public FiskalizacijaService(EfiskalizacijaClient client)
{
_client = client;
_retryPolicy = Policy
.Handle<HttpRequestException>(ex =>
{
// Retry samo za serverske greške (5xx)
var statusCode = ExtractStatusCode(ex);
return statusCode >= 500;
})
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: attempt =>
TimeSpan.FromSeconds(Math.Pow(2, attempt)) // 2s, 4s, 8s
);
}
public async Task<JsonDocument> FiskalizujSaRetryAsync(object podaci)
{
return await _retryPolicy.ExecuteAsync(async () =>
{
return await _client.FiskalizujAsync(podaci);
});
}
private int ExtractStatusCode(HttpRequestException ex)
{
// .NET 5+: ex.StatusCode
// Starije verzije: parsiraj iz Message
return (int)(ex.StatusCode ?? 0);
}
}
// build.gradle: implementation 'org.springframework.retry:spring-retry'
// ili pom.xml: spring-retry dependency
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
import org.springframework.web.client.HttpServerErrorException;
@Configuration
@EnableRetry
public class RetryConfig {}
@Service
public class FiskalizacijaService {
private final EfiskalizacijaClient client;
public FiskalizacijaService(EfiskalizacijaClient client) {
this.client = client;
}
@Retryable(
value = HttpServerErrorException.class, // Retry samo za 5xx
maxAttempts = 3,
backoff = @Backoff(delay = 2000, multiplier = 2) // 2s, 4s, 8s
)
public JsonNode fiskalizujSaRetry(Map<String, Object> podaci) throws Exception {
return client.fiskalizuj(podaci);
}
// Ili ručna implementacija
public JsonNode fiskalizujSaRetryManual(Map<String, Object> podaci, int maxRetries)
throws Exception {
Exception lastException = null;
for (int attempt = 0; attempt < maxRetries; attempt++) {
try {
return client.fiskalizuj(podaci);
} catch (HttpServerErrorException e) {
lastException = e;
if (attempt < maxRetries - 1) {
long delay = (long) Math.pow(2, attempt + 1) * 1000;
Thread.sleep(delay);
}
}
}
throw lastException;
}
}
Kada pokušati ponovo
| HTTP kôd | Retry? | Objašnjenje |
|---|---|---|
| 400 | Ne | Neispravan zahtev - ispravite parametre |
| 401 | Ne | Pogrešan potpis - proverite kredencijale |
| 429 | Da | Rate limit - sačekajte pre ponovnog pokušaja |
| 500 | Da | Serverska greška - privremeni problem |
| 503 | Da | VSDC nedostupan - pokušajte za 30s |
Podešavanja u admin panelu
Osim API konfiguracije, u admin panelu možete podesiti:
PDF podešavanja
- Primarna boja - HEX vrednost za zaglavlje (npr.
#667eea) - Logo - slika preduzeća za zaglavlje PDF-a
Štampač
- 80mm - standardni termalni štampač (podrazumevano)
- 58mm - mali termalni štampač (skalira sadržaj na 70%)
Email notifikacije
Konfiguracija SMTP-a za slanje računa emailom. Podešava se u admin panelu
ili putem environment varijable MAIL_DSN.
# Format MAIL_DSN:
MAIL_DSN=smtp://korisnik%40domen.rs:lozinka@smtp.server.rs:587
CORS podešavanja
Ako pozivate API direktno iz pregledača (frontend JavaScript), potrebno je podesiti CORS:
# Dozvoljeni origin-i za vaš domen
Access-Control-Allow-Origin: https://vaš-web-šop.rs
Access-Control-Allow-Headers: X-Api-Key, X-Timestamp, X-Signature, Content-Type
Access-Control-Allow-Methods: GET, POST, OPTIONS
Security headers
Naš API automatski vraća sledeće bezbednosne headere:
| Header | Vrednost |
|---|---|
X-Content-Type-Options | nosniff |
X-Frame-Options | DENY |
X-Request-Id | Jedinstveni ID zahteva (za debug) |
Strict-Transport-Security | max-age=31536000 |
X-Request-Id header iz odgovora za praćenje zahteva
prilikom komunikacije sa našom tehničkom podrškom.
Za početno podešavanje pogledajte vodič za instalaciju ili vodič za integraciju za kompletne primere kôda.