digitaldev-lx/laravel-moloni

A Laravel package for integrating with the Moloni invoicing API

Maintainers

Package info

github.com/digitaldev-lx/laravel-moloni

pkg:composer/digitaldev-lx/laravel-moloni

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-04-01 09:30 UTC

This package is auto-updated.

Last update: 2026-04-01 09:45:30 UTC


README

Packagist Version Tests PHPStan License

A Laravel package for integrating with the Moloni invoicing API. Provides a clean, fluent interface to manage companies, customers, products, invoices, and all other Moloni resources with full type safety through DTOs and enums.

Official page: digitaldev.pt/packages/laravel-moloni

Requirements

  • PHP 8.4+
  • Laravel 12 or 13

Installation

Install the package via Composer:

composer require digitaldev-lx/laravel-moloni

Publish the configuration file:

php artisan vendor:publish --tag=moloni-config

Run the migrations (required for OAuth token storage):

php artisan migrate

Configuration

Add the following environment variables to your .env file:

MOLONI_CLIENT_ID=your-client-id
MOLONI_CLIENT_SECRET=your-client-secret
MOLONI_USERNAME=your-username
MOLONI_PASSWORD=your-password
MOLONI_COMPANY_ID=your-company-id

You can obtain your API credentials from the Moloni Developer Portal.

Usage

Using the Facade

All API interactions are available through the Moloni facade:

use DigitaldevLx\LaravelMoloni\Facades\Moloni;

Companies

// List all companies
$companies = Moloni::companies()->getAll();

Customers

$companyId = config('moloni.company_id');

// List all customers
$customers = Moloni::customers()->getAll($companyId);

// Find a customer by VAT number
$customer = Moloni::customers()->getByVat($companyId, '123456789');

// Create a customer using a DTO
use DigitaldevLx\LaravelMoloni\DataTransferObjects\Customer as CustomerDto;

$dto = new CustomerDto(
    vat: '123456789',
    number: 'C001',
    name: 'John Doe',
    email: 'john@example.com',
    address: '123 Main Street',
    city: 'Lisbon',
    zipCode: '1000-001',
    countryId: 1,
);

$customer = Moloni::customers()->insert($companyId, $dto);

// Create a customer using an array
$customer = Moloni::customers()->insert($companyId, [
    'vat' => '123456789',
    'number' => 'C001',
    'name' => 'John Doe',
    'email' => 'john@example.com',
]);

Products

// List all products
$products = Moloni::products()->getAll($companyId);

// Find a product by reference
$product = Moloni::products()->getByReference($companyId, 'PROD-001');

// Create a product using a DTO
use DigitaldevLx\LaravelMoloni\DataTransferObjects\Product as ProductDto;
use DigitaldevLx\LaravelMoloni\Enums\ProductType;

$dto = new ProductDto(
    name: 'Widget',
    reference: 'PROD-001',
    type: ProductType::Product,
    categoryId: 1,
    unitId: 1,
    price: 29.99,
);

$product = Moloni::products()->insert($companyId, $dto);

Invoices

use DigitaldevLx\LaravelMoloni\DataTransferObjects\Document as DocumentDto;
use DigitaldevLx\LaravelMoloni\DataTransferObjects\DocumentProduct;
use DigitaldevLx\LaravelMoloni\DataTransferObjects\Payment;

// Create an invoice
$dto = new DocumentDto(
    documentSetId: 1,
    customerId: 1,
    date: '2026-03-31',
    expirationDate: '2026-04-30',
    products: [
        new DocumentProduct(
            productId: 1,
            qty: 2,
            price: 29.99,
        ),
    ],
    payments: [
        new Payment(
            paymentMethodId: 1,
            value: 59.98,
            date: '2026-03-31',
        ),
    ],
);

$invoice = Moloni::invoices()->insert($companyId, $dto);

// Get PDF link for a document
$pdfLink = Moloni::invoices()->getPdfLink($companyId, $invoiceId);

Other Document Types

The package supports all Moloni document types through dedicated resources:

  • Moloni::receipts() - Receipts
  • Moloni::creditNotes() - Credit Notes
  • Moloni::debitNotes() - Debit Notes
  • Moloni::simplifiedInvoices() - Simplified Invoices
  • Moloni::invoiceReceipts() - Invoice Receipts
  • Moloni::deliveryNotes() - Delivery Notes
  • Moloni::billsOfLading() - Bills of Lading
  • Moloni::waybills() - Waybills
  • Moloni::estimates() - Estimates
  • Moloni::documents() - Generic Documents

Settings Resources

Access configuration resources for taxes, payment methods, document sets, and more:

$taxes = Moloni::taxes()->getAll($companyId);
$paymentMethods = Moloni::paymentMethods()->getAll($companyId);
$documentSets = Moloni::documentSets()->getAll($companyId);
$warehouses = Moloni::warehouses()->getAll($companyId);
$units = Moloni::measurementUnits()->getAll($companyId);
$maturityDates = Moloni::maturityDates()->getAll($companyId);
$deliveryMethods = Moloni::deliveryMethods()->getAll($companyId);
$bankAccounts = Moloni::bankAccounts()->getAll($companyId);

// Global resources (no company_id needed)
$countries = Moloni::countries()->getAll();
$currencies = Moloni::currencies()->getAll();
$languages = Moloni::languages()->getAll();
$exemptions = Moloni::taxExemptions()->getAll();
$fiscalZones = Moloni::fiscalZones()->getAll($countryId);

Using DTOs

The package provides readonly Data Transfer Objects for type-safe data handling:

  • Customer - Customer data
  • Product - Product data
  • Document - Document header data
  • DocumentProduct - Document line item data
  • Supplier - Supplier data
  • Tax - Tax data
  • Payment - Payment data
  • Address - Address data

All DTOs are located in the DigitaldevLx\LaravelMoloni\DataTransferObjects namespace.

Using Events

The package dispatches events for all mutation operations. You can listen for these in your EventServiceProvider or using closures:

Event Dispatched When
DocumentCreated A document is successfully created
DocumentCancelled A document is cancelled
DocumentClosed A document is closed/finalized
CustomerCreated A new customer is created
CustomerUpdated A customer is updated
ProductCreated A new product is created
ProductUpdated A product is updated
TokenRefreshed The OAuth token is refreshed

All events are located in the DigitaldevLx\LaravelMoloni\Events namespace.

use DigitaldevLx\LaravelMoloni\Events\DocumentCreated;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        DocumentCreated::class => [
            SendInvoiceNotification::class,
        ],
    ];
}

Error Handling

The package provides granular exception handling that maps directly to Moloni's error system.

Exception Types

Exception When
AuthenticationException OAuth2 errors (invalid credentials, expired tokens, invalid client)
ValidationException Data validation errors (required fields, invalid NIF, invalid email, etc.)
RateLimitException API rate limit exceeded (HTTP 429)
MoloniException All other API errors

All exceptions extend MoloniException, so you can catch them all with a single catch block or handle each type individually.

Authentication Errors

use DigitaldevLx\LaravelMoloni\Exceptions\AuthenticationException;
use DigitaldevLx\LaravelMoloni\Enums\AuthError;

try {
    $customers = Moloni::customers()->getAll($companyId);
} catch (AuthenticationException $e) {
    $e->authError;        // AuthError enum (e.g. AuthError::InvalidGrant)
    $e->errorDescription; // Moloni's error description string
    $e->getMessage();     // Full formatted message
}

Available AuthError enum values: InvalidClient, InvalidUri, RedirectUriMismatch, InvalidRequest, UnsupportedGrantType, UnauthorizedClient, InvalidGrant, InvalidScope.

Validation Errors

use DigitaldevLx\LaravelMoloni\Exceptions\ValidationException;
use DigitaldevLx\LaravelMoloni\Enums\ValidationErrorCode;

try {
    $customer = Moloni::customers()->insert($companyId, $data);
} catch (ValidationException $e) {
    $e->errors;             // Array of ['code' => int, 'field' => string, 'description' => string]
    $e->getFieldErrors();   // ['field_name' => 'description', ...]
    $e->hasFieldError('vat'); // true/false
}

Validation error codes (mapped via ValidationErrorCode enum):

Code Meaning
1 Campo obrigatorio
2 Campo numerico invalido
3 Endereco de email invalido
4 Valor deve ser unico
5 Valor invalido
6 URL invalido
7 Codigo postal invalido
8 NIF portugues invalido
9 Data deve estar no formato AAAA-MM-DD
10 Associacao de documento invalida
11 Documento nao pode ser enviado para a AT
12 Data invalida
13 Numero de telefone invalido
14 Artigo tem taxas conflituantes
15 Artigo tem multiplas entradas de IVA
16 Identificacao do cliente obrigatoria (Art. 36 CIVA)
17 Limite de caracteres excedido

Catching All Errors

use DigitaldevLx\LaravelMoloni\Exceptions\MoloniException;
use DigitaldevLx\LaravelMoloni\Exceptions\AuthenticationException;
use DigitaldevLx\LaravelMoloni\Exceptions\ValidationException;
use DigitaldevLx\LaravelMoloni\Exceptions\RateLimitException;

try {
    $invoice = Moloni::invoices()->insert($companyId, $data);
} catch (AuthenticationException $e) {
    // Handle auth errors (invalid credentials, expired tokens)
} catch (ValidationException $e) {
    // Handle validation errors (invalid data)
} catch (RateLimitException $e) {
    // Handle rate limiting (retry later)
} catch (MoloniException $e) {
    // Handle all other API errors
}

HasMoloniDocuments Trait

Add the HasMoloniDocuments trait to any Eloquent model to associate it with Moloni documents:

use DigitaldevLx\LaravelMoloni\Concerns\HasMoloniDocuments;

class Order extends Model
{
    use HasMoloniDocuments;
}

// Then use it
$order->moloniDocuments;

Available Resources

Resource Accessor Method
Companies Moloni::companies()
Customers Moloni::customers()
Suppliers Moloni::suppliers()
Products Moloni::products()
Product Categories Moloni::productCategories()
Invoices Moloni::invoices()
Receipts Moloni::receipts()
Credit Notes Moloni::creditNotes()
Debit Notes Moloni::debitNotes()
Simplified Invoices Moloni::simplifiedInvoices()
Invoice Receipts Moloni::invoiceReceipts()
Delivery Notes Moloni::deliveryNotes()
Bills of Lading Moloni::billsOfLading()
Waybills Moloni::waybills()
Estimates Moloni::estimates()
Documents Moloni::documents()
Taxes Moloni::taxes()
Tax Exemptions Moloni::taxExemptions()
Payment Methods Moloni::paymentMethods()
Document Sets Moloni::documentSets()
Warehouses Moloni::warehouses()
Measurement Units Moloni::measurementUnits()
Maturity Dates Moloni::maturityDates()
Delivery Methods Moloni::deliveryMethods()
Bank Accounts Moloni::bankAccounts()
Countries Moloni::countries()
Fiscal Zones Moloni::fiscalZones()
Languages Moloni::languages()
Currencies Moloni::currencies()

Testing

vendor/bin/pest

Static Analysis

vendor/bin/phpstan analyse

Code Style

vendor/bin/pint

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

License

The MIT License (MIT). Please see License File for more information.

Credits