Closed girija45701 closed 3 years ago
Maybe you set wrong content type for your request?
The Content-Type for uploading the encrypted feed should match the Content-Type mentioned in createFeedDocument request.
Hi,
Thanks for reply. I am using the content types as "text/tab-separated-values; charset=UTF-8" in both while calling the CreateFeedDocument and the same is set on the RestRequest.
Here is the full error response:
`<?xml version="1.0" encoding="UTF-8"?>
SignatureDoesNotMatch
I have the same problem but in the createFeedDocument request.
Besides this I am a bit lost, what data should I load in the url that the previous request will return to me?
Not sure the exact issue you are having in CreateFeeddocument but here is an thread to look at:
https://github.com/amzn/selling-partner-api-models/issues/699
Hello to all. I have probably found the solution to upload documents. The following things are important:
The ContentType for the request to the predefined URL must be the same as the one specified for the createFeedDocument method.
The file must be AES 256 encrypted with the padding "PKSC5". BTW for the PHP developers among us: In PHP PKSC5 for AES encryption is not possible using the openssl function. I can provide assistance on request.
The key and the IV from the createFeedDocument method response must be decoded base64 before it can be used for encryption.
After the content of the file has been encrypted I encode it back into base64 and temporarily save the file and retrieve it as a resource.
The request method must be "PUT", not "POST"!
I then add the "resource" as body to the request and execute the request (important with PUT!). I get a response with 0 content length, but with HTTP status 200.
Enclosed my implementation using PHP:
<?php
// get amazon feed object
$AmazonFeeds = new Feeds\AmazonFeeds($AmazonAuth);
// example values
$tmpFileDirectory = dirname(__FILE__) .'/amazon/temp/request/';
$tmpFileprefix = 'amz_feeds';
$tmpFilename = 'random';
$tmpFilesuffix = 'tmp';
$contentType = 'application/pdf';
// do createFeedDocument - operation
$createFeedDocumentSpecifications = new Feeds\CreateFeedDocumentSpecification();
$createFeedDocumentSpecifications->contentType = $contentType;
$createFeedDocumentSpecifications = $AmazonFeeds->createFeedDocument($createFeedDocumentSpecifications);
// if createFeedDocument - operation Status 200 - OK
if (!is_null($createFeedDocumentSpecifications->Response)) {
// get payload
$createFeedDocumentSpecifications = $createFeedDocumentSpecifications->Response;
// get values
$url = $createFeedDocumentSpecifications->url;
// base64 decode
$initializationVector = base64_decode($createFeedDocumentSpecifications->encryptionDetails->initializationVector, true);
$key = base64_decode($createFeedDocumentSpecifications->encryptionDetails->key, true);
// get file to upload
$file = file_get_contents('test/dummy.pdf');
$fileResourceType = gettype($file);
// resource or string ? make it to a string
if ($fileResourceType == 'resource') {
$file = stream_get_contents($file);
} elseif ($fileResourceType == 'string') {
$file = $file;
}
// utf8 !
$UTF8Handler = new UTF8($file,true);
$file = $UTF8Handler->str;
// encrypt string and get value as base64 encoded string
$encryptedFile = xxxxxx\AESCryptoStreamFactory::encrypt($file, $key, $initializationVector);
$encryptedFilename = $tmpFileDirectory.$tmpFileprefix.'_'.$tmpFilename.'_enc_'.time().'.'.$tmpFilesuffix;
file_put_contents($encryptedFilename, $encryptedFile);
$resourceFile = fopen($encryptedFilename,'r');
// my http client
$client = new GuzzleHttp\Client(['exceptions' => false]);
$request = new Request(
// PUT!
'PUT',
// predefined url
$url,
// content type equal to content type from response createFeedDocument-operation
array('Content-Type' => $contentType),
// resource File
$resourceFile
);
$response = $client->send($request);
$HTTPStatusCode = $response->getStatusCode();
if($HTTPStatusCode == 200){
unlink($encryptedFilename);
}else{
$error = $response->getBody()->getContents();
}
hi @JacksonJeans ,
Can you please share a sample template that you were able to successfully upload. The problem i am facing is while creating the file with the first line. When i download the template from Amazon Seller central the first line has a Template Signature and template identifier like below:
TemplateSignature=Qk9EWV9DQVJFX1BST0RVQ1QsU01BTExfSE9NRV9BUFBMSUFOQ0VT
settings=contentLanguageTag=en_IN&feedType=113&headerLanguageTag=en_IN&templateIdentifier=926f3561-686e-40b7-89a5-7d153dc199bf×tamp=2020-11-17T15%3A20%3A04.970Z
My doubt is if i am generating the template at runtime, how the signature and identifier can be set.
Thanks in advance.
Hi @girija45701 ,
I suspect you are trying to upload FLAT_FILE
.
In my experience I did not have to generate values for TemplateSignature
or others, neither before nor during runtime. I use these templates on Amazon DE: Amazon DE Help
and the templates we have already successfully uploaded are these (Amazon Germany Templatefiles):
Which template are you trying to use and upload?
Hi @JacksonJeans ,
Yes, i am using Flat file.
I am using Category specific templates (Kategoriespezifische Lagerbestandsdateivorlagen). I went to my seller account and downloaded the templates and the header look like this.
Note : The version is different
Hey @girija45701 ,
There is a separate Flat_File
for each category. The top line must not be changed. The data stored there in the header is generated by Amazon.
In my Flat_Files
of the category-specific stocks the top line still contains the friendly hint from Amazon:
"The top three lines are for use by Amazon.de only. Do not change or delete the top three lines.".
Hi @JacksonJeans,
Yes, Just wondering what is the correct template for product. I see many different based on the region.
Hi all,
I just tried uploading through code and am getting Unauthorized exception. The createDocument returns fine. I then use to encrypt and upload the file. that works fine.
When i call the createFeed, it fails and throws me the exception. I use the same set of token that i use for createDocument (as i assume we will reuse the token as it is valid for 1 hour). The error i get is :
'Error calling CreateFeed: { "errors": [ { "code": "Unauthorized", "message": "Access to the resource is forbidden", "details": "" } ] }
Has anyone encountered this error or has any pointers.
Unfortunately I cannot tell you which product template you need. I just program the crap here,... our sales department is busy uploading the documents. ^^
But I can tell you that your POST request (createFeed) is broken. I had the same problem when my URI and canonical URI ended with "/".
So that I can say more about it, as well as others here, we need some more information. What does your request look like? Would you like to show us your canonical request and your HTTP body?
Please close the Issue if you now manage to upload documents. I will also reply to your new Issue at amzn/selling-partner-api-models#780 .
Closed the issue as the uploading is solved
Hi @girija45701 Could you please share the code that worked for you to upload the Feeds? I am getting the same error "Access to the resource is forbidden". Thank you
Hi @girija45701 Could you please share the code that worked for you to upload the Feeds? I am getting the same error "Access to the resource is forbidden". Thank you
Hi Netrush,
Still not able to work that out. Discussion is on another thread amzn/selling-partner-api-models#780
Hello to all. I have probably found the solution to upload documents. The following things are important:
The ContentType for the request to the predefined URL must be the same as the one specified for the createFeedDocument method.
The file must be AES 256 encrypted with the padding "PKSC5". BTW for the PHP developers among us: In PHP PKSC5 for AES encryption is not possible using the openssl function. I can provide assistance on request.
The key and the IV from the createFeedDocument method response must be decoded base64 before it can be used for encryption.
After the content of the file has been encrypted I encode it back into base64 and temporarily save the file and retrieve it as a resource.
The request method must be "PUT", not "POST"!
I then add the "resource" as body to the request and execute the request (important with PUT!). I get a response with 0 content length, but with HTTP status 200.
Enclosed my implementation using PHP:
<?php // get amazon feed object $AmazonFeeds = new Feeds\AmazonFeeds($AmazonAuth); // example values $tmpFileDirectory = dirname(__FILE__) .'/amazon/temp/request/'; $tmpFileprefix = 'amz_feeds'; $tmpFilename = 'random'; $tmpFilesuffix = 'tmp'; $contentType = 'application/pdf'; // do createFeedDocument - operation $createFeedDocumentSpecifications = new Feeds\CreateFeedDocumentSpecification(); $createFeedDocumentSpecifications->contentType = $contentType; $createFeedDocumentSpecifications = $AmazonFeeds->createFeedDocument($createFeedDocumentSpecifications); // if createFeedDocument - operation Status 200 - OK if (!is_null($createFeedDocumentSpecifications->Response)) { // get payload $createFeedDocumentSpecifications = $createFeedDocumentSpecifications->Response; // get values $url = $createFeedDocumentSpecifications->url; // base64 decode $initializationVector = base64_decode($createFeedDocumentSpecifications->encryptionDetails->initializationVector, true); $key = base64_decode($createFeedDocumentSpecifications->encryptionDetails->key, true); // get file to upload $file = file_get_contents('test/dummy.pdf'); $fileResourceType = gettype($file); // resource or string ? make it to a string if ($fileResourceType == 'resource') { $file = stream_get_contents($file); } elseif ($fileResourceType == 'string') { $file = $file; } // utf8 ! $UTF8Handler = new UTF8($file,true); $file = $UTF8Handler->str; // encrypt string and get value as base64 encoded string $encryptedFile = xxxxxx\AESCryptoStreamFactory::encrypt($file, $key, $initializationVector); $encryptedFilename = $tmpFileDirectory.$tmpFileprefix.'_'.$tmpFilename.'_enc_'.time().'.'.$tmpFilesuffix; file_put_contents($encryptedFilename, $encryptedFile); $resourceFile = fopen($encryptedFilename,'r'); // my http client $client = new GuzzleHttp\Client(['exceptions' => false]); $request = new Request( // PUT! 'PUT', // predefined url $url, // content type equal to content type from response createFeedDocument-operation array('Content-Type' => $contentType), // resource File $resourceFile ); $response = $client->send($request); $HTTPStatusCode = $response->getStatusCode(); if($HTTPStatusCode == 200){ unlink($encryptedFilename); }else{ $error = $response->getBody()->getContents(); }
Hello to all. I have probably found the solution to upload documents. The following things are important:
The ContentType for the request to the predefined URL must be the same as the one specified for the createFeedDocument method.
The file must be AES 256 encrypted with the padding "PKSC5". BTW for the PHP developers among us: In PHP PKSC5 for AES encryption is not possible using the openssl function. I can provide assistance on request.
The key and the IV from the createFeedDocument method response must be decoded base64 before it can be used for encryption.
After the content of the file has been encrypted I encode it back into base64 and temporarily save the file and retrieve it as a resource.
The request method must be "PUT", not "POST"!
I then add the "resource" as body to the request and execute the request (important with PUT!). I get a response with 0 content length, but with HTTP status 200.
Enclosed my implementation using PHP:
<?php // get amazon feed object $AmazonFeeds = new Feeds\AmazonFeeds($AmazonAuth); // example values $tmpFileDirectory = dirname(__FILE__) .'/amazon/temp/request/'; $tmpFileprefix = 'amz_feeds'; $tmpFilename = 'random'; $tmpFilesuffix = 'tmp'; $contentType = 'application/pdf'; // do createFeedDocument - operation $createFeedDocumentSpecifications = new Feeds\CreateFeedDocumentSpecification(); $createFeedDocumentSpecifications->contentType = $contentType; $createFeedDocumentSpecifications = $AmazonFeeds->createFeedDocument($createFeedDocumentSpecifications); // if createFeedDocument - operation Status 200 - OK if (!is_null($createFeedDocumentSpecifications->Response)) { // get payload $createFeedDocumentSpecifications = $createFeedDocumentSpecifications->Response; // get values $url = $createFeedDocumentSpecifications->url; // base64 decode $initializationVector = base64_decode($createFeedDocumentSpecifications->encryptionDetails->initializationVector, true); $key = base64_decode($createFeedDocumentSpecifications->encryptionDetails->key, true); // get file to upload $file = file_get_contents('test/dummy.pdf'); $fileResourceType = gettype($file); // resource or string ? make it to a string if ($fileResourceType == 'resource') { $file = stream_get_contents($file); } elseif ($fileResourceType == 'string') { $file = $file; } // utf8 ! $UTF8Handler = new UTF8($file,true); $file = $UTF8Handler->str; // encrypt string and get value as base64 encoded string $encryptedFile = xxxxxx\AESCryptoStreamFactory::encrypt($file, $key, $initializationVector); $encryptedFilename = $tmpFileDirectory.$tmpFileprefix.'_'.$tmpFilename.'_enc_'.time().'.'.$tmpFilesuffix; file_put_contents($encryptedFilename, $encryptedFile); $resourceFile = fopen($encryptedFilename,'r'); // my http client $client = new GuzzleHttp\Client(['exceptions' => false]); $request = new Request( // PUT! 'PUT', // predefined url $url, // content type equal to content type from response createFeedDocument-operation array('Content-Type' => $contentType), // resource File $resourceFile ); $response = $client->send($request); $HTTPStatusCode = $response->getStatusCode(); if($HTTPStatusCode == 200){ unlink($encryptedFilename); }else{ $error = $response->getBody()->getContents(); }
Hi, @JacksonJeans !
Could you help me with AES 256 encryption with "PKSC5" padding in PHP?
How could I do it?
I would greatly appreciate your help.
Hello @roger-sanchez117 ,
Please click on my profile and send me an email.
Hello @JacksonJeans
I've already sent you an email. Thanks a lot.
I am trying to do the same thing in NodeJS but getting the same error. Is someone able to do that successfully in Node?
The main issue is encryption. NodeJs has a createCipheriv method from the inbuilt crypto class. It accepts IV with a length of 16 and key with a length of 32. Anything larger is returning an invalid length error. But the IV that I am getting from the SP-API call has a length of 24 and after converting it to base64 the buffer length becomes 32. The same issue is with key, the original buffer length is 44 and after converting it to base64 the buffer length becomes 60.
I guess this is the reason why I am getting an invalid signature.
Any help is much appreciated.
@akashkaushik33 ,
If your IV is still 24 bytes long then it will still be base64 encoded. Please decode the key and the IV before you use it to encrypt your data.
The key and the IV from the createFeedDocument method response must be decoded base64 before it can be used for encryption.
After the content of the file has been encrypted I encode it back into base64 and temporarily save the file and retrieve it as a resource.
@JacksonJeans Thanks for the heads-up. Once I decoded them they are usable for encrypting the file.
But I am still getting the signature match error. I am trying to create a new product listing and here is the file that I am using:
// nodejs uses PKCS autopadding
Here is my code for ref
// function to encrypt file
const _encryptFile = (content, IV, key, algorithm = 'aes-256-cbc') => {
try {
const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), Buffer.from(IV));
const crypted = Buffer.concat([cipher.update(Buffer.from(content)), cipher.final()]);
return crypted.toString('base64');
} catch (e) {
console.log('Error in encrypt file', e);
return e
}
};
// reading file in utf-8 format
const productFileInUTFFormat = fs.readFileSync('../sample-from-amazon.xlsx', 'utf-8');
// decoding base64 encoded key and IV to ASCII
const decodedKey = Buffer.from(encryptionDetails.key, 'base64').toString('ascii');
const decodedIV = Buffer.from(encryptionDetails.initializationVector, 'base64').toString('ascii');
// encrypting file in base64
const encryptedFileContent = _encryptFile(productFileInUTFFormat, decodedIV, decodedKey);
// saving encrypted base 64 content as a file
fs.writeFileSync('tempfile', encryptedFileContent);
console.log("path.join(__dirname, '../tempfile')", path.join(__dirname, '../../tempfile'));
// using the file as input stream for upload
const formData = {
my_file: fs.createReadStream(path.join(__dirname, '../../tempfile'))
};
// uploading file
request.put(url, {
formData,
headers: {
'Content-Type': 'text/tab-separated-values; charset=UTF-8'
}
}).then(resp => {
console.log('>>>>>>>', resp);
}).catch(e => {
console.log('Errrrrrr', e);
});
I am a novice in cryptography and could really use some help with this.
@akashkaushik33 ,
Did you specify exactly the same content-type
in the createFeedDocument-Operation as in the header of your PUT request and uses as url
the prefixed url from the response of the createFeedDocument-Operation??
Note: You transfer your resource as array [attr:val]. But you must transfer the resource as such. Otherwise your function will transfer an object containing a 'resource' although Amazon expects a resource here. I got the information from the Java example from Amazon, look at line 42 to 54:
var readStream = fs.createReadStream(filename);
// uploading file
request.put(url, {
readStream,
headers: {
'Content-Type': 'text/tab-separated-values; charset=UTF-8'
}
}).then(resp => {
console.log('>>>>>>>', resp);
}).catch(e => {
console.log('Errrrrrr', e);
});
Since you use Java anyway, you can also use the DocumentHelper directly from Amazon, which I can only recommend. I had to write a PHP implementation for it, see above, and it works as described there.
I got the same error as you describe only when I tried to transfer the data via POST or specified an incorrect content-type which was not the same as in the createFeedDocument transfer operation. I guess in the end it doesn't matter if the resource is valid or not, until you upload your document it will have nothing to do with the document itself, but with the method you transfer it.
Your encryption will no longer be a problem. It looks completely correct.
@JacksonJeans Thanks a ton. I think it just worked. Instead of sending the file as a stream I converted it all to a string and uploaded it. Got no errors, but the response was empty. I guess which is correct from the upper comments.
Thanks a lot, @JacksonJeans . Really appreciate your help.
@akashkaushik33 , Check the HTTP response status after uploading. If it is 200, then everything is okay. You do not get a response in the HTTPBody.
I am glad that I could help you.
@akashkaushik33 , @girija45701 ,
Correction of myself:
Please do not encode the encrypted content in base64
. This makes absolutely no sense at this point. We use base64
to make strings URLsafe. Since we send the file as a resource in the HTTPBody
via PUT
, the resource does not need to be urlsafe. I forgot to mention that this leads to FATAL state when calling the getFeed()
method after the createFeed()
method.
After fixing this by not encoding the encrypted content in base64
my feed documents are successfully processed with DONE.
The example code above still works if you pay attention to the things I describe here.
EDIT
Here is my solution and the example file I uploaded and the response from getFeedDocument: Issue:133
@JacksonJeans can you supply the php code about UTF8 AND xxxxxx\AESCryptoStreamFactory
@JacksonJeans GuzzleHttp\Exception\ClientException
Client error: PUT https://tortuga-prod-na.s3-external-1.amazonaws.com/%2FNinetyDays/amzn1.tortuga.3.a3f5cd28-a862-4fa1-862c-b17696fec904.T1996C2C4DI62X?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20201126T092230Z&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Expires=300&X-Amz-Credential=AKIA5U6MO6RAPIVEYJJ3%2F20201126%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=4a008d9a1962a6d70ede580687707ad5ce91f28a2cdef25a86a5c76637a48571
resulted in a 400 Bad Request
response: <?xml version="1.0" encoding="UTF-8"?> InvalidArgument
Client error: PUT https://tortuga-prod-na.s3-external-1.amazonaws.com//NinetyDays/amzn1.tortuga.3.4e1a43c8-8ad3-4e98-bb44-1c0e03ad1cdb.T1U6NC6HHDDUEQ?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20201126T105711Z&X-Amz-SignedHeaders=content-type;host&X-Amz-Expires=300&X-Amz-Credential=AKIA5U6MO6RAPIVEYJJ3/20201126/us-east-1/s3/aws4_request&X-Amz-Signature=c0b47181643096221eda80174d7cd680a1200c68c869f7a40020c5ff1f7d4bec
resulted in a 403 Forbidden
response: <?xml version="1.0" encoding="UTF-8"?> SignatureDoesNotMatch
@shaoruisky , I cannot publish the UTF8 class.
How to encode strings in UTF8 with PHP you will find very fast.
Please do not just copy code and hope it works, but understand it. I have a lot of answers here in several issues that have been duplicated. A simple search is enough to understand how to encrypt and decrypt.
As there are some defs in PHP who have problems to encrypt and decrypt documents. Here is my static class.
The following code works for decrypting and encrypting:
Usage:
/**
* Receive encrypted string
* @param string $plainText [required]
* - unencrypted text
* @param string $key [required]
* - Key to encrypt
* @param string $iv [required]
* - The salt value for the block to encrypt.
* @return string
* - encrypted string
*/
$enrypt = AESCryptoStreamFactory::encrypt($plainText , $key , $iv );
/**
* Receive decrypted string
* @param string $encryptedText
* - encoded text
* @param string $key
* - Key to decrypt
* @param string $iv
* - The Salt value for the block to decrypt.
* @return string
* - decrypted string
*/
$decrypt = AESCryptoStreamFactory::decrypt($encryptedString, $key , $iv );
Class::
class AESCryptoStreamFactory
{
/**
* default: 8 fpr PKSC5!
* - The block size is a property of the used cipher algorithm.
* For AES it is always 16 bytes. So strictly speaking,
* PKCS5Padding cannot be used with AES since it is defined only for a block size of 8 bytes.
*
* The only difference between these padding schemes is that PKCS7Padding has the block size as a parameter,
* while for PKCS5Padding it is fixed at 8 bytes. When the Block size is 8 bytes they do exactly the same.
*
* - Eigene Implementierung durch getPaddedText();.
*
* - https://crypto.stackexchange.com/questions/43489/how-does-aes-ctr-pkcs5padding-works-when-the-bits-to-encrypt-is-more-than-8-bits
* - https://stackoverflow.com/questions/20770072/aes-cbc-pkcs5padding-vs-aes-cbc-pkcs7padding-with-256-key-size-performance-java/20770158
*/
public const BLOCK_SIZE = 8;
/**
* default: 16
* @var int IV_LENGTH
* - Vektor mit Zufallsdaten
*/
public const IV_LENGTH = 16;
/**
* default: AES256
* @var string CIPHER
* - Verschlüsselung
*/
public const CIPHER = 'AES256';
/**
* Erhalte aufegfüllten Text
* @param string $plainText
* - Plain Text
* @return string $plainText
* - aufgefüllter Plain Text
*/
protected static function getPaddedText(string $plainText): string
{
$stringLength = strlen($plainText);
if ($stringLength % static::BLOCK_SIZE) {
$plainText = str_pad($plainText, $stringLength + static::BLOCK_SIZE - $stringLength % static::BLOCK_SIZE, "\0");
}
return $plainText;
}
/**
* Erhalte verschlüsselten String
* @param string $plainText [required]
* - unverschlüsselter Text
* @param string $key [required]
* - Schlüssel zum verschlüsseln
* @param string $iv [required]
* - Der Salt Wert für den Block zum verschlüsseln.
* @return string
* - verschlüsselten String
*/
public static function encrypt(string $plainText, string $key, string $iv): string
{
$plainText = static::getPaddedText($plainText);
return openssl_encrypt($plainText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
}
/**
* Erhalte entschlüsselten String
* @param string $encryptedText
* - verschlüsselter Text
* @param string $key
* - Schlüssel zum entschlüsseln
* @param string $iv
* - Der Salt Wert für den Block zum entschlüsseln.
* @return string
* - entschlüsselten String
*/
public static function decrypt(string $encryptedText, string $key, string $iv): string
{
return openssl_decrypt($encryptedText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
}
}
@JacksonJeans HI, in your code you use a "pdf" file for the upload as an example. I am looking for a way to transmit the invoice files with this api. Sending FEEDs is no problem ... only I have not found the right FeedType: "UPLOAD_VAT_INVOICE", nor how I should make a link between the PDF file and the order. You can only upload one file (PDF) -> I did not find the link in another XML file ... even the XSD schemas do not have a "DATA" tag in which the PDF can be encoded as a string. Maybe you can give me a tip ... Thank you
@JacksonJeans HI, in your code you use a "pdf" file for the upload as an example. I am looking for a way to transmit the invoice files with this api. Sending FEEDs is no problem ... only I have not found the right FeedType: "UPLOAD_VAT_INVOICE", nor how I should make a link between the PDF file and the order. You can only upload one file (PDF) -> I did not find the link in another XML file ... even the XSD schemas do not have a "DATA" tag in which the PDF can be encoded as a string. Maybe you can give me a tip ... Thank you
Hello @DivingUp , You are good. That's exactly what I had tried first - just like that :-D
Currently there is no way to upload invoices via the Selling Partner API. I took my implementation of MWS and wrote myself a SignatureV2 to quickly (if that ever comes) adapt to SP-API.
I have left "pdf" as an example in this case. In the end it does not matter what you upload. Amazon accepts the most common files. Whether or not it makes sense for the feed processing is not clear.
@JacksonJeans Hi, then I'm glad that I didn't look through all the API's and found nothing ... It is stupid that I was only activated for the SP-API (not for the old MWS service) at Amazon (and that is also the case with all other new USERS) and I have no chance to automatically upload invoices .... that's not really great ...
@DivingUp , I understand your problem very well. I think Amazon wants to urge you to use the paid billing service.
Anyway, I would still try to upload the document via MWS FeedAPI. Unfortunately I don't have a new account that is not activated for MWS, otherwise I would try it with my InvoiceUploader.
For the authentication you need SignatureV2, your MerchantId as well as AWSAccessKeyId and AWSSecretAccessKey. Enclosed, I have linked the documentation for the InvoiceUploader.
@JacksonJeans Thanks for the hint. The old MWS service is not a problem if it were activated for my shop ... But since it is not, I cannot use the SignitureV2 service (I don't get any ... you currently only switch new shops for the SP- API free ... so you are virtually "locked out" to upload invoices ... I have now made a request to Amazon that you should activate the MWS service for me ... let's see if they do it and how long it takes. Thanks anyway...
@DivingUp , I hope for you and wish you good luck! Gladly tell us here about what Amazon answers.
Hi @DivingUp, did you manage to upload your invoices ? I'am facing the same problem. Only activated for SP API and not MWS...
@bastien-effetb & @JacksonJeans Hi, Unfortunately, I have not found a solution because the service for uploading invoices via SP-Api is currently not provided by amazon. I'm also waiting for a solution ... currently there doesn't seem to be any without MWS .... really sad ..
@shaoruisky , I cannot publish the UTF8 class.
How to encode strings in UTF8 with PHP you will find very fast.
Please do not just copy code and hope it works, but understand it. I have a lot of answers here in several issues that have been duplicated. A simple search is enough to understand how to encrypt and decrypt.
As there are some defs in PHP who have problems to encrypt and decrypt documents. Here is my static class.
The following code works for decrypting and encrypting:
Usage:
/** * Receive encrypted string * @param string $plainText [required] * - unencrypted text * @param string $key [required] * - Key to encrypt * @param string $iv [required] * - The salt value for the block to encrypt. * @return string * - encrypted string */ $enrypt = AESCryptoStreamFactory::encrypt($plainText , $key , $iv ); /** * Receive decrypted string * @param string $encryptedText * - encoded text * @param string $key * - Key to decrypt * @param string $iv * - The Salt value for the block to decrypt. * @return string * - decrypted string */ $decrypt = AESCryptoStreamFactory::decrypt($encryptedString, $key , $iv );
Class::
class AESCryptoStreamFactory { /** * default: 8 fpr PKSC5! * - The block size is a property of the used cipher algorithm. * For AES it is always 16 bytes. So strictly speaking, * PKCS5Padding cannot be used with AES since it is defined only for a block size of 8 bytes. * * The only difference between these padding schemes is that PKCS7Padding has the block size as a parameter, * while for PKCS5Padding it is fixed at 8 bytes. When the Block size is 8 bytes they do exactly the same. * * - Eigene Implementierung durch getPaddedText();. * * - https://crypto.stackexchange.com/questions/43489/how-does-aes-ctr-pkcs5padding-works-when-the-bits-to-encrypt-is-more-than-8-bits * - https://stackoverflow.com/questions/20770072/aes-cbc-pkcs5padding-vs-aes-cbc-pkcs7padding-with-256-key-size-performance-java/20770158 */ public const BLOCK_SIZE = 8; /** * default: 16 * @var int IV_LENGTH * - Vektor mit Zufallsdaten */ public const IV_LENGTH = 16; /** * default: AES256 * @var string CIPHER * - Verschlüsselung */ public const CIPHER = 'AES256'; /** * Erhalte aufegfüllten Text * @param string $plainText * - Plain Text * @return string $plainText * - aufgefüllter Plain Text */ protected static function getPaddedText(string $plainText): string { $stringLength = strlen($plainText); if ($stringLength % static::BLOCK_SIZE) { $plainText = str_pad($plainText, $stringLength + static::BLOCK_SIZE - $stringLength % static::BLOCK_SIZE, "\0"); } return $plainText; } /** * Erhalte verschlüsselten String * @param string $plainText [required] * - unverschlüsselter Text * @param string $key [required] * - Schlüssel zum verschlüsseln * @param string $iv [required] * - Der Salt Wert für den Block zum verschlüsseln. * @return string * - verschlüsselten String */ public static function encrypt(string $plainText, string $key, string $iv): string { $plainText = static::getPaddedText($plainText); return openssl_encrypt($plainText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv); } /** * Erhalte entschlüsselten String * @param string $encryptedText * - verschlüsselter Text * @param string $key * - Schlüssel zum entschlüsseln * @param string $iv * - Der Salt Wert für den Block zum entschlüsseln. * @return string * - entschlüsselten String */ public static function decrypt(string $encryptedText, string $key, string $iv): string { return openssl_decrypt($encryptedText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv); } }
@JacksonJeans Thanks for the code.
I tried your code to submit OrderFulfillment Feed. But Amazon reject the feed with error "The XML you submitted is ill-formed at the Amazon Envelope XML level at (or near) line 18, column 18". For testing, I encrypted xml contents using encrypt($contents, $key, $initializationVector) method and then decrypted the encrypted contents using decrypt($encryptedContent, $key, $initializationVector) I found that getPaddedText method is adding some special characters at the end of file i.e. at line 18, column 18 as reported by Amazon. I opened the file in VS Code and screenshot is attached.
Can anyone please help with the issue?
@AmandeepSingh179 Please comment out the getPaddedText() line in the encrypt method. Then it should work.
class AESCryptoStreamFactory
{
/**
* default: 8 fpr PKSC5!
* - The block size is a property of the used cipher algorithm.
* For AES it is always 16 bytes. So strictly speaking,
* PKCS5Padding cannot be used with AES since it is defined only for a block size of 8 bytes.
*
* The only difference between these padding schemes is that PKCS7Padding has the block size as a parameter,
* while for PKCS5Padding it is fixed at 8 bytes. When the Block size is 8 bytes they do exactly the same.
*
*
* - https://crypto.stackexchange.com/questions/43489/how-does-aes-ctr-pkcs5padding-works-when-the-bits-to-encrypt-is-more-than-8-bits
* - https://stackoverflow.com/questions/20770072/aes-cbc-pkcs5padding-vs-aes-cbc-pkcs7padding-with-256-key-size-performance-java/20770158
*/
public const BLOCK_SIZE = 8;
/**
* default: 16
* @var int IV_LENGTH
* - vector
*/
public const IV_LENGTH = 16;
/**
* default: AES256
* @var string CIPHER
* - encryption
*/
public const CIPHER = 'AES256';
/**
* Get encrypted string
* @param string $plainText [required]
* - unencrypted text
* @param string $key [required]
* - key to encrypt
* @param string $iv [required]
* - The salt value for the block to encrypt.
* @return string
* - encrypted string
*/
public static function encrypt(string $plainText, string $key, string $iv)
{
return openssl_encrypt($plainText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
}
/**
* Get decrypted string
* @param string $encryptedText
* - encrypted text
* @param string $key
* - decrypt key
* @param string $iv
* - The salt value for the block to decrypt.
* @return string
* - decrypted string
*/
public static function decrypt(string $encryptedText, string $key, string $iv)
{
return openssl_decrypt($encryptedText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
}
}
@AmandeepSingh179 Please comment out the getPaddedText() line in the encrypt method. Then it should work.
class AESCryptoStreamFactory { /** * default: 8 fpr PKSC5! * - The block size is a property of the used cipher algorithm. * For AES it is always 16 bytes. So strictly speaking, * PKCS5Padding cannot be used with AES since it is defined only for a block size of 8 bytes. * * The only difference between these padding schemes is that PKCS7Padding has the block size as a parameter, * while for PKCS5Padding it is fixed at 8 bytes. When the Block size is 8 bytes they do exactly the same. * * * - https://crypto.stackexchange.com/questions/43489/how-does-aes-ctr-pkcs5padding-works-when-the-bits-to-encrypt-is-more-than-8-bits * - https://stackoverflow.com/questions/20770072/aes-cbc-pkcs5padding-vs-aes-cbc-pkcs7padding-with-256-key-size-performance-java/20770158 */ public const BLOCK_SIZE = 8; /** * default: 16 * @var int IV_LENGTH * - vector */ public const IV_LENGTH = 16; /** * default: AES256 * @var string CIPHER * - encryption */ public const CIPHER = 'AES256'; /** * Get encrypted string * @param string $plainText [required] * - unencrypted text * @param string $key [required] * - key to encrypt * @param string $iv [required] * - The salt value for the block to encrypt. * @return string * - encrypted string */ public static function encrypt(string $plainText, string $key, string $iv) { return openssl_encrypt($plainText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv); } /** * Get decrypted string * @param string $encryptedText * - encrypted text * @param string $key * - decrypt key * @param string $iv * - The salt value for the block to decrypt. * @return string * - decrypted string */ public static function decrypt(string $encryptedText, string $key, string $iv) { return openssl_decrypt($encryptedText, static::CIPHER, $key, OPENSSL_RAW_DATA, $iv); } }
@JacksonJeans Thank you very much. It worked.
// utf8 ! $UTF8Handler = new UTF8($file,true); $file = $UTF8Handler->str;
Hello, thank you for your code, I m using it do encrypt the file. I m using uft8_encode ( php ) to convert the file, but I got a arror. So can you tell me where I can find the UTF8 class that you use ? Bus isn't correct using the php function uft8_encode ? I m uploading a Flat file, not xml.
Thanks
$createFeedDocumentSpecifications->encryptionDetails->initializationVector, true)
The createfeeddocument interface I requested did not return the encryptiondetails parameter, only the feeddocumentid and URL parameters
I am getting an "Signature does not match" error while uploading the encrypted feed document. I get the response from createFeedDocument call and it returns me the url as well. Since the url is signed, i do not sign it any further and just use the url as is. But strangely enough the error comes. I am not sure if anyone has faced this error and how they solved this.
I am using C# .NET.