'사업자번호가 설정되지 않았거나 유효하지 않습니다.', -11102 => 'CERTKEY가 유효하지 않습니다.', -11103 => '인증서가 만료되었거나 유효하지 않습니다.', -11104 => '해당 사업자가 등록되어 있지 않습니다.', -11105 => '이미 등록된 사업자입니다.', -11106 => '아이디가 이미 존재합니다.', -11201 => '필수 파라미터가 누락되었습니다.', -26001 => '공동인증서가 등록되어 있지 않습니다.', -32000 => '알 수 없는 오류가 발생했습니다.', -32001 => '사업자번호가 유효하지 않습니다.', -32002 => '아이디가 유효하지 않습니다.', -32003 => '비밀번호가 유효하지 않습니다.', -32004 => '상호명이 유효하지 않습니다.', -32005 => '대표자명이 유효하지 않습니다.', -32006 => '이메일 형식이 유효하지 않습니다.', -32010 => '이미 등록된 사업자번호입니다.', -32011 => '이미 등록된 아이디입니다.', -32012 => '이미 등록된 아이디입니다. 다른 아이디를 사용해주세요.', -32013 => '비밀번호 형식이 유효하지 않습니다. (영문/숫자/특수문자 조합 8자리 이상)', -32014 => '연락처 형식이 유효하지 않습니다.', -32020 => '파트너 사업자번호가 유효하지 않습니다.', -32021 => '파트너 인증키(CERTKEY)가 유효하지 않습니다.', -99999 => '서버 내부 오류가 발생했습니다.', ]; public function __construct() { $this->isTestMode = config('services.barobill.test_mode', true); $this->initializeConfig(); } public function switchServerMode(bool $isTestMode): self { if ($this->isTestMode !== $isTestMode) { $this->isTestMode = $isTestMode; $this->corpStateClient = null; $this->bankAccountClient = null; $this->cardClient = null; $this->initializeConfig(); } return $this; } protected function initializeConfig(): void { $dbConfig = $this->loadConfigFromDatabase(); if ($dbConfig) { $this->certKey = $dbConfig->cert_key; $this->corpNum = $dbConfig->corp_num ?? ''; $this->soapUrls = $this->buildSoapUrls($dbConfig->base_url); } else { $this->certKey = $this->isTestMode ? config('services.barobill.cert_key_test', '') : config('services.barobill.cert_key_prod', ''); $this->corpNum = config('services.barobill.corp_num', ''); $baseUrl = $this->isTestMode ? 'https://testws.baroservice.com' : 'https://ws.baroservice.com'; $this->soapUrls = $this->buildSoapUrls($baseUrl); } } protected function loadConfigFromDatabase(): ?BarobillConfig { try { return BarobillConfig::getActive($this->isTestMode); } catch (\Exception $e) { Log::warning('바로빌 DB 설정 로드 실패', ['error' => $e->getMessage()]); return null; } } protected function buildSoapUrls(string $baseUrl): array { $baseUrl = rtrim($baseUrl, '/'); return [ 'corpstate' => $baseUrl.'/CORPSTATE.asmx?WSDL', 'bankaccount' => $baseUrl.'/BANKACCOUNT.asmx?WSDL', 'card' => $baseUrl.'/CARD.asmx?WSDL', ]; } protected function getCorpStateClient(): ?SoapClient { if ($this->corpStateClient === null) { try { $this->corpStateClient = new SoapClient($this->soapUrls['corpstate'], [ 'trace' => true, 'encoding' => 'UTF-8', 'exceptions' => true, 'connection_timeout' => 30, 'cache_wsdl' => WSDL_CACHE_NONE, ]); } catch (Throwable $e) { Log::error('바로빌 CORPSTATE SOAP 클라이언트 생성 실패', [ 'error' => $e->getMessage(), 'url' => $this->soapUrls['corpstate'], ]); return null; } } return $this->corpStateClient; } protected function getBankAccountClient(): ?SoapClient { if ($this->bankAccountClient === null) { try { $this->bankAccountClient = new SoapClient($this->soapUrls['bankaccount'], [ 'trace' => true, 'encoding' => 'UTF-8', 'exceptions' => true, 'connection_timeout' => 30, 'cache_wsdl' => WSDL_CACHE_NONE, ]); } catch (Throwable $e) { Log::error('바로빌 BANKACCOUNT SOAP 클라이언트 생성 실패', [ 'error' => $e->getMessage(), 'url' => $this->soapUrls['bankaccount'], ]); return null; } } return $this->bankAccountClient; } protected function getCardClient(): ?SoapClient { if ($this->cardClient === null) { try { $this->cardClient = new SoapClient($this->soapUrls['card'], [ 'trace' => true, 'encoding' => 'UTF-8', 'exceptions' => true, 'connection_timeout' => 30, 'cache_wsdl' => WSDL_CACHE_NONE, ]); } catch (Throwable $e) { Log::error('바로빌 CARD SOAP 클라이언트 생성 실패', [ 'error' => $e->getMessage(), 'url' => $this->soapUrls['card'], ]); return null; } } return $this->cardClient; } protected function call(string $service, string $method, array $params = []): array { $client = match ($service) { 'corpstate' => $this->getCorpStateClient(), 'bankaccount' => $this->getBankAccountClient(), 'card' => $this->getCardClient(), default => null, }; if (! $client) { return [ 'success' => false, 'error' => "SOAP 클라이언트가 초기화되지 않았습니다. ({$service})", 'error_code' => -1, ]; } if (empty($this->certKey)) { return [ 'success' => false, 'error' => 'CERTKEY가 설정되지 않았습니다.', 'error_code' => -2, ]; } if (! isset($params['CERTKEY'])) { $params['CERTKEY'] = $this->certKey; } try { Log::info('바로빌 SOAP API 호출', [ 'service' => $service, 'method' => $method, 'test_mode' => $this->isTestMode, ]); $result = $client->$method($params); $resultProperty = $method.'Result'; if (isset($result->$resultProperty)) { $resultData = $result->$resultProperty; if (is_numeric($resultData) && $resultData < 0) { $errorMessage = $this->errorMessages[$resultData] ?? "바로빌 API 오류 코드: {$resultData}"; Log::error('바로빌 SOAP API 오류', [ 'method' => $method, 'error_code' => $resultData, 'error_message' => $errorMessage, ]); return [ 'success' => false, 'error' => $errorMessage, 'error_code' => $resultData, ]; } return [ 'success' => true, 'data' => $resultData, ]; } return [ 'success' => true, 'data' => $result, ]; } catch (SoapFault $e) { Log::error('바로빌 SOAP 오류', [ 'method' => $method, 'fault_code' => $e->faultcode ?? null, 'fault_string' => $e->faultstring ?? null, ]); return [ 'success' => false, 'error' => 'SOAP 오류: '.$e->getMessage(), 'error_code' => $e->getCode(), ]; } catch (Throwable $e) { Log::error('바로빌 SOAP API 호출 오류', [ 'method' => $method, 'error' => $e->getMessage(), ]); return [ 'success' => false, 'error' => 'API 호출 오류: '.$e->getMessage(), 'error_code' => -999, ]; } } protected function formatBizNo(string $bizNo): string { return preg_replace('/[^0-9]/', '', $bizNo); } // ========================================================================= // 회원사 조회 // ========================================================================= protected function getMember(): BarobillMember { $tenantId = $this->tenantId(); $member = BarobillMember::where('tenant_id', $tenantId)->first(); if (! $member) { throw new BadRequestHttpException(__('error.barobill.member_not_found')); } $this->switchServerMode($member->isTestMode()); return $member; } // ========================================================================= // SOAP 비즈니스 메서드 // ========================================================================= public function registCorp(array $data): array { $params = [ 'CorpNum' => $this->formatBizNo($data['biz_no']), 'CorpName' => $data['corp_name'], 'CEOName' => $data['ceo_name'], 'BizType' => $data['biz_type'] ?? '', 'BizClass' => $data['biz_class'] ?? '', 'PostNum' => $data['post_num'] ?? '', 'Addr1' => $data['addr'] ?? '', 'Addr2' => '', 'MemberName' => $data['manager_name'] ?? '', 'JuminNum' => '', 'ID' => $data['barobill_id'], 'PWD' => $data['barobill_pwd'], 'Grade' => '2', 'TEL' => $data['tel'] ?? '', 'HP' => $data['manager_hp'] ?? '', 'Email' => $data['manager_email'] ?? '', ]; return $this->call('corpstate', 'RegistCorp', $params); } public function getCorpState(string $corpNum): array { $params = [ 'CorpNum' => $this->formatBizNo($corpNum), ]; return $this->call('corpstate', 'GetCorpState', $params); } public function getBankAccountScrapRequestUrl(string $corpNum, string $userId, string $userPwd): array { $params = [ 'CorpNum' => $this->formatBizNo($corpNum), 'ID' => $userId, 'PWD' => $userPwd, ]; return $this->call('bankaccount', 'GetBankAccountScrapRequestURL', $params); } public function getBankAccountLogUrl(string $corpNum, string $userId, string $userPwd): array { $params = [ 'CorpNum' => $this->formatBizNo($corpNum), 'ID' => $userId, 'PWD' => $userPwd, ]; return $this->call('bankaccount', 'GetBankAccountLogURL', $params); } public function getCertificateRegistUrl(string $corpNum, string $userId, string $userPwd): array { $params = [ 'CorpNum' => $this->formatBizNo($corpNum), 'ID' => $userId, 'PWD' => $userPwd, ]; return $this->call('bankaccount', 'GetCertificateRegistURL', $params); } public function getBankAccounts(string $corpNum, bool $availOnly = true): array { $params = [ 'CorpNum' => $this->formatBizNo($corpNum), 'AvailOnly' => $availOnly, ]; return $this->call('bankaccount', 'GetBankAccountEx', $params); } public function getCards(string $corpNum, bool $availOnly = true): array { $params = [ 'CorpNum' => $this->formatBizNo($corpNum), 'AvailOnly' => $availOnly ? 1 : 0, ]; return $this->call('card', 'GetCardEx2', $params); } public function getCardScrapRequestUrl(string $corpNum, string $userId, string $userPwd): array { $params = [ 'CorpNum' => $this->formatBizNo($corpNum), 'ID' => $userId, 'PWD' => $userPwd, ]; return $this->call('card', 'GetCardScrapRequestURL', $params); } // ========================================================================= // 컨트롤러 비즈니스 메서드 // ========================================================================= public function registerLogin(array $data): array { $tenantId = $this->tenantId(); $barobillId = $data['barobill_id']; $password = $data['password']; $member = BarobillMember::where('tenant_id', $tenantId)->first(); if ($member) { $this->switchServerMode($member->isTestMode()); } $result = $this->getCorpState($member?->biz_no ?? config('services.barobill.corp_num')); if (! $result['success']) { throw new BadRequestHttpException($result['error']); } $member = BarobillMember::updateOrCreate( ['tenant_id' => $tenantId], [ 'barobill_id' => $barobillId, 'barobill_pwd' => $password, 'status' => 'active', ] ); return [ 'id' => $member->id, 'barobill_id' => $member->barobill_id, 'status' => $member->status, ]; } public function registerSignup(array $data): array { $tenantId = $this->tenantId(); $soapResult = $this->registCorp([ 'biz_no' => $data['business_number'], 'corp_name' => $data['company_name'], 'ceo_name' => $data['ceo_name'], 'biz_type' => $data['business_type'] ?? '', 'biz_class' => $data['business_category'] ?? '', 'addr' => $data['address'] ?? '', 'barobill_id' => $data['barobill_id'], 'barobill_pwd' => $data['password'], 'manager_name' => $data['manager_name'] ?? '', 'manager_hp' => $data['manager_phone'] ?? '', 'manager_email' => $data['manager_email'] ?? '', ]); if (! $soapResult['success']) { throw new BadRequestHttpException($soapResult['error']); } $member = BarobillMember::updateOrCreate( ['tenant_id' => $tenantId], [ 'biz_no' => $this->formatBizNo($data['business_number']), 'corp_name' => $data['company_name'], 'ceo_name' => $data['ceo_name'], 'biz_type' => $data['business_type'] ?? '', 'biz_class' => $data['business_category'] ?? '', 'addr' => $data['address'] ?? '', 'barobill_id' => $data['barobill_id'], 'barobill_pwd' => $data['password'], 'manager_name' => $data['manager_name'] ?? '', 'manager_hp' => $data['manager_phone'] ?? '', 'manager_email' => $data['manager_email'] ?? '', 'status' => 'active', 'server_mode' => $this->isTestMode ? 'test' : 'production', ] ); return [ 'id' => $member->id, 'barobill_id' => $member->barobill_id, 'biz_no' => $member->formatted_biz_no, 'status' => $member->status, ]; } public function getBankServiceRedirectUrl(array $params): array { $member = $this->getMember(); $result = $this->getBankAccountLogUrl( $member->biz_no, $member->barobill_id, $member->barobill_pwd ); if (! $result['success']) { throw new BadRequestHttpException($result['error']); } return ['url' => $result['data']]; } public function getIntegrationStatus(): array { $member = $this->getMember(); $bankResult = $this->getBankAccounts($member->biz_no); $cardResult = $this->getCards($member->biz_no); $bankCount = 0; if ($bankResult['success'] && isset($bankResult['data'])) { $data = $bankResult['data']; if (is_object($data) && isset($data->BankAccountInfo)) { $info = $data->BankAccountInfo; $bankCount = is_array($info) ? count($info) : 1; } elseif (is_array($data)) { $bankCount = count($data); } } $cardCount = 0; if ($cardResult['success'] && isset($cardResult['data'])) { $data = $cardResult['data']; if (is_object($data) && isset($data->CardInfo)) { $info = $data->CardInfo; $cardCount = is_array($info) ? count($info) : 1; } elseif (is_array($data)) { $cardCount = count($data); } } return [ 'bank_account_count' => $bankCount, 'card_count' => $cardCount, 'member' => [ 'barobill_id' => $member->barobill_id, 'biz_no' => $member->formatted_biz_no, 'status' => $member->status, 'server_mode' => $member->server_mode, ], ]; } public function getAccountLinkRedirectUrl(): array { $member = $this->getMember(); $result = $this->getBankAccountScrapRequestUrl( $member->biz_no, $member->barobill_id, $member->barobill_pwd ); if (! $result['success']) { throw new BadRequestHttpException($result['error']); } return ['url' => $result['data']]; } public function getCardLinkRedirectUrl(): array { $member = $this->getMember(); $result = $this->getCardScrapRequestUrl( $member->biz_no, $member->barobill_id, $member->barobill_pwd ); if (! $result['success']) { throw new BadRequestHttpException($result['error']); } return ['url' => $result['data']]; } public function getCertificateRedirectUrl(): array { $member = $this->getMember(); $result = $this->getCertificateRegistUrl( $member->biz_no, $member->barobill_id, $member->barobill_pwd ); if (! $result['success']) { throw new BadRequestHttpException($result['error']); } return ['url' => $result['data']]; } }