Files
sam-api/app/Http/Controllers/Api/V1/BarobillSyncController.php
김보곤 269a17b49c feat: [barobill] SOAP 동기화 서비스 신규 구축
- BarobillSoapService: PHP SoapClient 기반 SOAP 래퍼 (회원/계좌/카드/인증서)
- BarobillBankSyncService: 은행 거래내역 SOAP 조회 → DB 캐시 동기화
- BarobillCardSyncService: 카드 거래내역 SOAP 조회 → DB 캐시 동기화
- HometaxSyncService: 홈택스 세금계산서 upsert 동기화
- BarobillSyncController: 동기화/회원/인증서/잔액 API 11개 엔드포인트
- SyncBarobillDataJob: 매일 06:00/06:30 자동 동기화 스케줄러
- BarobillController.status() 보강: 실제 계좌/카드 수 표시
2026-03-17 13:03:24 +09:00

307 lines
10 KiB
PHP

<?php
namespace App\Http\Controllers\Api\V1;
use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Models\Barobill\BarobillMember;
use App\Services\Barobill\BarobillBankSyncService;
use App\Services\Barobill\BarobillCardSyncService;
use App\Services\Barobill\BarobillSoapService;
use App\Services\Barobill\HometaxSyncService;
use Carbon\Carbon;
use Illuminate\Http\Request;
class BarobillSyncController extends Controller
{
public function __construct(
private BarobillSoapService $soapService,
private BarobillBankSyncService $bankSyncService,
private BarobillCardSyncService $cardSyncService,
private HometaxSyncService $hometaxSyncService,
) {}
/**
* 수동 은행 동기화
*/
public function syncBank(Request $request)
{
$data = $request->validate([
'start_date' => 'nullable|date_format:Ymd',
'end_date' => 'nullable|date_format:Ymd',
]);
return ApiResponse::handle(function () use ($data) {
$tenantId = app('tenant_id');
$startDate = $data['start_date'] ?? Carbon::now()->subMonth()->format('Ymd');
$endDate = $data['end_date'] ?? Carbon::now()->format('Ymd');
return $this->bankSyncService->syncIfNeeded($tenantId, $startDate, $endDate);
}, __('message.fetched'));
}
/**
* 수동 카드 동기화
*/
public function syncCard(Request $request)
{
$data = $request->validate([
'start_date' => 'nullable|date_format:Ymd',
'end_date' => 'nullable|date_format:Ymd',
]);
return ApiResponse::handle(function () use ($data) {
$tenantId = app('tenant_id');
$startDate = $data['start_date'] ?? Carbon::now()->subMonth()->format('Ymd');
$endDate = $data['end_date'] ?? Carbon::now()->format('Ymd');
return $this->cardSyncService->syncCardTransactions($tenantId, $startDate, $endDate);
}, __('message.fetched'));
}
/**
* 수동 홈택스 동기화
*/
public function syncHometax(Request $request)
{
$data = $request->validate([
'invoices' => 'required|array',
'invoices.*.ntsConfirmNum' => 'required|string',
'invoice_type' => 'required|in:sales,purchase',
]);
return ApiResponse::handle(function () use ($data) {
$tenantId = app('tenant_id');
return $this->hometaxSyncService->syncInvoices(
$data['invoices'],
$tenantId,
$data['invoice_type']
);
}, __('message.fetched'));
}
/**
* 바로빌 등록계좌 목록 (SOAP 실시간)
*/
public function accounts()
{
return ApiResponse::handle(function () {
$member = $this->getMember();
if (! $member) {
return ['accounts' => [], 'message' => '바로빌 회원 정보 없음'];
}
$this->soapService->initForMember($member);
return [
'accounts' => $this->bankSyncService->getRegisteredAccounts($member),
];
}, __('message.fetched'));
}
/**
* 바로빌 등록카드 목록 (SOAP 실시간)
*/
public function cards()
{
return ApiResponse::handle(function () {
$member = $this->getMember();
if (! $member) {
return ['cards' => [], 'message' => '바로빌 회원 정보 없음'];
}
$this->soapService->initForMember($member);
return [
'cards' => $this->cardSyncService->getRegisteredCards($member),
];
}, __('message.fetched'));
}
/**
* 인증서 상태 조회 (만료일, 유효성)
*/
public function certificate()
{
return ApiResponse::handle(function () {
$member = $this->getMember();
if (! $member) {
return ['certificate' => null, 'message' => '바로빌 회원 정보 없음'];
}
$this->soapService->initForMember($member);
$corpNum = $member->biz_no;
$valid = $this->soapService->checkCertificateValid($corpNum);
$expireDate = $this->soapService->getCertificateExpireDate($corpNum);
$registDate = $this->soapService->getCertificateRegistDate($corpNum);
return [
'certificate' => [
'is_valid' => $valid['success'] && ($valid['data'] ?? 0) >= 0,
'expire_date' => $expireDate['success'] ? ($expireDate['data'] ?? null) : null,
'regist_date' => $registDate['success'] ? ($registDate['data'] ?? null) : null,
],
];
}, __('message.fetched'));
}
/**
* 충전잔액 조회
*/
public function balance()
{
return ApiResponse::handle(function () {
$member = $this->getMember();
if (! $member) {
return ['balance' => null, 'message' => '바로빌 회원 정보 없음'];
}
$this->soapService->initForMember($member);
$result = $this->soapService->getBalanceCostAmount($member->biz_no);
return [
'balance' => $result['success'] ? ($result['data'] ?? 0) : null,
'success' => $result['success'],
'error' => $result['error'] ?? null,
];
}, __('message.fetched'));
}
/**
* 바로빌 회원 등록 (SOAP RegistCorp)
*/
public function registerMember(Request $request)
{
$data = $request->validate([
'biz_no' => 'required|string|size:10',
'corp_name' => 'required|string',
'ceo_name' => 'required|string',
'biz_type' => 'nullable|string',
'biz_class' => 'nullable|string',
'addr' => 'nullable|string',
'barobill_id' => 'required|string',
'barobill_pwd' => 'required|string',
'manager_name' => 'nullable|string',
'manager_hp' => 'nullable|string',
'manager_email' => 'nullable|email',
]);
return ApiResponse::handle(function () use ($data) {
$tenantId = app('tenant_id');
$this->soapService->initForMember(
BarobillMember::withoutGlobalScopes()->where('tenant_id', $tenantId)->first()
?? new BarobillMember(['server_mode' => 'test'])
);
$result = $this->soapService->registCorp($data);
if ($result['success']) {
BarobillMember::withoutGlobalScopes()->updateOrCreate(
['tenant_id' => $tenantId],
[
'biz_no' => $data['biz_no'],
'corp_name' => $data['corp_name'],
'ceo_name' => $data['ceo_name'],
'biz_type' => $data['biz_type'] ?? null,
'biz_class' => $data['biz_class'] ?? null,
'addr' => $data['addr'] ?? null,
'barobill_id' => $data['barobill_id'],
'barobill_pwd' => $data['barobill_pwd'],
'manager_name' => $data['manager_name'] ?? null,
'manager_hp' => $data['manager_hp'] ?? null,
'manager_email' => $data['manager_email'] ?? null,
'status' => 'active',
]
);
}
return $result;
}, __('message.created'));
}
/**
* 바로빌 회원 수정 (SOAP UpdateCorpInfo)
*/
public function updateMember(Request $request)
{
$data = $request->validate([
'corp_name' => 'required|string',
'ceo_name' => 'required|string',
'biz_type' => 'nullable|string',
'biz_class' => 'nullable|string',
'addr' => 'nullable|string',
'manager_name' => 'nullable|string',
'manager_hp' => 'nullable|string',
'manager_email' => 'nullable|email',
]);
return ApiResponse::handle(function () use ($data) {
$member = $this->getMember();
if (! $member) {
return ['error' => 'NO_MEMBER', 'code' => 404, 'message' => '바로빌 회원 정보 없음'];
}
$this->soapService->initForMember($member);
$data['biz_no'] = $member->biz_no;
$result = $this->soapService->updateCorpInfo($data);
if ($result['success']) {
$member->update([
'corp_name' => $data['corp_name'],
'ceo_name' => $data['ceo_name'],
'biz_type' => $data['biz_type'] ?? $member->biz_type,
'biz_class' => $data['biz_class'] ?? $member->biz_class,
'addr' => $data['addr'] ?? $member->addr,
'manager_name' => $data['manager_name'] ?? $member->manager_name,
'manager_hp' => $data['manager_hp'] ?? $member->manager_hp,
'manager_email' => $data['manager_email'] ?? $member->manager_email,
]);
}
return $result;
}, __('message.updated'));
}
/**
* 바로빌 회원 상태 (SOAP GetCorpState)
*/
public function memberStatus()
{
return ApiResponse::handle(function () {
$member = $this->getMember();
if (! $member) {
return ['status' => null, 'message' => '바로빌 회원 정보 없음'];
}
$this->soapService->initForMember($member);
$result = $this->soapService->getCorpState($member->biz_no);
return [
'member' => [
'biz_no' => $member->formatted_biz_no,
'corp_name' => $member->corp_name,
'status' => $member->status,
'server_mode' => $member->server_mode,
],
'barobill_state' => $result['success'] ? $result['data'] : null,
'error' => $result['error'] ?? null,
];
}, __('message.fetched'));
}
/**
* 현재 테넌트의 바로빌 회원 조회
*/
private function getMember(): ?BarobillMember
{
$tenantId = app('tenant_id');
return BarobillMember::withoutGlobalScopes()
->where('tenant_id', $tenantId)
->first();
}
}