2026-06-25 22:20:40 +03:30
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
|
|
|
|
use App\Http\Controllers\Admin\TahesabController;
|
|
|
|
|
use App\Models\Customer;
|
|
|
|
|
use App\Models\Tahesab;
|
2026-06-27 22:10:29 +03:30
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
|
use Illuminate\Support\Facades\Log;
|
2026-06-25 22:20:40 +03:30
|
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
|
|
|
|
|
|
class TahesabSyncService
|
|
|
|
|
{
|
|
|
|
|
public const STATUS_PENDING = 'pending';
|
|
|
|
|
public const STATUS_SUCCESS = 'success';
|
|
|
|
|
public const STATUS_FAILED = 'failed';
|
|
|
|
|
|
|
|
|
|
public function responseOk(mixed $response): bool
|
|
|
|
|
{
|
|
|
|
|
return is_array($response) && ! empty($response['OK']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function buildLog(mixed $response = null, ?string $extra = null): string
|
|
|
|
|
{
|
|
|
|
|
$parts = array_filter([
|
|
|
|
|
$extra,
|
|
|
|
|
is_array($response) ? json_encode($response, JSON_UNESCAPED_UNICODE) : (is_string($response) ? $response : null),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return implode("\n", $parts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function markSuccess(Tahesab $tahesab, array $response, ?array $payload = null): void
|
|
|
|
|
{
|
|
|
|
|
$update = [
|
|
|
|
|
'sh_factor' => $response['Sh_factor'] ?? null,
|
|
|
|
|
'tahesab_id' => $response['OK'],
|
|
|
|
|
'factor_number' => $response['Factor_Number'] ?? null,
|
|
|
|
|
'tah_id' => $response['ID'] ?? null,
|
|
|
|
|
'res' => json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
|
|
|
|
|
'sync_status' => self::STATUS_SUCCESS,
|
|
|
|
|
'sync_log' => null,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if ($payload !== null) {
|
|
|
|
|
$update['data'] = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($this->hasSyncColumns()) {
|
|
|
|
|
Tahesab::where('id', $tahesab->id)->update($update);
|
|
|
|
|
} else {
|
|
|
|
|
unset($update['sync_status'], $update['sync_log']);
|
|
|
|
|
Tahesab::where('id', $tahesab->id)->update($update);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function markFailed(Tahesab $tahesab, mixed $response = null, ?string $extra = null): void
|
|
|
|
|
{
|
|
|
|
|
$update = [
|
|
|
|
|
'res' => is_array($response)
|
|
|
|
|
? json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
|
|
|
|
: (is_string($response) ? $response : null),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if ($this->hasSyncColumns()) {
|
|
|
|
|
$update['sync_status'] = self::STATUS_FAILED;
|
|
|
|
|
$update['sync_log'] = $this->buildLog($response, $extra);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Tahesab::where('id', $tahesab->id)->update($update);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function isFailed(Tahesab $tahesab): bool
|
|
|
|
|
{
|
|
|
|
|
if ($this->hasSyncColumns()) {
|
|
|
|
|
return ($tahesab->sync_status ?? null) === self::STATUS_FAILED
|
|
|
|
|
|| (empty($tahesab->tahesab_id) && ($tahesab->sync_status ?? null) !== self::STATUS_SUCCESS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return empty($tahesab->tahesab_id);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-27 22:10:29 +03:30
|
|
|
public function registerOnCreate(Customer $customer): void
|
|
|
|
|
{
|
|
|
|
|
if (! empty($customer->moshtari_code) || ! $this->isSyncEnabled()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$customerId = (int) $customer->id;
|
|
|
|
|
|
|
|
|
|
dispatch(function () use ($customerId) {
|
|
|
|
|
$service = app(TahesabSyncService::class);
|
|
|
|
|
$customer = Customer::find($customerId);
|
|
|
|
|
if ($customer === null || ! empty($customer->moshtari_code) || ! $service->isSyncEnabled()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$result = $service->registerCustomer($customer);
|
|
|
|
|
if (! ($result['ok'] ?? false)) {
|
|
|
|
|
Log::warning('Tahesab auto-register on customer create failed', [
|
|
|
|
|
'customer_id' => $customerId,
|
|
|
|
|
'message' => $result['message'] ?? null,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
Log::warning('Tahesab auto-register on customer create threw', [
|
|
|
|
|
'customer_id' => $customerId,
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
})->afterResponse();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-25 22:20:40 +03:30
|
|
|
public function registerCustomer(Customer $customer): array
|
|
|
|
|
{
|
2026-06-27 22:10:29 +03:30
|
|
|
$customer->refresh();
|
|
|
|
|
|
|
|
|
|
if (! empty($customer->moshtari_code)) {
|
|
|
|
|
return [
|
|
|
|
|
'ok' => true,
|
|
|
|
|
'message' => 'مشتری قبلاً در تهحساب ثبت شده است',
|
|
|
|
|
'moshtari_code' => $customer->moshtari_code,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-25 22:20:40 +03:30
|
|
|
$controller = app(TahesabController::class);
|
|
|
|
|
$result = $controller->createCustomerInTahesab($customer->id);
|
|
|
|
|
|
2026-06-27 22:10:29 +03:30
|
|
|
if (is_string($result)) {
|
2026-06-25 22:20:40 +03:30
|
|
|
return ['ok' => false, 'message' => $result];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$customer->refresh();
|
|
|
|
|
|
|
|
|
|
if (empty($customer->moshtari_code)) {
|
|
|
|
|
return ['ok' => false, 'message' => 'ثبت مشتری در تهحساب ناموفق بود'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'ok' => true,
|
|
|
|
|
'message' => 'مشتری با موفقیت در تهحساب ثبت شد',
|
|
|
|
|
'moshtari_code' => $customer->moshtari_code,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function retry(Tahesab $tahesab): array
|
|
|
|
|
{
|
|
|
|
|
if (! $this->isFailed($tahesab) && ! empty($tahesab->tahesab_id)) {
|
|
|
|
|
return ['ok' => false, 'message' => 'این سند قبلاً در تهحساب ثبت شده است'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$payload = json_decode((string) $tahesab->data, true) ?: [];
|
|
|
|
|
$handler = $payload['handler'] ?? $this->inferHandler($tahesab);
|
|
|
|
|
$controller = app(TahesabController::class);
|
|
|
|
|
|
|
|
|
|
$result = match ($handler) {
|
|
|
|
|
'wallet' => $controller->wallet(array_merge($payload, ['_retry_tahesab_id' => $tahesab->id]), $tahesab),
|
|
|
|
|
'walletGold' => $controller->walletGold(array_merge($payload, ['_retry_tahesab_id' => $tahesab->id]), $tahesab),
|
|
|
|
|
'storeTrade' => $controller->storeTrade(array_merge($payload, ['_retry_tahesab_id' => $tahesab->id]), $tahesab),
|
|
|
|
|
'goldReward' => $controller->goldReward(array_merge($payload, ['_retry_tahesab_id' => $tahesab->id]), $tahesab),
|
|
|
|
|
default => false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$tahesab->refresh();
|
|
|
|
|
|
|
|
|
|
if ($result !== false && ! empty($tahesab->tahesab_id)) {
|
|
|
|
|
return ['ok' => true, 'message' => 'سند با موفقیت در تهحساب ثبت شد', 'tahesab' => $tahesab];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'ok' => false,
|
|
|
|
|
'message' => $tahesab->sync_log ?: 'ارسال مجدد به تهحساب ناموفق بود',
|
|
|
|
|
'tahesab' => $tahesab,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function inferHandler(Tahesab $tahesab): string
|
|
|
|
|
{
|
|
|
|
|
if ($tahesab->order_id) {
|
|
|
|
|
return 'storeTrade';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($tahesab->type === 'ريال') {
|
|
|
|
|
return 'wallet';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$desc = (string) $tahesab->desc;
|
|
|
|
|
|
|
|
|
|
if (str_contains($desc, 'شارژ کیف') || str_contains($desc, 'برداشت از کیف')) {
|
|
|
|
|
return 'walletGold';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 'goldReward';
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-27 22:10:29 +03:30
|
|
|
public function isSyncEnabled(): bool
|
|
|
|
|
{
|
|
|
|
|
$config = app(ConfigService::class);
|
|
|
|
|
|
|
|
|
|
if ($config->getString('send_to_tahesab', 'false') !== 'true') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$host = trim($config->getString('tahesab_host', ''));
|
|
|
|
|
$port = trim($config->getString('tahesab_port', ''));
|
|
|
|
|
$token = trim($config->getString('tahesab_token', ''));
|
|
|
|
|
$db = trim($config->getString('tahesab_db', ''));
|
|
|
|
|
|
|
|
|
|
return $host !== '' && $port !== '' && $token !== '' && $db !== '';
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-25 22:20:40 +03:30
|
|
|
private function hasSyncColumns(): bool
|
|
|
|
|
{
|
|
|
|
|
return Schema::hasTable('tahesab') && Schema::hasColumn('tahesab', 'sync_status');
|
|
|
|
|
}
|
|
|
|
|
}
|