unfortunately our project runs on PHP 7.0 and we cannot upgrade it for now. And Twilio's library uses PHP 7.2+ on the version that contains the trusthub API support.
So I'm trying to do the request "Create EndUser of type: customer_profile_business_information" from this doc page using Guzzle instead of their library, and I'm following instructions from the curl example.
Everything worked well except the Attributes field that looks like it's being ignored, it's returning a blank object and of course on their interface it's also not showing.
So in case the link breaks, the curl code example is the following:
ATTRIBUTES=$(cat << EOF
{
"business_identity": "direct_customer",
"business_industry": "EDUCATION",
"business_name": "acme business",
"business_regions_of_operation": "USA_AND_CANADA",
"business_registration_identifier": "DUNS",
"business_registration_number": "123456789",
"business_type": "Partnership",
"social_media_profile_urls": "",
"website_url": "test.com"
}
EOF
)
curl -X POST https://trusthub.twilio.com/v1/EndUsers \
--data-urlencode "Attributes=$ATTRIBUTES" \
--data-urlencode "FriendlyName=friendly name" \
--data-urlencode "Type=customer_profile_business_information" \
-u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN
And here's the PHP code that I made:
<?php
// $company is a model
$token = base64_encode(\Config::get('twilio.accountSid') . ':' . \Config::get('twilio.authToken'));
$client = new \GuzzleHttp\Client(['base_uri' => 'https://trusthub.twilio.com/v1/', 'headers' => ['Authorization' => "Basic {$token}", 'Content-Type' => 'application/x-www-form-urlencoded']]);
$client->post("EndUsers", [
'form_params' => [
'FriendlyName' => $company->business_name,
'Type' => 'customer_profile_business_information',
'Attributes' => [
'business_name' => $company->business_name,
'business_identity' => 'direct_customer',
'business_type' => $company->business_type,
'business_industry' => $company->industry->twilio_name,
'business_registration_identifier' => 'EIN',
'business_registration_number' => $company->tax_id_number,
'business_regions_of_operation' => $company->region,
'website_url' => $company->website,
'social_media_profile_urls' => '',
]
]
]);
Is there something I'm missing here that it's not saving the Attributes data?
PS: the other fields (FriendlyName and Type) are being successfully saved.
Thank you!
Twilio developer evangelist here.
The Attributes property of Twilio resources tends to be a JSON string, and I think that's the case for this one too. So, rather than passing an array of attributes, you need to json_encode the array first. This should work for you:
<?php
// $company is a model
$token = base64_encode(\Config::get('twilio.accountSid') . ':' . \Config::get('twilio.authToken'));
$client = new \GuzzleHttp\Client(['base_uri' => 'https://trusthub.twilio.com/v1/', 'headers' => ['Authorization' => "Basic {$token}", 'Content-Type' => 'application/x-www-form-urlencoded']]);
$client->post("EndUsers", [
'form_params' => [
'FriendlyName' => $company->business_name,
'Type' => 'customer_profile_business_information',
'Attributes' => json_encode([
'business_name' => $company->business_name,
'business_identity' => 'direct_customer',
'business_type' => $company->business_type,
'business_industry' => $company->industry->twilio_name,
'business_registration_identifier' => 'EIN',
'business_registration_number' => $company->tax_id_number,
'business_regions_of_operation' => $company->region,
'website_url' => $company->website,
'social_media_profile_urls' => '',
])
]
]);
Related
I have this cURL command that I need to convert to PHP using Guzzle 7. I've have been researching this for a few (well, more than a few) days and getting nowhere fast. The cURL command uses the Simpli.fi api to create an organization under the parent org.
curl -i -X POST -H "X-App-Key: xxxx-xx-xx-xx-xxxxxx" -H "X-User-Key: xxxx-xx-xx-xx-xxxxxx" \
-H "Content-Type: application/json" \
-d '{
"organization": {
"name": "My New Organization",
"parent_id": "5786",
"custom_id": "<Put your organization identifier here or omit this optional field>"
}
}' \
"https://app.simpli.fi/api/organizations"
I was able to convert it using this website but it doesn't use Guzzle: https://onlinedevtools.in/curl
Here's what it gave me:
include('vendor/rmccue/requests/library/Requests.php');
Requests::register_autoloader();
$headers = array(
'X-App-Key' => 'xxxx-xx-xx-xx-xxxxxx',
'X-User-Key' => 'xxxx-xx-xx-xx-xxxxxx',
'Content-Type' => 'application/json'
);
$data = '{\n "organization": {\n "name": "My New Organization",\n "parent_id": "5786",\n "custom_id": "<Put your organization identifier here or omit this optional field>"\n }\n }';
$response = Requests::post('https://app.simpli.fi/api/organizations', $headers, $data);
Here's what I've tried so far aside from the converted code above:
public static function createOrganization()
{
self::init();
$client = new Client(['debug' => true]);
$request = $client->request('POST',
self::$baseUrl.'/organizations', [
'multipart' => [
[
'name' => 'data',
'contents' => "{'organization':{'name':'Pete's Pet Pagoda','parent_id':'1052385'}}",
],
],
'headers' => [
'x-app-key' => self::$appKey,
'x-user-key' => self::$userKey,
'Content-Type' => 'application/json',
]
]);
dd($response = $request->getStatusCode());
}
I'm getting quite a few different errors however this is the latest:
curl_setopt_array(): cannot represent a stream of type Output as a STDIO FILE*
Can anyone tell me what I'm doing wrong? Is there something missing?
UPDATE: After further research into this issue and chatting with a developer on the Laracast Slack channel, I've come to learn that this is a bug with the ['debug' => true] option when running on a Windows system and is described on this GITHUB page: https://github.com/guzzle/guzzle/issues/1413
I'm running on a Windows 10 Pro system. I corrected it on my end by changing the debug option to use fopen() like this:
'debug' => fopen('php://stderr', 'w'),
I use PHPStorm. It suggested using the 'wb' to make it binary safe. After changing it, the post requests worked fine.
You need to use body, not multipart. You can also use json.
$request = $client->request('POST',
self::$baseUrl.'/organizations', [
'headers' => [
'x-app-key' => self::$appKey,
'x-user-key' => self::$userKey,
'Content-Type' => 'application/json',
],
'body' => [
'{"organization":
[
{
"name":"Pete`s Pet Pagoda",
"parent_id":"1052385"
}
]
}',
],
]);
method 2
You can pass array to the json request option and it will convert it to json when sending the guzzle request. No need to use header as application/json as it applies internally.
$client->request('POST',
self::$baseUrl.'/organizations', [
'headers' => [
'x-app-key' => self::$appKey,
'x-user-key' => self::$userKey,
'Content-Type' => 'application/json',
],
'json' => [
"organization" => [
[
"name" => "Pete`s Pet Pagoda"
"parent_id" => "1052385"
]
]
],
]);
I hope this will help you. For further debugging use Middleware::tap(find more help here middleware+json+request)
try{
$client = new Client();
$response = $client->request('POST', self::$baseUrl.'/organizations',
[
'headers' => [
'x-app-key' => self::$appKey,
'x-user-key' => self::$userKey,
'Content-Type' => 'application/json',
],
'json' => [
'organization' =>
[
"name" => "some_name_value",
"parent_id" => "id_here",
"custom_id" => "custom id here"
]
]
]);
$_response = json_decode($response->getBody()->getContents(), true);
}
catch(BadResponseException $e){
$response = $e->getResponse();
$errorArray = json_decode($response->getBody()->getContents(), true);
//echo "<pre>";
//print_r($errorArray);
//return some message from errorarray;
}
I'm trying to secure my callback url when completed event is triggered.
My Controller:
public function callbackSubscriptionCompleted(
int $subscriptionId,
DocusignService $docusignService,
Request $request
) {
$signature = $request->headers->get("X-DocuSign-Signature-1");
$payload = file_get_contents('php://input');
$isValid = $docusignService->isValidHash($signature, $payload);
if (!$isValid) {
throw new ApiException(
Response::HTTP_BAD_REQUEST,
'invalid_subscription',
'Signature not OK'
);
}
return new Response("Signature OK", Response::HTTP_OK);
}
My DocusignService functions:
private function createEnvelope(Company $company, Subscription $subscription, LegalRepresentative $legalRepresentative, Correspondent $correspondent, $correspondents) : array
{
// ...
$data = [
'disableResponsiveDocument' => 'false',
'emailSubject' => 'Your Subscription',
'emailBlurb' => 'Subscription pending',
'status' => 'sent',
'notification' => [
'useAccountDefaults' => 'false',
'reminders' => [
'reminderEnabled' => 'true',
'reminderDelay' => '1',
'reminderFrequency' => '1'
],
'expirations' => [
'expireEnabled' => 'True',
'expireAfter' => '250',
'expireWarn' => '2'
]
],
'compositeTemplates' => [
[
'serverTemplates' => [
[
'sequence' => '1',
'templateId' => $this->templateId
]
],
'inlineTemplates' => [
[
'sequence' => '2',
'recipients' => [
'signers' => [
[
'email' => $legalRepresentative->getEmail(),
'name' => $legalRepresentative->getLastname(),
'recipientId' => '1',
'recipientSignatureProviders' => [
[
'signatureProviderName' => 'universalsignaturepen_opentrust_hash_tsp',
'signatureProviderOptions' => [
'sms' => substr($legalRepresentative->getCellphone(), 0, 3) == '+33' ? $legalRepresentative->getCellphone() : '+33' . substr($legalRepresentative->getCellphone(), 1),
]
]
],
'roleName' => 'Client',
'clientUserId' => $legalRepresentative->getId(),
'tabs' => [
'textTabs' => $textTabs,
'radioGroupTabs' => $radioTabs,
'checkboxTabs' => $checkboxTabs
]
]
]
]
]
]
]
],
'eventNotification' => [
"url" => $this->router->generate("api_post_subscription_completed_callback", [
"subscriptionId" => $subscription->getId()
], UrlGeneratorInterface::ABSOLUTE_URL),
"includeCertificateOfCompletion" => "false",
"includeDocuments" => "true",
"includeDocumentFields" => "true",
"includeHMAC" => "true",
"requireAcknowledgment" => "true",
"envelopeEvents" => [
[
"envelopeEventStatusCode" => "completed"
]
]
]
];
$response = $this->sendRequest(
'POST',
$this->getBaseUri() . '/envelopes',
[
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->getCacheToken()
],
json_encode($data)
);
}
public function isValidHash(string $signature, string $payload): bool
{
$hexHash = hash_hmac('sha256',utf8_encode($payload),utf8_encode($this->hmacKey));
$base64Hash = base64_encode(hex2bin($hexHash));
return $signature === $base64Hash;
}
I've created my hmac key in my Docusign Connect and i'm receiving the signature in the header and the payload but the verification always failed.
I've followed the Docusign documentation here
What's wrong ?
PS: Sorry for my bad english
Your code looks good to me. Make sure that you are only sending one HMAC signature. That way your hmacKey is the correct one.
As a check, I'd print out the utf8_encode($payload) and check that it looks right (it should be the incoming XML, no headers). Also, I don't think it should have a CR/NL in it at the beginning. That's the separator between the HTTP header and body.
Update
I have verified that the PHP code from the DocuSign web site works correctly.
The payload value must not contain either a leading or trailing newline. It should start with <?xml and end with >
I suspect that your software is adding a leading or trailing newline.
The secret (from DocuSign) ends with an =. It is a Base64 encoded value. Do not decode it. Just use it as a string.
Another update
The payload (the body of the request) contains zero new lines.
If you're printing the payload, you'll need to wrap it in <pre> since it includes < characters. Or look at the page source.
It contains UTF-8 XML such as
<?xml version="1.0" encoding="utf-8"?><DocuSignEnvelopeInformation xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.docusign.net/API/3.0"><EnvelopeStatus><RecipientStatuses><RecipientStatus><Type>Signer</Type><Email>larry#worldwidecorp.us</Email><UserName>Larry Kluger</UserName><RoutingOrder>1</RoutingOrder><Sent>2020-08-05T03:11:13.057</Sent><Delivered>2020-08-05T03:11:27.657</Delivered><DeclineReason xsi:nil="true" /><Status>Delivered</Status><RecipientIPAddress>5.102.239.40</RecipientIPAddress><CustomFields /><TabStatuses><TabStatus><TabType>Custom</TabType><Status>Active</Status><XPosition>223</XPosition><YPosition>744....
We've done some more testing, and the line
$payload = file_get_contents('php://input');
should be very early in your script. The problem is that a framework can munge the php://input stream so it won't work properly thereafter.
Note this page from the Symfony site -- it indicates that the right way to get the request body is:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ParameterBag;
$app->before(function (Request $request) {
$payload = $request->getContent();
hmac_verify($payload, $secret);
});
I would try to use the Symfony code instead of file_get_contents('php://input');
Does anybody know the correct way to PUT using Guzzle? my code is not working
but my post methods are working
$enrolment = $client->request('PUT', $url,[
'form_params' => [
'contactID' =>12345,
'type' =>'w'
],
'headers' => [
'apitoken' => $api_token,
'wstoken' => $ws_token
]
]);
resulted in a 500 Internal Server Error response:↵{"DATA":"","ERROR":true,"MESSAGES":"key [TYPE] doesn't exist","CODE":"0","DETAILS":""}
The PUT request does't accept form_params type as request option, so it may ignore the setting.
From Docs:
form_params
Used to send an application/x-www-form-urlencoded
POST request.
Maybe you can try using json for PUT request.
In the json part of Docs, it uses PUT as well.
$enrolment = $client->request('PUT', $url,[
'json' => [
'contactID' =>12345,
'type' =>'w'
],
'headers' => [
'apitoken' => $api_token,
'wstoken' => $ws_token
]
]);
I'm having trouble to pass the version parameter in a POST request using GuzzleHttp.
Client error: GET https://gateway.watsonplatform.net/tone-analyzer/api/v3/tone resulted in a 400 Bad Request response: {"code":400,"sub_code":"C00005","error":"Missing a minor version parameter in the URL. To use the latest version, add th (truncated...)
This is the latest I've tried:
$client = new \GuzzleHttp\Client([
'base_uri' => 'https://gateway.watsonplatform.net/'
]);
$toneAnalyserResponse = $client->request('POST', 'tone-analyzer/api/v3/tone', [
'auth' => ['{username}', '{password}'],
'headers' => [
'Content-Type' => 'text/plain',
],
'form_params' => [
'version' => '2017-09-21',
'tone_input' => $text,
'sentences' => true
]
]);
This was failing too:
$client = new \GuzzleHttp\Client([
'base_uri' => 'https://gateway.watsonplatform.net/'
]);
$toneAnalyserResponse = $client->request('POST', 'tone-analyzer/api/v3/tone', [
'auth' => ['{username}', '{password}'],
'headers' => [
'Content-Type' => 'text/plain',
],
'version' => '2017-09-21',
'tone_input' => $text,
'sentences' => true
]);
Failing as well if using GET instead of POST.
If I change the URI and add the version, then it works fine (well, it fails as it doesn't have the text to analyse in the request):
Change: tone-analyzer/api/v3/tone
To: tone-analyzer/api/v3/tone?version=2017-09-21
So, I guess the question is HOW DO YOU PASS URL PARAMETERS in a request using GuzzleHttp?
Note: my CURL command works fine (http 200):
curl -X POST -u "{username}":"{password}" --header 'Content-Type: text/plain' --header 'Accept: application/json' --header 'Content-Language: en' --header 'Accept-Language: en' -d 'I hate these new features On #ThisPhone after the update.\r\n \
I hate #ThisPhoneCompany products, you%27d have to torture me to get me to use #ThisPhone.\r\n \
The emojis in #ThisPhone are stupid.\r\n \
#ThisPhone is a useless, stupid waste of money.\r\n \
#ThisPhone is the worst phone I%27ve ever had - ever 😠.\r\n \
#ThisPhone another ripoff, lost all respect SHAME.\r\n \
I%27m worried my #ThisPhone is going to overheat like my brother%27s did.\r\n \
#ThisPhoneCompany really let me down... my new phone won%27t even turn on.' 'https://gateway.watsonplatform.net/tone-analyzer/api/v3/tone?version=2017-09-21&sentences=true'
You need to send "query" parameter for querystring. Pleaes check the below codes:
$toneAnalyserResponse = $client->request('POST', 'tone-analyzer/api/v3/tone', [
'auth' => ['{username}', '{password}'],
'headers' => [
'Content-Type' => 'text/plain',
],
'query' => [
'version' => '2017-09-21'
]
]);
I've managed to go through creating webhook using Shopify API, but I can create only one webhook per request. I've already tried to customize the request so it could possibly create a few webhooks at once, but it doesn't seem to work.
I'm using GuzzleHttp\Client for my requests and this is what my working request look like:
$client = new Client();
$response = $client->request(
'POST',
"https://{$store}/admin/webhooks.json",
[
'headers' => [
'X-Shopify-Access-Token' => $access_token,
'X-Shopify-Shop-Domain' => $store
],
'form_params' => [
'webhook' => [
"topic" => "orders/create",
"address" => $appAddress,
"format" => "json"
],
]
]);
But when I try something like this:
$client = new Client();
$response = $client->request(
'POST',
"https://{$store}/admin/webhooks.json",
[
'headers' => [
'X-Shopify-Access-Token' => $access_token,
'X-Shopify-Shop-Domain' => $store
],
'form_params' => [
'webhook' => [
[
"topic" => "orders/create",
"address" => $appAddress,
"format" => "json"
],
[
"topic" => "orders/delete",
"address" => $appAddress,
"format" => "json"
]
]
]
]);
Im getting this:
POST https://smshopify.myshopify.com/admin/webhooks.json resulted in
a 422 Unprocessable Entity response: {"errors":{"topic":["can't be blank","Invalid topic specified. Topics allowed: app/uninstalled,
carts/create, carts/u (truncated...)
Is there a way to create couple webhooks in one request, I couldn't find a word about it in Shopify documentation, and my attempts to modify request body are not very successful. What I've managed to do is just foreach topics array and to the single request for every webhook.
No, there is no way to create a batch of webhooks in one request. This is true for most Shopify resources - e.g. products must also be created one-by-one.