ruby_universign 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d1fa1af558c66f055f6fe0199c1733e6397f963c
4
+ data.tar.gz: c5b5525f439229fa8b4633bb804971324ba07481
5
+ SHA512:
6
+ metadata.gz: 5f81314ec97a5383cbc63eee709122508ec6c6af177c08f4359419f50499df4389f6b405576982aba02c6f9b5ee1553e28a8c756e5e401e039764ba39116afb3
7
+ data.tar.gz: 9cd5a8bf9c16232d8593304fed7e6ba098baf057646f02837bee5a78c61bbddfbd3fe175e0baae66f3158e9b2ea4f18e447832119a6da257960d34345554714d
data/.env.example ADDED
@@ -0,0 +1,3 @@
1
+ UNIVERSIGN_ENDPOINT=https://sign.test.cryptolog.com/sign/rpc/
2
+ UNIVERSIGN_LOGIN=foo@bar.com
3
+ UNIVERSIGN_PASSWORD=e_________Yp
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .env
11
+ .idea
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.3
data/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ v???
2
+ -------------------------
3
+
4
+ - rename gem to `ruby_universign` and `ESign` module to `Universign`
5
+
6
+ v0.1.6 (26/11/2015)
7
+ -------------------------
8
+
9
+ - Add `final_doc_sent` and `final_doc_requester_sent` parameters to `ESign::Service::Transaction`
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in universign.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 CapSens
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,149 @@
1
+ # RubyUniversign
2
+
3
+ RubyUniversign is a Ruby gem for interacting with [Universign](https://www.universign.com/) electronic signature API.
4
+
5
+ It ease requests to Universign API, documents uploads and following signature state.
6
+
7
+ This gem is **not** officialy made by Universign, but was originally created for [CapSens](https://capsens.eu/) internal usage.
8
+
9
+ This gem currently integrate electronic signature service, but not other Universign services (timestamping and server stamp).
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'ruby_universign', require: 'universign'
17
+ ```
18
+
19
+ And then run `bundle`
20
+
21
+ Or, #TODO
22
+
23
+ ## Usage
24
+
25
+ Configuration:
26
+
27
+ ```ruby
28
+ # if you're using Rails, put this in an initializer
29
+ Universign.configure do |config|
30
+ config.endpoint = ENV['UNIVERSIGN_ENDPOINT']
31
+ config.login = ENV['UNIVERSIGN_LOGIN']
32
+ config.password = ENV['UNIVERSIGN_PASSWORD']
33
+ end
34
+ ```
35
+
36
+ Then, you can create a transaction like this:
37
+
38
+ ```ruby
39
+ document_from_url = Universign::Document.new(
40
+ name: 'my_contract.pdf',
41
+ url: 'http://www.orimi.com/pdf-test.pdf'
42
+ )
43
+ document_from_content = Universign::Document.new(
44
+ name: 'another.pdf',
45
+ content: File.open('spec/fixtures/universign-guide-8.8.pdf').read
46
+ )
47
+
48
+ signer = Universign::TransactionSigner.new(
49
+ first_name: "Signer's first name",
50
+ last_name: "Signer's last name",
51
+ email: 'test@gmail.com',
52
+ phone_number: '0101010101',
53
+ success_url: 'https://google.com/',
54
+ signature: Universign::SignatureField.new(coordinate: [20, 20], page: 1)
55
+ )
56
+
57
+ transaction = Universign::Transaction.create(
58
+ documents: [document_from_url, document_from_content],
59
+ signers: [signer],
60
+ options: { profile: 'default', final_doc_sent: true }
61
+ )
62
+
63
+ transaction.url
64
+ # => "https://sign.test.universign.eu/fr/signature/?id=f052e35e-a792-4440-bb67-6b5c3f17aa30"
65
+
66
+ transaction.transaction_id
67
+ # => "9696179e-a43d-4803-beeb-9e5c02fd159b"
68
+
69
+ # get back universign transaction:
70
+ transaction = Universign::Transaction.new('9696179e-a43d-4803-beeb-9e5c02fd159b')
71
+ transaction.signed?
72
+
73
+ ```
74
+
75
+ ### `Universign::Document`
76
+
77
+ It can be created with either your file's content or your file's url.
78
+
79
+ ### `Universign::SignatureField`
80
+
81
+ Nothing much to say here. It follows Universign's signature field.
82
+
83
+ ### `Universign::TransactionSigner`
84
+
85
+ * `success_url` is where your user will be redirected after signing the documents.
86
+ * `phone_number` is optional. If you don't specify it, Universign will ask the user for it at the time of the signature.
87
+ * `email` is optional, unless you want to use transaction's `final_doc_sent` option (to send signed documents to user's email).
88
+
89
+ ### `Universign::Transaction`
90
+
91
+ To start a transaction with Universign, you only require documents and signers.
92
+
93
+ Options are, as the name imply, optional ! Available options are (snake_case of Universign's names):
94
+
95
+ ```
96
+ custom_id
97
+ description
98
+ handwritten_signature_mode
99
+ certificate_type
100
+ language
101
+ identification_type
102
+ handwritten_signature
103
+ profile
104
+ final_doc_sent
105
+ final_doc_requester_sent
106
+ ```
107
+
108
+ Default options are:
109
+ ```ruby
110
+ {
111
+ handwrittenSignatureMode: 1,
112
+ identificationType: 'sms',
113
+ language: 'fr',
114
+ certificateType: 'simple'
115
+ }
116
+ ```
117
+
118
+ For more informations on theses options, see Universign's official documentation
119
+
120
+ Once your transaction is created:
121
+ * `url` is where you must redirect your users for them to sign
122
+ * `transaction_id` is the id you must save to retrieve it later. You can request up-to-date informations from Universign with `Universign::Transaction.new(transaction_id)`.
123
+ * `signed?` returns a boolean that tells you if the transaction is signed, or not !
124
+
125
+ ## Universign documentation
126
+
127
+ As of September 25th 2018, all official Universign documentation can be found at [https://help.universign.com/hc/fr/sections/360000148149-Guides-Universign]().
128
+
129
+ ## Development
130
+
131
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
132
+
133
+ To install this gem onto your local machine, run `bundle exec rake install`.
134
+
135
+ (# TODO) To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
136
+
137
+ ## Contributing
138
+
139
+ Bug reports and pull requests are welcome on GitHub at https://github.com/CapSens/universign. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
140
+
141
+ ## License
142
+
143
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
144
+
145
+
146
+
147
+ ## notes
148
+
149
+ final_doc_sent: pour que le document soit envoyé par mail au signataire
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
data/bin/console ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "universign"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+ require 'dotenv'
13
+
14
+ Dotenv.load
15
+
16
+ Universign.configure do |config|
17
+ config.endpoint = ENV['UNIVERSIGN_ENDPOINT']
18
+ config.login = ENV['UNIVERSIGN_LOGIN']
19
+ config.password = ENV['UNIVERSIGN_PASSWORD']
20
+ end
21
+
22
+ require "irb"
23
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/lib/universign.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+
3
+ require "universign/version"
4
+ require 'xmlrpc/client'
5
+ require 'universign/service/document'
6
+ require 'universign/service/transaction'
7
+ require 'universign/safeguard'
8
+ require 'universign/signer'
9
+ require 'universign/transaction'
10
+ require 'universign/signature_field'
11
+ require 'universign/signer_infos'
12
+ require 'universign/transaction_signer'
13
+ require 'universign/client'
14
+ require 'universign/document'
15
+ require 'universign/configuration'
16
+ require 'universign/error'
17
+
18
+ module Universign
19
+ include Error
20
+ end
@@ -0,0 +1,32 @@
1
+ require 'singleton'
2
+
3
+ module Universign
4
+ class Client
5
+ include ::Singleton
6
+ attr_reader :client
7
+
8
+ def initialize
9
+ @client = XMLRPC::Client.new2(Universign.configuration.endpoint)
10
+ @client.user = Universign.configuration.login
11
+ @client.password = Universign.configuration.password
12
+ end
13
+
14
+ def method_missing(method, *args, &block)
15
+ if @client.respond_to?(method)
16
+ @client.send(method, *args, &block)
17
+ else
18
+ super(method, *args, &block)
19
+ end
20
+ end
21
+
22
+ # _ _
23
+ # _____ _____ ___ _ __ | |_(_) ___ _ __ ___
24
+ # / _ \ \/ / __/ _ \ '_ \| __| |/ _ \| '_ \/ __|
25
+ # | __/> < (_| __/ |_) | |_| | (_) | | | \__ \
26
+ # \___/_/\_\___\___| .__/ \__|_|\___/|_| |_|___/
27
+ # |_|
28
+ class InvalidCredentials < StandardError; end
29
+ class ErrorWhenSigningPDF < StandardError; end
30
+ class UnknownException < StandardError; end
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ module Universign
2
+ class Configuration
3
+ attr_accessor :login, :password, :endpoint
4
+
5
+ def initialize
6
+ @login = ''
7
+ @password = ''
8
+ @endpoint = ''
9
+ end
10
+ end
11
+
12
+ # @return [Universign::Configuration] Universign's current configuration
13
+ def self.configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ # Set Universign's configuration
18
+ # @param config [Universign::Configuration]
19
+ def self.configuration=(config)
20
+ @configuration = config
21
+ end
22
+
23
+ # Modify Universign's current configuration
24
+ # @yieldparam [Universign::Configuration] config current Universign config
25
+ # ```
26
+ # Universign.configure do |config|
27
+ # config.login = "your-mail@provider.com"
28
+ # end
29
+ # ```
30
+ def self.configure
31
+ yield configuration
32
+ end
33
+ end
@@ -0,0 +1,116 @@
1
+ module Universign
2
+ class Document
3
+ include Universign::Safeguard
4
+
5
+ attr_reader :name, :file_content, :file_url
6
+ attr_accessor :params
7
+
8
+ # Create a new Universign::Document
9
+ #
10
+ # @param [Hash] options
11
+ # @option options [Array<Byte>] :content Content of the PDF
12
+ # @option options [String] :url URL of the PDF
13
+ # @option options [String] :name Name of the PDF
14
+ # @option options [Hash] :meta_data Hash to join to the PDF
15
+ def initialize(options = {})
16
+ @params = HashWithIndifferentAccess.new
17
+
18
+ options.each do |key, value|
19
+ send("#{key}=", value)
20
+ end
21
+ end
22
+
23
+ # Create a new document from a Hash
24
+ #
25
+ # @param [Hash] data
26
+ # @return [Universign::Document]
27
+ def self.from_data(data)
28
+ @params = data
29
+
30
+ document = Universign::Document.new
31
+ document.params.merge!(@params)
32
+ document
33
+ end
34
+
35
+ # The raw content of the PDF document
36
+ #
37
+ # @return [Array<Byte>]
38
+ def content
39
+ @content ||= params['content']
40
+ end
41
+
42
+ def content=(data)
43
+ @content = data
44
+ params[:content] = XMLRPC::Base64.new(data)
45
+ end
46
+
47
+ # The URL to download the PDF document
48
+ #
49
+ # @return [String]
50
+ def url
51
+ @url ||= params['url']
52
+ end
53
+
54
+ def url=(data)
55
+ @url = data
56
+ params['url'] = data
57
+ end
58
+
59
+ # The type of this document
60
+ #
61
+ # @return [String]
62
+ def document_type
63
+ @document_type ||= params['documentType']
64
+ end
65
+
66
+ # The file name of this document
67
+ #
68
+ # @return [String]
69
+ def name
70
+ @name ||= params['name']
71
+ end
72
+
73
+ def name=(data)
74
+ @name = data
75
+ params['name'] = data
76
+ end
77
+
78
+ # The meta data of the PDF document
79
+ #
80
+ # @return [Hash]
81
+ def meta_data
82
+ @meta_data ||= params['metaData']
83
+ end
84
+
85
+ def meta_data=(data)
86
+ if !data.is_a?(Hash)
87
+ raise MetaDataMustBeAHash
88
+ end
89
+
90
+ @meta_data = data
91
+ params['metaData'] = data
92
+ end
93
+
94
+ # _ _
95
+ # _____ _____ ___ _ __ | |_(_) ___ _ __ ___
96
+ # / _ \ \/ / __/ _ \ '_ \| __| |/ _ \| '_ \/ __|
97
+ # | __/> < (_| __/ |_) | |_| | (_) | | | \__ \
98
+ # \___/_/\_\___\___| .__/ \__|_|\___/|_| |_|___/
99
+ # |_|
100
+ class UnknownDocument < StandardError; end
101
+ class NotSigned < StandardError; end
102
+ class MissingDocument < StandardError; end
103
+ class MetaDataMustBeAHash < StandardError; end
104
+ class DocumentURLInvalid < StandardError
105
+ attr_accessor :url
106
+
107
+ def initialize(url)
108
+ @url = url
109
+ end
110
+
111
+ def to_s
112
+ "Can't find document at '#{@url}''"
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,14 @@
1
+ module Universign
2
+ module Error
3
+ autoload :Client, 'client'
4
+ ERROR_CODE = {
5
+ 73002 => Universign::Client::ErrorWhenSigningPDF, # An error occured when signing the PDF document
6
+ 73010 => Universign::Client::InvalidCredentials, # The login and/or password are invalid.
7
+ 73025 => Universign::Document::UnknownDocument, # The used transaction id or custom id is invalid
8
+ 73027 => Universign::Document::NotSigned
9
+ }
10
+
11
+ class UniversignError < StandardError; end
12
+ class NotEnoughTokens < UniversignError; end
13
+ end
14
+ end
@@ -0,0 +1,59 @@
1
+ module Universign
2
+ module Safeguard
3
+ def self.included(klass)
4
+ klass.extend ClassMethods
5
+ end
6
+
7
+ def safeguard(callback = nil, &block)
8
+ self.class.safeguard(callback, &block)
9
+ end
10
+
11
+ module ClassMethods
12
+ def safeguard(callback = nil, &block)
13
+ block.call
14
+ rescue XMLRPC::FaultException => ex
15
+ if ex.faultCode == 73020
16
+ raise ex
17
+ end
18
+
19
+ known_exception = Universign::ERROR_CODE[ex.faultCode]
20
+
21
+ if known_exception
22
+ raise known_exception
23
+ elsif ex.faultString.include?('Error on document download for this URL')
24
+ url = ex.faultString.match(/<(.+)>/)[1] rescue 'unknown URL'
25
+ raise Universign::Document::DocumentURLInvalid.new(url)
26
+ elsif ex.faultString.include?('Invalid document URL')
27
+ url = ex.faultString.match(/<(.+)>/)[1] rescue 'unknown URL'
28
+ raise Universign::Document::DocumentURLInvalid.new(url)
29
+ elsif ex.faultString.include?('Not enough tokens')
30
+ raise Universign::NotEnoughTokens
31
+ elsif ex.faultString.include?('ID is unknown')
32
+ raise Universign::Document::UnknownDocument
33
+ else
34
+ handle_exception(ex, callback)
35
+ end
36
+
37
+ rescue RuntimeError => ex
38
+ if ex.message.include?('Authorization failed')
39
+ raise Universign::Client::InvalidCredentials
40
+ end
41
+ raise ex
42
+ end
43
+
44
+ private
45
+
46
+ def handle_exception(ex, callback)
47
+ if callback.respond_to?(:call)
48
+ if callback.lambda? && callback.arity.zero?
49
+ callback.call
50
+ else
51
+ callback.call(ex)
52
+ end
53
+ else
54
+ raise ex
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,45 @@
1
+ module Universign
2
+ module Service
3
+ module Document
4
+ # Retrieve documents signed
5
+ #
6
+ # @return [Array<Universign::Document>]
7
+ def documents
8
+ @client = Universign::Client.instance
9
+
10
+ @documents ||= safeguard do
11
+ result = @client.call('requester.getDocuments', @transaction_id)
12
+ result.map do |document|
13
+ Universign::Document.from_data(document)
14
+ end
15
+ end
16
+ end
17
+
18
+ def self.included(base)
19
+ base.extend(ClassMethods)
20
+ end
21
+
22
+ module ClassMethods
23
+ attr_reader :documents
24
+ end
25
+
26
+ # def signed_with_transaction_id(transaction_id)
27
+ # @client = Universign::Client.new.client
28
+ #
29
+ # safeguard(-> { return false }) do
30
+ # result = @client.call('requester.getTransactionInfo', transaction_id)
31
+ # !result['signerInfos'].any? { |s| s['status'] != 'signed' }
32
+ # end
33
+ # end
34
+ #
35
+ # def signed_with_custom_id(custom_id)
36
+ # @client = Universign::Client.new.client
37
+ #
38
+ # safeguard(-> { return false }) do
39
+ # result = @client.call('requester.getTransactionInfoByCustomId', custom_id)
40
+ # !result['signerInfos'].any? { |s| s['status'] != 'signed' }
41
+ # end
42
+ # end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,86 @@
1
+ module Universign
2
+ module Service
3
+ module Transaction
4
+ AVAILABLE_OPTIONS = {
5
+ custom_id: :customId,
6
+ description: :description,
7
+ handwritten_signature_mode: :handwrittenSignatureMode,
8
+ certificate_type: :certificateType,
9
+ language: :language,
10
+ identification_type: :identificationType,
11
+ handwritten_signature: :handwrittenSignature,
12
+ profile: :profile,
13
+ final_doc_sent: :finalDocSent,
14
+ final_doc_requester_sent: :finalDocRequesterSent
15
+ }
16
+
17
+ DEFAULT_OPTIONS = {
18
+ handwrittenSignatureMode: 1,
19
+ identificationType: 'sms',
20
+ language: 'fr',
21
+ certificateType: 'simple'
22
+ }
23
+
24
+ # Get a transaction from Universign
25
+ #
26
+ # @return [Universign::Transaction]
27
+ def get
28
+ @client = Universign::Client.instance
29
+
30
+ safeguard do
31
+ result = @client.call('requester.getTransactionInfo', @transaction_id)
32
+ self.from_data(result)
33
+ end
34
+ end
35
+
36
+ def self.included(base)
37
+ base.extend(ClassMethods)
38
+ end
39
+
40
+ module ClassMethods
41
+ # Signs a document
42
+ #
43
+ # @param [Array<Universign::Document>] documents Documents to sign
44
+ # @param [Array<Universign::SignerTransaction>] signers
45
+ # @param [Hash] options
46
+ # @option options: [String] :custom_id Custom ID of the document
47
+ # @option options: [String] :description Description of the signature
48
+ # @option options: [String] :handwritten_signature_mode Type of signature
49
+ # @option options: [String] :certificate_type Type of certificate
50
+ # @option options: [String] :language Document's language
51
+ # @option options: [String] :identification_type
52
+ # @option options: [Boolean] :handwritten_signature
53
+ # @option options: [String] :profile
54
+ # @option options: [Boolean] :final_doc_sent
55
+ # @option options: [Boolean] :final_doc_requester_sent
56
+ #
57
+ # @raise [ArgumentError] Raised if unknown_key passed in options
58
+ #
59
+ # @return [Universign::Transaction]
60
+ def create(documents:, signers:, options: {})
61
+ @client = Universign::Client.instance
62
+
63
+ sign_options = DEFAULT_OPTIONS.merge(
64
+ documents: documents.map(&:params),
65
+ signers: signers.map(&:params),
66
+ )
67
+
68
+ options.each do |key, value|
69
+ known_key = AVAILABLE_OPTIONS[key]
70
+
71
+ if known_key
72
+ sign_options[known_key] = value
73
+ else
74
+ raise "Unknown Key"
75
+ end
76
+ end
77
+
78
+ safeguard do
79
+ result = @client.call("requester.requestTransaction", sign_options)
80
+ Universign::Transaction.new(result['id'], result['url'])
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,16 @@
1
+ module Universign
2
+ class SignatureField
3
+ attr_reader :params
4
+
5
+ def initialize(coordinate:, page:)
6
+ @coordinate = coordinate
7
+ @page = page
8
+
9
+ @params = {
10
+ page: @page,
11
+ x: @coordinate[0],
12
+ y: @coordinate[1]
13
+ }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,41 @@
1
+ module Universign
2
+ class Signer
3
+ attr_accessor :params
4
+
5
+ def initialize(options = {})
6
+ @params = {}
7
+
8
+ options.each do |key, value|
9
+ send("#{key}=", value)
10
+ end
11
+ end
12
+
13
+ def self.from_data(data)
14
+ @params = data
15
+ end
16
+
17
+ # This signer’s firstname
18
+ #
19
+ # @return [String]
20
+ def first_name
21
+ @first_name || params['firstName']
22
+ end
23
+
24
+ def first_name=(data)
25
+ @first_name = data
26
+ params[:firstname] = data
27
+ end
28
+
29
+ # This signer’s lastname
30
+ #
31
+ # @return [String]
32
+ def last_name
33
+ @last_name || params['lastName']
34
+ end
35
+
36
+ def last_name=(data)
37
+ @last_name = data
38
+ params[:lastname] = data
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,56 @@
1
+ module Universign
2
+ class SignerInfos < Signer
3
+ # The status of the signer
4
+ #
5
+ # The existing statuses are:
6
+ #
7
+ # | Status | Description |
8
+ # |:--------------------:|:-------------------------------------------------------------------------------------------:|
9
+ # | `waiting` | The signer has not yet been invited to sign. Others signers must sign prior to this user |
10
+ # | `ready` | The signer has been invited to sign, but has not tried yet |
11
+ # | `accessed` | The signer has accessed the signature service |
12
+ # | `code-sent` | The signer agreed to sign and has been sent an OTP |
13
+ # | `signed` | The signer has successfully signed. |
14
+ # | `pending-validation` | The signer has successfully signed and is pending RA validation |
15
+ # | `canceled` | The signer refused to sign, or one of the previous signers canceled or failed its signature |
16
+ # | `failed` | An error occured during the signature. In this case, error is set |
17
+ def status
18
+ data['status']
19
+ end
20
+
21
+ # The error message in case status == `failed`
22
+ #
23
+ # @return [String]
24
+ def error
25
+ data['error']
26
+ end
27
+
28
+ # The URL of the signature page
29
+ #
30
+ # @return [String]
31
+ def url
32
+ data['url']
33
+ end
34
+
35
+ # the action date
36
+ #
37
+ # @return [String]
38
+ def action_date
39
+ data['actionDate']
40
+ end
41
+
42
+ # List of refused docs indexes
43
+ #
44
+ # @return [Array<Integer>]
45
+ def refused_docs
46
+ data['refusedDocs']
47
+ end
48
+
49
+ # The signer’s email
50
+ #
51
+ # @return [String]
52
+ def email
53
+ data['email']
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,101 @@
1
+ module Universign
2
+ class Transaction
3
+ include Universign::Safeguard
4
+ include Service::Transaction
5
+ include Service::Document
6
+
7
+ attr_reader :transaction_id, :url, :data
8
+
9
+ def initialize(transaction_id = nil, url = nil)
10
+ @transaction_id = transaction_id
11
+ @url = url
12
+ @data = {}
13
+
14
+ self.get
15
+ end
16
+
17
+ def from_data(data)
18
+ @data = data
19
+ end
20
+
21
+ # @return [String]
22
+ #
23
+ # The status of the transaction. The existing statuses are:
24
+ #
25
+ # | Status | Description |
26
+ # |-------------|--------------------------------------------------------------------------------------------------|
27
+ # | `ready` | Signers can connect and sign |
28
+ # | `expired` | The transaction has been requested more than 7 days ago. It will no more be available to signers |
29
+ # | `canceled` | A signer has canceled the transaction. Signers will no more be able to connect to the service |
30
+ # | `failed` | An error occured during a signature. The signers won’t be able to connect to the service |
31
+ # | `completed` | All signers have successfuly sign, the requester can retrieve the documents |
32
+ def status
33
+ data['status']
34
+ end
35
+
36
+ # @return [Array<String>]
37
+ def url
38
+ @url ||= data['signerInfos'].map { |si| si['url'] }
39
+ end
40
+
41
+ # A list of bean containing information about the signers
42
+ # and their progression in the signature process
43
+ #
44
+ # @return [Array<Universign::Signer]
45
+ def signers
46
+ raise 'NotImplementedYet'
47
+ end
48
+
49
+ # A bean containing information about the requester of a
50
+ # transaction
51
+ # @return
52
+ def initiator
53
+ data['initiatorInfo']
54
+ end
55
+
56
+ # The index of current signer if the status of transaction
57
+ # is ready or who ended the transactions for other status
58
+ #
59
+ # @return [Integer]
60
+ def current_signer
61
+ data['currentSigner']
62
+ end
63
+
64
+ # The creation date or last relaunch date of this transaction
65
+ #
66
+ # @return [Date]
67
+ def created_at
68
+ data['creationDate'].to_date
69
+ end
70
+
71
+ # The description of the Transaction
72
+ #
73
+ # @return [String]
74
+ def description
75
+ data['description']
76
+ end
77
+
78
+ # Whether the transaction was requested with requesting handwritten signature
79
+ # for each signature field or not.
80
+ #
81
+ # @return [Boolean]
82
+ def each_field
83
+ data['eachField']
84
+ end
85
+
86
+ # Whether the transaction is signed... or not !
87
+ #
88
+ # @return [Boolean]
89
+ def signed?
90
+ status == 'completed'
91
+ end
92
+
93
+ ########################
94
+
95
+ private
96
+
97
+ def client
98
+ @client ||= Universign::Client.new.client
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,107 @@
1
+ module Universign
2
+ class TransactionSigner < Signer
3
+ attr_accessor :phone_number, :signature, :callbacks
4
+
5
+ def initialize(options = {})
6
+ super(options)
7
+
8
+ options.each do |key, value|
9
+ send("#{key}=", value)
10
+ end
11
+ end
12
+
13
+ # This signer’s organization
14
+ #
15
+ # @params [String] data
16
+ def organization=(data)
17
+ @organization = data
18
+ params[:organisation] = data
19
+ end
20
+
21
+ # This signer’s e-mail address
22
+ #
23
+ # @params [String] data
24
+ def email=(data)
25
+ @email = data
26
+ params[:emailAddress] = data
27
+ end
28
+
29
+ # This signer’s mobile phone number that should be
30
+ # written in the international format
31
+ #
32
+ # Mandatory if the authentication by sms is activated
33
+ #
34
+ # @params [String] data
35
+ def phone_number=(data)
36
+ @phone_number = data
37
+ params[:phoneNum] = data
38
+ end
39
+
40
+ # The role of this transaction actor
41
+ #
42
+ # | Role | Description |
43
+ # |:----------:|:----------------------------------------------------------------------------------------:|
44
+ # | `signer` | (default) This actor is a signer and he will be able to view the documents and sign them |
45
+ # | `observer` | This actor is an observer and he will be able only to view the documents |
46
+ #
47
+ # @params [String] data
48
+ def role=(data)
49
+ @role = data
50
+ params[:role] = data
51
+ end
52
+
53
+ # A description of a visible PDF signature field. If none
54
+ # provided, no signature field will be produced on the
55
+ # signed document
56
+ #
57
+ # @params [Universign::SignatureField] data
58
+ def signature_field=(data)
59
+ if !data.instance_of?(Universign::SignatureField)
60
+ raise 'BadSignatureFieldType' # TODO: create custom Exception
61
+ end
62
+
63
+ @signature_field = data
64
+ params[:signatureField] = data.params
65
+ end
66
+
67
+ # This signer’s birth date. This is an option for the certified
68
+ # signature
69
+ #
70
+ # @params [Date] data
71
+ def birtdate=(data)
72
+ @birthdate = data
73
+ params[:birthDate] = data
74
+ end
75
+
76
+ # The url to where the signer will be redirected, after the signatures are completed.
77
+ # If it is null it takes the value of {Universign::Transaction#success_url}
78
+ # If it is also null, it takes the default Universign success URL
79
+ def success_url=(data)
80
+ @success_url = data
81
+ params[:successURL] = data
82
+ end
83
+
84
+ def cancel_url=(data)
85
+ @cancel_url = data
86
+ params[:cancelURL] = data
87
+ end
88
+
89
+ def fail_url=(data)
90
+ @fail_url = data
91
+ params[:failURL] = data
92
+ end
93
+
94
+ # Which authentication type will be used when a signer will attempt to sign.
95
+ #
96
+ # The available values are :
97
+ # | Type | Description |
98
+ # |:-------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
99
+ # | `none` | The signer won’t be asked an authentication code when signing |
100
+ # | `email` | The signer will be sent a authentication code by e-mail. Using this option implies that this signer has a valid email property set, otherwise, an exception is thrown |
101
+ # | `sms` | The signer will be sent a authentication code by sms. Using this option implies that this signer has a valid `phone_number` property set, in other cases, an exception is thrown |
102
+ def identification_type=(data)
103
+ @identification_type = data
104
+ params[:identificationType] = data
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,3 @@
1
+ module Universign
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'universign/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ruby_universign"
8
+ spec.version = Universign::VERSION
9
+ spec.authors = ["Nicolas Besnard", "Yassine Zenati", "Antoine Becquet"]
10
+ spec.email = ["besnard.nicolas@gmail.com", "yassine@capsens.eu", "antoine@capsens.eu"]
11
+
12
+ spec.summary = "Universign's API Wrapper"
13
+ spec.description = <<~EOF
14
+ Ruby gem for interacting with [Universign](https://www.universign.com/) electronic signature API.
15
+ It ease requests to Universign API, documents uploads and following signature state.
16
+ This gem is **not** officialy made by Universign, but was originally created for [CapSens](https://capsens.eu/) internal usage.
17
+ EOF
18
+ spec.homepage = "https://github.com/CapSens/universign"
19
+ spec.license = "MIT"
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.required_ruby_version = '>= 2.0'
27
+
28
+ spec.add_runtime_dependency 'activesupport', '>= 4.1'
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.10"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ spec.add_development_dependency "dotenv", "~> 2.0"
34
+ spec.add_development_dependency "webmock", "~> 3.0"
35
+ spec.add_development_dependency "vcr", "~> 4.0"
36
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_universign
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Nicolas Besnard
8
+ - Yassine Zenati
9
+ - Antoine Becquet
10
+ autorequire:
11
+ bindir: exe
12
+ cert_chain: []
13
+ date: 2018-09-26 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '4.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '4.1'
29
+ - !ruby/object:Gem::Dependency
30
+ name: bundler
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.10'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.10'
43
+ - !ruby/object:Gem::Dependency
44
+ name: rake
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '10.0'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '10.0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: rspec
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '3.0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '3.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: dotenv
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '2.0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '2.0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: webmock
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '3.0'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '3.0'
99
+ - !ruby/object:Gem::Dependency
100
+ name: vcr
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '4.0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '4.0'
113
+ description: |
114
+ Ruby gem for interacting with [Universign](https://www.universign.com/) electronic signature API.
115
+ It ease requests to Universign API, documents uploads and following signature state.
116
+ This gem is **not** officialy made by Universign, but was originally created for [CapSens](https://capsens.eu/) internal usage.
117
+ email:
118
+ - besnard.nicolas@gmail.com
119
+ - yassine@capsens.eu
120
+ - antoine@capsens.eu
121
+ executables: []
122
+ extensions: []
123
+ extra_rdoc_files: []
124
+ files:
125
+ - ".env.example"
126
+ - ".gitignore"
127
+ - ".rspec"
128
+ - ".travis.yml"
129
+ - CHANGELOG.md
130
+ - CODE_OF_CONDUCT.md
131
+ - Gemfile
132
+ - LICENSE.txt
133
+ - README.md
134
+ - Rakefile
135
+ - bin/console
136
+ - bin/setup
137
+ - lib/universign.rb
138
+ - lib/universign/client.rb
139
+ - lib/universign/configuration.rb
140
+ - lib/universign/document.rb
141
+ - lib/universign/error.rb
142
+ - lib/universign/safeguard.rb
143
+ - lib/universign/service/document.rb
144
+ - lib/universign/service/transaction.rb
145
+ - lib/universign/signature_field.rb
146
+ - lib/universign/signer.rb
147
+ - lib/universign/signer_infos.rb
148
+ - lib/universign/transaction.rb
149
+ - lib/universign/transaction_signer.rb
150
+ - lib/universign/version.rb
151
+ - ruby_universign.gemspec
152
+ homepage: https://github.com/CapSens/universign
153
+ licenses:
154
+ - MIT
155
+ metadata: {}
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '2.0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubyforge_project:
172
+ rubygems_version: 2.6.8
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: Universign's API Wrapper
176
+ test_files: []