merge goldali with abshode_khanegi branch
This commit is contained in:
@@ -7,8 +7,11 @@ use App\Models\Cnt;
|
||||
use App\Models\Customer;
|
||||
use App\Models\Order;
|
||||
use App\Models\Price;
|
||||
use App\Models\Product;
|
||||
use App\Models\TransactionsAdmin;
|
||||
use App\Models\Withdraw;
|
||||
use App\Services\ConfigService;
|
||||
use App\Services\EtehadiePriceService;
|
||||
use Carbon\Carbon;
|
||||
use Hekmatinasser\Verta\Verta;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -17,7 +20,8 @@ use Rooyesh\Wallet\models\Wallet;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function dashboardMaster(){
|
||||
public function dashboardMaster(Request $request)
|
||||
{
|
||||
$tallaPrice = Price::latest()->first();
|
||||
// $talla->update_date = Verta::instance(Carbon::parse($talla->updated_at))->format('Y/n/j-H:i');
|
||||
$new_customer_major = Customer::where('type',1)->whereDate('created_at','<=',now())->whereDate('created_at','>=',now()->subMonths(3))
|
||||
@@ -56,6 +60,49 @@ class DashboardController extends Controller
|
||||
|
||||
$transactions_admins = TransactionsAdmin::with(['withdraw','order',"user",'customerTo','customer'])->get();
|
||||
|
||||
$startDate = $request->startDateProduct
|
||||
? Carbon::parse($request->startDateProduct)->startOfDay()
|
||||
: now()->subMonth()->startOfDay();
|
||||
$endDate = $request->endDateProduct
|
||||
? Carbon::parse($request->endDateProduct)->endOfDay()
|
||||
: now()->endOfDay();
|
||||
|
||||
$products_report = Product::with(['limitedTransactions' => function ($query) use ($startDate, $endDate) {
|
||||
$query->whereBetween('created_at', [$startDate, $endDate]);
|
||||
}])
|
||||
->whereHas('limitedTransactions', function ($query) use ($startDate, $endDate) {
|
||||
$query->whereBetween('created_at', [$startDate, $endDate]);
|
||||
})
|
||||
->get();
|
||||
|
||||
$products_report->each(function ($product) {
|
||||
$product->transaction_sum_gold_buy = $product->limitedTransactions
|
||||
->where('gold', '>', 0)
|
||||
->sum('gold');
|
||||
$product->total_negative_gold_sell = $product->limitedTransactions
|
||||
->where('gold', '<', 0)
|
||||
->sum('gold');
|
||||
$product->total_gold = $product->limitedTransactions->sum('gold');
|
||||
});
|
||||
|
||||
$gramPrice = (int) ($tallaPrice->price ?? 0);
|
||||
$FPrices = [];
|
||||
if (app(ConfigService::class)->usesEtehadiePricing()) {
|
||||
$etehadie = app(EtehadiePriceService::class)->fetchMainGramPrice();
|
||||
if (($etehadie['price'] ?? 0) > 0) {
|
||||
$gramPrice = (int) round($etehadie['price'] / 10);
|
||||
$FPrices[] = [
|
||||
'id' => 'etehadie',
|
||||
'name' => 'اتحادیه',
|
||||
'price' => $gramPrice,
|
||||
'profit' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
$FPrices[] = ['id' => '1', 'name' => 'افجو', 'price' => $gramPrice, 'profit' => 0];
|
||||
|
||||
$gatewayRow = Cnt::where('key', 'is_gateway_open')->first();
|
||||
|
||||
return response()->json([
|
||||
'GT_all_customers'=>$GT_all_customers,
|
||||
'GT_no_login_customers'=>$GT_no_login_customers,
|
||||
@@ -65,7 +112,12 @@ class DashboardController extends Controller
|
||||
'GT_active_customers_data'=>$GT_active_customers_data,
|
||||
'GT_deActive_customers'=>$GT_all_customers-$GT_active_customers,
|
||||
'GT_transactions_admins'=>$transactions_admins,
|
||||
'price'=>$tallaPrice->price,
|
||||
'GT_products_report' => $products_report,
|
||||
'update_date' => now()->timezone('Asia/Tehran')->format('Y-m-d H:i:s'),
|
||||
'pricem' => round($gramPrice * 4.331802 / 10000) * 10000,
|
||||
'prices' => $FPrices,
|
||||
'is_gateway_open' => $gatewayRow && (string) $gatewayRow->value === '1',
|
||||
'price'=>$tallaPrice?->price ?? $gramPrice,
|
||||
'new_customer_major'=>$new_customer_major,
|
||||
'new_customer_single'=>$new_customer_single,
|
||||
'all_customer_major'=>$all_customer_major,
|
||||
@@ -80,6 +132,23 @@ class DashboardController extends Controller
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
public function gateway()
|
||||
{
|
||||
$cnt = Cnt::where('key', 'is_gateway_open')->first();
|
||||
if (!$cnt) {
|
||||
$cnt = Cnt::create([
|
||||
'key' => 'is_gateway_open',
|
||||
'value' => '1',
|
||||
]);
|
||||
} else {
|
||||
$cnt->value = ((string) $cnt->value === '0') ? '1' : '0';
|
||||
$cnt->save();
|
||||
}
|
||||
|
||||
return $cnt;
|
||||
}
|
||||
|
||||
public function getBalance(Request $request)
|
||||
{
|
||||
$startDate = Carbon::parse($request->startDate)->startOfDay();
|
||||
@@ -88,9 +157,15 @@ class DashboardController extends Controller
|
||||
$response["liman"]=[];
|
||||
$response["kalhor"]=[];
|
||||
|
||||
$excludeCustomerId = (int) app(ConfigService::class)->getInt('balance_report_exclude_customer_id', 4);
|
||||
|
||||
$response["pay_at_update"] = Order::whereNull('pay_at')->update(['pay_at' => DB::raw('created_at')]);
|
||||
//liman
|
||||
$response["liman"]["orders"] = Order::with(['transactions.product'])->where('customer_id', '!=', 4)->where('sts', 2)->whereBetween('pay_at', [$startDate, $endDate])->get();
|
||||
$response["liman"]["orders"] = Order::with(['transactions.product', 'customer'])
|
||||
->where('customer_id', '!=', $excludeCustomerId)
|
||||
->where('sts', 2)
|
||||
->whereBetween('pay_at', [$startDate, $endDate])
|
||||
->get();
|
||||
$response["liman"]["balance"]["gold"] = 0;
|
||||
$response["liman"]["balance"]["cash"] = 0;
|
||||
$response["liman"]["balance_abs"]["gold"] = 0;
|
||||
|
||||
@@ -151,8 +151,6 @@ class ProductController extends SuperController
|
||||
|
||||
public function getPrice()
|
||||
{
|
||||
// $RPrices = Http::get('https://pricing.liman.gold/pricingApi', [])->json();
|
||||
// $RPrices = Http::get('https://pricing.liman.gold/pricingApi', [])->json();
|
||||
// $RPrices =Http::timeout(40)->get('http://212.23.201.173:8381/price_list', [])->json();
|
||||
$RPrices = Http::timeout(40)->get('http://109.122.246.48:4949/api/free', [])->json();
|
||||
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Admin\TahesabController;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Cnt;
|
||||
use App\Models\Customer;
|
||||
use App\Models\Setting;
|
||||
use App\Models\TahesabCustomer;
|
||||
use App\Models\User;
|
||||
use App\Services\ConfigService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
@@ -85,7 +84,6 @@ class AuthController extends Controller
|
||||
$user->token_sms = $rand;
|
||||
$user->save();
|
||||
|
||||
// $this->SendMessage($mobile, 'رمز عبور یکبار مصرف شما جهت ورود به اپلیکشن ' . $this->ConfigService('name_fa') . ' ' . $rand . ' می باشد' . PHP_EOL . "@$subDomain" . " #" . $r and,true ,"sms");
|
||||
$this->SendMessage($mobile, 'رمز عبور یکبار مصرف شما جهت ورود به اپلیکشن ' . $this->Config('name_fa') . ' ' . $rand . ' می باشد' . PHP_EOL . "@$subDomain" . " #" . $rand,true ,"sms");
|
||||
|
||||
return $user->id;
|
||||
@@ -120,17 +118,20 @@ class AuthController extends Controller
|
||||
|
||||
public function register(Request $request)
|
||||
{
|
||||
$cfg = app(ConfigService::class);
|
||||
|
||||
$register = Setting::where('key', 'open_register')->first();
|
||||
if($register->is_active == 0){
|
||||
if ($register && (int) $register->is_active === 0) {
|
||||
throw new UnprocessableEntityHttpException('امکان ثبت نام وجود ندارد.');
|
||||
}
|
||||
|
||||
$data = $request->all();
|
||||
$data['type'] = 2;
|
||||
if (isset($data['referral_id'])) {
|
||||
$data['referral_to_id'] = $data['referral_id'];
|
||||
}
|
||||
$data['name'] = $data['fname'] . ' ' . $data['lname'];
|
||||
$data['active'] = Setting::where('key', 'auto_active_customer')->first()->value === 'true';
|
||||
$data['active'] = $cfg->getBool('auto_active_customer', false);
|
||||
if (isset($data['type']) && $data['type'] == 2) {
|
||||
$data['active'] = true;
|
||||
}
|
||||
@@ -138,40 +139,74 @@ class AuthController extends Controller
|
||||
if (!is_null($cnt)) {
|
||||
$data['gram'] = $cnt->value;
|
||||
}
|
||||
// Generate unique referral ID
|
||||
|
||||
do {
|
||||
$uniqueId = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz'), 0, 4);
|
||||
} while (Customer::where('referral_id', $uniqueId)->exists());
|
||||
$data['referral_id'] = $uniqueId;
|
||||
|
||||
// Handle referrer
|
||||
if (isset($data['n_code']) && $cfg->getBool('register_jibit_verify', false)) {
|
||||
$existing = Customer::where('n_code', $data['n_code'])->first();
|
||||
if (!empty($existing)) {
|
||||
return response()->json(['message' => 'کد ملی تکراری است'], 422);
|
||||
}
|
||||
$response = $this->jibitNcode($data['n_code'], $data['mobile']);
|
||||
if (isset($response['matched']) && $response['matched'] == true) {
|
||||
$data['is_legal'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$referrer = null;
|
||||
if (isset($data['referral_to_id'])) {
|
||||
$referrer = Customer::where('referral_id', $data['referral_to_id'])->first();
|
||||
if ($referrer) {
|
||||
// $messageRef = "{$data['name']} با کد معرف شما ثبت نام کرد.\nدر صورت احراز او پاداش میگیرید.\n" . $this->ConfigService('name_fa');
|
||||
// $this->Send($referrer->mobile, $messageRef);
|
||||
$AdminMessage = "با معرفی {$referrer->name}، کاربر {$data['name']} در اپ {$this->Config('name_fa')} ثبت نام کرده و منتظر تایید شماست.";
|
||||
User::where('send_sms', 1)->get()->each(function ($admin) use ($AdminMessage, $data,) {
|
||||
$this->send($admin->mobile, $AdminMessage);
|
||||
});
|
||||
if ($cfg->getBool('register_referral_sms_to_referrer', false)) {
|
||||
$messageRef = "{$data['name']} با کد معرف شما ثبت نام کرد.\nبا اولین خرید او پاداش دریافت میکنید.\n" . $this->Config('name_fa');
|
||||
$this->Send($referrer->mobile, $messageRef);
|
||||
} else {
|
||||
$adminMessage = "با معرفی {$referrer->name}، کاربر {$data['name']} در اپ {$this->Config('name_fa')} ثبت نام کرده و منتظر تایید شماست.";
|
||||
User::where('send_sms', 1)->get()->each(function ($admin) use ($adminMessage) {
|
||||
$this->send($admin->mobile, $adminMessage);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return response()->json(["message" => "کد نامعتبره"], 422);
|
||||
return response()->json(['message' => 'کد نامعتبره'], 422);
|
||||
}
|
||||
} else {
|
||||
if ($this->Config('referral_id') == "required") {
|
||||
return response()->json(["message" => "کد معرف اجباریست"], 422);
|
||||
if (
|
||||
$this->Config('referral_id') == 'required'
|
||||
|| $cfg->getBool('is_requaire_referral', false)
|
||||
) {
|
||||
return response()->json(['message' => 'کد معرف اجباریست'], 422);
|
||||
}
|
||||
}
|
||||
|
||||
$data['active'] = true;
|
||||
// Create customer
|
||||
|
||||
$customer = Customer::create($data);
|
||||
|
||||
// $tah = new TahesabController();
|
||||
// return $tah->store([
|
||||
// 'customer_id' => $customer->id,
|
||||
// ]);
|
||||
if ($referrer) {
|
||||
$referrer->increment('total_referrals');
|
||||
}
|
||||
|
||||
$message = "{$data['name']} عزیز، اکانت شما در پلتفرم {$this->Config('name_fa')} ایجاد شد.\nنام کاربری: {$data['mobile']}\n\nورود به نرم افزار:\n{$this->Config('app_url')}";
|
||||
if ($cfg->getBool('register_jibit_verify', false) && isset($customer->is_legal)) {
|
||||
$message = $customer->is_legal === 1
|
||||
? 'احرازهویت برای ' . $customer->name . ' با کد ' . $customer->id . ' انجام شد.'
|
||||
: 'احرازهویت برای ' . $customer->name . ' با کد ' . $customer->id . ' تأیید نشد لطفا از پنل ادمین اقدام فرمایید.';
|
||||
User::where('send_sms', 1)->get()->each(function ($admin) use ($message) {
|
||||
$this->SendMessage($admin->mobile, $message);
|
||||
});
|
||||
}
|
||||
|
||||
$message = "{$data['name']} عزیز، اکانت شما در پلتفرم {$this->Config('name_fa')} ایجاد شد.";
|
||||
if ($cfg->getBool('register_referral_bonus_message', false)) {
|
||||
$bonus = Setting::where('is_active', 1)->where('key', 'referral_bonus_amount')->first();
|
||||
if ($bonus) {
|
||||
$bonusValues = json_decode($bonus->value, true);
|
||||
$message .= "\nپس از اولین خرید مقدار، " . number_format((int) ($bonusValues[0]['customer'] ?? 0)) . ' ریال پاداش دریافت خواهید کرد.';
|
||||
}
|
||||
}
|
||||
$message .= "\nنام کاربری: {$data['mobile']}\nورود به نرم افزار: {$this->Config('app_url')}";
|
||||
$this->Send($data['mobile'], $message);
|
||||
|
||||
return response()->json($customer);
|
||||
|
||||
@@ -303,6 +303,46 @@ class Controller extends BaseController
|
||||
return $amount > 0 && $amount !== 1.0;
|
||||
}
|
||||
|
||||
public function jibitNcode($nationalCode, $mobile)
|
||||
{
|
||||
$counter = Cnt::where('key', 'jibit_counter')->first();
|
||||
if (empty($counter)) {
|
||||
$counter = new Cnt();
|
||||
$counter->key = 'jibit_counter';
|
||||
$counter->value = 0;
|
||||
$counter->save();
|
||||
}
|
||||
|
||||
$apiData['refresh'] = Http::withoutVerifying()->withToken(Storage::get('refreshTokenGibit'))
|
||||
->post('https://napi.jibit.ir/ide/v1/tokens/generate', [
|
||||
'apiKey' => 'J5WxrPxPOg',
|
||||
'secretKey' => '7dgeE70xQXsQL7r36j3wif6X9',
|
||||
])->json();
|
||||
Storage::put('accessTokenGibit', $apiData['refresh']['accessToken']);
|
||||
|
||||
$response = Http::withoutVerifying()->withToken(Storage::get('accessTokenGibit'))
|
||||
->get("https://napi.jibit.ir/ide/v1/services/matching?mobileNumber=$mobile&nationalCode=$nationalCode")
|
||||
->json();
|
||||
|
||||
if ((isset($response['code'])) && $response['code'] == 'forbidden') {
|
||||
$refreshToken = Http::withoutVerifying()->withToken(Storage::get('refreshTokenGibit'))
|
||||
->post('https://napi.jibit.ir/ide/v1/tokens/generate', [
|
||||
'apiKey' => 'J5WxrPxPOg',
|
||||
'secretKey' => '7dgeE70xQXsQL7r36j3wif6X9',
|
||||
])->json();
|
||||
Storage::put('accessTokenGibit', $refreshToken['accessToken']);
|
||||
Storage::put('refreshTokenGibit', $refreshToken['refreshToken']);
|
||||
$response = Http::withoutVerifying()->withToken(Storage::get('accessTokenGibit'))
|
||||
->get("https://napi.jibit.ir/ide/v1/services/matching?mobileNumber=$mobile&nationalCode=$nationalCode")
|
||||
->json();
|
||||
}
|
||||
|
||||
$counter->value = (int) $counter->value + 1;
|
||||
$counter->save();
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function jibit($method, $p1 = null, $p2 = null, $p3 = null)
|
||||
{
|
||||
$counter = Cnt::where('key', 'jibit_counter')->first();
|
||||
|
||||
@@ -5,8 +5,10 @@ namespace App\Http\Controllers\Customer;
|
||||
use App\DB\Te\TableEngine;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\CheckOrder;
|
||||
use App\Models\Customer;
|
||||
use App\Models\Order;
|
||||
use App\Models\Price;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -111,6 +113,38 @@ class OrderController extends Controller
|
||||
$this->Send(auth('customer')->user()->mobile,$message);
|
||||
return response()->json($order);
|
||||
}
|
||||
|
||||
public function profit_gold()
|
||||
{
|
||||
$customer = Customer::with(['wallet', 'orders'])
|
||||
->findOrFail(auth('customer')->id());
|
||||
|
||||
$lastPrice = Price::latest('created_at')->value('price') ?? 0;
|
||||
$goldAmount = $customer->wallet->first()->gold ?? 0;
|
||||
$orders = $customer->orders;
|
||||
|
||||
$averagePrice = $orders->avg('price') ?? 0;
|
||||
$averageFee = $orders->avg('fee') ?? 0;
|
||||
$totalProfit = $orders->sum(fn ($order) => $order->direction === 1
|
||||
? $order->profit_per_moment
|
||||
: -$order->profit_per_moment);
|
||||
|
||||
$feeSingle = (float) (Setting::where('key', 'fee_single')->value('value') ?? config('fee.single', 0));
|
||||
$currentPriceWithFee = $lastPrice + $feeSingle;
|
||||
$averagePriceWithFee = $averagePrice + $averageFee;
|
||||
|
||||
return response()->json([
|
||||
'current_portfolio_value' => $currentPriceWithFee * $goldAmount,
|
||||
'average_portfolio_value' => $averagePriceWithFee * $goldAmount,
|
||||
'unrealized_profit' => ($currentPriceWithFee - $averagePriceWithFee) * $goldAmount,
|
||||
'average_price' => $averagePrice,
|
||||
'average_fee' => $averageFee,
|
||||
'total_profit' => $totalProfit,
|
||||
'gold_holdings' => $goldAmount,
|
||||
'customer' => $customer->makeHidden(['password', 'token_sms']),
|
||||
]);
|
||||
}
|
||||
|
||||
public function table(Request $request){
|
||||
$records = Order::where('customer_id',auth('customer')->id())
|
||||
->where('type',1);
|
||||
|
||||
@@ -226,27 +226,6 @@ class WalletController extends Controller
|
||||
if (Customer::where("id", $customer->id)->first()->token_sms != $request->otp) {
|
||||
return response()->json(['message' => 'رمز دوم اشتباه میباشد'], 422);
|
||||
}
|
||||
/* try {
|
||||
$response = Http::withHeaders([
|
||||
'Authorization' => 'Bearer ' . $this->ConfigService('liman_token')
|
||||
])->post('https://limangold.ir/kernel/customer/fin', [
|
||||
'receive_bank_shaba' => $bCart->shaba_number,
|
||||
'receive_bank_card' => $bCart->cart_number,
|
||||
'receive_name' => $customer->name,
|
||||
'receive_account_number' => $bCart->account_number,
|
||||
'description' => $this->ConfigService('name_fa') . ' | ' . $customer->mobile . ' | ' . $customer->name . ' | ' . $Withdraw->id,
|
||||
'amount' => '1000'
|
||||
])->json();
|
||||
if (isset($response['id'])) {
|
||||
$Withdraw->note = $response['id'];
|
||||
$Withdraw->save();
|
||||
$message = "کاربر $userName با شماره موبایل $userMobile درخواست برداشت مبلغ " . number_format($withdrawAmount) . " ثبت کرد و به لیمان منتقل شد. با تشکر، $companyName";
|
||||
} else {
|
||||
$message = "کاربر $userName با شماره موبایل $userMobile درخواست برداشت مبلغ " . number_format($withdrawAmount) . " ریال را دارد. با تشکر، $companyName";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$message = "کاربر $userName با شماره موبایل $userMobile درخواست برداشت مبلغ " . number_format($withdrawAmount) . " ریال را دارد. با تشکر، $companyName";
|
||||
}*/
|
||||
}
|
||||
User::where('send_sms', 1)->get()->each(function ($admin) use ($message) {
|
||||
$this->Send($admin->mobile, $message . ' ' . now()->format('H:i:s'));
|
||||
|
||||
@@ -42,7 +42,7 @@ class SingleOrderController extends Controller
|
||||
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
};
|
||||
|
||||
if ($toBool($config->Config('montzar_price'))) {
|
||||
if ($config->usesMontzarPricing()) {
|
||||
$montzar_closed = $toBool($config->Config('montzar_closed'));
|
||||
$montzar_buy = !$toBool($config->Config('montzar_open_buy')) && (int) $data['direction'] === 1;
|
||||
$montzar_sell = !$toBool($config->Config('montzar_open_sell')) && (int) $data['direction'] === 2;
|
||||
@@ -507,7 +507,7 @@ class SingleOrderController extends Controller
|
||||
|
||||
private function sendProviderOrder(array &$data, $wallet_interface, int $direction)
|
||||
{
|
||||
if ($this->Config('montzar_price') == "true") {
|
||||
if (app(\App\Services\ConfigService::class)->usesMontzarPricing()) {
|
||||
try {
|
||||
return app(MontzarService::class)->sendOrder($data);
|
||||
} catch (UnprocessableEntityHttpException $e) {
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Models\Setting;
|
||||
use App\Models\ConfigStatic;
|
||||
use App\Models\Customer;
|
||||
use App\Services\MontzarService;
|
||||
use App\Services\EtehadiePriceService;
|
||||
use App\Models\Price;
|
||||
use App\Models\PriceCandle;
|
||||
use App\Models\PriceTrigger;
|
||||
@@ -140,6 +141,7 @@ class ReportController extends Controller
|
||||
$closed = false;
|
||||
|
||||
$montzar_price = 0;
|
||||
$etehadie_teh = 0;
|
||||
$tgn = 0;
|
||||
$pr_price = 0;
|
||||
|
||||
@@ -165,11 +167,14 @@ class ReportController extends Controller
|
||||
'main_product' => 0,
|
||||
'taban_gohar' => 0,
|
||||
'montzar' => 0,
|
||||
'etehadie_teh' => 0,
|
||||
'closed' => true,
|
||||
'time' => now(),
|
||||
]);
|
||||
};
|
||||
|
||||
$priceProvider = $config->priceProvider();
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| بستن قیمت طبق منطق قبلی
|
||||
@@ -222,14 +227,22 @@ class ReportController extends Controller
|
||||
$diff_buy = (float) $settingsMap->get('buy_fee', $difference);
|
||||
$diff_sell = (float) $settingsMap->get('sell_fee', $difference);
|
||||
|
||||
$montzar_price_enabled = $toBool($config->Config('montzar_price'));
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| اتحادیه (گلد علی / refPricer) — فقط وقتی price_provider=etehadie
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
if ($priceProvider === 'etehadie' && !$closed) {
|
||||
$etehadie = app(EtehadiePriceService::class)->fetchMainGramPrice();
|
||||
$etehadie_teh = (int) ($etehadie['price'] ?? 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Montzar - اولویت اول
|
||||
| Montzar — فقط وقتی price_provider=montzar
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
if ($montzar_price_enabled && !$closed) {
|
||||
if ($priceProvider === 'montzar' && !$closed) {
|
||||
try {
|
||||
$montzarBaseUrl = rtrim($config->Config('montzar_base_url') ?: 'https://montpar.ir', '/');
|
||||
$montzarProductId = (int) ($config->Config('montzar_product_id') ?: 25);
|
||||
@@ -347,18 +360,19 @@ class ReportController extends Controller
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| انتخاب قیمت اصلی
|
||||
| اولویت:
|
||||
| Montzar -> Taban Gohar -> Main Product
|
||||
| انتخاب قیمت اصلی (همان price_provider)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
if ($montzar_price_enabled && ($montzar_price ?? 0) !== 0 && !$closed) {
|
||||
if ($priceProvider === 'etehadie' && $etehadie_teh > 0 && !$closed) {
|
||||
$priceValue = (int) $etehadie_teh;
|
||||
$priceSource = 'etehadie';
|
||||
} elseif ($priceProvider === 'montzar' && ($montzar_price ?? 0) !== 0 && !$closed) {
|
||||
$priceValue = (int) $montzar_price;
|
||||
$priceSource = 'montzar';
|
||||
} elseif ($toBool($taban_gohar_price) && ($tgn ?? 0) !== 0 && !$closed) {
|
||||
} elseif ($priceProvider === 'taban_gohar' && $toBool($taban_gohar_price) && ($tgn ?? 0) !== 0 && !$closed) {
|
||||
$priceValue = $tgn;
|
||||
$priceSource = 'taban_gohar';
|
||||
} elseif ($toBool($main_product_price) && ($pr_price ?? 0) !== 0 && !$closed) {
|
||||
} elseif ($priceProvider === 'main_product' && $toBool($main_product_price) && ($pr_price ?? 0) !== 0 && !$closed) {
|
||||
$priceValue = $pr_price;
|
||||
$priceSource = 'main_product';
|
||||
} else {
|
||||
@@ -376,6 +390,7 @@ class ReportController extends Controller
|
||||
'main_product' => $pr_price ?? 0,
|
||||
'taban_gohar' => $tgn ?? 0,
|
||||
'montzar' => $montzar_price ?? 0,
|
||||
'etehadie_teh' => $etehadie_teh ?? 0,
|
||||
'closed' => true,
|
||||
'time' => now(),
|
||||
]);
|
||||
@@ -392,6 +407,7 @@ class ReportController extends Controller
|
||||
'main_product' => $pr_price ?? 0,
|
||||
'taban_gohar' => $tgn ?? 0,
|
||||
'montzar' => $montzar_price ?? 0,
|
||||
'etehadie_teh' => $etehadie_teh ?? 0,
|
||||
'closed' => true,
|
||||
'time' => now(),
|
||||
]);
|
||||
@@ -488,6 +504,7 @@ class ReportController extends Controller
|
||||
'main_product' => $pr_price ?? 0,
|
||||
'taban_gohar' => $tgn ?? 0,
|
||||
'montzar' => $montzar_price ?? 0,
|
||||
'etehadie_teh' => $etehadie_teh ?? 0,
|
||||
'closed' => false,
|
||||
'time' => now(),
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Site;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Cnt;
|
||||
use App\Models\Customer;
|
||||
use App\Models\Order;
|
||||
use App\Models\Price;
|
||||
use App\Models\TransactionsAdmin;
|
||||
use App\Models\User;
|
||||
use App\Models\Withdraw;
|
||||
use Illuminate\Http\Request;
|
||||
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
||||
use Rooyesh\Wallet\interfaces\WalletInterface;
|
||||
use Rooyesh\Wallet\models\Transaction;
|
||||
use Rooyesh\Wallet\models\Wallet;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
|
||||
class RequestsController extends Controller
|
||||
{
|
||||
public function order(Request $request)
|
||||
{
|
||||
$data = $request->all();
|
||||
|
||||
$transactionType = $data['method'] == 'cash' ? 'ریالی' : 'طلایی';
|
||||
|
||||
$customer_id = Customer::where('mobile', $request->mobile)->first()?->id;
|
||||
if (!$customer_id) {
|
||||
throw new UnprocessableEntityHttpException('کاربری با این شماره موبایل یافت نشد.');
|
||||
}
|
||||
|
||||
$data['customer_id'] = $customer_id;
|
||||
$latestPrice = Price::latest()->first();
|
||||
$data['price'] = $latestPrice->price;
|
||||
$data['price_id'] = $latestPrice->id;
|
||||
$data['difference'] = 0;
|
||||
$data['service_charge_percentage'] = 0;
|
||||
$data['pay_at'] = now();
|
||||
$data['sts'] = 2;
|
||||
$data['type'] = 2;
|
||||
$data['payment_sts'] = 4;
|
||||
$data['price_original'] = $data['price'];
|
||||
$data['total_price_original'] = $data['amount'] * ($data['price_original'] ?? 0);
|
||||
$data['payment_remind'] = 0;
|
||||
$data['fee'] = $data['difference'];
|
||||
$data['total_fee'] = $data['amount'] * $data['difference'];
|
||||
|
||||
$myCustomer = Customer::find($customer_id);
|
||||
$wallet_interface = new WalletInterface($customer_id, 2);
|
||||
$wallet = Wallet::where('owner', $customer_id)->whereNull('fund_id')->first();
|
||||
|
||||
$response = [];
|
||||
|
||||
// === اینجا قبلاً return بود و همه چیز قطع میشد ===
|
||||
if ($data['method'] == 'cash') {
|
||||
$response['cash'] = $this->processCashTransaction($data, $wallet, $wallet_interface, $myCustomer);
|
||||
} else if ($data['method'] == 'gold') {
|
||||
$response['gold'] = $this->processGoldTransaction($data, $wallet, $wallet_interface, $myCustomer);
|
||||
}
|
||||
|
||||
$response['Notifications'] = $this->sendTransactionNotifications($data, $transactionType);
|
||||
|
||||
return response()->json($response);
|
||||
}
|
||||
public function getWallet(Request $request)
|
||||
{
|
||||
$customer_id = Customer::where('mobile', $request->mobile)->first()?->id;
|
||||
if($customer_id && $customer_id !== null){
|
||||
$wallet = Wallet::select(['owner','gold','cash'])->where('owner',$customer_id)->first();
|
||||
}else{
|
||||
throw new UnprocessableEntityHttpException('کاربری با این شماره موبایل یافت نشد.');
|
||||
}
|
||||
return $wallet;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//for orders
|
||||
private function processCashTransaction(&$data, $wallet, $wallet_interface,$myCustomer)
|
||||
{
|
||||
$response = [];
|
||||
// $data['price'] += $data['difference'];
|
||||
$data['total_price'] = $data['amount'] /** $data['price']*/;
|
||||
$data['payment_amount'] = $data['total_price'];
|
||||
|
||||
// $data['service_charge'] = $data['service_charge_percentage'] * $data['amount'];
|
||||
// $data['service_charge_price'] = $data['service_charge'] * $data['price_original'];
|
||||
// $data['amount'] -= $data['service_charge'];
|
||||
if ($wallet->cash < $data['payment_amount']) {
|
||||
throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است');
|
||||
}
|
||||
|
||||
// $data['order'] = $order = Order::create($data);
|
||||
new TransactionInterface($wallet_interface, [
|
||||
"cash" => $data['payment_amount'],
|
||||
"gold" => 0,
|
||||
'expire_at' => now(),
|
||||
'active' => true,
|
||||
// 'order_id' => $order->id,
|
||||
'start_at' => now(),
|
||||
], 4, true, false);
|
||||
$response['1'] = 2;
|
||||
|
||||
return $response;
|
||||
}
|
||||
private function processGoldTransaction(&$data, $wallet, $wallet_interface,$myCustomer)
|
||||
{
|
||||
// if ($data['amount'] < 0.06){
|
||||
// throw new UnprocessableEntityHttpException('کمترین مقدار مجاز برای فروش 60 سوت است.');
|
||||
//
|
||||
// }
|
||||
|
||||
// $data['price'] -= $data['difference'];
|
||||
$montifyCompanyName = $this->Config('montify_name');
|
||||
$data['total_price'] = $data['amount'] * $data['price'];
|
||||
$data['payment_amount'] = $data['total_price'];
|
||||
|
||||
$data['service_charge'] = $data['service_charge_percentage'] * $data['amount'];
|
||||
$data['service_charge_price'] = $data['service_charge'] * $data['price_original'];
|
||||
$data['total_price'] = $data['amount'] * $data['price'] -$data['service_charge_price'];
|
||||
$data['payment_amount'] = $data['total_price'];
|
||||
$data['payment_amount'] -= $data['service_charge_price'];
|
||||
|
||||
if ($wallet->gold < $data['amount']) {
|
||||
throw new UnprocessableEntityHttpException('موجودی شما کمتر از میزان سفارش است');
|
||||
}
|
||||
$data['direction'] = 2;
|
||||
$data['description'] = "فروش {$data['amount']} گرم طلا برای $montifyCompanyName توسط کاربر {$myCustomer->name} با شماره موبایل {$myCustomer->mobile}";
|
||||
$data['order'] = $order = Order::create($data);
|
||||
new TransactionInterface($wallet_interface, [
|
||||
"gold" => $data['amount'],
|
||||
"cash" => 0,
|
||||
'expire_at' => now(),
|
||||
'active' => true,
|
||||
'order_id' => $order->id
|
||||
], 10, false, true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private function sendTransactionNotifications(array $data, string $transactionType)
|
||||
{
|
||||
$customer = Customer::findOrFail($data['customer_id']);
|
||||
$companyName = $this->Config('name_fa');
|
||||
$montifyCompanyName = $this->Config('montify_name');
|
||||
if($data['method'] == 'cash') {
|
||||
$message = "کاربر {$customer->name} با شماره موبایل {$customer->mobile} اقدام به برداشت موجودی از طرف $montifyCompanyName به مقدار " .
|
||||
"{$data['amount']} ریال " . " کرد. با تشکر، $companyName";
|
||||
}elseif ($data['method'] == 'gold') {
|
||||
$message = "کاربر {$customer->name} با شماره موبایل {$customer->mobile} اقدام به $transactionType طلا به مقدار " .
|
||||
"{$data['amount']} گرم به مبلغ " . number_format($data['total_price']) .
|
||||
" ریال کرد. با تشکر، $companyName";
|
||||
}
|
||||
User::where('send_sms', 1)->get()->each(function ($admin) use ($message, $data, $transactionType, $companyName) {
|
||||
$this->send($admin->mobile, $message);
|
||||
});
|
||||
// User::whereNotNull('telegram')->get()->each(function ($admin) use ($message, $data, $transactionType, $companyName) {
|
||||
// $this->SendTelegram($admin->telegram, $message);
|
||||
// });
|
||||
// $messageold = "کاربر {$customer->name} با شماره موبایل {$customer->mobile} اقدام به $transactionType طلا به مقدار " .
|
||||
// "{$data['amount']} گرم به مبلغ " . number_format($data['total_price']) .
|
||||
// " ریال کرد. با تشکر، $companyName";
|
||||
// $azOrTa = $data['direction'] == 1 ? 'از' : 'به';
|
||||
// $messageExam = "
|
||||
// {$transactionType} قلک طلا $azOrTa شما
|
||||
// {$data['amount']} گرم
|
||||
// در مظنه
|
||||
// " . number_format($data['price_original']) . " ریال";
|
||||
//
|
||||
//
|
||||
//// User::whereNotNull('telegram')->get()->each(function ($admin) use ($messageExam) {
|
||||
//// $this->SendTelegram($admin->telegram, $messageExam);
|
||||
//// });
|
||||
// User::where('send_sms', 1)->get()->each(function ($admin) use ($messageExam) {
|
||||
// $this->send($admin->mobile, $messageExam);
|
||||
// });
|
||||
|
||||
$wallet = Wallet::where('owner', $data['customer_id'])->whereNull('fund_id')->first();
|
||||
if($data['method'] == 'cash') {
|
||||
$pricePerGram = $data['total_price'];
|
||||
}elseif ($data['method'] == 'gold') {
|
||||
$pricePerGram = $data['total_price'] / $data['amount'];
|
||||
}
|
||||
|
||||
if($data['method'] == 'cash') {
|
||||
$customerMessage = "$companyName\nبرداشت ریالی \nمبلغ: {$data['amount']}\n" .
|
||||
"\nموجودی طلایی: {$wallet->gold}\n" .
|
||||
"مانده ریالی: " . number_format($wallet->cash) . "\n" . $this->Config('app_url');
|
||||
}elseif ($data['method'] == 'gold') {
|
||||
$customerMessage = "$companyName\nبرداشت طلایی \nگرم: {$data['amount']}\nقیمت گرم: " . number_format($pricePerGram) .
|
||||
"\nمبلغ کل: " . number_format($data['total_price']) . "\nموجودی طلایی: {$wallet->gold}\n" .
|
||||
"مانده ریالی: " . number_format($wallet->cash) . "\n" . $this->Config('app_url');
|
||||
}
|
||||
|
||||
|
||||
// $customerMessage = "$companyName\n$transactionType آبشده\nگرم: {$data['amount']}\nقیمت گرم: " . number_format($pricePerGram) .
|
||||
// "\nمبلغ کل: " . number_format($data['total_price']) . "\n\nموجودی طلایی: {$wallet->gold}\n" .
|
||||
// "مانده ریالی: " . number_format($wallet->cash) . "\n" . $this->Config('app_url');
|
||||
|
||||
// ارسال SMS به مشتری
|
||||
$this->send($customer->mobile, $customerMessage);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\Models\Customer;
|
||||
use App\Models\User;
|
||||
use App\Models\Withdraw;
|
||||
use App\Services\ConfigService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
||||
@@ -13,167 +14,253 @@ use Rooyesh\Wallet\interfaces\WalletInterface;
|
||||
|
||||
class paypingController extends Controller
|
||||
{
|
||||
private function cfg(): ConfigService
|
||||
{
|
||||
return app(ConfigService::class);
|
||||
}
|
||||
|
||||
private function paypingHeaders(): array
|
||||
{
|
||||
return [
|
||||
'payping' => $this->cfg()->getString(
|
||||
'payping_header_secret',
|
||||
(string) config('payment.payping_header_secret', '')
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
private function paypingStartUrl(): string
|
||||
{
|
||||
return $this->cfg()->getString(
|
||||
'payping_start_url',
|
||||
(string) config('payment.payping_start_url', 'https://payping.limangold.ir/payment/start')
|
||||
);
|
||||
}
|
||||
|
||||
private function paypingVerifyUrl(): string
|
||||
{
|
||||
return $this->cfg()->getString(
|
||||
'payping_verify_url',
|
||||
(string) config('payment.payping_verify_url', 'https://payping.limangold.ir/payment/verify')
|
||||
);
|
||||
}
|
||||
|
||||
/** اگر payping_gateway_items پر باشد (با کاما)، مثل گلد علی بین itemها میچرخد */
|
||||
private function resolveGatewayItem(): string
|
||||
{
|
||||
$list = $this->cfg()->getString('payping_gateway_items', '');
|
||||
if ($list === '') {
|
||||
return $this->cfg()->getString('payping_gateway_item', (string) config('payment.payping_gateway_item', 'gp1Q'));
|
||||
}
|
||||
|
||||
$items = array_values(array_filter(array_map('trim', explode(',', $list))));
|
||||
if ($items === []) {
|
||||
return 'gp1Q';
|
||||
}
|
||||
|
||||
$index = (int) cache()->get('payping_gateway_item_index', 0);
|
||||
if ($index >= count($items)) {
|
||||
$index = 0;
|
||||
}
|
||||
$item = $items[$index];
|
||||
cache()->put('payping_gateway_item_index', $index + 1);
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function startTransation(Request $request)
|
||||
{
|
||||
$user = auth()->user();
|
||||
$finalPriseP = $request->value;
|
||||
if ($user->id ==4){
|
||||
$finalPriseP =10700;
|
||||
if ($user->id == 4) {
|
||||
$finalPriseP = 10700;
|
||||
}
|
||||
|
||||
if ($this->cfg()->getBool('payping_round_toman', (bool) config('payment.payping_round_toman', false))) {
|
||||
$finalPriseP = (int) (ceil($finalPriseP / 10) * 10);
|
||||
}
|
||||
|
||||
$Withdraw = Withdraw::create([
|
||||
'customer_id' => auth('customer')->id(),
|
||||
"cash" => $finalPriseP,
|
||||
"note" => "واریز ریالی",
|
||||
'cash' => $finalPriseP,
|
||||
'note' => 'واریز ریالی',
|
||||
'sts' => 1,
|
||||
]);
|
||||
try {
|
||||
$response = Http::withHeaders([
|
||||
'payping' => '@XOKcNveJ$wpqYy%rdpb78oBh31Aos*bIdQemiUeeq9e9G!oey23SMXp40Ew2fodjx9J5buKNW0M4@nYMr1PR6d0KtuqhLQlx*K$', // Custom header with 'payping' value
|
||||
])->post("https://payping.limangold.ir/payment/start", [
|
||||
"amount" => $finalPriseP/10, // Payment amount
|
||||
"user_name" => $user->name, // User name
|
||||
"user_id" => $user->id, // User ID
|
||||
"user" => $user, // User ID
|
||||
"title" => $this->Config('name_fa'), // Title of the payment
|
||||
"return_url" => "https://".$this->Config('app_url')."/transactions", // Callback URL
|
||||
"server_call_url" => url("/customer/charge_account_verify/".$Withdraw->id), // Callback URL
|
||||
"item" => "gp1Q", // Item identifier
|
||||
])->json();
|
||||
|
||||
}catch (\Exception $exception){
|
||||
return response()->json(["message"=>"مشکل در ارتباط با درگاه","R"=>$response??$exception,], 422);
|
||||
if (isset($request->order_id)) {
|
||||
$Withdraw->order_id = $request->order_id;
|
||||
if (isset($request->product_id)) {
|
||||
$Withdraw->product_id = $request->product_id;
|
||||
}
|
||||
$Withdraw->save();
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'amount' => $finalPriseP / 10,
|
||||
'user_name' => $user->name,
|
||||
'user_id' => $user->id,
|
||||
'user' => $user,
|
||||
'title' => $this->Config('name_fa'),
|
||||
'return_url' => 'https://' . $this->Config('app_url') . '/transactions',
|
||||
'server_call_url' => url('/customer/charge_account_verify/' . $Withdraw->id),
|
||||
'item' => $this->resolveGatewayItem(),
|
||||
];
|
||||
|
||||
$gatewayName = $this->cfg()->getString('payping_gateway_name', (string) config('payment.payping_gateway_name', ''));
|
||||
if ($gatewayName !== '') {
|
||||
$payload['gateway'] = $gatewayName;
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::withHeaders($this->paypingHeaders())
|
||||
->post($this->paypingStartUrl(), $payload)
|
||||
->json();
|
||||
} catch (\Exception $exception) {
|
||||
return response()->json([
|
||||
'message' => 'مشکل در ارتباط با درگاه',
|
||||
'R' => $response ?? $exception,
|
||||
], 422);
|
||||
}
|
||||
|
||||
if (empty($response['id']) || empty($response['url'])) {
|
||||
return response()->json([
|
||||
'message' => 'مشکل در ارتباط با درگاه',
|
||||
'R' => $response ?? null,
|
||||
], 422);
|
||||
}
|
||||
|
||||
$Withdraw->tracked_number = $response['id'];
|
||||
$Withdraw->save();
|
||||
|
||||
return $response['url'];
|
||||
return response()->json(["res"=>$response], 422);
|
||||
}
|
||||
public function verifyTransationByID(Request $request ,$id)
|
||||
|
||||
public function verifyTransationByID(Request $request, $id)
|
||||
{
|
||||
if ($request->header('payping') !== '@XOKcNveJ$wpqYy%rdpb78oBh31Aos*bIdQemiUeeq9e9G!oey23SMXp40Ew2fodjx9J5buKNW0M4@nYMr1PR6d0KtuqhLQlx*K$') {
|
||||
$expectedSecret = $this->cfg()->getString(
|
||||
'payping_header_secret',
|
||||
(string) config('payment.payping_header_secret', '')
|
||||
);
|
||||
|
||||
if ($request->header('payping') !== $expectedSecret) {
|
||||
return response()->json(['auth needed'], 401);
|
||||
}
|
||||
$Withdraw = Withdraw::with('customer')->where('id',$id)->first();
|
||||
|
||||
$Withdraw = Withdraw::with('customer')->where('id', $id)->first();
|
||||
$app_url = $this->Config('app_url');
|
||||
$User = Customer::where('id', $Withdraw->customer_id)->first();
|
||||
$GTransaction = Http::withHeaders([
|
||||
'payping' => '@XOKcNveJ$wpqYy%rdpb78oBh31Aos*bIdQemiUeeq9e9G!oey23SMXp40Ew2fodjx9J5buKNW0M4@nYMr1PR6d0KtuqhLQlx*K$', // Custom header with 'payping' value
|
||||
])->post("https://payping.limangold.ir/payment/verify", [
|
||||
"id" => "$Withdraw->tracked_number",
|
||||
])->json();
|
||||
if (isset( $GTransaction['active']) and $GTransaction['active'] == 5) {
|
||||
|
||||
$GTransaction = Http::withHeaders($this->paypingHeaders())
|
||||
->post($this->paypingVerifyUrl(), [
|
||||
'id' => (string) ($Withdraw->tracked_number ?? $Withdraw->zify_code),
|
||||
])
|
||||
->json();
|
||||
|
||||
if (isset($GTransaction['active']) && $GTransaction['active'] == 5) {
|
||||
if ($Withdraw->cash == $GTransaction['amount'] * 10) {
|
||||
$Withdraw->zify_code = $Withdraw->tracked_number;
|
||||
$Withdraw->tracked_number = $GTransaction['invoiceNo'];//ok
|
||||
$Withdraw->sts = 2;//ok
|
||||
$Withdraw->tracked_number = $GTransaction['invoiceNo'];
|
||||
$Withdraw->sts = 2;
|
||||
$Withdraw->save();
|
||||
|
||||
$wallet_interface_fund = new WalletInterface($User->id, $User->type);
|
||||
$transaction = new TransactionInterface($wallet_interface_fund, $_inp = [
|
||||
"cash" => $GTransaction['amount'] * 10,
|
||||
new TransactionInterface($wallet_interface_fund, [
|
||||
'cash' => $GTransaction['amount'] * 10,
|
||||
'expire_at' => now(),
|
||||
'withdraw_id' => $Withdraw->id,
|
||||
'active' => true
|
||||
'active' => true,
|
||||
], 3, false, true);
|
||||
//----------sms
|
||||
$userMobile = $Withdraw->customer->mobile; // User's mobile number
|
||||
$userName = $Withdraw->customer->name; // User's name
|
||||
$chargedAmount = $Withdraw->cash; // Charged amount
|
||||
$companyName = $this->Config('name_fa'); // Company name from configuration
|
||||
$message = "کاربر $userName با شماره موبایل $userMobile حساب خود را به مقدار " . number_format($chargedAmount) . " ریال شارژ کرد. با تشکر، $companyName";
|
||||
User::where('send_sms', 1)->get()->each(function($admin) use ($message) {
|
||||
|
||||
$message = 'کاربر ' . $Withdraw->customer->name . ' با شماره موبایل ' . $Withdraw->customer->mobile
|
||||
. ' حساب خود را به مقدار ' . number_format($Withdraw->cash) . ' ریال شارژ کرد. با تشکر، ' . $this->Config('name_fa');
|
||||
|
||||
User::where('send_sms', 1)->get()->each(function ($admin) use ($message) {
|
||||
$this->Send($admin->mobile, $message);
|
||||
});
|
||||
return view('showTransactionReport', compact('Withdraw','app_url'));
|
||||
return redirect('https://app.gholaktala.ir/transactions');
|
||||
|
||||
return view('showTransactionReport', compact('Withdraw', 'app_url'));
|
||||
}
|
||||
return "مبلغ پرداختی دستکاری شده !";
|
||||
} else {
|
||||
$Withdraw->tracked_number;
|
||||
$Withdraw->sts = 3;//no
|
||||
$Withdraw->save();
|
||||
return view('showTransactionReport', compact('Withdraw','app_url'));
|
||||
|
||||
return 'مبلغ پرداختی دستکاری شده !';
|
||||
}
|
||||
|
||||
if ($this->cfg()->getBool('payping_strict_failed_withdraw', (bool) config('payment.payping_strict_failed_withdraw', false))) {
|
||||
if (!$Withdraw->zify_code) {
|
||||
$Withdraw->priority = 51;
|
||||
}
|
||||
$Withdraw->priority = 111;
|
||||
}
|
||||
$Withdraw->sts = 3;
|
||||
$Withdraw->save();
|
||||
|
||||
return view('showTransactionReport', compact('Withdraw', 'app_url'));
|
||||
}
|
||||
|
||||
public function verifyTransation()
|
||||
{
|
||||
$Withdraw = Withdraw::with('customer')->where('id', $_GET['clientrefid'])->first();
|
||||
$app_url = $this->Config('app_url');
|
||||
$User = Customer::where('id', $Withdraw->customer_id)->first();
|
||||
$url = 'https://zify.ir/api/order/v2/verify';
|
||||
$payload = [
|
||||
"order" => "$Withdraw->tracked_number",
|
||||
];
|
||||
$verifyResponse = Http::withToken("cf09ee0187335f173c786e7660d778d2dd71ab582d282999f751427f5923d525")
|
||||
->post($url, $payload);
|
||||
|
||||
$verifyResponse = Http::withToken('cf09ee0187335f173c786e7660d778d2dd71ab582d282999f751427f5923d525')
|
||||
->post('https://zify.ir/api/order/v2/verify', [
|
||||
'order' => (string) $Withdraw->tracked_number,
|
||||
]);
|
||||
|
||||
$decodedResponse = json_decode($verifyResponse, true);
|
||||
$verifyMessage = $decodedResponse['message'];
|
||||
// return $decodedResponse['data'];
|
||||
$verifyMessage = $decodedResponse['message'] ?? null;
|
||||
|
||||
if (isset($decodedResponse['data']['refid']) || $verifyMessage == []) {
|
||||
if ($Withdraw->cash == $decodedResponse['data']['amount'] * 10) {
|
||||
$Withdraw->zify_code = $Withdraw->tracked_number;//ok
|
||||
$Withdraw->tracked_number = $decodedResponse['data']['refid'];//ok
|
||||
$Withdraw->sts = 2;//ok
|
||||
$Withdraw->zify_code = $Withdraw->tracked_number;
|
||||
$Withdraw->tracked_number = $decodedResponse['data']['refid'];
|
||||
$Withdraw->sts = 2;
|
||||
$Withdraw->save();
|
||||
|
||||
$wallet_interface_fund = new WalletInterface($User->id, $User->type);
|
||||
$transaction = new TransactionInterface($wallet_interface_fund, $_inp = [
|
||||
"cash" => $decodedResponse['data']['amount'] * 10,
|
||||
new TransactionInterface($wallet_interface_fund, [
|
||||
'cash' => $decodedResponse['data']['amount'] * 10,
|
||||
'expire_at' => now(),
|
||||
'withdraw_id' => $_GET['clientrefid'],
|
||||
'active' => true
|
||||
'active' => true,
|
||||
], 3, false, true);
|
||||
|
||||
$User = Customer::where('id', $Withdraw->customer_id)->first();
|
||||
$url = 'https://limangold.ir/kernel/admin/fin';
|
||||
$payload = [
|
||||
"amount" => $decodedResponse['data']['amount'] * 10,
|
||||
"customer_id" => "23",
|
||||
"date_at" => \Carbon\Carbon::today()->format('Y/m/d'),
|
||||
"receive_account_number" => "000",
|
||||
"receive_bank" => "پیپینگ",
|
||||
"receive_name" => "لیمان گلد",
|
||||
"send_account_number" => "000",
|
||||
"send_bank" => " زیفای کد $Withdraw->zify_code",
|
||||
"send_name" => $User->name,
|
||||
"track_number" => $decodedResponse['data']['refid'],
|
||||
"type" => 1,
|
||||
];
|
||||
$verifyResponse = Http::withToken("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvbGltYW5nb2xkLmlyXC9rZXJuZWxcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNzI3NTMwNjA0LCJleHAiOjE3MzAxMjI2MDQsIm5iZiI6MTcyNzUzMDYwNCwianRpIjoiYXBINmlPZXlGTE9oTUhRdyIsInN1YiI6MiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyJ9.IrO6icD-EXjgZ3mYyeEAP4aqG7q35yJTCU_hQhEDGwk")
|
||||
->post($url, $payload);
|
||||
//----------sms
|
||||
// User information
|
||||
$userMobile = $Withdraw->customer->mobile; // User's mobile number
|
||||
$userName = $Withdraw->customer->name; // User's name
|
||||
$chargedAmount = $Withdraw->cash; // Charged amount
|
||||
// Company name
|
||||
$companyName = $this->Config('name_fa'); // Company name from configuration
|
||||
if ($this->cfg()->getBool('payping_fin_sync_enabled', (bool) config('payment.payping_fin_sync_enabled', false))) {
|
||||
$finUrl = $this->cfg()->getString('payping_fin_url', 'https://limangold.ir/kernel/admin/fin');
|
||||
$finToken = $this->cfg()->getString('payping_fin_token', '');
|
||||
if ($finUrl !== '' && $finToken !== '') {
|
||||
Http::withToken($finToken)->post($finUrl, [
|
||||
'amount' => $decodedResponse['data']['amount'] * 10,
|
||||
'customer_id' => $this->cfg()->getString('payping_fin_customer_id', '23'),
|
||||
'date_at' => \Carbon\Carbon::today()->format('Y/m/d'),
|
||||
'receive_account_number' => '000',
|
||||
'receive_bank' => 'پیپینگ',
|
||||
'receive_name' => $this->Config('name_fa'),
|
||||
'send_account_number' => '000',
|
||||
'send_bank' => ' زیفای کد ' . $Withdraw->zify_code,
|
||||
'send_name' => $User->name,
|
||||
'track_number' => $decodedResponse['data']['refid'],
|
||||
'type' => 1,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Constructing the SMS message
|
||||
$message = "کاربر $userName با شماره موبایل $userMobile حساب خود را به مقدار " . number_format($chargedAmount) . " ریال شارژ کرد. با تشکر، $companyName";
|
||||
$message = 'کاربر ' . $Withdraw->customer->name . ' با شماره موبایل ' . $Withdraw->customer->mobile
|
||||
. ' حساب خود را به مقدار ' . number_format($Withdraw->cash) . ' ریال شارژ کرد. با تشکر، ' . $this->Config('name_fa');
|
||||
|
||||
// Getting admin mobile numbers and sending the SMS
|
||||
User::where('send_sms', 1)->get()->each(function($admin) use ($message) {
|
||||
User::where('send_sms', 1)->get()->each(function ($admin) use ($message) {
|
||||
$this->Send($admin->mobile, $message);
|
||||
});
|
||||
|
||||
|
||||
|
||||
return view('showTransactionReport', compact('Withdraw','app_url'));
|
||||
return redirect('https://app.gholaktala.ir/transactions');
|
||||
|
||||
return view('showTransactionReport', compact('Withdraw', 'app_url'));
|
||||
}
|
||||
return "مبلغ پرداختی دستکاری شده !";
|
||||
} else {
|
||||
|
||||
$Withdraw->tracked_number;
|
||||
$Withdraw->sts = 3;//no
|
||||
$Withdraw->save();
|
||||
return view('showTransactionReport', compact('Withdraw','app_url'));
|
||||
return 'مبلغ پرداختی دستکاری شده !';
|
||||
}
|
||||
|
||||
$Withdraw->sts = 3;
|
||||
$Withdraw->save();
|
||||
|
||||
return view('showTransactionReport', compact('Withdraw', 'app_url'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,5 +65,6 @@ class Kernel extends HttpKernel
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'validation'=>ValidationMiddleware::class,
|
||||
'request.validation' => \App\Http\Middleware\ValidateRequests::class,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
|
||||
class ValidateRequests
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
//HMAC
|
||||
$clientId = $request->header('X-Client-Id');
|
||||
$timestamp = $request->header('X-Timestamp');
|
||||
$nonce = $request->header('X-Nonce');
|
||||
$signature = $request->header('X-Signature');
|
||||
|
||||
if (!$clientId || !$timestamp || !$nonce || !$signature) {
|
||||
abort(401, 'Missing HMAC headers');
|
||||
}
|
||||
|
||||
if ($clientId !== config('services.node.client_id')) {
|
||||
abort(401, 'Invalid client id');
|
||||
}
|
||||
|
||||
/** ✅ اینجا SECRET واقعاً استفاده میشود */
|
||||
$secret = config('services.node.secret');
|
||||
if (!$secret) {
|
||||
abort(500, 'Node shared secret not configured');
|
||||
}
|
||||
|
||||
|
||||
|
||||
$data = json_decode($request->getContent(), true);
|
||||
ksort($data);
|
||||
|
||||
$normalized = json_encode(
|
||||
$data,
|
||||
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
|
||||
);
|
||||
|
||||
$bodyHash = hash('sha256', $normalized);
|
||||
|
||||
|
||||
// $bodyHash = hash('sha256', $request->getContent() ?? '');
|
||||
|
||||
$canonical = implode("\n", [
|
||||
strtoupper($request->method()),
|
||||
'/' . ltrim($request->path(), '/'),
|
||||
$bodyHash,
|
||||
$timestamp,
|
||||
$nonce,
|
||||
]);
|
||||
|
||||
$expectedSignature = hash_hmac(
|
||||
'sha256',
|
||||
$canonical,
|
||||
$secret
|
||||
);
|
||||
|
||||
if (!hash_equals($expectedSignature, $signature)) {
|
||||
abort(401, 'Invalid HMAC signature');
|
||||
}
|
||||
|
||||
// return $next($request);
|
||||
|
||||
//ANTI REPLAY
|
||||
$timestamp = (int)$request->header('X-Timestamp');
|
||||
$nonce = $request->header('X-Nonce');
|
||||
|
||||
if (abs(time() - $timestamp) > 99999999999999999) {
|
||||
abort(401, 'Expired request');
|
||||
}
|
||||
|
||||
$key = "node_nonce:{$nonce}";
|
||||
if (Cache::has($key)) {
|
||||
abort(401, 'Replay attack detected');
|
||||
}
|
||||
|
||||
Cache::put($key, true, 60);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -20,30 +20,4 @@ class Cnt extends Model
|
||||
protected $casts = [
|
||||
'value_json'=>'array'
|
||||
];
|
||||
/* public function goldir_login(){
|
||||
$mobile = self::where('key', 'goldir_mobile')->value('value');
|
||||
$password = self::where('key', 'goldir_password')->value('value');
|
||||
$goldir_base_url=Controller::Config('goldir_base_url')??'https://gold.ir';
|
||||
|
||||
$goldir = Http::withOptions([
|
||||
'curl' => [
|
||||
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
|
||||
CURLOPT_TCP_FASTOPEN => 1,
|
||||
]
|
||||
])
|
||||
->withoutVerifying()
|
||||
->post($goldir_base_url.'/server/auth/login', [
|
||||
'mobile' => $mobile,
|
||||
'password' => $password,
|
||||
'provider' => 'customer',
|
||||
'type_app' => 'client',
|
||||
])->json();
|
||||
|
||||
Cnt::where('key', 'goldir')->update([
|
||||
'value' => $goldir['access_token'],
|
||||
'updated_at' => now()
|
||||
]);
|
||||
return $goldir['access_token'];
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
@@ -156,6 +156,12 @@ class Product extends Model
|
||||
public function transaction(){
|
||||
return $this->hasMany(Transaction::class);
|
||||
}
|
||||
|
||||
public function limitedTransactions()
|
||||
{
|
||||
return $this->hasMany(Transaction::class)->select('id', 'gold', 'title', 'created_at', 'product_id');
|
||||
}
|
||||
|
||||
public function transactionAgentPanel(){
|
||||
return $this->hasMany(Transaction::class)->where('note','برداشت بابت فروش')->orwhere('note','واریز بابت خرید')->orwhere('note','سند ورود طلا');
|
||||
}
|
||||
|
||||
@@ -6,9 +6,101 @@ use App\Models\Setting;
|
||||
|
||||
class ConfigService
|
||||
{
|
||||
public const PRICE_PROVIDERS = ['montzar', 'etehadie', 'taban_gohar', 'main_product'];
|
||||
|
||||
public function Config($name)
|
||||
{
|
||||
return Setting::where('key', $name)->value('value') ?? false;
|
||||
return $this->get($name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* مقدار setting؛ اگر نبود از config/price_providers.php یا default پارامتر.
|
||||
*/
|
||||
public function get(string $key, $default = null)
|
||||
{
|
||||
$row = Setting::where('key', $key)->first();
|
||||
|
||||
if ($row !== null && $row->value !== null && $row->value !== '') {
|
||||
return $row->value;
|
||||
}
|
||||
|
||||
$fromFile = config("price_providers.{$key}");
|
||||
|
||||
return $fromFile !== null ? $fromFile : $default;
|
||||
}
|
||||
|
||||
public function getString(string $key, string $default = ''): string
|
||||
{
|
||||
$value = $this->get($key, $default);
|
||||
|
||||
return trim((string) ($value === false ? $default : $value));
|
||||
}
|
||||
|
||||
public function getBool(string $key, bool $default = false): bool
|
||||
{
|
||||
$value = $this->get($key, $default);
|
||||
|
||||
if ($value === false || $value === null || $value === '') {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
public function getInt(string $key, int $default = 0): int
|
||||
{
|
||||
$value = $this->get($key, $default);
|
||||
|
||||
return (int) ($value === false || $value === null || $value === '' ? $default : $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* تنها منبع فعال قیمت — فقط یکی از: montzar | etehadie | taban_gohar | main_product
|
||||
*
|
||||
* اولویت:
|
||||
* 1) price_provider (یا price_primary_source قدیمی)
|
||||
* 2) montzar_price=true → montzar (ابشده خانگی و پروژههای فعلی)
|
||||
* 3) price_method پر و montzar خاموش → etehadie (گلد علی)
|
||||
* 4) پیشفرض فایل: montzar
|
||||
*/
|
||||
public function priceProvider(): string
|
||||
{
|
||||
foreach (['price_provider', 'price_primary_source'] as $key) {
|
||||
$explicit = strtolower($this->getString($key, ''));
|
||||
if (in_array($explicit, self::PRICE_PROVIDERS, true)) {
|
||||
return $explicit;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getBool('montzar_price', false)) {
|
||||
return 'montzar';
|
||||
}
|
||||
|
||||
if ($this->getString('price_method') !== '') {
|
||||
return 'etehadie';
|
||||
}
|
||||
|
||||
$default = strtolower((string) config('price_providers.price_provider', 'montzar'));
|
||||
|
||||
return in_array($default, self::PRICE_PROVIDERS, true) ? $default : 'montzar';
|
||||
}
|
||||
|
||||
/** @deprecated از priceProvider() استفاده کن */
|
||||
public function pricePrimarySource(): string
|
||||
{
|
||||
$provider = $this->priceProvider();
|
||||
|
||||
return in_array($provider, ['montzar', 'etehadie'], true) ? $provider : 'montzar';
|
||||
}
|
||||
|
||||
public function usesMontzarPricing(): bool
|
||||
{
|
||||
return $this->priceProvider() === 'montzar';
|
||||
}
|
||||
|
||||
public function usesEtehadiePricing(): bool
|
||||
{
|
||||
return $this->priceProvider() === 'etehadie';
|
||||
}
|
||||
|
||||
public function UpdateConfig($name, $value)
|
||||
@@ -53,4 +145,4 @@ class ConfigService
|
||||
$this->UpdateConfig('down_price', $priceInt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class EtehadiePriceService
|
||||
{
|
||||
public function __construct(private ConfigService $config)
|
||||
{
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->config->usesEtehadiePricing();
|
||||
}
|
||||
|
||||
public function apiUrl(): string
|
||||
{
|
||||
$url = trim((string) $this->config->getString(
|
||||
'etehadie_api_url',
|
||||
(string) config('price_providers.etehadie_api_url', 'http://109.122.246.48:4949/api/free')
|
||||
));
|
||||
|
||||
return $url !== '' ? $url : 'http://109.122.246.48:4949/api/free';
|
||||
}
|
||||
|
||||
/** کلید محصول در پاسخ API اتحادیه (همان price_method گلد علی) */
|
||||
public function priceKey(): string
|
||||
{
|
||||
$key = trim((string) $this->config->getString(
|
||||
'price_method',
|
||||
(string) config('price_providers.price_method', '')
|
||||
));
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{price: int, raw: array<string, mixed>|null}
|
||||
*/
|
||||
public function fetchMainGramPrice(): array
|
||||
{
|
||||
if (!$this->isEnabled() && $this->priceKey() === '') {
|
||||
return ['price' => 0, 'raw' => null];
|
||||
}
|
||||
|
||||
try {
|
||||
$prices = Http::withoutVerifying()
|
||||
->timeout(20)
|
||||
->get($this->apiUrl())
|
||||
->json();
|
||||
|
||||
if (!is_array($prices)) {
|
||||
return ['price' => 0, 'raw' => null];
|
||||
}
|
||||
|
||||
$key = $this->priceKey();
|
||||
$collection = collect($prices);
|
||||
$row = $key !== ''
|
||||
? $collection->firstWhere('key', $key)
|
||||
: $collection->first();
|
||||
|
||||
if (empty($row)) {
|
||||
return ['price' => 0, 'raw' => null];
|
||||
}
|
||||
|
||||
$base = (float) ($row['price'] ?? 0);
|
||||
$profit = (float) ($row['profit'] ?? 0);
|
||||
$gramPrice = (int) round($base / 10 + $profit);
|
||||
|
||||
return ['price' => $gramPrice, 'raw' => $row];
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Etehadie price fetch failed', ['message' => $e->getMessage()]);
|
||||
|
||||
return ['price' => 0, 'raw' => null];
|
||||
}
|
||||
}
|
||||
|
||||
/** @return array<int, array<string, mixed>>|null */
|
||||
public function fetchPriceList(): ?array
|
||||
{
|
||||
try {
|
||||
$prices = Http::withoutVerifying()
|
||||
->timeout(20)
|
||||
->get($this->apiUrl())
|
||||
->json();
|
||||
|
||||
return is_array($prices) ? $prices : null;
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Etehadie price list fetch failed', ['message' => $e->getMessage()]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function syncProductsFromApi(): bool
|
||||
{
|
||||
$prices = $this->fetchPriceList();
|
||||
if ($prices === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$products = \App\Models\Product::with([])->whereNotNull('reading_method')->get();
|
||||
$collection = collect($prices);
|
||||
$copper = null;
|
||||
$dollar = null;
|
||||
|
||||
foreach ($products as $product) {
|
||||
$RPrice = $collection->firstWhere('key', $product->reading_method);
|
||||
if (empty($RPrice)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($RPrice['name'] ?? '') === 'TGJU فلز مس') {
|
||||
$copper = ($RPrice['price'] ?? 0) + ($RPrice['profit'] ?? 0);
|
||||
}
|
||||
if (($RPrice['name'] ?? '') === 'TGJU دلار') {
|
||||
$dollar = ($RPrice['price'] ?? 0) + ($RPrice['profit'] ?? 0);
|
||||
}
|
||||
if (($RPrice['name'] ?? '') === 'گلد دات ای از آبشده - رسمی - نقد فردا') {
|
||||
$RPrice['price'] = ($RPrice['price'] ?? 0) / 4.3318;
|
||||
}
|
||||
|
||||
ProductPriceService::syncFromReference(
|
||||
$product,
|
||||
(int) $RPrice['price'],
|
||||
(int) ($RPrice['profit'] ?? 0)
|
||||
);
|
||||
}
|
||||
|
||||
if ($copper !== null && $dollar !== null) {
|
||||
$cp = ($copper / 1000000) * $dollar;
|
||||
$copperProduct = \App\Models\Product::find(33);
|
||||
if ($copperProduct) {
|
||||
ProductPriceService::syncFromReference($copperProduct, (int) round($cp), 0);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user