Download the PHP package digitaldev-lx/laravel-moloni without Composer
On this page you can find all versions of the php package digitaldev-lx/laravel-moloni. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package laravel-moloni
Laravel Moloni
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:
Publish the configuration file:
Run the migrations (required for OAuth token storage):
Configuration
Add the following environment variables to your .env file:
You can obtain your API credentials from the Moloni Developer Portal.
Sandbox
Moloni provides a sandbox environment for testing API integrations without affecting production data. The sandbox is available at https://api.moloni.pt/sandbox/ and exposes the same endpoints as production.
Getting sandbox credentials
- You need an active Moloni account. If your login does not yet have access to the demonstration company, request it from
[email protected]indicating the email associated with your login. - The OAuth2
client_id/client_secretare the same as in production. Only thebase_urlchanges. - Optionally, explore the API interactively at the Moloni API Explorer.
Switching environments
Set MOLONI_BASE_URL in your .env:
Important: token persistence between environments
OAuth2 tokens are stored in the moloni_tokens table and are not scoped per environment. When you switch MOLONI_BASE_URL, the existing token is no longer valid for the new host and the package will silently re-authenticate on the next call. To force a clean state, truncate the table:
A common pattern is to use a separate database for local/testing (already the default with SQLite) so production tokens are never reused against the sandbox host.
Usage
Using the Facade
All API interactions are available through the Moloni facade:
Companies
Customers
Products
Invoices
Other Document Types
The package supports all Moloni document types through dedicated resources:
Moloni::receipts()- ReceiptsMoloni::creditNotes()- Credit NotesMoloni::debitNotes()- Debit NotesMoloni::simplifiedInvoices()- Simplified InvoicesMoloni::invoiceReceipts()- Invoice ReceiptsMoloni::deliveryNotes()- Delivery NotesMoloni::billsOfLading()- Bills of LadingMoloni::waybills()- WaybillsMoloni::estimates()- EstimatesMoloni::purchaseOrders()- Purchase OrdersMoloni::internalDocuments()- Internal DocumentsMoloni::paymentReturns()- Payment ReturnsMoloni::ownAssetsMovementGuides()- Own Asset Movement GuidesMoloni::customerReturnNotes()- Customer Return NotesMoloni::globalGuides()- Global GuidesMoloni::supplierInvoices()- Supplier InvoicesMoloni::supplierPurchaseOrders()- Supplier Purchase OrdersMoloni::supplierSimplifiedInvoices()- Supplier Simplified InvoicesMoloni::supplierCreditNotes()- Supplier Credit NotesMoloni::supplierDebitNotes()- Supplier Debit NotesMoloni::supplierReturnNotes()- Supplier Return NotesMoloni::supplierReceipts()- Supplier ReceiptsMoloni::supplierWarrantyRequests()- Supplier Warranty RequestsMoloni::documents()- Generic Documents
Settings Resources
Access configuration resources for taxes, payment methods, document sets, and more:
Using DTOs
The package provides readonly Data Transfer Objects for type-safe data handling:
Customer- Customer dataSupplier- Supplier dataSalesman- Salesman dataProduct- Product dataProductStock- Stock movement dataDocument- Document header dataDocumentProduct- Document line item dataPayment- Payment dataAddress- Address dataTax- Tax dataVehicle- Vehicle dataDeduction- Withholding tax dataPriceClass- Price class data
All DTOs are located in the DigitaldevLx\LaravelMoloni\DataTransferObjects namespace.
Using Events
The package dispatches events for all mutation operations:
| Event | Dispatched When |
|---|---|
DocumentCreated |
A document is successfully created |
DocumentUpdated |
A document is updated |
DocumentDeleted |
A document is deleted |
DocumentCancelled |
A document is cancelled |
DocumentClosed |
A document is closed/finalized |
CustomerCreated |
A new customer is created |
CustomerUpdated |
A customer is updated |
CustomerDeleted |
A customer is deleted |
SupplierCreated |
A new supplier is created |
SupplierUpdated |
A supplier is updated |
SupplierDeleted |
A supplier is deleted |
SalesmanCreated |
A new salesman is created |
SalesmanUpdated |
A salesman is updated |
SalesmanDeleted |
A salesman is deleted |
ProductCreated |
A new product is created |
ProductUpdated |
A product is updated |
ProductDeleted |
A product is deleted |
TokenRefreshed |
The OAuth token is refreshed |
DocumentCreated, DocumentUpdated and DocumentDeleted carry both the API response and a documentType string (e.g. 'invoices', 'creditNotes') so a single listener can route by document type.
All events are located in the DigitaldevLx\LaravelMoloni\Events namespace.
Auto-discovery (recommended)
Create a listener class in your app/Listeners directory. Laravel automatically discovers and registers listeners based on the type-hint in the handle method:
Manual registration
Register listeners manually in your AppServiceProvider:
Closure listeners
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
Available AuthError enum values: InvalidClient, InvalidUri, RedirectUriMismatch, InvalidRequest, UnsupportedGrantType, UnauthorizedClient, InvalidGrant, InvalidScope.
Validation Errors
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
HasMoloniDocuments Trait
Add the HasMoloniDocuments trait to any Eloquent model to associate it with Moloni documents:
Available Resources
Account & Profile
| Resource | Accessor Method |
|---|---|
| My Profile | Moloni::myProfile() |
Company
| Resource | Accessor Method |
|---|---|
| Companies | Moloni::companies() |
| Subscription | Moloni::subscription() |
| Users | Moloni::users() |
Entities
| Resource | Accessor Method |
|---|---|
| Customers | Moloni::customers() |
| Customer Alternate Addresses | Moloni::customerAlternateAddresses() |
| Suppliers | Moloni::suppliers() |
| Salesmen | Moloni::salesmen() |
Products
| Resource | Accessor Method |
|---|---|
| Products | Moloni::products() |
| Product Categories | Moloni::productCategories() |
| Product Stocks | Moloni::productStocks() |
| Product Properties | Moloni::productProperties() |
| Price Classes | Moloni::priceClasses() |
Documents
| Resource | Accessor Method |
|---|---|
| Documents | Moloni::documents() |
| 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() |
| Purchase Orders | Moloni::purchaseOrders() |
| Internal Documents | Moloni::internalDocuments() |
| Payment Returns | Moloni::paymentReturns() |
| Own Asset Movement Guides | Moloni::ownAssetsMovementGuides() |
| Customer Return Notes | Moloni::customerReturnNotes() |
| Global Guides | Moloni::globalGuides() |
| Supplier Invoices | Moloni::supplierInvoices() |
| Supplier Purchase Orders | Moloni::supplierPurchaseOrders() |
| Supplier Simplified Invoices | Moloni::supplierSimplifiedInvoices() |
| Supplier Credit Notes | Moloni::supplierCreditNotes() |
| Supplier Debit Notes | Moloni::supplierDebitNotes() |
| Supplier Return Notes | Moloni::supplierReturnNotes() |
| Supplier Receipts | Moloni::supplierReceipts() |
| Supplier Warranty Requests | Moloni::supplierWarrantyRequests() |
Settings
| Resource | Accessor Method |
|---|---|
| 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() |
| CAE Codes | Moloni::caeCodes() |
| Vehicles | Moloni::vehicles() |
| Deductions | Moloni::deductions() |
| Identification Templates | Moloni::identificationTemplates() |
Global Data
| Resource | Accessor Method |
|---|---|
| Countries | Moloni::countries() |
| Fiscal Zones | Moloni::fiscalZones() |
| Languages | Moloni::languages() |
| Currencies | Moloni::currencies() |
| Currency Exchange | Moloni::currencyExchange() |
| Document Models | Moloni::documentModels() |
| Multibanco Gateways | Moloni::multibancoGateways() |
Testing
Run the package test suite with Pest:
When testing your own application against the Moloni API, prefer Http::fake() so no real requests leave your machine. The host you fake should match MOLONI_BASE_URL exactly:
For an end-to-end example see tests/Unit/MoloniClientTest.php.
Troubleshooting
| Symptom | Cause / Fix |
|---|---|
Repeated 401 after correct credentials |
The token in moloni_tokens was issued for a different MOLONI_BASE_URL. Truncate the table and re-authenticate. |
AuthenticationException with AuthError::InvalidGrant |
Wrong MOLONI_USERNAME / MOLONI_PASSWORD. Verify against the Moloni client area. |
AuthenticationException with AuthError::InvalidClient |
Wrong MOLONI_CLIENT_ID / MOLONI_CLIENT_SECRET. Re-check at the Developer Portal. |
RateLimitException |
Moloni returned HTTP 429. Back off and retry; consider batching requests. |
| Calls hit production while you expected sandbox | MOLONI_BASE_URL is not set or is cached. Run php artisan config:clear. |
Static Analysis
Code Style
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.