<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Micah Andrew F. Bule</title>
    <description>The latest articles on DEV Community by Micah Andrew F. Bule (@micahbule).</description>
    <link>https://dev.to/micahbule</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1025380%2F6fd810e3-eb8b-4cdc-9e13-4f3b58f1e486.jpeg</url>
      <title>DEV Community: Micah Andrew F. Bule</title>
      <link>https://dev.to/micahbule</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/micahbule"/>
    <language>en</language>
    <item>
      <title>WooCommerce Hosted Payment Plugin Development</title>
      <dc:creator>Micah Andrew F. Bule</dc:creator>
      <pubDate>Mon, 09 Jan 2023 15:49:45 +0000</pubDate>
      <link>https://dev.to/micahbule/woocommerce-hosted-payment-plugin-development-1kpm</link>
      <guid>https://dev.to/micahbule/woocommerce-hosted-payment-plugin-development-1kpm</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6DFdY245--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/cardmapr-nl-s8F8yglbpjo-unsplash.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6DFdY245--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/cardmapr-nl-s8F8yglbpjo-unsplash.jpg" alt="WooCommerce Hosted Payment Plugin Development" width="880" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://woocommerce.com"&gt;WooCommerce&lt;/a&gt; is a very popular e-commerce platform not just because it's easy to set up on top of any WordPress instance, it is also &lt;strong&gt;FREE&lt;/strong&gt;. However, imagine setting up your digital shop, finishing up configuring your product catalog and inventory, only to find out that your local payment service providers do not offer direct integrations with WooCommerce and international payment service providers just isn't viable due to certain reasons such as exorbitant fees or geographical restrictions.&lt;/p&gt;

&lt;p&gt;If it so happened that you also know how to &lt;a href="https://meeco.dev/wordpress-plugin-development-primer"&gt;develop WordPress plugins&lt;/a&gt;, then this guide is perfect for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this guide is and what is it not
&lt;/h2&gt;

&lt;p&gt;Different payment service providers offer different types of payment solutions. This guide is written specifically for &lt;strong&gt;Hosted Checkout&lt;/strong&gt; or &lt;strong&gt;Redirect-based&lt;/strong&gt; workflows and how to integrate it with WooCommerce.&lt;/p&gt;

&lt;p&gt;For an example of a hosted checkout experience, you can refer to &lt;a href="https://stripe.com/docs/payments/checkout"&gt;Stripe Checkout&lt;/a&gt; and &lt;a href="https://wordpress.org/plugins/search/stripe+checkout"&gt;several implementations&lt;/a&gt; of it by different plugin authors.&lt;/p&gt;

&lt;p&gt;While you can probably lift and apply most of the concepts and some of the code snippets from this guide to other types of payment solutions, this implementation is not a one-size-fits-all approach. Expect not the best of practices for developing plugins for the sake of simplicity, and that there can be better implementations out there aside from this one.&lt;/p&gt;

&lt;p&gt;Our objective is to have a functional payment plugin at the end of this guide. We will try to cover and support other common features such as real-time order status updates based on payment status, voiding and refunding, as well as manual captures in upcoming posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Definition of Terms
&lt;/h2&gt;

&lt;p&gt;Before we start writing any code, it's better to define some terms that will be used throughout this guide for clarity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WordPress Core&lt;/strong&gt; - refers to the primary process that executes every time you navigate throughout a WordPress application, whether it is a web page or the administration dashboard. Remember that WordPress is just a PHP application  after all, executed by the web server for every request it will receive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooks&lt;/strong&gt; - are basically callback functions that extend the WordPress Core.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Hooks&lt;/strong&gt; - are callback functions that introduce side-effects to the WordPress Core. Side effects are workflows triggered after certain events during the execution of the WordPress Core. Side effects can be in the form of interactions with the database or showing certain views or information to the user on certain processes or workflows. Action hooks may accept parameters but do not provide any return value to the process or function that called it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter Hooks&lt;/strong&gt; - are callback functions primarily used for transforming data within certain workflows during the execution of the WordPress Core. This is usually used for transforming data before being processed by WordPress Core or before being rendered as part of the UI that the user will see. Filter hooks may accept parameters and requires a return value to the process or function that called it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment Gateway&lt;/strong&gt; - throughout this guide, we will refer to this as the actual integration point between your payment service provider and WooCommerce. Technically, this is where the logic of your payment plugin will be written and/or configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start Writing Code
&lt;/h2&gt;

&lt;p&gt;While it's not the best practice, the whole plugin in this guide will be written in just one file, &lt;code&gt;index.php&lt;/code&gt;, for the sake of simplicity.&lt;/p&gt;

&lt;p&gt;After the &lt;a href="https://meeco.dev/wordpress-plugin-development-primer#the-initial-file"&gt;plugin header&lt;/a&gt;, first we want to write the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ABSPATH'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this snippet does is to check the existence of a constant named &lt;code&gt;ABSPATH&lt;/code&gt;. This is a global constant that WordPress usually sets in &lt;code&gt;wp-config.php&lt;/code&gt;. If there is no value, the script just terminates. This is to prevent the plugin file to be accessed directly via browser should it be exposed directly from the file system. While it's totally optional, this is a good way to ensure that attackers cannot abuse your plugin directly.&lt;/p&gt;

&lt;p&gt;Next, we define a callback function for the &lt;code&gt;plugins_loaded&lt;/code&gt; action hook for registering our custom payment method to WooCommerce, then call the action hook immediately after.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;wc_my_custom_gateway_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'plugins_loaded'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'wc_my_custom_gateway_init'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Plugin Initalization
&lt;/h3&gt;

&lt;p&gt;It is important to note that WooCommerce payment plugins are heavily dependent on the WooCommerce plugin itself, hence it needs to be loaded first before any payment plugin can be registered. In this sense, the following code snippets &lt;strong&gt;should be located within&lt;/strong&gt; the callback function above to avoid errors.&lt;/p&gt;

&lt;p&gt;Next, we define our payment gateway class and extend the &lt;code&gt;WC_Payment_Gateway&lt;/code&gt; parent class from WooCommerce. We will focus on this class later on, so we can leave it empty for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WC_My_Custom_Gateway&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WC_Payment_Gateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the class name of your payment gateway can be anything you want. However, it is best practice to make it as unique as possible to avoid conflicts with other WordPress plugins.&lt;/p&gt;

&lt;p&gt;After declaring our payment gateway class, we want to declare a function to register our payment gateway to WooCommerce and call it with the &lt;code&gt;woocommerce_payment_gateways&lt;/code&gt; filter hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;add_my_custom_gateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$methods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$methods&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'WC_My_Custom_Gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$methods&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'woocommerce_payment_gateways'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'add_my_custom_gateway'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After writing all that, you should have the an &lt;code&gt;index.php&lt;/code&gt; file with full content below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * PHP version 7
 * Plugin Name: WooCommerce Custom Payment Gateway
 * Description: A sample WooCommerce custom payment gateway plugin
 * Author: Mico
 * Author URI: https://meeco.dev
 * Version: 1.0.0
 * Requires at least: 5.3.2
 * Tested up to: 6.0.2
 */&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * This prevents direct access to the file; remember that WordPress
 * is just another PHP application served by a webserver, hence file
 * access through a browser is possible and can be abused by attackers.
 */&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;defined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ABSPATH'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Action hook callback to initialize your payment plugin
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;wc_my_custom_gateway_init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * Your payment gateway class
     */&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WC_My_Custom_Gateway&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WC_Payment_Gateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Filter hook callback to register your custom payment gateway to
     * valid WooCommerce Payment Gateways
     */&lt;/span&gt;
    &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;add_my_custom_gateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$methods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$methods&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'WC_My_Custom_Gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$methods&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;add_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'woocommerce_payment_gateways'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'add_my_custom_gateway'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * Make sure to load your plugin after all other plugins are loaded
 * to make sure WooCommerce has already loaded so you can register
 * your payment plugin.
 */&lt;/span&gt;
&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'plugins_loaded'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'wc_my_custom_gateway_init'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally, you can extract certain parts of the plugin to separate PHP files and just include it in &lt;code&gt;index.php&lt;/code&gt; using the &lt;code&gt;include_once&lt;/code&gt; directive. For example, you can move the payment gateway class to its own file instead of just declaring it within the &lt;code&gt;plugins_loaded&lt;/code&gt; action hook callback function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Payment Gateway Class
&lt;/h3&gt;

&lt;p&gt;Extending the &lt;code&gt;WC_Payment_Gateway&lt;/code&gt; class allows us to configure the behavior of our payment plugin. The first thing we need to do is define a public &lt;code&gt;__construct()&lt;/code&gt; function and fill out the following properties we are extending from the parent class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WC_My_Custom_Gateway&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WC_Payment_Gateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my_custom_gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;method_title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Payment Gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;method_description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Pay easily with my custom gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Your Payment Gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;id [required]&lt;/strong&gt; - unique gateway ID. Ideally no spaces nor special characters, and uses snake-case for readability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;method_title [required]&lt;/strong&gt; - the value for this is what is used to display the payment method in the payment method settings of WooCommerce.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;method_description [required]&lt;/strong&gt; - the value for this is shown on the actual payment method settings page itself, describing what the payment method is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;title [optional]&lt;/strong&gt; - shown as part of the method_title in the payment method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If there are no errors, you should be able to see the following in its respective URLs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MSGA1dIx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Payment-Settings-page.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MSGA1dIx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Payment-Settings-page.png" alt="WooCommerce Hosted Payment Plugin Development" width="880" height="386"&gt;&lt;/a&gt;&lt;/p&gt;
/wp-admin/admin.php?page=wc-settings&amp;amp;tab=checkout



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gcavK3sD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Payment-Method-Settings-page.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gcavK3sD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Payment-Method-Settings-page.png" alt="WooCommerce Hosted Payment Plugin Development" width="880" height="388"&gt;&lt;/a&gt;&lt;/p&gt;
/wp-admin/admin.php?page=wc-settings&amp;amp;tab=checkout&amp;amp;section=my_custom_gateway



&lt;h3&gt;
  
  
  Adding Settings for your Plugin
&lt;/h3&gt;

&lt;p&gt;Integrating with your chosen payment service probably requires your plugin users to input their credentials such as API keys, callback URLs, or other hosted checkout customizations. For this example, let's assume that we need to have the following fields for configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merchant ID&lt;/li&gt;
&lt;li&gt;Mode Dropdown Toggle&lt;/li&gt;
&lt;li&gt;Sandbox Public API Key&lt;/li&gt;
&lt;li&gt;Sandbox Secret API Key&lt;/li&gt;
&lt;li&gt;Live Public API Key&lt;/li&gt;
&lt;li&gt;Live Secret API Key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the mode field, it's best to use the radio group or checkbox as the value can normally be represented with two radio options or a boolean, respectively. We will just show the capability of the settings page to render a dropdown field for this guide.&lt;/p&gt;

&lt;p&gt;Extending the &lt;code&gt;WC_Payment_Gateway&lt;/code&gt; allows us to leverage the &lt;a href="https://developer.wordpress.org/plugins/settings/settings-api"&gt;WordPress Settings API&lt;/a&gt; and enables us to create our own settings page. In your payment gateway class, you can do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WC_My_Custom_Gateway&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WC_Payment_Gateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my_custom_gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;method_title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Payment Gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;method_description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Pay easily with my custom gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Your Payment Gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;init_form_fields&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;init_settings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'woocommerce_update_options_payment_gateways_'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'process_admin_options'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;init_form_fields&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;form_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'enabled'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Enable/Disable'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'label'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Enable/Disable the Custom Gateway'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'checkbox'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'default'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'no'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'merchant_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Merchant ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Your merchant ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'mode'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Mode'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'select'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'options'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s1"&gt;'sandbox'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sandbox'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'production'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Production'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Toggle between sandbox and production mode'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'default'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sandbox'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'production_public_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Production Public Key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'production_secret_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Production Secret Key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'sandbox_public_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sandbox Public Key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'sandbox_secret_key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Sandbox Secret Key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, declare a public method called &lt;code&gt;init_form_fields()&lt;/code&gt; within your payment gateway class. Inside this public method, we will assign an array to the class property &lt;code&gt;form_fields&lt;/code&gt;. This array will contain key-value pairs with each option key paired with another array value containing its configuration including but not limited to the title, type of the field, description, and even the default value. For all the available values and configurations per field type, check out the &lt;a href="https://woocommerce.com/document/settings-api"&gt;WooCommerce Settings API&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;Then, add the &lt;code&gt;$this-&amp;gt;init_form_fields()&lt;/code&gt; and &lt;code&gt;$this-&amp;gt;init_settings()&lt;/code&gt; call at the bottom part of the constructor. This will basically initialize all the values of the defined fields after the plugin has been registered.&lt;/p&gt;

&lt;p&gt;Lastly, call the &lt;code&gt;woocommerce_update_options_payment_gateways_{gateway_id}&lt;/code&gt; action hook with the method &lt;code&gt;process_admin_options&lt;/code&gt; for the gateway to be able to save the settings.&lt;/p&gt;

&lt;p&gt;At this point, if you don't encounter any errors, you will have the following screen below, complete with the functionality to save any data that you need to integrate with your payment service provider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lfFuqqrr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Payment-Method-Settings-Page-with-Fields.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lfFuqqrr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Payment-Method-Settings-Page-with-Fields.png" alt="WooCommerce Hosted Payment Plugin Development" width="880" height="387"&gt;&lt;/a&gt;&lt;/p&gt;
/wp-admin/admin.php?page=wc-settings&amp;amp;tab=checkout&amp;amp;section=my_custom_gateway



&lt;p&gt;Also, if you enable your payment gateway at this point, you should be able to see it as a payment method option in the actual checkout page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RTlEzofi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Sample-Checkout.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RTlEzofi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Sample-Checkout.png" alt="WooCommerce Hosted Payment Plugin Development" width="880" height="750"&gt;&lt;/a&gt;&lt;/p&gt;
/checkout



&lt;p&gt;You need to fix your permalinks to navigate to &lt;code&gt;/checkout&lt;/code&gt;, otherwise you'll be seeing something similar to &lt;code&gt;?page=X&lt;/code&gt; where X is the page ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  Process the Actual Payment
&lt;/h3&gt;

&lt;p&gt;For this section, we will set certain assumptions for the payment service provider, including data for endpoints, payload, and workflow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assume that we need to send the payload below as a POST request to the &lt;code&gt;/checkout&lt;/code&gt; endpoint of the payment service provider's public API to create a checkout.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"first_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{customer_first_name}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"last_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{customer_last_name}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"total_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{order_total_amount}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"redirect_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{redirect_url}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Assume that we will receive the payload below after creating a checkout. This usually contains the hosted checkout URL where the customers can process their payments.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"checkout_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chkout_fa21af237u3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"checkout_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://your-payment-service-provider.com/hosted-checkout?id=chkout_fa21af237u3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Assume that requests need to have an Authorization header with the base64-encoded secret key as value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have the assumptions above, you can add make the following changes to your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WC_My_Custom_Gateway&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WC_Payment_Gateway&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$mode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$secret_key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'my_custom_gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;method_title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'My Custom Payment Gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;method_description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Pay easily with my custom gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Your Payment Gateway'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;init_form_fields&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;init_settings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'woocommerce_update_options_payment_gateways_'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'process_admin_options'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="cd"&gt;/**
         * This is a pretty nifty trick to set public/secret keys on your
         * gateway instance if you implement mode as a dropdown instead of
         * checkbox or radio group
         */&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mode'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;secret_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'_secret_key'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * ASSUME THE FORM FIELDS SETTINGS IS HERE
     */&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cd"&gt;/** Get the actual order object via ID */&lt;/span&gt;
        &lt;span class="nv"&gt;$order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wc_get_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'first_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_billing_first_name&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="s1"&gt;'last_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_billing_last_name&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="cd"&gt;/** get_total() returns a string value so you need to convert */&lt;/span&gt;
                &lt;span class="s1"&gt;'total_amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;floatval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_total&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
                &lt;span class="s1"&gt;'redirect_url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_checkout_order_received_url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$request_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'body'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'method'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'headers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Basic '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;base64_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;secret_key&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_remote_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://your-payment-service-provider.com/checkout'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$request_args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;add_meta_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'_checkout_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'checkout_id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'result'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'success'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'redirect'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'checkout_url'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, the first thing we did was to add two protected properties in our payment gateway class: &lt;code&gt;mode&lt;/code&gt; and &lt;code&gt;secret_key&lt;/code&gt;. We also assigned values to them at the end of the &lt;code&gt;__construct()&lt;/code&gt; function, retrieving their corresponding values from the settings page we created earlier.&lt;/p&gt;

&lt;p&gt;Next, we defined a public function called &lt;code&gt;process_payment&lt;/code&gt;, taking in one parameter, &lt;code&gt;order_id&lt;/code&gt;. This is extended from the parent &lt;code&gt;WC_Payment_Gateway&lt;/code&gt; class and this is where the actual integration normally happens. For hosted checkout workflows, this is where you usually construct the payload with all the customer and transaction details that will populate your hosted checkout page.&lt;/p&gt;

&lt;p&gt;In our example, we retrieved all the details we need from the &lt;a href="https://woocommerce.github.io/code-reference/classes/WC-Order.html"&gt;WooCommerce Order object&lt;/a&gt;, from customer name to total transaction amount and constructed the JSON payload. After that, we constructed the request arguments, including the Base64 encoded secret key for the authorization header.&lt;/p&gt;

&lt;p&gt;Lastly, we sent the request to the public API of the payment service provider using the indicated endpoint and &lt;code&gt;wp_remote_post()&lt;/code&gt; function from WordPress. Assuming it was a successful attempt, all we need to is to decode the JSON response payload, associate the checkout ID to the order metadata – which is critical if you want to resolve order statuses in real-time with payment statuses, then returned an array with the &lt;code&gt;result&lt;/code&gt; and &lt;code&gt;redirect&lt;/code&gt; keys, the latter set to whatever the value of the checkout URL from the response payload is.&lt;/p&gt;

&lt;p&gt;This only follows the happy path for transacting. You need to also handle errors coming in from the payment service provider's public API.&lt;/p&gt;

&lt;p&gt;Ideally, you want to create an API library that you want to instantiate in your payment gateway class so you can abstract the construction of the request payload and headers, as well as the call to the actual public API endpoints of the payment service provider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final step
&lt;/h3&gt;

&lt;p&gt;If you're integrating with an actual payment service provider, all that's needed now is to test. If you have followed everything so far, you should be able to place an order in your e-commerce website using your custom payment gateway as the payment method, get redirect to the hosted checkout page, process your payment, and get redirected back to your e-commerce website.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;At this point, there are several other features that you probably need/want to develop to have a better workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your payment service provider's public API offers registration a notification mechanism for payment statuses – usually in the form of webhooks – you may want to implement handling webhooks using the &lt;a href="https://woocommerce.com/document/wc_api-the-woocommerce-api-callback"&gt;WooCommerce API Callback&lt;/a&gt; or even &lt;a href="https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints"&gt;WordPress Custom Endpoints&lt;/a&gt;, instead of just pinging back other API endpoints to check the payment status.&lt;/li&gt;
&lt;li&gt;If your payment service provider supports voiding and refunding, you probably want to implent the &lt;code&gt;process_refund()&lt;/code&gt; method from the &lt;code&gt;WC_Payment_Gateway&lt;/code&gt; parent class.&lt;/li&gt;
&lt;li&gt;Manual capture is a bit more tricky as this requires extending the admin dashboard user interface to provide for the manual capture functionality. I am reserving this to explain in detail for another technical post.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>technology</category>
      <category>wordpress</category>
      <category>plugindevelopment</category>
      <category>woocommerce</category>
    </item>
    <item>
      <title>WordPress + Warden</title>
      <dc:creator>Micah Andrew F. Bule</dc:creator>
      <pubDate>Wed, 04 Jan 2023 15:17:02 +0000</pubDate>
      <link>https://dev.to/micahbule/wordpress-warden-bki</link>
      <guid>https://dev.to/micahbule/wordpress-warden-bki</guid>
      <description>&lt;p&gt;Developing anything on top of WordPress can be a bit tricky, especially if you don't know where to start. Whether it be building an actual website using the CMS platform, or developing plugins and themes for it, it's almost always needed for you to have a local instance on your machine to test things out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Old-Fashioned Way
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmeeco.dev%2Fcontent%2Fimages%2F2023%2F01%2Fxampp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmeeco.dev%2Fcontent%2Fimages%2F2023%2F01%2Fxampp.png" alt="xampp" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
xampp client



&lt;p&gt;Back in the days when you can just spin up a web server using XAMPP, I can just download a copy of WordPress, put it in the default root directory of my local Apache instance, setup the database and configure WordPress to connect, then I should be good to go. For most occasions, this would suffice. However, due to the nature of WordPress as a very robust, flexible, and dynamic content management system, having particularly the ability to extend it using plugins, complexities arise when you have different plugins that may or may not be compatible with each other or the WordPress version you have installed. Then there's the issue of working on a specific project together with other members of a team, having different machines and your own configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Containers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmeeco.dev%2Fcontent%2Fimages%2F2023%2F01%2Feilis-garvey-dI_S0Kyq1Z0-unsplash.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmeeco.dev%2Fcontent%2Fimages%2F2023%2F01%2Feilis-garvey-dI_S0Kyq1Z0-unsplash.jpg" alt="containers" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://unsplash.com/@eilisgarvey" rel="noopener noreferrer"&gt;https://unsplash.com/@eilisgarvey&lt;/a&gt;



&lt;p&gt;Then came containerization.&lt;/p&gt;

&lt;p&gt;While I can probably explain it in my own words, it's probably best to focus on my next topic, so &lt;a href="https://yoast.com/developer-blog/set-up-wordpress-development-environment-in-docker" rel="noopener noreferrer"&gt;here's a Yoast blog&lt;/a&gt; I followed when I started using Docker to create WordPress instances on my local machine about a year ago.&lt;/p&gt;

&lt;p&gt;The primary idea is to compose your development environment – primarily your web server which would house your WordPress application, and its database – using containers. You can think of containers as "packaged environments" that can run the same way on any operating system. This way, it doesn't matter if you personally have multiple different  development machines, or multiple team members using different machines collaborating on your work. You can simply configure what your environments should be for different scenarios, and just share the same configuration between said machines, then just spin up the same environment for everyone. It also has an added bonus that you can now spin up multiple environments on the same machine, say for different WordPress versions that you need to test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automate all the way
&lt;/h2&gt;

&lt;p&gt;Warden is like XAMPP but on steroids as it leverages containerization technology.&lt;/p&gt;

&lt;p&gt;It's basically an automation tool to configure different platform environments using containers for the platform's components. For example, the latest Magento requires more than just a web server and database but also ElasticSearch for searching, Varnish as a reverse proxy, and RabbitMQ for queueing – among others. With Warden, all you need to do is create a directory for your Magento instance, run a command to create the configuration file for your Magento environment, then execute a command to build the environment. What Warden does is create containers for each of the services mentioned above, and tie them all together so they all know how to talk to each other within your container's network.&lt;/p&gt;

&lt;p&gt;The idea is pretty much the same for setting up WordPress.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a directory for your WordPress instance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /path/to/your/wordpress-instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty much self-explanatory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Initialize your WordPress instance directory
&lt;/h3&gt;

&lt;p&gt;Assuming you've already installed Warden and ran it for the first time without any hitch, you can directly go to your WordPress directory and execute the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;warden env-init my_wordpress_instance wordpress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;strong&gt;my_wordpress_instance&lt;/strong&gt; is an identifier for your WordPress instance. No spaces, preferably snake-cased. This will create a &lt;code&gt;.env&lt;/code&gt; file in the current working directory with default configurations for a WordPress instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Download WordPress into the WordPress instance directory
&lt;/h3&gt;

&lt;p&gt;You can just execute the following commands within the WP instance directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://wordpress.org/latest.zip
&lt;span class="nv"&gt;$ &lt;/span&gt;unzip latest.zip
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;wordpress/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; wordpress/ latest.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should download the latest version of WordPress in a zipped file to your current working directory, unzip it, move the contents to the current working directory, and remove downloaded files and unnecessary folders.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Run the WordPress environment on Warden
&lt;/h3&gt;

&lt;p&gt;Just execute the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;warden &lt;span class="nb"&gt;env &lt;/span&gt;up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should set up all necessary containers, from web server to database, tie them all up as configured in your &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Ideally, on first run, WordPress will let you configure the connection strings during set up, so just copy the values within the &lt;code&gt;.env&lt;/code&gt; file and you'll be good to go.&lt;/p&gt;




&lt;p&gt;As cherry on top, Warden does not just automate the container setup for you with the containers you need for your platform environment. It also provides several services out of the box to make your development way easier.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traefik&lt;/strong&gt; - handles all the networking needed within your Warden services so all containers can talk to each other&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNSMasq&lt;/strong&gt; - handles DNS and DHCP locally, which is particularly useful in conjunction with ngrok to route requests; no more localhost:port, use your environment name instead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portainer&lt;/strong&gt; - handle container management; pretty useful even without using Warden in general to manage other containers for development using its GUI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mailhog&lt;/strong&gt; - is an email testing tool which you can configure with your environment if you want to test mailing functionality of your platform (ex. order confirmation and invoice sending for e-commerce functionalities)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;For any developers out there who wants to take their WordPress development to the next level, Warden is definitely something worth trying.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>technology</category>
      <category>plugindevelopment</category>
      <category>warden</category>
    </item>
    <item>
      <title>WordPress Plugin Development Primer</title>
      <dc:creator>Micah Andrew F. Bule</dc:creator>
      <pubDate>Wed, 04 Jan 2023 13:17:41 +0000</pubDate>
      <link>https://dev.to/micahbule/wordpress-plugin-development-primer-2md2</link>
      <guid>https://dev.to/micahbule/wordpress-plugin-development-primer-2md2</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L9_Guaxe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2022/12/ben-kolde-bs2Ba7t69mM-unsplash.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L9_Guaxe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2022/12/ben-kolde-bs2Ba7t69mM-unsplash.jpg" alt="header_image" width="880" height="1122"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://unsplash.com/@benkolde"&gt;https://unsplash.com/@benkolde&lt;/a&gt;



&lt;h2&gt;
  
  
  What and Why?
&lt;/h2&gt;

&lt;p&gt;WordPress has been around for about two decades now and is basically responsible for almost half of all websites on the internet at this point. While there are tons of available alternatives out there for content management systems, especially with the rise of headless CMS counterparts, it's quite certain that WordPress is not going to sunsetted anytime soon.&lt;/p&gt;

&lt;p&gt;One of WordPress' killer feature by the time it took the internet is the Plugins ecosystem. Having the ability to extend and customize your content management system, making it more dynamic and robust, is one of the things that made WordPress so popular to a lot of digital users. From adding simple utility tools to your CMS to turning your website into an e-commerce shop, WordPress plugins got most of the existing use-cases covered. However, in case that the solution does not exist yet, it is worth mentioning that we, as web developers, can actually develop our own WordPress plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Plugin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The directory structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sample-wordpress-plugin
 |-- index.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;index.php&lt;/strong&gt; - this is our initial file and where we will call the action hook that would register our plugin to the WordPress Core so it would show up in the Plugins page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Assuming you already have a local WordPress instance, you can proceed to create your own plugin directory under &lt;code&gt;/your-wordpress-directory/wp-content/plugins&lt;/code&gt;. Otherwise, you can refer to my &lt;a href="https://meeco.dev/wordpress-warden"&gt;other blog post on how to setup a local WordPress instance using Warden&lt;/a&gt;. Moving forward this guide, assume all files are within the plugin directory in the path &lt;code&gt;/your-wordpress-directory/wp-content/plugins/sample-wordpress-plugin&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Initial File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="cd"&gt;/**
 * PHP version 7
 * Plugin Name: Sample WordPress Plugin
 * Description: A sample WordPress plugin
 * Author: Mico
 * Author URI: https://meeco.dev
 * Version: 1.0.0
 * Requires at least: 5.3.2
 * Tested up to: 6.0.2
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
index.php





&lt;p&gt;The comments at the start of the initial file is called the Plugin Header. The header is &lt;strong&gt;important&lt;/strong&gt; as that is what determines the plugin details that would appear on the Plugins page in the WordPress dashboard (refer to image 1.a below). While the first few lines are pretty self-explanatory, there are some that we think it is best to define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Author URI&lt;/strong&gt; - while optional, it's better to put your own personal or company URL here for easier contact reference and free marketing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version&lt;/strong&gt; - this is the plugin's current version, preferably using &lt;a href="https://semver.org"&gt;Semantic Versioning&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requires at least&lt;/strong&gt; - the minimum WordPress version that your plugin is compatible with. This is very important as any published plugins are regularly checked for compatibility across WordPress versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tested up to&lt;/strong&gt; - the latest version of WordPress that your plugin was tested to work with. Do note that this does not mean your plugin won't work on later versions of WordPress, this just means you tested your plugin's compatibility up to the value of this field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more details on what else you can put within this comment section, you can check the &lt;a href="https://developer.wordpress.org/plugins/plugin-basics/header-requirements"&gt;WordPress Header Requirements&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With just the header, and without any other issues, you should be able to see something like the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PgyD5Fd4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Plugins-page.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PgyD5Fd4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://meeco.dev/content/images/2023/01/Plugins-page.png" alt="WordPress Plugin Development Primer" width="880" height="395"&gt;&lt;/a&gt;&lt;/p&gt;
A sample Plugins page in a WordPress admin dashboard



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;While the plugin literally does nothing right now other than merely registering itself in a WordPress instance's loaded plugins, as a developer, you can now start to decide what to do from here. Below are some additional guides based on this primer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a payment plugin for WooCommerce&lt;/li&gt;
&lt;li&gt;Create a custom shortcode plugin&lt;/li&gt;
&lt;li&gt;Publish your plugin to WordPress Plugins public repository;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>technology</category>
      <category>wordpress</category>
      <category>woocommerce</category>
      <category>plugindevelopment</category>
    </item>
  </channel>
</rss>
