WordPress GuidesPlatform Adapters

Platform adapters

The developer API as a class: typed requests, exceptions instead of WP_Error, and one method for every webhook.

PayLexer\Integration\PlatformAdapter wraps the same calls as the paylexer_* functions. Use whichever you prefer: an adapter registers the same platform, gets the same orders and is subject to the same limits.

FunctionsAdapter
Platform IDPassed to every callSet once, as a property
InputArraysCheckoutRequest, SubscriptionRequest, Customer, LineItem
ErrorsReturns WP_ErrorThrows PayLexer\Integration\Exception
WebhooksOne action per eventOne on_event() method

1. Write the adapter

use PayLexer\Integration\PlatformAdapter;
use PayLexer\Integration\WebhookEvent;

final class Acme_PayLexer extends PlatformAdapter {

	protected string $id       = 'acme-bookings';
	protected string $name     = 'Acme Bookings';
	protected string $by       = 'PayLexer for Acme Bookings';
	protected string $plugin     = 'acme-bookings/acme-bookings.php';
	protected string $plugin_url = 'https://wordpress.org/plugins/acme-bookings/';
	protected array  $supports   = array( 'payments', 'refunds' );

	public static function instance(): self {
		static $instance = null;
		return $instance ??= new self();
	}

	// Logos for PayLexer → General: the platform's, and your add-on's own.
	private function __construct() {
		$this->icon       = plugins_url( 'assets/acme-bookings.svg', ACME_PAYLEXER_FILE );
		$this->addon_icon = plugins_url( 'assets/icon.png', ACME_PAYLEXER_FILE );
	}

	public function on_event( WebhookEvent $event ): void {
		$booking = acme_find_booking_by_paylexer_reference( $event->order_reference );
		if ( ! $booking ) {
			return;
		}
		// Events can arrive late or out of order: never move a refunded booking back to paid,
		// or a paid or refunded one to failed.
		match ( $event->type ) {
			'payment.completed' => $booking->is_refunded() ? null : $booking->mark_paid(),
			'payment.failed'    => $booking->is_paid() || $booking->is_refunded() ? null : $booking->mark_failed(),
			// Refunds made in the gateway's or PayLexer's dashboard, full or partial. Skip the
			// ones your own refund() sent: you recorded those already.
			'payment.refunded'  => $event->is_own_refund( $this->id ) ? null : $booking->set_refunded_total( $event->refund->total_refunded ?? $event->refund->amount ),
			default             => null,
		};
	}
}

The properties are the same as the paylexer_register_platform() arguments. $id and $name are required.

is_own_refund() needs API 1.1 or later. See WebhookEvent.

2. Register it

PlatformAdapter only exists while PayLexer is active, so load your class inside paylexer_init.

add_action( 'paylexer_init', function () {
	require_once __DIR__ . '/class-acme-paylexer.php';
	paylexer_register_adapter( Acme_PayLexer::instance() );
} );

paylexer_register_adapter() returns true or a WP_Error, like paylexer_register_platform().

3. Create a checkout

use PayLexer\Integration\CheckoutRequest;
use PayLexer\Integration\Customer;
use PayLexer\Integration\Exception as PayLexerException;
use PayLexer\Integration\LineItem;

try {
	$checkout = Acme_PayLexer::instance()->create_checkout( new CheckoutRequest(
		gateway: 'stripe',
		amount: 120.00,
		currency: 'USD',
		customer: new Customer( $email, $first_name, $last_name ),
		return_url: $thanks_url,
		reference: (string) $booking->id,
		items: array( new LineItem( 'Kayak tour', 60.00, 2 ) ),
	) );
} catch ( PayLexerException $e ) {
	// $e->get_error_code() is the same code the functions return, e.g. paylexer_unknown_gateway.
	return;
}

update_post_meta( $booking->id, '_paylexer_order_reference', $checkout['order_reference'] );
wp_redirect( $checkout['checkout_url'] );

create_checkout() returns the same array as paylexer_create_checkout(). Send the shopper to checkout_url, or pass the whole array to PayLexer.openCheckout() for the in-page modal.

Settle orders in on_event(), not when the shopper comes back to return_url. The return proves nothing. If you want to show "paid" straight away, call get_payment_status(), which asks the gateway.

4. Handle webhooks

on_event() receives every verified event for your platform. PayLexer has already checked the signature, the tenant and replays.

Throw to have the event delivered again later, for example when your database is down.

WebhookEvent reads payment and subscription events the same way:

PropertyDescription
typee.g. payment.completed, subscription.renewed
order_referenceThe order from create_checkout(). For a renewal, the renewal's order.
subscription_idFrom create_subscription(); empty for one-time payments.
amount, currency, status
rawThe whole event, as the hook-based API receives it.

See Webhooks for the event list and retry rules.

Next steps