Files
clinicpro/src/Subscription/Service/SubscriptionService.php
T

141 lines
4.8 KiB
PHP

<?php
namespace App\Subscription\Service;
use App\Config\Repository\SiteConfigRepository;
use App\Payment\Entity\Payment;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use App\Subscription\Entity\ClinicSubscription;
use App\Subscription\Entity\SubscriptionPlan;
use App\Subscription\Repository\ClinicSubscriptionRepository;
use App\Subscription\Repository\SubscriptionPeriodRepository;
use App\Subscription\Repository\SubscriptionPlanRepository;
class SubscriptionService
{
public function __construct(
private readonly ClinicSubscriptionRepository $subscriptionRepo,
private readonly SubscriptionPlanRepository $planRepo,
private readonly SubscriptionPeriodRepository $periodRepo,
private readonly SiteConfigRepository $configRepo,
) {}
public function getActiveSubscription(string $entityType, int $entityId): ?ClinicSubscription
{
return $this->subscriptionRepo->findActive($entityType, $entityId);
}
/**
* پلن مؤثر: پلن اشتراک فعال، یا در نبود اشتراک، پلن پیش‌فرض «free».
*/
public function getEffectivePlan(string $entityType, int $entityId): ?SubscriptionPlan
{
$subscription = $this->getActiveSubscription($entityType, $entityId);
return $subscription?->getPlan() ?? $this->planRepo->findByName('free');
}
public function hasFeature(string $entityType, int $entityId, string $feature): bool
{
$plan = $this->getEffectivePlan($entityType, $entityId);
return $plan !== null && $plan->hasFeature($feature);
}
public function getSecretaryLimit(string $entityType, int $entityId): int
{
$plan = $this->getEffectivePlan($entityType, $entityId);
return $plan?->getMaxSecretaries() ?? 1;
}
public function hasUsedTrial(string $entityType, int $entityId): bool
{
return $this->subscriptionRepo->hasUsedTrial($entityType, $entityId);
}
public function activateTrial(string $entityType, int $entityId): ClinicSubscription
{
if ($this->hasUsedTrial($entityType, $entityId)) {
throw new AppException(ErrorCodes::ERR_TRIAL_ALREADY_USED, null, 422);
}
$trialEnabled = $this->configRepo->get('trial_enabled');
if ($trialEnabled === '0') {
throw new AppException(ErrorCodes::ERR_TRIAL_DISABLED, null, 422);
}
$basicPlan = $this->planRepo->findByName('basic');
if ($basicPlan === null) {
throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, null, 500);
}
// Deactivating the trial period is a normal admin configuration, not a
// broken install — surface it as "trial disabled", the same 422 the
// trial_enabled flag produces, rather than a 500.
$trialPeriod = $this->periodRepo->findTrialPeriodForPlan($basicPlan);
if ($trialPeriod === null) {
throw new AppException(ErrorCodes::ERR_TRIAL_DISABLED, null, 422);
}
$expiresAt = $this->calculateExpiresAt(null, $trialPeriod->getDurationMonths());
$subscription = new ClinicSubscription(
$entityType,
$entityId,
$basicPlan,
$trialPeriod,
true,
$expiresAt,
null
);
$this->subscriptionRepo->save($subscription);
return $subscription;
}
public function createFromPayment(Payment $payment, string $entityType, int $entityId, string $periodUuid): ClinicSubscription
{
$period = $this->periodRepo->findByUuid($periodUuid);
if ($period === null) {
throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, null, 404);
}
$currentSub = $this->getActiveSubscription($entityType, $entityId);
$currentExpires = $currentSub?->getExpiresAt();
$expiresAt = $this->calculateExpiresAt($currentExpires, $period->getDurationMonths());
$subscription = new ClinicSubscription(
$entityType,
$entityId,
$period->getPlan(),
$period,
false,
$expiresAt,
$payment
);
$this->subscriptionRepo->save($subscription);
return $subscription;
}
/** حذف اشتراکِ ساخته‌شده از یک پرداخت (هنگام استرداد/برگشت وجه). */
public function deleteByPayment(\App\Payment\Entity\Payment $payment): void
{
$subscription = $this->subscriptionRepo->findByPayment($payment);
if ($subscription !== null) {
$this->subscriptionRepo->remove($subscription);
}
}
public function calculateExpiresAt(?int $currentExpiresAt, int $durationMonths): int
{
$base = max($currentExpiresAt ?? 0, time());
return $base + $durationMonths * 30 * 86400;
}
}