byrcsc/laravel-payrex · 1.x
Events and listeners.
Listen for typed Laravel PayRex webhook events, inspect their resources, customize event mappings, and design idempotent handlers.
Every verified webhook dispatches PayrexWebhookReceived. When its type exists
in config('payrex.webhooks.events'), the package also dispatches the mapped
typed event.
Listen for a typed event
namespace App\Listeners;
use App\Models\Order;
use ByRcsc\LaravelPayrex\Events\PaymentIntentSucceeded;
use Illuminate\Contracts\Queue\ShouldQueue;
final class MarkOrderAsPaid implements ShouldQueue
{
public function handle(PaymentIntentSucceeded $event): void
{
$intent = $event->event->paymentIntent();
if ($intent === null) {
return;
}
Order::query()
->where('payrex_payment_intent_id', $intent->id)
->whereNull('paid_at')
->update(['paid_at' => now()]);
}
}Laravel can discover the listener or you can register it explicitly in your application's event configuration.
Default event catalog
| PayRex type | Laravel event |
|---|---|
payment_intent.awaiting_capture | PaymentIntentAwaitingCapture |
payment_intent.succeeded | PaymentIntentSucceeded |
setup_intent.succeeded | SetupIntentSucceeded |
checkout_session.expired | CheckoutSessionExpired |
refund.created | RefundCreated |
refund.updated | RefundUpdated |
payout.created | PayoutCreated |
payout.deposited | PayoutDeposited |
billing_statement.created | BillingStatementCreated |
billing_statement.updated | BillingStatementUpdated |
billing_statement.deleted | BillingStatementDeleted |
billing_statement.finalized | BillingStatementFinalized |
billing_statement.sent | BillingStatementSent |
billing_statement.marked_uncollectible | BillingStatementMarkedUncollectible |
billing_statement.voided | BillingStatementVoided |
billing_statement.paid | BillingStatementPaid |
billing_statement.will_be_due | BillingStatementWillBeDue |
billing_statement.overdue | BillingStatementOverdue |
billing_statement_line_item.created | BillingStatementLineItemCreated |
billing_statement_line_item.updated | BillingStatementLineItemUpdated |
billing_statement_line_item.deleted | BillingStatementLineItemDeleted |
All typed events extend PayrexWebhookEvent.
Listen for every verified type
Use the generic event for audit logging or a PayRex type the installed package does not map yet:
use ByRcsc\LaravelPayrex\Events\PayrexWebhookReceived;
final class RecordPayrexEvent implements ShouldQueue
{
public function handle(PayrexWebhookReceived $event): void
{
$event->type();
$event->event->id;
$event->event->type;
$event->event->raw;
}
}The generic event fires even when a typed event fires.
Inspect the event envelope
$event->event is a readonly WebhookEvent:
$webhook = $event->event;
$webhook->id;
$webhook->type;
$webhook->livemode;
$webhook->pendingWebhooks;
$webhook->previousAttributes;
$webhook->resourceType();
$webhook->resourceData();
$webhook->resource();
$webhook->raw;resource() hydrates a supported subject into PaymentIntent, Payment,
PaymentMethod, Refund, CheckoutSession, SetupIntent, Customer,
BillingStatement, BillingStatementLineItem, or Payout. It returns null
for an unknown resource; the raw data remains available.
Convenience methods exist for common subjects:
$webhook->paymentIntent();
$webhook->payment();
$webhook->refund();
$webhook->checkoutSession();Use resource() plus instanceof for the other resource types.
Match event types
is() accepts exact names and a trailing wildcard:
if ($webhook->is('refund.created', 'refund.updated')) {
// ...
}
if ($webhook->is('billing_statement.*')) {
// ...
}Customize the map
Add an event type by mapping it to a class that extends
PayrexWebhookEvent:
use App\Events\CustomerUpdatedByPayrex;
'events' => [
// package defaults...
'customer.updated' => CustomerUpdatedByPayrex::class,
],namespace App\Events;
use ByRcsc\LaravelPayrex\Events\PayrexWebhookEvent;
final class CustomerUpdatedByPayrex extends PayrexWebhookEvent
{
}The package validates this map during application boot. Keys must be non-empty
strings and classes must extend PayrexWebhookEvent.
Idempotency
PayRex may deliver an event more than once. A listener should claim the event ID before performing one-time work:
if (! PayrexDelivery::query()->firstOrCreate([
'event_id' => $event->event->id,
])->wasRecentlyCreated) {
return;
}
// Perform the state transition once.Back the event ID with a unique database index so two queue workers cannot claim it simultaneously. Also make the domain update itself conditional; this protects against retries that happen after the claim but before work finishes.