fix pargar bugs 4
This commit is contained in:
@@ -255,7 +255,7 @@ class PargarController extends Controller
|
|||||||
return response()->json($result, ($result['ok'] ?? false) ? 200 : 422);
|
return response()->json($result, ($result['ok'] ?? false) ? 200 : 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncCustomerWallet($customerId)
|
public function syncCustomerWallet(Request $request, $customerId)
|
||||||
{
|
{
|
||||||
if (! app(ConfigService::class)->isPargarAccounting()) {
|
if (! app(ConfigService::class)->isPargarAccounting()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@@ -272,7 +272,8 @@ class PargarController extends Controller
|
|||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = app(PargarInboundWalletService::class)->syncWalletFromPargar($customer);
|
$force = $request->boolean('force');
|
||||||
|
$result = app(PargarInboundWalletService::class)->syncWalletFromPargar($customer, $force);
|
||||||
$appliedDocs = (int) ($result['documents']['applied'] ?? 0);
|
$appliedDocs = (int) ($result['documents']['applied'] ?? 0);
|
||||||
$appliedMande = (bool) ($result['mande']['applied'] ?? false);
|
$appliedMande = (bool) ($result['mande']['applied'] ?? false);
|
||||||
$mandeMessage = (string) ($result['mande']['message'] ?? '');
|
$mandeMessage = (string) ($result['mande']['message'] ?? '');
|
||||||
@@ -285,7 +286,6 @@ class PargarController extends Controller
|
|||||||
} elseif ($appliedMande) {
|
} elseif ($appliedMande) {
|
||||||
$message = 'ماندهٔ پرگار روی کیف اعمال شد';
|
$message = 'ماندهٔ پرگار روی کیف اعمال شد';
|
||||||
} else {
|
} else {
|
||||||
// فقط وقتی هیچچیز اعمال نشده، پیام رد/بدوناختلاف مانده را نشان بده
|
|
||||||
$message = $mandeMessage !== ''
|
$message = $mandeMessage !== ''
|
||||||
? $mandeMessage
|
? $mandeMessage
|
||||||
: 'سند قابلاعمالی یافت نشد';
|
: 'سند قابلاعمالی یافت نشد';
|
||||||
@@ -296,7 +296,6 @@ class PargarController extends Controller
|
|||||||
'message' => $message,
|
'message' => $message,
|
||||||
'result' => $result,
|
'result' => $result,
|
||||||
]);
|
]);
|
||||||
// همیشه ۲۰۰ — «قبلاً اعمال شده / بدون اختلاف» خطا نیست؛ فرانت خودش info میزند
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function uniqueReferralId(): string
|
private function uniqueReferralId(): string
|
||||||
|
|||||||
@@ -4,17 +4,24 @@ namespace App\Services;
|
|||||||
|
|
||||||
use App\Models\Customer;
|
use App\Models\Customer;
|
||||||
use App\Models\PargarSyncLog;
|
use App\Models\PargarSyncLog;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
||||||
use Rooyesh\Wallet\interfaces\WalletInterface;
|
use Rooyesh\Wallet\interfaces\WalletInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* اعمال اسناد ورودی پرگار روی کیف اپ — سندبهسند، نه تعدیل bulk از mande.
|
* اعمال اسناد/ماندهٔ ورودی پرگار روی کیف اپ.
|
||||||
|
*
|
||||||
|
* دکمهٔ ادمین فقط مانده (mande) را یکبار تنظیم میکند.
|
||||||
|
* اسناد جدید از webhook با اثرانگشت یکتا اعمال میشوند — بدون replay انبوه لاگهای قدیمی.
|
||||||
*/
|
*/
|
||||||
class PargarInboundWalletService
|
class PargarInboundWalletService
|
||||||
{
|
{
|
||||||
public const TX_TYPE_PARGAR = 22;
|
public const TX_TYPE_PARGAR = 22;
|
||||||
|
|
||||||
|
/** @var array<string, true> */
|
||||||
|
private array $seenFingerprints = [];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly PargarSyncService $sync,
|
private readonly PargarSyncService $sync,
|
||||||
private readonly WalletBroadcastService $walletBroadcast
|
private readonly WalletBroadcastService $walletBroadcast
|
||||||
@@ -25,7 +32,7 @@ class PargarInboundWalletService
|
|||||||
* @param array<string, mixed> $data
|
* @param array<string, mixed> $data
|
||||||
* @return array{applied: int, skipped: int, errors: list<string>, logs: list<int>}
|
* @return array{applied: int, skipped: int, errors: list<string>, logs: list<int>}
|
||||||
*/
|
*/
|
||||||
public function applyDocuments(Customer $customer, array $data): array
|
public function applyDocuments(Customer $customer, array $data, bool $broadcast = true): array
|
||||||
{
|
{
|
||||||
$transactions = is_array($data['transactions'] ?? null) ? $data['transactions'] : [];
|
$transactions = is_array($data['transactions'] ?? null) ? $data['transactions'] : [];
|
||||||
$accountId = trim((string) ($data['accountId'] ?? ''));
|
$accountId = trim((string) ($data['accountId'] ?? ''));
|
||||||
@@ -41,136 +48,132 @@ class PargarInboundWalletService
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
$walletApplied = false;
|
$lock = Cache::lock('pargar-wallet-apply:'.$customer->id, 60);
|
||||||
|
if (! $lock->get()) {
|
||||||
|
$result['errors'][] = 'همگامسازی دیگری برای این مشتری در حال اجراست';
|
||||||
|
|
||||||
foreach ($transactions as $tx) {
|
return $result;
|
||||||
if (! is_array($tx)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$docId = trim((string) ($tx['docId'] ?? ''));
|
|
||||||
if ($docId === '') {
|
|
||||||
$result['skipped']++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->isDocumentApplied($docId)) {
|
|
||||||
$result['skipped']++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$goldDelta = $this->transactionGoldDelta($tx);
|
|
||||||
$rialDelta = $this->transactionRialDelta($tx);
|
|
||||||
|
|
||||||
if (abs($goldDelta) < 0.01 && abs($rialDelta) < 1) {
|
|
||||||
$result['skipped']++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->isEchoOfRecentOutbound((int) $customer->id, $goldDelta, $rialDelta)) {
|
|
||||||
$this->recordSkippedDocument($customer, $docId, $accountId, $tx, 'echo_outbound');
|
|
||||||
$result['skipped']++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->applyTransactionDelta($customer, $tx, $goldDelta, $rialDelta);
|
|
||||||
$walletApplied = true;
|
|
||||||
|
|
||||||
$summary = $this->sync->summarizeInboundDocument([
|
|
||||||
'accountId' => $accountId,
|
|
||||||
'accountName' => $data['accountName'] ?? null,
|
|
||||||
'transactions' => [$tx],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$log = PargarSyncLog::create([
|
|
||||||
'customer_id' => $customer->id,
|
|
||||||
'amount' => $summary['amount'],
|
|
||||||
'desc' => $summary['desc'] !== '' ? $summary['desc'] : 'اعمال سند پرگار',
|
|
||||||
'type' => PargarSyncService::TYPE_DOCUMENT,
|
|
||||||
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
|
||||||
'pargar_ref' => $docId,
|
|
||||||
'data' => json_encode([
|
|
||||||
'accountId' => $accountId,
|
|
||||||
'accountName' => $data['accountName'] ?? null,
|
|
||||||
'transaction' => $tx,
|
|
||||||
'wallet_applied' => true,
|
|
||||||
'gold_delta' => $goldDelta,
|
|
||||||
'rial_delta' => $rialDelta,
|
|
||||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$result['applied']++;
|
|
||||||
$result['logs'][] = (int) $log->id;
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::warning('Pargar inbound document apply failed', [
|
|
||||||
'customer_id' => $customer->id,
|
|
||||||
'doc_id' => $docId,
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
]);
|
|
||||||
$result['errors'][] = "سند {$docId}: " . $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($walletApplied) {
|
try {
|
||||||
$this->walletBroadcast->broadcastForCustomer(
|
$walletApplied = false;
|
||||||
(int) $customer->id,
|
|
||||||
(int) ($customer->type ?? 2)
|
foreach ($transactions as $tx) {
|
||||||
);
|
if (! is_array($tx)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fingerprint = $this->transactionFingerprint($accountId, $tx);
|
||||||
|
if ($fingerprint === '' || $this->isFingerprintApplied($fingerprint)) {
|
||||||
|
$result['skipped']++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// علامت در حافظه تا در همین درخواست دوباره نخورد
|
||||||
|
$this->seenFingerprints[$fingerprint] = true;
|
||||||
|
|
||||||
|
$goldDelta = $this->transactionGoldDelta($tx);
|
||||||
|
$rialDelta = $this->transactionRialDelta($tx);
|
||||||
|
|
||||||
|
if (abs($goldDelta) < 0.01 && abs($rialDelta) < 1) {
|
||||||
|
$this->recordProcessedFingerprint($customer, $fingerprint, $accountId, $tx, false, 'zero_amount');
|
||||||
|
$result['skipped']++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isEchoOfRecentOutbound((int) $customer->id, $goldDelta, $rialDelta)) {
|
||||||
|
$this->recordProcessedFingerprint($customer, $fingerprint, $accountId, $tx, false, 'echo_outbound');
|
||||||
|
$result['skipped']++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->applyTransactionDelta($customer, $tx, $goldDelta, $rialDelta);
|
||||||
|
$walletApplied = true;
|
||||||
|
|
||||||
|
$summary = $this->sync->summarizeInboundDocument([
|
||||||
|
'accountId' => $accountId,
|
||||||
|
'accountName' => $data['accountName'] ?? null,
|
||||||
|
'transactions' => [$tx],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$log = PargarSyncLog::create([
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'amount' => $summary['amount'],
|
||||||
|
'desc' => $summary['desc'] !== '' ? $summary['desc'] : 'اعمال سند پرگار',
|
||||||
|
'type' => PargarSyncService::TYPE_DOCUMENT,
|
||||||
|
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
||||||
|
'pargar_ref' => $fingerprint,
|
||||||
|
'data' => json_encode([
|
||||||
|
'accountId' => $accountId,
|
||||||
|
'accountName' => $data['accountName'] ?? null,
|
||||||
|
'transaction' => $tx,
|
||||||
|
'wallet_applied' => true,
|
||||||
|
'fingerprint' => $fingerprint,
|
||||||
|
'gold_delta' => $goldDelta,
|
||||||
|
'rial_delta' => $rialDelta,
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$result['applied']++;
|
||||||
|
$result['logs'][] = (int) $log->id;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
unset($this->seenFingerprints[$fingerprint]);
|
||||||
|
Log::warning('Pargar inbound document apply failed', [
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'fingerprint' => $fingerprint,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
$result['errors'][] = 'سند: '.$e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($walletApplied && $broadcast) {
|
||||||
|
$this->walletBroadcast->broadcastForCustomer(
|
||||||
|
(int) $customer->id,
|
||||||
|
(int) ($customer->type ?? 2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
$lock->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* اسناد ذخیرهشدهٔ قبلی (قبل از deploy) را از لاگ دوباره اعمال میکند.
|
* دکمهٔ ادمین: فقط اختلاف ماندهٔ پرگار را یکبار اعمال میکند (نه replay اسناد).
|
||||||
*
|
*
|
||||||
* @return array{applied: int, skipped: int, errors: list<string>, logs: list<int>}
|
* @return array{
|
||||||
|
* documents: array{applied: int, skipped: int, errors: list<string>, logs: list<int>},
|
||||||
|
* mande: array{applied: bool, gold_delta?: float, rial_delta?: float, message: string}
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
public function replayStoredDocuments(Customer $customer): array
|
public function syncWalletFromPargar(Customer $customer, bool $forceMande = false): array
|
||||||
{
|
{
|
||||||
$accountId = trim((string) ($customer->pargar_account_id ?? ''));
|
$lock = Cache::lock('pargar-wallet-sync:'.$customer->id, 90);
|
||||||
|
if (! $lock->get()) {
|
||||||
$logs = PargarSyncLog::query()
|
return [
|
||||||
->where('type', PargarSyncService::TYPE_DOCUMENT)
|
'documents' => ['applied' => 0, 'skipped' => 0, 'errors' => ['همگامسازی در حال اجراست'], 'logs' => []],
|
||||||
->where(function ($q) use ($customer, $accountId) {
|
'mande' => ['applied' => false, 'message' => 'همگامسازی دیگری برای این مشتری در حال اجراست — لطفاً صبر کنید'],
|
||||||
$q->where('customer_id', $customer->id);
|
];
|
||||||
if ($accountId !== '') {
|
|
||||||
$q->orWhere('pargar_ref', $accountId);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
->orderBy('id')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$total = [
|
|
||||||
'applied' => 0,
|
|
||||||
'skipped' => 0,
|
|
||||||
'errors' => [],
|
|
||||||
'logs' => [],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($logs as $log) {
|
|
||||||
$data = json_decode((string) $log->data, true);
|
|
||||||
if (! is_array($data) || empty($data['transactions']) || ! is_array($data['transactions'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$batch = $this->applyDocuments($customer, $data);
|
|
||||||
$total['applied'] += $batch['applied'];
|
|
||||||
$total['skipped'] += $batch['skipped'];
|
|
||||||
$total['errors'] = array_merge($total['errors'], $batch['errors']);
|
|
||||||
$total['logs'] = array_merge($total['logs'], $batch['logs']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $total;
|
try {
|
||||||
|
// عمداً replay اسناد قدیمی نداریم — باعث شارژ چندباره میشد
|
||||||
|
return [
|
||||||
|
'documents' => ['applied' => 0, 'skipped' => 0, 'errors' => [], 'logs' => []],
|
||||||
|
'mande' => $this->applyLatestMandeDifference($customer, force: $forceMande),
|
||||||
|
];
|
||||||
|
} finally {
|
||||||
|
$lock->release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* آخرین ماندهٔ پرگار را (در صورت اختلاف) یکبار روی کیف اعمال میکند — فقط با درخواست ادمین.
|
|
||||||
*
|
|
||||||
* @return array{applied: bool, gold_delta?: float, rial_delta?: float, message: string}
|
* @return array{applied: bool, gold_delta?: float, rial_delta?: float, message: string}
|
||||||
*/
|
*/
|
||||||
public function applyLatestMandeDifference(Customer $customer): array
|
public function applyLatestMandeDifference(Customer $customer, bool $force = false): array
|
||||||
{
|
{
|
||||||
if ($this->hasPendingOutboundWalletSync((int) $customer->id)) {
|
if ($this->hasPendingOutboundWalletSync((int) $customer->id)) {
|
||||||
return [
|
return [
|
||||||
@@ -190,7 +193,7 @@ class PargarInboundWalletService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$data = json_decode((string) $log->data, true) ?: [];
|
$data = json_decode((string) $log->data, true) ?: [];
|
||||||
if (! empty($data['wallet_applied_from_mande'])) {
|
if (! $force && ! empty($data['wallet_applied_from_mande'])) {
|
||||||
return ['applied' => false, 'message' => 'ماندهٔ پرگار قبلاً روی کیف اعمال شده است'];
|
return ['applied' => false, 'message' => 'ماندهٔ پرگار قبلاً روی کیف اعمال شده است'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +217,7 @@ class PargarInboundWalletService
|
|||||||
|
|
||||||
$input = [
|
$input = [
|
||||||
'title' => 'تطبیق ماندهٔ پرگار',
|
'title' => 'تطبیق ماندهٔ پرگار',
|
||||||
'note' => 'mande-log:' . $log->id,
|
'note' => 'mande-log:'.$log->id,
|
||||||
'expire_at' => now(),
|
'expire_at' => now(),
|
||||||
'active' => true,
|
'active' => true,
|
||||||
];
|
];
|
||||||
@@ -236,6 +239,7 @@ class PargarInboundWalletService
|
|||||||
$data['wallet_applied_from_mande'] = true;
|
$data['wallet_applied_from_mande'] = true;
|
||||||
$data['applied_gold_delta'] = $goldDiff;
|
$data['applied_gold_delta'] = $goldDiff;
|
||||||
$data['applied_rial_delta'] = $rialDiff;
|
$data['applied_rial_delta'] = $rialDiff;
|
||||||
|
$data['applied_at'] = now()->toDateTimeString();
|
||||||
$log->data = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
$log->data = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||||
$log->desc = 'اعمال ماندهٔ پرگار روی کیف اپ';
|
$log->desc = 'اعمال ماندهٔ پرگار روی کیف اپ';
|
||||||
$log->save();
|
$log->save();
|
||||||
@@ -251,39 +255,79 @@ class PargarInboundWalletService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{
|
* @param array<string, mixed> $tx
|
||||||
* documents: array{applied: int, skipped: int, errors: list<string>, logs: list<int>},
|
|
||||||
* mande: array{applied: bool, gold_delta?: float, rial_delta?: float, message: string}
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
public function syncWalletFromPargar(Customer $customer): array
|
public function transactionFingerprint(string $accountId, array $tx): string
|
||||||
{
|
{
|
||||||
return [
|
// بدون docId — اگر پرگار هر بار شناسهٔ جدید بدهد همان مبلغ دوباره نخورد
|
||||||
'documents' => $this->replayStoredDocuments($customer),
|
$parts = [
|
||||||
'mande' => $this->applyLatestMandeDifference($customer),
|
$accountId,
|
||||||
|
trim((string) ($tx['date'] ?? '')),
|
||||||
|
trim((string) ($tx['docType'] ?? '')),
|
||||||
|
trim((string) ($tx['desc'] ?? '')),
|
||||||
|
$this->normalizeNumberString($tx['bedGold'] ?? 0),
|
||||||
|
$this->normalizeNumberString($tx['besGold'] ?? 0),
|
||||||
|
$this->normalizeNumberString($tx['bedRial'] ?? 0),
|
||||||
|
$this->normalizeNumberString($tx['besRial'] ?? 0),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
return 'fp:'.hash('sha256', implode('|', $parts));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isDocumentApplied(string $docId): bool
|
public function isFingerprintApplied(string $fingerprint): bool
|
||||||
{
|
{
|
||||||
if ($docId === '') {
|
if ($fingerprint === '' || isset($this->seenFingerprints[$fingerprint])) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$logs = PargarSyncLog::query()
|
return PargarSyncLog::query()
|
||||||
->where('type', PargarSyncService::TYPE_DOCUMENT)
|
->where('type', PargarSyncService::TYPE_DOCUMENT)
|
||||||
->where('pargar_ref', $docId)
|
->where('pargar_ref', $fingerprint)
|
||||||
->where('sync_status', PargarSyncService::STATUS_SUCCESS)
|
->where('sync_status', PargarSyncService::STATUS_SUCCESS)
|
||||||
->get(['data']);
|
->exists();
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($logs as $log) {
|
/**
|
||||||
$data = json_decode((string) $log->data, true);
|
* @param array<string, mixed> $tx
|
||||||
if (($data['wallet_applied'] ?? false) === true) {
|
*/
|
||||||
return true;
|
private function recordProcessedFingerprint(
|
||||||
}
|
Customer $customer,
|
||||||
|
string $fingerprint,
|
||||||
|
string $accountId,
|
||||||
|
array $tx,
|
||||||
|
bool $walletApplied,
|
||||||
|
string $reason
|
||||||
|
): void {
|
||||||
|
if ($this->isFingerprintApplied($fingerprint) && ! isset($this->seenFingerprints[$fingerprint])) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// اگر فقط در حافظه است و هنوز در DB نیست، برای skipها هم رکورد بساز تا تکرار نشود
|
||||||
|
if (PargarSyncLog::query()
|
||||||
|
->where('type', PargarSyncService::TYPE_DOCUMENT)
|
||||||
|
->where('pargar_ref', $fingerprint)
|
||||||
|
->exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PargarSyncLog::create([
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'desc' => $walletApplied
|
||||||
|
? 'اعمال سند پرگار'
|
||||||
|
: ($reason === 'echo_outbound'
|
||||||
|
? 'سند پرگار — قبلاً از پنل ارسال شده (بدون اعمال مجدد)'
|
||||||
|
: 'سند پرگار — رد شده'),
|
||||||
|
'type' => PargarSyncService::TYPE_DOCUMENT,
|
||||||
|
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
||||||
|
'pargar_ref' => $fingerprint,
|
||||||
|
'data' => json_encode([
|
||||||
|
'accountId' => $accountId,
|
||||||
|
'transaction' => $tx,
|
||||||
|
'wallet_applied' => $walletApplied,
|
||||||
|
'fingerprint' => $fingerprint,
|
||||||
|
'skip_reason' => $walletApplied ? null : $reason,
|
||||||
|
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,10 +370,7 @@ class PargarInboundWalletService
|
|||||||
*/
|
*/
|
||||||
private function transactionGoldDelta(array $tx): float
|
private function transactionGoldDelta(array $tx): float
|
||||||
{
|
{
|
||||||
$bes = $this->parseNumber($tx['besGold'] ?? 0);
|
return $this->parseNumber($tx['besGold'] ?? 0) - $this->parseNumber($tx['bedGold'] ?? 0);
|
||||||
$bed = $this->parseNumber($tx['bedGold'] ?? 0);
|
|
||||||
|
|
||||||
return $bes - $bed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -337,10 +378,7 @@ class PargarInboundWalletService
|
|||||||
*/
|
*/
|
||||||
private function transactionRialDelta(array $tx): float
|
private function transactionRialDelta(array $tx): float
|
||||||
{
|
{
|
||||||
$bes = $this->parseNumber($tx['besRial'] ?? 0);
|
return $this->parseNumber($tx['besRial'] ?? 0) - $this->parseNumber($tx['bedRial'] ?? 0);
|
||||||
$bed = $this->parseNumber($tx['bedRial'] ?? 0);
|
|
||||||
|
|
||||||
return $bes - $bed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isEchoOfRecentOutbound(int $customerId, float $goldDelta, float $rialDelta): bool
|
private function isEchoOfRecentOutbound(int $customerId, float $goldDelta, float $rialDelta): bool
|
||||||
@@ -375,31 +413,6 @@ class PargarInboundWalletService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $tx
|
|
||||||
*/
|
|
||||||
private function recordSkippedDocument(
|
|
||||||
Customer $customer,
|
|
||||||
string $docId,
|
|
||||||
string $accountId,
|
|
||||||
array $tx,
|
|
||||||
string $reason
|
|
||||||
): void {
|
|
||||||
PargarSyncLog::create([
|
|
||||||
'customer_id' => $customer->id,
|
|
||||||
'desc' => 'سند پرگار — قبلاً از پنل ارسال شده (بدون اعمال مجدد)',
|
|
||||||
'type' => PargarSyncService::TYPE_DOCUMENT,
|
|
||||||
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
|
||||||
'pargar_ref' => $docId,
|
|
||||||
'data' => json_encode([
|
|
||||||
'accountId' => $accountId,
|
|
||||||
'transaction' => $tx,
|
|
||||||
'wallet_applied' => false,
|
|
||||||
'skip_reason' => $reason,
|
|
||||||
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function hasPendingOutboundWalletSync(int $customerId): bool
|
private function hasPendingOutboundWalletSync(int $customerId): bool
|
||||||
{
|
{
|
||||||
return PargarSyncLog::query()
|
return PargarSyncLog::query()
|
||||||
@@ -409,6 +422,11 @@ class PargarInboundWalletService
|
|||||||
->exists();
|
->exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function normalizeNumberString(mixed $value): string
|
||||||
|
{
|
||||||
|
return (string) $this->parseNumber($value);
|
||||||
|
}
|
||||||
|
|
||||||
private function parseNumber(mixed $value): float
|
private function parseNumber(mixed $value): float
|
||||||
{
|
{
|
||||||
if ($value === null || $value === '') {
|
if ($value === null || $value === '') {
|
||||||
|
|||||||
Reference in New Issue
Block a user