Creating a Gateway
Step 2: Entry Class
<?php
declare(strict_types=1);
namespace MyPaymentGateway;
use PluginsFramework\Plugins\PluginContext;
use PluginsFramework\Plugins\PluginInterface;
final class MyPaymentGatewayPlugin implements PluginInterface
{
public function __construct(private PluginContext $ctx) {}
public function boot(): void
{
// 1. Register gateway with core
$this->ctx->registry->register('gateway', 'mygateway', [
'id' => 'mygateway',
'label' => 'My Gateway',
'module' => 'MyPaymentGateway',
'ipn_route' => 'mygateway.ipn',
'settings_prefix' => 'mygateway',
'enabled_option' => 'mygateway_gateway',
]);
// 2. Register admin menu (optional)
$this->ctx->registry->register('admin.menu', 'my-payment-gateway', [
'label' => 'My Gateway',
'route' => '/admin/mygateway',
'icon' => 'fa-solid fa-credit-card',
'permissions' => 'mygateway-settings',
]);
// 3. Hook into payment.charge filter
$this->ctx->hooks->addFilter('payment.charge', function (mixed $result, mixed $context = []): mixed {
if ($result !== null) return $result; // Another plugin handled it
$context = is_array($context) ? $context : [];
if (($context['gateway'] ?? '') !== 'mygateway') return $result;
$args = is_array($context['args'] ?? null) ? $context['args'] : [];
try {
return MyGateway::chargeRedirect($args);
} catch (\Throwable $e) {
report($e);
return back()->with('error', $e->getMessage());
}
});
}
}
Step 3: Gateway Logic Class
<?php
declare(strict_types=1);
namespace MyPaymentGateway;
final class MyGateway
{
public const GATEWAY_ID = 'mygateway';
public static function opt(string $key, mixed $default = null): mixed
{
return function_exists('get_static_option')
? get_static_option($key) ?? $default
: $default;
}
public static function enabled(): bool
{
return !empty(self::opt('mygateway_gateway'));
}
public static function getApiKey(): string
{
return (string) self::opt('mygateway_api_key', '');
}
public static function isTestMode(): bool
{
return !empty(self::opt('mygateway_test_mode'));
}
/**
* Build the redirect URL to the provider's hosted payment page.
*
* $args contains:
* - amount, title, description, ipn_url, order_id,
* - track, cancel_url, success_url, email, name, payment_type
*/
public static function chargeRedirect(array $args): \Symfony\Component\HttpFoundation\Response
{
$apiKey = self::getApiKey();
$amount = $args['amount'] ?? 0;
$track = $args['track'] ?? bin2hex(random_bytes(16));
$ipnUrl = $args['ipn_url'] ?? '';
$providerUrl = self::isTestMode()
? 'https://sandbox.provider.com/pay'
: 'https://api.provider.com/pay';
$params = [
'key' => $apiKey,
'amount' => number_format($amount, 2, '.', ''),
'currency' => 'USD',
'order_id' => $track,
'return_url' => $args['success_url'] ?? '',
'cancel_url' => $args['cancel_url'] ?? '',
'notify_url' => $ipnUrl,
'buyer_name' => $args['name'] ?? '',
'buyer_email' => $args['email'] ?? '',
];
$url = $providerUrl . '?' . http_build_query($params);
return redirect()->away($url);
}
/**
* Verify provider webhook/IPN signature.
*/
public static function verifyWebhook(\Illuminate\Http\Request $request): bool
{
$signature = $request->header('X-Signature', '');
$body = $request->getContent();
$secret = self::getApiKey();
$expected = hash_hmac('sha256', $body, $secret);
return hash_equals($expected, $signature);
}
/**
* Parse provider response to extract payment details.
*/
public static function parseWebhook(\Illuminate\Http\Request $request): array
{
$data = $request->all();
return [
'payment_type' => $data['payment_type'] ?? '',
'record_id' => (int) ($data['record_id'] ?? 0),
'transaction_id' => $data['transaction_id'] ?? null,
'amount' => (float) ($data['amount'] ?? 0),
'status' => $data['status'] ?? '',
];
}
}
Last updated: September 2026
Still stuck?
Our support team is ready to help you get set up.

