peopledoc / django-docusign Goto Github PK
View Code? Open in Web Editor NEWIntegration of DocuSign's SaaS signature platform with Django
License: Other
Integration of DocuSign's SaaS signature platform with Django
License: Other
I might be missing something, but is there anything currently implemented that verifies callback (POST) events actually originate from Docusign?
... and it is tested!
At the moment, dummy sentences are used as emailSubject
and emailBlurb
arguments on envelope creation: https://github.com/novapost/django-docusign/blob/0.4/django_docusign/backend.py#L99-L100
They should be customizable. Or perhaps they must be customized (mandatory, not optional).
$ make demo
[...]
django.db.utils.OperationalError: unable to open database file
This appears to be caused by a missing /var/
directory in the project root at the time syncdb is run.
Just remove SignatureCallbackView.clean_status
To reproduce: just reject the signature without filling the reason
In order to ease maintenance of the project, as a maintainer, I want to share the release and merging guidelines.
Analogous ticket in pydocusign here: peopledoc/pydocusign#76
There is some code in demo project that could be reused and moved to django_docusign's core.
Let's provide some SignatureCallbackView.
The callback view only processes the most recent recipient event in a callback payload. In the case of signing order, though, a signer marked as "Completed" is reported as a simultaneous event along with the next signer's "Sent" event in the ordering. This means the callback view misses the first signer's event.
Here are the RecipientStatuses
sections of two subsequent callback requests, to illustrate:
Callback request for a "Delivered" status for the first signer:
<RecipientStatus>
<Type>Signer</Type>
<Email>[email protected]</Email>
<UserName>First Signer</UserName>
<RoutingOrder>1</RoutingOrder>
<Sent>2016-11-14T15:42:29.037</Sent>
<Delivered>2016-11-14T15:45:46.667</Delivered>
<DeclineReason xsi:nil="true"/>
<Status>Delivered</Status>
<RecipientIPAddress>1.1.1.1</RecipientIPAddress>
<ClientUserId>17</ClientUserId>
<CustomFields/>
<AccountStatus>Active</AccountStatus>
<RecipientId>1aa6002b-5d93-4549-a45f-7e5906327d9e</RecipientId>
</RecipientStatus>
<RecipientStatus>
<Type>Signer</Type>
<Email>[email protected]</Email>
<UserName>Second Signer</UserName>
<RoutingOrder>2</RoutingOrder>
<DeclineReason xsi:nil="true"/>
<Status>Created</Status>
<RecipientIPAddress/>
<ClientUserId>18</ClientUserId>
<CustomFields/>
<AccountStatus>Active</AccountStatus>
<RecipientId>f599463d-8172-40ae-9e38-c004bdfcd9a8</RecipientId>
</RecipientStatus>
Callback request for a "Completed" status for the first signer:
<RecipientStatus>
<Type>Signer</Type>
<Email>[email protected]</Email>
<UserName>First Signer</UserName>
<RoutingOrder>1</RoutingOrder>
<Sent>2016-11-14T15:42:29.037</Sent>
<Delivered>2016-11-14T15:45:46.667</Delivered>
<Signed>2016-11-14T15:46:13.153</Signed>
<DeclineReason xsi:nil="true"/>
<Status>Completed</Status>
<RecipientIPAddress>1.1.1.1</RecipientIPAddress>
<ClientUserId>17</ClientUserId>
<CustomFields/>
<TabStatuses>
<TabStatus>
<TabType>SignHere</TabType>
<Status>Signed</Status>
<XPosition>227</XPosition>
<YPosition>338</YPosition>
<DocumentID>1</DocumentID>
<PageNumber>1</PageNumber>
</TabStatus>
</TabStatuses>
<AccountStatus>Active</AccountStatus>
<RecipientId>1aa6002b-5d93-4549-a45f-7e5906327d9e</RecipientId>
</RecipientStatus>
<RecipientStatus>
<Type>Signer</Type>
<Email>[email protected]</Email>
<UserName>Second Signer</UserName>
<RoutingOrder>2</RoutingOrder>
<Sent>2016-11-14T15:46:13.653</Sent>
<DeclineReason xsi:nil="true"/>
<Status>Sent</Status>
<RecipientIPAddress/>
<ClientUserId>18</ClientUserId>
<CustomFields/>
<AccountStatus>Active</AccountStatus>
<RecipientId>f599463d-8172-40ae-9e38-c004bdfcd9a8</RecipientId>
</RecipientStatus>
Look what happens when pydocusign. parses the recipient events in the last request:
>>> from pydocusign.parser import DocuSignCallbackParser
>>> p = DocuSignCallbackParser(xml_payload)
>>> p.recipient_events
[{'clientUserId': u'17',
'datetime': datetime.datetime(2016, 11, 14, 15, 42, 29, 37000, tzinfo=tzoffset(None, -28800)),
'recipient': u'17',
'recipientId': u'1aa6002b-5d93-4549-a45f-7e5906327d9e',
'status': 'Sent'},
{'clientUserId': u'17',
'datetime': datetime.datetime(2016, 11, 14, 15, 45, 46, 667000, tzinfo=tzoffset(None, -28800)),
'recipient': u'17',
'recipientId': u'1aa6002b-5d93-4549-a45f-7e5906327d9e',
'status': 'Delivered'},
{'clientUserId': u'17',
'datetime': datetime.datetime(2016, 11, 14, 15, 46, 13, 153000, tzinfo=tzoffset(None, -28800)),
'recipient': u'17',
'recipientId': u'1aa6002b-5d93-4549-a45f-7e5906327d9e',
'status': 'Signed'},
{'clientUserId': u'18',
'datetime': datetime.datetime(2016, 11, 14, 15, 46, 13, 653000, tzinfo=tzoffset(None, -28800)),
'recipient': u'18',
'recipientId': u'f599463d-8172-40ae-9e38-c004bdfcd9a8',
'status': 'Sent'}]
The "Completed" event on the first signer is not the last event in the order, so it is not processed; only the "Sent" event for the second signer is processed.
Any ideas on a graceful way to handle this problem? i wonder if there'd be even more missing events if there were multiple recipients sharing the latter signing order, since they'd probably all be sent at once.
If a recipient changes their own name when signing, DocuSign updates that recipient's name in the subsequent DocuSign Connect requests. The DocuSignCallbackView doesn't capture this change, which means a later DocuSign client request which uses the recipient's name (for example, post_recipient_view
) will fail with an UNKNOWN_ENVELOPE_RECIPIENT
error like this:
DocuSignException: DocuSign request failed: POST https://na2.docusign.net/restapi/v2/accounts/<account-id>/envelopes/<envelope_id>/views/recipient returned code 400 while expecting code 201; Message: {
"errorCode": "UNKNOWN_ENVELOPE_RECIPIENT",
"message": "The recipient you have identified is not a valid recipient of the specified envelope."
} ;
Seems to me we want to handle a potential name change in the callback view.
I'm trying to fill an "address" field in my docusign template prior to signing. Passing data was already discussed here.
But its not clear whether the support is actually working and how I would go about this. (Added variable to template and tried passing parameter, that didnt work).
def create_signature(self, signature):
"""Create signature backend-side."""
params = {'address': 'Home Street'}
if self.signature_backend.use_callback:
params['callback_url'] = self.cleaned_data['callback_url']
self.signature_backend.create_signature(
signature,
subject=signature.document_title,
**params
)
There is no documentation how to use this plugin or what feature it have.
pydocusign 0.9 introduces new feature: creating envelopes using templates.
Let's implement it in django-docusign!
Feature request: as a developer, i'd like to be able to copy an empty .env-sample
, save it as an ignored .env-local
file, fill in some values, and be able to run make test
without any environment variable-setting prefix.
i know it doesn't solve for a few tests that require a callback url to be deployed remotely.
Is there support for 1.8? I saw there was a ticket opened for 1.7 from the fall of 2014. This would be extremely helpful for a specific project I am working on.
Also it would be nice if the documentation included a basic example of how to get up an running.
For my project I need to have customers sign a contract to use a service. So something a long the lines of creating a document, having the user sign that document, then updating a database table entry that they have completed it, would be extremely helpful. Doesn't have to be exact to my needs, but I'm sure other potential users would benefit from a little hand holding as well.
Please let me know if more information could be published or is available else where. Thank you for your time.
As a developer and django-docusign user, in order to share DocuSign configuration across several views/forms/models... in code, I want to write the shared configuration once in Django settings, and have pydocusign.DocuSignClient
initialized with these settings.
See https://github.com/novapost/pydocusign/blob/0.11/pydocusign/client.py#L21-L29
pydocusign.DocuSignClient
initialization takes several arguments => allow developer to define them in DOCUSIGN
dictionary.
Example in settings.py:
DOCUSIGN = {
root_url: 'https://demo.docusign.net/restapi/v2',
integrator_key: 'some-secret-integrator-key',
}
Requires recent version of Tox to setup a nice test grid: https://testrun.org/tox/latest/config.html#generating-environments-conditional-settings
Django ships with a UUIDField, since 1.8. Perhaps the base model classes can use it when Django 1.8 or higher is installed.
i see the docusign_template_id
exists on the SignatureType
model in the demo, but the backend itself references this property that won't exist on an existing model unless explicitly added. This results in an AttributeError. Perhaps the docs ought to mention this, or perhaps the backend should handle cases where it doesn't exist, which seems sufficient given that the model field in the demo accepts empty strings.
At the moment, DocuSignCallbackView handles envelope notifications. Should also handle recipient notifications.
I am checking out the demo. I have created a sandbox account on the DocuSign site. I have stored those credentials under Change DocuSign settings .
I am trying to Create signature . I choose the document I want to upload, on the name field I type "DanaeVogiatzi" and on the e-mail field I type the same e-mail as I my DocuSign account.
In the database the records are being created. Each row misses the signature_backend_id and is saved as draft.
I am totally new on this digital signing thing ,so I am still trying to figure out how it works.
Am i doing something wrong? Should I make any extra settings on the Docusign site?
Thanks!
It seems that django_anysign's implementation of get_signature_callback_url()
uses the signature pk
as an argument. django_docusign doesn't override that. But neither the callback view in the package nor the demo take a pk
argument. So a NoReverseMatch is raised.
Implement utilities to handle DocuSign Connect's callbacks, i.e. eventNotification arguments in "create envelope" function. See https://www.docusign.com/p/RESTAPIGuide/RESTAPIGuide.htm#REST%20API%20References/Send%20an%20Envelope.htm
Context in workflow:
The view should be generic: provide bits to handle the DocuSign's request, but let the developer implement custom stuff to update the signature and trigger business-specific operations.
Why does SignatureCallbackView
inherit from TemplateResponseMixin
? The view only supports a POST, so aren't the mixin and the template unnecessary?
Add a wrapper for DocuSignClient.get_page_image()
in DocuSignBackend
(PR on the way)
DocuSign's API returns a 400 when a subject or blurb are not present in the envelope creation call. For this reason, i propose that we make them required arguments in the create_signature
function. That would change the function's signature, so may be backwards incompatible, but otherwise create_signature(obj)
will fail every time.
The documentation didn't indicate that the callback url needs to be namespaced as 'anysign' by default. i suppose the only way to override this is to subclass the backend and reference it in settings.ANYSIGN['BACKENDS']
? Perhaps the namespace could be a setting.
i wrote an app using this library based on the assumption that v2 of the API was supported (based on the implementation in the demo). Reverting to v1 is causing regressions. This app's demo should probably be tested against v1, to prevent further confusion.
Over time, pydocusign may support more Envelope attributes. To support this, allow passing arbitrary parameters to create_signature
.
if signer_return_url is None:
self.get_signer_return_url(signer)
https://github.com/novapost/django-docusign/blob/master/django_docusign/backend.py#L133
=> signer_return_url = self.get_signer_return_url(signer)
In demo project:
SettingsView
uses fields from SettingsForm
, such as root_url
. See https://github.com/novapost/django-docusign/blob/0.6/demo/django_docusign_demo/forms.py#L12.SettingsView
saves form's cleaned data in session, with keys like root_url
. See https://github.com/novapost/django-docusign/blob/0.6/demo/django_docusign_demo/views.py#L58docusign_setting()
utility looks for settings in session, else fallbacks to environment variables. See https://github.com/novapost/django-docusign/blob/0.6/demo/django_docusign_demo/views.py#L19docusign_settings()
uses keys such as PYDOCUSIGN_TEST_ROOT_URL
, whereas in session we may have root_url
. See https://github.com/novapost/django-docusign/blob/0.6/demo/django_docusign_demo/views.py#L29So, there is something wrong somewhere in this implementation...
pydocusign 0.7 embeds templates of DocuSign notification callbacks. So "callback_*.xml" samples in django-docusign demo's fixtures are obsolete. Let's take advantage of pydocusign feature!
In pydocusign version 0.13 (and incoming 0.13.1), there are changes about Envelope.post_recipient_view() and routingOrder => improve django-docusign with these changes.
I try to use this app in the context of my project, and I have to log in and sign a document in a iFrame (or web view), inside my app.
I checked your demo, and I understood the most part of the code.
Unfortunately, I have big troubles using a custom namespace for my Django app, as the default namespace for DocuSign components is 'anysign'.
Also, I have troubles in getting the absolute URL, for a given signer, in order to get my web view.
In home.html
, I have the same code of your demo:
...
{% for signer in signature.signers.all %}
<li>{{ signer.email }} => <a href="{{ signer.get_absolute_url }}">sign</a> [{{ signer.status }}{% if signer.status_datetime %} {{signer.status_datetime }}{% endif %}{% if signer.status_details %} {{signer.status_details }}{% endif %}]</li>
{% endfor %}
...
The problem in this code is {{ signer.get_absolute_url }}
. Indeed, this code will call the get_absolute_url
method of the signer
object, from django-anysign.
Using this code, I have the following error:
NoReverseMatch at /
Reverse for 'signer' not found. 'signer' is not a valid view function or pattern name.
I don't understand this error because I set my URLs correctly (I think) in urls.py
, like this:
anysign_patterns = [
url(r'^signer/(?P<pk>\d+)/$', signer_view, name='signer'),
url(r'^signer/(?P<pk>\d+)/return/$',
signer_return_view,
name='signer_return'),
url(r'^signer/(?P<pk>\d+)/canceled/$',
signer_canceled_view,
name='signer_canceled'),
url(r'^signer/(?P<pk>\d+)/error/$',
signer_error_view,
name='signer_error'),
url(r'^signer/(?P<pk>\d+)/declined/$',
signer_declined_view,
name='signer_declined'),
url(r'^signer/(?P<pk>\d+)/signed/$',
signer_signed_view,
name='signer_signed'),
]
urlpatterns = [
url(r'^$', home_view, name='home'),
url(r'^signature/add/$', create_signature_view, name="create_signature"),
url(r'', include((anysign_patterns, 'anysign'), namespace='anysign')),
]
and the signer_view
is a view from a class in views.py
, like your demo.
I don't understand why I have this issue calling a method for the signer
object...
Is there some "black magic" inside, or something I don't fully understand (I think this is the solution of my problem :) ).
Thanks in advance!
Django 1.7 has been released. It looks like tests do not pass with this version.
In the demo project, add examples with multiple signers, so that we make sure django-docusign works with multiple signers.
The most recent release to PyPI was v0.12 on 2015-09-10. There have been 8 merged PRs since then; could we issue a v0.13 release?
See https://github.com/novapost/django-docusign/blob/0.6/demo/django_docusign_demo/views.py#L169-L170
Bad pattern! The whole list is built before we get element 0.
Whereas with iterator's next() function, only first element is computed.
Not a big deal at the moment, because we have a single item in the list... but still ugly code ;)
The if
expression here will always return true, because the list comprehension only returns a list of True
values. PR incoming.
if all([True
for signer_event in self.docusign_parser.recipient_events
if signer_event['status'] == 'sent']):
signer_events = self.docusign_parser.recipient_events
# Else, do not care about "sent" event for signature.
else:
signature_event = None
signer_events = [self.docusign_parser.recipient_events[-1]]
I try to understand how to customize this Django module, based on my case:
XXX/views/sender
, the tabs;So, in this example, I have two distinct users:
In the model, we have only one user model: Signer
.
Is Signer
is supposed to be the creator and the signer of the document?
Also, do you have any idea how to to restrict the possibilities (to edit/modify tabs and signature) for both the creator and the signer?
I checked the code of the module, and I found that the post_recipient_view
method calls only XXX/views/recipient
, and not XXX/views/sender
.
So can we consider the Signer
model as a recipient only?
Thanks in advance, and thanks for this awesome work :)
The makemigrations
management command will create a new migration for the model inheriting SignatureType
whenever the keys in settings.ANYSIGN['BACKENDS']
change.
i think we need to make the function used for signature_backend_code.choices
deconstructible
At the moment, looks like signers are ordered from latest to oldest (by primary key descending?).
Make sure ordering is preserved. Make it easy to control the order.
Cases in SignatureCallbackView:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.