Skip to content

Releases: EKTIRAS/billing-php

v0.3.0 — document items, richer filters, monthly stats, regenerate PDF

19 Apr 15:27

Choose a tag to compare

Additive release pairing with the server's API v1.1. Existing v0.2.x callers keep working unchanged.

New DTOs

  • LineItem — one entry per line on a document
  • MonthlyStatsmonths[], bySource{}, totalsBySource{}, grandTotal

New methods

$doc->items                                    // line items on GET /documents/{id}
Billing::products()->list(includeInactive: true)
Billing::stats()->monthly(months: 12)          // per-source monthly revenue chart
Billing::documents()->regeneratePdf($id)       // re-render a stale PDF

Richer documents()->list() filters

All non-breaking. Pass any subset to list():

Filter Match
mark exact
full_number partial
customer_email partial
customer_company partial
source exact

Server side (same commit series in ektir-billing)

  • GET /documents/{id} response now includes the items array
  • GET /documents gains the filters above
  • GET /products?include_inactive=1
  • GET /stats/monthly
  • POST /documents/{id}/regenerate-pdf
  • POST /documents with send_email=true → 422 (was silently honoured before; server now rejects the field entirely)

Upgrading

No code changes required for existing SDK users. You'll just see new properties on the DTOs and new methods to use when you need them.

See CHANGELOG.md and README §7, §8.1, §9, §10.1, §16 for details.

v0.2.1 — security + forward-compat fixes

19 Apr 15:10

Choose a tag to compare

Security

  • PDF download no longer leaks the API Bearer token. Client::stream() previously attached Authorization: Bearer <key> to the signed PDF URL request. Signed URLs carry their own authentication — leaking the API key to that host is unnecessary and a latent risk. Fixed.

Fixed

  • Greek / Unicode filenames preserved in downloadPdf(). Full numbers like Α/2026/00001 now produce Α_2026_00001.pdf, matching what the README always claimed.
  • Unknown enum values no longer crash Document::fromArray. Catchable UnknownEnumValueException (subclass of EktirBillingException) instead of PHP's native ValueError.
  • DocumentPdfReady event fires exactly once — on the absent→present PDF transition, not on every subsequent poll. DocumentTracker::pending() now returns a previous_has_pdf flag.
  • PendingDocument::toArray() throws InvalidBuilderStateException (subclass of EktirBillingException) instead of plain \LogicException.
  • await() retries transient errors (RateLimitException, TimeoutException) with backoff instead of aborting.

Added

  • New exceptions: UnknownEnumValueException, InvalidBuilderStateException.
  • CHANGELOG.md (Keep a Changelog format).
  • 5 new tests covering the fixes above (8 total, 22 assertions, all green).

Upgrading

Fully backward-compatible for existing callers. Only behavioural change to watch:

If you wrote a custom DocumentTracker, the pending() return shape is now {id, previous_status, previous_has_pdf}. Implementations that still return the 2-key shape will see DocumentPdfReady fire on the first post-upgrade poll (treated as absent→present); no-op thereafter. See README §11 for the new example.

v0.2.0 — integrator owns email delivery

19 Apr 11:11

Choose a tag to compare

BREAKING change

The billing server no longer sends email to end customers on the integrator's behalf — you deliver the PDF from your own Laravel app, from your own sending domain.

Removed

  • PendingDocument::sendEmail() is gone from the fluent builder.

Added

  • Billing::documents()->pdfUrl($id) — returns a freshly-signed 24h URL you can drop into an email body or share.
  • Billing::documents()->pdfAttachment($id) — returns an Illuminate\Mail\Attachment ready to use inside your Mailable's attachments(). Bytes are fetched lazily when Laravel renders the mail.

Why

send_email=true on the server was convenient but wrong for integrators:

  • The "From" address was the billing infra's address, not the integrator's domain.
  • Subject / body / branding weren't customisable per integrator.
  • Deliverability (SPF, DKIM, DMARC) lived on someone else's server.

Now the integrator writes a normal Laravel Mailable, attaches the PDF (or links to it), and sends through their own stack.

Upgrade

Before:

Billing::documents()->build()
    ->receipt()
    ->forCustomer($customer)
    ->addItem($sku, 1, 10.00)
    ->payCard()
    ->sendEmail()      // ← remove
    ->create();

After:

$doc   = Billing::documents()->build()->receipt()->forCustomer($customer)->addItem($sku, 1, 10.00)->payCard()->create();
$ready = Billing::documents()->awaitPdf($doc->id);

Mail::to($customer->email)->send(new \App\Mail\InvoiceIssuedMail($doc));

// inside InvoiceIssuedMail::attachments():
// return [Billing::documents()->pdfAttachment($this->doc->id, "{$this->doc->fullNumber}.pdf")];

For async flows, listen for DocumentPdfReady and mail from the listener — see README §11.

See README §6.3 for the full 'attach or link' walkthrough.

v0.1.0 — initial release

19 Apr 10:50

Choose a tag to compare

Official Laravel SDK for the EKTIR Billing API.

Highlights

  • Typed SDK wrapping all 9 REST endpoints — documents (issue / list / find / cancel), products CRUD, EU OSS stats
  • Fluent PendingDocument builder: Billing::documents()->build()->receipt()->forCustomer(...)->addItem(...)->payCard()->create()
  • await() / awaitPdf() helpers for the myDATA → PDF → email async pipeline
  • One-line PDF download to any Laravel disk: Billing::documents()->downloadPdf($id, disk: 's3')
  • Webhook replacement: scheduled ektir:poll-documents command + DocumentSubmitted / DocumentPdfReady / DocumentFailed / DocumentStateChanged Laravel events. Bind a DocumentTracker and Event::listen(...) them like webhooks.
  • Typed exceptions mirroring the API error envelope (ValidationException, RateLimitException, AuthenticationException, NotFoundException, CancelForbiddenException, TimeoutException)
  • withApiKey($key) for multi-tenant apps
  • Http::fake()-friendly — full test recipes in the README

Requirements

  • PHP 8.2+
  • Laravel 11 or 12

Install

{
  "repositories": [
    { "type": "vcs", "url": "https://github.com/EKTIRAS/billing-php" }
  ],
  "require": { "ektiras/billing-php": "^0.1" }
}

See the README for the full integration guide — country-specific rules, awaiting PDFs, polling & events, error handling, and testing.