如何用PHP项目实现用户合同签署?

wen java案例 2

本文目录导读:

如何用PHP项目实现用户合同签署?

  1. 基本架构设计
  2. 实现电子签名的几种方式
  3. 完整的签署流程实现
  4. 完整API示例
  5. 数据库设计
  6. 安全建议

我来详细说明如何使用PHP实现用户合同签署功能,这是一个涉及法律效力的重要功能,需要确保安全性和合规性。

基本架构设计

系统核心组件

PHP合同签署系统
├── 用户管理模块 (用户认证/权限)
├── 合同管理模块 (模板/生成)
├── 电子签名模块 (签名生成/验证)
├── 签署流程模块 (签署顺序/状态)
├── 时间戳服务 (签名时间记录)
└── 存储管理模块 (合同存储/备份)

实现电子签名的几种方式

方式1:基于图片的签名(基础版)

<?php
// 1. 签名面板HTML
class SignaturePad {
    public function renderPad($userId, $contractId) {
        return '
        <div class="signature-container">
            <canvas id="signature-pad" width="400" height="200"></canvas>
            <button onclick="saveSignature()">确认签名</button>
            <button onclick="clearSignature()">清除重签</button>
        </div>
        <script>
        var canvas = document.getElementById("signature-pad");
        var signaturePad = new SignaturePad(canvas);
        function saveSignature() {
            if (signaturePad.isEmpty()) {
                alert("请先签名");
                return;
            }
            var dataUrl = signaturePad.toDataURL();
            // 发送到服务器
            fetch("/api/save-signature", {
                method: "POST",
                headers: {"Content-Type": "application/json"},
                body: JSON.stringify({
                    user_id: "'.$userId.'",
                    contract_id: "'.$contractId.'",
                    signature: dataUrl
                })
            });
        }
        </script>';
    }
}
// 2. 保存签名
class SignatureHandler {
    public function saveSignature($userId, $contractId, $base64Data) {
        // 解码Base64图片
        $imageData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $base64Data));
        // 生成唯一文件名
        $filename = "sign_{$userId}_{$contractId}_" . time() . ".png";
        $path = __DIR__ . "/uploads/signatures/" . $filename;
        // 保存图片
        file_put_contents($path, $imageData);
        // 记录到数据库
        $this->saveSignatureRecord($userId, $contractId, $filename);
        return $filename;
    }
}

方式2:数字签名(高级版)

<?php
// 使用OpenSSL实现数字签名
class DigitalSignature {
    private $privateKey;
    private $publicKey;
    public function __construct() {
        // 生成密钥对(实际生产环境应使用HSM或密钥管理服务)
        $config = [
            "digest_alg" => "sha256",
            "private_key_bits" => 2048,
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        ];
        $keyResource = openssl_pkey_new($config);
        openssl_pkey_export($keyResource, $this->privateKey);
        $this->publicKey = openssl_pkey_get_details($keyResource)["key"];
    }
    // 签署合同
    public function signContract($contractContent, $userId) {
        // 计算合同内容的哈希值
        $hash = hash("sha256", $contractContent);
        // 使用私钥签名
        $signature = "";
        openssl_sign($hash, $signature, $this->privateKey, OPENSSL_ALGO_SHA256);
        // Base64编码签名
        return [
            'signature' => base64_encode($signature),
            'hash' => $hash,
            'signed_at' => time(),
            'user_id' => $userId
        ];
    }
    // 验证签名
    public function verifySignature($contractContent, $signature, $userPublicKey) {
        $hash = hash("sha256", $contractContent);
        $decodedSignature = base64_decode($signature);
        return openssl_verify($hash, $decodedSignature, $userPublicKey, OPENSSL_ALGO_SHA256);
    }
}

完整的签署流程实现

合同生成

<?php
class ContractGenerator {
    public function generateContract($templateId, $userData) {
        // 加载合同模板
        $template = file_get_contents("templates/contract_{$templateId}.html");
        // 替换变量
        $replacements = [
            '{{user_name}}' => $userData['name'],
            '{{user_id_card}}' => $userData['id_card'],
            '{{date}}' => date('Y年m月d日'),
            '{{contract_id}}' => uniqid('CT_'),
        ];
        return str_replace(array_keys($replacements), $replacements, $template);
    }
    // 转换为PDF
    public function generatePDF($html) {
        require_once 'vendor/autoload.php';
        $mpdf = new \Mpdf\Mpdf();
        $mpdf->WriteHTML($html);
        $pdfPath = "contracts/contract_" . time() . ".pdf";
        $mpdf->Output($pdfPath, 'F');
        return $pdfPath;
    }
}

签署流程控制

<?php
class SigningProcess {
    private $db;
    // 创建签署任务
    public function createSigningTask($contractId, $signers) {
        // signers: [['user_id' => 1, 'order' => 1], ['user_id' => 2, 'order' => 2]]
        foreach ($signers as $signer) {
            $this->db->insert('signing_flow', [
                'contract_id' => $contractId,
                'user_id' => $signer['user_id'],
                'order' => $signer['order'],
                'status' => $signer['order'] == 1 ? 'pending' : 'waiting'
            ]);
        }
    }
    // 检查当前签署者
    public function getCurrentSigner($contractId) {
        return $this->db->query(
            "SELECT * FROM signing_flow 
             WHERE contract_id = ? AND status = 'pending' 
             ORDER BY `order` LIMIT 1",
            [$contractId]
        );
    }
    // 完成签署
    public function completeSigning($contractId, $userId, $signature) {
        // 验证是否是当前签署者
        $currentSigner = $this->getCurrentSigner($contractId);
        if ($currentSigner['user_id'] != $userId) {
            throw new Exception("不是当前签署者");
        }
        // 更新签署状态
        $this->db->update('signing_flow', 
            ['status' => 'signed', 'signed_at' => time(), 'signature' => $signature],
            ['contract_id' => $contractId, 'user_id' => $userId]
        );
        // 激活下一个签署者
        $nextSigner = $this->db->query(
            "SELECT * FROM signing_flow 
             WHERE contract_id = ? AND `order` > ? 
             ORDER BY `order` LIMIT 1",
            [$contractId, $currentSigner['order']]
        );
        if ($nextSigner) {
            $this->db->update('signing_flow',
                ['status' => 'pending'],
                ['id' => $nextSigner['id']]
            );
        } else {
            // 所有人都签署完成
            $this->finalizeContract($contractId);
        }
    }
    // 完成合同
    private function finalizeContract($contractId) {
        // 标记合同为已完成
        $this->db->update('contracts',
            ['status' => 'completed', 'completed_at' => time()],
            ['id' => $contractId]
        );
        // 发送通知给所有签署者
        $this->notifySigners($contractId);
    }
}

签名验证与安全措施

<?php
class SignatureSecurity {
    // IP和指纹记录
    public function recordSigningMetadata($contractId, $userId) {
        $metadata = [
            'ip_address' => $_SERVER['REMOTE_ADDR'],
            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
            'timestamp' => time(),
            'device_fingerprint' => $this->generateFingerprint()
        ];
        return $this->db->insert('signing_metadata', [
            'contract_id' => $contractId,
            'user_id' => $userId,
            'metadata' => json_encode($metadata)
        ]);
    }
    // 生成设备指纹
    private function generateFingerprint() {
        $components = [
            $_SERVER['HTTP_USER_AGENT'],
            $_SERVER['HTTP_ACCEPT_LANGUAGE'],
            // 更多浏览器特征
        ];
        return hash('sha256', implode('|', $components));
    }
    // 防篡改检查
    public function checkContractIntegrity($contractId) {
        $contract = $this->db->query(
            "SELECT content_hash, signatures FROM contracts WHERE id = ?",
            [$contractId]
        );
        // 重新计算当前合同内容的哈希
        $currentHash = hash_file('sha256', $contract['pdf_path']);
        return $currentHash === $contract['content_hash'];
    }
}

完整API示例

<?php
// API控制器
class ContractSigningController {
    public function initiateSigning(Request $request) {
        // 1. 验证用户身份
        $user = $this->auth->authenticate($request->token);
        // 2. 生成合同
        $generator = new ContractGenerator();
        $contractHtml = $generator->generateContract(
            $request->template_id,
            $user->getData()
        );
        // 3. 保存合同
        $contractId = $this->saveContract($user->id, $contractHtml);
        // 4. 创建签署流程
        $process = new SigningProcess();
        $process->createSigningTask($contractId, [
            ['user_id' => $user->id, 'order' => 1],
            ['user_id' => $request->counterparty_id, 'order' => 2]
        ]);
        return response()->json([
            'contract_id' => $contractId,
            'signing_url' => "/sign/{$contractId}"
        ]);
    }
    public function signContract(Request $request) {
        // 1. 验证
        $user = $this->auth->authenticate($request->token);
        $contractId = $request->contract_id;
        // 2. 数字签名
        $digitalSignature = new DigitalSignature();
        $contractContent = file_get_contents("contracts/{$contractId}.pdf");
        $signatureData = $digitalSignature->signContract(
            $contractContent,
            $user->id
        );
        // 3. 更新签署流程
        $process = new SigningProcess();
        $process->completeSigning(
            $contractId,
            $user->id,
            $signatureData
        );
        // 4. 记录元数据
        $security = new SignatureSecurity();
        $security->recordSigningMetadata($contractId, $user->id);
        return response()->json([
            'status' => 'success',
            'message' => '签署完成'
        ]);
    }
}

数据库设计

-- 合同表
CREATE TABLE contracts (
    id INT PRIMARY KEY AUTO_INCREMENT,
    contract_number VARCHAR(50) UNIQUE,VARCHAR(200),
    content TEXT,
    content_hash VARCHAR(64),
    pdf_path VARCHAR(500),
    status ENUM('draft', 'pending', 'signed', 'completed', 'expired'),
    created_at TIMESTAMP,
    completed_at TIMESTAMP
);
-- 签署流程表
CREATE TABLE signing_flow (
    id INT PRIMARY KEY AUTO_INCREMENT,
    contract_id INT,
    user_id INT,
    `order` INT,
    status ENUM('waiting', 'pending', 'signed', 'rejected'),
    signature TEXT,
    signed_at TIMESTAMP,
    FOREIGN KEY (contract_id) REFERENCES contracts(id)
);
-- 签署元数据表
CREATE TABLE signing_metadata (
    id INT PRIMARY KEY AUTO_INCREMENT,
    contract_id INT,
    user_id INT,
    ip_address VARCHAR(45),
    user_agent TEXT,
    device_fingerprint VARCHAR(64),
    timestamp TIMESTAMP,
    FOREIGN KEY (contract_id) REFERENCES contracts(id)
);

安全建议

  1. 使用HTTPS:所有传输必须加密
  2. 签名私钥保护:使用HSM或密钥管理服务
  3. 时间戳服务:使用权威时间戳机构
  4. 审计日志:记录所有签署操作
  5. 防重放攻击:使用nonce或时间戳

这个实现可以根据业务需求进行扩展,如果只是简单的电子签名,使用图片签名就足够了,对于法律效力的需求,建议使用数字签名方案。

抱歉,评论功能暂时关闭!