message = $message; $this->time = $time; $this->user = $user; } /** * Execute the job. * * @return void */ public function handle(MBBank $bank) { if ($this->time < time()) { Event(new Notification_Expiry_Time('Hết thời gian chờ')); } else { $history = $bank->GetTransaction(); $status = 0; $message = ''; if ($history === false) { $login = $bank->Login(); $history = $bank->GetTransaction(); } if ($history) { foreach ($history as $item) { if (empty($item->tracingType) && str_contains($item['description'], $this->message)) { $transaction = str_replace("\\BNK", "", $item['refNo']); // firstOrCreate is atomic against the unique(customer_id, trans, type) index — // a duplicate bank-statement poll (job retried, matched twice) cannot credit twice. $amount = (int) $item['creditAmount']; // type/status/uuid/public_code phải set đủ: unique index là // (customer_id, trans, type) nên type NULL làm chống trùng hụt một nửa, // và kế toán không lọc được dòng tiền vào theo loại khi lên báo cáo. $created = Transaction::firstOrCreate( ['trans' => $transaction, 'customer_id' => $this->user, 'type' => 'auto'], [ 'uuid' => (string) Str::uuid(), 'public_code' => 'DEP-AUTO-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6)), 'amount' => $amount, 'received_amount' => $amount, 'status' => TransactionStatus::COMPLETED->value, 'note' => 'Tự động khớp sao kê MB Bank', ] ); if ($created->wasRecentlyCreated) { Customer::find($this->user)->increment('credit', Customer::creditFor($amount)); Customer::find($this->user)->increment('total', $amount); $message = number_format($item['creditAmount'], 0, ',', '.') . "đ vào tài khoản"; $status = 1; break; } } } } if ($status === 1) { Event(new Notification_Pay_Success("+ $message", $this->user)); } else { dispatch(new GetTransactionPayment($this->message, $this->time, $this->user))->delay(now()->addSeconds(30)); } } } }