Dorokhov.codes

Creating a payment gateway

Types of payment gateway:

Type Description
Form based A user must click a button on a form that then redirects them to the payment processor on the gateway’s own website.
iFrame based A gateway payment system is loaded inside an iframe on your store.
Direct Payment fields are shown directly on the checkout page and the payment is made when ‘place order’ is pressed.
Offline No online payment is made.

1. Creating a child class

There are 5 properties in a child class that are required to specify (id, icon, has_fields, method_title, method_description).

function init_your_gateway_class() {
    class WC_Gateway_Your_Gateway extends WC_Payment_Gateway {
        public function __construct() {
            /**
             * Unique ID of the gateway. Used in option names.
             */
            $this->id = 'your_gateway';
            
            /**
             * If you want to show an image next to the gateway’s name 
             * on the frontend, enter a URL to an image.
             */
            $this->icon = plugin_dir_url( __FILE__ ) . 'assets/images/logo.png';
            
            /**
             * Can be set to true if you want payment fields 
             * to show on the checkout (if doing a direct integration).
             */
            $this->has_fields = false;
            
            /**
             * Title of the payment method shown on the admin page.
             */
            $this->method_title = 'My payment gateway';
            
            /**
             * Description for the payment method shown on the admin page.
             */
            $this->method_description = 'We are accepting credit cards.';

        }
    }
}

Initialize it after all the plugins are loaded:

add_action( 'plugins_loaded', 'init_your_gateway_class' );

As well as defining the class, we need to also tell WC that it exists. We will do this by filtering woocommerce_payment_gateways:

function add_your_gateway_class( $methods ) {
    $methods[] = 'WC_Gateway_Your_Gateway'; 
    return $methods;
}

add_filter( 'woocommerce_payment_gateways', 'add_your_gateway_class' );

2. Specify options for admin page

All this I have described on the Settings API page.

The basic fields that we should include are enabled, title, and description.

  • Enabled will be a checkbox that allows the user to enable or disable the gateway on the checkout page.
  • Title is different from the title we set in the constructor; this will be the title shown to customers at checkout and can be different from the admin title.
  • Description will be shown under the title when the gateway is selected at checkout, and tells customers what to do next.
'enabled' => [
    'title'   => __( 'Enable/Disable', 'wc-gateway-offline' ),
    'type'    => 'checkbox',
    'label'   => __( 'Enable Offline Payment', 'wc-gateway-offline' ),
    'default' => 'yes'
],

'title' => [
    'title'       => __( 'Title', 'wc-gateway-offline' ),
    'type'        => 'text',
    'description' => __( 'This controls the title for the payment method the customer sees during checkout.', 'wc-gateway-offline' ),
    'default'     => __( 'Offline Payment', 'wc-gateway-offline' ),
    'desc_tip'    => true,
],

'description' => [
    'title'       => __( 'Description', 'wc-gateway-offline' ),
    'type'        => 'textarea',
    'description' => __( 'Payment method description that the customer will see on your checkout.', 'wc-gateway-offline' ),
    'default'     => __( 'Please remit payment to Store Name upon pickup or delivery.', 'wc-gateway-offline' ),
    'desc_tip'    => true,
],

And then we should add the next calls to the constructor:

/**
 * Initialise settings form fields.
 *
 * Add an array of fields to be displayed on the gateway's settings screen.
 */
$this->init_form_fields();

/**
 * Init settings for gateways.
 */
$this->init_settings();

3. Processing payments

/**
 * Process Payment.
 *
 * Process the payment. This should return the success
 * and redirect in an array. e.g:
 *
 *        return array(
 *            'result'   => 'success',
 *            'redirect' => $this->get_return_url( $order )
 *        );
 *
 * @param int $order_id Order ID.
 * @return array
 */
public function process_payment( $order_id )
{
    $order = wc_get_order( $order_id );
    // or: $order = new WC_Order( $order_id );
    
    // Empty the cart:
    WC()->cart->empty_cart();
    
    // Error message if needed:
    wc_add_notice( 'Something went wrong.', 'error' );
    
    // Here we should specify the return url (thank you page or gateways' checkout page).
    return [
        'result'    => 'success',
        'redirect'  => $this->get_return_url( $order ),
    ];
}

We shouldn’t use reduce_order_stock() function:

/**
 * Stock levels are updated via actions (`woocommerce_payment_complete` and in 
 * transitions between order statuses), so it’s no longer needed to manually 
 * call the methods reducing stock levels while processing the payment.
 */
$order->reduce_order_stock();

WC_Payment_Gateway class has a method get_return_url() for getting a URL for thank you page. Without a parameter it will return a URL for a standard “thank you” page, but if we specify an order argument, we will get “thank you” page with all the details about the order.

After payment completing we should call:

$order->payment_complete();

If we are using an offline payment method, then we can do something like this:

// Mark as on-hold (we're awaiting the cheque)
$order->update_status('on-hold', __( 'Awaiting cheque payment', 'woocommerce' ));

If an error occurs

If payment fails, you should throw an error and return null:

wc_add_notice( __('Payment error:', 'woothemes') . $error_message, 'error' );
return;

3.1 Form request to the Gateway checkout page

// Receiving the order amount:
$order->get_total();

// Getting a current shop currency:
get_woocommerce_currency();

3.2 Redirect to Gateway checkout page

add_query_arg();