在Symfony与Doctrine项目中集成DocuSign的实现疑问
Symfony + Doctrine 集成 DocuSign 实战方案
一、代码结构与文件划分
按照Symfony的服务层规范拆分DocuSign逻辑,避免代码混乱:
1. 核心服务类(封装DocuSign API)
创建src/Service/DocuSignService.php,所有DocuSign的API调用、认证逻辑都在这里实现,复用快速入门中的核心代码:
<?php namespace App\Service; use DocuSign\eSign\Client\ApiClient; use DocuSign\eSign\Configuration; use DocuSign\eSign\Model\EnvelopeDefinition; use DocuSign\eSign\Model\Signer; use DocuSign\eSign\Model\SignHere; use DocuSign\eSign\Model\Tabs; use DocuSign\eSign\Model\Recipients; use DocuSign\eSign\Model\Document; use DocuSign\eSign\Model\RecipientViewRequest; class DocuSignService { private $apiClient; private $accountId; public function __construct(string $dsIntegratorKey, string $dsUserId, string $dsPrivateKey, string $dsBaseUrl) { $config = new Configuration(); $config->setHost($dsBaseUrl); $config->addDefaultHeader("Authorization", "Bearer " . $this->getAccessToken($dsIntegratorKey, $dsUserId, $dsPrivateKey, $dsBaseUrl)); $this->apiClient = new ApiClient($config); $this->accountId = $this->getAccountId(); } // JWT模式获取DocuSign访问令牌(后端静默认证) private function getAccessToken(string $integratorKey, string $userId, string $privateKey, string $baseUrl): string { $jwt = $this->apiClient->requestJWTUserToken( $integratorKey, $userId, $baseUrl, $privateKey, 3600 ); return $jwt[0]['access_token']; } // 获取DocuSign账户ID private function getAccountId(): string { $userInfo = $this->apiClient->getUserInfo(); return $userInfo->getAccounts()[0]->getAccountId(); } // 创建嵌入式签署信封并返回签署链接 public function createEmbeddedSigningEnvelope(string $signerEmail, string $signerName, string $documentPath, array $signHereTabs, string $auth0UserId): string { // 1. 构建文档对象 $document = new Document([ 'document_base64' => base64_encode(file_get_contents($documentPath)), 'name' => '用户合同', 'document_id' => '1' ]); // 2. 构建签署人(关联Auth0用户ID) $signer = new Signer([ 'email' => $signerEmail, 'name' => $signerName, 'recipient_id' => '1', 'client_user_id' => $auth0UserId // 绑定Auth0用户ID,便于后续关联记录 ]); // 3. 设置签署位置 $tabs = new Tabs(); $signHereList = []; foreach ($signHereTabs as $tab) { $signHereList[] = new SignHere([ 'document_id' => '1', 'page_number' => $tab['page'], 'x_position' => $tab['x'], 'y_position' => $tab['y'] ]); } $tabs->setSignHereTabs($signHereList); $signer->setTabs($tabs); // 4. 构建信封 $envelopeDefinition = new EnvelopeDefinition([ 'email_subject' => '请签署您的电子合同', 'documents' => [$document], 'recipients' => new Recipients(['signers' => [$signer]]), 'status' => 'sent' ]); // 5. 创建信封并生成签署链接 $envelopeApi = $this->apiClient->createEnvelopeApi(); $envelopeSummary = $envelopeApi->createEnvelope($this->accountId, $envelopeDefinition); $viewRequest = new RecipientViewRequest([ 'return_url' => $_ENV['APP_URL'] . '/sign-complete', // 签署完成跳转地址 'authentication_method' => 'none', 'email' => $signerEmail, 'user_name' => $signerName, 'client_user_id' => $auth0UserId ]); $viewUrl = $envelopeApi->createRecipientView($this->accountId, $envelopeSummary->getEnvelopeId(), $viewRequest); return $viewUrl->getUrl(); } }
2. 配置服务与环境变量
在.env中添加DocuSign配置项:
DS_INTEGRATOR_KEY=你的集成密钥 DS_USER_ID=你的DocuSign用户ID DS_PRIVATE_KEY_PATH=%kernel.project_dir%/config/docusign_private_key.pem DS_BASE_URL=https://demo.docusign.net/restapi
在config/services.yaml中注册服务:
services: App\Service\DocuSignService: arguments: $dsIntegratorKey: '%env(DS_INTEGRATOR_KEY)%' $dsUserId: '%env(DS_USER_ID)%' $dsPrivateKey: '%env(file:DS_PRIVATE_KEY_PATH)%' $dsBaseUrl: '%env(DS_BASE_URL)%'
3. 控制器处理前端API请求
创建src/Controller/DocuSignController.php,处理前端的签署请求,同时验证Auth0用户身份:
<?php namespace App\Controller; use App\Service\DocuSignService; use App\Entity\Contract; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Security; class DocuSignController extends AbstractController { #[Route('/api/docusign/init-sign', methods: ['POST'])] public function initSign(Request $request, DocuSignService $docuSignService, Security $security): JsonResponse { // 1. 验证Auth0登录状态 $user = $security->getUser(); if (!$user) { return new JsonResponse(['error' => '未授权访问'], 401); } $auth0UserId = $user->getUserIdentifier(); // 2. 解析前端传入的参数 $data = json_decode($request->getContent(), true); $signerEmail = $data['signer_email'] ?? ''; $signerName = $data['signer_name'] ?? ''; $signHereTabs = $data['sign_positions'] ?? []; // 3. 从Doctrine获取合同数据,生成待签署PDF $contract = $this->getDoctrine()->getRepository(Contract::class)->findOneBy(['auth0_user_id' => $auth0UserId]); $documentPath = $this->generateContractPdf($contract); // 4. 调用DocuSign服务生成签署链接 try { $signUrl = $docuSignService->createEmbeddedSigningEnvelope( $signerEmail, $signerName, $documentPath, $signHereTabs, $auth0UserId ); return new JsonResponse(['sign_url' => $signUrl]); } catch (\Exception $e) { return new JsonResponse(['error' => $e->getMessage()], 500); } } // 示例:生成合同PDF(可使用TCPDF/Snappy等库) private function generateContractPdf(Contract $contract): string { $tempPath = sys_get_temp_dir() . '/contract_' . $contract->getId() . '.pdf'; // 省略PDF生成逻辑(填充合同数据、排版等) return $tempPath; } }
二、与Auth0的关联逻辑
- 身份绑定:在创建DocuSign签署人时,将
client_user_id设置为Auth0用户ID,实现签署记录与平台用户的关联。 - 权限控制:通过Symfony的
Security组件验证用户登录状态,确保只有已认证用户才能发起签署请求。 - 认证隔离:DocuSign采用后端JWT静默认证,无需用户额外登录DocuSign平台,完全基于现有Auth0体系完成用户身份校验。
三、前端调用流程
- 前端收集签署人邮箱、姓名、签署位置等参数,通过POST请求调用
/api/docusign/init-sign接口。 - 后端返回签署链接后,前端可通过iframe嵌入该链接,或直接跳转至链接让用户完成签署。
- 用户签署完成后,DocuSign会跳转至预设的
return_url,前端可在此处更新合同状态(比如调用后端接口标记合同为已签署)。
内容的提问来源于stack exchange,提问作者Masary
相关产品推荐
相关产品推荐

