本文目录导读:

在 PHP 中实现“合同签订”功能,通常指的是电子合同签署,这不仅仅是简单的表单提交,而是涉及身份认证、意愿确认、防篡改的法律行为。
根据你的需求是企业内部简易流程还是对外具有法律效力的电子签,实现方式截然不同,以下是分层次的实现方案:
简易内部合同(基于数据库 + 文件存储)
适用于内部协议、员工确认书等,不需要第三方公证。
核心逻辑:生成合同内容 -> 用户确认 -> 存储签名数据 -> 生成 PDF 快照。
数据库设计
CREATE TABLE contracts (
id INT PRIMARY KEY AUTO_INCREMENT,VARCHAR(255),
content TEXT, -- 合同正文 (HTML或Markdown)
signer_name VARCHAR(100),
signer_email VARCHAR(100),
status ENUM('pending', 'signed', 'rejected'),
signature_data TEXT, -- 签名图片路径 (Base64或路径)
ip_address VARCHAR(45), -- 留痕
user_agent VARCHAR(255),-- 留痕
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
signed_at TIMESTAMP NULL
);
核心 PHP 代码示例(签名提交)
<?php
// submit_signature.php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$contract_id = $_POST['contract_id'];
$signature_data = $_POST['signature'] ?? ''; // 前端canvas生成的Base64图片
// 1. 防篡改校验:重新计算合同内容Hash
$stmt = $pdo->prepare("SELECT content FROM contracts WHERE id = ?");
$stmt->execute([$contract_id]);
$contract = $stmt->fetch();
// 计算本次签名的唯一标识 (绑定IP+时间+内容)
$signature_token = hash('sha256', $contract['content'] . $_SERVER['REMOTE_ADDR'] . time());
// 2. 存储签名
$update = $pdo->prepare("UPDATE contracts SET
status = 'signed',
signature_data = ?,
signed_at = NOW(),
signature_token = ? WHERE id = ?");
$update->execute([$signature_data, $signature_token, $contract_id]);
// 3. 生成PDF快照 (可选)
// 使用 Dompdf/TCPDF 将合同内容+签名图片渲染成PDF
// $pdf->Output('/storage/contracts/'.$contract_id.'.pdf');
redirect('/contract/success');
}
?>
前端签名板(JavaScript) 使用 Canvas 捕获手写签名:
<canvas id="signaturePad" width="400" height="200"></canvas>
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.0.0/dist/signature_pad.umd.min.js"></script>
<script>
const canvas = document.getElementById('signaturePad');
const signaturePad = new SignaturePad(canvas);
document.getElementById('signBtn').addEventListener('click', function() {
if (signaturePad.isEmpty()) {
alert('请签名');
return;
}
const signatureData = signaturePad.toDataURL('image/png');
document.getElementById('signatureInput').value = signatureData;
document.getElementById('signForm').submit();
});
</script>
接入第三方电子签平台(推荐,有法律效力)
如果需要真正法律效力的电子合同(如法大大、E签宝、腾讯电子签),必须使用 CA 机构证书,不建议自行实现加密(《电子签名法》要求可靠电子签名)。
推荐 SDK:
- E签宝 (相对成熟)
- 腾讯电子签 (微信生态好)
- 法大大 (庭审认可度高)
PHP 对接核心流程(以标准 REST API 为例):
初始化客户端
use GuzzleHttp\Client;
class EsignClient {
private $host = 'https://openapi.esign.cn';
private $appId = 'your_app_id';
private $appSecret = 'your_secret';
private function getHeaders($method, $path, $body = '') {
$timestamp = time() . '000'; // 毫秒
$bodyStr = ($method == 'GET') ? '' : json_encode($body);
// 计算签名 (具体算法见第三方文档)
$contentMd5 = base64_encode(md5($bodyStr, true));
$stringToSign = $method . "\n" . $contentMd5 . "\n" . $this->getContentType() . "\n" . $timestamp . "\n" . $path;
$signature = base64_encode(hash_hmac('sha256', $stringToSign, $this->appSecret, true));
return [
'Content-Type' => 'application/json',
'X-Tsign-Open-App-Id' => $this->appId,
'X-Tsign-Open-Auth-Mode' => 'APP',
'X-Tsign-Open-Timestamp' => $timestamp,
'X-Tsign-Open-Signature' => $signature,
];
}
}
发起签署流程(关键代码)
public function createSignFlow($contractName, $signers) {
$path = '/v3/sign-flow/create';
$body = [
'docs' => [
[
'fileId' => $this->uploadFile('/path/to/contract.pdf'),
'fileName' => $contractName
]
],
'signerInfo' => [
[
'signerType' => 'PERSON',
'signerAccount' => [
'thirdPartyUserId' => $signers['user_id'],
'name' => $signers['name'],
'idType' => 'CRED_PSN_CH_IDCARD',
'idNumber' => $signers['id_card']
],
'signFields' => [
[
'fileId' => $uploadedFileId,
'type' => 1, // 单行签名
'page' => 2, // 页码
'positions' => ['x' => 300, 'y' => 700] // 坐标
]
]
]
]
];
$client = new Client(['base_uri' => $this->host]);
$response = $client->request('POST', $path, [
'headers' => $this->getHeaders('POST', $path, $body),
'json' => $body
]);
return json_decode($response->getBody(), true)['data']['signFlowId'];
}
获取签署链接
// 生成链接返回给前端进行跳转签署
$path = '/v3/sign-flow/execute-url';
$body = [
'signFlowId' => $flowId,
'account' => ['thirdPartyUserId' => $userId]
];
// 返回的 shortUrl 用于跳转
注意:第三方签需要企业认证(营业执照),个人实名认证(人脸/银行卡),成本约 2-5 元/次,但具有《电子签名法》效力。
最简逻辑(防止篡改版)
如果只是想在 PHP 中做一个“我已阅读并同意”的确认(非正式合同),可以用 Token 防伪造:
<?php
// 生成合同签署Token
function generateContractToken($user_id, $contract_id, $secret_key) {
$data = $user_id . $contract_id . time();
$token = hash_hmac('sha256', $data, $secret_key);
return ['token' => $token, 'expires' => time() + 3600];
}
// 验证
function verifyContractToken($token, $user_id, $contract_id, $secret_key) {
// 重新计算并比对
return hash_equals($expectedToken, $token);
}
?>
安全与合规注意事项(必须做)
- 时间戳服务:如果自建系统,务必记录服务器时间,并加盖
RFC3161时间戳。 - 哈希链:将上一份合同的 Hash 值存于下一份合同元数据中,防止批量替换。
- 日志审计:记录每次签署的 IP、设备指纹、Cookies。
- 强制 HTTPS:必须使用 TLS 1.2+ 加密传输。
- 不可抵赖性:自建系统很难证明“自己签的”,建议用户注册时加入短信/邮件验证码二次确认。
总结建议
- 如果是个人项目/演示:使用 方案一(Canvas签名 + Dompdf导出PDF)即可。
- 如果是企业正式合同:强烈建议接通 E签宝/腾讯电子签 的 API,总成本最低,且免去法律纠纷,PHP 代码只需要处理 API 签名和回调逻辑,大约 200 行核心代码搞定。
你目前的具体应用场景是什么?(是电商平台入驻协议,还是企业内部审批流?)可以告诉我,我帮你细化那部分核心代码。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播