diff --git a/app/Http/Controllers/Api/Admin/Barobill/BarobillKakaotalkController.php b/app/Http/Controllers/Api/Admin/Barobill/BarobillKakaotalkController.php new file mode 100644 index 00000000..6b2216ec --- /dev/null +++ b/app/Http/Controllers/Api/Admin/Barobill/BarobillKakaotalkController.php @@ -0,0 +1,435 @@ +first(); + + if (!$member) { + return response()->json([ + 'success' => false, + 'message' => '바로빌 회원사가 등록되어 있지 않습니다.', + ], 404); + } + + $this->barobillService->setServerMode($member->server_mode ?? 'test'); + + return $member; + } + + // ========================================== + // 채널 관리 + // ========================================== + + /** + * 카카오톡 채널 목록 조회 + */ + public function getChannels(): JsonResponse + { + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->getKakaotalkChannels($member->biz_no); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + /** + * 카카오톡 채널 관리 URL 조회 + */ + public function getChannelManagementUrl(): JsonResponse + { + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->getKakaotalkChannelManagementUrl( + $member->biz_no, + $member->barobill_id + ); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + // ========================================== + // 템플릿 관리 + // ========================================== + + /** + * 카카오톡 템플릿 목록 조회 + */ + public function getTemplates(Request $request): JsonResponse + { + $request->validate([ + 'channel_id' => 'required|string', + ]); + + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->getKakaotalkTemplates( + $member->biz_no, + $request->input('channel_id') + ); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + /** + * 카카오톡 템플릿 관리 URL 조회 + */ + public function getTemplateManagementUrl(): JsonResponse + { + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->getKakaotalkTemplateManagementUrl( + $member->biz_no, + $member->barobill_id + ); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + // ========================================== + // 알림톡 발송 + // ========================================== + + /** + * 알림톡 단건 발송 + */ + public function sendAlimtalk(Request $request): JsonResponse + { + $validated = $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', + ]); + + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $buttons = $validated['buttons'] ?? []; + + // 버튼이 있으면 Ex 버전 사용 + if (!empty($buttons)) { + $result = $this->barobillService->sendATKakaotalkEx( + $member->biz_no, + $validated['sender_id'], + $validated['template_name'], + $validated['receiver_name'], + $validated['receiver_num'], + $validated['title'] ?? '', + $validated['message'], + $buttons, + $validated['sms_message'] ?? '', + $validated['sms_subject'] ?? '', + $validated['reserve_dt'] ?? '' + ); + } else { + $result = $this->barobillService->sendATKakaotalk( + $member->biz_no, + $validated['sender_id'], + $validated['template_name'], + $validated['receiver_name'], + $validated['receiver_num'], + $validated['title'] ?? '', + $validated['message'], + $validated['sms_message'] ?? '', + $validated['sms_subject'] ?? '', + $validated['reserve_dt'] ?? '' + ); + } + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + /** + * 알림톡 대량 발송 + */ + public function sendAlimtalkBulk(Request $request): JsonResponse + { + $validated = $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', + ]); + + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->sendATKakaotalks( + $member->biz_no, + $validated['sender_id'], + $validated['template_name'], + $validated['messages'], + $validated['reserve_dt'] ?? '' + ); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + // ========================================== + // 친구톡 발송 + // ========================================== + + /** + * 친구톡 텍스트 발송 + */ + public function sendFriendtalk(Request $request): JsonResponse + { + $validated = $request->validate([ + 'sender_id' => 'required|string', + 'channel_id' => 'required|string', + 'receiver_name' => 'required|string', + 'receiver_num' => 'required|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', + 'ad_yn' => 'nullable|boolean', + 'reserve_dt' => 'nullable|string', + ]); + + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->sendFTKakaotalk( + $member->biz_no, + $validated['sender_id'], + $validated['channel_id'], + $validated['receiver_name'], + $validated['receiver_num'], + $validated['message'], + $validated['buttons'] ?? [], + $validated['sms_message'] ?? '', + $validated['sms_subject'] ?? '', + $validated['ad_yn'] ?? false, + $validated['reserve_dt'] ?? '' + ); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + /** + * 친구톡 이미지 발송 + */ + public function sendFriendtalkImage(Request $request): JsonResponse + { + $validated = $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|url', + 'buttons' => 'nullable|array', + 'sms_message' => 'nullable|string', + 'sms_subject' => 'nullable|string', + 'ad_yn' => 'nullable|boolean', + 'reserve_dt' => 'nullable|string', + ]); + + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->sendFIKakaotalk( + $member->biz_no, + $validated['sender_id'], + $validated['channel_id'], + $validated['receiver_name'], + $validated['receiver_num'], + $validated['message'], + $validated['image_url'], + $validated['buttons'] ?? [], + $validated['sms_message'] ?? '', + $validated['sms_subject'] ?? '', + $validated['ad_yn'] ?? false, + $validated['reserve_dt'] ?? '' + ); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + /** + * 친구톡 와이드 이미지 발송 + */ + public function sendFriendtalkWide(Request $request): JsonResponse + { + $validated = $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|url', + 'buttons' => 'nullable|array', + 'sms_message' => 'nullable|string', + 'sms_subject' => 'nullable|string', + 'ad_yn' => 'nullable|boolean', + 'reserve_dt' => 'nullable|string', + ]); + + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->sendFWKakaotalk( + $member->biz_no, + $validated['sender_id'], + $validated['channel_id'], + $validated['receiver_name'], + $validated['receiver_num'], + $validated['message'], + $validated['image_url'], + $validated['buttons'] ?? [], + $validated['sms_message'] ?? '', + $validated['sms_subject'] ?? '', + $validated['ad_yn'] ?? false, + $validated['reserve_dt'] ?? '' + ); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + // ========================================== + // 전송 결과 조회 + // ========================================== + + /** + * 전송 결과 조회 (단건) + */ + public function getSendResult(string $sendKey): JsonResponse + { + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->getSendKakaotalk($member->biz_no, $sendKey); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + /** + * 전송 결과 조회 (다건) + */ + public function getSendResults(Request $request): JsonResponse + { + $validated = $request->validate([ + 'send_keys' => 'required|array|min:1', + 'send_keys.*' => 'required|string', + ]); + + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->getSendKakaotalks( + $member->biz_no, + $validated['send_keys'] + ); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } + + // ========================================== + // 예약 취소 + // ========================================== + + /** + * 예약 전송 취소 + */ + public function cancelReserved(string $sendKey): JsonResponse + { + $member = $this->resolveMember(); + if ($member instanceof JsonResponse) return $member; + + $result = $this->barobillService->cancelReservedKakaotalk($member->biz_no, $sendKey); + + if (!$result['success']) { + return response()->json($result, 422); + } + + return response()->json($result); + } +} diff --git a/app/Http/Controllers/Barobill/KakaotalkController.php b/app/Http/Controllers/Barobill/KakaotalkController.php new file mode 100644 index 00000000..6fe10865 --- /dev/null +++ b/app/Http/Controllers/Barobill/KakaotalkController.php @@ -0,0 +1,93 @@ +header('HX-Request')) { + return response('', 200)->header('HX-Redirect', route('barobill.kakaotalk.index')); + } + + $tenantId = session('selected_tenant_id', 1); + $currentTenant = Tenant::find($tenantId); + $barobillMember = BarobillMember::where('tenant_id', $tenantId)->first(); + + return view('barobill.kakaotalk.index', compact('currentTenant', 'barobillMember')); + } + + /** + * 채널 관리 + */ + public function channels(Request $request): View|Response + { + if ($request->header('HX-Request')) { + return response('', 200)->header('HX-Redirect', route('barobill.kakaotalk.channels')); + } + + $tenantId = session('selected_tenant_id', 1); + $currentTenant = Tenant::find($tenantId); + $barobillMember = BarobillMember::where('tenant_id', $tenantId)->first(); + + return view('barobill.kakaotalk.channels.index', compact('currentTenant', 'barobillMember')); + } + + /** + * 템플릿 관리 + */ + public function templates(Request $request): View|Response + { + if ($request->header('HX-Request')) { + return response('', 200)->header('HX-Redirect', route('barobill.kakaotalk.templates')); + } + + $tenantId = session('selected_tenant_id', 1); + $currentTenant = Tenant::find($tenantId); + $barobillMember = BarobillMember::where('tenant_id', $tenantId)->first(); + + return view('barobill.kakaotalk.templates.index', compact('currentTenant', 'barobillMember')); + } + + /** + * 발송 (알림톡/친구톡) + */ + public function send(Request $request): View|Response + { + if ($request->header('HX-Request')) { + return response('', 200)->header('HX-Redirect', route('barobill.kakaotalk.send')); + } + + $tenantId = session('selected_tenant_id', 1); + $currentTenant = Tenant::find($tenantId); + $barobillMember = BarobillMember::where('tenant_id', $tenantId)->first(); + + return view('barobill.kakaotalk.send.index', compact('currentTenant', 'barobillMember')); + } + + /** + * 전송내역 조회 + */ + public function history(Request $request): View|Response + { + if ($request->header('HX-Request')) { + return response('', 200)->header('HX-Redirect', route('barobill.kakaotalk.history')); + } + + $tenantId = session('selected_tenant_id', 1); + $currentTenant = Tenant::find($tenantId); + $barobillMember = BarobillMember::where('tenant_id', $tenantId)->first(); + + return view('barobill.kakaotalk.history.index', compact('currentTenant', 'barobillMember')); + } +} diff --git a/app/Services/Barobill/BarobillService.php b/app/Services/Barobill/BarobillService.php index 39b82202..b5a3d46a 100644 --- a/app/Services/Barobill/BarobillService.php +++ b/app/Services/Barobill/BarobillService.php @@ -18,6 +18,7 @@ * @see https://ws.baroservice.com/TI.asmx (세금계산서) * @see https://ws.baroservice.com/BANKACCOUNT.asmx (계좌조회) * @see https://ws.baroservice.com/CARD.asmx (카드조회) + * @see https://ws.baroservice.com/KAKAOTALK.asmx (카카오톡) */ class BarobillService { @@ -41,6 +42,11 @@ class BarobillService */ protected ?SoapClient $cardClient = null; + /** + * SOAP 클라이언트 (카카오톡용 - KAKAOTALK) + */ + protected ?SoapClient $kakaotalkClient = null; + /** * CERTKEY (인증키) */ @@ -115,6 +121,7 @@ public function switchServerMode(bool $isTestMode): self $this->tiClient = null; $this->bankAccountClient = null; $this->cardClient = null; + $this->kakaotalkClient = null; // 설정 재로드 $this->initializeConfig(); } @@ -167,6 +174,7 @@ protected function initializeConfig(): void 'ti' => $baseUrl . '/TI.asmx?WSDL', 'bankaccount' => $baseUrl . '/BANKACCOUNT.asmx?WSDL', 'card' => $baseUrl . '/CARD.asmx?WSDL', + 'kakaotalk' => $baseUrl . '/KAKAOTALK.asmx?WSDL', ]; } } @@ -197,6 +205,7 @@ protected function buildSoapUrls(string $baseUrl): array 'ti' => $baseUrl . '/TI.asmx?WSDL', 'bankaccount' => $baseUrl . '/BANKACCOUNT.asmx?WSDL', 'card' => $baseUrl . '/CARD.asmx?WSDL', + 'kakaotalk' => $baseUrl . '/KAKAOTALK.asmx?WSDL', ]; } @@ -314,6 +323,7 @@ protected function call(string $service, string $method, array $params = []): ar 'ti' => $this->getTiClient(), 'bankaccount' => $this->getBankAccountClient(), 'card' => $this->getCardClient(), + 'kakaotalk' => $this->getKakaotalkClient(), default => null, }; @@ -1158,4 +1168,439 @@ public function getHomeTaxUrl(string $corpNum, string $userId, string $userPwd): return $this->call('ti', 'GetHomeTaxURL', $params); } + + // ======================================== + // 카카오톡 서비스 (KAKAOTALK) + // ======================================== + + /** + * KAKAOTALK SOAP 클라이언트 가져오기 + */ + protected function getKakaotalkClient(): ?SoapClient + { + if ($this->kakaotalkClient === null) { + try { + $this->kakaotalkClient = new SoapClient($this->soapUrls['kakaotalk'], [ + 'trace' => true, + 'encoding' => 'UTF-8', + 'exceptions' => true, + 'connection_timeout' => 30, + 'cache_wsdl' => WSDL_CACHE_NONE, + ]); + } catch (Throwable $e) { + Log::error('바로빌 KAKAOTALK SOAP 클라이언트 생성 실패', [ + 'error' => $e->getMessage(), + 'url' => $this->soapUrls['kakaotalk'], + ]); + return null; + } + } + + return $this->kakaotalkClient; + } + + // --- 채널 관리 --- + + /** + * 카카오톡 채널 목록 조회 + * + * @param string $corpNum 회원사 사업자번호 + */ + public function getKakaotalkChannels(string $corpNum): array + { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + ]; + + return $this->call('kakaotalk', 'GetKakaotalkChannels', $params); + } + + /** + * 카카오톡 채널 관리 URL 조회 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $id 회원사 사용자 ID + */ + public function getKakaotalkChannelManagementUrl(string $corpNum, string $id): array + { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'ID' => $id, + ]; + + return $this->call('kakaotalk', 'GetKakaotalkChannelManagementURL', $params); + } + + // --- 템플릿 관리 --- + + /** + * 카카오톡 템플릿 목록 조회 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $channelId 카카오톡 채널 ID + */ + public function getKakaotalkTemplates(string $corpNum, string $channelId): array + { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'ChannelId' => $channelId, + ]; + + return $this->call('kakaotalk', 'GetKakaotalkTemplates', $params); + } + + /** + * 카카오톡 템플릿 관리 URL 조회 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $id 회원사 사용자 ID + */ + public function getKakaotalkTemplateManagementUrl(string $corpNum, string $id): array + { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'ID' => $id, + ]; + + return $this->call('kakaotalk', 'GetKakaotalkTemplateManagementURL', $params); + } + + // --- 알림톡 발송 --- + + /** + * 알림톡 단건 발송 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $senderId 발신프로필 키 (채널 ID) + * @param string $templateName 템플릿 이름 + * @param string $receiverName 수신자 이름 + * @param string $receiverNum 수신자 전화번호 + * @param string $title 알림톡 제목 + * @param string $message 알림톡 본문 + * @param string $smsMessage SMS 대체발송 메시지 (빈 값이면 대체발송 안 함) + * @param string $smsSubject SMS 대체발송 제목 + * @param string $reserveDT 예약일시 (YYYYMMDDHHmmss, 빈 값이면 즉시발송) + */ + public function sendATKakaotalk( + string $corpNum, + string $senderId, + string $templateName, + string $receiverName, + string $receiverNum, + string $title, + string $message, + string $smsMessage = '', + string $smsSubject = '', + string $reserveDT = '' + ): array { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SenderID' => $senderId, + 'TemplateName' => $templateName, + 'ReceiverName' => $receiverName, + 'ReceiverNum' => $receiverNum, + 'Title' => $title, + 'Message' => $message, + 'SmsMessage' => $smsMessage, + 'SmsSubject' => $smsSubject, + 'ReserveDT' => $reserveDT, + ]; + + return $this->call('kakaotalk', 'SendATKakaotalk', $params); + } + + /** + * 알림톡 단건 발송 (버튼 포함) + * + * @param string $corpNum 회원사 사업자번호 + * @param string $senderId 발신프로필 키 + * @param string $templateName 템플릿 이름 + * @param string $receiverName 수신자 이름 + * @param string $receiverNum 수신자 전화번호 + * @param string $title 알림톡 제목 + * @param string $message 알림톡 본문 + * @param array $buttons 버튼 배열 [{Name, ButtonType, Url1, Url2}] + * @param string $smsMessage SMS 대체발송 메시지 + * @param string $smsSubject SMS 대체발송 제목 + * @param string $reserveDT 예약일시 + */ + public function sendATKakaotalkEx( + string $corpNum, + string $senderId, + string $templateName, + string $receiverName, + string $receiverNum, + string $title, + string $message, + array $buttons = [], + string $smsMessage = '', + string $smsSubject = '', + string $reserveDT = '' + ): array { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SenderID' => $senderId, + 'TemplateName' => $templateName, + 'ReceiverName' => $receiverName, + 'ReceiverNum' => $receiverNum, + 'Title' => $title, + 'Message' => $message, + 'Buttons' => $buttons, + 'SmsMessage' => $smsMessage, + 'SmsSubject' => $smsSubject, + 'ReserveDT' => $reserveDT, + ]; + + return $this->call('kakaotalk', 'SendATKakaotalkEx', $params); + } + + /** + * 알림톡 대량 발송 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $senderId 발신프로필 키 + * @param string $templateName 템플릿 이름 + * @param array $messages 메시지 배열 [{ReceiverName, ReceiverNum, Title, Message, SmsMessage, SmsSubject}] + * @param string $reserveDT 예약일시 + */ + public function sendATKakaotalks( + string $corpNum, + string $senderId, + string $templateName, + array $messages, + string $reserveDT = '' + ): array { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SenderID' => $senderId, + 'TemplateName' => $templateName, + 'Messages' => $messages, + 'ReserveDT' => $reserveDT, + ]; + + return $this->call('kakaotalk', 'SendATKakaotalks', $params); + } + + // --- 친구톡 발송 --- + + /** + * 친구톡 텍스트 단건 발송 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $senderId 발신프로필 키 + * @param string $channelId 채널 ID + * @param string $receiverName 수신자 이름 + * @param string $receiverNum 수신자 전화번호 + * @param string $message 메시지 내용 + * @param array $buttons 버튼 배열 + * @param string $smsMessage SMS 대체발송 메시지 + * @param string $smsSubject SMS 대체발송 제목 + * @param bool $adYn 광고 여부 + * @param string $reserveDT 예약일시 + */ + public function sendFTKakaotalk( + string $corpNum, + string $senderId, + string $channelId, + string $receiverName, + string $receiverNum, + string $message, + array $buttons = [], + string $smsMessage = '', + string $smsSubject = '', + bool $adYn = false, + string $reserveDT = '' + ): array { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SenderID' => $senderId, + 'ChannelId' => $channelId, + 'ReceiverName' => $receiverName, + 'ReceiverNum' => $receiverNum, + 'Message' => $message, + 'Buttons' => $buttons, + 'SmsMessage' => $smsMessage, + 'SmsSubject' => $smsSubject, + 'AdYn' => $adYn ? 'Y' : 'N', + 'ReserveDT' => $reserveDT, + ]; + + return $this->call('kakaotalk', 'SendFTKakaotalk', $params); + } + + /** + * 친구톡 대량 발송 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $senderId 발신프로필 키 + * @param string $channelId 채널 ID + * @param array $messages 메시지 배열 + * @param bool $adYn 광고 여부 + * @param string $reserveDT 예약일시 + */ + public function sendFTKakaotalks( + string $corpNum, + string $senderId, + string $channelId, + array $messages, + bool $adYn = false, + string $reserveDT = '' + ): array { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SenderID' => $senderId, + 'ChannelId' => $channelId, + 'Messages' => $messages, + 'AdYn' => $adYn ? 'Y' : 'N', + 'ReserveDT' => $reserveDT, + ]; + + return $this->call('kakaotalk', 'SendFTKakaotalks', $params); + } + + /** + * 친구톡 이미지 발송 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $senderId 발신프로필 키 + * @param string $channelId 채널 ID + * @param string $receiverName 수신자 이름 + * @param string $receiverNum 수신자 전화번호 + * @param string $message 메시지 내용 + * @param string $imageUrl 이미지 URL + * @param array $buttons 버튼 배열 + * @param string $smsMessage SMS 대체발송 메시지 + * @param string $smsSubject SMS 대체발송 제목 + * @param bool $adYn 광고 여부 + * @param string $reserveDT 예약일시 + */ + public function sendFIKakaotalk( + string $corpNum, + string $senderId, + string $channelId, + string $receiverName, + string $receiverNum, + string $message, + string $imageUrl, + array $buttons = [], + string $smsMessage = '', + string $smsSubject = '', + bool $adYn = false, + string $reserveDT = '' + ): array { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SenderID' => $senderId, + 'ChannelId' => $channelId, + 'ReceiverName' => $receiverName, + 'ReceiverNum' => $receiverNum, + 'Message' => $message, + 'ImageURL' => $imageUrl, + 'Buttons' => $buttons, + 'SmsMessage' => $smsMessage, + 'SmsSubject' => $smsSubject, + 'AdYn' => $adYn ? 'Y' : 'N', + 'ReserveDT' => $reserveDT, + ]; + + return $this->call('kakaotalk', 'SendFIKakaotalk', $params); + } + + /** + * 친구톡 와이드 이미지 발송 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $senderId 발신프로필 키 + * @param string $channelId 채널 ID + * @param string $receiverName 수신자 이름 + * @param string $receiverNum 수신자 전화번호 + * @param string $message 메시지 내용 + * @param string $imageUrl 와이드 이미지 URL + * @param array $buttons 버튼 배열 + * @param string $smsMessage SMS 대체발송 메시지 + * @param string $smsSubject SMS 대체발송 제목 + * @param bool $adYn 광고 여부 + * @param string $reserveDT 예약일시 + */ + public function sendFWKakaotalk( + string $corpNum, + string $senderId, + string $channelId, + string $receiverName, + string $receiverNum, + string $message, + string $imageUrl, + array $buttons = [], + string $smsMessage = '', + string $smsSubject = '', + bool $adYn = false, + string $reserveDT = '' + ): array { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SenderID' => $senderId, + 'ChannelId' => $channelId, + 'ReceiverName' => $receiverName, + 'ReceiverNum' => $receiverNum, + 'Message' => $message, + 'ImageURL' => $imageUrl, + 'Buttons' => $buttons, + 'SmsMessage' => $smsMessage, + 'SmsSubject' => $smsSubject, + 'AdYn' => $adYn ? 'Y' : 'N', + 'ReserveDT' => $reserveDT, + ]; + + return $this->call('kakaotalk', 'SendFWKakaotalk', $params); + } + + // --- 전송 조회/관리 --- + + /** + * 전송 결과 조회 (단건) + * + * @param string $corpNum 회원사 사업자번호 + * @param string $sendKey 전송키 + */ + public function getSendKakaotalk(string $corpNum, string $sendKey): array + { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SendKey' => $sendKey, + ]; + + return $this->call('kakaotalk', 'GetSendKakaotalk', $params); + } + + /** + * 전송 결과 조회 (다건) + * + * @param string $corpNum 회원사 사업자번호 + * @param array $sendKeyList 전송키 배열 + */ + public function getSendKakaotalks(string $corpNum, array $sendKeyList): array + { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SendKeyList' => $sendKeyList, + ]; + + return $this->call('kakaotalk', 'GetSendKakaotalks', $params); + } + + /** + * 예약 전송 취소 + * + * @param string $corpNum 회원사 사업자번호 + * @param string $sendKey 전송키 + */ + public function cancelReservedKakaotalk(string $corpNum, string $sendKey): array + { + $params = [ + 'CorpNum' => $this->formatBizNo($corpNum), + 'SendKey' => $sendKey, + ]; + + return $this->call('kakaotalk', 'CancelReservedKakaotalk', $params); + } } diff --git a/resources/views/barobill/kakaotalk/channels/index.blade.php b/resources/views/barobill/kakaotalk/channels/index.blade.php new file mode 100644 index 00000000..2edf848d --- /dev/null +++ b/resources/views/barobill/kakaotalk/channels/index.blade.php @@ -0,0 +1,103 @@ +@extends('layouts.app') + +@section('title', '카카오톡 채널 관리') + +@section('content') +
+ +
+
+
+ 카카오톡 + + 채널 관리 +
+

채널 관리

+

카카오톡 채널 목록 조회 및 바로빌 채널 관리 페이지 연결

+
+
+ + +
+
+ + +
+
+
+ +

채널 목록 로딩 중...

+
+
+
+
+@endsection + +@push('scripts') + +@endpush diff --git a/resources/views/barobill/kakaotalk/history/index.blade.php b/resources/views/barobill/kakaotalk/history/index.blade.php new file mode 100644 index 00000000..255ef116 --- /dev/null +++ b/resources/views/barobill/kakaotalk/history/index.blade.php @@ -0,0 +1,170 @@ +@extends('layouts.app') + +@section('title', '카카오톡 전송내역') + +@section('content') +
+ +
+
+
+ 카카오톡 + + 전송내역 +
+

전송내역

+

카카오톡 발송 이력 조회 및 예약 관리

+
+
+ + @if(!$barobillMember) +
+

바로빌 회원사 연동 필요

+

전송내역을 조회하려면 먼저 바로빌 회원사를 등록해주세요.

+
+ @else + +
+

전송키로 조회

+
+ + +
+

여러 건 조회 시 전송키를 쉼표(,)로 구분하세요.

+
+ + +
+
+
+ + + +

전송키를 입력하여 발송 결과를 조회하세요.

+
+
+
+ @endif +
+@endsection + +@push('scripts') + +@endpush diff --git a/resources/views/barobill/kakaotalk/index.blade.php b/resources/views/barobill/kakaotalk/index.blade.php new file mode 100644 index 00000000..696025a7 --- /dev/null +++ b/resources/views/barobill/kakaotalk/index.blade.php @@ -0,0 +1,194 @@ +@extends('layouts.app') + +@section('title', '카카오톡 서비스') + +@section('content') + +@if($currentTenant) +
+
+
+
+ + + +
+
+
+ T-ID: {{ $currentTenant->id }} +
+

{{ $currentTenant->company_name }} - 카카오톡 서비스

+
+
+ @if($barobillMember) +
+
+

사업자번호

+

{{ $barobillMember->biz_no }}

+
+
+

바로빌 ID

+

{{ $barobillMember->barobill_id }}

+
+
+ @else +
+ + + + 바로빌 회원사 미연동 +
+ @endif +
+
+@endif + +
+ +
+
+

카카오톡 서비스

+

알림톡/친구톡 발송 및 관리

+
+
+ + @if(!$barobillMember) +
+ + + +

바로빌 회원사 연동 필요

+

카카오톡 서비스를 이용하려면 먼저 바로빌 회원사를 등록해주세요.

+ 회원사 관리로 이동 +
+ @else + +
+ + +
+ + + +
+

채널 관리

+

카카오톡 채널 목록 조회 및 관리

+
+ + + +
+ + + +
+

템플릿 관리

+

알림톡 템플릿 목록 조회 및 관리

+
+ + + +
+ + + +
+

메시지 발송

+

알림톡/친구톡 메시지 발송

+
+ + + +
+ + + +
+

전송내역

+

발송 이력 조회 및 예약 관리

+
+
+ + +
+
+
+
+ + + +
+
+

채널 현황

+

등록된 카카오톡 채널 목록

+
+
+ +
+
+
+ + + +

채널 정보 로딩 중...

+
+
+
+ @endif +
+@endsection + +@push('scripts') + +@endpush diff --git a/resources/views/barobill/kakaotalk/send/index.blade.php b/resources/views/barobill/kakaotalk/send/index.blade.php new file mode 100644 index 00000000..4ba8638c --- /dev/null +++ b/resources/views/barobill/kakaotalk/send/index.blade.php @@ -0,0 +1,421 @@ +@extends('layouts.app') + +@section('title', '카카오톡 메시지 발송') + +@section('content') +
+ +
+
+
+ 카카오톡 + + 메시지 발송 +
+

메시지 발송

+

알림톡 또는 친구톡 메시지를 발송합니다

+
+
+ + @if(!$barobillMember) +
+

바로빌 회원사 연동 필요

+

메시지 발송을 위해 먼저 바로빌 회원사를 등록해주세요.

+
+ @else + +
+ + +
+ + +
+
+

알림톡 발송

+
+
+ +
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ + +
+ +
+
+ + + (카카오톡 발송 실패 시 SMS로 발송) +
+ +
+ +
+ + +

비워두면 즉시 발송됩니다.

+
+
+ +
+
+
+
+ + + + @endif +
+@endsection + +@push('scripts') + +@endpush diff --git a/resources/views/barobill/kakaotalk/templates/index.blade.php b/resources/views/barobill/kakaotalk/templates/index.blade.php new file mode 100644 index 00000000..721b67c4 --- /dev/null +++ b/resources/views/barobill/kakaotalk/templates/index.blade.php @@ -0,0 +1,191 @@ +@extends('layouts.app') + +@section('title', '카카오톡 템플릿 관리') + +@section('content') +
+ +
+
+
+ 카카오톡 + + 템플릿 관리 +
+

템플릿 관리

+

알림톡 발송에 사용되는 템플릿 조회 및 관리

+
+ +
+ + +
+
+ + + +
+
+ + +
+
+
+

채널을 선택하면 템플릿 목록이 표시됩니다.

+
+
+
+
+ + + +@endsection + +@push('scripts') + +@endpush diff --git a/routes/api.php b/routes/api.php index 12f2a4f3..757fa8df 100644 --- a/routes/api.php +++ b/routes/api.php @@ -204,6 +204,33 @@ Route::put('/pricing-policies/{id}', [\App\Http\Controllers\Api\Admin\Barobill\BarobillBillingController::class, 'updatePricingPolicy'])->name('pricing-policies.update'); }); + // 바로빌 카카오톡 API + Route::prefix('barobill/kakaotalk')->name('barobill.kakaotalk.')->group(function () { + // 채널 관리 + Route::get('/channels', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'getChannels'])->name('channels'); + Route::get('/channels/management-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'getChannelManagementUrl'])->name('channels.management-url'); + + // 템플릿 관리 + Route::get('/templates', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'getTemplates'])->name('templates'); + Route::get('/templates/management-url', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'getTemplateManagementUrl'])->name('templates.management-url'); + + // 알림톡 발송 + Route::post('/send/alimtalk', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'sendAlimtalk'])->name('send.alimtalk'); + Route::post('/send/alimtalk-bulk', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'sendAlimtalkBulk'])->name('send.alimtalk-bulk'); + + // 친구톡 발송 + Route::post('/send/friendtalk', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'sendFriendtalk'])->name('send.friendtalk'); + Route::post('/send/friendtalk-image', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'sendFriendtalkImage'])->name('send.friendtalk-image'); + Route::post('/send/friendtalk-wide', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'sendFriendtalkWide'])->name('send.friendtalk-wide'); + + // 전송 결과 조회 + Route::get('/send/{sendKey}', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'getSendResult'])->name('send.result'); + Route::post('/send/results', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'getSendResults'])->name('send.results'); + + // 예약 취소 + Route::delete('/send/{sendKey}/cancel', [\App\Http\Controllers\Api\Admin\Barobill\BarobillKakaotalkController::class, 'cancelReserved'])->name('send.cancel'); + }); + // 테넌트 관리 API Route::prefix('tenants')->name('tenants.')->group(function () { // 고정 경로는 먼저 정의 diff --git a/routes/web.php b/routes/web.php index 0bcee5c7..4d7617c4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -596,6 +596,15 @@ // 카드내역 참조 Route::get('/card-transactions', [\App\Http\Controllers\Barobill\HometaxController::class, 'cardTransactions'])->name('card-transactions'); }); + + // 카카오톡 (알림톡/친구톡) + Route::prefix('kakaotalk')->name('kakaotalk.')->group(function () { + Route::get('/', [\App\Http\Controllers\Barobill\KakaotalkController::class, 'index'])->name('index'); + Route::get('/channels', [\App\Http\Controllers\Barobill\KakaotalkController::class, 'channels'])->name('channels'); + Route::get('/templates', [\App\Http\Controllers\Barobill\KakaotalkController::class, 'templates'])->name('templates'); + Route::get('/send', [\App\Http\Controllers\Barobill\KakaotalkController::class, 'send'])->name('send'); + Route::get('/history', [\App\Http\Controllers\Barobill\KakaotalkController::class, 'history'])->name('history'); + }); }); /*