laravel / cashier-paddle

Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services.
https://laravel.com/docs/cashier-paddle
MIT License
245 stars 57 forks source link

Problem with WebHook Custom handlers #58

Closed Benoit1980 closed 4 years ago

Benoit1980 commented 4 years ago

Hello,

        "php": "^7.4",
        "laravel/cashier-paddle": "^1.0.0-beta.2",
        "laravel/framework": "^7.24.0",

Based on the documentation: https://laravel.com/docs/7.x/cashier-paddle#defining-webhook-event-handlers

I have created a controller at \App\Http\Controllers\WebhookController and added this code in it:

<?php

namespace App\Http\Controllers;

use Laravel\Paddle\Http\Controllers\WebhookController as CashierController;

class WebhookController extends CashierController
{
    /**
     * Handle payment succeeded.
     *
     * @param  array  $payload
     * @return void
     */
    public function handlePaymentSucceeded(array $payload)
    {
        if (Receipt::where('order_id', $payload['order_id'])->count()) {
            return;
        }

        $this->findOrCreateCustomer($payload['passthrough'])->receipts()->create([
            'checkout_id' => $payload['checkout_id'],
            'order_id' => $payload['order_id'],
            'amount' => $payload['sale_gross'],
            'tax' => $payload['payment_tax'],
            'currency' => $payload['currency'],
            'quantity' => (int) $payload['quantity'],
            'receipt_url' => $payload['receipt_url'],
            'paid_at' => Carbon::createFromFormat('Y-m-d H:i:s', $payload['event_time'], 'UTC'),
        ]);
    }
}

I was hoping to be able to use the handlePaymentSucceeded to see if I could recreate a receipt(like the original method from CashierController as I need later on to add a bit more code to it).

I have also added this route in the web.php:

Route::post('paddle/webhook', '\App\Http\Controllers\WebhookController@handleWebhook');

When passing a purchase via:

           $payLink = $user->chargeProduct($product->product_id, [
               'quantity_variable' => 0,
           ]);

Which works perfectly as I receive both a Paypal receipt and Paddle receipt, I receive this error in telescope:

BadMethodCallException
Method App\Http\Controllers\WebhookController::handleWebhook does not exist.

If I remove this controller App\Http\Controllers\WebhookController, the receipt is created correctly in the database(demonstrating that everything works), but the issue only happens when I use the "extends CashierController".

Any idea if there is something missing in your doc there please? Perhaps the constructor needs to be copied aswell?

Thank you,

GrahamCampbell commented 4 years ago

I think the correct thing to add is simply:

Route::post('paddle/webhook', '\App\Http\Controllers\WebhookController');
Benoit1980 commented 4 years ago

I think the correct thing to add is simply:

Route::post('paddle/webhook', '\App\Http\Controllers\WebhookController');

Thank you GrahamCampbell, this error seems to be gone while adding it this way(the doc will need to be updated for this) but strangely enough, I did this:

<?php

namespace App\Http\Controllers;

use Carbon\Carbon;
use Laravel\Paddle\Http\Controllers\WebhookController as CashierController;
use Laravel\Paddle\Receipt;

class WebhookController extends CashierController
{

    /**
     * Handle payment succeeded.
     *
     * @param  array  $payload
     * @return void
     */
    public function handlePaymentSucceeded(array $payload)
    {
       logger($payload);
    }
}

And when overriding the method, I am not getting any Payload, but in telescope, I see the POST request as:

Method POST
Controller Action \App\Http\Controllers\WebhookController
Middleware web
Path /paddle/webhook
Status 200
Duration 144 ms
IP Address 127.0.0.1
Memory usage 4 MB
{
"event_time": "2020-08-16 22:20:25",
"p_country": "UK",
"p_coupon": "discount",
"p_coupon_savings": "0.64",
"p_currency": "EUR",
"p_earnings": "{"115809":"0.0000"}",
"p_order_id": "16961231",
"p_paddle_fee": "0.01",
"p_price": "0.01",
"p_product_id": "110981",
"p_quantity": "1",
"p_sale_gross": "0.01",
"p_tax_amount": "0.00",
"p_used_price_override": "1",
"passthrough": "{"billable_id":20,"billable_type":"App\\User"}",
"quantity": "1",
"p_signature": "secret[but set properly]"
}

I also receive the paddle email and Paypal receipt. I have tested my webhook URL with the test page in Paddle and I get:

With method: POST
Destination Status Code: 200

But as I said above, if I remove this custom controller, the sales happen correctly and the receipt is registered in the database. Something to do with the way I am overriding it perhaps?

Benoit1980 commented 4 years ago

So today I tried to pass a constructor in this class to see if I could log anything and strangely when adding the route as:

Route::post('paddle/webhook', '\App\Http\Controllers\WebhookController');

The above-reported error goes away but I am unable to access a class in the WebhookController, in this case

    public function handlePaymentSucceeded(array $payload)
    {
       logger($payload);
    }
driesvints commented 4 years ago

Route::post('paddle/webhook', '\App\Http\Controllers\WebhookController');

This was indeed the correct piece of code. We've updated the docs. Thanks for reporting.

but I am unable to access a class in the WebhookController, in this case

Your method needs to be protected, not public.

Benoit1980 commented 4 years ago

Route::post('paddle/webhook', '\App\Http\Controllers\WebhookController');

This was indeed the correct piece of code. We're updated the docs. Thanks for reporting.

but I am unable to access a class in the WebhookController, in this case

Your method needs to be protected, not public.

Thank you so much! I will check this tonight and will keep testing your script on a daily basis. Looks really cool, very good work!

Benoit1980 commented 4 years ago

Hello,

Just to let you know:

<?php

namespace App\Http\Controllers;

use Carbon\Carbon;
use Laravel\Paddle\Http\Controllers\WebhookController as CashierController;
use Laravel\Paddle\Http\Middleware\VerifyWebhookSignature;
use Laravel\Paddle\Receipt;

class WebhookController extends CashierController
{
    /**
     * Handle payment succeeded.
     *
     * @param  array  $payload
     * @return void
     */
    protected function handlePaymentSucceeded(array $payload)
    {
       logger('handlePaymentSucceeded' . $payload);
    }
}

Route: Route::post('paddle/webhook', '\App\Http\Controllers\WebhookController');

Still no logger payload showing. Has anyone managed to get the custom methods to work please? Anyone?

Also as you said just before the this post, the method should be protected but in the doc is shows public(should this be updated in your doc?) https://laravel.com/docs/7.x/cashier-paddle#defining-webhook-event-handlers

Something I have noticed:

Using Laravel Telescope I checked the request to the webhook and got this:

{
"event_time": "2020-08-25 21:23:17",
"p_country": "UK",
"p_coupon": "discount",
"p_coupon_savings": "0.64",
"p_currency": "EUR",
"p_earnings": "{"115809":"0.0000"}",
"p_order_id": "17229276",
"p_paddle_fee": "0.01",
"p_price": "0.01",
"p_product_id": "9879879",
"p_quantity": "1",
"p_sale_gross": "0.01",
"p_tax_amount": "0.00",
"p_used_price_override": "1",
"passthrough": "{"billable_id":20,"billable_type":"App\\User"}",
"quantity": "1",
"p_signature": "c4jLkZ6t4QoqZV8RMc4Q5reQy4PyR8+J5tJxXCFYQULZB8OhCIbFeaDW1zzorlB3yukbUt20yOmmEGlruY1enYdlIpPD5LvVFNF53nXf93zV1r5ovcVayUeD+pNvz6XZFztp8+B85lS9mk173PpHYiv393BxmmkPpp9F1czYtaATFi23UygzkUja4hCWNUKiFMruQNN9LndY8leZzkU0kUzYUjEQ+ZyOX8K6ELbSO24aTu9aL6l9/Bo0P/uKKLXo22dcKg/et3UcfpBVS8QU4t4TQzpyrxf60cIg84bqk8="
}

I then ran a fake webhook from Paddle and it works but something is different in the response, I get an extra parameter showing as:

[alert_name] => payment_succeeded

Which obviously is what is wrong with my custom webhook(I do not receive this parameter when I pay manually from my admin panel.

I use this code:

           $payLink = $user->chargeProduct($product->product_id, [
               'quantity_variable' => 0,
           ]);
                            <x-paddle-button url="{{$collection['pay_link']}}" class="float-left px-8 py-4">
                                <i class="fas fa-shopping-cart float-right"> Buy Now</i>
                            </x-paddle-button>

The widget shows properly and the sales happens, I receive all the Paypal receipts via email as well as the paddles receipts but the call back is not receiving [alert_name] => payment_succeeded.

Any idea why could cause this please?

Thank you.

Benoit1980 commented 4 years ago

Ok Forget about everything above,

I have just updated the package to the version 1.0.0 and all the errors are now gone! You can close the ticket and thanks again!d

Tofique-Ahmed commented 2 years ago

can somebody tell me how to get response from paddle while simple charged?