Amazon selling-partner-api feed encrypt file with Python

I tried GreatFeedDocument, then I received a status code is 200, but the get feed result's status:

"processingStatus": "FATAL"

I have too many times tried but I can't understand,

How I can encrypt the XML file?

Here is my python script.

from aws_auth import Auth
import requests
import json
from pprint import pprint
import base64, os
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Hash import SHA256


ownercd = "****"
sp_auth = Auth(ownercd)


def pad(s):
    # Data will be padded to 16 byte boundary in CBC mode
    return s + b"\0" * (AES.block_size - len(s) % AES.block_size)


def getKey(password):
    # Use SHA256 to hash password for encrypting AES
    hasher = SHA256.new(password.encode())
    return hasher.digest()


# Encrypt message with password
def encrypt(message, key, iv, key_size=256):
    message = pad(message)
    cipher = AES.new(key, AES.MODE_CBC, iv)
    return iv + cipher.encrypt(message)


# Encrypt file
def encrypt_file(file_name, key, iv):
    # Open file to get file Data
    with open(file_name, "rb") as fo:
        plaintext = fo.read()
    # Encrypt plaintext with key has been hash by SHA256.
    enc = encrypt(plaintext, key, iv)
    # write Encrypted file
    with open(file_name + ".enc", "wb") as fo:
        fo.write(enc)
    return enc


if sp_auth != False:
    x_amz_access_token = sp_auth[0]  # AWS SP-api access token
    AWSRequestsAuth = sp_auth[1]  # AWS signature
    config = sp_auth[2]  # From mongo's config
    feed_headers = {
        "Content-Type": "application/json",
        "x-amz-access-token": x_amz_access_token,
    }
    contentType = {"contentType": "application/xml; charset=UTF-8"}
    # [1.1] Create a FeedDocument
    creat_feed_res = requests.post(
        config["BASE_URL"] + "/feeds/2020-09-04/documents",
        headers=feed_headers,
        auth=AWSRequestsAuth,
        data=json.dumps(contentType),
    )

    # [1.2] Store the response
    CreatFeedResponse = creat_feed_res.json()["payload"]
    feedDocumentId = CreatFeedResponse["feedDocumentId"]
    initializationVector = CreatFeedResponse["encryptionDetails"][ "initializationVector"]
    url = CreatFeedResponse["url"]
    key = CreatFeedResponse["encryptionDetails"]["key"]

    # [1.3] Upload and encrypt document
    filename = "carton.xml"
    iv = base64.b64decode(initializationVector)

    encrypt_data = encrypt_file(filename, getKey(key), iv)
    headers = {"Content-Type": "application/xml; charset=UTF-8"}
    res = requests.put(url, headers=headers, data=encrypt_data)

    print(res.status_code)  # 200
    print(res.request.body)  # b'8L^\xbeY\xf....
    print(res.content.decode())

It is the GetFeed Response:

Can anyone help me with that? Thanks in advance!

Upvotes: 2

Views: 1620

Answers (1)

I solved my problem. Here is my example code.

import requests
import json
from pprint import pprint
import base64, os
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from base64 import b64encode
import pyaes

dirname = os.path.dirname(os.path.abspath(__file__))
xmlFile_Name = dirname + "/template/carton.xml"

def encrypt(key, initializationVector):
    # Create random 16 bytes IV, Create 32 bytes key
    key = base64.b64decode(key)
    iv = base64.b64decode(initializationVector)

    # Encryption with AES-256-CBC
    if os.path.isfile(xmlFile_Name) == False:
        print("===== CARTON FILE NOT FOUND FROM ===== :  {}".format(xmlFile_Name))
        sys.exit()
    with open(xmlFile_Name, "r") as fo:
        plaintext = fo.read()
        # print(plaintext)
        encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv))
        ciphertext = encrypter.feed(plaintext.encode("utf8"))
        ciphertext += encrypter.feed()
        res = requests.put(
            url,
            data=ciphertext,
            headers=contentType,
        )
        print("STATUS UPLOAD: ", res.status_code)  # 200
        if res.status_code == 200:
            print("===== FILE UPLOAD DONE =====")

# REQUEST 
feed_headers = {
    "Content-Type": "application/json",
    "x-amz-access-token": x_amz_access_token,
}
contentType = {"contentType": "application/xml; charset=UTF-8"}
creat_feed_res = requests.post(
        config["BASE_URL"] + "/feeds/2020-09-04/documents",
        headers=feed_headers,
        auth=AWSRequestsAuth,
        data=json.dumps(contentType),
    )
# [1.2] Store the response
CreatFeedResponse = creat_feed_res.json()["payload"]
feedDocumentId = CreatFeedResponse["feedDocumentId"]
initializationVector = CreatFeedResponse["encryptionDetails"][ "initializationVector"]
url = CreatFeedResponse["url"]
key = CreatFeedResponse["encryptionDetails"]["key"]

# print(feedDocumentId)
# print("KEY =>", key)
# print("IV    =>", initializationVector)
# print(url) #s3 url

contentType = {"Content-Type": "application/xml; charset=UTF-8"}
encryptFile = encrypt(key, initializationVector) 

# [3.1] Create a feed
feedType = "POST_FBA_INBOUND_CARTON_CONTENTS"
marketplaceIds = "A1VC38T7*****"
inputFeedDocumentId = feedDocumentId

createdFeedURL = "https://sellingpartnerapi-fe.amazon.com/feeds/2020-09-04/feeds"
createFeedBody = {
    "inputFeedDocumentId": inputFeedDocumentId,
    "feedType": feedType,
    "marketplaceIds": [marketplaceIds],
}
resCreatFeed = requests.post(
    createdFeedURL,
    headers=feed_headers,
    auth=AWSRequestsAuth,
    data=json.dumps(createFeedBody),
)
createFeedStatusCode = resCreatFeed.json()
if resCreatFeed.status_code == 202:
    # print("Steph 2).")
    feedId = createFeedStatusCode["payload"]["feedId"]
    print("FEED ID: ", feedId)

    # [3.2] CET a feed
    getFeed = requests.get(
        config["BASE_URL"] +
        "/feeds/2020-09-04/feeds/{}".format(str(feedId)),
        headers=feed_headers,
        auth=AWSRequestsAuth,
    )
    pprint(getFeed.json()['payload'])

Here is my Get Feed response:

{'payload': {
       'createdTime': '2021-02-24T07:43:22+00:00',
       'feedId': '14795****',
       'feedType': 'POST_FBA_INBOUND_CARTON_CONTENTS',
       'marketplaceIds': ['A1VC38T7****'],
       'processingStatus': 'DONE'}
}

Then I used the cartonID, I could download the get labels PDF file from the URL.

PackageLabelsToPrint is your cartonID you must set the CartonID here, for example: enter image description here

Last, I could download a PDF file using the URL Here is my GetLabel: enter image description here

Upvotes: 1

Related Questions