WordPress GuidesWebhooks

Webhooks

React to payment and subscription events in your plugin.

PayLexer sends every event to the merchant's site, where the PayLexer plugin receives it and runs these checks, in order:

Checks the event is for this site's tenant

Drops replayed deliveries

Logs the event under PayLexer → Webhook logs

Only then does it fire your hooks. You don't register a URL or handle a secret.

Hooks

For platform acme-bookings:

add_action( 'paylexer_platform_acme-bookings_payment_completed', function ( array $event ) {
	$reference = $event['data']['orderReference'];
	// Mark your order paid.
} );
HookArguments
paylexer_platform_{id}_{event}array $event
paylexer_platform_{id}_webhookarray $event, string $event_type, fired for every event

With a platform adapter, the same events arrive at its on_event() method instead.

{event} is the event type with . replaced by _:

Payment eventsSubscription events
payment_initiatedsubscription_created
payment_authorizedsubscription_activated
payment_capturedsubscription_renewed
payment_completedsubscription_payment_failed
payment_failedsubscription_cancelled
payment_cancelledsubscription_paused, subscription_resumed
payment_refundedsubscription_trial_started, subscription_trial_ended
payment_refund_voidedsubscription_updated

Fulfil orders from payment_completed.

Payload

{
  "event": "payment.completed",
  "timestamp": "2026-10-01T10:00:00.000Z",
  "tenantId": "…",
  "resourceId": "…",
  "platform": "acme-bookings",
  "data": {
    "orderReference": "ORD-ADF79AA433405B80",
    "amount": 120,
    "currency": "USD",
    "status": "completed",
    "paymentGateway": "stripe",
    "transactionId": "…",
    "transactionUrl": "…",
    "customer": { "email": "jo@example.com", "name": "Jo Doe" }
  }
}

For the subscription data fields, see Subscriptions.

Every order an event names for your platform (data.orderReference, and a renewal's data.renewalOrderReference) becomes one your platform can refund and check, including a subscription's first and renewal orders.

Refund events

Refund events (payment.refunded, payment.refund_voided) also carry, in data:

FieldNotes
refundId
refundAmountThis refund.
totalRefundedAll succeeded refunds on the order so far.
remainingRefundable
orderStatusrefunded or partially_refunded; completed after a voided refund took the total back to zero.
refundSourceWhere the refund was made.
reason

With an adapter, read them as $event->refund (WebhookRefund).

refundSource says where the refund was made:

refundSourceMade
Your platform idWhen you sent it with refund() / paylexer_refund(). You already recorded it, so skip it: $event->is_own_refund( 'your-id' ).
A gateway code, such as stripeIn the gateway's own dashboard.
dashboardIn the PayLexer dashboard.
api

Record every refund that isn't your own, full or partial, or your orders drift from the gateway. Set your refunded total from totalRefunded rather than adding refundAmount, so a repeated delivery changes nothing.

is_own_refund() needs API 1.1 or later.

Retries

Your handlerThe delivery answersThen
Throws400PayLexer retries it later with backoff.
Runs, but no active plugin has registered the event's platform (for example, your plugin is deactivated)503PayLexer retries it a limited number of times, so events aren't lost while your plugin is briefly off.

Throw when you couldn't save the result, for example when your database is down. If the order is simply unknown to you, return normally.

Neither the 400 nor the 503 counts toward PayLexer switching off the site's webhook after repeated failures, so one broken or deactivated plugin never stops events for the others.

Once retries run out the event is not delivered again; use paylexer_get_payment_status() to catch up on open orders.

Writing safe handlers

  • Be idempotent. A retry can deliver the same event twice, so check whether the order is already in the target state before acting. Two deliveries can also run at once, so lock per order (for example MySQL GET_LOCK()) around the check and the write.
  • Expect any order. A late payment_failed can arrive after payment_completed. Never move a paid order back to unpaid.
  • Trust only the event. Don't mark an order paid from the browser's return or redirect.

Next steps