在上一篇帖子里,我介绍了处理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来把一个格林威治时间转换为本地时间。
AccountType 和 account.SellerApiContext 这两个是啥意思呀!刚接触哦Ebay API 很多还不懂。
AccountType是我自己定义的类型,包含ebay的token灯。
account.SelleApiContext是ApiContext类型变量:
ApiContext context = AppSettingHelper.GetApiContext(account.ebayToken);
有了token才可以获得一个ApiContext用来访问ebay api.
请教一下博主:record number能够用来作为订单号吗?如果一个订单中有多个商品时,这个record number是不是同一个?
1)record number对一个ebay id来说是唯一的。
2)一个订单有多个商品时,貌似订单中每笔交易也有一个唯一的record number,你可以参见http://www.ebaymaster.net/?p=5