feat(accounting): add Shayegan as fixed boursecu provider
Wire outbound Invoice/Document sync after confirmed ops, store sync status with timeout lock, and keep SQL credentials out of frontend responses. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Shayegan;
|
||||
use App\Services\ShayeganClient;
|
||||
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([
|
||||
'ok' => ! empty($result['ok']),
|
||||
'message' => $result['message'] ?? null,
|
||||
'http_status' => $result['http_status'] ?? null,
|
||||
'timeout' => ! empty($result['timeout']),
|
||||
// never echo Config passwords
|
||||
'body' => $this->sanitizeBodyForAdmin($result['body'] ?? null),
|
||||
], $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 table(Request $request)
|
||||
{
|
||||
if (! Schema::hasTable('shayegan')) {
|
||||
return response()->json(['data' => [], 'total' => 0]);
|
||||
}
|
||||
|
||||
$q = Shayegan::query()->orderByDesc('id');
|
||||
if ($request->filled('sync_status')) {
|
||||
$q->where('sync_status', $request->get('sync_status'));
|
||||
}
|
||||
if ($request->filled('type')) {
|
||||
$q->where('type', $request->get('type'));
|
||||
}
|
||||
|
||||
$perPage = min(100, max(10, (int) $request->get('per_page', 20)));
|
||||
$page = $q->paginate($perPage);
|
||||
|
||||
return response()->json([
|
||||
'data' => $page->items(),
|
||||
'total' => $page->total(),
|
||||
'current_page' => $page->currentPage(),
|
||||
'per_page' => $page->perPage(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$row = Shayegan::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);
|
||||
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(['ok' => false, 'message' => 'timeout', 'row' => $row], 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(['ok' => true, 'row' => $row]);
|
||||
}
|
||||
|
||||
$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(['ok' => false, 'message' => $row->sync_log, 'row' => $row], 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user