diff --git a/app/Http/Controllers/ESign/EsignApiController.php b/app/Http/Controllers/ESign/EsignApiController.php index 005ae234..2f6be04e 100644 --- a/app/Http/Controllers/ESign/EsignApiController.php +++ b/app/Http/Controllers/ESign/EsignApiController.php @@ -827,11 +827,13 @@ public function send(Request $request, int $id): JsonResponse $sendMethod = $request->input('send_method', 'email'); $smsFallback = $request->boolean('sms_fallback', true); $templateName = $request->input('template_name'); + $completionTemplateName = $request->input('completion_template_name'); $contract->update([ 'status' => 'pending', 'send_method' => $sendMethod, 'sms_fallback' => $smsFallback, + 'completion_template_name' => $completionTemplateName, 'updated_by' => auth()->id(), ]); @@ -1090,6 +1092,20 @@ private function sendAlimtalk( } unset($btn); + // 버튼 URL 도메인을 현재 환경의 도메인으로 치환 + $appHost = parse_url(config('app.url'), PHP_URL_HOST); + foreach ($buttons as &$btn) { + foreach (['Url1', 'Url2'] as $urlKey) { + if (! empty($btn[$urlKey])) { + $parsed = parse_url($btn[$urlKey]); + if (isset($parsed['host']) && $parsed['host'] !== $appHost) { + $btn[$urlKey] = str_replace($parsed['host'], $appHost, $btn[$urlKey]); + } + } + } + } + unset($btn); + $receiverNum = preg_replace('/[^0-9]/', '', $signer->phone); \Log::info('E-Sign 알림톡 발송 시도', [ diff --git a/app/Http/Controllers/ESign/EsignPublicController.php b/app/Http/Controllers/ESign/EsignPublicController.php index 6a86563f..7270f579 100644 --- a/app/Http/Controllers/ESign/EsignPublicController.php +++ b/app/Http/Controllers/ESign/EsignPublicController.php @@ -754,7 +754,8 @@ private function sendCompletionAlimtalk(EsignContract $contract, EsignSigner $si return ['success' => false, 'channel' => 'alimtalk', 'error' => '등록된 카카오톡 채널이 없습니다']; } - $templateName = $this->resolveTemplateName('전자계약_완료'); + $templateName = $contract->completion_template_name + ?: $this->resolveTemplateName('전자계약_완료'); $documentUrl = config('app.url').'/esign/sign/'.$signer->access_token.'/api/document'; $signUrl = config('app.url').'/esign/sign/'.$signer->access_token; $completedAt = $contract->completed_at?->format('Y-m-d H:i') ?? now()->format('Y-m-d H:i'); @@ -807,6 +808,20 @@ private function sendCompletionAlimtalk(EsignContract $contract, EsignSigner $si } unset($btn); + // 버튼 URL 도메인을 현재 환경의 도메인으로 치환 + $appHost = parse_url(config('app.url'), PHP_URL_HOST); + foreach ($buttons as &$btn) { + foreach (['Url1', 'Url2'] as $urlKey) { + if (! empty($btn[$urlKey])) { + $parsed = parse_url($btn[$urlKey]); + if (isset($parsed['host']) && $parsed['host'] !== $appHost) { + $btn[$urlKey] = str_replace($parsed['host'], $appHost, $btn[$urlKey]); + } + } + } + } + unset($btn); + $receiverNum = preg_replace('/[^0-9]/', '', $signer->phone); Log::info('E-Sign 완료 알림톡 발송 시도', [ diff --git a/app/Models/ESign/EsignContract.php b/app/Models/ESign/EsignContract.php index e4611cc7..47e863a5 100644 --- a/app/Models/ESign/EsignContract.php +++ b/app/Models/ESign/EsignContract.php @@ -27,6 +27,7 @@ class EsignContract extends Model 'status', 'send_method', 'sms_fallback', + 'completion_template_name', 'metadata', 'expires_at', 'completed_at', diff --git a/resources/views/esign/send.blade.php b/resources/views/esign/send.blade.php index 5be98c0e..2e412593 100644 --- a/resources/views/esign/send.blade.php +++ b/resources/views/esign/send.blade.php @@ -39,6 +39,8 @@ const [templateLoading, setTemplateLoading] = useState(false); const [templateError, setTemplateError] = useState(''); const [templatePreview, setTemplatePreview] = useState(null); + const [selectedCompletionTemplate, setSelectedCompletionTemplate] = useState(''); + const [completionTemplatePreview, setCompletionTemplatePreview] = useState(null); const fetchContract = useCallback(async () => { try { @@ -57,6 +59,8 @@ setTemplates([]); setSelectedTemplate(''); setTemplatePreview(null); + setSelectedCompletionTemplate(''); + setCompletionTemplatePreview(null); try { const res = await fetch('/esign/contracts/alimtalk-templates', { headers: getHeaders() }); const json = await res.json(); @@ -92,6 +96,7 @@ send_method: sendMethod, sms_fallback: smsFallback, template_name: (sendMethod === 'alimtalk' || sendMethod === 'both') ? selectedTemplate : undefined, + completion_template_name: (sendMethod === 'alimtalk' || sendMethod === 'both') ? selectedCompletionTemplate : undefined, }), }); const json = await res.json(); @@ -178,10 +183,10 @@ className="rounded text-blue-600 focus:ring-blue-500" /> )} - {/* 알림톡 템플릿 선택 */} + {/* 서명 요청 알림톡 템플릿 선택 */} {needsAlimtalk && (
- + {templateLoading ? (
템플릿 목록 조회 중...
) : templateError ? ( @@ -223,6 +228,42 @@ className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring )}
)} + + {/* 완료 알림톡 템플릿 선택 */} + {needsAlimtalk && !templateLoading && !templateError && templates.length > 0 && ( +
+ +

모든 서명자 서명 완료 시 발송될 알림톡 템플릿을 선택하세요.

+ + + {completionTemplatePreview && ( +
+

미리보기

+
{completionTemplatePreview.content}
+ {completionTemplatePreview.buttons?.length > 0 && ( +
+ {completionTemplatePreview.buttons.map((btn, i) => ( + {btn.Name} + ))} +
+ )} +
+ )} +
+ )} {/* 서명자 연락처 확인 */}