diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 5b1c3f5..119733c 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -45,7 +45,7 @@ {"id":"fotospiel-app-bqm","title":"Paddle catalog sync: unit tests for service + jobs","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:01:22.090498843+01:00","created_by":"soeren","updated_at":"2026-01-01T16:01:27.71412122+01:00","closed_at":"2026-01-01T16:01:27.71412122+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-bxu","title":"Checkout refactor: Stripe/Paddle payment integration + webhooks","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:06:32.279485614+01:00","created_by":"soeren","updated_at":"2026-01-01T16:06:37.876950599+01:00","closed_at":"2026-01-01T16:06:37.876950599+01:00","close_reason":"Completed in codebase (verified)"} {"id":"fotospiel-app-bzb","title":"Paddle catalog sync: migration for paddle sync columns","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:00:02.362257158+01:00","created_by":"soeren","updated_at":"2026-01-01T16:00:08.018770606+01:00","closed_at":"2026-01-01T16:00:08.018770606+01:00","close_reason":"Completed in codebase (verified)"} -{"id":"fotospiel-app-ci5","title":"Paddle catalog sync: configure log channel/Slack hook for sync outcomes","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:20.543083527+01:00","created_by":"soeren","updated_at":"2026-01-01T15:59:20.543083527+01:00"} +{"id":"fotospiel-app-ci5","title":"Paddle catalog sync: configure log channel/Slack hook for sync outcomes","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:20.543083527+01:00","created_by":"soeren","updated_at":"2026-01-02T22:02:15.857149244+01:00","closed_at":"2026-01-02T22:02:15.857149244+01:00","close_reason":"Completed"} {"id":"fotospiel-app-cwq","title":"Integrations health: unified Paddle/RevenueCat/webhook status dashboard","description":"Add a superadmin integrations health dashboard for Paddle/RevenueCat/webhooks.\nScope: show latest webhook processing status/lag, recent failures, retry backlog, and config presence (env set) without exposing secrets.\nInclude per-provider status badges and time-window filters, plus links to related logs/actions.\n","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-02T17:34:20.84661157+01:00","created_by":"soeren","updated_at":"2026-01-02T18:33:07.133704488+01:00","closed_at":"2026-01-02T18:33:07.133704488+01:00","close_reason":"Closed"} {"id":"fotospiel-app-d39","title":"Superadmin control surface spec and access matrix","description":"Define the minimal superadmin control surface, permissions, and mapping to tenant/guest responsibilities. Document scope and non-goals.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T14:16:06.994379577+01:00","updated_at":"2026-01-01T14:20:43.080701114+01:00","closed_at":"2026-01-01T14:20:43.080701114+01:00"} {"id":"fotospiel-app-dl5","title":"SEC-API-01 Signed URL middleware + asset migration","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:52:24.24098702+01:00","created_by":"soeren","updated_at":"2026-01-01T15:52:29.8793891+01:00","closed_at":"2026-01-01T15:52:29.8793891+01:00","close_reason":"Completed in codebase (verified)"} diff --git a/app/Jobs/PullPackageFromPaddle.php b/app/Jobs/PullPackageFromPaddle.php index 639f8a8..c4c5535 100644 --- a/app/Jobs/PullPackageFromPaddle.php +++ b/app/Jobs/PullPackageFromPaddle.php @@ -31,7 +31,9 @@ class PullPackageFromPaddle implements ShouldQueue } if (! $package->paddle_product_id && ! $package->paddle_price_id) { - Log::warning('Paddle pull skipped for package without linkage', ['package_id' => $package->id]); + Log::channel('paddle-sync')->warning('Paddle pull skipped for package without linkage', [ + 'package_id' => $package->id, + ]); return; } @@ -52,9 +54,11 @@ class PullPackageFromPaddle implements ShouldQueue 'paddle_snapshot' => $snapshot, ])->save(); - Log::info('Paddle package pull completed', ['package_id' => $package->id]); + Log::channel('paddle-sync')->info('Paddle package pull completed', [ + 'package_id' => $package->id, + ]); } catch (Throwable $exception) { - Log::error('Paddle package pull failed', [ + Log::channel('paddle-sync')->error('Paddle package pull failed', [ 'package_id' => $package->id, 'message' => $exception->getMessage(), 'exception' => $exception, diff --git a/app/Jobs/SyncCouponToPaddle.php b/app/Jobs/SyncCouponToPaddle.php index d97af06..fa46f6a 100644 --- a/app/Jobs/SyncCouponToPaddle.php +++ b/app/Jobs/SyncCouponToPaddle.php @@ -47,7 +47,7 @@ class SyncCouponToPaddle implements ShouldQueue 'paddle_last_synced_at' => now(), ])->save(); } catch (PaddleException $exception) { - Log::error('Failed syncing coupon to Paddle', [ + Log::channel('paddle-sync')->error('Failed syncing coupon to Paddle', [ 'coupon_id' => $this->coupon->id, 'message' => $exception->getMessage(), 'status' => $exception->status(), diff --git a/app/Jobs/SyncPackageAddonToPaddle.php b/app/Jobs/SyncPackageAddonToPaddle.php index f3deeac..59aa63d 100644 --- a/app/Jobs/SyncPackageAddonToPaddle.php +++ b/app/Jobs/SyncPackageAddonToPaddle.php @@ -89,7 +89,7 @@ class SyncPackageAddonToPaddle implements ShouldQueue ]), ])->save(); } catch (Throwable $exception) { - Log::error('Paddle addon sync failed', [ + Log::channel('paddle-sync')->error('Paddle addon sync failed', [ 'addon_id' => $addon->id, 'message' => $exception->getMessage(), 'exception' => $exception, @@ -160,7 +160,7 @@ class SyncPackageAddonToPaddle implements ShouldQueue ]), ])->save(); - Log::info('Paddle addon dry-run snapshot generated', [ + Log::channel('paddle-sync')->info('Paddle addon dry-run snapshot generated', [ 'addon_id' => $addon->id, ]); } diff --git a/app/Jobs/SyncPackageToPaddle.php b/app/Jobs/SyncPackageToPaddle.php index e215fb5..e50a500 100644 --- a/app/Jobs/SyncPackageToPaddle.php +++ b/app/Jobs/SyncPackageToPaddle.php @@ -85,7 +85,7 @@ class SyncPackageToPaddle implements ShouldQueue ], ])->save(); } catch (Throwable $exception) { - Log::error('Paddle package sync failed', [ + Log::channel('paddle-sync')->error('Paddle package sync failed', [ 'package_id' => $package->id, 'message' => $exception->getMessage(), 'exception' => $exception, @@ -131,7 +131,7 @@ class SyncPackageToPaddle implements ShouldQueue ], ])->save(); - Log::info('Paddle package dry-run snapshot generated', [ + Log::channel('paddle-sync')->info('Paddle package dry-run snapshot generated', [ 'package_id' => $package->id, ]); } diff --git a/config/logging.php b/config/logging.php index 1b0d765..34a92c2 100644 --- a/config/logging.php +++ b/config/logging.php @@ -80,6 +80,18 @@ return [ 'days' => env('LOG_DAILY_DAYS', 14), 'replace_placeholders' => true, ], + 'paddle-sync' => [ + 'driver' => 'stack', + 'channels' => explode(',', env('PADDLE_SYNC_LOG_STACK', 'paddle-sync-file')), + 'ignore_exceptions' => false, + ], + 'paddle-sync-file' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/paddle-sync.log'), + 'level' => env('LOG_LEVEL', 'info'), + 'days' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], 'slack' => [ 'driver' => 'slack', diff --git a/tests/Unit/PaddleSyncLoggingConfigTest.php b/tests/Unit/PaddleSyncLoggingConfigTest.php new file mode 100644 index 0000000..52154f5 --- /dev/null +++ b/tests/Unit/PaddleSyncLoggingConfigTest.php @@ -0,0 +1,26 @@ +assertIsArray($channel); + $this->assertSame('stack', $channel['driver'] ?? null); + $this->assertNotEmpty($channel['channels'] ?? null); + } + + public function test_paddle_sync_file_channel_is_configured(): void + { + $channel = config('logging.channels.paddle-sync-file'); + + $this->assertIsArray($channel); + $this->assertSame('daily', $channel['driver'] ?? null); + $this->assertSame('paddle-sync.log', basename((string) ($channel['path'] ?? ''))); + } +}