本文目录导读:

我来详细说明如何使用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)
);
安全建议
- 使用HTTPS:所有传输必须加密
- 签名私钥保护:使用HSM或密钥管理服务
- 时间戳服务:使用权威时间戳机构
- 审计日志:记录所有签署操作
- 防重放攻击:使用nonce或时间戳
这个实现可以根据业务需求进行扩展,如果只是简单的电子签名,使用图片签名就足够了,对于法律效力的需求,建议使用数字签名方案。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播