Laravel Cashier Webhook:获取当前用户

9

我正在使用以下方法来覆盖默认的handleCustomerSubscriptionDeleted方法,将其放置在app/Http/Controllers/WebHookController.php中:

<?php namespace App\Http\Controllers;

use Laravel\Cashier\WebhookController;
use Symfony\Component\HttpFoundation\Response;

class StripeWebhooks extends WebhookController {

  /**
   * Handle stripe subscription webhook
   *
   * @param  array  $payload
   * @return Response
   */
  public function handleCustomerSubscriptionDeleted($payload)
  {
    $billable = $this->getBillable($payload['data']['object']['customer']);

    if ($billable && $billable->subscribed()) {
      $billable->subscription()->cancel();

    }

    return new Response('Webhook Handled', 200);
  }

}

为了真正覆盖默认设置,我应该更新我的路由到扩展控制器,还是仍然引用默认设置?我认为保留默认设置甚至不会让Laravel知道我的控制器存在,但也想确认一下。
当前:
Route::post('stripe/webhook', '\Laravel\Cashier\WebhookController@handleWebhook');

另一部分内容涉及到handleCustomerSubscriptionDeleted,我也想能够获取当前用户并执行其他操作;在这种情况下,自动将记录设置为未发布状态。检索用户的最佳方法是什么?看起来$payload['data']['object']['customer']可能会持有并关联到用户表中的stripe_id列,但需要确认。感谢任何帮助!

更新

我认为(根据文档),应该更像这样:
<?php namespace App\Http\Controllers;

use App\Models\Dashboard\Listing;
use App\User;
use Symfony\Component\HttpFoundation\Response;
use Laravel\Cashier\WebhookController as BaseController;

class WebhookController extends BaseController
{
  /**
   * Handle stripe subscription webhook
   *
   * @param  array  $payload
   * @return Response
   */
  public function handleCustomerSubscriptionDeleted($payload)
  {
    $billable = $this->getBillable($payload['data']['object']['customer']);

    if ($billable && $billable->subscribed()) {
      $billable->subscription()->cancel();

      // Get current user
      $user = User::find($billable);

      // Set each listing to draft
      foreach ($user->listings as $listing) {
        $current_listing = Listing::find($listing->id);
        if ($current_listing->status == 'published') {
          $current_listing->status = 'draft';
          $current_listing->save();
        }
      }
    }

    return new Response('Webhook Handled', 200);
  }
}

我随后将我的路由更新为以下内容:
Route::post('stripe/webhook', 'WebhookController@handleWebhook');

但它仍未触发。但是,我也在想,当用户取消订阅时是否会调用handleCustomerSubscriptionDeleted,还是在他们的宽限期结束并实际取消订阅后才会调用。是否有比在本地等待更可靠的方法来测试这一点?

更新#2

我已将覆盖类更新为以下内容:

<?php namespace App\Http\Controllers;

use App\Models\Dashboard\Listing;
use App\User;
use Symfony\Component\HttpFoundation\Response;
use Laravel\Cashier\WebhookController as BaseController;

class WebhookController extends BaseController
{

  /**
   * Handle stripe subscription webhook
   *
   * @param  array  $payload
   * @return Response
   */
  public function handleCustomerSubscriptionDeleted(array $payload)
  {
    $billable = $this->getBillable($payload['data']['object']['customer']);

    if ($billable && $billable->subscribed()) {
      $billable->subscription()->cancel();

      // Get current user
      $user = User::where('stripe_id', $billable)->firstOrFail();

      // Set each listing to draft
      foreach ($user->listings as $listing) {
        $current_listing = Listing::find($listing->id);
        if ($current_listing->status == 'published') {
          $current_listing->status = 'draft';
          $current_listing->save();
        }
      }
    }

    return new Response('Webhook Handled', 200);
  }
}

我所做的更改是将$billable更改为按用户搜索,因为响应返回的是用户,而不是我曾经认为的用户ID。我最终尝试了@Shaz提到的localtunnel.me,这确实允许我向其发送请求,但是由于无法传递我想要影响的客户ID,我不确定如何验证一切是否真正起作用。我可能会尝试深入挖掘以查看是否可以通过Cashier手动运行事件,但仍然似乎有些奇怪。
更新#3
尝试做一些更简单的事情,例如监听customer.subscription.created(因为它立即触发):
<?php namespace App\Http\Controllers;

use App\Models\Dashboard\Listing;
use App\User;
use Symfony\Component\HttpFoundation\Response;
use Laravel\Cashier\WebhookController as BaseController;

class WebhookController extends BaseController
{

  /**
   * @param array $payload
   */
  public function handleCustomerSubscriptionCreated(array $payload) {
    $billable = $this->getBillable($payload['data']['object']['customer']);

    if ($billable) {
      // Get current user
      $user = User::where('stripe_id', $billable)->firstOrFail();

      $user->first_name = 'Helloooooo';
      $user->save();
    }
  }

  /**
   * Handle stripe subscription webhook
   *
   * @param  array  $payload
   * @return Response
   */
  public function handleCustomerSubscriptionDeleted(array $payload)
  {
    $billable = $this->getBillable($payload['data']['object']['customer']);

    if ($billable && $billable->subscribed()) {
      $billable->subscription()->cancel();

      // Get current user
      $user = User::where('stripe_id', $billable)->firstOrFail();

      // Set each listing to draft
      foreach ($user->listings as $listing) {
        $current_listing = Listing::find($listing->id);
        if ($current_listing->status == 'published') {
          $current_listing->status = 'draft';
          $current_listing->save();
        }
      }
    }

    return new Response('Webhook Handled', 200);
  }
}

我使用localtunnel.me来设置webhook,但是它似乎没有正确响应webhook,因为我在Stripe响应日志中看到500错误,尽管我的“测试Webhooks”事件从Stripe仪表板触发(显然没有设置客户ID)是正常的。关于500错误的响应不幸地丢失/截断在Laravel输出的混乱源代码中:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="robots" content="noindex,nofollow" />
        <style>
            /* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */
            html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}

            html { backgr...
3个回答

6

阅读这个问题的答案 - 事件在订阅实际结束的日期/时间触发,而不是客户发起取消时。

根据Laravel Cashier文档 - 您的路由实际上应该是:

Route::post('stripe/webhook', 'Laravel\Cashier\WebhookController@handleWebhook');

请确保您在Stripe中的Webhook设置实际指向yourapp.com/stripe/webhook
根据Laravel\Cashier代码中的handleWebhook方法 - 是的,任何以handle开头、驼峰式命名为strip事件的适当命名方法都将覆盖现有方法。
如果您想在路由中使用扩展控制器,请记得在类的开头添加构造函数。
public function __construct(){
  parent::__construct();
}

因此,当您执行时,handleWebhook方法可用。

Route::post('stripe/webhook', 'WebhookController@handleWebhook');

不要在路由中调用 Laravel\Cashier\WebhookController,而是采用其他方法。


太棒了!我会试一下。现在我需要找到一种实际触发此Webhook进行测试的方法 - 我能否有可靠的方法来做到这一点?这是我正在努力解决的另一个问题。谢谢! - Zach
我使用 http://localtunnel.me/ 在本地机器上测试 Webhooks。我相当确定 Stripe 有一个沙盒模式,可以让你玩耍并触发通知。否则,只需创建一个带有单个操作的路由来启动该 Stripe 事件即可。 - Shaz MJ
我在原始问题更新#2中更新了我的进展情况,但是添加__construct实际上破坏了我的路由,所以我不确定是否应该将其放在那里。有没有示例可以用来触发Stripe事件并传递customer_id,以便我可以确保必要的DB更新也已完成?希望Cashier只是发出事件,我可以在此之外连接它。 - Zach
1
我相信我之所以会收到“无法调用父构造函数”的错误信息,是因为该父类没有构造函数。 - Zach
尝试在扩展类的构造函数中放置parent::handleWebhook()。 对于500错误,请记住在handleCustomerSubscriptionCreated()方法的结尾处添加return new Response('Webhook已处理',200); - Shaz MJ
在构造函数中添加parent::handleWebhook片段时,出现了undefined index $id的错误-- 尝试查看缺失响应消息是否会触发已创建的订阅事件。 - Zach

4
我已经做到了这一点,并将路由指向我的控制器。
Route::post(
    'stripe/webhook',
    'WebhookController@handleWebhook'
);

由于我们正在扩展基础控制器,因此基础控制器中的任何方法都已经可用。这个控制器的重点是要访问所有基础控制器的方法。这样,您可以扩展任何新方法或覆盖现有方法。
我还遇到了一个问题,就是无法捕获我的代码中的Webhook事件。需要注意的是,如果您在Stripe的测试环境中触发Webhook事件,请确保添加env变量。
CASHIER_ENV=testing

在基本的 webhook 控制器中,首先检查事件是否存在,如果 webhook 是在测试环境中生成的,则该事件将不存在。
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();
    }
}

只是想补充一下,人们可以使用parent :: function来调用Laravel的handleCustomerSubscriptionDeleted函数,并将其添加到他们喜欢的内容中。 parent :: handleCustomerSubscriptionDeleted($ payload); - Volkan Ozyilmaz

1
$billable = $this->getBillable($payload['data']['object']['customer']);
$user = $billable; // billable is User object

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接