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');
Related
I've edited this question as I realised I was completely on the wrong track, however I still have an issue.
Using Guzzle, how do I send an object in JSON-form from my shop server, which does not use Laravel, to my returns server, which does use Laravel?
I keep receiving the following error:
Client error: `POST https://returns.jdoe.blah.test/createReturn` resulted in a `419 unknown status`.
I think it has something to do with the fact that I don't have a token, but I don't know what to do with it. I know that Laravel uses CSRF tokens, but my shop server does not use that form.
In the shop server, when a user makes an order, it is saved in the object "$order". I added the following code to order_details.php, in an attempt to pass two particular attributes of the order object:
$client = new Client();
$url = "https://returns.jdoe.blah.test/createReturn";
$post_data = array(
'orderId' => $order['aufnr'],
'customerId' => $order['kundennummer']
);
$data = json_encode($post_data);
$request = $client->post($url, array(
'content-type' => 'application/json'
));
$request->setBody($data);
$response = $request->send();
Then in my Laravel project, I have:
web.php
Route::post('/createReturn', 'ProductReturnsController#createReturn');
ProductReturnsController.php
<?php
namespace App\Http\Controllers;
use App\ProductReturn;
use Illuminate\Http\Request;
class ProductReturnsController extends Controller
{
public function createReturn($json)
{
echo "hallo";
/* $jsonDecoded = json_decode($json);
$orderId = $jsonDecoded['orderId'];
echo $orderId;*/
return view('data');
}
}
data.blade.php
<html>
<head>
Test
</head>
<body>
This is a test page.
</body>
</html>
If you need anything else from me to help me solve this, please don't hesitate to ask. Thanks :).
The response to your question is actually on the first page of Guzzle documentation: http://docs.guzzlephp.org/en/stable/
You are doing var_dump($response) which is in fact the response object for the request you made. That object has a method getBody(),
So try doing
dd($response->getBody());
instead.
Try dd($response->getBody()->getContents()) or dd((string) $response->getBody()). Response body is a stream object, so if you want a string you have to do an additional method call.
I'm new to PHP development
I'm trying to learn by implementing some real projects for fun.
So I tried to build a bitcoin app where customers can pay in cryptocurrency.
So I start with Coinbase commerce API
I successfully implement the charge page and everything is working well until I reached the point where I have to deal with WEBHOOKS ๐
I'm following this documentation
https://github.com/coinbase/coinbase-commerce-php/blob/master/README.md
And that's the WEBHOOKs code
`<?php
require_once __DIR__ . "/vendor/autoload.php";
use CoinbaseCommerce\Webhook;
/**
* To run this example please read README.md file
* Past your Webhook Secret Key from Settings/Webhook section
* Make sure you don't store your Secret Key in your source code!
*/
$secret = 'SECRET_KEY';
$headerName = 'X-Cc-Webhook-Signature';
$headers = getallheaders();
$signraturHeader = isset($headers[$headerName]) ? $headers[$headerName] : null;
$payload = trim(file_get_contents('php://input'));
try {
$event = Webhook::buildEvent($payload, $signraturHeader, $secret);
http_response_code(200);
echo sprintf('Successully verified event with id %s and type %s.', $event->id, $event->type);
} catch (\Exception $exception) {
http_response_code(400);
echo 'Error occured. ' . $exception->getMessage();
}
`
When I access to the we hooks URL I got this error
Error occured. Invalid payload provided. No JSON object could be decoded
Please ๐ I want someone to explain to me this error
Thanks in advance.
Seems like you are making a GET (No payload data) request to a url that is expecting a POST (Has payload data) request from the web-hook.
To test API's with POST, PUT, GET requests, you can use tools like PostMan.
You can build JSON payloads manually and test your endpoints.
Try this
$headerName = 'x-cc-webhook-signature';
$signraturHeader = isset($headers[$headerName]) ? $headers[$headerName] : null;
instead of
$headerName = 'X-Cc-Webhook-Signature';
$signraturHeader = isset($headers[$headerName]) ? $headers[$headerName] : null;
I am trying to find the logged in user in my application using Auth but i get trying to get property of non-object which i understand clearly that it is returning null.
In my code below, an event triggers my webhook and post is sent to the address below. The function orderCreateWebhook triggers but that is where the error comes from..
The line $get_template = Order::where('id', Auth::user()->id);. Why is Auth returning null please? I am logged as well because i use auth in this same controller for another function which works fine.
Is it because it a webhook ?
Controller
public function registerOrderCreateWebhook(Request $request)
{
$shop = "feas.myshopify.com";
$token = "8f43d89a64e922d7d343c1173f6d";
$shopify = Shopify::setShopUrl($shop)->setAccessToken($token);
Shopify::setShopUrl($shop)->setAccessToken($token)->post("admin/webhooks.json", ['webhook' =>
['topic' => 'orders/create',
'address' => 'https://larashop.domain.com/order-create-webhook',
'format' => 'json'
]
]);
}
public function orderCreateWebhook(Request $request)
{
$get_template = Order::where('id', Auth::user()->id);
$baseurl = "https://apps.domain.net/smsapi";
$query = "?key=7e3e4d4a6cfebc08eadc&to=number&msg=message&sender_id=Shopify";
$final_uri = $baseurl.$query;
$response = file_get_contents($final_uri);
header ("Content-Type:text/xml");
}
In your function registerOrderCreateWebhook you appear to be making a request to shopify api and providing your webhook as the address which shopify will redirect the user to upon success. If this is correct, that request does not know about the user who generated the original request that made the api request since the request is coming from a completely different origin.
You would need to pass some key along with the url and then obtain the user within orderCreateWebhook. Something like:
Shopify::setShopUrl($shop)->setAccessToken($token)->post("admin/webhooks.json",
['webhook' =>
['topic' => 'orders/create',
'address' => 'https://larashop.domain.com/order-create-webhook/some-unique-key',
'format' => 'json'
]
]);
My suggestion would be to have a unique hash stored somewhere that relates back to the user in your system, perhaps a column in your users table. I wouldn't use the user_id for security reasons. So you would end up with something like:
//route
Route::get('/order-create-webhook/{uniqueKey}', 'YourController#orderCreateWebhook');
//or
Route::post('/order-create-webhook/{uniqueKey}', 'YourController#orderCreateWebhook');
// depending on the request type used by api which calls this endpoint
// controller function
public function orderCreateWebhook($uniqueKey, Request $request)
{
$user = User::where('unique_key', $uniqueKey)->first();
$get_template = Order::where('id', Auth::user()->id);
$baseurl = "https://apps.domain.net/smsapi";
$query = "?key=7e3e4d4a6cfebc08eadc&to=number&msg=message&sender_id=Shopify";
$final_uri = $baseurl.$query;
$response = file_get_contents($final_uri);
header ("Content-Type:text/xml");
}
Is it because it a webhook ?
Yes, you can't use sessions in a webhook. It's the shopify server which is making the call. You should read the doc, it may exist a way to give an unique identifier in your call to shopify api and get it back in the webhook to find your user associated.
just use this to get authenticated user
use the facade in your class/Controller
use Illuminate\Support\Facades\Auth
public function getAuthUser(){
$user = Auth::user()
if(!is_null($user)
{
//user is authenticated
}
else
{
// no user
}
}
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.
I am a newbie to JWT Token System in laravel 5 and using tymon JWT Auth
I managed to create my custom JWT token and my code as follows
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Tymon\JWTAuth\JWTManager as JWT;
use JWTAuth;
use JWTFactory;
use Tymon\JWTAuth\Exceptions\JWTException;
public function login(Request $request)
{
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$payload = JWTFactory::make($customClaims);
$token = JWTAuth::encode($payload);
// return response()->json(compact('token')); // This didnt work?Why?
return response()->json(compact($token))->header('Authorization','Bearer '.$token);
}
public function getUser(){
$token = JWTAuth::parseToken();
echo $token;
}
Here are my following clarifications required
// return response()->json(compact('token'));
Why this gave me an empty json object as {"token":{}}
Is it the right way, i could send my custom data in token and get it back the foo and baz values from the same token?
The output of my code while testing with postman is an empty array. as []. But my headers are added with Authorization โBearer eyJ0eXAiOiJKV1QiLCJhbG...
Is this correct?
3a. Instead of a simple blank array, i need a success message as 'authorized':true. How can i achieve it?
How should i pass this token back to test. Where should this token be passed using postman. I passed it through Headers as shown in the image
How could i parse this token using laravel and get the custom data i.e foo and baz sent as a token. The method i called is getUser here.
I dont think the token creation is being built properly. Below is working code for login token creation. For this, make sure that the 'user' model under your config/jwt.php is the correct eloquent user model for your application.
$user = array(
'user' => $request->input('email'),
'password' => $request->input('pass')
);
$customClaims= ['usr' => $user['user']];
if(!$token = JWTAuth::attempt($user, $customClaims)){
abort(401);
}
else{
return response()->json(compact('token'));
}
Also included in the above code with the custom claims variable, you were on the right track with that just needs to be passed as a second parameter in the attempt function.
Only the client needs to send the authorization: Bearertoken header to prove that they are who they say they are (I am coming from an android client/server jwt background. So sorry if this doesnt apply to your application).
3a. For any subsequent pages that the user browses to, you simply add an if statement like this
if(!$user = JWTAuth::parseToken()->authenticate()){
abort(401);
}
else{
// Code allowing the user to see protected content
}
See answer to question 3. include an http header with authorization BearerToken
To extract the data from the JWT Payload, you will need to decode the base64 encoded text from the text after the first period in the token and send that to a string. Then run that string through the base64_decode($string) function. That should start to give you some of the payload data.
Hope this helps.
I had the same problem here and i got the following solution:
public function whatEver()
{
$token = JWTAuth::parseToken();
$response = $token->getPayload()->get('foo');
return $response
}
this should return bar.
you can use this method in your user model :
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* #return array
*/
public function getJWTCustomClaims()
{
return [
'perms' => '
'
];
}