我想通过在他们的亚马逊账户中给他们礼品卡余额来奖励注册我的应用程序的用户,为此我使用了类似于 Amazon Incentive api 的 Load Amazon Balance api。
亚马逊需要数字签名的授权负载才能访问 api。我将我的代码与亚马逊使用 aws 密钥和 aws 访问密钥进行数字签名的过程进行了比较。但它仍然给我
"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."
我在帖子中附上了我班级的代码。任何人都可以通过查看代码并告诉我在向 api 发送信息的过程中是否有任何错误来帮助我。谢谢。
<?php
/**
* Created by PhpStorm.
*
* Date: 07-09-2018
* Time: 01:18 PM
*/
namespace App;
use App\AppConstant\AppConstant;
use App\Entities\Models\User;
use Illuminate\Support\Facades\Auth;
class AmazonReward
{
/**
* This is the bulk of the logic for making AGCOD calls.
*/
//Constants
public static $apikey;
public static $clientId = env("client_id");
public static $clientSecret = env("client_secret");
public static $awsSecretKey = env("client_secret");
public static $partnerId = env("partner_id");
public static $SERVICE_NAME = "AGCODService";
public static $ACCEPT_HEADER = "accept";
public static $CONTENT_HEADER = "content-type";
public static $HOST_HEADER = "host";
public static $XAMZDATE_HEADER = "x-amz-date";
public static $XAMZTARGET_HEADER = "x-amz-target";
public static $AUTHORIZATION_HEADER = "Authorization";
public static $AWS_SHA256_ALGORITHM = "AWS4-HMAC-SHA256";
public static $KEY_QUALIFIER = "AWS4";
public static $TERMINATION_STRING = "aws4_request";
const SERVICE_NAME = "AGCODService";
const ACCEPT_HEADER = "accept";
const CONTENT_HEADER = "content-type";
const HOST_HEADER = "host";
const XAMZDATE_HEADER = "x-amz-date";
const XAMZTARGET_HEADER = "x-amz-target";
const AUTHORIZATION_HEADER = "Authorization";
const AWS_SHA256_ALGORITHM = "AWS4-HMAC-SHA256";
const KEY_QUALIFIER = "AWS4";
const TERMINATION_STRING = "aws4_request";
const ENDPOINT ='agcod-v2.amazon.com';
// const ENDPOINT ='agcod-v2-gamma.amazon.com';
public static function get_paypload_giftcard($partnerId, $gcRequestId, $currencyCode, $gcAmount, $customer_id)
{
$amount = trim($gcAmount);
$payload = array(
"loadBalanceRequestId" => $gcRequestId,
"partnerId" => $partnerId,
"amount" =>
array(
"currencyCode" => $currencyCode,
"value" => floatval($amount)
),
"account" =>
array(
"id" => $customer_id,
"type" => "2"
),
"timestamp" => time(),
// "transactionSource" => [
// "sourceId" => ""
// ],
"externalReference" => "",
"notificationDetails" => [
"notificationMessage" => ""
]
);
return json_encode($payload);
}
/**
* Builds the "formal" request that can be hashed to verify the contents of the request.
* The request does not get sent to the server in this format, but the hash of this is.
*
* @return The formal request
*/
public static function buildCanonicalRequest($serviceOperation, $payloadHash, $customer_id) {
$ACCEPT_HEADER = self::ACCEPT_HEADER;
$HOST_HEADER = self::HOST_HEADER;
$XAMZDATE_HEADER = self::XAMZDATE_HEADER;
$XAMZTARGET_HEADER = self::XAMZTARGET_HEADER;
$ACCEPT_HEADER = self::ACCEPT_HEADER;
$dateTimeString = Self::getTimeStamp();
$header1 = Self::header1($serviceOperation);
$canonicalRequest = "POST\n/$serviceOperation HTTP/1.1\n\n$header1\n\n$ACCEPT_HEADER;$HOST_HEADER;$XAMZDATE_HEADER;$XAMZTARGET_HEADER\n$payloadHash";
return $canonicalRequest;
}
/**
* Returns part of the header used in the canonical request.
*
* @return the portion of the header.
*/
public static function header1($serviceOperation) {
$ACCEPT_HEADER = self::ACCEPT_HEADER;
$XAMZDATE_HEADER = self::XAMZDATE_HEADER;
$XAMZTARGET_HEADER = self::XAMZTARGET_HEADER;
$HOST_HEADER = Self::HOST_HEADER;
$dateTimeString = Self::getTimeStamp();
$endpoint = self::ENDPOINT;
$contentType = Self::getContentType();
return
"$ACCEPT_HEADER:$contentType\n$HOST_HEADER:$endpoint\n$XAMZDATE_HEADER:$dateTimeString\n$XAMZTARGET_HEADER:com.amazonaws.agcod.AGCODService.$serviceOperation";
}
public static function disburseFunds($customer_id){
$apiString = "https://" . Self::ENDPOINT . "/LoadAmazonBalance";
$ch = curl_init();
$xAmzDateTime = gmdate('Ymd\THis\Z');
$xAmzDate = substr($xAmzDateTime, 0, 8);
$xAmzTarget = "com.amazonaws.agcod.AGCODService.LoadAmazonBalance";
$region = "us-east-1";
$serviceOperation = "LoadAmazonBalance";
// $canonicalRequestHash = Self::myHash($canonicalRequest);
$payload = Self::get_paypload_giftcard( Self::$partnerId, Self::$partnerId . time(), "USD", 5.00, $customer_id);
$payloadHash = Self::myHash($payload);
$canonicalRequest = Self::buildCanonicalRequest($serviceOperation, $payloadHash , $customer_id);
$dateTimeString = Self::getTimeStamp();
$curl_response = Self::invokeRequest($payload, $xAmzDateTime,$canonicalRequest, $serviceOperation);
echo "<pre>";
print_r($curl_response);
exit;
$json = json_decode($curl_response);
if ( isset($json->message) || ! isset($json->cardInfo) )
{
echo "<pre>";
print_r($json);
exit;
$ret = array();
$ret['success']=false;
$ret['errorCode'] = time();
return $ret;
}
$ret = array();
$ret['success']=true;
$ret['gcValue']= $json->cardInfo->value->amount;
$ret["gcCode"]= $json->gcClaimCode;
$ret["gcResponseId"]= $json->gcId; ////done
$ret["gcRequestId"]=$json->creationRequestId;
echo "<pre>";
print_r($ret);
exit;
return $ret;
// echo "<pre>";
// print_r($canonicalRequestHash);
// exit;
// // $canonicalRequestHash = myHash($canonicalRequest);
// // $stringToSign = buildStringToSign($canonicalRequestHash);
// echo "<pre>";
// print_r($this->buildAuthSignature($stringToSign));
// exit;
// $authotization = "AWS4-HMAC-SHA256 Credential=AKIAIGHKAVYIDBOH3O3A/" . $xAmzTarget . "/" . $region . "/AGCODService/aws4_request,SignedHeaders=accept;host;x-amz-date;x-amz-target, Signature=ec86661c1d39f74b5891666505bb7656b172b0d060d911bee3b6a1c29ae17657";
// /*$post = http_build_query([
// ["Authorization" => "DEMO1"],
// ["x-amz-date" => $xAmzDate],
// ["x-amz-target" => $xAmzTarget],
// ["Accept" => "application/json"],
// ["Host" => "agcod-v2-gamma.amazon.com"]
// ]);*/
// // $post = [];
// $post = [];
// $post["loadBalanceRequestId"] = "Amazon123456";
// $post["partnerId"] = "";
// $post["amount"] = [];
// $post["amount"]["currencyCode"] = "";
// $post["amount"]["value"] = "";
// $post["account"] = [];
// $post["account"]["id"] = "F2044";
// $post["account"]["type"] = "0";
// $post["transactionSource"] = [];
// $post["transactionSource"]["sourceId"] = "";
// $post["externalReference"] = "";
// $post["notificationDetails"] = [];
// $post["notificationDetails"]["notificationMessage"] = "";
// $customHeaders = [
// "Authorization" => "Amazon",
// "x-amz-date" => $xAmzDateTime,
// "x-amz-target" => $xAmzTarget,
// "Accept" => "application/json",
// "Host" => "agcod-v2-gamma.amazon.com"
// ];
// curl_setopt($ch, CURLOPT_URL, $apiString);
// curl_setopt($ch, CURLOPT_POST, 1);
// curl_setopt($ch, CURLOPT_POSTFIELDS,$post);
// curl_setopt($ch, CURLOPT_HTTPHEADER, $customHeaders);
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// $server_output = curl_exec($ch);
// curl_close ($ch);
// // Further processing ...
// if ($server_output == "OK") {
// }else{
// }
}
/**
* Builds the authenication string used to prove that the request is allowed and made by the right party.
*
* @param $stringToSign The string to sign.
* @return The authenication signature.
*/
public static function buildAuthSignature($dateString, $stringToSign) {
$AWS_SHA256_ALGORITHM = Self::AWS_SHA256_ALGORITHM;
$SERVICE_NAME = Self::SERVICE_NAME;
$TERMINATION_STRING = Self::TERMINATION_STRING;
$ACCEPT_HEADER = Self::ACCEPT_HEADER;
$HOST_HEADER = Self::HOST_HEADER;
$XAMZDATE_HEADER = Self::XAMZDATE_HEADER;
$XAMZTARGET_HEADER = Self::XAMZTARGET_HEADER;
$awsKeyId = Self::$clientId;
$regionName= Self::getRegion();
$derivedKey = Self::buildDerivedKey(Self::getDateString($dateString));
$derivedKey_lower = Self::buildDerivedKey(Self::getDateString($dateString), false);
$dateString = Self::getDateString($dateString);
// Calculate signature per http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
$finalSignature = Self::hmac($stringToSign, $derivedKey, false);
// Assemble Authorization Header with signing information
// per http://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
$authorizationValue =
$AWS_SHA256_ALGORITHM
. " Credential=" . $awsKeyId
. "/" . $dateString . "/" . $regionName . "/" . $SERVICE_NAME . "/" . $TERMINATION_STRING . ","
. " SignedHeaders="
. $ACCEPT_HEADER . ";" . $HOST_HEADER . ";" . $XAMZDATE_HEADER . ";" . $XAMZTARGET_HEADER . ","
. " Signature="
. $finalSignature;
return $authorizationValue;
}
/**
* Hashes the string using sha256, the standard AWS hash.
*
* @param $data a string to sign
* @return a string hash of $data
*/
public static function myHash($data) {
return hash("sha256",$data);
}
/**
* Builds the string that gets hashed and used in the authenication.
*
* @param $canonicalRequestHash The hash of the canonicalRequest
* @return The string to sign.
*/
public static function buildStringToSign($canonicalRequestHash){
$AWS_SHA256_ALGORITHM = Self::AWS_SHA256_ALGORITHM;
$TERMINATION_STRING = Self::TERMINATION_STRING;
$SERVICE_NAME = Self::SERVICE_NAME;
$awsSecretKey = Self::$awsSecretKey;
$regionName = Self::getRegion();
$dateTimeString = Self::getTimeStamp();
$dateString = Self::getDateString($dateTimeString);
$stringToSign = "$AWS_SHA256_ALGORITHM\n$dateTimeString\n$dateString/$regionName/$SERVICE_NAME/$TERMINATION_STRING\n$canonicalRequestHash";
return $stringToSign;
}
/**
* Gets the time stamp used to make the request. If not set by the client it is set to the current time on the first call to this function.
*
* @return The time stamp
*/
public static function getTimeStamp() {
global $timeStamp;
if(!isset($timeStamp)) {
//GMT time. Format is YYYYMMDDTHHmmssZ where T and Z are literals, YYYY is 4 digit year, MM is 2 digit month, DD is 2 digit day, HH is 2 digit hour (24 hour clock) mm is 2 digit minute, ss is 2 digit second.
$timeStamp = gmdate('Ymd\THis\Z');
}
return $timeStamp;
}
/**
* Get the format that we will make the request in. This tells the server how to parse the request.
* This value is retrieved from the client and can either be json or xml.
*
* @return The request format as to be passed to the AGCOD server.
*/
public static function getContentType() {
return "application/json"; //Request in JSON format
}
/**
* Makes the service call to the AGCOD server.
*
* @return The repsonse from the server (in XML or JSON format) with HTML character escaped.
*/
public static function invokeRequest($payload, $dateTimeString,$canonicalRequest, $serviceOperation) {
$KEY_QUALIFIER = self::KEY_QUALIFIER;
$ACCEPT_HEADER = self::ACCEPT_HEADER;
$CONTENT_HEADER = self::CONTENT_HEADER;
$HOST_HEADER = self::HOST_HEADER;
$XAMZDATE_HEADER = self::XAMZDATE_HEADER;
$XAMZTARGET_HEADER = self::XAMZTARGET_HEADER;
$AUTHORIZATION_HEADER = self::AUTHORIZATION_HEADER;
$canonicalRequestHash = Self::myHash($canonicalRequest);
$stringToSign = Self::buildStringToSign($canonicalRequestHash);
$authorizationValue = Self::buildAuthSignature($dateTimeString, $stringToSign);
$endpoint = Self::ENDPOINT;
$regionName = Self::getRegion();
$SERVICE_NAME = "AGCODService";
$serviceTarget = "com.amazonaws.agcod." . $SERVICE_NAME . "." . $serviceOperation;
$contentType = Self::getContentType();
$url = "https://" . Self::ENDPOINT . "/" . $serviceOperation;
// print_r($url);
// exit;
//Prepare to send the data to the server
$handle = curl_init($url);
//Yes, do POST not GET
curl_setopt($handle, CURLOPT_POST, true);
//This is header, not post fields
curl_setopt($handle, CURLOPT_HTTPHEADER , array(
"Content-Type:$contentType",
'Content-Length: ' . strlen($payload),
$AUTHORIZATION_HEADER. ":" . $authorizationValue,
$XAMZDATE_HEADER . ":" . $dateTimeString,
$XAMZTARGET_HEADER . ":" . $serviceTarget,
"host:" . Self::ENDPOINT,
$ACCEPT_HEADER . ":" . $contentType
));
curl_setopt($handle, CURLOPT_POSTFIELDS, $payload);
//Yes, don't print the result to the web page, just give it to us in a string.
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
//Do the request
$result = curl_exec($handle);
if (empty($result)) {
// some kind of an error happened
die(curl_error($handle));
curl_close($handle); // close cURL handler
}
//Free the resource
curl_close($handle);
$signaturePos = strpos($authorizationValue, "Signature=");
if($signaturePos == FALSE || $signaturePos + 10 >= strlen($authorizationValue)) {
$signatureStr = "Malformed";
}
else {
$signatureStr = substr($authorizationValue, $signaturePos + 10);
}
print_r($result);
exit;
return $result;
}
/**
* Gets the region based on the server we connect too.
*
* @return The region.
*/
public static function getRegion() {
$endpoint = Self::ENDPOINT;
$regionName = "us-east-1";
if ($endpoint == "agcod-v2-eu.amazon.com" || $endpoint == "agcod-v2-eu-gamma.amazon.com") {
$regionName = "eu-west-1";
}
else if ($endpoint == "agcod-v2-fe.amazon.com" || $endpoint == "agcod-v2-fe-gamma.amazon.com") {
$regionName = "us-west-2";
}
return $regionName;
}
/**
* Performs a hmac hash using sha256, which is what AWS uses.
*
* @param $data The data to sign.
* @param $key The key to sign the data with.
* @param $raw true to provide a raw ascii string, false to use a hex string.
* @return the hash of $data
*/
public static function hmac($data, $key, $raw = true) {
return hash_hmac("sha256", $data, $key, $raw);
}
/**
* Gets the date for the request, which is either what the client passed in, or today if none was given.
*
* @return The date in YYYYMMDD format.
*/
public static function getDateString($dateTimeString) {
// $dateTimeString = Self::getTimeStamp();
return substr($dateTimeString, 0, 8);
}
/**
* Builds the derived key, which is used for authorizating the request.
*
* @param $rawOutput true to return an ascii string using raw byes, false to return a hex string
*/
public static function buildDerivedKey($dateString, $rawOutput = true) {
$KEY_QUALIFIER = Self::KEY_QUALIFIER;
$TERMINATION_STRING = Self::TERMINATION_STRING;
$SERVICE_NAME= Self::SERVICE_NAME;
// Get pasted AWS Secret Key from user input
$awsSecretKey = Self::$awsSecretKey;
// Append Key Qaulifier, "AWS4", to secret key per http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html
$signatureAWSKey = $KEY_QUALIFIER . $awsSecretKey;
$regionName = Self::getRegion();
// $dateString = Self::getDateString();
$kDate = Self::hmac($dateString, $signatureAWSKey);
$kRegion = Self::hmac($regionName, $kDate);
$kService = Self::hmac($SERVICE_NAME, $kRegion);
// Derived the Signing key (derivedKey aka kSigning)
$derivedKey = Self::hmac($TERMINATION_STRING, $kService, $rawOutput);
return $derivedKey;
}
}