Skip to main content
WooCommerce’s payment gateway API lets you plug in any payment provider by extending the WC_Payment_Gateway class. This guide walks you through building a custom plugin that collects card details at checkout and processes them through the Accelebit API server-to-server, including 3D Secure redirects, refunds, and asynchronous webhook updates.

Architecture

The sequence below shows how WooCommerce, your plugin, and Accelebit interact during a checkout.

Prerequisites

Before you start, make sure you have:
  • An Accelebit account with API keys
  • WordPress with WooCommerce 8.0+ installed
  • PHP 8.0+
  • An SSL certificate on your WordPress site (required for handling card data)

Plugin structure

Create the following files in wp-content/plugins/accelebit-gateway/:
accelebit-gateway/
  accelebit-gateway.php
  includes/
    class-accelebit-gateway.php
    class-accelebit-webhook-handler.php
1

Plugin bootstrap

The main plugin file registers the gateway class with WooCommerce and hooks up the webhook listener.
<?php
/**
 * Plugin Name: Accelebit Payment Gateway
 * Description: Accept card payments via Accelebit Payments API
 * Version: 1.0.0
 * Requires PHP: 8.0
 */

defined('ABSPATH') || exit;

add_action('plugins_loaded', function () {
    if (!class_exists('WC_Payment_Gateway')) {
        return;
    }
    require_once __DIR__ . '/includes/class-accelebit-gateway.php';
    require_once __DIR__ . '/includes/class-accelebit-webhook-handler.php';

    add_filter('woocommerce_payment_gateways', function ($gateways) {
        $gateways[] = 'WC_Accelebit_Gateway';
        return $gateways;
    });

    // Register webhook listener
    add_action('woocommerce_api_accelebit_webhook', [new Accelebit_Webhook_Handler(), 'handle']);
});
2

Payment gateway class

The gateway class handles the settings form, renders the card fields at checkout, submits payments to Accelebit, and processes refunds. The key methods are shown below.
<?php

class WC_Accelebit_Gateway extends WC_Payment_Gateway
{
    private string $api_url = 'https://api.gateway.accelebit.com';

    public function __construct()
    {
        $this->id = 'accelebit';
        $this->method_title = 'Accelebit';
        $this->method_description = 'Accept card payments via Accelebit Payments API';
        $this->has_fields = true;
        $this->supports = ['products', 'refunds'];

        $this->init_form_fields();
        $this->init_settings();

        $this->title = $this->get_option('title', 'Credit / Debit Card');
        $this->description = $this->get_option('description', 'Pay securely with your card.');
        $this->enabled = $this->get_option('enabled');

        add_action(
            'woocommerce_update_options_payment_gateways_' . $this->id,
            [$this, 'process_admin_options']
        );
    }

    public function init_form_fields(): void
    {
        $this->form_fields = [
            'enabled' => [
                'title' => 'Enable/Disable',
                'type' => 'checkbox',
                'label' => 'Enable Accelebit Payments',
                'default' => 'no',
            ],
            'title' => [
                'title' => 'Title',
                'type' => 'text',
                'default' => 'Credit / Debit Card',
            ],
            'description' => [
                'title' => 'Description',
                'type' => 'textarea',
                'default' => 'Pay securely with your card.',
            ],
            'secret_key' => [
                'title' => 'Secret Key',
                'type' => 'password',
                'description' => 'Your Accelebit secret key (starts with smtgw_sk_)',
            ],
            'webhook_secret' => [
                'title' => 'Webhook Secret',
                'type' => 'password',
                'description' => 'Used to verify incoming webhook signatures',
            ],
        ];
    }

    public function payment_fields(): void
    {
        if ($this->description) {
            echo '<p>' . esc_html($this->description) . '</p>';
        }
        ?>
        <fieldset>
            <p class="form-row form-row-wide">
                <label for="accelebit-card-number">Card Number <span class="required">*</span></label>
                <input id="accelebit-card-number" name="accelebit_card_number"
                       type="text" autocomplete="cc-number" maxlength="19" />
            </p>
            <p class="form-row form-row-first">
                <label for="accelebit-expiry-month">Expiry Month <span class="required">*</span></label>
                <input id="accelebit-expiry-month" name="accelebit_expiry_month"
                       type="text" autocomplete="cc-exp-month" maxlength="2" placeholder="MM" />
            </p>
            <p class="form-row form-row-last">
                <label for="accelebit-expiry-year">Expiry Year <span class="required">*</span></label>
                <input id="accelebit-expiry-year" name="accelebit_expiry_year"
                       type="text" autocomplete="cc-exp-year" maxlength="2" placeholder="YY" />
            </p>
            <p class="form-row form-row-first">
                <label for="accelebit-cvv">CVV <span class="required">*</span></label>
                <input id="accelebit-cvv" name="accelebit_cvv"
                       type="text" autocomplete="cc-csc" maxlength="4" />
            </p>
            <p class="form-row form-row-last">
                <label for="accelebit-holder">Cardholder Name <span class="required">*</span></label>
                <input id="accelebit-holder" name="accelebit_holder_name"
                       type="text" autocomplete="cc-name" />
            </p>
        </fieldset>
        <?php
    }

    public function process_payment($order_id): array
    {
        $order = wc_get_order($order_id);

        $payload = [
            'amount' => $order->get_total(),
            'currency' => $order->get_currency(),
            'paymentMethod' => 'card',
            'customerId' => (string) $order->get_customer_id() ?: $order->get_billing_email(),
            'card' => [
                'number' => sanitize_text_field($_POST['accelebit_card_number']),
                'expiryMonth' => sanitize_text_field($_POST['accelebit_expiry_month']),
                'expiryYear' => sanitize_text_field($_POST['accelebit_expiry_year']),
                'cvv' => sanitize_text_field($_POST['accelebit_cvv']),
                'holderName' => sanitize_text_field($_POST['accelebit_holder_name']),
            ],
            'billing' => [
                'firstName' => $order->get_billing_first_name(),
                'lastName' => $order->get_billing_last_name(),
                'email' => $order->get_billing_email(),
                'phone' => $order->get_billing_phone(),
                'country' => $order->get_billing_country(),
                'address' => $order->get_billing_address_1(),
                'addressLine2' => $order->get_billing_address_2() ?: null,
                'city' => $order->get_billing_city(),
                'postalCode' => $order->get_billing_postcode(),
                'state' => $order->get_billing_state() ?: null,
            ],
            'returnUrl' => $this->get_return_url($order),
            'merchantRef' => (string) $order_id,
        ];

        $response = wp_remote_post($this->api_url . '/v1/payments', [
            'headers' => [
                'Content-Type' => 'application/json',
                'X-API-Key' => $this->get_option('secret_key'),
                'Idempotency-Key' => 'woo_' . $order_id . '_' . time(),
            ],
            'body' => wp_json_encode($payload),
            'timeout' => 30,
        ]);

        if (is_wp_error($response)) {
            wc_add_notice('Payment failed. Please try again.', 'error');
            return ['result' => 'fail'];
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        // 3DS redirect required
        if (!empty($body['data']['threeDsRequired'])) {
            $order->update_meta_data('_accelebit_payment_id', $body['data']['id']);
            $order->save();

            return [
                'result' => 'success',
                'redirect' => $body['data']['threeDsData']['redirectUrl'],
            ];
        }

        // Payment captured
        if (($body['data']['status'] ?? '') === 'captured') {
            $order->payment_complete($body['data']['id']);
            $order->add_order_note('Accelebit payment captured: ' . $body['data']['id']);

            return [
                'result' => 'success',
                'redirect' => $this->get_return_url($order),
            ];
        }

        // Payment failed
        $error_msg = $body['error']['message'] ?? 'Payment was declined.';
        wc_add_notice($error_msg, 'error');
        $order->update_status('failed', 'Accelebit: ' . $error_msg);

        return ['result' => 'fail'];
    }

    public function process_refund($order_id, $amount = null, $reason = ''): bool
    {
        $order = wc_get_order($order_id);
        $payment_id = $order->get_transaction_id();

        if (!$payment_id) {
            return false;
        }

        $response = wp_remote_post($this->api_url . '/v1/refunds', [
            'headers' => [
                'Content-Type' => 'application/json',
                'X-API-Key' => $this->get_option('secret_key'),
                'Idempotency-Key' => 'woo_refund_' . $order_id . '_' . time(),
            ],
            'body' => wp_json_encode([
                'transactionId' => $payment_id,
                'amount' => number_format((float) $amount, 2, '.', ''),
                'currency' => $order->get_currency(),
                'reason' => $reason ?: 'Refund from WooCommerce',
            ]),
            'timeout' => 30,
        ]);

        if (is_wp_error($response)) {
            return false;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        $status = wp_remote_retrieve_response_code($response);

        if ($status === 201) {
            $order->add_order_note(
                sprintf('Accelebit refund created: %s', $body['data']['id'] ?? 'unknown')
            );
            return true;
        }

        return false;
    }
}
3

Webhook handler

The webhook handler listens for Accelebit events and updates WooCommerce order statuses asynchronously. This is especially important for 3DS payments, where the final status arrives after the customer has left the checkout page.
<?php

class Accelebit_Webhook_Handler
{
    public function handle(): void
    {
        $payload = file_get_contents('php://input');
        $signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';

        // Verify signature
        $settings = get_option('woocommerce_accelebit_settings', []);
        $secret = $settings['webhook_secret'] ?? '';
        $expected = hash_hmac('sha256', $payload, $secret);

        if (!hash_equals($expected, $signature)) {
            wp_send_json(['error' => 'Invalid signature'], 401);
            return;
        }

        $event = json_decode($payload, true);
        $type = $event['event'] ?? '';
        $data = $event['data'] ?? [];

        match ($type) {
            'payment.captured' => $this->handle_captured($data),
            'payment.failed' => $this->handle_failed($data),
            'refund.created' => $this->handle_refund($data),
            default => null,
        };

        wp_send_json(['received' => true], 200);
    }

    private function handle_captured(array $data): void
    {
        $orders = wc_get_orders([
            'meta_key' => '_accelebit_payment_id',
            'meta_value' => $data['transactionId'],
            'limit' => 1,
        ]);

        if (!empty($orders)) {
            $order = $orders[0];
            $order->payment_complete($data['transactionId']);
            $order->add_order_note('Accelebit payment captured via webhook');
        }
    }

    private function handle_failed(array $data): void
    {
        $orders = wc_get_orders([
            'meta_key' => '_accelebit_payment_id',
            'meta_value' => $data['transactionId'],
            'limit' => 1,
        ]);

        if (!empty($orders)) {
            $orders[0]->update_status('failed', 'Accelebit payment failed via webhook');
        }
    }

    private function handle_refund(array $data): void
    {
        // Refund webhooks can be used for reconciliation logging
    }
}
4

Configure the plugin in WooCommerce admin

After uploading the plugin files:
  1. Activate the plugin in WordPress Admin > Plugins.
  2. Go to WooCommerce > Settings > Payments > Accelebit.
  3. Enable the gateway and enter your Accelebit secret key.
  4. Set your webhook URL in the Accelebit Dashboard:
    https://yourstore.com/?wc-api=accelebit_webhook
    
  5. Copy the webhook signing secret from the Dashboard into the Webhook Secret field in the plugin settings.

Testing

Before taking live payments, verify your integration end-to-end:
  1. Use your Accelebit test secret key (smtgw_sk_test_...).
  2. Place a test order using card number 4111111111111111.
  3. Confirm the order status updates to “Processing” after payment.
  4. Issue a refund from WooCommerce and verify it processes through Accelebit.