Skip to content
Building a WooCommerce Payment Gateway from Scratch

Building a WooCommerce Payment Gateway from Scratch

Al Amin Ahamed

Al Amin Ahamed

Senior Engineer

12 min read
𝕏 in

I've built payment gateway integrations for Paystack, GoCardless, Razorpay, and ShipStation. Here's the complete guide I wish existed when I started.

Gateway Architecture

A WooCommerce gateway is a class that extends WC_Payment_Gateway. You register it with:

PHP
add_filter('woocommerce_payment_gateways', function ($gateways) {
    $gateways[] = My_Gateway::class;
    return $gateways;
});

The Four Methods You Must Implement

1. process_payment()

This is called when the customer clicks "Place Order". You must:

  1. Create the transaction on your payment provider's API
  2. Return ['result' => 'success', 'redirect' => $url] to redirect the customer
PHP
public function process_payment(int $order_id): array
{
    $order = wc_get_order($order_id);

    $response = $this->api->create_transaction([
        'amount' => $order->get_total() * 100, // kobo/pence/cents
        'email'  => $order->get_billing_email(),
        'reference' => $this->generate_reference($order_id),
    ]);

    if (! $response->success) {
        wc_add_notice($response->message, 'error');
        return ['result' => 'failure'];
    }

    return [
        'result'   => 'success',
        'redirect' => $response->authorization_url,
    ];
}

2. Webhook Handler

Register a REST route for your webhook:

PHP
add_action('woocommerce_api_my_gateway', [$this, 'handle_webhook']);

Always verify the signature before processing:

PHP
public function handle_webhook(): void
{
    $payload = file_get_contents('php://input');
    $signature = $_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] ?? '';

    if (hash_hmac('sha512', $payload, $this->secret_key) !== $signature) {
        status_header(401);
        exit;
    }

    $event = json_decode($payload);
    // process event...
}

3. process_refund()

WooCommerce calls this from the admin when a refund is issued:

PHP
public function process_refund(int $order_id, float $amount = null, string $reason = ''): bool|WP_Error
{
    $order = wc_get_order($order_id);
    $transaction_id = $order->get_transaction_id();

    $result = $this->api->refund($transaction_id, (int)($amount * 100));

    if ($result->success) {
        $order->add_order_note("Refunded {$amount} via My Gateway");
        return true;
    }

    return new WP_Error('refund_failed', $result->message);
}

Testing Checklist

  • [ ] Successful payment completes order
  • [ ] Failed payment shows error, order stays pending
  • [ ] Webhook correctly marks order as paid
  • [ ] Refund deducts correct amount
  • [ ] Duplicate webhook delivery doesn't double-process
  • [ ] Invalid signature rejects with 401
Share 𝕏 in
Al Amin Ahamed

Al Amin Ahamed

Senior software engineer & AI practitioner. Laravel, PHP, WordPress plugins, WooCommerce extensions.

About me →

One email a month. No noise.

What I shipped, what I read, occasional deep dive. Unsubscribe anytime.