Search code examples
c#resthttphttp-status-code-400walmart-api

Walmart API POST failing with 400 Bad Request (inventory feed) ARCA


I am having problems with a POST request to the Walmart Marketplace API for bulk data exchange and am hoping for some help.

Background: I have been successful in writing signature authentication routines, and can successfully execute GET commands such as get products etc. This indicates to me that Authentication signatures are properly formatted, and headers (for the most part) are correct.

Problem: I am receiving a 400 Bad Request response, Request Content is Invalid. response when attempting to submit a test feed to Walmarts API. I have read that this problem is common, but I have yet to find any forum post that clearly explains the actual problem, or how to fix it. Here are my current parameters:

ARCA ARCA Rest Client For Chrome

URL:

https://marketplace.walmartapis.com/v2/feeds?feedType=inventory

Headers:

Accept: application/xml
WM_SVC.NAME: Walmart Marketplace
WM_CONSUMER.ID: <Consumer ID>
WM_SEC.AUTH_SIGNATURE: <Good Auth Signature>
WM_QOS.CORRELATION_ID: 15649814651
WM_SEC.TIMESTAMP: <Timestamp>
WM_CONSUMER.CHANNEL.TYPE: <Channel Type>
Content-Type: multipart/form-data

File attachment (not raw payload although that has been tried)

<?xml version="1.0" encoding="utf-8"?>
<InventoryFeed xmlns="http://walmart.com/">
  <InventoryHeader>
    <version>1.4</version>
  </InventoryHeader>
  <inventory>
    <sku>KON04418</sku>
    <quantity>
      <unit>EACH</unit>
      <amount>4</amount>
    </quantity>
    <fulfillmentLagTime>1</fulfillmentLagTime>
  </inventory>
</InventoryFeed>

When I take this exact same XML and test it at Walmart API Explorer the file is accepted with Response Code 200 (OK).

I have validated with Notepad++ XML Tools plugin that the XML conforms to the XSD provided by Walmart. I have seen numerous posts regarding Boundaries that need to be applied, so I have additionally attempted to change the Content-Type, and add Boundaries but have been unsuccessful in getting the request accepted.

Any help in getting this request to return a response code 200 would be greatly appreciated.

Lastly, once this request validates in ARCA, I will be implementing in C#. I already have all of the code written, but there's a bit of fuzziness about how to add an attachment to an HttpWebRequest vs. just submitting a raw data stream. If any clarity could be provided on the difference I would again, appreciate it.


Solution

  • So this answer isn't clean and elegant, more of a work around than anything. I have spoken with a few individuals inside the Walmart engineering team and have been told that a C# SDK should be forthcoming in the next few months.

    After all of my research, it appears there is some tricks to how you submit a multi-part form to Walmart and the system is very inflexible. I've seen posts about adding specifically formatted boundaries into the body of the HTTP request, but had no such luck. I was unable to attach the body as a file, or as a data stream to the request.

    The work around is pretty simple, and ugly unfortunately. It takes a bit of setup, but you can create a .jar wrapper around the Walmart Java SDK and call it from your .Net program.

    So.. steps in the process:

    • Grab the appropriate .XSD files and generate C# classes from them.
    • Build properly formatted XML inventory file. Make sure to include namespaces!! Walmart will fail particular calls if you don't include the appropriate ns2 / ns3 namespaces.
    • Dynamically generate a batch file to call your Java module. Spawning a command line process directly seemed to make things cranky for some reason so I opted for the batch file instead.

          string path = @Directory.GetParent(Environment.CurrentDirectory).ToString();
      
          if (File.Exists(@"../inventory.bat"))
          {
              File.Delete(@"../inventory.bat");
          }
      
          string batchCommand = @"cd " + path + Environment.NewLine + @"java -jar WalmartWrapper.jar SubmitInventoryFeed inventoryFeed.xml";
          File.WriteAllText(path + @"\\inventory.bat", batchCommand);
      
          ProcessStartInfo info = new ProcessStartInfo();
          info.UseShellExecute = true;
          info.FileName = @"inventory.bat";
          info.WorkingDirectory = path;
          var p = Process.Start(info);
          p.WaitForExit();`
      
    • From here, the Java module takes over. It took a bit of hacking around to make it work more like an SDK and less like a sample program.. Here's some of the sample code for making things work..

    • Entry Point

      if ("SubmitInventoryFeed".equals(args[0].trim())) {
          if (args.length < 2) 
          {
              System.out.println("Need second argument for SubmitInventoryFeed");
              return;
          }
          String filename = args[1];
          Feed inventoryFeed = new Feed();
          try 
          {
              inventoryFeed.submitInventoryFeed(filename);
          } catch (Exception ex) {
              System.out.println("submitInventoryFeed failed: " + ex.getMessage());
          }
      }
      
    • SDK Call (This is the bare bones of submitInventoryFeed without error checking)

      String path = Paths.get(".").toAbsolutePath().normalize().toString();
      File itemFile = FileHandler.getFile(filename.trim());
      String filePath = path + "\\" + "MarketplaceClientConfig.properties";
      WalmartMarketplace wm = Utils.getClient(filePath);
      Response response = wm.submitFeed(MarketplaceFeedType.inventory, itemFile);
      
    • You can use ResponseChecker.isResponse200(response, true) to test for successful submissions

    • Use FeedAcknowledgement ack = response.readEntity(FeedAcknowledgement.class); to grab the actual response to check errors

    I will be the first to say I can't wait to replace this work around with the C# SDK pending from Walmart, but for the time being, this is the only way I have been able to submit. I've looked through the walmart code in depth, but unfortunately, there is some Java magic happening under the hood that does the file attachment so there's not really any way to gain access to the exact procedure and reverse engineer for C#. I think that someone who really knew Java inside and out could figure it out, but I have enough Java background to allow me to cobble together a workable, albeit ugly solution.