├── library ├── HelloSign │ ├── Test │ │ ├── logo.jpg │ │ ├── nda.docx │ │ ├── omega-multi.pdf │ │ ├── bulk_send_test_signers.csv │ │ ├── TestUtils.php │ │ ├── WarningTest.php │ │ ├── EventTest.php │ │ ├── AbstractTest.php │ │ ├── OAuthTest.php │ │ ├── AccountTest.php │ │ ├── TemplateSignatureRequestTest.php │ │ ├── TeamTest.php │ │ ├── UnclaimedDraftTest.php │ │ ├── EmbeddedSignatureRequestTest.php │ │ ├── ApiAppTest.php │ │ ├── TemplateTest.php │ │ └── BulkSendTest.php │ ├── Error.php │ ├── SignerList.php │ ├── SignatureList.php │ ├── AttachmentList.php │ ├── ApiAppList.php │ ├── BulkSendJobList.php │ ├── TemplateList.php │ ├── SignatureRequestList.php │ ├── SignerGroup.php │ ├── Signer.php │ ├── Attachment.php │ ├── FileResponse.php │ ├── EmbeddedResponse.php │ ├── EmbeddedBulkSendJob.php │ ├── EmbeddedSignatureRequest.php │ ├── BaseException.php │ ├── Warning.php │ ├── OAuthToken.php │ ├── OAuthTokenRequest.php │ ├── TemplateSignatureRequest.php │ ├── AbstractObject.php │ ├── AbstractResourceList.php │ ├── Signature.php │ ├── AbstractSignatureRequestWrapper.php │ ├── AbstractList.php │ ├── Team.php │ ├── BulkSendJob.php │ ├── Event.php │ ├── ApiApp.php │ ├── UnclaimedDraft.php │ ├── AbstractResource.php │ ├── Account.php │ ├── AbstractSignatureRequest.php │ └── SignatureRequest.php └── lib │ └── REST.php ├── .gitignore ├── .travis.yml ├── composer.json ├── phpunit.xml.dist ├── LICENSE ├── HelloSign.php ├── CONTRIBUTING.md └── README.md /library/HelloSign/Test/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellosign/hellosign-php-sdk/HEAD/library/HelloSign/Test/logo.jpg -------------------------------------------------------------------------------- /library/HelloSign/Test/nda.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellosign/hellosign-php-sdk/HEAD/library/HelloSign/Test/nda.docx -------------------------------------------------------------------------------- /library/HelloSign/Test/omega-multi.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hellosign/hellosign-php-sdk/HEAD/library/HelloSign/Test/omega-multi.pdf -------------------------------------------------------------------------------- /library/HelloSign/Test/bulk_send_test_signers.csv: -------------------------------------------------------------------------------- 1 | name, email_address 2 | One Testerson, test1@example.com 3 | Two Testerson, test2@example.com 4 | Three Testerson, test3@example.com 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | phpunit.xml 3 | composer.phar 4 | .buildpath 5 | .settings 6 | .project 7 | installed.json 8 | phpunit_test_file1.pdf 9 | phpunit_test_file2.pdf 10 | phpunit_test_file3.zip 11 | phpunit_test_template_file.pdf 12 | test.php 13 | .idea 14 | *.swp 15 | vendor 16 | .phpunit.result.cache 17 | -------------------------------------------------------------------------------- /library/HelloSign/Test/TestUtils.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./library/HelloSign/Test/ 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 hellosign.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /library/HelloSign/Error.php: -------------------------------------------------------------------------------- 1 | client->getSignatureRequests(); 34 | $this->assertTrue(is_array($response->getWarnings())); 35 | } 36 | 37 | public function testObjectWarnings() 38 | { 39 | $response = $this->client->getAccount(); 40 | $this->assertTrue(is_array($response->getWarnings())); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /library/HelloSign/ApiAppList.php: -------------------------------------------------------------------------------- 1 | group = $array['name']; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /library/HelloSign/Signer.php: -------------------------------------------------------------------------------- 1 | file_url; 64 | } 65 | 66 | /** 67 | * @return integer 68 | * @ignore 69 | */ 70 | public function getExpiresAt() 71 | { 72 | return $this->expires_at; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /library/HelloSign/EmbeddedResponse.php: -------------------------------------------------------------------------------- 1 | sign_url; 64 | } 65 | 66 | /** 67 | * @return integer 68 | * @ignore 69 | */ 70 | public function getExpiresAt() 71 | { 72 | return $this->expires_at; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /library/HelloSign/Test/EventTest.php: -------------------------------------------------------------------------------- 1 | array( 37 | "event_time" => "1348177752", 38 | "event_type" => "signature_request_sent", 39 | "event_hash" => "7d0dace3a863bce194333c2a4e90c65f89af08e7c2068230c1c17b9c2b292dc6", 40 | "event_metadata" => array( 41 | "related_signature_id" => "ad4d8a769b555fa5ef38691465d426682bf2c992", 42 | "reported_for_account_id" => "63522885f9261e2b04eea043933ee7313eb674fd", 43 | "reported_for_app_id" => null 44 | ) 45 | ), 46 | "signature_request" => array( 47 | "signature_request_id" => "abcde12345" 48 | ) 49 | ))); 50 | 51 | $event = new Event($data); 52 | 53 | 54 | $this->assertTrue($event->isValid('4e94adf59d17c417ce0d1d2cb34b953ba7a1eebe1ecbe31bb1c586af92af1e1d')); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /library/HelloSign/Test/AbstractTest.php: -------------------------------------------------------------------------------- 1 | client = new Client($_ENV['API_KEY'], null, $api_url, $oauth_token_url); 56 | $this->team_member_1 = "sdk-test-php+test1@hellosign.com"; 57 | $this->team_member_2 = "sdk-test-php+test2@hellosign.com"; 58 | // $this->client->enableDebugMode(); 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We're excited you're considering contributing to our project! Your patches are essential to keeping our SDKs bug free and up-to-date. We want to keep it as easy as possible to contribute. There are only a few guidelines you must follow that will allow us to keep up with pull requests and maintain a high quality code base. 4 | 5 | ## Getting Started 6 | 7 | * Make sure you have a [GitHub account](https://github.com/signup/free) 8 | * Make sure you have a [HelloSign account](http://www.hellosign.com) 9 | * Submit a new [issue ticket](https://github.com/hellosign/hellosign-php-sdk/issues), assuming one does not already exist. 10 | * Clearly describe the issue including steps to reproduce when it is a bug. 11 | * Make sure to include the version you are using which has the issue (and confirm that the issue you are having has not been fixed in a later version) 12 | * Fork the repository on GitHub 13 | 14 | ## Making Changes 15 | 16 | * Make commits of logical units. 17 | * Check for unnecessary whitespace with `git diff --check` before committing. 18 | * Make sure your commit messages are in [the proper format](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). 19 | * Make sure you have added any necessary tests for your changes. 20 | * Run all unit tests, if available, to ensure nothing else was accidentally broken. 21 | 22 | ### Making trivial changes 23 | 24 | For changes of a trivial nature to comments and documentation, it is not 25 | always necessary to create a new issue ticket. 26 | 27 | ## Submitting Changes 28 | 29 | * Push your changes to your fork of the repository. 30 | * Submit a pull request to the repository in the HelloFax organization. 31 | * Update your issue ticket to mark that you have submitted code and are ready for it to be reviewed (Status: Ready for Merge). 32 | * Include a link to the pull request in the ticket. 33 | * The HelloSign dev team looks at Pull Requests on a regular basis. 34 | * After feedback has been given we expect responses within two weeks. After two 35 | weeks we may close the pull request if it isn't showing any activity. 36 | 37 | # Additional Resources 38 | 39 | * [General GitHub documentation](http://help.github.com/) 40 | * [GitHub pull request documentation](http://help.github.com/send-pull-requests/) 41 | * [HelloSign API documentation](https://app.hellosign.com/api/documentation) 42 | * Email apisupport@hellosign.com if you have questions 43 | -------------------------------------------------------------------------------- /library/HelloSign/EmbeddedBulkSendJob.php: -------------------------------------------------------------------------------- 1 | request must be left of the union 51 | * operator so that its values (e.g. test_mode) take precedence over 52 | * our defaults. 53 | */ 54 | return $this->request->toParams(array( 55 | 'except' => array( 56 | ) 57 | )) + $this->toArray(array( 58 | 'except' => array( 59 | 'request', 60 | 'skip_me_now' 61 | ) 62 | )); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /library/HelloSign/EmbeddedSignatureRequest.php: -------------------------------------------------------------------------------- 1 | request must be left of the union 51 | * operator so that its values (e.g. test_mode) take precedence over 52 | * our defaults. 53 | */ 54 | return $this->request->toParams(array( 55 | 'except' => array( 56 | ) 57 | )) + $this->toArray(array( 58 | 'except' => array( 59 | 'request', 60 | 'skip_me_now' 61 | ) 62 | )); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /library/HelloSign/BaseException.php: -------------------------------------------------------------------------------- 1 | name = $name; 58 | 59 | // make sure everything is assigned properly 60 | parent::__construct($message, $code, $previous); 61 | } 62 | 63 | /** 64 | * @return string 65 | * @ignore 66 | */ 67 | public function getName() 68 | { 69 | return $this->name; 70 | } 71 | 72 | /** 73 | * Custom string representation of object 74 | * 75 | * @return string 76 | * @ignore 77 | */ 78 | public function __toString() { 79 | return get_class($this) . ": [{$this->name}] {$this->message}\n"; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /library/HelloSign/Test/OAuthTest.php: -------------------------------------------------------------------------------- 1 | enableDebugMode(); 39 | 40 | return $oauth_client; 41 | } 42 | 43 | /** 44 | * @group create 45 | */ 46 | public function testCreateAccount() 47 | { 48 | //fails if account has already been created 49 | $random_email = rand(1, 10000000) . "@example.com"; 50 | $response = $this->client->createAccount( 51 | new Account($random_email), 52 | $_ENV['CLIENT_ID'], 53 | $_ENV['CLIENT_SECRET'] 54 | ); 55 | 56 | $this->assertInstanceOf('HelloSign\Account', $response); 57 | $this->assertInstanceOf('HelloSign\OAuthToken', $response->getOAuthData()); 58 | 59 | return $response->getOAuthData(); 60 | } 61 | 62 | /** 63 | * @depends testCreateAccount 64 | * @group oauth 65 | */ 66 | public function testRefreshToken($token) 67 | { 68 | $oauth_client = $this->getOAuthClient($token); 69 | $response = $oauth_client->refreshOAuthToken($token); 70 | $this->assertInstanceOf('HelloSign\OAuthToken', $response); 71 | 72 | return $response; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /library/HelloSign/Warning.php: -------------------------------------------------------------------------------- 1 | setMessage($warning_data->warning_msg); 60 | $this->setName($warning_data->warning_name); 61 | } 62 | } 63 | 64 | /** 65 | * set warning message 66 | * 67 | * @param String $message 68 | * @return static 69 | */ 70 | public function setMessage($message) 71 | { 72 | isset($message) && $this->message = $message; 73 | return $this; 74 | } 75 | 76 | /** 77 | * set warning name 78 | * 79 | * @param String $name 80 | * @return static 81 | */ 82 | public function setName($name) 83 | { 84 | isset($name) && $this->name = $name; 85 | return $this; 86 | } 87 | 88 | /** 89 | * get warning message 90 | * 91 | * @return String 92 | */ 93 | public function getMessage() 94 | { 95 | return $this->message; 96 | } 97 | 98 | /** 99 | * get warning name 100 | * 101 | * @return String 102 | */ 103 | public function getName() 104 | { 105 | return $this->name; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /library/HelloSign/Test/AccountTest.php: -------------------------------------------------------------------------------- 1 | expectException(Error::class); 40 | $this->expectExceptionMessage("Account already exists"); 41 | $random_email = rand(1, 10000000) . "@example.com"; 42 | $response = $this->client->createAccount( 43 | new Account($random_email) 44 | ); 45 | 46 | $this->assertInstanceOf('HelloSign\Account', $response); 47 | $this->assertNotNull($response->account_id); 48 | 49 | //trying to create it again should fail 50 | $response = $this->client->createAccount( 51 | new Account($random_email) 52 | ); 53 | 54 | return $response; 55 | } 56 | 57 | /** 58 | * @group update 59 | */ 60 | public function testUpdateAccount() 61 | { 62 | $callback_url = $_ENV['CALLBACK_URL']; 63 | $account = new Account; 64 | $account->setCallbackUrl($callback_url); 65 | $response = $this->client->updateAccount($account); 66 | 67 | $this->assertInstanceOf('HelloSign\Account', $response); 68 | $this->assertNotNull($response->getId()); 69 | $this->assertEquals($response->getCallbackUrl(), $callback_url); 70 | $this->assertEquals($response, $account); 71 | 72 | return $response; 73 | } 74 | 75 | /** 76 | * @group read 77 | */ 78 | public function testGetAcount() 79 | { 80 | $response = $this->client->getAccount(); 81 | 82 | $this->assertInstanceOf('HelloSign\Account', $response); 83 | $this->assertNotNull($response->account_id); 84 | 85 | return $response; 86 | } 87 | 88 | /** 89 | * @depends testGetAcount 90 | * @group read 91 | */ 92 | public function testIsAccountValid($account) 93 | { 94 | $this->assertTrue($this->client->isAccountValid($account->getEmail())); 95 | 96 | $this->assertFalse($this->client->isAccountValid('abc123@xyz456.com')); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /library/HelloSign/OAuthToken.php: -------------------------------------------------------------------------------- 1 | access_token; 82 | } 83 | 84 | /** 85 | * @return string 86 | * @ignore 87 | */ 88 | public function getTokenType() 89 | { 90 | return $this->token_type; 91 | } 92 | 93 | /** 94 | * @return integer 95 | * @ignore 96 | */ 97 | public function getExpiresIn() 98 | { 99 | return $this->expires_in; 100 | } 101 | 102 | /** 103 | * @return array 104 | * @ignore 105 | */ 106 | public function toParams() 107 | { 108 | return $this->toArray(array( 109 | 'except' => array( 110 | 'state' 111 | ) 112 | )) + array( 113 | 'grant_type' => 'refresh_token' 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /library/HelloSign/OAuthTokenRequest.php: -------------------------------------------------------------------------------- 1 | state = $state; 76 | return $this; 77 | } 78 | 79 | /** 80 | * @param string $code 81 | * @return OAuthTokenRequest 82 | * @ignore 83 | */ 84 | public function setCode($code) 85 | { 86 | $this->code = $code; 87 | return $this; 88 | } 89 | 90 | /** 91 | * @param string $client_id 92 | * @return OAuthTokenRequest 93 | * @ignore 94 | */ 95 | public function setClientID($client_id) 96 | { 97 | $this->client_id = $client_id; 98 | return $this; 99 | } 100 | 101 | /** 102 | * @param string $client_secret 103 | * @return OAuthTokenRequest 104 | * @ignore 105 | */ 106 | public function setClientSecret($client_secret) 107 | { 108 | $this->client_secret = $client_secret; 109 | return $this; 110 | } 111 | 112 | /** 113 | * @return array 114 | * @ignore 115 | */ 116 | public function toParams() 117 | { 118 | return $this->toArray() + array( 119 | 'grant_type' => 'authorization_code' 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /library/HelloSign/TemplateSignatureRequest.php: -------------------------------------------------------------------------------- 1 | template_ids[] = $id; 69 | } else { 70 | $this->template_ids[$order] = $id; 71 | } 72 | return $this; 73 | } 74 | 75 | /** 76 | * Set the signer to the list of signers for this request 77 | * 78 | * @param string $role 79 | * @param mixed $email_or_signer 80 | * @param string $name 81 | * @return TemplateSignatureRequest 82 | * @see AbstractSignatureRequest::addSigner() 83 | */ 84 | public function setSigner($role, $email_or_signer, $name = null) 85 | { 86 | return parent::addSigner($email_or_signer, $name, $role); 87 | } 88 | 89 | /** 90 | * Sets the CC email address for the provided role 91 | * 92 | * @param string $role 93 | * @param string $email 94 | * @return TemplateSignatureRequest 95 | */ 96 | public function setCC($role, $email) 97 | { 98 | $obj = new stdClass; 99 | $obj->email_address = $email; 100 | 101 | $this->ccs[$role] = $obj; 102 | 103 | return $this; 104 | } 105 | 106 | /** 107 | * @return array 108 | * @ignore 109 | */ 110 | public function toParams() 111 | { 112 | $except = array( 113 | 'use_text_tags', 114 | 'hide_text_tags', 115 | 'use_preexisting_fields' 116 | ); 117 | return $this->toArray(array( 118 | 'except' => $except 119 | )); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /library/HelloSign/Test/TemplateSignatureRequestTest.php: -------------------------------------------------------------------------------- 1 | client->getTemplates(); 44 | $template = $templates[0]; 45 | 46 | $request = new TemplateSignatureRequest; 47 | $request->setTemplateId($template->getId()); 48 | $request->setSubject('Purchase Order'); 49 | $request->setMessage('Glad we could come to an agreement.'); 50 | 51 | foreach ($template->getSignerRoles() as $i => $role) { 52 | $request->setSigner($role->name, "george$i@example.com", "George {$role->name}"); 53 | } 54 | foreach ($template->getCCRoles() as $i => $role) { 55 | $request->setCC($role->name, "oscar$i@example.com"); 56 | } 57 | foreach ($template->getCustomFields() as $i => $field) { 58 | $request->setCustomFieldValue($field->name, 'My String'); 59 | } 60 | 61 | $response = $this->client->sendTemplateSignatureRequest($request); 62 | 63 | $this->assertInstanceOf('HelloSign\SignatureRequest', $response); 64 | $this->assertNotNull($response->getId()); 65 | 66 | return $response->getId(); 67 | } 68 | 69 | /** 70 | * @group create 71 | */ 72 | public function testSendTemplateSignatureRequestWithSignerGroup() 73 | { 74 | // Retrieve Template Information 75 | $templates = $this->client->getTemplates(); 76 | $template = $templates[0]; 77 | $signer_role = $template->getSignerRoles()[0]->name; 78 | 79 | // Enable Test Mode 80 | $request = new TemplateSignatureRequest; 81 | 82 | // Set Request Params 83 | $request->setTemplateId($template->getId()); 84 | $request->setSubject('Purchase Order'); 85 | $request->setMessage('Glad we could come to an agreement.'); 86 | 87 | // Add Signer Group to Signature Request 88 | $request->addGroup("Client Group", $signer_role); 89 | $request->addGroupSigner("Jack Example", "jack@example.com", 0, $signer_role); 90 | $request->addGroupSigner("Jill Example", "jill@example.com", 1, $signer_role); 91 | $request->addGroupSigner("Jane Example", "jane@example.com", 2, $signer_role); 92 | 93 | $response = $this->client->sendTemplateSignatureRequest($request); 94 | 95 | $this->assertInstanceOf('HelloSign\SignatureRequest', $response); 96 | $this->assertNotNull($response->getId()); 97 | 98 | return $response->getId(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /library/HelloSign/Test/TeamTest.php: -------------------------------------------------------------------------------- 1 | client->removeAllTeamMembers(); 41 | $accounts = $response->getAccounts(); 42 | 43 | $this->assertInstanceOf('HelloSign\Team', $response); 44 | $this->assertNotNull($response->getName()); 45 | // A team can have more than 1 admin 46 | $this->assertGreaterThan(0, count($accounts)); 47 | } catch (Error $e) { 48 | if ($e->getMessage() != 'Team does not exist') { 49 | throw $e; 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * @depends testRemoveAllTeamMembers 56 | * @group destroy 57 | */ 58 | public function testDestroyTeam() 59 | { 60 | try { 61 | $response = $this->client->destroyTeam(); 62 | $this->assertTrue($response); 63 | } catch (Error $e) { 64 | if ($e->getMessage() != 'No team to delete') { 65 | throw $e; 66 | } 67 | } 68 | } 69 | 70 | /** 71 | * @depends testDestroyTeam 72 | * @group create 73 | */ 74 | public function testCreateTeam() 75 | { 76 | $name = 'SSS'.time(); 77 | $response = $this->client->createTeam(new Team($name)); 78 | 79 | $this->assertInstanceOf('HelloSign\Team', $response); 80 | $this->assertEquals($response->getName(), $name); 81 | } 82 | 83 | /** 84 | * @depends testCreateTeam 85 | * @group update 86 | */ 87 | public function testUpdateTeamName() 88 | { 89 | $name = 'SSS'.time(); 90 | $response = $this->client->updateTeamName($name); 91 | 92 | $this->assertInstanceOf('HelloSign\Team', $response); 93 | $this->assertEquals($response->getName(), $name); 94 | 95 | return $response; 96 | } 97 | 98 | /** 99 | * @depends testUpdateTeamName 100 | * @group update 101 | */ 102 | public function testInviteTeamMember($account) 103 | { 104 | $accounts_count = count($account->getAccounts()); 105 | $response = $this->client->inviteTeamMember($this->team_member_1); 106 | $team = $this->client->getTeam(); 107 | 108 | $this->assertInstanceOf('HelloSign\Team', $response); 109 | $this->assertNotNull($response->getName()); 110 | $this->assertEquals( 111 | count($response->getAccounts()) + count($team->getInvitedAccounts()), 112 | $accounts_count + 1 113 | ); 114 | } 115 | 116 | /** 117 | * @depends testCreateTeam 118 | * @group read 119 | */ 120 | public function testGetTeam() 121 | { 122 | $response = $this->client->getTeam(); 123 | 124 | $this->assertInstanceOf('HelloSign\Team', $response); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /library/HelloSign/AbstractObject.php: -------------------------------------------------------------------------------- 1 | fromArray($array, $options); 48 | } 49 | 50 | /** 51 | * Export to array 52 | * 53 | * @param array $options Accepted keys: except, only, include_null 54 | * @return array 55 | */ 56 | public function toArray($options = array()) 57 | { 58 | $array = get_object_vars($this); 59 | 60 | // default value 61 | if (!isset($options['include_null'])) { 62 | $options['include_null'] = false; 63 | } 64 | 65 | if (!isset($options['except'])) { 66 | $options['except'] = array(); 67 | } 68 | 69 | $options['except'][] = 'resource_type'; 70 | 71 | if (isset($options['only'])) { 72 | foreach ($array as $key => $value) { 73 | if (!in_array($key, $options['only'])) { 74 | unset($array[$key]); 75 | } 76 | } 77 | } else { 78 | foreach ($options['except'] as $value) { 79 | unset($array[$value]); 80 | } 81 | } 82 | 83 | // nested call & remove null 84 | foreach ($array as $key => $value) { 85 | if (!$options['include_null'] && $value === null) { 86 | unset($array[$key]); 87 | } elseif ($value instanceof AbstractObject || $value instanceof AbstractList) { 88 | $array[$key] = $value->toArray(); 89 | } 90 | } 91 | return $array; 92 | } 93 | 94 | /** 95 | * Populate from object 96 | * 97 | * @param stdClass $object 98 | * @param array $options 99 | * @return static 100 | * @see static::fromArray() 101 | */ 102 | public function fromObject($object, $options = array()) 103 | { 104 | return $this->fromArray((array) $object, $options); 105 | } 106 | 107 | /** 108 | * Populate from array 109 | * 110 | * @param array $array 111 | * @param array $options Accepted keys: except 112 | * @return static 113 | */ 114 | public function fromArray($array, $options = array()) 115 | { 116 | // default options 117 | if (!isset($options['except'])) { 118 | $options['except'] = array(); 119 | } 120 | 121 | foreach ($options['except'] as $value) { 122 | unset($array[$value]); 123 | } 124 | 125 | foreach ($array as $key => $value) { 126 | $this->{$key} = $value; 127 | } 128 | 129 | return $this; 130 | } 131 | 132 | /** 133 | * @ignore 134 | */ 135 | public function __get($property) 136 | { 137 | if (property_exists($this, $property)) { 138 | return $this->$property; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /library/HelloSign/AbstractResourceList.php: -------------------------------------------------------------------------------- 1 | fromResponse($response); 89 | $this->warningsFromResponse($response); 90 | } 91 | } 92 | 93 | /** 94 | * @return integer 95 | * @ignore 96 | */ 97 | public function getPage() 98 | { 99 | return $this->page; 100 | } 101 | 102 | /** 103 | * @return integer 104 | * @ignore 105 | */ 106 | public function getNumPages() 107 | { 108 | return $this->num_pages; 109 | } 110 | 111 | /** 112 | * @return integer 113 | * @ignore 114 | */ 115 | public function getNumResults() 116 | { 117 | return $this->num_results; 118 | } 119 | 120 | /** 121 | * @return integer 122 | * @ignore 123 | */ 124 | public function getPageSize() 125 | { 126 | return $this->page_size; 127 | } 128 | 129 | /** 130 | * @return array 131 | * @ignore 132 | */ 133 | public function getWarnings() 134 | { 135 | return $this->warnings; 136 | } 137 | 138 | /** 139 | * Populate from response 140 | * 141 | * @param stdClass $response 142 | * @return static 143 | * @see static::setCollection() 144 | */ 145 | public function fromResponse($response) 146 | { 147 | $this->page = $response->list_info->page; 148 | $this->num_pages = $response->list_info->num_pages; 149 | $this->num_results = $response->list_info->num_results; 150 | $this->page_size = $response->list_info->page_size; 151 | 152 | property_exists($response, $this->list_type) && $this->setCollection($response->{$this->list_type}); 153 | 154 | return $this; 155 | } 156 | 157 | public function warningsFromResponse($response) 158 | { 159 | if (property_exists($response, 'warnings') && is_array($response->warnings)) { 160 | foreach ($response->warnings as $warning) { 161 | array_push($this->warnings, new Warning($warning)); 162 | } 163 | $this->warnings = $response->warnings; 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /library/HelloSign/Signature.php: -------------------------------------------------------------------------------- 1 | signature_id; 110 | } 111 | 112 | /** 113 | * @return string 114 | * @ignore 115 | */ 116 | public function getSignerEmail() 117 | { 118 | return $this->signer_email_address; 119 | } 120 | 121 | /** 122 | * @return string 123 | * @ignore 124 | */ 125 | public function getSignerName() 126 | { 127 | return $this->signer_name; 128 | } 129 | 130 | /** 131 | * @return integer 132 | * @ignore 133 | */ 134 | public function getOrder() 135 | { 136 | return $this->order; 137 | } 138 | 139 | /** 140 | * @return string 141 | * @ignore 142 | */ 143 | public function getStatusCode() 144 | { 145 | return $this->status_code; 146 | } 147 | 148 | /** 149 | * @return integer 150 | * @ignore 151 | */ 152 | public function getSignedAt() 153 | { 154 | return $this->signed_at; 155 | } 156 | 157 | /** 158 | * @return integer 159 | * @ignore 160 | */ 161 | public function getLastViewedAt() 162 | { 163 | return $this->last_viewed_at; 164 | } 165 | 166 | /** 167 | * @return integer 168 | * @ignore 169 | */ 170 | public function getLastRemindedAt() 171 | { 172 | return $this->last_reminded_at; 173 | } 174 | 175 | /** 176 | * @return boolean 177 | * @ignore 178 | */ 179 | public function hasPin() 180 | { 181 | return $this->has_pin; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /library/HelloSign/AbstractSignatureRequestWrapper.php: -------------------------------------------------------------------------------- 1 | request = $request; 68 | $this->setClientId($client_id); 69 | } 70 | 71 | /** 72 | * @param string $id 73 | * @return static 74 | * @ignore 75 | */ 76 | public function setClientId($id) 77 | { 78 | $this->client_id = $id; 79 | return $this; 80 | } 81 | 82 | /** 83 | * @return string 84 | * @ignore 85 | */ 86 | public function getClientId() 87 | { 88 | return $this->client_id; 89 | } 90 | 91 | /** 92 | * Include only if enabling embedded signing 93 | * when using EmbeddedSignatureRequest 94 | * with createUnclaimedDraftEmbeddedWithTemplate 95 | * @return static 96 | * @ignore 97 | */ 98 | public function setEmbeddedSigning() 99 | { 100 | $this->is_for_embedded_signing = true; 101 | return $this; 102 | } 103 | 104 | /** 105 | * @param AbstractSignatureRequest $request 106 | * @return static 107 | * @ignore 108 | */ 109 | public function setRequest(AbstractSignatureRequest $request) 110 | { 111 | $this->request = $request; 112 | return $this; 113 | } 114 | 115 | /** 116 | * @return AbstractSignatureRequest 117 | * @ignore 118 | */ 119 | public function getRequest() 120 | { 121 | return $this->request; 122 | } 123 | 124 | /** 125 | * @return boolean 126 | * @ignore 127 | */ 128 | public function isUsingTemplate() 129 | { 130 | return $this->request instanceof TemplateSignatureRequest; 131 | } 132 | 133 | /** 134 | * @param boolean $skip_me_now Set to true to disable the "Me (Now)" option 135 | * for the preparer. 136 | * @return static 137 | * @ignore 138 | */ 139 | public function enableSkipMeNow() 140 | { 141 | $this->skip_me_now = true; 142 | return $this; 143 | } 144 | 145 | /** 146 | * @return array 147 | * @ignore 148 | */ 149 | public function toParams() 150 | { 151 | return $this->toArray(array( 152 | 'except' => array( 153 | 'request' 154 | ) 155 | )) + $this->request->toParams(array( 156 | 'except' => array( 157 | 'title' 158 | ) 159 | )); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /library/HelloSign/Test/UnclaimedDraftTest.php: -------------------------------------------------------------------------------- 1 | client->getAccount(); 42 | 43 | $request = new SignatureRequest; 44 | $request->setRequesterEmail($account->getEmail()); 45 | $request->setTitle('NDA with Acme Co.'); 46 | $request->setSubject('The NDA we talked about'); 47 | $request->setMessage('Please sign this NDA and let\'s discuss.'); 48 | $request->addSigner('bale@example.com', 'Bale'); 49 | $request->addSigner('beck@example.com', 'Beck'); 50 | $request->addCC('ancelotti@example.com'); 51 | $request->addFile(__DIR__ . '/nda.docx'); 52 | $request->addAttachment('Passport', 0, 'Attach your passport', false); 53 | $client_id = $_ENV['CLIENT_ID']; 54 | $draft = new UnclaimedDraft($request, $client_id); 55 | $draft->setUsePreexistingFields(true); 56 | 57 | $response = $this->client->createUnclaimedDraft($draft); 58 | $sign_url = $response->getClaimUrl(); 59 | 60 | 61 | $this->assertInstanceOf('HelloSign\UnclaimedDraft', $response); 62 | $this->assertNotNull($response); 63 | $this->assertEquals($draft, $response); 64 | 65 | $this->assertNotEmpty($sign_url); 66 | } 67 | 68 | /** 69 | * The difference is that you don't set a client id here 70 | * @group create 71 | */ 72 | public function testCreateUnclaimedDraft() 73 | { 74 | $request = new SignatureRequest; 75 | $request->addFile(__DIR__ . '/nda.docx'); 76 | 77 | $draft = new UnclaimedDraft($request); 78 | 79 | $response = $this->client->createUnclaimedDraft($draft); 80 | $sign_url = $response->getClaimUrl(); 81 | 82 | 83 | $this->assertInstanceOf('HelloSign\UnclaimedDraft', $response); 84 | $this->assertNotNull($response); 85 | $this->assertEquals($draft, $response); 86 | 87 | $this->assertNotEmpty($sign_url); 88 | } 89 | 90 | /** 91 | * @group embedded 92 | */ 93 | public function testCreateUnclaimedDraftEmbeddedWithTemplate() 94 | { 95 | $client_id = $_ENV['CLIENT_ID']; 96 | 97 | $templates = $this->client->getTemplates(); 98 | $template = $templates[0]; 99 | $templateId = $template->getId(); 100 | 101 | $baseReq = new TemplateSignatureRequest(); 102 | $baseReq->setTemplateId($templateId); 103 | foreach ($template->getSignerRoles() as $i => $role) { 104 | $baseReq->setSigner($role->name, "signer".$i."@example.com", "signer $i"); 105 | } 106 | $baseReq->setSigningRedirectUrl('http://hogwarts.edu/success'); 107 | $baseReq->setRequestingRedirectUrl('http://hogwarts.edu'); 108 | $baseReq->setRequesterEmailAddress('herman@hogwarts.com'); 109 | $baseReq->addMetadata('House', 'Griffyndor'); 110 | 111 | $request = new EmbeddedSignatureRequest($baseReq); 112 | $request->setClientId($client_id); 113 | $request->setEmbeddedSigning(); 114 | 115 | $response = $this->client->createUnclaimedDraftEmbeddedWithTemplate($request); 116 | 117 | $this->assertTrue($response instanceof \HelloSign\UnclaimedDraft); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /library/HelloSign/AbstractList.php: -------------------------------------------------------------------------------- 1 | setCollection($array); 67 | } 68 | 69 | /** 70 | * @ignore 71 | */ 72 | public function offsetExists($offset): bool 73 | { 74 | return isset($this->collection[$offset]); 75 | } 76 | 77 | /** 78 | * @ignore 79 | */ 80 | public function offsetGet($offset): mixed 81 | { 82 | return $this->collection[$offset]; 83 | } 84 | 85 | /** 86 | * @ignore 87 | */ 88 | public function offsetSet($offset, $value): void 89 | { 90 | if (is_null($offset)) { 91 | $this->collection[] = $value; 92 | } else { 93 | $this->collection[$offset] = $value; 94 | } 95 | } 96 | 97 | /** 98 | * @ignore 99 | */ 100 | public function offsetUnset($offset): void 101 | { 102 | unset($this->collection[$offset]); 103 | } 104 | 105 | /** 106 | * @ignore 107 | */ 108 | public function rewind(): void 109 | { 110 | $this->position = 0; 111 | } 112 | 113 | /** 114 | * @ignore 115 | */ 116 | public function current(): mixed 117 | { 118 | return $this->collection[$this->position]; 119 | } 120 | 121 | /** 122 | * @ignore 123 | */ 124 | public function key(): int 125 | { 126 | return $this->position; 127 | } 128 | 129 | /** 130 | * @ignore 131 | */ 132 | public function next(): void 133 | { 134 | ++$this->position; 135 | } 136 | 137 | /** 138 | * @ignore 139 | */ 140 | public function valid(): bool 141 | { 142 | return isset($this->collection[$this->position]); 143 | } 144 | 145 | /** 146 | * @ignore 147 | */ 148 | public function count(): int 149 | { 150 | return count($this->collection); 151 | } 152 | 153 | /** 154 | * Populate collection from array 155 | * 156 | * @param array $array 157 | * @return static 158 | */ 159 | public function setCollection($array): static 160 | { 161 | foreach ($array as $key => $object) { 162 | $class_name = "HelloSign\\{$this->resource_class}"; 163 | $resource = new $class_name; 164 | $resource->fromObject($object); 165 | 166 | $this->collection[$key] = $resource; 167 | } 168 | 169 | return $this; 170 | } 171 | 172 | /** 173 | * Export to array 174 | * 175 | * @param array $options Accepted keys: except, only, include_null 176 | * @return array 177 | * @see AbstractObject::toArray() 178 | */ 179 | public function toArray($options = array()) 180 | { 181 | $array = array(); 182 | foreach ($this->collection as $key => $obj) { 183 | $array[$key] = $obj->toArray($options); 184 | } 185 | 186 | return $array; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /library/HelloSign/Team.php: -------------------------------------------------------------------------------- 1 | name = $name_or_obj; 73 | } else { 74 | parent::__construct($name_or_obj); 75 | } 76 | } 77 | 78 | /** 79 | * @return string 80 | * @ignore 81 | */ 82 | public function getName() 83 | { 84 | return $this->name; 85 | } 86 | 87 | /** 88 | * @param string $name Name of the new Team 89 | * @return Team 90 | * @ignore 91 | */ 92 | public function setName($name) 93 | { 94 | $this->name = $name; 95 | return $this; 96 | } 97 | 98 | /** 99 | * @return array 100 | * @ignore 101 | */ 102 | public function getAccounts() 103 | { 104 | return $this->accounts; 105 | } 106 | 107 | /** 108 | * @return array 109 | * @ignore 110 | */ 111 | public function getInvitedAccounts() 112 | { 113 | return $this->invited_accounts; 114 | } 115 | 116 | /** 117 | * @return array 118 | * @ignore 119 | */ 120 | public function toCreateParams() 121 | { 122 | return $this->toArray(array( 123 | 'only' => array( 124 | 'name' 125 | ) 126 | )); 127 | } 128 | 129 | /** 130 | * @param stdClass $array 131 | * @param array $options 132 | * @return Team 133 | * @ignore 134 | */ 135 | public function fromArray($array, $options = array()) 136 | { 137 | if (array_key_exists('accounts', $array)) { 138 | $this->setAccounts($array['accounts']); 139 | } 140 | 141 | if (array_key_exists('invited_accounts', $array)) { 142 | $this->setInvitedAccounts($array['invited_accounts']); 143 | } 144 | 145 | if (!isset($options['except'])) { 146 | $options['except'] = array(); 147 | } 148 | 149 | $options['except'] = array_merge($options['except'], array( 150 | 'accounts', 151 | 'invited_accounts', 152 | )); 153 | 154 | return parent::fromArray($array, $options); 155 | } 156 | 157 | /** 158 | * @param array $accounts 159 | * @return SignatureRequest 160 | * @ignore 161 | */ 162 | protected function setAccounts($accounts) 163 | { 164 | // reset accounts array 165 | $this->accounts = array(); 166 | 167 | foreach ($accounts as $account) { 168 | $resource = new Account; 169 | $resource->fromObject($account); 170 | 171 | $this->accounts[] = $resource; 172 | } 173 | 174 | return $this; 175 | } 176 | 177 | /** 178 | * @param array $accounts 179 | * @return SignatureRequest 180 | * @ignore 181 | */ 182 | protected function setInvitedAccounts($accounts) 183 | { 184 | // reset accounts array 185 | $this->invited_accounts = array(); 186 | 187 | foreach ($accounts as $account) { 188 | $resource = new Account; 189 | $resource->fromObject($account); 190 | 191 | $this->invited_accounts[] = $resource; 192 | } 193 | 194 | return $this; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /library/HelloSign/Test/EmbeddedSignatureRequestTest.php: -------------------------------------------------------------------------------- 1 | setTitle('Embedded NDA'); 45 | $request->addSigner('jack@example.com', 'Jack'); 46 | $request->addFile(__DIR__ . '/nda.docx'); 47 | 48 | // Turn it into an embedded request 49 | $client_id = $_ENV['CLIENT_ID']; 50 | $embedded_request = new EmbeddedSignatureRequest($request, $client_id); 51 | 52 | // Send it to HelloSign 53 | $response = $this->client->createEmbeddedSignatureRequest($embedded_request); 54 | 55 | 56 | $this->assertInstanceOf('HelloSign\SignatureRequest', $response); 57 | $this->assertNotNull($response->getId()); 58 | 59 | 60 | $signatures = $response->getSignatures(); 61 | return $signatures[0]->getId(); 62 | } 63 | 64 | /** 65 | * @group create 66 | */ 67 | public function testCreateEmbeddedRequesting() 68 | { 69 | // Create the signature request 70 | $request = new SignatureRequest; 71 | $request->setRequesterEmail('jolene_request1@example.com'); 72 | $request->addFile(__DIR__ . '/nda.docx'); 73 | 74 | // Turn it into an embedded request 75 | $client_id = $_ENV['CLIENT_ID']; 76 | $draft_request = new UnclaimedDraft($request, $client_id); 77 | 78 | // Send it to HelloSign 79 | $response = $this->client->createUnclaimedDraft($draft_request); 80 | 81 | 82 | $this->assertInstanceOf('HelloSign\UnclaimedDraft', $response); 83 | $this->assertNotNull($response->getClaimUrl()); 84 | } 85 | 86 | /** 87 | * @group create 88 | */ 89 | public function testCreateEmbeddedRequestingWithEmbeddedSigning() 90 | { 91 | // Create the signature request 92 | $request = new SignatureRequest; 93 | $request->setRequesterEmail('jolene_request2@example.com'); 94 | $request->addFile(__DIR__ . '/nda.docx'); 95 | 96 | // Turn it into an embedded request 97 | $client_id = $_ENV['CLIENT_ID']; 98 | $draft_request = new UnclaimedDraft($request, $client_id); 99 | $draft_request->setIsForEmbeddedSigning(true); 100 | // Send it to HelloSign 101 | $response = $this->client->createUnclaimedDraft($draft_request); 102 | 103 | 104 | $this->assertInstanceOf('HelloSign\UnclaimedDraft', $response); 105 | $this->assertNotNull($response->getClaimUrl()); 106 | } 107 | 108 | /** 109 | * @group create 110 | */ 111 | public function testCreateEmbeddedSignatureRequestWithTemplate() 112 | { 113 | // Get a template 114 | 115 | $templates = $this->client->getTemplates(); 116 | $template = $templates[0]; 117 | 118 | // Create the signature request 119 | 120 | $request = new TemplateSignatureRequest; 121 | $request->setTemplateId($template->getId()); 122 | $request->setSubject('Purchase Order'); 123 | $request->setMessage('Glad we could come to an agreement.'); 124 | 125 | foreach ($template->getSignerRoles() as $i => $role) { 126 | $request->setSigner($role->name, "george$i@example.com", "George {$role->name}"); 127 | } 128 | foreach ($template->getCCRoles() as $i => $role) { 129 | $request->setCC($role->name, "oscar$i@example.com"); 130 | } 131 | foreach ($template->getCustomFields() as $i => $field) { 132 | $request->setCustomFieldValue($field->name, 'My String'); 133 | } 134 | 135 | // Turn it into an embedded request 136 | $client_id = $_ENV['CLIENT_ID']; 137 | $embedded_request = new EmbeddedSignatureRequest($request, $client_id); 138 | 139 | // Send it to HelloSign 140 | $response = $this->client->createEmbeddedSignatureRequest($embedded_request); 141 | 142 | $this->assertInstanceOf('HelloSign\SignatureRequest', $response); 143 | $this->assertNotNull($response->getId()); 144 | $signatures = $response->getSignatures(); 145 | return $signatures[0]->getId(); 146 | } 147 | 148 | /** 149 | * @depends testCreateEmbeddedSignatureRequest 150 | * @group read 151 | */ 152 | public function testGetEmbeddedSignUrl($id) 153 | { 154 | $response = $this->client->getEmbeddedSignUrl($id); 155 | $sign_url = $response->getSignUrl(); 156 | 157 | $this->assertNotEmpty($sign_url); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /library/HelloSign/Test/ApiAppTest.php: -------------------------------------------------------------------------------- 1 | expectException(Error::class); 46 | $this->expectExceptionMessage("An app with the same name already exists"); 47 | $wl = array('primary_button_color' => '#626567', 'primary_button_text_color' => '#ffffff'); 48 | $wl = json_encode($wl); 49 | 50 | $name = $this->generateApiAppName('Test'); 51 | $domain = "www.testdomain.com"; 52 | $callback = $_ENV['CALLBACK_URL']; 53 | $logo = __DIR__ . "/logo.jpg"; 54 | 55 | $app = new ApiApp; 56 | $app->setName($name); 57 | $app->setDomain($domain); 58 | $app->setCallbackUrl($callback); 59 | $app->setLogo($logo); 60 | $app->setWhiteLabeling($wl); 61 | $app->setInsertEverywhere(false); 62 | 63 | $response = $this->client->createApiApp($app); 64 | 65 | $this->assertNotNull($response->getClientId()); 66 | 67 | //trying to create it again should fail 68 | $duplicate_app = new ApiApp; 69 | $duplicate_app->setName($name); 70 | $duplicate_app->setDomain($domain); 71 | $duplicate_app->setCallbackUrl($callback); 72 | $duplicate_app->setLogo($logo); 73 | $duplicate_app->setWhiteLabeling($wl); 74 | 75 | $second_response = $this->client->createApiApp($duplicate_app); 76 | 77 | return $second_response; 78 | } 79 | 80 | /** 81 | * @group oauth 82 | */ 83 | public function testCreateOauthApiApp() 84 | { 85 | $name = $this->generateApiAppName('Test Oauth'); 86 | $domain = "www.testdomain.com"; 87 | $callback = $_ENV['CALLBACK_URL']; 88 | $logo = __DIR__ . "/logo.jpg"; 89 | $oauth_callback = $_ENV['CALLBACK_URL']; 90 | $oauth_scope = "basic_account_info,request_signature"; 91 | 92 | $app = new ApiApp; 93 | $app->setName($name); 94 | $app->setDomain($domain); 95 | $app->setCallbackUrl($callback); 96 | $app->setLogo($logo); 97 | $app->setOauthOptions($oauth_callback, $oauth_scope); 98 | 99 | $response = $this->client->createApiApp($app); 100 | 101 | $this->assertNotNull($response->getClientId()); 102 | $this->assertEquals($response->name, $name); 103 | $this->assertNotNull($response->oauth); 104 | 105 | return $response; 106 | } 107 | 108 | /** 109 | * @group update 110 | */ 111 | public function testUpdateApiApp() 112 | { 113 | 114 | $name = $this->generateApiAppName('Test'); 115 | $domain = "www.testdomain.com"; 116 | 117 | $app = new ApiApp; 118 | $app->setName($name); 119 | $app->setDomain($domain); 120 | 121 | $response = $this->client->createApiApp($app); 122 | $client_id = $response->getClientId(); 123 | 124 | $callback = "http://www.testcallback.com"; 125 | 126 | $update = new ApiApp; 127 | $update->setCallbackUrl($callback); 128 | $updated_response = $this->client->updateApiApp($client_id, $update); 129 | 130 | $this->assertInstanceOf('HelloSign\ApiApp', $response); 131 | $this->assertEquals($updated_response->getCallbackUrl(), $callback); 132 | 133 | return $response; 134 | } 135 | 136 | /** 137 | * @group read 138 | */ 139 | public function testGetApiApp() 140 | { 141 | $name = $this->generateApiAppName('Test'); 142 | $domain = "www.testdomain.com"; 143 | $callback_url = $_ENV['CALLBACK_URL']; 144 | 145 | $app = new ApiApp; 146 | $app->setName($name); 147 | $app->setDomain($domain); 148 | $app->setCallbackUrl($callback_url); 149 | 150 | $response = $this->client->createApiApp($app); 151 | $client_id = $response->getClientId(); 152 | 153 | $app = $this->client->getApiApp($client_id); 154 | 155 | $this->assertNotNull($app->name); 156 | $this->assertNotNull($app->callback_url); 157 | $this->assertNotNull($app->domain); 158 | 159 | } 160 | 161 | /** 162 | * @group delete 163 | */ 164 | public function testDeleteApiApp() 165 | { 166 | $this->expectException(Error::class); 167 | $this->expectExceptionMessage("Not found"); 168 | # Note that we won't be actually deleting an API app, 169 | # but rather checking to make sure we get a Not found error 170 | 171 | $client_id = 'c7e5031edf091e76796088e4e06443e9'; #random 172 | 173 | $response = $this->client->deleteApiApp($client_id); 174 | } 175 | 176 | /** 177 | * @group list 178 | */ 179 | public function testGetApiApps() 180 | { 181 | $list = $this->client->getApiApps(1, 1); 182 | 183 | $this->assertNotNull($list); 184 | $this->assertCount(1, $list); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /library/HelloSign/BulkSendJob.php: -------------------------------------------------------------------------------- 1 | template_ids[] = $id; 107 | } else { 108 | $this->template_ids[$order] = $id; 109 | } 110 | return $this; 111 | } 112 | 113 | /** 114 | * Adds a list of signers in a CSV file. Required unless signer_list is used. 115 | * 116 | * @param string $file path for signer_file 117 | * @return BulkSendJob 118 | * @ignore 119 | */ 120 | public function addSignerfile($file) 121 | { 122 | if (!file_exists($file)) { 123 | throw new Error('file not found', 'File does not exist. Please use an absolute file path.'); 124 | } 125 | 126 | $this->signer_file = fopen($file, 'rb'); 127 | return $this; 128 | } 129 | 130 | /** 131 | * Adds a list of signers defined in a JSON array. Required unless signer_file is used. 132 | * 133 | * @param array $signer_list 134 | * @return BulkSendJob 135 | * @ignore 136 | */ 137 | public function addSignerList($signer_list) 138 | { 139 | $this->signer_list = json_encode($signer_list); 140 | return $this; 141 | } 142 | 143 | /** 144 | * Sets the CC email address for the provided role 145 | * 146 | * @param string $role 147 | * @param string $email 148 | * @return BulkSendJob 149 | */ 150 | public function setCC($role, $email) 151 | { 152 | $obj = new stdClass; 153 | $obj->email_address = $email; 154 | 155 | $this->ccs[$role] = $obj; 156 | 157 | return $this; 158 | } 159 | 160 | /** 161 | * @return string 162 | * @ignore 163 | */ 164 | public function getId() 165 | { 166 | return $this->bulk_send_job_id; 167 | } 168 | 169 | /** 170 | * @return SignatureRequestList 171 | * @ignore 172 | */ 173 | public function getSignatureRequests() 174 | { 175 | return $this->signature_requests; 176 | } 177 | 178 | /** 179 | * Constructor 180 | * 181 | * @param stdClass $response 182 | * @param array $options 183 | * @ignore 184 | */ 185 | public function __construct($response = null, $options = array()) 186 | { 187 | if (isset($response->signature_requests)) 188 | { 189 | $this->signature_requests = $response->signature_requests; 190 | } 191 | 192 | if (isset($response->list_info)) 193 | { 194 | $this->list_info = $response->list_info; 195 | } 196 | 197 | parent::__construct($response, $options); 198 | } 199 | 200 | /** 201 | * @return array 202 | * @ignore 203 | */ 204 | public function toParams() 205 | { 206 | $except = array( 207 | 'use_text_tags', 208 | 'hide_text_tags', 209 | 'use_preexisting_fields', 210 | 'requesting_redirect_url', 211 | 'signers', 212 | 'requester_email_address', 213 | 'signing_options', 214 | 'allow_decline' 215 | ); 216 | return $this->toArray(array( 217 | 'except' => $except 218 | )); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /library/HelloSign/Event.php: -------------------------------------------------------------------------------- 1 | event_metadata = new stdClass; 107 | 108 | parent::__construct($response, $options); 109 | } 110 | 111 | /** 112 | * @return string 113 | * @ignore 114 | */ 115 | public function getTime() 116 | { 117 | return $this->event_time; 118 | } 119 | 120 | /** 121 | * @return string 122 | * @ignore 123 | */ 124 | public function getType() 125 | { 126 | return $this->event_type; 127 | } 128 | 129 | /** 130 | * @return string 131 | * @ignore 132 | */ 133 | public function getAccountId() 134 | { 135 | return $this->event_metadata->reported_for_account_id; 136 | } 137 | 138 | /** 139 | * @return boolean 140 | * @ignore 141 | */ 142 | public function hasAccountId() 143 | { 144 | return isset($this->event_metadata->reported_for_account_id); 145 | } 146 | 147 | /** 148 | * @return string 149 | * @ignore 150 | */ 151 | public function getRelatedSignatureId() 152 | { 153 | return $this->event_metadata->related_signature_id; 154 | } 155 | 156 | /** 157 | * @return boolean 158 | * @ignore 159 | */ 160 | public function hasRelatedSignatureId() 161 | { 162 | return isset($this->event_metadata->related_signature_id); 163 | } 164 | 165 | /** 166 | * @return Signature 167 | * @ignore 168 | */ 169 | public function getRelatedSignature() 170 | { 171 | if (!$this->hasRelatedSignatureId()) { 172 | return null; 173 | } 174 | 175 | foreach ($this->signature_request->getSignatures() as $signature) { 176 | if ($signature->getId() == $this->getRelatedSignatureId()) { 177 | return $signature; 178 | } 179 | } 180 | 181 | return null; 182 | } 183 | 184 | /** 185 | * @return SignatureRequest 186 | * @ignore 187 | */ 188 | public function getSignatureRequest() 189 | { 190 | return $this->signature_request; 191 | } 192 | 193 | /** 194 | * @param stdClass $response 195 | * @param array $options 196 | * @return SignatureRequest 197 | * @ignore 198 | */ 199 | public function fromResponse($response, $options = array()) 200 | { 201 | if(isset($response->signature_request)) { 202 | $this->setSignatureRequest($response->signature_request); 203 | } 204 | 205 | return parent::fromResponse($response, $options); 206 | } 207 | 208 | /** 209 | * Check the event is valid or not 210 | * @param string $api_key 211 | * @return boolean 212 | * @see Event::calculateHash() 213 | */ 214 | public function isValid($api_key) 215 | { 216 | if (!$api_key) { 217 | return false; 218 | } 219 | 220 | return ($this->event_hash == $this->calculateHash($api_key)) 221 | ? true : false; 222 | } 223 | 224 | /** 225 | * @return string 226 | * @ignore 227 | */ 228 | protected function calculateHash($api_key) 229 | { 230 | return hash_hmac("sha256", $this->event_time . $this->event_type, $api_key); 231 | } 232 | 233 | /** 234 | * @param stdClass $signature_request 235 | * @return Event 236 | * @ignore 237 | */ 238 | protected function setSignatureRequest($signature_request) 239 | { 240 | $this->signature_request = new SignatureRequest; 241 | $this->signature_request->fromObject($signature_request); 242 | 243 | return $this; 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /library/HelloSign/ApiApp.php: -------------------------------------------------------------------------------- 1 | null, "scopes" => null]; 103 | 104 | /** 105 | * A JSON array of set white labeling options 106 | * 107 | * @var string 108 | */ 109 | protected $white_labeling_options = null; 110 | 111 | /** 112 | * Key-value pair to set option to allow signer to insert everywhere 113 | * Defaults to true 114 | * 115 | * @var array 116 | */ 117 | protected $options = ["can_insert_everywhere" => true]; 118 | 119 | 120 | /** 121 | * Constructor 122 | * 123 | * @param stdClass $response 124 | * @param array $options 125 | * @see static::fromResponse() 126 | */ 127 | public function __construct($response = null, $options = array()) 128 | { 129 | parent::__construct($response, $options); 130 | } 131 | 132 | /** 133 | * @return ApiApp 134 | * @ignore 135 | */ 136 | public function setName($name) 137 | { 138 | $this->name = $name; 139 | return $this; 140 | } 141 | 142 | /** 143 | * @return string 144 | * @ignore 145 | */ 146 | public function getCallbackUrl() 147 | { 148 | return $this->callback_url; 149 | } 150 | 151 | /** 152 | * @return ApiApp 153 | * @ignore 154 | */ 155 | public function setDomain($domain) 156 | { 157 | $this->domain = $domain; 158 | return $this; 159 | } 160 | 161 | /** 162 | * @return ApiApp 163 | * @ignore 164 | */ 165 | public function setLogo($file) 166 | { 167 | $this->custom_logo_file = fopen($file, 'rb'); 168 | return $this; 169 | } 170 | 171 | /** 172 | * @return ApiApp 173 | * @ignore 174 | */ 175 | public function setWhiteLabeling($wl) 176 | { 177 | $this->white_labeling_options = $wl; 178 | return $this; 179 | } 180 | 181 | /** 182 | * @return string 183 | * @ignore 184 | */ 185 | public function getClientId() 186 | { 187 | return $this->client_id; 188 | } 189 | 190 | /** 191 | * @return string 192 | * @ignore 193 | */ 194 | public function getOwner() 195 | { 196 | return $this->owner_account; 197 | } 198 | 199 | /** 200 | * @return boolean 201 | * @ignore 202 | */ 203 | public function isApproved() 204 | { 205 | return $this->is_approved; 206 | } 207 | 208 | /** 209 | * @return boolean 210 | * @ignore 211 | */ 212 | public function hasCallbackUrl() 213 | { 214 | return isset($this->callback_url); 215 | } 216 | 217 | /** 218 | * @param string $url 219 | * @return ApiApp 220 | * @ignore 221 | */ 222 | public function setCallbackUrl($url) 223 | { 224 | $this->callback_url = $url; 225 | return $this; 226 | } 227 | 228 | /** 229 | * @param boolean 230 | * @return ApiApp 231 | * @ignore 232 | */ 233 | public function setInsertEverywhere($insert) 234 | { 235 | $this->options = ["can_insert_everywhere" => $insert]; 236 | return $this; 237 | } 238 | 239 | /** 240 | * @param string $url 241 | * @param string $scopes 242 | * @return ApiApp 243 | * @ignore 244 | */ 245 | public function setOauthOptions($url, $scopes) 246 | { 247 | $this->oauth = ["callback_url" => $url, "scopes" => $scopes]; 248 | return $this; 249 | } 250 | 251 | /** 252 | * @return array 253 | * @ignore 254 | */ 255 | public function toCreateParams($options = array()) 256 | { 257 | return $this->toArray(array('only' => array( 258 | 'name', 259 | 'domain', 260 | 'callback_url', 261 | 'custom_logo_file', 262 | 'options', 263 | 'oauth', 264 | 'white_labeling_options' 265 | ))); 266 | } 267 | 268 | /** 269 | * @return array 270 | * @ignore 271 | */ 272 | public function toUpdateParams() 273 | { 274 | return $this->toArray(array( 275 | 'only' => array( 276 | 'name', 277 | 'domain', 278 | 'callback_url', 279 | 'custom_logo_file', 280 | 'options', 281 | 'oauth', 282 | 'white_labeling_options' 283 | ) 284 | )); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /library/HelloSign/UnclaimedDraft.php: -------------------------------------------------------------------------------- 1 | is_for_embedded_signing = $is_for_embedded_signing; 97 | } 98 | 99 | /** 100 | * @param boolean $use_preexisting_fields 101 | * @return UnclaimedDraft 102 | * @ignore 103 | */ 104 | public function setUsePreexistingFields($use_preexisting_fields) 105 | { 106 | $this->use_preexisting_fields = $use_preexisting_fields; 107 | return $this; 108 | } 109 | 110 | /** 111 | * @param boolean $skip_me_now Set to true to disable the "Me (Now)" option 112 | * for the preparer. 113 | * @return UnclaimedDraft 114 | * @ignore 115 | */ 116 | public function enableSkipMeNow() 117 | { 118 | $this->skip_me_now = true; 119 | return $this; 120 | } 121 | 122 | /** 123 | * @param string $id 124 | * @return UnclaimedDraft 125 | * @ignore 126 | */ 127 | public function setClientId($id) 128 | { 129 | if ($id) { 130 | $this->type = 'request_signature'; 131 | } 132 | 133 | return parent::setClientId($id); 134 | } 135 | 136 | /** 137 | * @param string $type 138 | * @return UnclaimedDraft 139 | * @ignore 140 | */ 141 | public function setType($type) 142 | { 143 | $this->type = $type; 144 | return $this; 145 | } 146 | 147 | /** 148 | * @return string 149 | * @ignore 150 | */ 151 | public function getType() 152 | { 153 | return $this->type; 154 | } 155 | 156 | /** 157 | * Returns true if this Unclaimed Draft is to be embedded 158 | * 159 | * @return boolean true if this Unclaimed Draft is to be embedded 160 | */ 161 | public function isForEmbeddedSigning() 162 | { 163 | return $this->is_for_embedded_signing; 164 | } 165 | 166 | /** 167 | * @return string claim URL 168 | * @ignore 169 | */ 170 | public function getClaimUrl() 171 | { 172 | return $this->claim_url; 173 | } 174 | 175 | /** 176 | * @return array 177 | * @ignore 178 | */ 179 | public function toParams() 180 | { 181 | $except = array( 182 | 'request', 183 | 'claim_url' 184 | ); 185 | 186 | // Skip including files local to this object if specified on the request instead 187 | if (isset($this->request) && !sizeof($this->file)) { 188 | array_push($except, 'file'); 189 | } 190 | 191 | if (!$this->isForEmbeddedSigning()) { 192 | $except[] = 'is_for_embedded_signing'; 193 | } 194 | 195 | if (!$this->getClientId()) { 196 | $except[] = 'client_id'; 197 | } 198 | 199 | /** 200 | * Here we union (using the + operator) the param arrays for the 201 | * SignatureRequest object with our self (the UnclaimedDraft 202 | * object) to get the final params array. The order of this union is 203 | * important! The params from $this->request must be left of the union 204 | * operator so that its values (e.g. test_mode) take precedence over 205 | * our defaults. 206 | */ 207 | return $this->request->toParams(array( 208 | 'except' => array( 209 | 'title' // title not supported for unclaimed draft endpoints 210 | ) 211 | )) + $this->toArray(array( 212 | 'except' => $except 213 | )); 214 | } 215 | 216 | public function toEditParams() 217 | { 218 | $fields_to_include = array( 219 | 'client_id', 220 | 'test_mode', 221 | 'requesting_redirect_url', 222 | 'signing_redirect_url', 223 | 'is_for_embedded_signing', 224 | 'requester_email_address' 225 | ); 226 | 227 | $params = $this->toArray(); 228 | 229 | foreach ($params as $key => $value) { 230 | if (!in_array($key, $fields_to_include)) { 231 | unset($params[$key]); 232 | } 233 | } 234 | 235 | return $params; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /library/HelloSign/Test/TemplateTest.php: -------------------------------------------------------------------------------- 1 | expectException(Error::class); 46 | $this->expectExceptionMessage("Page not found"); 47 | $templates = $this->client->getTemplates(9999, 1); 48 | } 49 | 50 | /** 51 | * @group read 52 | */ 53 | public function testGetTemplates() 54 | { 55 | $templates = $this->client->getTemplates(); 56 | $template = $templates[0]; 57 | 58 | $template2 = $this->client->getTemplate($template->getId()); 59 | 60 | $this->assertInstanceOf('HelloSign\TemplateList', $templates); 61 | $this->assertGreaterThan(0, count($templates)); 62 | 63 | $this->assertInstanceOf('HelloSign\Template', $template); 64 | $this->assertNotNull($template->getId()); 65 | 66 | $this->assertInstanceOf('HelloSign\Template', $template2); 67 | $this->assertNotNull($template2->getId()); 68 | 69 | $this->assertEquals($template, $template2); 70 | 71 | return $template; 72 | } 73 | 74 | /** 75 | * @depends testGetTemplates 76 | * @group update 77 | */ 78 | public function testAddTemplateUser($template) 79 | { 80 | $this->expectException(Error::class); 81 | $this->expectExceptionMessage("Account does not belong to your team"); 82 | $invite = $this->client->inviteTeamMember($this->team_member_2); 83 | $response = $this->client->addTemplateUser($template->getId(), $this->team_member_2); 84 | 85 | $this->assertInstanceOf('HelloSign\Error', $response); 86 | } 87 | 88 | /** 89 | * @depends testGetTemplates 90 | * @group update 91 | */ 92 | public function testRemoveTemplateUser($template) 93 | { 94 | $this->expectException(Error::class); 95 | $this->expectExceptionMessage("Account does not belong to your team"); 96 | $response = $this->client->removeTemplateUser($template->getId(), $this->team_member_2); 97 | 98 | $this->assertInstanceOf('HelloSign\Error', $response); 99 | } 100 | 101 | /** 102 | * @group embedded 103 | */ 104 | public function testCreateEmbeddedDraft() 105 | { 106 | $client_id = $_ENV['CLIENT_ID']; 107 | 108 | $request = new Template(); 109 | $request->setClientId($client_id); 110 | $request->addFile(__DIR__ . '/nda.docx'); 111 | $request->setTitle('Test Title'); 112 | $request->setSubject('Test Subject'); 113 | $request->setMessage('Test Message'); 114 | $request->addSignerRole('Test Role', 1); 115 | $request->addSignerRole('Test Role 2', 2); 116 | $request->addCCRole('Test CC Role'); 117 | $request->addMergeField('Test Merge', 'text'); 118 | $request->addMergeField('Test Merge 2', 'checkbox'); 119 | $request->addMetadata('custom_id', '1234'); 120 | $request->addMetadata('favorite_movie', 'Big Fish'); 121 | $request->setUsePreexistingFields(true); 122 | $request->addAttachment('Passport', 0, 'Attach your passport', false); 123 | 124 | $return = $this->client->createEmbeddedDraft($request); 125 | 126 | $this->assertTrue(is_string($return->getId())); 127 | $this->assertTrue(is_string($return->getEditUrl())); 128 | $this->assertTrue($return->isEmbeddedDraft()); 129 | return $return->getId(); 130 | } 131 | 132 | /** 133 | * @group embedded 134 | */ 135 | public function testGetEmbeddedEditUrl() 136 | { 137 | $this->expectException(Error::class); 138 | $this->expectExceptionMessage("Template not found"); 139 | # Similar to the delete_template function, we can't actually test this for success without human interaction. 140 | # Instead, we'll be checking for a 404 - Template not found status code, which means our parameters are correct 141 | 142 | $template_id = 'ax5d921d0d3ccfcd594d2b8c897ba774d89c9234'; #random 143 | 144 | $res = $this->client->getEmbeddedEditUrl($template_id); 145 | } 146 | 147 | /** 148 | * @group embedded 149 | */ 150 | public function testDeleteTemplate() 151 | { 152 | $this->expectException(Error::class); 153 | $this->expectExceptionMessage("Template not found"); 154 | # Note that we won't be actually deleting a template, 155 | # but rather checking to make sure we get a 404 - Template not found error 156 | 157 | $template_id = 'ax5d921d0d3ccfcd594d2b8c897ba774d89c9234'; #random 158 | 159 | $res = $this->client->deleteTemplate($template_id); 160 | } 161 | 162 | /** 163 | * @group read 164 | * @group download 165 | * @group newTemplate 166 | */ 167 | public function testGetTemplateFiles() 168 | { 169 | $templates = $this->client->getTemplates(); 170 | $template_id = $templates[0]->getId(); 171 | 172 | $file1 = 'phpunit_test_template_file.pdf'; 173 | if (file_exists($file1)) { 174 | unlink($file1); 175 | } 176 | 177 | $response = $this->client->getTemplateFiles($template_id, $file1); 178 | $this->assertGreaterThan(0, filesize($file1)); 179 | 180 | return $response; 181 | } 182 | 183 | /** 184 | * @group updateFile 185 | */ 186 | public function testUpdateTemplateFiles() 187 | { 188 | $templates = $this->client->getTemplates(); 189 | $template_id = $templates[0]->getId(); 190 | $client_id = $_ENV['CLIENT_ID']; 191 | 192 | $request = new Template(); 193 | $request->setClientId($client_id); 194 | $request->addFile(__DIR__ . '/nda.docx'); 195 | $request->setMessage('PHP SDK Test Update File Message'); 196 | $request->setSubject('PHP SDK Test Update File Subject'); 197 | 198 | $response = $this->client->updateTemplateFiles($template_id, $request); 199 | $this->assertTrue(is_string($response->getId())); 200 | return $response; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /library/HelloSign/Test/BulkSendTest.php: -------------------------------------------------------------------------------- 1 | createMock(BulkSendJob::class); 40 | $this->assertClassHasAttribute('test_mode', BulkSendJob::class); 41 | $this->assertClassHasAttribute('allow_decline', BulkSendJob::class); 42 | $this->assertClassHasAttribute('template_ids', BulkSendJob::class); 43 | $this->assertClassHasAttribute('title', BulkSendJob::class); 44 | $this->assertClassHasAttribute('subject', BulkSendJob::class); 45 | $this->assertClassHasAttribute('message', BulkSendJob::class); 46 | $this->assertClassHasAttribute('signing_redirect_url', BulkSendJob::class); 47 | $this->assertClassHasAttribute('signer_file', BulkSendJob::class); 48 | $this->assertClassHasAttribute('signer_list', BulkSendJob::class); 49 | $this->assertClassHasAttribute('custom_fields', BulkSendJob::class); 50 | $this->assertClassHasAttribute('ccs', BulkSendJob::class); 51 | $this->assertClassHasAttribute('metadata', BulkSendJob::class); 52 | $this->assertClassHasAttribute('client_id', BulkSendJob::class); 53 | } 54 | 55 | /** 56 | * @group send 57 | */ 58 | public function testSendBulkSendJobWithSignerFile() 59 | { 60 | $templates = $this->client->getTemplates(); 61 | $template = $templates[0]; 62 | 63 | $signers = __DIR__ . "/bulk_send_test_signers.csv"; 64 | 65 | $request = new BulkSendJob; 66 | $request->setTitle('Bulk Send Job Example Title'); 67 | $request->setTemplateId($template->getId()); 68 | $request->addSignerFile($signers); 69 | 70 | $response = $this->client->sendBulkSendJobWithTemplate($request); 71 | 72 | $this->assertInstanceOf('HelloSign\BulkSendJob', $response); 73 | $this->assertNotNull($response->getId()); 74 | } 75 | 76 | /** 77 | * @group send 78 | */ 79 | public function testSendBulkSendJobWithSignerList() 80 | { 81 | $templates = $this->client->getTemplates(); 82 | $template = $templates[0]; 83 | $signer_role = $template->getSignerRoles()[0]->name; 84 | $custom_field = $template->getCustomFields()[0]->name; 85 | 86 | $signers = array( 87 | array( 88 | "signers" => array( 89 | $signer_role => array( 90 | "name" => "Adam HelloSign", 91 | "email_address" => "test1@example.com", 92 | "pin" => "1234" 93 | ) 94 | ) 95 | ), 96 | array( 97 | "signers" => array( 98 | $signer_role => array( 99 | "name" => "Jane HelloSign", 100 | "email_address" => "test2@example.com" 101 | ) 102 | ), 103 | "custom_fields" => array( 104 | $custom_field => "123 Main St." 105 | ) 106 | ) 107 | ); 108 | 109 | $request = new BulkSendJob; 110 | $request->setTitle('Bulk Send Job Example Title'); 111 | $request->setTemplateId($template->getId()); 112 | $request->addSignerList($signers); 113 | 114 | $response = $this->client->sendBulkSendJobWithTemplate($request); 115 | 116 | $this->assertInstanceOf('HelloSign\BulkSendJob', $response); 117 | $this->assertNotNull($response->getId()); 118 | } 119 | 120 | /** 121 | * @group sendWithParametersEnabled 122 | */ 123 | public function testSendBulkSendJobWithAllParameters() 124 | { 125 | $templates = $this->client->getTemplates(); 126 | $template = $templates[0]; 127 | $field = $template->getCustomFields()[0]; 128 | $cc = $template->getCCRoles()[0]; 129 | $app = $this->client->getApiApps()[0]; 130 | $app_id = $app->getClientId(); 131 | 132 | $signers = __DIR__ . "/bulk_send_test_signers.csv"; 133 | 134 | $request = new BulkSendJob; 135 | $request->enableAllowDecline(); 136 | $request->setTemplateId($template->getId()); 137 | $request->setTitle('Bulk Send Job With All Parameters'); 138 | $request->setSubject('Bulk Send Job Subject'); 139 | $request->setMessage('Bulk Send Job Message'); 140 | $request->setSigningRedirectUrl('http://www.calbears.com'); 141 | $request->addSignerFile($signers); 142 | 143 | if (isset($field)) { 144 | $request->setCustomFieldValue($field->name, 'Test Test'); 145 | } 146 | 147 | if (isset($cc)) { 148 | $request->setCC($cc->name, 'cc@example.com'); 149 | } 150 | 151 | $request->addMetadata('user_id', '1234'); 152 | $request->setClientId($app_id); 153 | 154 | $response = $this->client->sendBulkSendJobWithTemplate($request); 155 | 156 | $this->assertInstanceOf('HelloSign\BulkSendJob', $response); 157 | $this->assertNotNull($response->getId()); 158 | } 159 | 160 | /** 161 | * @group embedded 162 | */ 163 | public function testSendEmbeddedBulkSendJob() 164 | { 165 | $templates = $this->client->getTemplates(); 166 | $template = $templates[0]; 167 | 168 | $signers = __DIR__ . "/bulk_send_test_signers.csv"; 169 | 170 | $request = new BulkSendJob; 171 | $request->setTitle('Embedded Bulk Send Job Example Title'); 172 | $request->setTemplateId($template->getId()); 173 | $request->addSignerFile($signers); 174 | 175 | // Turn it into an embedded request 176 | $client_id = $_ENV['CLIENT_ID']; 177 | $embedded_request = new EmbeddedBulkSendJob($request, $client_id); 178 | 179 | // Send it to HelloSign 180 | $response = $this->client->sendEmbeddedBulkSendJobWithTemplate($embedded_request); 181 | 182 | $this->assertInstanceOf('HelloSign\BulkSendJob', $response); 183 | $this->assertNotNull($response->getId()); 184 | } 185 | 186 | /** 187 | * @group list 188 | */ 189 | public function testGetBulkSendJobs() 190 | { 191 | // Testing default getBulkSendJobs() 192 | $list = $this->client->getBulkSendJobs(); 193 | $this->assertNotNull($list); 194 | $this->assertEquals($list->getPageSize(), 20); 195 | $this->assertEquals($list->getPage(), 1); 196 | } 197 | 198 | /** 199 | * @group list 200 | */ 201 | public function testGetBulkSendJobsWithParams() 202 | { 203 | // Testing parameters for getBulkSendJobs() 204 | $list = $this->client->getBulkSendJobs(2, 5); 205 | $this->assertNotNull($list); 206 | $this->assertEquals($list->getPageSize(), 5); 207 | $this->assertEquals($list->getPage(), 2); 208 | } 209 | 210 | /** 211 | * @group read 212 | */ 213 | public function testGetBulkSendJob() 214 | { 215 | $templates = $this->client->getTemplates(); 216 | $template = $templates[0]; 217 | 218 | $signers = __DIR__ . "/bulk_send_test_signers.csv"; 219 | 220 | $request = new BulkSendJob; 221 | $request->setTitle('Bulk Send Job Example Title'); 222 | $request->setTemplateId($template->getId()); 223 | $request->addSignerFile($signers); 224 | 225 | $response = $this->client->sendBulkSendJobWithTemplate($request); 226 | $id = $response->getId(); 227 | 228 | $bulk_send_job = $this->client->getBulkSendJob($id); 229 | $requests = $bulk_send_job->getSignatureRequests(); 230 | 231 | $this->assertNotNull($bulk_send_job->getId()); 232 | $this->assertNotNull($bulk_send_job->getSignatureRequests()); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /library/HelloSign/AbstractResource.php: -------------------------------------------------------------------------------- 1 | attachments = new AttachmentList; 120 | 121 | if (isset($response)) { 122 | $this->fromResponse($response, $options); 123 | $this->warningsFromResponse($response); 124 | } 125 | } 126 | 127 | /** 128 | * @return SignatureRequest 129 | * @ignore 130 | */ 131 | public function enableTestMode() 132 | { 133 | $this->test_mode = true; 134 | return $this; 135 | } 136 | 137 | /** 138 | * @return SignatureRequest 139 | * @ignore 140 | */ 141 | public function disableTestMode() 142 | { 143 | $this->test_mode = false; 144 | return $this; 145 | } 146 | 147 | /** 148 | * @return SignatureRequest 149 | * @ignore 150 | */ 151 | public function enableAllowDecline() 152 | { 153 | $this->allow_decline = true; 154 | return $this; 155 | } 156 | 157 | /** 158 | * @return SignatureRequest 159 | * @ignore 160 | */ 161 | public function disableAllowDecline() 162 | { 163 | $this->allow_decline = false; 164 | return $this; 165 | } 166 | 167 | /** 168 | * @param string $title 169 | * @return SignatureRequest 170 | * @ignore 171 | */ 172 | public function setTitle($title) 173 | { 174 | $this->title = $title; 175 | return $this; 176 | } 177 | 178 | /** 179 | * @return string 180 | * @ignore 181 | */ 182 | public function getTitle() 183 | { 184 | return $this->title; 185 | } 186 | 187 | /** 188 | * @param string $subject 189 | * @return SignatureRequest 190 | * @ignore 191 | */ 192 | public function setSubject($subject) 193 | { 194 | $this->subject = $subject; 195 | return $this; 196 | } 197 | 198 | /** 199 | * @return string 200 | * @ignore 201 | */ 202 | public function getSubject() 203 | { 204 | return $this->subject; 205 | } 206 | 207 | /** 208 | * @param string $message 209 | * @return SignatureRequest 210 | * @ignore 211 | */ 212 | public function setMessage($message) 213 | { 214 | $this->message = $message; 215 | return $this; 216 | } 217 | 218 | /** 219 | * @return string 220 | * @ignore 221 | */ 222 | public function getMessage() 223 | { 224 | return $this->message; 225 | } 226 | 227 | /** 228 | * @param string $file path for file 229 | * @return AbstractResource 230 | * @ignore 231 | */ 232 | public function addFile($file) 233 | { 234 | if (!file_exists($file)) { 235 | throw new Error('file not found', 'File does not exist. Please use an absolute file path.'); 236 | } 237 | 238 | $this->file[] = fopen($file, 'rb'); 239 | return $this; 240 | } 241 | 242 | /** 243 | * @param string $file_url 244 | * @return AbstractResource 245 | */ 246 | public function addFileUrl($file_url) { 247 | if (empty($file_url)) { 248 | throw new Error('unknown', 'Empty file URL'); 249 | } 250 | if (filter_var($file_url, FILTER_VALIDATE_URL) === false) { 251 | throw new Error('unknown', 'Invalid file URL'); 252 | } 253 | $this->file_url[] = $file_url; 254 | return $this; 255 | } 256 | 257 | /** 258 | * Set the metadata key to the provided value. 259 | * @param string $key 260 | * @param string $value 261 | */ 262 | public function addMetadata($key, $value) 263 | { 264 | $this->metadata[$key] = $value; 265 | } 266 | 267 | /** 268 | * Get the current metadata value for the provided key. 269 | * @param string $key 270 | * @return string|NULL 271 | */ 272 | public function getMetadata($key) 273 | { 274 | if (!is_array($this->metadata)) { 275 | $this->metadata = json_decode(json_encode($this->metadata), true); 276 | } 277 | return (isset($this->metadata[$key])) ? $this->metadata[$key] : null; 278 | } 279 | 280 | /** 281 | * @return array 282 | * @ignore 283 | */ 284 | public function getWarnings() 285 | { 286 | return $this->warnings; 287 | } 288 | 289 | /** 290 | * Populate from response 291 | * 292 | * @param stdClass $response 293 | * @param array $options 294 | * @return static 295 | * @see static::fromObject() 296 | * @see static::fromArray() 297 | */ 298 | public function fromResponse($response, $options = array()) 299 | { 300 | return $this->fromObject($response->{$this->resource_type}, $options); 301 | } 302 | 303 | public function warningsFromResponse($response) 304 | { 305 | if (property_exists($response, 'warnings') && is_array($response->warnings)) { 306 | foreach ($response->warnings as $warning) { 307 | array_push($this->warnings, new Warning($warning)); 308 | } 309 | $this->warnings = $response->warnings; 310 | } 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /library/HelloSign/Account.php: -------------------------------------------------------------------------------- 1 | email_address = $email_or_obj; 122 | } else { 123 | parent::__construct($email_or_obj); 124 | } 125 | } 126 | 127 | /** 128 | * @return string 129 | * @ignore 130 | */ 131 | public function getId() 132 | { 133 | return $this->account_id; 134 | } 135 | 136 | /** 137 | * @return boolean 138 | * @ignore 139 | */ 140 | public function hasId() 141 | { 142 | return isset($this->account_id); 143 | } 144 | 145 | /** 146 | * @return string 147 | * @ignore 148 | */ 149 | public function getEmail() 150 | { 151 | return $this->email_address; 152 | } 153 | 154 | /** 155 | * @return boolean 156 | * @ignore 157 | */ 158 | public function hasEmail() 159 | { 160 | return isset($this->email_address); 161 | } 162 | 163 | /** 164 | * @return boolean 165 | * @ignore 166 | */ 167 | public function isPaidHS() 168 | { 169 | return $this->is_paid_hs; 170 | } 171 | 172 | /** 173 | * @return boolean 174 | * @ignore 175 | */ 176 | public function isPaidHF() 177 | { 178 | return $this->is_paid_hf; 179 | } 180 | 181 | /** 182 | * @return integer 183 | * @ignore 184 | */ 185 | public function getTemplatesLeft() 186 | { 187 | return $this->quotas->templates_left; 188 | } 189 | 190 | /** 191 | * @return integer 192 | * @ignore 193 | */ 194 | public function getApiSigReqsLeft() 195 | { 196 | return $this->quotas->api_signature_requests_left; 197 | } 198 | 199 | /** 200 | * @return integer 201 | * @ignore 202 | */ 203 | public function getDocumentsLeft() 204 | { 205 | return $this->quotas->documents_left; 206 | } 207 | 208 | /** 209 | * @return string 210 | * @ignore 211 | */ 212 | public function getCallbackUrl() 213 | { 214 | return $this->callback_url; 215 | } 216 | 217 | /** 218 | * @return boolean 219 | * @ignore 220 | */ 221 | public function hasCallbackUrl() 222 | { 223 | return isset($this->callback_url); 224 | } 225 | 226 | /** 227 | * @param string $url 228 | * @return Account 229 | * @ignore 230 | */ 231 | public function setCallbackUrl($url) 232 | { 233 | $this->callback_url = $url; 234 | return $this; 235 | } 236 | 237 | /** 238 | * @return string 239 | * @ignore 240 | */ 241 | public function getRoleCode() 242 | { 243 | return $this->role_code; 244 | } 245 | 246 | /** 247 | * @return boolean 248 | * @ignore 249 | */ 250 | public function hasRoleCode() 251 | { 252 | return isset($this->role_code); 253 | } 254 | 255 | /** 256 | * @return boolean 257 | * @ignore 258 | */ 259 | public function isTeamAdmin() 260 | { 261 | return ($this->role_code == 'a'); 262 | } 263 | 264 | /** 265 | * @param string $email 266 | * @return Account 267 | * @ignore 268 | */ 269 | public function setEmail($email) 270 | { 271 | $this->email = $email; 272 | return $this; 273 | } 274 | 275 | /** 276 | * @param string $password 277 | * @return Account 278 | * @ignore 279 | * @deprecated This field will be ignored 280 | */ 281 | public function setPassword($password) 282 | { 283 | return $this; 284 | } 285 | 286 | /** 287 | * @return OAuthToken 288 | * @ignore 289 | */ 290 | public function getOAuthData() 291 | { 292 | return $this->oauth_data; 293 | } 294 | 295 | /** 296 | * @return boolean 297 | * @ignore 298 | */ 299 | public function hasOAuthData() 300 | { 301 | return isset($this->oauth_data); 302 | } 303 | 304 | /** 305 | * @param stdClass $response 306 | * @param array $options 307 | * @return Account 308 | * @ignore 309 | */ 310 | public function fromResponse($response, $options = array()) 311 | { 312 | isset($response->oauth_data) && $this->setOAuthData($response->oauth_data); 313 | 314 | return parent::fromResponse($response, $options); 315 | } 316 | 317 | /** 318 | * @return array 319 | * @ignore 320 | */ 321 | public function toCreateParams() 322 | { 323 | return $this->toArray(array( 324 | 'only' => array( 325 | 'email_address' 326 | ) 327 | )); 328 | } 329 | 330 | /** 331 | * @return array 332 | * @ignore 333 | */ 334 | public function toUpdateParams() 335 | { 336 | return $this->toArray(array( 337 | 'only' => array( 338 | 'callback_url' 339 | ) 340 | )); 341 | } 342 | 343 | /** 344 | * @param stdClass $oauth_data 345 | * @return Account 346 | * @ignore 347 | */ 348 | protected function setOAuthData($oauth_data) 349 | { 350 | $this->oauth_data = new OAuthToken($oauth_data); 351 | 352 | return $this; 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /library/HelloSign/AbstractSignatureRequest.php: -------------------------------------------------------------------------------- 1 | signers = new SignerList; 113 | 114 | parent::__construct($response, $options); 115 | } 116 | 117 | /** 118 | * @param string $url 119 | * @return SignatureRequest 120 | * @ignore 121 | */ 122 | public function setSigningRedirectUrl($url) 123 | { 124 | $this->signing_redirect_url = $url; 125 | return $this; 126 | } 127 | 128 | /** 129 | * @param string $url 130 | * @return static 131 | * @ignore 132 | */ 133 | public function setRequestingRedirectUrl($url) 134 | { 135 | $this->requesting_redirect_url = $url; 136 | return $this; 137 | } 138 | 139 | /** 140 | * Adds a signer to the signature request 141 | * 142 | * @param mixed $email_or_signer 143 | * @param string $name 144 | * @param string $index 145 | * @return AbstractSignatureRequest 146 | */ 147 | public function addSigner($email_or_signer, $name = null, $index = null) 148 | { 149 | $signer = ($email_or_signer instanceof Signer) 150 | ? $email_or_signer 151 | : new Signer(array( 152 | 'name' => $name, 153 | 'email_address' => $email_or_signer 154 | )); 155 | 156 | if (isset($index)) { 157 | $this->signers[$index] = $signer; 158 | } else { 159 | $this->signers[] = $signer; 160 | } 161 | 162 | return $this; 163 | } 164 | 165 | /** 166 | * Adds a Signer Group to the Signature Request 167 | * 168 | * @param string $name Signer Group name 169 | * @param mixed $group_index_or_role Group Index or Signer Role. Defaults to 0. 170 | * @return AbstractSignatureRequest 171 | */ 172 | public function addGroup($name, $group_index_or_role = 0) 173 | { 174 | $group = new SignerGroup(array( 175 | 'name' => $name 176 | )); 177 | 178 | $this->signers[$group_index_or_role] = $group; 179 | 180 | return $this; 181 | } 182 | 183 | /** 184 | * Adds Signers to a Signer Group in the Signature Request 185 | * 186 | * @param string $name Name of the Signer 187 | * @param string $email Email address of the Signer 188 | * @param integer $signer_index Signer Index of the Signer 189 | * @param mixed $group_index_or_role Group Index or Signer Role. Defaults to 0. 190 | * @return AbstractSignatureRequest 191 | */ 192 | public function addGroupSigner($name, $email, $signer_index, $group_index_or_role = 0) 193 | { 194 | $signer = new Signer(array( 195 | 'name' => $name, 196 | 'email_address' => $email 197 | ) 198 | ); 199 | 200 | $this->signers[$group_index_or_role]->$signer_index = $signer; 201 | 202 | return $this; 203 | } 204 | 205 | /** 206 | * @param array $array 207 | * @param array $options 208 | * @return AbstractSignatureRequest 209 | * @ignore 210 | */ 211 | public function fromArray($array, $options = array()) 212 | { 213 | array_key_exists('signers', $array) && $this->setSigners($array['signers']); 214 | 215 | if (!isset($options['except'])) { 216 | $options['except'] = array(); 217 | } 218 | 219 | $options['except'][] = 'signers'; 220 | 221 | return parent::fromArray($array, $options); 222 | } 223 | 224 | /** 225 | * @param array $signers 226 | * @return AbstractSignatureRequest 227 | * @ignore 228 | */ 229 | protected function setSigners($signers) 230 | { 231 | $this->signers->setCollection($signers); 232 | 233 | return $this; 234 | } 235 | 236 | /** 237 | * @param string $email 238 | * @return SignatureRequest 239 | * @ignore 240 | */ 241 | public function setRequesterEmailAddress($email) 242 | { 243 | $this->requester_email_address = $email; 244 | return $this; 245 | } 246 | 247 | /** 248 | * Alias to setRequesterEmailAddress 249 | * @ignore 250 | */ 251 | public function setRequesterEmail($email) 252 | { 253 | return $this->setRequesterEmailAddress($email); 254 | } 255 | 256 | /** 257 | * Enable or disable text tags 258 | * @param boolean $use_text_tags 259 | */ 260 | public function setUseTextTags($use_text_tags) 261 | { 262 | $this->use_text_tags = $use_text_tags; 263 | return $this; 264 | } 265 | 266 | /** 267 | * Enable or disable hiding text tags 268 | * @param boolean $use_text_tags 269 | */ 270 | public function setHideTextTags($hide_text_tags) 271 | { 272 | $this->hide_text_tags = $hide_text_tags; 273 | return $this; 274 | } 275 | 276 | /** 277 | * @param array $options 278 | * @return SignatureRequest 279 | * @ignore 280 | */ 281 | public function setSignerOptions($options) 282 | { 283 | $this->signing_options = json_encode($options); 284 | return $this; 285 | } 286 | 287 | /** 288 | * Set the value for a custom field with the given field name 289 | * and optionally define a Role allowed to edit it and if the field is required to be filled 290 | * 291 | * @param string $field_name field name to be filled in 292 | * @param string $value 293 | * @param string $editor 294 | * @param string $required 295 | * @return AbstractSignatureRequest 296 | */ 297 | public function setCustomFieldValue($field_name, $value, $editor = null, $required = null) 298 | { 299 | $custom_fields = isset($this->custom_fields) ? json_decode($this->custom_fields) : array(); 300 | 301 | $custom_fields[] = array( 302 | 'name' => $field_name, 303 | 'value' => $value, 304 | 'editor' => $editor, 305 | 'required' => $required 306 | ); 307 | 308 | $this->custom_fields = json_encode($custom_fields); 309 | 310 | return $this; 311 | } 312 | 313 | /** 314 | * @param string $id 315 | * @return SignatureRequest 316 | * @ignore 317 | */ 318 | public function setClientId($id) 319 | { 320 | $this->client_id = $id; 321 | return $this; 322 | } 323 | 324 | } 325 | -------------------------------------------------------------------------------- /library/HelloSign/SignatureRequest.php: -------------------------------------------------------------------------------- 1 | signatures = new SignatureList; 161 | 162 | parent::__construct($response, $options); 163 | } 164 | 165 | /** 166 | * @return string 167 | * @ignore 168 | */ 169 | public function getId() 170 | { 171 | return $this->signature_request_id; 172 | } 173 | 174 | /** 175 | * @return SignatureRequest 176 | * @ignore 177 | */ 178 | public function enableAllowReassign() 179 | { 180 | $this->allow_reassign = true; 181 | return $this; 182 | } 183 | 184 | /** 185 | * @return SignatureRequest 186 | * @ignore 187 | */ 188 | public function disableAllowReassign() 189 | { 190 | $this->allow_reassign = false; 191 | return $this; 192 | } 193 | 194 | /** 195 | * @param array $form_fields 196 | * @return SignatureRequest 197 | * @ignore 198 | */ 199 | public function setFormFieldsPerDocument($form_fields) 200 | { 201 | $this->form_fields_per_document = json_encode($form_fields); 202 | return $this; 203 | } 204 | 205 | /** 206 | * Set the value for a custom field with the given field name 207 | * and optionally define a Role allowed to edit it and if the field is required to be filled 208 | * 209 | * @param string $field_name field name to be filled in 210 | * @param string $value 211 | * @param string $editor 212 | * @param string $required 213 | * @return SignatureRequest 214 | */ 215 | public function setCustomFieldValue($field_name, $value, $editor = null, $required = null) 216 | { 217 | $custom_fields = isset($this->custom_fields) ? json_decode($this->custom_fields) : array(); 218 | $custom_fields[] = array( 219 | 'name' => $field_name, 220 | 'value' => $value, 221 | 'editor' => $editor, 222 | 'required' => $required 223 | ); 224 | $this->custom_fields = json_encode($custom_fields); 225 | return $this; 226 | } 227 | 228 | /** 229 | * @param string $email 230 | * @return SignatureRequest 231 | * @ignore 232 | */ 233 | public function addCC($email) 234 | { 235 | $this->cc_email_addresses[] = $email; 236 | return $this; 237 | } 238 | 239 | /** 240 | * Adds an Attachment to the SignatureRequest 241 | * 242 | * @param string $name Name of the Attachment. 243 | * @param integer $signer_index Index of the signer to upload this Attachment. 244 | * @param string $instructions Instructions for uploading the Attachment. (optional) 245 | * @param boolean $required Whether or not the signer is required to upload this Attachment. (optional) 246 | * @return SignatureRequest 247 | */ 248 | public function addAttachment($name, $signer_index, $instructions = null, $required = null) 249 | { 250 | $attachment = new Attachment(array( 251 | 'name' => $name, 252 | 'instructions' => $instructions, 253 | 'signer_index' => $signer_index, 254 | 'required' => $required 255 | )); 256 | 257 | $this->attachments[] = $attachment; 258 | 259 | return $this; 260 | } 261 | 262 | /** 263 | * @param array $options 264 | * @return array 265 | * @ignore 266 | */ 267 | public function toParams($options = array()) 268 | { 269 | // default value 270 | if (!isset($options['except'])) { 271 | $options['except'] = array(); 272 | } 273 | 274 | $options['except'] = array_merge($options['except'], array( 275 | 'signature_request_id', 276 | 'is_complete', 277 | 'has_error', 278 | 'files_url', 279 | 'signing_url', 280 | 'details_url', 281 | 'response_data', 282 | 'signatures' 283 | )); 284 | 285 | // requester_email_address param only accepted in unclaimed_draft/create_embedded 286 | if (!$this->requester_email_address) { 287 | $options['except'][] = 'requester_email_address'; 288 | } 289 | 290 | return $this->toArray($options); 291 | } 292 | 293 | /** 294 | * @param array $array 295 | * @param array $options 296 | * @return SignatureRequest 297 | * @ignore 298 | */ 299 | public function fromArray($array, $options = array()) 300 | { 301 | array_key_exists('signatures', $array) && $this->setSignatures($array['signatures']); 302 | 303 | if (!isset($options['except'])) { 304 | $options['except'] = array(); 305 | } 306 | 307 | $options['except'] = array_merge($options['except'], array( 308 | 'signatures', 309 | 'original_title', 310 | 'final_copy_uri' 311 | )); 312 | 313 | return parent::fromArray($array, $options); 314 | } 315 | 316 | /** 317 | * @return boolean true, if all signers have signed the document, false 318 | * otherwise 319 | * @ignore 320 | */ 321 | public function isComplete() 322 | { 323 | return $this->is_complete; 324 | } 325 | 326 | /** 327 | * @return boolean 328 | * @ignore 329 | */ 330 | public function hasError() 331 | { 332 | return $this->has_error; 333 | } 334 | 335 | /** 336 | * @return SignatureList 337 | * @ignore 338 | */ 339 | public function getSignatures() 340 | { 341 | return $this->signatures; 342 | } 343 | 344 | /** 345 | * @param array $signatures 346 | * @return SignatureRequest 347 | * @ignore 348 | */ 349 | protected function setSignatures($signatures) 350 | { 351 | $this->signatures->setCollection($signatures); 352 | 353 | return $this; 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /library/lib/REST.php: -------------------------------------------------------------------------------- 1 | 'application/xml', 39 | 'json' => 'application/json', 40 | 'serialize' => 'application/vnd.php.serialized', 41 | 'php' => 'text/plain', 42 | 'csv' => 'text/csv' 43 | ); 44 | 45 | protected $auto_detect_formats = array( 46 | 'application/xml' => 'xml', 47 | 'text/xml' => 'xml', 48 | 'application/json' => 'json', 49 | 'text/json' => 'json', 50 | 'text/csv' => 'csv', 51 | 'application/csv' => 'csv', 52 | 'application/vnd.php.serialized' => 'serialize' 53 | ); 54 | 55 | protected $guzzleClient; 56 | protected $headers = []; 57 | protected $statusCode; 58 | 59 | protected $server; 60 | protected $ca_info; 61 | protected $auth = 'any'; 62 | protected $user; 63 | protected $pass; 64 | 65 | protected $is_https = false; 66 | protected $debug_mode = false; 67 | 68 | protected $format; 69 | protected $mime_type; 70 | 71 | 72 | function __construct($config = array(), $http_options = array()) 73 | { 74 | // If a URL was passed to the library 75 | empty($config) OR $this->initialize($config); 76 | 77 | $default_http_options = ['connect_timeout' => 300.0, 'timeout' => 30.0, 'allow_redirects' => true]; 78 | if (!empty($this->server)) { 79 | $default_http_options['base_uri'] = $this->server; 80 | } 81 | 82 | $this->guzzleClient = new \GuzzleHttp\Client(array_merge($default_http_options, $http_options)); 83 | } 84 | 85 | public function initialize($config) 86 | { 87 | isset($config['server']) AND $this->server = $config['server']; 88 | isset($config['auth']) AND $this->auth = $config['auth']; 89 | isset($config['user']) AND $this->user = $config['user']; 90 | isset($config['pass']) AND $this->pass = $config['pass']; 91 | isset($config['debug_mode']) AND $this->debug_mode = $config['debug_mode']; 92 | 93 | if (substr($this->server, 0, 5) === 'https') { 94 | $this->is_https = true; 95 | isset($config['ca_info']) AND $this->ca_info = $config['ca_info']; 96 | } 97 | } 98 | 99 | public function get($uri, $params = array(), $format = null) 100 | { 101 | return $this->call('get', $uri, $params, $format); 102 | } 103 | 104 | public function post($uri, $params = array(), $format = null) 105 | { 106 | return $this->call('post', $uri, $params, $format); 107 | } 108 | 109 | public function delete($uri, $params = array(), $format = null) 110 | { 111 | return $this->call('delete', $uri, $params, $format); 112 | } 113 | 114 | protected function call($method, $uri, $params = array(), $format = null) 115 | { 116 | if ($format !== null) { 117 | $this->format($format); 118 | } 119 | 120 | $options = []; 121 | 122 | if ($this->mime_type !== null) { 123 | $options['headers'] = array_merge($this->headers, ['Accept' => $this->mime_type]); 124 | } 125 | 126 | if ($this->is_https === true) { 127 | if (empty($this->ca_info)) { 128 | $options['verify'] = false; 129 | } 130 | else { 131 | $options['verify'] = $this->ca_info; 132 | } 133 | } 134 | 135 | // If authentication is enabled use it 136 | if ($this->auth != '' && $this->user != '') { 137 | $options['auth'] = [$this->user, $this->pass]; 138 | } 139 | 140 | // If we have an oauth token, push to guzzle client 141 | # https://stackoverflow.com/questions/38029422/php-guzzle-with-basic-auth-and-bearer-token 142 | if (!empty($this->headers['Authorization'])) { 143 | $options['headers']['Authorization'] = $this->headers['Authorization']; 144 | } 145 | 146 | if ($this->debug_mode === true) { 147 | $options['debug'] = true; 148 | } 149 | 150 | // We still want the response even if there is an error code over 400 151 | $options['http_errors'] = false; 152 | 153 | // Call the correct method with parameters 154 | if (!empty($params)) { 155 | if (strtoupper($method) == 'POST') { 156 | $options['multipart'] = []; 157 | foreach (self::to_1_level_array($params) as $name => $value) { 158 | $options['multipart'][] = ['name' => $name, 'contents' => $value]; 159 | } 160 | } else { 161 | $options['query'] = $params; 162 | } 163 | } 164 | 165 | // Execute and return the response from the REST server 166 | $response = $this->guzzleClient->{$method}($uri, $options); 167 | $this->statusCode = $response->getStatusCode(); 168 | 169 | // Format and return 170 | $contentType = $response->hasHeader('Content-Type') ? current($response->getHeader('Content-Type')) : ''; 171 | return $this->formatResponse((string)$response->getBody(), $contentType); 172 | } 173 | 174 | public function setApiKey($key, $name = 'X-API-KEY') 175 | { 176 | $this->setHeader($name, $key); 177 | } 178 | 179 | public function acceptLanguage($lang) 180 | { 181 | if (is_array($lang)) { 182 | $lang = implode(', ', $lang); 183 | } 184 | 185 | $this->setHeader('Accept-Language', $lang); 186 | } 187 | 188 | public function setHeader($name, $content = null) 189 | { 190 | $this->headers[$name] = $content; 191 | } 192 | 193 | public function enableDebugMode() 194 | { 195 | $this->debug_mode = true; 196 | } 197 | 198 | public function disableDebugMode() 199 | { 200 | $this->debug_mode = false; 201 | } 202 | 203 | public function disableCertificateCheck() { 204 | $this->ca_info = null; 205 | } 206 | 207 | // If a type is passed in that is not supported, use it as a mime type 208 | public function format($format) 209 | { 210 | if (array_key_exists($format, $this->supported_formats)) { 211 | $this->format = $format; 212 | $this->mime_type = $this->supported_formats[$format]; 213 | } 214 | else { 215 | $this->mime_type = $format; 216 | } 217 | } 218 | 219 | // Return HTTP status code 220 | public function getStatus() 221 | { 222 | return $this->statusCode; 223 | } 224 | 225 | protected function formatResponse($response, $contentType) 226 | { 227 | // It is a supported format, so just run its formatting method 228 | if (array_key_exists($this->format, $this->supported_formats)) { 229 | return $this->{'_'.$this->format}($response); 230 | } 231 | 232 | if (array_key_exists($contentType, $this->auto_detect_formats)) { 233 | return $this->{'_'.$this->auto_detect_formats[$contentType]}($response); 234 | } 235 | 236 | return $response; 237 | } 238 | 239 | 240 | // Format XML for output 241 | protected function _xml($string) 242 | { 243 | return $string ? (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : array(); 244 | } 245 | 246 | // Format HTML for output 247 | // This function is DODGY! Not perfect CSV support but works with my REST_Controller 248 | protected function _csv($string) 249 | { 250 | $data = array(); 251 | 252 | // Splits 253 | $rows = explode("\n", trim($string)); 254 | $headings = explode(',', array_shift($rows)); 255 | foreach ($rows as $row) { 256 | // The substr removes " from start and end 257 | $data_fields = explode('","', trim(substr($row, 1, -1))); 258 | 259 | if (count($data_fields) === count($headings)) { 260 | $data[] = array_combine($headings, $data_fields); 261 | } 262 | 263 | } 264 | 265 | return $data; 266 | } 267 | 268 | // Encode as JSON 269 | protected function _json($string) 270 | { 271 | return json_decode(trim($string)); 272 | } 273 | 274 | // Encode as Serialized array 275 | protected function _serialize($string) 276 | { 277 | return unserialize(trim($string)); 278 | } 279 | 280 | // Encode raw PHP 281 | protected function _php($string) 282 | { 283 | $string = trim($string); 284 | $populated = array(); 285 | eval("\$populated = \"$string\";"); 286 | return $populated; 287 | } 288 | 289 | /** 290 | * Convert nested array to 1 level array 291 | * 292 | * @param array $array 293 | * @param string $prefix 294 | * @return array 295 | */ 296 | public static function to_1_level_array($array, $prefix = null) 297 | { 298 | $return = array(); 299 | 300 | foreach ($array as $key => $value) { 301 | $name = $prefix ? "{$prefix}[{$key}]" : $key; 302 | 303 | if (is_array($value) || is_object($value)) { 304 | $return += self::to_1_level_array($value, $name); 305 | } 306 | else { 307 | $return[$name] = $value; 308 | } 309 | } 310 | 311 | return $return; 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠ This SDK has been deprecated ⚠ 2 | 3 | This SDK is now deprecated and will no longer receive feature updates or bug fixes. Security fixes will still be applied as needed. 4 | 5 | The new `dropbox/sign` SDK can be found at [hellosign/dropbox-sign-php](https://github.com/hellosign/dropbox-sign-php)! 6 | 7 | The new SDK and this legacy SDK are _not_ backwards-compatible! 8 | 9 | Please [see here for a comprehensive migration guide](https://developers.hellosign.com/docs/sdks/php/migration-guide/). 10 | 11 | ---- 12 | 13 | ## HelloSign PHP SDK 14 | [![Build Status](https://travis-ci.org/hellosign/hellosign-php-sdk.svg?branch=v3)](https://travis-ci.org/hellosign/hellosign-php-sdk) 15 | 16 | This is the official PHP SDK for HelloSign's API. [View API Documentation and Examples.](https://app.hellosign.com/api/documentation) 17 | 18 | ## Installation 19 | 20 | ### Requirements 21 | 22 | The latest version of the SDK requires PHP version 8.0 or higher. 23 | For PHP 7.x use SDK version 3.7.* 24 | 25 | You can import this SDK into your library two ways, either through including the base HelloSign.php file into your 26 | project or using [Composer](https://getcomposer.org/doc/00-intro.md). 27 | 28 | To use Composer: 29 | 30 | - First, install Composer if you don't have it already 31 | 32 | ```shell 33 | curl -sS https://getcomposer.org/installer | php 34 | ``` 35 | 36 | - Create a `composer.json` file and add the following: 37 | 38 | ```json 39 | { 40 | "require": { 41 | "hellosign/hellosign-php-sdk": "^3.0" 42 | } 43 | } 44 | ``` 45 | 46 | - Install `hellosign-php-sdk` package via Composer 47 | 48 | ```shell 49 | php composer.phar install 50 | ``` 51 | 52 | - Include the library in your script 53 | 54 | ```php 55 | require_once 'vendor/autoload.php'; 56 | ``` 57 | - See below for how to configure your Client class. 58 | 59 | ## Configuration 60 | 61 | All HelloSign API requests can be made using the `HelloSign\Client` class. This class must be initialized with your authentication details such as an API key (preferred), email/password combo, or OAuth credentials. 62 | 63 | ### API key Config 64 | ```php 65 | $client = new HelloSign\Client($apikey); 66 | ``` 67 | 68 | ### Email/Password Config 69 | 70 | ```php 71 | $client = new HelloSign\Client($email_address, $password); 72 | ``` 73 | 74 | ### Oauth Config 75 | 76 | ```php 77 | $client = new HelloSign\Client($oauth_token); //instance of HelloSign\OAuthToken 78 | ``` 79 | 80 | Your app users are almost ready to start signing! 81 | See below for the most common use cases for this wrapper. 82 | 83 | ## Usage 84 | 85 | You can test your authentication by calling 86 | 87 | ```php 88 | $account = $client->getAccount(); 89 | ``` 90 | 91 | ### Retrieving fields returned from the API 92 | 93 | Using magic methods 94 | 95 | ```php 96 | $signature_request->title; 97 | ``` 98 | 99 | Or if you want to get all attributes in an array 100 | 101 | ```php 102 | $signature_request->toArray(); 103 | ``` 104 | 105 | ### Creating a Signature Request 106 | 107 | ```php 108 | $request = new HelloSign\SignatureRequest; 109 | $request->enableTestMode(); 110 | $request->setTitle('NDA with Acme Co.'); 111 | $request->setSubject('The NDA we talked about'); 112 | $request->setMessage('Please sign this NDA and let\'s discuss.'); 113 | $request->addSigner('jack@example.com', 'Jack'); 114 | $request->addSigner('jill@example.com', 'Jill'); 115 | $request->addCC('lawyer@example.com'); 116 | $request->addFile('nda.pdf'); //Adding file from local 117 | 118 | $response = $client->sendSignatureRequest($request); 119 | ``` 120 | 121 | To specify a URL to a remote file instead use: 122 | 123 | ```php 124 | $request->addFileURL('PUBLIC_URL_TO_YOUR_FILE'); 125 | ``` 126 | 127 | If you are using Text Tags in your document, you can enable and configure them through the respective methods: 128 | 129 | ```php 130 | $request->setUseTextTags(true); 131 | $request->setHideTextTags(true); 132 | ``` 133 | 134 | Or if you want to set Form Fields per Document: 135 | 136 | ```php 137 | $request->setFormFieldsPerDocument( 138 | array( 139 | array( //document 1 140 | array( //field 1 141 | "api_id"=> $random_prefix . "_1", 142 | "name"=> "", 143 | "type"=> "text", 144 | "x"=> 112, 145 | "y"=> 328, 146 | "width"=> 100, 147 | "height"=> 16, 148 | "required"=> true, 149 | "signer"=> 0 150 | ), 151 | array( //field 2 152 | "api_id"=> $random_prefix . "_2", 153 | "name"=> "", 154 | "type"=> "signature", 155 | "x"=> 530, 156 | "y"=> 415, 157 | "width"=> 150, 158 | "height"=> 30, 159 | "required"=> true, 160 | "signer"=> 1 161 | ), 162 | ), 163 | ) 164 | ); 165 | ``` 166 | 167 | ### Retrieving a User's Templates 168 | 169 | The HelloSign API provides paged lists for user templates and signature requests. These lists are represented as objects that can be iterated upon. 170 | 171 | ```php 172 | $templates = $client->getTemplates($page_number); 173 | foreach ($templates as $template) { 174 | echo $template->getTitle() . "\n"; 175 | } 176 | ``` 177 | 178 | ### Creating a Signature Request from a Template 179 | 180 | ```php 181 | $request = new HelloSign\TemplateSignatureRequest; 182 | $request->enableTestMode(); 183 | $request->setTemplateId($template->getId()); 184 | $request->setSubject('Purchase Order'); 185 | $request->setMessage('Glad we could come to an agreement.'); 186 | $request->setSigner('Client', 'george@example.com', 'George'); 187 | $request->setCC('Accounting', 'accounting@example.com'); 188 | $request->setCustomFieldValue('Cost', '$20,000'); 189 | 190 | $response = $client->sendTemplateSignatureRequest($request); 191 | ``` 192 | 193 | ### Checking the Status of a Signature Request 194 | 195 | ```php 196 | $response = $client->getSignatureRequest($signature_request_id); 197 | if ($response->isComplete()) { 198 | echo 'All signers have signed this request.'; 199 | } else { 200 | foreach ($response->getSignatures() as $signature) { 201 | echo $signature->getStatusCode() . "\n"; 202 | } 203 | } 204 | ``` 205 | 206 | ### Creating an Embedded Signature Request to use for Embedded Signing 207 | Refer to the (Embedded Signing Walkthrough)[https://app.hellosign.com/api/embeddedSigningWalkthrough] for more details. 208 | 209 | ```php 210 | // Create the SignatureRequest or TemplateSignatureRequest object 211 | $request = ... 212 | 213 | // Turn it into an embedded request 214 | $embedded_request = new HelloSign\EmbeddedSignatureRequest($request, $client_id); 215 | 216 | // Send it to HelloSign 217 | $response = $client->createEmbeddedSignatureRequest($embedded_request); 218 | 219 | // Grab the signature ID for the signature page that will be embedded in the 220 | // page (for the demo, we'll just use the first one) 221 | $signatures = $response->getSignatures(); 222 | $signature_id = $signatures[0]->getId(); 223 | 224 | // Retrieve the URL to sign the document 225 | $response = $client->getEmbeddedSignUrl($signature_id); 226 | 227 | // Store it to use with the embedded.js HelloSign.open() call 228 | $sign_url = $response->getSignUrl(); 229 | ``` 230 | 231 | 232 | ### Creating an Embedded Template draft 233 | 234 | ```php 235 | $template = new HelloSign\Template(); 236 | $template->enableTestMode(); 237 | $template->setClientId($client_id); 238 | $template->addFile('nda.pdf'); 239 | $template->setTitle('Test Title'); 240 | $template->setSubject('Test Subject'); 241 | $template->setMessage('Test Message'); 242 | $template->addSignerRole('Test Role'); 243 | $template->addMetadata('custom_id', '1234'); 244 | 245 | $response = $client->createEmbeddedDraft($template); 246 | ``` 247 | 248 | ### Creating an Unclaimed Draft to use for Embedded Requesting 249 | 250 | ```php 251 | 252 | $draft = new HelloSign\UnclaimedDraft($request, $client_id); 253 | // optionally change it to a self-signing draft with $draft->setType("send_document"); 254 | $response = $client->createUnclaimedDraft($draft); 255 | 256 | // Store it to use with the embedded.js HelloSign.open() call 257 | $sign_url = $response->getClaimUrl(); 258 | ``` 259 | 260 | 261 | ### Enabling OAuth 262 | 263 | ```php 264 | // If the account does not exist 265 | if !($client->isAccountValid($email)) { 266 | // Create new account 267 | $account = $client->createAccount( 268 | new HelloSign\Account($email), 269 | $client_id, 270 | $client_secret 271 | ); 272 | 273 | // Get OAuth token 274 | $token = $account->getOAuthData(); 275 | } else { 276 | // Create the OAuthTokenRequest object 277 | $oauth_request = new HelloSign\OAuthTokenRequest(array( 278 | 'code' => $code, 279 | 'state' => $state, 280 | 'client_id' => $client_id, 281 | 'client_secret' => $client_secret 282 | )); 283 | 284 | // Request OAuth token for the first time 285 | $token = $client->requestOAuthToken($oauth_request); 286 | } 287 | 288 | // Export token to array, store it to use later 289 | $hellosign_oauth = $token->toArray(); 290 | 291 | // Populate token from array 292 | $token = new HelloSign\OAuthToken($hellosign_oauth); 293 | 294 | // Refresh token if it expired 295 | $client->refreshOAuthToken($token); 296 | 297 | // Provide the user's OAuth access token to the client 298 | $client = new HelloSign\Client($token); 299 | ``` 300 | 301 | ## Displaying warnings 302 | 303 | Any warnings returned from the API can be accessed via the returned object / list via the getWarnings method: 304 | 305 | ````php 306 | $response = $this->client->getSignatureRequests(); 307 | print_r($response->getWarnings()); 308 | ```` 309 | 310 | ## Testing 311 | 312 | This project contains PHPUnit tests that check the SDK code and can also be referenced for examples. Most are functional and integrated tests that walk through real user scenarios. 313 | 314 | In order to pass the unit tests, you will need: 315 | 316 | 1. The API Key for a confirmed HelloSign account 317 | 2. The client ID and secret key from a confirmed HelloSign API App 318 | 3. A HelloSign subscription (to create a team) 319 | 4. A HelloSign API subscription (to access paid API endpoints) 320 | 5. A template with 1 signer role named 'Signer' 321 | 6. A Team with 1 additional team member 322 | 323 | *** WARNING: these tests will add and remove users from your team. Use with caution. 324 | 325 | ### To run the tests 326 | 327 | - Copy file `phpunit.xml.dist` to `phpunit.xml` 328 | - Edit the new file and enter your values for `API_KEY`, `CLIENT_ID`, `CLIENT_SECRET`, `CALLBACK_URL`, `API_URL`, AND `OAUTH_TOKEN_URL` 329 | - Run `./vendor/bin/phpunit` 330 | 331 | ## License 332 | 333 | ``` 334 | The MIT License (MIT) 335 | 336 | Copyright (C) 2014 hellosign.com 337 | 338 | Permission is hereby granted, free of charge, to any person obtaining a copy 339 | of this software and associated documentation files (the "Software"), to deal 340 | in the Software without restriction, including without limitation the rights 341 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 342 | copies of the Software, and to permit persons to whom the Software is 343 | furnished to do so, subject to the following conditions: 344 | 345 | The above copyright notice and this permission notice shall be included in all 346 | copies or substantial portions of the Software. 347 | 348 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 349 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 350 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 351 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 352 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 353 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 354 | SOFTWARE. 355 | ``` 356 | --------------------------------------------------------------------------------