feat: [barobill] 카카오톡/SMS 발송 API 이관
- BarobillKakaotalkController 생성 (12개 엔드포인트) - 채널/템플릿 조회, 알림톡/친구톡 발송, 결과 조회, 예약 취소 - BarobillSmsController 생성 (4개 엔드포인트) - SMS 발송, 발신번호 관리, 전송 상태 조회 - finance.php 라우트 등록 (barobill/kakaotalk/*, barobill/sms/*) - 기존 BarobillSoapService SOAP 메서드 활용
This commit is contained in:
379
app/Http/Controllers/Api/V1/BarobillKakaotalkController.php
Normal file
379
app/Http/Controllers/Api/V1/BarobillKakaotalkController.php
Normal file
@@ -0,0 +1,379 @@
|
||||
<?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\BarobillSoapService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BarobillKakaotalkController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private BarobillSoapService $soapService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 카카오톡 채널 목록
|
||||
*/
|
||||
public function channels()
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->getKakaotalkChannels($corpNum);
|
||||
|
||||
return $this->formatResult($result, 'channels');
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카카오톡 채널 관리 URL
|
||||
*/
|
||||
public function channelManagementUrl()
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->getKakaotalkChannelManagementUrl($corpNum, $member->barobill_id);
|
||||
|
||||
return $this->formatResult($result, 'url');
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카카오톡 템플릿 목록
|
||||
*/
|
||||
public function templates(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'channel_id' => 'required|string',
|
||||
]);
|
||||
|
||||
return ApiResponse::handle(function () use ($data) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->getKakaotalkTemplates($corpNum, $data['channel_id']);
|
||||
|
||||
return $this->formatResult($result, 'templates');
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카카오톡 템플릿 관리 URL
|
||||
*/
|
||||
public function templateManagementUrl()
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->getKakaotalkTemplateManagementUrl($corpNum, $member->barobill_id);
|
||||
|
||||
return $this->formatResult($result, 'url');
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 알림톡 발송
|
||||
*/
|
||||
public function sendAlimtalk(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'sender_id' => 'required|string',
|
||||
'template_name' => 'required|string',
|
||||
'receiver_name' => 'required|string',
|
||||
'receiver_num' => 'required|string',
|
||||
'title' => 'nullable|string',
|
||||
'message' => 'required|string',
|
||||
'buttons' => 'nullable|array',
|
||||
'buttons.*.Name' => 'required_with:buttons|string',
|
||||
'buttons.*.ButtonType' => 'required_with:buttons|string',
|
||||
'buttons.*.Url1' => 'nullable|string',
|
||||
'buttons.*.Url2' => 'nullable|string',
|
||||
'sms_message' => 'nullable|string',
|
||||
'sms_subject' => 'nullable|string',
|
||||
'reserve_dt' => 'nullable|string',
|
||||
]);
|
||||
|
||||
return ApiResponse::handle(function () use ($data) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$buttons = $data['buttons'] ?? [];
|
||||
|
||||
if (! empty($buttons)) {
|
||||
$result = $this->soapService->sendATKakaotalkEx(
|
||||
$corpNum,
|
||||
$data['sender_id'],
|
||||
$data['sender_id'],
|
||||
$data['template_name'],
|
||||
$data['receiver_name'],
|
||||
$data['receiver_num'],
|
||||
$data['title'] ?? '',
|
||||
$data['message'],
|
||||
$buttons,
|
||||
$data['sms_message'] ?? '',
|
||||
$data['sms_subject'] ?? '',
|
||||
'',
|
||||
$data['reserve_dt'] ?? ''
|
||||
);
|
||||
} else {
|
||||
$result = $this->soapService->sendATKakaotalk(
|
||||
$corpNum,
|
||||
$data['sender_id'],
|
||||
$data['sender_id'],
|
||||
$data['template_name'],
|
||||
$data['receiver_name'],
|
||||
$data['receiver_num'],
|
||||
$data['title'] ?? '',
|
||||
$data['message'],
|
||||
$data['sms_message'] ?? '',
|
||||
$data['sms_subject'] ?? '',
|
||||
'',
|
||||
$data['reserve_dt'] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
return $this->formatResult($result, 'send_key');
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 알림톡 대량 발송
|
||||
*/
|
||||
public function sendAlimtalkBulk(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'sender_id' => 'required|string',
|
||||
'template_name' => 'required|string',
|
||||
'messages' => 'required|array|min:1',
|
||||
'messages.*.ReceiverName' => 'required|string',
|
||||
'messages.*.ReceiverNum' => 'required|string',
|
||||
'messages.*.Title' => 'nullable|string',
|
||||
'messages.*.Message' => 'required|string',
|
||||
'messages.*.SmsMessage' => 'nullable|string',
|
||||
'messages.*.SmsSubject' => 'nullable|string',
|
||||
'reserve_dt' => 'nullable|string',
|
||||
]);
|
||||
|
||||
return ApiResponse::handle(function () use ($data) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->sendATKakaotalks(
|
||||
$corpNum,
|
||||
$data['sender_id'],
|
||||
$data['sender_id'],
|
||||
$data['template_name'],
|
||||
$data['messages'],
|
||||
$data['reserve_dt'] ?? ''
|
||||
);
|
||||
|
||||
return $this->formatResult($result, 'send_key');
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 친구톡 텍스트 발송
|
||||
*/
|
||||
public function sendFriendtalk(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'sender_id' => 'required|string',
|
||||
'channel_id' => 'required|string',
|
||||
'receiver_name' => 'required|string',
|
||||
'receiver_num' => 'required|string',
|
||||
'message' => 'required|string',
|
||||
'buttons' => 'nullable|array',
|
||||
'sms_message' => 'nullable|string',
|
||||
'sms_subject' => 'nullable|string',
|
||||
'ad_yn' => 'nullable|boolean',
|
||||
'reserve_dt' => 'nullable|string',
|
||||
]);
|
||||
|
||||
return ApiResponse::handle(function () use ($data) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->sendFTKakaotalk(
|
||||
$corpNum,
|
||||
$data['sender_id'],
|
||||
$data['channel_id'],
|
||||
$data['receiver_name'],
|
||||
$data['receiver_num'],
|
||||
$data['message'],
|
||||
$data['buttons'] ?? [],
|
||||
$data['sms_message'] ?? '',
|
||||
$data['sms_subject'] ?? '',
|
||||
$data['ad_yn'] ?? false,
|
||||
$data['reserve_dt'] ?? ''
|
||||
);
|
||||
|
||||
return $this->formatResult($result, 'send_key');
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 친구톡 이미지 발송
|
||||
*/
|
||||
public function sendFriendtalkImage(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'sender_id' => 'required|string',
|
||||
'channel_id' => 'required|string',
|
||||
'receiver_name' => 'required|string',
|
||||
'receiver_num' => 'required|string',
|
||||
'message' => 'required|string',
|
||||
'image_url' => 'required|string',
|
||||
'buttons' => 'nullable|array',
|
||||
'sms_message' => 'nullable|string',
|
||||
'sms_subject' => 'nullable|string',
|
||||
'ad_yn' => 'nullable|boolean',
|
||||
'reserve_dt' => 'nullable|string',
|
||||
]);
|
||||
|
||||
return ApiResponse::handle(function () use ($data) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->sendFIKakaotalk(
|
||||
$corpNum,
|
||||
$data['sender_id'],
|
||||
$data['channel_id'],
|
||||
$data['receiver_name'],
|
||||
$data['receiver_num'],
|
||||
$data['message'],
|
||||
$data['image_url'],
|
||||
$data['buttons'] ?? [],
|
||||
$data['sms_message'] ?? '',
|
||||
$data['sms_subject'] ?? '',
|
||||
$data['ad_yn'] ?? false,
|
||||
$data['reserve_dt'] ?? ''
|
||||
);
|
||||
|
||||
return $this->formatResult($result, 'send_key');
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 친구톡 와이드 이미지 발송
|
||||
*/
|
||||
public function sendFriendtalkWide(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'sender_id' => 'required|string',
|
||||
'channel_id' => 'required|string',
|
||||
'receiver_name' => 'required|string',
|
||||
'receiver_num' => 'required|string',
|
||||
'message' => 'required|string',
|
||||
'image_url' => 'required|string',
|
||||
'buttons' => 'nullable|array',
|
||||
'sms_message' => 'nullable|string',
|
||||
'sms_subject' => 'nullable|string',
|
||||
'ad_yn' => 'nullable|boolean',
|
||||
'reserve_dt' => 'nullable|string',
|
||||
]);
|
||||
|
||||
return ApiResponse::handle(function () use ($data) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->sendFWKakaotalk(
|
||||
$corpNum,
|
||||
$data['sender_id'],
|
||||
$data['channel_id'],
|
||||
$data['receiver_name'],
|
||||
$data['receiver_num'],
|
||||
$data['message'],
|
||||
$data['image_url'],
|
||||
$data['buttons'] ?? [],
|
||||
$data['sms_message'] ?? '',
|
||||
$data['sms_subject'] ?? '',
|
||||
$data['ad_yn'] ?? false,
|
||||
$data['reserve_dt'] ?? ''
|
||||
);
|
||||
|
||||
return $this->formatResult($result, 'send_key');
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 전송 결과 조회 (단건)
|
||||
*/
|
||||
public function getSendResult(string $sendKey)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($sendKey) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->getSendKakaotalk($corpNum, $sendKey);
|
||||
|
||||
return $this->formatResult($result, 'result');
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 전송 결과 조회 (다건)
|
||||
*/
|
||||
public function getSendResults(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'send_keys' => 'required|array|min:1',
|
||||
'send_keys.*' => 'required|string',
|
||||
]);
|
||||
|
||||
return ApiResponse::handle(function () use ($data) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->getSendKakaotalks($corpNum, $data['send_keys']);
|
||||
|
||||
return $this->formatResult($result, 'results');
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 예약 발송 취소
|
||||
*/
|
||||
public function cancelReserved(string $sendKey)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($sendKey) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->cancelReservedKakaotalk($corpNum, $sendKey);
|
||||
|
||||
return $this->formatResult($result, 'result');
|
||||
}, __('message.deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 바로빌 회원 조회 및 SOAP 초기화
|
||||
*/
|
||||
private function initSoap(): array
|
||||
{
|
||||
$member = $this->getMember();
|
||||
if (! $member) {
|
||||
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException(
|
||||
__('error.barobill.member_not_found', [], 'ko') ?: '바로빌 회원사가 등록되어 있지 않습니다.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->soapService->initForMember($member);
|
||||
|
||||
return [$member, $member->biz_no];
|
||||
}
|
||||
|
||||
private function getMember(): ?BarobillMember
|
||||
{
|
||||
$tenantId = app('tenant_id');
|
||||
|
||||
return BarobillMember::withoutGlobalScopes()
|
||||
->where('tenant_id', $tenantId)
|
||||
->first();
|
||||
}
|
||||
|
||||
private function formatResult(array $result, string $key): array
|
||||
{
|
||||
if (! ($result['success'] ?? false)) {
|
||||
throw new \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException(
|
||||
$result['error'] ?? '바로빌 API 호출 실패'
|
||||
);
|
||||
}
|
||||
|
||||
return [$key => $result['data'] ?? null];
|
||||
}
|
||||
}
|
||||
131
app/Http/Controllers/Api/V1/BarobillSmsController.php
Normal file
131
app/Http/Controllers/Api/V1/BarobillSmsController.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?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\BarobillSoapService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BarobillSmsController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private BarobillSoapService $soapService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* SMS 발송
|
||||
*/
|
||||
public function send(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'from_number' => 'required|string',
|
||||
'to_name' => 'required|string',
|
||||
'to_number' => 'required|string',
|
||||
'contents' => 'required|string',
|
||||
'send_dt' => 'nullable|string',
|
||||
'ref_key' => 'nullable|string',
|
||||
]);
|
||||
|
||||
return ApiResponse::handle(function () use ($data) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->sendSMSMessage(
|
||||
$corpNum,
|
||||
$member->barobill_id,
|
||||
$data['from_number'],
|
||||
$data['to_name'],
|
||||
$data['to_number'],
|
||||
$data['contents'],
|
||||
$data['send_dt'] ?? '',
|
||||
$data['ref_key'] ?? ''
|
||||
);
|
||||
|
||||
return $this->formatResult($result, 'send_key');
|
||||
}, __('message.created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 발신번호 목록
|
||||
*/
|
||||
public function fromNumbers()
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->getSMSFromNumbers($corpNum);
|
||||
|
||||
return $this->formatResult($result, 'from_numbers');
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 발신번호 등록 여부 확인
|
||||
*/
|
||||
public function checkFromNumber(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'from_number' => 'required|string',
|
||||
]);
|
||||
|
||||
return ApiResponse::handle(function () use ($data) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->checkSMSFromNumber($corpNum, $data['from_number']);
|
||||
|
||||
return $this->formatResult($result, 'result');
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* SMS 전송 상태 조회
|
||||
*/
|
||||
public function sendState(string $sendKey)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($sendKey) {
|
||||
[$member, $corpNum] = $this->initSoap();
|
||||
|
||||
$result = $this->soapService->getSMSSendState($corpNum, $sendKey);
|
||||
|
||||
return $this->formatResult($result, 'state');
|
||||
}, __('message.fetched'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 바로빌 회원 조회 및 SOAP 초기화
|
||||
*/
|
||||
private function initSoap(): array
|
||||
{
|
||||
$member = $this->getMember();
|
||||
if (! $member) {
|
||||
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException(
|
||||
__('error.barobill.member_not_found', [], 'ko') ?: '바로빌 회원사가 등록되어 있지 않습니다.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->soapService->initForMember($member);
|
||||
|
||||
return [$member, $member->biz_no];
|
||||
}
|
||||
|
||||
private function getMember(): ?BarobillMember
|
||||
{
|
||||
$tenantId = app('tenant_id');
|
||||
|
||||
return BarobillMember::withoutGlobalScopes()
|
||||
->where('tenant_id', $tenantId)
|
||||
->first();
|
||||
}
|
||||
|
||||
private function formatResult(array $result, string $key): array
|
||||
{
|
||||
if (! ($result['success'] ?? false)) {
|
||||
throw new \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException(
|
||||
$result['error'] ?? '바로빌 API 호출 실패'
|
||||
);
|
||||
}
|
||||
|
||||
return [$key => $result['data'] ?? null];
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,9 @@
|
||||
use App\Http\Controllers\Api\V1\BarobillBankTransactionController;
|
||||
use App\Http\Controllers\Api\V1\BarobillCardTransactionController;
|
||||
use App\Http\Controllers\Api\V1\BarobillController;
|
||||
use App\Http\Controllers\Api\V1\BarobillKakaotalkController;
|
||||
use App\Http\Controllers\Api\V1\BarobillSettingController;
|
||||
use App\Http\Controllers\Api\V1\BarobillSmsController;
|
||||
use App\Http\Controllers\Api\V1\BarobillSyncController;
|
||||
use App\Http\Controllers\Api\V1\BillController;
|
||||
use App\Http\Controllers\Api\V1\CalendarController;
|
||||
@@ -397,6 +399,30 @@
|
||||
Route::delete('/{id}/journal-entries', [TaxInvoiceController::class, 'deleteJournalEntries'])->whereNumber('id')->name('v1.tax-invoices.journal-entries.destroy');
|
||||
});
|
||||
|
||||
// Barobill Kakaotalk API (바로빌 카카오톡)
|
||||
Route::prefix('barobill/kakaotalk')->group(function () {
|
||||
Route::get('/channels', [BarobillKakaotalkController::class, 'channels'])->name('v1.barobill.kakaotalk.channels');
|
||||
Route::get('/channels/management-url', [BarobillKakaotalkController::class, 'channelManagementUrl'])->name('v1.barobill.kakaotalk.channels.management-url');
|
||||
Route::get('/templates', [BarobillKakaotalkController::class, 'templates'])->name('v1.barobill.kakaotalk.templates');
|
||||
Route::get('/templates/management-url', [BarobillKakaotalkController::class, 'templateManagementUrl'])->name('v1.barobill.kakaotalk.templates.management-url');
|
||||
Route::post('/send/alimtalk', [BarobillKakaotalkController::class, 'sendAlimtalk'])->name('v1.barobill.kakaotalk.send.alimtalk');
|
||||
Route::post('/send/alimtalk-bulk', [BarobillKakaotalkController::class, 'sendAlimtalkBulk'])->name('v1.barobill.kakaotalk.send.alimtalk-bulk');
|
||||
Route::post('/send/friendtalk', [BarobillKakaotalkController::class, 'sendFriendtalk'])->name('v1.barobill.kakaotalk.send.friendtalk');
|
||||
Route::post('/send/friendtalk-image', [BarobillKakaotalkController::class, 'sendFriendtalkImage'])->name('v1.barobill.kakaotalk.send.friendtalk-image');
|
||||
Route::post('/send/friendtalk-wide', [BarobillKakaotalkController::class, 'sendFriendtalkWide'])->name('v1.barobill.kakaotalk.send.friendtalk-wide');
|
||||
Route::get('/send/{sendKey}', [BarobillKakaotalkController::class, 'getSendResult'])->name('v1.barobill.kakaotalk.send.result');
|
||||
Route::post('/send/results', [BarobillKakaotalkController::class, 'getSendResults'])->name('v1.barobill.kakaotalk.send.results');
|
||||
Route::delete('/send/{sendKey}/cancel', [BarobillKakaotalkController::class, 'cancelReserved'])->name('v1.barobill.kakaotalk.send.cancel');
|
||||
});
|
||||
|
||||
// Barobill SMS API (바로빌 문자)
|
||||
Route::prefix('barobill/sms')->group(function () {
|
||||
Route::post('/send', [BarobillSmsController::class, 'send'])->name('v1.barobill.sms.send');
|
||||
Route::get('/from-numbers', [BarobillSmsController::class, 'fromNumbers'])->name('v1.barobill.sms.from-numbers');
|
||||
Route::post('/check-from-number', [BarobillSmsController::class, 'checkFromNumber'])->name('v1.barobill.sms.check-from-number');
|
||||
Route::get('/send-state/{sendKey}', [BarobillSmsController::class, 'sendState'])->name('v1.barobill.sms.send-state');
|
||||
});
|
||||
|
||||
// Bad Debt API (악성채권 추심관리)
|
||||
Route::prefix('bad-debts')->group(function () {
|
||||
Route::get('', [BadDebtController::class, 'index'])->name('v1.bad-debts.index');
|
||||
|
||||
Reference in New Issue
Block a user