I'm going through a refactoring stage in the application I helped build and I haven't ever run into a similar situation, so I don't quite know if there is a way to simplify this.
The scenario: we connect our internal database to Reverb.com via their API to update our listings' inventory quantity and prices as well as to pull orders into our internal database. Reverb's API requires an authentication token for each call. The token is created by first sending the email and password and receiving the token in the response. We have three classes. The first class is mainly to setup the authentication token. The other two classes are for orders and inventory respectively. Our current setup instantiates separate objects for each class. This creates three different calls to Reverb to create an authentication token. I'm trying to remove this redundancy.
Here's the first class (revclass.php):
<?php
namespace rev;
class reverbclass
{
protected $reverbEmail;
protected $reverbPassword;
protected $reverbAuth;
public function __construct(){
//Retrieve email and password from database
$reverbinfo = $this->getReverbAppId();
$this->reverbEmail = $reverbinfo['reverb_email'];
$this->reverbPassword = $reverbinfo['reverb_pass'];
//Send email and password and receive back authentication token
$request = $this->getAuth($this->reverbEmail, $this->reverbPassword);
$reverbInfo = json_decode($request, true);
$this->reverbAuth = $reverbInfo['token'];
}
}
Here's the second class (revorderclass.php):
<?php
namespace rev;
use rev\reverbclass;
class revorderclass extends reverbclass
{
public function getOrders(){
$url = 'https://reverb.com/api/my/orders/selling/awaiting_shipment.json';
$postString = '';
$headers = array(
"Content-type: application/hal+json",
"X-Auth-Token: $this->reverbAuth"
);
$response = $this->reverbCurl($url, 'GET', $headers, $post_string);
return $response;
}
}
Here's the inventory class (revinventoryclass.php):
<?php
namespace rev;
use rev\reverbclass;
class revinventoryclass extends reverbclass
{
public function getReverbListings($page){
$url = 'https://reverb.com/api/my/listings.json?page=' . $page;
$postString = '';
$headers = array(
"Content-type: application/hal+json",
"X-Auth-Token: $this->reverbAuth"
);
$response = $this->reverbCurl($url, 'GET', $headers, $post_string);
return $response;
}
}
And here's where I instantiate the classes (revclasses.php):
<?php
//Reverb Classes
include_once 'classes/rev/revclass.php';
include_once 'classes/rev/revorderclass.php';
include_once 'classes/rev/revinventoryclass.php';
//Reverb Class Declarations
$reverb = new \rev\reverbclass();
$revorder = new \rev\revorderclass();
$revinventory = new \rev\revinventoryclass();
And here's an example of a call to retrieve orders and then we'll parse them:
<?php
require 'rev/revclasses.php';
$request = $revorder->getOrders();
I only included the code I thought was critical in the question so as to not muddy the waters more. Again, everything works, but I'm trying to remove the duplicate API authentication calls that happen when all three classes are instantiated as well as make the code more Object Oriented. Right now it feels too procedural. Any tips/corrections/critique is much appreciated!
My recommendation would be to create a separate class, which acts as the API client, which you pass as a dependency in the constructor of all the classes, that need to interact with Reverb API.
$client = new ReverbClient($hostname);
$client->authenticate($apiKey);
$inventory = new Inventorty($client);
$orderRepository = new Orders($client);
And then your getReverbListings() method you only call:
$listing = $this->client->get('my/listings.json?page=' . $page, $parameters);
The client class is responsible for adding all the headers and turning the responses in some usable arrays.
Related
I am trying to implement Authorize.net webhook on a Laravel project. From merchant interface, I added a webhook endpoint. But when I try to retrieve events it throws invalid JSON error. What am I doing wrong in the code below?
namespace App\Http\Controllers\Api\Anet;
use Illuminate\Http\Request;
use net\authorize\api\contract\v1 as AnetAPI;
use net\authorize\api\controller as AnetController;
use App\Http\Controllers\Controller;
use JohnConde\Authnet\AuthnetWebhook;
class xxxController extends Controller
{
public function webhook(){
$headers = getallheaders();
$payload = file_get_contents("php://input");
$webhook = new AuthnetWebhook(hex2bin('XXXXXD4FF0A6060E23DBCD9AE507E20XXXXX'), $payload, $headers);
if ($webhook->isValid()) {
// Get the transaction ID
$transactionId = $webhook->payload->id;
// Here you can get more information about the transaction
$request = AuthnetApiFactory::getJsonApiHandler('services.authorize.login', 'services.authorize.key');
$response = $request->getTransactionDetailsRequest(array(
'transId' => $transactionId
));
/* You can put these response values in the database or whatever your business logic dictates.
$response->transaction->transactionType
$response->transaction->transactionStatus
$response->transaction->authCode
$response->transaction->AVSResponse
*/
}
}
}
Error:
"message": "Invalid JSON sent in the Webhook notification",
"exception": "JohnConde\\Authnet\\AuthnetInvalidJsonException",
"file": "/var/www/html/staging/vendor/stymiee/authnetjson/src/authnet/AuthnetWebhook.php",
"line": 67,
Your problem is that you are not getting a webhook notification. The code you are using is purposed for validating a webhook notification, rather than making a webhooks request. You have to make a request to get a webhook.
When you set up your endpoint, you can use that code (although I don't think the hex2bin() is required) to validate webhooks and then extract information from them.
To create a webhooks request, you can use code like this-
$webhooksArray = array(' net.authorize.payment.authorization.created','
net.authorize.payment.authcapture.created','
net.authorize.payment.capture.created');
$webhooksUrl = 'https://{yourserver.com}/{your path}/{your endpoint}';
$webhook = new AuthnetAPIFactory();
$handler = $webhook->getWebhooksHandler($login,$transId);
$createWebhooks = $handler->createWebhooks($webhooksArray,$webhooksUrl);
This will enroll you in events, which will automatically be sent to your endpoint
i.e https://{yourserver.com}/{your path}/{your endpoint}.
Then you can use your code above to validate the webhooks when they hit your endpoint. Once you are enrolled in events and webhooks are being sent to your endpoint, you can retrieve the history using code like this-
$webhook = new AuthnetAPIFactory();
$handler = $webhook->getWebhooksHandler($login,$transId);
$history = $handler->getNotificationHistory();
print_r($history);
You can retrieve a specific webhook like this-
$webhook = new AuthnetAPIFactory();
$handler = $webhook->getWebhooksHandler($login,$transId);
$webhook = $handler->getWebhook($webhookId);
Where $webhookId is the id tied to the webhook you wish to retrieve. You can search through the namespace to see the other method calls for specific webhook actions.
I know, the reply to this question is too late. But I recently faced this issue. I resolved it for one of my projects.
The controller method must have the Request $request parameter and the route should be POST not GET.
The Controller:
class AuthnetWebhookController extends Controller
{
public function webhookListener(Request $request)
{
.....
}
// other methods
}
The route:
Route::post('authnet-webhook-listener', 'AuthnetWebhookController#webhookListener');
I have already written an application in a procedural way and am trying to move into into a Laravel framework. I'm having trouble with the SOAP exchange section as I am getting an ID value that authenticates the user but cannot access that value (as a cookie) later in the program to authenticate the search.
Here is my code so far:
<?php namespace App;
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
use Illuminate\Http\RedirectResponse;
class SoapController {
private $auth_response;
private $cookie;
private $search_client;
private $search_response;
public function soapExchange() {
// create SOAP client and add service details
SoapWrapper::add(function ($service) {
$service
->name('WoSAuthenticate')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
SoapWrapper::service('WoSAuthenticate', function($service) {
// call authenticate() method to get SID cookie
$auth_response = $service->call('authenticate', []);
$cookie = $auth_response->return;
// test for cookie return
// print($cookie);
});
// create SOAP client and add service details
$search_client = new SoapWrapper;
$search_client::add(function ($service) {
$service
->name('WoSSearch')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
if (isset($auth_response->return)) {
// if there is an SID returned then add it to the cookie attribute of the search client
$search_client->__setCookie('SID', $cookie);
} else {
// route to relevant view to display throttle error
return redirect('throttle');
}
}
}
I am successfully retrieving the response from the Web API call and getting a code to authenticate the user, saved as $cookie. However, I need then to create another SoapWrapper for performing the search and this needs the ID code attached by using the __setCookie method. If nothing is returned by the authenticate call then it redirects to an error message via throttle.blade.php elsewhere.
Surely there is a way to return a value created from a function so that it can be used elsewhere?
** EDIT **
Looked into employing SoapClient instead and including all operations within a single function. It all relates to a specific Web API anyway so I guess separation of concerns is not so much of an issue. FYI the new class I am trying is this:
<?php namespace App\Models;
use SoapClient;
use Illuminate\Http\RedirectResponse;
class SoapWrapper {
public function soapExchange() {
// set WSDL for authentication and create new SOAP client
$auth_url = "http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl";
// array options are temporary and used to track request & response data
$auth_client = #new SoapClient($auth_url);
// set WSDL for search and create new SOAP client
$search_url = "http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl";
// array options are temporary and used to track request & response data
$search_client = #new SoapClient($search_url);
// run 'authenticate' method and store as variable
$auth_response = $auth_client->authenticate();
// call 'setCookie' method on '$search_client' storing SID (Session ID) as the response (value) given from the 'authenticate' method
// check if an SID has been set, if not it means Throttle server has stopped the query, therefore display error message
if (isset($auth_response->return)) {
$search_client->__setCookie('SID',$auth_response->return);
} else {
return Redirect::route('throttle');
}
}
}
Maybe try $GLOBALS?
<?php
$GLOBALS[data] = "something";
function abc(){
echo $GLOBALS[data];
}
?>
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
class SoapController extends Controller {
public $resultSoapStatus;
public $resultSoapAuthority;
public function heySoap{
SoapWrapper::add(function ($service) ...
$data = [
'MerchantID' => $MerchantID,
'Amount' => $Amount,
'Description' => $Description,
'Email' => $Email,
'Mobile' => $Mobile,
'CallbackURL' => $CallbackURL
];
SoapWrapper::service('test', function ($service) use ($data) {
$resultSoap = $service->call('PaymentRequest', [$data]);
$this->resultSoapStatus = $resultSoap->Status;
$this->resultSoapAuthority = $resultSoap->Authority;
});
if($this->resultSoapStatus == 100 && strlen($this->resultSoapAuthority) == 36)
{
//Do Something
}
else
{
return Redirect::back();
}
}
}
Enjoy bro
I have two cakePHP apps on 2 different servers. One app is required to get data from the first one; I have succeeded to put the Restful architecture in place but I failed to implement an authentication procedure to the requests the server sends. I need to authenticate to secure the data. I have looked around on the web but can't seem to get it working. Can anyone point me to a resource / tutorial that explains this in detail.
What I would ultimately need would be a way to authenticate my server every time it sends a request to the other server. Any help would be appreciated.
I finally got it to work after some research; indeed one of the solutions is OAuth. In case you are facing the same problem, I can advise you this Plugin made for CakePHP.
In details what I did was put the OAuth Plugin into my API Server and I used it like so for my restful controller:
class RestObjectController extends AppController {
public $components = array('RequestHandler', 'OAuth.OAuth');
public $layout = FALSE;
public function token() {
$this->autoRender = false;
try {
$this->OAuth->grantAccessToken();
} catch (OAuth2ServerException $e) {
$e->sendHttpResponse();
}
}
public function index() {
$objects = $this->Object->find('all');
$this->set(array(
'objects' => $objects,
'_serialize' => array('objects')
));
}
The function RestObject.token() is what I would call to get an Access token which will be used to give me access to the Resources in my controller. (Note that by declaring OAuth in my controller components, all the resources within my controller will need an access token to be accessible).
So on the client Server I would get an access token in the following way:
public function acquireAccessToken(){
$this->autoRender = FALSE;
App::uses('HttpSocket', 'Network/Http');
$link = API_SERVER."rest_objects/token";
$data = array(
'grant_type' => 'client_credentials',
'client_id' => 'xxxx',
'client_secret' => 'xxxx'
);
$response = $httpSocket->post($link, $data);
if($response->code == 200){
$data = json_decode($response->body, true);
return $data['access_token'];
}
return FALSE;
}
This assumes that you have clients already set up as explained in the Plugin Doc (replace xxxx by the real values for the client credentials). Once I have my access token, all I have to do is use it as follows:
public function test(){
$this->layout = FALSE;
App::uses('HttpSocket', 'Network/Http');
$httpSocket = new HttpSocket();
if($access_token = $this->acquireAccessToken()){
$link = API_SERVER."rest_objects.json"; //For the index as e.g.
$data = array('access_token' => $access_token);
$response = $httpSocket->get($link, $data);
}
}
And here you have it! So start by reading the Oauth Specification to understand the Protocol (in particular the Obtaining Authorization part), see which protocol (can be different from the one I used) applies and adapt to your case by using the Plugin
Tutorial Here
I'm trying to access to the Guzzle Response object from Goutte. Because that object has nice methods that i want to use. getEffectiveUrl for example.
As far as i can see there is no way doing it without hacking the code.
Or without accessing the response object, is there a way to get the last redirected url froum goutte?
A little late, but:
If you are only interested in getting the URL you were last redirected to, you could simply do
$client = new Goutte\Client();
$crawler = $client->request('GET', 'http://www.example.com');
$url = $client->getHistory()->current()->getUri();
EDIT:
But, extending Goutte to serve your needs is fairly easy. All you need is to override the createResponse() method and store the GuzzleResponse
namespace Your\Name\Space;
class Client extends \Goutte\Client
{
protected $guzzleResponse;
protected function createResponse(\Guzzle\Http\Message\Response $response)
{
$this->guzzleResponse = $response;
return parent::createResponse($response);
}
/**
* #return \Guzzle\Http\Message\Response
*/
public function getGuzzleResponse()
{
return $this->guzzleResponse;
}
}
Then you can access the response object as desired
$client = new Your\Name\Space\Client();
$crawler = $client->request('GET', 'http://localhost/redirect');
$response = $client->getGuzzleResponse();
echo $response->getEffectiveUrl();
I am trying to login to my magento's web services from a server that does not have SoapClient enabled. So I figured I would install and use Pear's SOAP_Client but I can't figure out how to login.
With SoapClient I use:
$client = new SoapClient($WSDL);
$session = $client->login($user, $api_key);
$response = $client->call($session, $method, $arguments);
But I can't find an analog to the login method for SOAP_Client
I gather that I should be setting something in the $proxy_params of the constructor, but I can't find what the indexes should be.
$proxy_params = array();
$client = new SOAP_Client($wsdl, true, false, $proxy_params);
$client->call($method, $arguments)
So I figured this out, and there are a couple of factors here.
There isn't a login function for SoapClient, the login I was calling is a call as defined in the WSDL
The various magento API methods are not defined in the WSDL, you provide an argument resource method to method defined as call by the WSDL. This created a bit of confusion because using $client->call() seems to invoke call as defined by the SOAP_Client class, so I need to use $client->call('call') to invoke the SOAP method call
The final code ended up being:
$method = 'catalog_product.info';
$args = array($product_id);
$client = new SOAP_Client($wsdl, true);
$session_id = $client->call(
'login',
array(
'username'=>$username,
'apiKey'=> $pasword
)
);
$ret = $client->call(
'call',
array(
'sessionId'=>$session_id,
'resourcePath'=>$method,
'args'=>$args
)
);