提问人:Mica 提问时间:11/6/2023 最后编辑:John RotensteinMica 更新时间:11/10/2023 访问量:54
Amazon SP-API 开发工具包
Amazon SP-API SDK
问:
有人可以解释一下如何在 AWS 上创建和上传 xml 文件作为源。我尝试了 https://github.com/amazon-php/sp-api-sdk/blob/5.x/src/AmazonPHP/SellingPartner/Api/FeedsApi/FeedsSDK.php#L225 并 https://developer-docs.amazon.com/sp-api/docs/feeds-api-v2021-06-30-use-case-guide,但是每次我都获得 url 和 feedDocumentId,但是当我尝试将其上传到该 url 时,我会得到 url:
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId>AKIAX2ZVOZFBB4YFXXHU</AWSAccessKeyId>
<StringToSign>AWS4-HMAC-SHA256 20231106T080142Z 20231106/eu-west-1/s3/aws4_request c2cbc4a511455b617c11d9ea1b41bfd2134b4f7d646b286e7848a9203ef5e13e</StringToSign>
<SignatureProvided>e01f1837a5e5cc2d4defae363a83812b2e48c5b4d9c28fcc46f6a96a9a7d24cf</SignatureProvided>
<StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 33 31 31 30 36 54 30 38 30 31 34 32 5a 0a 32 30 32 33 31 31 30 36 2f 65 75 2d 77 65 73 74 2d 31 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 63 32 63 62 63 34 61 35 31 31 34 35 35 62 36 31 37 63 31 31 64 39 65 61 31 62 34 31 62 66 64 32 31 33 34 62 34 66 37 64 36 34 36 62 32 38 36 65 37 38 34 38 61 39 32 30 33 65 66 35 65 31 33 65</StringToSignBytes>
<CanonicalRequest>GET /dc372987-1dd1-43b3-b516-27dc78dca0ec.amzn1.tortuga.4.eu.TH04D1RNGNQZS X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAX2ZVOZFBB4YFXXHU%2F20231106%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20231106T080142Z&X-Amz-Expires=300&X-Amz-SignedHeaders=content-type%3Bhost content-type: host:tortuga-prod-eu.s3-eu-west-1.amazonaws.com content-type;host UNSIGNED-PAYLOAD</CanonicalRequest>
<CanonicalRequestBytes>47 45 54 0a 2f 64 63 33 37 32 39 38 37 2d 31 64 64 31 2d 34 33 62 33 2d 62 35 31 36 2d 32 37 64 63 37 38 64 63 61 30 65 63 2e 61 6d 7a 6e 31 2e 74 6f 72 74 75 67 61 2e 34 2e 65 75 2e 54 48 30 34 44 31 52 4e 47 4e 51 5a 53 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 58 32 5a 56 4f 5a 46 42 42 34 59 46 58 58 48 55 25 32 46 32 30 32 33 31 31 30 36 25 32 46 65 75 2d 77 65 73 74 2d 31 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 33 31 31 30 36 54 30 38 30 31 34 32 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 33 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 0a 68 6f 73 74 3a 74 6f 72 74 75 67 61 2d 70 72 6f 64 2d 65 75 2e 73 33 2d 65 75 2d 77 65 73 74 2d 31 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes>
<RequestId>EJ9N9STNFFT5FT97</RequestId>
<HostId>rVh8aMdyJ3nzfI2vIqgvn0WHCwkfEvZAAMtZ33JY6Bk8OyFNCucBKesfAe3gLyvLjo50ZVsfY223ZyzfEcYp+g==</HostId>
</Error>
这就是我正在做的,但每次我都收到错误:
public function createFeed($distributionChannelTeamId)
{
$res=$this->createFeedDocument($distributionChannelTeamId);
$url=$res['url'];
$feedDocumentId=$res['feedDocumentId'];
if($res['url'] && $res['feedDocumentId']){
$requestXml = '<?xml version="1.0" encoding="iso-8859-1"?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
<Header>
<DocumentVersion>1.01</DocumentVersion>
<MerchantIdentifier>MYID</MerchantIdentifier>
</Header>
<MessageType>Product</MessageType>
<PurgeAndReplace>false</PurgeAndReplace>
<Message>
<MessageID>1</MessageID>
<OperationType>Update</OperationType>
<Product>
<SKU>56789</SKU>
<StandardProductID>
<Type>ASIN</Type>
<Value>B0EXAMPLEG</Value>
</StandardProductID>
<ProductTaxCode>A_GEN_NOTAX</ProductTaxCode>
<DescriptionData>
<Title>Example Product Title</Title>
<Brand>Example Product Brand</Brand>
<Description>This is an example product description.</Description>
<BulletPoint>Example Bullet Point 1</BulletPoint>
<BulletPoint>Example Bullet Point 2</BulletPoint>
<MSRP currency="USD">25.19</MSRP>
<Manufacturer>Example Product Manufacturer</Manufacturer>
<ItemType>example-item-type</ItemType>
</DescriptionData>
<ProductData>
<Health>
<ProductType>
<HealthMisc>
<Ingredients>Example Ingredients</Ingredients>
<Directions>Example Directions</Directions>
</HealthMisc>
</ProductType>
</Health>
</ProductData>
</Product>
</Message>
</AmazonEnvelope>';
$response = Http::withHeaders(['Content-Type' => 'application/xml'
])->put($url,[
'body' => utf8_encode($requestXml)
]);
Log::channel('amazon')->info('Amazon create feed response: ', ['response' => $response]);
dd($response);
}else{
dump('no res');
dd($res);
}
}
答:
0赞
Mica
11/6/2023
#1
我找到了使用 Amazon SP-API (https://github.com/amazon-php/sp-api-sdk 在 Laravel 中创建产品提要的解决方案:
composer require amazon-php/sp-api-sdk
composer require nyholm/psr7 -W
use AmazonPHP\SellingPartner\AccessToken;
use AmazonPHP\SellingPartner\Model\Feeds\CreateFeedDocumentSpecification;
use AmazonPHP\SellingPartner\Model\Feeds\CreateFeedSpecification;
use AmazonPHP\SellingPartner\Regions;
use AmazonPHP\SellingPartner\SellingPartnerSDK;
use App\Models\Amazon\AmazonAuth;
use Exception;
use GuzzleHttp\Handler\CurlFactory;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\App;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
use GuzzleHttp\Client;
use AmazonPHP\SellingPartner\Api\FeedsApi\FeedsSDK;
use AmazonPHP\SellingPartner\Configuration;
use AmazonPHP\SellingPartner\Exception\ApiException;
use AmazonPHP\SellingPartner\Exception\InvalidArgumentException;
use AmazonPHP\SellingPartner\HttpFactory;
use AmazonPHP\SellingPartner\HttpSignatureHeaders;
use AmazonPHP\SellingPartner\ObjectSerializer;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Log\LoggerInterface;
use AmazonPHP\SellingPartner\OAuth;
use Buzz\Client\Curl;
use Nyholm\Psr7\Factory\Psr17Factory;
use Psr\Log\NullLogger;
public function getAccessToken($channelId)
{
$amazonAuth = AmazonAuth::where('channel_id', $channelId)->first(); //my local database channel
$isExpired = Carbon::parse($amazonAuth->expires_at)->isPast();
$token = null;
if ($isExpired) {
$data = [
'grant_type' => 'refresh_token',
'refresh_token' => $amazonAuth->refresh_token,
'client_id' => TenantService::getLwaClientId(),
'client_secret' => TenantService::getLwaSecret()
];
$response = Http::asForm()->post(
'https://api.amazon.com/auth/o2/token',
$data
);
if ($response->successful()) {
$body = $response->json();
$amazonAuth->access_token = $body['access_token'];
$amazonAuth->expires_at = now()->addSeconds($body['expires_in'])->toDateTimeString();
$amazonAuth->save();
$token = $body['access_token'];
} else {
Log::channel('amazon')->info('Amazon access token refresh failed for amazon auth id: ' . $amazonAuth->id);
}
} else {
$token = $amazonAuth->access_token;
}
if ($token) {
$accessToken = new AccessToken(
$token,
$amazonAuth->refresh_token,
'refresh_token',
(int) $amazonAuth->expires_in,
'refresh_token'
);
return $accessToken;
}
}
public function createFeedTest()
{
$client = new Client();
$factory = new Psr17Factory();
// $httpFactory = new HttpFactory($factory, $factory);
$region = 'eu';
$accessToken = $this->getAccessToken(150);
$logger = new NullLogger();
$configuration = Configuration::forIAMUser(
$getLwaClientId,
$getLwaSecret,
$getAwsAccessKey,
$getAwsSecretKey,
);
$sdk = SellingPartnerSDK::create($client, $factory, $factory, $configuration, $logger);
$region = Regions::EUROPE;
$specification = new CreateFeedDocumentSpecification;
$feedDoc = $sdk->feeds()->createFeedDocument(
$accessToken,
$region,
$specification->setContentType('text/xml; charset=utf-8')
);
$feedDocID = $feedDoc['feed_document_id'];
$urlFeedUpload = $feedDoc['url'];
$fileContent = '<?xml version="1.0" encoding="utf-8" ?>
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
<Header>
<DocumentVersion>1.01</DocumentVersion>
<MerchantIdentifier>MYMERCHANTTOKEN</MerchantIdentifier>
</Header>
<MessageType>Product</MessageType>
<PurgeAndReplace>false</PurgeAndReplace>
<Message>
<MessageID>1</MessageID>
<OperationType>Update</OperationType>
<Product>
<SKU>56789</SKU>
<StandardProductID>
<Type>ASIN</Type>
<Value>B0EXAMPLEG</Value>
</StandardProductID>
<ProductTaxCode>A_GEN_NOTAX</ProductTaxCode>
<DescriptionData>
<Title>Example Product Title</Title>
<Brand>Example Product Brand</Brand>
<Description>This is an example product description.</Description>
<BulletPoint>Example Bullet Point 1</BulletPoint>
<BulletPoint>Example Bullet Point 2</BulletPoint>
<MSRP currency="USD">25.19</MSRP>
<Manufacturer>Example Product Manufacturer</Manufacturer>
<ItemType>example-item-type</ItemType>
<CountryOfOrigin>DE</CountryOfOrigin>
<UnitCount>1</UnitCount>
<PPUCountType>stück</PPUCountType>
<IsExpirationDatedProduct>false</IsExpirationDatedProduct>
</DescriptionData>
<ProductData>
<Health>
<ProductType>
<HealthMisc>
<Ingredients>Example Ingredients</Ingredients>
<Directions>Example Directions</Directions>
</HealthMisc>
</ProductType>
</Health>
</ProductData>
<IsHeatSensitive>false</IsHeatSensitive>
</Product>
</Message>
</AmazonEnvelope>';
dump($feedDoc);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $urlFeedUpload);
curl_setopt($curl, CURLOPT_UPLOAD, true);
curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml; charset=utf-8'));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_BINARYTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_PUT, 1);
curl_setopt($curl, CURLOPT_INFILE, fopen('data://text/plain,' . $fileContent, 'r'));
curl_setopt($curl, CURLOPT_INFILESIZE, strlen($fileContent));
#Only use below option on TEST environment if you have a self-signed certificate!!! On production this can cause security issues
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($curl);
dump($response);
curl_close($curl);
$specificationNewFeed = new CreateFeedSpecification([
'feed_type' => 'POST_PRODUCT_DATA',
'marketplace_ids' => ['A1PA6795UKMFR9'],
'input_feed_document_id' => $feedDocID
]);
$responseFeed = $sdk->feeds()->createFeed(
$accessToken,
$region,
$specificationNewFeed
);
dd($responseFeed);
}
评论