wusoma / paapi5-nodejs-sdk

Product Advertising API 5.0 SDK for NodeJS
Apache License 2.0
14 stars 6 forks source link

Help with PAAPI5 Go Request #3

Closed davidalvarez305 closed 2 years ago

davidalvarez305 commented 2 years ago

Hey, I know this doesn't belong here but I'm not sure where else to ask this question. In trying to request PAAPI5 from Go using very similar code as to the one used in this library, I keep receiving this error message:

"The request has not been correctly signed. If you are using an AWS SDK, requests are signed for you automatically; otherwise, go to https://webservices.amazon.com/paapi5/documentation/sending-request.html#signing."

Would you be able to help me figure out exactly what I'm doing wrong? If not, I apologize for the inconvinience:

func makeHash(hash hash.Hash, b []byte) []byte {
    hash.Reset()
    hash.Write(b)
    return hash.Sum(nil)
}

func buildCanonicalString(method, uri, query, signedHeaders, canonicalHeaders, payloadHash string) string {
    return strings.Join([]string{
        method,
        uri,
        query,
        canonicalHeaders,
        signedHeaders,
        payloadHash,
    }, "\n")
}

func buildStringToSign(date, credentialScope, canonicalRequest string) string {
    return strings.Join([]string{
        "AWS4-HMAC-SHA256",
        date,
        credentialScope,
        hex.EncodeToString(makeHash(sha256.New(), []byte(canonicalRequest))),
    }, "\n")
}

func buildSignature(strToSign string, sig string) (string, error) {
    return hex.EncodeToString(HMACSHA256([]byte(sig), []byte(strToSign))), nil
}

func HMACSHA256(key []byte, data []byte) []byte {
    hash := hmac.New(sha256.New, key)
    hash.Write(data)
    return hash.Sum(nil)
}

func SearchPaapi5Items(keyword string) []AmazonSearchResultsPage {
    var products []AmazonSearchResultsPage

    resources := []string{
        "Images.Primary.Medium",
        "ItemInfo.Title",
        "Offers.Listings.Price",
        "ItemInfo.ByLineInfo",
        "ItemInfo.Features",
        "ItemInfo.ProductInfo"}

    d := AmazonPaapi5RequestBody{
        Marketplace: "www.amazon.com",
        PartnerType: "Associates",
        PartnerTag:  os.Getenv("AMAZON_PARTNER_TAG"),
        Keywords:    keyword,
        SearchIndex: "All",
        ItemCount:   10,
        Resources:   resources,
    }

    body, e := json.Marshal(d)

    if e != nil {
        return products
    }

    method := "POST"
    service := "ProductAdvertisingAPI"
    url := "https://webservices.amazon.com/paapi5/searchitems"
    host := "webservices.amazon.com"
    region := os.Getenv("AWS_REGION")
    contentType := "application/json; charset=UTF-8"
    amazonTarget := "com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems"
    contentEncoding := "amz-1.0"
    t := time.Now()
    amazonDate := strftime.Format(t, "%Y%m%d")
    xAmazonDate := strftime.Format(t, "%Y%m%dT%H%M%SZ")
    canonicalUri := "/paapi5/searchitems"
    canonicalQuerystring := ""
    canonicalHeaders := "content-type:" + contentType + "\n" + "host:" + host + "\n" + "x-amz-date:" + xAmazonDate + "\n" + "x-amz-target:" + amazonTarget + "\n" + "content-encoding:" + contentEncoding
    credentialScope := amazonDate + "/" + region + "/" + service + "/" + "aws4_request"
    signedHeaders := "content-type;host;x-amz-date;x-amz-target;content-encoding"

    kSecret := os.Getenv("AWS_SECRET_ACCESS_KEY")
    kDate := hex.EncodeToString(HMACSHA256([]byte("AWS4"+kSecret), []byte(amazonDate)))
    kRegion := hex.EncodeToString(HMACSHA256([]byte(kDate), []byte(region)))
    kService := hex.EncodeToString(HMACSHA256([]byte(kRegion), []byte(service)))
    signingKey := hex.EncodeToString(HMACSHA256([]byte(kService), []byte("aws4_request")))

    canonicalRequest := buildCanonicalString(method, canonicalUri, canonicalQuerystring, signedHeaders, canonicalHeaders, hex.EncodeToString(makeHash(sha256.New(), []byte(body))))
    stringToSign := buildStringToSign(xAmazonDate, credentialScope, canonicalRequest)
    signature, err := buildSignature(stringToSign, signingKey)
    if err != nil {
        fmt.Println("Error while building signature.")
    }

    authorizationHeader := "AWS4-HMAC-SHA256" + " Credential=" + os.Getenv("AWS_ACCESS_KEY_ID") + "/" + credentialScope + ", SignedHeaders=" + signedHeaders + ", Signature=" + signature

    client := &http.Client{}
    req, err := http.NewRequest(method, url, bytes.NewBuffer(body))

    if err != nil {
        fmt.Println("Request failed: ", err)
        return products
    }

    req.Header.Set("content-type", contentType)
    req.Header.Set("host", host)
    req.Header.Set("x-amz-date", xAmazonDate)
    req.Header.Set("x-amz-target", amazonTarget)
    req.Header.Set("content-encoding", contentEncoding)
    req.Header.Set("Authorization", authorizationHeader)

    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error while fetching Amazon SERP", err)
        return products
    }
    defer resp.Body.Close()

    respDump, err := httputil.DumpResponse(resp, true)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("RESPONSE:\n%s", string(respDump))

    return products
}
davidalvarez305 commented 2 years ago

The problem was that my function is using EST instead of UTC time. Sorry!