exists()) { return $code; } } throw new \RuntimeException('Failed to generate unique reward code'); } /** * Redeem reward by code. */ public static function redeem(string $code, string $GlobalUID, string $clientIp = '', int $UserID = 0): array { $code = strtoupper($code); $now = Carbon::now(); if (!$UserID) { $user = GlobalUserInfo::getGameUserInfo('GlobalUID', $GlobalUID); if (!$user) { throw new \InvalidArgumentException('user_not_exist'); } $UserID = $user->UserID; } return DB::connection('write')->transaction(function () use ($code, $UserID, $GlobalUID, $clientIp, $now) { $rewardCode = RewardCode::where('code', $code)->lockForUpdate()->first(); if (!$rewardCode || $rewardCode->status != self::STATUS_ACTIVE) { throw new \InvalidArgumentException('code_not_found'); } if ($rewardCode->expire_at && $now->gt(Carbon::parse($rewardCode->expire_at))) { throw new \InvalidArgumentException('code_expired'); } if ($rewardCode->claimed_count >= $rewardCode->total_count) { throw new \InvalidArgumentException('code_limit_reached'); } if ($rewardCode->claimed_amount >= $rewardCode->total_amount) { throw new \InvalidArgumentException('code_amount_exhausted'); } if (RewardCodeClaim::where('reward_code_id', $rewardCode->id)->where('UserID', $UserID)->exists()) { throw new \InvalidArgumentException('already_claimed'); } $remainingCount = $rewardCode->total_count - $rewardCode->claimed_count; $remainingAmount = $rewardCode->total_amount - $rewardCode->claimed_amount; if ($remainingAmount < $rewardCode->min_amount) { throw new \InvalidArgumentException('code_amount_exhausted'); } // fixed-point to support decimals (<1) $scale = 100; $min = (int)round($rewardCode->min_amount * $scale); $max = (int)round($rewardCode->max_amount * $scale); $remainingScaled = (int)floor($remainingAmount * $scale); $minReserve = ($remainingCount - 1) * $min; $maxPossible = $remainingScaled - $minReserve; $maxPossible = max($min, min($max, $maxPossible)); $reward = mt_rand($min, $maxPossible) / $scale; $reward = round($reward, 2); $rewardCode->claimed_count += 1; $rewardCode->claimed_amount = round($rewardCode->claimed_amount + $reward, 2); $rewardCode->save(); RewardCodeClaim::create([ 'reward_code_id' => $rewardCode->id, 'code' => $rewardCode->code, 'UserID' => $UserID, 'GlobalUID' => $GlobalUID, 'amount' => $reward, 'client_ip' => $clientIp, ]); $scoreAmount = (int)round($reward * NumConfig::NUM_VALUE); OuroGameService::AddScore($UserID, $scoreAmount, OuroGameService::REASON_RewardCode,false); return [ 'code' => $rewardCode->code, 'amount' => $reward, 'claimed_count' => $rewardCode->claimed_count, 'claimed_amount' => $rewardCode->claimed_amount, 'total_count' => $rewardCode->total_count, 'total_amount' => $rewardCode->total_amount, 'expire_at' => $rewardCode->expire_at, ]; }); } }