Files
boursecu_server/app/Services/ReferralGiftService.php
T
2026-08-03 11:51:16 +03:30

381 lines
14 KiB
PHP

<?php
namespace App\Services;
use App\Http\Controllers\Admin\TahesabController;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\Order;
use App\Models\Setting;
use App\Models\TransactionsAdmin;
use App\Models\Withdraw;
use Illuminate\Support\Facades\Log;
use Rooyesh\Wallet\interfaces\TransactionInterface;
use Rooyesh\Wallet\interfaces\WalletInterface;
use Rooyesh\Wallet\models\Transaction;
class ReferralGiftService
{
public function isDeferredToFirstOrder(): bool
{
return app(ConfigService::class)->getBool('referral_gift_on_first_order', false);
}
/**
* پاداش معرف/معرف‌شونده هنگام ثبت‌نام یا تأیید احراز — مگر اینکه به اولین سفارش موکول شده باشد.
*/
public function tryGiveOnRegistration(
Customer $customer,
?Customer $referrer,
bool $referrerGiftAllowed,
bool $allowInvitedGiftWithoutReferrer = true
): void {
if ($this->isDeferredToFirstOrder()) {
return;
}
$this->depositGifts($customer, $referrer, $referrerGiftAllowed, $allowInvitedGiftWithoutReferrer);
}
/**
* پاداش معرف/معرف‌شونده پس از اولین سفارش — فقط وقتی تنظیم referral_gift_on_first_order روشن است.
*
* توجه: has_page_earn_money فقط نمایش منوی «کسب درآمد» در اپ است؛ واریز پاداش را قطع نمی‌کند.
* اگر اولین سفارش قبلاً ثبت شده و پاداش نرفته باشد، با سفارش‌های بعدی هم (تا وقتی تکراری نباشد) جبران می‌شود.
*/
public function tryGiveOnFirstOrder(Customer $customer): void
{
if (!$this->isDeferredToFirstOrder()) {
return;
}
if (! Order::where('customer_id', $customer->id)->exists()) {
return;
}
$referrer = $customer->referrer();
$referrerGiftAllowed = $this->resolveReferrerGiftPermission($referrer);
$this->depositGifts($customer, $referrer, $referrerGiftAllowed, true);
}
/**
* واریز معوق/دستی پاداش (مثلاً جبران وقتی گیت اشتباه جلوی واریز را گرفته).
* Idempotent — اگر قبلاً «پاداش معرفی» (یا عنوان قدیمی) خورده باشد دوباره نمی‌زند.
*/
public function givePendingGifts(Customer $customer, bool $requireExistingOrder = false): void
{
if ($requireExistingOrder && ! Order::where('customer_id', $customer->id)->exists()) {
return;
}
$referrer = $customer->referrer();
$referrerGiftAllowed = $this->resolveReferrerGiftPermission($referrer);
$this->depositGifts($customer, $referrer, $referrerGiftAllowed, true);
}
public function resolveReferrerGiftPermission(?Customer $referrer): bool
{
if ($referrer === null) {
return false;
}
$referralLimit = Setting::where('key', 'referral_limit')->first();
$limitValue = $referralLimit->value ?? null;
if (
$referralLimit
&& $referralLimit->is_active
&& $limitValue !== null
&& $limitValue !== ''
&& is_numeric($limitValue)
) {
$totalReferrals = Customer::where('referral_to_id', $referrer->referral_id)
->where('is_legal', '!=', 0)
->count();
return $totalReferrals < (int) $limitValue;
}
return true;
}
private function depositGifts(
Customer $customer,
?Customer $referrer,
bool $referrerGiftAllowed,
bool $allowInvitedGiftWithoutReferrer
): void {
$config = app(ConfigService::class);
$giftForInviter = (float) $config->get('gift_for_referrer', 0);
$giftForInvited = (float) $config->get('gift_for_referral', 0);
$companyName = $config->getString('name_fa');
$companyUrl = $config->resolvePublicSiteUrl(request());
if ($referrer && $referrerGiftAllowed && $giftForInviter > 0 && !$this->referrerGiftAlreadyGiven($customer, $referrer)) {
$this->giveReferrerGift($customer, $referrer, $giftForInviter, $companyName, $companyUrl, $config);
}
$canGiveInvited = $giftForInvited > 0
&& !$this->invitedGiftAlreadyGiven($customer)
&& ($allowInvitedGiftWithoutReferrer || $referrer !== null);
if ($canGiveInvited) {
$this->giveInvitedGift($customer, $giftForInvited, $companyName, $companyUrl, $config);
}
}
private function referrerGiftAlreadyGiven(Customer $invited, Customer $referrer): bool
{
return TransactionsAdmin::where('customer_id', $invited->id)
->where('customer_to_id', $referrer->id)
->whereIn('note', Controller::accountingReferralRewardDescAliases())
->exists();
}
private function invitedGiftAlreadyGiven(Customer $customer): bool
{
return TransactionsAdmin::where('customer_id', $customer->id)
->where('note', 'پاداش بابت ثبت نام')
->exists();
}
/**
* وقتی price_provider=montzar و کلید referral_gift_buy_from_montzar روشن است،
* قبل از واریز پاداش همان وزن از همکار خرید می‌شود.
* خاموش بودن کلید = روال قبلی (واریز محلی بدون خرید همکار).
* شکست خرید → واریز نمی‌شود (ثبت‌نام/سفارش خراب نمی‌شود؛ با givePendingGifts قابل جبران است).
*/
private function ensureMontzarCoverPurchase(
float $grams,
Customer $beneficiary,
string $reason,
ConfigService $config
): bool {
if (!$config->usesMontzarPricing()) {
return true;
}
if (!$config->getBool('referral_gift_buy_from_montzar', true)) {
return true;
}
if ($grams <= 0) {
return true;
}
$livePrice = (float) $config->Config('montzar_live_price');
$data = [
'amount' => $grams,
'direction' => 1, // خرید پوشش از همکار
'customer' => [
'name' => trim($reason . ' - ' . (string) ($beneficiary->name ?? '')),
],
'total_price' => (int) round($livePrice * $grams),
];
try {
$parsed = app(MontzarService::class)->submitOrder($data);
Log::info('Referral gift covered by Montzar buy', [
'grams' => $grams,
'reason' => $reason,
'beneficiary_id' => $beneficiary->id,
'provider_id' => $parsed['provider_id'] ?? null,
]);
return true;
} catch (\Throwable $e) {
report($e);
Log::error('Referral gift blocked: Montzar cover buy failed', [
'grams' => $grams,
'reason' => $reason,
'beneficiary_id' => $beneficiary->id,
'error' => $e->getMessage(),
]);
return false;
}
}
private function giveReferrerGift(
Customer $customer,
Customer $referrer,
float $giftForInviter,
string $companyName,
string $companyUrl,
ConfigService $config
): void {
$rewardDesc = Controller::accountingReferralRewardDesc();
if (!$this->ensureMontzarCoverPurchase($giftForInviter, $referrer, $rewardDesc, $config)) {
return;
}
$withdraw = Withdraw::create([
'user_id' => $customer->id,
'customer_id' => $referrer->id,
'cash' => 0,
'gold' => $giftForInviter,
'sts' => 5,
]);
$walletInterface = new WalletInterface($referrer->id, 2);
new TransactionInterface($walletInterface, [
'cash' => 0,
'gold' => $giftForInviter,
'expire_at' => now(),
'active' => true,
'title' => $rewardDesc,
'withdraw_id' => $withdraw->id,
'start_at' => now(),
], 8, true, false);
$transaction = Transaction::where('customer_id', $referrer->id)->latest()->first();
TransactionsAdmin::create([
'customer_id' => $customer->id,
'customer_to_id' => $referrer->id,
'user_id' => $customer->id,
'wallet' => null,
'transaction_id' => $transaction->id,
'cash' => 0,
'note' => $rewardDesc,
'type' => 1,
'gold' => $giftForInviter,
'balance_cash' => 0,
'withdraw_id' => $withdraw->id,
]);
if ($config->isTahesabAccounting()) {
try {
app(TahesabController::class)->referrerGoldReward(
$customer,
$giftForInviter,
$rewardDesc
);
} catch (\Exception $e) {
report($e);
}
}
if ($config->isZarinAccounting()) {
try {
app(\App\Http\Controllers\Admin\ZarinController::class)->referrerGoldReward(
$customer,
$giftForInviter,
$rewardDesc
);
} catch (\Exception $e) {
report($e);
}
}
try {
app(PargarSyncService::class)->queueWalletDocument(
$referrer,
'gold',
(float) $giftForInviter,
[
'withdraw_id' => $withdraw->id,
'note' => $rewardDesc,
'source' => 'referral_gift',
]
);
} catch (\Throwable $e) {
report($e);
}
$currentGold = Controller::formatSmsGoldWeight((float) $walletInterface->wallet->refresh()->gold);
$message = "سلام {$referrer->name} عزیز،\n\n{$giftForInviter} گرم طلا بابت معرفی {$customer->name} به حساب شما واریز شد.\nموجودی فعلی: {$currentGold} گرم.\n\nاطلاعات بیشتر: {$companyUrl}\n\nبا احترام،\n{$companyName}";
Controller::Send($referrer->mobile, $message);
}
private function giveInvitedGift(
Customer $customer,
float $giftForInvited,
string $companyName,
string $companyUrl,
ConfigService $config
): void {
if (!$this->ensureMontzarCoverPurchase($giftForInvited, $customer, 'پاداش بابت ثبت نام', $config)) {
return;
}
$withdraw = Withdraw::create([
'user_id' => $customer->id,
'customer_id' => $customer->id,
'cash' => 0,
'gold' => $giftForInvited,
'sts' => 5,
]);
$walletInterface = new WalletInterface($customer->id, 2);
new TransactionInterface($walletInterface, [
'cash' => 0,
'gold' => $giftForInvited,
'expire_at' => now(),
'active' => true,
'title' => 'پاداش بابت ثبت نام',
'withdraw_id' => $withdraw->id,
'start_at' => now(),
], 8, true, false);
$transaction = Transaction::where('customer_id', $customer->id)->latest()->first();
TransactionsAdmin::create([
'customer_id' => $customer->id,
'customer_to_id' => $customer->id,
'user_id' => $customer->id,
'wallet' => null,
'transaction_id' => $transaction->id,
'cash' => 0,
'note' => 'پاداش بابت ثبت نام',
'type' => 1,
'gold' => $giftForInvited,
'balance_cash' => 0,
'withdraw_id' => $withdraw->id,
]);
if ($config->isTahesabAccounting()) {
try {
app(TahesabController::class)->goldReward([
'customer_id' => $customer->id,
'value' => $giftForInvited,
'desc' => 'پاداش بابت ثبت نام',
]);
} catch (\Exception $e) {
report($e);
}
}
if ($config->isZarinAccounting()) {
try {
app(\App\Http\Controllers\Admin\ZarinController::class)->goldReward([
'customer_id' => $customer->id,
'value' => $giftForInvited,
'desc' => 'پاداش بابت ثبت نام',
]);
} catch (\Exception $e) {
report($e);
}
}
try {
app(PargarSyncService::class)->queueWalletDocument(
$customer,
'gold',
(float) $giftForInvited,
[
'withdraw_id' => $withdraw->id,
'note' => 'پاداش بابت ثبت نام',
'source' => 'referral_register_gift',
]
);
} catch (\Throwable $e) {
report($e);
}
$currentGold = Controller::formatSmsGoldWeight((float) $walletInterface->wallet->refresh()->gold);
$message = "سلام {$customer->name} عزیز،\n\n{$giftForInvited} گرم طلا بابت ثبت نام به حساب شما واریز شد.\nموجودی فعلی: {$currentGold} گرم.\n\nاطلاعات بیشتر: {$companyUrl}\n\nبا احترام،\n{$companyName}";
Controller::Send($customer->mobile, $message);
}
}