Connecting WooCommerce To Salesforce: OAuth2 Authorization

Along with learning Typescript in the fast few months, I’ve also been putting a lot of time an energy into learning Salesforce and the Apex language. So far it has been an amazing journey and my end goal for learning Salesforce is so that I can write REST APIs that would synchronize WooCommerce and Salesforce seamlessly.

With that purpose in mind, I realized doing something like this is no easy task or something you can write in a day or two and never have to touch it again, specially since the whole ecosystem (WordPress, WooCommerce, Salesforce) gets updated very frequent and things change all the time. I’ve taken the approach of dividing this humongous task into smaller modules that would help me accomplish this in faster and more maintainable way. Being able to re-use the components and sort of add or delete them as needed is what I’m after and the first step would be to establish a connection with Salesforce so that I can begin sending my Requests.

Since this would be tied to WooCommerce on this side, I figured I would do it the WordPress/WooCommerce way so I wrote a little plugin that integrates with WooCommerce and handles the saving of all the data received from Salesforce after the initial oAuth2 authorization and saves it to the database for later use. Note that this part doesn’t include the option the revoke the token or to send the refresh token in order to obtain a new access token once the old one has expired.

Register Our Integration With WooCommerce

Let’s start coding and register our registration with WooCommerce so that it appears under the WooCommerce -> Settings -> Integrations tab. This file’s sole purpose is to help WooCommerce know this is an integration and to load other PHP Classes we are going to be using. The actual fields, settings and any other logic will be in another file.

<?php
/**
 * Plugin Name: WooCommerce To Salesforce Integration
 * Plugin URI: https://www.wpsuperstar.com/
 * Description: Connects WooCommerce to Salesforce
 * Version: 0.0.1
 * Author: Yojacnce Rabelo
 * Author URI: https://www.wpsuperstar.com/
 *
 */
if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly
}

if ( ! class_exists( 'WooCommerce_Salesforce_Integration' ) ) :

    define( 'WC_SF_OAUTH2_PATH',   wp_normalize_path( plugin_dir_path( __FILE__ ) ) );
    define( 'WC_SF_OAUTH2_URL',    plugin_dir_url( __FILE__ ) );

    /**
     * Register the WooCommerce/Salesforce Integration Class
     */
    class WooCommerce_Salesforce_Integration {

        /**
         * Construct the plugin.
         */
        public function __construct() {
            add_action( 'plugins_loaded', array( $this, 'init' ) );
        }

        /**
         * Initialize the Plugin
         */
        public function init() {

            // Checks if WooCommerce is installed.
            if ( class_exists( 'WC_Integration' ) ) {
                // Include our integration class.
                include_once WC_SF_OAUTH2_PATH . 'class-woocommerce-salesforce-integration.php';
                include_once WC_SF_OAUTH2_PATH . 'includes/class-endpoints.php';
                include_once WC_SF_OAUTH2_PATH . 'includes/class-authorization.php';

                $this->oauth2_endpoints = new WC_SF_OAUTH2_EndPoints();
                $this->oauth2_authorization = new WC_SF_OAUTH2_Authorization();

                // Register the integration.
                add_filter( 'woocommerce_integrations', array( $this, 'add_integration' ), 10, 1 );

                // Include the rest of the files here as necessary
            } else {
                // throw an admin error if you like
            }
        }

        /**
         * Register the WooCommerce/Salesforce Integration
         *
         * @param $integrations
         * @return array
         */
        public function add_integration( $integrations ) {
            $integrations[] = 'WC_OAUTH2_SF';
            return $integrations;
        }
    }

    $woocommerce_salesforce_integration = new WooCommerce_Salesforce_Integration( __FILE__ );

endif;

Integration Fields Using WooCommerce’s Settings API

Everything is pretty standard, just a few fields to save our the values we get back from Salesforce.

  • SF App Consumer Key
  • SF App Consumer Secret
  • SF Login URI
  • SF oAuth2 Token
    Salesforce Token (Not actually used to communicate back and forth but it was used for obtaining the Access Token
  • SF Access Token
    Access token sent with every request for authenticating purposes.
  • SF Refresh Token
    Refresh token used whenever the Access Token needs to be renewed/refreshed
  • SF Instance URI
    All REST Request will be send to this base Uri.
  • Debug Log
    Log events such as API requests
<?php
/**
 * Class WC_OAUTH2_SF
 */
class WC_OAUTH2_SF extends WC_Integration {

    public $text_domain = 'wcsf_oauth2';

    /**
     * WC_OAUTH2_SF constructor
     */
    public function __construct() {

        $this->id                 = 'wc_sf_oauth2';
        $this->method_title       = __( 'WooCommerce to Salesforce Connection', $this->text_domain );
        $this->method_description = __( 'Connects WooCommerce to Salesforce', $this->text_domain );

        // Load the settings.
        $this->init_form_fields();
        $this->init_settings();

        // Make sure the settings get saved
        add_action( 'woocommerce_update_options_integration_' .  $this->id, array( $this, 'process_admin_options' ) );
    }

    /**
     * Initialize integration settings form fields.
     */
    public function init_form_fields() {
        $this->form_fields = array(
            'consumer_key' => array(
                'title'             => __( 'SF App Consumer Key', $this->text_domain ),
                'type'              => 'text',
                'description'       => __( '', $this->text_domain ),
                'desc_tip'          => true,
            ),
            'consumer_secret' => array(
                'title'             => __( 'SF App Consumer Secret', $this->text_domain ),
                'type'              => 'text',
                'description'       => __( '', $this->text_domain ),
                'desc_tip'          => true,
            ),
            'login_uri' => array(
                'title'             => __( 'SF Login URI', $this->text_domain ),
                'type'              => 'text',
                'description'       => __( '', $this->text_domain ),
                'desc_tip'          => true,
            ),
            'oath2_token' => array(
                'title'             => __( 'SF oAuth2 Token', $this->text_domain ),
                'type'              => 'text',
                'description'       => __( 'Salesforce Token (Not actually used to communicate back and forth but it was used for obtaining the Access Token', $this->text_domain ),
                'desc_tip'          => true,
            ),
            'access_token' => array(
                'title'             => __( 'SF Access Token', $this->text_domain ),
                'type'              => 'text',
                'description'       => __( 'Access token sent with every request for authenticating purposes.', $this->text_domain ),
                'desc_tip'          => true
            ),
            'refresh_token' => array(
                'title'             => __( 'SF Refresh Token', $this->text_domain ),
                'type'              => 'text',
                'description'       => __( 'Refresh token used whenever the Access Token needs to be renewed.', $this->text_domain ),
                'desc_tip'          => true
            ),
            'instance_uri' => array(
                'title'             => __( 'SF Instance URI', $this->text_domain ),
                'type'              => 'text',
                'description'       => __( 'All REST Request will be send to this base Uri.', $this->text_domain ),
                'desc_tip'          => true
            ),
            // Will be used in the future
            'debug' => array(
                'title'             => __( 'Debug Log', $this->text_domain ),
                'type'              => 'checkbox',
                'label'             => __( 'Enable logging', $this->text_domain ),
                'default'           => 'no',
                'description'       => __( 'Log events such as API requests', $this->text_domain ),
            ),
        );
    }
}

Register Custom WordPress Endpoints

Next we register 2 endpoints which are used in the 2 step process. What follows takes care of registering two endpoints, oauth2 and oauth2-step2. It will also add some quick and dirty logic to overriding what each of these endpoints display. These templates are called authorization-step-1.php and authorization-step-2.php.

<?php

/**
* Class WC_SF_EndPoints
*/
class WC_SF_EndPoints {

    protected $settings_key_id = 'woocommerce_wc_sf_settings';
    
    public function __construct() {
        $this->includes();
        $this->hooks();
    }
    
    public function includes() {	}
    
    
    /**
    * WordPress Hooks
    */
    public function hooks() {
        // Add the oauth2 Endpoint
        add_action( 'init',                                 array( $this, 'register_authorization_endpoints' ) );
        // Create custom templates for endpoints
        add_action( 'template_redirect',                    array( $this, 'register_custom_template_authorization_step_1' ) );
        add_action( 'template_redirect',                    array( $this, 'register_custom_template_authorization_step_2' ) );
    }
    
    /**
    * Register the endpoints for the Salesforce 2 Step oAuth2 authorization process
    */
    public function register_authorization_endpoints() {
        // register a "json" endpoint to be applied to posts and pages
        add_rewrite_endpoint( 'oauth2', EP_ROOT );
        add_rewrite_endpoint( 'oauth2-step2', EP_ROOT );
    }
    
    /**
    * Register template for the Step 1 of the oAuth2 Authorization
    */
    public function register_custom_template_authorization_step_1() {
        global $wp_query;
        
        if( isset( $wp_query->query_vars['oauth2'] ) ) {
            // include custom template
            include WC_SF_PATH . 'frontend/authorization-step-1.php';
            exit;
        }
    }
    
    /**
    * Register template for the Step 2 of the oAuth2 Authorization
    */
    public function register_custom_template_authorization_step_2() {
    
        global $wp_query;
        
        if( isset( $wp_query->query_vars['oauth2-step2'] ) ) {
            // include custom template
            include WC_SF_PATH . 'frontend/authorization-step-2.php';
            exit;
        }
    }
}

Endpoint Templates

Finally, these are the layouts for those 2 endpoints.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <title>WooCommerce To Salesforce — Authorization Process</title>
    <!-- Bootstrap -->
    <link href="<?php echo WC_SF_URL; ?>assets/css/bootstrap.min.css" rel="stylesheet" />
    <link href="<?php echo WC_SF_URL; ?>assets/css/bootstrap-theme.min.css" rel="stylesheet" />
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
</head>
<body>
<div id="authorization-page" class="container" style="background: #f5f5f5; border: 1px solid #ccc; padding:50px;margin-top:100px;">
    <div class="row">
        <h2>WooCommerce To Salesforce
            <small>Authorization Process</small>
        </h2>
        <hr/>
        <div class="col-xs-12 col-sm-6">
            <p class="lead">Please use this page to complete the initial WooCommerce To Salesforce Authorization Process</p>
            <p>Please enter your Consumer Key. This Consumer key must be obtain from the Salesforce App you created. If you haven't created one yet, please go ahead and do so. Login to your Salesforce account and then click on <strong>"Setup => Create => Apps => New."</strong></p>

            <p>If you already have an App created bu don't know where to get this information, the login to your Salesforce and go to <strong>"Setup => Create => Apps"</strong>. In the Conencted Apps section, click <strong>"Edit"</strong> next to the App you previously created and on the new "Edit Screen" just hit the save button. On this new screen you can get your Consumer Key and Consumer Secret. Please store these in a save place.</p>
        </div>
        <div class="col-xs-12 col-sm-6">
            <form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
                <p><label for="authorization-app-consumer-key">SF App Consumer Key</label>
<input type="text" class="form-control" name="authorization-app-consumer-key"
                           id="authorization-app-consumer-key" required/>
                </p>
                <p>
                    <label for="authorization-app-consumer-secret">SF App Consumer Secret</label>
                    <input type="text" class="form-control" name="authorization-app-consumer-secret"
                           id="authorization-app-consumer-secret" required/>
                </p>
                <p>
                    <label for="authorization-login-uri">SF Login URI</label>
                    <input type="text" class="form-control" readonly="readonly" name="authorization-login-uri"
                           id="authorization-login-uri" value="https://login.salesforce.com" />
                </p>
                <p>
                    <label for="authorization-step-2-uri">Step 2 URI</label>
                    <input value="<?php echo home_url('/'); ?>oauth2-step2/" type="text" class="form-control"
                           readonly="readonly" name="authorization-step-2-uri" id="authorization-step-2-uri"/>
                </p>
                <hr/>
                <p><input type="submit" class="btn btn-primary" name="authorization-step-1" value="Proceed To Step 2"/>
                </p>
            </form>
        </div>
    </div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="<?php echo WC_SF_URL; ?>assets/js/bootstrap.min.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <title>WooCommerce To Salesforce — Authorization Process (Step 2)</title>
    <!-- Bootstrap -->
    <link href="<?php echo WC_SF_URL; ?>assets/css/bootstrap.min.css" rel="stylesheet">
    <link href="<?php echo WC_SF_URL; ?>assets/css/bootstrap-theme.min.css" rel="stylesheet">
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
</head>
<body>
<div id="authorization-page" class="container">
    <div class="row">
        <h2>WooCommerce To Salesforce
           <small>Authorization Process (Step 2)</small>
        </h2>
        <p>All done! Please make sure that the following settings have been saved to this instance of the WooCommerce To Salesforce Integration.</p>
        <ul>
            <li>Salesforce App Consumer Key</li>
            <li>Salesforce App Consumer Secret</li>
            <li>Salesforce Login URI</li>
            <li>Salesforce oAuth2 Token</li>
            <li>Salesforce Access Token</li>
            <li>Salesforce Refresh Token</li>
            <li>Salesforce Instance URI</li>
        </ul>
        <hr/>
    </div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="<?php echo WC_SF_URL; ?>assets/js/bootstrap.min.js"></script>
</body>
</html>

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s