269 lines
8.5 KiB
PHP
269 lines
8.5 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Http\Controllers\Admin\ZarinController;
|
|
use App\Models\Customer;
|
|
use App\Models\Zarin;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
class ZarinSyncService
|
|
{
|
|
public const STATUS_PENDING = 'pending';
|
|
public const STATUS_SUCCESS = 'success';
|
|
public const STATUS_FAILED = 'failed';
|
|
|
|
public function responseOk(mixed $response): bool
|
|
{
|
|
if (! is_array($response)) {
|
|
return false;
|
|
}
|
|
|
|
if ($this->hasNumericIdKey($response)) {
|
|
return true;
|
|
}
|
|
|
|
$data = $response['data'] ?? null;
|
|
if (! is_array($data)) {
|
|
return false;
|
|
}
|
|
|
|
$row = array_is_list($data) ? ($data[0] ?? null) : $data;
|
|
|
|
return is_array($row) && $this->hasNumericIdKey($row);
|
|
}
|
|
|
|
private function hasNumericIdKey(array $row): bool
|
|
{
|
|
foreach (['idFactor', 'idSanad', 'pkRadifSanad', 'idHesab', 'OK'] as $key) {
|
|
if (! array_key_exists($key, $row)) {
|
|
continue;
|
|
}
|
|
$value = $row[$key];
|
|
if ($value === null || $value === '' || $value === false) {
|
|
continue;
|
|
}
|
|
if (is_numeric($value) || (is_string($value) && trim($value) !== '')) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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(Zarin $row, array $response, ?array $payload = null): void
|
|
{
|
|
$flat = $this->flattenResponse($response);
|
|
|
|
$zarinId = $flat['OK']
|
|
?? $flat['idFactor']
|
|
?? $flat['idSanad']
|
|
?? $flat['pkRadifSanad']
|
|
?? $flat['idHesab']
|
|
?? null;
|
|
|
|
$shFactor = $flat['Sh_factor']
|
|
?? $flat['pkRadifSanad']
|
|
?? $flat['idRadifSanad']
|
|
?? null;
|
|
|
|
$update = [
|
|
'sh_factor' => $shFactor !== null ? (string) $shFactor : null,
|
|
'zarin_id' => $zarinId !== null ? (string) $zarinId : null,
|
|
'factor_number' => isset($flat['Factor_Number']) ? (string) $flat['Factor_Number'] : (isset($flat['idFactor']) ? (string) $flat['idFactor'] : 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()) {
|
|
Zarin::where('id', $row->id)->update($update);
|
|
} else {
|
|
unset($update['sync_status'], $update['sync_log']);
|
|
Zarin::where('id', $row->id)->update($update);
|
|
}
|
|
}
|
|
|
|
public function markFailed(Zarin $row, 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);
|
|
}
|
|
|
|
Zarin::where('id', $row->id)->update($update);
|
|
}
|
|
|
|
public function isFailed(Zarin $row): bool
|
|
{
|
|
if ($this->hasSyncColumns()) {
|
|
return ($row->sync_status ?? null) === self::STATUS_FAILED
|
|
|| (empty($row->zarin_id) && ($row->sync_status ?? null) !== self::STATUS_SUCCESS);
|
|
}
|
|
|
|
return empty($row->zarin_id);
|
|
}
|
|
|
|
public function registerOnCreate(Customer $customer): void
|
|
{
|
|
if (! empty($customer->moshtari_code) || ! $this->isSyncEnabled()) {
|
|
return;
|
|
}
|
|
|
|
$customerId = (int) $customer->id;
|
|
|
|
dispatch(function () use ($customerId) {
|
|
$service = app(ZarinSyncService::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('Zarin auto-register on customer create failed', [
|
|
'customer_id' => $customerId,
|
|
'message' => $result['message'] ?? null,
|
|
]);
|
|
}
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Zarin auto-register on customer create threw', [
|
|
'customer_id' => $customerId,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
})->afterResponse();
|
|
}
|
|
|
|
public function registerCustomer(Customer $customer): array
|
|
{
|
|
$customer->refresh();
|
|
|
|
if (! empty($customer->moshtari_code)) {
|
|
return [
|
|
'ok' => true,
|
|
'message' => 'مشتری قبلاً در زرین ثبت شده است',
|
|
'moshtari_code' => $customer->moshtari_code,
|
|
];
|
|
}
|
|
|
|
$controller = app(ZarinController::class);
|
|
$result = $controller->createCustomerInZarin($customer->id);
|
|
|
|
if (is_string($result)) {
|
|
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(Zarin $row): array
|
|
{
|
|
if (! $this->isFailed($row) && ! empty($row->zarin_id)) {
|
|
return ['ok' => false, 'message' => 'این سند قبلاً در زرین ثبت شده است'];
|
|
}
|
|
|
|
$payload = json_decode((string) $row->data, true) ?: [];
|
|
$handler = $payload['handler'] ?? $this->inferHandler($row);
|
|
$controller = app(ZarinController::class);
|
|
|
|
$result = match ($handler) {
|
|
'wallet' => $controller->wallet(array_merge($payload, ['_retry_zarin_id' => $row->id]), $row),
|
|
'walletGold' => $controller->walletGold(array_merge($payload, ['_retry_zarin_id' => $row->id]), $row),
|
|
'storeTrade' => $controller->storeTrade(array_merge($payload, ['_retry_zarin_id' => $row->id]), $row),
|
|
'goldReward' => $controller->goldReward(array_merge($payload, ['_retry_zarin_id' => $row->id]), $row),
|
|
default => false,
|
|
};
|
|
|
|
$row->refresh();
|
|
|
|
if ($result !== false && ! empty($row->zarin_id)) {
|
|
return ['ok' => true, 'message' => 'سند با موفقیت در زرین ثبت شد', 'zarin' => $row];
|
|
}
|
|
|
|
return [
|
|
'ok' => false,
|
|
'message' => $row->sync_log ?: 'ارسال مجدد به زرین ناموفق بود',
|
|
'zarin' => $row,
|
|
];
|
|
}
|
|
|
|
private function inferHandler(Zarin $row): string
|
|
{
|
|
if ($row->order_id) {
|
|
return 'storeTrade';
|
|
}
|
|
|
|
if ($row->type === 'ريال') {
|
|
return 'wallet';
|
|
}
|
|
|
|
$desc = (string) $row->desc;
|
|
|
|
if (str_contains($desc, 'شارژ کیف') || str_contains($desc, 'برداشت از کیف')) {
|
|
return 'walletGold';
|
|
}
|
|
|
|
return 'goldReward';
|
|
}
|
|
|
|
public function isSyncEnabled(): bool
|
|
{
|
|
$config = app(ConfigService::class);
|
|
|
|
return $config->isZarinAccounting()
|
|
&& trim($config->getString('zarin_api', '')) !== ''
|
|
&& trim($config->getString('zarin_token', '')) !== '';
|
|
}
|
|
|
|
private function flattenResponse(array $response): array
|
|
{
|
|
$data = $response['data'] ?? null;
|
|
if (is_array($data)) {
|
|
$row = array_is_list($data) ? ($data[0] ?? []) : $data;
|
|
if (is_array($row)) {
|
|
return array_merge($response, $row);
|
|
}
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
private function hasSyncColumns(): bool
|
|
{
|
|
return Schema::hasTable('zarin') && Schema::hasColumn('zarin', 'sync_status');
|
|
}
|
|
}
|