PHPackages                             c-delouvencourt/pennylane-laravel - 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. [API Development](/categories/api)
4. /
5. c-delouvencourt/pennylane-laravel

ActiveLibrary[API Development](/categories/api)

c-delouvencourt/pennylane-laravel
=================================

Pennylane API wrapper for Laravel 11

2.0.4(1mo ago)05.1k↓26.4%1MITPHPPHP ^8.1

Since Jul 8Pushed 2mo agoCompare

[ Source](https://github.com/cldt-fr/laravel-pennylane)[ Packagist](https://packagist.org/packages/c-delouvencourt/pennylane-laravel)[ Docs](https://github.com/c-delouvencourt/laravel-pennylane)[ RSS](/packages/c-delouvencourt-pennylane-laravel/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (6)Dependencies (8)Versions (8)Used By (0)

Pennylane API v2 wrapper for Laravel
====================================

[](#pennylane-api-v2-wrapper-for-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/f71ceee857b57760bb8bbdd19977daf129ab47cd11d3b682d083f25487b82b2a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f632d64656c6f7576656e636f7572742f6c61726176656c2d70656e6e796c616e652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/c-delouvencourt/laravel-pennylane)

A comprehensive Laravel wrapper for the [Pennylane API v2](https://pennylane.com/api/external/v2/). Covers all ~127 endpoints with typed Response DTOs, cursor-based pagination, OAuth 2.0 support, and automatic retry.

Built on Laravel's HTTP client (`Illuminate\Http\Client`).

**Requirements:** PHP 8.1+, Laravel 8+

---

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

[](#installation)

```
composer require c-delouvencourt/laravel-pennylane
```

Publish the configuration file:

```
php artisan vendor:publish --provider="CLDT\PennylaneLaravel\PennylaneLaravelServiceProvider" --tag="config"
```

### Authentication

[](#authentication)

#### Bearer token (default)

[](#bearer-token-default)

```
PENNYLANE_API_KEY=your_api_key
```

#### OAuth 2.0

[](#oauth-20)

```
PENNYLANE_AUTH_METHOD=oauth2
PENNYLANE_OAUTH_CLIENT_ID=your_client_id
PENNYLANE_OAUTH_CLIENT_SECRET=your_client_secret
PENNYLANE_OAUTH_REDIRECT_URI=https://your-app.com/callback
PENNYLANE_OAUTH_TOKEN=your_access_token
PENNYLANE_OAUTH_REFRESH_TOKEN=your_refresh_token
```

### Retry

[](#retry)

All HTTP requests are automatically retried on **recoverable** failures (5xx server errors, 429 rate limiting, and connection timeouts). Client errors (400, 401, 403, 404, 422) are **not** retried since they would never succeed. You can configure this behavior via environment variables:

```
PENNYLANE_RETRY_TIMES=3      # Number of attempts (default: 3)
PENNYLANE_RETRY_SLEEP=500     # Delay in ms between attempts (default: 500)
PENNYLANE_RETRY_THROW=true    # Throw exception after all attempts fail (default: true)
```

Or directly in `config/pennylane-laravel.php`:

```
'retry' => [
    'times' => 3,
    'sleep' => 500,
    'throw' => true,
],
```

### Error Handling

[](#error-handling)

All API calls throw typed exceptions on failure. Every exception extends `PennylaneApiException`, so you can catch specific errors or handle them all at once:

```
use CLDT\PennylaneLaravel\Exceptions\PennylaneApiException;
use CLDT\PennylaneLaravel\Exceptions\PennylaneNotFoundException;
use CLDT\PennylaneLaravel\Exceptions\PennylaneBadRequestException;
use CLDT\PennylaneLaravel\Exceptions\PennylaneAuthenticationException;

try {
    $invoice = $pennylane->customerInvoices()->get(999);
} catch (PennylaneNotFoundException $e) {
    // 404 — resource not found
    $e->getStatusCode();    // 404
    $e->getErrorMessage();  // Error message from the API
    $e->getResponseBody();  // Full decoded JSON response
} catch (PennylaneBadRequestException $e) {
    // 400 — includes structured error details
    $e->getField();          // ?string — the field that caused the error
    $e->getBadRequestCode(); // ?BadRequestCode enum — e.g. BadRequestCode::NotExistRequiredKey
    $e->getPayload();        // ?string
} catch (PennylaneAuthenticationException $e) {
    // 401 — invalid or missing token
} catch (PennylaneApiException $e) {
    // Catch-all for any other API error (403, 422, 429, 5xx...)
    $e->getStatusCode();
    $e->getErrorMessage();
}
```

Exception hierarchy:

ExceptionHTTP Status`PennylaneBadRequestException`400`PennylaneAuthenticationException`401`PennylaneAuthorizationException`403`PennylaneNotFoundException`404`PennylaneValidationException`422`PennylaneRateLimitException`429`PennylaneServerException`5xx`PennylaneApiException`Any other non-2xxAll exceptions provide: `getStatusCode()`, `getErrorMessage()`, `getResponseBody()`, `getResponse()`.

---

Usage
-----

[](#usage)

All methods return typed Response DTOs with readonly properties. List endpoints return a `PaginatedResponse` with cursor-based pagination.

### Pagination

[](#pagination)

```
use CLDT\PennylaneLaravel\PennylaneLaravel;

$result = app(PennylaneLaravel::class)->customers()->list();

$result->items;       // array of CustomerResponse
$result->has_more;    // bool
$result->next_cursor; // ?string

// Fetch next page
$nextPage = app(PennylaneLaravel::class)->customers()->list(['cursor' => $result->next_cursor]);
```

---

Available Resources
-------------------

[](#available-resources)

### Customers

[](#customers)

```
$pennylane = app(PennylaneLaravel::class);

// List all customers (company + individual)
$customers = $pennylane->customers()->list();

// Get a customer
$customer = $pennylane->customers()->get('123');

// Customer categories
$categories = $pennylane->customers()->categories('123');
$pennylane->customers()->updateCategories('123', $data);

// Customer contacts
$contacts = $pennylane->customers()->contacts('123');
```

### Company Customers

[](#company-customers)

```
$customer = $pennylane->companyCustomers()->create([
    'name' => 'Acme Corp',
    'emails' => ['contact@acme.com'],
    'billing_address' => [
        'address' => '1 rue de la Paix',
        'postal_code' => '75001',
        'city' => 'Paris',
        'country_alpha2' => 'FR',
    ],
]);

$customer = $pennylane->companyCustomers()->get('123');
$customer = $pennylane->companyCustomers()->update('123', $data);
```

### Individual Customers

[](#individual-customers)

```
$customer = $pennylane->individualCustomers()->create([
    'first_name' => 'John',
    'last_name' => 'Doe',
    'emails' => ['john@example.com'],
]);

$customer = $pennylane->individualCustomers()->get('123');
$customer = $pennylane->individualCustomers()->update('123', $data);
```

### Products

[](#products)

```
$products = $pennylane->products()->list();
$product = $pennylane->products()->get('1');

$product = $pennylane->products()->create([
    'label' => 'Product 1',
    'unit' => 'piece',
    'price_before_tax' => '10.00',
    'price' => '12.00',
    'vat_rate' => 'FR_200',
    'currency' => 'EUR',
]);

$product = $pennylane->products()->update('1', ['description' => 'Updated']);
```

### Customer Invoices

[](#customer-invoices)

```
// List & CRUD
$invoices = $pennylane->customerInvoices()->list();
$invoice = $pennylane->customerInvoices()->create($data);
$invoice = $pennylane->customerInvoices()->get(1);
$invoice = $pennylane->customerInvoices()->update(1, $data);
$pennylane->customerInvoices()->delete(1);

// Actions
$invoice = $pennylane->customerInvoices()->finalize(1);
$invoice = $pennylane->customerInvoices()->markAsPaid(1);
$pennylane->customerInvoices()->sendByEmail(1, ['emails' => ['client@example.com']]);
$invoice = $pennylane->customerInvoices()->linkCreditNote(1, $data);
$invoice = $pennylane->customerInvoices()->createFromQuote($data);
$invoice = $pennylane->customerInvoices()->updateImported(1, $data);

// Import with file upload (multipart/form-data)
$invoice = $pennylane->customerInvoices()->import(
    fields: ['create_customer' => true],
    attachments: [
        ['name' => 'file', 'contents' => file_get_contents('/path/to/invoice.pdf'), 'filename' => 'invoice.pdf'],
    ],
);

// Sub-resources
$sections = $pennylane->customerInvoices()->invoiceLineSections(1);
$lines = $pennylane->customerInvoices()->invoiceLines(1);
$payments = $pennylane->customerInvoices()->payments(1);
$matched = $pennylane->customerInvoices()->matchedTransactions(1);
$pennylane->customerInvoices()->matchTransaction(1, $data);
$pennylane->customerInvoices()->unmatchTransaction(1, 5);
$appendices = $pennylane->customerInvoices()->appendices(1);

// Upload appendix (multipart/form-data)
$pennylane->customerInvoices()->uploadAppendix(1,
    attachments: [
        ['name' => 'file', 'contents' => file_get_contents('/path/to/appendix.pdf'), 'filename' => 'appendix.pdf'],
    ],
);

$categories = $pennylane->customerInvoices()->categories(1);
$pennylane->customerInvoices()->updateCategories(1, $data);
$fields = $pennylane->customerInvoices()->customHeaderFields(1);
```

### Supplier Invoices

[](#supplier-invoices)

```
$invoices = $pennylane->supplierInvoices()->list();
$invoice = $pennylane->supplierInvoices()->get(1);
$invoice = $pennylane->supplierInvoices()->update(1, $data);
$invoice = $pennylane->supplierInvoices()->validateAccounting(1);

// Import with file upload (multipart/form-data)
$invoice = $pennylane->supplierInvoices()->import(
    fields: ['create_supplier' => true],
    attachments: [
        ['name' => 'file', 'contents' => file_get_contents('/path/to/invoice.pdf'), 'filename' => 'invoice.pdf'],
    ],
);

// Sub-resources
$lines = $pennylane->supplierInvoices()->invoiceLines(1);
$payments = $pennylane->supplierInvoices()->payments(1);
$pennylane->supplierInvoices()->updatePaymentStatus(1, $data);
$matched = $pennylane->supplierInvoices()->matchedTransactions(1);
$pennylane->supplierInvoices()->matchTransaction(1, $data);
$pennylane->supplierInvoices()->unmatchTransaction(1, 5);
$pennylane->supplierInvoices()->linkPurchaseRequest(1, $data);
$categories = $pennylane->supplierInvoices()->categories(1);
$pennylane->supplierInvoices()->updateCategories(1, $data);
```

### Quotes

[](#quotes)

```
$quotes = $pennylane->quotes()->list();
$quote = $pennylane->quotes()->create($data);
$quote = $pennylane->quotes()->get(1);
$quote = $pennylane->quotes()->update(1, $data);
$quote = $pennylane->quotes()->updateStatus(1, ['status' => 'accepted']);
$pennylane->quotes()->sendByEmail(1, $data);

// Sub-resources
$lines = $pennylane->quotes()->invoiceLines(1);
$sections = $pennylane->quotes()->invoiceLineSections(1);
$appendices = $pennylane->quotes()->appendices(1);
$pennylane->quotes()->uploadAppendix(1, $data);
```

### Suppliers

[](#suppliers)

```
$suppliers = $pennylane->suppliers()->list();
$supplier = $pennylane->suppliers()->create($data);
$supplier = $pennylane->suppliers()->get(1);
$supplier = $pennylane->suppliers()->update(1, $data);
$categories = $pennylane->suppliers()->categories(1);
$pennylane->suppliers()->updateCategories(1, $data);
```

### Billing Subscriptions

[](#billing-subscriptions)

```
$subscriptions = $pennylane->billingSubscriptions()->list();
$subscription = $pennylane->billingSubscriptions()->create($data);
$subscription = $pennylane->billingSubscriptions()->get(1);
$subscription = $pennylane->billingSubscriptions()->update(1, $data);
$sections = $pennylane->billingSubscriptions()->invoiceLineSections(1);
$lines = $pennylane->billingSubscriptions()->invoiceLines(1);
```

### Bank Accounts &amp; Transactions

[](#bank-accounts--transactions)

```
// Bank accounts
$accounts = $pennylane->bankAccounts()->list();
$account = $pennylane->bankAccounts()->create($data);
$account = $pennylane->bankAccounts()->get(1);

// Bank establishments
$establishments = $pennylane->bankEstablishments()->list();

// Transactions
$transactions = $pennylane->transactions()->list();
$transaction = $pennylane->transactions()->create($data);
$transaction = $pennylane->transactions()->get(1);
$transaction = $pennylane->transactions()->update(1, $data);
$categories = $pennylane->transactions()->categories(1);
$pennylane->transactions()->updateCategories(1, $data);
$matched = $pennylane->transactions()->matchedInvoices(1);
```

### Ledger (Accounts, Entries, Lines, Attachments)

[](#ledger-accounts-entries-lines-attachments)

```
// Ledger accounts
$accounts = $pennylane->ledgerAccounts()->list();
$account = $pennylane->ledgerAccounts()->create($data);
$account = $pennylane->ledgerAccounts()->get(1);
$account = $pennylane->ledgerAccounts()->update(1, $data);

// Ledger entries
$entries = $pennylane->ledgerEntries()->list();
$entry = $pennylane->ledgerEntries()->create($data);
$entry = $pennylane->ledgerEntries()->get(1);
$entry = $pennylane->ledgerEntries()->update(1, $data);
$lines = $pennylane->ledgerEntries()->ledgerEntryLines(1);

// Ledger entry lines
$lines = $pennylane->ledgerEntryLines()->list();
$line = $pennylane->ledgerEntryLines()->get(1);
$pennylane->ledgerEntryLines()->letter($data);
$pennylane->ledgerEntryLines()->unletter($data);
$lettered = $pennylane->ledgerEntryLines()->letteredLines(1);
$categories = $pennylane->ledgerEntryLines()->categories(1);
$pennylane->ledgerEntryLines()->updateCategories(1, $data);

// Ledger attachments
$attachments = $pennylane->ledgerAttachments()->list();

// Upload (multipart/form-data)
$pennylane->ledgerAttachments()->upload(
    attachments: [
        ['name' => 'file', 'contents' => file_get_contents('/path/to/attachment.pdf'), 'filename' => 'attachment.pdf'],
    ],
);
```

### Categories &amp; Category Groups

[](#categories--category-groups)

```
$categories = $pennylane->categories()->list();
$category = $pennylane->categories()->create($data);
$category = $pennylane->categories()->get(1);
$category = $pennylane->categories()->update(1, $data);

$groups = $pennylane->categoryGroups()->list();
$group = $pennylane->categoryGroups()->get(1);
$categories = $pennylane->categoryGroups()->categories(1);
```

### Journals

[](#journals)

```
$journals = $pennylane->journals()->list();
$journal = $pennylane->journals()->create($data);
$journal = $pennylane->journals()->get(1);
```

### Commercial Documents

[](#commercial-documents)

```
$documents = $pennylane->commercialDocuments()->list();
$document = $pennylane->commercialDocuments()->get(1);
$appendices = $pennylane->commercialDocuments()->appendices(1);
$pennylane->commercialDocuments()->uploadAppendix(1, $data);
$lines = $pennylane->commercialDocuments()->invoiceLines(1);
$sections = $pennylane->commercialDocuments()->invoiceLineSections(1);
```

### SEPA &amp; GoCardless Mandates

[](#sepa--gocardless-mandates)

```
// SEPA mandates
$mandates = $pennylane->sepaMandates()->list();
$mandate = $pennylane->sepaMandates()->create($data);
$mandate = $pennylane->sepaMandates()->get(1);
$mandate = $pennylane->sepaMandates()->update(1, $data);
$pennylane->sepaMandates()->delete(1);

// GoCardless mandates
$mandates = $pennylane->gocardlessMandates()->list();
$mandate = $pennylane->gocardlessMandates()->get(1);
$pennylane->gocardlessMandates()->sendMailRequest($data);
$pennylane->gocardlessMandates()->cancel(1);
$associations = $pennylane->gocardlessMandates()->associations(1);

// Pro account mandates
$mandates = $pennylane->proAccountMandates()->list();
```

### Exports

[](#exports)

```
$export = $pennylane->exports()->createAnalyticalGeneralLedger($data);
$status = $pennylane->exports()->getAnalyticalGeneralLedger(1);

$export = $pennylane->exports()->createFec($data);
$status = $pennylane->exports()->getFec(1);
```

### File Attachments

[](#file-attachments)

```
$files = $pennylane->fileAttachments()->list();

// Upload (multipart/form-data)
$file = $pennylane->fileAttachments()->upload(
    attachments: [
        ['name' => 'file', 'contents' => file_get_contents('/path/to/document.pdf'), 'filename' => 'document.pdf'],
    ],
);
```

### Purchase Requests

[](#purchase-requests)

```
$requests = $pennylane->purchaseRequests()->list();
$request = $pennylane->purchaseRequests()->get(1);
$request = $pennylane->purchaseRequests()->update(1, $data);
$pennylane->purchaseRequests()->import($data);
```

### Other Resources

[](#other-resources)

```
// Users
$user = $pennylane->users()->create($data);
$user = $pennylane->users()->find(['email' => 'user@example.com']);
$user = $pennylane->users()->update(1, $data);
$me = $pennylane->users()->me();
// Or directly:
$me = $pennylane->me();

// Companies
$pennylane->companies()->create($data);
$pennylane->companies()->completeRegistration(1, $data);

// Webhooks
$webhook = $pennylane->webhooks()->get();

// Customer invoice templates
$templates = $pennylane->customerInvoiceTemplates()->list();

// E-Invoices - Import with file upload (multipart/form-data)
$pennylane->eInvoices()->import(
    fields: ['create_customer' => true],
    attachments: [
        ['name' => 'file', 'contents' => file_get_contents('/path/to/invoice.pdf'), 'filename' => 'invoice.pdf'],
    ],
);

// Fiscal years
$years = $pennylane->fiscalYears()->list();

// Trial balance
$balance = $pennylane->trialBalance()->get(['start_date' => '2024-01-01', 'end_date' => '2024-12-31']);

// Changelogs
$changes = $pennylane->changelogs()->customerInvoices(['since' => '2024-01-01']);
$changes = $pennylane->changelogs()->supplierInvoices($params);
$changes = $pennylane->changelogs()->customers($params);
$changes = $pennylane->changelogs()->suppliers($params);
$changes = $pennylane->changelogs()->products($params);
$changes = $pennylane->changelogs()->ledgerEntryLines($params);
$changes = $pennylane->changelogs()->transactions($params);
$changes = $pennylane->changelogs()->quotes($params);
```

---

Migrating from v2.0 to v2.1
---------------------------

[](#migrating-from-v20-to-v21)

### HTTP client

[](#http-client)

The underlying HTTP client has been migrated from **GuzzleHTTP** to **Laravel's HTTP facade** (`Illuminate\Http\Client`). This change is transparent for most usages, but if you were injecting `GuzzleHttp\ClientInterface` directly, you need to update to `Illuminate\Http\Client\PendingRequest`.

### File uploads

[](#file-uploads)

The `import()` and `upload()` methods now use `multipart/form-data` and accept two parameters instead of one:

MethodBeforeAfter`customerInvoices()->import()``import(array $data)``import(array $fields = [], array $attachments = [])``customerInvoices()->uploadAppendix()``uploadAppendix(int $id, array $data)``uploadAppendix(int $id, array $fields = [], array $attachments = [])``supplierInvoices()->import()``import(array $data)``import(array $fields = [], array $attachments = [])``fileAttachments()->upload()``upload(array $data)``upload(array $fields = [], array $attachments = [])``ledgerAttachments()->upload()``upload(array $data)``upload(array $fields = [], array $attachments = [])``eInvoices()->import()``import(array $data)``import(array $fields = [], array $attachments = [])`Each attachment is an array with the following keys:

```
['name' => 'file', 'contents' => $fileContents, 'filename' => 'invoice.pdf']
```

### Error handling

[](#error-handling-1)

API errors now throw typed exceptions (`PennylaneApiException` and subclasses) instead of silently returning partial data. If your code relied on checking response arrays for errors, wrap your calls in try/catch blocks instead. See the [Error Handling](#error-handling) section.

### Retry

[](#retry-1)

Automatic retry is now configured by default (3 attempts, 500ms delay) and only retries recoverable errors (5xx, 429, connection failures). Client errors (4xx) are thrown immediately. See the [Retry](#retry) section to customize.

### Dependencies

[](#dependencies)

- Removed: `guzzlehttp/guzzle`
- Added: `illuminate/http`

Migrating from v1 to v2
-----------------------

[](#migrating-from-v1-to-v2)

v1v2Notes`invoices()``customerInvoices()``invoices()` still works as deprecated alias`estimates()``quotes()``estimates()` still works as deprecated alias`enums()->get('unit')`RemovedUse PHP enums in `Dto\Enums\*` insteadArray returnsDTO returnsAll methods return typed Response DTOsOffset paginationCursor paginationUse `PaginatedResponse` with `next_cursor``PENNYLANE_API_KEY``PENNYLANE_API_KEY`Same env var, new config structure---

Response DTOs
-------------

[](#response-dtos)

All responses use readonly properties and can be created from arrays:

```
use CLDT\PennylaneLaravel\Dto\Responses\ProductResponse;

$product = $pennylane->products()->get('1');
$product->id;                // int
$product->label;             // string
$product->price_before_tax;  // ?string
$product->currency;          // ?string
$product->created_at;        // ?string
```

PHP Enums
---------

[](#php-enums)

All API enum values are available as PHP 8.1 backed enums:

```
use CLDT\PennylaneLaravel\Dto\Enums\InvoiceStatus;
use CLDT\PennylaneLaravel\Dto\Enums\Currency;
use CLDT\PennylaneLaravel\Dto\Enums\VatRate;
use CLDT\PennylaneLaravel\Dto\Enums\PaymentConditions;

InvoiceStatus::Draft->value;          // 'draft'
Currency::EUR->value;                 // 'EUR'
VatRate::FR200->value;                // 'FR_200'
PaymentConditions::Days30->value;     // '30_days'
```

Available enums: `AccountType`, `BadRequestCode`, `BillingSubscriptionMode`, `BillingSubscriptionOccurrenceRuleType`, `BillingSubscriptionPaymentMethod`, `BillingSubscriptionStatus`, `CategoryDirection`, `CommercialDocumentType`, `Currency`, `CustomerBillingLanguage`, `DiscountType`, `ExportStatus`, `InvoiceAccountingStatus`, `InvoicePaymentStatus`, `InvoiceStatus`, `MandateStatus`, `PaymentConditions`, `PaymentStatus`, `ProductUnit`, `PurchaseRequestStatus`, `QuoteStatus`, `SepaSequenceType`, `SupplierDueDateRule`, `SupplierPaymentMethod`, `VatRate`.

---

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Clement de Louvencourt](https://github.com/c-delouvencourt)
- [Romain Bertolucci](https://github.com/ashraam)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance86

Actively maintained with recent releases

Popularity24

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity53

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 ~104 days

Recently: every ~8 days

Total

7

Last Release

56d ago

Major Versions

1.0.2 → 2.0.02026-02-19

PHP version history (2 changes)1.0.0PHP ^7.4|^8.0

2.0.0PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/f9f297bd2d9cafd14dc7b7f55220bb058c82eaee69181facc3df0e0fc5b32cc3?d=identicon)[cldt](/maintainers/cldt)

---

Top Contributors

[![cldt-fr](https://avatars.githubusercontent.com/u/26087186?v=4)](https://github.com/cldt-fr "cldt-fr (12 commits)")

---

Tags

laravelinvoicescustomersPennylane

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/c-delouvencourt-pennylane-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/c-delouvencourt-pennylane-laravel/health.svg)](https://phpackages.com/packages/c-delouvencourt-pennylane-laravel)
```

###  Alternatives

[laraveldaily/laravel-invoices

Missing invoices for Laravel

1.5k1.3M4](/packages/laraveldaily-laravel-invoices)[essa/api-tool-kit

set of tools to build an api with laravel

52680.5k](/packages/essa-api-tool-kit)[resend/resend-laravel

Resend for Laravel

1191.4M6](/packages/resend-resend-laravel)[joggapp/laravel-aws-sns

Laravel package for the SNS events by AWS

3171.8k](/packages/joggapp-laravel-aws-sns)[simplestats-io/laravel-client

Client for SimpleStats!

4515.5k](/packages/simplestats-io-laravel-client)[dragon-code/laravel-json-response

Automatically always return a response in JSON format

1118.6k1](/packages/dragon-code-laravel-json-response)

PHPackages © 2026

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