Problem of environment files with inter-apis calls laravel - php

Good morning, everyone,
As part of the development of demonstration APIs, I realized two APIs :
HelloWorld
Notify
The first one allows to ask for a HelloWorld to be performed, the second one allows to send e-mails according to defined templates.
In my demonstration I make from Postman (or from a React application) an API call to HelloWorld which then makes an API call to Notifier to send the message.
If from Postman I call directly my Notifier API to send an email, I do not encounter any problem (the .env file is well configured for sending emails in this API).
On the other hand if I call my API from HelloWorld to Notifier (the .env file of HelloWorld is not configured for sending e-mails), I encounter an error:
Expected response code 250 but got code "530", with message "530 5.7.1
Authentication required
On the other hand if I configure the .env file of the HelloWorld API (which does not send an e-mail at any time), I do not have any more error and my e-mail is well sent by Notifier.
This is the API Call in HelloWorld :
$client = new Client();
$response = $client->post("http://vhost-url.local/api/notifier/sendmail", [
'json' => [
'to' => $to,
'template' => $template,
'parameters' => $parameters
],
]);
And this is the action called in Notifier API :
public function sendmail(Request $request)
{
$params = json_decode($request->getContent(), true);
try{
switch ($params['template']) {
case 'HELLO_WORLD':
Mail::to($params['to'])
->send(new HelloWorld([
'message' => $params['parameters']['message']
]));
break;
default:
throw new \Exception("Ce template n'existe pas");
break;
}
} catch(\Exception $e) {
return response()
->json([
'message' => $e->getMessage(),
], 500);
}
return response()
->json([
'message' => 'Le mail a bien été envoyé'
], 200);
}
My question is: During an API call (with Guzzle in my case), is the environment file of the source API used instead of the environment file of the destination API? And if so, how to fix this problem?

I'm not sure if this helps but I have had similar problems. The .env files get messed up when cross-communicating Laravel projects (on Windows only I believe).
See for instance https://github.com/laravel/framework/issues/19454 .
The solution is to run php artisan config:cache to create a cached version of your .env variables. Note that you should never use env('...') in your code, instead you should refer to them using a config file like config('app.env'). .env variables can not be dynamic for this reason.
For custom env variables, I usually create a config/project.php file like so:
return [
'my_custom_var' => env('PROJECT_MY_CUSTOM_VAR')
];
That way you can cache it and call the variable using config('project.my_custom_var');

Related

Laravel not sending email to environment variable

I am using the following working code in Laravel:
Mail::to('mail#example.com')->send(new \App\Mail\CheckinInfo([
'client' => $client,
]));
when I use an email address from the environment, it stopped working:
Mail::to(env('MAIL_FROM_ADDRESS'))->send(new \App\Mail\CheckinInfo([
'client' => $client,
]));
In .env I got:
MAIL_FROM_ADDRESS=mail#example.com
and I tried
MAIL_FROM_ADDRESS="mail#example.com"
The cache and config are cleared.
The error message is:
[2022-07-29 18:44:26] production.ERROR: An email must have a "To", "Cc", or "Bcc" header. {"exception":"[object] (Symfony\\Component\\Mime\\Exception\\LogicException(code: 0): An email must have a \"To\", \"Cc\", or \"Bcc\" header. at /www/htdocs/app/laravel/vendor/symfony/mime/Message.php:128)
As a note when you do a config:cache the settings will not be grabbed from your .env anymore.
You should create a config/settings.php or similar file and store your env vars there, ie:
config/settings.php
return [
'mail_from_address' => env('MAIL_FROM_ADDRESS'),
]
and you should reference it as so
config('settings.mail_from_address')
No your mistake on the first line, maybe you didn't close the bracket
Try this way
Mail::to(env('MAIL_FROM_ADDRESS'))->send(new \App\Mail\CheckinInfo([
'client' => $client,
])); //Notice I added the closing parenthesis for To()
You are trying to access the functions of the env methods. This is not possible
I hope this is just the problem
You can see the documentation for the framework,
about this queueing-mail laravel documentation

Getting "SignatureDoesNotMatch" when using GuzzleHttp to follow a redirect to a pre-signed S3 URL

I am consuming an API which receives a POST request with some parameters and uses them to generate a file in S3. This API returns a 303 redirect response with Location: set to the signed URL to access the S3 file.
This works file when accessing the API via e.g. Postman however when accessing it via GuzzleHttp (v7.4) it fails with error SignatureDoesNotMatch.
While debugging I have used code:
$client = new Client([
'allow_redirects' => true,
'on_stats' => function (TransferStats $stats) {
var_dump($stats->getEffectiveUri());
}
]);
$client->post('https://api.example.com', [
'json' => [ ... ]
]);
Doing this I have confirmed that the URL is correct and copy/pasting the exact url that is accessed actually works. However using GuzzleHttp it does not work at all.
Update: The API developer has informed me that this issue was also because they were using v2 of the AWS S3 signature. They have now changed it to v4 which makes my code work as is (I think they may have had the same issues from other clients).
The issue turned out to be that when Guzzle follows redirects, it retains most of the original request headers. However the Amazon computed signature also validates the signature against (at least some of) the headers. The offending header was the Content-Type header which was still sent even though the request no longer had any content.
To fix this I created a new Guzzle middleware:
use Psr\Http\Message\RequestInterface;
class RemoveContentType {
public function __invoke(callable $handler) {
return function (RequestInterface $request, array $options) use ($handler) {
if ($request->getHeaderLine('Content-Type')
&& $request->getBody()->getSize() === 0) {
return $handler($request->withoutHeader('Content-Type'), $options);
}
return $handler($request, $options);
};
}
}
this would remove the content type from a request which has an empty body.
I then modified my code to use this middleware:
$stack = HandlerStack::create();
$stack->push(new RemoveContentType ());
$client = new Client([
'handler' => $stack,
'allow_redirects' => true,
]);
$client->post('https://api.example.com', [
'json' => [ ... ]
]);
This finally solved my issue.

Laravel REST API request object is empty

Environment
I created an application using Laravel 5.7 and implemented a REST API. I have a route in routes/api.php that triggers a middleware which checks if the incoming request has a parameter called api_token.
This is a production environment and here are the specifics:
Linux Ubuntu 18.04 LTS 64-bit
nginx/1.14.0
Laravel 5.7
PHP 7.2
APP_ENV in the .env file is set to 'production' and APP_DEBUG is set to 'false'.
Problem
My problem is that the incoming request object is always empty when it arrives at the server. At least that's what my Laravel application says.
These are my routes in routes/api.php:
Route::middleware('rest')->group(function() {
Route::get('device-location/{deviceID}', 'PositionDataController#getDeviceLocation');
Route::get('device-location/all', 'PositionDataController#getAllLocations');
Route::post('device-location', 'PositionDataController#storeDeviceLocation');
Route::put('device-location/{deviceID}', 'PositionDataController#updateDeviceLocation');
Route::delete('device-location/{deviceID}', 'PositionDataController#deleteDeviceLocation');
});
The routes are in a middleware group called 'rest' as you can see. I'm using the Route::get('device-location/{deviceID}', 'PositionDataController#getDeviceLocation'); route to test the functionality.
Here's the code from the middleware:
public function handle($request, Closure $next)
{
if(request()->all()) {
$deviceID = request()->device_id;
}
else {
return response()->json([
'error' => 'The request object is empty.',
'request' => request(),
'parameters' => request()->all(),
'content' => request()->getContent(),
'input' => request()->input()
], 500);
}
$device = MobileDevice::where('device_id', $deviceID)->first();
if($device) {
$deviceToken = $device->api_token;
if($deviceToken == request()->api_token) {
return $next($request);
}
else {
return response()->json(['error' => 'Token does not match.'], 500);
}
}
else {
return response()->json(['error' => 'The device with the id [' . $deviceID . '] could not be found.'], 500);
}
}
The middleware first checks if there are parameters in the request object and then does some logic to check if the right token was sent. If the request object is empty it returns some data to help me understand what went wrong.
I use Postman (https://www.getpostman.com) to test the API. Here's my Postman setup:
Postman setup
Postman headers
This is the response I get in Postman:
Postman response
I get the same result if I call that route in a browser.
Regardless of if I put in parameters or not the request seems to be always empty.
Here are the things that I've tried to do:
Not using the middleware
Using the $request variable instead of the helper request()
Switching between 'application/json' and 'application/x-www-form-urlencoded' in the Headers of my Postman setup
Calling that route in a browser
Updating to Laravel 5.7
The strange thing is that all of this works perfectly on my local environment and on a test server that has the same specs as the production server.
UPDATE 01:
So it seems to be even worse...
If I add a route like this in web.php:
Route::get('/request-return', function() {
return request()->all();
});
and visit that route like this:
laravel.application/request-return?device_id=1&api_token=XgkQLs8G7OYTqdwsXmjJT5G9bxv20DRi
I get back an empty array [].
So it seems like the parameters don't get to the server itself.
You are getting device id through GET request so use the below line instead of $request()->device_id.
Use this and let me know
$name = $request->input('device_id')
Okay I could solve the problem. It didn't have anything to do with Laravel. It was a nginx configuration problem:
https://serverfault.com/questions/231578/nginx-php-fpm-where-are-my-get-params

Unable to generate a Cashier PDF in Laravel

I am using Laravel 5 to generate a PDF from a subscription generated from Cashier. The docs say this is as simple as calling:
return $user->downloadInvoice($invoice->id, [
'vendor' => 'Your Company',
'product' => 'Your Product',
]);
Unfortunately I'm getting an odd error:
No hint path defined for [cashier]
The code I am actually using is as follows:
Route::get('billing/invoices/download/{id}', function($id){
$user = Auth::user();
//$invoice = $user->invoices()->find($id);
return $user->downloadInvoice($id, [
'vendor' => 'Certify Me',
//'product' => $invoice->lines->data[0]['plan']->name,
'product' => 'Subscription',
]);
});
The docs make me assume that the PDF is automatically generated. I'd then assume I could override the PDF layout if I chose to.
I just ran into this (L5.1, Cashier 6.0). This seems to be caused by the service provider not being correctly loaded.
Here is how I fixed it:
Check that you have added the correct service provider, at the time of writing that is Laravel\Cashier\CashierServiceProvider to your config/app.php
If it still doesn't work, go run php artisan config:clear to make sure that the service provider is picked up.
Happy invoicing!
I'm going to resurrect this beast.
I had a similar issue because the service provider was not loaded. If you checkout CashierServiceProvider you'll see it adds the necessary 'namespace' for the 'cashier' prefixed views.
public function boot()
{
$this->loadViewsFrom(__DIR__.'/../../views', 'cashier');
$this->publishes([
__DIR__.'/../../views' => base_path('resources/views/vendor/cashier'),
]);
}
Add Laravel\Cashier\CashierServiceProvider to your config/app.php file and inside the providers key.
For anyone who runs across this like we did.

Can't access local environment of my project

I have a Laravel project that i recently tried to set in my local environment.
The project is REST-API based, and all of the requests are made with API.
When i try to send an API request to my local environment project i get UnauthorizedRequestException.
Since i am not fully familiar with Laravel i couldn't tell where do i set the authentication.
I tried to trace the error but all i get is a bunch of functions that do not help me. or at least that's what i think because non of them shows where i can find the authentication settings.
This is the error i get in the browser when i try to send an API request:
{
success: false,
errors: "Not Authorized",
data: null
}
This is an error that was set up in case of unauthorized access and the code for it is:
App::error(function(UnauthorizedRequestException $exception, $code)
{
$headers = array(
'Access-Control-Allow-Origin' => '*',
);
return Response::json(
array(
'sucess' => false,
'errors' => $exception->getMessage(),
'data' => null,
),
$exception->getCode(),
$headers
);
});
This code is located in app/start/global.php.
If someone knows where i can set the authentication so i can start working on my local environment let me know. Thanks.

Categories