Implement Weeztix integration per documentation: database config and subscriber coupon_code, OAuth redirect/callback, admin setup UI with company/coupon selection via AJAX, synchronous coupon creation on public subscribe with duplicate and rate-limit handling, Mailwizz field mapping for coupon codes, subscriber table and CSV export, and connection hint on the pages list. Made-with: Cursor
222 lines
6.9 KiB
PHP
222 lines
6.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Jobs;
|
|
|
|
use App\Models\MailwizzConfig;
|
|
use App\Models\Subscriber;
|
|
use App\Services\MailwizzService;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|
use Illuminate\Queue\SerializesModels;
|
|
use Illuminate\Support\Facades\Log;
|
|
use RuntimeException;
|
|
use Throwable;
|
|
|
|
class SyncSubscriberToMailwizz implements ShouldBeUnique, ShouldQueue
|
|
{
|
|
use Dispatchable;
|
|
use InteractsWithQueue;
|
|
use Queueable;
|
|
use SerializesModels;
|
|
|
|
/**
|
|
* Seconds before the unique lock expires if the worker dies before releasing it.
|
|
*/
|
|
public int $uniqueFor = 86400;
|
|
|
|
public int $tries = 3;
|
|
|
|
/**
|
|
* @var list<int>
|
|
*/
|
|
public array $backoff = [10, 30, 60];
|
|
|
|
public function __construct(public Subscriber $subscriber)
|
|
{
|
|
$this->onQueue('mailwizz');
|
|
}
|
|
|
|
public function uniqueId(): string
|
|
{
|
|
return (string) $this->subscriber->getKey();
|
|
}
|
|
|
|
public function handle(): void
|
|
{
|
|
$subscriber = Subscriber::query()
|
|
->with(['preregistrationPage.mailwizzConfig'])
|
|
->find($this->subscriber->id);
|
|
|
|
if ($subscriber === null) {
|
|
return;
|
|
}
|
|
|
|
$page = $subscriber->preregistrationPage;
|
|
$config = $page?->mailwizzConfig;
|
|
|
|
if ($page === null || $config === null) {
|
|
return;
|
|
}
|
|
|
|
if (! $this->configIsComplete($config)) {
|
|
Log::warning('SyncSubscriberToMailwizz: incomplete Mailwizz config', [
|
|
'subscriber_id' => $subscriber->id,
|
|
'page_id' => $page->id,
|
|
]);
|
|
$this->fail(new RuntimeException('Incomplete Mailwizz configuration.'));
|
|
|
|
return;
|
|
}
|
|
|
|
$apiKey = $config->api_key;
|
|
if (! is_string($apiKey) || $apiKey === '') {
|
|
Log::warning('SyncSubscriberToMailwizz: missing API key', ['subscriber_id' => $subscriber->id]);
|
|
$this->fail(new RuntimeException('Mailwizz API key is missing.'));
|
|
|
|
return;
|
|
}
|
|
|
|
$service = new MailwizzService($apiKey);
|
|
$listUid = $config->list_uid;
|
|
|
|
$search = $service->searchSubscriber($listUid, $subscriber->email);
|
|
|
|
if ($search === null) {
|
|
$this->createInMailwizz($service, $subscriber, $config, $listUid);
|
|
} else {
|
|
$this->updateInMailwizz($service, $subscriber, $config, $listUid, $search['subscriber_uid']);
|
|
}
|
|
|
|
$subscriber->update([
|
|
'synced_to_mailwizz' => true,
|
|
'synced_at' => now(),
|
|
]);
|
|
}
|
|
|
|
public function failed(?Throwable $exception): void
|
|
{
|
|
Log::error('SyncSubscriberToMailwizz failed', [
|
|
'subscriber_id' => $this->subscriber->id,
|
|
'message' => $exception?->getMessage(),
|
|
]);
|
|
}
|
|
|
|
private function configIsComplete(MailwizzConfig $config): bool
|
|
{
|
|
if ($config->list_uid === '' || $config->field_email === '' || $config->field_first_name === '' || $config->field_last_name === '') {
|
|
return false;
|
|
}
|
|
|
|
if ($config->tag_field === null || $config->tag_field === '' || $config->tag_value === null || $config->tag_value === '') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function buildBasePayload(Subscriber $subscriber, MailwizzConfig $config, bool $phoneEnabled): array
|
|
{
|
|
$data = [
|
|
$config->field_email => $subscriber->email,
|
|
$config->field_first_name => $subscriber->first_name,
|
|
$config->field_last_name => $subscriber->last_name,
|
|
];
|
|
|
|
if ($phoneEnabled && $config->field_phone !== null && $config->field_phone !== '') {
|
|
$phone = $subscriber->phoneDisplay();
|
|
if ($phone !== null && $phone !== '') {
|
|
$data[$config->field_phone] = $phone;
|
|
}
|
|
}
|
|
|
|
$couponField = $config->field_coupon_code;
|
|
if (is_string($couponField) && $couponField !== '' && $subscriber->coupon_code !== null && $subscriber->coupon_code !== '') {
|
|
$data[$couponField] = $subscriber->coupon_code;
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
private function createInMailwizz(
|
|
MailwizzService $service,
|
|
Subscriber $subscriber,
|
|
MailwizzConfig $config,
|
|
string $listUid
|
|
): void {
|
|
$page = $subscriber->preregistrationPage;
|
|
$data = $this->buildBasePayload($subscriber, $config, $page->isPhoneFieldEnabledForSubscribers());
|
|
$tagField = $config->tag_field;
|
|
$tagValue = $config->tag_value;
|
|
if ($tagField !== null && $tagField !== '' && $tagValue !== null && $tagValue !== '') {
|
|
$data[$tagField] = [$tagValue];
|
|
}
|
|
|
|
$service->createSubscriber($listUid, $data);
|
|
}
|
|
|
|
private function updateInMailwizz(
|
|
MailwizzService $service,
|
|
Subscriber $subscriber,
|
|
MailwizzConfig $config,
|
|
string $listUid,
|
|
string $subscriberUid
|
|
): void {
|
|
$page = $subscriber->preregistrationPage;
|
|
$data = $this->buildBasePayload($subscriber, $config, $page->isPhoneFieldEnabledForSubscribers());
|
|
|
|
$tagField = $config->tag_field;
|
|
$tagValue = $config->tag_value;
|
|
if ($tagField !== null && $tagField !== '' && $tagValue !== null && $tagValue !== '') {
|
|
$full = $service->getSubscriber($listUid, $subscriberUid);
|
|
if ($full === null) {
|
|
throw new RuntimeException('Mailwizz getSubscriber returned an empty payload.');
|
|
}
|
|
$existingCsv = $this->extractTagCsvFromResponse($full, $tagField);
|
|
$merged = $this->mergeCheckboxlistTags($existingCsv, $tagValue);
|
|
$data[$tagField] = $merged;
|
|
}
|
|
|
|
$service->updateSubscriber($listUid, $subscriberUid, $data);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $apiResponse
|
|
*/
|
|
private function extractTagCsvFromResponse(array $apiResponse, string $tagField): string
|
|
{
|
|
$record = data_get($apiResponse, 'data.record');
|
|
if (! is_array($record)) {
|
|
$record = data_get($apiResponse, 'data');
|
|
}
|
|
if (! is_array($record)) {
|
|
return '';
|
|
}
|
|
|
|
$raw = $record[$tagField] ?? data_get($record, "fields.{$tagField}");
|
|
|
|
if (is_array($raw)) {
|
|
return implode(',', array_map(static fn (mixed $v): string => is_scalar($v) ? (string) $v : '', $raw));
|
|
}
|
|
|
|
return is_string($raw) ? $raw : '';
|
|
}
|
|
|
|
/**
|
|
* @return list<string>
|
|
*/
|
|
private function mergeCheckboxlistTags(string $existingCsv, string $newValue): array
|
|
{
|
|
$parts = array_filter(array_map('trim', explode(',', $existingCsv)), static fn (string $s): bool => $s !== '');
|
|
if (! in_array($newValue, $parts, true)) {
|
|
$parts[] = $newValue;
|
|
}
|
|
|
|
return array_values($parts);
|
|
}
|
|
}
|