REST API Extended - Example: WP-to-WP

Note: This article relates to the REST API Extended pro add-on.

Overview

The AffiliateWP REST API provides a powerful set of tools for interacting with the plugin and its data from afar.

One of the most common use cases (and the basis of some of our most popular requested features) have to do with being able to interact with AffiliateWP running on one WordPress-powered site via another WordPress-powered site.

The following examples cover how to send requests from one WordPress site to another WordPress site running AffiliateWP. We'll cover how to build requests and analyze responses, as well as give a primer on what kind of experience to expect when interacting with the AffiliateWP REST API endpoints.

What you need to know

The following examples assume you have a baseline of knowledge about how WordPress handles sending remote requests and retrieving responses.

If you're unfamiliar with the HTTP API or REST API, we recommend checking out the HTTP API article in the official plugin developer handbook as well as the REST API handbook.

Information Discovery

As outlined in the Managing Affiliates[ 1], Creatives[2], Payouts[3], Referrals[4], and Visits[5] articles, the REST API Extended add-on brings full CRUD (Create, Read, Update, and Delete) capabilities when interacting with AffiliateWP from afar.

For the most part, remote responses are structured consistently across object types. That said, depending on endpoint used, certain fields specific to the given object types will be required in various circumstances.

The easiest way to determine which fields are required or which parameters are accepted is to send an OPTIONS request to the endpoint; the response from the OPTIONS request should tell you everything you need to know.

For instance, the following is a section of the response of an OPTIONS request sent to the /v1/creatives/ endpoint with REST API Extended activated:

{
  "namespace": "affwp/v1",
  "methods": [
    "GET",
    "POST",
    "PUT",
    "PATCH"
  ],
  "endpoints": [
    {
      "methods": [
        "POST",
        "PUT",
        "PATCH"
      ],
      "args": {
        "name": {
          "required": false,
          "description": "Name of the creative.",
          "type": "string"
        },
        "description": {
          "required": false,
          "description": "Description for the creative.",
          "type": "string"
        },
        "url": {
          "required": false,
          "description": "URL the creative points to.",
          "type": "string"
        },
        "text": {
          "required": false,
          "description": "Text for the creative.",
          "type": "string"
        },
        "image": {
          "required": false,
          "description": "ID of the media library image associated with the creative",
          "type": "integer"
        },
        "status": {
          "required": false,
          "description": "The creative status.",
          "type": "string"
        },
        "date": {
          "required": false,
          "description": "The date the creative was created.",
          "type": "string"
        }
      }
    }
  ],

You can see that the available request methods for this endpoint are clearly listed and available arguments for each defined and listed within the hierarchy below. Other useful information passed back in an OPTIONS request includes schema information – defining the fields that will be present in a single object, and more.

Building Requests

Now that you have a good basis in what you need to know and how to find out more information, let's build some sample requests.

Authenticating

If you remember from the REST API Extended - Overview article, all requests sent need to include an Authorization header built using the Basic Auth scheme. The Basic Auth header in AffiliateWP is achieved using the public key and token values. The following is an example of a Basic Auth header:

Authorization : Basic N2Q4NTM1OWM4NzlkYWNjOWE2ZmMxZjgxYjQ2ZDYyZDE6YmE3YjUwZGUyMjZkOGI2YzRkNjQyMjA1YjcwNDUwMjY=

"Authorization" is the key, and "Basic N2Q4NTM..." is the value. The value is built by encoding the combination of the public key and token values, separated by a colon, and concatenating "Basic " on the front. For example:

"Basic " . base64_encode( "{$public_key}:{$token}" );

When using WordPress HTTP API functions, the Authorization header should be sent as follows:

wp_remote_*( 'request_url', array(<br>    'headers' => array(<br>        'Authorization' => 'Basic hash_value'<br>    )<br>) );

For more information on how the AffiliateWP REST API authenticates, check out the REST API - Authentication article.

Example Request: Create an affiliate

As noted in the Managing Affiliates article, a user_id is required when creating an affiliate. Alternatively, the create_user argument can be sent along with a payment_email value to create a user and an affiliate in the same step.

The following is the WordPress code needed to remotely create an affiliate when you already have a valid user_id:

$request_url = add_query_arg( array( 'user_id' => 5 ), 'https://example.com/wp-json/affwp/v1/affiliates' );<br><br>$response = wp_remote_post( $request_url, array(<br>    'headers' => array(<br>        'Authorization' => 'Basic hash_value'<br>    )<br>) );

In the above snippet, note that the user_id value is passed via $request_url vs via the 'body' argument, which is supported for POST requests. This is done for consistency with other endpoints' examples, namely those leveraging the PUT or PATCH methods, which don't support sending parameters via the request body.

Example Request: Create a payout

Similarly to creating affiliates, new payouts also require certain fields, specifically affiliate_id and referrals values.

The following is the code needed to remotely create a new payout, given a valid affiliate_id and list of referral IDs:

$request_url = add_query_arg( array(<br>    'affiliate_id'  => 2,<br>    'referrals'     => '2,3,4',<br>    'payout_method' => 'REST',<br>), 'https://example.com/wp-json/affwp/v1/payouts' );<br><br>$response = wp_remote_post( $request_url, array(<br>    'headers' => array(<br>        'Authorization' => 'Basic hash_value'<br>    )<br>) );

Note that the payout_method value is not necessarily needed, just nice to provide context that the payout was generated via REST.

Example Request: Updating a referral

When updating an object via REST, it's worth noting that since the ID of the object you're modifying is actually part of the endpoint, that primary key's value doesn't also need to be sent with the request.

The following is an example for how to update a referral's status from unpaid to paid:

$request_url = add_query_arg( array( 'status' => 'paid', 'https://example.com/wp-json/affwp/v1/referrals/3' );<br><br>$response = wp_remote_request( $request_url, array(<br>    'method'  => 'PATCH',<br>    'headers' => array(<br>        'Authorization' => 'Basic hash_value'<br>    )<br>) );

Note that instead of wp_remote_post() as before, this request uses wp_remote_request() with the 'method' => 'PATCH' argument passed. This tells the REST API that you're specifically sending a PATCH request.

Also note that the referral ID of 3 is passed as part of the endpoint rather than in the URL like status is.

Analyzing Responses

In addition to crafting requests, it's important to have a good handle on how responses are structured and to be familiar with how to validate that a request was successful within the WordPress sphere.

In the first two examples under "Building Requests" we talked about crafting requests for creating new affiliates and payouts.

When examining a response, one of the simplest ways to confirm it was successful is to look at the response code:

$response_code = wp_remote_retrieve_response_code( $response );

if ( 201 === $response_code ) {
    // success
}

In the case of AffiliateWP's  creation and update endpoints, successful requests will send back responses with status code 201. For successful  deletion requests, response codes will be the default 200.

Responses sent back for successful deletion requests will also include a deleted: true key:value pair, along with a copy the former object:

{<br>  "deleted": true,<br>  "previous": {<br>    "affiliate_id": 35,<br>    "user_id": 11,<br>    "rate": "",<br>    "rate_type": "",<br>    "payment_email": "test-test2@affiliate.dev",<br>    "status": "active",<br>    "earnings": 0,<br>    "unpaid_earnings": 0,<br>    "referrals": 0,<br>    "visits": 0,<br>    "date_registered": "2017-01-31 07:31:05",<br>    "id": 35<br>  }<br>}

All together now

Now that we've covered how to craft requests and analyze responses, let's look at how to craft a create affiliate request, then examine and proceed with parsing the retrieved information on success:

$request_url = add_query_arg( array( 'user_id' => 5 ), 'https://example.com/wp-json/affwp/v1/affiliates' );<br>
// Send the request, storing the return in $response.<br>$response = wp_remote_post( $request_url, array(<br>    'headers' => array(<br>        'Authorization' => 'Basic hash_value'<br>    )<br>) );

// Check for the requisite response code. If 201, retrieve the response body and continue.
if ( 201 === wp_remote_retrieve_response_code( $response ) ) {
    $body = wp_remote_retrieve_body( $response );
    // do something successful
} else {
    // maybe display an error message
}

Wrap up

In this article we've covered a lot of information. The major points covered were:

  • How to build Authorization headers for requests
  • Fundamentals for crafting remote requests
  • Fundamentals for examining responses

In closing, it's worth mentioning that all of the examples in this article are working examples, given that the Authorization header values have been crafted correctly.

One major point not covered here is API key security. It's easy enough to hard-code API keys and offload crafting the Authorizationheader values to a helper function, but if you're planning to distribute your code, precautions should be taken to ensure you don't accidentally distribute your keys as well.

If you're working on the front-end with JavaScript, one common method for mitigating unintended exposure of secret keys is to offload the sending of remote requests and parsing of responses to Ajax handlers, allowing PHP to handle the business end of the request.