feat:E-Sign 법인도장(Corporate Seal) 등록 및 자동 서명 기능

- 계약 생성 화면에 법인도장 업로드 UI 추가 (미리보기/삭제)
- store()에서 base64 이미지 디코딩 후 저장, creator signer에 연결
- send()에서 법인도장 있는 작성자 자동 서명 처리 (상대방에게만 이메일 발송)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-13 14:36:11 +09:00
parent c1932c7803
commit 0c47d7b996
2 changed files with 104 additions and 3 deletions

View File

@@ -99,6 +99,7 @@ public function store(Request $request): JsonResponse
'metadata' => 'nullable|array',
'metadata.*' => 'nullable|string|max:500',
'file' => 'nullable|file|mimes:pdf,doc,docx|max:20480',
'creator_stamp_image' => 'nullable|string',
]);
$tenantId = session('selected_tenant_id', 1);
@@ -178,6 +179,21 @@ public function store(Request $request): JsonResponse
]);
}
// 법인도장 이미지 처리
if ($request->input('creator_stamp_image')) {
$creatorSigner = EsignSigner::withoutGlobalScopes()
->where('contract_id', $contract->id)
->where('role', 'creator')
->first();
if ($creatorSigner) {
$imageData = base64_decode($request->input('creator_stamp_image'));
$imagePath = "esign/{$tenantId}/signatures/{$contract->id}_{$creatorSigner->id}_stamp.png";
Storage::disk('local')->put($imagePath, $imageData);
$creatorSigner->update(['signature_image_path' => $imagePath]);
}
}
// 감사 로그
EsignAuditLog::create([
'tenant_id' => $tenantId,
@@ -440,16 +456,45 @@ public function send(Request $request, int $id): JsonResponse
'updated_by' => auth()->id(),
]);
// 법인도장이 있는 작성자 자동 서명 처리
$creatorSigner = $contract->signers->firstWhere('role', 'creator');
if ($creatorSigner && $creatorSigner->signature_image_path) {
$creatorSigner->update([
'status' => 'signed',
'signed_at' => now(),
'sign_ip_address' => $request->ip(),
'sign_user_agent' => $request->userAgent(),
]);
EsignAuditLog::create([
'tenant_id' => $tenantId,
'contract_id' => $contract->id,
'signer_id' => $creatorSigner->id,
'action' => 'signed',
'ip_address' => $request->ip(),
'user_agent' => $request->userAgent(),
'metadata' => ['auto_stamp' => true, 'signer_name' => $creatorSigner->name],
'created_at' => now(),
]);
// 계약 상태를 partially_signed로 변경
$contract->update(['status' => 'partially_signed']);
}
// 서명 순서 유형에 따라 알림 발송
if ($contract->sign_order_type === 'parallel') {
// 동시 서명: 모든 서명자에게 발송
// 동시 서명: 서명 안 한 서명자에게 발송
foreach ($contract->signers as $s) {
if ($s->status === 'signed') continue;
$s->update(['status' => 'notified']);
Mail::to($s->email)->send(new EsignRequestMail($contract, $s));
}
} else {
// 순차 서명: 첫 번째 서명자에게 발송
$nextSigner = $contract->signers()->orderBy('sign_order')->first();
// 순차 서명: 다음 미서명 서명자에게 발송
$nextSigner = $contract->signers()
->where('status', '!=', 'signed')
->orderBy('sign_order')
->first();
if ($nextSigner) {
$nextSigner->update(['status' => 'notified']);
Mail::to($nextSigner->email)->send(new EsignRequestMail($contract, $nextSigner));