PHPackages                             developeritsme/fiscal-service - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. developeritsme/fiscal-service

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

developeritsme/fiscal-service
=============================

Montenegro Fiscal Service

0.9.1(2mo ago)0481↓86.7%MITPHPPHP ^8.0

Since May 22Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/developeritsme/fiscal-service)[ Packagist](https://packagist.org/packages/developeritsme/fiscal-service)[ RSS](/packages/developeritsme-fiscal-service/feed)WikiDiscussions master Synced today

READMEChangelog (10)Dependencies (8)Versions (14)Used By (0)

Montenegro Fiscal Service
=========================

[](#montenegro-fiscal-service)

PHP library for integrating with Montenegro's fiscal service (Poreska Uprava). Handles invoice registration, cash deposits, and terminal code (TCR) management.

Requirements
------------

[](#requirements)

- PHP 8.0 or higher
- Extensions: `xmlwriter`, `dom`, `openssl`, `curl`
- PKCS12 certificate from Montenegro Tax Authority

Installation
------------

[](#installation)

```
composer require developeritsme/fiscal-service
```

Usage
-----

[](#usage)

### Initialize the Fiscal Service

[](#initialize-the-fiscal-service)

```
use DeveloperItsMe\FiscalService\Fiscal;
use DeveloperItsMe\FiscalService\Certificate;

// From file (production)
$fiscal = Fiscal::fromFile('/path/to/certificate.pfx', 'certificate-password');

// From file (test environment)
$fiscal = Fiscal::fromFile('/path/to/certificate.pfx', 'certificate-password', true);

// From raw PKCS12 content
$fiscal = Fiscal::fromContent($pkcs12Content, 'certificate-password', true);

// From a pre-built Certificate instance
$certificate = Certificate::fromFile('/path/to/certificate.pfx', 'certificate-password');
$fiscal = Fiscal::fromCertificate($certificate, true);

// Check certificate expiration date
$expiresAt = $fiscal->certificate()->expiresAt(); // Returns DateTimeImmutable
```

### Register an Invoice

[](#register-an-invoice)

```
use DeveloperItsMe\FiscalService\Models\Invoice;
use DeveloperItsMe\FiscalService\Models\Item;
use DeveloperItsMe\FiscalService\Models\Seller;
use DeveloperItsMe\FiscalService\Models\Buyer;
use DeveloperItsMe\FiscalService\Models\PaymentMethod;
use DeveloperItsMe\FiscalService\Requests\RegisterInvoice;

// Create seller
$seller = new Seller('Company Name', '12345678', true); // name, TIN, isVAT
$seller->setAddress('Street Address')
    ->setTown('Podgorica');

// Create invoice (parameter: decimal precision for items — 2, 3, or 4; default 2)
$invoice = (new Invoice(4))
    ->setNumber(1)
    ->setEnu('ab123cd456')           // TCR code (format: xx000xx000)
    ->setBusinessUnitCode('bu123bu123') // format: xx000xx000
    ->setSoftwareCode('sw123sw456')     // format: xx000xx000
    ->setOperatorCode('op123op789')     // format: xx000xx000
    ->setSeller($seller);

// Add buyer (optional, required for non-cash invoices)
$buyer = new Buyer('Buyer Name', '87654321');
$buyer->setAddress('Buyer Street')
    ->setTown('Budva')
    ->setCountry('MNE');
$invoice->setBuyer($buyer);

// Add items
$item = (new Item())
    ->setName('Product Name')
    ->setCode('1234567890123') // barcode/SKU (optional)
    ->setUnit('pcs')
    ->setQuantity(2)
    ->setUnitPrice(10.00)
    ->setVatRate(21); // 0, 7, 15, or 21

$invoice->addItem($item);

// Add payment method
$total = 20.00;
$invoice->addPaymentMethod(new PaymentMethod($total, PaymentMethod::TYPE_BANKNOTE));

// Send to fiscal service
$request = new RegisterInvoice($invoice);
$response = $fiscal->request($request)->send();

if ($response->ok()) {
    $data = $response->data();
    // $data['ikof']   - Invoice identification code (IIC)
    // $data['jikr']   - Fiscal identification code (FIC)
    // $data['url']    - QR code verification URL
    // $data['number'] - Invoice number
}

// Access raw request/response XML
$xmlRequest = $response->request();
$xmlResponse = $response->body();
```

### Register a TCR (Terminal/Cash Register)

[](#register-a-tcr-terminalcash-register)

Register a TCR once through your application. Once registered, you receive an ENU code that can be used for all subsequent invoice requests.

```
use DeveloperItsMe\FiscalService\Models\BusinessUnit;
use DeveloperItsMe\FiscalService\Requests\RegisterTCR;

$businessUnit = (new BusinessUnit())
    ->setIdNumber('12345678')
    ->setUnitCode('bu123bu123')       // format: xx000xx000
    ->setSoftwareCode('sw123sw456')   // format: xx000xx000
    ->setMaintainerCode('mt123mt456') // format: xx000xx000
    ->setInternalId('internal-1');

$request = new RegisterTCR($businessUnit);
$response = $fiscal->request($request)->send();

if ($response->ok()) {
    $data = $response->data();
    // $data['code'] - Registered TCR code (ENU)
}
```

### Register a Cash Deposit

[](#register-a-cash-deposit)

Must be registered once per day before sending invoices.

```
use DeveloperItsMe\FiscalService\Models\CashDeposit;
use DeveloperItsMe\FiscalService\Requests\RegisterCashDeposit;
use Carbon\Carbon;

$cashDeposit = (new CashDeposit())
    ->setDate(Carbon::now())
    ->setIdNumber('12345678')
    ->setAmount(0) // initial amount in register
    ->setEnu('TCR-CODE')
    ->setOperation(CashDeposit::OPERATION_INITIAL);

$request = new RegisterCashDeposit($cashDeposit);
$response = $fiscal->request($request)->send();

if ($response->ok()) {
    $data = $response->data();
    // $data['fcdc'] - Fiscal cash deposit code
}
```

### Corrective Invoice

[](#corrective-invoice)

To correct a previously sent invoice:

```
use DeveloperItsMe\FiscalService\Models\CorrectiveInvoice;

// Reference the original invoice
$corrective = new CorrectiveInvoice(
    $originalIkof,           // IKOF of original invoice
    $originalDateTime        // DateTime of original invoice
);

$invoice = (new Invoice())
    // ... set other invoice properties
    ->setCorrectiveInvoice($corrective);

// Add items with negative quantities to reverse
$item = (new Item())
    ->setName('Product Name')
    ->setQuantity(-1) // negative to reverse
    ->setUnitPrice(10.00)
    ->setVatRate(21);

$invoice->addItem($item);
```

### Supply and Tax Periods

[](#supply-and-tax-periods)

For invoices covering a specific period:

```
// Supply period (for services/goods delivered over time)
$invoice->setSupplyPeriod('2025-01-01', '2025-01-31');

// Or single date
$invoice->setSupplyPeriod('2025-01-15');

// Tax period (format: MM/YYYY)
$invoice->setTaxPeriod('01/2025');
```

### Invoice Types

[](#invoice-types)

```
// Cash vs Non-cash (TypeOfInv)
$invoice->setMethod(Invoice::TYPE_CASH);     // Default
$invoice->setMethod(Invoice::TYPE_NONCASH);

// Invoice types (InvType)
$invoice->setInvoiceType(Invoice::TYPE_INVOICE);      // Regular invoice
$invoice->setInvoiceType(Invoice::TYPE_CORRECTIVE);   // Corrective invoice
$invoice->setInvoiceType(Invoice::TYPE_ADVANCE);      // Advance payment
$invoice->setInvoiceType(Invoice::TYPE_CREDIT_NOTE);  // Credit note
```

### Payment Methods

[](#payment-methods)

Payment methods are restricted by invoice method. Using a disallowed type throws `InvalidArgumentException`.

**Cash invoices** (`TYPE_CASH`):

```
PaymentMethod::TYPE_BANKNOTE    // Cash
PaymentMethod::TYPE_CARD        // Card payment
PaymentMethod::TYPE_ORDER       // Order/Check
PaymentMethod::TYPE_OTHER_CASH  // Other cash
```

**Non-cash invoices** (`TYPE_NONCASH`):

```
PaymentMethod::TYPE_BUSINESS_CARD // Business card
PaymentMethod::TYPE_VOUCHER       // Voucher
PaymentMethod::TYPE_COMPANY       // Company card
PaymentMethod::TYPE_ORDER         // Order/Check
PaymentMethod::TYPE_ADVANCE       // Advance payment
PaymentMethod::TYPE_ACCOUNT       // Bank transfer
PaymentMethod::TYPE_FACTORING     // Factoring
PaymentMethod::TYPE_OTHER         // Other
```

`TYPE_ADVANCE` and `TYPE_VOUCHER` require `setAdvIIC()`, and `TYPE_COMPANY` requires `setCompCard()`:

```
$pm = (new PaymentMethod(100, PaymentMethod::TYPE_ADVANCE))
    ->setAdvIIC('aabb0011ccdd2233eeff44556677aabb');

$pm = (new PaymentMethod(100, PaymentMethod::TYPE_COMPANY))
    ->setCompCard('COMP-CARD-123');
```

### Item Rebates and VAT Exemptions

[](#item-rebates-and-vat-exemptions)

```
// Rebate (discount percentage, reduces base price by default)
$item = (new Item('Product', 21))
    ->setUnitPrice(100)
    ->setRebate(10);        // 10% discount, reduces base price

// Or: rebate that does not reduce base price
$item->setRebate(10, false);

// VAT exemption (only valid when VAT rate is 0)
$item = (new Item('Exempt Product', 0))
    ->setUnitPrice(50)
    ->setExemptFromVAT(Item::EXEMPT_CL17); // VAT_CL17 through VAT_CL44
```

### Additional Invoice Fields

[](#additional-invoice-fields)

```
// Payment deadline (format: YYYY-MM-DD)
$invoice->setPayDeadline('2025-12-31');

// Bank account number (max 50 characters)
$invoice->setBankAccNum('550-12332-44');

// Note (max 200 characters)
$invoice->setNote('Please pay within 30 days');
```

### Subsequent Delivery

[](#subsequent-delivery)

For invoices sent after the fact (e.g. no internet at time of sale):

```
$invoice->setSubsequentDeliveryType('NOINTERNET');
// Also: BOUNDBOOK, SERVICE, TECHNICALERROR, BUSINESSNEEDS

// Works on cash deposits too
$cashDeposit->setSubsequentDeliveryType('TECHNICALERROR');
```

### IIC References (Advance Invoices)

[](#iic-references-advance-invoices)

Reference previous advance invoices when issuing the final invoice:

```
use DeveloperItsMe\FiscalService\Models\IICRef;

$invoice->addIICRef(new IICRef(
    'aabb0011ccdd2233eeff44556677aabb', // IIC of the advance invoice
    '2025-01-15T10:00:00+01:00',        // Issue date/time
    100.00                               // Amount (optional)
));
```

### Error Handling

[](#error-handling)

All exceptions extend `FiscalException`, so you can catch broadly or specifically.

```
use DeveloperItsMe\FiscalService\Exceptions\FiscalException;
use DeveloperItsMe\FiscalService\Exceptions\CertificateException;
use DeveloperItsMe\FiscalService\Exceptions\InvalidArgumentException;
use DeveloperItsMe\FiscalService\Exceptions\ValidationException;

// InvalidArgumentException — thrown immediately by setters on invalid input
try {
    $invoice->setNumber(0);          // must be > 0
    $invoice->setMethod('INVALID');  // must be CASH or NONCASH
} catch (InvalidArgumentException $e) {
    // $e->getMessage()
}

// ValidationException — thrown by validate() or toArray() for missing/invalid fields
try {
    $invoice->validate();
} catch (ValidationException $e) {
    $e->getErrors();    // ['field' => ['message', ...], ...]
    $e->getMessages();  // ['field: message', ...]
}

// CertificateException — invalid certificate or wrong password
try {
    $fiscal = Fiscal::fromFile('/path/to/cert.pfx', 'password');
} catch (CertificateException $e) {
    $errors = $e->getOpensslErrors();
}

// Response errors — fiscal service rejection or connection failure
$response = $fiscal->request($request)->send();

if ($response->failed()) {
    $error = $response->error();   // Error message (includes connection errors like timeouts)
    $errors = $response->errors(); // Detailed errors array
}
```

### Array Serialization

[](#array-serialization)

All models support `toArray()` for inspection, logging, or JSON encoding. Validation runs automatically before serialization.

```
$arr = $invoice->toArray();
// ['uuid' => '...', 'number' => 'bu001/1/2025/en001', 'seller' => [...], 'items' => [...], ...]

json_encode($invoice->toArray());
```

### Request Timeouts

[](#request-timeouts)

```
$request = new RegisterInvoice($invoice);
$request->timeout(60);         // Total timeout in seconds (default: 30)
$request->connect_timeout(15); // Connection timeout in seconds (default: 10)
```

License
-------

[](#license)

MIT

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance87

Actively maintained with recent releases

Popularity12

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~150 days

Recently: every ~17 days

Total

13

Last Release

65d ago

PHP version history (2 changes)0.1.0-alphaPHP ^7.3|^8.0

0.5.0PHP ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/3b958247c4209402873f204ce8924dd9c818f6b3d6872441235531276557489a?d=identicon)[developeritsme](/maintainers/developeritsme)

---

Top Contributors

[![developeritsme](https://avatars.githubusercontent.com/u/1728050?v=4)](https://github.com/developeritsme "developeritsme (85 commits)")

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/developeritsme-fiscal-service/health.svg)

```
[![Health](https://phpackages.com/badges/developeritsme-fiscal-service/health.svg)](https://phpackages.com/packages/developeritsme-fiscal-service)
```

###  Alternatives

[illuminate/support

The Illuminate Support package.

630113.0M41.3k](/packages/illuminate-support)[spatie/holidays

Calculate public holidays

402860.1k2](/packages/spatie-holidays)[craftcms/feed-me

Import content from XML, RSS, CSV or JSON feeds into entries, categories, Craft Commerce products, and more.

293952.6k33](/packages/craftcms-feed-me)[solspace/craft-freeform

The most flexible and user-friendly form building plugin!

54681.3k18](/packages/solspace-craft-freeform)[pimcore/data-importer

Adds a comprehensive import functionality to Pimcore Datahub

46855.5k5](/packages/pimcore-data-importer)[flarum/core

Delightfully simple forum software.

201.4M2.3k](/packages/flarum-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
