Codeigniter and Mandrill api, unable to send email - php

I am trying to email by using Mandrill with CodeIgniter. I can use Mandrill API as described in their documentation:
require_once(mandrill/Mandrill.php);
$Mandrill = new Mandrill($apikey);
$params = array(
"html" => "<p>\r\n\tHi Adam,</p>\r\n<p>\r\n\tThanks for registering.</p>\r\n<p>etc etc</p>",
"text" => null,
"from_email" => "xxx#xxx.example.com",
"from_name" => "chris french",
"subject" => "Your recent registration",
"to" => array(array("email" => xxx#yyy.example.com")),
"track_opens" => true,
"track_clicks" => true,
"auto_text" => true
);
$Mandrill->messages->send($params, true));
This is pretty straight forward, but when I try to send Mandrill mail via CodeIgniter, I get error as result; here is my code:
$this->load->library('mandrill/Mandrill');
$this->Mandrill->apikey($apikey);
//...
//All other options
$this->Mandrill->messages->send($params, true));
Library is loading successfully, sending email is where I get error.
Error thrown:
Fatal error: Call to a member function send() on null

I guess you load mandrill class wrongly in order to make it "CodeIgniter way" do as follows:
put class (the file mandrill.php and folder mandrill in application/libraries folder) see picture
load class using $this->load->library('mandrill', array($apikey));
fix all typos in $params (you are missing ", last line has two brackets ))in the end)
modify mandrill.php so it accepts array as parameter in constructor; see SOLUTION part of answer
use API as in tutorials from Mandrill (send, ping... whatever)
valid code
$this->load->library('mandrill', array($apikey)); //load mandrill and provide apikey
$params = array(
"html" => "<p>\r\n\tHi Adam,</p>\r\n<p>\r\n\tThanks for registering.</p>\r\n<p>etc etc</p>",
"text" => null,
"from_email" => "xxx#xxx.example.com",
"from_name" => "chris french",
"subject" => "Your recent registration",
"to" => array(array("email" => "xxx#yyy.example.com")),
"track_opens" => true,
"track_clicks" => true,
"auto_text" => true
);
$this->mandrill->messages->send($params, true);
Edit (comments)
please note: I removed real api-key = MY_KEY, email from = EMAIL_FROM, email to = EMAIL_TO
$msg = array(
"html" => "The Message",
"text" => null,
"from_email" => "EMAIL_FROM",
"from_name" => "John Doe",
"subject" => "Acme",
"to" => array(array("email" => "EMAIL_TO")),
"track_opens" => true,
"track_clicks" => true,
"auto_text" => true
);
require_once APPPATH.'libraries/Mandrill.php';
$mandrill = new Mandrill('MY_KEY');
var_dump($mandrill->messages->send($msg, true));
//var_dump($mandrill->users->ping());
$this->load->library('Mandrill', array("MY_KEY")); //load mandrill and provide apikey
var_dump($this->mandrill->messages->send($msg, true));
//var_dump($this->mandrill->users->ping());
Above code sends identical email twice (using different load methods); response && few var_dump() are:
method using require_once...
object(Mandrill)[14] //setup
public 'apikey' => string 'MY_KEY' (length=22)
public 'ch' => resource(40, curl)
public 'root' => string 'https://mandrillapp.com/api/1.0/' (length=32)
public 'debug' => boolean false
array (size=1) //this is response
0 =>
array (size=4)
'email' => string 'EMAIL_TO' (length=24)
'status' => string 'sent' (length=4)
'_id' => string 'f7af72b1e1364a58a2eb302e94a1dc1e' (length=32)
'reject_reason' => null
method using $this->load->library();
object(Mandrill)[30] //setup
public 'apikey' => string 'MY_KEY' (length=22)
public 'ch' => resource(41, curl)
public 'root' => string 'https://mandrillapp.com/api/1.0/' (length=32)
public 'debug' => boolean false
array (size=1) //this is response
0 =>
array (size=4)
'email' => string 'EMAIL_TO' (length=24)
'status' => string 'sent' (length=4)
'_id' => string '5965091637fa42dd98b40a934523022e' (length=32)
'reject_reason' => null
SOLUTION
In order to make it work I changed the way how API_KEY is retrieved in Mandrill constructor, since CodeIgniter's loader is sending parameter to constructor as array()
Mandrill.php
public function __construct($apikey=null) {
if(is_array($apikey)) $apikey = $apikey[0]; //added this line
if(!$apikey) $apikey = getenv('MANDRILL_APIKEY');
if(!$apikey) $apikey = $this->readConfigs();
//... rest of the file
Another option is CI-Mandrill, note that it uses version 1.0; installation time ~5 minutes or less

Related

Empty Payload. JSON content expected (guzzle, php, azur active directory and outlook)

I am a student in programming and I have difficulties with my projects, little documentation meets my expectations and I would need help.
in this project I have to send from my application the token, the json flow on outlook to schedule a meeting
about Project : the principle is on a page to create a meeting which will then be linked to the outlook calendar this document helped me a lot https://learn.microsoft.com/fr-fr/graph/api/calendar-post-events?view=graph-rest-1.0&tabs=http https://youtu.be/orVsKsRs2Us
(data is on another page)
the whole function
public function Postcalendrier($ID,$data) {
$token=$_SESSION['token'];
$ID=$_SESSION["ID"];
$calendarGroup="/calendarGroups/{myID}/";
$calendar="calendars/{myID}/events";
$urlcalendar="https://graph.microsoft.com/v1.0/users/".$ID;
$url=$urlcalendar.$calendarGroup.$calendar;
$headers = [
'Authorization' => 'Bearer '.$token->access_token,
'Accept' => 'application/json',
'Content-Type' => 'application/json',
];
$json = json_encode([$data])
$reponse = $this->guzzle->request('POST', $url,['headers' =>
$headers,'json' => $json],['debug' => true]);
return $response->getStatusCode();
}
$data = array(
"subject" => $Subject,
"body" => array(
"contentType" => "HTML",
"content" => $Content
),
"start" => array(
"dateTime" => $StartDateTime."T".$StartHourTime.":00",
"timeZone" => "$localDatetime"
),
"end" => array(
"dateTime" => $StartDateTime."T".$EndHourTime.":00",
"timeZone" => "$localDatetime"
),
"location" => array(
"displayName" => $Location
),
"attendees"=> [array(
"emailAddress"=> array(
"address"=> $addressmail,
"name"=>$prenom
),
"type"=> "required"
)]
);
I get this error message
Empty Payload. JSON content expected
so I tried with a different JSON but
I got this message instead and I don't really understand it
code UnableToDeserializePostBody message were unable to deserialize
this is my new json
$json = [
'json' => json_encode([$data])
];
thank you for your attention

Amazon Polly AudioStream is always empty

I am trying to get Polly to read something for me, using PHP.
I have created a new project, installed Amazon composer require aws/aws-sdk-php and then created a file with code from SDK documentation example and modified a few minor things such as changing credential from default to value, var_dump to var_export and finally saved the content of the stream to file
<?php
require 'vendor/autoload.php';
use Aws\Exception\AwsException;
use Aws\Polly\PollyClient;
use Aws\Credentials\Credentials;
// Create a PollyClient
$client = new Aws\Polly\PollyClient([
//'profile' => 'default',
'credentials' => new Credentials('XXXXXXXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'),
'version' => '2016-06-10',
'region' => 'us-east-2'
]);
try {
$result = $client->synthesizeSpeech([
'Text' => 'Hello',
'OutputFormat' => 'json', //json|mp3|ogg_vorbis|pcm
'VoiceId' => 'Joanna',
]);
var_export($result);
$data = $result->get('AudioStream')->getContents();
echo "\n\n";
var_export($data);
$file = fopen('test.txt','w+');
fwrite($file,$data);
fclose($file);
} catch (AwsException $e) {
echo $e->getMessage() . "\n";
}
The result I'm getting is following
Aws\Result::__set_state(array(
'data' => array (
'AudioStream' => GuzzleHttp\Psr7\Stream::__set_state(array(
'stream' => NULL,
'size' => NULL,
'seekable' => true,
'readable' => true,
'writable' => true,
'uri' => 'php://temp',
'customMetadata' => array (),
)),
'ContentType' => 'application/x-json-stream',
'RequestCharacters' => '5',
'#metadata' => array (
'statusCode' => 200,
'effectiveUri' => 'https://polly.us-east-2.amazonaws.com/v1/speech',
'headers' => array (
'x-amzn-requestid' => 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
'x-amzn-requestcharacters' => '5',
'content-type' => 'application/x-json-stream',
'transfer-encoding' => 'chunked',
'date' => 'Sat, 18 Sep 2021 05:11:20 GMT',
),
'transferStats' => array (
'http' => array (
0 => array (),
),
),
),
),
'monitoringEvents' => array (),
))
''
As you can see the size of the AudioStream is null (nothing in it) and also the created file is also empty since there is nothing in the stream to read.
If I change a credential to an invalid string, I get errors, and with the valid credential, the status code is 200, which makes me believe that my request is successful.
I changed voiceId to any other valid or invalid id and even changed the region with others with valid values getting status 200 and with invalid ones getting error messages, but I'm still not getting anything out of polly, it doesn't feel like talking!
Note: When I run $arr_voices = $polly->describeVoices();, I can read list of the voices without error.
Note: I had the same issue with .NET SDK too, which makes me think either there is something wrong with my request or some error message is missing from API.
Question
What I'm doing wrong?
You're not doing anything wrong, but it only outputs JSON if you're looking for speech marks. Try switching to an audio output format like MP3 as shown below.
$result = $client->synthesizeSpeech([
'Text' => 'Hello',
'OutputFormat' => 'mp3', //json|mp3|ogg_vorbis|pcm
'VoiceId' => 'Joanna',
]);
If you're looking for speech marks- metadata on the speech that will be synthesized- you need to specify SpeechMarkTypes as shown here https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-polly-2016-06-10.html#synthesizespeech

PHP / Docusign - Verify HMAC signature on completed event

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');

Amazon Pay SDK InvalidSignatureError

I'm integrating Amazon Pay php SDK from documentation, but getting this error.
Here's my php implementation code:
$amazonpay_config = array(
'public_key_id' => 'XXXXXXXX',
'private_key' => 'my_private_key_path',
'region' => 'US',
'sandbox' => true
);
$payload = array(
'webCheckoutDetails' => array(
'checkoutReviewReturnUrl' => 'https://www.example.com/review',
'checkoutResultReturnUrl' => 'https://www.example.com/result'
),
'storeId' => 'amzn1.application-oa2-client.XXXXXXXXX'
);
$headers = array('x-amz-pay-Idempotency-Key' => uniqid());
$requestResult = [
'error' => 1,
'msg' => 'Error. Can not create checkout session.',
'checkoutSession' => null,
'payloadSign' => null
];
$client = new Client($amazonpay_config);
$resultCheckOut = $client->createCheckoutSession($payload, $headers);
$resultSignPayload = $client->generateButtonSignature($payload);
if($resultCheckOut['status'] !== 201) {
return json_encode($requestResult, true);
}
else {
$requestResult = [
'error' => 0,
'msg' => null,
'checkoutSession' => json_decode($resultCheckOut['response']),
'payloadSign' => $resultSignPayload
];
return $requestResult;
}
Here's JS implementation code for generating Amazon Pay button.
amazon.Pay.renderButton('#amazon-pay-btn', {
// set checkout environment
merchantId: 'XXXXXXXX',
ledgerCurrency: 'USD',
sandbox: true,
checkoutLanguage: 'en_US',
productType: 'PayOnly',
placement: 'Cart',
buttonColor: 'Gold',
createCheckoutSessionConfig: {
payloadJSON: jsonResult['checkoutSession'],
signature: jsonResult['payloadSign'],
publicKeyId: 'XXXXXXXXXXX'
}
});
Couple of problems with the code, mainly that you aren't passing the payload and signature to the front-end correctly. For the payload, you're using jsonResult['checkoutSession'], while it should be jsonResult['payloadSign']. This doesn't contain the payload though but from the PHP code it's apparently the signature that you have put in there. The full code sample should more like this (not tested).
Back-end:
$headers = array('x-amz-pay-Idempotency-Key' => uniqid());
$requestResult = [
'error' => 1,
'msg' => 'Error. Can not create checkout session.',
'signature' => null,
'payload' => null
];
$client = new Client($amazonpay_config);
$resultCheckOut = $client->createCheckoutSession($payload, $headers);
$resultSignature = $client->generateButtonSignature($payload);
if($resultCheckOut['status'] !== 201) {
return json_encode($requestResult, true);
}
else {
$requestResult = [
'error' => 0,
'msg' => null,
'signature' => $resultSignature,
'payload' => $payload
];
return json_encode($requestResult);
}
Front-end:
amazon.Pay.renderButton('#amazon-pay-btn', {
// set checkout environment
merchantId: 'XXXXXXXX',
ledgerCurrency: 'USD',
sandbox: true,
checkoutLanguage: 'en_US',
productType: 'PayOnly',
placement: 'Cart',
buttonColor: 'Gold',
createCheckoutSessionConfig: {
payloadJSON: JSON.stringify(jsonResult['payload']),
signature: jsonResult['signature'],
publicKeyId: 'XXXXXXXXXXX'
}
});
I'm not sure how you're passing $requestResult back to the front-end, potentially there's some additional JSON encoding/decoding required to get the right string. To prevent a signature mismatch error, please make sure that the payload string used for the signature generation in the backend, and the payload string assigned to the 'payloadJSON' parameter match exactly (especially pay attention to whitespaces, escape characters, line breaks, etc.).
Two comments about this issue:
I have defined the payload as an string (that's the way current AmazonPay doc states - Link).
$payload = '{
"webCheckoutDetails": {
"checkoutReviewReturnUrl": "https://www.example.com/review",
"checkoutResultReturnUrl": "https://www.example.com/result"
},
"storeId": "amzn1.application-oa2-client.XXXXXXXXX"
}';
instead of array
$payload = array(
'webCheckoutDetails' => array(
'checkoutReviewReturnUrl' => 'https://www.example.com/review',
'checkoutResultReturnUrl' => 'https://www.example.com/result'
),
'storeId' => 'amzn1.application-oa2-client.XXXXXXXXX'
);
The signature was created, but when rendering the button and clicking on it I get the following error.
Error Message: Signature Dk4qznkoiTVqjcY8Yn1l0iLbsoIj2pEAHWVtgYrphLtFXR9BKhJJPD53It4qYOswS1T/STYMHRy5jtCHGqvLntDjuy0MrhkpoHTpYEtwdOqGHA2qk+QnSGV5LoYldQ/UkAxSG7m8s2iOr11q2sWxUjrk2M3fgzAIxDeZRjJYeAr97eGANYva3jtGDfM6cJdieInBM4dEWWxKqGIh6HxOrY5K/ga26494vAwZAGvXRhZG48FOVp/XCr0mbu6V5pkEOzRJSc+hN5WKAs/c49UsfKPx75Ce7QbaBCZZT1UiczfyYx/mBuZuysUlGmnXPhLOLTPw4+SIizH/pOQyClOQyw== does not match signedString AMZN-PAY-RSASSA-PSS dfff7a87b93cfa78685a233f2dd59e18ad0451b2e3a90af11e500fcc0ceee924 for merchant XXXXXXXX
I was some time till I realized that this was the reason of the error. Actually, while writing this, the new lines in the string were the reason. If string is only in one line, it works.
The button only needs the payload and the signed payload. The $client->createCheckoutSession is not needed. More over, the checkoutSessionId of the resultCheckOut is different from the one obtained when the checkoutReviewReturnUrl is called.

Retrieving JSON response using HTTPful library for Laravel

I am currently building a e-mail client (inbound and outbound sending) using Mandrill as the e-mail sending / inbound service and Laravel 3.x.
In order to send messages, I am using the HTTPful bundle with the Mandrill using the following code in my mail/compose POST method.
$url = 'https://mandrillapp.com/api/1.0/messages/send.json';
$data = array(
'key' => '{removedAPIkey}',
'message' => array (
'to' => array( array( "email" => $_to ) ),
'from_name' => Auth::user()->name,
'from_email' => Auth::user()->email,
'subject' => $_subject,
'html' => $_body
),
'async' => true
);
$request = Httpful::post($url)->sendsJson()->body($data)->send();
Link to better formatted code above: http://paste.laravel.com/m79
Now as far as I can tell from the API log, the request is correctly made (with the expected JSON) and a response of the following format is sent back:
[
{
"email": "test#test.com",
"status": "queued",
"_id": "longmessageID"
}
]
However, what I am trying to do is access the response from the request (specifically the _id attribute), which is in JSON. Now as far as I'm aware, the HTTPful class should do this automatically (using json_decode()). However, accessing:
$request->_id;
is not working and I'm not entirely sure how to get this data out (it is required so I can record this for soft-bounce, hard-bounce and rejection messages for postmaster-like functionality)
Any assistance would be appreciated.
Edit
Using the following code, results in the mail being sent but an error returned:
$url = 'https://mandrillapp.com/api/1.0/messages/send.json';
$data = array(
'key' => '{removedAPIkey}',
'message' => array (
'to' => array( array( "email" => $_to ) ),
'from_name' => Auth::user()->name,
'from_email' => Auth::user()->email,
'subject' => $_subject,
'html' => $_body
),
'async' => true
);
$request = Httpful::post($url)->sendsJson()->body($data)->send();
if ( $request[0]->status == "queued" ) {
$success = true;
}
Results in an exception being thrown: Cannot use object of type Httpful\Response as array
I must say, a huge thanks to Aiias for his assistance. I managed to fix this myself (I must have spent hours looking at this). For anyone who wants to know, the HTTPful bundle has a body array, where the response is kept. Therefore, the code below works:
$url = 'https://mandrillapp.com/api/1.0/messages/send.json';
$data = array(
'key' => '{removedAPIkey}',
'message' => array (
'to' => array( array( "email" => $_to ) ),
'from_name' => Auth::user()->name,
'from_email' => Auth::user()->email,
'subject' => $_subject,
'html' => $_body
),
'async' => true
);
$request = Httpful::post($url)->sendsJson()->body($data)->send();
if ( $request->body[0]->status == "queued" ) {
$success = true;
}
Again, huge thanks to Aiias for clearing some major confusion up for me!

Categories