Я только хочу получить информацию, используя Quickbooks API (кажется, что это должно быть возможно через их API). Я установил приложение на их сайте разработки, связал его с созданной мной компанией Quickbooks и пытаюсь запустить этот код, чтобы получить что-нибудь из ответа curl, но все, что я получаю, — это сообщения об ошибках авторизации (401). Почему он не авторизуется? Я изучал этот сайт в течение 12 часов, и ни один из их примеров, которые они приводят, даже не работает. Использую эту страницу в качестве ссылки: https://developer.intuit.com/docs/0050_quickbooks_api/0010_your_first_request/rest_essentials_for_the_quickbooks_api и это: https://developer.intuit.com/docs/0100_accounting/0300_developer_guides/0015_calling_data_services#/The_authorization_header
Мой файл index.php выглядит следующим образом:
<?php
define('IS_SANDBOX', 1);
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . 'oAuth.php');
// GET baseURL/v3/company/companyID/resourceName/entityID
// consumer and consumer_secret
$oAuth = new QuickBooks_IPP_OAuth('qyprdwX21R3klmiskW3AaYLnDRGNLn', 'FDPpxScC6CIgoA07Uc2NYtZJk45CqNDI1Gw4zntn');
$request = array(
'url' => array(
'base_request_uri' => IS_SANDBOX == 1 ? 'https://sandbox-quickbooks.api.intuit.com' : 'https://quickbooks.api.intuit.com',
'version' => 'v3',
'company' => 'company',
'companyID' => '123145768959777'
),
'query' => 'SELECT * FROM ESTIMATE',
'headers' => array(
'Host' => IS_SANDBOX == 1 ? 'sandbox-quickbooks.api.intuit.com' : 'quickbooks.api.intuit.com',
'Accept' => 'application/json',
'User-Agent' => 'APIExplorer'
)
);
$request_url = implode('/', $request['url']) . '/query?query=' . str_replace('+', ' ', str_replace('%7E', '~', rawurlencode($request['query']))) . '&minorversion=4';
// token, and token_secret
$headers = $oAuth->sign('GET', $request_url, 'qyprdaiy37CxGCuB8ow8XK76FYii3rnRU4AIQrHsZDcVFNnV', 'wWcpmPffdPABp6LNNyYgnraTft7bgdygAmTML0aB');
$request['headers']['Authorization'] = 'OAuth ' . array_pop($headers);
$response = curl($request_url, $request['headers']);
echo '<pre>', var_dump($response), '</pre>';
echo '<pre>', var_dump($request['headers']), '</pre>';function curl($url, $headers) {
try {
$request_headers = array();
$ch = curl_init();
if (FALSE === $ch)
throw new Exception('failed to initialize');
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
if (!empty($headers)) {
foreach($headers as $key => $value)
{
if ($key == 'GET')
{
$request_headers[] = $key . ' ' . $value;
continue;
}
$request_headers[] = $key . ': ' . $value;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disable SSL Verfication, so we can get all info from non-SSL site!
}
$data = curl_exec($ch);
$header = curl_getinfo($ch);
echo '<h2>Curl Get Info</h2>';
echo '<pre>', var_dump($header), '</pre>';
if (FALSE === $data)
throw new Exception(curl_error($ch), curl_errno($ch));
else
return $data;
curl_close($ch);
} catch(Exception $e) {
trigger_error(sprintf(
'Curl failed with error #%d: %s',
$e->getCode(), $e->getMessage()), E_USER_ERROR);
}
}
echo '<pre>', var_dump($request_url), '</pre>';?>
Мой файл oAuth.php выглядит так:
<?php
/**
* QuickBooks PHP DevKit
*
* Copyright (c) 2010 Keith Palmer / ConsoliBYTE, LLC.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* @author Keith Palmer <[email protected]>
* @license LICENSE.txt
*
* @package QuickBooks
*/
class QuickBooks_IPP_OAuth
{
private $_secrets;
protected $_oauth_consumer_key;
protected $_oauth_consumer_secret;
protected $_oauth_access_token;
protected $_oauth_access_token_secret;
protected $_version = null;
protected $_signature = null;
protected $_keyfile;
/**
*
*/
const NONCE = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const METHOD_POST = 'POST';
const METHOD_GET = 'GET';
const METHOD_PUT = 'PUT';
const METHOD_DELETE = 'DELETE';
const DEFAULT_VERSION = '1.0';
const DEFAULT_SIGNATURE = 'HMAC-SHA1';
const SIGNATURE_PLAINTEXT = 'PLAINTEXT';
const SIGNATURE_HMAC = 'HMAC-SHA1';
const SIGNATURE_RSA = 'RSA-SHA1';
/**
* Create our OAuth instance
*/
public function __construct($oauth_consumer_key, $oauth_consumer_secret)
{
$this->_oauth_consumer_key = $oauth_consumer_key;
$this->_oauth_consumer_secret = $oauth_consumer_secret;
$this->_version = QuickBooks_IPP_OAuth::DEFAULT_VERSION;
$this->_signature = QuickBooks_IPP_OAuth::DEFAULT_SIGNATURE;
}
/**
* Set the signature method
*
*
*/
public function signature($method, $keyfile = null)
{
$this->_signature = $method;
$this->_keyfile = $keyfile;
}
/**
* Sign an OAuth request and return the signing data (auth string, URL, etc.)
*
*
*/
public function sign($method, $url, $oauth_token = null, $oauth_token_secret = null, $params = array())
{
/*
print('got in: [' . $method . '], ' . $url);
print_r($params);
print('<br /><br /><br />');
*/
if (!is_array($params))
{
$params = array();
}
$params = array_merge($params, array(
'oauth_consumer_key' => $this->_oauth_consumer_key,
'oauth_signature_method' => $this->_signature,
'oauth_nonce' => $this->_nonce(),
'oauth_timestamp' => $this->_timestamp(),
'oauth_version' => $this->_version,
));
// Add in the tokens if they were passed in
if ($oauth_token)
{
$params['oauth_token'] = $oauth_token;
}
if ($oauth_token_secret)
{
$params['oauth_secret'] = $oauth_token_secret;
}
// Generate the signature
$signature_and_basestring = $this->_generateSignature($this->_signature, $method, $url, $params);
$params['oauth_signature'] = $signature_and_basestring[1];
/*
print('<pre>');
print('BASE STRING IS [' . $signature_and_basestring[0] . ']' . "\n\n");
print('SIGNATURE IS: [' . $params['oauth_signature'] . ']');
print('</pre>');
*/
$normalized = $this->_normalize($params);
/*
print('NORMALIZE 1 [' . $normalized . ']' . "\n");
print('NORMZLIZE 2 [' . $this->_normalize2($params) . ']' . "\n");
*/
if (false !== ($pos = strpos($url, '?')))
{
$url = substr($url, 0, $pos);
}
$normalized_url = $url . '?' . $normalized; // normalized URL
return array (
0 => $signature_and_basestring[0], // signature basestring
1 => $signature_and_basestring[1], // signature
2 => $normalized_url,
3 => $this->_generateHeader($params, $normalized), // header string
);
}
protected function _generateHeader($params, $normalized)
{
// oauth_signature="' . $this->_escape($params['oauth_signature']) . '",
$str = '';
if (isset($params['oauth_token']))
$str .= rawurlencode('oauth_token') . '="' . rawurlencode($params['oauth_token']) . '", ';
$nonce = rawurlencode(md5(mt_rand()));
$nonce_chars = str_split($nonce);
$formatted_nonce = '';
foreach($nonce_chars as $n => $chr)
{
if (in_array($n, array(8, 12, 16, 20)))
$formatted_nonce .= '-';
$formatted_nonce .= $chr;
}
$str .= rawurlencode('oauth_nonce') . '="' . $formatted_nonce . '", ' .
rawurlencode('oauth_consumer_key') . '="' . rawurlencode($params['oauth_consumer_key']) . '", ' .
rawurlencode(oauth_signature_method) . '="' . rawurlencode($params['oauth_signature_method']) . '", ' .
rawurlencode(oauth_timestamp) . '="' . rawurlencode($params['oauth_timestamp']) . '", ' .
rawurlencode(oauth_version) . '="' . rawurlencode($params['oauth_version']) . '", ' .
rawurlencode(oauth_signature) . '="' . $this->_escape($params['oauth_signature']) . '"';
return str_replace(array(' ', ' ', ' '), '', str_replace(array("\r", "\n", "\t"), ' ', $str));
}
/**
*
*
*/
protected function _escape($str)
{
if ($str === false)
{
return $str;
}
else
{
return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode($str)));
}
}
protected function _timestamp()
{
//return 1326976195;
//return 1318622958;
return time();
}
protected function _nonce($len = 5)
{
//return '1234';
$tmp = str_split(QuickBooks_IPP_OAuth::NONCE);
shuffle($tmp);
//return 'kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg';
return substr(implode('', $tmp), 0, $len);
}
protected function _normalize($params)
{
$normalized = array();
ksort($params);
foreach ($params as $key => $value)
{
// all names and values are already urlencoded, exclude the oauth signature
if ($key != 'oauth_secret')
{
if (is_array($value))
{
$sort = $value;
sort($sort);
foreach ($sort as $subkey => $subvalue)
{
$normalized[] = $this->_escape($key) . '=' . $this->_escape($subvalue);
}
}
else
{
$normalized[] = $this->_escape($key) . '=' . $this->_escape($value);
}
}
}
return implode('&', $normalized);
}
protected function _generateSignature($signature, $method, $url, $params = array())
{
/*
print('<pre>params for signing');
print_r($params);
print('</pre>');
*/
//if (false !== strpos($url, 'get_access'))
/*if (true)
{
print($url . '<br />' . "\r\n\r\n");
die('NORMALIZE MINE [' . $this->_normalize($params) . ']');
}*/
/*
print('<pre>');
print('NORMALIZING [' . "\n");
print($this->_normalize($params) . "]\n\n\n");
print('SECRET KEY FOR SIGNING [' . $secret . ']' . "\n");
print('</pre>');
*/
if (false !== ($pos = strpos($url, '?')))
{
$tmp = array();
parse_str(substr($url, $pos + 1), $tmp);
// Bad hack for magic quotes... *sigh* stupid PHP
if (get_magic_quotes_gpc())
{
foreach ($tmp as $key => $value)
{
if (!is_array($value))
{
$tmp[$key] = stripslashes($value);
}
}
}
$params = array_merge($tmp, $params);
$url = substr($url, 0, $pos);
}
//print('url [' . $url . ']' . "\n");
//print_r($params);
$sbs = $this->_escape($method) . '&' . $this->_escape($url) . '&' . $this->_escape($this->_normalize($params));
//print('sbs [' . $sbs . ']' . "\n");
// Which signature method?
switch ($signature)
{
case QuickBooks_IPP_OAuth::SIGNATURE_HMAC:
return $this->_generateSignature_HMAC($sbs, $method, $url, $params);
case QuickBooks_IPP_OAuth::SIGNATURE_RSA:
return $this->_generateSignature_RSA($sbs, $method, $url, $params);
}
return false;
}/*
// Pull the private key ID from the certificate
$privatekeyid = openssl_get_privatekey($cert);
// Sign using the key
$sig = false;
$ok = openssl_sign($base_string, $sig, $privatekeyid);
// Release the key resource
openssl_free_key($privatekeyid);
base64_encode($sig)
*/protected function _generateSignature_RSA($sbs, $method, $url, $params = array())
{
// $res = ...
$res = openssl_pkey_get_private('file://' . $this->_keyfile);
/*
print('key id is: [');
print_r($res);
print(']');
print("\n\n\n");
*/
$signature = null;
$retr = openssl_sign($sbs, $signature, $res);
openssl_free_key($res);
return array(
0 => $sbs,
1 => base64_encode($signature),
);
}/*
$key = $request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret);
$signature = base64_encode(hash_hmac("sha1", $base_string, $key, true));
*/
protected function _generateSignature_HMAC($sbs, $method, $url, $params = array())
{
$secret = $this->_escape($this->_oauth_consumer_secret);
$secret .= '&';
if (!empty($params['oauth_secret']))
{
$secret .= $this->_escape($params['oauth_secret']);
}
//print('generating signature from [' . $secret . ']' . "\n\n");
return array(
0 => $sbs,
1 => base64_encode(hash_hmac('sha1', $sbs, $secret, true)),
);
}
}
?>
$request['headers']
выглядит так:
array(4) {
["Host"]=>
string(33) "sandbox-quickbooks.api.intuit.com"["Accept"]=>
string(16) "application/json"["User-Agent"]=>
string(11) "APIExplorer"["Authorization"]=>
string(306) "OAuth oauth_token="qyprdaiy37CxGCuB8ow8XK76FYii3rnRU4AIQrHsZDcVFNnV",oauth_nonce="189f7f21-6dd9-c136-e208-0f33141feea5",oauth_consumer_key="qyprdwX21R3klmiskW3AaYLnDRGNLn",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1462545676",oauth_version="1.0",oauth_signature="BIpYveqCxlfVT4Ps4qJypS%2BXHh8%3D""}
Ответ выглядит так:
message=ApplicationAuthenticationFailed; errorCode=003200; statusCode=401
SignatureBaseString: GET&https%3A%2F%2Fsandbox-quickbooks.api.intuit.com%2Fv3%2Fcompany%2F123145768959777%2Fquery&minorversion%3D4%26oauth_consumer_key%3DqyprdwX21R3klmiskW3AaYLnDRGNLn%26oauth_nonce%3D189f7f21-6dd9-c136-e208-0f33141feea5%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1462545676%26oauth_token%3Dqyprdaiy37CxGCuB8ow8XK76FYii3rnRU4AIQrHsZDcVFNnV%26oauth_version%3D1.0%26query%3DSELECT%2520%252A%2520FROM%2520ESTIMATE
$request_url
выглядит так:
https://sandbox-quickbooks.api.intuit.com/v3/company/123145768959777/query?query=SELECT%20%2A%20FROM%20ESTIMATE&minorversion=4
Я забыл что-то сделать здесь? Или, может быть, что-то не так? Я должен получать все оценки в компании Quickbook с удостоверением личности 123145768959777
, но все, что я получаю, — это 401 сообщение об ошибке авторизации.
Я забыл что-то сделать здесь? Или, может быть, что-то не так?
Определенно да. Смотрите ниже для деталей:
$ headers = $ oAuth-> sign (null, …
null
не является допустимым методом HTTP-запроса. Допустимые методы HTTP-запроса GET
, POST
и т.д. Пожалуйста, обратитесь к спецификации HTTP и спецификации OAuth.
$ headers = $ oAuth-> sign (null, $ _SERVER [‘REQUEST_URI’],
Почему вы подписываете URI запроса сервера? Вы должны подписать URL, который вы отправляете запрос а не URL, который пользователь посещает на вашем собственном сайте.
$ headers = $ oAuth-> sign (null, $ _SERVER [‘REQUEST_URI’], ‘qyprdaiy37CxGCuB8ow8XK76FYii3rnRU4AIQrHsZDcVFNnV’, ‘wWcpmPffdPABp6LNNyYgnragAmtb7);
Вы не можете жестко закодировать токен доступа OAuth и его секрет. Они меняются каждые 6 месяцев и, следовательно, должны храниться где-то в базе данных / файле, чтобы их можно было изменять без редактирования кода каждые 6 месяцев.
$ request_url = implode (‘/’, $ request [‘url’]). ‘/ query? query =’. str_replace (‘+’, », str_replace (‘% 7E’, ‘~’, rawurlencode ($ request [‘query’]))). ‘&MinorVersion = 4’ ;
Это URL, который вы должны подписать.
Я должен получать все оценки из компании Quickbook с идентификатором 123145768959777, но все, что я получаю, — это 401 сообщение об ошибке авторизации.
Если вам нужна дополнительная помощь, было бы целесообразно опубликовать ваши фактические HTTP-запросы и ответы. Не так много кто-нибудь сможет сказать вам, не увидев отправленных запросов и полученных ответов.
Кроме того … вы понимаете, что вся эта тяжелая работа уже была проделана для вас, используя библиотеку, из которой вы взяли код, верно? например Вам не нужно делать ничего из того, что вы делаете — это просто заново изобретать колесо. Просто делать:
require_once dirname(__FILE__) . '/config.php';
$EstimateService = new QuickBooks_IPP_Service_Estimate();
$estimates = $EstimateService->query($Context, $realm, "SELECT * FROM Estimate STARTPOSITION 1 MAXRESULTS 10");
foreach ($estimates as $Estimate)
{
print('Estimate # ' . $Estimate->getDocNumber() . "\n");
}
Полезные ссылки:
Других решений пока нет …