在上一篇帖子里,我介绍了处理ebay订单需要了解的几个基本概念:订单号OrderID,交易号TransactionID,和唯一标识一个交易的OrderLineItemID,在这个帖子里,我们介绍:
- 利用ebay api GetOrders函数获取卖家订单信息,包括调用这个函数时一些需要注意的事项。
- 设计数据库表来存储订单信息。
- 和处理订单相关的其他一些ebay api函数。
一般ebay卖家的历史订单数量很大,如果每次调用GetOrders都获取所有订单既浪费时间,也没必要, GetOrders提供几个过滤器来过滤订单,过滤器输入主要有以下几项:
- CreateTimeFrom / CreateTimeTo: 指定只获取这段时间范围内产生的订单,注意最大的时间范围是90天。如果输入中指定了NumberOfDays,或者输入指定了Order IDs,则CreateTimeFrom/CreateTimeTo失效!
- ModTimeFrom / ModTimeTo:指定获取在这段时间范围内发生修改的订单(创建或者状态改变,如shipped等),最多能获取最近30天状态发生改变的订单,请使用这个Filter!!
- NumberOfDays:指定获取最近NumberOfDays天数里生成或者被修改的订单,这个参数我也没用过!
- OrderIDArray:只获取订单号在这个数组中的订单。
GetOrders还有其他过滤器输入,在此不表,看官如有兴趣可参考ebay官方文档。上述的几个输入,是有优先次序的:
OrderIDArray > NumberOfDays > CreateTimeFrom/CreateTimeTo > ModTimeFrom/ModeTimeTo
举个例子,如果输入中同时指定了OrderIDArray和NumberOfDays,则NumberOfDays失效!
======================================
获取订单的方式:
1)系统自动下载订单:每隔一段时间(例如5分钟)调用一次GetOrders,
- 将ModTimeFrom设置为上次下载结束时间的前2分钟,将ModTimeTo设置为当前时间
- 注意ebay api的时间都是UTC时间,如果使用c#/.net的话,需要用DateTime.ToUniversalTime将本地时间转化为ebay时间(反过来通过DateTime.ToLocalTime将ebay的UTC时间转化为本地时间)。
- 需要为GetOrders设置分页Pagination,GetOrders每页最大可以包含100条数据。
- 通过HasMoreOrders字段看是否还有其他记录,详见下面的代码:
2)手动下载订单:
- 同上,不过系统在数据库中记录上次下载到什么时间,当前下载从上次下载结束时间的前2分钟开始,注意上次下载结束到现在的时间间隔如果超过30天,则只能下载最近30天的数据。
我在ebay管家婆中实现的获取订单的方式是,先用DetailLevel=ReturnSummary来调用GetOrder,这样就可以返回指定时间内所有订单的订单号:
// Given a time period specified by startDate and endDate, returns all the order ids created in that period.
// note startDate/endDate are all local date.
// CAUTION: ****The maximum date range that may be specified with the ModTimeFrom and ModTimeTo fields is 30 days.****
// If ModTimeTo-ModTimeFrom > 30 days, then select orders in range [ModTimeTo-30, ModTimeTo].
public static StringCollection GetAllOrderIds(AccountType account, DateTime startDate, DateTime endDate)
{
if (account.SellerApiContext == null)
return null;
GetOrdersCall getOrdersApiCall = new GetOrdersCall(account.SellerApiContext);
DetailLevelCodeType[] detailLevels = new DetailLevelCodeType[] { DetailLevelCodeType.ReturnSummary };
getOrdersApiCall.DetailLevelList = new DetailLevelCodeTypeCollection(detailLevels);
getOrdersApiCall.Pagination = new eBay.Service.Core.Soap.PaginationType();
getOrdersApiCall.Pagination.EntriesPerPage = 100;
getOrdersApiCall.Pagination.PageNumber = 1;
//
// If ModTimeTo-ModTimeFrom > 30 days, then select orders in range [ModTimeTo-30, ModTimeTo].
//
TimeSpan ts = endDate - startDate;
if (ts.TotalDays > 30)
{
Logger.WriteSystemLog(String.Format("ModTimeTo-ModTimeFrom> 30 days, select range [ModTimeTo-30, ModTimeTo]"), LogLevel.Warning);
startDate = endDate.AddDays(-29);
}
getOrdersApiCall.ModTimeFrom = startDate.ToUniversalTime();
getOrdersApiCall.ModTimeTo = endDate.ToUniversalTime();
getOrdersApiCall.OutputSelector = new string[] {
"HasMoreOrders",
"OrderArray.Order",
"OrderArray.Order.ID",
"OrderList.OrderId",
};
try
{
getOrdersApiCall.Execute();
}
catch (System.Exception ex)
{
Logger.WriteSystemLog(String.Format("GetAllOrderIds: GetOrdersCall threw exception with err msg={0}", ex.Message),
LogLevel.Error);
return null;
}
if (getOrdersApiCall.OrderList == null)
return null;
Logger.WriteSystemLog(String.Format("Retrieved {0} orders", getOrdersApiCall.OrderList.Count));
StringCollection orderIds = new StringCollection();
foreach (OrderType order in getOrdersApiCall.OrderList)
{
orderIds.Add(order.OrderID);
}
while (getOrdersApiCall.HasMoreOrders)
{
getOrdersApiCall.Pagination.PageNumber++;
getOrdersApiCall.ModTimeFrom = startDate.ToUniversalTime();
getOrdersApiCall.ModTimeTo = endDate.ToUniversalTime();
getOrdersApiCall.Execute();
Logger.WriteSystemLog(String.Format("Has more orders : retrieved {0} orders", getOrdersApiCall.OrderList.Count));
foreach (OrderType order in getOrdersApiCall.OrderList)
{
orderIds.Add(order.OrderID);
}
}
return orderIds;
}
获得了订单号后,再调用GetOrders,指定DetailLevel=ReturnAll,这样就可以获取这些订单的详细信息,然后我们分析这些信息并存储到数据库:
特别注意的是:需要检查GetOrders获取的Order的checkout status,如果checkout status不是compeleted,就说明订单付款未完成,这种Order需要忽略掉。
还需要检查订单的eBayPaymentStatus,必须是NoPaymentFailure才可以。
public static List<EbayTransactionType> GetAllOrders(AccountType account, TimeFilter timeFilter, StringCollection orderIds)
{
List<EbayTransactionType> transList = new List<EbayTransactionType>();
GetOrdersCall getOrdersApiCall = new GetOrdersCall(account.SellerApiContext);
getOrdersApiCall.IncludeFinalValueFee = true;
DetailLevelCodeType[] detailLevels = new DetailLevelCodeType[] { DetailLevelCodeType.ReturnAll };
getOrdersApiCall.DetailLevelList = new DetailLevelCodeTypeCollection(detailLevels);
if (orderIds != null)
getOrdersApiCall.OrderIDList = orderIds;
OrderTypeCollection orders = getOrdersApiCall.GetOrders(timeFilter, TradingRoleCodeType.Seller, OrderStatusCodeType.All);
foreach (OrderType order in orders)
{
if (order.CheckoutStatus.Status != CompleteStatusCodeType.Complete)
continue;
if (order.CheckoutStatus.eBayPaymentStatus != PaymentStatusCodeType.NoPaymentFailure)
continue;
foreach (TransactionType trans in order.TransactionArray)
{
// Process each transaction.
}
}
return transList;
} // GetAllOrders
上面程序的一个小问题就是:如果timeFilter和orderIds都指定了,那么timeFilters失效。
为什么用OrderIDs这个过滤器而不用NumberOfDays了?因为获取了订单的号后,我们可以给用户展示一个进度条,这样的设计比较友好。
在具体分析每一个ebay交易的时候,有些问题需要注意:
- 多种款式商品(multi-variation):一口价的list,有些可以设置成含多种款式的,比如衣服,可以有红色、蓝色等,尺寸有S/M/L/XL等,一个ebay交易,是不是multi-variation对分析订单是有影响的。
比方说有一个multi-variation的list,里面有红色、黄色两种狗狗衣服,每种颜色有S/M/L/XL不同大小,我们会在上架时给他们设置不同的SKU:
- Dog Clothes Red S (sku=100001)
- Dog Clothes Red M (sku=100002)
- Dog Clothes Red L (sku=100003)
- Dog Clothes Red XL (sku=100004)
- Dog Clothes Yellow S (sku=100005)
- ...
ebay api中定义的TransactionType含有两个成员:ItemTitle/ItemSKU,对于非multi-variation list,这两个成员就是你上架时设置的;但是对于multi-variation list,这两个成员不好用了!比方说用户买了“Dog Clothes Red L”,我们要这样才能正确获取标题和SKU:
// What is the valid way to determine if there is a variation?
if (trans.Variation != null && trans.Variation.VariationTitle != null && trans.Variation.VariationTitle.Trim() != "")
{
// ItemTitle
ebayTrans.ItemTitle = trans.Variation.VariationTitle;
// ItemSKU
ebayTrans.ItemSKU = trans.Variation.SKU;
}
else
{
// ItemTitle
ebayTrans.ItemTitle = trans.Item.Title;
// ItemSKU
ebayTrans.ItemSKU = trans.Item.SKU;
}
订单是否已经付款?有些订单用户使用信用卡付款的,这些付款经过paypal处理的时候需要几天时间,在ebay管家婆中,我都视这些订单为已付款状况:【2015/3/18更新注意如果是checkout status=compelete && eBayPaymentStatus=NoPaymentFailure,则到这步的订单应该都付款了!】
ebayTrans.IsPaid = order.PaidTimeSpecified;
if (ebayTrans.IsPaid == false)
{
// Some payment is paid using credit card, and while PayPal is processing the payment,
// the transaction is marked as unpaid. we should view it as paid.
if (order.OrderStatusSpecified && order.OrderStatus == OrderStatusCodeType.Completed)
ebayTrans.IsPaid = true;
}
if (ebayTrans.IsPaid == false)
{
if (order.CheckoutStatus.StatusSpecified && order.CheckoutStatus.Status == CompleteStatusCodeType.Complete)
ebayTrans.IsPaid = true;
}
获取评价信息:这样我们可以了解买家是否留好评:【2015/3/18更新:ebay限制每天调用GetFeedback的次数,因此按我原来的方式是不行的,不能一个订单一个订单的获取feedback,应该批量下载feedback处理和存储】
GetFeedbackCall getFeedbackApiCall = new GetFeedbackCall(account.SellerApiContext);
getFeedbackApiCall.DetailLevelList = new DetailLevelCodeTypeCollection(detailLevels);
getFeedbackApiCall.OrderLineItemID = trans.OrderLineItemID;
FeedbackDetailTypeCollection feedbacks = getFeedbackApiCall.GetFeedback();
foreach (FeedbackDetailType feedback in feedbacks)
{
if (feedback.CommentingUser == account.ebayAccount)
ebayTrans.IsSellerLeftFeedback = true;
if (feedback.CommentingUser == ebayTrans.BuyerId)
ebayTrans.IsBuyerLeftFeedback = true;
}
if (trans.ShippingDetails != null)
{
if (trans.ShippingDetails.ShipmentTrackingDetails.Count == 1)
{
ShipmentTrackingDetailsType shipmentDetails = trans.ShippingDetails.ShipmentTrackingDetails[0];
ebayTrans.ShippingTrackingNo = shipmentDetails.ShipmentTrackingNumber;
}
}
// IsShipped
ebayTrans.IsShipped = order.ShippedTimeSpecified;
if (order.ShippedTimeSpecified)
ebayTrans.ShippedDate = order.ShippedTime;
另外注意一点:关于调用ebay api获取的时间都是格林威治时间,中国是+8区,所以如果获取的时间是2013/04/11 02:10:00,则北京时间是2012/04/11 10:10:00,c#中可以通过DateTime.ToLocalTime来把一个格林威治时间转换为本地时间。