You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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的关联逻辑

  1. 身份绑定:在创建DocuSign签署人时,将client_user_id设置为Auth0用户ID,实现签署记录与平台用户的关联。
  2. 权限控制:通过Symfony的Security组件验证用户登录状态,确保只有已认证用户才能发起签署请求。
  3. 认证隔离:DocuSign采用后端JWT静默认证,无需用户额外登录DocuSign平台,完全基于现有Auth0体系完成用户身份校验。

三、前端调用流程

  1. 前端收集签署人邮箱、姓名、签署位置等参数,通过POST请求调用/api/docusign/init-sign接口。
  2. 后端返回签署链接后,前端可通过iframe嵌入该链接,或直接跳转至链接让用户完成签署。
  3. 用户签署完成后,DocuSign会跳转至预设的return_url,前端可在此处更新合同状态(比如调用后端接口标记合同为已签署)。

内容的提问来源于stack exchange,提问作者Masary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 08:40:25