PHP-Laravel Amazon feed XML Parsing Fatal Error at Line 1, Column 1

87 views Asked by At
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
<Header>
<DocumentVersion>1.02</DocumentVersion>
<MerchantIdentifier>MYMERCHANT123</MerchantIdentifier>
</Header>
<MessageType>ProcessingReport</MessageType>
<Message>
<MessageID>1</MessageID>
<ProcessingReport>
<DocumentTransactionID>172210019669</DocumentTransactionID>
<StatusCode>Complete</StatusCode>
<ProcessingSummary>
<MessagesProcessed>0</MessagesProcessed>
<MessagesSuccessful>0</MessagesSuccessful>
<MessagesWithError>1</MessagesWithError>
<MessagesWithWarning>0</MessagesWithWarning>
</ProcessingSummary>
<Result>
<MessageID>0</MessageID>
<ResultCode>Error</ResultCode>
<ResultMessageCode>5001</ResultMessageCode>
<ResultDescription>XML Parsing Fatal Error at Line 1, Column 1: Content is not allowed in prolog. Content is not allowed in prolog.</ResultDescription>
</Result>
</ProcessingReport>
</Message>
</AmazonEnvelope>

That is my error, and this is my code:

$requestXml = '<?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>MYMERCHANT123</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>';

   // $requestXml = trim($requestXml); // doesn't help
  // $requestXml = preg_replace('/[[:^print:]]/', '', $requestXml); 
 //  $requestXml = preg_replace('/^(\xEF\xBB\xBF)/', '', $requestXml);


  $response = Http::withHeaders([
    'Content-Type' => 'text/xml; charset=utf-8'
  ])->put($url, [
        'body' => $requestXml 
      ]);

I hope so that someone can help me, because I'm going crazy, because few days I can't fix this and it is important.

I tried everything that I found on internet, read everything on Stackoverflow and nothning solved my problem.

Also I tried to call it from file (that I saved through Notepad++ as UTF-8)

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

There are 1 answers

0
milica98 On BEST ANSWER

I found solution for this create product feed in Laravel with 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);
  }