PHP implementation of Bitstamp API - php

I am trying to implement a PHP API to bitstamp to do a re-occurring transaction by placing the PHP code in the crontab.
I am trying to cause the API to communicate with bitstamp to buy X amount of BTC per execution (and then control the frequency from the crontab), this should be the very definition of the basic implementation.
Here is the joy, I am absolutely not a PHP coder. The guys at BX Media were nice enough to post their PHP frame on github: (https://github.com/willmoss/bitstamp-php-api).
However, the way I understand what they have done is that I must create the "parent" PHP to then include their API code which would also include my credentials
So I put together the most basic PHP code
<?php
require('bitstamp.php');
$KEY = 'XXXXXXXXXXXXXXXX';
$SECRET = 'XXXXXXXXXXXXXXXX';
$CLIENT_ID = 'XXXXX';
$bs = new Bitstamp("KEY","SECRET","CLIENT_ID");
// print_r($bs->ticker());
$bs->buyBTC(0.01); // buy 0.01 bitcoins at ask price
//$bs->bitstamp_query("buy", array('amount'=>'0.05','price'=>'50'));
?>
note: that "bitstamp.php" is in the same directory and is what is presently on Github.
<?php
/**
* #package Bitstamp API
* #author https://bxmediaus.com - BX MEDIA - PHP + Bitcoin. We are ready to work on your next bitcoin project. Only high quality coding. https://bxmediaus.com
* #version 0.1
* #access public
* #license http://www.opensource.org/licenses/LGPL-3.0
*/
class Bitstamp
{
private $key;
private $secret;
private $client_id;
public $redeemd; // Redeemed code information
public $withdrew; // Withdrawal information
public $info; // Result from getInfo()
public $ticker; // Current ticker (getTicker())
public $eurusd; // Current eur/usd
/**
* Bitstamp::__construct()
* Sets required key and secret to allow the script to function
* #param Bitstamp API Key $key
* #param Bitstamp Secret $secret
* #return
*/
public function __construct($key, $secret, $client_id)
{
if (isset($secret) && isset($key) && isset($client_id))
{
$this->key = $key;
$this->secret = $secret;
$this->client_id = $client_id;
} else
die("NO KEY/SECRET/CLIENT ID");
}
/**
* Bitstamp::bitstamp_query()
*
* #param API Path $path
* #param POST Data $req
* #return Array containing data returned from the API path
*/
public function bitstamp_query($path, array $req = array())
{
// API settings
$key = $this->key;
// generate a nonce as microtime, with as-string handling to avoid problems with 32bits systems
$mt = explode(' ', microtime());
$req['nonce'] = $mt[1] . substr($mt[0], 2, 6);
$req['key'] = $key;
$req['signature'] = $this->get_signature($req['nonce']);
// generate the POST data string
$post_data = http_build_query($req, '', '&');
// any extra headers
$headers = array();
// our curl handle (initialize if required)
static $ch = null;
if (is_null($ch))
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT,
'Mozilla/4.0 (compatible; MtGox PHP client; ' . php_uname('s') . '; PHP/' .
phpversion() . ')');
}
curl_setopt($ch, CURLOPT_URL, 'https://www.bitstamp.net/api/' . $path .'/');
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// run the query
$res = curl_exec($ch);
if ($res === false)
throw new \Exception('Could not get reply: ' . curl_error($ch));
$dec = json_decode($res, true);
if (!$dec)
throw new \Exception('Invalid data received, please make sure connection is working and requested API exists');
return $dec;
}
/**
* Bitstamp::ticker()
* Returns current ticker from Bitstamp
* #return $ticker
*/
function ticker() {
$ticker = $this->bitstamp_query('ticker');
$this->ticker = $ticker; // Another variable to contain it.
return $ticker;
}
/**
* Bitstamp::eurusd()
* Returns current EUR/USD rate from Bitstamp
* #return $ticker
*/
function eurusd() {
$eurusd = $this->bitstamp_query('eur_usd');
$this->eurusd = $eurusd; // Another variable to contain it.
return $eurusd;
}
/**
* Bitstamp::buyBTC()
*
* #param float $amount
*/
function buyBTC($amount){
if (!isset($ticker))
$this->ticker();
$ticker = $this->ticker;
return $this->bitstamp_query('buy', array('amount' => $amount, 'price' => $ticker['ask']));
}
/**
* Bitstamp::sellBTC()
*
* #param float $amount
* #param float $price
* #param string $currency
*/
function sellBTC($amount){
if (!isset($ticker))
$this->ticker();
$ticker = $this->ticker;
return $this->bitstamp_query('sell', array('amount' => $amount, 'price' => $ticker['bid']));
}
/**
* Bitstamp::get_signature()
* Compute bitstamp signature
* #param float $nonce
*/
private function get_signature($nonce)
{
$message = $nonce.$this->client_id.$this->key;
return strtoupper(hash_hmac('sha256', $message, $this->secret));
}
}
I am getting a failure on the execution. As the author of the Bitstamp API has it apparently working with his clients, I assume the error is on my "parent" PHP code. (Note: I am using the real key and secret on my local version).
Anyone have any experience with this API or in general or suggestions?

I'm not sure if this is just anonymization or the actual code, so let me know if I have this wrong, but you have this line:
$bs = new Bitstamp("KEY","SECRET","CLIENT_ID");
This passes the actual strings "KEY", "SECRET", and "CLIENT_ID" to the function; what you want to do is pass the variables you defined on the lines above, like this:
$bs = new Bitstamp($KEY,$SECRET,$CLIENT_ID);

As the author of this api (I am CEO of Bx Media):
You need to remake the constructor like this:
$bs = new Bitstamp("put your key here","put your secret here","put your client ID here");
Ie. you actually put your key/secret/ID between the speech marks.
In PHP, when you put a $ sign, it sets something as a variable.
See more information here: http://www.w3schools.com/php/php_variables.asp
If you want to use variables, you can also use IMSoP's solution.
Please note, we also updated the library tonight with some new functions. So please update your repo to the latest commit to get the new code.
Cheers!

Related

Testing DocuSign webhooks with a connect key

I am using DocuSign Connect to retrieve webhooks from DocuSign and digest them within my Larave; application. Here is the basic idea.
<?php
namespace App\Http\Controllers;
use App\Http\Middleware\VerifyDocusignWebhookSignature;
use App\Mail\PaymentRequired;
use App\Models\PaymentAttempt;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
class DocusignWebhookController extends Controller
{
/**
* Create a new controller instance.
* If a DocuSign Connect key is preset, validate the request.
*/
public function __construct()
{
$this->gocardlessTabs = ['GoCardless Agreement Number', 'GoCardless Amount', 'GoCardless Centre'];
$this->assumedCustomer = 2;
if (config('docusign.connect_key')) {
$this->middleware(VerifyDocusignWebhookSignature::class);
}
}
/**
* Handle an incoming DocuSign webhook.
*/
public function handleWebhook(Request $request)
{
$payload = json_decode($request->getContent(), true);
$shouldProcessWebhook = $this->determineIfEnvelopeRelevant($payload);
if ($shouldProcessWebhook) {
switch ($payload['status']) {
case 'sent':
return $this->handleSentEnvelopeStatus($payload);
break;
case 'completed':
return $this->handleCompletedEnvelopeStatus($payload);
break;
case 'voided':
// ...
break;
default:
}
}
}
}
The logic itself works fine but if you look here:
if (config('docusign.connect_key')) {
$this->middleware(VerifyDocusignWebhookSignature::class);
}
If I specify a connect key I run some middleware to verify the webhook came from DocuSign.
The class to verify the signature came from DocuSign and looks like this:
<?php
namespace App\DocuSign;
/**
* This class is used to validate HMAC keys sent from DocuSign webhooks.
* For more information see: https://developers.docusign.com/platform/webhooks/connect/hmac/
*
* Class taken from: https://developers.docusign.com/platform/webhooks/connect/validate/
*
* Sample headers
* [X-Authorization-Digest, HMACSHA256]
* [X-DocuSign-AccountId, caefc2a3-xxxx-xxxx-xxxx-073c9681515f]
* [X-DocuSign-Signature-1, DfV+OtRSnsuy.....NLXUyTfY=]
* [X-DocuSign-Signature-2, CL9zR6MI/yUa.....O09tpBhk=]
*/
class HmacVerifier
{
/**
* Compute a hmac hash from the given payload.
*
* Useful reference: https://www.php.net/manual/en/function.hash-hmac.php
* NOTE: Currently DocuSign only supports SHA256.
*
* #param string $secret
* #param string $payload
*/
public static function computeHash($secret, $payload)
{
$hexHash = hash_hmac('sha256', $payload, utf8_encode($secret));
$base64Hash = base64_encode(hex2bin($hexHash));
return $base64Hash;
}
/**
* Validate that a given hash is valid.
*
* #param string $secret: the secret known only by our application
* #param string $payload: the payload received from the webhook
* #param string $verify: the string we want to verify in the request header
*/
public static function validateHash($secret, $payload, $verify)
{
return hash_equals($verify, self::computeHash($secret, $payload));
}
}
Now, in order to test this locally I've written a test but whenever I run it, the middleware tells me the webhook isn't valid.
Here is my test class
<?php
namespace Tests\Feature\Http\Middleware;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class VerifyDocusignWebhookSignatureTest extends TestCase
{
use RefreshDatabase, WithFaker;
public function setUp(): void
{
parent::setUp();
config(['docusign.connect_key' => 'probably-best-not-put-on-stack-overflow']);
$this->docusignConnectKey = config('docusign.connect_key');
}
/**
* Given a JSON payload, can we parse it and do what we need to do?
*
* #test
*/
public function it_can_retrieve_a_webhook_with_a_connect_key()
{
Mail::fake();
$payload = '{"status":"sent","documentsUri":"/envelopes/2ba67e2f-0db6-46af-865a-e217c9a1c514/documents","recipientsUri":"/envelopes/2ba67e2f-0db6-46af-865a-e217c9a1c514/recipients","attachmentsUri":"/envelopes/2ba67e2f-0db6-46af-865a-e217c9a1c514/attachments","envelopeUri":"/envelopes/2ba67e2f-0db6-46af-865a-e217c9a1c514","emailSubject":"Please DocuSign: newflex doc test.docx","envelopeId":"2ba67e2f-0db6-46af-865a-e217c9a1c514","signingLocation":"online","customFieldsUri":"/envelopes/2ba67e2f-0db6-46af-865a-e217c9a1c514/custom_fields","notificationUri":"/envelopes/2ba67e2f-0db6-46af-865a-e217c9a1c514/notification","enableWetSign":"true","allowMarkup":"false","allowReassign":"true","createdDateTime":"2022-02-14T11:36:01.18Z","lastModifiedDateTime":"2022-02-14T11:37:48.633Z","initialSentDateTime":"2022-02-14T11:37:49.477Z","sentDateTime":"2022-02-14T11:37:49.477Z","statusChangedDateTime":"2022-02-14T11:37:49.477Z","documentsCombinedUri":"/envelopes/2ba67e2f-0db6-46af-865a-e217c9a1c514/documents/combined","certificateUri":"/envelopes/2ba67e2f-0db6-46af-865a-e217c9a1c514/documents/certificate","templatesUri":"/envelopes/2ba67e2f-0db6-46af-865a-e217c9a1c514/templates","expireEnabled":"true","expireDateTime":"2022-06-14T11:37:49.477Z","expireAfter":"120","sender":{"userName":"Newable eSignature","userId":"f947420b-6897-4f29-80b3-4deeaf73a3c5","accountId":"366e9845-963a-41dd-9061-04f61c921f28","email":"e-signature#newable.co.uk"},"recipients":{"signers":[{"tabs":{"textTabs":[{"validationPattern":"","validationMessage":"","shared":"false","requireInitialOnSharedChange":"false","requireAll":"false","value":"","required":"true","locked":"false","concealValueOnDocument":"false","disableAutoSize":"false","maxLength":"4000","tabLabel":"GoCardless Amount","font":"lucidaconsole","fontColor":"black","fontSize":"size9","localePolicy":{},"documentId":"1","recipientId":"56041698","pageNumber":"1","xPosition":"319","yPosition":"84","width":"84","height":"22","tabId":"207f970c-4d3c-4d0c-be6b-1f3aeecf5f95","tabType":"text"},{"validationPattern":"","validationMessage":"","shared":"false","requireInitialOnSharedChange":"false","requireAll":"false","value":"","required":"true","locked":"false","concealValueOnDocument":"false","disableAutoSize":"false","maxLength":"4000","tabLabel":"GoCardless Centre","font":"lucidaconsole","fontColor":"black","fontSize":"size9","localePolicy":{},"documentId":"1","recipientId":"56041698","pageNumber":"1","xPosition":"324","yPosition":"144","width":"84","height":"22","tabId":"f6919e94-d4b7-4ef4-982d-3fc6c16024ab","tabType":"text"},{"validationPattern":"","validationMessage":"","shared":"false","requireInitialOnSharedChange":"false","requireAll":"false","value":"","required":"true","locked":"false","concealValueOnDocument":"false","disableAutoSize":"false","maxLength":"4000","tabLabel":"GoCardless Agreement Number","font":"lucidaconsole","fontColor":"black","fontSize":"size9","localePolicy":{},"documentId":"1","recipientId":"56041698","pageNumber":"1","xPosition":"332","yPosition":"200","width":"84","height":"22","tabId":"9495a53c-1f5e-42a5-beec-9abcf77b4387","tabType":"text"}]},"creationReason":"sender","isBulkRecipient":"false","requireUploadSignature":"false","name":"Jesse","firstName":"","lastName":"","email":"Jesse.Orange#newable.co.uk","recipientId":"56041698","recipientIdGuid":"246ce44f-0c11-4632-ac24-97f31911594e","requireIdLookup":"false","userId":"b23ada8e-577e-4517-b0fa-e6d8fd440f21","routingOrder":"1","note":"","status":"sent","completedCount":"0","deliveryMethod":"email","totalTabCount":"3","recipientType":"signer"},{"tabs":{"signHereTabs":[{"stampType":"signature","name":"SignHere","tabLabel":"Signature 7ac0c7c8-f838-4674-9e37-10a0df2f81c1","scaleValue":"1","optional":"false","documentId":"1","recipientId":"38774161","pageNumber":"1","xPosition":"161","yPosition":"275","tabId":"371bc702-1a91-4b71-8c77-a2e7abe3210e","tabType":"signhere"}]},"creationReason":"sender","isBulkRecipient":"false","requireUploadSignature":"false","name":"Jesse Orange","firstName":"","lastName":"","email":"jesseorange360#gmail.com","recipientId":"38774161","recipientIdGuid":"844f781c-1516-4a5a-821a-9d8fb2319369","requireIdLookup":"false","userId":"f544f7ff-91bb-4175-894e-b42ce736f273","routingOrder":"2","note":"","status":"created","completedCount":"0","deliveryMethod":"email","totalTabCount":"1","recipientType":"signer"}],"agents":[],"editors":[],"intermediaries":[],"carbonCopies":[],"certifiedDeliveries":[],"inPersonSigners":[],"seals":[],"witnesses":[],"notaries":[],"recipientCount":"2","currentRoutingOrder":"1"},"purgeState":"unpurged","envelopeIdStamping":"true","is21CFRPart11":"false","signerCanSignOnMobile":"true","autoNavigation":"true","isSignatureProviderEnvelope":"false","hasFormDataChanged":"false","allowComments":"true","hasComments":"false","allowViewHistory":"true","envelopeMetadata":{"allowAdvancedCorrect":"true","enableSignWithNotary":"false","allowCorrect":"true"},"anySigner":null,"envelopeLocation":"current_site","isDynamicEnvelope":"false"}';
// Compute a hash as in production this will come from DocuSign
$hash = $this->computeHash($this->docusignConnectKey, $payload);
// Validate the hash as we're going to use it as the header
$this->assertTrue($this->validateHash($this->docusignConnectKey, $payload, $hash));
// Convert this response to an array for the test
$payload = json_decode($payload, true);
// Post as JSON as Laravel only accepts POSTing arrays
$this->postJson(route('webhook-docusign'), $payload, [
'x-docusign-signature-3' => $hash
])->assertStatus(200);
$this->assertDatabaseHas('payment_attempts', [
'envelope_id' => $payload['envelopeId']
]);
Mail::assertNothingSent();
}
/**
* As we're testing we need a way to verify the signature so we're computing the hash.
*/
private function computeHash($secret, $payload)
{
$hexHash = hash_hmac('sha256', $payload, utf8_encode($secret));
$base64Hash = base64_encode(hex2bin($hexHash));
return $base64Hash;
}
/**
* Validate that a given hash is valid.
*
* #param string $secret: the secret known only by our application
* #param string $payload: the payload received from the webhook
* #param string $verify: the string we want to verify in the request header
*/
private function validateHash($secret, $payload, $verify)
{
return hash_equals($verify, self::computeHash($secret, $payload));
}
}
I'm also using webhook.site to compare hashes:
Given this I can tell you that x-docusign-signature-3 matches the hash I generate when I run
$hash = $this->computeHash($this->docusignConnectKey, $payload);
So, my issue surely must stem from the way I'm sending the data back through?
When you compute your own HMAC on the incoming payload (to see if it matches the HMAC that was sent in the header), you must use the incoming payload as is.
In your code:
public function handleWebhook(Request $request)
{
$payload = json_decode($request->getContent(), true);
$shouldProcessWebhook = $this->determineIfEnvelopeRelevant($payload);
you are sending the json decoded payload to your check method. That is not right, you should send the raw payload, as it arrived.
(Decoding, then encoding JSON doesn't necessarily give you the same byte sequence as the original.)
The JSON decode method should only be applied to the payload after you've confirmed that the payload came from DocuSign.
Plus, doing the JSON decode before you've authenticated the sender is a security issue. A bad guy could be trying to send you some bad input. The rule is trust nothing until you've verified the sender (via the HMAC in this case).
Bonus comment
I recommend that you also configure DocuSign Connect webhook's Basic Authentication feature. Basic Authentication is often checked at the web server level. HMAC, since it must be computed, is usually check at the app level. Using both provides solid defense against bad guys.

Php Rest API: problem creating an array separated with ampersands

I have a doubt: An api requests the following structure:
API endpoint
● Production:
https://api-cbt.mercadolibre.com/api/orders/search/?days=<recentdays>&status=<order_status>&page=
<pageno>&access_token=xxx
● HTTP Method: GET
● Content-type header: content-type:application/json
Consequently I created the following structure:
$variables = [];
$variables['days'] = 10;
$variables['page'] = 1;
$query = $meli->get('/api/orders/search/', $variables, array('access_token' => $token));
but for some reason gives HTTPCODE = 401 (this is because $variables and commas are not been considered properly, I know that because access_token work perfect with other API calls).
My main question is what I have to do in order to produce a result like days=&status=&page=.. Im confused because I expected an array to be considered in that way but its not what is going on in my experience.
EDIT------
About GET:
/**
* Execute a GET Request
*
* #param string $path
* #param array $params
* #param boolean $assoc
* #return mixed
*/
public function get($path, $params = null, $assoc = false) {
$exec = $this->execute($path, null, $params, $assoc);
return $exec;
}
the library is provided by MercadoLibre at https://github.com/mercadolibre/php-sdk/blob/master/Meli/meli.php
thanks in advance..
I would threat access_token as a variable to pass within query string:
$variables = [];
$variables['days'] = 10;
$variables['page'] = 1;
$variables['access_token'] => $token;
$query = $meli->get('/api/orders/search/', $variables)
Looking at get() it seems that third argument should be boolean, not an array.

Amazon ElasticSearch service Signature mismatch for PUT Request - Amazon SDK php V2

I am using Amazon ElasticSearch Service and when i tried to create SignatureV4 Request it is working fine for search operations (GET Requests). But when i tried to do some operations like create indices (Using PUT request), it will trough the Signature mismatch error.
I am using Amazon SDK version 2 SignatureV4 library for signing the requests. Also created a custom Elasticsearch handler to add tokens to the request.
Does anybody have such issue with SignatureV4 library in Amazon SDK php V2.
{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'PUT\n/test_index_2\n\nhost:search-test-gps2gj4zx654muo6a5m3vxm3cy.eu-west-1.es.amazonaws.com\nx-amz-date:XXXXXXXXXXXX\n\nhost;x-amz-date\n271d5ef919251148dc0b5b3f3968c3debc911a41b60ef4e92c55b98057d6cdd4'\n\nThe String-to-Sign should have been\n'AWS4-HMAC-SHA256\XXXXXXXXXXXX\n20170511/eu-west-1/es/aws4_request\n0bd34812e0727fba7c54068b0ae1114db235cfc2f97059b88be43e8b264e1d57'\n"}
This tweak only necessary for the users who are still using Amazon SDK PHP version 2. In version 3, it supported by default.
For signed request i updated the current elsticsearch client handler by adding a middle ware for signing the request.
$elasticConfig = Configure::read('ElasticSearch');
$middleware = new AwsSignatureMiddleware();
$defaultHandler = \Elasticsearch\ClientBuilder::defaultHandler();
$awsHandler = $middleware($defaultHandler);
$clientBuilder = \Elasticsearch\ClientBuilder::create();
$clientBuilder->setHandler($awsHandler)
->setHosts([$elasticConfig['host'].':'.$elasticConfig['port']]);
$client = $clientBuilder->build();
I used the following library for this purpose
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Signature\SignatureInterface;
use Guzzle\Http\Message\Request;
class AwsSignatureMiddleware
{
/**
* #var \Aws\Credentials\CredentialsInterface
*/
protected $credentials;
/**
* #var \Aws\Signature\SignatureInterface
*/
protected $signature;
/**
* #param CredentialsInterface $credentials
* #param SignatureInterface $signature
*/
public function __construct()
{
$amazonConf = Configure::read('AmazonSDK');
$this->credentials = new \Aws\Common\Credentials\Credentials($amazonConf['key'], $amazonConf['secret']);
$this->signature = new \Aws\Common\Signature\SignatureV4('es', 'eu-west-1');
}
/**
* #param $handler
* #return callable
*/
public function __invoke($handler)
{
return function ($request) use ($handler) {
$headers = $request['headers'];
if ($headers['host']) {
if (is_array($headers['host'])) {
$headers['host'] = array_map([$this, 'removePort'], $headers['host']);
} else {
$headers['host'] = $this->removePort($headers['host']);
}
}
if (!empty($request['body'])) {
$headers['x-amz-content-sha256'] = hash('sha256', $request['body']);
}
$psrRequest = new Request($request['http_method'], $request['uri'], $headers);
$this->signature->signRequest($psrRequest, $this->credentials);
$headerObj = $psrRequest->getHeaders();
$allHeaders = $headerObj->getAll();
$signedHeaders = array();
foreach ($allHeaders as $header => $allHeader) {
$signedHeaders[$header] = $allHeader->toArray();
}
$request['headers'] = array_merge($signedHeaders, $request['headers']);
return $handler($request);
};
}
protected function removePort($host)
{
return parse_url($host)['host'];
}
}
The exact line i tweaked for this purpose is
if (!empty($request['body'])) {
$headers['x-amz-content-sha256'] = hash('sha256', $request['body']);
}
For PUT and POST request the payload hash was wrong because i was not considering the request body while generating payload.
Hope this code is beneficial for anyone who is using Amazon SDK PHP version 2 and using the IAM based authentication for Elasticsearch Hosted service in Amazon cloud.

Upgrading Facebook API version

I have recently received a Facebook developer notification:
Graph API v2.1 Upgrade Notice
foobarapplication has been making recent API calls to Graph API v2.0,
which will reach the end of the 2-year deprecation window on Monday,
August 8, 2016. Please migrate all calls to v2.1 or higher in order to
avoid potential broken experiences.
We recommend using our new Graph API Upgrade Tool to see which of your
calls are affected by this change as well as any replacement calls in
newer versions. You can also use our changelog to see the full list of
changes.
A year ago I have upgraded Facebook for the given PHP application, by extracting the PHP SDK and changing source-code usages. The login review was successful and there were no serious problems since then. However, the app needs to upgrade from Facebook API 2.0 soon. I have an idea as of how to achieve this, but am not sure whether I am correct. Let us consider the following functionalities:
FacebookRedirectLoginHelper class:
/**
* Stores CSRF state and returns a URL to which the user should be sent to
* in order to continue the login process with Facebook. The
* provided redirectUrl should invoke the handleRedirect method.
*
* #param array $scope List of permissions to request during login
* #param string $version Optional Graph API version if not default (v2.0)
* #param boolean $displayAsPopup Indicate if the page will be displayed as a popup
*
* #return string
*/
public function getLoginUrl($scope = array(), $version = null, $displayAsPopup = false)
{
$version = ($version ?: FacebookRequest::GRAPH_API_VERSION);
$this->state = $this->random(16);
$this->storeState($this->state);
$params = array(
'client_id' => $this->appId,
'redirect_uri' => $this->redirectUrl,
'state' => $this->state,
'sdk' => 'php-sdk-' . FacebookRequest::VERSION,
'scope' => implode(',', $scope)
);
if ($displayAsPopup)
{
$params['display'] = 'popup';
}
return 'https://www.facebook.com/' . $version . '/dialog/oauth?' .
http_build_query($params, null, '&');
}
/**
* Returns a URL to which the user should be sent to re-request permissions.
*
* #param array $scope List of permissions to re-request
* #param string $version Optional Graph API version if not default (v2.0)
*
* #return string
*/
public function getReRequestUrl($scope = array(), $version = null)
{
$version = ($version ?: FacebookRequest::GRAPH_API_VERSION);
$this->state = $this->random(16);
$this->storeState($this->state);
$params = array(
'client_id' => $this->appId,
'redirect_uri' => $this->redirectUrl,
'state' => $this->state,
'sdk' => 'php-sdk-' . FacebookRequest::VERSION,
'auth_type' => 'rerequest',
'scope' => implode(',', $scope)
);
return 'https://www.facebook.com/' . $version . '/dialog/oauth?' .
http_build_query($params, null, '&');
}
FacebookRequest class:
/**
* FacebookRequest - Returns a new request using the given session. optional
* parameters hash will be sent with the request. This object is
* immutable.
*
* #param FacebookSession $session
* #param string $method
* #param string $path
* #param array|null $parameters
* #param string|null $version
* #param string|null $etag
*/
public function __construct(
FacebookSession $session, $method, $path, $parameters = null, $version = null, $etag = null
)
{
$this->session = $session;
$this->method = $method;
$this->path = $path;
if ($version) {
$this->version = $version;
} else {
$this->version = static::GRAPH_API_VERSION;
}
$this->etag = $etag;
$params = ($parameters ?: array());
if ($session
&& !isset($params["access_token"])) {
$params["access_token"] = $session->getToken();
}
if (FacebookSession::useAppSecretProof()
&& !isset($params["appsecret_proof"])) {
$params["appsecret_proof"] = $this->getAppSecretProof(
$params["access_token"]
);
}
$this->params = $params;
}
FacebookCurlHttpClient class:
/**
* Detect versions of Curl which report incorrect header lengths when
* using Proxies.
*
* #return boolean
*/
private static function needsCurlProxyFix()
{
$ver = self::$facebookCurl->version();
$version = $ver['version_number'];
return $version < self::CURL_PROXY_QUIRK_VER;
}
My idea is as follows:
getLoginUrl is called from the application; a version of 2.6. should be specified from now on
I do not really use getReRequestUrl, so I will not make changes in the code for it
FacebookRequest will be instantiated with a $version of 2.6
needsCurlProxyFix will be left as it is
Basically, I will use the PHP lib released in 2014, but with specifying $version at calls. Is my approach feasible, or should I use a new client-side library?
As things turned out, my version was up to date and I did not need to do any changes for Facebook's upgrade to version 2.1. except changing the version name used.

Facebook PHP SDK for Graph API

Is it possible to print to my log files the exact request from Facebook PHP SDK to the Facebook Graphs Server?
Can someone explain me how to modify the Facebook PHP Library https://github.com/facebook/php-sdk
I found:
/**
* Invoke the Graph API.
*
* #param String $path the path (required)
* #param String $method the http method (default 'GET')
* #param Array $params the query/post data
* #return the decoded response object
* #throws FacebookApiException
*/
protected function _graph($path, $method = 'GET', $params = array()) {
if (is_array($method) && empty($params)) {
$params = $method;
$method = 'GET';
}
$params['method'] = $method; // method override as we always do a POST
$result = json_decode($this->_oauthRequest(
$this->getUrl('graph', $path),
$params
), true);
// results are returned, errors are thrown
if (is_array($result) && isset($result['error'])) {
$this->throwAPIException($result);
}
return $result;
}
You should rather have a look at the makeRequest function where the actual http request takes place. Since I wouldn't play around in the api, you could also extend the class and override the method:
class FacebookLogger extends Facebook {
protected function makeRequest($url, $params, $ch=null) {
var_dump($url);
var_dump($params);
parent::makeRequest($url, $params, $ch);
}
}

Categories