function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Grainne RyanGrainne Ryan 

Amazon MWS - Signature Does Not Match

Hi
I am trying to bring Amazon orders into Salesforce using the Amazon webservice but I keep getting the same error message:
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

But the signature I send is correct. I know because I have used Amazon MWS Scratchpad (this works perfectly) and when I use the timestamp from this in my program I get the same signature.  

I have no idea what else to do and I would appreciate any suggestions though I am 100% certain that I am using the correct information (access key, secret key etc.) from Amazon
My code is below
Thanks
Gráinne
 public static void Listorder() {

        //Current time in GMT ISO 8601
        String timestamp = datetime.now().formatGMT('yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'');
        timestamp = EncodingUtil.urlEncode(timestamp,'UTF-8');
        
        //Amazon Variables
        String action = 'ListOrders';
        String version = '2013-09-01';
        String signatureVersion = '2';
        String signatureMethod = 'HmacSHA256';
        String marketplaceId= 'amazon marketplace id';
        String fulfillmentChannel = 'AFN';
        String sellerId = 'amazon seller id';
        String endpoint = 'https://mws.amazonservices.com/Orders/2013-09-01';
        String accessKey = 'amazon access key';
        String amazonSecretKey = 'amazon secret key';
    
        //Construct a query string with the query information
        String queryString = 'AWSAccessKeyId=' + accessKey + 
            '&Action=' + action  +
            '&CreatedAfter=2016-09-30T23%3A00%3A00Z' + 
            '&MarketplaceId.Id.1=' + MarketplaceId  +
            '&SellerId=' + SellerId +
            '&SignatureMethod=' + SignatureMethod  +
            '&SignatureVersion=' + SignatureVersion  +
            '&Timestamp=' + timestamp  +
            '&Version=' + version ;

        String stringtoSign = 'POST' + '\n' +
            'mws.amazonservices.co.uk' + '\n' +
            '/Orders/2013-09-01' + '\n' +
            queryString;
        
        //Covert query string to signature using Amazon secret key as key
        Blob mac = Crypto.generateMac('HMacSHA256', blob.valueof(stringtoSign),blob.valueof(amazonSecretKey));
        String signature = EncodingUtil.base64Encode(mac);
        signature = EncodingUtil.urlEncode(signature,'UTF-8');

        System.debug(signature);
     
        HttpRequest req = new HttpRequest();    
        
        req.setEndpoint(endpoint +'?AWSAccessKeyId=' + accessKey +
            '&Action=' + action +
            '&SellerId=' + sellerId +
            '&SignatureVersion=2' +
            '&Timestamp=' + timestamp +
            '&Version=' + version +
            '&Signature=' + signature +
            '&SignatureMethod='+ signatureMethod +
            '&CreatedAfter=2016-09-30T23%3A00%3A00Z' +
            '&MarketplaceId.Id.1=' + marketplaceId);
        
        req.setMethod('POST');
        
        Http http = new Http();
        try {
            HttpResponse res = http.send(req);
            System.debug(res.getStatus());
            System.debug(res.getStatusCode());
            System.debug(res.getBody());
            } 
        catch(System.CalloutException e) {
            System.debug(e);
        }

   }


 
Best Answer chosen by Grainne Ryan
Grainne RyanGrainne Ryan
Sorry, this was just a silly mistake - the endpoint should have been .co.uk not .com

All Answers

Grainne RyanGrainne Ryan
Sorry, this was just a silly mistake - the endpoint should have been .co.uk not .com
This was selected as the best answer
AKumAKum

Hi @Grainne ,

I want to send products via amazon's feed api to amazon seller account . It seems that you tried something similar . Can you please explain if you have done something related to product feeds.
Link for my question :  https://developer.salesforce.com/forums/ForumsMain?id=9060G000000BbN4QAK

The basic questions I have is :

1. How we can generate these long xml's for sending to feed api ?

2. Handling these things in Batch would be better or should I try some other approach ?

Thanks !

 

Grainne RyanGrainne Ryan
Hi Akum

In order to send feeds to Amazon you need to include an XML body and ContentMD5Value in the Amazon request.   This first function prepares the XML body, it assumes you are looping through all the products you have in Salesforce that you want to create or update in Amazon.  You need to pass it your seller id and a list of the products you want to send to Amazon.  And update it with your own values.
 
//Prepare the feed 
private String prepare_body(List<Products__c> products, String sellerId){
        
       String xmlBody = '<?xml version="1.0"?>' + 
                     '<AmazonEnvelope xmlns:xsi=http://www.w3.org/2001/XMLSchema-
                     instance xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">'+
	               '<Header>' +
            		'<DocumentVersion>1.01</DocumentVersion>' +
            		'<MerchantIdentifier>' + sellerId + '</MerchantIdentifier>' +
            	      '</Header>' +
            	      '<MessageType>OrderAdjustment</MessageType>' +
            	      '<Message>' +
            		  '<MessageID>1</MessageID>' +
                       '<OperationType>Update</OperationType>' ;
     
        // replace all the values here with your own
        For(Product__c product : products){
    	xmlBody  += '<Product>' +
      			'<SKU>product.SKU__c</SKU>' +
      			'<StandardProductID>' +
        			'<Type>ASIN</Type>' +
        			'<Value>product.ASIN__c</Value>' +
      			'</StandardProductID>' +
      			'<ProductTaxCode>product.TAXCODE__c</ProductTaxCode>' +
      			'<DescriptionData>' +
        			'<Title>product.TITLE__c</Title>' +
        			'<Brand>product.BRAND__c</Brand>' +
        			'<Description>product.DESCRIPTION__c</Description>' +
        			'<BulletPoint>product.BULLETPOINT1__c</BulletPoint>' +
        			'<BulletPoint>product.BULLETPOINT2__c</BulletPoint>' +
        			'<MSRP currency="USD"> product.MSRP__c</MSRP>' +
                    '<Manufacturer>product.MANUFACTURER__c</Manufacturer>' +
        				'<ItemType>example-item-type</ItemType>' +
      			'</DescriptionData>' +
      			'<ProductData>' +
        			'<Health>' +
          		          '<ProductType>' +
            				'<HealthMisc>' +										 
                               '<Ingredients>product.INGREDIENTS__c</Ingredients>' +
              					'<Directions>product.DIRECTIONS__c</Directions>' +
            				'</HealthMisc>' +
          		           '</ProductType>' +
        			  '</Health>' +
     			 '</ProductData>' +
    		'</Product>';
            }
            xmlBody += '</Message>' +
                                 '</AmazonEnvelope>' ;

            return xmlBody;
    }

Then use this XML Body to get the ContentMD5Value:
private String get_contentmd5(String xmlBody){
        	Blob targetBlob = Blob.valueOf(xmlBody);
        	Blob hash = Crypto.generateDigest('MD5', targetBlob);    
        	String contentMD5 = EncodingUtil.base64Encode(hash);
        	contentMD5 =  EncodingUtil.URLENCODE(contentMD5,'UTF-8');
        
        	return contentMD5;
    }

Then send the request to Amazon, you will need to prepare a signature to do this.
private void send_feed_to_amazon(String xmlBody, String ContentMD5){

         String timestamp = datetime.now().formatGMT('yyyy-MM-
           dd\'T\'HH:mm:ss.SSS\'Z\'');
        
        //Construct a query string with the query information
        String queryString = 'AWSAccessKeyId=' + accessKey + 
            '&Action=SubmitFeed'+
            '&FeedType=_POST_PRODUCT_DATA_'+
            '&MarketplaceId.Id.1=' + MarketplaceId  +
            '&SellerId=' + SellerId +
            '&SignatureMethod=' + SignatureMethod  +
            '&SignatureVersion=' + SignatureVersion  +
            '&Timestamp=' + timestamp  +
            '&Version=' + version;

        String stringtoSign = 'POST' + '\n' +
            'mws.amazonservices.co.uk' + '\n' +
            '/Orders/2013-09-01' + '\n' +
            queryString;
        
        //Covert query string to signature using Amazon secret key as key
        Blob mac = Crypto.generateMac('HMacSHA256', 
        blob.valueof(stringtoSign),blob.valueof(amazonSecretKey));

        String signature = EncodingUtil.base64Encode(mac);
        signature = EncodingUtil.urlEncode(signature,'UTF-8');


      //Create a new request
       HttpRequest request = new HttpRequest();    
        
       request.setEndpoint(endpoint +'?AWSAccessKeyId=' + accessKey +
            '&Action=SubmitFeed'+
            '&FeedType=_POST_PRODUCT_DATA_'+
            '&ContentMD5Value=' + ContentMD5+
            '&SellerId=' + sellerId +
            '&SignatureVersion=2' +
            '&Timestamp=' + timestamp +
            '&Version=' + version +
            '&Signature=' + signature +
            '&SignatureMethod='+ signatureMethod +
            '&MarketplaceIdList.Id.1=' + marketplaceId);
        
        request.setMethod('POST');
        request.setBody(xmlBody);
        
        Http http = new Http();
        try {
            HttpResponse response = http.send(request);
            System.debug(response.getStatus());
            System.debug(response.getStatusCode());
            System.debug(response.getBody());
        } 
        catch(System.CalloutException e) {
            System.debug(e);
        }
}

This should return your FeedSubmissionId and you can use that to check the status and confirm that the feed was accepted.
See http://docs.developer.amazonservices.com/en_UK/feeds/Feeds_GetFeedSubmissionResult.html

I hope this helps.
Gráinne