| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- <?php
- namespace App\Services\WorldCup;
- use App\Services\WorldCup\Repositories\SqlWorldCupReferralRepository;
- use App\Services\WorldCup\Repositories\WorldCupReferralRepositoryInterface;
- use Log;
- class WorldCupReferralRewardService
- {
- private $repository;
- private $referralService;
- public function __construct(
- WorldCupReferralRepositoryInterface $repository = null,
- WorldCupReferralService $referralService = null
- ) {
- $this->repository = $repository ?: new SqlWorldCupReferralRepository();
- $this->referralService = $referralService ?: new WorldCupReferralService();
- }
- public function getInviteState(int $userId): array
- {
- $state = $this->repository->ensureUserState($userId);
- return [
- 'invite_code' => $state['invite_code'],
- 'referral_bound' => !empty($state['referred_by_user_id']),
- 'referred_by_user_id' => $state['referred_by_user_id'] ?? null,
- ];
- }
- public function bindInvite(int $inviteeId, string $inviteCode, string $bindType = 'manual'): array
- {
- $inviteCode = trim($inviteCode);
- if ($inviteCode === '') {
- return $this->fail('Invite code is required');
- }
- $this->repository->ensureUserState($inviteeId);
- $referrer = $this->repository->findUserByInviteCode($inviteCode);
- if (!$referrer) {
- return $this->fail('Invite code not found');
- }
- $referrerId = (int)$referrer['user_id'];
- if ($referrerId === $inviteeId) {
- return $this->fail('Cannot invite yourself');
- }
- $exists = $this->repository->findReferralByInvitee($inviteeId);
- if ($exists) {
- return [
- 'success' => true,
- 'status' => 'exists',
- 'data' => $exists,
- ];
- }
- $referral = $this->repository->bindReferral($referrerId, $inviteeId, $bindType);
- return [
- 'success' => true,
- 'status' => 'created',
- 'data' => $referral,
- ];
- }
- public function handleFirstDeposit(int $userId, float $payAmt, string $orderSn): array
- {
- if (!$this->repository->isFirstSuccessfulOrder($userId, $orderSn)) {
- Log::info('Order is not the first successful deposit', [
- 'user_id' => $userId,
- 'order_sn' => $orderSn,
- ]);
- return $this->ok('not_first_deposit');
- }
- $referral = $this->repository->findReferralByInvitee($userId);
- if (!$referral) {
- Log::info('No referral found for user', [
- 'user_id' => $userId,
- 'order_sn' => $orderSn,
- ]);
- return $this->ok('not_bound');
- }
- $exists = $this->repository->findRewardByInvitee($userId);
- if ($exists) {
- Log::info('Reward already exists for user', [
- 'user_id' => $userId,
- 'order_sn' => $orderSn,
- 'reward_id' => $exists['reward_id'],
- ]);
- return [
- 'success' => true,
- 'status' => 'exists',
- 'data' => $exists,
- ];
- }
- $firstDepositAmount = (int)round($payAmt * 100);
- $calculation = $this->referralService->calculateReward($firstDepositAmount);
- if (!$calculation['qualifies']) {
- Log::info('User is not qualified for reward', [
- 'user_id' => $userId,
- 'order_sn' => $orderSn,
- ]);
- return $this->ok('not_qualified');
- }
- $risk = $this->scoreRisk((int)$referral['referrer_id'], $userId);
- $reward = $this->repository->createReward([
- 'referrer_id' => (int)$referral['referrer_id'],
- 'invitee_id' => $userId,
- 'first_deposit_order_sn' => $orderSn,
- 'first_deposit_amt' => $firstDepositAmount,
- 'reward_each' => $calculation['reward_each'],
- 'total_liability' => $calculation['total_liability'],
- 'risk_score' => $risk['risk_score'],
- 'risk_level' => $risk['risk_level'],
- 'signals' => json_encode($risk['signals']),
- 'status' => 'reviewing',
- ]);
- return [
- 'success' => true,
- 'status' => 'created',
- 'data' => $reward,
- ];
- }
- public function generateMissingRewards(int $limit = 200): array
- {
- $orders = $this->repository->paidOrdersMissingRewards($limit);
- $result = [
- 'checked' => count($orders),
- 'created' => 0,
- 'skipped' => 0,
- ];
- foreach ($orders as $order) {
- $handled = $this->handleFirstDeposit(
- (int)$order['user_id'],
- ((float)$order['amount']) / 100,
- (string)$order['order_sn']
- );
- if (($handled['status'] ?? '') === 'created') {
- $result['created']++;
- } else {
- $result['skipped']++;
- }
- }
- return $result;
- }
- public function inviteLog(int $userId, string $type = 'invited', int $limit = 20): array
- {
- $type = $type === 'deposits' ? 'deposits' : 'invited';
- $limit = $this->normalizeLimit($limit);
- return [
- 'server_time' => time(),
- 'stats' => $this->repository->inviteStats($userId),
- 'type' => $type,
- 'list' => $this->repository->inviteLogs($userId, $type, $limit),
- ];
- }
- public function rewardLog(int $userId, int $limit = 20): array
- {
- $limit = $this->normalizeLimit($limit);
- return [
- 'stats' => $this->repository->rewardStats($userId),
- 'list' => $this->repository->rewardLogs($userId, $limit),
- ];
- }
- private function scoreRisk(int $referrerId, int $inviteeId): array
- {
- $referrer = $this->repository->findUserState($referrerId) ?: [];
- $invitee = $this->repository->findUserState($inviteeId) ?: [];
- $signals = [];
- $score = 0;
- if (!empty($referrer['device_fp'])
- && !empty($invitee['device_fp'])
- && $referrer['device_fp'] === $invitee['device_fp']) {
- $signals[] = 'same_device';
- $score += 40;
- }
- if (!empty($referrer['pay_account_hash'])
- && !empty($invitee['pay_account_hash'])
- && $referrer['pay_account_hash'] === $invitee['pay_account_hash']) {
- $signals[] = 'same_payment';
- $score += 40;
- }
- if (!empty($referrer['signup_ip'])
- && !empty($invitee['signup_ip'])
- && $referrer['signup_ip'] === $invitee['signup_ip']) {
- $signals[] = 'same_ip';
- $score += 20;
- }
- return [
- 'risk_score' => $score,
- 'risk_level' => $score >= 40 ? 'high' : ($score >= 20 ? 'medium' : 'low'),
- 'signals' => $signals,
- ];
- }
- private function normalizeLimit(int $limit): int
- {
- if ($limit <= 0) {
- return 20;
- }
- return min($limit, 100);
- }
- private function ok(string $status): array
- {
- return [
- 'success' => true,
- 'status' => $status,
- 'data' => [],
- ];
- }
- private function fail(string $message): array
- {
- return [
- 'success' => false,
- 'message' => $message,
- 'data' => [],
- ];
- }
- }
|