['路由' => '处理器类名']] * * @var array */ protected static array $packageHandlers = []; /** * 分发Webhook请求到指定包的处理器 * * @param string $packageName 包名 * @param string $handlerRoute Handler路由 * @param Request $request 请求对象 * @return array * @throws \Exception */ public function dispatch(string $packageName, string $handlerRoute, Request $request): array { // 只支持POST请求 if ($request->method() !== 'POST') { throw new \Exception("Webhook只支持POST请求,当前方法: {$request->method()}"); } // 获取服务配置 $service = $this->getServiceByPackage($packageName); // 验证Webhook签名(在实例化之前) if (!$this->validateWebhookSignature($request, $service)) { throw new \Exception('Webhook签名验证失败'); } // 获取处理器类 $handlerClass = $this->getPackageHandler($packageName, $handlerRoute); if (!$handlerClass) { throw new \Exception("包 {$packageName} 的处理器 {$handlerRoute} 不存在"); } // 检查处理器类是否存在 if (!class_exists($handlerClass)) { throw new \Exception("处理器类 {$handlerClass} 不存在"); } // 检查处理器是否继承自WebhookReceiver if (!is_subclass_of($handlerClass, WebhookReceiver::class)) { throw new \Exception("处理器类 {$handlerClass} 必须继承自 WebhookReceiver"); } // 创建处理器实例,传入服务配置对象 $handler = new $handlerClass($packageName, $request, $service); // 执行处理 $response = $handler->handle($handlerRoute); // 返回响应数据 return $response->getData(true); } /** * 注册包处理器 * * @param string $packageName 包名 * @param string $handlerRoute Handler路由 * @param string $handlerClass 处理器类名 */ public static function registerPackageHandler(string $packageName, string $handlerRoute, string $handlerClass): void { if (!isset(static::$packageHandlers[$packageName])) { static::$packageHandlers[$packageName] = []; } static::$packageHandlers[$packageName][$handlerRoute] = $handlerClass; Log::info("注册包处理器", [ 'package_name' => $packageName, 'handler_route' => $handlerRoute, 'handler_class' => $handlerClass, ]); } /** * 批量注册包处理器 * * @param string $packageName 包名 * @param array $handlers 处理器映射 ['route' => 'ClassName'] */ public static function registerPackageHandlers(string $packageName, array $handlers): void { foreach ($handlers as $route => $handlerClass) { static::registerPackageHandler($packageName, $route, $handlerClass); } } /** * 获取包的处理器类 * * @param string $packageName 包名 * @param string $handlerRoute Handler路由 * @return string|null */ protected function getPackageHandler(string $packageName, string $handlerRoute): ?string { return static::$packageHandlers[$packageName][$handlerRoute] ?? null; } /** * 根据包名获取服务配置对象 * * @param string $packageName 包名 * @return ServiceModel * @throws \Exception */ protected function getServiceByPackage(string $packageName): ServiceModel { // 查找对应的服务配置 $service = ServiceModel::where('code', $packageName)->first(); if (!$service) { throw new \Exception("包 {$packageName} 对应的服务配置不存在,请先在thirdparty_services表中注册"); } return $service; } /** * 根据包名获取服务代码 * * @param string $packageName 包名 * @return string * @throws \Exception */ protected function getServiceCodeByPackage(string $packageName): string { return $this->getServiceByPackage($packageName)->code; } /** * 验证Webhook签名 * * @param Request $request 请求对象 * @param ServiceModel $service 服务配置 * @return bool */ protected function validateWebhookSignature(Request $request, ServiceModel $service): bool { $webhookSecret = $service->webhook_secret; if (!$webhookSecret) { // 如果没有配置密钥,则跳过签名验证 return true; } $signature = $request->header('X-Signature'); if (!$signature) { return false; } // 使用POST参数排序后拼接的方式生成签名字符串 $signString = $this->buildSignString($request->post()); $expectedSignature = hash_hmac('sha256', $signString, $webhookSecret); return hash_equals($expectedSignature, $signature); } /** * 构建签名字符串(支付宝方式:参数排序后拼接) * * @param array $params 请求参数 * @return string */ protected function buildSignString(array $params): string { // 过滤空值参数 $params = array_filter($params, function ($value) { return $value !== '' && $value !== null; }); // 按参数名排序 ksort($params); // 拼接参数 $stringToBeSigned = ''; foreach ($params as $key => $value) { // 跳过签名参数本身 if ($key === 'sign' || $key === 'signature') { continue; } // 处理数组和对象类型的值 if (is_array($value) || is_object($value)) { $value = json_encode($value, JSON_UNESCAPED_UNICODE); } $stringToBeSigned .= $key . '=' . $value . '&'; } // 去除最后一个&符号 return rtrim($stringToBeSigned, '&'); } /** * 获取已注册的包列表 * * @return array */ public function getRegisteredPackages(): array { $packages = []; foreach (static::$packageHandlers as $packageName => $handlers) { $packages[$packageName] = [ 'name' => $packageName, 'handlers' => array_keys($handlers), 'handler_count' => count($handlers), ]; } return $packages; } /** * 获取指定包的处理器列表 * * @param string $packageName 包名 * @return array */ public function getPackageHandlers(string $packageName): array { return static::$packageHandlers[$packageName] ?? []; } /** * 检查包是否已注册 * * @param string $packageName 包名 * @return bool */ public function isPackageRegistered(string $packageName): bool { return isset(static::$packageHandlers[$packageName]); } /** * 检查包的处理器是否已注册 * * @param string $packageName 包名 * @param string $handlerRoute Handler路由 * @return bool */ public function isHandlerRegistered(string $packageName, string $handlerRoute): bool { return isset(static::$packageHandlers[$packageName][$handlerRoute]); } /** * 注销包处理器 * * @param string $packageName 包名 * @param string|null $handlerRoute Handler路由,为空则注销整个包 */ public static function unregisterPackageHandler(string $packageName, ?string $handlerRoute = null): void { if ($handlerRoute === null) { // 注销整个包 unset(static::$packageHandlers[$packageName]); Log::info("注销包", ['package_name' => $packageName]); } else { // 注销指定处理器 unset(static::$packageHandlers[$packageName][$handlerRoute]); Log::info("注销包处理器", [ 'package_name' => $packageName, 'handler_route' => $handlerRoute, ]); } } /** * 清空所有已注册的包处理器 */ public static function clearAllHandlers(): void { static::$packageHandlers = []; Log::info("清空所有包处理器"); } }