Skip to content

v1.1.0-beta

Pre-release
Pre-release

Choose a tag to compare

@Foysal50x Foysal50x released this 05 Jun 20:23

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 a success transaction and settles the invoice in one DB transaction (routes through InvoiceObserver → activate / advancePeriod / reactivate). Idempotent on UNIQUE(gateway, transaction_id), so replayed at-least-once webhooks never double-settle.
    • recordFailedPayment($invoice, …) — writes a failed transaction; leaves the invoice Pending for dunning.
    • recordRefund($transaction, …) — accumulates refunded_amount (partials supported); a full refund flips the transaction and invoice to Refunded.
  • 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 a gateway_response array cast on Transaction.
  • Invoice/transaction read APIlatestInvoice($sub, ?InvoiceKind), pendingInvoice($sub), overdueInvoice($sub), successfulTransaction($invoice) — read through the (overridable) repository instead of querying the Invoice model 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 a RefundController example. CheckoutController / TrialController / SuspensionController read invoices via the billing API. Docs (package + website) cover the ledger, refunds, and read API.

Fixed

  • TransactionIdGenerator uniqueness is now scoped to the gateway — it takes the row's gateway via a constructor argument (passed by TransactionObserver) 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