Skip to content
Paybob

Development

Events

The events Paybob fires, their payloads, and how to listen to them.

Paybob fires a Laravel event whenever something important happens. Addons listen to them to send messages, sync other systems or keep their own records. Every event is fired after the change is saved, so a listener never sees a half-finished state.

Listening

In an addon, register listeners in your provider's register():

PHP
use App\Events\TransactionStatusUpdated;

public function register(): void
{
    $this->listen(TransactionStatusUpdated::class, NotifyCustomer::class);
}

$this->listen() only calls your listener when your addon is enabled in the event's Brand, and runs it inside that Brand. A listener that throws is logged, and the action that fired the event carries on.

Every event

EventFires when
App\Events\TransactionStatusUpdatedA transaction's status changes.
App\Events\TransactionRefundedA transaction is refunded, fully or partly.
App\Events\InvoiceStatusUpdatedAn invoice's status changes.
App\Events\InvoiceEmailSentAn invoice email is sent successfully.
App\Events\BrandContextChangedThe current Brand changes during a request or task.

All properties are plain values: strings, numbers and nulls. Money amounts are decimal strings, such as "1250.00000000": use bcmath for any calculation, never floats.

The Brand on every event

Every event except BrandContextChanged carries its Brand:

MemberGives
brandId, brandSlug, brandNameThe Brand it happened in.
brand()The Brand model.
merchant()The Brand's General settings, display-ready: name(), logoUrl(), supportEmail(), addressLines(), socialLinks() and so on.

TransactionStatusUpdated

Fires when a transaction's status changes:

  • a gateway completes a payment;
  • SMS verification completes a pending payment;
  • someone clicks Approve or Bulk Approve (once per transaction actually approved);
  • someone changes the status with Edit (only when it really changes).

Refunds fire TransactionRefunded instead, and Send IPN fires nothing.

PropertyTypeDescription
transactionIdintThe internal record ID, to load the full transaction if you need it.
tIdstringPaybob's payment ID.
trxId?stringThe provider's transaction ID, if recorded.
oldStatusstringThe status before: pending, completed, failed, canceled, voided, refunded or partially_refunded.
newStatusstringThe status after, from the same list.
amountstringThe transaction's base amount.
currency?stringSuch as USD.
customerName, customerEmail, customerPhone?stringThe customer's details on this payment.
PHP
public function handle(TransactionStatusUpdated $event): void
{
    if ($event->newStatus !== 'completed') {
        return;
    }

    // $event->tId, $event->amount, $event->currency, $event->customerEmail …
}

TransactionRefunded

Fires once for each refund, whether from the panel or the API.

PropertyTypeDescription
transactionIdintThe internal record ID.
tIdstringPaybob's payment ID.
trxId?stringThe provider's transaction ID, if any.
amountstringThis refund's amount.
totalRefundedstringThe total refunded on this transaction so far, including this one.
reasonstringThe reason given for this refund.
statusstringThe resulting status: refunded or partially_refunded.
currency?string
customerName, customerEmail, customerPhone?string

Paybob keeps a refund total rather than a record of each refund, so this event is the only moment you see each individual refund. Store it yourself if you need the history.

InvoiceStatusUpdated

Fires when an invoice's status changes: Mark Paid, Mark Unpaid, Refund, Cancel (single or bulk), a status change on the edit page, or an invoice being paid automatically when its payment completes.

PropertyTypeDescription
invoiceIdintThe internal record ID.
iIdstringThe invoice ID, as in its public link.
oldStatus, newStatusstringpaid, unpaid, refunded or canceled.
grandTotalstringThe invoice's grand total.
currency?string
customerName, customerEmail?string
actorId?intThe user who made the change, or null when it happened automatically.

InvoiceEmailSent

Fires after an invoice email is accepted for sending. A failed send doesn't fire it.

PropertyTypeDescription
invoiceIdintThe internal record ID.
iIdstringThe invoice ID.
customerName, customerEmail?stringThe invoice's customer.
receiverEmailstringWhere the email was actually sent. It can differ from the customer's email.
subjectstringThe subject as sent.
renderedBodystringThe body as sent, with placeholders filled in.
invoiceUrlstringThe invoice's public link.
sentAtstringWhen it was sent.
actorId, actorEmail?int, ?stringWho sent it.

BrandContextChanged

Fires when the current Brand changes: once per request when the Brand is known (from the panel's address, a custom domain or an API key), and when background work enters and leaves a Brand.

PropertyTypeDescription
brandId?intThe Brand now current, or null when there's none.
brandSlug?stringIts slug.
previousBrandId?intThe Brand before.

Use it to switch per-Brand configuration, such as a mail transport or an API client. Register it with a plain Event::listen(), not $this->listen(), so it also runs in Brands where your addon is off and you can restore the default:

PHP
use App\Events\BrandContextChanged;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(BrandContextChanged::class, function (): void {
        addon_enabled('my-addon')
            ? MyClient::configure(addon_settings('my-addon')->all())
            : MyClient::reset();
    });
}

Your own events

Addons can define and fire their own events like any Laravel application, and other addons can listen to them:

PHP
namespace Addons\SmsNotifications\Events;

use Illuminate\Foundation\Events\Dispatchable;

class SmsSent
{
    use Dispatchable;

    public function __construct(
        public readonly string $to,
        public readonly string $message,
    ) {}
}
PHP
SmsSent::dispatch($phone, $text);

Give your event brandId, brandSlug and brandName properties and use App\Events\Concerns\CarriesBrand, and $this->listen() handles it per Brand like a core event. Wrap your own dispatch in try/catch and report(), so a broken listener never breaks your feature.

Webhooks are separate

Events are for code running inside Paybob. To notify an outside website or app, use the payment's webhook instead. It's described in the API reference.