all customer from pargar fixing 3

This commit is contained in:
2026-07-22 01:05:00 +03:30
parent 01031e9e01
commit 133f4156c6
13 changed files with 214 additions and 59 deletions
@@ -400,6 +400,10 @@ class CustomerController extends SuperController
public function cardChecker(Request $request){
if (!app(ConfigService::class)->isJibitEnabled()) {
throw new UnprocessableEntityHttpException('سرویس احراز هویت جیبیت غیرفعال است.');
}
if (strlen($request->cart_number) != 16) {
throw new UnprocessableEntityHttpException('شماره کارت اشتباه می‌باشد.');
}
+2 -2
View File
@@ -193,7 +193,7 @@ class AuthController extends Controller
} while (Customer::where('referral_id', $uniqueId)->exists());
$data['referral_id'] = $uniqueId;
if (isset($data['n_code']) && $cfg->getBool('register_jibit_verify', false)) {
if (isset($data['n_code']) && $cfg->isRegisterJibitVerifyEnabled()) {
$existing = Customer::where('n_code', $data['n_code'])->first();
if (!empty($existing)) {
return response()->json(['message' => 'کد ملی تکراری است'], 422);
@@ -247,7 +247,7 @@ class AuthController extends Controller
}
}
if ($cfg->getBool('register_jibit_verify', false) && isset($customer->is_legal)) {
if ($cfg->isRegisterJibitVerifyEnabled() && isset($customer->is_legal)) {
$message = $customer->is_legal === 1
? 'احرازهویت برای ' . $customer->name . ' با کد ' . $customer->id . ' انجام شد.'
: 'احرازهویت برای ' . $customer->name . ' با کد ' . $customer->id . ' تأیید نشد لطفا از پنل ادمین اقدام فرمایید.';
+8
View File
@@ -734,6 +734,10 @@ class Controller extends BaseController
public function jibitNcode($nationalCode, $mobile)
{
if (!app(ConfigService::class)->isJibitEnabled()) {
return ['matched' => false, 'message' => 'سرویس احراز هویت جیبیت غیرفعال است.'];
}
$counter = Cnt::where('key', 'jibit_counter')->first();
if (empty($counter)) {
$counter = new Cnt();
@@ -784,6 +788,10 @@ class Controller extends BaseController
public static function jibit($method, $p1 = null, $p2 = null, $p3 = null)
{
if (!app(ConfigService::class)->isJibitEnabled()) {
throw new UnprocessableEntityHttpException('سرویس احراز هویت جیبیت غیرفعال است.');
}
$counter = Cnt::where('key', 'jibit_counter')->first();
if(empty($counter)){
$counter = new Cnt();
@@ -206,7 +206,7 @@ class CustomerController extends Controller
// $tah = new TahesabController();
// $tah->store(["customer_id" => $customer->id]);
$cfg = app(ConfigService::class);
if (!$wasLegal && $cfg->getBool('register_jibit_verify', false)) {
if (!$wasLegal && $cfg->isRegisterJibitVerifyEnabled()) {
$response = $this->jibitNcode($customer->n_code, $customer->mobile);
if (isset($response['matched']) and $response['matched'] == true) {
@@ -235,7 +235,7 @@ class CustomerController extends Controller
}
} elseif (!$customer->is_legal && !$cfg->getBool('register_jibit_verify', false)) {
} elseif (!$customer->is_legal && !$cfg->isRegisterJibitVerifyEnabled()) {
return response()->json([
'message' => 'اطلاعات شما با موفقیت ثبت شد. لطفاً برای تکمیل احراز هویت با پشتیبانی یا مدیر سامانه تماس بگیرید.',
], 422);
@@ -219,7 +219,12 @@ class PargarInboundController extends Controller
], 422);
}
$this->sync()->confirmOrders($orderIds);
$result = $this->sync()->confirmOrders($orderIds);
// سند در پرگار ثبت شد → ماندهٔ معطل‌مانده را همگام کن
foreach ($result['customer_ids'] ?? [] as $customerId) {
$this->inboundWallet()->queueWalletSyncAfterResponse((int) $customerId);
}
return response()->json([
'success' => true,
@@ -243,37 +248,41 @@ class PargarInboundController extends Controller
? Customer::where('pargar_account_id', $accountId)->first()
: null;
// فقط لاگ — اعمال خودکار روی کیف کاملاً خاموش است
// اگر هنوز موجودی بالا می‌رود = worker/php قدیمی هنوز زنده است → ری‌استارت الزامی
// فقط لاگ — replay تاریخچه روی کیف خاموش است؛ بعد از ذخیره، مانده به‌صورت خودکار هم‌تراز می‌شود
PargarSyncLog::create([
'customer_id' => $customer?->id,
'amount' => $summary['amount'],
'desc' => $customer
? ('[no-apply] '.($summary['desc'] !== '' ? $summary['desc'] : 'دریافت اسناد از پرگار'))
: '[no-apply] دریافت اسناد از پرگار — مشتری یافت نشد',
? ($summary['desc'] !== '' ? $summary['desc'] : 'دریافت اسناد از پرگار')
: 'دریافت اسناد از پرگار — مشتری یافت نشد',
'type' => PargarSyncService::TYPE_DOCUMENT,
'sync_status' => PargarSyncService::STATUS_SUCCESS,
'pargar_ref' => $accountId !== '' ? $accountId : null,
'data' => json_encode(array_merge($data, [
'_summary' => $summary,
'wallet_applied' => false,
'auto_apply_disabled' => true,
'code_build' => 'no-apply-v2',
'code_build' => 'docs-then-mande-auto',
]), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
]);
\Illuminate\Support\Facades\Log::info('Pargar documents stored without wallet apply', [
\Illuminate\Support\Facades\Log::info('Pargar documents stored; queue auto mande sync', [
'account_id' => $accountId,
'customer_id' => $customer?->id,
'tx_count' => is_array($data['transactions'] ?? null) ? count($data['transactions']) : 0,
]);
// به‌محض ورود اسناد → ack + اعمال آخرین مانده
if ($customer) {
$this->inboundWallet()->queueWalletSyncAfterResponse((int) $customer->id);
}
return response()->json([
'success' => true,
'message' => 'اسناد ثبت شد بدون تغییر کیف (no-apply-v2)',
'message' => 'اسناد ثبت شد؛ همگام‌سازی مانده در صف است',
'data' => [
'wallet_applied' => false,
'build' => 'no-apply-v2',
'auto_mande_queued' => (bool) $customer,
'build' => 'docs-then-mande-auto',
],
]);
}
@@ -285,7 +294,6 @@ class PargarInboundController extends Controller
$customer = Customer::where('pargar_account_id', $accountId)->first();
// اگر مشتری هنوز وارد نشده، مانده را با pargar_ref نگه می‌داریم تا بعد از import/claim اعمال شود
$goldInv = isset($data['GoldInv']) ? (float) str_replace('/', '.', (string) $data['GoldInv']) : 0;
$rialInv = isset($data['RialInv']) ? (float) str_replace('/', '.', (string) $data['RialInv']) : 0;
@@ -310,7 +318,6 @@ class PargarInboundController extends Controller
]);
}
// فقط مقایسه برای لاگ — اعمال روی کیف فقط با دکمه ادمین / صف auto پس از import-claim
$ownerType = (int) ($customer->type ?? 2);
$walletInterface = new WalletInterface($customer->id, $ownerType);
$wallet = $walletInterface->wallet;
@@ -321,9 +328,13 @@ class PargarInboundController extends Controller
$rialDiff = $rialInv - $appCash;
$hasDiff = abs($goldDiff) >= 0.01 || abs($rialDiff) >= 1;
$desc = $hasDiff
? 'اختلاف مانده پرگار با اپ — کیف اپ دست نخورده'
: 'دریافت مانده از پرگار — بدون اختلاف';
$pendingOutbound = $this->inboundWallet()->hasPendingOutboundToPargar($customer);
$desc = $pendingOutbound
? 'مانده دریافت شد — منتظر ثبت سند پنل در پرگار'
: ($hasDiff
? 'اختلاف مانده پرگار با اپ — در صف همگام‌سازی خودکار'
: 'دریافت مانده از پرگار — بدون اختلاف');
PargarSyncLog::create([
'customer_id' => $customer->id,
@@ -338,18 +349,22 @@ class PargarInboundController extends Controller
'gold_diff' => $goldDiff,
'rial_diff' => $rialDiff,
'wallet_adjusted' => false,
'awaiting_outbound_confirm' => $pendingOutbound,
]), JSON_UNESCAPED_UNICODE),
]);
// اولین مانده برای مشتریِ هنوز واردنشده به اپ — یک‌بار هم‌تراز کن
if ($customer->last_seen_at === null && $hasDiff) {
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
}
// همیشه صف همگام‌سازی: اگر سند pending نباشد اعمال می‌شود؛ وگرنه بعد از تأیید سند اعمال می‌شود
$this->inboundWallet()->queueWalletSyncAfterResponse((int) $customer->id);
return response()->json([
'success' => true,
'message' => 'مانده مشتری با موفقیت دریافت شد',
'data' => null,
'message' => $pendingOutbound
? 'مانده ذخیره شد؛ بعد از ثبت سند در پرگار خودکار سینک می‌شود'
: 'مانده دریافت شد؛ همگام‌سازی خودکار در صف است',
'data' => [
'awaiting_outbound_confirm' => $pendingOutbound,
'auto_sync_queued' => true,
],
]);
}
@@ -11,6 +11,7 @@ use Illuminate\Http\Request;
use Hekmatinasser\Verta\Verta;
use Carbon\Carbon;
use App\Models\Log;
use App\Services\ConfigService;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
class BankAccountController extends SuperController
@@ -55,7 +56,7 @@ class BankAccountController extends SuperController
}
//with verification in jibit
if($this->Config('jibit_verification') == 'true') {
if (app(ConfigService::class)->isJibitCardVerificationEnabled()) {
$p1 = $apiData['iban'] = preg_replace('/\D/', '', $request->cart_number);
$p2 = $apiData['nationalCode'] = $user->n_code;
$p3 = $apiData['birthDate'] = Verta::instance(Carbon::parse($request->birth_date ?? $user->birth_date))->format('Ymd');
+21
View File
@@ -735,6 +735,27 @@ class ConfigService
return $this->getBool('enable_conditional_order', false);
}
/**
* سوییچ اصلی جیبیت (can_edit=0، پیش‌فرض روشن).
* خاموش = هیچ فراخوانی احراز هویت به جیبیت انجام نمی‌شود.
*/
public function isJibitEnabled(): bool
{
return $this->getBool('jibit_enabled', true);
}
/** تطبیق کدملی/موبایل در ثبت‌نام و پروفایل — فقط اگر جیبیت سراسری و این سوییچ روشن باشد */
public function isRegisterJibitVerifyEnabled(): bool
{
return $this->isJibitEnabled() && $this->getBool('register_jibit_verify', false);
}
/** تطبیق کارت بانکی با جیبیت — فقط اگر جیبیت سراسری و این سوییچ روشن باشد */
public function isJibitCardVerificationEnabled(): bool
{
return $this->isJibitEnabled() && $this->getBool('jibit_verification', false);
}
/** کارمزد ریالی هر بار ثبت تریگر سفارش شرطی */
public function conditionalOrderFee(): int
{
+64 -28
View File
@@ -11,13 +11,15 @@ use Rooyesh\Wallet\interfaces\WalletInterface;
use Rooyesh\Wallet\models\Wallet;
/**
* همگام‌سازی کیف با پرگار — فقط با دکمهٔ ادمین.
* همگام‌سازی کیف با پرگار.
*
* قواعد سخت:
* 1) webhook اسناد (/documents) هرگز کیف را عوض نمی‌کند — فقط لاگ.
* 2) دکمه: دسته‌های سند را فقط «مصرف‌شده» علامت می‌زند (بدون دلتا روی کیف)،
* سپس مانده (mande) را به‌عنوان منبع حقیقت روی کیف می‌نشاند.
* 3) اعمال تک‌تک سطرهای تاریخچهٔ پرگار ممنوع است — باعث تورم/تایم‌اوت می‌شود.
* 1) webhook اسناد inbound فقط لاگ می‌کند؛ کیف را با replay تاریخچه عوض نمی‌کند.
* 2) مانده برای مشتریانی که سند ارسالیِ pending به پرگار ندارند → خودکار روی کیف ست می‌شود.
* 3) اگر سند پنل هنوز در پرگار ثبت نشده، مانده اعمال نمی‌شود؛ بعد از تأیید ثبت در پرگار،
* ماندهٔ جدید خودکار سینک می‌شود (تا موجودی مشتری بی‌دلیل کم/زیاد نشود).
* 4) دکمهٔ ادمین می‌تواند force کند.
* 5) اعمال تک‌تک سطرهای تاریخچهٔ پرگار ممنوع است.
*/
class PargarInboundWalletService
{
@@ -44,19 +46,36 @@ class PargarInboundWalletService
}
/**
* دکمهٔ ادمین:
* ۱) علامت مصرف دسته‌های سند (بدون نوشتن روی کیف)
* آیا سندی از پنل در صف ثبت داخل پرگار است؟ (کیف یا سفارش)
* در این حالت مانده را اعمال نمی‌کنیم تا بعد از ثبت سند، ماندهٔ درست بیاید.
*/
public function hasPendingOutboundToPargar(Customer $customer): bool
{
return PargarSyncLog::query()
->where('customer_id', $customer->id)
->whereIn('type', [
PargarSyncService::TYPE_WALLET_CASH,
PargarSyncService::TYPE_WALLET_GOLD,
PargarSyncService::TYPE_ORDER,
])
->where('sync_status', PargarSyncService::STATUS_PENDING)
->exists();
}
/**
* دکمهٔ ادمین / همگام‌سازی خودکار:
* ۱) علامت مصرف دسته‌های سند inbound (بدون نوشتن روی کیف)
* ۲) هم‌ترازی مانده با آخرین لاگ mande
*
* @param bool $forceMande ادمین: حتی با سند ارسالی pending
* @param bool $auto مسیر خودکار: سند ارسالی pending محترم بماند
* @return array{
* documents: array{applied: int, skipped: int, batches: int, acknowledged: int, errors: list<string>},
* mande: array{applied: bool, gold_delta?: float, rial_delta?: float, message: string}
* }
*/
public function syncWalletFromPargar(Customer $customer, bool $forceMande = false): array
public function syncWalletFromPargar(Customer $customer, bool $forceMande = false, bool $auto = false): array
{
unset($forceMande); // مانده در دکمه همیشه force می‌شود
if (self::isApplyFrozen()) {
return [
'documents' => [
@@ -88,11 +107,19 @@ class PargarInboundWalletService
}
try {
// ۱) تاریخچهٔ اسناد را روی کیف replay نکن — فقط مصرف‌شده علامت بزن
$this->attachOrphanDocumentLogs($customer);
$this->attachOrphanMandeLogs($customer);
// ۱) تاریخچهٔ اسناد inbound را روی کیف replay نکن — فقط مصرف‌شده علامت بزن
$documents = $this->acknowledgePendingDocumentBatches($customer);
// ۲) منبع حقیقت موجودی = ماندهٔ پرگار
$mande = $this->applyLatestMandeDifference($customer, force: true);
// خودکار: اگر سند پنل هنوز در پرگار ثبت نشده → اعمال نکن (force=false)
// ادمین: force تا بتوان اصلاح کرد
$mande = $this->applyLatestMandeDifference(
$customer,
force: $auto ? false : ($forceMande || true)
);
if (($documents['acknowledged'] ?? 0) > 0 || ($mande['applied'] ?? false)) {
$this->walletBroadcast->broadcastForCustomer(
@@ -251,11 +278,11 @@ class PargarInboundWalletService
*/
public function applyLatestMandeDifference(Customer $customer, bool $force = false): array
{
// دکمهٔ ادمین (force): حتی با سند pending خروجی، مانده را هم‌تراز کن تا کیف متورم اصلاح شود
if (! $force && $this->hasPendingOutboundWalletSync((int) $customer->id)) {
// دکمهٔ ادمین (force): حتی با سند pending خروجی، مانده را هم‌تراز کن
if (! $force && $this->hasPendingOutboundToPargar($customer)) {
return [
'applied' => false,
'message' => 'سند ارسالی از پنل هنوز تأیید نشده — مانده اعمال نشد تا شارژ برنگردد',
'message' => 'سند پنل هنوز در پرگار ثبت نشده — مانده بعد از ثبت سند اعمال می‌شود',
];
}
@@ -374,15 +401,6 @@ class PargarInboundWalletService
return round((float) $query->sum('gold'), 3);
}
private function hasPendingOutboundWalletSync(int $customerId): bool
{
return PargarSyncLog::query()
->where('customer_id', $customerId)
->whereIn('type', [PargarSyncService::TYPE_WALLET_CASH, PargarSyncService::TYPE_WALLET_GOLD])
->where('sync_status', PargarSyncService::STATUS_PENDING)
->exists();
}
private function parseNumber(mixed $value): float
{
if ($value === null || $value === '') {
@@ -393,7 +411,7 @@ class PargarInboundWalletService
}
/**
* پس از اتصال کد پرگار / claim ثبت‌نام — همگام‌سازی کیف در پس‌زمینه.
* همگام‌سازی خودکار در پس‌زمینه — بعد از مانده / تأیید سند در پرگار / import.
*/
public function queueWalletSyncAfterResponse(int $customerId): void
{
@@ -403,10 +421,8 @@ class PargarInboundWalletService
return;
}
$this->attachOrphanMandeLogs($customer);
try {
$this->syncWalletFromPargar($customer, true);
$this->syncWalletFromPargar($customer, forceMande: false, auto: true);
} catch (\Throwable $e) {
Log::warning('Pargar auto wallet sync failed', [
'customer_id' => $customerId,
@@ -435,4 +451,24 @@ class PargarInboundWalletService
})
->update(['customer_id' => $customer->id]);
}
/**
* اسناد یتیم (قبل از لینک مشتری) را به customer_id وصل می‌کند.
*/
public function attachOrphanDocumentLogs(Customer $customer): int
{
if (! $customer->hasValidPargarAccountId()) {
return 0;
}
$accountId = trim((string) $customer->pargar_account_id);
return PargarSyncLog::query()
->where('type', PargarSyncService::TYPE_DOCUMENT)
->where('pargar_ref', $accountId)
->where(function ($q) {
$q->whereNull('customer_id')->orWhere('customer_id', 0);
})
->update(['customer_id' => $customer->id]);
}
}
+17 -1
View File
@@ -303,6 +303,7 @@ class PargarSyncService
public function confirmOrders(array $orderIds): array
{
$confirmed = 0;
$customerIds = [];
foreach ($orderIds as $orderId) {
$id = trim((string) $orderId);
@@ -325,6 +326,9 @@ class PargarSyncService
if ($log) {
$this->markSuccess($log, $id, ['OrderId' => $id]);
if ($log->customer_id) {
$customerIds[(int) $log->customer_id] = true;
}
$confirmed++;
}
@@ -345,6 +349,9 @@ class PargarSyncService
if ($log) {
$this->markSuccess($log, $id, ['OrderId' => $id]);
if ($log->customer_id) {
$customerIds[(int) $log->customer_id] = true;
}
$confirmed++;
}
@@ -364,10 +371,19 @@ class PargarSyncService
$this->markSuccess($log, (string) $order->id, ['OrderId' => (string) $order->id]);
}
if ($order->customer_id) {
$customerIds[(int) $order->customer_id] = true;
} elseif ($log?->customer_id) {
$customerIds[(int) $log->customer_id] = true;
}
$confirmed++;
}
return ['confirmed' => $confirmed];
return [
'confirmed' => $confirmed,
'customer_ids' => array_keys($customerIds),
];
}
public function retry(PargarSyncLog $log): array