json(['id'=>$id] , 201); } /** * add paginate to list * @param array $list * @param object $table * @return array mixed */ protected function addPaginate($table,$list){ $list = $this->addLimitPage($list); $list['count'] = $table->count(); $list['rows'] = $table->offset( $list['page']*$list['limit']) ->limit($list['limit']) ->get(); $list['page'] = (int)$list['page']; $list['limit'] = (int)$list['limit']; return $list; } /** * add page and limit to list * @param $list * @return mixed */ protected function addLimitPage($list){ if (!isset($list['page'])){ $list['page'] = 0; } if (!isset($list['limit'])){ $list['limit'] = 5; } if ($list['limit']>50){ $list['limit'] = 50; } return $list; } /** مبلغ ریالی در پیامک — جداکننده هزارگان */ public static function formatSmsRialAmount(float|int|string $value): string { return number_format(abs((float) $value), 0, '.', ','); } /** وزن طلا در پیامک — تا ۳ رقم اعشار، بدون گرد کردن به صفر برای زیر ۱ گرم */ public static function formatSmsGoldWeight(float|int|string $value): string { $formatted = rtrim(rtrim(number_format(abs((float) $value), 3, '.', ''), '0'), '.'); return $formatted !== '' ? $formatted : '0'; } /** * گزینه‌های Guzzle/cURL — فقط وقتی افزونه curl و ثابت‌ها موجود باشند. * * @param array $curlOptions * @return array> */ public static function httpClientOptions(array $curlOptions = []): array { $curl = $curlOptions; if (defined('CURLOPT_IPRESOLVE') && defined('CURL_IPRESOLVE_V4')) { $resolveOpt = (int) constant('CURLOPT_IPRESOLVE'); if (! array_key_exists($resolveOpt, $curl)) { $curl[$resolveOpt] = (int) constant('CURL_IPRESOLVE_V4'); } } return $curl === [] ? [] : ['curl' => $curl]; } public static function Send($to , $msg, $is_customer = true) { $r = self::SendMessage($to , $msg, $is_customer); // if ($r == 200){ // Sms::set($to,$msg , $is_customer); // } } public static function SendMessage($to, $msg ,$is_send = false,$force_method ='null') { $config = app(ConfigService::class); // $config = config('message', false); $credentials = [ "telegram" => [ "token" => self::Config('telegram_bot_token') ], "bale" => [ "token" => self::Config('bale_bot_token') // "token" => Setting::where('key', 'bale_bot_token')->where('is_active', true)->first()->value ?? null ], "sms" => [ 'user_name' => 'ysms1362', 'password' => 'tL4w0PfNy2()', 'sender' => '3000164545', ], ]; $platforms = []; $send_sms = true; $user = User::where('mobile', $to)->first() ?: Customer::where('mobile', $to)->first(); if ($user) { $baleToken = trim((string) ($credentials['bale']['token'] ?? '')); $baleChatId = trim((string) ($user->bale ?? '')); if ($baleToken !== '' && $baleChatId !== '') { try { $baleResponse = Http::withoutVerifying()->get( "https://tapi.bale.ai/bot{$baleToken}/sendMessage", [ 'chat_id' => $baleChatId, 'text' => "جناب {$user->name} \n{$msg}", ] ); $balePayload = $baleResponse->json(); if ($baleResponse->successful() && ($balePayload['ok'] ?? false) === true) { $platforms[] = "bale:{$baleChatId}"; $send_sms = false; } } catch (\Throwable $e) { report($e); } } } //SMS if ($send_sms or $force_method=="sms") { /* $endpoint = "https://www.0098sms.com/sendsmslink.aspx"; $response = Http::withoutVerifying()->get($endpoint, [ 'FROM' => $credentials['sms']['sender'], 'TO' => $to, 'TEXT' => trim($msg), 'USERNAME' => $credentials['sms']['user_name'], 'PASSWORD' => $credentials['sms']['password'], 'DOMAIN' => '0098', ]); $platforms[] = "0098:{$to}";*/ try { $http = Http::timeout(15) ->withHeaders([ 'Content-Type' => 'application/json', 'secret_key' => 'f21debfb-83ac-4624-a2f4-f08375043181', ]); $curlOptions = self::httpClientOptions(); if ($curlOptions !== []) { $http = $http->withOptions($curlOptions); } $response = $http->post('http://185.231.112.101/api/createsms', [ 'phone' => $to, 'host' => $config->Config('name_fa') ?? gethostname(), 'text' => trim($msg), 'platform' => 'rayegansms', ]); $platforms[] = $response->successful() ? "rayegansms:{$to}" : "rayegansms_failed:{$to}:{$response->status()}"; } catch (\Throwable $e) { report($e); $platforms[] = "rayegansms_error:{$to}"; } } Message::create([ 'sms' => $msg, 'receiver' => $to, 'platforms' => implode(', ', $platforms), // ذخیره لیست سامانه‌ها ]); } public static function SendTelegram($to , $msg) { // "chatId" => 504951108, // "chatId" => 5870340227, fathi $response = Http::withHeaders([ 'x-secret-key' => 'PvQM36mgk2FUELHjcm4YG49Nu56uQxot', ])->post('https://tele.ygram.ir/send-message', ["message" => $msg, "chatId" => $to, "botToken" => 'Paramisgold_bot']); return $response->json(); } public static function Config($name) { $row = Setting::where('key', $name)->first(); if ($row && $row->is_active && $row->value !== null && $row->value !== '') { return $row->value; } return false; } protected static function metalTypeSettingValue(): ?string { $metalType = Setting::where('key', 'metal_type')->value('value'); if ($metalType === null) { return null; } $metalType = trim((string) $metalType); return $metalType !== '' ? $metalType : null; } /** برچسب موجودی کیف اصلی: مقدار setting یا «طلا» */ public static function metalTypeLabel(): string { return self::metalTypeSettingValue() ?? 'طلا'; } /** برچسب معاملات آبشده: مقدار setting یا «آبشده» */ public static function meltedGoldLabel(): string { return self::metalTypeSettingValue() ?? 'آبشده'; } public static function cashTransactionTitles(): array { return ['برداشت وجه نقد', 'واریز وجه نقد']; } public static function legacyMeltedTransactionTitles(): array { return [ 'واریز طلای ابشده', 'واریز طلای آبشده', 'برداشت طلای ابشده', 'برداشت طلای آبشده', ]; } protected static function metalLabelVariants(): array { return array_values(array_unique([ self::meltedGoldLabel(), self::metalTypeLabel(), 'طلا', 'آبشده', ])); } public static function genericMetalTransactionTitles(): array { $titles = self::legacyMeltedTransactionTitles(); foreach (self::metalLabelVariants() as $label) { $titles[] = "موجودی {$label}"; $titles[] = "برداشت {$label}"; $titles[] = "خرید {$label}"; $titles[] = "فروش {$label}"; } return array_values(array_unique($titles)); } public static function isCashTransactionTitle(?string $title): bool { return $title !== null && in_array($title, self::cashTransactionTitles(), true); } public static function isGenericMetalTransactionTitle(?string $title): bool { if ($title === null || self::isCashTransactionTitle($title)) { return false; } return in_array($title, self::genericMetalTransactionTitles(), true); } public static function productBuyTitle(string $productTitle): string { return 'خرید ' . $productTitle; } public static function productSellTitle(string $productTitle): string { return 'فروش ' . $productTitle; } public static function mainMetalBuyTitle(): string { return self::productBuyTitle(self::metalTypeLabel()); } public static function mainMetalSellTitle(): string { return self::productSellTitle(self::metalTypeLabel()); } /** * آیا محصول به کیف اصلی متصل است؟ (setting: main_wallet_products) * موجودی این محصولات = gold کیف اصلی؛ تراکنش‌هایشان روی کیف اصلی می‌نشیند. */ public static function isMainWalletProduct(?int $productId): bool { if (!$productId) { return false; } return app(ConfigService::class)->isMainWalletProduct((int) $productId); } /** * شناسه کیف مؤثر برای تراکنش محصول. * محصولات متصل به کیف اصلی → null (کیف اصلی)؛ بقیه → کیف محصول خودشان. */ public static function effectiveProductWalletId(?int $productId): ?int { return self::isMainWalletProduct($productId) ? null : $productId; } /** واحد وزن معاملات فلز — از ستینگ unit؛ پیش‌فرض گرم */ public static function tradeWeightUnit(): string { return self::resolveTradeWeightUnit( Setting::where('key', 'unit')->value('value') ); } public static function resolveTradeWeightUnit(?string $value): string { $unit = trim((string) $value); if ($unit === 'کیلوگرم' || $unit === 'kilogram' || $unit === 'kg') { return 'کیلوگرم'; } return 'گرم'; } public static function weightToGrams(float $value, ?string $fromUnit = null): float { $from = $fromUnit ?? self::tradeWeightUnit(); return $from === 'کیلوگرم' ? $value * 1000 : $value; } public static function gramsToTradeUnit(float $grams): float { return self::tradeWeightUnit() === 'کیلوگرم' ? $grams / 1000 : $grams; } public static function productStorageWeightUnit($product): string { $unit = trim((string) (is_object($product) ? ($product->unit ?? '') : ($product['unit'] ?? ''))); if (self::isWeightBasedProductUnit($unit)) { return $unit; } return 'گرم'; } public static function productLineWeightToGrams($product, $quantity): float { $groupType = is_object($product) ? ($product->group_type ?? '') : ($product['group_type'] ?? ''); if ((string) $groupType === 'سکه') { return 0.0; } $qty = (float) $quantity; $unit = trim((string) (is_object($product) ? ($product->unit ?? '') : ($product['unit'] ?? ''))); $amount = (float) (is_object($product) ? ($product->amount ?? 0) : ($product['amount'] ?? 0)); if (self::isWeightBasedProductUnit($unit)) { return self::weightToGrams($qty, $unit); } return self::weightToGrams($amount * $qty, self::productStorageWeightUnit($product)); } public static function isWeightBasedProductUnit(?string $unit): bool { $unit = trim((string) $unit); return in_array($unit, ['کیلوگرم', 'گرم'], true); } public static function meltedGoldBuyTitle(): string { return 'خرید ' . self::meltedGoldLabel(); } public static function meltedGoldSellTitle(): string { return 'فروش ' . self::meltedGoldLabel(); } public static function meltedGoldDepositTitle(): string { return 'موجودی ' . self::meltedGoldLabel(); } public static function meltedGoldWithdrawTitle(): string { return 'برداشت ' . self::meltedGoldLabel(); } public static function isCountBasedProduct(?array $product): bool { if (!$product) { return false; } if (self::usesQuantityTrade($product)) { return false; } if (self::isWeightBasedProductUnit($product['unit'] ?? null)) { return false; } if (isset($product['direct_price']) && (int) $product['direct_price'] !== 0) { return true; } $group = trim((string) ($product['group_type'] ?? '')); if (in_array($group, ['مفتول مس'], true)) { return true; } return in_array($group, ['سکه', 'گوی', 'مکعب', 'شمش', 'سکه پارسیان'], true); } public static function productUnitPayload($product): array { if (!$product) { return []; } if (is_array($product)) { return $product; } return [ 'unit' => $product->unit ?? null, 'direct_price' => $product->direct_price ?? null, 'group_type' => $product->group_type ?? null, 'amount' => $product->amount ?? null, 'trade_by_quantity' => $product->trade_by_quantity ?? null, ]; } public static function convertWeight(float $value, ?string $fromUnit, ?string $toUnit): float { $from = trim((string) ($fromUnit ?? '')); $to = trim((string) ($toUnit ?? '')); if ($from === '' || $to === '' || $from === $to) { return $value; } $grams = self::weightToGrams($value, $from); return $to === 'کیلوگرم' ? $grams / 1000 : $grams; } /** * مقدار و واحد نمایشی تراکنش — منبع حقیقت: unit محصول در ادمین. * معامله مقداری + وزن دارد: تعداد ذخیره‌شده × وزن هر واحد. */ public static function resolveTransactionDisplayGoldAndUnit(?array $payload, float $gold, bool $onMainWallet = false): array { if ($payload === []) { return [$gold, self::tradeWeightUnit()]; } $sign = $gold < 0 ? -1 : 1; $absGold = abs($gold); if ( self::usesQuantityTrade($payload) && self::hasProductUnitWeight($payload) && self::isWeightBasedProductUnit($payload['unit'] ?? null) ) { $factor = (float) ($payload['amount'] ?? 1); return [ round($absGold * $factor, 3) * $sign, trim((string) $payload['unit']), ]; } $unit = self::productQuantityUnit($payload); $productWeightUnit = self::isWeightBasedProductUnit($payload['unit'] ?? null) ? trim((string) $payload['unit']) : null; if ($onMainWallet && $productWeightUnit) { $tradeUnit = self::tradeWeightUnit(); if ($productWeightUnit !== $tradeUnit) { return [ round(self::convertWeight($absGold, $tradeUnit, $productWeightUnit), 3) * $sign, $productWeightUnit, ]; } return [$gold, $productWeightUnit]; } return [$gold, $unit]; } public static function productQuantityUnit(?array $product): string { $unit = trim((string) ($product['unit'] ?? '')); if ($unit !== '') { return $unit; } if (self::isCountBasedProduct($product)) { return 'عدد'; } return self::tradeWeightUnit(); } /** مطابق ادمین: وزن «دارد» => amount > 0 ؛ «ندارد» => amount = 0 */ public static function isMeltedTradeGroup(?string $groupType): bool { $group = trim((string) $groupType); return in_array($group, ['آبشده', 'کاتد مس'], true); } public static function hasProductUnitWeight(?array $product): bool { if (!$product || !isset($product['amount'])) { return false; } if (self::isMeltedTradeGroup($product['group_type'] ?? null)) { return false; } return (float) $product['amount'] > 0; } /** معامله مقداری: فقط فیلد trade_by_quantity */ public static function usesQuantityTrade($product): bool { $value = is_array($product) ? ($product['trade_by_quantity'] ?? null) : (is_object($product) ? ($product->trade_by_quantity ?? null) : null); return !empty($value); } /** ضریب قیمت محصول — بدون وزن قطعه همیشه ۱ */ public static function productPriceMultiplier($product): float { $payload = is_array($product) ? $product : [ 'amount' => is_object($product) ? ($product->amount ?? null) : null, 'unit' => is_object($product) ? ($product->unit ?? null) : null, 'direct_price' => is_object($product) ? ($product->direct_price ?? null) : null, 'group_type' => is_object($product) ? ($product->group_type ?? null) : null, ]; if (!self::hasProductUnitWeight($payload)) { return 1.0; } $amount = (float) ($payload['amount'] ?? 0); 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(); if(empty($counter)){ $counter = new Cnt(); $counter->key = "jibit_counter"; $counter->value = 0; $counter->save(); } $apiData['refresh'] = Http::withoutVerifying()->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']); if (!Storage::exists('accessTokenGibit') || !Storage::exists('refreshTokenGibit')) { Storage::put('accessTokenGibit', 'توکن اولیه'); Storage::put('refreshTokenGibit', 'توکن رفرش اولیه'); } $apiData['cartMatching'] = Http::withoutVerifying()->withToken(Storage::get('accessTokenGibit'))->get("https://napi.jibit.ir/ide/v1/services/matching?cardNumber=$p1&nationalCode=$p2&birthDate=$p3")->json(); if ((isset($apiData['cartMatching']['code'])) and $apiData['cartMatching']['code'] == "forbidden") { $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']); Storage::put('refreshTokenGibit', $apiData['refresh']['refreshToken']); $apiData['cartMatching'] = Http::withoutVerifying()->withToken(Storage::get('accessTokenGibit'))->get("https://napi.jibit.ir/ide/v1/services/matching?cardNumber=$p1&nationalCode=$p2&birthDate=$p3")->json(); } //update jibit counter $counter->value = (int)$counter->value + 1; $counter->save(); if (isset( $apiData['cartMatching']['matched']) and $apiData['cartMatching']['matched'] == true) { $apiData['cartData'] = Http::withoutVerifying()->withToken(Storage::get('accessTokenGibit'))->get("https://napi.jibit.ir/ide/v1/cards?number=$p1&iban=true")->json(); return $apiData; }elseif(isset( $apiData['cartMatching']['matched']) and $apiData['cartMatching']['matched'] == false){ return $apiData; } else { throw new UnprocessableEntityHttpException($apiData['cartMatching']['message']); } } }