Link existing Paddle IDs
This commit is contained in:
@@ -24,7 +24,7 @@
|
|||||||
{"id":"fotospiel-app-64l","title":"SEC-FE-01 CSP nonce/hashing rollout","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:54:47.607047443+01:00","created_by":"soeren","updated_at":"2026-01-01T15:55:56.477104351+01:00","closed_at":"2026-01-01T15:55:56.477104351+01:00","close_reason":"Completed in codebase (verified) - duplicate of fotospiel-app-zli"}
|
{"id":"fotospiel-app-64l","title":"SEC-FE-01 CSP nonce/hashing rollout","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:54:47.607047443+01:00","created_by":"soeren","updated_at":"2026-01-01T15:55:56.477104351+01:00","closed_at":"2026-01-01T15:55:56.477104351+01:00","close_reason":"Completed in codebase (verified) - duplicate of fotospiel-app-zli"}
|
||||||
{"id":"fotospiel-app-6dp","title":"Coupon ops enhancements (redemption service, preview endpoint, widget, export)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:09:09.275919717+01:00","created_by":"soeren","updated_at":"2026-01-01T16:09:14.882264149+01:00","closed_at":"2026-01-01T16:09:14.882264149+01:00","close_reason":"Completed in codebase (verified)"}
|
{"id":"fotospiel-app-6dp","title":"Coupon ops enhancements (redemption service, preview endpoint, widget, export)","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:09:09.275919717+01:00","created_by":"soeren","updated_at":"2026-01-01T16:09:14.882264149+01:00","closed_at":"2026-01-01T16:09:14.882264149+01:00","close_reason":"Completed in codebase (verified)"}
|
||||||
{"id":"fotospiel-app-6oj","title":"Security review: media pipeline code audit (AV/EXIF, signed URLs, storage separation)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:31.390878341+01:00","created_by":"soeren","updated_at":"2026-01-01T16:05:31.390878341+01:00"}
|
{"id":"fotospiel-app-6oj","title":"Security review: media pipeline code audit (AV/EXIF, signed URLs, storage separation)","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:05:31.390878341+01:00","created_by":"soeren","updated_at":"2026-01-01T16:05:31.390878341+01:00"}
|
||||||
{"id":"fotospiel-app-6yt","title":"Paddle migration: register sandbox webhooks + document events consumed","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:56:34.333714988+01:00","created_by":"soeren","updated_at":"2026-01-01T15:56:34.333714988+01:00"}
|
{"id":"fotospiel-app-6yt","title":"Paddle migration: register sandbox webhooks + document events consumed","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:56:34.333714988+01:00","created_by":"soeren","updated_at":"2026-01-02T22:23:52.212191068+01:00","closed_at":"2026-01-02T22:23:52.212191068+01:00","close_reason":"Completed"}
|
||||||
{"id":"fotospiel-app-7bu","title":"Paddle migration: extend config/env handling for Paddle keys/webhook secrets","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:57:27.242854801+01:00","created_by":"soeren","updated_at":"2026-01-01T15:57:32.890355888+01:00","closed_at":"2026-01-01T15:57:32.890355888+01:00","close_reason":"Completed in codebase (verified)"}
|
{"id":"fotospiel-app-7bu","title":"Paddle migration: extend config/env handling for Paddle keys/webhook secrets","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:57:27.242854801+01:00","created_by":"soeren","updated_at":"2026-01-01T15:57:32.890355888+01:00","closed_at":"2026-01-01T15:57:32.890355888+01:00","close_reason":"Completed in codebase (verified)"}
|
||||||
{"id":"fotospiel-app-7u1","title":"Paddle catalog sync: PaddlePackagePull job","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:00:47.468892178+01:00","created_by":"soeren","updated_at":"2026-01-01T16:00:53.126602817+01:00","closed_at":"2026-01-01T16:00:53.126602817+01:00","close_reason":"Completed in codebase (verified)"}
|
{"id":"fotospiel-app-7u1","title":"Paddle catalog sync: PaddlePackagePull job","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:00:47.468892178+01:00","created_by":"soeren","updated_at":"2026-01-01T16:00:53.126602817+01:00","closed_at":"2026-01-01T16:00:53.126602817+01:00","close_reason":"Completed in codebase (verified)"}
|
||||||
{"id":"fotospiel-app-95m","title":"Paddle migration: admin catalog sync UI for packages","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:57:49.790409261+01:00","created_by":"soeren","updated_at":"2026-01-01T15:57:55.418180246+01:00","closed_at":"2026-01-01T15:57:55.418180246+01:00","close_reason":"Completed in codebase (verified)"}
|
{"id":"fotospiel-app-95m","title":"Paddle migration: admin catalog sync UI for packages","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:57:49.790409261+01:00","created_by":"soeren","updated_at":"2026-01-01T15:57:55.418180246+01:00","closed_at":"2026-01-01T15:57:55.418180246+01:00","close_reason":"Completed in codebase (verified)"}
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
{"id":"fotospiel-app-mx5","title":"Localized SEO: sitemap updated with locale alternates","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:15.177013722+01:00","created_by":"soeren","updated_at":"2026-01-01T16:02:20.812287917+01:00","closed_at":"2026-01-01T16:02:20.812287917+01:00","close_reason":"Completed in codebase (verified)"}
|
{"id":"fotospiel-app-mx5","title":"Localized SEO: sitemap updated with locale alternates","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T16:02:15.177013722+01:00","created_by":"soeren","updated_at":"2026-01-01T16:02:20.812287917+01:00","closed_at":"2026-01-01T16:02:20.812287917+01:00","close_reason":"Completed in codebase (verified)"}
|
||||||
{"id":"fotospiel-app-mxw","title":"Security review: configure env assumptions for dynamic testing","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:04:29.498402235+01:00","created_by":"soeren","updated_at":"2026-01-01T16:04:29.498402235+01:00"}
|
{"id":"fotospiel-app-mxw","title":"Security review: configure env assumptions for dynamic testing","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T16:04:29.498402235+01:00","created_by":"soeren","updated_at":"2026-01-01T16:04:29.498402235+01:00"}
|
||||||
{"id":"fotospiel-app-n8q","title":"Paddle migration: draft production cutover procedure","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:56:51.427425262+01:00","created_by":"soeren","updated_at":"2026-01-01T15:56:51.427425262+01:00"}
|
{"id":"fotospiel-app-n8q","title":"Paddle migration: draft production cutover procedure","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-01T15:56:51.427425262+01:00","created_by":"soeren","updated_at":"2026-01-01T15:56:51.427425262+01:00"}
|
||||||
{"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-nfi","title":"Paddle catalog sync: add Link existing Paddle entity action in admin","status":"closed","priority":2,"issue_type":"task","created_at":"2026-01-01T15:59:09.164334657+01:00","created_by":"soeren","updated_at":"2026-01-02T22:15:15.030896509+01:00","closed_at":"2026-01-02T22:15:15.030896509+01:00","close_reason":"Completed"}
|
||||||
{"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-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-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":"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-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"}
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ class PackageResource extends Resource
|
|||||||
->colors([
|
->colors([
|
||||||
'success' => 'synced',
|
'success' => 'synced',
|
||||||
'warning' => 'syncing',
|
'warning' => 'syncing',
|
||||||
'info' => 'dry-run',
|
'info' => ['dry-run', 'linked', 'pulled'],
|
||||||
'danger' => ['failed', 'pull-failed'],
|
'danger' => ['failed', 'pull-failed'],
|
||||||
])
|
])
|
||||||
->formatStateUsing(fn ($state) => $state ? Str::headline($state) : null)
|
->formatStateUsing(fn ($state) => $state ? Str::headline($state) : null)
|
||||||
@@ -316,6 +316,42 @@ class PackageResource extends Resource
|
|||||||
->body('Das Paket wird im Hintergrund mit Paddle abgeglichen.')
|
->body('Das Paket wird im Hintergrund mit Paddle abgeglichen.')
|
||||||
->send();
|
->send();
|
||||||
}),
|
}),
|
||||||
|
Actions\Action::make('linkPaddle')
|
||||||
|
->label('Paddle verknüpfen')
|
||||||
|
->icon('heroicon-o-link')
|
||||||
|
->color('info')
|
||||||
|
->form([
|
||||||
|
TextInput::make('paddle_product_id')
|
||||||
|
->label('Paddle Produkt-ID')
|
||||||
|
->required()
|
||||||
|
->maxLength(191),
|
||||||
|
TextInput::make('paddle_price_id')
|
||||||
|
->label('Paddle Preis-ID')
|
||||||
|
->required()
|
||||||
|
->maxLength(191),
|
||||||
|
])
|
||||||
|
->fillForm(fn (Package $record) => [
|
||||||
|
'paddle_product_id' => $record->paddle_product_id,
|
||||||
|
'paddle_price_id' => $record->paddle_price_id,
|
||||||
|
])
|
||||||
|
->action(function (Package $record, array $data): void {
|
||||||
|
$record->linkPaddleIds($data['paddle_product_id'], $data['paddle_price_id']);
|
||||||
|
|
||||||
|
PullPackageFromPaddle::dispatch($record->id);
|
||||||
|
|
||||||
|
app(SuperAdminAuditLogger::class)->recordModelMutation(
|
||||||
|
'linked',
|
||||||
|
$record,
|
||||||
|
SuperAdminAuditLogger::fieldsMetadata($data),
|
||||||
|
static::class
|
||||||
|
);
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->success()
|
||||||
|
->title('Paddle-Verknüpfung gespeichert')
|
||||||
|
->body('Die IDs wurden gespeichert und ein Pull wurde angestoßen.')
|
||||||
|
->send();
|
||||||
|
}),
|
||||||
Actions\Action::make('pullPaddle')
|
Actions\Action::make('pullPaddle')
|
||||||
->label('Status von Paddle holen')
|
->label('Status von Paddle holen')
|
||||||
->icon('heroicon-o-cloud-arrow-down')
|
->icon('heroicon-o-cloud-arrow-down')
|
||||||
|
|||||||
@@ -152,6 +152,16 @@ class Package extends Model
|
|||||||
return is_string($message) && $message !== '' ? $message : null;
|
return is_string($message) && $message !== '' ? $message : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function linkPaddleIds(string $productId, string $priceId): void
|
||||||
|
{
|
||||||
|
$this->forceFill([
|
||||||
|
'paddle_product_id' => $productId,
|
||||||
|
'paddle_price_id' => $priceId,
|
||||||
|
'paddle_sync_status' => 'linked',
|
||||||
|
'paddle_synced_at' => now(),
|
||||||
|
])->save();
|
||||||
|
}
|
||||||
|
|
||||||
public function getActivatesImmediatelyAttribute(): bool
|
public function getActivatesImmediatelyAttribute(): bool
|
||||||
{
|
{
|
||||||
// Default: Pakete werden nach Kauf sofort freigeschaltet (digitale Dienstleistung).
|
// Default: Pakete werden nach Kauf sofort freigeschaltet (digitale Dienstleistung).
|
||||||
|
|||||||
31
tests/Unit/PackagePaddleLinkTest.php
Normal file
31
tests/Unit/PackagePaddleLinkTest.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit;
|
||||||
|
|
||||||
|
use App\Models\Package;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class PackagePaddleLinkTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function test_link_paddle_ids_updates_fields(): void
|
||||||
|
{
|
||||||
|
$package = Package::factory()->create([
|
||||||
|
'paddle_product_id' => null,
|
||||||
|
'paddle_price_id' => null,
|
||||||
|
'paddle_sync_status' => null,
|
||||||
|
'paddle_synced_at' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$package->linkPaddleIds('pro_123', 'pri_123');
|
||||||
|
|
||||||
|
$package->refresh();
|
||||||
|
|
||||||
|
$this->assertSame('pro_123', $package->paddle_product_id);
|
||||||
|
$this->assertSame('pri_123', $package->paddle_price_id);
|
||||||
|
$this->assertSame('linked', $package->paddle_sync_status);
|
||||||
|
$this->assertNotNull($package->paddle_synced_at);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user