Skip to main content
Version: 2.0.0

Developer

This page covers the parts of YuiCraftGpWebpay (Yui\GpWebpay) relevant to developers: the plugin's public services, the gateway integration contract, the webhook endpoint, the console command, and the MCP tools it registers.

There is no Twig variable or GraphQL surface in this plugin — GP WebPay integrates purely through the store's payment gateway registry and PHP services below.

Gateway registration

The plugin registers itself as a payment gateway on PaymentService::EVENT_REGISTER_PAYMENT_GATEWAYS (from the main store plugin, yui\craft\services\PaymentService):

Event::on(
PaymentService::class,
PaymentService::EVENT_REGISTER_PAYMENT_GATEWAYS,
static function(RegisterPaymentGatewaysEvent $event): void {
$event->gateways[] = \Yui\GpWebpay\gateways\GpWebpayGateway::class;
}
);

GpWebpayGateway extends the store's yui\craft\base\BasePaymentGateway and implements getHandle() (returns gpwebpay), prepareOrderData(), and prepareOrderItems(). This is the same extension point every other payment plugin in this ecosystem uses (see Barion, Stripe, Pay) — if you need to inspect or wrap gateway behavior from another plugin, hook the same event and check $event->gateways for GpWebpayGateway::class.

Services

Access services through Plugin::$plugin:

use Yui\GpWebpay\Plugin;

$api = Plugin::$plugin->getApi();
$recurring = Plugin::$plugin->getRecurringPayments();
$dashboard = Plugin::$plugin->getDashboard();
$license = Plugin::$plugin->getLicense();

ApiService

Wraps the GP WebPay signing/verification API (AdamStipak\Webpay\Api).

  • prepare(array $data, array $items, ?string $code = null): self — builds and signs a payment request from order data and items.
  • getRedirectUri(string $operation = self::PAYMENT_REQUEST_OPERATION_CREATE_ORDER): ?string — returns the URL to redirect the customer to after prepare().
  • getParams(): array — the signed request parameters (useful for logging/debugging a prepared request).
  • checkPayments(?int $orderId = null): void — re-checks pending payments against GP WebPay; same logic the Check Payments CP button triggers. Pass an order ID to check a single order, or omit to scan all pending_payment orders.
  • testCredentials(): bool — validates merchant number, key files, and password by attempting a real init/sign cycle. Same logic the Test Credentials CP button uses.

Example — checking a single order from custom code (e.g. a queue job or console command):

Plugin::$plugin->getApi()->checkPayments($order->id);

RecurringPaymentService

Manages the gpwebpay-subscription recurring/subscription lifecycle, backed by RecurringPaymentRecord.

Key methods:

  • createOrUpdateInitial(array $orderData, array $items, array $paymentParams): ?RecurringPaymentRecord — creates the initial recurring payment record when a subscription order is placed.
  • handleGatewayUpdate(array $params): ?RecurringPaymentRecord — applies a GP WebPay webhook payload to the matching record. This is what WebhookController::actionIndex() calls after signature verification.
  • getDuePayments(int $limit = 100): array / processDuePayments(int $limit = 100, bool $dryRun = false): array — used by the gpwebpay/recurring/process console command.
  • getForUser(int $userId, ?string $status = null): array / serializeForCustomer(RecurringPaymentRecord $record): array — customer-facing subscription listing, used by SubscriptionController.
  • cancelByPublicParams(), keepByPublicParams(), cancelById(), pauseById(), resumeById(), retryById() — lifecycle transitions used by both the customer subscription controller and the CP recurring-payments screen.

Recurring payment statuses: pending_initial, active, past_due, retrying, failed, paused, canceling, canceled, expired.

DashboardService and LicenseService

DashboardService backs the CP dashboard widget and index screen (aggregate counts, recent activity). LicenseService backs the license CP screens (gpwebpay/license/*) and follows the same license-registry pattern as the other add-on plugins — see the Project Manager plugin docs for the license flow itself.

Webhook endpoint

POST /gpwebpay/webhook (site route, anonymous, CSRF-exempt) receives GP WebPay's asynchronous callback for recurring/subscription payments.

Security model — both checks must pass:

  1. Optional shared-secret token param, compared with hash_equals() against the configured webhookSecret setting (legacy/extra layer, only enforced if a secret is configured).
  2. Mandatory cryptographic DIGEST/DIGEST1 verification using the same public/private key pair configured for the gateway. Verification fails closed: if keys are not configured or the digest is missing/invalid, the webhook is rejected with HTTP 403.

On success, the controller calls getRecurringPayments()->handleGatewayUpdate($params) and returns {"success": true, "id": <recurringPaymentId>}.

If you need to test webhook delivery locally, you cannot bypass digest verification — configure real (sandbox) keys first, see the Settings page.

Console command

php craft gpwebpay/recurring/process [--limit=100] [--dryRun=0]

Runs RecurringPaymentService::processDuePayments() and prints one line per processed payment plus a summary count. Intended to be run on a schedule (cron) to advance due recurring payments. --dryRun=1 reports what would be processed without charging anything — use it to verify the due-payment set before enabling the real cron entry.

MCP tools

When the yui\mcp plugin is installed, GP WebPay registers four MCP tools (McpPlugin::EVENT_REGISTER_TOOLS):

Tool namePurpose
gpwebpay_list_recurringList recurring/subscription payments, optionally filtered by status; paginated, newest update first.
gpwebpay_get_recurringGet one recurring payment by ID, order ID, increment ID, or order number, including its full attempt history.
gpwebpay_check_paymentReturn stored local payment-transaction records for an order (status, amounts, raw gateway response) without calling GP WebPay.
gpwebpay_summaryAggregate overview: active count, overdue count, failed/past-due count, subscriptions pending cancellation, totals per status.

These are read-only against local data except where noted; they do not trigger live GP WebPay calls or state changes.

Extending or wrapping this plugin

There are no plugin-specific events for hooking into the payment or webhook flow beyond the store's shared PaymentService::EVENT_REGISTER_PAYMENT_GATEWAYS. If you need to react to a recurring payment status change from another plugin/module, the supported approach is:

  • Poll RecurringPaymentService::getRecurringPayments() / getSummaryCounts(), or
  • Wrap WebhookController behavior indirectly by reading the resulting RecurringPaymentRecord after handleGatewayUpdate() runs (e.g. from a queue job triggered on order/payment save events already exposed by the store plugin).

If your integration needs a dedicated event on status transition, treat that as a feature request for developer, not something to work around by monkey-patching the trait.