You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中如何从控制器检查Stripe Webhook事件是否完成?

解决方案:确保Stripe Webhook完成后再跳转至仪表盘

针对你的需求,这里提供几种可行的实现方案,兼顾可靠性与用户体验:

方案1:数据库标记Webhook状态 + 前端轮询

这是最稳妥的落地方案,核心思路是通过数据库记录Webhook的处理状态,前端通过轮询确认状态后再完成跳转。

步骤1:添加Webhook状态字段

在users表(或单独创建webhook_statuses关联表)新增字段,记录需要等待的Webhook事件状态:

// 生成迁移文件
php artisan make:migration add_stripe_webhook_statuses_to_users_table

// 迁移文件内容
public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->json('stripe_webhook_status')->default(json_encode([
            'webhook_received' => false,
            'webhook_handled' => false
        ]));
    });
}

步骤2:修改Webhook监听器更新状态

在WebhookReceived和WebhookHandled监听器中,更新对应用户的状态(需从Stripe事件的metadata中关联到用户ID/邮箱):

// WebhookReceived监听器示例
public function handle(WebhookReceived $event)
{
    // 原有业务逻辑...
    
    // 从Stripe事件元数据中获取用户ID
    $userId = $event->payload['metadata']['user_id'];
    $user = User::find($userId);
    
    if ($user) {
        $status = json_decode($user->stripe_webhook_status, true);
        $status['webhook_received'] = true;
        $user->update(['stripe_webhook_status' => json_encode($status)]);
    }
}

// WebhookHandled监听器同理,更新webhook_handled为true

步骤3:后端添加状态检查接口

// 在UserController中新增方法
public function checkWebhookStatus(Request $request)
{
    $user = User::where('email', $request->email)->first();
    if (!$user) {
        return $this->error('用户不存在');
    }
    
    $status = json_decode($user->stripe_webhook_status, true);
    $allCompleted = $status['webhook_received'] && $status['webhook_handled'];
    
    return $this->success([
        'all_completed' => $allCompleted,
        'status' => $status
    ]);
}

步骤4:前端轮询逻辑

支付完成后,前端显示加载状态,定期调用上述接口,直到返回all_completed为true再跳转至登录/仪表盘:

// 示例:使用async/await实现轮询
async function waitForWebhookCompletion(userEmail) {
    const checkInterval = setInterval(async () => {
        try {
            const response = await fetch('/api/check-webhook-status', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                },
                body: JSON.stringify({ email: userEmail })
            });
            const data = await response.json();
            
            if (data.data.all_completed) {
                clearInterval(checkInterval);
                // 跳转到目标页面
                window.location.href = '/login';
            }
        } catch (error) {
            console.error('检查Webhook状态失败:', error);
        }
    }, 1500); // 每1.5秒检查一次
}

方案2:后端延迟检查(带超时)

如果不想依赖前端轮询,可以在后端做有限时间内的等待检查,但需设置超时避免请求挂死:

public function paymentVerificationUser(Request $request)
{
    try {
        // 原有业务逻辑...
        
        $user = User::with(['prefectures', 'files'])->where(['email' => $data['user']['email'], 'status' => 'A'])->first();
        
        // 最多等待30秒
        $timeout = 30;
        $startTime = time();
        $allCompleted = false;
        
        while (time() - $startTime < $timeout) {
            $status = json_decode($user->stripe_webhook_status, true);
            $allCompleted = $status['webhook_received'] && $status['webhook_handled'];
            
            if ($allCompleted) break;
            sleep(1); // 每秒刷新一次状态
            $user->refresh(); // 重新从数据库拉取最新数据
        }
        
        // 即使超时也返回结果,前端可后续做兜底处理
        return $this->success(array_merge($data, ['webhooks_completed' => $allCompleted]));
    } catch (\Exception $th) {
        return $this->error($th);
    }
}

注意:此方案可能触发服务器请求超时(若Webhook处理耗时超过服务器配置的超时时间),建议配合前端兜底逻辑。

方案3:WebSocket实时通知(最优体验)

如果项目已集成WebSocket(如Laravel Echo + Pusher),可以在Webhook处理完成后实时推送通知给前端,收到通知后直接跳转:

  1. 在Webhook监听器中触发广播事件:
public function handle(WebhookHandled $event)
{
    // 原有业务逻辑...
    $userId = $event->payload['metadata']['user_id'];
    event(new WebhookCompleted($userId));
}
  1. 定义WebhookCompleted广播事件:
class WebhookCompleted implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;
    
    public $userId;
    
    public function __construct($userId)
    {
        $this->userId = $userId;
    }
    
    public function broadcastOn()
    {
        return new PrivateChannel('user.'.$this->userId);
    }
}
  1. 前端监听通道并处理跳转:
Echo.private(`user.${userId}`)
    .listen('WebhookCompleted', (e) => {
        window.location.href = '/dashboard';
    });

内容的提问来源于stack exchange,提问作者Nadiya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 13:28:09