From b576fe97e806cc64d2225189de4ebca3efd9bd1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Fri, 20 Feb 2026 22:39:04 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20[barobill]=20=EB=B0=94=EB=A1=9C?= =?UTF-8?q?=EB=B9=8C=20=EC=97=B0=EB=8F=99=20=EA=B4=80=EB=A6=AC=20API=207?= =?UTF-8?q?=EA=B0=9C=20=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SOAP 기반 BarobillSoapService 생성 (MNG 코드 포팅) - BarobillMember, BarobillConfig 모델 생성 - BarobillController 7개 메서드 (login, signup, status, URL 조회) - FormRequest 검증 클래스 3개 생성 - 라우트 등록 (POST /barobill/login, /signup, GET /status 등) - i18n 메시지 키 추가 (ko/en) - config/services.php에 barobill 설정 추가 --- .../Controllers/Api/V1/BarobillController.php | 73 +++ .../Barobill/BankServiceUrlRequest.php | 21 + .../Barobill/BarobillLoginRequest.php | 21 + .../Barobill/BarobillSignupRequest.php | 30 + app/Models/Tenants/BarobillConfig.php | 40 ++ app/Models/Tenants/BarobillMember.php | 66 ++ app/Services/Barobill/BarobillSoapService.php | 604 ++++++++++++++++++ config/services.php | 13 + lang/en/message.php | 7 + lang/ko/error.php | 5 + lang/ko/message.php | 7 + routes/api/v1/finance.php | 12 + 12 files changed, 899 insertions(+) create mode 100644 app/Http/Controllers/Api/V1/BarobillController.php create mode 100644 app/Http/Requests/Barobill/BankServiceUrlRequest.php create mode 100644 app/Http/Requests/Barobill/BarobillLoginRequest.php create mode 100644 app/Http/Requests/Barobill/BarobillSignupRequest.php create mode 100644 app/Models/Tenants/BarobillConfig.php create mode 100644 app/Models/Tenants/BarobillMember.php create mode 100644 app/Services/Barobill/BarobillSoapService.php diff --git a/app/Http/Controllers/Api/V1/BarobillController.php b/app/Http/Controllers/Api/V1/BarobillController.php new file mode 100644 index 0000000..c8b58c4 --- /dev/null +++ b/app/Http/Controllers/Api/V1/BarobillController.php @@ -0,0 +1,73 @@ + $this->barobillSoapService->registerLogin($request->validated()), + __('message.barobill.login_success') + ); + } + + public function signup(BarobillSignupRequest $request) + { + return ApiResponse::handle( + fn () => $this->barobillSoapService->registerSignup($request->validated()), + __('message.barobill.signup_success') + ); + } + + public function bankServiceUrl(BankServiceUrlRequest $request) + { + return ApiResponse::handle( + fn () => $this->barobillSoapService->getBankServiceRedirectUrl($request->validated()), + __('message.fetched') + ); + } + + public function status() + { + return ApiResponse::handle( + fn () => $this->barobillSoapService->getIntegrationStatus(), + __('message.fetched') + ); + } + + public function accountLinkUrl() + { + return ApiResponse::handle( + fn () => $this->barobillSoapService->getAccountLinkRedirectUrl(), + __('message.fetched') + ); + } + + public function cardLinkUrl() + { + return ApiResponse::handle( + fn () => $this->barobillSoapService->getCardLinkRedirectUrl(), + __('message.fetched') + ); + } + + public function certificateUrl() + { + return ApiResponse::handle( + fn () => $this->barobillSoapService->getCertificateRedirectUrl(), + __('message.fetched') + ); + } +} diff --git a/app/Http/Requests/Barobill/BankServiceUrlRequest.php b/app/Http/Requests/Barobill/BankServiceUrlRequest.php new file mode 100644 index 0000000..ece0f68 --- /dev/null +++ b/app/Http/Requests/Barobill/BankServiceUrlRequest.php @@ -0,0 +1,21 @@ + ['required', 'string', 'max:10'], + 'account_type' => ['required', 'string', 'max:10'], + ]; + } +} diff --git a/app/Http/Requests/Barobill/BarobillLoginRequest.php b/app/Http/Requests/Barobill/BarobillLoginRequest.php new file mode 100644 index 0000000..7bb41f4 --- /dev/null +++ b/app/Http/Requests/Barobill/BarobillLoginRequest.php @@ -0,0 +1,21 @@ + ['required', 'string', 'max:50'], + 'password' => ['required', 'string', 'max:255'], + ]; + } +} diff --git a/app/Http/Requests/Barobill/BarobillSignupRequest.php b/app/Http/Requests/Barobill/BarobillSignupRequest.php new file mode 100644 index 0000000..0047ad0 --- /dev/null +++ b/app/Http/Requests/Barobill/BarobillSignupRequest.php @@ -0,0 +1,30 @@ + ['required', 'string', 'max:20'], + 'company_name' => ['required', 'string', 'max:100'], + 'ceo_name' => ['required', 'string', 'max:50'], + 'business_type' => ['nullable', 'string', 'max:50'], + 'business_category' => ['nullable', 'string', 'max:50'], + 'address' => ['nullable', 'string', 'max:255'], + 'barobill_id' => ['required', 'string', 'max:50'], + 'password' => ['required', 'string', 'max:255'], + 'manager_name' => ['nullable', 'string', 'max:50'], + 'manager_phone' => ['nullable', 'string', 'max:20'], + 'manager_email' => ['nullable', 'email', 'max:100'], + ]; + } +} diff --git a/app/Models/Tenants/BarobillConfig.php b/app/Models/Tenants/BarobillConfig.php new file mode 100644 index 0000000..faae2c6 --- /dev/null +++ b/app/Models/Tenants/BarobillConfig.php @@ -0,0 +1,40 @@ + 'boolean', + ]; + + public static function getActiveTest(): ?self + { + return self::where('environment', 'test')->first(); + } + + public static function getActiveProduction(): ?self + { + return self::where('environment', 'production')->first(); + } + + public static function getActive(bool $isTestMode = false): ?self + { + return $isTestMode ? self::getActiveTest() : self::getActiveProduction(); + } +} diff --git a/app/Models/Tenants/BarobillMember.php b/app/Models/Tenants/BarobillMember.php new file mode 100644 index 0000000..0aed74c --- /dev/null +++ b/app/Models/Tenants/BarobillMember.php @@ -0,0 +1,66 @@ + 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'barobill_pwd' => 'encrypted', + 'last_sales_fetch_at' => 'datetime', + 'last_purchases_fetch_at' => 'datetime', + ]; + + protected $hidden = [ + 'barobill_pwd', + ]; + + public function tenant(): BelongsTo + { + return $this->belongsTo(Tenant::class); + } + + public function getFormattedBizNoAttribute(): string + { + $bizNo = preg_replace('/[^0-9]/', '', $this->biz_no); + if (strlen($bizNo) === 10) { + return substr($bizNo, 0, 3).'-'.substr($bizNo, 3, 2).'-'.substr($bizNo, 5); + } + + return $this->biz_no; + } + + public function isTestMode(): bool + { + return $this->server_mode !== 'production'; + } +} diff --git a/app/Services/Barobill/BarobillSoapService.php b/app/Services/Barobill/BarobillSoapService.php new file mode 100644 index 0000000..5113b1a --- /dev/null +++ b/app/Services/Barobill/BarobillSoapService.php @@ -0,0 +1,604 @@ + '사업자번호가 설정되지 않았거나 유효하지 않습니다.', + -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']]; + } +} diff --git a/config/services.php b/config/services.php index 3865719..c0a790a 100644 --- a/config/services.php +++ b/config/services.php @@ -58,4 +58,17 @@ 'exchange_secret' => env('INTERNAL_EXCHANGE_SECRET'), ], + /* + |-------------------------------------------------------------------------- + | Barobill API (SOAP) + |-------------------------------------------------------------------------- + | 바로빌 SOAP 연동 설정 (계좌/카드/인증서 등) + */ + 'barobill' => [ + 'cert_key_test' => env('BAROBILL_CERT_KEY_TEST', ''), + 'cert_key_prod' => env('BAROBILL_CERT_KEY_PROD', ''), + 'corp_num' => env('BAROBILL_CORP_NUM', ''), + 'test_mode' => env('BAROBILL_TEST_MODE', true), + ], + ]; diff --git a/lang/en/message.php b/lang/en/message.php index f2036b5..40d7ebf 100644 --- a/lang/en/message.php +++ b/lang/en/message.php @@ -117,4 +117,11 @@ 'cancelled' => 'Tax invoice has been cancelled.', 'bulk_issued' => 'Tax invoices have been issued in bulk.', ], + + // Barobill Integration + 'barobill' => [ + 'login_success' => 'Barobill login registered successfully.', + 'signup_success' => 'Barobill signup completed successfully.', + 'connection_success' => 'Barobill connection test succeeded.', + ], ]; diff --git a/lang/ko/error.php b/lang/ko/error.php index 106d360..59f5b04 100644 --- a/lang/ko/error.php +++ b/lang/ko/error.php @@ -291,6 +291,11 @@ 'no_base_salary' => '기본급이 설정되지 않았습니다.', ], + // 바로빌 연동 관련 + 'barobill' => [ + 'member_not_found' => '바로빌 회원 정보가 등록되어 있지 않습니다.', + ], + // 세금계산서 관련 'tax_invoice' => [ 'not_found' => '세금계산서 정보를 찾을 수 없습니다.', diff --git a/lang/ko/message.php b/lang/ko/message.php index ec881e2..a907f89 100644 --- a/lang/ko/message.php +++ b/lang/ko/message.php @@ -566,6 +566,13 @@ 'downloaded' => '문서가 다운로드되었습니다.', ], + // 바로빌 연동 + 'barobill' => [ + 'login_success' => '바로빌 로그인 정보가 등록되었습니다.', + 'signup_success' => '바로빌 회원가입이 완료되었습니다.', + 'connection_success' => '바로빌 연동 테스트 성공', + ], + // CEO 대시보드 부가세 현황 'vat' => [ 'sales_tax' => '매출세액', diff --git a/routes/api/v1/finance.php b/routes/api/v1/finance.php index 2d03aca..9490c2a 100644 --- a/routes/api/v1/finance.php +++ b/routes/api/v1/finance.php @@ -15,6 +15,7 @@ use App\Http\Controllers\Api\V1\BadDebtController; use App\Http\Controllers\Api\V1\BankAccountController; use App\Http\Controllers\Api\V1\BankTransactionController; +use App\Http\Controllers\Api\V1\BarobillController; use App\Http\Controllers\Api\V1\BarobillSettingController; use App\Http\Controllers\Api\V1\BillController; use App\Http\Controllers\Api\V1\CalendarController; @@ -254,6 +255,17 @@ Route::post('/test-connection', [BarobillSettingController::class, 'testConnection'])->name('v1.barobill-settings.test-connection'); }); +// Barobill Integration API (바로빌 연동 관리) +Route::prefix('barobill')->group(function () { + Route::post('/login', [BarobillController::class, 'login'])->name('v1.barobill.login'); + Route::post('/signup', [BarobillController::class, 'signup'])->name('v1.barobill.signup'); + Route::get('/bank-service-url', [BarobillController::class, 'bankServiceUrl'])->name('v1.barobill.bank-service-url'); + Route::get('/status', [BarobillController::class, 'status'])->name('v1.barobill.status'); + Route::get('/account-link-url', [BarobillController::class, 'accountLinkUrl'])->name('v1.barobill.account-link-url'); + Route::get('/card-link-url', [BarobillController::class, 'cardLinkUrl'])->name('v1.barobill.card-link-url'); + Route::get('/certificate-url', [BarobillController::class, 'certificateUrl'])->name('v1.barobill.certificate-url'); +}); + // Tax Invoice API (세금계산서) Route::prefix('tax-invoices')->group(function () { Route::get('', [TaxInvoiceController::class, 'index'])->name('v1.tax-invoices.index');