使用 AWS SP-API 将 Feed 上传到 Amazon

问题描述 投票:0回答:1

我的问题是在url中上传xml文件,该文件应该在创建提要后上传,但每次我都会收到错误SignatureDoesNotMatch

我正在尝试上传从请求获取的url上的xml文件

createFeedDocument
https://developer-docs.amazon.com/sp-api/docs/feeds-api-v2021-06-30-use-case -指南

public function createFeedDocument($distributionChannelTeamId){
    $response = Http::withHeaders([
        'x-amz-access-token'=> $this->getAccessToken($distributionChannelTeamId),
    ])->post(
        'https://sellingpartnerapi-eu.amazon.com/feeds/2021-06-30/documents',
        [
            "contentType"=> "text/tab-separated-values; charset=UTF-8"
        ]
    );

    Log::channel('amazon')->info('Amazon create feed document response: ', ['response' => $response->json()]);
    if($response->successful()){
        $body = $response->json();
        return $body;
    }else{
        Log::channel('amazon')->info('Amazon create feed document failed for distribution channel team id: ' . $distributionChannelTeamId);
        Log::channel('amazon')->info('Amazon create feed document failed: ', ['response' => $response->json()]);
    }

}`

然后我在我的真实销售帐户上收到这样的回复(这是示例)

{
    "feedDocumentId": "3d4e42b5-1d6e-44e8-a89c-2abfca0625bb",
    "url": "https://d34o8swod1owfl.cloudfront.net/Feed_101__POST_PRODUCT_DATA_.xml"
}

然后我想创建Feed并按照文档所述上传它:

public function createFeed($distributionChannelTeamId)
{
    $res=$this->createFeedDocument($distributionChannelTeamId);

    $url=$res['url'];
    $feedDocumentId=$res['feedDocumentId'];

    $prefixAmazon = Storage::disk('amazon')->getDriver()->getAdapter()->getPathPrefix();
    $requestXml = file_get_contents($prefixAmazon . 'createFeed.xml');

    $response = Http::withHeaders(['Content-Type' => 'text/xml'
    ])->post($url,[
        'body' => utf8_encode($requestXml)
    ]);

    Log::channel('amazon')->info('Amazon create feed response: ', ['response' => $response]);`

我在日志中得到了这个,因为请求是

403
,亚马逊创建提要响应:

{“响应”:{“照亮\ Http \ Client \ Response”:“

SignatureDoesNotMatch
我们计算的请求签名与您提供的签名不匹配。检查您的密钥和签名方法。AKIAX2ZVOZFBB4YFXXHUAWS4-HMAC-SHA256 20231031T161031Z 20231031/eu-west-1/s3/aws4_request 93dfa574b9431ebb05c7c80589f14946f14741d8ca907ea25415a1891c25965aa30675d84223aea473a1772fc2907ce7a9c7b3a4575aad2ee2549665992d86684 1 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 33 31 30 33 31 54 31 36 31 30 33 31 5a 0a 32 30 32 33 31 30 33 3 1 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 39 33 64 66 61 35 37 34 62 39 34 33 31 65 62 62 30 35 63 37 63 3 8 30 35 38 39 66 31 34 39 34 36 66 31 34 37 34 31 64 38 63 61 39 30 37 65 61 32 35 34 31 35 61 31 38 39 31 63 32 35 39 36 35 61POST /3e46e671-9500-49c1-8920-18a4732287c9.amzn1.tortuga.4.eu.T1OBJR31A06AXO X-Amz-算法=AWS4-HMAC-SHA256&X-Amz-凭证=AKIAX2ZVOZFBB4YFXXHU%2F20231031%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20231031T161031Z&X-Amz-Expires=300&X-Amz-Sign edHeaders=内容类型% 3B主机 内容类型:文本/xml 主机:tortuga-prod-eu.s3-eu-west-1.amazonaws.com 内容类型;主机 无符号有效负载50 4f 53 54 0a 2f 33 65 34 36 65 36 37 31 2d 39 35 30 30 2d 34 39 63 31 2d 38 39 32 30 2d 31 38 61 34 37 33 32 32 3 8 37 63 39 2e 61 6d 7a 6e 31 2e 74 6f 72 74 75 67 61 2e 34 2e 65 75 2e 54 31 4f 42 4a 52 33 31 41 30 36 41 58 4f 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 6 9 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 4 2 42 34 59 46 58 58 48 55 25 32 46 32 30 32 33 31 30 33 31 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 7 3 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 33 31 30 33 31 54 31 36 31 30 33 31 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 33 30 30 26 58 2 d 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 6 5 3a 74 65 78 74 2f 78 6d 6c 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 6 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 44FEWNEPN090HEGZVTvmwWxEA8m50 0fDCGb9NXTXeYVrE+GRFih0e/3RppPUc243qA5QEA80aCCQsC0MM/h9gNQ4cUYGT0BLYboiz+mw=="}}

laravel amazon-web-services amazon-s3 aws-sdk amazon-selling-partner-api
1个回答
0
投票

我找到了使用 Amazon SP-API 在 Laravel 中创建产品源的解决方案 (https://github.com/amazon-php/sp-api-sdk):

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>A2BSV6DB9TAL2</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);
  }
© www.soinside.com 2019 - 2024. All rights reserved.