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

oknrviil  于 2023-11-20  发布在  其他
关注(0)|答案(1)|浏览(153)

我的问题是上传xml文件在url应该上传后饲料创建,但每次我得到错误SignatureDoesNotMatch
我试图上传xml文件的网址,我从请求createFeedDocumenthttps://developer-docs.amazon.com/sp-api/docs/feeds-api-v2021-06-30-use-case-guide

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"
}


然后我想把它上传到文件中,就像docs说的:

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,亚马逊创建提要响应:
{"response ":{" Illuminate\Http\Client\Response":"SignatureDoesNotMatch我们计算的请求签名与您提供的签名不匹配。请检查您的密钥和签名方法。AKIAX2ZVOZFBB4YFXXHUAWS4-HMAC-SHA256 20231031T161031Z 20231031/eu-west-1/s3/aws4_请求93dfa574b9431ebb05c7c80589f14946f14741d8ca907ea25415a189 1c25965aa30675d84223aea473a1772fc2907ce7a9c7b3a4575aad2ee 2549665992d866841 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 33 31 30 30 3 31 54 31 36 31 30 33 31 5a 0a 32 30 32 33 31 30 33 31 2f 65 75 2d 77 65 73 74 2d 31 2f 73 33 2f 61 77 7 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 37 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-Algorithm = AWS4-HMAC-SHA256 & X-Amz-Credential = AKIAX2ZVOZFBB4YFXXHU % 2F20231031% 2Feu-west-1%2Fs3% 2Faws4_request & X-Amz-Date = 20231031T161031Z & X-Amz-Expires = 300 & X-Amz-SignedHeaders = content-type % 3B主机content-type:text/xml主机:www.example.com content-type;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 2d 34 39 63 31 2d 38 39 32 30 2d 31 38 61 34 37 33 32 32 38 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 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 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 73 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 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 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 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 44FEWNEPN090HEGZVTvmwWxEA8m500fDCGb9NXTXeYVrE + GRFih0e/3RppPUc243qA5QEA80aCCQsC0MM/h9gNQ4cUYGT0BLYboiz + mw =="}}

sd2nnvve

sd2nnvve1#

我在Laravel中使用Amazon SP-API(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>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);
  }

字符串

相关问题