Ive been running into issues with Laravel Cashier when i deployed my app to heroku.
One my local environment everything is fine but on my staging server , no POST request body is ever sent to stripe.
I tried swapping api keys as i thought maybe the api version on stripe differs between the two but that doesn't work (see screenshots below)
Things i know are correct
API creds , they wont show up on stripe logs if it wasent
Composer version matches both environments (Laravel Cashier 10.5.2, Laravel 5.8.36, Stripe-php 17.7.0)
I cant seem to find anything that logs out going api requests. Ive even tried manually calling the stripe functions as low as i can get in the stack still no POST body.
Im sure some one else has ran into this. Google search on laravel cashier ALWAYS sends me back to the laravel website, like WTF.
this is my stripe method on my User model. All other code is from Cashier
public function activateSubscription() {
if ($this->hasStripeId() &&
$this->has_default_payment_method &&
$this->has_active_subscription) {
return;
}
try {
$this->newSubscription(env('STRIPE_SUBSCRIPTION_NAME'), env('STRIPE_PLAN_ID'))
->create(null, [
'name' => $this->fullname,
'email' => $this->email,
]);
$this->notify(new UserRegistered());
} catch (\Stripe\Exception\InvalidRequestException $e) {
Log::debug('Invalid Request', [
'body' => $e->getHttpBody(),
'headers' => $e->getHttpHeaders(),
'json' => $e->getJsonBody(),
'error_code' => $e->getStripeCode(),
]);
}
}
Edit
Ive removed some personal details from the POST request body
I figured it out , i had a \n at the end of my stripe secret api key on heroku environment variables.
For some reason that caused all requests to stripe to strip the POST body.
Removed that, ran a php artisan config:clear and it worked
Related
I am using a rest api to store/retrieve my data which is stored in a postgres database. The api is not laravel, its an external service!
Now i want to create a website with laravel (framework version 7.3.0) and i'm stuck on how to implement the api calls correctly.
For example: i want to have a custom user provider with which users can log-in on the website. But the validation of the provided credentials is done by the api not by laravel.
How do i do that?
Just make a Registration controller and a Login Controller by "php artisan make:controller ControllerName" and write Authentication logics there.
In previous versions of Laravel you had a command like "php artisan make:auth" that will make everything needed to do these operations. But in Laravel 7.0 you need to install a package called laravel/ui.
Run "composer required laravel/ui" to install that package
Then run "php artisan ui bootstrap --auth"
and now, you are able to run "php artisan make:auth"
This command will make whole Registration (Signup) and Login system for you.
and in orer to work with REST, you may need to know REST (Http) verbs. Learn about GET, POST, PUT, PATH, DELETE requests and how to make those request with PHP and Laravel collection methods. Learn about JSON parsing, encoding, and decoding. Then you can work with REST easily. and work without any template codes from other packages.
Thank you so much. I hope this answer give you some new information/thought. Thanks again.
Edit:
This might not be the best way. But this is what I did at that time. I tried curl and guzzle to build the request with session cookie and everything in the header to make it look like a request from a web browser. Couldn't make it work.
I used the web socket's channel id for the browser I want the changes to happen and concatenated it with the other things, then encrypted it with encrypt($string). After that, I used the encrypted string to generate a QR code.
Mobile app (which was already logged in as an authenticated used) scanned it and made a post request with that QR string and other data. Passport took care of the authentication part of this request. After decrypting the QR string I had the web socket's channel id.
Then I broadcasted in that channel with proper event and data. Caught that broadcast in the browser and reloaded that page with JavaScript.
/*... processing other data ...*/
$broadcastService = new BroadcastService();
$broadcastService->trigger($channelId, $eventName, encrypt($$data));
/*... returned response to the mobile app...*/
My BroadcastService :
namespace App\Services;
use Illuminate\Support\Facades\Log;
use Pusher\Pusher;
use Pusher\PusherException;
class BroadcastService {
public $broadcast = null;
public function __construct() {
$config = config('broadcasting.connections.pusher');
try {
$this->broadcast = new Pusher($config['key'], $config['secret'], $config['app_id'], $config['options']);
} catch (PusherException $e) {
Log::info($e->getMessage());
}
}
public function trigger($channel, $event, $data) {
$this->broadcast->trigger($channel, $event, $data);
}
}
In my view :
<script src="{{asset('assets/js/pusher.js')}}"></script>
<script src="{{asset('assets/js/app.js')}}" ></script>
<script>
<?php
use Illuminate\Support\Facades\Cookie;
$channel = 'Channel id';
?>
Echo.channel('{{$channel}}')
.listen('.myEvent' , data => {
// processing data
window.location.reload();
});
</script>
I used Laravel Echo for this.
Again this is not the best way to do it. This is something that just worked for me for that particular feature.
There may be a lot of better ways to do it. If someone knows a better approach, please let me know.
As of my understanding, you are want to implement user creation and authentication over REST. And then retrieve data from the database. Correct me if I'm wrong.
And I'm guessing you already know how to communicate over API using token. You are just stuck with how to implement it with laravel.
You can use Laravel Passport for the authentication part. It has really good documentation.
Also, make use of this medium article. It will help you to go over the step by step process.
I am using this tutorial to integrate Stripe into my Laravel site using Cashier:
https://appdividend.com/2018/12/05/laravel-stripe-payment-gateway-integration-tutorial-with-example/
This tutorial was written for Cashier 9, so it does not work out of the box with Cashier 10. However, it does work making the adjustments in this SO answer: https://stackoverflow.com/a/57812759/2002457
Except, it only works for existing Stripe customers. When I register a brand new user and try to view a plan, it gives this error: User is not a Stripe customer. See the createAsStripeCustomer method.
So, I try to do just that:
public function show(Plan $plan, Request $request)
{
if($request->user()->stripe_id === null)
{
$request->user()->createAsStripeCustomer();
}
$paymentMethods = $request->user()->paymentMethods();
$intent = $request->user()->createSetupIntent();
return view('plans.show', compact('plan', 'intent'));
}
Which yields this error: No API key provided. (HINT: set your API key using "Stripe::setApiKey(<API-KEY>)". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support#stripe.com if you have any questions.
This SO answer addresses this problem: https://stackoverflow.com/a/34508056/2002457
But the solution only works in Cashier 9, because Billable changed, so it's not clear how to set the API key.
What am I doing wrong here to create a new customer if they're not a Stripe customer already?
EDIT
- I am using the default cashier config, and I've confirmed it is pointing at the .env vars.
I put in a dd(config('cashier.key')); to confirm that config is working
I removed the old services.php config parts
The env vars are set correctly
Here's the show method:
public function show(Plan $plan, Request $request)
{
$paymentMethods = $request->user()->paymentMethods();
$intent = $request->user()->createSetupIntent();
return view('plans.show', compact('plan', 'intent'));
}
And here's the error now: User is not a Stripe customer. See the createAsStripeCustomer method.
Cashier 10 introduced some changes to the configuration including setting up the cashier.php configuration file. The upgrade guide details how, this pull request commit shows the file.
Few things to debug this:
make sure you've setup the config for cashier 10 correctly.
make sure that the config key cashier.key is available (e.g. ddd(config('cashier.key'));
double check that that your .env var's are setup correctly for stripe's API key
I am using the Microsoft Graph and I need to set up a webhook to receive changes to email and calendar events. I was able to get it working with my PHP Laravel application, but now that I am trying to subscribe to notifications, I am running into issues with validating the notificationUrl, which is pointing to a public server of mine.
The script for creating the webhook is returning the following error:
Client error: POST https://graph.microsoft.com/v1.0/subscriptions resulted in a 400 Bad Request response:
{
"error": {
"code": "InvalidRequest",
"message": "Subscription validation request failed. Response must ex (truncated...)
The truncated part I believe is
Subscription validation request failed. Must respond with 200 OK to this request.
Here is my code for creating the subscription:
$data = [
"changeType" => "created",
"notificationUrl" => "https://anatbanielmethod.successengine.net/office365/webhooks/events",
"resource" => "me/events",
"expirationDateTime" => "2018-12-20T18:23:45.9356913Z",
"clientState" => "secret",
];
$result = $graph->createRequest('POST', '/subscriptions')
->attachBody($data)
->execute();
and here is my method for my notificationUrl:
public function events()
{
//if validationToken exists return that to validate notificationUrl
if(isset($_REQUEST['validationToken'])){
return response($_REQUEST['validationToken'], 200)
->header('Content-Type', 'text/plain');
}
//process event normally for those that have already been validated
}
Once again this URL is public and live and I have tested it by using Postman to send it test posts and it is working fine. Also, I added this route to my VerifyCsrfToken middleware to allow a third party post to hit this URL.
Originally I set up a simple single page PHP script to test validating the notificationUrl and that simple script worked fine. It successfully validates Webhooks created that point to it. Here is that one page script code:
<?php
if(isset($_REQUEST['validationToken'])){
echo $_REQUEST['validationToken']; // needed only once when subscribing
} else {
//process like normal not a validation Token request...
}
}
So I would expect that the Laravel endpoint would work like the simple one page PHP script, and it is when I test both URLs in Postman, but the Laravel endpoint is not validating when Office365 attempts to validate it when creating a new webhook.
I have searched all over for help on this and read through all of the Microsoft developer documentation I can find on webhooks and these are some of the more helpful parts of the documentation but I am still not finding an answer to this issue:
https://learn.microsoft.com/en-us/graph/api/subscription-post-subscriptions?view=graph-rest-1.0
https://learn.microsoft.com/en-us/graph/webhooks#notification-endpoint-validation
Any ideas of this?
Thanks Marc! You were correct about the linefeed being the issue, I am still not sure where the line feed is coming from, some how Laravel appears to be adding it. Needless to say I found a solution by adding an "ob_clean();" right before returning the response. Below is my updated notificationUrl method:
public function events()
{
//if validationToken exists return that to validate notificationUrl
if(isset($_REQUEST['validationToken'])){
ob_clean();//this line is cleaning out that previously added linefeed
return response($_REQUEST['validationToken'], 200)
->header('Content-Type', 'text/plain');
}
//process event normally for those that have already been validated
}
It's odd that JakeD's answer requires the use of ob_clean(). here is my webhook controller method in my Laravel 5.7.x app:
use Illuminate\Http\Request;
public function webhook (Request $request) {
if (filled($request->input('validationToken'))) {
return response($request->input('validationToken'))
->header('Content-Type', 'text/plain');
}
// code to process the webhook after validation is complete
}
I don't see an extra linefeed character and the Microsoft Graph API subscription is validated and created.
I am trying to implement sharing to LinkedIn in our project. It works for me like a charm, however, for all of my colleagues, it does not. It returns 401: Unknown authentication scheme just for sharing request (profile requests work fine).
The LinkedIn app that we use is owned by me. For testing purposes another developer tried to create an app in his account, but the problem stayed: I was the only one who could share.
We are using linkedinapi/linkedin for back-end. Here's the code:
protected function openConnection()
{
$credentials = \Config::get('services.linkedin');
try
{
$this->connection = new \LinkedIn\LinkedIn([
'api_key' => $credentials['client_id'],
'api_secret' => $credentials['client_secret'],
'callback_url' => $credentials['redirect'],
]);
$this->connection->setAccessToken(\Auth::user()->linkedin->token['access_token']);
}
catch (\Exception $e)
{
\Log::error('Failed LinkedIn auth, exception is attached', [$e]);
throw new CouldNotPostException('Please, re-link your LinkedIn account, failed to send your post to LinkedIn.');
}
}
protected function send()
{
$object = [
'content' => [
'title' => $this->post->title,
'description' => \Str::limit($this->post->description, 253),
'submitted-url' => $this->post->shortlink,
'submitted_image_url' => \URL::to($this->post->image),
],
'comment' => $this->content,
'visibility' => [
'code' => 'connections-only'
]
];
try
{
$result = $this->connection->post('/people/~/shares?format=json', $object);
$this->posted($result['updateKey']);
}
catch (\Exception $e)
{
\Log::error('Failed LinkedIn posting, exception is attached', [$e]);
throw new CouldNotPostException('Failed to send your post to LinkedIn, our developers have been notified of this error.');
}
}
UPD: turns out it can post only on my machine, so there is something wrong with the configuration on their machines. Looking for it.
Investigation
I asked my colleague to login to LinkedIn on my machine, to see if posting fails for his account or for his computer. It posted, which showed that something is up with my configuration, that is why it works.
My next step was to re-install all of the vendor packages, including the mentioned above linkedinapi/linkedin. After that posting stopped working for me as well. After inspecting the way that library signs requests, I found that it does not match the way LinkedIn requires it. The library was including a oauth2_access_token parameter in the URL, but LinkedIn expects an Authorization HTTP header as it is stated in their documentation:
Once you've obtained an Access Token, you can start making authenticated API requests on behalf of the user. This is accomplished by including an "Authorization" header in your HTTP call to LinkedIn's API.
Solution
So for now I changed the way I make the request. Instead of
$result = $this->connection->post('/people/~/shares?format=json', $object);
I use a different function that allows me to include the header manually:
$result = $this->connection->fetch(
'/people/~/shares?format=json',
$object,
\LinkedIn\LinkedIn::HTTP_METHOD_POST,
[ 'Authorization: Bearer ' . $this->connection->getAccessToken() ]
);
Also, I created a pull request to the repository.
I am having trouble setting up my delayed mailing with laravel 5.1
In App\Exceptions\Handler in the render method I want to mail any occuring errors to myself: Like this
Mail::send('emails.servererror', ['exception' => $e, 'request' => $request] ,function($message)
{
...
});
This works perfectly, except that I would like to delay the sending of the email With Mail::later. E.G.
Mail::later(5, 'emails.servererror', ['exception' => $e, 'request' => $request] ,function($message)
{
...
});
The part where I am having trouble is the following:
This method will automatically take care of pushing a job onto the queue to send the mail message in the background. Of course, you will need to configure your queues before using this feature.
I have read the documentation several times. I still can't figure out what precisely I should do to make the Mail::later work.