- DummyDataSeeder 및 개별 시더 추가 (Client, BadDebt, Deposit 등) - payments.paid_at nullable 마이그레이션 - subscriptions 취소 컬럼 추가 - clients 테이블 bad_debt 컬럼 제거 - PlanController, ClientService 수정 - 불필요한 claudedocs, flow-test 파일 정리
220 lines
7.7 KiB
PHP
220 lines
7.7 KiB
PHP
<?php
|
|
|
|
namespace Database\Seeders\Dummy;
|
|
|
|
use App\Models\Tenants\Payment;
|
|
use App\Models\Tenants\Plan;
|
|
use App\Models\Tenants\Subscription;
|
|
use Database\Seeders\DummyDataSeeder;
|
|
use Illuminate\Database\Seeder;
|
|
|
|
class DummyPaymentSeeder extends Seeder
|
|
{
|
|
public function run(): void
|
|
{
|
|
$tenantId = DummyDataSeeder::TENANT_ID;
|
|
$userId = DummyDataSeeder::USER_ID;
|
|
|
|
// 1. 요금제 생성 (없으면)
|
|
$plans = $this->createPlans($userId);
|
|
|
|
// 2. 구독 생성
|
|
$subscription = $this->createSubscription($tenantId, $plans['standard']->id, $userId);
|
|
|
|
// 3. 결제 내역 생성
|
|
$paymentCount = $this->createPayments($subscription, $userId);
|
|
|
|
$this->command->info(' ✓ plans: '.count($plans).'건 생성');
|
|
$this->command->info(' ✓ subscriptions: 1건 생성');
|
|
$this->command->info(' ✓ payments: '.$paymentCount.'건 생성');
|
|
}
|
|
|
|
/**
|
|
* 요금제 생성
|
|
*/
|
|
private function createPlans(int $userId): array
|
|
{
|
|
$plansData = [
|
|
'free' => [
|
|
'name' => '무료 체험',
|
|
'code' => 'FREE',
|
|
'description' => '14일 무료 체험',
|
|
'price' => 0,
|
|
'billing_cycle' => Plan::BILLING_MONTHLY,
|
|
'features' => ['basic_features', 'limited_users'],
|
|
'is_active' => true,
|
|
],
|
|
'starter' => [
|
|
'name' => '스타터',
|
|
'code' => 'STARTER',
|
|
'description' => '소규모 팀을 위한 기본 플랜',
|
|
'price' => 29000,
|
|
'billing_cycle' => Plan::BILLING_MONTHLY,
|
|
'features' => ['basic_features', 'email_support', 'up_to_5_users'],
|
|
'is_active' => true,
|
|
],
|
|
'standard' => [
|
|
'name' => '스탠다드',
|
|
'code' => 'STANDARD',
|
|
'description' => '성장하는 팀을 위한 표준 플랜',
|
|
'price' => 79000,
|
|
'billing_cycle' => Plan::BILLING_MONTHLY,
|
|
'features' => ['all_features', 'priority_support', 'up_to_20_users', 'api_access'],
|
|
'is_active' => true,
|
|
],
|
|
'professional' => [
|
|
'name' => '프로페셔널',
|
|
'code' => 'PRO',
|
|
'description' => '전문가 팀을 위한 고급 플랜',
|
|
'price' => 149000,
|
|
'billing_cycle' => Plan::BILLING_MONTHLY,
|
|
'features' => ['all_features', 'dedicated_support', 'unlimited_users', 'api_access', 'custom_integrations'],
|
|
'is_active' => true,
|
|
],
|
|
'enterprise' => [
|
|
'name' => '엔터프라이즈',
|
|
'code' => 'ENTERPRISE',
|
|
'description' => '대기업 맞춤형 플랜',
|
|
'price' => 499000,
|
|
'billing_cycle' => Plan::BILLING_MONTHLY,
|
|
'features' => ['all_features', 'dedicated_support', 'unlimited_users', 'api_access', 'custom_integrations', 'sla', 'on_premise'],
|
|
'is_active' => true,
|
|
],
|
|
'yearly_standard' => [
|
|
'name' => '스탠다드 (연간)',
|
|
'code' => 'STANDARD_YEARLY',
|
|
'description' => '연간 결제 시 20% 할인',
|
|
'price' => 758400, // 79000 * 12 * 0.8
|
|
'billing_cycle' => Plan::BILLING_YEARLY,
|
|
'features' => ['all_features', 'priority_support', 'up_to_20_users', 'api_access'],
|
|
'is_active' => true,
|
|
],
|
|
];
|
|
|
|
$plans = [];
|
|
foreach ($plansData as $key => $data) {
|
|
$plans[$key] = Plan::firstOrCreate(
|
|
['code' => $data['code']],
|
|
array_merge($data, ['created_by' => $userId])
|
|
);
|
|
}
|
|
|
|
return $plans;
|
|
}
|
|
|
|
/**
|
|
* 구독 생성
|
|
*/
|
|
private function createSubscription(int $tenantId, int $planId, int $userId): Subscription
|
|
{
|
|
// 기존 활성 구독이 있으면 반환
|
|
$existing = Subscription::where('tenant_id', $tenantId)
|
|
->where('status', Subscription::STATUS_ACTIVE)
|
|
->first();
|
|
|
|
if ($existing) {
|
|
return $existing;
|
|
}
|
|
|
|
// 새 구독 생성 (12개월 전부터 시작)
|
|
return Subscription::create([
|
|
'tenant_id' => $tenantId,
|
|
'plan_id' => $planId,
|
|
'started_at' => now()->subMonths(12)->startOfMonth(),
|
|
'ended_at' => now()->addMonths(1)->endOfMonth(),
|
|
'status' => Subscription::STATUS_ACTIVE,
|
|
'created_by' => $userId,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 결제 내역 생성
|
|
*/
|
|
private function createPayments(Subscription $subscription, int $userId): int
|
|
{
|
|
// 이미 결제 내역이 있으면 스킵
|
|
if ($subscription->payments()->count() > 0) {
|
|
return 0;
|
|
}
|
|
|
|
$plan = $subscription->plan;
|
|
$paymentMethods = [
|
|
Payment::METHOD_CARD,
|
|
Payment::METHOD_CARD,
|
|
Payment::METHOD_CARD, // 카드 60%
|
|
Payment::METHOD_BANK,
|
|
Payment::METHOD_BANK, // 계좌이체 30%
|
|
Payment::METHOD_VIRTUAL, // 가상계좌 10%
|
|
];
|
|
|
|
$statuses = [
|
|
Payment::STATUS_COMPLETED,
|
|
Payment::STATUS_COMPLETED,
|
|
Payment::STATUS_COMPLETED,
|
|
Payment::STATUS_COMPLETED,
|
|
Payment::STATUS_COMPLETED,
|
|
Payment::STATUS_COMPLETED,
|
|
Payment::STATUS_COMPLETED,
|
|
Payment::STATUS_COMPLETED, // 완료 80%
|
|
Payment::STATUS_CANCELLED, // 취소 10%
|
|
Payment::STATUS_REFUNDED, // 환불 10%
|
|
];
|
|
|
|
$count = 0;
|
|
|
|
// 12개월치 결제 내역 생성
|
|
for ($i = 12; $i >= 0; $i--) {
|
|
$paymentDate = now()->subMonths($i)->startOfMonth();
|
|
|
|
// 이번 달은 대기 상태로
|
|
$status = $i === 0
|
|
? Payment::STATUS_PENDING
|
|
: $statuses[array_rand($statuses)];
|
|
|
|
// 금액 변동 (할인, 프로모션 등)
|
|
$baseAmount = $plan->price;
|
|
$amount = match (true) {
|
|
$i >= 10 => $baseAmount * 0.5, // 첫 3개월 50% 할인
|
|
$i >= 6 => $baseAmount * 0.8, // 다음 4개월 20% 할인
|
|
default => $baseAmount, // 이후 정가
|
|
};
|
|
|
|
// 취소/환불은 금액 0
|
|
if (in_array($status, [Payment::STATUS_CANCELLED, Payment::STATUS_REFUNDED])) {
|
|
$amount = $baseAmount;
|
|
}
|
|
|
|
$paidAt = $status === Payment::STATUS_COMPLETED
|
|
? $paymentDate->copy()->addDays(rand(1, 5))
|
|
: null;
|
|
|
|
$transactionId = $status === Payment::STATUS_COMPLETED
|
|
? 'TXN'.$paymentDate->format('Ymd').str_pad(rand(1, 9999), 4, '0', STR_PAD_LEFT)
|
|
: null;
|
|
|
|
$memo = match ($status) {
|
|
Payment::STATUS_CANCELLED => '고객 요청에 의한 취소',
|
|
Payment::STATUS_REFUNDED => '서비스 불만족으로 인한 환불',
|
|
Payment::STATUS_PENDING => '결제 대기 중',
|
|
default => null,
|
|
};
|
|
|
|
Payment::create([
|
|
'subscription_id' => $subscription->id,
|
|
'amount' => $amount,
|
|
'payment_method' => $paymentMethods[array_rand($paymentMethods)],
|
|
'transaction_id' => $transactionId,
|
|
'paid_at' => $paidAt,
|
|
'status' => $status,
|
|
'memo' => $memo,
|
|
'created_by' => $userId,
|
|
'created_at' => $paymentDate,
|
|
]);
|
|
|
|
$count++;
|
|
}
|
|
|
|
return $count;
|
|
}
|
|
}
|