Automating Crypto Buys with the Coinbase API and PHP

As part of a separate effort to automate buying and selling crypto for myself, I splintered off and decided to do a quick, fun project. I created a “Virtual Swear Jar” which is a simple progressive web app on my phone. With a single tap on the app icon, it opens and buys $10 worth of crypto through Coinbase. I have it set to purchase Ethereum but it can easily be swapped for Bitcoin or any of the other coins listed on Coinbase. Like many of my other weekend projects, this one is not day-to-day practical but it helped me explore the Coinbase API for the first time.

The Official PHP SDK for Coinbase has been (understandably) deprecated so I’ve explicitly used CURL for everything.

Step 1: Get Coinbase API service epoch time

The API requires all calls to be within 30 seconds of the API service time. Coinbase makes this somewhat easy with a time endpoint. Rather than attempting to sync my server / adjust time offset, I opted to just call their time endpoint for the current API service time and use that for the remainder of my execution.

$url="https://api.coinbase.com/v2/time";
$curl = curl_init();
curl_setopt_array($curl, array(
  CURLOPT_URL => $url,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_MAXREDIRS => 1,
  CURLOPT_TIMEOUT => 5,
  CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
  CURLOPT_CUSTOMREQUEST => 'GET',
));

$response = curl_exec($curl);
$err = curl_error($curl);
$response = json_decode($response, true);

if(!empty($err)){
  echo $err;
  die();
}

if(!empty($response['errors'])){
  echo "<pre>";
  print_r($response);
  echo "</pre>";
  die();
}

$cb_time=$response['data']['epoch'];

Step 2: Get Wallet/Account ID

Your single account is composed of a unique wallet for each currency and each has a unique account ID. To get these, we have to hit the accounts endpoint with a signed/authenticated request. The signature has to be an HMAC hash using sha256 algorithm. The message must be a concatenation of “timestamp + method + requestPath + body” where body can be null for GET requests like below. POST requests have to be json format.

$api_secret=<SHOULD BE REFERENCED FROM AN ENVIRONMENTAL VARIABLE SAFE>;
$url="https://api.coinbase.com/v2/accounts";
$message = strval($cb_time) . "GET" . "/v2/accounts";
$signature = hash_hmac('sha256', $message, $api_secret);

$curl = curl_init();
curl_setopt_array($curl, array(
  CURLOPT_URL => $url,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_MAXREDIRS => 1,
  CURLOPT_TIMEOUT => 5,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "CB-ACCESS-KEY: <SHOULD BE REFERENCED FROM AN ENVIRONMENTAL VARIABLE SAFE>",
    "CB-ACCESS-SIGN: $signature",
    "CB-ACCESS-TIMESTAMP: $cb_time"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);
$response = json_decode($response, true);

if(!empty($err)){
  echo $err;
  die();
}

if(!empty($response['errors'])){
  echo "<pre>";
  print_r($response);
  echo "</pre>";
  die();
}

echo "<pre>";
print_r($response);
echo "</pre>";

This will output your account IDs and, for the sake of this tinkering, I’m manually copying mine for step 3.

Step 3: Making the Buy call

$acct=<SHOULD BE REFERENCED FROM AN ENVIRONMENTAL VARIABLE SAFE>;
$api_secret=<SHOULD BE REFERENCED FROM AN ENVIRONMENTAL VARIABLE SAFE>;
$url="https://api.coinbase.com/v2/accounts/$acct/buys";
$method = "POST";
$post = [
    'amount' => 10,//10 = 10 USD
    'currency' => 'USD',
    'commit'   => 'true'//set to false to disable the commitment/completion of transaction for user confirmation.
];
$post=json_encode($post);

$message = strval($cb_time) . $method . "/v2/accounts/$acct/buys" . $post;
$signature = hash_hmac('sha256', $message, $api_secret);

$curl = curl_init();
curl_setopt_array($curl, array(
  CURLOPT_VERBOSE => true,
  CURLOPT_URL => $url,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_MAXREDIRS => 1,
  CURLOPT_TIMEOUT => 5,
  CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
  CURLOPT_CUSTOMREQUEST => "$method",
  CURLOPT_POSTFIELDS => $post,
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/json",
    "CB-ACCESS-KEY: <SHOULD BE REFERENCED FROM AN ENVIRONMENTAL VARIABLE SAFE>",
    "CB-VERSION: 2016-03-08",
    "CB-ACCESS-SIGN: $signature",
    "CB-ACCESS-TIMESTAMP: $cb_time"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);
$response = json_decode($response, true);

if(!empty($err)){
  echo $err;
  die();
}

if(!empty($response['errors'])){
  echo "<pre>";
  print_r($response);
  echo "</pre>";
  die();
}

$amount_purchased=$response['data']['amount']['amount'];//0.000002238
$symbol_purchased=$response['data']['amount']['currency'];//BTC
$transaction_total=$response['data']['total']['amount'];//10.99 ($)
$symbol_price=$response['data']['unit_price']['amount'];//44682.75 ($)
$reference=$response['data']['user_reference'];//12345

echo "$amount_purchased $symbol_purchased purchased for $$transaction_total @ $".number_format($symbol_price); 
echo "REF: $reference<br /><pre>";
print_r($response);
echo "</pre>";

And we’re done! You’ll see the transaction confirmation triggers like any other and the currencies should appear in your account as soon as the transaction completes.

Step 4: Adding some flair/styling

As mentioned above, I just used a simple PWA for this project and applied some simple, quick styling to make it look nice. Here’s a demo of the final result: