laraditz / my-invois
Laravel package for interacting with MyInvois API.
Fund package maintenance!
raditzfarhan
Buy Me A Coffee
Requires
- php: ^8.2
- illuminate/support: ^11.0|^12.0
- sabre/xml: ^4.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2025-07-13 15:49:30 UTC
README
A Developer-Friendly Laravel SDK for MyInvois e-Invoicing
Easily integrate with MyInvois, the official e-Invoicing platform by Lembaga Hasil Dalam Negeri Malaysia (LHDNM), using this powerful Laravel SDK. MyInvois enables taxpayers to seamlessly submit issued documents to the tax authority and receive real-time updates on document statuses.
This package provides a clean, object-oriented interface for creating, managing, and sending e-Invoices—helping you stay compliant with Malaysia’s digital tax regulations while keeping your codebase elegant and maintainable.
Warning
This SDK is still actively under development and may contain bugs. Use at your own risk.
Installation
Requirements
- PHP >= 8.2
- Laravel >= 11.0
- Composer
Quick Start
Here's a complete guide to get started with this package:
1. Install Package
composer require laraditz/my-invois
2. Configure Environment Variables
Configure your variables in your .env
file (recommended) or publish the config file and change it there.
Required Variables:
MYINVOIS_CLIENT_ID="<your_client_id>"
MYINVOIS_CLIENT_SECRET="<your_client_secret>"
Optional Variables:
MYINVOIS_PASSPHRASE="<your_passpharase>" // if required
MYINVOIS_SANDBOX=true // set true for testing, false for production
MYINVOIS_DISK="local" // disk for storing documents
MYINVOIS_DOCUMENT_PATH="myinvois/" // path for storing documents
MYINVOIS_CERTIFICATE_PATH="/path/to/certificate.p12" // path to certificate file
MYINVOIS_PRIVATE_KEY_PATH="/path/to/private_key.pem" // path to private key file
3. Publish Config (Optional)
You can publish the config file via this command:
php artisan vendor:publish --provider="Laraditz\MyInvois\MyInvoisServiceProvider" --tag="config"
4. Publish Migration
You can publish the migration file via this command:
php artisan vendor:publish --provider="Laraditz\MyInvois\MyInvoisServiceProvider" --tag="migrations"
5. Run Migration
Run the migration command to create the necessary database tables:
php artisan migrate
6. Test Connection
Test your setup with a simple authentication call:
use Laraditz\MyInvois\Facades\MyInvois; // Test authentication $token = MyInvois::auth()->token(); // will throw an error if failed echo "Connection successful!";
Available Methods
Below are all methods available under this SDK. Refer to Platform API and E-Invoice API for more information.
Authentication Service auth()
Method | Description | Parameters |
---|---|---|
token() |
Generate access token for API call | client_id , client_secret , grant_type , scope , onbehalfof |
Document Type Service documentType()
Method | Description | Parameters |
---|---|---|
all() |
Get list of all document types | - |
get() |
Get document type by ID | id |
version() |
Get document type version | id , vid |
Document Service document()
Method | Description | Parameters |
---|---|---|
submit() |
Submit one or more signed documents | documents[] , format |
Notification Service notification()
Method | Description | Parameters |
---|---|---|
all() |
Get all notifications for taxpayer | - |
Taxpayer Service taxpayer()
Method | Description | Parameters |
---|---|---|
validate() |
Validate TIN (Tax Identification Number) | tin , idType , idValue |
Document Generation Methods
Method | Description | Parameters | Return type |
---|---|---|---|
generateDocument() |
Generate document in XML or JSON format | Invoice $data , Format $format |
string |
Usage
Basic Authentication
use Laraditz\MyInvois\Facades\MyInvois; // Get access token $accessToken = MyInvois::auth()->token(); // Or with specific parameters $accessToken = MyInvois::auth()->token( client_id: 'your_client_id', client_secret: 'your_client_secret', grant_type: 'client_credentials', scope: 'InvoicingAPI' ); // Get access token on behalf of $accessToken = MyInvois::auth()->token(onbehalfof: 'C25845632020'); // taxpayer's TIN or ROB number
Document Types
use Laraditz\MyInvois\Facades\MyInvois; // Get all document types $documentTypes = MyInvois::documentType()->all(); // Get document type by ID $documentType = MyInvois::documentType()->get(1); // Get document type version $version = MyInvois::documentType()->version(id: 1, vid: 2);
Document Submission
use Laraditz\MyInvois\Facades\MyInvois; use Laraditz\MyInvois\Data\Invoice; use Laraditz\MyInvois\Enums\Format; // Create Invoice object $invoice = new Invoice( ID: 'INV-001', IssueDate: now(), IssueTime: now(), InvoiceTypeCode: new Data('380'), // Standard Invoice DocumentCurrencyCode: 'MYR', // ... add other required data ); // Submit document $result = MyInvois::document()->submit( documents: [$invoice], // can submit multiple invoices format: Format::XML );
Taxpayer Validation
use Laraditz\MyInvois\Facades\MyInvois; // Validate TIN $validation = MyInvois::taxpayer()->validate(tin: 'AB123456789012', idType: 'NRIC', idValue: '200101011234');
Notification Retrieval
use Laraditz\MyInvois\Facades\MyInvois; // Get all notifications $notifications = MyInvois::notification()->all();
Document Generation
Typically you won't need to generate the document as you will be using the Document Submission service. But if you want to manually generate the document for debugging or other purposes, you able to do so using below code.
use Laraditz\MyInvois\Facades\MyInvois; use Laraditz\MyInvois\Data\Invoice; use Laraditz\MyInvois\Enums\Format; // Create Invoice object $invoice = new Invoice( ID: 'INV-001', IssueDate: now(), IssueTime: now(), InvoiceTypeCode: new Data('380'), DocumentCurrencyCode: 'MYR', // ... other data ); // Generate XML document $xmlDocument = MyInvois::generateDocument($invoice, Format::XML); // Then, to display the xml on browser // MyInvois::helper()->displayXml($xmlDocument);
Advanced Usage with Query String, Payload and Params
The service offers a flexible, fluent interface that lets you dynamically configure parameters on the fly—right before the HTTP request is sent. Effortlessly chain methods like payload()
for the request body, queryString()
for URL queries, and params()
for path parameters, all after invoking the service method.
use Laraditz\MyInvois\Facades\MyInvois; // Using query string $result = MyInvois::documentType() ->queryString(['page' => 1, 'limit' => 10]) ->all(); // Using params $result = MyInvois::documentType() ->params(['id' => 1, 'vid' => 2]) ->version();
Error Handling
use Laraditz\MyInvois\Facades\MyInvois; use Laraditz\MyInvois\Exceptions\MyInvoisApiError; try { $result = MyInvois::document()->submit( documents: [$invoice], format: Format::XML ); if ($result['success']) { echo "Document submitted successfully. Request ID: " . $result['request_id']; } } catch (MyInvoisApiError $e) { echo "Error: " . $e->getMessage(); } catch (\Throwable $th) { throw $th; }
Sandbox Mode
For testing, you can use sandbox mode:
// In .env (Recommended) MYINVOIS_SANDBOX=true // Or in config (Not recommended) 'MYINVOIS_SANDBOX' => true
Certificate and Signature
For documents that require digital signature:
// Make sure certificate and private key paths are correct in .env MYINVOIS_CERTIFICATE_PATH="/path/to/certificate.p12" MYINVOIS_PRIVATE_KEY_PATH="/path/to/private_key.pem" MYINVOIS_PASSPHRASE="your_passphrase" // Package will automatically add signature if certificate exists $xmlDocument = MyInvois::generateXMLDocument($invoice);
Complete Example: Creating and Submitting Invoice
use Laraditz\MyInvois\Facades\MyInvois; use Laraditz\MyInvois\Data\Invoice; use Laraditz\MyInvois\Data\AccountingSupplierParty; use Laraditz\MyInvois\Data\AccountingCustomerParty; use Laraditz\MyInvois\Data\Party; use Laraditz\MyInvois\Data\PostalAddress; use Laraditz\MyInvois\Data\PartyIdentification; use Laraditz\MyInvois\Data\PartyLegalEntity; use Laraditz\MyInvois\Data\Contact; use Laraditz\MyInvois\Data\InvoiceLine; use Laraditz\MyInvois\Data\Item; use Laraditz\MyInvois\Data\Price; use Laraditz\MyInvois\Data\TaxCategory; use Laraditz\MyInvois\Data\TaxScheme; use Laraditz\MyInvois\Data\TaxSubtotal; use Laraditz\MyInvois\Data\TaxTotal; use Laraditz\MyInvois\Data\LegalMonetaryTotal; use Laraditz\MyInvois\Data\Money; use Laraditz\MyInvois\Data\Country; use Laraditz\MyInvois\Data\Data; use Laraditz\MyInvois\Enums\Format; // Create supplier party $supplierParty = new Party( PartyIdentification: [new PartyIdentification('123456789012')], PartyName: [new Data('ABC Company Sdn Bhd')], PostalAddress: new PostalAddress( StreetName: '123 Main Street', CityName: 'Kuala Lumpur', PostalZone: '50000', Country: new Country('MY') ), PartyLegalEntity: [new PartyLegalEntity( RegistrationName: 'ABC Company Sdn Bhd' )], Contact: new Contact( Name: 'John Doe', Telephone: '+60123456789', Email: 'john@abc.com' ) ); // Create customer party $customerParty = new Party( PartyIdentification: [new PartyIdentification('987654321098')], PartyName: [new Data('XYZ Corporation')], PostalAddress: new PostalAddress( StreetName: '456 Business Ave', CityName: 'Petaling Jaya', PostalZone: '46100', Country: new Country('MY') ), PartyLegalEntity: [new PartyLegalEntity( RegistrationName: 'XYZ Corporation Sdn Bhd' )], Contact: new Contact( Name: 'Jane Smith', Telephone: '+60987654321', Email: 'jane@xyz.com' ) ); // Create invoice line $invoiceLine = new InvoiceLine( ID: '1', InvoicedQuantity: 2, LineExtensionAmount: new Money(200.00, 'MYR'), Item: new Item( Name: 'Product A', Description: 'High quality product', SellersItemIdentification: new Data('PROD-001') ), Price: new Price( PriceAmount: new Money(100.00, 'MYR') ), TaxTotal: new TaxTotal( TaxAmount: new Money(12.00, 'MYR'), TaxSubtotal: [new TaxSubtotal( TaxableAmount: new Money(200.00, 'MYR'), TaxAmount: new Money(12.00, 'MYR'), TaxCategory: new TaxCategory( ID: 'S', Percent: 6.0, TaxScheme: new TaxScheme('SST') ) )] ) ); // Create tax total $taxTotal = new TaxTotal( TaxAmount: new Money(12.00, 'MYR'), TaxSubtotal: [new TaxSubtotal( TaxableAmount: new Money(200.00, 'MYR'), TaxAmount: new Money(12.00, 'MYR'), TaxCategory: new TaxCategory( ID: 'S', Percent: 6.0, TaxScheme: new TaxScheme('SST') ) )] ); // Create legal monetary total $legalMonetaryTotal = new LegalMonetaryTotal( LineExtensionAmount: new Money(200.00, 'MYR'), TaxExclusiveAmount: new Money(200.00, 'MYR'), TaxInclusiveAmount: new Money(212.00, 'MYR'), PayableAmount: new Money(212.00, 'MYR') ); // Create invoice $invoice = new Invoice( ID: 'INV-2024-001', IssueDate: now(), IssueTime: now(), InvoiceTypeCode: new Data('380'), // Standard Invoice DocumentCurrencyCode: 'MYR', AccountingSupplierParty: new AccountingSupplierParty($supplierParty), AccountingCustomerParty: new AccountingCustomerParty($customerParty), InvoiceLine: [$invoiceLine], TaxTotal: $taxTotal, LegalMonetaryTotal: $legalMonetaryTotal ); // Submit invoice try { $result = MyInvois::document()->submit( documents: [$invoice], format: Format::XML ); if ($result['success']) { echo "Invoice submitted successfully!"; echo "Request ID: " . $result['request_id']; echo "Response: " . json_encode($result['data'], JSON_PRETTY_PRINT); } } catch (Exception $e) { echo "Error: " . $e->getMessage(); }
Supported Data Structures
This package supports UBL (Universal Business Language) data structures for e-invoice:
- Invoice: Main invoice document
- Party: Supplier and customer information
- Address: Postal address
- Contact: Contact information
- InvoiceLine: Invoice line items
- Item: Product/service information
- Price: Item pricing
- TaxCategory: Tax categories
- TaxTotal: Tax totals
- LegalMonetaryTotal: Legal monetary totals
- Money: Monetary values with currency
Environment Variables Reference
Variable | Description | Default | Required |
---|---|---|---|
MYINVOIS_CLIENT_ID |
Client ID from MyInvois | - | Yes |
MYINVOIS_CLIENT_SECRET |
Client Secret from MyInvois | - | Yes |
MYINVOIS_PASSPHRASE |
Passphrase for certificate | - | No |
MYINVOIS_SANDBOX |
Sandbox mode for testing | false | No |
MYINVOIS_DISK |
Disk for storing documents | local | No |
MYINVOIS_DOCUMENT_PATH |
Path for storing documents | myinvois/ | No |
MYINVOIS_CERTIFICATE_PATH |
Path to certificate file | storage/app/myinvois.p12 | No |
MYINVOIS_PRIVATE_KEY_PATH |
Path to private key file | storage/app/myinvois.pem | No |
Best Practices
- Error Handling: Always use try-catch to handle errors
- Validation: Validate data before sending to API
- Logging: Use logging for tracking requests and responses
- Testing: Use sandbox mode for testing
- Security: Ensure certificates and private keys are stored securely
- Monitoring: Monitor request history for debugging
Troubleshooting
Error: Missing Client ID/Secret
- Ensure
MYINVOIS_CLIENT_ID
andMYINVOIS_CLIENT_SECRET
are set in.env
Error: Certificate not found
- Ensure certificate and private key paths are correct
- Check file permissions
Error: Invalid document format
- Ensure document follows correct UBL format
- Check all required fields
Error: API timeout
- Check internet connection
- Try again after a few minutes
Migration Files
This package will create the following tables when migration is run:
myinvois_clients
- Store client informationmyinvois_access_tokens
- Store access tokensmyinvois_requests
- Store all requests and responsesmyinvois_documents
- Store submitted documentsmyinvois_document_histories
- Store previously submitted documents
Exception Handling
This package provides several exceptions for error handling:
MyInvoisApiError
Exception for MyInvois API errors:
use Laraditz\MyInvois\Exceptions\MyInvoisApiError; try { $result = MyInvois::document()->submit($data); } catch (MyInvoisApiError $e) { // Handle API error Log::error('MyInvois API Error: ' . $e->getMessage()); } catch (\Throwable $th) { // Handle other errors throw $th; }
MyInvoisException
Exception for general package errors:
use Laraditz\MyInvois\Exceptions\MyInvoisException; try { $result = MyInvois::document()->submit($data); } catch (MyInvoisException $e) { // Handle general package error Log::error('MyInvois Error: ' . $e->getMessage()); } catch (\Throwable $th) { // Handle other errors throw $th; }
To Do
- Add all APIs
- Add documentation
- Add test
- Refactor code
Testing
composer test
Changelog
Please see CHANGELOG for more information what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Support
If you encounter issues or have questions:
- Documentation: Refer to official MyInvois documentation
- Issues: Open an issue on GitHub repository
- Email: Send email to raditzfarhan@gmail.com
Security
If you discover any security related issues, please email raditzfarhan@gmail.com instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.