Integration of Payment Gateway in WooCommerce
WooCommerce provides standardized interface for payment gateways via WC_Payment_Gateway class. Ready plugins exist for most popular providers, but often outdated, contain bugs or lack needed functionality — API refunds, partial capture, webhooks. In such cases write custom gateway class.
Custom gateway development takes 2–4 business days including tests.
Plugin Structure
wp-content/plugins/mypay-gateway/
├── mypay-gateway.php # Entry point, registration
├── includes/
│ ├── class-wc-gateway-mypay.php
│ └── class-mypay-api-client.php
└── assets/
└── js/checkout.js
Basic Gateway Class
class WC_Gateway_MyPay extends WC_Payment_Gateway
{
public function __construct()
{
$this->id = 'mypay';
$this->method_title = 'MyPay';
$this->has_fields = false;
$this->supports = ['products', 'refunds'];
$this->init_form_fields();
$this->init_settings();
$this->title = $this->get_option('title');
$this->description = $this->get_option('description');
$this->api_key = $this->get_option('api_key');
$this->secret_key = $this->get_option('secret_key');
$this->testmode = 'yes' === $this->get_option('testmode');
add_action('woocommerce_update_options_payment_gateways_' . $this->id,
[$this, 'process_admin_options']);
add_action('woocommerce_api_mypay_callback', [$this, 'handle_webhook']);
}
public function init_form_fields(): void
{
$this->form_fields = [
'enabled' => ['title' => 'Enable', 'type' => 'checkbox', 'default' => 'yes'],
'title' => ['title' => 'Title', 'type' => 'text', 'default' => 'Bank Card'],
'api_key' => ['title' => 'API Key', 'type' => 'password'],
'secret_key' => ['title' => 'Secret Key', 'type' => 'password'],
'testmode' => ['title' => 'Test Mode', 'type' => 'checkbox', 'default' => 'no'],
];
}
}
Payment Process
public function process_payment(int $order_id): array
{
$order = wc_get_order($order_id);
$client = new MyPay_API_Client($this->api_key, $this->secret_key, $this->testmode);
$response = $client->createPayment([
'amount' => (int) ($order->get_total() * 100),
'currency' => get_woocommerce_currency(),
'order_id' => $order_id,
'description' => 'Order #' . $order->get_order_number(),
'success_url' => $this->get_return_url($order),
'fail_url' => wc_get_checkout_url(),
'callback_url'=> home_url('/wc-api/mypay_callback'),
'customer' => [
'email' => $order->get_billing_email(),
'phone' => $order->get_billing_phone(),
],
]);
if ($response['status'] === 'error') {
wc_add_notice($response['message'], 'error');
return ['result' => 'failure'];
}
$order->update_meta_data('_mypay_payment_id', $response['payment_id']);
$order->save();
return [
'result' => 'success',
'redirect' => $response['payment_url'],
];
}
Webhook Handling
Webhook URL: https://site.com/?wc-api=mypay_callback
public function handle_webhook(): void
{
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
// Signature verification
$signature = hash_hmac('sha256', $raw, $this->secret_key);
if (!hash_equals($signature, $_SERVER['HTTP_X_SIGNATURE'] ?? '')) {
wp_die('Invalid signature', 'Forbidden', ['response' => 403]);
}
$order = wc_get_orders([
'meta_key' => '_mypay_payment_id',
'meta_value' => $data['payment_id'],
'limit' => 1,
])[0] ?? null;
if (!$order) {
wp_die('Order not found', '', ['response' => 404]);
}
match ($data['status']) {
'succeeded' => $order->payment_complete($data['payment_id']),
'failed' => $order->update_status('failed', 'Payment declined: ' . $data['reason']),
'refunded' => $order->update_status('refunded'),
default => null,
};
wp_die('OK', '', ['response' => 200]);
}
Refunds in WooCommerce
Refund support added via process_refund method:
public function process_refund(int $order_id, ?float $amount = null, string $reason = ''): bool|\WP_Error
{
$order = wc_get_order($order_id);
$payment_id = $order->get_meta('_mypay_payment_id');
if (!$payment_id) {
return new \WP_Error('mypay_refund', 'Payment ID not found');
}
$client = new MyPay_API_Client($this->api_key, $this->secret_key, $this->testmode);
$response = $client->refund($payment_id, (int) ($amount * 100), $reason);
if ($response['status'] === 'error') {
return new \WP_Error('mypay_refund', $response['message']);
}
$order->add_order_note(sprintf('Refund %s RUB via MyPay. ID: %s', $amount, $response['refund_id']));
return true;
}
After this, "Refund" button in WooCommerce admin auto-calls this method.
Testing
WooCommerce has built-in test mode. For webhook testing use ngrok or localtunnel — provider must reach local server. All test card numbers from provider docs.







