3

Coinbase API へのアクセスは非常に簡単でした。必要なのは API キーだけでした。次に、「ナンス」と「署名」と呼ばれるものが必要です。リクエストで新しい API「Secret」、ナンス、およびキーを渡しましたが、「invalid_key」エラーが返されます。何を与える?

3 月 12 日編集: OAuth を介した API との対話に関するチュートリアルを追加しました。

4

3 に答える 3

3

API が非常に単純だったという事実 (キーのみが必要) は、かなり安全ではなかったことを意味します。そのため、彼らは 1 週間ほど前にセキュリティを強化しました。ブログ投稿は次のとおりです。

http://blog.coinbase.com/post/75936737678/more-security-and-granular-control-with-the-new-api

API キーに加えて、誰もが API の「シークレット」を取得できるようになりました。API にリクエストを行うときはいつでも、次の 3 つのパラメーターを含める必要があります。

  • API キー。
  • 何かを識別するために使用する一意の番号である「ナンス」。この場合、作成するすべてのリクエストには新しい番号が必要であり、各リクエストのナンスはその前のものよりも大きくする必要があります。
  • API の「署名」。これは API の「シークレット」ではありません。

署名はナンスであり、その後にリクエスト、パラメーターなどすべてを投稿する完全な URL が続きます。この URL には nonce も含まれているため、全体をまとめると次のようになります。

12345https://coinbase.com/api/v1/buttons?nonce=12345&name=Socks&price=9.95

次に、そのすべてを取得して、「SHA256」ハッシュとしてエンコードします。それが何を意味するのかわからなくても、慌てる必要はありません。PHP に既に組み込まれている関数を使用して、1 行で実行できます。

いずれにせよ、私はこれをすべて理解するのに苦労していたので、少し時間をかけてこのスクリプトをまとめました。これにより、API への GET と POST が非常に簡単になります。人の意見が聞きたい!

<?php

function coinbaseRequest($what,$getOrPost,$parameters){

//Obviously, your API Key and Secret go here.
$apikey = "blahblahblah";
$apisecret = "blahblahblahblah";    
$nonce = file_get_contents("nonce.txt") + 1;
file_put_contents("nonce.txt", $nonce, LOCK_EX);

$url = "https://coinbase.com/api/v1/" . $what . "?nonce=" . $nonce;

if($parameters != ""){
$parameters = http_build_query(json_decode($parameters), true);
}

//Here I go, hashing the Signature! Thanks, PHP, for making this easy!

$signature = hash_hmac("sha256", $nonce . $url . $parameters, $apisecret);

$ch = curl_init();

curl_setopt_array($ch, array(
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => array(
        "ACCESS_KEY: " . $apikey,
        "ACCESS_NONCE: " . $nonce,
        "ACCESS_SIGNATURE: " . $signature
    )));

if($getOrPost == "post"){
curl_setopt_array($ch, array(
    CURLOPT_POSTFIELDS => $parameters,
    CURLOPT_POST => true,
));
}

$results = curl_exec($ch);
curl_close($ch);

echo $results;
}

//This is a POST example.
coinbaseRequest("buttons", "post", '{
    "button": {
    "name": "test",
    "price_string": "1.23",
    "price_currency_iso": "USD",
    "variable_price": true
    }
}');


//This is a GET example.
coinbaseRequest("account/balance", "get", false);

?>

ノート:

  • ノンスに使っ(microtime(true)*100)てみました。問題は、10 進数を作成し、最後の数桁が削除または四捨五入され続けたため、機能しなかったことです。次に、「これをねじ込みます」と考え、空のnonce.txtファイルを作成して書き込み1、ナンスを取得するために、そのファイルの内容を取得し、 を追加1して、ファイルを新しい番号に置き換えました。これは、私が行った合計リクエスト数を示すカウンターとしての 2 つ目の目的を果たしました。

    しかし、誰かが私に、現在のマイクロタイムに基づいて ID を生成する PHP の「uniqid」関数を指摘しました。したがって、これを試すこともできます:

    $nonce = hexdec(uniqid());
    

    これには、外部ファイルにアクセスしないという利点があります。実際、自分が行ったリクエストの数を確認できるのが本当に気に入っているので、おそらく (悪い)nonce.txtメソッドを使い続けるでしょう。

  • このcoinbaseRequest()関数には 3 つのパラメーターがあります。1 つ目は、リクエストを行うディレクトリです。つまり、「https://coinbase.com/api/v1/」の後に来るはずのディレクトリです。2 番目のパラメーターは、GET 要求か POST 要求かによって、"get" または "post" です。(わかる?)

    3 番目のパラメーターは、リクエストで渡すすべてのクエリです。これは、(関数に含まれるキー、ノンス、および署名以外に) パラメーターを取らない GET 要求でない限り、JSON としてフォーマットする必要がありますfalse

編集、3 月 3 日:

coinbaseRequestによって返されたものを取得してボタンに変換する小さな関数を作成しました。

function makebutt($data){

$data = json_decode($data,true);
$buttoncode = $data["button"]["code"];

return ("<a class=\"coinbase-button\" data-code=\"" . $buttoncode . "\" href=\"https://coinbase.com/checkouts/" . $buttoncode . "\">Pay With Bitcoin</a><script src=\"https://coinbase.com/assets/button.js\" type=\"text/javascript\"></script>");
}
于 2014-02-12T13:45:40.510 に答える
0

大いなる誓いのチュートリアル

<?php

/*OAuth is great. It's also complicated. Or rather, it LOOKS complicated.

This whole script is just one big long function. It's a really, really ugly
function. I broke down everything "Barney-style" to demonstrate all the steps
in the process, and because there are some things you have to decide -- how to
record the user data, for instance.

Let's get this train wreck a rollin'.*/

function oauthRequest($apiPath,$getOrPost,$parameters){

/*You get this info from https://coinbase.com/oauth/applications*/
$clientId = "#####";
$clientSecret = "#####";
$callbackUrl = "http://www.blah.com/oauth.php";

function curling($url,$getpost,$params){
    if($params != ""){
        $params = http_build_query(json_decode($params), true);
    }
    if($getpost == "get"){
        $ispost = false;
        $url .= $params;
    }
    $ch = curl_init();
    curl_setopt_array($ch, array(
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true
    ));
    if($getpost == "post"){
        curl_setopt_array($ch, array(
            CURLOPT_POST => $ispost,
            CURLOPT_POSTFIELDS => $params
        ));
    }
    $results = curl_exec($ch);
    curl_close($ch);
    return $results;
}

/*There are two people involved here: the Client (you), and the User (the
person accessing your app or site).

You'll need 3 pieces of data for each user before your app can access their
Coinbase account: a User Code, an Access Token, and a Refresh Token.

For the purposes of this demonstration, I'm recording all of the user data in
a .txt file on my server. THIS IS PROBABLY A BAD IDEA in real life because .txt
files aren't secure at all, and this text file will only store the data for one
user (it gets overwritten every time). This is the kind of stuff you'd put in an
SQL database if you have one, or maybe in the user's cookies.*/

if(!file_exists("userdata.txt") || file_get_contents("userdata.txt") == ""){
    file_put_contents("userdata.txt",json_encode(array(
        "userCode" => "",
        "accessToken" => "",
        "refreshToken" => ""
    )), LOCK_EX);
}
$userData = json_decode(file_get_contents("userdata.txt"), true);

/*Okay. So. First thing we're going to do is see if we have a User Code for
this user. These are big alphanumeric strings that are 64 characters long. If
we have one, it'll either be in the URL of this page (the $_GET array), or
it'll be in that userdata.txt file.*/

if(array_key_exists("code",$_GET) && $_GET["code"] != ""){
    $userCode = $_GET["code"];
}else if(array_key_exists("userCode",$userData) && $userData["userCode"] != ""){
    $userCode = $userData["userCode"];
}else{

/*If we don't have a User Code, then this next snippet of code will fire. It'll
return the link for a special user-specific Coinbase page to which the user
will need to go to authorize your app to access their Coinbase account (by
signing into Coinbase and clicking a green "Authorize" button).

After authorizing your app, they'll be automatically taken to the Redirect URL
you specified, with their User Code added to the end of the URL. So if your
Redirect URL is www.blah.com/oauth.php, they'll go to www.blah.com/oauth.php?
code=123451234512345 .

This User Code never expires, and so theoretically the user should only need to
go to the authorization link once. However, if you don't make a way of getting
the User Code in the future (my fancy "userdata.txt" in this case) or they de-
authorized your app from within their Coinbase account, then they'll need to go
to the link again and re-authorize your app from the beginning.

I have it set up so my Redirect URL and the rest of my OAuth script are all on
the same page: www.blah.com/oauth.php . So the user will just start back at the
beginning of this script, and THIS time the script will see the User Code in
the URL (the $_GET array), and so will skip this next bit.

Whew. You with me so far?*/
    return ("https:/*coinbase.com/oauth/authorize?" . http_build_query(array(
        "response_type" => "code",
        "client_id" => $clientId,
        "redirect_uri" => $callbackUrl
    )));
    die;
}

/*Here I am, recording the User Code for future use in userdata.txt*/
$userData["userCode"] = $userCode;
file_put_contents("userdata.txt",json_encode($userData),LOCK_EX);

/*Alright, we've got the User Code. Now we need the Access Token -- another 64-
character string. The difference is that the Access Token expires every 2 hours
(7200 seconds). Let's see if we already have one in the userdata.txt file.*/
if(array_key_exists("accessToken",$userData) && $userData["accessToken"] != ""){
    $accessToken = $userData["accessToken"];
    $refreshToken = $userData["refreshToken"];
}else{

/*If we're here, it's because we don't have an Access Token for this user. We
get one by making this POST request:*/
    $authorization = json_decode(curling(
        "https:/*coinbase.com/oauth/token" . "?" . http_build_query(array(
            "grant_type" => "authorization_code",
            "code" => $userCode,
            "redirect_uri" => $callbackUrl,
            "client_id" => $clientId,
            "client_secret" => $clientSecret
        )), "post", ""), true);
    if(array_key_exists("error",$authorization)){

/*If something goes wrong here, I'm going to clean out userdata.txt and ask the
user to try again.*/
        file_put_contents("userdata.txt","",LOCK_EX);
        die("Something went wrong. Please refresh the page and try again.");
    }
    $accessToken = $authorization["access_token"];
    $refreshToken = $authorization["refresh_token"];
}

/*The Refresh Token is what you use to get a new Access Token once the current
Access Token has expired. The Refresh Token never expires, but can only be used
once. Anytime you get an Access Token, you'll also be given a Refresh Token.

If you don't have the Refresh Token and a working Access Token for the user,
they'll need to re-authorize your app all over again.

I'm backing up the Access Token and Refresh Token to userdata.txt*/
$userData["accessToken"] = $accessToken;
$userData["refreshToken"] = $refreshToken;
file_put_contents("userdata.txt",json_encode($userData),LOCK_EX);

/*Alright! At this point, we should have the three bits of user data we need:
the User Code, the Access Token, and the Refresh Token. So now lets try
actually making an API request.

This whole script is really just one big function called "oauthRequest". You
pass three parameters to the function: the path of the API request (everything
after https:/*coinbase.com/api/v1/), whether this API query is a GET or a POST,
and any parameters that go along with that GET or POST request. These params
first come into play here.

Let's make the API request:*/
$results = curling("https:/*coinbase.com/api/v1/" . $apiPath . "?" . http_build_query(array(
        "access_token" => $accessToken
    )), $getOrPost, $parameters);

/*Now we're going to make sure the request actually worked, and didn't get
rejected because the Access Token was expired. If it WAS expired, the
results should be blank. (It'll return a 401 if you want to get fancy.)*/
$resultsArray = json_decode($results);
if(count($resultsArray) < 1){

/*Looks like it did expire, so now we make a POST request using the Refresh
token, which will return a new Access Token AND a new Refresh Token.*/
    $reAuthorization = json_decode(curling(
        "https:/*coinbase.com/oauth/token?" . http_build_query(array(
            "grant_type" => "refresh_token",
            "code" => $userCode,
            "refresh_token" => $refreshToken
        )), "post", ""), true);
    $accessToken = $reAuthorization["access_token"];
    $refreshToken = $reAuthorization["refresh_token"];

/*Let's back those up to userdata.txt...*/
    $userData["accessToken"] = $accessToken;
    $userData["refreshToken"] = $refreshToken;
    file_put_contents("userdata.txt",json_encode($userData),LOCK_EX);

/*...and try the API request all over again:*/
    $results = curling("https:/*coinbase.com/api/v1/" . $apiPath . "?" . http_build_query(array(
            "access_token" => $accessToken
        )), $getOrPost, $parameters);

/*If it doesn't work THIS time, I'm going to clean out userdata.txt and ask
the user to try again. One of the codes probably got all mungled up.*/
    $resultsArray = json_decode($results);
    if(array_key_exists("error",$resultsArray)){
        file_put_contents("userdata.txt","",LOCK_EX);
        die("Something went wrong. Please refresh the page and try again.");
    }
}

/*If, however, everything went right, then this function will return the JSON
string with the data from the API! Hooray!*/
return $results;

}

/*Here are 4 different example requests you can make.*/

/*
echo oauthRequest("account/generate_receive_address","post","");

echo oauthRequest("buttons","post",'{
    "button": {
        "name": "test",
        "type": "buy_now",
        "price_string": ".01",
        "price_currency_iso": "USD"
    }
}');

echo oauthRequest("prices/buy","get",'{
    "qty": 1,
    "currency": "USD"
}');

echo oauthRequest("account/balance","get","");
*/

?>
于 2014-03-12T19:42:27.363 に答える