v1.1.0-beta
Pre-release
Pre-release
Adds a host-reported transaction ledger, an invoice/transaction read API, and a gateway-scoping fix for generated transaction ids. Tashil still never moves money — it records what the host's gateway reports.
Added
- Transaction ledger API on
Tashil::billing():recordPayment($invoice, …)— writes asuccesstransaction and settles the invoice in one DB transaction (routes throughInvoiceObserver→ activate / advancePeriod / reactivate). Idempotent onUNIQUE(gateway, transaction_id), so replayed at-least-once webhooks never double-settle.recordFailedPayment($invoice, …)— writes afailedtransaction; leaves the invoicePendingfor dunning.recordRefund($transaction, …)— accumulatesrefunded_amount(partials supported); a full refund flips the transaction and invoice toRefunded.
TransactionRepositoryInterface+EloquentTransactionRepository— the ledger sits behind an overridable repository like every other persistence concern.- Events
PaymentRecorded,PaymentFailed,PaymentRefunded— carry the transaction + invoice, dispatched after commit. Invoice::markAsRefunded()/Invoice::isRefunded(), plus agateway_responsearray cast onTransaction.- Invoice/transaction read API —
latestInvoice($sub, ?InvoiceKind),pendingInvoice($sub),overdueInvoice($sub),successfulTransaction($invoice)— read through the (overridable) repository instead of querying theInvoicemodel directly. Read paths are uncached (per-subscription invoices change on every payment/dunning step).
Changed
- Cookbook examples now use the ledger API instead of hand-written
Transaction::create()+markAsPaid(); added aRefundControllerexample.CheckoutController/TrialController/SuspensionControllerread invoices via the billing API. Docs (package + website) cover the ledger, refunds, and read API.
Fixed
TransactionIdGeneratoruniqueness is now scoped to the gateway — it takes the row's gateway via a constructor argument (passed byTransactionObserver) and checks the composite(gateway, transaction_id), matching the DB constraint. The old global check wrongly rejected an id already used under a different gateway; the same id can legitimately exist per gateway.
Full Changelog: v1.0.0-beta...v1.1.0-beta