Monthly Archives: April 2013

群发消息功能

做ebay的人都知道:沟通很重要!发完货后,过了一两个礼拜,问问买家的情况:怎么样了啊?货收到了没?有没有什么问题,喜欢不?买家收到这样的信后,即使长时间没收到货,或者货质量一般,他们一般也会感动于你的嘘寒问暖,差评的可能性就小很多!

我做ebay 2年多,一直是这样做,100%好评!可以最近有点懈怠,好久没问买家情况,得了一个差评,还有几个case :..(

跟ebay买家沟通,通过ebay网站的功能,实在效率较低,所以在管家婆中,我想把这块功能做好,做的简单方便:

  • 消息群发功能:可以同时给N个买家一起发信,问他们收到货没有。
  • 每个交易的来往信件都显示在一起,便于卖家比较对比。

下面是已经完成的功能截图:

群发消息:先按住shift键,选中所有要发消息的交易:

sendmsg01

然后 右键 选中“给买家写信“,弹出发信对话框:在这里,上面显示了所有的接收者,中间是消息模板,你可以选择一个模板(点击”管理消息模板“ 可以添加修改消息模板),选中一个模板,下面消息正文出现了模板的内容,你可以加以编辑,然后点击发送。

sendmsg02

正在发送消息:
sendmsg03

最后是 某一交易的所有往来信件 对话框:

sendmsg04

下载订单功能完成

今天完成了ebay管家婆的下载订单功能。

用户可以在管家婆左侧选择下载订单的开始时间结束时间(北京时间),然后点击”下载ebay数据“按钮,就可以下载数据了。下载数据的时候,管家婆会弹出一个进度窗口提示总共要更新多少订单和当前在下载的订单,是不是很一目了然? 🙂

另外管家婆管理的所有ebay账号的订单都会被下载,对于系统中没有的订单信息,管家婆会保存此订单信息;对于系统中已经有的订单,管家婆会更新系统中的订单信息(例如系统中的订单还是未付款状况,但是用户在上次下载数据后已经付款了,这样系统中的订单也会被更新成已付款状况)。

ebaymaster_syncorder

利用ebay api获取ebay订单(2)

在上一篇帖子里,我介绍了处理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来把一个格林威治时间转换为本地时间。

利用ebay api获取ebay订单(1)

订单管理是ebay管家婆的核心功能之一,订单管理包括下载订单处理订单(标记已发货,留好评,上传追踪号等等),我们这里要讲的是如何利用ebay api下载ebay订单。

ebay提供了好几个api函数来获取订单,推荐使用的是GetOrders,在详细介绍这个函数的用法前,我们先讲清楚几个基本概念。

如果大家list过商品,都知道一个list要么是fixed price(一口价),要么是auction(拍卖),对于fixed price的商品,可以有好多件;对于auction的商品,只能有一件。而买家买的时候,多数情况下买一件,有时也买多件:买多件的时候他可以选择Combined Payment,就是合并付款,那么一个订单里面有好几个商品,或者他一件一件付款,那么每个商品就是一个订单,如下图所示:

基本概念:

  • 订单(Order):一个订单里面可以包含一笔交易,或者多比交易,每个订单由一个订单号(OrderID)来唯一标识。
  • 交易(Transaction):每笔交易对应于买家购买的一件商品,交易由交易号(TransactionID)来标识,每笔交易包含一个对应的商品的ItemID。
  • 商品(Item):表示用户购买的上架商品,用ItemID来标识,一个上架商品可能是多件(fixed price),也可能是一件(fixed price或者auction)。

ebay中很多操作是基于交易而非订单的,比如买家留好评:买家A买了两件商品,合并付款在一个订单里面,他留好评的时候是留两个,而不是一个,也就是每个交易留一个好评,所以我们要有一种方法来唯一标识交易。

你可能问我们刚刚说的交易号(TransactionID)不行么?答案是不行,交易号只能在一个订单里面唯一标识一笔交易,而且对于一个 list只有一件商品,买家购买完后,其交易号是0!

我们来看一个例子:

OrderID                                                     TransactionID                                ItemID

133226486015                                        1075831541017                          271157242416

133226486015                                        1075831542017                          271157242416

271157242416-1075838043017             1075838043017                          271157242416

221203982620-0                                      0                                                 221203982620

这个例子显示了三个订单:

  • 订单号133226486015有两笔交易:对应的ItemID是一致的,表示是在一个fixed price listing里面购买了两种商品。
  • 订单号271157242416-1075838043017有一笔交易。
  • 订单号221203982620-0有一笔交易,其TransactionID是0,表明该listing只有一个商品。

我们可以看到订单号有两种表示方式:

  • 没有连字符(-)的:表明这个订单里面有多比交易:
  • 有连字符的(-):表明这个订单只有一笔交易,而且订单号是 OrderID = ItemID-TransactionID

我们上面提到ebay很多操作基于交易的,对于含多笔交易的订单,怎么唯一标识里面的一个交易了?TransactionID不行,ebay引入了一个叫OrderLineItemID

OrderLineItemID = ItemID + TransactionID

每个OrderLineItemID唯一标识一个交易,对于单笔订单,这个OrderLineItemID=OrderID,而对于含多笔交易的订单,OrderLineItemID!=OrderID。

明白了基本的概念,我将在下一篇帖子里讲述如何利用GetOrders获取订单信息。