I'm implementing gocardless api in my website. I'm stuck in webhook. When i'm sending an webhook from sandbox test environment into my website, it showing 200 response, but after that no code is executing. and also i'm not seeing anything in response body, its showing null.
I'm using laravel 5.7 for that. Here is my code
route.php
Route::post('/webhook', 'HomeController#webhook');
HomeController.php
public function webhook()
{
$webhook_endpoint_secret = env("GOCARDLESS_WEBHOOK_ENDPOINT_SECRET");
$request_body = file_get_contents('php://input');
$headers = getallheaders();
$signature_header = $headers["Webhook-Signature"];
try {
$events = Webhook::parse($request_body, $signature_header, $webhook_endpoint_secret);
foreach ($events as $event) {
print("Processing event " . $event->id . "\n");
switch ($event->resource_type) {
case "mandates":
$this->process_mandate_event($event);
break;
default:
print("Don't know how to process an event with resource_type " . $event->resource_type . "\n");
break;
}
}
header("HTTP/1.1 204 OK");
} catch(InvalidSignatureException $e) {
header("HTTP/1.1 498 Invalid Token");
}
}
public function process_mandate_event($event)
{
switch ($event->action) {
case "cancelled":
print("Mandate " . $event->links["mandate"] . " has been cancelled!\n");
break;
default:
print("Don't know how to process a mandate " . $event->action . " event\n");
break;
}
}
I tried to execute some database query, nothing is working anyway. Can anyone point me out what and where i'm doing wrong?
$responseBody = file_get_contents('php://input');
if ($responseBody <> "") {
$response_new = json_decode($responseBody, true);
foreach ($response_new["events"] as $event) {
print_r($event); // you will see all the data which you want
//if($event['resource_type'] == 'subscriptions')
//payments,mandates or etc...
//
}
}
use email sending the code to debugging, when webhook called, email sends to your address with response body then you will data in the email body.
Hope you understand
The best way to handle the webhooks with Laravel and it's structure is to add a Middleware to verify the webhook signature:
public function handle($request, Closure $next)
{
$signature = $request->header('Webhook-Signature');
if (!$signature) {
throw WebhookFailed::missingSignature();
}
if (!$this->isValid($signature, $request->getContent(), $request->route('configKey'))) {
throw WebhookFailed::invalidSignature($signature);
}
return $next($request);
}
The isValid method will check the signature of the webhook and your saved secret.
Then at your controller, you can handle the events that come from the webhook (remember that Gocardless can send more than one event in a single webhook request).
public function __invoke(Request $request)
{
$payload = $request->input();
foreach ($payload['events'] as $event) {
// Do whatever do you need with the events.
}
}
return response()->json(['message' => 'ok']);
}
We have created a package for Laravel can help you with the handling and processing of the Gocardless webhooks.
Nestednet/Gocardless-laravel
Got the solution. I was having the problem while getting the headers values. In laravel you can't get header value using $headers = getallheaders(); You need to use use Request; and then Request::header("Webhook-Signature"); which then solved my problem.
Related
Good morning everyone.
I need to send a notification whenever a user swipes a tab on a particular sensor.
The problem is not the connection to the sensor, which at this moment already takes place and subject to user access.
Currently I have created a server socket inside my yii2 app to be able to send the notification event to the client and update them in real time.
This is my controller server
class ServerController extends Controller
{
public function actionStart()
{
// $server = new CommandsServer();
$server = new ChatServer();
$server->port = 80; //This port must be busy by WebServer and we handle an error
$server->on(WebSocketServer::EVENT_WEBSOCKET_OPEN_ERROR, function ($e) use ($server) {
echo "Error opening port " . $server->port . "\n";
$server->port += 1; //Try next port to open
$server->start();
});
$server->on(WebSocketServer::EVENT_WEBSOCKET_OPEN, function ($e) use ($server) {
echo "Server started at port " . $server->port;
});
$server->start();
}
}
This is my chat server which I created for testing
<?php
namespace frontend\daemons;
use consik\yii2websocket\events\WSClientEvent;
use consik\yii2websocket\WebSocketServer;
use Ratchet\ConnectionInterface;
class ChatServer extends WebSocketServer
{
public function init()
{
parent::init();
$this->on(self::EVENT_CLIENT_CONNECTED, function (WSClientEvent $e) {
$e->client->name = null;
});
}
protected function getCommand(ConnectionInterface $from, $msg)
{
$request = json_decode($msg, true);
return !empty($request['action']) ? $request['action'] : parent::getCommand($from, $msg);
}
public function commandPing(ConnectionInterface $client, $msg)
{
$arr = ["Neo", "Morpheus", "Trinity", "Cypher", "Tank"];
$res = ['type' => 'ping', 'message' => json_encode(array_rand($arr, 1))];
foreach ($this->clients as $chatClient) {
$chatClient->send(json_encode($res));
}
}
}
Now, what I wish I could do is be able to use that commandPing inside another controller, but I haven't found a way I can implement this.
In this sense the round would be:
user swipes card on sensor -> the sensor calls my method to see if the user is actually authorized to enter -> I call commandPing (as an example) to send a notification to the customer (OK / KO )
On the web interface side I will then intercept the message via new Websocket (but this is not a problem)
So long story short i was stuck with this old PHP utility that is using an old zend framework. I am having trouble when it times-out it doesnt retry so it fails. i have done everything from changing timeout timers to changing max redirects and everything else i can do with the settings and still get the same thing.
I get a consistent
Fatal error: Uncaught exception 'Zend_Http_Client_Exception' with message 'Unable to read response, or response is empty'
and if i happen to try it enough it will work without issue. its just getting to retry it is the problem.
Im not sure where to think to put the retry logic.
I've seen some say it would go somewhere here (Zend_http_client)
public function request($method = null)
{
if (! $this->uri instanceof Zend_Uri_Http) {
/** #see Zend_Http_Client_Exception */
require_once 'Zend/Http/Client/Exception.php';
throw new Zend_Http_Client_Exception('No valid URI has been passed to the client');
}
if ($method) {
$this->setMethod($method);
}
$this->redirectCounter = 0;
$response = null;
// Make sure the adapter is loaded
if ($this->adapter == null) {
$this->setAdapter($this->config['adapter']);
}
// Send the first request. If redirected, continue.
do {
// Clone the URI and add the additional GET parameters to it
$uri = clone $this->uri;
if (! empty($this->paramsGet)) {
$query = $uri->getQuery();
if (! empty($query)) {
$query .= '&';
}
$query .= http_build_query($this->paramsGet, null, '&');
$uri->setQuery($query);
}
$body = $this->_prepareBody();
$headers = $this->_prepareHeaders();
// Open the connection, send the request and read the response
$this->adapter->connect($uri->getHost(), $uri->getPort(),
($uri->getScheme() == 'https' ? true : false));
$this->last_request = $this->adapter->write($this->method,
$uri, $this->config['httpversion'], $headers, $body);
$response = $this->adapter->read();
if (! $response) {
/** #see Zend_Http_Client_Exception */
require_once 'Zend/Http/Client/Exception.php';
throw new Zend_Http_Client_Exception('Unable to read response, or response is empty');
}
$response = Zend_Http_Response::fromString($response);
if ($this->config['storeresponse']) {
$this->last_response = $response;
}
I was thinking it could also go here (in my own code)
$eventResponse = new Response($Client->Event(
$theEvent,null));
if (!$throwEventResponse->getResponseStatusOk()) {
$ex = new ResponseException("Unable to complete event.");
$ex->setErrorList($throwEventResponse->getErrorList());
throw $ex;
}
Im at a bit of a loss and im not sure how to go about it to best work for what i need.
Thanks in advance!
Found that by changing the type of HTTP request from HTML 1.1 to 1.0 fixed the issue i was having with the calls moving to the next without waiting for the previous call to finish.
pushing the second request before the other was done caused a weird caching issue and the request failed.
I'm trying to handle this problem:
My app send JSON POST request with several information encoded in a Json. Example:
{"UserInfoA":{"1":123,"2":"hello","3":"bye","4":{"subinfo":1,"subinfo2":10}},
"UserInfoB":{"a":"12345678","b":"asd"}} // and so on...
each UserInfo have:
Its own entity (although some request may have information of more than one entity).
A controller to persist this Object on DB and then give back the ID on this DB.
So, to achieve this problem I did another controller like JsonHandler, which receive this request and then forward to each controller after gut this JSON into differents objects. Example:
public function getJson (Request $request){
if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
$data = json_decode($request->getContent(), true);
}
if (!isset($data['UserInfoA'])){
return new JsonResponse('ERROR');
}
$uia = $data['UserInfoA'];
$idInfoA = $this->forward('Path::dataAPersist',array('data'=>$uia));
}
// same with userinfoB and other objects
return $idInfoA ;
This works perfectly but Is it correct? Should i use services instead?
EDIT : I need to response the ID in a Json and this->forward returns a Response, so i can't use JsonResponse and if a send directly $idInfoA just send the IDNUMBER not in a JSON, how can i do it?
To sum up : a Json listener that receive the information, work it and then dispatch to the corresponding controller. This listener, should be a controller, a service or another thing?
I recommend the use of symfony-bundles/json-request-bundle since it does the JsonRequestTransformerListener for you. You just need to recieve the request parameters as usual:
...
$request->get('some_parameter');
...
hi you have to use service to make the Transformation
class php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class JsonRequestTransformerListener {
public function onKernelRequest(GetResponseEvent $event) {
$request = $event->getRequest();
$content = $request->getContent();
if (empty($content)) {
return;
}
if (!$this->isJsonRequest($request)) {
return;
}
if (!$this->transformJsonBody($request)) {
$response = Response::create('Unable to parse request.', 400);
$event->setResponse($response);
}
}
private function isJsonRequest(Request $request) {
return 'json' === $request->getContentType();
}
private function transformJsonBody(Request $request) {
$data = json_decode($request->getContent(), true);
if (json_last_error() !== JSON_ERROR_NONE) {
return false;
}
if ($data === null) {
return true;
}
$request->request->replace($data);
return true;
}
}
And In Your Service.yml
kernel.event_listener.json_request_transformer:
class: You\NameBundle\Service\JsonRequestTransformerListener
tags:
- { name: "kernel.event_listener", event: "kernel.request",method: "onKernelRequest", priority: "100" }
Now You can call The Default request function to get Data
$request->request->all();
You can use symfony ParamConverter to to convert the json into any object you want and raise a meaningful Exception if anything goes wrong.
You can add custom ParamConverters and use them in your actions with annotation
I have a function in my Laravel application that generates TwiML for a holding queue. It seems that when I try to dynamically generate the value for the waitUrl attribute, I end up getting a 500 server error during runtime. Routes are properly established and I'm able to view the correct XML at the waitURL in the browser. However, the error persists.
If I create a static XML file with the same exact content, or use a TwiML Bin, it works like a charm.
Here are the relevant functions:
public function wait() {
return $this->generateWaitTwiml();
}
public function onHold($agentId) {
return $this->generateHoldQueueTwiml($agentId, '/phone/wait');
}
private function generateHoldQueueTwiml($agentId, $waitUrl = null) {
$queue = $agentId . '_hold';
if ($waitUrl === null){
$waitUrl = 'path_to_static.xml';
}
$queue = $agentId . '_hold';
$response = new Twiml();
$response->enqueue(
$queue,
['waitUrl' => $waitUrl]
);
return response($response)->header('Content-Type', 'application/xml');
}
private function generateWaitTwiml() {
$response = new Twiml();
$response
->play('http://path_to_my.mp3');
return response($response)->header('Content-Type', 'application/xml');
}
This was resolved by excluding the URIs from the CSRF verification (in VerifyCsrfToken.php):
class VerifyCsrfToken extends Middleware {
protected $except = [
'uri/',
'uri2/*',
];
}
searching for a good authentication method for my rest api i came a cross this :
"What is stateless authentication?
Again, stateless means without state. But, how can we identify a user from a token without having any state on the server? Surprisingly, it’s very easy! just send all the data to the client.
So what would you store/send (send to client/network)? The most trivial example is an access token. Access tokens usually have a unique ID, an expiration date and the ID of the client that created it. To store this, you would just put this data into a JSON object, and encode it using base64."
Now, having a self-contained token, you will need to make sure that nobody can manipulate the data. For this you should sign it using MAC algorithm or any other digital signature method available.
This is a little confusing for me , how can i validate the access token when the request comes(nothing stored to match it), but i find it a good idea and i want to implement it, any advice will be very helpful.
My rest api is very simple I receive every request to index.php , then i create new object with the request class to analyze every element of the request.
request class looks like this :
<?php
class Request {
public $url_elements;
public $verb;
public $parameters;
public function __construct() {
$this->verb = $_SERVER['REQUEST_METHOD'];
$this->url_elements = explode('/', $_SERVER['PATH_INFO']);
$this->parseIncomingParams();
$this->format = 'json';
if(isset($this->parameters['format'])) {
$this->format = $this->parameters['format'];
}
return true;
}
public function parseIncomingParams() {
$parameters = array();
if (isset($_SERVER['QUERY_STRING'])) {
parse_str($_SERVER['QUERY_STRING'], $parameters);
}
$body = file_get_contents("php://input");
$content_type = false;
if(isset($_SERVER['CONTENT_TYPE'])) {
$content_type = $_SERVER['CONTENT_TYPE'];
}
switch($content_type) {
case "application/json":
$body_params = json_decode($body);
if($body_params) {
foreach($body_params as $param_name => $param_value) {
$parameters[$param_name] = $param_value;
}
}
$this->format = "json";
break;
case "application/x-www-form-urlencoded":
parse_str($body, $postvars);
foreach($postvars as $field => $value) {
$parameters[$field] = $value;
}
$this->format = "html";
break;
default:
break;
}
$this->parameters = $parameters;
}
}
?>
After this i proceed with the proper controller that is the first element after index.php/
Thank you very much for your time and sorry if the question is not very clear as i am new to rest :/