Amazon SNS - aws-sdk-php - php

$accessKey = 'XZA...';
$accessSecret = 'YKW...';
$credentials = new Aws\Credentials\Credentials($accessKey, $accessSecret);
$sharedConfig = [
'region' => 'us-east-1',
'version' => 'latest',
'credentials' => $credentials
];
$sdk = new Aws\Sdk($sharedConfig);
$sns = new SnsClient($sharedConfig);
$payload = [
'PhoneNumber' => '+999999999', // E.164 format
'Message' => md5(time()),
'MessageAttributes' => [
'DefaultSenderID' => ['DataType'=>'String','StringValue'=>'MyBrandName'],
'DefaultSMSType' => ['DataType'=>'String','StringValue'=>'Transactional']
]
];
try {
$data = $sns->publish( $payload );
$MessageId = $data->get('MessageId');
} catch ( Exception $e ) { }
I'm using the AWS SDK for PHP - Version 3.
The code above works well when i'm sending a single SMS message except the attribute DefaultSenderID wich is not working when i send a SMS to a mobile device.
Amazon documentation says that DefaultSenderID – A string, such as your business brand, that is displayed as the sender on the receiving device. Support for sender IDs varies by country. The sender ID can be 1 - 11 alphanumeric characters, and it must contain at least one letter.
Anyone has experienced this problem using the Amazon SNS?

For anybody still struggling with this.
If you look at the documentation here, you will find that you need to add the key AWS.SNS.SMS.SenderID to the payload's MessageAttributes.
The following should work:
$payload = [
'PhoneNumber' => '+999999999', // E.164 format
'Message' => md5(time()),
'MessageAttributes' => [
'AWS.SNS.SMS.SenderID' => [
'DataType' => 'String',
'StringValue' => 'YourSenderID',
]
]
];
try {
$data = $sns->publish($payload);
$MessageId = $data->get('MessageId');
} catch (Exception $e) { }

Related

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.

Error SHARED_VIEW_USER_LACKS_PERMISSION on DocuSign\eSign\Model\RecipientViewRequest

My scenario would be this flow in my application: Register > Sign Document > Return to Finish Page.
The user register on my application and he need to sign a document to finish his registration. He is not a DocuSign user. At the moment all my tests are at the Sandbox environment.
The envelope creation works great. If I don't use the client_user_id it sends the email for signing. But I need to use the client_user_id to use the embedded signing and get the URL for next step.
When I try to to get the URL of the envelope, I receive the following error:
errorCode: SHARED_VIEW_USER_LACKS_PERMISSION
message: User lacks shared permission to envelope. Only a user with shared access to the envelope may perform the requested operation.
Here is the code I'm using on my PHP application to try to get the URL of the recent created envelope:
$envelope = $this->docusignlib->create_document_for_signing($user, $file);
$result = $this->docusignlib->get_url_document($user, $envelope['envelope_id'], $return_url);
public function create_document_for_signing($user, $file)
{
# Document
$document = new DocuSign\eSign\Model\Document([
'document_base64' => base64_encode(file_get_contents($file)),
'name' => 'Document name',
'file_extension' => 'pdf',
'document_id' => '1'
]);
# Sign Here Position
$signHere = new DocuSign\eSign\Model\SignHere([
'document_id' => '1', 'page_number' => '2', 'recipient_id' => '1',
'tab_label' => 'Sign here', 'x_position' => '100', 'y_position' => '720'
]);
# The signer object
$signer = new DocuSign\eSign\Model\Signer([
'email' => $user->user_email,
'name' => $user->user_name,
'recipient_id' => "1",
'client_user_id' => $user->user_id,
'tabs' => new DocuSign\eSign\Model\Tabs([
'sign_here_tabs' => [$signHere]
])
]);
# Next, create the top level envelope definition and populate it.
$envelopeDefinition = new DocuSign\eSign\Model\EnvelopeDefinition([
'email_subject' => "Email subject",
'documents' => [$document],
'recipients' => new DocuSign\eSign\Model\Recipients(['signers' => [$signer]]),
'status' => "sent"
]);
$config = new DocuSign\eSign\Configuration();
$config->setHost($this->api);
$config->addDefaultHeader("Authorization", "Bearer " . $this->accessToken);
$apiClient = new DocuSign\eSign\Client\ApiClient($config);
$envelopeApi = new DocuSign\eSign\Api\EnvelopesApi($apiClient);
return $envelopeApi->createEnvelope($this->accountId, $envelopeDefinition);
}
public function get_url_document($user, $envelopeId, $returnUrl)
{
$recipientViewRequest = new DocuSign\eSign\Model\RecipientViewRequest([
'user_name' => $user->user_name,
'email' => $user->user_email,
"recipient_id" => "1",
"client_user_id" => $user->user_id,
"authentication_method" => "email",
"return_url" => $returnUrl
]);
$config = new DocuSign\eSign\Configuration();
$config->setHost($this->api);
$config->addDefaultHeader("Authorization", "Bearer " . $this->accessToken);
$apiClient = new DocuSign\eSign\Client\ApiClient($config);
$envelopeApi = new DocuSign\eSign\Api\EnvelopesApi($apiClient);
return $envelopeApi->createEnvelopeRecipientSharedView($this->accountId, $envelopeId, $recipientViewRequest);
}
I couldn't find ANYTHING related to this error on the documentation and I checked all the permissions and everything seems ok. I'm using the admin user of my demoaccount. Any ideas what I'm doing wrong here?
Thanks!
SHARED_VIEW_USER_LACKS_PERMISSION is about the user and the account. You may want to try a different account and/or a new envelope. I would also ensure that you are making API call to demo.docusign.net URL and not www.docusign.net since you are still in demo/sandbox.
The accessToken should match the account and if you're using the token generator, it's the account that you used when token generator prompted you to log in.

Amazon SNS Ignores FilterPolicy on email protocol (PHP SDK)

The following code subscribes to an SNS HTTP Endpoint:
$protocol = 'http';
$endpoint = 'http://test.com/endpoint.php';
$filterPolicyTest = array(
'test' => ['1','2','3']
);
$filterPolicyString = json_encode($filterPolicyTest);
$result = $this->awsClient->subscribe([
'Protocol' => $protocol,
'Endpoint' => $endpoint,
'ReturnSubscriptionArn' => true,
'TopicArn' => 'myTopicARN',
'Attributes' => [
'FilterPolicy' => $filterPolicyString,
],
]);
After confirming the subscription, I can view the subscription in the AWS console and see the Filter policy.
Now I try the same thing but with an email:
$protocol = 'email';
$endpoint = 'myemail#gmail.com';
$filterPolicyTest = array(
'test' => ['1','2','3']
);
$filterPolicyString = json_encode($filterPolicyTest);
$result = $this->awsClient->subscribe([
'Protocol' => $protocol,
'Endpoint' => $endpoint,
'ReturnSubscriptionArn' => true,
'TopicArn' => 'myTopicARN',
'Attributes' => [
'FilterPolicy' => $filterPolicyString,
],
]);
No errors are thrown. I successfully receive a confirmation email and click the link to subscribe. But, in the AWS console for this subscription, I see:
The FilterPolicy attribute is completely ignored.
I am using the PHP SDK version 3. Here is the documentation for this request. I tried submitting a FilterPolicy both as an array and json encoded string with no luck.

AWS SNS Push notification not delivered to device though its displaying delivered in cloudwatch

AWS SNS Push notification is not working for android GCM in my application though same code is working pretty good for IOS. I put cloudwatch to track down the error but it displaying status as delivered in logs but no push notification received in Device.
Relevant code of application
$platform = 1;
$token = 'user-device-token';
$gcmPlatformArn = 'AWS_GCM_ARN';
$ApnsPlatformArn = 'AWS_IOS_ARN';
$message = 'welcome';
$sns = SnsClient::factory([
'region' => 'my-region',
'version' => 'latest',
'credentials' => [
'key' => 'my-key',
'secret' => 'my-secret'
]
]);
if ($platform == 1) { //For Android
$endPointArn = $sns->createPlatformEndpoint([
'PlatformApplicationArn' => $gcmPlatformArn,
'Token' => $token
]);
} else {
$endPointArn = $sns->createPlatformEndpoint([
'PlatformApplicationArn' => $ApnsPlatformArn,
'Token' => $token
]);
}
$endPoint = $endPointArn['EndpointArn'];
return $sns->publish(['Message' => $message,
'TargetArn' => $endPoint]);

AWS-CloudWatch: InvalidSequenceTokenException

I have a php worker where i log events to AWS could watch.
Unfortunately i got the following error when i try to submit it.
InvalidSequenceTokenException Error executing "PutLogEvents" on
"https://logs.eu-west-1.amazonaws.com"; AWS HTTP error: Client error:
POST https://logs.eu-west-1.amazonaws.com resulted in a 400 Bad
Request response:
{"__type":"InvalidSequenceTokenException","expectedSequenceToken":"999999999999990356407851919528174
(truncated...) InvalidSequenceTokenException (client): The given
sequenceToken is invalid. The next expected sequenceToken is:
495599999999988500356407851919528174642 -
{"__type":"InvalidSequenceTokenException","expectedSequenceToken":"495573099999999900356407851919528174642","message":"The
given sequenceToken is invalid. The next expected sequenceToken is:
495579999999900356407851919528174642"}
and this is my code
$date = new DateTime();
$instance= = new CloudWatchLogsClient([
'region' => 'eu-west-1',
'version' => 'latest',
'credentials' => [
'key' => 'XXX',
'secret' => 'XXXX'
]
]);
$instance->putLogEvents([
'logGroupName' => "WorkerLog",
'logStreamName' => "log",
'logEvents' => [
[
'timestamp' => $date->getTimestamp(),
'message' => "test log"
]
]
]);
http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
You must include a sequence token with your request. If you don't have one you must use describeLogStreams (http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_DescribeLogStreams.html) to get the stream sequence.
When you make a call to putLogEvents you will get the nextToken in the response. You also must be ready for the case in which someone else pushes to the stream and invalidates the nextToken. (in this case you need to describe the stream again to get an updated token).
The DescribeLogStreams does not support the same volume call as PutLogEvent. You may got throttled if calling it frequently.
The recommended way to do it is calling the PutLogEvents directly and catch the InvalidSequenceTokenException. Then retry the PutLogEvents with Sequence Token in the InvalidSequenceTokenException message.
Correct sequence token can be found in the expectedSequenceToken field of InvalidSequenceTokenException
try {
$result = $client->describeLogStreams([
'logGroupName' => $logGroupName,
'logStreamNamePrefix' => $logStreamName,
]);
$uploadSequenceToken = $logStreams[0]['uploadSequenceToken'];
$client->putLogEvents([
'logGroupName' => $logGroupName,
'logStreamName' => $logStreamName,
'logEvents' => [
[
'timestamp' => $timestamp,
'message' => $message
],
],
'sequenceToken' => $uploadSequenceToken,
]);
} catch (\InvalidSequenceTokenException $e) {
$client->putLogEvents([
'logGroupName' => $logGroupName,
'logStreamName' => $logStreamName,
'logEvents' => [
[
'timestamp' => $timestamp,
'message' => $message
],
],
'sequenceToken' => $e->expectedSequenceToken,
]);
}
This is my working solution: before send new putLogEvents you must take the last uploadSequenceToken.
try {
$client = \Aws\CloudWatchLogs\CloudWatchLogsClient::factory($configCloudWatch);
$logStreamName = 'testLogStream';
$logGroupName = 'testGroupName';
$result = $client->describeLogStreams([
'logGroupName' => $logGroupName,
'logStreamNamePrefix' => $logStreamName,
]);
$logStreams=$result->get('logStreams');
if (!$logStreams)
throw new \Exception('No log stream found');
if (count($logStreams)!=1)
throw new \Exception('Multiple log stream found');
$uploadSequenceToken = $logStreams[0]['uploadSequenceToken'];
$client->putLogEvents([
'logGroupName' => $logGroupName,
'logStreamName' => $logStreamName,
'logEvents' => [
[
'timestamp' => round(microtime(true) * 1000),
// message is required
'message' => json_encode([ ... ]
),
],
],
'sequenceToken' => $uploadSequenceToken,
]);
} catch (\Exception $e) {
\Log::error(__METHOD__, ['exception' => $e]);
}

Categories