312 lines
11 KiB
PHP
312 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Shayegan;
|
|
use App\Models\ShayeganInboundCharge;
|
|
use App\Services\ConfigService;
|
|
use App\Services\ShayeganClient;
|
|
use App\Services\ShayeganInboundChargeService;
|
|
use App\Services\ShayeganSyncService;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
class ShayeganController extends Controller
|
|
{
|
|
/** @var ShayeganSyncService */
|
|
private $sync;
|
|
|
|
/** @var ShayeganClient */
|
|
private $client;
|
|
|
|
public function __construct(ShayeganSyncService $sync, ShayeganClient $client)
|
|
{
|
|
$this->sync = $sync;
|
|
$this->client = $client;
|
|
}
|
|
|
|
public function healthCheck()
|
|
{
|
|
$result = $this->client->healthCheck();
|
|
$status = ! empty($result['ok']) ? 200 : 422;
|
|
|
|
return response()->json(array_merge([
|
|
'ok' => ! empty($result['ok']),
|
|
'message' => $result['message'] ?? null,
|
|
'http_status' => $result['http_status'] ?? null,
|
|
'timeout' => ! empty($result['timeout']),
|
|
'body' => $this->sanitizeBodyForAdmin($result['body'] ?? null),
|
|
], $this->client->debugExchange($result)), $status);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
public function storeTrade(array $data)
|
|
{
|
|
return $this->sync->recordTrade($data);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
public function wallet(array $data)
|
|
{
|
|
return $this->sync->recordWalletCash($data);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
public function walletGold(array $data)
|
|
{
|
|
return $this->sync->recordWalletGold($data);
|
|
}
|
|
|
|
public function registerCustomer($customerId)
|
|
{
|
|
if (! app(\App\Services\ConfigService::class)->isShayeganAccounting()) {
|
|
return response()->json(['ok' => false, 'message' => 'حسابداری شایگان فعال نیست'], 422);
|
|
}
|
|
|
|
$customer = \App\Models\Customer::findOrFail($customerId);
|
|
$result = $this->sync->registerCustomer($customer);
|
|
|
|
return response()->json($result, ($result['ok'] ?? false) ? 200 : 422);
|
|
}
|
|
|
|
public function table(Request $request)
|
|
{
|
|
if (! Schema::hasTable('shayegan')) {
|
|
return response()->json([
|
|
'rows' => [],
|
|
'query' => [
|
|
'limit' => (int) $request->input('limit', 20),
|
|
'page' => 1,
|
|
'total' => 0,
|
|
'filters' => $request->input('filters', []),
|
|
'search' => $request->input('search'),
|
|
],
|
|
]);
|
|
}
|
|
|
|
$query = Shayegan::with('customer')->orderByDesc('id');
|
|
\App\DB\Te\TableEngine::create($query, $request)
|
|
->initSearch(['desc', 'type', 'shayegan_number', 'sync_status'])
|
|
->select(['*'])
|
|
->export($query);
|
|
|
|
return response()->json($query);
|
|
}
|
|
|
|
public function show($id)
|
|
{
|
|
$row = Shayegan::with('customer')->findOrFail($id);
|
|
|
|
return response()->json($row);
|
|
}
|
|
|
|
/**
|
|
* Manual retry — refuses timeout rows unless force=1 after operator verified remote.
|
|
*/
|
|
public function retrySanad(Request $request, $id)
|
|
{
|
|
$row = Shayegan::findOrFail($id);
|
|
if ($row->sync_status === ShayeganSyncService::STATUS_SUCCESS) {
|
|
return response()->json(['ok' => true, 'message' => 'already synced', 'row' => $row]);
|
|
}
|
|
if ($row->sync_status === ShayeganSyncService::STATUS_TIMEOUT && ! $request->boolean('force')) {
|
|
return response()->json([
|
|
'ok' => false,
|
|
'message' => 'این سند timeout شده؛ بدون بررسی نتیجه در حسابگر دوباره ارسال نکنید. در صورت تایید force=1 بفرستید.',
|
|
], 422);
|
|
}
|
|
|
|
$payload = json_decode((string) $row->data, true);
|
|
$put = is_array($payload) ? ($payload['PutObject'] ?? null) : null;
|
|
$endpoint = (string) ($row->endpoint ?: 'Document/Put');
|
|
if (! is_array($put) || $put === []) {
|
|
return response()->json(['ok' => false, 'message' => 'payload خالی است'], 422);
|
|
}
|
|
|
|
// Clear timeout lock only when force retry requested
|
|
if ($row->sync_status === ShayeganSyncService::STATUS_TIMEOUT) {
|
|
$row->sync_status = ShayeganSyncService::STATUS_PENDING;
|
|
$row->save();
|
|
}
|
|
|
|
$result = $this->client->put($endpoint, $put);
|
|
$exchange = $this->client->debugExchange($result);
|
|
if (! empty($result['timeout'])) {
|
|
$row->sync_status = ShayeganSyncService::STATUS_TIMEOUT;
|
|
$row->sync_log = (string) ($result['message'] ?? 'timeout');
|
|
$row->res = json_encode(['timeout' => true], JSON_UNESCAPED_UNICODE);
|
|
$row->save();
|
|
|
|
return response()->json(array_merge(['ok' => false, 'message' => 'timeout', 'row' => $row], $exchange), 422);
|
|
}
|
|
|
|
if (! empty($result['ok'])) {
|
|
$row->sync_status = ShayeganSyncService::STATUS_SUCCESS;
|
|
$row->sync_log = null;
|
|
$row->shayegan_number = $result['number'] ?? null;
|
|
$row->shayegan_guid = $result['guid'] ?? null;
|
|
$row->res = json_encode($result['body'] ?? [], JSON_UNESCAPED_UNICODE);
|
|
$row->save();
|
|
|
|
return response()->json(array_merge(['ok' => true, 'row' => $row], $exchange));
|
|
}
|
|
|
|
$row->sync_status = ShayeganSyncService::STATUS_FAILED;
|
|
$row->sync_log = (string) ($result['message'] ?? 'failed');
|
|
$row->res = json_encode($result['body'] ?? [], JSON_UNESCAPED_UNICODE);
|
|
$row->save();
|
|
|
|
return response()->json(array_merge(['ok' => false, 'message' => $row->sync_log, 'row' => $row], $exchange), 422);
|
|
}
|
|
|
|
/**
|
|
* وبهوک دریافت شارژ حساب مشتری از پنل حسابداری شایگان
|
|
*/
|
|
public function inboundWebhook(Request $request, ShayeganInboundChargeService $inboundService)
|
|
{
|
|
$secret = app(ConfigService::class)->getString('shayegan_webhook_secret', '');
|
|
$key = $request->header('X-Shayegan-Key') ?: $request->input('secret');
|
|
|
|
if ($secret !== '' && $key !== $secret) {
|
|
return response()->json([
|
|
'ok' => false,
|
|
'message' => 'کلید امنیتی وبهوک شایگان نامعتبر است (X-Shayegan-Key).',
|
|
], 403);
|
|
}
|
|
|
|
$res = $inboundService->registerCharge($request->all(), 'webhook');
|
|
|
|
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
|
|
}
|
|
|
|
/**
|
|
* دریافت لیست شارژهای در انتظار تایید برای نمایش اعلان در پنل ادمین
|
|
*/
|
|
public function pendingCharges()
|
|
{
|
|
if (! Schema::hasTable('shayegan_inbound_charges')) {
|
|
return response()->json(['ok' => true, 'charges' => []]);
|
|
}
|
|
|
|
$charges = ShayeganInboundCharge::with('customer')
|
|
->where('status', 'pending')
|
|
->orderByDesc('id')
|
|
->limit(20)
|
|
->get();
|
|
|
|
return response()->json([
|
|
'ok' => true,
|
|
'charges' => $charges,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* جدول شارژهای دریافتی از شایگان
|
|
*/
|
|
public function chargesTable(Request $request)
|
|
{
|
|
if (! Schema::hasTable('shayegan_inbound_charges')) {
|
|
return response()->json([
|
|
'rows' => [],
|
|
'query' => [
|
|
'limit' => (int) $request->input('limit', 20),
|
|
'page' => 1,
|
|
'total' => 0,
|
|
'filters' => $request->input('filters', []),
|
|
'search' => $request->input('search'),
|
|
],
|
|
]);
|
|
}
|
|
|
|
$query = ShayeganInboundCharge::with(['customer', 'approver'])->orderByDesc('id');
|
|
|
|
if ($request->filled('status')) {
|
|
$query->where('status', $request->input('status'));
|
|
}
|
|
|
|
if ($request->filled('search')) {
|
|
$s = trim((string) $request->input('search'));
|
|
$query->where(function ($q) use ($s) {
|
|
$q->where('shayegan_number', 'like', "%{$s}%")
|
|
->orWhere('customer_tafsil', 'like', "%{$s}%")
|
|
->orWhereHas('customer', function ($cq) use ($s) {
|
|
$cq->where('name', 'like', "%{$s}%")
|
|
->orWhere('mobile', 'like', "%{$s}%")
|
|
->orWhere('n_code', 'like', "%{$s}%");
|
|
});
|
|
});
|
|
}
|
|
|
|
$limit = max(1, (int) $request->input('limit', 20));
|
|
$page = max(1, (int) $request->input('page', 1));
|
|
$total = $query->count();
|
|
$rows = $query->skip(($page - 1) * $limit)->take($limit)->get();
|
|
|
|
return response()->json([
|
|
'rows' => $rows,
|
|
'query' => [
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'total' => $total,
|
|
],
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* تایید شارژ و افزایش کیف پول مشتری در بورس مس
|
|
*/
|
|
public function approveCharge(Request $request, $id, ShayeganInboundChargeService $inboundService)
|
|
{
|
|
$charge = ShayeganInboundCharge::with('customer')->findOrFail($id);
|
|
$res = $inboundService->approveCharge($charge, auth()->user());
|
|
|
|
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
|
|
}
|
|
|
|
/**
|
|
* رد درخواست شارژ
|
|
*/
|
|
public function rejectCharge(Request $request, $id, ShayeganInboundChargeService $inboundService)
|
|
{
|
|
$charge = ShayeganInboundCharge::findOrFail($id);
|
|
$reason = $request->input('reason');
|
|
$res = $inboundService->rejectCharge($charge, auth()->user(), $reason);
|
|
|
|
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
|
|
}
|
|
|
|
/**
|
|
* استعلام اسناد جدید از شایگان
|
|
*/
|
|
public function syncChargesFromShayegan(ShayeganInboundChargeService $inboundService)
|
|
{
|
|
$res = $inboundService->pollFromShayegan();
|
|
|
|
return response()->json($res, ($res['ok'] ?? false) ? 200 : 422);
|
|
}
|
|
|
|
/**
|
|
* @param mixed $body
|
|
* @return mixed
|
|
*/
|
|
private function sanitizeBodyForAdmin($body)
|
|
{
|
|
if (! is_array($body)) {
|
|
return $body;
|
|
}
|
|
// Drop huge Result lists from health if present
|
|
if (isset($body['Result']) && is_array($body['Result'])) {
|
|
$body['Result'] = array_slice($body['Result'], 0, 3);
|
|
}
|
|
|
|
return $body;
|
|
}
|
|
}
|