Accessing the API with PHP

Paired with our FoxyClient PHP library, interacting with the API can be straightforward. This example will give you a quick overview of setting up a basic standalone script which looks to see if a customer exists based on an email address, and if not, creates one. A real world application for this kind of set up would be Foxy's Single Sign On functionality.

Requirements

This tutorial assumes you have access to PHP on your local system. It also assumes you've already create a Foxy store at admin.foxycart.com.

Getting Started

Firstly, let's create a directory locally where we'll build the script. For this example, we'll call it /foxy-sso. Within it, make a file and name it sso.php.

We'll need to do a little work within the command line, so open that up and cd into the foxy-sso directory we just created.

Install Composer

The FoxyClient library that we'll be using makes use of Composer to manage its dependencies, so in order to make use of it, we need to get it installed on your system.

There are a couple different ways to install Composer depending on your operating system. Refer to Composer's Getting Started docs for details for your set up. If installing via the command line, make sure you do that into the directory we created in the previous step.

Depending on the installation approach that you take - make note of the way that it says to make calls to Composer as it does depend on how it's installed. It will either look like simply composer or php composer.phar.

Require FoxyClient

Now that we have Composer installed, we can use it to include the FoxyClient library into our project. In the command line, assuming you're still active on the foxy-sso directory, run the following command (and substitue the starting composer command to match how you were instructed to call it when installing):

composer require foxycart/foxyclient:~1.0

When you run that command, it may take a little while to execute completely, as it will be downloading all the needed dependencies. The output will look something like this once complete:

./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing doctrine/cache (v1.6.0)
    Loading from cache

  - Installing react/promise (v2.4.1)
    Downloading: 100%

  - Installing guzzlehttp/streams (3.0.0)
    Loading from cache

  - Installing guzzlehttp/ringphp (1.1.0)
    Loading from cache

  - Installing guzzlehttp/guzzle (5.3.1)
    Downloading: 100%

  - Installing guzzlehttp/cache-subscriber (0.1.0)
    Loading from cache

  - Installing foxycart/foxyclient (1.0.0)
    Downloading: 100%

Writing lock file
Generating autoload files

It first creates the composer.json file in our directory - which Composer uses to track the dependencies for your project, and then fetches the FoxyClient package we directed it to include, along with all of its dependencies. If you check in the directory we created earlier, you'll see the composer.json file, a composer.lock file which represents the dependencies as they have been downloaded as a read-only reference, along with a /vendor folder which contains all the dependencies we just downloaded.

Set up our script

With FoxyClient downloaded, we can now start building out our script. Open up the sso.php file in your preferred text editor and as a start, paste in the following:

<?php
require __DIR__ . '/vendor/autoload.php';
session_start();

use GuzzleHttp\Client;
use GuzzleHttp\Subscriber\Cache\CacheSubscriber;
use Foxy\FoxyClient\FoxyClient;

$guzzle_config = array(
    'defaults' => array(
        'debug' => false,
        'exceptions' => false
    )
);
$guzzle = new Client($guzzle_config);
CacheSubscriber::attach($guzzle);

This initial code does a few things. Firstly it uses the autoload.php script that Composer created for us, which basically is a quick way for us to include the packages we just downloaded. We also enable sessions which we'll use later. The second section makes use of PHP's use command to bring those namespaces into the current scope, making them a little easier to work with. Finally we set up Guzzle which is a PHP HTTP client, and CacheSubscriber which assists with caching server-side requests to cut down on unneccessary requests and improve performance.

Create an OAuth Client

The Foxy API uses OAuth 2.0 for authenticating access to a Foxy user or store, and depending on the type of integration with the API you're building and how it will be distributed, dictates how you need to add OAuth to your application. You can see a full overview of OAuth and the Foxy API within our authentication section.

For the purpose of this tutorial though, let's just use the quick start approach for generating an OAuth client and the access token for your store. To do that, navigate to the Foxy administration and to the "Integrations" section. Here you'll see a list of any existing integrations your store has access to - which if you've just created it, it will be blank. Click the "Get Token" button, fill in the two fields, and press "Create Integration".

After submitting the form, you'll be provided with four values within the success dialog - Client ID, Client Secret, Access Token and Refresh Token. This is the only time you will see these values - so make sure that you copy and paste these values somewhere on your side before leaving the page.

The tokens you just received provide access to your entire store data. As such, they should be treated carefully and stored securely. Review our overview on Securely Storing Tokens for details on that.

Connect tokens into our script

With our OAuth Client created and access tokens generated for our store, we can now start connecting our integration through the API. Within our sso.php page, let's create the FoxyClient object and plug in our details, adding this after what we've already inserted:

$config = array(
    'client_id' => 'INSERT_CLIENT_ID',
    'client_secret' => 'INSERT_CLIENT_SECRET',
    'refresh_token' => 'INSERT_REFRESH_TOKEN'
);

if (isset($_SESSION['access_token'])) {
    $config['access_token'] = $_SESSION['access_token'];
}
if (isset($_SESSION['access_token_expires'])) {
    $config['access_token_expires'] = $_SESSION['access_token_expires'];
}

$fc = new FoxyClient($guzzle, $config);

This portion creates a $config array that contains the credentials we just received (obviously updated to the values you received), and then creates a new FoxyClient object, passing in the Guzzle instance we created previously and the config object.

You'll note that the $config object didn't include the access token we received earlier. As the access token only lasts for 2 hours, it's not something that we need to hard-code into our script. Instead, the FoxyClient instance will refresh the access token as it's needed, and we'll store it in the session.

In this example, we're hard-coding the OAuth values into our script as a simple standalone example. As the tokens and client credentials can grant access to your whole store, these values should never be committed to a public code repository, or placed anywhere that they're publicly accessible. Ideally, the client_secret and refesh_token should be stored securely in a database (preferably encrypted) or in an environment variable. If your application will be distributed instead of hosted centrally, you'll have to create and register an OAuth client with each installation. See the Authentication section for more information on that.

Searching for a customer

The main use of the API within our SSO script will be to search to see if a given customer is already in our store. If they are, we'll grab their customer ID, but if they're not, we'll create one. As such, we'll be needing to access the customers portion of the API. As a Hypermedia API, we access the API through it's URI's - so let's get the URI for the customers. From viewing the API Reference, we can see that the customers relation is within store, accessible from the API homepage.

$errors = array();
$customer_id = 0;
$customers_uri = "";

if (!isset($_SESSION['customers_uri']) || $_SESSION['customers_uri'] == '') {
    $result = $fc->get();
    if ($store_uri = $fc->getLink('fx:store')) {
        $result = $fc->get($store_uri);
        if ($customers_uri = $fc->getLink('fx:customers')) {
            $_SESSION['customers_uri'] = $customers_uri;
        }
    }
} else {
    $customers_uri = $_SESSION['customers_uri'];
}

You'll see that we're making use of the session again - this time to store the fx:customers link relation. If we can't find it in the session then we perform the requests to step through the API to get it and save it to session for next time.

With the fx:customers URI in hand, we can now perform a request to see if our customer exists. For the purpose of our example, we'll have the customer details hard-coded, but you'd be getting this information dynamically from the authentication system of your website.

$customer_email = 'customer@example.com';
$customer_password_hash = '$2a$10$N9q8uLOickgxa72ZMRZoMyeIjZAgcfl72ldGxad68LJoZdL17lhWy';

if ($customers_uri != "") {
    $result = $fc->get($customers_uri, array('email' => $customer_email, 'is_anonymous' => 0));
    $errors = $fc->getErrors($result);
    if (!count($errors)) {
        if ($result['total_items'] > 0) {
            $customer_id = $result['_embedded']['fx:customers'][0]['id'];
        } else {
            $result = $fc->post($customers_uri, array('email' => $customer_email, 'password_hash' => $customer_password_hash));
            $errors = $fc->getErrors($result);
            if (!count($errors)) {
                $result = $fc->get($fc->getLastResponseHeader("location"));
                if (!count($errors)) {
                    $customer_id = $result['id'];
                }
            }
        }
    }
} else {
    array_push($errors, "Unable to get fx:customers URI");
}

if (count($errors) > 0) {
    error_log(join(", ", $errors));
}

The code here does a couple different actions. It first performs a GET request to the customers URI, filtered based on the customers email and also filtering out any guest records. If there wasn't any errors from that request, it checks how many results were returned. If at least one, it grabs the customer's id from the first response. If there wasn't any results it instead performs a POST request to create one, using the email and password hash we hardcoded, and if successful performs a GET on the new customer resource to get it's id. Finally there is a couple lines of code for logging any errors triggered by the API calls above it to your servers error log.

When creating a customer, you'll need to pass over the password hash you have stored to synchronise the customer. Look at the customers relation in the API Reference for details on the fields that are possible. Depending on your hashing method, you may need to also pass password_salt and password_hash_config attributes. You'll also need to confirm that the customer password encrpytion method set in your store administration matches your websites method.

Finally we'll need to update the access_token and access_token_expires values that we are keeping in the session, just in case they were refreshed during this execution.

if (!isset($_SESSION['access_token']) || ($fc->getAccessToken() != $_SESSION['access_token'] && $fc->getAccessToken() != '')) {
    $_SESSION['access_token'] = $fc->getAccessToken();
}
if (!isset($_SESSION['access_token_expires']) || ($fc->getAccessTokenExpires() != $_SESSION['access_token_expires'] && $fc->getAccessTokenExpires() != '')) {
    $_SESSION['access_token_expires'] = $fc->getAccessTokenExpires();
}

Expanding for full SSO functionality

For the full Single Sign On functionality, there is a little more to it than what is detailed above. For instance, you would want to track the user's Foxy customer ID on their user record within your database, and if that is present, you don't need to interact with the API at all - you can simply use that. If you need to create a user, you'd then want to save the ID witin the user record in your database. You will also need to generate the auth_token to pass on to the Foxy checkout to automatically log them in.

You can see a PHP example of an SSO endpoint on our wiki, which you could customise with code from this tutorial.