I'm using Laravel 5.4 and Laravel Cashier. I would like to be able to catch Stripe webhooks in my localhost:8888
To do so I installed ultrahook and I started it like this
Ultrahook
http://stripe.leococo.ultrahook.com -> http://localhost:8888/stripe/webhook
Laravel routes
Route::post('stripe/webhook', '\Laravel\Cashier\Http\Controllers\WebhookController#handleWebhook');
Stripe Webhook configuration
http://stripe.leococo.ultrahook.com
Problem
When I send a webhook from Stripe I get Test webhook sent successfully
In the terminal ultrahook I get this
[2017-05-31 19:26:04] POST http://localhost:8888/stripe/webhook - 200
But it seems the handleWebhook function is not triggered. It does not stop on a break point neither die('test')
I tried php artisan route:clear php artisan config:clear. I don't know wether it is normal or not, but I do not see anything in the network section in the Chrome Inspector
Add the following line in your .env
CASHIER_ENV=testing
Laravel/Cashier checks if your call to the webhook has a valid event id.
To verify this, eventExistsOnStripe calls back stripe servers with this event id to check its existence.
Here is the main webhook entry point from Laravel/Cashier 7.0:
public function handleWebhook(Request $request)
{
$payload = json_decode($request->getContent(), true);
if (! $this->isInTestingEnvironment() && ! $this->eventExistsOnStripe($payload['id'])) {
return;
}
$method = 'handle'.studly_case(str_replace('.', '_', $payload['type']));
if (method_exists($this, $method)) {
return $this->{$method}($payload);
} else {
return $this->missingMethod();
}
}
isInTestingEnvironment just check whether we are in a testing environnment or not : Thank you Cpt Obvious :)
protected function isInTestingEnvironment()
{
return getenv('CASHIER_ENV') === 'testing';
}
Related
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 have set up a listener by adding it in EventServiceProvider
protected $subscribe = [
MyListener::class
];
The listener (MyListener) has a subscribe function that subscribes to the events the listener wants to listen for - and it works fine.
Now, I'm trying to add a check to restrict which events should be listened to. Something like
public function subscribe($events)
{
$config = ConfigService::getUserConfig();
if ($config->shouldSubscribe) {
$events->listen(.....);
}
}
I'm having some issues after adding this logic however.
It seems that when running composer install it executes the subscribe method.
This causes an issue, because there is no active session when running composer install - so I'm met with a SQL error - it can't find which database to search for configuration in - followed by this error
Script #php artisan package:discover handling the post-autoload-dump event returned with error code 1
How can I conditionally subscribe to certain events in the listener?
It is not the exact response for your answer, but it should work in your case. You can detect if your code is running from console by using Application::runningInConsole() function.
Example:
public function subscribe($events)
{
// Running from cli script, abort ship!
if(app()->runningInConsole())
{
return;
}
$config = ConfigService::getUserConfig();
if ($config->shouldSubscribe) {
$events->listen(.....);
}
}
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've followed this tutorial
https://tutsforweb.com/restful-api-in-laravel-56-using-jwt-authentication/
and it's working fine,
but when i try to run
php artisan route:list
i get this error ,
Tymon\JWTAuth\Exceptions\JWTException : The token could not be parsed from the request
at F:\My_Projects\Laravel\api-laravel-jwt\vendor\tymon\jwt-auth\src\JWT.php:185
181| */
182| public function parseToken()
183| {
184| if (! $token = $this->parser->parseToken()) {
> 185| throw new JWTException('The token could not be parsed from the request');
186| }
187|
188| return $this->setToken($token);
189| }
Exception trace:
1 Tymon\JWTAuth\JWT::parseToken()
F:\My_Projects\Laravel\api-laravel-jwt\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php:223
2 Illuminate\Support\Facades\Facade::__callStatic("parseToken", [])
F:\My_Projects\Laravel\api-laravel-jwt\app\Http\Controllers\ProductController.php:14
Please use the argument -v to see more details.
and i've been trying to get around it, but couldn't succeed, any help?
This is a github Repo with my code:-
https://github.com/Imohamedgabr/laravel-5.7-jwt-starter
in the product controller, in this part:-
public function __construct()
{
$this->user = JWTAuth::parseToken()->authenticate();
}
it calls the parse token, that's why it goes to the jwt auth parse token and fails there.
so while there's no authenticated user provided, it fails.
hope this helps.
I've tried to using queue for everytime user register and send an email to them to verify.
I'm doing it successfully using Laravel 5.1
I just wandering how can I can stop current queue if I got an error and then when I fix it I restart the job from the last queue?.
How about the error like this:
[InvalidArgumentException]
View [emails.versify_email] not found.
[InvalidArgumentException]
View [emails.versify_email] not found.
[InvalidArgumentException]
View [emails.versify_email] not found.
I've tried at homestead using:
public function failed(){
//I've tried send email but it not sending
}
or at AppServiceProvider
Queue::failing(function ($connection, $job, $data) {
$user ='mymail#gmail.com';
Mail::send('emails.fail_queue', ['user' => $user], function ($m) use ($user) {
$m->subject('Failing:' . $user)
->to($user);
});
});
None of the them is working.
what should i do if its like that when happened at production?.
When an exception is thrown from the handler Laravel tries to release the job back to the queue unless it is explicitly deleted.
https://github.com/laravel/framework/blob/5.1/src/Illuminate/Queue/Worker.php#L198-L227
You can use the $delay parameter to put back in the queue with delay. Or better just bury the job yourself, if you are able to detect the issue.
$this->job->bury()