【问题标题】:DocuSign Python SDK - Retrieve newly generated document to sign?DocuSign Python SDK - 检索新生成的文档以进行签名?
【发布时间】:2018-08-01 03:51:26
【问题描述】:

我正在尝试为我的公司开发一个 Web 应用程序,该应用程序将生成一个供一位用户签名的 pdf。用户在表单中输入信息,然后使用 weasyprint,我格式化的 html 文件生成包含用户信息的 pdf。最后,使用docusign的python SDK,我想要一个需要签名才能被用户签名的字段。直到docusign位工作为止的一切。

以下是我目前正在使用的代码,基于@Lee 的示例。

def Signview(request):
    myobject = MYOBJECT.objects.filter().order_by('-id')[0]
    username = "myUsername"
    integrator_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx"
    base_url = "https://demo.docusign.net/restapi"
    oauth_base_url = "account-d.docusign.com"
    redirect_uri = "http://localhost:8000/path/to/redirect/uri"
    private_key_filename = "path/to/pKey.txt"
    user_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    client_user_id = 'Your System ID' #This is the actual string I'm using

    # Add a recipient to sign the document
    signer = docusign.Signer()
    signer.email = myobject.email #myobject is a model from my database, containing this information.
    signer.name = myobject.name
    signer.recipient_id = '1'
    signer.client_user_id = client_user_id

    sign_here = docusign.SignHere()
    sign_here.document_id = '1'
    sign_here.recipient_id = '1'
    sign_here.anchor_case_sensitive = 'true'
    sign_here.anchor_horizontal_alignment = 'left'
    sign_here.anchor_ignore_if_not_present = 'false'
    sign_here.anchor_match_whole_word = 'true'

    sign_here.anchor_string = 'Sign Here'
    sign_here.anchor_units = 'cms'
    sign_here.anchor_x_offset = '0'
    sign_here.anchor_y_offset = '0'
    sign_here.tab_label = 'sign_here'
    tabs = docusign.Tabs()
    tabs.sign_here_tabs = [sign_here]

    # Create a signers list, attach tabs to signer, append signer to signers.
    # Attach signers to recipients objects
    signers = []
    tabs = tabs
    signer.tabs = tabs
    signers.append(signer)
    recipients = docusign.Recipients()
    recipients.signers = signers

    # Create an envelope to be signed
    envelope_definition = docusign.EnvelopeDefinition()
    envelope_definition.email_subject = 'My email subject'
    envelope_definition.email_blurb = 'My email blurb.'

    # Add a document to the envelope_definition
    pdfpath = "path/to/mypdf.pdf"
    with open(pdfpath, 'rb') as signfile:
        file_data = signfile.read()
        doc = docusign.Document()
        base64_doc = base64.b64encode(file_data).decode('utf-8')
        doc.document_base64 = base64_doc
        doc.name = "mypdf_signed.pdf"
        doc.document_id = '1'
        envelope_definition.documents = [doc]
        signfile.close()
    envelope_definition.recipients = recipients
    envelope_definition.status = 'sent'

    api_client = docusign.ApiClient(base_url)

    oauth_login_url = api_client.get_jwt_uri(integrator_key, redirect_uri, oauth_base_url)
    print(oauth_login_url)

    api_client.configure_jwt_authorization_flow(private_key_filename, oauth_base_url, integrator_key, user_id, 3600)
    docusign.configuration.api_client = api_client

    auth_api = AuthenticationApi()
    envelopes_api = Envelopes_Api()

    try: #login here via code
        login_infoi = auth_api.login()
        login_accounts = login_info.login_accoutns
        base_url, _ = login_accounts[0].base_url.split('/v2')
        api_client.host = base_url
        docusign.configuration.api_client = api_client

        envelope_summary = envelopes_api.create_envelope(login_accounts[0].account_id, envelope_definition = envelope_definition)

        print(envelope_summary)
    except ApiException as e:
        raise Exception("Exception when calling DocuSign API: %s" % e)
    except Exception as e:
        print(e)

以下是我收到的错误(错误请求 API 异常):

Environment:


Request Method: GET
Request URL: http://localhost:8000/createquote/genloa/sign/

Django Version: 2.0.6
Python Version: 3.7.0
Installed Applications:
['createquote',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "C:\users\wkstat\appdata\local\programs\python\python37\lib\site-packages\django\core\handlers\exception.py" in inner
  35.             response = get_response(request)

File "C:\users\wkstat\appdata\local\programs\python\python37\lib\site-packages\django\core\handlers\base.py" in _get_response
  128.                 response = self.process_exception_by_middleware(e, request)

File "C:\users\wkstat\appdata\local\programs\python\python37\lib\site-packages\django\core\handlers\base.py" in _get_response
  126.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\wkstat\Desktop\Development\LNQuoteTool\createquote\views.py" in Signview
  141.  api_client.configure_jwt_authorization_flow(private_key_filename, oauth_base_url, integrator_key, user_id, 3600)

File "C:\users\wkstat\appdata\local\programs\python\python37\lib\site-packages\docusign_esign\api_client.py" in configure_jwt_authorization_flow
  126.                                 post_params=self.sanitize_for_serialization({"assertion": assertion, "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer"}))

File "C:\users\wkstat\appdata\local\programs\python\python37\lib\site-packages\docusign_esign\api_client.py" in request
  430.                                          body=body)

File "C:\users\wkstat\appdata\local\programs\python\python37\lib\site-packages\docusign_esign\rest.py" in POST
  244.                             body=body)

File "C:\users\wkstat\appdata\local\programs\python\python37\lib\site-packages\docusign_esign\rest.py" in request
  200.             raise ApiException(http_resp=r)

Exception Type: ApiException at /createquote/genloa/sign/
Exception Value: (400)
Reason: Bad Request
HTTP response headers: HTTPHeaderDict({'Cache-Control': 'private', 'Content-Type': 'text/html', 'X-AspNetMvc-Version': '5.2', 'X-DocuSign-TraceToken': 'ec891538-ed2f-4250-bc50-966c96065052', 'X-DocuSign-Node': 'DA1DFE4', 'Date': 'Tue, 31 Jul 2018 18:07:22 GMT', 'Content-Length': '11'})
HTTP response body: b'Bad Request'

我已将重定向 URI 添加到我的 docusign 管理员帐户。我不明白我做错了什么。

下面是几个请求的变量:

oauth_base_url:account-d.docusign.com

oauth_login_url:https://account-d.docusign.com/oauth/auth?response_type=code&client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&scope=signature%20impersonation&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fcreatequote%2Fgenloa%2Fsign%2F

【问题讨论】:

  • 欢迎来到 StackOverflow!请编辑您的问题以添加其他信息:发生了什么?你收到错误信息了吗? (如果有,请附上信息。)
  • 谢谢你,@LarryK!说实话,我还没有运行这段代码。我会尽我所能完成整理,并添加有关发生情况的信息。此外,我只是想了解如何使用这些变量。我一直在阅读文档,但我觉得我仍然不明白应该是什么。当我运行程序时,在填写我创建的表单后,会创建一个特定表单的 PDF。我想知道如何使用新生成的 PDF 进行签名。这就是模板吗?
  • 此外,我只是真的不明白“信封”和“模板”之间的区别是什么,而且我觉得通过我所写的文档中的措辞并不明显阅读。

标签: django python-3.x docusignapi


【解决方案1】:

我在尝试将 docusign 与我们的系统集成时遇到了同样的问题。首先,我建议您查看 C# 版本的文档签名文档,该文档比 python 文档更好。 Github Repo。另外,请查看 python sdk repo 以查看示例。

为了回答您的问题,我将在此处发布带有简化代码的实现。在我们的实现中,我们没有实现模板 api。我们只需生成自己的 pdf 文件并将 PDF 作为 Document 附加 到 Envelope Definition。此外,您需要将收件人信息(包括选项卡和签名者)和通知信息添加到定义中。 基本上,信封定义的大部分参数都是可选的,你需要弄清楚你需要使用它们。之后,你可以使用信封定义来创建信封。

请注意,在这些过程之后,您将获得一个信封ID。您可能希望将信封状态更改为“已发送”以通过 docusign 发送。或者,如果您想将其与您的应用程序集成,您可以创建一个 docusign.RecipientViewRequest() 来获取临时 url,签名者可以使用它来打开一个 docusign 页面并签署合同。

def create_envelope(file_data, other_info):
    ## .....
    ## file_data is the pdf data
    envelope_definition = create_envelope_definition(file_data, other_info)
    envelope_id = docusign_create_envelope(envelope_definition)
    ## .....

def create_envelope_definition(file_data, other_info):

    envelope_definition = docusign.EnvelopeDefinition()
    envelope_definition.email_subject = 'your email subject'
    envelope_definition.email_blurb = 'your email message here'

    # add a document to the envelope
    doc = docusign_parameter_document(file_data)
    envelope_definition.documents = [doc]

    # Add recipients to sign the document
    recipients = docusign_parameter_recipients(other_info['recipients_info'])

    envelope_definition.recipients = recipients
    envelope_definition.status = 'your init envelope status' # if you want to send it when you create the envelope instantly, set it to sent
    envelope_definition.event_notification = docusign_parameter_event_notification()

    envelope_definition.notification = docusign_parameter_notification(other_info['notification_setting'])

    return envelope_definition

def docusign_parameter_document(file_data):
    doc = docusign.Document()
    base64_doc = base64.b64encode(file_data).decode("utf-8")
    doc.document_base64 = base64_doc
    doc.name = 'your file name'
    doc.document_id = '1'
    return doc

def docusign_parameter_recipients(other_info):
    signers = []
    for e in other_info.contract_signers:
        signer = docusign_parameter_signer(e)
        tabs = docusign_parameter_tabs(other_info, e)
        signer.tabs = tabs
        signers.append(signer)

    recipients = docusign.Recipients()
    recipients.signers = signers
    return recipients


def docusign_parameter_signer(....):
    signer = docusign.Signer()
    signer.email = 'your signer email'
    signer.name = 'your signer name'
    signer.recipient_id = '1' 
    client_user_id = 'your local system id'
    signer.client_user_id = client_user_id
    return signer

def docusign_parameter_tabs(contract_signer):
    sign_here = docusign.SignHere()
    sign_here.document_id = '1'
    sign_here.recipient_id = '1' # notice: here i just simply set up it as 1, after the envelope created, you could retrieve the actual id by using envelopes_api.list_recipients
    sign_here.anchor_case_sensitive = 'true'
    sign_here.anchor_horizontal_alignment = 'left'
    sign_here.anchor_ignore_if_not_present = 'false'
    sign_here.anchor_match_whole_word = 'true'
    sign_here.anchor_string = 'your placeholder string' # you need to use the string to place your sign here tab, otherwise, you need to set up a x,y coordinate to place the tab
    sign_here.anchor_units = 'cms'
    sign_here.anchor_x_offset = '0'
    sign_here.anchor_y_offset = '0'
    sign_here.tab_label = 'sign_here'
    tabs = docusign.Tabs()
    tabs.sign_here_tabs = [sign_here]
    return tabs

def docusign_parameter_event_notification():
    ## return a EventNotification() object

def docusign_parameter_notification(contract):
    ## return a Notification() object

def docusign_create_envelope(envelope_definition):
    ##......some variable declarations here

    api_client = docusign_auth(base_url, integrator_key, redirect_uri, oauth_base_url, private_key_filename, user_id)

    auth_api = AuthenticationApi()
    envelopes_api = EnvelopesApi()

    try:
        ## atuh......
        envelope_summary = envelopes_api.create_envelope(login_accounts[0].account_id,
                                                         envelope_definition=envelope_definition)
        return envelope_summary.envelope_id
    except ApiException as e:
        raise Exception("Exception when calling DocuSign API: %s" % e)

def docusign_auth(base_url, integrator_key, redirect_uri, oauth_base_url, private_key_filename, user_id):
    api_client = docusign.ApiClient(base_url)
    api_client.configure_jwt_authorization_flow(private_key_filename, oauth_base_url, integrator_key, user_id, 3600)
    docusign.configuration.api_client = api_client
    return docusign.configuration.api_client

如果您有任何问题,请在下方留下 cmets。我会尝试解决这个问题。希望对您有所帮助。


已编辑 问题:如何访问pdf文件并通过docusign发送?

如果您知道 pdf 文件的路径(包括文件名和扩展名)。然后,你只需要使用下面的代码:

with open(filepath, "rb") as fileobj: 
    file_data = fileobj.read() 

获取pdf文件数据作为变量file_data。 那么,请注意功能

docusign_parameter_document() 

在函数中,需要这一行

base64_doc = base64.b64encode(file_data).decode("utf-8") 

将 file_data 变量传递给函数并获取 base64 编码数据。最后,您可以将 base64_doc 附加到 Document 对象。


已编辑 DEVELOPMENT模式的简短工作示例

def test(): 
    username = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    integrator_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    # Do not change here if your are using development env
    base_url = "https://demo.docusign.net/restapi"
    # Do not change here if your are using development env
    oauth_base_url = "account-d.docusign.com"
    redirect_uri = "https://xxxxxx.xx"

    # Path to your test key file
    private_key_filename = os.path.dirname(os.path.abspath(__file__)) + "/test_private_key.txt"

    # Same as your username
    user_id = "xxxxxxxxxxxxxxxxxxxxxx"

    # Add a recipient to sign the document
    signer = docusign.Signer()
    signer.email = 'xxxxxxx@xxxxx.com'
    signer.name = 'your name'
    signer.recipient_id = '1'
    client_user_id = '1'
    signer.client_user_id = client_user_id

    # Create a SignHere tab somewhere on the document for the signer to sign
    sign_here = docusign.SignHere()
    sign_here.document_id = '1'
    sign_here.recipient_id = '1'
    sign_here.anchor_case_sensitive = 'true'
    sign_here.anchor_horizontal_alignment = 'left'
    sign_here.anchor_ignore_if_not_present = 'false'
    sign_here.anchor_match_whole_word = 'true'

    ############################################################################## 
    #Please notice that, the string 'Sign Here' need to be shown in your pdf file#
    ############################################################################## 
    sign_here.anchor_string = 'Sign Here'
    sign_here.anchor_units = 'cms'
    sign_here.anchor_x_offset = '0'
    sign_here.anchor_y_offset = '0'
    sign_here.tab_label = 'sign_here'
    tabs = docusign.Tabs()
    tabs.sign_here_tabs = [sign_here]


    # Create a signers list, attach tabs to signer, append signer to singers
    # attach signers to recipients object
    signers = []
    tabs = tabs
    signer.tabs = tabs
    signers.append(signer)
    recipients = docusign.Recipients()
    recipients.signers = signers

    # Create an envelope to be signed
    envelope_definition = docusign.EnvelopeDefinition()
    envelope_definition.email_subject = 'mysubject'
    envelope_definition.email_blurb = 'myblurb'

    # add a document to the envelope_definition
    pdfpath = os.path.dirname(os.path.abspath(__file__)) + "/blank1.pdf"
    with open(pdfpath, 'rb') as signfile:
        file_data = signfile.read()
        doc = docusign.Document()
        base64_doc = base64.b64encode(file_data).decode('utf-8')
        doc.document_base64 = base64_doc
        doc.name = 'myDoc'
        doc.document_id = '1'
        envelope_definition.documents = [doc]
        signfile.close()
    envelope_definition.recipients = recipients
    envelope_definition.status = 'sent'
    cwd = os.getcwd()
    print(cwd)

    api_client = docusign.ApiClient(base_url)

    ### You need open your browser, visit the url onece and approve
    oauth_login_url = api_client.get_jwt_uri(integrator_key, redirect_uri, oauth_base_url)

    api_client.configure_jwt_authorization_flow(private_key_filename, oauth_base_url, integrator_key, user_id, 3600)
    docusign.configuration.api_client = api_client

    auth_api = AuthenticationApi()
    envelopes_api = EnvelopesApi()

    try:
        ############################################
        # Notice: you need login here via your code here
        ############################################
        login_info = auth_api.login()
        login_accounts = login_info.login_accounts
        base_url, _ = login_accounts[0].base_url.split('/v2')
        api_client.host = base_url
        docusign.configuration.api_client = api_client

        envelope_summary = envelopes_api.create_envelope(login_accounts[0].account_id,
                                                         envelope_definition=envelope_definition)
        print(envelope_summary)
    except ApiException as e:
        raise Exception("Exception when calling DocuSign API: %s" % e)
    except Exception as e:
        print(e)

【讨论】:

  • 我明白了,这看起来很有帮助!我会看看这个,看看我是否可以确定在我的项目中可以使用的方法。就我的其他尝试而言,我在这里发布了另一个线程:stackoverflow.com/questions/51487926/… 当然,我也会研究 C# 实现,因为就 python SDK 而言,我不太明白我做错了什么去。非常感谢您的时间和帮助! :)
  • 就实施而言,我确实有几个问题: - 标签:这些是必要的吗?根据我在参考中读到的内容; “自定义选项卡使帐户可以拥有一个或多个预配置(自定义)选项卡。自定义选项卡可以在用户标记文档时节省时间,因为用户不必手动设置选项卡的参数。此类别可以管理自定义选项卡以编程方式,包括创建、删除等。”从阅读本文中,我不太明白为什么它们是必要的。他们具体是做什么的?
  • - Notifications/EventNotifications:这些有必要吗?在我的工作流程中,用户填写了一个生成 PDF 的表单,该 PDF 保存到我的数据库中的模型中。保存后,我试图从数据库中获取该文件,并使用 docusign 对其进行签名。一次只会有一个用户签名,我打算让我公司的电子邮件地址接收带有签名文档的电子邮件,所以如果不需要通知,我宁愿不使用它们。此外,就这些功能中的许多功能而言,我正计划将它们浓缩成一个功能。
  • 嗨,@JCoder96。我很抱歉造成混乱。澄清一下,通知/事件通知是可选的。 EventNotification 仅充当 webhook 配置。因此,如果您在服务器上设置了一个侦听器,它将在客户完成签名时收到通知。 通知可用于设置合同的到期日和提醒。 (在下一条评论中继续)
  • 标签,但是,在您的情况下是必要的,因为您需要在您的 pdf 上添加一个 sign_here 选项卡。完整的过程是: 1、使用docusign.SignHere()创建一个sign_here对象; 2、使用docusign.Tabs()创建一个tabs对象; 3、将sign_here作为列表[sign_here]附加到tabs.sign_here_tabs; 4、使用docusign.Signer()创建一个signer对象; 5、将tabs对象附加到signer.tabs; 6、使用docusign.Recipients()创建一个recipients对象,并将signer(将对象放入列表)附加到recipients.signers。 (在下一条评论中继续)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
相关资源
最近更新 更多