Add Paddle sync log channel

This commit is contained in:
Codex Agent
2026-01-02 22:02:36 +01:00
parent 0430f0b1cc
commit bb67d68eba
7 changed files with 51 additions and 9 deletions

View File

@@ -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-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-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-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-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-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)"} {"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)"}

View File

@@ -31,7 +31,9 @@ class PullPackageFromPaddle implements ShouldQueue
} }
if (! $package->paddle_product_id && ! $package->paddle_price_id) { 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; return;
} }
@@ -52,9 +54,11 @@ class PullPackageFromPaddle implements ShouldQueue
'paddle_snapshot' => $snapshot, 'paddle_snapshot' => $snapshot,
])->save(); ])->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) { } catch (Throwable $exception) {
Log::error('Paddle package pull failed', [ Log::channel('paddle-sync')->error('Paddle package pull failed', [
'package_id' => $package->id, 'package_id' => $package->id,
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
'exception' => $exception, 'exception' => $exception,

View File

@@ -47,7 +47,7 @@ class SyncCouponToPaddle implements ShouldQueue
'paddle_last_synced_at' => now(), 'paddle_last_synced_at' => now(),
])->save(); ])->save();
} catch (PaddleException $exception) { } 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, 'coupon_id' => $this->coupon->id,
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
'status' => $exception->status(), 'status' => $exception->status(),

View File

@@ -89,7 +89,7 @@ class SyncPackageAddonToPaddle implements ShouldQueue
]), ]),
])->save(); ])->save();
} catch (Throwable $exception) { } catch (Throwable $exception) {
Log::error('Paddle addon sync failed', [ Log::channel('paddle-sync')->error('Paddle addon sync failed', [
'addon_id' => $addon->id, 'addon_id' => $addon->id,
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
'exception' => $exception, 'exception' => $exception,
@@ -160,7 +160,7 @@ class SyncPackageAddonToPaddle implements ShouldQueue
]), ]),
])->save(); ])->save();
Log::info('Paddle addon dry-run snapshot generated', [ Log::channel('paddle-sync')->info('Paddle addon dry-run snapshot generated', [
'addon_id' => $addon->id, 'addon_id' => $addon->id,
]); ]);
} }

View File

@@ -85,7 +85,7 @@ class SyncPackageToPaddle implements ShouldQueue
], ],
])->save(); ])->save();
} catch (Throwable $exception) { } catch (Throwable $exception) {
Log::error('Paddle package sync failed', [ Log::channel('paddle-sync')->error('Paddle package sync failed', [
'package_id' => $package->id, 'package_id' => $package->id,
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
'exception' => $exception, 'exception' => $exception,
@@ -131,7 +131,7 @@ class SyncPackageToPaddle implements ShouldQueue
], ],
])->save(); ])->save();
Log::info('Paddle package dry-run snapshot generated', [ Log::channel('paddle-sync')->info('Paddle package dry-run snapshot generated', [
'package_id' => $package->id, 'package_id' => $package->id,
]); ]);
} }

View File

@@ -80,6 +80,18 @@ return [
'days' => env('LOG_DAILY_DAYS', 14), 'days' => env('LOG_DAILY_DAYS', 14),
'replace_placeholders' => true, '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' => [ 'slack' => [
'driver' => 'slack', 'driver' => 'slack',

View File

@@ -0,0 +1,26 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
class PaddleSyncLoggingConfigTest extends TestCase
{
public function test_paddle_sync_channel_is_configured(): void
{
$channel = config('logging.channels.paddle-sync');
$this->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'] ?? '')));
}
}