├── .travis.yml ├── README.md ├── amazonmws.go ├── amazonmws_test.go └── urlgen.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | script: go get github.com/ezkl/go-amazon-mws-api && go test 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amazon Marketplace Web Services (MWS) API [![Build Status](https://travis-ci.org/ezkl/go-amazon-mws-api.png?branch=master)](https://travis-ci.org/ezkl/go-amazon-mws-api) 2 | 3 | This Amazon MWS API client is based heavily on @DDRBoxman's [go-amazon-product-api](https://github.com/DDRBoxman/go-amazon-product-api). 4 | 5 | ## Documentation 6 | 7 | You can view the auto-generated documentation at [http://godoc.org/github.com/ezkl/go-amazon-mws-api](http://godoc.org/github.com/ezkl/go-amazon-mws-api). 8 | 9 | ## Usage 10 | 11 | ```go 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "github.com/ezkl/go-amazon-mws-api" 17 | ) 18 | 19 | func main() { 20 | var api amazonmws.AmazonMWSAPI 21 | 22 | api.AccessKey = "" 23 | api.SecretKey = "" 24 | api.Host = "mws.amazonservices.com" 25 | api.MarketplaceId = "ATVPDKIKX0DER" 26 | api.SellerId = "" 27 | 28 | asins := []string{"0195019199"} 29 | 30 | result,err := api.GetLowestOfferListingsForASIN(asins) 31 | 32 | if (err != nil) { 33 | fmt.Println(err) 34 | } 35 | 36 | fmt.Println(result) 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /amazonmws.go: -------------------------------------------------------------------------------- 1 | // amazonmws provides methods for interacting with the Amazon Marketplace Services API. 2 | package amazonmws 3 | 4 | import ( 5 | "fmt" 6 | ) 7 | 8 | /* 9 | GetLowestOfferListingsForASIN takes a list of ASINs and returns the result. 10 | */ 11 | func (api AmazonMWSAPI) GetLowestOfferListingsForASIN(items []string) (string, error) { 12 | params := make(map[string]string) 13 | 14 | for k, v := range items { 15 | key := fmt.Sprintf("ASINList.ASIN.%d", (k + 1)) 16 | params[key] = string(v) 17 | } 18 | 19 | params["MarketplaceId"] = string(api.MarketplaceId) 20 | 21 | return api.genSignAndFetch("GetLowestOfferListingsForASIN", "/Products/2011-10-01", params) 22 | } 23 | 24 | /* 25 | GetCompetitivePricingForAsin takes a list of ASINs and returns the result. 26 | */ 27 | func (api AmazonMWSAPI) GetCompetitivePricingForASIN(items []string) (string, error) { 28 | params := make(map[string]string) 29 | 30 | for k, v := range items { 31 | key := fmt.Sprintf("ASINList.ASIN.%d", (k + 1)) 32 | params[key] = string(v) 33 | } 34 | 35 | params["MarketplaceId"] = string(api.MarketplaceId) 36 | 37 | return api.genSignAndFetch("GetCompetitivePricingForASIN", "/Products/2011-10-01", params) 38 | } 39 | 40 | func (api AmazonMWSAPI) GetMatchingProductForId(idType string, idList []string) (string, error) { 41 | params := make(map[string]string) 42 | 43 | for k, v := range idList { 44 | key := fmt.Sprintf("IdList.Id.%d", (k + 1)) 45 | params[key] = string(v) 46 | } 47 | 48 | params["IdType"] = idType 49 | params["MarketplaceId"] = string(api.MarketplaceId) 50 | 51 | return api.genSignAndFetch("GetMatchingProductForId", "/Products/2011-10-01", params) 52 | } 53 | -------------------------------------------------------------------------------- /amazonmws_test.go: -------------------------------------------------------------------------------- 1 | package amazonmws 2 | 3 | import ( 4 | "net/url" 5 | "testing" 6 | ) 7 | 8 | func Test_SignAmazonUrl_1(t *testing.T) { 9 | 10 | urlString := `https://mws-eu.amazonservices.com/Products/2011-10-01?ASINList.ASIN.1=1561706337&ASINList.ASIN.10=1561711969&ASINList.ASIN.11=1561712027&ASINList.ASIN.12=2841051498&ASINList.ASIN.13=1561712043&ASINList.ASIN.14=1562413473&ASINList.ASIN.15=2729702806&ASINList.ASIN.16=2729702776&ASINList.ASIN.17=1561718939&ASINList.ASIN.18=2841090930&ASINList.ASIN.19=156171951X&ASINList.ASIN.2=1561712930&ASINList.ASIN.20=2729702032&ASINList.ASIN.3=1561713066&ASINList.ASIN.4=2729701737&ASINList.ASIN.5=1561711837&ASINList.ASIN.6=1561711845&ASINList.ASIN.7=1561711896&ASINList.ASIN.8=1561711810&ASINList.ASIN.9=1561712019&AWSAccessKeyId=AKIAJLUHOXLR5S2L6A6A&Action=GetLowestOfferListingsForASIN&MarketplaceId=APJ6JRA9NG5V4&SellerId=A2APQUVDBVWV7E&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2013-03-29T02%3A16%3A18Z` 11 | 12 | signedUrl := `https://mws-eu.amazonservices.com/Products/2011-10-01?ASINList.ASIN.10=1561711969&ASINList.ASIN.11=1561712027&ASINList.ASIN.12=2841051498&ASINList.ASIN.13=1561712043&ASINList.ASIN.14=1562413473&ASINList.ASIN.15=2729702806&ASINList.ASIN.16=2729702776&ASINList.ASIN.17=1561718939&ASINList.ASIN.18=2841090930&ASINList.ASIN.19=156171951X&ASINList.ASIN.1=1561706337&ASINList.ASIN.20=2729702032&ASINList.ASIN.2=1561712930&ASINList.ASIN.3=1561713066&ASINList.ASIN.4=2729701737&ASINList.ASIN.5=1561711837&ASINList.ASIN.6=1561711845&ASINList.ASIN.7=1561711896&ASINList.ASIN.8=1561711810&ASINList.ASIN.9=1561712019&AWSAccessKeyId=AKIAJLUHOXLR5S2L6A6A&Action=GetLowestOfferListingsForASIN&MarketplaceId=APJ6JRA9NG5V4&SellerId=A2APQUVDBVWV7E&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2013-03-29T02%3A16%3A18Z&Signature=uXzBEQfZEL1NMvWZrw8l%2BfN5JPH2vy8MRammjZ1J5fY%3D` 13 | 14 | var api AmazonMWSAPI 15 | 16 | api.SecretKey = "1234567890" 17 | 18 | url, err := url.Parse(urlString) 19 | if err != nil { 20 | t.Error("Could not parse urlstring") 21 | } 22 | 23 | resultUrl, err := SignAmazonUrl(url, api) 24 | if err != nil { 25 | t.Error("Signing failure: %v", err) 26 | } 27 | 28 | if signedUrl != resultUrl { 29 | t.Log(resultUrl, "\n") 30 | t.Error("Signed url does not match") 31 | } 32 | } 33 | 34 | func Test_GetLowestOfferListingForAsin(t *testing.T) { 35 | } 36 | -------------------------------------------------------------------------------- /urlgen.go: -------------------------------------------------------------------------------- 1 | package amazonmws 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha256" 6 | "encoding/base64" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "net/url" 11 | "sort" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | type AmazonMWSAPI struct { 17 | AccessKey string 18 | SecretKey string 19 | Host string 20 | AuthToken string 21 | MarketplaceId string 22 | SellerId string 23 | } 24 | 25 | func (api AmazonMWSAPI) genSignAndFetch(Action string, ActionPath string, Parameters map[string]string) (string, error) { 26 | genUrl, err := GenerateAmazonUrl(api, Action, ActionPath, Parameters) 27 | if err != nil { 28 | return "", err 29 | } 30 | 31 | SetTimestamp(genUrl) 32 | 33 | signedurl, err := SignAmazonUrl(genUrl, api) 34 | if err != nil { 35 | return "", err 36 | } 37 | 38 | resp, err := http.Get(signedurl) 39 | if err != nil { 40 | return "", err 41 | } 42 | 43 | defer resp.Body.Close() 44 | body, err := ioutil.ReadAll(resp.Body) 45 | if err != nil { 46 | return "", err 47 | } 48 | 49 | return string(body), nil 50 | } 51 | 52 | func GenerateAmazonUrl(api AmazonMWSAPI, Action string, ActionPath string, Parameters map[string]string) (finalUrl *url.URL, err error) { 53 | result, err := url.Parse(api.Host) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | result.Host = api.Host 59 | result.Scheme = "https" 60 | result.Path = ActionPath 61 | 62 | values := url.Values{} 63 | values.Add("Action", Action) 64 | 65 | if (api.AuthToken != "") { 66 | values.Add("MWSAuthToken", api.AuthToken) 67 | } 68 | 69 | values.Add("AWSAccessKeyId", api.AccessKey) 70 | values.Add("SellerId", api.SellerId) 71 | values.Add("SignatureVersion", "2") 72 | values.Add("SignatureMethod", "HmacSHA256") 73 | values.Add("Version", "2011-10-01") 74 | 75 | for k, v := range Parameters { 76 | values.Set(k, v) 77 | } 78 | 79 | params := values.Encode() 80 | result.RawQuery = params 81 | 82 | return result, nil 83 | } 84 | 85 | func SetTimestamp(origUrl *url.URL) (err error) { 86 | values, err := url.ParseQuery(origUrl.RawQuery) 87 | if err != nil { 88 | return err 89 | } 90 | values.Set("Timestamp", time.Now().UTC().Format(time.RFC3339)) 91 | origUrl.RawQuery = values.Encode() 92 | 93 | return nil 94 | } 95 | 96 | func SignAmazonUrl(origUrl *url.URL, api AmazonMWSAPI) (signedUrl string, err error) { 97 | escapeUrl := strings.Replace(origUrl.RawQuery, ",", "%2C", -1) 98 | escapeUrl = strings.Replace(escapeUrl, ":", "%3A", -1) 99 | 100 | params := strings.Split(escapeUrl, "&") 101 | sort.Strings(params) 102 | sortedParams := strings.Join(params, "&") 103 | 104 | toSign := fmt.Sprintf("GET\n%s\n%s\n%s", origUrl.Host, origUrl.Path, sortedParams) 105 | 106 | hasher := hmac.New(sha256.New, []byte(api.SecretKey)) 107 | _, err = hasher.Write([]byte(toSign)) 108 | if err != nil { 109 | return "", err 110 | } 111 | 112 | hash := base64.StdEncoding.EncodeToString(hasher.Sum(nil)) 113 | 114 | hash = url.QueryEscape(hash) 115 | 116 | newParams := fmt.Sprintf("%s&Signature=%s", sortedParams, hash) 117 | 118 | origUrl.RawQuery = newParams 119 | 120 | return origUrl.String(), nil 121 | } 122 | --------------------------------------------------------------------------------