Authentication

The Purchase API uses client signing in order to secure your data and prevent any malicious requests. The steps below indicate how to use your API keys to sign your request and successfully authenticate.

Task 1: Generate a checksum for the request content

Provide a SHA-256 checksum of your message content. The message_content variable contains the body of the message you plan to send (without any headers). If your message content is empty (as is often the case for GET requests), substitute an empty string '' for the content.

content_checksum = HexEncode(SHA256(message_content))

Sample content checksum for the string "sample payload":

eee57820203860ea469843dfba7bbb970021cae59fcc6e99056937bdec33fd02

Task 2: Collect signature elements

The unsigned request consists of four elements that you concatenate together into a single string ready for signing.

First element — The request method (GET, PUT, POST, DELETE) in all upper-case:

POST

Second element — The URI of the request in canonical form. The canonical URI is the URI-encoded version of everything in the URI from the HTTP host, including any question mark character (?) and all query string parameters. Normalize URI paths according to RFC 3986 by removing redundant and relative path components and convert everything to lower-case (except where upper-case is required in any parameters):

https://purchase.api.abebooks.com/v1/orders

Third element — A timestamp for the request, expressed in ISO 8601 format (YYYY-MM-DDThh:mm:ssZ) using UTC. Your request is valid for five minutes from this timestamp:

2017-09-18T23:25:35Z

Fourth element — The content checksum calculated in Task 1:

eee57820203860ea469843dfba7bbb970021cae59fcc6e99056937bdec33fd02

Task 3: Create the final string

Concatenate all elements together, placing a single newline character between each:

action = 'POST'
uri = 'https://purchase.api.abebooks.com/v1/orders'
abe_date = '2017-09-18T23:25:35Z'
content_checksum = 'eee57820203860ea469843dfba7bbb970021cae59fcc6e99056937bdec33fd02'
final_string = action + '\n' + uri + '\n' + abe_date + '\n' + content_checksum

Task 4: Generate the request signature

Use your secret key with a SHA-256 HMAC algorithm to sign the final string. Credentials can be obtained from Manage API Keys.

secret_key = '9ea20986-8f49-42f1-aa27-63EXAMPLEKEY'
access_key = 'EXAMPLEACCESSKEY'

abe_signature = HMAC(secret_key, final_string, SHA256).hexdigest()

Signature for the example in Task 3:

35922da3a3b457cc0a7510fd7ae1be15e93b0dcc4cdb0db5ba87434a182fc585

Task 5: Submit the request

Call the API with the appropriate request method and URI, providing the following headers:

Header Description
Abe-Date The timestamp used during signing
Abe-Access-Key Your Purchase API access key
Abe-Signature The signature calculated above
Abe-RequestId A unique ID (GUID strongly recommended) for duplicate handling and error reporting

Example full HTTP request:

POST https://purchase.api.abebooks.com/v1/orders HTTP/1.1
Abe-Date: 2017-09-18T23:25:35Z
Abe-Access-Key: EXAMPLEACCESSKEY
Abe-Signature: 9b6156bdd88cddf86fee8bab59533fdb23bf3887a890b62bba606d36396e6100
Abe-RequestId: f27d1de5-e37e-4760-b00c-d539cd7ce68e

sample content

Sample Code

Python

from datetime import datetime
import hashlib, hmac
import uuid
import requests

# Task 1: get message body checksum (empty string if GET request)
message_body = "sample payload"
body_checksum = hashlib.sha256(message_body.encode()).hexdigest()

# Task 2: Gather signature elements
method = 'POST'
uri = 'https://purchase.api.abebooks.com/v1/orders'
timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')

# Task 3: create the final string
final_string = '\n'.join([method, uri, timestamp, body_checksum])

# Task 4: generate the request signature
access_key = 'SAMPLE_ACCESS_KEY'
secret_key = 'SAMPLE_SECRET_KEY'
signature = hmac.new(
    key=secret_key.encode(),
    msg=final_string.encode(),
    digestmod=hashlib.sha256
).hexdigest()

# Task 5: submit the request
requestid = str(uuid.uuid4())
header_dictionary = {
    'Abe-Date': timestamp,
    'Abe-Access-Key': access_key,
    'Abe-Signature': signature,
    'Abe-RequestId': requestid,
    'Content-Type': 'application/json'
}
response = requests.post(uri, data=message_body, headers=header_dictionary)

Java

import org.apache.http.client.methods.*;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.HttpEntity;
import org.apache.http.util.EntityUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;

public class SigningTutorial {

  public static final String HMAC_SHA256 = "HmacSHA256";

  public static void main(String[] args)
      throws IOException, NoSuchAlgorithmException, InvalidKeyException {

    String accessKey = "SAMPLE_ACCESS_KEY";
    String secretKey = "SAMPLE_SECRET_KEY";

    // Task 1: get message body checksum (empty string if GET request)
    String messageBody = "sample content";
    String bodyChecksum = getChecksum(messageBody);

    // Task 2: Gather signature elements
    String method = "POST";
    String uri = "https://purchase.api.abebooks.com/v1/orders";
    String dateTime = Instant.now().toString();

    // Task 3: create the final string
    String finalString = method + "\n" + uri + "\n" + dateTime + "\n" + bodyChecksum;
    String requestId = UUID.randomUUID().toString();

    // Task 4: generate the request signature
    String signature = getHMAC(secretKey, finalString);

    // Task 5: submit the request
    CloseableHttpClient httpClient = HttpClients.createDefault();
    HttpPost uriRequest = new HttpPost(uri);

    ContentType json = ContentType.create("application/json");
    uriRequest.setEntity(new StringEntity(messageBody, json));

    uriRequest.setHeader("Abe-Date", dateTime);
    uriRequest.setHeader("Abe-Access-Key", accessKey);
    uriRequest.setHeader("Abe-Signature", signature);
    uriRequest.setHeader("Abe-RequestId", requestId);

    CloseableHttpResponse response = httpClient.execute(uriRequest);
    try {
      System.out.println(response.getStatusLine());
      HttpEntity entity = response.getEntity();
      System.out.println(EntityUtils.toString(entity));
      EntityUtils.consume(entity);
    } finally {
      response.close();
    }
  }

  public static String getChecksum(String message) throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    md.update(message.getBytes(StandardCharsets.UTF_8));
    return Hex.encodeHexString(md.digest());
  }

  public static String getHMAC(String secretKey, String message)
      throws NoSuchAlgorithmException, InvalidKeyException {
    Mac sha256HMAC = Mac.getInstance(HMAC_SHA256);
    SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), HMAC_SHA256);
    sha256HMAC.init(keySpec);
    return Hex.encodeHexString(sha256HMAC.doFinal(message.getBytes()));
  }
}

C#

using System;
using System.Net;
using System.IO;
using System.Text;
using System.Security.Cryptography;

namespace PurchaseApiExample
{
  public class SigningTutorial
  {
    static public void Main()
    {
      // Task 1: get message body checksum (empty string if GET request)
      string messageBody = "sample content";
      string bodyChecksum = GetChecksum(messageBody);

      // Task 2: Gather signature elements
      string method = "POST";
      string uri = "https://purchase.api.abebooks.com/v1/orders";
      string timeStamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");

      // Task 3: create the final string
      string finalString = method + "\n" + uri + "\n" + timeStamp + "\n" + bodyChecksum;

      // Task 4: generate the request signature
      string secretKey = "EXAMPLE_SECRET_KEY";
      string signature = GetHMAC(secretKey, finalString);

      // Task 5: submit the request
      string accessKey = "EXAMPLE_ACCESS_KEY";
      WebRequest request = WebRequest.Create(uri);
      request.Method = method;

      request.Headers.Add("Abe-Date", timeStamp);
      request.Headers.Add("Abe-Access-Key", accessKey);
      request.Headers.Add("Abe-Signature", signature);
      request.Headers.Add("Abe-RequestId", Guid.NewGuid().ToString());

      if (!string.IsNullOrEmpty(messageBody))
      {
        byte[] messageBodyBytes = Encoding.UTF8.GetBytes(messageBody);
        request.ContentType = "application/json";
        request.ContentLength = messageBodyBytes.Length;
        Stream newStream = request.GetRequestStream();
        newStream.Write(messageBodyBytes, 0, messageBodyBytes.Length);
      }

      using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
      {
        StreamReader reader = new StreamReader(response.GetResponseStream());
        Console.WriteLine(reader.ReadToEnd());
      }
    }

    private static string GetChecksum(string message)
    {
      byte[] messageBytes = Encoding.ASCII.GetBytes(message);
      using (var sha256 = new SHA256Managed())
      {
        byte[] hashBytes = sha256.ComputeHash(messageBytes);
        return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
      }
    }

    private static string GetHMAC(string secret, string message)
    {
      byte[] keyByte = Encoding.ASCII.GetBytes(secret);
      byte[] messageBytes = Encoding.ASCII.GetBytes(message);
      using (var hmacsha256 = new HMACSHA256(keyByte))
      {
        byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
        return BitConverter.ToString(hashmessage).Replace("-", "").ToLower();
      }
    }
  }
}