feat: clean Weeztix and Mailwizz when admin deletes subscriber
Run CleanupSubscriberIntegrationsService before delete: remove coupon code in Weeztix via list+DELETE API; update Mailwizz contact to strip configured source tag from the tag field and clear the mapped coupon field. Extract MailwizzCheckboxlistTags and MailwizzSubscriberFormPayload for shared sync/cleanup behaviour. Add WeeztixService list and delete helpers. Integration failures are logged only; local delete always proceeds. Feature tests cover Mailwizz strip+clear and Weeztix delete paths. Made-with: Cursor
This commit is contained in:
@@ -250,6 +250,159 @@ final class WeeztixService
|
||||
throw new RuntimeException('Weeztix API rate limited after retries.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists coupon codes for the coupon selected in config (GET /coupon/{guid}/codes).
|
||||
*
|
||||
* @return list<array{guid: string, code: string}>
|
||||
*/
|
||||
public function listCouponCodesForConfiguredCoupon(): array
|
||||
{
|
||||
$this->assertCompanyGuid();
|
||||
$couponGuid = $this->config->coupon_guid;
|
||||
if (! is_string($couponGuid) || $couponGuid === '') {
|
||||
throw new LogicException('Weeztix coupon is not configured.');
|
||||
}
|
||||
|
||||
$url = config('weeztix.api_base_url').'/coupon/'.$couponGuid.'/codes';
|
||||
$json = $this->apiRequest('get', $url, []);
|
||||
|
||||
return $this->normalizeCouponCodeListResponse($json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft-deletes a coupon code in Weeztix by matching the human-readable code string.
|
||||
*/
|
||||
public function deleteCouponCodeByCodeString(string $code): void
|
||||
{
|
||||
$trimmed = trim($code);
|
||||
if ($trimmed === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->assertCompanyGuid();
|
||||
$couponGuid = $this->config->coupon_guid;
|
||||
if (! is_string($couponGuid) || $couponGuid === '') {
|
||||
throw new LogicException('Weeztix coupon is not configured.');
|
||||
}
|
||||
|
||||
$rows = $this->listCouponCodesForConfiguredCoupon();
|
||||
$codeGuid = null;
|
||||
foreach ($rows as $row) {
|
||||
if (strcasecmp($row['code'], $trimmed) === 0) {
|
||||
$codeGuid = $row['guid'];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($codeGuid === null) {
|
||||
Log::info('Weeztix: coupon code not found when deleting (already removed or unknown)', [
|
||||
'code' => $trimmed,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$url = config('weeztix.api_base_url').'/coupon/'.$couponGuid.'/codes/'.$codeGuid;
|
||||
$token = $this->getValidAccessToken();
|
||||
$response = $this->sendApiRequest('delete', $url, [], $token);
|
||||
|
||||
if ($response->status() === 401) {
|
||||
$this->refreshAccessToken();
|
||||
$this->config->refresh();
|
||||
$response = $this->sendApiRequest('delete', $url, [], (string) $this->config->access_token);
|
||||
}
|
||||
|
||||
if ($response->status() === 404) {
|
||||
Log::info('Weeztix: coupon code already deleted remotely', [
|
||||
'code' => $trimmed,
|
||||
'code_guid' => $codeGuid,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($response->failed()) {
|
||||
$this->logFailedResponse('deleteCouponCodeByCodeString', $url, $response);
|
||||
|
||||
throw new RuntimeException('Weeztix API delete coupon code failed: '.$response->status());
|
||||
}
|
||||
|
||||
Log::debug('Weeztix API', [
|
||||
'action' => 'deleteCouponCodeByCodeString',
|
||||
'url' => $url,
|
||||
'http_status' => $response->status(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $json
|
||||
* @return list<array{guid: string, code: string}>
|
||||
*/
|
||||
private function normalizeCouponCodeListResponse(array $json): array
|
||||
{
|
||||
$candidates = [
|
||||
data_get($json, 'data'),
|
||||
data_get($json, 'data.codes'),
|
||||
data_get($json, 'data.records'),
|
||||
data_get($json, 'codes'),
|
||||
$json,
|
||||
];
|
||||
|
||||
foreach ($candidates as $raw) {
|
||||
if (! is_array($raw)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->isListArray($raw)) {
|
||||
$normalized = $this->normalizeCouponCodeRows($raw);
|
||||
if ($normalized !== []) {
|
||||
return $normalized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $rows
|
||||
* @return list<array{guid: string, code: string}>
|
||||
*/
|
||||
private function normalizeCouponCodeRows(array $rows): array
|
||||
{
|
||||
$out = [];
|
||||
foreach ($rows as $row) {
|
||||
if (! is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$guid = data_get($row, 'guid')
|
||||
?? data_get($row, 'id')
|
||||
?? data_get($row, 'coupon_code_guid');
|
||||
$code = data_get($row, 'code')
|
||||
?? data_get($row, 'coupon_code')
|
||||
?? data_get($row, 'name');
|
||||
if (! is_string($guid) || $guid === '' || ! is_string($code) || $code === '') {
|
||||
continue;
|
||||
}
|
||||
$out[] = ['guid' => $guid, 'code' => $code];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, mixed> $arr
|
||||
*/
|
||||
private function isListArray(array $arr): bool
|
||||
{
|
||||
if ($arr === []) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return array_keys($arr) === range(0, count($arr) - 1);
|
||||
}
|
||||
|
||||
public static function generateUniqueCode(string $prefix = 'PREREG', int $length = 6): string
|
||||
{
|
||||
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
||||
|
||||
Reference in New Issue
Block a user