Skip to content

Webhook failed with status code 405 (Got HTTP/1.1 405 Method Not Allowed instead of 20x) #48

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
starloungeswitzerland opened this issue Jun 29, 2018 · 30 comments
Labels
help wanted v1 Related to Laravel-Mollie v1

Comments

@starloungeswitzerland
Copy link

Hi,
I'm using larval-mollie.
no problem to generate payment url and make payment with Mollie web form, but my web hook still return 405 error.

Routes/web.php
Route::post('pay/status', ['as' => 'pay.status', 'uses' => 'PaymentController@statusPayment']);

  • the controller is not protected by auth middleware;
  • pay/status is the web hook url;
  • in Http/Middleware/VerifyCsrfToken I have insert:
    protected $except = [ 'pay/status' ];

But in Mollie transaction detail I have this error:
Webhook failed with status code 405 (Got HTTP/1.1 405 Method Not Allowed instead of 20x)

Any ideas? Thanks!

@sandervanhooft
Copy link
Collaborator

Hi @starlounge ,

What’s in the application logs in storage/logs?

@sandervanhooft sandervanhooft added help wanted v1 Related to Laravel-Mollie v1 labels Jun 29, 2018
@starloungeswitzerland
Copy link
Author

Hi @sandervanhooft
nothing reported to log.
In my web hook function I have:
`public function statusPayment(Request $request)
{

	if ($request->has('id')) {
		
		$payment_ID = $request->id;
		Log::info('[MC][mollie] Post: '.$payment_ID);

	}
	else {
		Log::info('[MC][mollie] Error: post is null');
	}		

}
`

@sandervanhooft
Copy link
Collaborator

The 405 error message suggests the problem occurs at routing level, so the controller is not even invoked.

A few things you could check:

  • is the route in the ‘web’ group? (Not in api route group for example)
  • is it using any other middleware which could cause this issue? (Move the route to the root of the routes/web.php file (Laravel 5.4 and above)).

@sandervanhooft
Copy link
Collaborator

For debugging it may help to make the post calls using cURL or Postman.

@starloungeswitzerland
Copy link
Author

starloungeswitzerland commented Jun 29, 2018

yes,
the route is into web group (web.php)
I have tried to moving at root of web.php but nothing change.

I use this to create payment:

$orderID = 'OP-'.Carbon::now()->timestamp;
$payment = Mollie::api()->payments()->create([
		    "amount"      => 30.00,
		    "description" => "Pagamento ordine ".$orderID,
		    "redirectUrl" => "https://mining.starlounge-switzerland.ch/pay/end/$orderID",
			"webhookUrl" => "https://mining.starlounge-switzerland.ch/pay/status/",
			"metadata"     => array(
				"order_id" => $orderID,
			),
		]);

@starloungeswitzerland
Copy link
Author

starloungeswitzerland commented Jun 29, 2018

Another note: if I change route verb from post to any I get 403 error...

@sandervanhooft
Copy link
Collaborator

Are you initializing / interacting with middleware in the PaymentController's _construct method?

@sandervanhooft
Copy link
Collaborator

Also check if you have any folder in your /public directory which is named pay or pay/status.

@starloungeswitzerland
Copy link
Author

no interaction with middleware in PaymentController
in /public directory only css and images as usual...

@sandervanhooft
Copy link
Collaborator

You can reach / visit other routes?
It seems to be a routing problem imho.

@starloungeswitzerland
Copy link
Author

Web app work well, no problem with other routes :-(

@sandervanhooft
Copy link
Collaborator

What happens if you try this at the top of routes/web.php?

Route::post('pay/status', function () {
    Log::info(request());
});

@starloungeswitzerland
Copy link
Author

Nothing...

Webhook failed with status code 405 (Got HTTP/1.1 405 Method Not Allowed instead of 20x) June 29, 2018 at 3:53 PM

@sandervanhooft
Copy link
Collaborator

sandervanhooft commented Jun 29, 2018

And with a dummy url?
Perhaps also run php artisan route:clear

@starloungeswitzerland
Copy link
Author

screenshot

@sandervanhooft
Copy link
Collaborator

I am not sure what's causing this. It's not caused by the Laravel-Mollie package.

My last attempt would be to disable all web middleware in the App/Http/Kernel, check if it works this way. If it does, re-enable one by one to find the culprit.

@sandervanhooft
Copy link
Collaborator

Feel free to paste the routes/web.php here if you want me to look at it.

@starloungeswitzerland
Copy link
Author

Route::post('pay/status', function () {
    Log::info(request());
});

// Route::post('pay/status', ['as' => 'pay.status', 'uses' => 'PaymentController@statusPayment']);
Route::get('pay/start', ['as' => 'pay.start', 'uses' => 'PaymentController@startPayment']);
Route::get('pay/end/{order}', ['as' => 'pay.end', 'uses' => 'PaymentController@endPayment']);


Auth::routes();

Route::get('lang/{lang}', ['as'=>'lang.switch', 'uses'=>'LanguageController@switchLang']);

Route::get('/', 'DashboardController@index')->name('home');
Route::get('/home', 'DashboardController@index')->name('home');
Route::get('/dashboard', 'DashboardController@index')->name('dashboard');

Route::get('user/personalpermissions/{id}', 'UserController@personalPermissions')->name('users.personalpermissions');
Route::put('user/syncpermissions/{id}', 'UserController@syncPermissions')->name('users.syncpermissions');

Route::get('users/changestate/{id}/{state}', ['as' => 'users.changestate', 'uses' => 'UserController@changeState']);
Route::get('users/profile', ['as' => 'users.profile', 'uses' => 'UserController@editProfile']);
Route::get('users/payments/{id?}', ['as' => 'users.payments', 'uses' => 'UserController@payments']);
Route::resource('users', 'UserController');

Route::resource('roles', 'RoleController');

Route::resource('permissions', 'PermissionController');

Route::get('wallets/create/{user_id}', array('as' => 'createwallet', 'uses' => 'WalletController@create'));
Route::get('wallets/changestate/{id}/{state}/{user}', ['as' => 'wallets.changestate', 'uses' => 'WalletController@changeState']);
Route::resource('wallets', 'WalletController');

Route::get('contracts/changestate/{id}/{state}', ['as' => 'contracts.changestate', 'uses' => 'MiningContractController@changeState']);
Route::get('contracts/transactiondetail/{id}', ['as' => 'contracts.transactiondetail', 'uses' => 'MiningContractController@transactionDetail']);
Route::get('contracts/payments/{id}', ['as' => 'contracts.payments', 'uses' => 'MiningContractController@payments']);
Route::resource('contracts', 'MiningContractController');

Route::get('plans/changestate/{id}/{state}', ['as' => 'plans.changestate', 'uses' => 'MiningPlanController@changeState']);
Route::post('plans/buy/{id}/{user}', ['as' => 'plans.buy', 'uses' => 'MiningPlanController@buyPlan']);
Route::resource('plans', 'MiningPlanController');

Route::get('coinvalue/suspensions', ['as' => 'coinvalue.suspensions', 'uses' => 'CoinValueController@suspensions']);
Route::post('coinvalue/accounting', ['as' => 'coinvalue.accounting', 'uses' => 'CoinValueController@accounting']);
Route::get('coinvalue/suspensionsbymonth', ['as' => 'coinvalue.suspensionsbymonth', 'uses' => 'CoinValueController@suspensionsbymonth']);
Route::resource('coinvalue', 'CoinValueController', ['only' => ['index', 'create', 'store', 'show', 'edit', 'update']]);
Route::delete('coinvalue', ['as'=>'coinvalue.destroy', 'uses'=>'CoinValueController@destroy']);

Route::post('contracts/savefile', ['as' => 'contracts.savefile', 'uses' => 'ContractFileController@saveContractFile']);
Route::get('contracts/downloadfile/{id}', ['as' => 'contracts.downloadfile', 'uses' => 'ContractFileController@downloadContractFile']);
Route::get('contracts/deletefile/{id}/{contractId}', ['as' => 'contracts.deletefile', 'uses' => 'ContractFileController@deleteContractFile']);

Route::resource('accounting', 'ContractAccountingController', ['only' => ['index', 'update']]);
Route::get('accounting/create/{period}', ['as' => 'accounting.create', 'uses' => 'ContractAccountingController@create']);
Route::get('accounting/singlecreate/{period}/{contract}', ['as' => 'accounting.singlecreate', 'uses' => 'ContractAccountingController@singlecreate']);
Route::get('accounting/{id}/{reference}/{return}/edit', array('as' => 'accounting.edit', 'uses' => 'ContractAccountingController@edit'));
Route::get('accounting/{id}/{return}/destroy', array('as' => 'accounting.destroy', 'uses' => 'ContractAccountingController@destroy'));
Route::get('accounting/notify/{period}', ['as' => 'accounting.notify', 'uses' => 'ContractAccountingController@notify']);
Route::get('accounting/notifytest/{period}', ['as' => 'accounting.notifytest', 'uses' => 'ContractAccountingController@notifytest']);
Route::get('accounting/manual', ['as' => 'accounting.manual', 'uses' => 'ContractAccountingController@manual']);
Route::get('accounting/manualcreate/{period}', ['as' => 'accounting.manualcreate', 'uses' => 'ContractAccountingController@manualcreate']);
Route::post('accounting/manualupdate', array('as' => 'accounting.manualupdate', 'uses' => 'ContractAccountingController@manualupdate'));
Route::get('accounting/singlemanualcreate/{period}/{contract}', ['as' => 'accounting.singlemanualcreate', 'uses' => 'ContractAccountingController@singlemanualcreate']);

Route::get('workers/statistics', ['as' => 'workers.statistics', 'uses' => 'WorkersController@statistics']);
Route::resource('workers', 'WorkersController');


/*
 * Rest routes 	
*/

Route::get('rest/coindata/{coin}', ['as' => 'rest.coindata', 'uses' => 'RestController@coinData']);
Route::get('rest/coinvalue/{coin}', ['as' => 'rest.coinvalue', 'uses' => 'RestController@coinValue']);

/* 
 * Testing route . remove for production	 
*/

Route::get("/emailtest", ['middleware' => ['auth', 'isAdmin'], function() {
   Mail::raw('Messaggio inviato da MiningCentral con Mailgun', function($message)
	{
		$message->subject('Messaggio da MiningCentral con Mailgun');
		$message->from('noreply@starlounge-switzerland.ch', 'Starlounge Switzerland GmbH');
		$message->to('poletto.gioacchino@gmail.com');
	});
}]);

@starloungeswitzerland
Copy link
Author

Tip:
/pay/start and /pay/end work very well

@sandervanhooft
Copy link
Collaborator

Try removing the / at the end in the webhookUrl here:

$orderID = 'OP-'.Carbon::now()->timestamp;
$payment = Mollie::api()->payments()->create([
		    "amount"      => 30.00,
		    "description" => "Pagamento ordine ".$orderID,
		    "redirectUrl" => "https://mining.starlounge-switzerland.ch/pay/end/$orderID",
			"webhookUrl" => "https://mining.starlounge-switzerland.ch/pay/status/",
			"metadata"     => array(
				"order_id" => $orderID,
			),
		]);

@sandervanhooft
Copy link
Collaborator

I think that should do the trick

@sandervanhooft
Copy link
Collaborator

"webhookUrl" => "https://mining.starlounge-switzerland.ch/pay/status/"

Should be:

"webhookUrl" => "https://mining.starlounge-switzerland.ch/pay/status"

@starloungeswitzerland
Copy link
Author

Yeah!!!!!
It works!
Many thanks man!

@sandervanhooft
Copy link
Collaborator

@starlounge No problem, happy it works now! :)

@willemstuursma
Copy link
Contributor

willemstuursma commented Jul 2, 2018

@sandervanhooft can we cover this with more examples?

The way the webhook is constructed doesn't seem the right Laravel way to me.

@sandervanhooft
Copy link
Collaborator

@willemstuursma I'll open a new issue for this.

@TVBZ
Copy link

TVBZ commented Feb 12, 2023

Hi Sander. I'm having the same problem, Webhook failed with status code 405 (Method Not Allowed), but I have no trailing slashes in url. Can you have a look please?

My web router file:

<?php

use App\Http\Controllers\PaymentController;
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;

Route::domain(env('APP_URL'))->group(function () {

    Route::get('/', function () {
        return view('site.home');
    })->name('home');

    /******************/
    /*** INVOICES ***/
    /******************/

    Route::prefix('/invoice')
        ->name('invoice.')
        ->group(function () {

            Route::get('/{uuid}/processing-payment', function ($uuid) {
                return 'payment success!';
            })->name('processing-payment');

            Route::get('/{uuid}/prepare-payment', [PaymentController::class, 'preparePayment'])
                ->name('pay');
        });

    /********************/
    /*** WEBHOOKS ***/
    /********************/

    Route::prefix('/webhooks')
        ->name('webhooks.')
        ->group(function () {

            Route::get('/mollie', [PaymentController::class, 'handleWebhookNotification'])
                ->name('mollie');
        });
});

My controller:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
// use Mollie\Laravel\Facades\Mollie;
use App\Models\Payment;
use App\Models\Invoice;

class PaymentController extends Controller
{
    public function preparePayment($invoice_uuid)
    {
        $invoice = Invoice::where('uuid', $invoice_uuid)->first();
        if (!$invoice) {
            abort(403);
        }

        $payment = Mollie::api()->payments->create([
            "amount" => [
                "currency" => "EUR",
                "value" => str_replace(',', '.', $invoice->total_incl),
            ],
            "description" => "Factuur " . $invoice->number_formatted,
            "redirectUrl" => route('invoice.processing-payment', ['uuid' => $invoice->uuid]),
            "webhookUrl" => route('webhooks.mollie'),
            "metadata" => [
                "order_id" => $invoice->uuid,
            ],
        ]);

        // redirect customer to Mollie checkout page
        return redirect($payment->getCheckoutUrl(), 303);
    }

    public function handleWebhookNotification(Request $request)
    {
        // dd($request->all()); 

        $paymentId = $request->input('id');
        $mollie_payment = Mollie::api()->payments->get($paymentId);

        if ($mollie_payment->isPaid()) {

            Payment::create([
                'payment_id' => $mollie_payment->id,
                'status' => $mollie_payment->status,
                'mode' => $mollie_payment->mode,
                'order_id' => $mollie_payment->order_id,
            ]);
        }
    }
}

Thank you

@TVBZ
Copy link

TVBZ commented Feb 12, 2023

Because I can see 2 different ways of writing it in you examples, I've tried changing ->payments-> to ->payments()-> in both functions, but that doesn't change anything. The payment is prepared correctly, after payment the customer is returned to success page, but the webhook is not executed because of the error 405, invalid method.

image

The laravel log is also not showing anything usefull. :(

@TVBZ
Copy link

TVBZ commented Feb 12, 2023

Haha.. I was going a get instead of post request. Nevermind! 🤣

@sandervanhooft
Copy link
Collaborator

Glad you figured it out @TVBZ . Enjoy!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted v1 Related to Laravel-Mollie v1
Projects
None yet
Development

No branches or pull requests

4 participants