Why has DocuSign EventNotification stopped working? - php

In 2017 I integrated DocuSign with a website using the Official DocuSign PHP Client (https://github.com/docusign/docusign-php-client).
I took the example code and modified it so that it registered an EventNotification which would call back to the website once the signing process is complete. Code below.
require_once('docusign-php-client/autoload.php');
$username = "XXXXX";
$password = "XXXXX";
$integrator_key = "XXXXX";
// change to production (www.docusign.net) before going live
$host = "https://www.docusign.net/restapi";
// create configuration object and configure custom auth header
$config = new DocuSign\eSign\Configuration();
$config->setHost($host);
$config->addDefaultHeader("X-DocuSign-Authentication", "{\"Username\":\"" . $username . "\",\"Password\":\"" . $password . "\",\"IntegratorKey\":\"" . $integrator_key . "\"}");
// instantiate a new docusign api client
$apiClient = new DocuSign\eSign\Client\ApiClient($config);
$accountId = null;
try {
//*** STEP 1 - Login API: get first Account ID and baseURL
$authenticationApi = new DocuSign\eSign\Api\AuthenticationApi($apiClient);
$options = new \DocuSign\eSign\Api\AuthenticationApi\LoginOptions();
$loginInformation = $authenticationApi->login($options);
if (isset($loginInformation) && count($loginInformation) > 0) {
$loginAccount = $loginInformation->getLoginAccounts()[0];
$host = $loginAccount->getBaseUrl();
$host = explode("/v2", $host);
$host = $host[0];
// UPDATE configuration object
$config->setHost($host);
// instantiate a NEW docusign api client (that has the correct baseUrl/host)
$apiClient = new DocuSign\eSign\Client\ApiClient($config);
if (isset($loginInformation)) {
$accountId = $loginAccount->getAccountId();
if (!empty($accountId)) {
//*** STEP 2 - Signature Request from a Template
// create envelope call is available in the EnvelopesApi
$envelopeApi = new DocuSign\eSign\Api\EnvelopesApi($apiClient);
// assign recipient to template role by setting name, email, and role name. Note that the
// template role name must match the placeholder role name saved in your account template.
$templateRole = new DocuSign\eSign\Model\TemplateRole();
$templateRole->setEmail($vars['client_email']['value']);
//$templateRole->setEmail("robbielewis#me.com");
$templateRole->setName($vars['client_name']['value']);
$templateRole->setRoleName("Signee");
// pre-populate text tabs
$tabFields = array();
foreach ($vars as $var) {
$tabField = new \DocuSign\eSign\Model\Text();
$tabField->setTabLabel($var['label']);
$tabField->setValue($var['value']);
$tabFields[] = $tabField;
}
$tabs = new DocuSign\eSign\Model\Tabs();
$tabs->setTextTabs($tabFields);
$templateRole->setTabs($tabs);
// create event notification webhook
$envelope_events = [
(new \DocuSign\eSign\Model\EnvelopeEvent())->setEnvelopeEventStatusCode("sent"),
(new \DocuSign\eSign\Model\EnvelopeEvent())->setEnvelopeEventStatusCode("delivered"),
(new \DocuSign\eSign\Model\EnvelopeEvent())->setEnvelopeEventStatusCode("completed"),
(new \DocuSign\eSign\Model\EnvelopeEvent())->setEnvelopeEventStatusCode("declined"),
(new \DocuSign\eSign\Model\EnvelopeEvent())->setEnvelopeEventStatusCode("voided"),
(new \DocuSign\eSign\Model\EnvelopeEvent())->setEnvelopeEventStatusCode("sent"),
(new \DocuSign\eSign\Model\EnvelopeEvent())->setEnvelopeEventStatusCode("sent")
];
$recipient_events = [
(new \DocuSign\eSign\Model\RecipientEvent())->setRecipientEventStatusCode("Sent"),
(new \DocuSign\eSign\Model\RecipientEvent())->setRecipientEventStatusCode("Delivered"),
(new \DocuSign\eSign\Model\RecipientEvent())->setRecipientEventStatusCode("Completed"),
(new \DocuSign\eSign\Model\RecipientEvent())->setRecipientEventStatusCode("Declined"),
(new \DocuSign\eSign\Model\RecipientEvent())->setRecipientEventStatusCode("AuthenticationFailed"),
(new \DocuSign\eSign\Model\RecipientEvent())->setRecipientEventStatusCode("AutoResponded")
];
$event_notification = new \DocuSign\eSign\Model\EventNotification();
$event_notification->setUrl("https://www.XXXXX.com/wp-content/plugins/XXXXX/XXXXX.php?action=docusign-callback");
$event_notification->setLoggingEnabled("true");
$event_notification->setRequireAcknowledgment("true");
$event_notification->setUseSoapInterface("false");
$event_notification->setIncludeCertificateWithSoap("false");
$event_notification->setSignMessageWithX509Cert("false");
$event_notification->setIncludeDocuments("true");
$event_notification->setIncludeEnvelopeVoidReason("true");
$event_notification->setIncludeTimeZone("true");
$event_notification->setIncludeSenderAccountAsCustomField("true");
$event_notification->setIncludeDocumentFields("true");
$event_notification->setIncludeCertificateOfCompletion("true");
$event_notification->setEnvelopeEvents($envelope_events);
$event_notification->setRecipientEvents($recipient_events);
// instantiate a new envelope object and configure settings
$envelop_definition = new DocuSign\eSign\Model\EnvelopeDefinition();
$envelop_definition->setEmailSubject("Please review and sign your booking contract");
$envelop_definition->setTemplateId("XXXXX");
$envelop_definition->setTemplateRoles(array($templateRole));
$envelop_definition->setEventNotification($event_notification);
// set envelope status to "sent" to immediately send the signature request
$envelop_definition->setStatus("sent");
// optional envelope parameters
$options = new \DocuSign\eSign\Api\EnvelopesApi\CreateEnvelopeOptions();
$options->setCdseMode(null);
$options->setMergeRolesOnDraft(null);
// create and send the envelope (aka signature request)
$envelop_summary = $envelopeApi->createEnvelope($accountId, $envelop_definition, $options);
if (!empty($envelop_summary)) {
echo "Contract sent at " . date('d/m/Y H:i:s') .' to '.$templateRole->getEmail();
}
}
}
}
} catch (DocuSign\eSign\ApiException $ex) {
echo "Exception: " . $ex->getMessage() . "\n";
file_put_contents('XXXXX.log', "\n\n" . date('d/m/Y H:i:s') . ' - Docusign API error ' . $ex->getMessage(), FILE_APPEND);
wp_mail('XXXXX', 'Docusign API error', date('d/m/Y H:i:s') . ' - Docusign API error: ' . $ex->getMessage());
}
This worked fine for a couple of years until recently we stopped getting the EventNotification request to our server from Docusign when the envelope is signed. The envelope is still created successfully and sent to the recipient.
Things I have tried to resolve the issue:
Created many test envelopes using my script and signed them
Monitored the access logs on the server for any http requests following the signing of an envelope that was created by the script, nothing received
Updated the DocuSign PHP Client to the latest version
Enabled logging in DocuSign and checked that the EventNotification request is being received by DocuSign when the envelope is created, see here: https://pastebin.com/xbS338Hx
Contacted my host to ensure that there is no hardware firewall that could be preventing the server receiving HTTP requests from DocuSign
Turned off Cloudflare DNS proxy to make sure that it isn't blocking the request to the server
Contacted DocuSign Support who said to post here as they cannot provide assistance with code issues.
What else can I try to pinpoint the cause of this issue?

Using DocuSign Connect Failures screen in DocuSign Admin I was able to see failed connections in the list.
https://www.XXXXX.com/wp-content/plugins/sXXXXX/XXXXX.php?action=docusign-callback :: Error - The remote server returned an error: (403) Forbidden.
It turned out to be iThemes Security Plugin for Wordpress denying the requests. I disabled the plugin and republished the most recent failed request in DocuSign Admin. The request came through successfully.

Related

Expected OAuth 2 access token, login cookie or other valid authentication credential

I have followed this guide Quick start but it did not work. I am using google-ads-api PHP library can be found here. I did follow it generated Refresh token. Client ID and Secret Id of my cloud project which which is of type WEB, Published and has access to google ads api.
My developer token is approved. after following all this I made call to google-ads-api and it said bad request after that it returned user authentication failed, request failed. After that
I followed this guide to generate access token and placed it in my ini. It still shows the bad request failed and the message Details: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
Full error message is here
[2021-08-27T11:14:41.690565+02:00] google-ads.WARNING: Request made: Host: "googleads.googleapis.com", Method: "/google.ads.googleads.v8.services.KeywordPlanIdeaService/GenerateKeywordIdeas", CustomerId: 4278642742, RequestId: "Gl-RPU4lhQ9V1-u38_GVXA", IsFault: 1, FaultMessage: "["Authentication of the request failed."]"
[2021-08-27T11:14:41.719424+02:00] google-ads.NOTICE: Request
Method Name: /google.ads.googleads.v8.services.KeywordPlanIdeaService/GenerateKeywordIdeas
Host: googleads.googleapis.com
Headers: {
"x-goog-api-client": "gl-php/7.4.21 gapic/ gax/1.7.1 grpc/1.39.0",
"x-goog-request-params": "customer_id=4278642742",
"developer-token": "REDACTED"
}
Request:
{"customerId":"4278642742","language":"languageConstants/1000","geoTargetConstants":["geoTargetConstants/1008021","geoTargetConstants/1014312"],"keywordPlanNetwork":"GOOGLE_SEARCH_AND_PARTNERS","keywordSeed":{"keywords":["Social media management is the process of managing your online presence on social media platforms like Facebook, Instagram, and Twitter by creating, publishing, and analyzing content you post. Managing social media also includes engaging and interacting with social media users. You can use tools, services, and social media managers to oversee your social media management.","No matter how you approach social media management, whether with the help of an agency or a toolset, it’s essential to understand more than social media management’s definition. You want to know what it includes, as well as how to make it a success for your company."]}}
Response
Headers: {
"request-id": "Gl-RPU4lhQ9V1-u38_GVXA",
"date": "Fri, 27 Aug 2021 09:14:47 GMT",
"alt-svc": "h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43""
}
Fault
Status code: 16
Details: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
Failure: {"errors":[{"errorCode":{"authenticationError":"AUTHENTICATION_ERROR"},"message":"Authentication of the request failed."}],"requestId":"Gl-RPU4lhQ9V1-u38_GVXA"}
Request with ID 'Gl-RPU4lhQ9V1-u38_GVXA' has failed.
Google Ads failure details:
authentication_error: Authentication of the request failed.
The code I am running
public static function main()
{
if (!class_exists(Server::class)) {
echo 'Please install "react/http" package to be able to run this example';
exit(1);
}
$loop = Factory::create();
// Creates a socket for localhost with random port.
$socket = new \React\Socket\Server(0, $loop);
print 'Enter your OAuth2 client ID here: ';
$clientId = trim(fgets(STDIN));
print 'Enter your OAuth2 client secret here: ';
$clientSecret = trim(fgets(STDIN));
$redirectUrl = str_replace('tcp:', 'http:', $socket->getAddress());
$oauth2 = new OAuth2(
[
'clientId' => $clientId,
'clientSecret' => $clientSecret,
'authorizationUri' => self::AUTHORIZATION_URI,
'redirectUri' => $redirectUrl . self::OAUTH2_CALLBACK_PATH,
'tokenCredentialUri' => CredentialsLoader::TOKEN_CREDENTIAL_URI,
'scope' => self::SCOPE,
// Create a 'state' token to prevent request forgery. See
// https://developers.google.com/identity/protocols/OpenIDConnect#createxsrftoken
// for details.
'state' => sha1(openssl_random_pseudo_bytes(1024))
]
);
$authToken = null;
$server = new Server(
$loop,
function (ServerRequestInterface $request) use ($oauth2, $loop, &$authToken) {
// Stops the server after tokens are retrieved.
if (!is_null($authToken)) {
$loop->stop();
}
// Check if the requested path is the one set as the redirect URI.
if (
$request->getUri()->getPath()
!== parse_url($oauth2->getRedirectUri(), PHP_URL_PATH)
) {
return new Response(
404,
['Content-Type' => 'text/plain'],
'Page not found'
);
}
// Exit if the state is invalid to prevent request forgery.
$state = $request->getQueryParams()['state'];
if (empty($state) || ($state !== $oauth2->getState())) {
throw new UnexpectedValueException(
"The state is empty or doesn't match expected one." . PHP_EOL
);
};
// Set the authorization code and fetch refresh and access tokens.
$code = $request->getQueryParams()['code'];
$oauth2->setCode($code);
$authToken = $oauth2->fetchAuthToken();
$refreshToken = $authToken['refresh_token'];
print 'Your refresh token is: ' . $refreshToken . PHP_EOL;
$propertiesToCopy = '[GOOGLE_ADS]' . PHP_EOL;
$propertiesToCopy .= 'developerToken = "INSERT_DEVELOPER_TOKEN_HERE"' . PHP_EOL;
$propertiesToCopy .= <<<EOD
; Required for manager accounts only: Specify the login customer ID used to authenticate API calls.
; This will be the customer ID of the authenticated manager account. You can also specify this later
; in code if your application uses multiple manager account + OAuth pairs.
; loginCustomerId = "INSERT_LOGIN_CUSTOMER_ID_HERE"
EOD;
$propertiesToCopy .= PHP_EOL . '[OAUTH2]' . PHP_EOL;
$propertiesToCopy .= "clientId = \"{$oauth2->getClientId()}\"" . PHP_EOL;
$propertiesToCopy .= "clientSecret = \"{$oauth2->getClientSecret()}\"" . PHP_EOL;
$propertiesToCopy .= "refreshToken = \"$refreshToken\"" . PHP_EOL;
print 'Copy the text below into a file named "google_ads_php.ini" in your home '
. 'directory, and replace "INSERT_DEVELOPER_TOKEN_HERE" with your developer '
. 'token:' . PHP_EOL;
print PHP_EOL . $propertiesToCopy;
return new Response(
200,
['Content-Type' => 'text/plain'],
'Your refresh token has been fetched. Check the console output for '
. 'further instructions.'
);
}
);
$server->listen($socket);
printf(
'Log into the Google account you use for Google Ads and visit the following URL '
. 'in your web browser: %1$s%2$s%1$s%1$s',
PHP_EOL,
$oauth2->buildFullAuthorizationUri(['access_type' => 'offline'])
);
$loop->run();
}
```
Lastly this is my
google_ads_php.ini file
```[GOOGLE_ADS]
; Required AdWords API properties. Details can be found at:
; https://developers.google.com/adwords/api/docs/guides/basic-concepts#soap_and_xml
developerToken = "AIzaSyDpRMH__DIsA6mdCchZKJeFoDwq4l*****"
clientCustomerId = "427-864-*****"
; Optional. Set a friendly application name identifier.
; userAgent = "INSERT_USER_AGENT_HERE"
; Optional additional AdWords API settings.
; endpoint = "https://adwords.google.com/"
; isPartialFailure = false
; Optional setting for utility usage tracking in the user agent in requests.
; Defaults to true.
; includeUtilitiesInUserAgent = true
[ADWORDS_REPORTING]
; Optional reporting settings.
; isSkipReportHeader = false
; isSkipColumnHeader = false
; isSkipReportSummary = false
; isUseRawEnumValues = false
[OAUTH2]
; Required OAuth2 credentials. Uncomment and fill in the values for the
; appropriate flow based on your use case. See the README for guidance:
; https://github.com/googleads/googleads-php-lib/blob/master/README.md#getting-started
; For installed application or web application flow.
[OAUTH2]
clientId = "4016385****-eu0426e2lm7tlin2qa0kou4qfk80****.apps.googleusercontent.com"
clientSecret = "DPBf9pMtBHWZSk2hB*******"
refreshToken = "1//03nOACQc-vMyhCgYIARAAGAMSNwF-L9Irj8qSRZlvPdzR4n6_EbfHMLtt_tZNxJpOmZFJPLG7EAuaI-hYPB1GDQ-************"
access_token= "ya29.a0ARrdaM-eqkzlCJLcqOmVvVujzjaYnuzi3cfUGKrEG3GTlGpaoJ5Z3feK5eL_l7VrwnYYPFRqjqlDQ1eQO9FA2M8VsKbwXAI77NUsz7QxHCTo65YJHSQ5QzDOMu8Xkrzcp2Kg8KYKotTHVJ2Kiw**********"
; For service account flow.
; jsonKeyFilePath = "INSERT_ABSOLUTE_PATH_TO_OAUTH2_JSON_KEY_FILE_HERE"
; scopes = "https://www.googleapis.com/auth/adwords"
; impersonatedEmail = "INSERT_EMAIL_OF_ACCOUNT_TO_IMPERSONATE_HERE"
[SOAP]
; Optional SOAP settings. See SoapSettingsBuilder.php for more information.
; compressionLevel = <COMPRESSION_LEVEL>
[CONNECTION]
; Optional proxy settings to be used by requests.
; If you don't have username and password, just specify host and port.
; proxy = "protocol://user:pass#host:port"
; Enable transparent HTTP gzip compression for all reporting requests.
; enableReportingGzip = false
[LOGGING]
; Optional logging settings.
; soapLogFilePath = "path/to/your/soap.log"
; soapLogLevel = "INFO"
; reportDownloaderLogFilePath = "path/to/your/report-downloader.log"
; reportDownloaderLogLevel = "INFO"
; batchJobsUtilLogFilePath = "path/to/your/bjutil.log"
; batchJobsUtilLogLevel = "INFO"

Making the DocuSign API transition from sandbox to live

I integrated the PHP API for DocuSign by creating a sandbox account, downloading the php sample and placing the correct credentials in, testing (which worked as expected) and then ran through the live process of 20 transactions, having an enterprise account, etc. All was approved and everything seemed in order according to the admin panel, but making the changes documented in the go live docs just results in a server 500 error. Anyone else experienced this? Maybe some part of the live transition escaped me?
The code below should return login info:
// DocuSign account credentials & Integrator Key
$username = "someemail#somewhere.com"; //or the account string
$password = "xxxxx";
$integrator_key = "abc-123";
$host = "https://demo.docusign.net/restapi";
// create a new DocuSign configuration and assign host and header(s)
$config = new DocuSign\eSign\Configuration();
$config->setHost($host);
$config->addDefaultHeader("X-DocuSign-Authentication", "{\"Username\":\"" . $username . "\",\"Password\":\"" . $password . "\",\"IntegratorKey\":\"" . $integrator_key . "\"}");
/////////////////////////////////////////////////////////////////////////
// STEP 1: Login() API
/////////////////////////////////////////////////////////////////////////
// instantiate a new docusign api client
$apiClient = new DocuSign\eSign\ApiClient($config);
// we will first make the Login() call which exists in the AuthenticationApi...
$authenticationApi = new DocuSign\eSign\Api\AuthenticationApi($apiClient);
// optional login parameters
$options = new \DocuSign\eSign\Api\AuthenticationApi\LoginOptions();
// call the login() API
$loginInformation = $authenticationApi->login($options);
// parse the login results
if(isset($loginInformation) && count($loginInformation) > 0)
{
// note: defaulting to first account found, user might be a
// member of multiple accounts
$loginAccount = $loginInformation->getLoginAccounts()[0];
if(isset($loginInformation))
{
$accountId = $loginAccount->getAccountId();
if(!empty($accountId))
{
echo "Account ID = $accountId\n";
// works as expected
}
}
}
Now, try the same thing using the live URL and credentials as suggested by the go live docs here:
$username = "live-user-account"; <--- live username or email
$password = "xxxxx"; <--- live pw
$integrator_key = "abc-123"; <--- same integrator key
$host = "https://www.docusign.net/restapi/v2/login_information"; <--- live URL
All I get is a server 500 error. I tried with just https://www.docusign.net/restapi and same thing. Server 500 error.
Is there some part of the go live process I overlooked?
Side note - this is attached to an enterprise level account
Ok so get ready for an idiotic ride....
Another stack overflow post answers this here, but I will skip to the actual answer found here
Basically - you have to redefine the $apiClient variable by calling back the api with returned info...
$username = "xxx-xx-xxxx"; <--- your username string or email
$password = "xxxxx"; <--- your password
$integrator_key = "aaa-bbb-ccc"; <--- your integrator key
$host = "https://www.docusign.net/restapi"; <--- LIVE url
// create a new DocuSign configuration and assign host and header(s)
$config = new DocuSign\eSign\Configuration();
$config->setHost($host);
$config->addDefaultHeader("X-DocuSign-Authentication", "{\"Username\":\"" . $username . "\",\"Password\":\"" . $password . "\",\"IntegratorKey\":\"" . $integrator_key . "\"}");
/////////////////////////////////////////////////////////////////////////
// STEP 1: Login() API
/////////////////////////////////////////////////////////////////////////
// instantiate a new docusign api client
$apiClient = new DocuSign\eSign\ApiClient($config);
// we will first make the Login() call which exists in the AuthenticationApi...
$authenticationApi = new DocuSign\eSign\Api\AuthenticationApi($apiClient);
// optional login parameters
$options = new \DocuSign\eSign\Api\AuthenticationApi\LoginOptions();
// call the login() API
$loginInformation = $authenticationApi->login($options);
// parse the login results
if(isset($loginInformation) && count($loginInformation) > 0)
{
// note: defaulting to first account found, user might be a
// member of multiple accounts
$loginAccount = $loginInformation->getLoginAccounts()[0];
if(isset($loginInformation))
{
$accountId = $loginAccount->getAccountId();
// HERE is where we all go wrong
$host = $loginAccount->getBaseUrl();
$host = explode("/v2",$host);
$host = $host[0];
// UPDATE configuration object and then re-call the api client
$config->setHost($host);
$apiClient = new DocuSign\eSign\ApiClient($config);
if(!empty($accountId))
{
//echo "Account ID = $accountId\n";
}
}
}
Then poof, your envelope calls will magically work after that...
This is one FUGLY implementation of a sandbox -> live process. We got some great PHP sample for sandbox but then no connected real world examples of how you have to change things around in the live environment. Yes, we get that live can differ, but just leave this one small piece commented in the php sample and say "when going live, uncomment this" ... Literally nothing else in the code is different other than the login creds.
simple as that.
It kind of makes the sandbox pointless since I now have to re-test everything to make sure this live code is stable.
Thank you support for trying.

Docusign single signer but multiple signatures on document

I am using Docusign php api. I am signing the document by this api . everything is fine . But now i need signatures of recipients at multiple places. I don't know how many times i need the signature , Even i don't know the position where i need the signature tab , I just have a placeholder in the html doc and that is #\b . I have to put signature tab where is #\b
// create a new DocuSign configuration and assign host and header(s)
$config = new DocuSign\eSign\Configuration();
$config->setHost($host);
$config->addDefaultHeader("X-DocuSign-Authentication", "{\"Username\":\"" . $username . "\",\"Password\":\"" . $password . "\",\"IntegratorKey\":\"" . $integrator_key . "\"}");
/////////////////////////////////////////////////////////////////////////
// STEP 1: Login() API
/////////////////////////////////////////////////////////////////////////
// instantiate a new docusign api client
$apiClient = new DocuSign\eSign\ApiClient($config);
// we will first make the Login() call which exists in the AuthenticationApi...
$authenticationApi = new DocuSign\eSign\Api\AuthenticationApi($apiClient);
// optional login parameters
$options = new \DocuSign\eSign\Api\AuthenticationApi\LoginOptions();
// call the login() API
$loginInformation = $authenticationApi->login($options);
// parse the login results
if(isset($loginInformation) && count($loginInformation) > 0)
{
// note: defaulting to first account found, user might be a
// member of multiple accounts
$loginAccount = $loginInformation->getLoginAccounts()[0];
if(isset($loginInformation))
{
$accountId = $loginAccount->getAccountId();
}
}
if(empty($accountId))
{
return false;
}
/////////////////////////////////////////////////////////////////////////
// STEP 2: Create & Send Envelope (aka Signature Request)
/////////////////////////////////////////////////////////////////////////
// set recipient information
// instantiate a new envelopeApi object
$envelopeApi = new DocuSign\eSign\Api\EnvelopesApi($apiClient);
// Add a document to the envelope
$document = new DocuSign\eSign\Model\Document();
$document->setDocumentBase64(base64_encode(file_get_contents($documentFileName)));
$document->setName($documentName);
$document->setDocumentId("1");
// Create a |SignHere| tab somewhere on the document for the recipient to sign
$signHere = new \DocuSign\eSign\Model\SignHere();
$signHere->setXPosition("20");
$signHere->setYPosition("20");
$signHere->setDocumentId("1");
$signHere->setPageNumber("1");
$signHere->setRecipientId("1");
// add the signature tab to the envelope's list of tabs
$tabs = new DocuSign\eSign\Model\Tabs();
$tabs->setSignHereTabs(array($signHere));
// add a signer to the envelope
$signer = new \DocuSign\eSign\Model\Signer();
$signer->setEmail($recipientEmail);
$signer->setName($recipientName);
$signer->setRecipientId("1");
$signer->setClientUserId('12345');
$signer->setTabs($tabs);
// Add a recipient to sign the document
$recipients = new DocuSign\eSign\Model\Recipients();
$recipients->setSigners(array($signer));
$envelop_definition = new DocuSign\eSign\Model\EnvelopeDefinition();
$envelop_definition->setEmailSubject("Please sign this doc");
// set envelope status to "sent" to immediately send the signature request
$envelop_definition->setStatus("sent");
$envelop_definition->setRecipients($recipients);
$envelop_definition->setDocuments(array($document));
// create and send the envelope! (aka signature request)
$envelop_summary = $envelopeApi->createEnvelope($accountId, $envelop_definition, null);
if($envelop_summary)
{
$document=json_decode($envelop_summary);
$envloped=$document->envelopeId;
// get the document url
$ReturnUrl=add_query_arg(array('action'=>'docusign_request','_uid'=>$formprocess,'_envelopid'=>$envloped),get_permalink());
$viewrequest = new DocuSign\eSign\Model\RecipientViewRequest();
$viewrequest->setUserName($recipientName);
$viewrequest->setEmail($recipientEmail);
$viewrequest->setRecipientId(1);
$viewrequest->setClientUserId('12345');
$viewrequest->setAuthenticationMethod('email');
$viewrequest->setReturnUrl($ReturnUrl);
$envelopview=$envelopeApi->createRecipientView($accountId,$document->envelopeId,$viewrequest);
$redirecturl=$envelopview->getUrl();
wp_redirect($redirecturl);
// get the doc url
}
You should be using Docusign Anchor tabs for this purpose
https://github.com/docusign/docusign-soap-sdk/wiki/Code-Walkthrough-_-Anchor-Tabs
Anchor Tabs are a solution for this. Since an Anchor Tab works by matching a string that is present in the document’s text, we would just need to examine the document and see if there is a suitable bit of text. Many contracts have something like “Sign Here” printed on the document, so we could use that as the anchor text, and then DocuSign would place a Signature Tab on the document everywhere it finds the text “Sign Here”.
// Create a |SignHere| tab somewhere on the document for the recipient to sign
$signHere = new \DocuSign\eSign\Model\SignHere();
$signHere->setDocumentId("1");
$signHere->setPageNumber("1");
$signHere->setRecipientId("1");
$signHere->AnchorTabItem->AnchorTabString = "#\b";
$signHere->AnchorTabItem->Unit = "Pixels";
$signHere->AnchorTabItem->XOffset = 50;
$signHere->AnchorTabItem->YOffset = 100;
$signHere->AnchorTabItem->IgnoreIfNotPresent = true;

Google drive service account and "Unauthorized client or scope in request"

I have 3 service accounts that are using the drive sdk.
1, works, 2 do not.
The error that comes back is "Error refreshing the OAuth2 token, message: '{ "error" : "unauthorized_client", "error_description" : "Unauthorized client or scope in request." }'"
All 3 accounts are registered in the developer console.
All 3 are authorised for "Managed Client API access" within Google Apps Console.
All 3 have the scope "https://www.googleapis.com/auth/drive.readonly".
All 3 in drive, has a specific folder for it shared for "view only".
I am using PHP and I pass one parameter to the page which is called "type" and reflects what the purpose of the account is for, 1 for public, 1 for member and 1 for admin.
For example
http://www.somehost.com/oauth_client.php?type=googledrive_admin
The p12 certificate and user values are stored on the server. All "ini" files have the same structure of values, client_id, client_email, scope and query filter. In all cases the only item that changes between the files is the client_id and client_email.
My code is as follows:
<?php
include (__DIR__ . "/google-api-php-client/autoload.php");
google_api_php_client_autoload("Google_Auth_AssertionCredentials");
google_api_php_client_autoload("Google_Client");
google_api_php_client_autoload("Google_Service_Drive");
google_api_php_client_autoload("Google_Service_OAuth2");
$type = $_GET['type'];
$path = __DIR__ . "/secure/";
$certificate = $path . $type . ".p12";
$ini_path = $path . $type . ".ini";
$ini = parse_ini_file($ini_path);
$service_scope = $ini['scope'];
$service_account_id = $ini['id'];
$service_account_email = $ini['email'];
$service_query = $ini['q'];
$service_account_key = file_get_contents($certificate);
$credentials = new Google_Auth_AssertionCredentials(
$service_account_email,
array($service_scope),
$service_account_key
);
$credentials -> sub = $service_account_email;
$google_client = new Google_Client();
$google_client -> setAssertionCredentials($credentials);
if ($google_client -> getAuth() -> isAccessTokenExpired()) {
$google_client -> getAuth() -> refreshTokenWithAssertion(); **//FAILS HERE**
}
$drive = new Google_Service_Drive($google_client);
$result = array();
$pageToken = NULL;
do {
try {
$parameters = array();
if ($pageToken) {
$parameters['pageToken'] = $pageToken;
}
$parameters['q'] = $service_query;
$files = $drive -> files -> listFiles($parameters);
$result = array_merge($result, $files -> getItems());
$pageToken = $files -> getNextPageToken();
} catch (Exception $e) {
print "An error occurred: " . $e -> getMessage();
$pageToken = NULL;
}
} while ($pageToken);
echo json_encode($result) . "\n";
?>
Each ini file is structured as follows
id="35{code}.apps.googleusercontent.com"
email="35{code}#developer.gserviceaccount.com"
scope="https://www.googleapis.com/auth/drive.readonly"
q="mimeType != 'application/vnd.google-apps.folder'"
What I just cannot work out is why this works for one service account and not the others when I have gone through the same process for all of them. Any ideas and help appreciated.
Thanks for this post and your comment about "Resolved by removing the line $credentials -> sub = $service_account_email;"
I am facing a similar issue here. Apparently, $credentials -> sub = $service_account_email is only accepted for the first/primary service account created in the Google Developers Console. Besides that, it will also produce unexpected errors with certain OAuth2 scopes (as what I encountered with Fusion Tables).
Hence, here is the general advise:
DO NOT include $credentials -> sub = $service_account_email unnecessarily. Only do this when you are trying to impersonating a
difference user (with the condition that the appropriate setup has
been correctly done in Google Apps Admin Console).
Even though it may not produce any error under certain cases, there are some unexpected behaviors when including an email address of the service account itself in the JWT claim set as the value of the "sub" field.

How can I tell the Google Adwords API which client/account in my MCC I am querying?

Using the latest PHP CLient Library (v2.6.3) I can't seem to figure out to get all campaigns for a client in my MCC (my client center) account.
I can easily get all accounts via:
$user = new AdWordsUser(NULL, $email, $password, $devToken, $applicationToken, $userAgent, NULL, $settingsFile);
$service = $user->GetServicedAccountService();
$selector = new ServicedAccountSelector();
$selector->enablePaging = false;
$graph = $service->get($selector);
$accounts = $graph->accounts; // all accounts!
Now that I've done that, I want to get all the campaigns within each account. Running the code as documented here doesn't work.
// Get the CampaignService.
// ** Different than example because example calls a private method ** //
$campaignService = $user->GetCampaignService('v201101');
// Create selector.
$selector = new Selector();
$selector->fields = array('Id', 'Name');
$selector->ordering = array(new OrderBy('Name', 'ASCENDING'));
// Get all campaigns.
$page = $campaignService->get($selector);
// Display campaigns.
if (isset($page->entries)) {
foreach ($page->entries as $campaign) {
print 'Campaign with name "' . $campaign->name . '" and id "'
. $campaign->id . "\" was found.\n";
}
}
All the above code will do is throw an error:
Fatal error: Uncaught SoapFault exception: [soap:Server]
QuotaCheckError.INVALID_TOKEN_HEADER # message=null
stack=com.google.ads.api.authserver.common.AuthException at
com.go;
I have a feeling that the reason this fails is that GetCampaignService needs an account's id...but I can't figure out how to specify this id.
What am I doing wrong?
The problem ended up being that I was given the wrong developerToken. I didn't think INVALID_TOKEN_HEADER really meant what it said because SOME calls still worked with the faulty token. I don't know why.

Categories