diff --git a/app/Http/Controllers/Api/Admin/Barobill/BarobillMemberController.php b/app/Http/Controllers/Api/Admin/Barobill/BarobillMemberController.php
index 58e982c2..39d103b7 100644
--- a/app/Http/Controllers/Api/Admin/Barobill/BarobillMemberController.php
+++ b/app/Http/Controllers/Api/Admin/Barobill/BarobillMemberController.php
@@ -24,6 +24,8 @@ public function __construct(
/**
* 회원사 조회 및 비밀번호 검증 헬퍼
*
+ * 회원사의 서버 모드에 따라 BarobillService의 서버 모드도 자동 전환합니다.
+ *
* @return BarobillMember|JsonResponse 회원사 객체 또는 에러 응답
*/
private function validateMemberForUrlApi(int $id): BarobillMember|JsonResponse
@@ -45,6 +47,9 @@ private function validateMemberForUrlApi(int $id): BarobillMember|JsonResponse
], 422);
}
+ // 회원사의 서버 모드로 BarobillService 전환
+ $this->barobillService->setServerMode($member->server_mode ?? 'test');
+
return $member;
}
@@ -858,57 +863,89 @@ public function getServiceCodes(): JsonResponse
}
/**
- * 서버 모드 설정 (테스트/운영 서버 전환)
+ * 회원사별 서버 모드 변경
*
- * 세션에 바로빌 서버 모드를 저장합니다.
- * BarobillService 생성 시 이 세션 값을 읽어 사용합니다.
+ * 특정 회원사의 바로빌 서버 모드(테스트/운영)를 변경합니다.
+ * 주의: 운영 서버로 전환 시 요금이 부과됩니다.
*/
- public function setServerMode(Request $request): JsonResponse
+ public function updateServerMode(Request $request, int $id): JsonResponse
{
+ $member = BarobillMember::find($id);
+
+ if (!$member) {
+ return response()->json([
+ 'success' => false,
+ 'message' => '회원사를 찾을 수 없습니다.',
+ ], 404);
+ }
+
$validated = $request->validate([
- 'mode' => 'required|in:test,production',
+ 'server_mode' => 'required|in:test,production',
+ 'confirmed' => 'required|boolean',
], [
- 'mode.required' => '서버 모드를 선택해주세요.',
- 'mode.in' => '서버 모드는 test 또는 production 이어야 합니다.',
+ 'server_mode.required' => '서버 모드를 선택해주세요.',
+ 'server_mode.in' => '서버 모드는 test 또는 production 이어야 합니다.',
+ 'confirmed.required' => '경고 확인이 필요합니다.',
]);
- $mode = $validated['mode'];
- session(['barobill_server_mode' => $mode]);
+ // 확인 체크
+ if (!$validated['confirmed']) {
+ return response()->json([
+ 'success' => false,
+ 'message' => '서버 변경 경고를 확인해주세요.',
+ ], 422);
+ }
- Log::info('바로빌 서버 모드 변경', [
- 'mode' => $mode,
+ $oldMode = $member->server_mode ?? 'test';
+ $newMode = $validated['server_mode'];
+
+ // 변경 없으면 바로 반환
+ if ($oldMode === $newMode) {
+ return response()->json([
+ 'success' => true,
+ 'message' => '서버 모드가 이미 ' . ($newMode === 'test' ? '테스트' : '운영') . ' 서버입니다.',
+ 'data' => $member,
+ ]);
+ }
+
+ $member->update(['server_mode' => $newMode]);
+
+ Log::info('바로빌 회원사 서버 모드 변경', [
+ 'member_id' => $id,
+ 'biz_no' => $member->biz_no,
+ 'corp_name' => $member->corp_name,
+ 'old_mode' => $oldMode,
+ 'new_mode' => $newMode,
'user_id' => auth()->id(),
]);
return response()->json([
'success' => true,
- 'mode' => $mode,
- 'message' => ($mode === 'test' ? '테스트' : '운영') . ' 서버로 전환되었습니다.',
+ 'message' => ($newMode === 'test' ? '테스트' : '운영') . ' 서버로 변경되었습니다.',
+ 'data' => $member->fresh(),
]);
}
/**
- * 서버 모드 조회
- *
- * 현재 세션에 저장된 바로빌 서버 모드를 반환합니다.
- * 세션 값이 없으면 .env 설정값을 반환합니다.
+ * 회원사별 서버 모드 조회
*/
- public function getServerMode(): JsonResponse
+ public function getServerMode(int $id): JsonResponse
{
- $sessionMode = session('barobill_server_mode');
+ $member = BarobillMember::find($id);
- if ($sessionMode) {
- $mode = $sessionMode;
- } else {
- // .env 설정 기준 (BAROBILL_TEST_MODE)
- $isTestMode = config('services.barobill.test_mode', true);
- $mode = $isTestMode ? 'test' : 'production';
+ if (!$member) {
+ return response()->json([
+ 'success' => false,
+ 'message' => '회원사를 찾을 수 없습니다.',
+ ], 404);
}
return response()->json([
'success' => true,
- 'mode' => $mode,
- 'source' => $sessionMode ? 'session' : 'env',
+ 'data' => [
+ 'server_mode' => $member->server_mode ?? 'test',
+ 'server_mode_label' => $member->server_mode_label,
+ ],
]);
}
}
diff --git a/app/Models/Barobill/BarobillMember.php b/app/Models/Barobill/BarobillMember.php
index 7c087565..8c6e14c5 100644
--- a/app/Models/Barobill/BarobillMember.php
+++ b/app/Models/Barobill/BarobillMember.php
@@ -27,6 +27,7 @@ class BarobillMember extends Model
'manager_email',
'manager_hp',
'status',
+ 'server_mode',
];
protected $casts = [
@@ -85,4 +86,36 @@ public function getStatusColorAttribute(): string
default => 'bg-gray-100 text-gray-800',
};
}
+
+ /**
+ * 서버 모드 라벨
+ */
+ public function getServerModeLabelAttribute(): string
+ {
+ return match ($this->server_mode) {
+ 'test' => '테스트',
+ 'production' => '운영',
+ default => '테스트',
+ };
+ }
+
+ /**
+ * 서버 모드별 색상 클래스
+ */
+ public function getServerModeColorAttribute(): string
+ {
+ return match ($this->server_mode) {
+ 'test' => 'bg-amber-100 text-amber-800',
+ 'production' => 'bg-green-100 text-green-800',
+ default => 'bg-amber-100 text-amber-800',
+ };
+ }
+
+ /**
+ * 테스트 모드 여부
+ */
+ public function isTestMode(): bool
+ {
+ return $this->server_mode !== 'production';
+ }
}
diff --git a/app/Services/Barobill/BarobillService.php b/app/Services/Barobill/BarobillService.php
index 9b065bf8..39b82202 100644
--- a/app/Services/Barobill/BarobillService.php
+++ b/app/Services/Barobill/BarobillService.php
@@ -96,15 +96,55 @@ class BarobillService
public function __construct()
{
- // 1. 세션에서 서버 모드 가져오기 (최우선)
- $sessionMode = session('barobill_server_mode');
- if ($sessionMode) {
- $this->isTestMode = ($sessionMode === 'test');
- } else {
- // 2. .env에서 테스트 모드 설정 가져오기 (기본값: true = 테스트 모드)
- $this->isTestMode = config('services.barobill.test_mode', true);
+ // 기본값: .env 설정 사용
+ $this->isTestMode = config('services.barobill.test_mode', true);
+ $this->initializeConfig();
+ }
+
+ /**
+ * 서버 모드 전환 (회원사별 설정 적용)
+ *
+ * @param bool $isTestMode true: 테스트서버, false: 운영서버
+ */
+ public function switchServerMode(bool $isTestMode): self
+ {
+ if ($this->isTestMode !== $isTestMode) {
+ $this->isTestMode = $isTestMode;
+ // SOAP 클라이언트 초기화 (새 서버로 재연결)
+ $this->corpStateClient = null;
+ $this->tiClient = null;
+ $this->bankAccountClient = null;
+ $this->cardClient = null;
+ // 설정 재로드
+ $this->initializeConfig();
}
+ return $this;
+ }
+
+ /**
+ * 서버 모드 문자열로 전환
+ *
+ * @param string $mode 'test' 또는 'production'
+ */
+ public function setServerMode(string $mode): self
+ {
+ return $this->switchServerMode($mode === 'test');
+ }
+
+ /**
+ * 현재 서버 모드 조회
+ */
+ public function getServerMode(): string
+ {
+ return $this->isTestMode ? 'test' : 'production';
+ }
+
+ /**
+ * 설정 초기화 (서버 모드에 따른 설정 로드)
+ */
+ protected function initializeConfig(): void
+ {
// DB에서 활성화된 설정 가져오기 (우선순위)
$dbConfig = $this->loadConfigFromDatabase();
diff --git a/resources/views/barobill/members/index.blade.php b/resources/views/barobill/members/index.blade.php
index dc132333..c85551a8 100644
--- a/resources/views/barobill/members/index.blade.php
+++ b/resources/views/barobill/members/index.blade.php
@@ -10,30 +10,16 @@
회원사관리
바로빌 연동 회원사를 관리합니다
-
-
-
- 서버:
-
-
-
-
-
+
@@ -137,6 +123,78 @@ class="w-full px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@endsection
@push('scripts')
@@ -635,49 +693,111 @@ function closeBarobillDropdown() {
}
});
- // 바로빌 서버 모드 관리
- const BarobillServer = {
- currentMode: 'test',
+ // 회원사별 서버 모드 변경 관리
+ const ServerModeManager = {
+ pendingMemberId: null,
+ pendingMode: null,
- async init() {
- try {
- const res = await fetch('/api/admin/barobill/members/server-mode', {
- headers: {
- 'X-CSRF-TOKEN': '{{ csrf_token() }}',
- 'Accept': 'application/json'
- }
- });
- const data = await res.json();
- if (data.success) {
- this.currentMode = data.mode;
- this.updateUI();
- }
- } catch (error) {
- console.error('서버 모드 조회 실패:', error);
+ // 서버 모드 변경 요청 (경고 모달 표시)
+ requestChange(memberId, memberName, currentMode) {
+ this.pendingMemberId = memberId;
+ const newMode = currentMode === 'test' ? 'production' : 'test';
+ this.pendingMode = newMode;
+
+ const modal = document.getElementById('serverModeConfirmModal');
+ const memberNameEl = document.getElementById('serverModeModalMemberName');
+ const currentModeEl = document.getElementById('serverModeModalCurrentMode');
+ const newModeEl = document.getElementById('serverModeModalNewMode');
+ const warningEl = document.getElementById('serverModeWarning');
+ const confirmCheckbox = document.getElementById('serverModeConfirmCheckbox');
+
+ memberNameEl.textContent = memberName;
+ currentModeEl.textContent = currentMode === 'test' ? '테스트 서버' : '운영 서버';
+ currentModeEl.className = currentMode === 'test'
+ ? 'font-semibold text-amber-600'
+ : 'font-semibold text-green-600';
+ newModeEl.textContent = newMode === 'test' ? '테스트 서버' : '운영 서버';
+ newModeEl.className = newMode === 'test'
+ ? 'font-semibold text-amber-600'
+ : 'font-semibold text-green-600';
+
+ // 운영 서버로 전환 시 추가 경고
+ if (newMode === 'production') {
+ warningEl.innerHTML = `
+
+
+
+
+
⚠️ 요금 부과 안내
+
+ - • 운영 서버 사용 시 실제 요금이 부과됩니다.
+ - • 회원사 등록, 세금계산서 발행 등 모든 API 호출에 과금됩니다.
+ - • 테스트 목적이라면 테스트 서버를 사용해 주세요.
+
+
+
+
+ `;
+ } else {
+ warningEl.innerHTML = `
+
+
+
+
+
테스트 서버 안내
+
+ - • 테스트 서버는 개발/테스트 용도로만 사용됩니다.
+ - • 테스트 데이터는 실제 국세청에 전송되지 않습니다.
+ - • 운영 환경에서는 반드시 운영 서버로 전환해 주세요.
+
+
+
+
+ `;
}
+
+ confirmCheckbox.checked = false;
+ document.getElementById('serverModeConfirmBtn').disabled = true;
+ modal.classList.remove('hidden');
},
- async setMode(mode) {
- if (mode === this.currentMode) return;
+ // 확인 체크박스 상태 변경
+ onConfirmCheckChange(checked) {
+ document.getElementById('serverModeConfirmBtn').disabled = !checked;
+ },
+
+ // 서버 모드 변경 실행
+ async confirmChange() {
+ if (!this.pendingMemberId || !this.pendingMode) return;
+
+ const confirmBtn = document.getElementById('serverModeConfirmBtn');
+ const originalText = confirmBtn.textContent;
+ confirmBtn.disabled = true;
+ confirmBtn.textContent = '변경 중...';
try {
- const res = await fetch('/api/admin/barobill/members/server-mode', {
+ const res = await fetch(`/api/admin/barobill/members/${this.pendingMemberId}/server-mode`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json'
},
- body: JSON.stringify({ mode })
+ body: JSON.stringify({
+ server_mode: this.pendingMode,
+ confirmed: true
+ })
});
const data = await res.json();
if (data.success) {
- this.currentMode = mode;
- this.updateUI();
showToast(data.message, 'success');
- // 목록 새로고침
+ this.closeModal();
htmx.trigger(document.body, 'memberUpdated');
} else {
showToast(data.message || '서버 모드 변경 실패', 'error');
@@ -685,31 +805,23 @@ function closeBarobillDropdown() {
} catch (error) {
console.error('서버 모드 변경 실패:', error);
showToast('서버 모드 변경 중 오류가 발생했습니다.', 'error');
+ } finally {
+ confirmBtn.disabled = false;
+ confirmBtn.textContent = originalText;
}
},
- updateUI() {
- const testBtn = document.getElementById('testServerBtn');
- const prodBtn = document.getElementById('prodServerBtn');
-
- // 버튼 스타일 초기화
- testBtn.classList.remove('bg-amber-500', 'text-white', 'bg-gray-200', 'text-gray-600');
- prodBtn.classList.remove('bg-green-500', 'text-white', 'bg-gray-200', 'text-gray-600');
-
- if (this.currentMode === 'test') {
- testBtn.classList.add('bg-amber-500', 'text-white');
- prodBtn.classList.add('bg-gray-200', 'text-gray-600');
- } else {
- testBtn.classList.add('bg-gray-200', 'text-gray-600');
- prodBtn.classList.add('bg-green-500', 'text-white');
- }
+ // 모달 닫기
+ closeModal() {
+ document.getElementById('serverModeConfirmModal').classList.add('hidden');
+ this.pendingMemberId = null;
+ this.pendingMode = null;
}
};
// 초기화
document.addEventListener('DOMContentLoaded', function() {
MemberModal.init();
- BarobillServer.init();
});
@endpush
diff --git a/resources/views/barobill/members/partials/table.blade.php b/resources/views/barobill/members/partials/table.blade.php
index 13789aee..25079915 100644
--- a/resources/views/barobill/members/partials/table.blade.php
+++ b/resources/views/barobill/members/partials/table.blade.php
@@ -46,6 +46,9 @@ class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm te
상태
|
+
+ 서버
+ |
바로빌 서비스
|
@@ -88,6 +91,25 @@ class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm te
{{ $member->status_label }}
+
+
+ |
|