├── .gitignore
├── README.md
├── _config.php
├── certs
└── ca-bundle.crt
├── composer.json
├── index.php
├── lib
├── OAuthSimple.php
└── XeroOAuth.php
├── license.txt
├── partner.php
├── private.php
├── public.php
└── tests
├── testRunner.php
└── tests.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .settings
2 | .project
3 | .buildpath
4 | .gitattributes
5 | *.pem
6 | *.p12
7 | *.cer
8 | *.pfx
9 | <<<<<<< HEAD
10 | .*
11 | =======
12 | >>>>>>> 65378c19b093593e1019e5cf6b6ffc72e389ecc5
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Warning: This lib is no longer maintained.
2 |
3 | ## Looking for OAuth 2.0?
4 | Please checkout [Xero PHP sdk for OAuth 2.0](https://github.com/XeroAPI/xero-php-oauth2) and it's companion [kitchen sync app](https://github.com/XeroAPI/xero-php-oauth2-app)
5 |
6 | ## Looking for OAuth 1.0a?
7 | Please checkout the community project https://github.com/calcinai/xero-php
8 |
9 |
10 | XeroOAuth-PHP (DEPRECATED)
11 | -----------------------
12 | This repository has been archived and no further issues or pull requests will reviewed. Feel free to fork the repo and work with the code.
13 |
14 | PHP library for working with the Xero OAuth API.
15 |
16 | Intro
17 | ======
18 | XeroOAuth-PHP is a sample library for use with the Xero API ( To complete the OAuth flow follow this URL: ' . $authurl . ' To complete the OAuth flow follow this URL: ' . $authurl . 'Howdy
7 |
8 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/lib/OAuthSimple.php:
--------------------------------------------------------------------------------
1 | ing uses OAuth without that minimal set of
60 | * signatures.
61 | *
62 | * If you want to use the higher order security that comes from the
63 | * OAuth token (sorry, I don't provide the functions to fetch that because
64 | * sites aren't horribly consistent about how they offer that), you need to
65 | * pass those in either with .signatures() or as an argument to the
66 | * .sign() or .getHeaderString() functions.
67 | *
68 | * Example:
69 |
70 | sign(Array('path'=>'http://example.com/rest/',
73 | 'parameters'=> 'foo=bar&gorp=banana',
74 | 'signatures'=> Array(
75 | 'api_key'=>'12345abcd',
76 | 'shared_secret'=>'xyz-5309'
77 | )));
78 | ?>
79 | Some Link;
80 |
81 | *
82 | * that will sign as a "GET" using "SHA1-MAC" the url. If you need more than
83 | * that, read on, McDuff.
84 | */
85 |
86 | /** OAuthSimple creator
87 | *
88 | * Create an instance of OAuthSimple
89 | *
90 | * @param api_key {string} The API Key (sometimes referred to as the consumer key) This value is usually supplied by the site you wish to use.
91 | * @param shared_secret (string) The shared secret. This value is also usually provided by the site you wish to use.
92 | */
93 | public function __construct ($APIKey = "",$sharedSecret=""){
94 | if (!empty($APIKey))
95 | $this->_secrets{'consumer_key'}=$APIKey;
96 | if (!empty($sharedSecret))
97 | $this->_secrets{'shared_secret'}=$sharedSecret;
98 | $this->_default_signature_method="HMAC-SHA1";
99 | $this->_action="GET";
100 | $this->_nonce_chars="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
101 | return $this;
102 | }
103 |
104 | /** reset the parameters and url
105 | *
106 | */
107 | function reset() {
108 | $this->_parameters=null;
109 | $this->path=null;
110 | $this->sbs=null;
111 | return $this;
112 | }
113 |
114 | /** set the parameters either from a hash or a string
115 | *
116 | * @param {string,object} List of parameters for the call, this can either be a URI string (e.g. "foo=bar&gorp=banana" or an object/hash)
117 | */
118 | function setParameters ($parameters=Array()) {
119 |
120 | if (is_string($parameters))
121 | $parameters = $this->_parseParameterString($parameters);
122 | if (empty($this->_parameters))
123 | $this->_parameters = $parameters;
124 | elseif (!empty($parameters))
125 | $this->_parameters = array_merge($this->_parameters,$parameters);
126 | if (empty($this->_parameters['oauth_nonce']))
127 | $this->_getNonce();
128 | if (empty($this->_parameters['oauth_timestamp']))
129 | $this->_getTimeStamp();
130 | if (empty($this->_parameters['oauth_consumer_key']))
131 | $this->_getApiKey();
132 | if (empty($this->_parameters['oauth_token']))
133 | $this->_getAccessToken();
134 | if (empty($this->_parameters['oauth_signature_method']))
135 | $this->setSignatureMethod();
136 | if (empty($this->_parameters['oauth_version']))
137 | $this->_parameters['oauth_version']="1.0";
138 | return $this;
139 | }
140 |
141 | // convienence method for setParameters
142 | function setQueryString ($parameters) {
143 | return $this->setParameters($parameters);
144 | }
145 |
146 | /** Set the target URL (does not include the parameters)
147 | *
148 | * @param path {string} the fully qualified URI (excluding query arguments) (e.g "http://example.org/foo")
149 | */
150 | function setURL ($path) {
151 | if (empty($path))
152 | throw new OAuthSimpleException('No path specified for OAuthSimple.setURL');
153 | $this->_path=$path;
154 | return $this;
155 | }
156 |
157 | /** convienence method for setURL
158 | *
159 | * @param path {string} see .setURL
160 | */
161 | function setPath ($path) {
162 | return $this->_path=$path;
163 | }
164 |
165 | /** set the "action" for the url, (e.g. GET,POST, DELETE, etc.)
166 | *
167 | * @param action {string} HTTP Action word.
168 | */
169 | function setAction ($action) {
170 | if (empty($action))
171 | $action = 'GET';
172 | $action = strtoupper($action);
173 | if (preg_match('/[^A-Z]/',$action))
174 | throw new OAuthSimpleException('Invalid action specified for OAuthSimple.setAction');
175 | $this->_action = $action;
176 | return $this;
177 | }
178 |
179 | /** set the signatures (as well as validate the ones you have)
180 | *
181 | * @param signatures {object} object/hash of the token/signature pairs {api_key:, shared_secret:, oauth_token: oauth_secret:}
182 | */
183 | function signatures ($signatures) {
184 | if (!empty($signatures) && !is_array($signatures))
185 | throw new OAuthSimpleException('Must pass dictionary array to OAuthSimple.signatures');
186 | if (!empty($signatures)){
187 | if (empty($this->_secrets)) {
188 | $this->_secrets=Array();
189 | }
190 | $this->_secrets=array_merge($this->_secrets,$signatures);
191 | }
192 | // Aliases
193 | if (isset($this->_secrets['api_key']))
194 | $this->_secrets['consumer_key'] = $this->_secrets['api_key'];
195 | if (isset($this->_secrets['access_token']))
196 | $this->_secrets['oauth_token'] = $this->_secrets['access_token'];
197 | if (isset($this->_secrets['access_secret']))
198 | $this->_secrets['oauth_secret'] = $this->_secrets['access_secret'];
199 | if (isset($this->_secrets['access_token_secret']))
200 | $this->_secrets['oauth_secret'] = $this->_secrets['access_token_secret'];
201 | if (isset($this->_secrets['rsa_private_key']))
202 | $this->_secrets['private_key'] = $this->_secrets['rsa_private_key'];
203 | if (isset($this->_secrets['rsa_public_key']))
204 | $this->_secrets['public_key'] = $this->_secrets['rsa_public_key'];
205 | // Gauntlet
206 | if (empty($this->_secrets['consumer_key']))
207 | throw new OAuthSimpleException('Missing required consumer_key in OAuthSimple.signatures');
208 | if (empty($this->_secrets['shared_secret']))
209 | throw new OAuthSimpleException('Missing requires shared_secret in OAuthSimple.signatures');
210 | if (!empty($this->_secrets['oauth_token']) && empty($this->_secrets['oauth_secret']))
211 | throw new OAuthSimpleException('Missing oauth_secret for supplied oauth_token in OAuthSimple.signatures');
212 | return $this;
213 | }
214 |
215 | function setTokensAndSecrets($signatures) {
216 | return $this->signatures($signatures);
217 | }
218 |
219 | /** set the signature method (currently only Plaintext or SHA-MAC1)
220 | *
221 | * @param method {string} Method of signing the transaction (only PLAINTEXT and SHA-MAC1 allowed for now)
222 | */
223 | function setSignatureMethod ($method="") {
224 | if (empty($method))
225 | $method = $this->_default_signature_method;
226 | $method = strtoupper($method);
227 | switch($method)
228 | {
229 | case 'RSA-SHA1':
230 | $this->_parameters['oauth_signature_method']=$method;
231 | break;
232 | case 'PLAINTEXT':
233 | case 'HMAC-SHA1':
234 | $this->_parameters['oauth_signature_method']=$method;
235 | break;
236 | default:
237 | throw new OAuthSimpleException ("Unknown signing method $method specified for OAuthSimple.setSignatureMethod");
238 | }
239 | return $this;
240 | }
241 |
242 | /** sign the request
243 | *
244 | * note: all arguments are optional, provided you've set them using the
245 | * other helper functions.
246 | *
247 | * @param args {object} hash of arguments for the call
248 | * {action, path, parameters (array), method, signatures (array)}
249 | * all arguments are optional.
250 | */
251 | function sign($args=array()) {
252 | if (!empty($args['action']))
253 | $this->setAction($args['action']);
254 | if (!empty($args['path']))
255 | $this->setPath($args['path']);
256 | if (!empty($args['method']))
257 | $this->setSignatureMethod($args['method']);
258 | if (!empty($args['signatures']))
259 | $this->signatures($args['signatures']);
260 | if (empty($args['parameters']))
261 | $args['parameters']=array(); // squelch the warning.
262 | $this->setParameters($args['parameters']);
263 | $normParams = $this->_normalizedParameters();
264 | $this->_parameters['oauth_signature'] = $this->_generateSignature($normParams);
265 | return Array(
266 | 'parameters' => $this->_parameters,
267 | 'signature' => $this->_oauthEscape($this->_parameters['oauth_signature']),
268 | 'signed_url' => $this->_path . '?' . $this->_normalizedParameters('true'),
269 | 'header' => $this->getHeaderString(),
270 | 'sbs'=> $this->sbs
271 | );
272 | }
273 |
274 | /** Return a formatted "header" string
275 | *
276 | * NOTE: This doesn't set the "Authorization: " prefix, which is required.
277 | * I don't set it because various set header functions prefer different
278 | * ways to do that.
279 | *
280 | * @param args {object} see .sign
281 | */
282 | function getHeaderString ($args=array()) {
283 | if (empty($this->_parameters['oauth_signature']))
284 | $this->sign($args);
285 |
286 | $result = 'OAuth ';
287 |
288 | foreach ($this->_parameters as $pName=>$pValue)
289 | {
290 | if (strpos($pName,'oauth_') !== 0)
291 | continue;
292 | if (is_array($pValue))
293 | {
294 | foreach ($pValue as $val)
295 | {
296 | $result .= $pName .'="' . $this->_oauthEscape($val) . '", ';
297 | }
298 | }
299 | else
300 | {
301 | $result .= $pName . '="' . $this->_oauthEscape($pValue) . '", ';
302 | }
303 | }
304 | return preg_replace('/, $/','',$result);
305 | }
306 |
307 | // Start private methods. Here be Dragons.
308 | // No promises are kept that any of these functions will continue to exist
309 | // in future versions.
310 | function _parseParameterString ($paramString) {
311 | $elements = explode('&',$paramString);
312 | $result = array();
313 | foreach ($elements as $element)
314 | {
315 | list ($key,$token) = explode('=',$element);
316 | if ($token)
317 | $token = urldecode($token);
318 | if (!empty($result[$key]))
319 | {
320 | if (!is_array($result[$key]))
321 | $result[$key] = array($result[$key],$token);
322 | else
323 | array_push($result[$key],$token);
324 | }
325 | else
326 | $result[$key]=$token;
327 | }
328 | //error_log('Parse parameters : '.print_r($result,1));
329 | return $result;
330 | }
331 |
332 | function _oauthEscape($string) {
333 | if ($string === 0)
334 | return 0;
335 | if (empty($string))
336 | return '';
337 | if (is_array($string))
338 | throw new OAuthSimpleException('Array passed to _oauthEscape');
339 | $string = rawurlencode($string);
340 | $string = str_replace('+','%20',$string);
341 | $string = str_replace('!','%21',$string);
342 | $string = str_replace('*','%2A',$string);
343 | $string = str_replace('\'','%27',$string);
344 | $string = str_replace('(','%28',$string);
345 | $string = str_replace(')','%29',$string);
346 | return $string;
347 | }
348 |
349 | function _getNonce($length=5) {
350 | $result = '';
351 | $cLength = strlen($this->_nonce_chars);
352 | for ($i=0; $i < $length; $i++)
353 | {
354 | $rnum = rand(0,$cLength);
355 | $result .= substr($this->_nonce_chars,$rnum,1);
356 | }
357 | $this->_parameters['oauth_nonce'] = $result;
358 | return $result;
359 | }
360 |
361 | function _getApiKey() {
362 | if (empty($this->_secrets['consumer_key']))
363 | {
364 | throw new OAuthSimpleException('No consumer_key set for OAuthSimple');
365 | }
366 | $this->_parameters['oauth_consumer_key']=$this->_secrets['consumer_key'];
367 | return $this->_parameters['oauth_consumer_key'];
368 | }
369 |
370 | function _getAccessToken() {
371 | if (!isset($this->_secrets['oauth_secret']))
372 | return '';
373 | if (!isset($this->_secrets['oauth_token']))
374 | throw new OAuthSimpleException('No access token (oauth_token) set for OAuthSimple.');
375 | $this->_parameters['oauth_token'] = $this->_secrets['oauth_token'];
376 | return $this->_parameters['oauth_token'];
377 | }
378 |
379 | function _getTimeStamp() {
380 | return $this->_parameters['oauth_timestamp'] = time();
381 | }
382 |
383 | function _normalizedParameters($filter='false') {
384 | $elements = array();
385 | $ra = 0;
386 | ksort($this->_parameters);
387 | foreach ( $this->_parameters as $paramName=>$paramValue) {
388 | if($paramName=='xml'){
389 | if($filter=="true")
390 | continue;
391 | }
392 | if (preg_match('/\w+_secret/',$paramName))
393 | continue;
394 | if (is_array($paramValue))
395 | {
396 | sort($paramValue);
397 | foreach($paramValue as $element)
398 | array_push($elements,$this->_oauthEscape($paramName).'='.$this->_oauthEscape($element));
399 | continue;
400 | }
401 | array_push($elements,$this->_oauthEscape($paramName).'='.$this->_oauthEscape($paramValue));
402 |
403 | }
404 | return join('&',$elements);
405 | }
406 |
407 | function _readFile($filePath) {
408 |
409 | $fp = fopen($filePath,"r");
410 |
411 | $file_contents = fread($fp,8192);
412 |
413 | fclose($fp);
414 |
415 | return $file_contents;
416 | }
417 |
418 | function _generateSignature () {
419 | $secretKey = '';
420 | if(isset($this->_secrets['shared_secret']))
421 | $secretKey = $this->_oauthEscape($this->_secrets['shared_secret']);
422 | $secretKey .= '&';
423 | if(isset($this->_secrets['oauth_secret']))
424 | $secretKey .= $this->_oauthEscape($this->_secrets['oauth_secret']);
425 | switch($this->_parameters['oauth_signature_method'])
426 | {
427 | case 'RSA-SHA1':
428 |
429 | $publickey = "";
430 | // Fetch the public key
431 | if($publickey = openssl_get_publickey($this->_readFile($this->_secrets['public_key']))){
432 |
433 | }else{
434 | throw new OAuthSimpleException('Cannot access public key for signing');
435 | }
436 |
437 | $privatekeyid = "";
438 | // Fetch the private key
439 | if($privatekeyid = openssl_pkey_get_private($this->_readFile($this->_secrets['private_key'])))
440 | {
441 | // Sign using the key
442 | $this->sbs = $this->_oauthEscape($this->_action).'&'.$this->_oauthEscape($this->_path).'&'.$this->_oauthEscape($this->_normalizedParameters());
443 |
444 | $ok = openssl_sign($this->sbs, $signature, $privatekeyid);
445 |
446 | // Release the key resource
447 | openssl_free_key($privatekeyid);
448 |
449 | return base64_encode($signature);
450 |
451 | }else{
452 | throw new OAuthSimpleException('Cannot access private key for signing');
453 | }
454 |
455 |
456 | case 'PLAINTEXT':
457 | return urlencode($secretKey);
458 |
459 | case 'HMAC-SHA1':
460 | $this->sbs = $this->_oauthEscape($this->_action).'&'.$this->_oauthEscape($this->_path).'&'.$this->_oauthEscape($this->_normalizedParameters());
461 | //error_log('SBS: '.$sigString);
462 | return base64_encode(hash_hmac('sha1',$this->sbs,$secretKey,true));
463 |
464 | default:
465 | throw new OAuthSimpleException('Unknown signature method for OAuthSimple');
466 | }
467 | }
468 | }
469 | ?>
470 |
--------------------------------------------------------------------------------
/lib/XeroOAuth.php:
--------------------------------------------------------------------------------
1 | params = array ();
27 | $this->headers = array ();
28 | $this->auto_fixed_time = false;
29 | $this->buffer = null;
30 | $this->request_params = array();
31 |
32 | if (! empty ( $config ['application_type'] )) {
33 | switch ($config ['application_type']) {
34 | case "Public" :
35 | $this->_xero_defaults = array (
36 | 'xero_url' => 'https://api.xero.com/',
37 | 'site' => 'https://api.xero.com',
38 | 'authorize_url' => 'https://api.xero.com/oauth/Authorize',
39 | 'signature_method' => 'HMAC-SHA1'
40 | );
41 | break;
42 | case "Private" :
43 | $this->_xero_defaults = array (
44 | 'xero_url' => 'https://api.xero.com/',
45 | 'site' => 'https://api.xero.com',
46 | 'authorize_url' => 'https://api.xero.com/oauth/Authorize',
47 | 'signature_method' => 'RSA-SHA1'
48 | );
49 | break;
50 | case "Partner" :
51 | $this->_xero_defaults = array (
52 | 'xero_url' => 'https://api.xero.com/',
53 | 'site' => 'https://api.xero.com',
54 | 'authorize_url' => 'https://api.xero.com/oauth/Authorize',
55 | 'signature_method' => 'RSA-SHA1'
56 | );
57 | break;
58 | }
59 | }
60 |
61 | $this->_xero_consumer_options = array (
62 | 'request_token_path' => 'oauth/RequestToken',
63 | 'access_token_path' => 'oauth/AccessToken',
64 | 'authorize_path' => 'oauth/Authorize'
65 | );
66 |
67 | // Remove forced dependency on BASE_PATH constant.
68 | // Note that __DIR__ is PHP 5.3 and above only.
69 | $base_path = defined ( 'BASE_PATH' ) ? BASE_PATH : dirname ( __DIR__ );
70 |
71 | $this->_xero_curl_options = array ( // you probably don't want to change any of these curl values
72 | 'curl_connecttimeout' => 30,
73 | 'curl_timeout' => 20,
74 | // for security you may want to set this to TRUE. If you do you need
75 | // to install the servers certificate in your local certificate store.
76 | 'curl_ssl_verifypeer' => 2,
77 | // include ca-bundle.crt from http://curl.haxx.se/ca/cacert.pem
78 | 'curl_cainfo' => $base_path . '/certs/ca-bundle.crt',
79 | 'curl_followlocation' => false, // whether to follow redirects or not
80 | // TRUE/1 is not a valid ssl verifyhost value with curl >= 7.28.1 and 2 is more secure as well.
81 | // More details here: http://php.net/manual/en/function.curl-setopt.php
82 | 'curl_ssl_verifyhost' => 2,
83 | // support for proxy servers
84 | 'curl_proxy' => false, // really you don't want to use this if you are using streaming
85 | 'curl_proxyuserpwd' => false, // format username:password for proxy, if required
86 | 'curl_encoding' => '', // leave blank for all supported formats, else use gzip, deflate, identity
87 | 'curl_verbose' => true
88 | );
89 |
90 | $this->config = array_merge ( $this->_xero_defaults, $this->_xero_consumer_options, $this->_xero_curl_options, $config );
91 | }
92 |
93 | /**
94 | * Utility function to parse the returned curl headers and store them in the
95 | * class array variable.
96 | *
97 | * @param object $ch
98 | * curl handle
99 | * @param string $header
100 | * the response headers
101 | * @return the string length of the header
102 | */
103 | private function curlHeader($ch, $header) {
104 | $i = strpos ( $header, ':' );
105 | if (! empty ( $i )) {
106 | $key = str_replace ( '-', '_', strtolower ( substr ( $header, 0, $i ) ) );
107 | $value = trim ( substr ( $header, $i + 2 ) );
108 | $this->response ['headers'] [$key] = $value;
109 | }
110 | return strlen ( $header );
111 | }
112 |
113 | /**
114 | * Utility function to parse the returned curl buffer and store them until
115 | * an EOL is found.
116 | * The buffer for curl is an undefined size so we need
117 | * to collect the content until an EOL is found.
118 | *
119 | * This function calls the previously defined streaming callback method.
120 | *
121 | * @param object $ch
122 | * curl handle
123 | * @param string $data
124 | * the current curl buffer
125 | */
126 | private function curlWrite($ch, $data) {
127 | $l = strlen ( $data );
128 | if (strpos ( $data, $this->config ['streaming_eol'] ) === false) {
129 | $this->buffer .= $data;
130 | return $l;
131 | }
132 |
133 | $buffered = explode ( $this->config ['streaming_eol'], $data );
134 | $content = $this->buffer . $buffered [0];
135 |
136 | $this->metrics ['tweets'] ++;
137 | $this->metrics ['bytes'] += strlen ( $content );
138 |
139 | if (! function_exists ( $this->config ['streaming_callback'] ))
140 | return 0;
141 |
142 | $metrics = $this->update_metrics ();
143 | $stop = call_user_func ( $this->config ['streaming_callback'], $content, strlen ( $content ), $metrics );
144 | $this->buffer = $buffered [1];
145 | if ($stop)
146 | return 0;
147 |
148 | return $l;
149 | }
150 |
151 | /**
152 | * Extracts and decodes OAuth parameters from the passed string
153 | *
154 | * @param string $body
155 | * the response body from an OAuth flow method
156 | * @return array the response body safely decoded to an array of key => values
157 | */
158 | function extract_params($body) {
159 | $kvs = explode ( '&', $body );
160 | $decoded = array ();
161 | foreach ( $kvs as $kv ) {
162 | $kv = explode ( '=', $kv, 2 );
163 | $kv [0] = $this->safe_decode ( $kv [0] );
164 | $kv [1] = $this->safe_decode ( $kv [1] );
165 | $decoded [$kv [0]] = $kv [1];
166 | }
167 | return $decoded;
168 | }
169 |
170 | /**
171 | * Encodes the string or array passed in a way compatible with OAuth.
172 | * If an array is passed each array value will will be encoded.
173 | *
174 | * @param mixed $data
175 | * the scalar or array to encode
176 | * @return $data encoded in a way compatible with OAuth
177 | */
178 | private function safe_encode($data) {
179 | if (is_array ( $data )) {
180 | return array_map ( array (
181 | $this,
182 | 'safe_encode'
183 | ), $data );
184 | } else if (is_scalar ( $data )) {
185 | return str_ireplace ( array (
186 | '+',
187 | '%7E'
188 | ), array (
189 | ' ',
190 | '~'
191 | ), rawurlencode ( $data ) );
192 | } else {
193 | return '';
194 | }
195 | }
196 |
197 | /**
198 | * Decodes the string or array from it's URL encoded form
199 | * If an array is passed each array value will will be decoded.
200 | *
201 | * @param mixed $data
202 | * the scalar or array to decode
203 | * @return $data decoded from the URL encoded form
204 | */
205 | private function safe_decode($data) {
206 | if (is_array ( $data )) {
207 | return array_map ( array (
208 | $this,
209 | 'safe_decode'
210 | ), $data );
211 | } else if (is_scalar ( $data )) {
212 | return rawurldecode ( $data );
213 | } else {
214 | return '';
215 | }
216 | }
217 |
218 | /**
219 | * Prepares the HTTP method for use in the base string by converting it to
220 | * uppercase.
221 | *
222 | * @param string $method
223 | * an HTTP method such as GET or POST
224 | * @return void value is stored to a class variable
225 | * @author themattharris
226 | */
227 | private function prepare_method($method) {
228 | $this->method = strtoupper ( $method );
229 | }
230 |
231 | /**
232 | * Makes a curl request.
233 | * Takes no parameters as all should have been prepared
234 | * by the request method
235 | *
236 | * @return void response data is stored in the class variable 'response'
237 | */
238 | private function curlit() {
239 | $this->request_params = array();
240 |
241 |
242 | // configure curl
243 | $c = curl_init ();
244 | $useragent = (isset ( $this->config ['user_agent'] )) ? (empty ( $this->config ['user_agent'] ) ? 'XeroOAuth-PHP' : $this->config ['user_agent']) : 'XeroOAuth-PHP';
245 | curl_setopt_array ( $c, array (
246 | CURLOPT_USERAGENT => $useragent,
247 | CURLOPT_CONNECTTIMEOUT => $this->config ['curl_connecttimeout'],
248 | CURLOPT_TIMEOUT => $this->config ['curl_timeout'],
249 | CURLOPT_RETURNTRANSFER => TRUE,
250 | CURLOPT_SSL_VERIFYPEER => $this->config ['curl_ssl_verifypeer'],
251 | CURLOPT_CAINFO => $this->config ['curl_cainfo'],
252 | CURLOPT_SSL_VERIFYHOST => $this->config ['curl_ssl_verifyhost'],
253 | CURLOPT_FOLLOWLOCATION => $this->config ['curl_followlocation'],
254 | CURLOPT_PROXY => $this->config ['curl_proxy'],
255 | CURLOPT_ENCODING => $this->config ['curl_encoding'],
256 | CURLOPT_URL => $this->sign ['signed_url'],
257 | CURLOPT_VERBOSE => $this->config ['curl_verbose'],
258 | // process the headers
259 | CURLOPT_HEADERFUNCTION => array (
260 | $this,
261 | 'curlHeader'
262 | ),
263 | CURLOPT_HEADER => FALSE,
264 | CURLINFO_HEADER_OUT => TRUE
265 | ) );
266 |
267 | /* ENTRUST CERTIFICATE DEPRECATED
268 | if ($this->config ['application_type'] == "Partner") {
269 | curl_setopt_array ( $c, array (
270 | // ssl client cert options for partner apps
271 | CURLOPT_SSLCERT => $this->config ['curl_ssl_cert'],
272 | CURLOPT_SSLKEYPASSWD => $this->config ['curl_ssl_password'],
273 | CURLOPT_SSLKEY => $this->config ['curl_ssl_key']
274 | ) );
275 | }
276 | */
277 |
278 | if ($this->config ['curl_proxyuserpwd'] !== false)
279 | curl_setopt ( $c, CURLOPT_PROXYUSERPWD, $this->config ['curl_proxyuserpwd'] );
280 |
281 | if (isset ( $this->config ['is_streaming'] )) {
282 | // process the body
283 | $this->response ['content-length'] = 0;
284 | curl_setopt ( $c, CURLOPT_TIMEOUT, 0 );
285 | curl_setopt ( $c, CURLOPT_WRITEFUNCTION, array (
286 | $this,
287 | 'curlWrite'
288 | ) );
289 | }
290 |
291 | switch ($this->method) {
292 | case 'GET' :
293 | $contentLength = 0;
294 | break;
295 | case 'POST' :
296 | curl_setopt ( $c, CURLOPT_POST, TRUE );
297 | $post_body = $this->safe_encode ( $this->xml );
298 | curl_setopt ( $c, CURLOPT_POSTFIELDS, $post_body );
299 | $this->request_params ['xml'] = $post_body;
300 | $contentLength = strlen ( $post_body );
301 | $this->headers ['Content-Type'] = 'application/x-www-form-urlencoded';
302 |
303 | break;
304 | case 'PUT' :
305 | $fh = tmpfile();
306 | if ($this->format == "file") {
307 | $put_body = $this->xml;
308 | } else {
309 | $put_body = $this->safe_encode ( $this->xml );
310 | $this->headers ['Content-Type'] = 'application/x-www-form-urlencoded';
311 | }
312 | fwrite ( $fh, $put_body );
313 | rewind ( $fh );
314 | curl_setopt ( $c, CURLOPT_PUT, true );
315 | curl_setopt ( $c, CURLOPT_INFILE, $fh );
316 | curl_setopt ( $c, CURLOPT_INFILESIZE, strlen ( $put_body ) );
317 | $contentLength = strlen ( $put_body );
318 |
319 | break;
320 | default :
321 | curl_setopt ( $c, CURLOPT_CUSTOMREQUEST, $this->method );
322 | }
323 |
324 | if (! empty ( $this->request_params )) {
325 | // if not doing multipart we need to implode the parameters
326 | if (! $this->config ['multipart']) {
327 | foreach ( $this->request_params as $k => $v ) {
328 | $ps [] = "{$k}={$v}";
329 | }
330 | $this->request_payload = implode ( '&', $ps );
331 | }
332 | curl_setopt ( $c, CURLOPT_POSTFIELDS, $this->request_payload);
333 | } else {
334 | // CURL will set length to -1 when there is no data
335 | $this->headers ['Content-Length'] = $contentLength;
336 | }
337 |
338 | $this->headers ['Expect'] = '';
339 |
340 | if (! empty ( $this->headers )) {
341 | foreach ( $this->headers as $k => $v ) {
342 | $headers [] = trim ( $k . ': ' . $v );
343 | }
344 | curl_setopt ( $c, CURLOPT_HTTPHEADER, $headers );
345 | }
346 |
347 | if (isset ( $this->config ['prevent_request'] ) && false == $this->config ['prevent_request'])
348 | return;
349 |
350 | // do it!
351 | $response = curl_exec ( $c );
352 | if ($response === false) {
353 | $response = 'Curl error: ' . curl_error ( $c );
354 | $code = 1;
355 | } else {
356 | $code = curl_getinfo ( $c, CURLINFO_HTTP_CODE );
357 | }
358 |
359 | $info = curl_getinfo ( $c );
360 |
361 | curl_close ( $c );
362 | if (isset ( $fh )) {
363 | fclose( $fh );
364 | }
365 |
366 | // store the response
367 | $this->response ['code'] = $code;
368 | $this->response ['response'] = $response;
369 | $this->response ['info'] = $info;
370 | $this->response ['format'] = $this->format;
371 | return $code;
372 | }
373 |
374 | /**
375 | * Make an HTTP request using this library.
376 | * This method doesn't return anything.
377 | * Instead the response should be inspected directly.
378 | *
379 | * @param string $method
380 | * the HTTP method being used. e.g. POST, GET, HEAD etc
381 | * @param string $url
382 | * the request URL without query string parameters
383 | * @param array $params
384 | * the request parameters as an array of key=value pairs
385 | * @param string $format
386 | * the format of the response. Default json. Set to an empty string to exclude the format
387 | *
388 | */
389 | function request($method, $url, $params = array(), $xml = "", $format = 'xml') {
390 | // removed these as function parameters for now
391 | $useauth = true;
392 | $multipart = false;
393 | $this->headers = array ();
394 |
395 | if (isset ( $format )) {
396 | switch ($format) {
397 | case "pdf" :
398 | $this->headers ['Accept'] = 'application/pdf';
399 | break;
400 | case "json" :
401 | $this->headers ['Accept'] = 'application/json';
402 | break;
403 | case "xml" :
404 | default :
405 | $this->headers ['Accept'] = 'application/xml';
406 | break;
407 | }
408 | }
409 |
410 | if (isset ( $params ['If-Modified-Since'] )) {
411 | $modDate = "If-Modified-Since: " . $params ['If-Modified-Since'];
412 | $this->headers ['If-Modified-Since'] = $params ['If-Modified-Since'];
413 | }
414 |
415 | if ($xml !== "") {
416 | $xml = trim($xml);
417 | $this->xml = $xml;
418 | }
419 |
420 | if ($method == "POST")
421 | $params ['xml'] = $xml;
422 |
423 | $this->prepare_method ( $method );
424 | $this->config ['multipart'] = $multipart;
425 | $this->url = $url;
426 | $oauthObject = new OAuthSimple ();
427 | try {
428 | $this->sign = $oauthObject->sign ( array (
429 | 'path' => $url,
430 | 'action' => $method,
431 | 'parameters' => array_merge ( $params, array (
432 | 'oauth_signature_method' => $this->config ['signature_method']
433 | ) ),
434 | 'signatures' => $this->config
435 | ) );
436 | }
437 |
438 | catch ( Exception $e ) {
439 | $errorMessage = 'XeroOAuth::request() ' . $e->getMessage ();
440 | $this->response['response'] = $errorMessage;
441 | $this->response['helper'] = $url;
442 | return $this->response;
443 | }
444 | $this->format = $format;
445 |
446 | $curlRequest = $this->curlit ();
447 |
448 | if ($this->response ['code'] == 401 && isset ( $this->config ['session_handle'] )) {
449 | if ((strpos ( $this->response ['response'], "oauth_problem=token_expired" ) !== false)) {
450 | $this->response ['helper'] = "TokenExpired";
451 | } else {
452 | $this->response ['helper'] = "TokenFatal";
453 | }
454 | }
455 | if ($this->response ['code'] == 403) {
456 | $errorMessage = "It looks like your Xero Entrust cert issued by Xero is either invalid or has expired. See http://developer.xero.com/api-overview/http-response-codes/#403 for more";
457 | // default IIS page isn't informative, a little swap
458 | $this->response ['response'] = $errorMessage;
459 | $this->response ['helper'] = "SetupIssue";
460 | }
461 | if ($this->response ['code'] == 0) {
462 | $errorMessage = "It looks like your Xero Entrust cert issued by Xero is either invalid or has expired. See http://developer.xero.com/api-overview/http-response-codes/#403 for more";
463 | $this->response ['response'] = $errorMessage;
464 | $this->response ['helper'] = "SetupIssue";
465 | }
466 |
467 | return $this->response;
468 | }
469 |
470 | /**
471 | * Convert the response into usable data
472 | *
473 | * @param string $response
474 | * the raw response from the API
475 | * @param string $format
476 | * the format of the response
477 | * @return string the concatenation of the host, API version and API method
478 | */
479 | function parseResponse($response, $format) {
480 | if (isset ( $format )) {
481 | switch ($format) {
482 | case "pdf" :
483 | $theResponse = $response;
484 | break;
485 | case "json" :
486 | $theResponse = json_decode ( $response );
487 | break;
488 | default :
489 | $theResponse = simplexml_load_string ( $response );
490 | break;
491 | }
492 | }
493 | return $theResponse;
494 | }
495 |
496 | /**
497 | * Utility function to create the request URL in the requested format
498 | *
499 | * @param string $request
500 | * the API method without extension
501 | * @return string the concatenation of the host, API version and API method
502 | */
503 | function url($request, $api = "core") {
504 | if ($request == "RequestToken") {
505 | $this->config ['host'] = $this->config ['site'] . '/oauth/';
506 | } elseif ($request == "Authorize") {
507 | $this->config ['host'] = $this->config ['authorize_url'];
508 | $request = "";
509 | } elseif ($request == "AccessToken") {
510 | $this->config ['host'] = $this->config ['site'] . '/oauth/';
511 | } else {
512 | if (isset ( $api )) {
513 | if ($api == "core") {
514 | $api_stem = "api.xro";
515 | $api_version = $this->config ['core_version'];
516 | }
517 | if ($api == "payroll") {
518 | $api_stem = "payroll.xro";
519 | $api_version = $this->config ['payroll_version'];
520 | }
521 | if ($api == "file") {
522 | $api_stem = "files.xro";
523 | $api_version = $this->config ['file_version'];
524 | }
525 | }
526 | $this->config ['host'] = $this->config ['xero_url'] . $api_stem . '/' . $api_version . '/';
527 | }
528 |
529 | return implode ( array (
530 | $this->config ['host'],
531 | $request
532 | ) );
533 | }
534 |
535 | /**
536 | * Refreshes the access token for partner API type applications
537 | *
538 | * @param string $accessToken
539 | * the current access token for the session
540 | * @param string $sessionHandle
541 | * the current session handle for the session
542 | * @return array response array from request
543 | */
544 | function refreshToken($accessToken, $sessionHandle) {
545 | $code = $this->request ( 'GET', $this->url ( 'AccessToken', '' ), array (
546 | 'oauth_token' => $accessToken,
547 | 'oauth_session_handle' => $sessionHandle
548 | ) );
549 | if ($this->response ['code'] == 200) {
550 |
551 | $response = $this->extract_params ( $this->response ['response'] );
552 |
553 | return $response;
554 | } else {
555 | $this->response ['helper'] = "TokenFatal";
556 | return $this->response;
557 | }
558 | }
559 |
560 | /**
561 | * Returns the current URL.
562 | * This is instead of PHP_SELF which is unsafe
563 | *
564 | * @param bool $dropqs
565 | * whether to drop the querystring or not. Default true
566 | * @return string the current URL
567 | */
568 | public static function php_self($dropqs = true)
569 | {
570 | $protocol = 'http';
571 | if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on')
572 | {
573 | $protocol = 'https';
574 | }
575 | elseif (isset($_SERVER['SERVER_PORT']) && ($_SERVER['SERVER_PORT'] == '443'))
576 | {
577 | $protocol = 'https';
578 | }
579 |
580 | $url = sprintf('%s://%s%s', $protocol, $_SERVER['SERVER_NAME'], $_SERVER['REQUEST_URI']);
581 | $parts = parse_url($url);
582 | $port = $_SERVER['SERVER_PORT'];
583 | $scheme = $parts['scheme'];
584 | $host = $parts['host'];
585 | $path = @$parts['path'];
586 | $qs = @$parts['query'];
587 | $port or $port = ($scheme == 'https') ? '443' : '80';
588 | if (($scheme == 'https' && $port != '443') || ($scheme == 'http' && $port != '80'))
589 | {
590 | $host = "$host:$port";
591 | }
592 |
593 | $url = "$scheme://$host$path";
594 | if (!$dropqs) return "{$url}?{$qs}";
595 | else return $url;
596 | }
597 |
598 | /*
599 | * Run some basic checks on our config options etc to make sure all is ok
600 | */
601 | function diagnostics() {
602 | $testOutput = array ();
603 |
604 | /* ENTRUST CERTIFICATE DEPRECATED
605 | if ($this->config ['application_type'] == 'Partner') {
606 | if (! file_get_contents ( $this->config ['curl_ssl_cert'] )) {
607 | $testOutput ['ssl_cert_error'] = "Can't read the Xero Entrust cert. You need one for partner API applications. http://developer.xero.com/documentation/getting-started/partner-applications/ \n";
608 | } else {
609 | $data = openssl_x509_parse ( file_get_contents ( $this->config ['curl_ssl_cert'] ) );
610 | $validFrom = date ( 'Y-m-d H:i:s', $data ['validFrom_time_t'] );
611 | if (time () < $data ['validFrom_time_t']) {
612 | $testOutput ['ssl_cert_error'] = "Xero Entrust cert not yet valid - cert valid from " . $validFrom . "\n";
613 | }
614 | $validTo = date ( 'Y-m-d H:i:s', $data ['validTo_time_t'] );
615 | if (time () > $data ['validTo_time_t']) {
616 | $testOutput ['ssl_cert_error'] = "Xero Entrust cert expired - cert valid to " . $validFrom . "\n";
617 | }
618 | }
619 | }
620 | */
621 |
622 | if ($this->config ['application_type'] == 'Partner' || $this->config ['application_type'] == 'Private') {
623 |
624 | if (! file_exists ( $this->config ['rsa_public_key'] ))
625 | $testOutput ['rsa_cert_error'] = "Can't read the self-signed SSL cert. Private and Partner API applications require a self-signed X509 cert http://developer.xero.com/documentation/advanced-docs/public-private-keypair/ \n";
626 | if (file_exists ( $this->config ['rsa_public_key'] )) {
627 | $data = openssl_x509_parse ( file_get_contents ( $this->config ['rsa_public_key'] ) );
628 | $validFrom = date ( 'Y-m-d H:i:s', $data ['validFrom_time_t'] );
629 | if (time () < $data ['validFrom_time_t']) {
630 | $testOutput ['ssl_cert_error'] = "Application cert not yet valid - cert valid from " . $validFrom . "\n";
631 | }
632 | $validTo = date ( 'Y-m-d H:i:s', $data ['validTo_time_t'] );
633 | if (time () > $data ['validTo_time_t']) {
634 | $testOutput ['ssl_cert_error'] = "Application cert cert expired - cert valid to " . $validFrom . "\n";
635 | }
636 | }
637 | if (! file_exists ( $this->config ['rsa_private_key'] ))
638 | $testOutput ['rsa_cert_error'] = "Can't read the self-signed cert key. Check your rsa_private_key config variable. Private and Partner API applications require a self-signed X509 cert http://developer.xero.com/documentation/advanced-docs/public-private-keypair/ \n";
639 | if (file_exists ( $this->config ['rsa_private_key'] )) {
640 | $cert_content = file_get_contents ( $this->config ['rsa_public_key'] );
641 | $priv_key_content = file_get_contents ( $this->config ['rsa_private_key'] );
642 | if (! openssl_x509_check_private_key ( $cert_content, $priv_key_content ))
643 | $testOutput ['rsa_cert_error'] = "Application certificate and key do not match \n";
644 | ;
645 | }
646 | }
647 |
648 | return $testOutput;
649 | }
650 | }
651 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Xero Limited
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/partner.php:
--------------------------------------------------------------------------------
1 | '——YOUR-CONSUMER-KEY—',
40 | 'shared_secret' => '——YOUR-CONSUMER-SECRET—',
41 | // API versions
42 | 'core_version' => '2.0',
43 | 'payroll_version' => '1.0',
44 | 'file_version' => '1.0'
45 | );
46 |
47 | if (XRO_APP_TYPE == "Private" || XRO_APP_TYPE == "Partner") {
48 | $signatures ['rsa_private_key'] = BASE_PATH . '/certs/privatekey.pem';
49 | $signatures ['rsa_public_key'] = BASE_PATH . '/certs/publickey.cer';
50 | }
51 |
52 | /* ENTRUST CERTIFICATE DEPRECATED
53 | if (XRO_APP_TYPE == "Partner") {
54 | $signatures ['curl_ssl_cert'] = BASE_PATH . '/certs/entrust-cert.pem';
55 | $signatures ['curl_ssl_password'] = '1234';
56 | $signatures ['curl_ssl_key'] = BASE_PATH . '/certs/entrust-private-nopass.pem';
57 | }
58 | */
59 |
60 | $XeroOAuth = new XeroOAuth ( array_merge ( array (
61 | 'application_type' => XRO_APP_TYPE,
62 | 'oauth_callback' => OAUTH_CALLBACK,
63 | 'user_agent' => $useragent
64 | ), $signatures ) );
65 |
66 | $initialCheck = $XeroOAuth->diagnostics ();
67 | $checkErrors = count ( $initialCheck );
68 | if ($checkErrors > 0) {
69 | // you could handle any config errors here, or keep on truckin if you like to live dangerously
70 | foreach ( $initialCheck as $check ) {
71 | echo 'Error: ' . $check . PHP_EOL;
72 | }
73 | } else {
74 |
75 | $here = XeroOAuth::php_self ();
76 | session_start ();
77 | $oauthSession = retrieveSession ();
78 |
79 | include 'tests/tests.php';
80 |
81 | if (isset ( $_REQUEST ['oauth_verifier'] )) {
82 | $XeroOAuth->config ['access_token'] = $_SESSION ['oauth'] ['oauth_token'];
83 | $XeroOAuth->config ['access_token_secret'] = $_SESSION ['oauth'] ['oauth_token_secret'];
84 |
85 | $code = $XeroOAuth->request ( 'GET', $XeroOAuth->url ( 'AccessToken', '' ), array (
86 | 'oauth_verifier' => $_REQUEST ['oauth_verifier'],
87 | 'oauth_token' => $_REQUEST ['oauth_token']
88 | ) );
89 |
90 | if ($XeroOAuth->response ['code'] == 200) {
91 |
92 | $response = $XeroOAuth->extract_params ( $XeroOAuth->response ['response'] );
93 | $session = persistSession ( $response );
94 |
95 | unset ( $_SESSION ['oauth'] );
96 | header ( "Location: {$here}" );
97 | } else {
98 | outputError ( $XeroOAuth );
99 | }
100 | // start the OAuth dance
101 | } elseif (isset ( $_REQUEST ['authenticate'] ) || isset ( $_REQUEST ['authorize'] )) {
102 | $params = array (
103 | 'oauth_callback' => OAUTH_CALLBACK
104 | );
105 |
106 | $response = $XeroOAuth->request ( 'GET', $XeroOAuth->url ( 'RequestToken', '' ), $params );
107 |
108 | if ($XeroOAuth->response ['code'] == 200) {
109 |
110 | $scope = "";
111 | // $scope = 'payroll.payrollcalendars,payroll.superfunds,payroll.payruns,payroll.payslip,payroll.employees,payroll.TaxDeclaration';
112 | if ($_REQUEST ['authenticate'] > 1)
113 | $scope = 'payroll.employees,payroll.payruns,payroll.timesheets';
114 |
115 | print_r ( $XeroOAuth->extract_params ( $XeroOAuth->response ['response'] ) );
116 | $_SESSION ['oauth'] = $XeroOAuth->extract_params ( $XeroOAuth->response ['response'] );
117 |
118 | $authurl = $XeroOAuth->url ( "Authorize", '' ) . "?oauth_token={$_SESSION['oauth']['oauth_token']}&scope=" . $scope;
119 | echo '
Accounting API
8 |
9 |
37 |
38 |
Payroll API
39 |
40 |
45 |
46 |
File API
47 |
48 |
52 |
53 |
Connection Admin
54 | ';
55 |
56 | if (XRO_APP_TYPE == 'Partner') echo '
'; 122 | if (is_object($obj)) 123 | print_r($obj); 124 | elseif (is_array($obj)) 125 | print_r($obj); 126 | else 127 | echo $obj; 128 | if (!is_cli()) 129 | echo ''; 130 | } 131 | 132 | function is_cli() 133 | { 134 | return (PHP_SAPI == 'cli' && empty($_SERVER['REMOTE_ADDR'])); 135 | } 136 | -------------------------------------------------------------------------------- /tests/tests.php: -------------------------------------------------------------------------------- 1 | refreshToken($oauthSession['oauth_token'], $oauthSession['oauth_session_handle']); 13 | if ($XeroOAuth->response['code'] == 200) { 14 | $session = persistSession($response); 15 | $oauthSession = retrieveSession(); 16 | } else { 17 | outputError($XeroOAuth); 18 | if ($XeroOAuth->response['helper'] == "TokenExpired") $XeroOAuth->refreshToken($oauthSession['oauth_token'], $oauthSession['session_handle']); 19 | } 20 | 21 | } elseif ( isset($oauthSession['oauth_token']) && isset($_REQUEST) ) { 22 | 23 | $XeroOAuth->config['access_token'] = $oauthSession['oauth_token']; 24 | $XeroOAuth->config['access_token_secret'] = $oauthSession['oauth_token_secret']; 25 | $XeroOAuth->config['session_handle'] = $oauthSession['oauth_session_handle']; 26 | 27 | 28 | if (isset($_REQUEST['accounts'])) { 29 | $response = $XeroOAuth->request('GET', $XeroOAuth->url('Accounts', 'core'), array('Where' => $_REQUEST['where'])); 30 | if ($XeroOAuth->response['code'] == 200) { 31 | $accounts = $XeroOAuth->parseResponse($XeroOAuth->response['response'], $XeroOAuth->response['format']); 32 | echo "There are " . count($accounts->Accounts[0]). " accounts in this Xero organisation, the first one is: "; 33 | pr($accounts->Accounts[0]->Account); 34 | } else { 35 | outputError($XeroOAuth); 36 | } 37 | } 38 | 39 | if (isset($_REQUEST['payments'])) { 40 | if (!isset($_REQUEST['method'])) { 41 | $response = $XeroOAuth->request('GET', $XeroOAuth->url('Payments', 'core'), array('Where' => 'Status=="AUTHORISED"')); 42 | if ($XeroOAuth->response['code'] == 200) { 43 | $payments = $XeroOAuth->parseResponse($XeroOAuth->response['response'], $XeroOAuth->response['format']); 44 | echo "There are " . count($payments->Payments[0]). " payments in this Xero organisation, the first one is: "; 45 | pr($payments->Payments[0]->Payment); 46 | } else { 47 | outputError($XeroOAuth); 48 | } 49 | 50 | } elseif (isset($_REQUEST['method']) && $_REQUEST['method'] == "post" && $_REQUEST['payments']== 1 ) { 51 | $response = $XeroOAuth->request('GET', $XeroOAuth->url('Payments', 'core'), array('Where' => 'Status=="AUTHORISED"')); 52 | if ($XeroOAuth->response['code'] == 200) { 53 | $payment = $XeroOAuth->parseResponse($XeroOAuth->response['response'], $XeroOAuth->response['format']); 54 | if(count($payment->Payments[0]) > 0){ 55 | echo "Deleting the first available payment with ID: " . $payment->Payments[0]->Payment->PaymentID . ""; 56 | } 57 | } 58 | $xml = "
090
330 | ITEM-CODE-01
415 |