⬆️ ⬇️

Integration of Ultima 2C and Ebay. Personal experience

The partner developer agreed to share his personal integration experience with EBay.



We will try to analyze the whole process step by step, let us indicate which details of the connection should be paid particularly careful attention; what basic logic will need to be implemented in the handlers and services of your configuration for regular interaction with EBAY. Well and also, we will highlight typical difficulties and inconveniences that you absolutely inevitably encounter with this integration (both you and customers).





1.Ebay Features



First of all, of course, the unpleasant features of Ebay, which not only the developer will have to deal with:



  1. Ebay does not like RF. Technical support - stripped down, some functions in the most native cabinet of the Ebay seller can periodically fall off. Connect the API reluctantly.



  2. Of course, no problem to trade as a non-Russian seller. And specify in the settings another country of origin. But you will need a real legal entity (in my case, a specially registered Chinese, for example), a phone with the code of the specified country for confirmation. PayPal, as the main payment system, very carefully checks the data, especially for legal entities and merchants. Billing address and other data must be real



  3. EBay is introducing more and more limits on products that you can post for free. We do not post the whole range, we choose what is needed.


2. Merchant Integration Platform (MIP)



What is it you can read here .



But only the introductory part. This talmudic work is not worth detailed examination. It will take technically very little. And the help at bugs and muffled errors of integration - any.



Briefly, this is api –adjusting the Ebay API. The latter has its own Trading Api and Shopping Api , but too cumbersome, and EBAY support itself wraps up at the MIP saying that in order to avoid any of your particular problem, you must use the MIP.



MIP allows you to send all the data to enter the ibeya account (goods, availability, prices, seller’s rules) and receive everything important at the output (actually orders) via the xml feeds system.



2.1 Administrative tasks



To arrange according to all the rules the account of the seller with the legal entity, link paypal, write in support of the MIP from your account with the request of the MIP to connect to the store.



2.2 The task of the developer





3. Connection steps:



3.1 Seller integration platform



After the MIP reports that your store has been connected, the menu item “Seller integration platform” will appear on the ebay website in My Ebay -> Overview -> Account.



3.2 Some more settings:





regulations



We are talking about the rules of payment, delivery, return .



When you click on them, the client is sent to the appropriate point of the cabinet, where he creates policies for each of these sections.



Important!



  1. The Cabinet allows you to create as many as you want. BUT MIP accepts only 1 payment policy, 1 delivery, 1 return. Therefore, the task in the settings within one record is to set all possible rules: For Any Weight, Region, and so on.



  2. The policy is invited to give a name, there are no restrictions on input. But for MIP it is necessary that the policy be called Latin, without spaces or underscores.


politics



3.3 Ribbon Scheme



Then go to the "Ribbon Scheme" and choose the format (recommended by Ebay) and the file format (we prefer XML). You can also download the archive with examples of feeds (and file structures on the FTP server).



scheme



3.4 FTP Setup



There we receive / generate data for access to the MIP FTP server, where we will load feeds with goods and from where we will collect feeds with reserves. (Server, port, username, password.)



Setup is complete, then - to the logic



3.5 Preparing metadata for storing settings with us



For all the above settings for connecting to Mip, you need to create a directory.



In the reference book, it is proposed to immediately score constants indicating references to the data that will be sent to the MIP and used when pumping reserves.



 EbayAccountSetting ID Long Not NULL, Name string(256) Long Not NULL, MipServerAddress string(256) Long Not NULL, MipServerPort Ineteger Not NULL, MipServerLogin string(256) Long Not NULL, MipServerPassword string(2048) Long Not NULL, NotifyEmails string(256) Long Not NULL, ), -      ,      ShippingPolicy string(256) Long Not NULL, ReturnPolicy string(256) Long Not NULL, PaymentPolicy string(256) Long Not NULL, IsSuborder Boolean Not NULL default true, LangID Long Not NULL , Reference Dictionary Language , - ,    FirmID Long Not NULL , Reference Dictionary Firm, - ,    OfficeReserveID Long Not NULL , Reference Dictionary Store, -   StoreReserveID Long Not NULL , Reference Dictionary Store, -   CurrencyID Long Not NULL , Reference Dictionary Currency,   PriceTypeID Long Not NULL , Reference Dictionary PriceType, -    PriceZoneID Long Not NULL , Reference Dictionary PriceZone, -    AgentGroupID Long Not NULL , Reference Dictionary AgentGroup, -   PaymentTypeID Long Not NULL , Reference Dictionary PaymentType -      


I recommend adding informational fields, such as a link to an ebay account, a username and password from the site, and so on, so as not to debug this information if necessary.



Depending on the needs of the client and the specifics of accounting for sales documents, we add links to other directories defining the properties of the created reserves or unloaded goods.



We also need to make a simple table in the oracle to keep track of what products we loaded into ebay.



We will need to remove these goods from the sale, if in the main request (goods that are currently in the sample) they will not (availability or price disappear), but in another way they will be pulling out a chore and uncomfortable.



 CREATE TABLE EBAY_EXPORTED_GOODS ( EBAY_ACOUNT_ID NUMBER(18) NOT NULL, - FK    EBAY_ACCOUNT_SETTINGS ARTICLE_ID NUMBER(18) NOT NULL, - FK    ARTICLES UPLOAD_DT DATE ) 


We also need to send for each product the code of its category in the EBAy system, and so that managers can easily map our products to their categories will need a directory of Ebay categories.



We can take as a basis our directory of product categories ArticleGroups. The new directory will also be a tree.



 EbayCategories ID long PK Name string – varchar(2048) ParentID long (referential   ) Code – string (   ebay    PK   SQL    ID         ) 


Directory must be tree-by-ParentID.



Finally, in our basic ArticleGroups reference, we will make an EbayCode string.



In the Editing Form of our native product categories, you need to make sure that the manager can select the appropriate Ebay category. After selecting, while saving our category in the field



EbayCode of our ArticleGroups will save the Code of the selected entry from the Ebay Category directory.



Next, we will need to create Sales Documents, Customers, Shipping Addresses, when importing orders.



To do this, in the Sale document add EbayDocumentNo string (2048)

In the CustomerAttribute add EbayUserID string (256)

In DeliveryAddress EbayAddressID string (256)



4 Integration



4.1 Feed System



If we look at the Mip admin panel in the seller’s office, go to the FTP server with the credentials obtained in 3.4, and also look at the example of feeds downloaded in 3.3, we will see the same structure

feeds1

feeds2



Feeds are divided into folders. The folders contain feeds of the same name (Availability.xml, Distribution.xml ...)



3 feeds, we must give ebay, informing about our products, their availability and prices (Availability, Distribution, Product)



1 feed we need to upload - Order



The feeds that we download from us to EBAY are placed by us in the root of the corresponding folder. Everything. Next, Ebay enqueues itself, transfers it to the processing folder, then to the archive folder, issues a log.



The feed that we upload from EBAY to us is also taken from the root of the order. Editing, deleting or doing something else with the file is not necessary.



Consider which class to write and, most importantly, parse XML to be as convenient as possible.



Maximum flexibility is desirable, the ability not to go by tags, but to jump over elements and grab them by enumerators, since most of the feed will be slag, which is not necessary to import.



System.Xml.Linq.XDocument was enough for me



Three feeds are responsible for transferring data from us to Ebay:



4.1.1 Product



Descriptive info for goods. Name, attributes (characteristics), category.



All required tags look in the downloaded example.



 <productRequest> <product> <SKU>369866</SKU> <productInformation localizedFor="en_US"> <title>LCD cable for Acer for Aspire 3820, 3820T, 3820G, 3820TG, 3820TZ</title> <description> <productDescription>LCD cable for Acer for Aspire 3820, 3820T, 3820G, 3820TG, 3820TZ</productDescription> </description> <pictureURL>http://oursite.com/good_big_pics/369866.jpg</pictureURL> <category categoryType="EBAY_LEAF_CATEGORY">168061</category> <!-- categoryType should be EBAY_LEAF_CATEGORY for the category to be applied. --> <conditionInfo> <condition>New</condition> <conditionDescription>Brand new</conditionDescription> </conditionInfo> </productInformation> </product> </productRequest> 


Remarks:



 -<condition>New</condition> <conditionDescription>Brand new</conditionDescription> 


Can be considered constant. We were unable to understand personally the limitations on posting used goods - The names of Enumerable states that you dig out in the documentation. The item must be new.



All tags with the localizedFor = "en_US" attribute mean that for one product you can place different information for different ebay subsites corresponding to the specified locale (for example, different names for En and Ru)



4.1.2 Availability



Transmits only availability in units.



 <inventoryRequest> <inventory> <SKU>369866</SKU> <totalShipToHomeQuantity>5</totalShipToHomeQuantity> </inventory> </inventoryRequest> 


4.1.3 Distribution



Prices, purchase limits, POLICIES, which we talked about in 3.2

Then just need English. Shipping, Return and Payment Names.



 <listingDetails> <shippingPolicyName>BaseShipping</shippingPolicyName> <!-- Replace business policy names with your seller's business policy names. --> <maxQuantityPerBuyer>5</maxQuantityPerBuyer> <paymentPolicyName>Base</paymentPolicyName> <!-- Replace business policy names with your seller's business policy names. --> <returnPolicyName>BaseReturn</returnPolicyName> <!-- Replace business policy names with your seller's business policy names. --> <pricingDetails> <listPrice>7.58</listPrice> </pricingDetails> 


The currency is not indicated here, it is indicated by the client in the personal account.



4.1.4 Feed Relationship



The rules for posting feeds are very simple.



  1. You yourself set the frequency with which they fast in Ebay, there are no limits. Accordingly, the task that will generate feeds is adjusted according to the work schedule of other tasks updating the essential parameters of the goods (availability, prices). Too often, it should not be tugged, since the MIP has its own turn, and sometimes a bulk feed can stand in a queue for a couple of hours.



  2. The SKU tag serves as a unique product identifier in 3 feeds; accordingly, it’s better not to be original, and not to add complex constructions with part numbers there, but to post something equally unambiguous and unique - our product code.



  3. Feeds are “perceived” in turn. First you have to go to the FTP Product, then Availabiliy, then Distribution.



  4. Removal of goods is made by sending availability = 0.

    Therefore, in advance, provide a table in which you will store goods that have already been posted in MIP



  5. Each xml is packaged in zip and put on ftp, in the same folder. Provide a library to work with FTP.

    We took Renci.SshNet.SftpClient

    An example of a file upload method by this library



     private void Upload(long accountId, string zipFileName, string innerPath) { var EbayAccount = DictionaryManager.GetRecord<EbayAccountSettings>(accountId); var serverAdr = EbayAccount.MipServerAddress; //mip.ebay.com; var port = (int)EbayAccount.MipServerPort;// 22 var login = EbayAccount.MipServerLogin; // my acc var password = EbayAccount.MipServerPassword; using(var fStream = new System.IO.FileStream(zipFileName, System.IO.FileMode.Open)) { using(var c = new Renci.SshNet.SftpClient(serverAdr, port, login, password)) { c.Connect(); c.OperationTimeout = new TimeSpan(0, 15, 0); var fullPath = innerPath +System.IO.Path.GetFileName(zipFileName); /*innerPath –       . , store/availability. zipFileName –         */ if (c.Exists(fullPath)) { try { c.DeleteFile(fullPath); } catch (Exception e) { LogManager.GetLogger().Info(@" ebay files to mip. error delete exist file. fileName = {0}; error = {1}", fullPath, e.Message); } } c.UploadFile(fStream, fullPath, true); } } } 


  6. After a while (depending on the ebay queue), the result of the MIP processing is placed on the FTP in the same folder, in a subfolder

    Output / {current_date: M-dd-YYYY} / .... .xml


For example:



        FTP   /store/availability    /store/availability/output/Jul-18-2016/ 


  1. You can also load in parallel or just watch the results of feed processing in the office in the Tape Archive



  2. In 99% of cases, if there was any mistake, at the Product.xml and Availability.xml stages, MIP will keep silent and say that everything is ok. The error will be, unless at you frankly malformed xml.


Even if errors are related to these feeds, errors will only be visible as a result of processing the last feed, Distribution.xml



  1. The question of whether it makes sense to parse the results of uploading to our epers is an extremely controversial one.

    There is no consistency, no serializability in errors.


Moreover, by interest 70 of them have to apply in writing in support of the MIP, which in itself is an event with 50% performance. Requests often have to be sent several times, since the success of the request depends on the employee you get.



Google errors helps a little.



Also, it often turns out that just Mip himself had some kind of issue about those. details of which they do not apply, simply stating that Use for Health, we have fixed.



Also, the presence of an error in the response does not guarantee that all other goods are not loaded.



Sometimes one mistake blocks all products, even those to which it does not apply, sometimes only from the entire feed.



Summarizing, we can say that it makes sense to just follow the results of unloading in the office.



For the sake of tautology, you can stop at this solution:



Parsing by dates the contents of the folder / output / distribution. Only capture tags in xml

And in the textually-informative-list form, simply duplicate them into a separate directory in the date, if the client wants to look at the error in the time, and not in the office.



Defect response example



 <?xml version='1.0' encoding='UTF-8'?> <!-- RequestID: 5089523166 --><distributionResponse> <responseMessage> <SKU>6124</SKU> <channelID>EBAY_RU</channelID> <status>FAILURE</status> <error> <errorID>API_95</errorID> <severity>ERROR</severity> <category>REQUEST</category> <message><![CDATA[         .]]></message> </error> </responseMessage> <status>FAILURE</status> </distributionResponse> 


So, let's try to generate and fill in successively 3 feeds.



 var startDate = DateTime.Now; //   ,    ,     var accountId = 1L; //  var account = DictionaryManager.GetRecord<EbayAccountSetting>(accountId); //  var productDoc = ExportXmlProducts(account, startDate); // xml   ZipAndUploadFile(accountId, productDoc, "Product", "/store/product/"); // var availDoc = ExportXmlAvailability(account, startDate); // xml   ZipAndUploadFile(accountId, availDoc, "Availability", "/store/availability/"); // var distrDoc = ExportXmlAvailability(account, startDate); // xml     ZipAndUploadFile(accountId, availDoc, "Distribution", "/store/distribution/"); // 


We form xml Products Products.xml



 private XDocument ExportXmlProducts(EbayAccountSetting account, DateTime startDate) { var listings = new XElement("productRequest"); //   ,       ,    Ebay var sql = @" SELECT A.ID, NVL(PT.STRING_VALUE, A.ONLINE_NAME) AS NAME, P.VALUE PRICE, CASE WHEN PH.ARTICLE_ID IS NOT NULL THEN PH.ARTICLE_ID||'_'||PH.VIEW_ID ELSE '' END ICON_ID, A.FEATURE_SET_ID, B.NAME, AG.EBAY_CODE EBAY_CODE FROM ARTICLES A JOIN /*        */ VTB_STOCK VB ON VB.ARTICLE_ID=A.ID and VB.STORE_ID =:vStoreID JOIN PRICES P ON A.ID=P.ARTICLE_ID AND P.PRICE_TYPE_ID=:vPriceTypeD AND P.PRICE_ZONE_ID=:vPriceZoneID /*        */ JOIN BRANDS B ON A.BRAND_ID=B.ID JOIN ARTICLE_GROUPS AG ON A.GROUP_ID=AG.ID LEFT JOIN ARTICLE_PHOTOS PH ON A.ID=PH.ARTICLE_ID AND PH.PHOTO_ORDER=1 LEFT JOIN KERNEL.PROP_TRANSLATIONS PT ON PT.VALUE_ID = a.ID AND PT.OBJECT_ID = :vObjectID AND PT.LANG_ID = :vLangID WHERE VB.QUANTITY>VB.RESERVE_QUANTITY /*    */ AND P.VALUE>0 /*    */ AND AG.EBAY_CODE IS NOT NULL /*   Ebay */ "; var pars = new Dictionary<string, object>{ {"vObjectID", productObjectId}, {"vLangID", account.LangID}, {"vStoreID", account.StoreReserveID}, {"vPriceTypeD", account.PriceTypeID}, {"vPriceZoneID", account.PriceZoneID}}; var goods = SqlService.Select(sql, pars); // xml    foreach (var good in goods) { var propNode = GetProperties(Convert.ToInt64(good["ID"]), account.LangID);//   - var productNode = new XElement("product", new XElement("SKU", good["ID"].ToString()), new XElement("productInformation", new XAttribute("localizedFor", "en_US"), new XElement("title", good["NAME"].ToString()), new XElement("description", new XElement("productDescription", good["NAME"].ToString())) , "",//propNode, new XElement("pictureUrl", String.Format(@"http://mysite.com/good_pics/{0}.jpg", good["ICON_ID"].ToString())), new XElement("category", new XAttribute("Type", "eBayLeafCategory"), good["EBAY_CODE"].ToString()), new XElement("conditionInfo", new XElement("Condition", "new"), new XElement("conditionDescription", @"Brand new"))) ); listings.Add(productNode); AddToExported(Convert.ToInt64(good["ID"]), account.ID, startDate); } var doc = new XDocument(listings); return doc; } 


A couple of important notes:





We need to send the characteristics of the goods. We recall the structure of patterns of description and characteristics. Our task is to bring all the characteristics of the goods to the form CHARACTERISTIC (string) - VALUE (string), even in cases when it is a matter of several values ​​for one characteristic.



 private string GetProperties(long articleId, long langId) { var sql = @"WITH T AS ( WITH T AS (SELECT F.ID AS PROP_ID, NVL(PT.STRING_VALUE, F.NAME) AS PROP_NAME, V.SORT_ORDER, CASE WHEN F.TYPE_ID=1 THEN TO_CHAR(D.NUMBER_VALUE) WHEN F.TYPE_ID=2 THEN TO_CHAR(D.BOOLEAN_VALUE) WHEN F.TYPE_ID=3 THEN (D.STRING_VALUE) WHEN F.TYPE_ID=4 THEN TEXT_VALUE WHEN F.TYPE_ID in (5,6,7) THEN nvl(PT1.STRING_VALUE,V.VALUE) END VAL, CASE WHEN UN.ID IS NOT NULL THEN ' '|| NVL(PT2.STRING_VALUE, UN.NAME) ELSE '' END AS UNIT_NAME FROM ARTICLE_FEATURE_VALUES D JOIN ARTICLE_FEATURES F ON D.FEATURE_ID=F.ID LEFT JOIN ARTICLE_FEATURE_UNITS UN ON F.UNIT_MEASUREMENT_ID=UN.ID LEFT JOIN ARTICLE_FEATURE_VALID_VALUES V ON D.VALUE_ID=V.ID LEFT JOIN KERNEL.PROP_TRANSLATIONS PT ON PT.VALUE_ID = F.ID AND PT.OBJECT_ID = :vPropObjectID AND PT.LANG_ID = :vLangID LEFT JOIN KERNEL.PROP_TRANSLATIONS PT1 ON PT1.VALUE_ID = V.ID AND PT1.OBJECT_ID = :vValObjectID AND PT1.LANG_ID = :vLangID LEFT JOIN KERNEL.PROP_TRANSLATIONS PT2 ON PT2.VALUE_ID = UN.ID AND PT2.OBJECT_ID = :vUnitObjectID AND PT2.LANG_ID = :vLangID WHERE D.ARTICLE_ID=:vProdID ) SELECT PROP_ID, PROP_NAME, LISTAGG(VAL||UNIT_NAME, ',')WITHIN GROUP (ORDER BY SORT_ORDER) VAL FROM T GROUP BY PROP_ID, PROP_NAME"; var pars = new Dictionary<string, object>{ {"vLangID", langId}, {"vProdID", articleId}, {"vValObjectID", valueObjectId}, {"vUnitObjectID", unitObjectId}, {"vPropObjectID", featureObjectId} }; var features = SqlService.Select(sql, pars); var featuresqueue = features.Select(r => new XElement("attribute", new XAttribute("name", (string)r["PROP_NAME"]), (string)r["VAL"]).ToString()) .ToList(); return string.Join("", featuresqueue); } 


Let's create the residual file Availability.xml



 private XDocument ExportXmlAvailability(EbayAccountSetting account, DateTime startDate) { var listings = new XElement("inventoryRequest"); //        .         -    -   = 0 var sql = @" SELECT EG.aRTICLE_ID ID, case when last_export_date < :vDat then NVL(VB.QUANTITY, 0) - NVL(VB.RESERVE_QUANTITY, 0) else 0 end QUANTITY FROM EBAY_EXPORTED_GOODS EG LEFT JOIN VTB_STOCK VB ON VB.ARTICLE_ID=EG.ARTICLE_ID and VB.STORE_ID =:vStoreID WHERE EG.EBAY_ACCOUNT_ID=:vId "; var pars = new Dictionary<string, object>{ {"vId", account.ID}, {"vStoreID", account.ReserveStoreID}}; var goods = SqlService.Select(sql, pars); foreach (var good in goods) { var productNode = new XElement("inventory", new XElement("SKU", good["ID"].ToString()), new XElement("totalShipToHomeQuantity", good["QUANTITY"].ToString())); listings.Add(productNode); } var doc = new XDocument(listings); return doc; } 


By the same principle we form Distribution.xml



We pack our xml in zip and uploaded the method we already described



 private void ZipAndUploadFile(long accountId, XDocument doc, string fileName, string innerPath) { var tempDirectory = @"C:\WINDOWS\Temp\EbayTemp";//     .  ,      System.IO.Directory.CreateDirectory(tempDirectory); var cFullFileName = System.IO.Path.Combine(tempDirectory, fileName); var xmlFileName = System.IO.Path.ChangeExtension(cFullFileName, "xml"); doc.Save(xmlFileName); var zipFullFileName = System.IO.Path.ChangeExtension(cFullFileName, "zip"); var zipper = new ZipBuilder(); zipper.AddFile(xmlFileName, delete: false); zipper.SaveArchive(zipFullFileName); Upload(accountId, zipFullFileName, innerPath); } 


For the transfer of orders from Ebay to us is responsible 1 feed.



4.1.5 Order



Orders fall into the order / output folder



There are also files grouped by date.

{date: M-dd-YYYY} / .... .xml

For example / store / order / output / Jul-10-2016 /



You can parse the output folder by date.



But for convenience, MIP dumps the last response feed into the file / store / order / output / order-latest



Moreover, the recent order is not overwritten by new ones, but is held there for a certain time. So even despite the possibility of the file task, current clients had quite regularly (every 5–10 minutes) to parse this particular file.



Life hacking:



For convenience of testing, you can take out logic from the task to the service and create a user command, and for the test, make it possible to transfer the input to the xml file parameter. Then, for testing, you can download the document in ibeya’s office or via FTP and run it several times.



In the header of the sales document, add the EBAY Number field. When processing an order, we check that there are no orders with such a number (otherwise, we just ignore).



Finally, all feed orders have already been paid. The settlement scheme in this case is equivalent to acquiring, where the bank is paypal. Accordingly, after creating an order, it is necessary to create the appropriate acquiring documents (in the basic configuration via the PaymentService methods).



The order feed contains the following important information.



  1. INFA client: Fio, email, phone



  2. Address, detail, including zip



  3. List of products with a unique identifier that you submitted in feeds 1-3 in the SKU tag



  4. Price of goods

    Do not forget that at the moment of import you can’t get the actual price for the column, because it is not known what the price of the feed was for which the customer saw and bought this product. We take it from Ebay.



  5. Delivery price

    Total import algorithm should look roughly like this:

    • Take the order number Ebay. Check whether it has not imported it.
    • Make GetOrCreateAgent - by data (# Fio, email, phone #)
    • We do the agent GetOrCreateDeliveryAddress
    • We create a reserve, fill out the goods with the necessary prices.
    • We activate delivery tch, we put down an address code and the price from Ebay.


Feed example



 <?xml version='1.0' encoding='UTF-8'?> <pendingOrderFulfillmentResponse> <pendingOrderFulfillment> <order> <orderID>222034713176-1766499808012</orderID> <channelID>EBAY_US</channelID> <createdDate>2016-07-20T12:45:09.000Z</createdDate> <buyer> <buyerID>andreyka.mosin.1997-3</buyerID> <firstName></firstName> <lastName></lastName> <email>andreyka.mosin.1997@mail.ru</email> </buyer> <seller> <sellerID>pdirect.ru</sellerID> </seller> <logisticsPlan fulfillmentType="SHIP"> <shipping shippingType="DIRECT"> <shippingService> <service/> <method>RU_ExpeditedMoscowOnly</method> </shippingService> <shipToAddress> <addressID>4504234220015</addressID> <name> </name> <phone>9527720655</phone> <addressLine1> 16</addressLine1> <addressLine2/> <city></city> <stateOrProvince/> <postalCode>607183</postalCode> <country>RU</country> </shipToAddress> <expectedDeliveryDate/> </shipping> </logisticsPlan> <lineItem> <lineItemID>222034713176-1766499808012</lineItemID> <listing> <channelID>CustomCode</channelID> <itemID>222034713176</itemID> <SKU>413480</SKU> <title>ELM327 BlueTooth V1.5 BIG  ELM327 BlueTooth V1.5 BIG chip pic18f25k80</title> </listing> <quantity>1</quantity> <unitPrice currencyCode="RUB">990.0</unitPrice> <subtotal> <priceline type="ITEM"> <amount currencyCode="RUB">990.00</amount> </priceline> <priceline type="SHIPPING"> <amount currencyCode="">350.00</amount> </priceline> <priceline type="ITEM_TAX"> <description>SalesTax</description> <amount currencyCode="RUB">0.00</amount> </priceline> <priceline type="RECYCLING"> <description>ElectronicWasteRecyclingFee</description> <amount currencyCode="RUB">0.00</amount> </priceline> <sumtotal currencyCode="RUB">1340.00</sumtotal> </subtotal> </lineItem> <payment> <paymentID>3TD678480B805680J</paymentID> <total currencyCode="RUB">1340.0</total> </payment> <total> <priceline type="ITEM"> <amount currencyCode="RUB">990.0</amount> </priceline> <priceline type="ITEM_TAX"> <amount currencyCode="RUB">0.0</amount> </priceline> <priceline type="SHIPPING"> <amount currencyCode="RUB">350.0</amount> </priceline> <priceline type="DISCOUNT"> <amount currencyCode="RUB">0.0</amount> </priceline> <sumtotal currencyCode="RUB">1340.0</sumtotal> </total> <status> <paymentStatus>PAID</paymentStatus> <logisticsStatus>NOT_SHIPPED</logisticsStatus> </status> <note/> </order> </pendingOrderFulfillment> </pendingOrderFulfillmentResponse> 


Import examples



 var accountId = 1L; //  var EbayAccount = DictionaryManager.GetRecord<EbayAccountSetting>(accountId); //     var doc = Download(accountId); // Renci foreach (var ordXml in doc.Root.Elements("pendingOrderFulfillment")) { if (ordXml.Elements("order").Count() == 0) { continue; } CreateReserve(ordXml.Elements("order").First(), EbayAccount); //  } 


We organize the jump as well as upload our feed to Ebay



 private XDocument Download(long accountId) { var EbayAccount = DictionaryManager.GetRecord<EbayAccountSetting>(accountId); var serverAdr = EbayAccount.MipServerAddress; //mip.ebay.com; var port = (int)EbayAccount.MipServerPort;// 22 var login = EbayAccount.MipServerLogin; // my acc var password = EbayAccount.MipServerPassword; var doc = new XDocument(); using(var c = new Renci.SshNet.SftpClient(serverAdr, port, login, password)) { c.Connect(); c.OperationTimeout = new TimeSpan(0, 15, 0); byte[] fileBytes = c.ReadAllBytes(@"/store/order/output/order-latest"); if ( fileBytes == null || fileBytes.Length == 0) { return doc; } using (var stream = new System.IO.MemoryStream(fileBytes, false)) { try { doc = XDocument.Load(new System.Xml.XmlTextReader(stream)); } catch(Exception e) { LogManager.GetLogger().Info(@"Import ebay files. error = {0}", e.Message); } } } return doc; } 


Actually creating a sales document.



 private void CreateReserve(XElement ebayOrdXml, EbayAccountSetting EbayAccount) { var ebayOrderID = ebayOrdXml.Elements("orderID").First().Value; if (CheckOrderExists(ebayOrderID)) //   Ebay      .   .   - . { LogManager.GetLogger().Info(@"  "); return; } var shippingXml = ebayOrdXml.Elements("logisticsPlan").First().Elements("shipping").First().Elements("shipToAddress").First(); var delivAmountFromEbay = 0M; //  .    ,            . delivAmountFromEbay = Decimal.Parse(ebayOrdXml.Elements("total").First().Elements("priceline").First().Elements("amount").First().Value.Trim(), System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.CultureInfo.InvariantCulture); //       var phone = shippingXml.Elements("phone").First().Value; var firstName = shippingXml.Elements("firstName").First().Value; var lastName = shippingXml.Elements("lastName").First().Value; var Street1 = shippingXml.Elements("addressLine1").First().Value; var Street2 = shippingXml.Elements("addressLine2").First().Value; var CityName = shippingXml.Elements("city").First().Value; var StateOrProvince = shippingXml.Elements("stateOrProvince").First().Value; var PostalCode = shippingXml.Elements("postalCode").First().Value; var EbayAddressID = shippingXml.Elements("addressID").First().Value; var ebayBuyerUserID = ebayOrdXml.Elements("buyerID").First().Value.Trim(); var email =ebayOrdXml.Elements("buyer").First().Elements("email").First().Value; //    var agentID = CreateAgent(firstName, lastName, email, phone,Street1,ebayBuyerUserID, EbayAccount); //    var adressID = CreateAgentAddress(agentID, PostalCode, firstName, phone, CityName, StateOrProvince, EbayAddressID, Street1, Street2); //  ,   var initialSubtype = SaleDocument.Subtypes.Reserve; var document = DocumentManager.NewDocument<SaleDocument>(initialSubtype); document.AgentID = agentID; document.OfficeID = EbayAccount.OfficeReserveID; document.PriceTypeID = EbayAccount.PriceTypeID; document.FirmID = EbayAccount.FirmID; document.StoreID = EbayAccount.StoreReserveID; //  ,  ,  SKU foreach(var transXml in ebayOrdXml.Elements("lineItem")) { var articleNo= Convert.ToInt64(transXml.Elements("listing").First().Elements("SKU").First().Value); var qty= Convert.ToInt64(transXml.Elements("quantity").First().Value); //  .    ,            . var price = Decimal.Parse(transXml.Elements("unitPrice").First().Value.Replace(".", ",")); document.Articles.Add(new SaleArticleTablePartRow { ArticleID = articleNo, SaleQuantity = qty, ReservedQuantity = qty, OriginalPrice = price, SalePrice = price, Amount = price * qty }); } //         .     outsource.     var deliveryWizardRow = WebService.GetDeliveryWizardRow(document, adressID, AgentType.Constants.CustomerID, DateTime.Now, "outsource", EbayAccount.ID, delivAmountFromEbay); document.DeliveryWizard.Clear(); document.DeliveryWizard.Add(deliveryWizardRow); document.Delivery.AddRange(DeliveryService.CreateDeliveries(document)); DocumentManager.SaveDocument(document); } 


Ebay



 private bool CheckOrderExists(string ebayOrderID) { return (from d in DataContext.GetTable<SaleDocument>().Where(x => x.EbayDocumentNo == ebayOrderID) select d).Any(); } 


. Ebay, –



 private long CreateAgent(string firstName, string lastName, string email, string phone, string Street1, string ebayBuyerUserID, EbayAccountSetting EbayAccount) { var customer = new CustomerAttribute() {}; var privatePerson = new PrivatePersonAttribute() {}; var agent = new Agent() {}; var exAgents = (from d in DataContext.GetTable<Agent>().Where(x => x.Customer.EbayUserID == ebayBuyerUserID) select new { Id = d.ID, CustomerID = d.CustomerID, PrivatePersonID = d.PrivatePersonID }).ToList(); if (exAgents.Any()) { customer = DictionaryManager.GetRecord<CustomerAttribute>(exAgents.First().CustomerID.GetValueOrDefault()); privatePerson = DictionaryManager.GetRecord<PrivatePersonAttribute>(exAgents.First().PrivatePersonID.GetValueOrDefault()); agent = DictionaryManager.GetRecord<Agent>(exAgents.First().Id); } else { customer = DictionaryManager.NewRecord(new CustomerAttribute { EbayUserID = ebayBuyerUserID, PriceTypeID = EbayAccount.PriceTypeID //. .  }); privatePerson = DictionaryManager.NewRecord(new PrivatePersonAttribute { GenderID = Gender.Constants.Undisclosed, }); agent = DictionaryManager.NewRecord(new Agent { TypeID = AgentType.Constants.CustomerID, FormID = AgentForm.Constants.PrivatePersonID, GroupID = EbayAccount.AgentGroupID }); } customer.ReserveLifetime = 1; DictionaryManager.SaveRecord(customer); privatePerson.FirstName = firstName; privatePerson.LastName = lastName; privatePerson.Phone =phone; privatePerson.Email = email; DictionaryManager.SaveRecord(privatePerson); agent.Name = string.Format("{0} {1}", lastName, firstName); agent.PrivatePersonID = privatePerson.ID; agent.CustomerID = customer.ID; DictionaryManager.SaveRecord(agent); return agent.ID; } private long CreateAgentAddress(long agentID, string PostalCode, string firstName, string phone, string CityName, string StateOrProvince, string EbayAddressID, string Street1, string Street2) { var address = new DeliveryAddress() {}; var exAddress = DictionaryManager.GetRecords<DeliveryAddress>(x => x. EbayAddressID == EbayAddressID);//  . . ebay if (exAddress.Any()) { address = exAddress.First(); } else { address = DictionaryManager.NewRecord(new DeliveryAddress { // .        DeliveryAreaID = DeliveryArea.Constants.Default, Latitude = 0M, Longitude = 0M, EbayAddressID = EbayAddressID }); } address.Address = String.Format("{1}, {2}, {3}, {4}", PostalCode, CityName, StateOrProvince, Street1, Street2); if (address.Latitude == 0) { //   var locations = GeoService.GetLocations(address.Address); if (locations.Any()) { address.Latitude = locations.First().Latitude; address.Longitude = locations.First().Longitude; address.DeliveryAreaID = DeliveryService.FindDeliveryArea(address.Latitude, address.Longitude); } } address.ContactPersonName = firstName; address.ContactPersonPhones = phone; DictionaryManager.SaveRecord(address); return address.ID; } 


5. Ebay



Ebay .

http://www.ebay.com/sch/allcategories/all-categories



, , Ebay, .



MIP , Products.xml , - , , ).

( ); , , , , .



:





5.1 Ebay.



. Ebay . , .



HTML, http://www.ebay.com/sch/allcategories/all-categories , Api Ebay.



Api, MIP, . , . – Ebay Mip.



5.2. API Ebay.



Api Ebay Ebay Developer. https://developer.ebay.com/base/membership/signin/fyp , , .



Application AppId, DevId CertId. , Ebay, , .



5.3.



string , - Ebay, .



, string



 EbayAppId EbayAppToken EbayDevId EbayCertId 


Ebay, , .



 EbayEndPoint EbayVersion EbaySiteId – 215,      . 


5.4 wsdl



wsdl http://developer.ebay.com/webservices/latest/ebaySvc.wsdl

, , https://go.developer.ebay.com/api-documentation .

wsdl.

, .



wsdl.exe ( c:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools), .



:



 >"c:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\wsdl.exe http://wstest.dpd.ru/services/geography?wsdl /out:MyEbayclass.cs 


:



a) x64 .



) ,



,



5.5. -



We put down the handler code the code of the generated wsdl class.

We need to create an object of our class to use it later.



 private static object _lock = new Object(); private eBayAPIInterfaceService EbayService { get { if (_instance == null) { lock ( _lock) { if (_instance == null) { _instance = new eBayAPIInterfaceService(); _instance.Timeout = 1200000; return _instance; } } } return _instance; } } 


Get a list of categories from ebay.



 var a = Constants.Url; var endpoint = Constants.Ebay.ApiEndpoint; var callName = "GetCategories"; var siteId = "215" ;//  var appId = Constants.EbayAppId;// '// use your app ID var devId = Constants.EbayDevId;// use your dev ID var certId = Constants.EbayCertId; // use your cert ID var version= "405"; var token = Constants.EbayToken; // Build the request URL var requestURL = endpoint + "?callname=" + callName + "&siteid=" + siteId + "&appid=" + appId + "&version=" + version + "&routing=default"; EbayService.Url = requestURL; // Set credentials EbayService.RequesterCredentials = new CustomSecurityHeaderType(); EbayService.RequesterCredentials.eBayAuthToken = token; EbayService.RequesterCredentials.Credentials = new UserIdPasswordType() EbayService.RequesterCredentials.Credentials.AppId = appId; EbayService.RequesterCredentials.Credentials.DevId = devId; EbayService.RequesterCredentials.Credentials.AuthCert = certId; GetCategoriesRequestType request = new GetCategoriesRequestType(); request.Version = "405"; request.CategorySiteID = "215"; request.CategoryParent = new string[] {"0"}; request.ViewAllNodes = true; request.LevelLimit = 0; request.DetailLevel = new DetailLevelCodeType[] {DetailLevelCodeType.ReturnAll}; var a = ConstantManager["Url"].ToString(); var endpoint = ConstantManager["Ebay. Api Endpoint"].ToString(); var callName = "GetCategories"; var siteId = "215" ;//  var appId = ConstantManager["EbayAppId"].ToString() ;// '// use your app ID var devId = ConstantManager["EbayDevId"].ToString();// use your dev ID var certId = ConstantManager["EbayCertId"].ToString(); // use your cert ID var version= "405"; var token = onstantManager["EbayToken"].ToString(); // Build the request URL var requestURL = endpoint + "?callname=" + callName + "&siteid=" + siteId + "&appid=" + appId + "&version=" + version + "&routing=default"; GetCategoriesResponseType cats = EbayService.GetCategories(request); if (cats.Errors != null && cats.Errors.Length > 0 ) { foreach(var er in cats.Errors) { //     } throw new Exception("      "); } if ( cats.CategoryArray == null || cats.CategoryArray.Length <= 0) { //   ,     ,     } CategoryType[] catArray = cats.CategoryArray; SyncronizeCats(-1, "", catArray); //     


Let's write the function of importing lines with categories from Ebay, let's not forget to delete records from us that do not come anymore. It is better to go ladder on the directory tree



 private void SyncronizeCats(long parentID, string ebayParentCode, CategoryType[] catArray) { //          var ourEbayCats = DictionaryManager.GetRecords<ArticleGroup>(x => (x.ParentID == parentId)); //    var processedCodes = new List<string>(); foreach(var ebayCat in catArray.Where(c => (string.IsNullOrEmpty(ebayParentCode) && c.CategoryLevel == 1 ) || (c.CategoryParentID != null && c.CategoryParentID.Any(h => h.Trim() != c.CategoryID && h.Trim() == ebayParentCode)))) { if (!string.IsNullOrEmpty(ebayCat.CategoryID) && !string.IsNullOrEmpty(ebayCat.CategoryName)) { GetOrCreateCategory(bayCat.CategoryID.Trim(), ebayCat.CategoryName, parentID); processedCodes.Add(ebayCat.CategoryID.Trim()); } } foreach(var catRow in ourEbayCats) { if (!(processedCodes.Contains(catRow.EbeyCode))) { // ,       DictionaryManager.DeleteRecord<ArticleGroup>(catRow.ID); } else { //   SyncronizeCats(catRow.ID, catRow.Code, catArray); } } } 


And finally, an update or the creation of a category.



 private void GetOrCreateCategory(string ebayCategoryID, string ebayCatCategoryName, long parentID) { var ebayCat = new ArticleGroup() { }; var cats = DictionaryManager.GetRecords<ArticleGroup>(x => x.Name == ebayCatCategoryName); if (cats.Any()) { ebayCat = cats.First(); } else { ebayCat = DictionaryManager.NewRecord( new ArticleGroup() { }); } // ,     DictionaryManager.SaveRecord(ebayCat); } 


Total



Technically, integration is not difficult (the entire volume of the code does not exceed several killobytes), but in detail a lot of pitfalls emerge with some of which we tried to introduce you.



')

Source: https://habr.com/ru/post/307782/



All Articles