Deploy Ladill Hosting / deploy (push) Failing after 17s
Shared web hosting extracted from the platform monolith, with CI deploy to /var/www/ladill-hosting matching Bird/Domains/Email. Co-authored-by: Cursor <cursoragent@cursor.com>
656 lines
23 KiB
PHP
656 lines
23 KiB
PHP
<?php
|
|
|
|
namespace App\Services\ResellerClub;
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class ResellerClubCustomerService
|
|
{
|
|
private const DEFAULT_API_URL = 'https://httpapi.com/api';
|
|
|
|
private string $apiUrl;
|
|
|
|
private string $authUserId;
|
|
|
|
private string $apiKey;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->apiUrl = rtrim($this->configuredApiUrl(), '/');
|
|
$this->authUserId = (string) config('mailinfra.registrar_auth_userid');
|
|
$this->apiKey = (string) config('mailinfra.registrar_api_key');
|
|
}
|
|
|
|
public function isConfigured(): bool
|
|
{
|
|
return $this->authUserId !== '' && $this->apiKey !== '';
|
|
}
|
|
|
|
/**
|
|
* Build a URL with all parameters as query string values, including auth.
|
|
*
|
|
* @param array<string, mixed> $params
|
|
*/
|
|
private function buildQueryUrl(string $endpoint, array $params = []): string
|
|
{
|
|
$all = array_merge([
|
|
'auth-userid' => $this->authUserId,
|
|
'api-key' => $this->apiKey,
|
|
], $params);
|
|
|
|
$separator = str_contains($endpoint, '?') ? '&' : '?';
|
|
|
|
return $endpoint.$separator.http_build_query($all);
|
|
}
|
|
|
|
/**
|
|
* @return array{success: bool, customer_id?: string, created?: bool, message?: string}
|
|
*/
|
|
public function resolveCustomerForUser(User $user): array
|
|
{
|
|
if (! $this->isConfigured()) {
|
|
return ['success' => false, 'message' => 'ResellerClub is not configured.'];
|
|
}
|
|
|
|
if ($user->hasResellerClubCustomer()) {
|
|
return [
|
|
'success' => true,
|
|
'customer_id' => (string) $user->rc_customer_id,
|
|
'created' => false,
|
|
];
|
|
}
|
|
|
|
$email = strtolower(trim((string) $user->email));
|
|
if ($email === '') {
|
|
return ['success' => false, 'message' => 'User email is required to provision a ResellerClub customer.'];
|
|
}
|
|
|
|
$lookup = $this->findCustomerByEmail($email);
|
|
if (! ($lookup['success'] ?? false)) {
|
|
return ['success' => false, 'message' => $lookup['message'] ?? 'Could not verify the ResellerClub customer.'];
|
|
}
|
|
|
|
if ($lookup['found'] ?? false) {
|
|
return $this->attachCustomerToUser(
|
|
$user,
|
|
(string) $lookup['customer_id'],
|
|
$email,
|
|
false
|
|
);
|
|
}
|
|
|
|
$created = $this->createCustomerForUser($user);
|
|
if (! ($created['success'] ?? false)) {
|
|
return ['success' => false, 'message' => $created['message'] ?? 'Could not create the ResellerClub customer.'];
|
|
}
|
|
|
|
$attached = $this->attachCustomerToUser(
|
|
$user,
|
|
(string) $created['customer_id'],
|
|
$email,
|
|
false
|
|
);
|
|
|
|
if (! ($attached['success'] ?? false)) {
|
|
return $attached;
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'customer_id' => (string) $attached['customer_id'],
|
|
'created' => true,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Best-effort customer provisioning for user creation hooks.
|
|
*/
|
|
public function provisionCustomerForUser(User $user): ?string
|
|
{
|
|
$result = $this->resolveCustomerForUser($user);
|
|
|
|
if (! ($result['success'] ?? false)) {
|
|
Log::warning('ResellerClubCustomerService: automatic provisioning skipped', [
|
|
'user_id' => $user->id,
|
|
'message' => $result['message'] ?? 'Unknown error.',
|
|
]);
|
|
|
|
return null;
|
|
}
|
|
|
|
return (string) ($result['customer_id'] ?? '');
|
|
}
|
|
|
|
/**
|
|
* @return array{success: bool, customer_id?: string, found?: bool, message?: string}
|
|
*/
|
|
public function findCustomerByEmail(string $email): array
|
|
{
|
|
try {
|
|
$response = Http::timeout(15)->get($this->apiUrl.'/customers/details.json', [
|
|
'auth-userid' => $this->authUserId,
|
|
'api-key' => $this->apiKey,
|
|
'username' => strtolower(trim($email)),
|
|
]);
|
|
|
|
$data = $response->json();
|
|
|
|
if ($response->successful() && is_array($data)) {
|
|
$customerId = (string) ($data['customerid'] ?? $data['customer-id'] ?? '');
|
|
|
|
if ($customerId !== '') {
|
|
return [
|
|
'success' => true,
|
|
'found' => true,
|
|
'customer_id' => $customerId,
|
|
];
|
|
}
|
|
}
|
|
|
|
if (in_array($response->status(), [400, 404], true)) {
|
|
return [
|
|
'success' => true,
|
|
'found' => false,
|
|
];
|
|
}
|
|
|
|
$message = $this->extractErrorMessage(is_array($data) ? $data : [], $response->body());
|
|
|
|
if ($message !== '' && $this->looksLikeMissingCustomerMessage($message)) {
|
|
return [
|
|
'success' => true,
|
|
'found' => false,
|
|
];
|
|
}
|
|
|
|
Log::warning('ResellerClubCustomerService: customer lookup failed', [
|
|
'email' => $email,
|
|
'status' => $response->status(),
|
|
'response' => $data ?: $response->body(),
|
|
]);
|
|
|
|
return [
|
|
'success' => false,
|
|
'message' => $message !== '' ? $message : 'Could not look up the ResellerClub customer.',
|
|
];
|
|
} catch (\Throwable $e) {
|
|
Log::error('ResellerClubCustomerService: findCustomerByEmail failed', [
|
|
'email' => $email,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return ['success' => false, 'message' => 'Could not look up the ResellerClub customer.'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array{success: bool, customer_id?: string, message?: string}
|
|
*/
|
|
public function createCustomerForUser(User $user): array
|
|
{
|
|
$defaults = (array) config('mailinfra.resellerclub_customer_defaults', []);
|
|
$email = strtolower(trim((string) $user->email));
|
|
$name = trim(implode(' ', array_filter([
|
|
trim((string) ($user->first_name ?? '')),
|
|
trim((string) ($user->last_name ?? '')),
|
|
])));
|
|
|
|
try {
|
|
$response = Http::timeout(30)->post($this->buildQueryUrl($this->apiUrl.'/customers/v2/signup.xml', [
|
|
'username' => $email,
|
|
'passwd' => $this->generatePlatformPassword(),
|
|
'name' => $name !== '' ? $name : (trim((string) $user->name) !== '' ? (string) $user->name : $email),
|
|
'company' => trim((string) ($user->company ?? '')) !== '' ? (string) $user->company : (string) ($defaults['company'] ?? config('app.name', 'Ladill')),
|
|
'address-line-1' => trim((string) ($user->address_line_1 ?? '')) !== '' ? (string) $user->address_line_1 : (string) ($defaults['address_line_1'] ?? 'Not provided'),
|
|
'city' => trim((string) ($user->city ?? '')) !== '' ? (string) $user->city : (string) ($defaults['city'] ?? 'Accra'),
|
|
'state' => trim((string) ($user->state ?? '')) !== '' ? (string) $user->state : (string) ($defaults['state'] ?? 'Not Applicable'),
|
|
'country' => trim((string) ($user->country ?? '')) !== '' ? strtoupper((string) $user->country) : (string) ($defaults['country'] ?? 'GH'),
|
|
'zipcode' => trim((string) ($user->zipcode ?? '')) !== '' ? (string) $user->zipcode : (string) ($defaults['zipcode'] ?? '00000'),
|
|
'phone-cc' => trim((string) ($user->phone_cc ?? '')) !== '' ? ltrim((string) $user->phone_cc, '+') : (string) ($defaults['phone_cc'] ?? '233'),
|
|
'phone' => trim((string) ($user->phone ?? '')) !== '' ? (string) $user->phone : (string) ($defaults['phone'] ?? '000000000'),
|
|
'lang-pref' => (string) ($defaults['lang_pref'] ?? 'en'),
|
|
'address-line-2' => trim((string) ($user->address_line_2 ?? '')) !== '' ? (string) $user->address_line_2 : (string) ($defaults['address_line_2'] ?? ''),
|
|
'address-line-3' => (string) ($defaults['address_line_3'] ?? ''),
|
|
]));
|
|
|
|
$body = trim((string) $response->body());
|
|
$customerId = $this->extractCustomerIdFromBody($body);
|
|
|
|
if ($response->failed() || $customerId === '') {
|
|
$message = $this->extractXmlErrorMessage($body);
|
|
|
|
Log::warning('ResellerClubCustomerService: customer signup failed', [
|
|
'user_id' => $user->id,
|
|
'email' => $email,
|
|
'status' => $response->status(),
|
|
'body' => $body,
|
|
]);
|
|
|
|
return [
|
|
'success' => false,
|
|
'message' => $message !== '' ? $message : 'Could not create the ResellerClub customer.',
|
|
];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'customer_id' => $customerId,
|
|
];
|
|
} catch (\Throwable $e) {
|
|
Log::error('ResellerClubCustomerService: createCustomerForUser failed', [
|
|
'user_id' => $user->id,
|
|
'email' => $email,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return ['success' => false, 'message' => 'Could not create the ResellerClub customer.'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array{success: bool, customer_id?: string, message?: string}
|
|
*/
|
|
public function attachCustomerToUser(User $user, string $customerId, ?string $email = null, bool $markLinked = false): array
|
|
{
|
|
$customerId = trim($customerId);
|
|
|
|
if ($customerId === '') {
|
|
return ['success' => false, 'message' => 'A valid ResellerClub customer ID is required.'];
|
|
}
|
|
|
|
$existing = User::query()
|
|
->where('rc_customer_id', $customerId)
|
|
->where('id', '!=', $user->id)
|
|
->first();
|
|
|
|
if ($existing) {
|
|
return ['success' => false, 'message' => 'This ResellerClub account is already linked to another Ladill account.'];
|
|
}
|
|
|
|
$payload = [
|
|
'rc_customer_id' => $customerId,
|
|
'rc_email' => $email ?: $user->rc_email ?: $user->email,
|
|
];
|
|
|
|
if ($markLinked) {
|
|
$payload['rc_linked_at'] = now();
|
|
}
|
|
|
|
$user->forceFill($payload)->save();
|
|
|
|
return [
|
|
'success' => true,
|
|
'customer_id' => $customerId,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array{success: bool, token?: string, message?: string}
|
|
*/
|
|
public function generateLoginToken(User $user, string $ipAddress): array
|
|
{
|
|
if (! $user->hasResellerClubCustomer()) {
|
|
return ['success' => false, 'message' => 'ResellerClub customer is not set for this user.'];
|
|
}
|
|
|
|
try {
|
|
$response = Http::timeout(15)->get($this->apiUrl.'/customers/generate-login-token.json', [
|
|
'auth-userid' => $this->authUserId,
|
|
'api-key' => $this->apiKey,
|
|
'customer-id' => (string) $user->rc_customer_id,
|
|
'ip' => $ipAddress,
|
|
]);
|
|
|
|
$data = $response->json();
|
|
$rawBody = trim((string) $response->body());
|
|
|
|
if ($response->failed()) {
|
|
return [
|
|
'success' => false,
|
|
'message' => $this->extractErrorMessage(is_array($data) ? $data : [], $rawBody) ?: 'Could not create the ResellerClub login token.',
|
|
];
|
|
}
|
|
|
|
$token = is_array($data)
|
|
? (string) ($data['token'] ?? $data['loginid'] ?? $data['userLoginId'] ?? '')
|
|
: trim($rawBody, "\"'");
|
|
|
|
if ($token === '') {
|
|
return ['success' => false, 'message' => 'Could not create the ResellerClub login token.'];
|
|
}
|
|
|
|
return ['success' => true, 'token' => $token];
|
|
} catch (\Throwable $e) {
|
|
Log::error('ResellerClubCustomerService: generateLoginToken failed', [
|
|
'user_id' => $user->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return ['success' => false, 'message' => 'Could not create the ResellerClub login token.'];
|
|
}
|
|
}
|
|
|
|
public function generatePlatformPassword(): string
|
|
{
|
|
$upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
|
|
$lower = 'abcdefghijkmnopqrstuvwxyz';
|
|
$numbers = '23456789';
|
|
$special = '!@$#%_+?';
|
|
$pool = $upper.$lower.$numbers.$special;
|
|
|
|
$password = [
|
|
$upper[random_int(0, strlen($upper) - 1)],
|
|
$lower[random_int(0, strlen($lower) - 1)],
|
|
$numbers[random_int(0, strlen($numbers) - 1)],
|
|
$special[random_int(0, strlen($special) - 1)],
|
|
];
|
|
|
|
while (count($password) < 12) {
|
|
$password[] = $pool[random_int(0, strlen($pool) - 1)];
|
|
}
|
|
|
|
shuffle($password);
|
|
|
|
return implode('', $password);
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* success: bool,
|
|
* configured: bool,
|
|
* api_url: string,
|
|
* auth_userid_present: bool,
|
|
* current_customer_id: string,
|
|
* current_rc_email: string,
|
|
* linked: bool,
|
|
* steps: array<int, array{label: string, status: string, message: string}>
|
|
* }
|
|
*/
|
|
public function runDiagnostics(User $user, string $ipAddress): array
|
|
{
|
|
$steps = [];
|
|
|
|
if (! $this->isConfigured()) {
|
|
$steps[] = $this->diagnosticStep('Configuration', 'error', 'ResellerClub is not configured.');
|
|
|
|
return $this->diagnosticSummary($user, $steps, false);
|
|
}
|
|
|
|
$steps[] = $this->diagnosticStep('Configuration', 'success', 'ResellerClub credentials are configured.');
|
|
|
|
if ($user->hasResellerClubCustomer()) {
|
|
$steps[] = $this->diagnosticStep(
|
|
'Stored customer',
|
|
'success',
|
|
'Current user already has ResellerClub customer ID '.$user->rc_customer_id.'.'
|
|
);
|
|
} else {
|
|
$email = strtolower(trim((string) $user->email));
|
|
if ($email === '') {
|
|
$steps[] = $this->diagnosticStep('Customer lookup', 'error', 'User email is required to provision a ResellerClub customer.');
|
|
|
|
return $this->diagnosticSummary($user, $steps, false);
|
|
}
|
|
|
|
$lookup = $this->findCustomerByEmail($email);
|
|
if (! ($lookup['success'] ?? false)) {
|
|
$steps[] = $this->diagnosticStep(
|
|
'Customer lookup',
|
|
'error',
|
|
$lookup['message'] ?? 'Could not look up the ResellerClub customer.'
|
|
);
|
|
|
|
return $this->diagnosticSummary($user, $steps, false);
|
|
}
|
|
|
|
if ($lookup['found'] ?? false) {
|
|
$customerId = (string) ($lookup['customer_id'] ?? '');
|
|
$steps[] = $this->diagnosticStep(
|
|
'Customer lookup',
|
|
'success',
|
|
'Existing ResellerClub customer found: '.$customerId.'.'
|
|
);
|
|
|
|
$attached = $this->attachCustomerToUser($user, $customerId, $email, false);
|
|
if (! ($attached['success'] ?? false)) {
|
|
$steps[] = $this->diagnosticStep(
|
|
'Attach customer',
|
|
'error',
|
|
$attached['message'] ?? 'Could not attach the ResellerClub customer to this Ladill account.'
|
|
);
|
|
|
|
return $this->diagnosticSummary($user, $steps, false);
|
|
}
|
|
|
|
$steps[] = $this->diagnosticStep('Attach customer', 'success', 'Existing ResellerClub customer attached to this Ladill account.');
|
|
$user->refresh();
|
|
} else {
|
|
$steps[] = $this->diagnosticStep('Customer lookup', 'warning', 'No existing ResellerClub customer was found for this email.');
|
|
|
|
$created = $this->createCustomerForUser($user);
|
|
if (! ($created['success'] ?? false)) {
|
|
$steps[] = $this->diagnosticStep(
|
|
'Create customer',
|
|
'error',
|
|
$created['message'] ?? 'Could not create the ResellerClub customer.'
|
|
);
|
|
|
|
return $this->diagnosticSummary($user, $steps, false);
|
|
}
|
|
|
|
$customerId = (string) ($created['customer_id'] ?? '');
|
|
$steps[] = $this->diagnosticStep('Create customer', 'success', 'New ResellerClub customer created: '.$customerId.'.');
|
|
|
|
$attached = $this->attachCustomerToUser($user, $customerId, $email, false);
|
|
if (! ($attached['success'] ?? false)) {
|
|
$steps[] = $this->diagnosticStep(
|
|
'Attach customer',
|
|
'error',
|
|
$attached['message'] ?? 'Could not attach the new ResellerClub customer to this Ladill account.'
|
|
);
|
|
|
|
return $this->diagnosticSummary($user, $steps, false);
|
|
}
|
|
|
|
$steps[] = $this->diagnosticStep('Attach customer', 'success', 'New ResellerClub customer attached to this Ladill account.');
|
|
$user->refresh();
|
|
}
|
|
}
|
|
|
|
$token = $this->generateLoginToken($user, $ipAddress);
|
|
if (! ($token['success'] ?? false)) {
|
|
$steps[] = $this->diagnosticStep(
|
|
'Generate login token',
|
|
'error',
|
|
$token['message'] ?? 'Could not create the ResellerClub login token.'
|
|
);
|
|
|
|
return $this->diagnosticSummary($user, $steps, false);
|
|
}
|
|
|
|
$steps[] = $this->diagnosticStep('Generate login token', 'success', 'ResellerClub login token generated successfully.');
|
|
|
|
return $this->diagnosticSummary($user, $steps, true);
|
|
}
|
|
|
|
private function configuredApiUrl(): string
|
|
{
|
|
$value = trim((string) config('mailinfra.registrar_api_url'));
|
|
|
|
return $value !== '' ? $value : self::DEFAULT_API_URL;
|
|
}
|
|
|
|
private function looksLikeMissingCustomerMessage(string $message): bool
|
|
{
|
|
$value = strtolower(trim($message));
|
|
|
|
return str_contains($value, 'not found')
|
|
|| str_contains($value, 'does not exist')
|
|
|| str_contains($value, 'no customer')
|
|
|| str_contains($value, 'unable to find')
|
|
|| str_contains($value, 'entity not found')
|
|
|| str_contains($value, 'no entity found');
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
private function extractErrorMessage(array $data, string $fallbackBody = ''): string
|
|
{
|
|
foreach (['message', 'error', 'description', 'msg'] as $key) {
|
|
$value = $this->sanitizeErrorText((string) ($data[$key] ?? ''));
|
|
if ($value !== '') {
|
|
return $value;
|
|
}
|
|
}
|
|
|
|
return $this->sanitizeErrorText($fallbackBody);
|
|
}
|
|
|
|
private function extractCustomerIdFromBody(string $body): string
|
|
{
|
|
if ($body !== '' && preg_match('/^\d+$/', $body) === 1) {
|
|
return $body;
|
|
}
|
|
|
|
$xml = $this->parseXml($body);
|
|
if ($xml === null) {
|
|
return '';
|
|
}
|
|
|
|
$flattened = strtolower(preg_replace('/\s+/', ' ', strip_tags($body)) ?? '');
|
|
if (str_contains($flattened, 'error')) {
|
|
return '';
|
|
}
|
|
|
|
foreach (['customerid', 'customer-id', 'entityid', 'entity-id', 'id', 'description'] as $key) {
|
|
$value = trim((string) ($xml[$key] ?? ''));
|
|
if ($value !== '' && preg_match('/^\d+$/', $value) === 1) {
|
|
return $value;
|
|
}
|
|
}
|
|
|
|
if (preg_match('/\b(\d{3,})\b/', trim(strip_tags($body)), $matches) === 1) {
|
|
return $matches[1];
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
private function extractXmlErrorMessage(string $body): string
|
|
{
|
|
$xml = $this->parseXml($body);
|
|
|
|
if ($xml !== null) {
|
|
foreach (['message', 'error', 'description', 'msg', 'status'] as $key) {
|
|
$value = $this->sanitizeErrorText((string) ($xml[$key] ?? ''));
|
|
if ($value !== '' && ! preg_match('/^\d+$/', $value)) {
|
|
return $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $this->sanitizeErrorText($body);
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
private function parseXml(string $body): ?array
|
|
{
|
|
$body = trim($body);
|
|
if ($body === '' || ! str_starts_with($body, '<')) {
|
|
return null;
|
|
}
|
|
|
|
$previous = libxml_use_internal_errors(true);
|
|
|
|
try {
|
|
$xml = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NONET);
|
|
if (! $xml instanceof \SimpleXMLElement) {
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode(json_encode($xml, JSON_THROW_ON_ERROR), true, 512, JSON_THROW_ON_ERROR);
|
|
|
|
return is_array($data) ? $data : null;
|
|
} catch (\Throwable) {
|
|
return null;
|
|
} finally {
|
|
libxml_clear_errors();
|
|
libxml_use_internal_errors($previous);
|
|
}
|
|
}
|
|
|
|
private function sanitizeErrorText(string $value): string
|
|
{
|
|
$value = trim($value);
|
|
if ($value === '') {
|
|
return '';
|
|
}
|
|
|
|
if ($this->looksLikeHtmlDocument($value)) {
|
|
return 'ResellerClub temporarily blocked or rejected the request. Please try again in a moment.';
|
|
}
|
|
|
|
$plainText = trim(preg_replace('/\s+/', ' ', strip_tags($value)) ?? '');
|
|
|
|
return $plainText;
|
|
}
|
|
|
|
private function looksLikeHtmlDocument(string $value): bool
|
|
{
|
|
$normalized = strtolower(ltrim($value));
|
|
|
|
return str_starts_with($normalized, '<!doctype html')
|
|
|| str_starts_with($normalized, '<html')
|
|
|| str_contains($normalized, '<body')
|
|
|| str_contains($normalized, '<head')
|
|
|| str_contains($normalized, '<title>')
|
|
|| str_contains($normalized, 'cloudflare');
|
|
}
|
|
|
|
/**
|
|
* @return array{label: string, status: string, message: string}
|
|
*/
|
|
private function diagnosticStep(string $label, string $status, string $message): array
|
|
{
|
|
return [
|
|
'label' => $label,
|
|
'status' => $status,
|
|
'message' => $message,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array{label: string, status: string, message: string}> $steps
|
|
* @return array{
|
|
* success: bool,
|
|
* configured: bool,
|
|
* api_url: string,
|
|
* auth_userid_present: bool,
|
|
* current_customer_id: string,
|
|
* current_rc_email: string,
|
|
* linked: bool,
|
|
* steps: array<int, array{label: string, status: string, message: string}>
|
|
* }
|
|
*/
|
|
private function diagnosticSummary(User $user, array $steps, bool $success): array
|
|
{
|
|
$user->refresh();
|
|
|
|
return [
|
|
'success' => $success,
|
|
'configured' => $this->isConfigured(),
|
|
'api_url' => $this->apiUrl,
|
|
'auth_userid_present' => $this->authUserId !== '',
|
|
'current_customer_id' => (string) ($user->rc_customer_id ?? ''),
|
|
'current_rc_email' => (string) ($user->rc_email ?? ''),
|
|
'linked' => $user->hasLinkedResellerClubAccount(),
|
|
'steps' => $steps,
|
|
];
|
|
}
|
|
}
|