Twilio Send reply not hitting provided URL - laravel

I have configured Twilio phone number and given settings as per the documentation. On the callback URL i have written code like this
Route::post('/replyMessages', function(){
Log::info('inside reply messages URL');
$response = new \Twilio\TwiML\MessagingResponse();
Log::info(print_r($response, true));
$message = $response->message("Testing sms from twilio to staffing backbone");
print_r($response);
Log::info('SMS reply URL callback');
});
When user is replied from their mobile nothing is happening. If URL hits the log, then it will print the text given but not working. Kindly help me on this.
As per one of the document in Twilio they are expected to install ngrok is that necessary in order to call our URL? documentation link

ngrok is not explicitly required, it's just recommended as an easy way to make your local development environment accessible from outside of your network.
If the code is running on your local development machine, ensure that you able to query that endpoint from a device outside of your network (eg, from a mobile phone with wifi turned off). If not, ngrok will help with this.
If the code has been deployed to a server that is already publicly accessible, query it and ensure there are no errors being output.
Original answer (related to code snippet):
Try switching out print_r($response); with echo $response;.
print_r will be outputting the structure of the MessagingResponse object, whereas echo will cast your $response to a string and output the XML payload that Twilio is expecting.
Your current payload probably looks something like this:
Twilio\TwiML\MessagingResponse Object
(
[name:protected] => Response
[attributes:protected] => Array
(
)
[children:protected] => Array
(
[0] => Twilio\TwiML\Messaging\Message Object
(
[name:protected] => Message
[attributes:protected] => Array
(
)
[children:protected] => Array
(
[0] => Testing sms from twilio to staffing backbone
)
)
)
)
=> true
But Twilio will be expecting an XML payload as below:
<?xml version="1.0" encoding="UTF-8"?>
<Response><Message>Testing sms from twilio to staffing backbone</Message></Response>

Since it's a POST request, I'm guessing the VerifyCsrfToken middleware is stopping this request.
Add 'replyMessages' to the $except array in app/Http/Middleware/VerifyCsrfToken.php file, so it should end up like this:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
*
* #var bool
*/
protected $addHttpCookie = true;
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'replyMessages'
];
}

As per checking my configuration is correct and there is some problem in the Twilio.
The initial mobile which we have tried for sending replies and it is recording log and everything works but it is not triggered the callback URL. Later we have tried to reply from new mobile number it worked. Nothing changed in the configuration. So following up with Twilio on the resolution.
Thanks all for the help.

Related

Paypal webhook verification fails everytime

I have successfully implemented webhook integration using Sandbox in Paypal.
Now I want to make it more secure so that only Paypal signed notification is accepted.
I was trying to verify webhook signature using
https://developer.paypal.com/docs/api/webhooks/v1/#verify-webhook-signature_post
But it always returns FAILURE.
The request is :
{"auth_algo":"SHA256withRSA","transmission_time":"2020-08-17T12:11:08Z","cert_url":"https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-1d93a270","webhook_id":"0JD18557VD498931R","transmission_id":"bbaae190-e082-11ea-aa52-1fdbf2bc8461","webhook_event":{"summary":"Payment completed for $ 5.0 USD","event_type":"PAYMENT.SALE.COMPLETED","create_time":"2020-08-17T12:11:05.015Z","resource":{"billing_agreement_id":"I-DNVD3H9UWYHL","amount":{"total":"5.00","currency":"USD","details":{"subtotal":"5.00"}},"payment_mode":"INSTANT_TRANSFER","update_time":"2020-08-17T12:10:39Z","create_time":"2020-08-17T12:10:39Z","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","transaction_fee":{"currency":"USD","value":"0.45"},"protection_eligibility":"ELIGIBLE","links":[{"method":"GET","rel":"self","href":"https://api.sandbox.paypal.com/v1/payments/sale/8TV124151P468690Y"},{"method":"POST","rel":"refund","href":"https://api.sandbox.paypal.com/v1/payments/sale/8TV124151P468690Y/refund"}],"id":"8TV124151P468690Y","state":"completed","invoice_number":""},"resource_type":"sale","links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2XT265922L1486124-74F09092JL7840709","rel":"self","targetSchema":null,"method":"GET","enctype":null,"schema":null},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2XT265922L1486124-74F09092JL7840709/resend","rel":"resend","targetSchema":null,"method":"POST","enctype":null,"schema":null}],"id":"WH-2XT265922L1486124-74F09092JL7840709"},"transmission_sig":"RYILWohVPkK0hUrMjTSU3+fCgd6NTgqkjrZtsOJiC7FR3U3atOK1k29/Md8DQRReAicdfOpXrS7E4vrvB17HOM39w/D3i4Ohy34HL3CqSsZovL69lhfFmviCGkXjjSbkBhpKGJvQAB4q0E9AWl/SBZc4MUNGezIbk/laJZ6ikQuwGeEHCFaPVrza7kSlZRo03lM9sYSb7q3ixewYmz8voKIyJ2RYjOgsAohNFWgovtKwG+ac66YCp3ZRJLe4fL2Q1UaEDn5BnUhK+5Q2+EqD+BixpqNTuSmYqRwkyDTdrH1EPV5DRU4uYM0gJLXnBovGaqHe8JujpVs+dJu4Mrmgdg=="}
and the result is
{"verification_status":"FAILURE"}
Can someone help, please !!
If you are getting those values from the webhook simulator as they said in their documentation you can't verify mock webhooks.
What I used to do when I need to validate the value because some test need that part it's that using the smart buttons to generate the request and then catch the response in some webhook.
Verify Paypal webhook notification
How to verify the authenticity of the notification in php:
// get request headers
$headers = apache_request_headers();
// get http payload
$body = file_get_contents( 'php://input' );
// compose signature string: The third part is the ID of the webhook ITSELF(!),
// NOT the ID of the webhook event sent. You find the ID of the webhook
// in Paypal's developer backend where you have created the webhook
$data =
$headers['Paypal-Transmission-Id'] . '|' .
$headers['Paypal-Transmission-Time'] . '|' .
'<WEBHOOK ID FROM THE DEVELOPER DASHBOARD>' . '|' . crc32( $body );
// load certificate and extract public key
$pubKey = openssl_pkey_get_public( file_get_contents( $headers['Paypal-Cert-Url'] ) );
$key = openssl_pkey_get_details( $pubKey )['key'];
// verify data against provided signature
$result = openssl_verify(
$data,
base64_decode( $headers['Paypal-Transmission-Sig'] ),
$key, 'sha256WithRSAEncryption'
);
if ( $result == 1 ) {
// webhook notification is verified
} elseif ( $result == 0 ) {
// webhook notification is NOT verified
} else {
// there was an error verifying this
};
The transmission id, the transmission date, the webhook id and a CRC over the HTTP body. The first two can be found in the header of the request, the webhook id in the developer backend (of course, that id will never change), the CRC is calculated like shown below.
The certificate's location is in the header, too, so we load it and extract the private key.
Last thing to watch out for: The name of the algorithm provided by Paypal (again in a header field) is not exactly the same as understood by PHP. Paypal calls it "sha256WithRSA" but openssl_verify will expect "sha256WithRSAEncryption".

Acessing auth user attribute

I am in the guzzle controller making a request to an external api.
I wanna use an id from the user who is logged in.
I have been doing the request with a static id, but now i want it dynamically.
I tried like this:
$science = Auth::user()->science_id;
$client = new Client(['headers' => ['Accept' => 'application/json']]);
$request = $client->get(
'https://url_to_the_api/'.$science.'/degree',
[
'auth' => ['client', 'secret'],
]
);
$data = $request->getBody()->getContents();
return $data;
And i have the error
500(internal server error)
and this message:
"Trying to get property 'science_id' of non-object"
What am i missing?
Thanks for your time
If you are using it in web app then make sure you first check if user is already authenticated by using auth middleware or manually by using Auth::check() function.
Or
If you are trying to hit this by api that will not work here because session will not be maintained in that case. That's why JWT tokens were introduced to maintain the state of an application.
I've solved it like this:
$science = auth('api')->user()->science_id;
Thanks for the help!

Laravel HTTP tests and request attributes

On my app I add attributes to the HTTP request so I can use it later. My app is a multi domain app (.co.uk, .dk, .de). I findout the domain in the RouteServiceProvider and add the detected language to the HTTP request so I can load the data according to the language and some other things.
I findout and add the website directly in the RouteServiceProvider:
$website = Website::where('domain', '=', request()->getHttpHost())->first();
request()->attributes->add(['website' => $website]);
Then in my controller or anywere else I just have to query the request
if (!$request->attributes->has('website')) {
\Log::error('Abort HTTP request: invalid website: ' . request()->getHttpHost());
abort('500');
}
$language = $request->attributes->get('website')->language();
When testing my app the code execute normally (website is found in the RouteServiceProvider) but then it break in the controller:
testing.ERROR: Abort HTTP request: invalid website
When looking at the attribute, the data are empty in controller but not in the RouteServiceProvider:
dump($request->attributes); // in RouteServiceProvider.php
Symfony\Component\HttpFoundation\ParameterBag {
#parameters: array:1 [
"website" => ...
dump($request->attributes); // in controller
Symfony\Component\HttpFoundation\ParameterBag {
#parameters: []
}
It looks like the request object in the controller is no longer the same. When dumping :
dump(['RouteServiceProvider' => request()]);
I get:
"RouteServiceProvider" => Illuminate\Http\Request {#385
And in controller:
dump(['Controller' => request()]);
"Controller" => Illuminate\Http\Request {#9379
How can I fix this?
First of all, I agree that this code should be in a middleware instead of the RouteServiceProvider.
If the language is you only concern here, I would suggest to just use app()->setLocale() instead of saving the website in your request. If you need other informations contained in the website object, I would suggest to store it in the session instead of the request because I think that this kind of information is more under the responsability of the session than the request, which is more designed to handle inputs, http verbs, headers, ...
This could solve your problem, if it is not the case, let us see more of your code and of the new dd() results

How to find referer of redirect to oauth client's callback

I am having trouble setting up a generic oauth client (and can't find good material on google).
I have this as my route to receive the callback from the oauth process:
Route::get('/oauth/callback', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('https://www.wunderlist.com/oauth/access_token', [
'client_id' => 'xxxxxxxxxxxxxxx',
'client_secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'code' => $request->code
]);
});
but in order to make it generic, I must be able to identify where the redirect came from.
something in the lines of
$service = App\Service::where(<field>, $request-><information about the referer>);
does Request contain any kind of information that can help me identify the source of the redirect? I looked at the object with dd() and couldn't find anything
You should use request()->headers->get('referer') to check the referrer url.
I managed to work around the problem by defining the callback url so that it contains a query parameter identifying the service.
This means that I tell the service to callback to /oauth/callback?service=XXX
and I find it in my services table like this:
$service = Service::where('slug', Input::get('service'))->firstOrFail();

Laravel 5.0 custom 404 does not use middleware

I'm using a middleware to parse the output of the templates. This is working fine for all pages.
However when I want to show a 404 (got a custom page for that) it doesn't treat it as a http request (that's what I think) since it doesn't go through the middleware.
My question is, how to have ALL requests go through the middleware.
The error pages don't go through the routes.php.
In Kernel.php move your middleware from the $routeMiddleware array to $middleware array.
Middleware in this array will run on every request (tested in 5.1).
For people like me who spending hours in 2020 because of this weird behaviour...
Now in Laravel there is a new instrument «Fallback Routes».
Add this to /routes/web.php:
Route::fallback(function () {
return view("404"); // template should exists
});
After that all requests will go throught middlewares.
At Laravel 5.4 and probably some older ones you can modify the file app/exceptions/Handler.php and the function render like this:
if( is_a( $exception, \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class ) ) {
return redirect()->route( 'error_404' );
}
// ... old code in the function ...
in this way every 404 errors are redirected to certain real route that acts like other routes of site.
You may also submit any data from current request to show a reasonable error at the target.
I had a use case where api routes needs to always return json response.
All routes return json response (laravel checks through $request->expectsJson()) IF user specifically ask for it by sending accept: application/json header.
But many a times user doesn't send the header and they get an html response instead.
As for my use case, all api routes will always send json response we can force the routes to return json, by manually attaching accept: application/json header using a middleware.
App\Http\Middleware\ForceJsonOnAPIs.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Str;
class ForceJsonOnAPIs
{
/**
* Handle an incoming request.
*
* #param Request $request
* #param Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
// Force Json accept type on api routes
if ($request->is('api/*') && !Str::contains($request->header('accept'), ['/json', '+json'])) {
$request->headers->set('accept', 'application/json,' . $request->header('accept'));
}
return $next($request);
}
}
Register the middleware in App\Http\Kernel.php
// add the new middleware ForceJsonOnAPIs
protected $middleware = [
\App\Http\Middleware\ForceJsonOnAPIs::class,
// rest of the middleware,
];
Important: You can assign the middle to $middlewareGroups in Kernel like web or api, But you will get into trouble when 404 exception occurs. The issue is The error pages don't go through the routes.php (Thanks to #Björn Answer above) thus the routes middleware won't get called and the 404 will return html response.
It's the same case for validation or authentication exceptions.
In my opinion, it's best to assign the middleware in the $middleware array as it runs on each request. This way all exceptions will automatically return correct exceptions as json in all routes.

Resources