Appendix A - Domain/currency mapping
| Domain | Currency |
| AbeBooks.com | USD |
| AbeBooks.co.uk | GBP |
| AbeBooks.it | EUR |
| AbeBooks.fr | EUR |
| AbeBooks.de | EUR |
| IberLibro.com | EUR |
| ZVAB | EUR |
Appendix B - Order Status Descriptions
| Order Status | Description |
| 04 | Extra Shipping Accepted |
| 05 | Ordered |
| 14 | Item Unavailable |
| 17 | Cancelled By Buyer |
| 19 | Credit Card Rejected |
| 24 | Refunded |
| 35 | Shipped |
| 53 | Max Price Exceeded |
| 54 | Inactive Seller |
| 90 | Extra Shipping Rejected |
| 93 | Extra Shipping Requested |
Appendix C - Response Status Codes
| Status Code | Description |
| 2000 | Order status successfully retrieved |
| 2002 | Dryrun succeeded |
| 2003 | Duplicate request received for order currently being processed |
| 2010 | Order created successfully |
Error Codes
| Status code | Description |
| 4000 | Malformed message |
| 4001 | The credit card token provided did not match a credit card on your account |
| 4002 | No listings were included with the request |
| 4002 | One or more listings had a quantity less than 1 |
| 4002 | The total number of items ordered exceeded the limit of 250 |
| 4003 | Unable to parse JSON |
| 4090 | Received duplicate RequestID with different content |
| 4010 | Authentication failure |
| 4011 | The Abe-Date header was missing |
| 4012 | Unable to parse the Abe-Date header |
| 4013 | Request expired |
| 4014 | Invalid access key |
| 4015 | Access key expired |
| 4016 | The Abe-Signature header was missing |
| 4017 | Invalid request signature |
| 4018 | The Abe-RequestId header was missing or invalid |
| 4030 | Not permitted to access |
| 4220 | The request was malformed, details can be found in the response body |
| 4293 | Too many requests with an invalid CCToken were made |
| 4292 | Too many failed authentication attempts were made |
| 4291 | Request exceeds rate limit |
| 4041 | Order not found |
| 4042 | Could not find the resource/malformed URI |
| 4050 | HTTP method not supported |
| 4060 | Requested mediatype not supported |
| 4150 | Submitted mediatype not supported |
| 5000 | Server error, this incident has been recorded |
| 5030 | Server is temporarily unavailable or busy |
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).hexdigest()
# Task 2: Gather signature elements
# First element
method = 'POST'
# Second element
uri = 'https://purchase.api.abebooks.com/v1/orders'
# Third element
timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
# Fourth element is the body checksum
# Task 3: create the final string
final_string = '\n'.join([method, uri, timestamp, body_checksum])
print "final string to sign: " + final_string
# Task 4: generate the request signature
access_key = 'SAMPLE_ACCESS_KEY'
secret_key = 'SAMPLE_SECRET_KEY'
signature = hmac.new(key=secret_key, msg=final_string, digestmod=hashlib.sha256).hexdigest()
print signature
# Task 5: submit the request: (you can use any library you like)
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)Sample Code: 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.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
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 = SigningTutorial.getChecksum(messageBody);
// Task 2: Gather signature elements
// First element
String method = "POST";
// Second element
String uri = "https://purchase.api.abebooks.com/v1/orders";
// Third element
String dateTime = Instant.now().toString();
// Fourth element is the body checksum
// Task 3: create the final string
String finalString = method + "\n" + encodedURI + "\n" + dateTime + "\n" + bodyChecksum;
String requestId = UUID.randomUUID().toString();
System.out.println(finalString);
System.out.println(requestId);
// Task 4: generate the request signature
String signature = SigningTutorial.getHMAC(secretKey, finalString);
// Task 5: submit the request:
// We used apache's http client as an example, you don't have to;
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpUriRequest uriRequest = new HttpPost(uri);;
ContentType json = ContentType.create("application/json");
((HttpPost)uriRequest).setEntity(new StringEntity(messageBody, json));
// Set headers
uriRequest.setHeader("Abe-Date", dateTime);
uriRequest.setHeader("Abe-Access-Key", accessKey);
uriRequest.setHeader("Abe-Signature", signature);
uriRequest.setHeader("Abe-RequestId", requestId);
// Execute the method
CloseableHttpResponse response = httpClient.execute(uriRequest);
try {
// do something useful with the response and ensure it is fully consumed
System.out.println(response.getStatusLine());
HttpEntity entity1 = response.getEntity();
System.out.println(EntityUtils.toString(entity1));
EntityUtils.consume(entity1);
} finally {
response.close();
}
}
public static String getChecksum(String message) throws NoSuchAlgorithmException
{
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(message.getBytes(StandardCharsets.UTF_8));
String checksum = Hex.encodeHexString(md.digest());
return checksum;
}
public static String getHMAC(String secretKey, String message) throws NoSuchAlgorithmException, InvalidKeyException
{
Mac sha256_HMAC = Mac.getInstance(HMAC_SHA256);
SecretKeySpec secret_key = new SecretKeySpec(secretKey.getBytes(), HMAC_SHA256);
sha256_HMAC.init(secret_key);
String hash = Hex.encodeHexString(sha256_HMAC.doFinal(message.getBytes()));
return hash;
}
}Sample Code: C#
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Globalization;
using System.Security.Cryptography;
namespace FileAPI
{
public class SigningTutorial
{
static public void Main()
{
// Task 1: get message body checksum (empty string if GET request)
string messageBody = "sample content";
string bodyChecksum = SigningTutorial.GetChecksum(messageBody);
// Task 2: Gather signature elements
// First element
string method = "POST";
// Second element
string uri = "https://purchase.api.abebooks.com/v1/orders";
string encodedURI = SigningTutorial.UrlEncode(uri.ToLower());
// Third element
string timeStamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
// Fourth element is the body checksum
// Task 3: create the final string
string finalString = method + "\n" + encodedURI + "\n" + timeStamp + "\n" + bodyChecksum;
// Task 4: generate the request signature
string secretKey = "EXAMPLE_SECRET_KEY";
string signature = SigningTutorial.GetHMAC(secretKey, finalString);
// Task 5: submit the request
WebRequest request = WebRequest.Create(uri);
request.Method = method;
// Set the headers
string accessKey = "EXAMPLE_ACCESS_KEY";
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());
// Set the message body properties
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);
}
// Execute the method
string result = null;
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
result = reader.ReadToEnd();
Console.WriteLine(result);
// other handling...
}
}
private static string GetChecksum(string message)
{
var encoding = new System.Text.ASCIIEncoding();
byte[] messageBytes = encoding.GetBytes(message);
using (var sha256 = new SHA256Managed())
{
byte[] hashbytes = sha256.ComputeHash(messageBytes);
string hexhash = BitConverter.ToString(hashbytes).Replace("-", "").ToLower();
return hexhash;
}
}
private static string GetHMAC(string secret, string message)
{
var encoding = new System.Text.ASCIIEncoding();
byte[] keyByte = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
string hexhash = BitConverter.ToString(hashmessage).Replace("-", "").ToLower();
return hexhash;
}
}
}
}