How to Create Payment Gateway for Woocommerce

Woocommerce is one of the leading e-commerce platforms. Woocommerce supports multiple Payment Gateways by default but you can also create a plugin for payment gateway on your own. In this article, we will be discussing how to create a payment gateway plugin for Woocommerce. Following are the steps to create a payment gateway plugin:

    1. Create a Plugin
    2. Extend WC_Payment _Gateway
    3. Create your Plugin Options
    4. Field Validations
    5. Payments Process

Create a Plugin

There can be two ways of creating a plugin in WordPress. Fill in the information like Plugin Name, Plugin URL, Description, Version, Author, Author URL. etc. Either you can create just one file for starters or you can use WordPress Plugin Boilerplate Generator.

Plugin Creation
Plugin Creation

Upload it to directory /wp-content/plugins/ and you will see the plugin appearing in your admin area. You can activate it from here.

Plugin Activation
Plugin Activation

Extend WC_Payment _Gateway

Woocommerce provide us multiple core classes like WC_Payment_Gateway which are extendable to add custom functionality. Payment gateways are php classes. For custom payment functionality, we will have to extend the default WooCommerce class WC_Payment_Gateway with our custom class WC_MyApp_Gateway to gain the functionality associated with WC_Payment_Gateway. Our class will have the following functions:

  • constructor()
  • init_form_fields()
  • payment_fields()
  • validate_fields()
  • process_payment()

All the business logic will be done in this WC_MyApp_Gateway. Following code shows us the plugin custom class structure.

class WC_MyApp_Gateway extends WC_Payment_Gateway {

 		public function __construct() {
 		
 		}
 		public function init_form_fields(){

	 	}
		public function payment_fields() {
 
		}
	 	public function payment_scripts() {
 
	 	}
		public function validate_fields() {
 
		}
 		public function process_payment( $order_id ) {
 
	 	}
		public function webhook() {
 
	 	}
 	}
}

Create Plugin Options

Let’s assume that we are creating a payment gateway plugin, which redirects the user to the Payments gateway’s website and redirects to the success or failure page after the payment is done. And for the backend creates a webhook to receive the status of the payment via a callback. We are going to make a plugin which supports particular countries and currencies. For this purpose, we will have to declare the countries we want to support and endpoint URLs in order to determine the success or failure of payments:

private $allowedCurrencies = array(
       		 'SEK' ,'EUR' ,'NOK' ,'USD' ,'CLP'
  );
private $SUCCESS_CALLBACK_URL = "myApp_payment_success";
private $FAILURE_CALLBACK_URL = "smyApp_payment_failure";
private $SUCCESS_REDIRECT_URL = "/checkout/order-received/";
private $FAILURE_REDIRECT_URL = "/checkout/order-received/";
private $API_HOST = " ";
private $API_SESSION_CREATE_ENDPOINT = "/checkout/v1/session/create";

In the constructor of the class WC_MyApp_Gateway, we will do the following:

  1. Define Class Properties
  2. Initialize Plugin Settings
  3. Append Options to Properties
  4. Save Options
  5. Register Callbacks

1. Define Class Properties

In class Constructor, we will define class properties. These properties include ‘id’, an ‘icon’ to show at the checkout page, ‘Method title’ and ‘Method Description’. These properties will show at options page and checkout page.

$this->id = ‘MyApp’;     // payment gateway plugin ID
$this->icon =” ";    // URL of icon that will be displayed on the checkout page
$this->has_fields = true;
$this->method_title = ‘MyApp Payment Gateway Plugin';
$this->method_description = 'MyApp Payment Gateway Plugin.'; //displayed on option page
$this->supports = array(
                   'products'
        );

2. Initialize Plugin Settings

When Class properties are defined, the second step is to initialize form fields. For this purpose, we will use init_form_fields() function.

 
 // Method with all the options fields
$this->init_form_fields();

Depending on the payment process, the plugin may have different form fields. We will use

  • Enable / Disable
  • Title
  • Description
  • Test Mode
  • Test MerchantID
  • Test Auth Token
  • Live MerchantID
  • Live Auth Token
  • Country
public function init_form_fields() {
    $this->form_fields = array(
     'enabled' => array(
        'title'       => 'Enable/Disable',
        'label'       => 'Enable myApp Gateway',
        'type'        => 'checkbox',
        'description' => '',
        'default'     => 'no'
    ),
    'title' => array(
        'title'       => 'Title',
        'type'        => 'text',
        'description' => 'This controls the title which the user sees during checkout.',
        'default'     => 'MyApp',
        'desc_tip'    => true,
    ),
    'description' => array(
        'title'       => 'Description',
        'type'        => 'text',
        'description' => 'This controls the description which the user sees during checkout.',
        'desc_tip'    => true,
    ),
    'testmode' => array(
        'title'       => 'Test mode',
        'label'       => 'Enable Test Mode',
        'type'        => 'checkbox',
        'description' => 'Place the payment gateway in test mode using test API keys.',
        'default'     => 'yes',
        'desc_tip'    => true,
    ),
    'test_merchant_id' => array(
        'title'       => 'Test MerchantID',
        'type'        => 'text',
        'placeholder' => 'Enter Test MerchantID'
    ),
    'test_auth_token' => array(
        'title'       => 'Test Auth Token',
        'type'        => 'text',
        'placeholder' => 'Enter Test Auth Token'
    ),
    'merchant_id' => array(
        'title'       => 'Live MerchantID',
        'type'        => 'text',
        'placeholder' => 'Enter Live MerchantID'
    ),
    'auth_token' => array(
        'title'       => 'Live Auth Token',
        'type'        => 'text',
        'placeholder' => 'Enter Live Auth Token'
    ),
    'country_code' => array(
        'type' => 'select',
        'title' => 'Country',
        'label' => 'Country',
        'options' => array(
            ''   => 'Select Country',
            'SE' => 'Sweden',
            'FI' => 'Finland',
            'NO' => 'Norway',
            'DE' => 'Germany',
            'CL' => 'Chile',
        ),
     )
  )
}

Our form field or options page will look like this:

Plugin Options Page
Plugin Options Page

3. Append Options to Properties

Once the form fields are initialized, we will append the field options to the properties we defined earlier in the constructor.

$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );

 // Checking if valid to use
 if($this->is_valid_for_use()) {
 $this->enabled = $this->get_option( 'enabled' );
}
else {
    $this->enabled = 'no';
}

// Site URL
$this->siteUrl = get_site_url();

$this->testmode = 'yes' === $this->get_option( 'testmode' );
$this->merchant_id = $this->testmode ? $this->get_option( 'test_merchant_id' ) : $this->get_option( 'merchant_id' );
$this->auth_token = $this->testmode ? $this->get_option( 'test_auth_token' ) :
$this->get_option( 'auth_token' );
$this->country_code = $this->get_option( 'country_code' );

4. Save Options

After appending the options availed from form fields to the properties, now we will save the options using action hook:

add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );

5. Register Callbacks

Now we will register the callback hooks which we will use to receive the payment response from the gateway.


add_action( 'woocommerce_api_'. $this->SUCCESS_CALLBACK_URL, array( $this, 'payment_success'));
add_action( 'woocommerce_api_' . $this->FAILURE_CALLBACK_URL, array( $this, 'payment_failure'));

After we have done this, the endpoints created for the above-created callbacks will look like this:


$this->siteUrl . "//wc-api/" . {SUCCESS_CALLBACK_URL}
$this->siteUrl . "//wc-api/" . {FAILURE_CALLBACK_URL}

Our complete constructor will look like this:

$this->id = ‘MyApp’;     // payment gateway plugin ID
$this->icon =” ";    // URL of icon that will be displayed on the checkout page      	
$this->has_fields = true;
$this->method_title = ‘MyApp Payment Gateway Plugin';
$this->method_description = 'MyApp Payment Gateway Plugin.'; //displayed on option page            
$this->supports = array(
                   'products'
        );

// Method with all the options fields
$this->init_form_fields();

// Load the settings.
$this->init_settings();
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );

 // Checking if valid to use
 if($this->is_valid_for_use()) {
 $this->enabled = $this->get_option( 'enabled' );
}
else {
    $this->enabled = 'no';
}

$this->testmode = 'yes' === $this->get_option( 'testmode' );
$this->merchant_id = $this->testmode ? $this->get_option( 'test_merchant_id' ) : $this->get_option( 'merchant_id' );
$this->auth_token = $this->testmode ? $this->get_option( 'test_auth_token' ) :
$this->get_option( 'auth_token' );
$this->country_code = $this->get_option( 'country_code' );
       
// Site URL
$this->siteUrl = get_site_url(); 

// This action hook saves the settings
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );

 // Callback Actions 
add_action( 'woocommerce_api_'. $this->SUCCESS_CALLBACK_URL, array( $this, 'payment_success'));
add_action( 'woocommerce_api_' . $this->FAILURE_CALLBACK_URL, array( $this, 'payment_failure'));

Field Validations

In our case, we will be allowing only five countries and their respective currencies for payments. So, we will have to validate Country and Currency Fields to move further or show an error on the admin page. We can do this while using WooCommerce hooks like get_woocommerce_currency(), validate_country_code_field().

Validate Currency

“admin_options” method is called before the settings page is loaded. In this method, we will check if the selected currency is supported by our plugin or not. We can get store currency by using get_woocommerce_currency() and compare it with the currencies we allowed and stored in $allowedCurrencies.

function is_valid_for_use () {
        return in_array(get_woocommerce_currency(), $this->allowedCurrencies);
}

function admin_options() {
        if ( $this->is_valid_for_use() ) {
	parent::admin_options();
} 
else {
            ?>
                <div class="notice error is-dismissible" >
                 <p><?php _e( 'MyApp Does not support the selected currency ' . get_woocommerce_currency() . '!', 'my-text-domain' ); ?></p>
                </div>
            <?php
      }
}

Validate Form Field

Validating the form fields before storing is a very common use case that every plugin developer has to implement. To achieve this woocommerce has provided a specific way to validate the individual fields. We have to create a method with “validate_” prefix and “_field” postfix to the field name you want to validate. validate_{field_name}_field();

We will allow our payment gateway plugin in limited countries. To achieve this, we will have to perform a couple of checks in order to confirm that the store country is supported by our plugin.

public function validate_country_code_field( $key, $value){
        if( $this->validate_currency_with_country($value) ){
          return $value;
        } else{
            ?>
                <div class="notice error is-dismissible" >
                    <p><?php _e( 'MyApp does not support ' . get_woocommerce_currency() . ' for the selected country! ' . $this->validate_currency_with_country($value), 'my-text-domain' ); ?></p>
                </div>
            <?php
        }
    }

We will use validate_currency_with_country(); to validate if the store currency is supported by the country.

private function validate_currency_with_country ($value) {
        $status = false;
        switch($value){
            case "CL":
                $status = get_woocommerce_currency() == 'CLP';
                break;
            case "DE":
                $status = get_woocommerce_currency() == 'EUR';
                break;
            case "SE":
                $status = get_woocommerce_currency() == 'SEK';
                break;
            case "NO":
                $status = get_woocommerce_currency() == 'NOK';
                break;
            case "FI":
                $status = get_woocommerce_currency() == 'EUR';
                break;
        }
        return $status;

Payment Process

Once we have validated all the fields and plugin compatibility with Woocommerce, we will have to create a session for payment platform and redirect the user to that platform. In this session, we will send complete information of the order, including order_id, amount, currency, user_id. process_payment($order_id); is called when a user clicks on Places an order on Checkout Page. We wil override this in our plugin and get the result of success and redirect in an array.

public function process_payment( $order_id ) {
        global $woocommerce;
  
//To receive order id 
        $order = wc_get_order( $order_id );

//To receive order amount
        $amount = $order->get_total();

//To receive woocommerce Currency
        $currency = get_woocommerce_currency();

//To receive user id and order details
        $merchantCustomerId = $order->get_user_id();
        $merchantOrderId = $order->get_order_number();
        $orderIdString = '?orderId=' . $order_id;
        $transaction = array(
            "amount" => $amount,
            "currency" => $currency,
        );
        $transactions = array(
            $transaction
        );
//Create a session and send it to Payment platform while handling errors 
       $requestBody = array(
            'country' => $this->country_code,
            'merchantId' => $this->merchant_id,
            'transactions' => $transactions,
            "redirectOnSuccessUrl" => $this->siteUrl . $this->SUCCESS_REDIRECT_URL . $orderIdString,
            "redirectOnFailureUrl" => $this->siteUrl . $this->FAILURE_REDIRECT_URL . $orderIdString,
            "callbackOnSuccessUrl" => $this->siteUrl . "//wc-api/" . $this->SUCCESS_CALLBACK_URL . $orderIdString,
            "callbackOnFailureUrl" => $this->siteUrl . "//wc-api/" . $this->FAILURE_CALLBACK_URL . $orderIdString,
            "redirectTarget" => "TOP",
            "merchantCustomerId" => $merchantCustomerId,
            "merchantOrderId" => $merchantOrderId,
        );

        $header = array(
            'Authorization' => $this->auth_token,
            'Content-Type' => 'application/json'
        );

        $args = array(
            'method' => 'POST',
            'headers' => $header,
            'body' => json_encode($requestBody),
        );
        
        $apiUrl = $this->api_host . $this->API_SESSION_CREATE_ENDPOINT;
        $response = wp_remote_post( $apiUrl, $args );    
    
        if( !is_wp_error( $response ) ) {
            $body = json_decode( $response['body'], true );
            if ( $body['status'] == 'OK' ) {
                $sessionId = $body['payload']['sessionId'];
                $url = $body['payload']['url'];
                $order->update_meta_data( 'myApp_session_id', $sessionId );
                $session_note = "MyApp SessionID: " . $sessionId;
                $order->add_order_note( $session_note );
                update_post_meta( $order_id, '_session_id', $sessionId );
                $order->update_status( 'processing');
                return array(
                    'result' => 'success',
                    'redirect' => $url
                );
		    } else {
                wc_add_notice(  'Please try again', 'error' );
                return;
		    }
        } else {
            wc_add_notice(  'Connection error.', 'error' );
            return;
        }
    }

In order to get the response from the payment platform, we will define call back functions which return the results either the transaction was successful or not. The call back functions defined in the constructor will trigger the webhooks to return the result.

Success Response Callback

payment_success() function is called to get the success response from Payment Gateway Platform. Data is fetched in a file. To make this data usable, we will store the data in a variable by using file_get_content(); This response is in encoded form, we will decode response using json_decode(); and store it in a variable. Now we have complete order information, which can be used to update the metadata.

public function payment_success() {
        // Getting POST data
        $postData = file_get_contents( 'php://input' );
        $response  = json_decode( $postData );
        $orderId = $_GET['orderId'];
        $order = wc_get_order( $orderId );
        
        if ($order && $response) {
            $order->update_meta_data( 'myApp_callback_payload', $postData );
            if ( $response->event === 'CHECKOUT_SUCCEEDED' ) {
                $order->update_meta_data( 'myApp_event', $response->event );
                if ($response->payload->reservations && $response->payload->reservations[0] && $response->payload->reservations[0]->reservationId) {
                    $order->update_meta_data( 'myApp_reservation_id', $response->payload->reservations[0]->reservationId );
                    $reservation_note = "MyApp ReservationID: " . $response->payload->reservations[0]->reservationId;
                    $order->add_order_note( $reservation_note );
                    update_post_meta( $orderId, '_myApp_reservation_id', $response->payload->reservations[0]->reservationId );
                }
                $order->update_status( 'completed');
                $order->payment_complete();
                $order->reduce_order_stock();
            } else {
                $order->update_meta_data( 'myApp_event', $response->event );
                if ($response->payload->reservations && $response->payload->reservations[0] && $response->payload->reservations[0]->reservationId) {
                    $order->update_meta_data( 'myApp_reservation_id', $response->payload->reservations[0]->reservationId );
                }
                $order->update_status( 'failed');
            }
        }
    }}

Failure Response Callback

payment_failure() function is called to get the success response from Payment Gateway Platform. Data is fetched in a file. To make this data usable, we will store the data in a variable by using file_get_content(); This response is in encoded form, we will decode response using json_decode(); and store it in a variable. Now we have complete order information, which can be used to update the metadata.

public function payment_failure() {
        // Getting POST data
        $postData = file_get_contents( 'php://input' );
        $response  = json_decode( $postData );
        $orderId = $_GET['orderId'];
        $order = wc_get_order( $orderId );

        if ($order && $response) {
            $order->update_meta_data( 'myApp_callback_payload', $postData );
            $order->update_meta_data( 'myApp_event', $response->event );
            if ($response->payload->reservations && $response->payload->reservations[0] && $response->payload->reservations[0]->reservationId) {
                $order->update_meta_data( 'myApp_reservation_id', $response->payload->reservations[0]->reservationId );
            }
            $order->update_status( 'failed');
        }
    }

Last Say!

Woocommerce is an extensively used eCommerce platform. You can find many extensions developed by the WordPress community members at different WordPress forums but time can come in a developers life, where he will have to develop some custom functionality in order to meet the needs of a project.

WP inCare always strives to provide you all the necessary updates to move ahead from your competition and grab the opportunity.Click To Tweet

Plugin development can be a difficult task to do especially if you are new to the development. Hopefully, this woocommerce payment gateway plugin tutorial will help you to achieve your goal. If you want to develop a WordPress Plugin and you are not experienced enough there is no need of worry WE ARE HERE!. We are experienced in WordPress Developer if you want to develop any WordPress plugin Contact us now!

What we do Best

WordPress
Maintenance
you Need!

WordPress Updates

Security Checks

Daily Cloud Backups

Speed Optimization

Premium Plugins

Developer Consultation

Leave a Comment

Your email address will not be published. Required fields are marked *