feat(subscribe): queue Weeztix coupon, then Mailwizz; document queues

- RegisterSubscriberOnPage: persist subscriber then dispatch integrations
- IssueWeeztixCouponForSubscriber on weeztix queue; dispatches Mailwizz after
  coupon attempt (idempotent if coupon_code already set); failed() fallback
- SyncSubscriberToMailwizz implements ShouldQueueAfterCommit
- Deployment: worker listens weeztix,mailwizz,default; warn against sync in prod
- .env.example: QUEUE_CONNECTION notes for subscribe UX

Made-with: Cursor
This commit is contained in:
2026-04-05 11:34:01 +02:00
parent 7ed660ec55
commit d802ce2a7c
6 changed files with 222 additions and 71 deletions

View File

@@ -4,22 +4,20 @@ declare(strict_types=1);
namespace App\Http\Controllers;
use App\Exceptions\WeeztixCouponCodeConflictException;
use App\Http\Requests\SubscribePublicPageRequest;
use App\Jobs\SyncSubscriberToMailwizz;
use App\Models\PageBlock;
use App\Models\PreregistrationPage;
use App\Models\Subscriber;
use App\Models\WeeztixConfig;
use App\Services\WeeztixService;
use App\Services\RegisterSubscriberOnPage;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Throwable;
class PublicPageController extends Controller
{
public function __construct(
private readonly RegisterSubscriberOnPage $registerSubscriberOnPage
) {}
public function show(PreregistrationPage $publicPage): View
{
$publicPage->load(['blocks' => fn ($q) => $q->orderBy('sort_order')]);
@@ -56,17 +54,7 @@ class PublicPageController extends Controller
], 422);
}
$subscriber = $publicPage->subscribers()->create($validated);
$publicPage->loadMissing('weeztixConfig');
$weeztix = $publicPage->weeztixConfig;
if ($this->weeztixCanIssueCodes($weeztix)) {
$this->tryAttachWeeztixCouponCode($subscriber, $weeztix);
}
if ($publicPage->mailwizzConfig !== null) {
SyncSubscriberToMailwizz::dispatch($subscriber->fresh());
}
$this->registerSubscriberOnPage->storeAndQueueIntegrations($publicPage, $validated);
return response()->json([
'success' => true,
@@ -74,56 +62,6 @@ class PublicPageController extends Controller
]);
}
private function weeztixCanIssueCodes(?WeeztixConfig $config): bool
{
if ($config === null || ! $config->is_connected) {
return false;
}
$company = $config->company_guid;
$coupon = $config->coupon_guid;
return is_string($company) && $company !== '' && is_string($coupon) && $coupon !== '';
}
private function tryAttachWeeztixCouponCode(Subscriber $subscriber, WeeztixConfig $config): void
{
$freshConfig = $config->fresh();
if ($freshConfig === null) {
return;
}
$service = new WeeztixService($freshConfig);
$maxAttempts = 5;
for ($attempt = 0; $attempt < $maxAttempts; $attempt++) {
try {
$code = WeeztixService::generateUniqueCode(
is_string($freshConfig->code_prefix) && $freshConfig->code_prefix !== ''
? $freshConfig->code_prefix
: 'PREREG'
);
$service->createCouponCode($code);
$subscriber->update(['coupon_code' => $code]);
return;
} catch (WeeztixCouponCodeConflictException) {
continue;
} catch (Throwable $e) {
Log::error('Weeztix coupon creation failed', [
'subscriber_id' => $subscriber->id,
'message' => $e->getMessage(),
]);
return;
}
}
Log::warning('Weeztix coupon: exhausted duplicate retries', [
'subscriber_id' => $subscriber->id,
]);
}
private function resolvePageState(PreregistrationPage $page): string
{
if ($page->isBeforeStart()) {