- HometaxController 생성 (HOMETAX.asmx SOAP API 연동) - 매출 세금계산서 목록 조회 (GetHomeTaxTIBySalesEx) - 매입 세금계산서 목록 조회 (GetHomeTaxTIByPurchaseEx) - 홈택스 수집 요청 및 상태 조회 기능 - React 기반 UI (매출/매입 탭 전환, 통계 대시보드) - CSV 엑셀 다운로드 기능 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
681 lines
25 KiB
PHP
681 lines
25 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Barobill;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Barobill\BarobillConfig;
|
|
use App\Models\Barobill\BarobillMember;
|
|
use App\Models\Tenants\Tenant;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Response;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\View\View;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
|
|
/**
|
|
* 바로빌 홈택스 매입/매출 조회 컨트롤러
|
|
*
|
|
* 홈택스에 신고된 세금계산서 및 현금영수증 내역을 조회합니다.
|
|
*
|
|
* @see https://dev.barobill.co.kr/services/hometax
|
|
*/
|
|
class HometaxController extends Controller
|
|
{
|
|
/**
|
|
* 바로빌 SOAP 설정
|
|
*/
|
|
private ?string $certKey = null;
|
|
private ?string $corpNum = null;
|
|
private bool $isTestMode = false;
|
|
private ?string $soapUrl = null;
|
|
private ?\SoapClient $soapClient = null;
|
|
|
|
// 바로빌 파트너사 (본사) 테넌트 ID
|
|
private const HEADQUARTERS_TENANT_ID = 1;
|
|
|
|
public function __construct()
|
|
{
|
|
// DB에서 활성화된 바로빌 설정 조회
|
|
$activeConfig = BarobillConfig::where('is_active', true)->first();
|
|
|
|
if ($activeConfig) {
|
|
$this->certKey = $activeConfig->cert_key;
|
|
$this->corpNum = $activeConfig->corp_num;
|
|
$this->isTestMode = $activeConfig->environment === 'test';
|
|
// 홈택스 조회는 HOMETAX.asmx 사용
|
|
$baseUrl = $this->isTestMode
|
|
? 'https://testws.baroservice.com'
|
|
: 'https://ws.baroservice.com';
|
|
$this->soapUrl = $baseUrl . '/HOMETAX.asmx?WSDL';
|
|
} else {
|
|
$this->isTestMode = config('services.barobill.test_mode', true);
|
|
$this->certKey = $this->isTestMode
|
|
? config('services.barobill.cert_key_test', '')
|
|
: config('services.barobill.cert_key_prod', '');
|
|
$this->corpNum = config('services.barobill.corp_num', '');
|
|
$this->soapUrl = $this->isTestMode
|
|
? 'https://testws.baroservice.com/HOMETAX.asmx?WSDL'
|
|
: 'https://ws.baroservice.com/HOMETAX.asmx?WSDL';
|
|
}
|
|
|
|
$this->initSoapClient();
|
|
}
|
|
|
|
/**
|
|
* SOAP 클라이언트 초기화
|
|
*/
|
|
private function initSoapClient(): void
|
|
{
|
|
if (!empty($this->certKey) || $this->isTestMode) {
|
|
try {
|
|
$context = stream_context_create([
|
|
'ssl' => [
|
|
'verify_peer' => false,
|
|
'verify_peer_name' => false,
|
|
'allow_self_signed' => true
|
|
]
|
|
]);
|
|
|
|
$this->soapClient = new \SoapClient($this->soapUrl, [
|
|
'trace' => true,
|
|
'encoding' => 'UTF-8',
|
|
'exceptions' => true,
|
|
'connection_timeout' => 30,
|
|
'stream_context' => $context,
|
|
'cache_wsdl' => WSDL_CACHE_NONE
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
Log::error('바로빌 홈택스 SOAP 클라이언트 생성 실패: ' . $e->getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 홈택스 매입/매출 메인 페이지
|
|
*/
|
|
public function index(Request $request): View|Response
|
|
{
|
|
if ($request->header('HX-Request')) {
|
|
return response('', 200)->header('HX-Redirect', route('barobill.hometax.index'));
|
|
}
|
|
|
|
// 현재 선택된 테넌트 정보
|
|
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
|
|
$currentTenant = Tenant::find($tenantId);
|
|
|
|
// 해당 테넌트의 바로빌 회원사 정보
|
|
$barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
|
|
|
|
return view('barobill.hometax.index', [
|
|
'certKey' => $this->certKey,
|
|
'corpNum' => $this->corpNum,
|
|
'isTestMode' => $this->isTestMode,
|
|
'hasSoapClient' => $this->soapClient !== null,
|
|
'currentTenant' => $currentTenant,
|
|
'barobillMember' => $barobillMember,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 매출 세금계산서 목록 조회 (GetHomeTaxTIBySalesEx)
|
|
*/
|
|
public function sales(Request $request): JsonResponse
|
|
{
|
|
try {
|
|
$startDate = $request->input('startDate', date('Ymd', strtotime('-1 month')));
|
|
$endDate = $request->input('endDate', date('Ymd'));
|
|
$page = (int)$request->input('page', 1);
|
|
$limit = (int)$request->input('limit', 50);
|
|
|
|
// 현재 테넌트의 바로빌 회원 정보 조회
|
|
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
|
|
$barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
|
|
$userId = $barobillMember?->barobill_id ?? '';
|
|
|
|
$result = $this->callSoap('GetHomeTaxTIBySalesEx', [
|
|
'ID' => $userId,
|
|
'StartDate' => $startDate,
|
|
'EndDate' => $endDate,
|
|
'CountPerPage' => $limit,
|
|
'CurrentPage' => $page,
|
|
'OrderDirection' => 2 // 2: 내림차순
|
|
]);
|
|
|
|
if (!$result['success']) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => $result['error'],
|
|
'error_code' => $result['error_code'] ?? null
|
|
]);
|
|
}
|
|
|
|
$resultData = $result['data'];
|
|
|
|
// 에러 코드 체크
|
|
$errorCode = $this->checkErrorCode($resultData);
|
|
if ($errorCode && !in_array($errorCode, [-60005, -60001])) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => $this->getErrorMessage($errorCode),
|
|
'error_code' => $errorCode
|
|
]);
|
|
}
|
|
|
|
// 데이터가 없는 경우
|
|
if ($errorCode && in_array($errorCode, [-60005, -60001])) {
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'invoices' => [],
|
|
'summary' => ['totalAmount' => 0, 'totalTax' => 0, 'count' => 0],
|
|
'pagination' => ['currentPage' => 1, 'maxPageNum' => 1]
|
|
]
|
|
]);
|
|
}
|
|
|
|
// 데이터 파싱
|
|
$parsed = $this->parseInvoices($resultData, 'sales');
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'invoices' => $parsed['invoices'],
|
|
'pagination' => [
|
|
'currentPage' => $resultData->CurrentPage ?? 1,
|
|
'countPerPage' => $resultData->CountPerPage ?? 50,
|
|
'maxPageNum' => $resultData->MaxPageNum ?? 1,
|
|
'maxIndex' => $resultData->MaxIndex ?? 0
|
|
],
|
|
'summary' => $parsed['summary']
|
|
]
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
Log::error('홈택스 매출 조회 오류: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => '서버 오류: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 매입 세금계산서 목록 조회 (GetHomeTaxTIByPurchaseEx)
|
|
*/
|
|
public function purchases(Request $request): JsonResponse
|
|
{
|
|
try {
|
|
$startDate = $request->input('startDate', date('Ymd', strtotime('-1 month')));
|
|
$endDate = $request->input('endDate', date('Ymd'));
|
|
$page = (int)$request->input('page', 1);
|
|
$limit = (int)$request->input('limit', 50);
|
|
|
|
// 현재 테넌트의 바로빌 회원 정보 조회
|
|
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
|
|
$barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
|
|
$userId = $barobillMember?->barobill_id ?? '';
|
|
|
|
$result = $this->callSoap('GetHomeTaxTIByPurchaseEx', [
|
|
'ID' => $userId,
|
|
'StartDate' => $startDate,
|
|
'EndDate' => $endDate,
|
|
'CountPerPage' => $limit,
|
|
'CurrentPage' => $page,
|
|
'OrderDirection' => 2 // 2: 내림차순
|
|
]);
|
|
|
|
if (!$result['success']) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => $result['error'],
|
|
'error_code' => $result['error_code'] ?? null
|
|
]);
|
|
}
|
|
|
|
$resultData = $result['data'];
|
|
|
|
// 에러 코드 체크
|
|
$errorCode = $this->checkErrorCode($resultData);
|
|
if ($errorCode && !in_array($errorCode, [-60005, -60001])) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => $this->getErrorMessage($errorCode),
|
|
'error_code' => $errorCode
|
|
]);
|
|
}
|
|
|
|
// 데이터가 없는 경우
|
|
if ($errorCode && in_array($errorCode, [-60005, -60001])) {
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'invoices' => [],
|
|
'summary' => ['totalAmount' => 0, 'totalTax' => 0, 'count' => 0],
|
|
'pagination' => ['currentPage' => 1, 'maxPageNum' => 1]
|
|
]
|
|
]);
|
|
}
|
|
|
|
// 데이터 파싱
|
|
$parsed = $this->parseInvoices($resultData, 'purchase');
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => [
|
|
'invoices' => $parsed['invoices'],
|
|
'pagination' => [
|
|
'currentPage' => $resultData->CurrentPage ?? 1,
|
|
'countPerPage' => $resultData->CountPerPage ?? 50,
|
|
'maxPageNum' => $resultData->MaxPageNum ?? 1,
|
|
'maxIndex' => $resultData->MaxIndex ?? 0
|
|
],
|
|
'summary' => $parsed['summary']
|
|
]
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
Log::error('홈택스 매입 조회 오류: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => '서버 오류: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 홈택스 수집 요청 (RequestHomeTaxTICollect)
|
|
*/
|
|
public function requestCollect(Request $request): JsonResponse
|
|
{
|
|
try {
|
|
$collectType = $request->input('type', 'all'); // all, sales, purchase
|
|
|
|
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
|
|
$barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
|
|
$userId = $barobillMember?->barobill_id ?? '';
|
|
|
|
$result = $this->callSoap('RequestHomeTaxTICollect', [
|
|
'ID' => $userId,
|
|
'CollectType' => $this->getCollectTypeCode($collectType)
|
|
]);
|
|
|
|
if (!$result['success']) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => $result['error']
|
|
]);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => '홈택스 데이터 수집이 요청되었습니다. 잠시 후 다시 조회해주세요.',
|
|
'data' => $result['data']
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
Log::error('홈택스 수집 요청 오류: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => '서버 오류: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 수집 상태 확인 (GetCollectState)
|
|
*/
|
|
public function collectStatus(Request $request): JsonResponse
|
|
{
|
|
try {
|
|
$tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
|
|
$barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
|
|
$userId = $barobillMember?->barobill_id ?? '';
|
|
|
|
$result = $this->callSoap('GetHomeTaxTICollectState', [
|
|
'ID' => $userId
|
|
]);
|
|
|
|
if (!$result['success']) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => $result['error']
|
|
]);
|
|
}
|
|
|
|
$data = $result['data'];
|
|
$state = $this->parseCollectState($data);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => $state
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
Log::error('홈택스 수집 상태 조회 오류: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => '서버 오류: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 세금계산서 파싱
|
|
*/
|
|
private function parseInvoices($resultData, string $type = 'sales'): array
|
|
{
|
|
$invoices = [];
|
|
$totalAmount = 0;
|
|
$totalTax = 0;
|
|
|
|
$rawList = [];
|
|
$listProperty = $type === 'sales' ? 'HomeTaxTISalesList' : 'HomeTaxTIPurchaseList';
|
|
$itemProperty = $type === 'sales' ? 'HomeTaxTISales' : 'HomeTaxTIPurchase';
|
|
|
|
if (isset($resultData->$listProperty) && isset($resultData->$listProperty->$itemProperty)) {
|
|
$rawList = is_array($resultData->$listProperty->$itemProperty)
|
|
? $resultData->$listProperty->$itemProperty
|
|
: [$resultData->$listProperty->$itemProperty];
|
|
}
|
|
|
|
foreach ($rawList as $item) {
|
|
$supplyAmount = floatval($item->SupplyAmount ?? 0);
|
|
$taxAmount = floatval($item->TaxAmount ?? 0);
|
|
$totalAmount += $supplyAmount;
|
|
$totalTax += $taxAmount;
|
|
|
|
// 날짜 포맷팅
|
|
$writeDate = $item->WriteDate ?? '';
|
|
$formattedDate = '';
|
|
if (!empty($writeDate) && strlen($writeDate) >= 8) {
|
|
$formattedDate = substr($writeDate, 0, 4) . '-' . substr($writeDate, 4, 2) . '-' . substr($writeDate, 6, 2);
|
|
}
|
|
|
|
$invoices[] = [
|
|
'ntsConfirmNum' => $item->NTSConfirmNum ?? '', // 국세청 승인번호
|
|
'writeDate' => $writeDate,
|
|
'writeDateFormatted' => $formattedDate,
|
|
'invoicerCorpNum' => $item->InvoicerCorpNum ?? '', // 공급자 사업자번호
|
|
'invoicerCorpName' => $item->InvoicerCorpName ?? '', // 공급자 상호
|
|
'invoiceeCorpNum' => $item->InvoiceeCorpNum ?? '', // 공급받는자 사업자번호
|
|
'invoiceeCorpName' => $item->InvoiceeCorpName ?? '', // 공급받는자 상호
|
|
'supplyAmount' => $supplyAmount,
|
|
'supplyAmountFormatted' => number_format($supplyAmount),
|
|
'taxAmount' => $taxAmount,
|
|
'taxAmountFormatted' => number_format($taxAmount),
|
|
'totalAmount' => $supplyAmount + $taxAmount,
|
|
'totalAmountFormatted' => number_format($supplyAmount + $taxAmount),
|
|
'taxType' => $item->TaxType ?? '', // 과세유형
|
|
'taxTypeName' => $this->getTaxTypeName($item->TaxType ?? ''),
|
|
'issueType' => $item->IssueType ?? '', // 발급유형
|
|
'issueTypeName' => $this->getIssueTypeName($item->IssueType ?? ''),
|
|
'purposeType' => $item->PurposeType ?? '', // 영수/청구
|
|
'purposeTypeName' => $this->getPurposeTypeName($item->PurposeType ?? ''),
|
|
'modifyCode' => $item->ModifyCode ?? '',
|
|
'remark' => $item->Remark1 ?? '',
|
|
'collectDate' => $item->CollectDate ?? '', // 수집일
|
|
];
|
|
}
|
|
|
|
return [
|
|
'invoices' => $invoices,
|
|
'summary' => [
|
|
'totalAmount' => $totalAmount,
|
|
'totalTax' => $totalTax,
|
|
'totalSum' => $totalAmount + $totalTax,
|
|
'count' => count($invoices)
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 에러 코드 체크
|
|
*/
|
|
private function checkErrorCode($data): ?int
|
|
{
|
|
if (isset($data->CurrentPage) && is_numeric($data->CurrentPage) && $data->CurrentPage < 0) {
|
|
return (int)$data->CurrentPage;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 에러 메시지 반환
|
|
*/
|
|
private function getErrorMessage(int $errorCode): string
|
|
{
|
|
$messages = [
|
|
-10002 => '인증 실패 (-10002). CERTKEY가 올바르지 않거나 만료되었습니다.',
|
|
-60001 => '등록된 홈택스 정보가 없습니다 (-60001).',
|
|
-60002 => '홈택스 인증서가 등록되지 않았습니다 (-60002).',
|
|
-60003 => '홈택스 수집 서비스가 활성화되지 않았습니다 (-60003).',
|
|
-60004 => '홈택스 부서사용자 ID가 등록되지 않았습니다 (-60004).',
|
|
-60005 => '조회된 데이터가 없습니다 (-60005).',
|
|
-60010 => '홈택스 로그인 실패 (-60010). 부서사용자 ID/비밀번호를 확인해주세요.',
|
|
-60011 => '홈택스 데이터 수집 중입니다 (-60011). 잠시 후 다시 조회해주세요.',
|
|
];
|
|
return $messages[$errorCode] ?? '바로빌 API 오류: ' . $errorCode;
|
|
}
|
|
|
|
/**
|
|
* 수집 유형 코드 반환
|
|
*/
|
|
private function getCollectTypeCode(string $type): int
|
|
{
|
|
return match($type) {
|
|
'sales' => 1,
|
|
'purchase' => 2,
|
|
default => 0 // all
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 과세유형 코드 -> 명칭
|
|
*/
|
|
private function getTaxTypeName(string $code): string
|
|
{
|
|
return match($code) {
|
|
'01' => '과세',
|
|
'02' => '영세',
|
|
'03' => '면세',
|
|
default => $code
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 발급유형 코드 -> 명칭
|
|
*/
|
|
private function getIssueTypeName(string $code): string
|
|
{
|
|
return match($code) {
|
|
'01' => '정발행',
|
|
'02' => '역발행',
|
|
'03' => '위수탁',
|
|
default => $code
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 영수/청구 코드 -> 명칭
|
|
*/
|
|
private function getPurposeTypeName(string $code): string
|
|
{
|
|
return match($code) {
|
|
'01' => '영수',
|
|
'02' => '청구',
|
|
default => $code
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 수집 상태 파싱
|
|
*/
|
|
private function parseCollectState($data): array
|
|
{
|
|
return [
|
|
'salesLastCollectDate' => $data->SalesLastCollectDate ?? '',
|
|
'purchaseLastCollectDate' => $data->PurchaseLastCollectDate ?? '',
|
|
'isCollecting' => ($data->CollectState ?? 0) == 1,
|
|
'collectStateText' => ($data->CollectState ?? 0) == 1 ? '수집 중' : '대기',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 엑셀 다운로드
|
|
*/
|
|
public function exportExcel(Request $request): StreamedResponse|JsonResponse
|
|
{
|
|
try {
|
|
$type = $request->input('type', 'sales'); // sales or purchase
|
|
$invoices = $request->input('invoices', []);
|
|
|
|
if (empty($invoices)) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => '저장할 데이터가 없습니다.'
|
|
]);
|
|
}
|
|
|
|
$typeName = $type === 'sales' ? '매출' : '매입';
|
|
$filename = "홈택스_{$typeName}_" . date('Ymd_His') . ".csv";
|
|
|
|
return response()->streamDownload(function () use ($invoices, $type) {
|
|
$handle = fopen('php://output', 'w');
|
|
|
|
// UTF-8 BOM
|
|
fprintf($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
|
|
|
// 헤더
|
|
if ($type === 'sales') {
|
|
fputcsv($handle, [
|
|
'작성일', '국세청승인번호', '공급받는자 사업자번호', '공급받는자 상호',
|
|
'공급가액', '세액', '합계', '과세유형', '발급유형', '영수/청구'
|
|
]);
|
|
} else {
|
|
fputcsv($handle, [
|
|
'작성일', '국세청승인번호', '공급자 사업자번호', '공급자 상호',
|
|
'공급가액', '세액', '합계', '과세유형', '발급유형', '영수/청구'
|
|
]);
|
|
}
|
|
|
|
// 데이터
|
|
foreach ($invoices as $inv) {
|
|
if ($type === 'sales') {
|
|
fputcsv($handle, [
|
|
$inv['writeDateFormatted'] ?? '',
|
|
$inv['ntsConfirmNum'] ?? '',
|
|
$inv['invoiceeCorpNum'] ?? '',
|
|
$inv['invoiceeCorpName'] ?? '',
|
|
$inv['supplyAmount'] ?? 0,
|
|
$inv['taxAmount'] ?? 0,
|
|
$inv['totalAmount'] ?? 0,
|
|
$inv['taxTypeName'] ?? '',
|
|
$inv['issueTypeName'] ?? '',
|
|
$inv['purposeTypeName'] ?? ''
|
|
]);
|
|
} else {
|
|
fputcsv($handle, [
|
|
$inv['writeDateFormatted'] ?? '',
|
|
$inv['ntsConfirmNum'] ?? '',
|
|
$inv['invoicerCorpNum'] ?? '',
|
|
$inv['invoicerCorpName'] ?? '',
|
|
$inv['supplyAmount'] ?? 0,
|
|
$inv['taxAmount'] ?? 0,
|
|
$inv['totalAmount'] ?? 0,
|
|
$inv['taxTypeName'] ?? '',
|
|
$inv['issueTypeName'] ?? '',
|
|
$inv['purposeTypeName'] ?? ''
|
|
]);
|
|
}
|
|
}
|
|
|
|
fclose($handle);
|
|
}, $filename, [
|
|
'Content-Type' => 'text/csv; charset=utf-8',
|
|
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
Log::error('홈택스 엑셀 다운로드 오류: ' . $e->getMessage());
|
|
return response()->json([
|
|
'success' => false,
|
|
'error' => '다운로드 오류: ' . $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SOAP 호출
|
|
*/
|
|
private function callSoap(string $method, array $params = []): array
|
|
{
|
|
if (!$this->soapClient) {
|
|
return [
|
|
'success' => false,
|
|
'error' => '바로빌 홈택스 SOAP 클라이언트가 초기화되지 않았습니다.'
|
|
];
|
|
}
|
|
|
|
if (empty($this->certKey) && !$this->isTestMode) {
|
|
return [
|
|
'success' => false,
|
|
'error' => 'CERTKEY가 설정되지 않았습니다.'
|
|
];
|
|
}
|
|
|
|
if (empty($this->corpNum)) {
|
|
return [
|
|
'success' => false,
|
|
'error' => '사업자번호가 설정되지 않았습니다.'
|
|
];
|
|
}
|
|
|
|
// CERTKEY와 CorpNum 자동 추가
|
|
if (!isset($params['CERTKEY'])) {
|
|
$params['CERTKEY'] = $this->certKey ?? '';
|
|
}
|
|
if (!isset($params['CorpNum'])) {
|
|
$params['CorpNum'] = $this->corpNum;
|
|
}
|
|
|
|
try {
|
|
Log::info("바로빌 홈택스 API 호출 - Method: {$method}, CorpNum: {$this->corpNum}");
|
|
|
|
$result = $this->soapClient->$method($params);
|
|
$resultProperty = $method . 'Result';
|
|
|
|
if (isset($result->$resultProperty)) {
|
|
$resultData = $result->$resultProperty;
|
|
|
|
// 에러 코드 체크
|
|
if (is_numeric($resultData) && $resultData < 0) {
|
|
return [
|
|
'success' => false,
|
|
'error' => $this->getErrorMessage((int)$resultData),
|
|
'error_code' => (int)$resultData
|
|
];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'data' => $resultData
|
|
];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'data' => $result
|
|
];
|
|
} catch (\SoapFault $e) {
|
|
Log::error('바로빌 홈택스 SOAP 오류: ' . $e->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'error' => 'SOAP 오류: ' . $e->getMessage(),
|
|
'error_code' => $e->getCode()
|
|
];
|
|
} catch (\Throwable $e) {
|
|
Log::error('바로빌 홈택스 API 호출 오류: ' . $e->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'error' => 'API 호출 오류: ' . $e->getMessage()
|
|
];
|
|
}
|
|
}
|
|
}
|