Php 我如何与Coinbase';什么是API?为什么它总是失败?

Php 我如何与Coinbase';什么是API?为什么它总是失败?,php,ajax,bitcoin,coinbase-php,coinbase-api,Php,Ajax,Bitcoin,Coinbase Php,Coinbase Api,访问Coinbase API过去非常简单:您只需要一个API密钥。现在你需要一个叫做“暂时”和“签名”的东西。我在请求中传递了我的新API“Secret”、nonce和密钥,但它返回了一个“invalid_Key”错误。有什么好处 编辑3月12日:该API过去非常简单,只需要一个密钥,这意味着它非常不安全。所以他们在一周前加强了安全措施。以下是博客帖子: 现在,每个人除了获得API密钥外,还可以获得一个API“机密”。无论何时向API发出请求,都必须包含三个参数: 您的API密钥 “nonc

访问Coinbase API过去非常简单:您只需要一个API密钥。现在你需要一个叫做“暂时”和“签名”的东西。我在请求中传递了我的新API“Secret”、nonce和密钥,但它返回了一个“invalid_Key”错误。有什么好处


编辑3月12日:

该API过去非常简单,只需要一个密钥,这意味着它非常不安全。所以他们在一周前加强了安全措施。以下是博客帖子:

现在,每个人除了获得API密钥外,还可以获得一个API“机密”。无论何时向API发出请求,都必须包含三个参数:

  • 您的API密钥
  • “nonce”,这是一个唯一的数字,您可以使用它来标识某些东西。在这种情况下,您发出的每个请求都需要有一个新的数字,并且每个请求的nonce必须大于之前的一个
  • 你的API“签名”。这不是你的API“秘密”
签名是您的nonce,后面紧跟着要发布请求、参数和所有内容的完整URL。此URL还包含nonce,因此整个内容看起来如下所示:

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

然后你把整个东西编码成“SHA256”散列。如果您不知道这意味着什么,不要惊慌——您可以使用PHP已经内置的函数在一行中完成

无论如何,我在弄清楚这一切时遇到了一些困难,所以我花了一点时间在上面并编写了这个脚本,这使得获取和发布到API非常容易。我很想听听人们的想法

<?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);

?>
这样做的优点是不访问外部文件。实际上,我非常喜欢能够看到我发出了多少请求,因此可能会坚持使用(坏)
nonce.txt
方法

  • coinbaseRequest()
    函数有三个参数。第一个是您正在向其发出请求的目录——即,在“”之后应该出现的任何内容。第二个参数是“get”或“post”,这取决于它是get还是post请求。(有意义吗?)

    第三个参数是您在请求中传递的所有查询。这应该被格式化为JSON,除非它是一个GET请求,不接受任何参数(除了函数为您提供的密钥、Nonce和签名),在这种情况下,您应该将其保留为
    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>");
    }
    
    函数makebutt($data){
    $data=json_decode($data,true);
    $buttoncode=$data[“button”][“code”];
    返回(“”);
    }
    
    它不起作用,因为Coinbase最近实现了OAuth2协议。这可确保安全传输用户的个人信息:

    你们伟大的OAUTH教程

    <?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","");
    */
    
    ?>
    

    +1,但如果你以后可以像提问一样提问,然后将大部分材料放在回答中,这有助于遵循问答格式。它还允许其他人回答可能有更好答案的问题。(你总是可以在问题的末尾添加一个帖子脚本,说这是一个自我回答的问题,然后在你添加/编辑了一个你满意的答案后将其删除)。@halfer谢谢!我只是编辑它来反映这一点。太好了,谢谢。小诡辩:如果你发现自己在使用“它不起作用”这句话,即使是在一个辅导问题中,也希望它能结束或吸引到反对票。这里的读者常说“不工作”是“可能的最没有帮助的故障报告”
    ;)。所以你可能想解释一下到底出了什么问题。但除此之外,感谢您想要添加有用的信息!啊。我也修好了。显然,我还有一些学习要做!没问题,干得好!实际上,它们提供了两种不同的身份验证方法:OAuth2和上面列出的方法:
    
    <?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","");
    */
    
    ?>