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:
@@ -235,7 +235,7 @@ class SettingsController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* تنظیمات پیشرفته: ۱۵ دسترسی از سوپرادمین + بقیهٔ can_edit=0 از DB لوکال.
|
* تنظیمات پیشرفته: ۱۶ دسترسی از سوپرادمین + بقیهٔ can_edit=0 از DB لوکال.
|
||||||
*/
|
*/
|
||||||
private function composeSystemSettings($localRows)
|
private function composeSystemSettings($localRows)
|
||||||
{
|
{
|
||||||
@@ -319,7 +319,7 @@ class SettingsController extends Controller
|
|||||||
/**
|
/**
|
||||||
* get_settings ادمین (و پایهٔ publicIndex قبل از فیلتر show_to_user):
|
* get_settings ادمین (و پایهٔ publicIndex قبل از فیلتر show_to_user):
|
||||||
* can_edit=1 (+ runtime + local-owned) از DB لوکال
|
* can_edit=1 (+ runtime + local-owned) از DB لوکال
|
||||||
* فقط ۱۵ دسترسی سوپرادمین از overlay؛ بقیهٔ can_edit=0 از کپی لوکال
|
* فقط ۱۶ دسترسی سوپرادمین از overlay؛ بقیهٔ can_edit=0 از کپی لوکال
|
||||||
* اگر سوپرادمین در دسترس نباشد کل جدول لوکال برمیگردد.
|
* اگر سوپرادمین در دسترس نباشد کل جدول لوکال برمیگردد.
|
||||||
*/
|
*/
|
||||||
private function composeGetSettings($localRows)
|
private function composeGetSettings($localRows)
|
||||||
|
|||||||
@@ -275,6 +275,9 @@ class WalletController extends Controller
|
|||||||
try {
|
try {
|
||||||
$data['direction'] = $deposit ? 0 : 1;
|
$data['direction'] = $deposit ? 0 : 1;
|
||||||
$data['withdraw_id'] = $Withdraw->id;
|
$data['withdraw_id'] = $Withdraw->id;
|
||||||
|
if (!$deposit && trim((string) ($data['note'] ?? '')) === '') {
|
||||||
|
$data['note'] = 'برداشت وجه';
|
||||||
|
}
|
||||||
app(\App\Http\Controllers\Admin\ZarinController::class)->wallet($data);
|
app(\App\Http\Controllers\Admin\ZarinController::class)->wallet($data);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
report($e);
|
report($e);
|
||||||
@@ -1446,6 +1449,10 @@ class WalletController extends Controller
|
|||||||
|
|
||||||
$withdraw = Withdraw::findOrFail($request->id);
|
$withdraw = Withdraw::findOrFail($request->id);
|
||||||
$withdraw->update($data);
|
$withdraw->update($data);
|
||||||
|
|
||||||
|
if ($withdraw->customer) {
|
||||||
|
$this->broadcastCustomerWalletAssets($withdraw->customer);
|
||||||
|
}
|
||||||
// $tahesab = new TahesabController();
|
// $tahesab = new TahesabController();
|
||||||
// $tahesab->wallet([
|
// $tahesab->wallet([
|
||||||
// 'customer_id' => $withdraw->customer_id,
|
// 'customer_id' => $withdraw->customer_id,
|
||||||
|
|||||||
@@ -1861,12 +1861,21 @@ class ZarinController extends Controller
|
|||||||
: ['korf' => 'k', 'parDar' => 1];
|
: ['korf' => 'k', 'parDar' => 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** شرح سند ریالی زرین — همیشه با واریز/برداشت شروع شود تا «خرید فی» دیده نشود */
|
/**
|
||||||
|
* شرح سند ریالی زرین — همیشه «واریز وجه» یا «برداشت وجه».
|
||||||
|
* دشارژ ادمین و برداشت مشتری هر دو → «برداشت وجه» (پیشوند «واریز اینترنتی به» را زرین خودش میگذارد).
|
||||||
|
*/
|
||||||
private function zarinRialWalletSharh(bool $isDeposit, ?string $note): string
|
private function zarinRialWalletSharh(bool $isDeposit, ?string $note): string
|
||||||
{
|
{
|
||||||
$base = $isDeposit ? 'واریز' : 'برداشت';
|
$base = $isDeposit ? 'واریز وجه' : 'برداشت وجه';
|
||||||
$normalized = self::accountingWalletDesc($isDeposit, $note);
|
$normalized = self::accountingWalletDesc($isDeposit, $note);
|
||||||
if ($normalized === '' || $normalized === $base) {
|
$generic = [
|
||||||
|
'', 'واریز', 'برداشت', 'واریز وجه', 'برداشت وجه',
|
||||||
|
'واریز ریالی', 'برداشت ریالی',
|
||||||
|
'شارژ', 'دشارژ', 'شارژ حساب', 'کسر',
|
||||||
|
'واریز به کیف پول', 'برداشت از کیف پول', 'برداشت از حساب',
|
||||||
|
];
|
||||||
|
if (in_array($normalized, $generic, true) || in_array(trim((string) $note), $generic, true)) {
|
||||||
return $base;
|
return $base;
|
||||||
}
|
}
|
||||||
if (str_starts_with($normalized, $base)) {
|
if (str_starts_with($normalized, $base)) {
|
||||||
@@ -2029,7 +2038,7 @@ class ZarinController extends Controller
|
|||||||
$data = array_merge($data, [
|
$data = array_merge($data, [
|
||||||
'customer_id' => $existing->customer_id,
|
'customer_id' => $existing->customer_id,
|
||||||
'value' => $existing->amount,
|
'value' => $existing->amount,
|
||||||
'direction' => str_contains((string) $existing->desc, 'برداشت') ? 1 : 0,
|
'direction' => $this->sync()->rialWalletDirectionFromDesc((string) $existing->desc),
|
||||||
'withdraw_id' => $existing->withdraw_id,
|
'withdraw_id' => $existing->withdraw_id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,13 +227,13 @@ class Controller extends BaseController
|
|||||||
return app(ConfigService::class)->Config($name);
|
return app(ConfigService::class)->Config($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** دامنهٔ عمومی سایت — فقط از درخواست HTTP فعلی */
|
/** دامنهٔ عمومی سایت — settings.app_url؛ نه هاست API */
|
||||||
protected static function publicHost(?\Illuminate\Http\Request $request = null): string
|
protected static function publicHost(?\Illuminate\Http\Request $request = null): string
|
||||||
{
|
{
|
||||||
return app(ConfigService::class)->resolvePublicHost($request);
|
return app(ConfigService::class)->resolvePublicHost($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** آدرس کامل وباپ کاربر — مثلاً https://example.ir */
|
/** آدرس کامل وباپ کاربر از settings.app_url — مثلاً https://example.ir */
|
||||||
protected static function publicSiteUrl(?\Illuminate\Http\Request $request = null): string
|
protected static function publicSiteUrl(?\Illuminate\Http\Request $request = null): string
|
||||||
{
|
{
|
||||||
return app(ConfigService::class)->resolvePublicSiteUrl($request);
|
return app(ConfigService::class)->resolvePublicSiteUrl($request);
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ use App\Http\Controllers\Admin\TahesabController;
|
|||||||
use App\Services\ConfigService;
|
use App\Services\ConfigService;
|
||||||
use App\Services\AssetSummaryService;
|
use App\Services\AssetSummaryService;
|
||||||
use App\Services\WalletBalanceService;
|
use App\Services\WalletBalanceService;
|
||||||
|
use App\Services\WalletBroadcastService;
|
||||||
use App\Services\ZaarboxPriceService;
|
use App\Services\ZaarboxPriceService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
||||||
@@ -372,43 +375,72 @@ class WalletController extends Controller
|
|||||||
return response()->json(['message' => 'لطفا کارت بانکی را وارد کنید'], 422);
|
return response()->json(['message' => 'لطفا کارت بانکی را وارد کنید'], 422);
|
||||||
}
|
}
|
||||||
$bCart = BankAccount::where('id', $data['card_id'])->first();
|
$bCart = BankAccount::where('id', $data['card_id'])->first();
|
||||||
if ($bCart->sts != 2) {
|
if (!$bCart || $bCart->sts != 2) {
|
||||||
return response()->json(['message' => 'کارت انتخاب شده نا معتبر است'], 422);
|
return response()->json(['message' => 'کارت انتخاب شده نا معتبر است'], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$wallet_interface_fund = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type);
|
$amount = (int) ($data['value'] ?? 0);
|
||||||
if ($data['value'] < 0) {
|
if ($amount <= 0) {
|
||||||
throw new UnprocessableEntityHttpException('مقدار برداشت باید مثبت باشد');
|
throw new UnprocessableEntityHttpException('مقدار برداشت باید مثبت باشد');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->validateWithdrawAmount((int) $data['value']);
|
|
||||||
|
|
||||||
$extraNote = trim((string) ($data['extra_note'] ?? ''));
|
$extraNote = trim((string) ($data['extra_note'] ?? ''));
|
||||||
if (strlen($extraNote) > 500) {
|
if (strlen($extraNote) > 500) {
|
||||||
return response()->json(['message' => 'توضیحات اضافی حداکثر ۵۰۰ کاراکتر است'], 422);
|
return response()->json(['message' => 'توضیحات اضافی حداکثر ۵۰۰ کاراکتر است'], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$Withdraw = Withdraw::create([
|
$otp = (string) ($request->otp ?? '');
|
||||||
'customer_id' => auth('customer')->id(),
|
if ($otp === '' || (string) ($customer->token_sms ?? '') !== $otp) {
|
||||||
"cash" => $data['value'],
|
return response()->json(['message' => 'رمز دوم اشتباه میباشد'], 422);
|
||||||
"note" => "برداشت ریالی",
|
}
|
||||||
'extra_note' => $extraNote !== '' ? $extraNote : null,
|
|
||||||
"bank_account_id" => $data['card_id'],
|
$lock = Cache::lock('customer-rial-withdraw:'.$customer->id, 20);
|
||||||
'sts' => 1,
|
if (!$lock->get()) {
|
||||||
'type' => 2
|
return response()->json(['message' => 'درخواست برداشت در حال انجام است'], 429);
|
||||||
]);
|
}
|
||||||
$transaction = new TransactionInterface($wallet_interface_fund, $_inp = [
|
|
||||||
"cash" => $data['value'],
|
try {
|
||||||
'expire_at' => now(),
|
$customer->refresh();
|
||||||
'withdraw_id' => $Withdraw->id,
|
if ((string) ($customer->token_sms ?? '') !== $otp) {
|
||||||
'active' => true
|
return response()->json(['message' => 'رمز دوم اشتباه میباشد'], 422);
|
||||||
], 4, false, false);
|
}
|
||||||
|
|
||||||
|
$this->validateWithdrawAmount($amount);
|
||||||
|
|
||||||
|
$Withdraw = DB::transaction(function () use ($customer, $data, $amount, $extraNote) {
|
||||||
|
$withdraw = Withdraw::create([
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'cash' => $amount,
|
||||||
|
'note' => 'برداشت ریالی',
|
||||||
|
'extra_note' => $extraNote !== '' ? $extraNote : null,
|
||||||
|
'bank_account_id' => $data['card_id'],
|
||||||
|
'sts' => 1,
|
||||||
|
'type' => 2,
|
||||||
|
]);
|
||||||
|
$this->debitCustomerRialWithdrawOnce($withdraw, $amount, $customer);
|
||||||
|
$customer->token_sms = null;
|
||||||
|
$customer->save();
|
||||||
|
|
||||||
|
return $withdraw;
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
$lock->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
app(WalletBroadcastService::class)->broadcastForCustomer(
|
||||||
|
(int) $customer->id,
|
||||||
|
(int) $customer->type
|
||||||
|
);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
app(\App\Services\PargarSyncService::class)->queueWalletDocument(
|
app(\App\Services\PargarSyncService::class)->queueWalletDocument(
|
||||||
$customer,
|
$customer,
|
||||||
'cash',
|
'cash',
|
||||||
-1 * (float) $data['value'],
|
-1 * (float) $amount,
|
||||||
[
|
[
|
||||||
'withdraw_id' => $Withdraw->id,
|
'withdraw_id' => $Withdraw->id,
|
||||||
'note' => 'برداشت ریالی',
|
'note' => 'برداشت ریالی',
|
||||||
@@ -421,18 +453,18 @@ class WalletController extends Controller
|
|||||||
|
|
||||||
$userMobile = $customer->mobile; // User's mobile number
|
$userMobile = $customer->mobile; // User's mobile number
|
||||||
$userName = $customer->name; // User's name
|
$userName = $customer->name; // User's name
|
||||||
$withdrawAmount = $data['value']; // amount
|
$withdrawAmount = $amount; // amount
|
||||||
if (app(ConfigService::class)->isTahesabAccounting()) {
|
if (app(ConfigService::class)->isTahesabAccounting()) {
|
||||||
try {
|
try {
|
||||||
$result = app(TahesabController::class)->wallet([
|
$result = app(TahesabController::class)->wallet([
|
||||||
"customer_id" => auth('customer')->id(),
|
"customer_id" => auth('customer')->id(),
|
||||||
"value" => $data['value'],
|
"value" => $amount,
|
||||||
"direction" => 1,
|
"direction" => 1,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
\Log::info('Tahesab withdraw result', [
|
\Log::info('Tahesab withdraw result', [
|
||||||
'customer_id' => auth('customer')->id(),
|
'customer_id' => auth('customer')->id(),
|
||||||
'value' => $data['value'],
|
'value' => $amount,
|
||||||
'result' => $result,
|
'result' => $result,
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
@@ -444,8 +476,10 @@ class WalletController extends Controller
|
|||||||
try {
|
try {
|
||||||
app(\App\Http\Controllers\Admin\ZarinController::class)->wallet([
|
app(\App\Http\Controllers\Admin\ZarinController::class)->wallet([
|
||||||
"customer_id" => auth('customer')->id(),
|
"customer_id" => auth('customer')->id(),
|
||||||
"value" => $data['value'],
|
"value" => $amount,
|
||||||
"direction" => 1,
|
"direction" => 1,
|
||||||
|
"withdraw_id" => $Withdraw->id,
|
||||||
|
"note" => 'برداشت ریالی',
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
report($e);
|
report($e);
|
||||||
@@ -454,11 +488,6 @@ class WalletController extends Controller
|
|||||||
|
|
||||||
$companyName = $this->Config('name_fa'); // Company name from configuration
|
$companyName = $this->Config('name_fa'); // Company name from configuration
|
||||||
$message = "کاربر $userName با شماره موبایل $userMobile درخواست برداشت مبلغ " . number_format($withdrawAmount) . " ریال را دارد. با تشکر، $companyName";
|
$message = "کاربر $userName با شماره موبایل $userMobile درخواست برداشت مبلغ " . number_format($withdrawAmount) . " ریال را دارد. با تشکر، $companyName";
|
||||||
if (isset($request->otp)) {
|
|
||||||
if (Customer::where("id", $customer->id)->first()->token_sms != $request->otp) {
|
|
||||||
return response()->json(['message' => 'رمز دوم اشتباه میباشد'], 422);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
User::where('send_sms', 1)->get()->each(function ($admin) use ($message) {
|
User::where('send_sms', 1)->get()->each(function ($admin) use ($message) {
|
||||||
if (self::isSmsNotifEnabled('sms_notif_withdraw_admin')) {
|
if (self::isSmsNotifEnabled('sms_notif_withdraw_admin')) {
|
||||||
$this->Send($admin->mobile, $message . ' ' . now()->format('H:i:s'));
|
$this->Send($admin->mobile, $message . ' ' . now()->format('H:i:s'));
|
||||||
@@ -675,6 +704,25 @@ class WalletController extends Controller
|
|||||||
return response()->json(true);
|
return response()->json(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function debitCustomerRialWithdrawOnce(Withdraw $withdraw, int $amount, Customer $customer): void
|
||||||
|
{
|
||||||
|
$alreadyDebited = Transaction::query()
|
||||||
|
->where('withdraw_id', $withdraw->id)
|
||||||
|
->where('cash', '<', 0)
|
||||||
|
->exists();
|
||||||
|
if ($alreadyDebited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$wallet = new WalletInterface((int) $customer->id, (int) $customer->type);
|
||||||
|
new TransactionInterface($wallet, [
|
||||||
|
'cash' => $amount,
|
||||||
|
'expire_at' => now(),
|
||||||
|
'withdraw_id' => $withdraw->id,
|
||||||
|
'active' => true,
|
||||||
|
], 4, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
private function validateWithdrawAmount(int $value): void
|
private function validateWithdrawAmount(int $value): void
|
||||||
{
|
{
|
||||||
$wallet_interface_fund = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type);
|
$wallet_interface_fund = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type);
|
||||||
|
|||||||
@@ -121,8 +121,11 @@ class ConfigService
|
|||||||
try {
|
try {
|
||||||
$row = Setting::where('key', $key)->first();
|
$row = Setting::where('key', $key)->first();
|
||||||
|
|
||||||
if ($row !== null && $row->is_active && $row->value !== null && $row->value !== '') {
|
if ($row !== null && $row->value !== null && $row->value !== '') {
|
||||||
return $row->value;
|
// app_url لینک ورود پیامک است — حتی اگر سوییچ is_active خاموش باشد باید خوانده شود
|
||||||
|
if ($key === 'app_url' || $row->is_active) {
|
||||||
|
return $row->value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
// DB در دسترس نیست (deploy / .env اشتباه) — از config/price_providers.php استفاده شود
|
// DB در دسترس نیست (deploy / .env اشتباه) — از config/price_providers.php استفاده شود
|
||||||
@@ -1357,7 +1360,7 @@ class ConfigService
|
|||||||
return '';
|
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
|
public function resolvePublicSiteUrl(?\Illuminate\Http\Request $request = null): string
|
||||||
{
|
{
|
||||||
$fromAppUrl = $this->normalizedAppUrl();
|
$fromAppUrl = $this->normalizedAppUrl();
|
||||||
@@ -1366,23 +1369,21 @@ class ConfigService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($request !== null) {
|
if ($request !== null) {
|
||||||
$origin = rtrim(trim((string) $request->getSchemeAndHttpHost()), '/');
|
$origin = $this->originIfNotApiHost($request);
|
||||||
$host = $this->parseHostFromUrl($origin);
|
if ($origin !== '') {
|
||||||
if ($this->isUsableHost($host)) {
|
|
||||||
return $origin;
|
return $origin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! app()->runningInConsole() && request()) {
|
if (! app()->runningInConsole() && request()) {
|
||||||
$origin = rtrim(trim((string) request()->getSchemeAndHttpHost()), '/');
|
$origin = $this->originIfNotApiHost(request());
|
||||||
$host = $this->parseHostFromUrl($origin);
|
if ($origin !== '') {
|
||||||
if ($this->isUsableHost($host)) {
|
|
||||||
return $origin;
|
return $origin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$host = $this->resolvePublicHost($request);
|
$host = $this->resolvePublicHost($request);
|
||||||
if ($host !== '') {
|
if ($host !== '' && ! $this->hostMatchesLaravelAppUrl($host)) {
|
||||||
return 'https://' . $host;
|
return 'https://' . $host;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1400,17 +1401,23 @@ class ConfigService
|
|||||||
{
|
{
|
||||||
$candidates = [];
|
$candidates = [];
|
||||||
|
|
||||||
$appUrlHost = $this->parseHostFromUrl((string) $this->get('app_url', ''));
|
$appUrlHost = $this->parseHostFromUrl($this->getString('app_url', ''));
|
||||||
if ($appUrlHost !== '') {
|
if ($appUrlHost !== '') {
|
||||||
$candidates[] = $appUrlHost;
|
$candidates[] = $appUrlHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request !== null) {
|
if ($request !== null) {
|
||||||
$candidates[] = trim((string) $request->getHost());
|
$host = trim((string) $request->getHost());
|
||||||
|
if (! $this->hostMatchesLaravelAppUrl($host)) {
|
||||||
|
$candidates[] = $host;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! app()->runningInConsole() && request()) {
|
if (! app()->runningInConsole() && request()) {
|
||||||
$candidates[] = trim((string) request()->getHost());
|
$host = trim((string) request()->getHost());
|
||||||
|
if (! $this->hostMatchesLaravelAppUrl($host)) {
|
||||||
|
$candidates[] = $host;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $candidates;
|
return $candidates;
|
||||||
@@ -1419,7 +1426,7 @@ class ConfigService
|
|||||||
/** settings.app_url as https://host… without trailing slash; empty if unusable */
|
/** settings.app_url as https://host… without trailing slash; empty if unusable */
|
||||||
private function normalizedAppUrl(): string
|
private function normalizedAppUrl(): string
|
||||||
{
|
{
|
||||||
$raw = trim((string) $this->get('app_url', ''));
|
$raw = $this->getString('app_url', '');
|
||||||
if ($raw === '') {
|
if ($raw === '') {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -1443,6 +1450,28 @@ class ConfigService
|
|||||||
return $origin;
|
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
|
private function isUsableHost(string $host): bool
|
||||||
{
|
{
|
||||||
$host = strtolower(trim($host));
|
$host = strtolower(trim($host));
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use App\Models\Setting;
|
|||||||
class SuperAdminProgramSettingsOverlay
|
class SuperAdminProgramSettingsOverlay
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* فقط این ۱۵ دسترسی از سوپرادمین خوانده میشود.
|
* فقط این ۱۶ دسترسی از سوپرادمین خوانده میشود.
|
||||||
* هر کلید دیگری (قیمت، ساعت کاری، برندینگ، …) مال پنل خانگی است.
|
* هر کلید دیگری (قیمت، ساعت کاری، برندینگ، …) مال پنل خانگی است.
|
||||||
*/
|
*/
|
||||||
public const SUPERADMIN_OWNED_KEYS = [
|
public const SUPERADMIN_OWNED_KEYS = [
|
||||||
@@ -23,6 +23,7 @@ class SuperAdminProgramSettingsOverlay
|
|||||||
'enable_gold_credit',
|
'enable_gold_credit',
|
||||||
'enable_product_funds',
|
'enable_product_funds',
|
||||||
'group_sms',
|
'group_sms',
|
||||||
|
'has_landing',
|
||||||
'has_page_earn_money',
|
'has_page_earn_money',
|
||||||
'has_page_gift_card',
|
'has_page_gift_card',
|
||||||
'has_page_notification',
|
'has_page_notification',
|
||||||
@@ -40,6 +41,7 @@ class SuperAdminProgramSettingsOverlay
|
|||||||
'enable_conditional_order',
|
'enable_conditional_order',
|
||||||
'enable_gold_credit',
|
'enable_gold_credit',
|
||||||
'enable_product_funds',
|
'enable_product_funds',
|
||||||
|
'has_landing',
|
||||||
'has_page_earn_money',
|
'has_page_earn_money',
|
||||||
'has_page_gift_card',
|
'has_page_gift_card',
|
||||||
'has_page_notification',
|
'has_page_notification',
|
||||||
@@ -119,7 +121,7 @@ class SuperAdminProgramSettingsOverlay
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* آخرین مقادیر ۱۵ دسترسی را در settings لوکال ذخیره میکند (can_edit=0).
|
* آخرین مقادیر ۱۶ دسترسی را در settings لوکال ذخیره میکند (can_edit=0).
|
||||||
*
|
*
|
||||||
* @param array<string, array<string, mixed>> $map
|
* @param array<string, array<string, mixed>> $map
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Services;
|
|||||||
|
|
||||||
use App\Http\Controllers\Admin\ZarinController;
|
use App\Http\Controllers\Admin\ZarinController;
|
||||||
use App\Models\Customer;
|
use App\Models\Customer;
|
||||||
|
use App\Models\Withdraw;
|
||||||
use App\Models\Zarin;
|
use App\Models\Zarin;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@@ -567,6 +568,29 @@ class ZarinSyncService
|
|||||||
->all();
|
->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* درخواست برداشت مشتری cash را مثبت ذخیره میکند (type=2) — علامت مبلغ برای جهت سند کافی نیست.
|
||||||
|
* 0 = واریز، 1 = برداشت
|
||||||
|
*/
|
||||||
|
public function rialWalletDirectionFromWithdraw(Withdraw $withdraw): int
|
||||||
|
{
|
||||||
|
if ((int) ($withdraw->type ?? 0) === 2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
$note = (string) ($withdraw->note ?? '');
|
||||||
|
if ($note !== '' && str_contains($note, 'برداشت')) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((float) ($withdraw->cash ?? 0)) >= 0 ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** شرح محلی سند ریالی: «برداشت وجه» برداشت است؛ «واریز وجه» واریز. */
|
||||||
|
public function rialWalletDirectionFromDesc(string $desc): int
|
||||||
|
{
|
||||||
|
return str_contains($desc, 'برداشت') ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
private function backfillWithdraw(ZarinController $controller, int $withdrawId): bool
|
private function backfillWithdraw(ZarinController $controller, int $withdrawId): bool
|
||||||
{
|
{
|
||||||
$withdraw = \App\Models\Withdraw::query()->find($withdrawId);
|
$withdraw = \App\Models\Withdraw::query()->find($withdrawId);
|
||||||
@@ -581,7 +605,7 @@ class ZarinSyncService
|
|||||||
$result = $controller->wallet([
|
$result = $controller->wallet([
|
||||||
'customer_id' => (int) $withdraw->customer_id,
|
'customer_id' => (int) $withdraw->customer_id,
|
||||||
'value' => abs($cash),
|
'value' => abs($cash),
|
||||||
'direction' => $cash >= 0 ? 0 : 1,
|
'direction' => $this->rialWalletDirectionFromWithdraw($withdraw),
|
||||||
'withdraw_id' => $withdrawId,
|
'withdraw_id' => $withdrawId,
|
||||||
'note' => $withdraw->note,
|
'note' => $withdraw->note,
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -905,6 +905,66 @@ SELECT 'has_chat', 'true', 'چت پشتیبانی', 1, 1,
|
|||||||
'روشن: چت پشتیبانی در اپ مشتری نمایش داده میشود (فقط وقتی show_to_users_chat هم روشن باشد). خاموش: دکمه و صفحه چت برای کاربر مخفی است.', 'switch'
|
'روشن: چت پشتیبانی در اپ مشتری نمایش داده میشود (فقط وقتی show_to_users_chat هم روشن باشد). خاموش: دکمه و صفحه چت برای کاربر مخفی است.', 'switch'
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'has_chat' LIMIT 1);
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'has_chat' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'has_landing', 'false', 'لندینگ وبسایت', 0, 1,
|
||||||
|
'روشن: منوی تنظیمات وبسایت در ادمین و تنظیمات لندینگ برای اپ کاربر فعال است. خاموش: منو مخفی است.', 'switch'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'has_landing' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_site', '', 'تنظیمات کلی سایت', 1, 1,
|
||||||
|
'محتوای تنظیمات کلی لندینگ وبسایت. در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_site' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_header', '', 'هدر و ناوبری', 1, 1,
|
||||||
|
'محتوای هدر و ناوبری لندینگ. در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_header' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_hero', '', 'هیرو', 1, 1,
|
||||||
|
'محتوای سکشن هیرو لندینگ. در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_hero' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_live_price', '', 'لیبلهای قیمت لحظهای', 1, 1,
|
||||||
|
'لیبلها و متن قیمت لحظهای لندینگ. در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_live_price' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_calculator', '', 'عنوان ماشین حساب', 1, 1,
|
||||||
|
'عنوان و متن ماشین حساب لندینگ. در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_calculator' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_feature', '', 'امکانات (چرا نیازی طلا)', 1, 1,
|
||||||
|
'متن امکانات لندینگ (چرا نیازی طلا). در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_feature' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_steps', '', 'مراحل شروع', 1, 1,
|
||||||
|
'متن مراحل شروع لندینگ. در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_steps' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_trust', '', 'اعتماد و مجوزها', 1, 1,
|
||||||
|
'متن اعتماد و مجوزها (بدون اینماد). در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_trust' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_faq', '', 'سوالات متداول', 1, 1,
|
||||||
|
'سوالات متداول لندینگ. در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_faq' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_buttom_cta', '', 'سکشن پایین صفحه لندینگ برای معرفی', 1, 1,
|
||||||
|
'متن دعوت به اقدام پایین صفحه لندینگ. در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_buttom_cta' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'landing_footer', '', 'فوتر', 1, 1,
|
||||||
|
'محتوای فوتر لندینگ. در اپ کاربر با get_settings در دسترس است.', 'longtext'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'landing_footer' LIMIT 1);
|
||||||
|
|
||||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
SELECT 'deposit_sms_support_number', '', 'شماره تماس پشتیبانی', 1, 1,
|
SELECT 'deposit_sms_support_number', '', 'شماره تماس پشتیبانی', 1, 1,
|
||||||
'شماره تماس پشتیبانی که در صفحه واریز، درباره ما و قوانین به مشتری نشان داده میشود. با کلیک، تماس تلفنی برقرار میشود.', 'number'
|
'شماره تماس پشتیبانی که در صفحه واریز، درباره ما و قوانین به مشتری نشان داده میشود. با کلیک، تماس تلفنی برقرار میشود.', 'number'
|
||||||
@@ -2137,6 +2197,47 @@ UPDATE `settings` SET
|
|||||||
`description` = 'روشن: چت پشتیبانی در اپ مشتری نمایش داده میشود (فقط وقتی show_to_users_chat هم روشن باشد). خاموش: دکمه و صفحه چت برای کاربر مخفی است.'
|
`description` = 'روشن: چت پشتیبانی در اپ مشتری نمایش داده میشود (فقط وقتی show_to_users_chat هم روشن باشد). خاموش: دکمه و صفحه چت برای کاربر مخفی است.'
|
||||||
WHERE `key` = 'has_chat';
|
WHERE `key` = 'has_chat';
|
||||||
|
|
||||||
|
UPDATE `settings` SET
|
||||||
|
`title_fa` = 'لندینگ وبسایت',
|
||||||
|
`type` = 'switch',
|
||||||
|
`can_edit` = 0,
|
||||||
|
`description` = 'روشن: منوی تنظیمات وبسایت در ادمین و تنظیمات لندینگ برای اپ کاربر فعال است. خاموش: منو مخفی است.'
|
||||||
|
WHERE `key` = 'has_landing';
|
||||||
|
|
||||||
|
UPDATE `settings` SET `title_fa` = 'تنظیمات کلی سایت', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'محتوای تنظیمات کلی لندینگ وبسایت. در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_site';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'هدر و ناوبری', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'محتوای هدر و ناوبری لندینگ. در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_header';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'هیرو', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'محتوای سکشن هیرو لندینگ. در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_hero';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'لیبلهای قیمت لحظهای', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'لیبلها و متن قیمت لحظهای لندینگ. در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_live_price';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'عنوان ماشین حساب', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'عنوان و متن ماشین حساب لندینگ. در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_calculator';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'امکانات (چرا نیازی طلا)', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'متن امکانات لندینگ (چرا نیازی طلا). در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_feature';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'مراحل شروع', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'متن مراحل شروع لندینگ. در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_steps';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'اعتماد و مجوزها', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'متن اعتماد و مجوزها (بدون اینماد). در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_trust';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'سوالات متداول', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'سوالات متداول لندینگ. در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_faq';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'سکشن پایین صفحه لندینگ برای معرفی', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'متن دعوت به اقدام پایین صفحه لندینگ. در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_buttom_cta';
|
||||||
|
UPDATE `settings` SET `title_fa` = 'فوتر', `type` = 'longtext', `can_edit` = 1,
|
||||||
|
`description` = 'محتوای فوتر لندینگ. در اپ کاربر با get_settings در دسترس است.'
|
||||||
|
WHERE `key` = 'landing_footer';
|
||||||
|
|
||||||
-- [super-admin can_edit=0]
|
-- [super-admin can_edit=0]
|
||||||
-- UPDATE `settings` SET
|
-- UPDATE `settings` SET
|
||||||
-- `title_fa` = 'نمایش چت به کاربران',
|
-- `title_fa` = 'نمایش چت به کاربران',
|
||||||
|
|||||||
@@ -72,8 +72,10 @@ UPDATE `settings` SET `menu` = CASE
|
|||||||
'products_site_link_enabled', 'products_site_link_url', 'products_site_link_text', 'has_chat',
|
'products_site_link_enabled', 'products_site_link_url', 'products_site_link_text', 'has_chat',
|
||||||
'enable_conditional_order', 'enable_product_funds', 'has_page_price_trigger', 'has_page_notification',
|
'enable_conditional_order', 'enable_product_funds', 'has_page_price_trigger', 'has_page_notification',
|
||||||
'has_page_gift_card', 'enable_customer_level_profit', 'jibit_enabled', 'customer_direct_sms',
|
'has_page_gift_card', 'enable_customer_level_profit', 'jibit_enabled', 'customer_direct_sms',
|
||||||
'has_page_earn_money', 'group_sms', 'customer_special_pricing', 'permision__enabled', 'show_to_users_chat'
|
'has_page_earn_money', 'group_sms', 'customer_special_pricing', 'permision__enabled', 'show_to_users_chat',
|
||||||
|
'has_landing'
|
||||||
) THEN 'customer_features'
|
) THEN 'customer_features'
|
||||||
|
WHEN `key` LIKE 'landing_%' THEN 'website_settings'
|
||||||
ELSE 'misc'
|
ELSE 'misc'
|
||||||
END;
|
END;
|
||||||
|
|
||||||
@@ -108,6 +110,7 @@ UPDATE `settings` SET `show_to_user` = 1 WHERE `key` IN (
|
|||||||
'fee_deduct_from_gold',
|
'fee_deduct_from_gold',
|
||||||
'fee_service_charge_percentage',
|
'fee_service_charge_percentage',
|
||||||
'has_chat',
|
'has_chat',
|
||||||
|
'has_landing',
|
||||||
'has_page_earn_money',
|
'has_page_earn_money',
|
||||||
'has_page_gift_card',
|
'has_page_gift_card',
|
||||||
'has_page_notification',
|
'has_page_notification',
|
||||||
@@ -148,3 +151,5 @@ UPDATE `settings` SET `show_to_user` = 1 WHERE `key` IN (
|
|||||||
'wss_path',
|
'wss_path',
|
||||||
'zaarbox_api_url'
|
'zaarbox_api_url'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
UPDATE `settings` SET `show_to_user` = 1 WHERE `key` LIKE 'landing_%';
|
||||||
|
|||||||
@@ -60,6 +60,16 @@ class ConfigServiceOtpHostTest extends TestCase
|
|||||||
$this->assertSame('https://my.goldarvin.ir', $cfg->resolvePublicSiteUrl($request));
|
$this->assertSame('https://my.goldarvin.ir', $cfg->resolvePublicSiteUrl($request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_does_not_fall_back_to_laravel_api_host(): void
|
||||||
|
{
|
||||||
|
config(['app.url' => 'https://goldarvin.gholaktala.ir/server']);
|
||||||
|
$request = Request::create('https://goldarvin.gholaktala.ir/server/admin/customer', 'POST');
|
||||||
|
$this->app->instance('request', $request);
|
||||||
|
|
||||||
|
$this->assertSame('', $this->cfgWithAppUrl(null)->resolvePublicSiteUrl($request));
|
||||||
|
$this->assertSame('', $this->cfgWithAppUrl(null)->resolvePublicHost($request));
|
||||||
|
}
|
||||||
|
|
||||||
public function test_app_url_without_scheme_gets_https(): void
|
public function test_app_url_without_scheme_gets_https(): void
|
||||||
{
|
{
|
||||||
$request = Request::create('https://goldarvin.gholaktala.ir/server/auth/login', 'POST');
|
$request = Request::create('https://goldarvin.gholaktala.ir/server/auth/login', 'POST');
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class SuperAdminProgramSettingsOverlayTest extends TestCase
|
|||||||
'enable_gold_credit',
|
'enable_gold_credit',
|
||||||
'enable_product_funds',
|
'enable_product_funds',
|
||||||
'group_sms',
|
'group_sms',
|
||||||
|
'has_landing',
|
||||||
'has_page_earn_money',
|
'has_page_earn_money',
|
||||||
'has_page_gift_card',
|
'has_page_gift_card',
|
||||||
'has_page_notification',
|
'has_page_notification',
|
||||||
@@ -29,7 +30,7 @@ class SuperAdminProgramSettingsOverlayTest extends TestCase
|
|||||||
];
|
];
|
||||||
|
|
||||||
$this->assertSame($expected, SuperAdminProgramSettingsOverlay::SUPERADMIN_OWNED_KEYS);
|
$this->assertSame($expected, SuperAdminProgramSettingsOverlay::SUPERADMIN_OWNED_KEYS);
|
||||||
$this->assertCount(15, SuperAdminProgramSettingsOverlay::SUPERADMIN_OWNED_KEYS);
|
$this->assertCount(16, SuperAdminProgramSettingsOverlay::SUPERADMIN_OWNED_KEYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_overlay_keeps_only_access_keys(): void
|
public function test_overlay_keeps_only_access_keys(): void
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit;
|
||||||
|
|
||||||
|
use App\Models\Withdraw;
|
||||||
|
use App\Services\ZarinSyncService;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ZarinRialWalletDirectionTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_customer_rial_withdraw_is_direction_one_even_if_cash_positive(): void
|
||||||
|
{
|
||||||
|
$withdraw = new Withdraw([
|
||||||
|
'type' => 2,
|
||||||
|
'cash' => 200000000,
|
||||||
|
'note' => 'برداشت ریالی',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertSame(1, app(ZarinSyncService::class)->rialWalletDirectionFromWithdraw($withdraw));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_admin_deposit_with_positive_cash_is_direction_zero(): void
|
||||||
|
{
|
||||||
|
$withdraw = new Withdraw([
|
||||||
|
'type' => null,
|
||||||
|
'cash' => 200000000,
|
||||||
|
'note' => 'افزایش موجودی',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertSame(0, app(ZarinSyncService::class)->rialWalletDirectionFromWithdraw($withdraw));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_admin_deduction_with_negative_cash_is_direction_one(): void
|
||||||
|
{
|
||||||
|
$withdraw = new Withdraw([
|
||||||
|
'cash' => -50000000,
|
||||||
|
'note' => 'کسر',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertSame(1, app(ZarinSyncService::class)->rialWalletDirectionFromWithdraw($withdraw));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_desc_bardasht_is_withdraw_and_variz_is_deposit(): void
|
||||||
|
{
|
||||||
|
$svc = app(ZarinSyncService::class);
|
||||||
|
$this->assertSame(1, $svc->rialWalletDirectionFromDesc('برداشت وجه'));
|
||||||
|
$this->assertSame(0, $svc->rialWalletDirectionFromDesc('واریز وجه'));
|
||||||
|
$this->assertSame(0, $svc->rialWalletDirectionFromDesc('واریز — افزایش موجودی'));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user