253 lines
8.2 KiB
PHP
253 lines
8.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Barobill;
|
|
|
|
use App\Models\Barobill\BarobillBillingRecord;
|
|
use App\Models\Barobill\BarobillMonthlySummary;
|
|
use App\Models\Barobill\BarobillSubscription;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* 바로빌 과금 서비스
|
|
*
|
|
* 월정액 구독 관리 및 과금 처리
|
|
*/
|
|
class BarobillBillingService
|
|
{
|
|
/**
|
|
* 회원사의 구독 목록 조회
|
|
*/
|
|
public function getSubscriptions(int $memberId): array
|
|
{
|
|
return BarobillSubscription::where('member_id', $memberId)
|
|
->orderBy('service_type')
|
|
->get()
|
|
->toArray();
|
|
}
|
|
|
|
/**
|
|
* 구독 등록/수정
|
|
*/
|
|
public function saveSubscription(int $memberId, string $serviceType, array $data): BarobillSubscription
|
|
{
|
|
return BarobillSubscription::updateOrCreate(
|
|
[
|
|
'member_id' => $memberId,
|
|
'service_type' => $serviceType,
|
|
],
|
|
[
|
|
'monthly_fee' => $data['monthly_fee'] ?? BarobillSubscription::DEFAULT_MONTHLY_FEES[$serviceType] ?? 0,
|
|
'started_at' => $data['started_at'] ?? now()->toDateString(),
|
|
'ended_at' => $data['ended_at'] ?? null,
|
|
'is_active' => $data['is_active'] ?? true,
|
|
'memo' => $data['memo'] ?? null,
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 구독 해지
|
|
*/
|
|
public function cancelSubscription(int $subscriptionId): bool
|
|
{
|
|
$subscription = BarobillSubscription::find($subscriptionId);
|
|
if (! $subscription) {
|
|
return false;
|
|
}
|
|
|
|
$subscription->update([
|
|
'ended_at' => now()->toDateString(),
|
|
'is_active' => false,
|
|
]);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 월별 과금 처리 (배치용)
|
|
*
|
|
* 매월 1일에 실행하여 전월 구독료 과금
|
|
*/
|
|
public function processMonthlyBilling(?string $billingMonth = null): array
|
|
{
|
|
$billingMonth = $billingMonth ?? now()->format('Y-m');
|
|
$billedAt = Carbon::parse($billingMonth.'-01');
|
|
|
|
$results = [
|
|
'billing_month' => $billingMonth,
|
|
'processed' => 0,
|
|
'skipped' => 0,
|
|
'errors' => [],
|
|
];
|
|
|
|
// 활성 구독 조회
|
|
$subscriptions = BarobillSubscription::active()
|
|
->with('member')
|
|
->get();
|
|
|
|
foreach ($subscriptions as $subscription) {
|
|
try {
|
|
// 이미 과금된 기록이 있는지 확인
|
|
$exists = BarobillBillingRecord::where('member_id', $subscription->member_id)
|
|
->where('billing_month', $billingMonth)
|
|
->where('service_type', $subscription->service_type)
|
|
->where('billing_type', 'subscription')
|
|
->exists();
|
|
|
|
if ($exists) {
|
|
$results['skipped']++;
|
|
|
|
continue;
|
|
}
|
|
|
|
// 과금 기록 생성
|
|
BarobillBillingRecord::create([
|
|
'member_id' => $subscription->member_id,
|
|
'billing_month' => $billingMonth,
|
|
'service_type' => $subscription->service_type,
|
|
'billing_type' => 'subscription',
|
|
'quantity' => 1,
|
|
'unit_price' => $subscription->monthly_fee,
|
|
'total_amount' => $subscription->monthly_fee,
|
|
'billed_at' => $billedAt,
|
|
'description' => BarobillSubscription::SERVICE_TYPES[$subscription->service_type].' 월정액',
|
|
]);
|
|
|
|
$results['processed']++;
|
|
|
|
} catch (\Exception $e) {
|
|
$results['errors'][] = [
|
|
'member_id' => $subscription->member_id,
|
|
'service_type' => $subscription->service_type,
|
|
'error' => $e->getMessage(),
|
|
];
|
|
Log::error('월정액 과금 처리 실패', [
|
|
'subscription_id' => $subscription->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
// 월별 집계 갱신
|
|
$this->updateMonthlySummaries($billingMonth);
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* 건별 사용량 과금 (세금계산서)
|
|
*/
|
|
public function recordUsage(int $memberId, string $serviceType, int $quantity, ?string $billingMonth = null): BarobillBillingRecord
|
|
{
|
|
$billingMonth = $billingMonth ?? now()->format('Y-m');
|
|
$unitPrice = BarobillBillingRecord::USAGE_UNIT_PRICES[$serviceType] ?? 0;
|
|
|
|
$record = BarobillBillingRecord::updateOrCreate(
|
|
[
|
|
'member_id' => $memberId,
|
|
'billing_month' => $billingMonth,
|
|
'service_type' => $serviceType,
|
|
'billing_type' => 'usage',
|
|
],
|
|
[
|
|
'quantity' => $quantity,
|
|
'unit_price' => $unitPrice,
|
|
'total_amount' => $quantity * $unitPrice,
|
|
'billed_at' => now()->toDateString(),
|
|
'description' => BarobillBillingRecord::SERVICE_TYPES[$serviceType].' '.$quantity.'건',
|
|
]
|
|
);
|
|
|
|
// 집계 갱신
|
|
BarobillMonthlySummary::updateOrCreateSummary($memberId, $billingMonth);
|
|
|
|
return $record;
|
|
}
|
|
|
|
/**
|
|
* 월별 집계 갱신
|
|
*/
|
|
public function updateMonthlySummaries(string $billingMonth): void
|
|
{
|
|
// 해당 월에 과금 기록이 있는 회원사 조회
|
|
$memberIds = BarobillBillingRecord::where('billing_month', $billingMonth)
|
|
->distinct()
|
|
->pluck('member_id');
|
|
|
|
foreach ($memberIds as $memberId) {
|
|
BarobillMonthlySummary::updateOrCreateSummary($memberId, $billingMonth);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 월별 과금 현황 조회
|
|
*/
|
|
public function getMonthlyBillingList(string $billingMonth, ?int $tenantId = null): array
|
|
{
|
|
$query = BarobillMonthlySummary::with(['member.tenant'])
|
|
->where('billing_month', $billingMonth);
|
|
|
|
if ($tenantId) {
|
|
$query->whereHas('member', function ($q) use ($tenantId) {
|
|
$q->where('tenant_id', $tenantId);
|
|
});
|
|
}
|
|
|
|
return $query->orderBy('grand_total', 'desc')->get()->toArray();
|
|
}
|
|
|
|
/**
|
|
* 월별 합계 조회
|
|
*/
|
|
public function getMonthlyTotal(string $billingMonth, ?int $tenantId = null): array
|
|
{
|
|
$query = BarobillMonthlySummary::where('billing_month', $billingMonth);
|
|
|
|
if ($tenantId) {
|
|
$query->whereHas('member', function ($q) use ($tenantId) {
|
|
$q->where('tenant_id', $tenantId);
|
|
});
|
|
}
|
|
|
|
$result = $query->selectRaw('
|
|
COUNT(*) as member_count,
|
|
SUM(bank_account_fee) as bank_account_fee,
|
|
SUM(card_fee) as card_fee,
|
|
SUM(hometax_fee) as hometax_fee,
|
|
SUM(subscription_total) as subscription_total,
|
|
SUM(tax_invoice_count) as tax_invoice_count,
|
|
SUM(tax_invoice_amount) as tax_invoice_amount,
|
|
SUM(usage_total) as usage_total,
|
|
SUM(grand_total) as grand_total
|
|
')->first();
|
|
|
|
return [
|
|
'billing_month' => $billingMonth,
|
|
'member_count' => (int) ($result->member_count ?? 0),
|
|
'bank_account_fee' => (int) ($result->bank_account_fee ?? 0),
|
|
'card_fee' => (int) ($result->card_fee ?? 0),
|
|
'hometax_fee' => (int) ($result->hometax_fee ?? 0),
|
|
'subscription_total' => (int) ($result->subscription_total ?? 0),
|
|
'tax_invoice_count' => (int) ($result->tax_invoice_count ?? 0),
|
|
'tax_invoice_amount' => (int) ($result->tax_invoice_amount ?? 0),
|
|
'usage_total' => (int) ($result->usage_total ?? 0),
|
|
'grand_total' => (int) ($result->grand_total ?? 0),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 연간 과금 추이 조회
|
|
*/
|
|
public function getYearlyTrend(int $year, ?int $tenantId = null): array
|
|
{
|
|
$months = [];
|
|
for ($m = 1; $m <= 12; $m++) {
|
|
$billingMonth = sprintf('%d-%02d', $year, $m);
|
|
$months[$billingMonth] = $this->getMonthlyTotal($billingMonth, $tenantId);
|
|
}
|
|
|
|
return $months;
|
|
}
|
|
}
|