isPargarAccounting()) { return response()->json([ 'success' => false, 'data' => null, 'message' => 'Pargar integration is disabled', ], 503); } [$username, $password] = $this->pargarLoginCredentials($request); $expectedUser = $this->normalizeCredential(trim((string) $this->Config('pargar_username'))); $expectedPass = $this->normalizeCredential(trim((string) $this->Config('pargar_password'))); if ($username !== $expectedUser || $password !== $expectedPass || $expectedUser === '') { return response()->json([ 'success' => false, 'data' => null, 'message' => 'Invalid credentials!', ]); } $token = PargarAuth::issueToken($username); return response()->json([ 'success' => true, 'data' => ['token' => $token], 'message' => 'You are logged in successfully.', ]); } public function upsertCustomers(Request $request) { $payload = $this->pargarPayload($request); $customers = $payload['Customers'] ?? []; if (! is_array($customers) || $customers === []) { return response()->json([ 'success' => false, 'message' => 'Customers list is required', 'data' => null, ], 422); } $messages = []; foreach ($customers as $row) { if (! is_array($row)) { continue; } $accountId = trim((string) ($row['AccountId'] ?? '')); $fullName = trim((string) ($row['FullName'] ?? '')); $mobile = Customer::normalizeMobile((string) ($row['CellPhone'] ?? '')); if ($accountId === '' || $mobile === null) { $messages[] = 'ردیف نامعتبر: AccountId یا CellPhone خالی است'; continue; } $byAccount = Customer::whereNull('deleted_at') ->where('pargar_account_id', $accountId) ->first(); if ($byAccount && Customer::normalizeMobile($byAccount->mobile) !== $mobile) { $messages[] = "کد حساب {$accountId} قبلاً برای مشتری دیگری ثبت شده است"; continue; } $customer = Customer::findActiveByMobile($mobile); if ($customer) { $currentAccount = (string) ($customer->pargar_account_id ?? ''); if ($currentAccount === $accountId) { $messages[] = 'کد حساب مشتری وجود دارد'; continue; } if ($currentAccount !== '' && $currentAccount !== $accountId) { $messages[] = "شماره {$mobile} قبلاً به حساب پرگار دیگری متصل است"; continue; } $customer->pargar_account_id = $accountId; $this->applyPargarCustomerName($customer, $fullName); $customer->save(); $messages[] = "کد حسابداری مشتری {$fullName} ویرایش شد"; continue; } if ($byAccount) { if (Customer::isMobileTaken($mobile, (int) $byAccount->id)) { $messages[] = "شماره {$mobile} تکراری است"; continue; } if ($fullName !== '') { $this->applyPargarCustomerName($byAccount, $fullName); } $byAccount->mobile = $mobile; if (empty($byAccount->password)) { $byAccount->password = $mobile; } $byAccount->save(); $messages[] = "مشتری با کد حسابداری {$accountId} به‌روزرسانی شد"; continue; } try { $customer = new Customer(); $this->applyPargarCustomerName($customer, $fullName !== '' ? $fullName : 'مشتری'); $customer->mobile = $mobile; $customer->pargar_account_id = $accountId; $customer->active = true; $customer->type = 2; $customer->password = $mobile; $customer->referral_id = $this->uniqueReferralId(); $customer->save(); $messages[] = "مشتری با کد حسابداری {$accountId} ایجاد شد"; } catch (\Throwable $e) { $messages[] = "خطا برای {$mobile}: " . ( $e instanceof UnprocessableEntityHttpException ? $e->getMessage() : 'ثبت مشتری ناموفق بود' ); } } return response()->json([ 'success' => true, 'message' => implode(' | ', $messages), 'data' => null, ]); } public function listOrders(Request $request) { $orders = Order::query() ->where('sts', 2) ->whereNull('pargar_confirmed_at') ->limit(100) ->get(); $documents = $orders->map(function (Order $order) { $customer = Customer::find((int) ($order->customer_id ?? 0)); // direction اپ: 1=خرید مشتری، 2=فروش مشتری // OrderType پرگار از دید فروشگاه است: خرید مشتری = فروش فروشگاه (-1)، فروش مشتری = خرید فروشگاه (1) $isCustomerBuy = (int) ($order->direction ?? 1) === 1; $customerDirection = (int) ($order->direction ?? 0); return [ 'OrderId' => (string) $order->id, 'OrderType' => $isCustomerBuy ? -1 : 1, 'PriceType' => 1, 'CustomerAccountId' => (int) ($customer->pargar_account_id ?? 0), 'ItemId' => 11, 'ItemName' => $this->pargarItemName($order), 'OrderDate' => $order->pay_at ? Jalalian::fromDateTime($order->pay_at)->format('Y/m/d-H:i:s') : Jalalian::fromDateTime($order->created_at)->format('Y/m/d-H:i:s'), 'Fee' => (float) ($order->price ?? 0), 'Count' => 1, 'Weight' => (float) ($order->amount ?? 0), 'Price' => (float) ($order->total_price ?? 0), 'Carat' => 750, 'Description' => self::accountingShopTradeDesc($customerDirection, $this->isCartOrder($order)), ]; })->values(); return response()->json([ 'state' => 'true', 'msg' => '', 'data' => [ 'documents' => $documents, 'duplicated' => 'false', 'result' => 'OK', ], ]); } public function confirmOrders(Request $request) { $payload = $this->pargarPayload($request); $orderIds = $payload['OrderIds'] ?? []; if (! is_array($orderIds)) { return response()->json([ 'success' => false, 'message' => 'OrderIds must be an array', 'data' => null, ], 422); } $this->sync()->confirmOrders($orderIds); return response()->json([ 'success' => true, 'message' => 'تاییدیه اسناد با موفقیت دریافت شد', 'data' => null, ]); } public function storeDocuments(Request $request) { $data = $this->pargarPayload($request); $accountId = trim((string) ($data['accountId'] ?? '')); $transactions = $data['transactions'] ?? []; $customer = $accountId !== '' ? Customer::where('pargar_account_id', $accountId)->first() : null; PargarSyncLog::create([ 'customer_id' => $customer?->id, 'desc' => 'دریافت اسناد از پرگار', 'type' => 'document', 'sync_status' => PargarSyncService::STATUS_SUCCESS, 'pargar_ref' => $accountId, 'data' => json_encode($data, JSON_UNESCAPED_UNICODE), ]); return response()->json([ 'success' => true, 'message' => 'اسناد مشتری با موفقیت دریافت شد', 'data' => null, ]); } public function storeMande(Request $request) { $data = $this->pargarPayload($request); $accountId = trim((string) ($data['AccountId'] ?? '')); $customer = Customer::where('pargar_account_id', $accountId)->first(); if (! $customer) { return response()->json([ 'success' => false, 'message' => 'مشتری یافت نشد', 'data' => null, ], 422); } $goldInv = isset($data['GoldInv']) ? (float) str_replace('/', '.', (string) $data['GoldInv']) : 0; $rialInv = isset($data['RialInv']) ? (float) str_replace('/', '.', (string) $data['RialInv']) : 0; // خواندن و نوشتن روی همان کیف اصلی مشتری — نه first() تصادفی $ownerType = (int) ($customer->type ?? 2); $walletInterface = new WalletInterface($customer->id, $ownerType); $wallet = $walletInterface->wallet; $goldDiff = $goldInv - (float) ($wallet->gold ?? 0); $rialDiff = $rialInv - (float) ($wallet->cash ?? 0); if (abs($goldDiff) >= 0.01 || abs($rialDiff) >= 1) { new TransactionInterface($walletInterface, [ 'cash' => $rialDiff, 'gold' => $goldDiff, 'title' => 'تعدیل حسابداری پرگار', 'expire_at' => now(), 'active' => true, 'start_at' => now(), ], 22, $goldDiff > 0, $rialDiff > 0); } PargarSyncLog::create([ 'customer_id' => $customer->id, 'desc' => 'دریافت مانده از پرگار', 'type' => 'mande', 'amount' => $goldInv, 'sync_status' => PargarSyncService::STATUS_SUCCESS, 'pargar_ref' => $accountId, 'data' => json_encode($data, JSON_UNESCAPED_UNICODE), ]); return response()->json([ 'success' => true, 'message' => 'مانده مشتری با موفقیت دریافت شد', 'data' => null, ]); } public function listNewCustomers(Request $request) { $customers = Customer::query() ->whereNull('deleted_at') ->where(function ($q) { $q->whereNull('pargar_account_id') ->orWhere('pargar_account_id', '') ->orWhere('pargar_account_id', '0') ->orWhere('pargar_account_id', '-1'); }) ->limit(100) ->get(['id', 'fname', 'lname', 'name', 'mobile']); if ($customers->isEmpty()) { return response()->json([ 'success' => true, 'message' => 'مشتری جدید وجود ندارد', 'data' => null, ]); } $rows = $customers->map(function (Customer $customer) { $fname = trim((string) ($customer->fname ?? '')); $lname = trim((string) ($customer->lname ?? '')); if ($fname === '' && $lname === '') { $nameParts = preg_split('/\s+/', trim((string) ($customer->name ?? '')), 2) ?: ['', '']; $fname = trim((string) ($nameParts[0] ?? '')); $lname = trim((string) ($nameParts[1] ?? '')); } return [ 'id' => (string) $customer->id, 'name' => $fname, 'family' => $lname, 'mobile' => (string) $customer->mobile, ]; }); return response()->json([ 'state' => 'true', 'msg' => '', 'customer' => $rows, ]); } public function confirmCustomers(Request $request) { $payload = $this->pargarPayload($request); $rows = $payload['customers'] ?? []; if (! is_array($rows)) { return response()->json([ 'success' => false, 'message' => 'customers must be an array', 'data' => null, ], 422); } foreach ($rows as $row) { if (! is_array($row)) { continue; } $customerId = $row['customer_id'] ?? $row['id_customer'] ?? null; $accountId = $row['accounting_id'] ?? null; if ($customerId === null || $accountId === null) { continue; } $normalizedAccountId = trim((string) $accountId); if ($normalizedAccountId === '' || in_array($normalizedAccountId, ['0', '-1'], true)) { continue; } $customer = Customer::find($customerId); if ($customer) { $customer->pargar_account_id = $normalizedAccountId; $customer->save(); $log = PargarSyncLog::query() ->where('customer_id', $customer->id) ->where('type', 'customer') ->where('sync_status', PargarSyncService::STATUS_PENDING) ->latest('id') ->first(); if ($log) { $this->sync()->markSuccess($log, $normalizedAccountId); } } } return response()->json([ 'success' => true, 'message' => 'تاییدیه ثبت مشتری با موفقیت دریافت شد', 'data' => null, ]); } /** * @return array{0: string, 1: string} */ private function pargarLoginCredentials(Request $request): array { $payload = $this->pargarPayload($request); return [ $this->normalizeCredential((string) ($payload['username'] ?? '')), $this->normalizeCredential((string) ($payload['password'] ?? '')), ]; } /** * @return array */ private function pargarPayload(Request $request): array { $payload = $request->all(); if ($payload !== []) { return $payload; } $decoded = json_decode((string) $request->getContent(), true); return is_array($decoded) ? $decoded : []; } private function normalizeCredential(string $value): string { $persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; $arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩']; $latin = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; return trim(str_replace(array_merge($persian, $arabic), array_merge($latin, $latin), $value)); } private function applyPargarCustomerName(Customer $customer, string $fullName): void { $fullName = trim($fullName); if ($fullName === '') { return; } [$fname, $lname] = $this->splitPargarFullName($fullName); $customer->fname = $fname; $customer->lname = $lname; // mutator مدل Customer مقدار name را از fname + lname می‌سازد $customer->name = trim("$fname $lname"); } /** * @return array{0: string, 1: string} */ private function splitPargarFullName(string $fullName): array { $parts = preg_split('/\s+/u', trim($fullName), 2) ?: ['', '']; return [ trim((string) ($parts[0] ?? '')), trim((string) ($parts[1] ?? '')), ]; } private function uniqueReferralId(): string { do { $referral = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz'), 0, 4); } while (Customer::where('referral_id', $referral)->exists()); return $referral; } private function pargarItemName(Order $order): string { return $this->isCartOrder($order) ? 'سبد خرید' : 'آبشده'; } private function isCartOrder(Order $order): bool { if (Schema::hasTable('cart') && Schema::hasColumn('cart', 'order_id')) { return Cart::where('order_id', $order->id)->exists(); } return PargarSyncLog::where('order_id', $order->id) ->whereIn('desc', ['سبد خرید', 'فروش سبد به مشتری']) ->exists(); } }