feat: landing settings, instant withdraw deduct, and OTP site URL

Add has_landing plus landing_* deploy keys, deduct balance on withdraw request, keep app_url for SMS login links, and clarify Zarin rial wallet document titles.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-05 17:01:39 +03:30
co-authored by Cursor
parent 1c7ea16300
commit 8cee34bfba
13 changed files with 344 additions and 58 deletions
+43 -14
View File
@@ -121,8 +121,11 @@ class ConfigService
try {
$row = Setting::where('key', $key)->first();
if ($row !== null && $row->is_active && $row->value !== null && $row->value !== '') {
return $row->value;
if ($row !== null && $row->value !== null && $row->value !== '') {
// app_url لینک ورود پیامک است — حتی اگر سوییچ is_active خاموش باشد باید خوانده شود
if ($key === 'app_url' || $row->is_active) {
return $row->value;
}
}
} catch (\Throwable $e) {
// DB در دسترس نیست (deploy / .env اشتباه) — از config/price_providers.php استفاده شود
@@ -1357,7 +1360,7 @@ class ConfigService
return '';
}
/** Full user-facing site URL, e.g. https://example.ir — prefers settings.app_url */
/** Full user-facing site URL, e.g. https://example.ir — فقط settings.app_url؛ هاست API جایگزین نمی‌شود */
public function resolvePublicSiteUrl(?\Illuminate\Http\Request $request = null): string
{
$fromAppUrl = $this->normalizedAppUrl();
@@ -1366,23 +1369,21 @@ class ConfigService
}
if ($request !== null) {
$origin = rtrim(trim((string) $request->getSchemeAndHttpHost()), '/');
$host = $this->parseHostFromUrl($origin);
if ($this->isUsableHost($host)) {
$origin = $this->originIfNotApiHost($request);
if ($origin !== '') {
return $origin;
}
}
if (! app()->runningInConsole() && request()) {
$origin = rtrim(trim((string) request()->getSchemeAndHttpHost()), '/');
$host = $this->parseHostFromUrl($origin);
if ($this->isUsableHost($host)) {
$origin = $this->originIfNotApiHost(request());
if ($origin !== '') {
return $origin;
}
}
$host = $this->resolvePublicHost($request);
if ($host !== '') {
if ($host !== '' && ! $this->hostMatchesLaravelAppUrl($host)) {
return 'https://' . $host;
}
@@ -1400,17 +1401,23 @@ class ConfigService
{
$candidates = [];
$appUrlHost = $this->parseHostFromUrl((string) $this->get('app_url', ''));
$appUrlHost = $this->parseHostFromUrl($this->getString('app_url', ''));
if ($appUrlHost !== '') {
$candidates[] = $appUrlHost;
}
if ($request !== null) {
$candidates[] = trim((string) $request->getHost());
$host = trim((string) $request->getHost());
if (! $this->hostMatchesLaravelAppUrl($host)) {
$candidates[] = $host;
}
}
if (! app()->runningInConsole() && request()) {
$candidates[] = trim((string) request()->getHost());
$host = trim((string) request()->getHost());
if (! $this->hostMatchesLaravelAppUrl($host)) {
$candidates[] = $host;
}
}
return $candidates;
@@ -1419,7 +1426,7 @@ class ConfigService
/** settings.app_url as https://host… without trailing slash; empty if unusable */
private function normalizedAppUrl(): string
{
$raw = trim((string) $this->get('app_url', ''));
$raw = $this->getString('app_url', '');
if ($raw === '') {
return '';
}
@@ -1443,6 +1450,28 @@ class ConfigService
return $origin;
}
/** هاست Laravel APP_URL همان بک‌اند است — نباید لینک ورود اپ شود */
private function hostMatchesLaravelAppUrl(string $host): bool
{
$apiHost = $this->parseHostFromUrl((string) config('app.url', ''));
if ($apiHost === '' || ! $this->isUsableHost($apiHost)) {
return false;
}
return $this->normalizeHost($host) === $this->normalizeHost($apiHost);
}
private function originIfNotApiHost(\Illuminate\Http\Request $request): string
{
$origin = rtrim(trim((string) $request->getSchemeAndHttpHost()), '/');
$host = $this->parseHostFromUrl($origin);
if (! $this->isUsableHost($host) || $this->hostMatchesLaravelAppUrl($host)) {
return '';
}
return $origin;
}
private function isUsableHost(string $host): bool
{
$host = strtolower(trim($host));