Show Paddle sync errors

This commit is contained in:
Codex Agent
2026-01-02 21:16:23 +01:00
parent 3e9f09571b
commit 77b7af13d4
5 changed files with 63 additions and 8 deletions

View File

@@ -9,14 +9,14 @@
{"id":"fotospiel-app-29o","title":"Paddle catalog sync: PackageResource sync status badges + timestamp","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:01:10.009385187+01:00","created_by":"soeren","updated_at":"2026-01-01T16:01:15.639525807+01:00","closed_at":"2026-01-01T16:01:15.639525807+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-2hq","title":"Security review: marketing/API controller+validation review","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:08.862737923+01:00","created_by":"soeren","updated_at":"2026-01-01T16:05:08.862737923+01:00"}
{"id":"fotospiel-app-33m","title":"Security review checklist: Guest PWA dynamic tests","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:04:40.730459361+01:00","created_by":"soeren","updated_at":"2026-01-01T16:04:40.730459361+01:00"}
{"id":"fotospiel-app-38f","title":"Paddle catalog sync: surface last sync error/log context in admin","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:14.865414785+01:00","created_by":"soeren","updated_at":"2026-01-01T15:59:14.865414785+01:00"}
{"id":"fotospiel-app-38f","title":"Paddle catalog sync: surface last sync error/log context in admin","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:14.865414785+01:00","created_by":"soeren","updated_at":"2026-01-02T21:16:09.109922491+01:00","closed_at":"2026-01-02T21:16:09.109922491+01:00","close_reason":"Completed"}
{"id":"fotospiel-app-3ut","title":"SEC-API-03 Synthetic monitoring + alert config","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:52:46.793875724+01:00","created_by":"soeren","updated_at":"2026-01-01T15:52:46.793875724+01:00"}
{"id":"fotospiel-app-3xa","title":"Security review: event admin code audit (policies, PKCE, file handling)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:20.115675149+01:00","created_by":"soeren","updated_at":"2026-01-01T16:05:20.115675149+01:00"}
{"id":"fotospiel-app-4ar","title":"SEC-BILL-03 Failed capture notifications + ledger hook","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:54:33.266516715+01:00","created_by":"soeren","updated_at":"2026-01-01T15:54:33.266516715+01:00"}
{"id":"fotospiel-app-4i4","title":"Security review: map roles/data","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:58.370301875+01:00","created_by":"soeren","updated_at":"2026-01-01T16:03:03.997327414+01:00","closed_at":"2026-01-01T16:03:03.997327414+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-4zu","title":"SEC-IO-02 Refresh-token management UI + audit logs","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:51:50.24186222+01:00","created_by":"soeren","updated_at":"2026-01-01T15:51:50.24186222+01:00"}
{"id":"fotospiel-app-55n","title":"Tenant admin onboarding: add Paddle error UX + test coverage","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:08:40.463283816+01:00","created_by":"soeren","updated_at":"2026-01-01T16:08:40.463283816+01:00"}
{"id":"fotospiel-app-574","title":"Paddle catalog sync: extend PaddleClient tests/mocks for catalog endpoints","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:03.486301225+01:00","created_by":"soeren","updated_at":"2026-01-01T15:59:03.486301225+01:00"}
{"id":"fotospiel-app-574","title":"Paddle catalog sync: extend PaddleClient tests/mocks for catalog endpoints","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:03.486301225+01:00","created_by":"soeren","updated_at":"2026-01-02T21:11:39.626820206+01:00","closed_at":"2026-01-02T21:11:39.626820206+01:00","close_reason":"Deprioritized"}
{"id":"fotospiel-app-576","title":"Tenant admin onboarding: legacy asset audit + component inventory","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:07:59.996563146+01:00","created_by":"soeren","updated_at":"2026-01-01T16:08:05.599274641+01:00","closed_at":"2026-01-01T16:08:05.599274641+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-5dl","title":"Paddle catalog sync: PaddleCatalogService scaffold","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:00:24.916655836+01:00","created_by":"soeren","updated_at":"2026-01-01T16:00:30.566084195+01:00","closed_at":"2026-01-01T16:00:30.566084195+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-5iy","title":"Security review: confirm env/header defaults","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:03:20.808188183+01:00","created_by":"soeren","updated_at":"2026-01-01T16:03:26.388002115+01:00","closed_at":"2026-01-01T16:03:26.388002115+01:00","close_reason":"Completed in codebase (verified)"}
@@ -65,13 +65,13 @@
{"id":"fotospiel-app-jk4","title":"Checkout refactor: CheckoutController + marketing route alignment","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:06:21.088319132+01:00","created_by":"soeren","updated_at":"2026-01-01T16:06:26.663419594+01:00","closed_at":"2026-01-01T16:06:26.663419594+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-jqy","title":"Tenant admin onboarding: Playwright skeleton for welcome flow","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:08:11.226297707+01:00","created_by":"soeren","updated_at":"2026-01-01T16:08:16.827679424+01:00","closed_at":"2026-01-01T16:08:16.827679424+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-ko0","title":"Security review checklist: Webhooks/Billing dynamic tests","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:04:51.987093237+01:00","created_by":"soeren","updated_at":"2026-01-01T16:04:51.987093237+01:00"}
{"id":"fotospiel-app-kry","title":"Paddle catalog sync: add DTO helpers for Paddle product/price responses","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:58:57.817750548+01:00","created_by":"soeren","updated_at":"2026-01-01T15:58:57.817750548+01:00"}
{"id":"fotospiel-app-kry","title":"Paddle catalog sync: add DTO helpers for Paddle product/price responses","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:58:57.817750548+01:00","created_by":"soeren","updated_at":"2026-01-02T21:11:27.970220923+01:00","closed_at":"2026-01-02T21:11:27.970220923+01:00","close_reason":"Deprioritized"}
{"id":"fotospiel-app-kso","title":"SEC-MS-02 Streaming upload refactor + tests","description":"Current state (code scan)\n- Guest uploads: App\\\\Http\\\\Controllers\\\\Api\\\\EventPublicController@upload uses Storage::disk()-\u003eputFile (stream-friendly) but still does watermark/thumbnail work inline.\n- Tenant admin uploads: App\\\\Http\\\\Controllers\\\\Api\\\\Tenant\\\\PhotoController@store and @uploadDirect use Storage::disk()-\u003eput($path, file_get_contents(...)) which loads entire file into memory.\n- Photobooth ingest already streams from import disk via readStream -\u003e Storage::disk()-\u003eput($path, $stream).\n- Presigned upload flow is stubbed to a local upload-direct endpoint; no true presigned S3 handling yet.\n- No tenant upload feature tests exist; guest upload tests exist and cover limits/security.\n\nGoal\n- Stream uploads to disk (avoid full in-memory buffers) for tenant-admin upload endpoints and keep behavior consistent across sources.\n\nPlan\n1) Introduce a small streaming upload helper/service\n - New service (e.g. App\\\\Services\\\\Storage\\\\UploadStreamService) that accepts UploadedFile + disk + destination path.\n - Use fopen on UploadedFile::getRealPath (or $file-\u003egetStream()) and Storage::disk($disk)-\u003eput($path, $stream) / writeStream.\n - Always close stream; return stored size and checksum (hash_file on stored path) for asset metadata.\n\n2) Refactor tenant upload endpoints to use streaming\n - Update PhotoController@store and @uploadDirect to use the helper instead of file_get_contents.\n - Use Storage::disk()-\u003eputFileAs (or helper) to preserve deterministic paths without buffering.\n - Keep existing validation, watermark, thumbnail, asset recording, and package usage logic.\n\n3) Optional consistency pass on guest upload\n - Consider routing EventPublicController@upload through the same helper for consistent storage + checksum handling, while keeping current validation/limits.\n\n4) Tests\n - Add Feature tests for tenant upload endpoints:\n - /api/v1/tenant/events/{slug}/photos (store) uploads a fake image and persists Photo + EventMediaAsset with expected path/size.\n - /api/v1/tenant/events/{slug}/upload-direct (presigned) uploads a fake image and stores asset + thumbnail.\n - Ensure existing guest upload tests still pass (no behavioral changes).\n\n5) Safety/ops\n - Verify streaming logic handles empty/invalid files gracefully and still reports errors via ApiError.\n - Keep request-time processing (thumb/watermark) unchanged for now; consider queuing in a follow-up if CPU spikes persist.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:53:03.729137616+01:00","created_by":"soeren","updated_at":"2026-01-02T20:51:17.752365339+01:00","closed_at":"2026-01-02T20:51:17.752365339+01:00","close_reason":"Closed"}
{"id":"fotospiel-app-kxe","title":"Paddle customer success metrics (tenant ↔ Paddle sync, sandbox seeding, rollout/rollback)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:10:34.454400334+01:00","created_by":"soeren","updated_at":"2026-01-02T17:03:51.48872094+01:00","closed_at":"2026-01-02T17:03:51.48872094+01:00","close_reason":"Closed"}
{"id":"fotospiel-app-l3n","title":"Session changes 2025-09-08 (PRP split, PWA scaffolding, Filament resources, API)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:10:18.204088457+01:00","created_by":"soeren","updated_at":"2026-01-01T16:10:23.815135505+01:00","closed_at":"2026-01-01T16:10:23.815135505+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-l6a","title":"Registration flow fixes: JSON redirect, error clearing, role handling","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:07:16.253760139+01:00","created_by":"soeren","updated_at":"2026-01-01T16:07:21.964843904+01:00","closed_at":"2026-01-01T16:07:21.964843904+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-l8q","title":"SEC-GT-02 Join-token analytics dashboard (Grafana)","description":"Logging + Filament summaries exist; Grafana dashboard still missing.","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:52:12.920875329+01:00","created_by":"soeren","updated_at":"2026-01-01T15:52:12.920875329+01:00"}
{"id":"fotospiel-app-ln3","title":"Paddle catalog sync: announce workflow change to admin users","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:49.021233635+01:00","created_by":"soeren","updated_at":"2026-01-01T15:59:49.021233635+01:00"}
{"id":"fotospiel-app-ln3","title":"Paddle catalog sync: announce workflow change to admin users","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:49.021233635+01:00","created_by":"soeren","updated_at":"2026-01-02T21:11:09.349495631+01:00","closed_at":"2026-01-02T21:11:09.349495631+01:00","close_reason":"Deprioritized"}
{"id":"fotospiel-app-lnb","title":"SEC-GT-01 Hash join tokens + data migration","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:52:01.658868778+01:00","created_by":"soeren","updated_at":"2026-01-01T15:52:07.314317124+01:00","closed_at":"2026-01-01T15:52:07.314317124+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-lqp","title":"Integrations health (Paddle/RevenueCat/webhooks)","description":"Health/status dashboard for payment and webhook integrations.","status":"closed","priority":3,"issue_type":"feature","created_at":"2026-01-01T14:20:25.197673148+01:00","updated_at":"2026-01-02T18:45:16.225355969+01:00","closed_at":"2026-01-02T18:45:16.225355969+01:00","close_reason":"Closed"}
{"id":"fotospiel-app-ml7","title":"SEC-GT-03 Tighten gallery/photo rate limits + alerting","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:52:18.593415508+01:00","created_by":"soeren","updated_at":"2026-01-01T15:52:18.593415508+01:00"}
@@ -83,8 +83,8 @@
{"id":"fotospiel-app-nfi","title":"Paddle catalog sync: add Link existing Paddle entity action in admin","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:09.164334657+01:00","created_by":"soeren","updated_at":"2026-01-01T15:59:09.164334657+01:00"}
{"id":"fotospiel-app-niv","title":"Paddle catalog sync: Package model casts/fillable + factory","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:00:13.646318173+01:00","created_by":"soeren","updated_at":"2026-01-01T16:00:19.296543136+01:00","closed_at":"2026-01-01T16:00:19.296543136+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-o4n","title":"Audit PayPal SDK migration doc vs code (PayPal integration missing)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:07:34.316575518+01:00","created_by":"soeren","updated_at":"2026-01-01T16:07:34.316575518+01:00"}
{"id":"fotospiel-app-o96","title":"Paddle catalog sync: seed sandbox catalog via MCP","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:37.63819424+01:00","created_by":"soeren","updated_at":"2026-01-01T15:59:37.63819424+01:00"}
{"id":"fotospiel-app-oix","title":"Paddle catalog sync: Playwright smoke for admin sync action","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:31.939471627+01:00","created_by":"soeren","updated_at":"2026-01-01T15:59:31.939471627+01:00"}
{"id":"fotospiel-app-o96","title":"Paddle catalog sync: seed sandbox catalog via MCP","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:37.63819424+01:00","created_by":"soeren","updated_at":"2026-01-02T21:05:42.225830987+01:00","closed_at":"2026-01-02T21:05:42.225830987+01:00","close_reason":"Not needed"}
{"id":"fotospiel-app-oix","title":"Paddle catalog sync: Playwright smoke for admin sync action","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:31.939471627+01:00","created_by":"soeren","updated_at":"2026-01-02T21:11:19.467604455+01:00","closed_at":"2026-01-02T21:11:19.467604455+01:00","close_reason":"Deprioritized"}
{"id":"fotospiel-app-oof","title":"Paddle migration: tenant admin billing pages (Paddle transactions/portal)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:58:23.581152289+01:00","created_by":"soeren","updated_at":"2026-01-01T15:58:29.178846317+01:00","closed_at":"2026-01-01T15:58:29.178846317+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-p2k","title":"Security review: seed test identities/fixtures","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:03:31.980868874+01:00","created_by":"soeren","updated_at":"2026-01-01T16:03:37.575153506+01:00","closed_at":"2026-01-01T16:03:37.575153506+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-pcz","title":"Security review: route/middleware inventory for marketing/API","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:03:54.409559375+01:00","created_by":"soeren","updated_at":"2026-01-01T16:04:00.000669934+01:00","closed_at":"2026-01-01T16:04:00.000669934+01:00","close_reason":"Completed in codebase (verified)"}
@@ -101,7 +101,7 @@
{"id":"fotospiel-app-umr","title":"Paddle migration: build Paddle API service layer","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:57:38.529187953+01:00","created_by":"soeren","updated_at":"2026-01-01T15:57:44.178675076+01:00","closed_at":"2026-01-01T15:57:44.178675076+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-v31","title":"SEC-MS-01 AV + EXIF scrubber worker integration","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:52:52.476048623+01:00","created_by":"soeren","updated_at":"2026-01-01T15:52:58.118336529+01:00","closed_at":"2026-01-01T15:52:58.118336529+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-v4g","title":"Localized SEO: locale-prefixed routing + redirects","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:01:52.666356454+01:00","created_by":"soeren","updated_at":"2026-01-01T16:01:58.299584583+01:00","closed_at":"2026-01-01T16:01:58.299584583+01:00","close_reason":"Completed in codebase (verified)"}
{"id":"fotospiel-app-v84","title":"Paddle migration: seed sandbox catalog via MCP + validate activation flow","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:56:45.739701297+01:00","created_by":"soeren","updated_at":"2026-01-01T15:56:45.739701297+01:00"}
{"id":"fotospiel-app-v84","title":"Paddle migration: seed sandbox catalog via MCP + validate activation flow","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:56:45.739701297+01:00","created_by":"soeren","updated_at":"2026-01-02T21:11:49.428323337+01:00","closed_at":"2026-01-02T21:11:49.428323337+01:00","close_reason":"Deprioritized"}
{"id":"fotospiel-app-vc3","title":"Localized SEO: add tests for hreflang/canonical tags","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:30.822184391+01:00","created_by":"soeren","updated_at":"2026-01-01T16:02:30.822184391+01:00"}
{"id":"fotospiel-app-vel","title":"Localized SEO: update PRP/marketing playbooks for hreflang strategy","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:42.156443813+01:00","created_by":"soeren","updated_at":"2026-01-01T16:02:42.156443813+01:00"}
{"id":"fotospiel-app-vk4","title":"Registration flow fixes: JSON redirect, error clearing, role handling","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:07:01.574904029+01:00","created_by":"soeren","updated_at":"2026-01-01T16:11:18.65499639+01:00","closed_at":"2026-01-01T16:11:18.65499639+01:00","close_reason":"Duplicate of fotospiel-app-l6a"}

View File

@@ -1 +1 @@
fotospiel-app-kso
fotospiel-app-38f

View File

@@ -193,6 +193,11 @@ class PackageResource extends Resource
->label('Zuletzt synchronisiert')
->content(fn (?Package $record) => $record?->paddle_synced_at ? $record->paddle_synced_at->diffForHumans() : '')
->columnSpanFull(),
Placeholder::make('paddle_sync_error')
->label('Letzter Fehler')
->content(fn (?Package $record) => $record?->paddle_sync_error_message ?? '')
->visible(fn (?Package $record) => filled($record?->paddle_sync_error_message))
->columnSpanFull(),
]),
]);
}
@@ -280,6 +285,11 @@ class PackageResource extends Resource
->label('Sync am')
->dateTime()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('paddle_sync_error_message')
->label('Sync-Fehler')
->getStateUsing(fn (Package $record) => $record->paddle_sync_error_message)
->wrap()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
Tables\Filters\SelectFilter::make('type')

View File

@@ -145,6 +145,13 @@ class Package extends Model
];
}
public function getPaddleSyncErrorMessageAttribute(): ?string
{
$message = data_get($this->paddle_snapshot, 'error.message');
return is_string($message) && $message !== '' ? $message : null;
}
public function getActivatesImmediatelyAttribute(): bool
{
// Default: Pakete werden nach Kauf sofort freigeschaltet (digitale Dienstleistung).

View File

@@ -0,0 +1,38 @@
<?php
namespace Tests\Unit;
use App\Models\Package;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PackagePaddleSyncErrorTest extends TestCase
{
use RefreshDatabase;
public function test_paddle_sync_error_message_returns_message_when_present(): void
{
$package = Package::factory()->create([
'paddle_snapshot' => [
'error' => [
'message' => 'Sync failed.',
],
],
]);
$this->assertSame('Sync failed.', $package->paddle_sync_error_message);
}
public function test_paddle_sync_error_message_returns_null_when_missing(): void
{
$package = Package::factory()->create([
'paddle_snapshot' => [
'product' => [
'id' => 'pro_123',
],
],
]);
$this->assertNull($package->paddle_sync_error_message);
}
}