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 afterprepare().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 theCheck PaymentsCP button triggers. Pass an order ID to check a single order, or omit to scan allpending_paymentorders.testCredentials(): bool— validates merchant number, key files, and password by attempting a real init/sign cycle. Same logic theTest CredentialsCP 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 whatWebhookController::actionIndex()calls after signature verification.getDuePayments(int $limit = 100): array/processDuePayments(int $limit = 100, bool $dryRun = false): array— used by thegpwebpay/recurring/processconsole command.getForUser(int $userId, ?string $status = null): array/serializeForCustomer(RecurringPaymentRecord $record): array— customer-facing subscription listing, used bySubscriptionController.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:
- Optional shared-secret
tokenparam, compared withhash_equals()against the configuredwebhookSecretsetting (legacy/extra layer, only enforced if a secret is configured). - Mandatory cryptographic
DIGEST/DIGEST1verification 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 name | Purpose |
|---|---|
gpwebpay_list_recurring | List recurring/subscription payments, optionally filtered by status; paginated, newest update first. |
gpwebpay_get_recurring | Get one recurring payment by ID, order ID, increment ID, or order number, including its full attempt history. |
gpwebpay_check_payment | Return stored local payment-transaction records for an order (status, amounts, raw gateway response) without calling GP WebPay. |
gpwebpay_summary | Aggregate 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
WebhookControllerbehavior indirectly by reading the resultingRecurringPaymentRecordafterhandleGatewayUpdate()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.