Importing Amazon Seller Central Orders
Importing Amazon Seller Central Orders
Has anyone created a module to do this currently? We want to manage all of our orders including shipping from AbleCommerce and would like to import the orders from Amazon (currently re-keying) them in. If there is a large demand for this I am willing to create this plugin. I just do not want to reinvent the wheel. I have not been able to find any plugins for this to date.
Re: Importing Amazon Seller Central Orders
We do this. I pull orders using the Amazon MWS C# library. It's not exactly what I would consider a publishing-ready plugin though. There were a lot of customizations I added that the Amazon stuff has dependencies on (e.g., a custom plugins system, a task system that is a plugin for the previous, wrappers around the MWS library for fetching the orders as a task, processing the results, preparing XML feeds to post back to Amazon, another task that sends pending feeds, another plugin that tracks the status of submitted feeds...probably more I am forgetting at the moment). There's quite a bit involved in the whole thing to be honest. I'm sure I can offer some tips, or post some code if you need help with something specific.
Re: Importing Amazon Seller Central Orders
Would you be willing to share what you have or pay you for the "Module/function documentation" ? It may be better than trying to recreate the wheel...
Re: Importing Amazon Seller Central Orders
Well, let's see.......First we have some settings to define:
Next we have a static Services class that makes things easier later on...
Then there is the OrderService itself:
Here is the OrderFetcher code. It's only dependency is on the MWS library. It's pretty much the same as the sample code they give you in the download though (some slight differences to accommodate how I designed the rest of the system):
That should be everything necessary to pull down the orders from seller central. All you have to do is call Services.OrderService.FetchNewOrders(DateTime, OrderReceivedCallback) with the start date and the callback method as arguments. That's where you will have to actually decide how to put the data into your system. That's where things start getting a bit more complex as far as dealing with what will happen down the road with fulfillment and such. This where all my other custom dependencies start entering the mix. Hopefully that will at least get you started though, and if you have any specific questions I'll try to answer them. Sorry if the documentation is lacking. It's not my strong suit. I try to write clean code that speaks for itself.
Code: Select all
using CommerceBuilder.Common;
using CommerceBuilder.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AbleCommerce.Plugins.Amazon.MWS
{
public static class Settings
{
public static void Save()
{
AbleContext.Current.Store.Settings.Save();
}
public static bool UseMock
{
get { return AlwaysConvert.ToBool(AbleContext.Current.Store.Settings.GetValueByKey(AmazonSettingKeys.UseMock), true); }
set { AbleContext.Current.Store.Settings.SetValueByKey(AmazonSettingKeys.UseMock, value.ToString()); }
}
public static string MerchantID
{
get { return AbleContext.Current.Store.Settings.GetValueByKey(AmazonSettingKeys.MerchantID); }
set { AbleContext.Current.Store.Settings.SetValueByKey(AmazonSettingKeys.MerchantID, value); }
}
public static string MarketplaceID
{
get { return AbleContext.Current.Store.Settings.GetValueByKey(AmazonSettingKeys.MarketplaceID); }
set { AbleContext.Current.Store.Settings.SetValueByKey(AmazonSettingKeys.MarketplaceID, value); }
}
public static string AccessKey
{
get { return AbleContext.Current.Store.Settings.GetValueByKey(AmazonSettingKeys.AccessKey); }
set { AbleContext.Current.Store.Settings.SetValueByKey(AmazonSettingKeys.AccessKey, value); }
}
public static string SecretKey
{
get { return AbleContext.Current.Store.Settings.GetValueByKey(AmazonSettingKeys.SecretKey); }
set { AbleContext.Current.Store.Settings.SetValueByKey(AmazonSettingKeys.SecretKey, value); }
}
private static class AmazonSettingKeys
{
public static readonly string MerchantID = "AMWS_MerchantID";
public static readonly string MarketplaceID = "AMWS_MarketplaceID";
public static readonly string AccessKey = "AMWS_AccessKey";
public static readonly string SecretKey = "AMWS_SecretKey";
public static readonly string UseMock = "AMWS_UseMock";
}
}
}
Code: Select all
using CommerceBuilder.Common;
using System;
using System.Collections.Generic;
using System.Reflection;
using AbleCommerce.Plugins.Amazon.MWS.Orders;
namespace AbleCommerce.Plugins.Amazon.MWS
{
internal delegate void RetriableMethodCall();
public static class Services
{
/// <summary>
/// Gets the order service
/// </summary>
public static OrderService OrderService { get; private set; }
/// <summary>
/// Static constructor
/// </summary>
static Services()
{
OrderService = new OrderService();
}
/// <summary>
/// Gets the Application Name
/// </summary>
static internal string ApplicationName
{
get
{
Assembly assembly = Assembly.GetExecutingAssembly();
AssemblyTitleAttribute titleAtt = ((AssemblyTitleAttribute)Attribute.GetCustomAttribute(assembly, typeof(AssemblyTitleAttribute), false));
return (titleAtt != null && !string.IsNullOrWhiteSpace(titleAtt.Title)) ? titleAtt.Title : assembly.GetName().Name;
}
}
/// <summary>
/// Gets the Application Version
/// </summary>
static internal string ApplicationVersion
{
get
{
Assembly assembly = Assembly.GetExecutingAssembly();
return string.Format("{0}.{1}", assembly.GetName().Version.Major, assembly.GetName().Version.Minor);
}
}
}
}
Code: Select all
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MarketplaceWebServiceOrders;
using MarketplaceWebServiceOrders.Mock;
using MarketplaceWebServiceOrders.Model;
using IMarketplaceWebServiceOrders = MarketplaceWebServiceOrders.MarketplaceWebServiceOrders;
namespace AbleCommerce.Plugins.Amazon.MWS.Orders
{
public delegate void OrderReceivedCallback(Order order, IList<OrderItem> items);
public class OrderService
{
/// <summary>
/// Internal contructor
/// </summary>
internal OrderService() { }
/// <summary>
/// Retrieves unshipped orders from the order service
/// </summary>
/// <param name="startDate">The starting date to begin retrieving orders</param>
public void FetchNewOrders(DateTime startDate, OrderReceivedCallback callback)
{
OrderFetcher orderFetcher = new OrderFetcher(
GetOrderService(),
Settings.MerchantID,
new string[] { Settings.MarketplaceID }
);
orderFetcher.FetchOrders(startDate, callback);
}
/// <summary>
/// Gets the order service
/// </summary>
/// <returns>An interface for the Amazon order service</returns>
private IMarketplaceWebServiceOrders GetOrderService()
{
if (Settings.UseMock)
{
return new MarketplaceWebServiceOrdersMock();
}
else
{
MarketplaceWebServiceOrdersConfig config = new MarketplaceWebServiceOrdersConfig();
config.ServiceURL = "https://mws.amazonservices.com/Orders/2011-01-01";
return new MarketplaceWebServiceOrdersClient(
Services.ApplicationName,
Services.ApplicationVersion,
Settings.AccessKey,
Settings.SecretKey,
config
);
}
}
}
}
Code: Select all
using MarketplaceWebServiceOrders;
using MarketplaceWebServiceOrders.Mock;
using MarketplaceWebServiceOrders.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IMarketplaceWebServiceOrders = MarketplaceWebServiceOrders.MarketplaceWebServiceOrders;
namespace AbleCommerce.Plugins.Amazon.MWS.Orders
{
internal class OrderFetcher
{
/// <summary>
/// Default throttling limit for ListOrders calls; default to 1 per 15 seconds.
/// </summary>
private const int LIST_ORDERS_DEFAULT_THROTTLE_LIMIT = 15 * 1000;
/// <summary>
/// Default throttling limit for ListOrderItems calls; default to 1 per 2 seconds
/// </summary>
private const int LIST_ORDER_ITEMS_DEFAULT_THROTTLE_LIMIT = 2 * 1000;
private IMarketplaceWebServiceOrders mwsService;
private string mwsSellerId;
private string[] mwsMarketplaceIdList;
/// <summary>
/// Creates a new instance of the OrderFetcher class.
/// </summary>
internal OrderFetcher(IMarketplaceWebServiceOrders service, string sellerId, string[] marketplaceIdList)
{
mwsService = service;
mwsSellerId = sellerId;
mwsMarketplaceIdList = marketplaceIdList;
}
/// <summary>
/// Fetches all orders created between the starting time and the server's
/// local system time minus two minutes.
/// <param name="startTime">The starting time period of orders to fetch.</param>
internal void FetchOrders(DateTime startTime, OrderReceivedCallback callback)
{
FetchOrders(startTime, DateTime.MinValue, callback);
}
/// <summary>
/// Fetches all orders created in the given time period and processes them locally.
/// <param name="startTime">The starting time period of orders to fetch.</param>
/// <param name="endTime">The ending time period of orders to fetch.</param>
internal void FetchOrders(DateTime startTime, DateTime endTime, OrderReceivedCallback callback)
{
ListOrdersRequest request = new ListOrdersRequest();
request.LastUpdatedAfter = startTime.AddMinutes(-2);
if (endTime != DateTime.MinValue)
{
request.LastUpdatedBefore = endTime.AddMinutes(-2);
}
request.SellerId = mwsSellerId;
request.MarketplaceId = new MarketplaceIdList();
request.MarketplaceId.Id = new List<string>();
foreach (string marketplaceId in mwsMarketplaceIdList)
{
request.MarketplaceId.Id.Add(marketplaceId);
}
request.OrderStatus = new OrderStatusList();
request.OrderStatus.Status = new List<OrderStatusEnum>();
request.OrderStatus.Status.Add(OrderStatusEnum.Unshipped);
request.OrderStatus.Status.Add(OrderStatusEnum.PartiallyShipped);
List<Order> orderList = new List<Order>();
ListOrdersResponse response = null;
OrderFetcher.InvokeRetriable(LIST_ORDERS_DEFAULT_THROTTLE_LIMIT, delegate()
{
response = mwsService.ListOrders(request);
});
ProcessOrders(response.ListOrdersResult.Orders.Order, callback);
String nextTokenString = response.ListOrdersResult.NextToken;
while (!string.IsNullOrEmpty(nextTokenString))
{
// If NextToken is set, continue looping through the orders.
ListOrdersByNextTokenRequest nextRequest = new ListOrdersByNextTokenRequest();
nextRequest.NextToken = nextTokenString;
nextRequest.SellerId = mwsSellerId;
ListOrdersByNextTokenResponse nextResponse = null;
OrderFetcher.InvokeRetriable(LIST_ORDERS_DEFAULT_THROTTLE_LIMIT, delegate()
{
nextResponse = mwsService.ListOrdersByNextToken(nextRequest);
});
ProcessOrders(nextResponse.ListOrdersByNextTokenResult.Orders.Order, callback);
nextTokenString = nextResponse.ListOrdersByNextTokenResult.NextToken;
}
}
/// <summary>
/// Method called by the FetchOrders method to process the orders.
/// </summary>
/// <param name="orders">List of orders returned by FetchOrders</param>
internal void ProcessOrders(List<Order> orders, OrderReceivedCallback callback)
{
foreach (Order order in orders)
{
IList<OrderItem> items = FetchOrderItems(order.AmazonOrderId);
if (callback != null) callback.Invoke(order, items);
}
}
/// <summary>
/// Fetches the OrderItems for the specified orderId.
/// </summary>
private IList<OrderItem> FetchOrderItems(string orderId)
{
List<OrderItem> orderItems = new List<OrderItem>();
ListOrderItemsRequest request = new ListOrderItemsRequest();
request.SellerId = mwsSellerId;
request.AmazonOrderId = orderId;
ListOrderItemsResponse response = null;
OrderFetcher.InvokeRetriable(LIST_ORDER_ITEMS_DEFAULT_THROTTLE_LIMIT, delegate()
{
response = mwsService.ListOrderItems(request);
});
orderItems.AddRange(response.ListOrderItemsResult.OrderItems.OrderItem);
String nextTokenString = response.ListOrderItemsResult.NextToken;
while (!string.IsNullOrEmpty(nextTokenString))
{
// If NextToken is set, continue looping through the orders.
ListOrderItemsByNextTokenRequest nextRequest = new ListOrderItemsByNextTokenRequest();
nextRequest.NextToken = nextTokenString;
nextRequest.SellerId = mwsSellerId;
ListOrderItemsByNextTokenResponse nextResponse = null;
OrderFetcher.InvokeRetriable(LIST_ORDER_ITEMS_DEFAULT_THROTTLE_LIMIT, delegate()
{
nextResponse = mwsService.ListOrderItemsByNextToken(nextRequest);
});
orderItems.AddRange(nextResponse.ListOrderItemsByNextTokenResult.OrderItems.OrderItem);
nextTokenString = nextResponse.ListOrderItemsByNextTokenResult.NextToken;
}
return orderItems;
}
/// <summary>
/// Invokes a method in a retriable fashion.
/// </summary>
/// <param name="throttledWaitTime">The amount of time to wait if the request is throttled.</param>
/// <param name="method">The method to invoke.</param>
private static void InvokeRetriable(int throttledWaitTime, RetriableMethodCall method)
{
bool retryRequest = false;
do
{
retryRequest = false;
try
{
// Perform some action
method.Invoke();
}
catch (MarketplaceWebServiceOrdersException ordersErr)
{
// If the request is throttled, wait and try again.
if (ordersErr.ErrorCode == "RequestThrottled")
{
Console.WriteLine("Request is throttled; waiting...");
retryRequest = true;
System.Threading.Thread.Sleep(throttledWaitTime);
}
else
{
// On any other error, re-throw the exception to be handled by the caller
throw;
}
}
} while (retryRequest);
}
}
}
Re: Importing Amazon Seller Central Orders
Oh, and obviously you need to create a UI to add/update the settings we defined. It won't work without your MerchantID and all that jazz. I also realize looking over the code that I should have made the config.ServiceURL a setting as well so that if it changes I won't need to modify the code and recompile. Oops, heh.