本文目录导读:

在 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 行核心代码搞定。
你目前的具体应用场景是什么?(是电商平台入驻协议,还是企业内部审批流?)可以告诉我,我帮你细化那部分核心代码。