Quantcast
Channel: Active questions tagged feed - Stack Overflow
Viewing all articles
Browse latest Browse all 547

Amazon SPI - upload invoices via CreateFeed call (NOT MWS)

$
0
0

I try to use the Amazon Sellers Central SPI API. For our most use cases it works. But I have problems to upload a pdf invoice via creatFeed call. The support does not helps in that cases and the documentation is not described very well:https://developer-docs.amazon.com/sp-api/docs/option-3-upload-invoices-through-sp-api-or-seller-central

I tried to take over the java example into php code, but it doesnt work. I get the positive response from the first createFeedDocument request with a valid feedDocumentId and url. If I try to post the next curl request to upload the pdf document to the feedDocumentId, I've got an error.

I hope that somebody can help and solved the same issue in the past. I'm happy to share other amazon spi knowledge.

Here are some details:

URL: https://sellingpartnerapi-eu.amazon.com/feeds/2021-06-30/feeds

   HEADER: Array(    [0] => authorization:AWS4-HMAC-SHA256 Credential=890938122806/20240206/eu-west-1/execute-api/aws4_request,SignedHeaders=host;user-agent;x-amz-access-token;x-amz-date,Signature=ba24f98ea3f09247aeb43bdd1c6d9fcdbcaa530c979a4746931b98aa48475213    [1] => content-type:application/json;charset=utf-8    [2] => host:sellingpartnerapi-eu.amazon.com    [3] => user-agent:Backend mit amazonuk@example.de    [4] => x-amz-access-token:Atza|IwEBIC0NgdF25X5W1-8i1KMazp_obJEkr_1tb6DxEXXXXXXXXXXXXXXXXXXXXyGiU3cVAsyiDHZWl_qJZnOr0UTfKCe0CZWRJmlC2v2Rp2nIEsSXXXXXXXXXXXXXXXQMR57FjYlmMCwcq7knR5_02Z3zJtStR-QFejZw63aPE7e-LIePQPYVW7AlIaZpP4LJ4bXXXXXXXXXXXV997VANMEctK_7r5ZJpzwzV_Bqz2wYXXXXXXXXXdWoxrV6iadSx-j3cTzb6LPtgeB-7S2QFu8s7ftf05Xukk0XXXXXXXXXXXIHW0v7_kWFhehVzBhQqYDEklUMRCXaAbXXXXXXX3RWWZKr1K10s7ZQ    [5] => x-amz-date:20240206T165714Z)POST-Parameters:         {"feedType": "UPLOAD_VAT_INVOICE",          "marketplaceIds": ["A1RKKUPIHCS9HS"          ],"pdfDocument": "%PDF-1.33 0 obj<</Type /Page/Parent 1 0 R/Resources 2 0 R/Contents 4 0 R>>endobj4 0 obj<</Filter /FlateDecode /Length 1096>>streamxœÍVKoÛF¾çWÌ1¥Õ>I®•`ùÛk).....(PDF as base64_encode String)","ContentMd5": "1B2M2Y8AsgTpgAmY7PhCfg==","feedOptions":          {"OrderId":"404-9999999-3381931","DocumentType":"Invoice","InvoiceNumber":24010109016131          },"inputFeedDocumentId":"amzn1.tortuga.4.eu.94cdaac5-977b-4dd4-8788-56cbd7bf5d35.T5U9S8ENNFPG4"          }    Response:{"errors": [    {"code": "InvalidInput","message": "Could not process payload","details": ""    }  ]}PHP-Code:    $post = '        {"feedType": "'.$feedType.'",          "marketplaceIds": ["'.$marketplaceIds.'"          ],"pdfDocument": "'.$FeedContent.'","ContentMd5": "'.$ContentMd5.'","feedOptions":          {"OrderId":"'.$feedOptions['OrderId'].'","DocumentType":"'.$feedOptions['DocumentType'].'","InvoiceNumber":'.$feedOptions['InvoiceNumber'].'          },"inputFeedDocumentId":"'.$inputFeedDocumentId.'"          }    ';    $url = '/feeds/2021-06-30/feeds';    $result = amazonRequest($connect_SPI, $method, $url, $qs, $post);PHP FUNCTIONS:// ######################## Standard functions - connection of Amazon API #######################function httpRequest($url, $post = '', $header = null, &$status = null) {    $ch = curl_init($url);    curl_setopt_array($ch, [        CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2,        CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true,        CURLOPT_CONNECTTIMEOUT => 5,    ]);    if ($post) curl_setopt($ch, CURLOPT_POSTFIELDS, $post);    if ($header) curl_setopt($ch, CURLOPT_HTTPHEADER, $header);    $out = curl_exec($ch);    if (curl_errno($ch)) exit('Error: ' . curl_error($ch));    if ($status !== null) $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);    return $out;}function getAccessToken($connect_SPI) {    $date = gmdate('Ymd\THis\Z');    //Amazon Doku für Tokenverwendung: https://developer-docs.amazon.com/sp-api/docs/connecting-to-the-selling-partner-api#step-1-request-a-login-with-amazon-access-token    //Je Account wird ein eigener Token benötigt - sonst Grant Permission Fehler    $token_cache = $_SERVER["DOCUMENT_ROOT"] . '/temp/amazon/amazon-access-token_'.$connect_SPI['amazon_spi.SPI_IAM_USER_KEY'].'.json';    // Return existing access token if exists and not expired    if (file_exists($token_cache)) {        $file = file_get_contents($token_cache);        $json = json_decode($file, true);        if ($json && !empty($json['token'])) {            if (!empty($json['expires']) && time() < $json['expires']) {                return $json['token'];            }        }    }    // Otherwise get new access token    $post = 'grant_type=refresh_token&refresh_token=' . $connect_SPI['amazon_spi.SPI_APP_REFRESH_TOKEN']        . '&client_id=' . $connect_SPI['amazon_spi.SPI_APP_LWA_ID'] . '&client_secret=' . $connect_SPI['amazon_spi.SPI_APP_LWA_SECRET'];    $url = 'https://api.amazon.com/auth/o2/token';    $header = ['user-agent:' . 'Samurai-Backend mit '.$connect_SPI['amazon_spi.login_web_user']];    $response = httpRequest($url, $post, $header);    // Validate new access token response    if (strpos($response, '{"access_token":') !== 0) {        $out_file = 'Error: Access token response was bad: ' . $response. '\n\r'.' Poststring:'.$post.'\n\r'."URL: ".$url.'\n\r'."Header: ".$header;        if(!file_put_contents($_SERVER['DOCUMENT_ROOT']."/temp/amazon/response/".$date.".log", $out_file)){            echo 'Logfile kann nicht erstellt werden: ' . $_SERVER['DOCUMENT_ROOT']."/temp/amazon/response/".$date.".log";        }        exit('Error: Access token response was bad: ' . $response. '\n\r'.' Poststring:'.$post.'\n\r'."Header: ".print_r($header, true));    }    if (strpos($response, 'expires_in') === false) {        exit('Error: No "expires_in" found in response: ' . $response);    }    $json = json_decode($response, true);    if (!$json || empty($json['access_token']) || empty($json['expires_in'])) {        exit('Error: Access token JSON decode failure: ' . $response);    }    // Cache access token with an expires timestamp    $cacheData = json_encode(['token' => $json['access_token'],'expires' => time() + $json['expires_in'],    ]);    file_put_contents($token_cache, $cacheData);    // Return access token    return $json['access_token'];}function amazonRequest($connect_SPI, $method, $path, $qs = '', $post = '') {    // Get access token    $accessToken = getAccessToken($connect_SPI);    // Two formats for date used throughout    $date = gmdate('Ymd\THis\Z');    $ymd = gmdate('Ymd');    // Build a canonical request. This is just a highly-structured and    // ordered version of the request you will be making. Each part is    // newline-separated. The number of headers is variable, but this    // uses four headers. Headers must be in alphabetical order.    $canonicalRequest = $method . "\n" // HTTP method        . $path . "\n" //  Path component of the URL        . $qs . "\n" // Query string component of the URL (without '?')        . 'host:' . $connect_SPI['amazon_spi.SPI_serviceUrl'] . "\n" // Header        . 'user-agent:' . 'Samurai-Backend mit '.$connect_SPI['amazon_spi.login_web_user'] . "\n" // Header        . 'x-amz-access-token:' . $accessToken . "\n" // Header        . 'x-amz-date:' . $date . "\n" // Header        . "\n" // A newline is needed here after the headers        . 'host;user-agent;x-amz-access-token;x-amz-date' . "\n" // Header names        //. 'x-amzn-api-sandbox: dynamic{' . "\n" // Header        . hash('sha256', $post). "\n" // Hash of the payload (empty string okay)        . '}'. "\n";    //echo $canonicalRequest;    // Create signing key, which is hashed four times, each time adding    // more data to the key. Don't ask me why Amazon does it this way.    $signKey = hash_hmac('sha256', $ymd, 'AWS4' . $connect_SPI['amazon_spi.SPI_AWS_ACCESS_KEY_ID'], true);    $signKey = hash_hmac('sha256', $connect_SPI['amazon_spi.SPI_REGION'], $signKey, true);    $signKey = hash_hmac('sha256', 'execute-api', $signKey, true);    $signKey = hash_hmac('sha256', 'aws4_request', $signKey, true);    // Create a String-to-Sign, which indicates the hash that is used and    // some data about the request, including the canonical request from above.    $stringToSign = 'AWS4-HMAC-SHA256' . "\n"        . $date . "\n"        . $ymd . '/' . $connect_SPI['amazon_spi.SPI_REGION'] . '/execute-api/aws4_request' . "\n"        . hash('sha256', $canonicalRequest);    // Sign the string with the key, which will create the signature    // you'll need for the authorization header.    $signature = hash_hmac('sha256', $stringToSign, $signKey);    // Create Authorization header, which is the final step. It does NOT use    // newlines to separate the data; it is all one line, just broken up here    // for easier reading.    $authorization = 'AWS4-HMAC-SHA256 '        . 'Credential=' . $connect_SPI['amazon_spi.SPI_IAM_USER_KEY'] . '/' . $ymd . '/'        . $connect_SPI['amazon_spi.SPI_REGION'] . '/execute-api/aws4_request,'        . 'SignedHeaders=host;user-agent;x-amz-access-token;x-amz-date,'        . 'Signature=' . $signature;    // Create the header array for the cURL request. The headers must be    // in alphabetical order. You must include all of the headers that were    // in the canonical request above, plus you add in the authorization header    // and an optional content-type header (for POST requests with JSON payload).    $headers = [];    $headers[] = 'authorization:' . $authorization;    if ($post) $headers[] = 'content-type:application/json;charset=utf-8';    $headers[] = 'host:' . $connect_SPI['amazon_spi.SPI_serviceUrl'];    $headers[] = 'user-agent:' . 'Samurai-Backend mit '.$connect_SPI['amazon_spi.login_web_user'];    $headers[] = 'x-amz-access-token:' . $accessToken;    $headers[] = 'x-amz-date:' . $date;    // Run the http request and capture the status code    $status = '';    if(!strpos($path, 'http')){        $fullUrl = 'https://'. $connect_SPI['amazon_spi.SPI_serviceUrl'] . $path . ($qs ? '?' . $qs : '');    }else{ //wenn ein voller Pfad mitgegeben wird diesen verwenden - benötigt u.a. bei createFeedDocument --> URL        $fullUrl = $path . ($qs ? '?' . $qs : '');    }    echo $log_file = "HEADER: " . print_r($headers, true)."\n\r"."POST: " . $post."\n\r"."canonicalRequest:".$canonicalRequest."\n\r"."fullUrl:".$fullUrl."\n\r";    $result = httpRequest($fullUrl, $post, $headers, $status);    echo $log_file .= "Response:".$result;    if(!file_put_contents($_SERVER['DOCUMENT_ROOT']."/temp/amazon/response/".$date.".log", $log_file)){        echo 'Logfile kann nicht erstellt werden: ' . $_SERVER['DOCUMENT_ROOT']."/temp/amazon/response/".$date.".log";    }    if (strpos($result, 'Error:') === 0){        echo "Fehler:". $result;    }    //if (empty($result)) exit('Error: Empty response');    //if ($status != 200 || $status != 202 || $status != '') exit('Error: Status code ' . $status . ': ' . $result);    //if (strpos($result, '{') !== 0) exit('Error: Invalid JSON: ' . $result);    // Decode json and return it    $json = json_decode($result, true);    if (!$json) exit('Error: Problem decoding JSON: ' . $result);    return $json;}

For a positive response I expect a json like this:

{"feedId": "23492394"}


Viewing all articles
Browse latest Browse all 547

Latest Images

Trending Articles



Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>