using System; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using Sockeye.Models; using Sockeye.Util; namespace Sockeye.Biz { /// /// Process vendor notifications /// Turn vendordata into fully filled out purchase /// attempt to match to existing customer or create one if necessary /// /// A Separate job will make licenses from the new purchases where feasible /// /// internal static class SockBotProcessVendorNotifications { private static ILogger log = Sockeye.Util.ApplicationLogging.CreateLogger("SockBotProcessVendorNotifications"); private static DateTime lastSweep = DateTime.MinValue; #if (DEBUG) private static TimeSpan PROCESS_EVERY_INTERVAL = new TimeSpan(0, 0, 30);//every 30 seconds during development #else private static TimeSpan PROCESS_EVERY_INTERVAL = new TimeSpan(0, 5, 10);//every 5 minutes #endif //////////////////////////////////////////////////////////////////////////////////////////////// // DoSweep // public static async Task DoWorkAsync() { //This will get triggered roughly every minute, but we don't want to check that frequently if (DateTime.UtcNow - lastSweep < PROCESS_EVERY_INTERVAL) return; if (ServerBootConfig.MIGRATING) return;//don't do this during migration (migration is one time only so can remove after up and running) log.LogDebug("Process vendor notifications starting"); await ProcessVendorNotificationDataIntoPurchases(); lastSweep = DateTime.UtcNow; } private static async Task ProcessVendorNotificationDataIntoPurchases() { using (AyContext ct = Sockeye.Util.ServiceProviderProvider.DBContext) { //get a list of all actionable vendor notifications var vnList = await ct.VendorNotification .Where(z => z.Processed == null) .OrderBy(z => z.Id) .ToListAsync(); try { foreach (var vn in vnList) { log.LogDebug($"Processing vendor notification {vn.Id}-{vn.Created}"); if (string.IsNullOrWhiteSpace(vn.VendorData)) { var err = $"VendorNotification record {vn.Id}-{vn.Created} has no vendor data"; await NotifyEventHelper.AddOpsProblemEvent("SockBotProcessVendorNotifications: " + err); log.LogError(err); continue; } //Parse json vendordata if (await ParseVendorNotificationData(vn, ct, log)) { //success, save vendornotification as processed vn.Processed = DateTime.UtcNow; await ct.SaveChangesAsync(); } } } catch (Exception ex) { var err = "SockBotProcessVendorNotifications error running job"; //serious issue requires immediate notification await NotifyEventHelper.AddOpsProblemEvent(err, ex); log.LogError(ex, err); } } } internal static async Task ParseVendorNotificationData(VendorNotification vn, AyContext ct, ILogger log) { try { var jData = JObject.Parse(vn.VendorData); //It's a test purchase, no need to process it any further...or is there?? bool IsTestOrder = false; if (jData["order_notification"]["purchase"]["is_test"] != null && jData["order_notification"]["purchase"]["is_test"].Value() == true) IsTestOrder = true; //fundamentally validate the object is a purchase notification if (jData["order_notification"]["purchase"]["purchase_id"] == null) { //this is not the expected format data, stop processing and alert: throw new System.FormatException($"Vendor data unexpected format:{vn.VendorData}"); } //parse purchases collection needed up front as it potentially contains Customer record relevant data var jaPurchaseList = (JArray)jData["order_notification"]["purchase"]["purchase_item"]; string RavenDBId = string.Empty; foreach (JObject jPurchase in jaPurchaseList) { /////DATABASE ID if available used to matchup //Capture raven database id if present if (jPurchase["additional_information"] != null) { var jaAdditionalItems = (JArray)jPurchase["additional_information"]; foreach (JObject jAdditionalItem in jaAdditionalItems) { if (jAdditionalItem["additional_id"] != null && jAdditionalItem["additional_id"].Value() == "DATABASEID") { RavenDBId = jAdditionalItem["additional_value"].Value(); break; } } } } /////////////////////////////////////////////////////////////////////////////////////////// #region CUSTOMER MAKE OR LOCATED /////////////////////////////////////////////////////////////////////////////////////////// //Note: always use reg name not customer name as it may vary between the two and regname is the one we care about var jCustomerName = jData["order_notification"]["purchase"]["customer_data"]["reg_name"].Value() ?? throw new System.FormatException($"Vendor data empty reg_name:{vn.VendorData}"); if (jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["email"] == null)//we can't process orders with no email at all hard no throw new System.FormatException($"Vendor data empty email:{vn.VendorData}"); var jCustomerEmail = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["email"].Value(); var jCustomerAccountNumber = jData["order_notification"]["purchase"]["customer_data"]["shopper_id"].Value();//appears to be mycommerce customer id number hopefully static between orders var customerBiz = CustomerBiz.GetBiz(ct); //attempt to match to existing customer //databaseID for raven is best match,account number is next most ideal match, , then name but could be multiple in sockeye from rockfish sites so name will start the same, finally email if nothing else Customer customer = null; //First best match is RavenDBID if (!string.IsNullOrWhiteSpace(RavenDBId)) customer = await ct.Customer.AsNoTracking().FirstOrDefaultAsync(z => z.DbId == RavenDBId); //if not found then try match in order of best matching if (customer == null) customer = await ct.Customer.AsNoTracking().FirstOrDefaultAsync(z => z.AccountNumber == jCustomerAccountNumber) ?? await ct.Customer.AsNoTracking().FirstOrDefaultAsync(z => z.Name.StartsWith(jCustomerName)) ?? await ct.Customer.AsNoTracking().FirstOrDefaultAsync(z => z.EmailAddress == jCustomerEmail); //still no match, consider it a new customer if (customer == null) { //New customer customer = new Customer(); customer.Name = jCustomerName; customer.EmailAddress = jCustomerEmail; customer.AccountNumber = jCustomerAccountNumber; UpdateCustomerFromVendorData(jData, customer); customer = await customerBiz.CreateAsync(customer); if (customer == null) throw new System.ApplicationException($"Error creating new Customer: {customerBiz.GetErrorsAsString()} vendor data :{vn.VendorData}"); } else { //existing customer //here there could be several potential issues: //name differs because it was a separate site in rockfish, it's not cool to change the name //email differs, this can happen and is ok //account number differs if was empty then it's ok. If it wasn't empty and it differs this is unfortunately normal as users may re-buy again with new account or buy an addon with a new account //so the vendor account nubmer should just be the most recent for finding them purposes I guess // if (customer.EmailAddress != jCustomerEmail) customer.EmailAddress = jCustomerEmail;//assume it was empty or has been recently updated if (customer.AccountNumber != jCustomerAccountNumber) customer.AccountNumber = jCustomerAccountNumber;//see above //refresh UpdateCustomerFromVendorData(jData, customer); customer = await customerBiz.PutAsync(customer); if (customer == null) throw new System.ApplicationException($"Error updating existing Customer: {customerBiz.GetErrorsAsString()} vendor data :{vn.VendorData}"); } /////////////////////////////////////////////////////////////////////////////////////////// #endregion customer make or locate /////////////////////////////////////////////////////////////////////////////////////////// #region MAKE PURCHASE RECORD /////////////////////////////////////////////////////////////////////////////////////////// var salesOrderNumber = jData["order_notification"]["purchase"]["purchase_id"].Value(); if (IsTestOrder) salesOrderNumber = "test-order-" + salesOrderNumber; #if (DEBUG) salesOrderNumber += "-debug-test"; #endif //https://www.newtonsoft.com/json/help/html/DatesInJSON.htm var purchaseDate = jData["order_notification"]["purchase"]["purchase_date"].Value(); if (await ct.Purchase.AnyAsync(z => z.SalesOrderNumber == salesOrderNumber)) throw new System.ApplicationException($"Sales order already exists: {salesOrderNumber} will not be processed"); //iterate purchase items array foreach (JObject jPurchase in jaPurchaseList) { Purchase p = new Purchase(); p.PurchaseDate = purchaseDate; p.CustomerId = customer.Id; p.RegTo = jCustomerName; p.VendorId = vn.VendorId; p.SalesOrderNumber = salesOrderNumber; var SalesItemProductVendorCode = jPurchase["product_id"].Value(); var product = await ct.Product.AsNoTracking().FirstOrDefaultAsync(z => z.VendorCode == SalesItemProductVendorCode) ?? throw new System.ArgumentOutOfRangeException($"Vendor product code:{SalesItemProductVendorCode} was not found in Sockeye Products, record not processed"); p.ProductId = product.Id; p.PGroup = product.PGroup; if (jPurchase["accounting"] != null) { p.Currency = jPurchase["accounting"]["currency"].Value(); p.ProductNet = jPurchase["accounting"]["product_net"].Value(); p.Discount = jPurchase["accounting"]["discount"].Value(); p.VendorFee = jPurchase["accounting"]["margin_net"].Value(); p.Revenue = jPurchase["accounting"]["your_revenue"].Value(); } if (!string.IsNullOrWhiteSpace(RavenDBId)) p.DbId = RavenDBId; if (jPurchase["promotion_coupon"] != null) p.CouponCode = jPurchase["promotion_coupon"].Value(); if (jPurchase["promotion"] != null) p.CouponCode += $" {jPurchase["promotion"].Value()}"; p.Quantity = jPurchase["quantity"].Value(); p.VendorNotificationId = vn.Id; p.IsRenewal = false;//default, set to true in subscription block processor below //it's a subscription? if (jPurchase["subscription"] != null) { if (jPurchase["subscription"]["expiration_date"] != null) p.ExpireDate = jPurchase["subscription"]["expiration_date"].Value(); if (jPurchase["subscription"]["original_purchase_id"] != null) p.IsRenewal = true;//the existance of the field is enough, we're going to assume the original purchase id is most definitive though there are other original_xx fields: /* Renewal example: if it's a renewal it will have several "original_XXXX" fields (they won't exist on first non-renewal purchase): "subscription": { "expiration_date": "2023-03-07T00:15:13.0000000Z", "id": "833674953-1", "interval": "Monthly without end", "original_notification_no": "7925", "original_purchase_id": "833674953", "original_running_no": "1", */ } PurchaseBiz pbiz = PurchaseBiz.GetBiz(ct); p = await pbiz.CreateAsync(p); if (p == null) { //did not save, throw an error throw new System.ApplicationException($"Error creating purchase: {pbiz.GetErrorsAsString()} for product item: {SalesItemProductVendorCode} vendor data :{vn.VendorData}"); } } /////////////////////////////////////////////////////////////////////////////////////////// #endregion make purchase record } catch (Exception ex) { var err = $"ParseVendorNotificationData: VendorNotification record {vn.Id}-{vn.Created} triggered exception, see log"; await NotifyEventHelper.AddOpsProblemEvent(err);//notify, this is serious log.LogError(ex, err); return false; } return true; //successfully processed so vendor notification should be set to processed by caller } private static void UpdateCustomerFromVendorData(JObject jData, Customer c) { c.Active = true;//if they just made a purchase they are active even if they weren't before c.DoNotContact = false;//if they just made a purchase they are contactable even if they weren't before c.Address = c.PostAddress = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["address"]["street1"].Value(); c.City = c.PostCity = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["address"]["city"].Value(); //State doesn't always exist in mycommerce notifications if (jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["address"]["state"] != null) c.Region = c.PostRegion = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["address"]["state"].Value(); c.Country = c.PostCountry = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["address"]["country"].Value(); c.PostCode = c.AddressPostal = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["address"]["postal_code"].Value(); var firstName = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["first_name"].Value() ?? "FirstNameEmpty"; var lastName = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["last_name"].Value() ?? "LastNameEmpty"; var language = jData["order_notification"]["purchase"]["customer_data"]["language"].Value() ?? "LanguageEmpty"; if (string.IsNullOrWhiteSpace(c.Notes)) c.Notes = string.Empty; if (!c.Notes.Contains(lastName)) { if (c.Notes.Length > 0 && !c.Notes.EndsWith('\n')) c.Notes += "\n"; c.Notes += $"Purchase contact:{firstName} {lastName}, language: {language}\n"; } } #region SAMPLE VENDOR PURCHASE NOTIFICATIONS /* ///////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////// V7 MULTIPLE PRODUCTS IN ONE ORDER///////////////////////////// { "creation_date": "2023-01-22T23:21:49Z", "id": 357678128, "order_notification": { "purchase": { "customer_data": { "billing_contact": { "address": { "city": "Courtenay", "country": "Canada", "country_id": "CA", "postal_code": "V9J9T6", "state": "British Columbia", "state_id": "BC", "street1": "05-3610 Christie Parkway" }, "company": "GZTestCo", "email": "gzmailadmin@gmail.com", "first_name": "Test", "last_name": "Testerson" }, "customer_payment_data": { "currency": "USD", "payment_method": "Other" }, "delivery_contact": { "address": { "city": "Courtenay", "country": "Canada", "country_id": "CA", "postal_code": "V9J9T6", "state": "British Columbia", "state_id": "BC", "street1": "05-3610 Christie Parkway" }, "company": "GZTestCo", "email": "gzmailadmin@gmail.com", "first_name": "Test", "last_name": "Testerson" }, "language": "English", "language_iso": "en", "reg_name": "GZTestCo", "shopper_id": "65860321", "subscribe_newsletter": false, "user_id": "gzmailadmin@gmail.com-38" }, "is_test": true, "payment_complete_date": "2023-01-22T23:21:48Z", "payment_status": "testpaymentarrived", "payment_status_id": "TCA", "purchase_date": "2023-01-22T23:21:47Z", "purchase_id": 843671213, "purchase_item": [ { "additional_information": [ { "additional_id": "ADDITIONAL1", "additional_value": "YES" }, { "additional_id": "ADDITIONAL2", "additional_value": "YES" }, { "additional_id": "ADDITIONAL3", "additional_value": "YES" } ], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 0, "product_id": 300740317, "product_name": "Up to 5 AyaNova schedulable resource 1 year subscription license", "product_single_price": 695.0, "purchase_item_key": [], "quantity": 1, "running_no": 1, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "subscription": { "expiration_date": "2024-01-22T23:21:48Z", "id": "843671213-1", "interval": "Yearly without end", "renewal_discount_count": "", "renewal_discount_start": "", "renewal_type": "auto", "retention_discount_count": "", "retention_discount_percent": "", "start_date": "2023-01-23T00:00:00", "status": "ToProcess", "status_id": "TOP" }, "vat_pct": 12.0 }, { "additional_information": [ { "additional_id": "ADDITIONAL1", "additional_value": "YES" }, { "additional_id": "ADDITIONAL2", "additional_value": "YES" }, { "additional_id": "ADDITIONAL3", "additional_value": "YES" } ], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 0, "product_id": 300740314, "product_name": "optional add-on AyaNova RI (responsive interface) 1 year subscription license", "product_single_price": 199.0, "purchase_item_key": [], "quantity": 1, "running_no": 2, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "subscription": { "expiration_date": "2024-01-22T23:21:48Z", "id": "843671213-2", "interval": "Yearly without end", "renewal_discount_count": "", "renewal_discount_start": "", "renewal_type": "auto", "retention_discount_count": "", "retention_discount_percent": "", "start_date": "2023-01-23T00:00:00", "status": "ToProcess", "status_id": "TOP" }, "vat_pct": 12.0 }, { "additional_information": [ { "additional_id": "ADDITIONAL1", "additional_value": "YES" } ], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 0, "product_id": 300740321, "product_name": "optional add-on AyaNova WBI (web browser interface) 1 year subscription license", "product_single_price": 99.0, "purchase_item_key": [], "quantity": 1, "running_no": 3, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "subscription": { "expiration_date": "2024-01-22T23:21:48Z", "id": "843671213-3", "interval": "Yearly without end", "renewal_discount_count": "", "renewal_discount_start": "", "renewal_type": "auto", "retention_discount_count": "", "retention_discount_percent": "", "start_date": "2023-01-23T00:00:00", "status": "ToProcess", "status_id": "TOP" }, "vat_pct": 12.0 }, { "additional_information": [ { "additional_id": "ADDITIONAL1", "additional_value": "YES" } ], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 0, "product_id": 300740322, "product_name": "optional add-on AyaNova MBI (minimal browser interface) 1 year subscription license", "product_single_price": 99.0, "purchase_item_key": [], "quantity": 1, "running_no": 4, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "subscription": { "expiration_date": "2024-01-22T23:21:48Z", "id": "843671213-4", "interval": "Yearly without end", "renewal_discount_count": "", "renewal_discount_start": "", "renewal_type": "auto", "retention_discount_count": "", "retention_discount_percent": "", "start_date": "2023-01-23T00:00:00", "status": "ToProcess", "status_id": "TOP" }, "vat_pct": 12.0 }, { "additional_information": [ { "additional_id": "ADDITIONAL1", "additional_value": "YES" } ], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 0, "product_id": 300740323, "product_name": "optional add-on AyaNova QBI(QuickBooks interface) 1 year subscription license", "product_single_price": 99.0, "purchase_item_key": [], "quantity": 1, "running_no": 5, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "subscription": { "expiration_date": "2024-01-22T23:21:48Z", "id": "843671213-5", "interval": "Yearly without end", "renewal_discount_count": "", "renewal_discount_start": "", "renewal_type": "auto", "retention_discount_count": "", "retention_discount_percent": "", "start_date": "2023-01-23T00:00:00", "status": "ToProcess", "status_id": "TOP" }, "vat_pct": 12.0 } ], "purchase_origin": "online" } } } ///////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////v7 export to xls add on///////////////////////////// { "creation_date": "2023-01-20T00:18:33Z", "id": 357520816, "order_notification": { "purchase": { "customer_data": { "billing_contact": { "address": { "city": "Cambridge", "country": "Canada", "country_id": "CA", "postal_code": "N1T 1J3", "state": "Ontario", "state_id": "ON", "street1": "181 Shearson Crescent" }, "company": "Cambridge Elevating Inc", "email": "richard.wright@cambridgeelevating.com", "first_name": "Richard", "last_name": "Wright" }, "customer_payment_data": { "currency": "USD", "payment_method": "MasterCard" }, "delivery_contact": { "address": { "city": "Cambridge", "country": "Canada", "country_id": "CA", "postal_code": "N1T 1J3", "state": "Ontario", "state_id": "ON", "street1": "181 Shearson Crescent" }, "company": "Cambridge Elevating Inc", "email": "richard.wright@cambridgeelevating.com", "first_name": "Richard", "last_name": "Wright" }, "language": "English", "language_iso": "en", "reg_name": "Cambridge Elevating Inc", "shopper_id": "62545677", "subscribe_newsletter": false, "user_id": "richard.wright@cambridgeelevating.com-5" }, "is_test": false, "payment_complete_date": "2023-01-20T00:18:33Z", "payment_status": "complete", "payment_status_id": "PCA", "purchase_date": "2023-01-20T00:18:31Z", "purchase_id": 843101393, "purchase_item": [ { "accounting": { "currency": "USD", "product_net": 6.65, "discount": 0.0, "product_vat": 0.86, "shipping": 0.0, "shipping_vat": 0.0, "eu_vat": -0.86, "margin_net": -2.5, "your_revenue": 4.15 }, "additional_information": [ { "additional_id": "ADDITIONAL1", "additional_value": "YES" } ], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 7953, "product_id": 300740327, "product_name": "optional add-on plug-in Export to XLS 1 year subscription license", "product_single_price": 6.65, "purchase_item_key": [], "quantity": 1, "running_no": 1, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "subscription": { "expiration_date": "2024-01-20T18:36:22Z", "id": "771160083-3", "interval": "Yearly without end", "original_notification_no": "7779", "original_purchase_id": "771160083", "original_running_no": "3", "renewal_discount_count": "", "renewal_discount_start": "", "renewal_type": "auto", "retention_discount_count": "", "retention_discount_percent": "", "start_date": "2022-01-20T00:00:00", "status": "ToProcess", "status_id": "TOP" }, "vat_pct": 13.0 } ], "purchase_origin": "Subscription", "sequential_invoice_no": "e5-DE-2023-00000163106" } } } ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////// V7 Scheduleable user ////////////////////// { "creation_date": "2023-01-20T00:18:32Z", "id": 357520809, "order_notification": { "purchase": { "customer_data": { "billing_contact": { "address": { "city": "Cambridge", "country": "Canada", "country_id": "CA", "postal_code": "N1T 1J3", "state": "Ontario", "state_id": "ON", "street1": "181 Shearson Crescent" }, "company": "Cambridge Elevating Inc", "email": "richard.wright@cambridgeelevating.com", "first_name": "Richard", "last_name": "Wright" }, "customer_payment_data": { "currency": "USD", "payment_method": "MasterCard" }, "delivery_contact": { "address": { "city": "Cambridge", "country": "Canada", "country_id": "CA", "postal_code": "N1T 1J3", "state": "Ontario", "state_id": "ON", "street1": "181 Shearson Crescent" }, "company": "Cambridge Elevating Inc", "email": "richard.wright@cambridgeelevating.com", "first_name": "Richard", "last_name": "Wright" }, "language": "English", "language_iso": "en", "reg_name": "Cambridge Elevating Inc", "shopper_id": "62545677", "subscribe_newsletter": false, "user_id": "richard.wright@cambridgeelevating.com-5" }, "is_test": false, "payment_complete_date": "2023-01-20T00:18:32Z", "payment_status": "complete", "payment_status_id": "PCA", "purchase_date": "2023-01-20T00:18:31Z", "purchase_id": 843101383, "purchase_item": [ { "accounting": { "currency": "USD", "product_net": 577.5, "discount": 0.0, "product_vat": 75.08, "shipping": 0.0, "shipping_vat": 0.0, "eu_vat": -75.08, "margin_net": -29.05, "your_revenue": 548.45 }, "additional_information": [ { "additional_id": "ADDITIONAL1", "additional_value": "YES" }, { "additional_id": "ADDITIONAL2", "additional_value": "YES" }, { "additional_id": "ADDITIONAL3", "additional_value": "YES" } ], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 7952, "product_id": 300807973, "product_name": "Up to 15 AyaNova schedulable resource 1 year subscription license", "product_single_price": 577.5, "purchase_item_key": [], "quantity": 1, "running_no": 1, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "subscription": { "expiration_date": "2024-01-20T18:36:22Z", "id": "771160083-1", "interval": "Yearly without end", "original_notification_no": "7777", "original_purchase_id": "771160083", "original_running_no": "1", "renewal_discount_count": "", "renewal_discount_start": "", "renewal_type": "auto", "retention_discount_count": "", "retention_discount_percent": "", "start_date": "2022-01-20T00:00:00", "status": "ToProcess", "status_id": "TOP" }, "vat_pct": 13.0 } ], "purchase_origin": "Subscription", "sequential_invoice_no": "e5-DE-2023-00000163105" } } } ///////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// NOT A LICENSE PRODUCT - CUSTOMIZATION SERVICES FOR REPORT TEMPLATE { "creation_date": "2023-01-18T15:45:50Z", "id": 357446088, "order_notification": { "purchase": { "customer_data": { "billing_contact": { "address": { "city": "Brunswick", "country": "USA", "country_id": "US", "postal_code": "38014", "state": "Tennessee", "state_id": "TN", "street1": "PO Box 374" }, "company": "Tri-Star Medical Technologies LLC", "email": "stan@tri-starmedical.com", "first_name": "Stan", "last_name": "Hilton" }, "customer_payment_data": { "currency": "USD", "payment_method": "Visa" }, "delivery_contact": { "address": { "city": "Brunswick", "country": "USA", "country_id": "US", "postal_code": "38014", "state": "Tennessee", "state_id": "TN", "street1": "PO Box 374" }, "company": "Tri-Star Medical Technologies LLC", "email": "stan@tri-starmedical.com", "first_name": "Stan", "last_name": "Hilton" }, "language": "English", "language_iso": "en", "reg_name": "Tri-Star Medical Technologies LLC", "shopper_id": "65821033", "subscribe_newsletter": false, "user_id": "stan@tri-starmedical.com-1" }, "is_test": false, "payment_complete_date": "2023-01-18T15:45:49Z", "payment_status": "complete", "payment_status_id": "PCA", "purchase_date": "2023-01-18T15:45:48Z", "purchase_id": 842819563, "purchase_item": [ { "accounting": { "currency": "USD", "product_net": 50.0, "discount": 0.0, "product_vat": 4.63, "shipping": 0.0, "shipping_vat": 0.0, "eu_vat": -4.63, "margin_net": -5.14, "your_revenue": 44.86 }, "additional_information": [], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 7945, "product_id": 300525428, "product_name": "AyaNova customization services", "product_single_price": 50.0, "purchase_item_key": [], "quantity": 1, "running_no": 1, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "vat_pct": 9.25 } ], "purchase_origin": "online", "sequential_invoice_no": "e5-US-2023-00000083100" } } } ///////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////// TEST PURCHASE USING SPECIAL TEST CREDIT CARD NUMBER PROVIDED BY MYCOMMERCE { "creation_date": "2023-01-18T04:24:28Z", "id": 357424814, "order_notification": { "purchase": { "customer_data": { "billing_contact": { "address": { "city": "Courtenay", "country": "Canada", "country_id": "CA", "postal_code": "V9J9T6", "state": "British Columbia", "state_id": "BC", "street1": "05-3610 Christie Parkway" }, "company": "GZTestCo", "email": "gzmailadmin@gmail.com", "first_name": "Test", "last_name": "Testerson" }, "customer_payment_data": { "currency": "USD", "payment_method": "Other" }, "delivery_contact": { "address": { "city": "Courtenay", "country": "Canada", "country_id": "CA", "postal_code": "V9J9T6", "state": "British Columbia", "state_id": "BC", "street1": "05-3610 Christie Parkway" }, "company": "GZTestCo", "email": "gzmailadmin@gmail.com", "first_name": "Test", "last_name": "Testerson" }, "language": "English", "language_iso": "en", "reg_name": "GZTestCo", "shopper_id": "65817245", "subscribe_newsletter": false, "user_id": "gzmailadmin@gmail.com-36" }, "is_test": true, "payment_complete_date": "2023-01-18T04:24:28Z", "payment_status": "testpaymentarrived", "payment_status_id": "TCA", "purchase_date": "2023-01-18T04:24:28Z", "purchase_id": 842769483, "purchase_item": [ { "additional_information": [], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 0, "product_id": 300525428, "product_name": "AyaNova customization services", "product_single_price": 50.0, "purchase_item_key": [], "quantity": 1, "running_no": 1, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "vat_pct": 12.0 } ], "purchase_origin": "online" } } } ///////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////// RAVEN PERPETUAL { "creation_date": "2023-01-14T06:17:59Z", "id": 357214426, "order_notification": { "purchase": { "customer_data": { "billing_contact": { "address": { "city": "Frankfort", "country": "USA", "country_id": "US", "postal_code": "46041", "state": "Indiana", "state_id": "IN", "street1": "47 N Jackson St." }, "company": "ACCS", "email": "internet@accs.net", "first_name": "Marcus", "last_name": "Hodges" }, "customer_payment_data": { "currency": "USD", "payment_method": "Visa" }, "delivery_contact": { "address": { "city": "Frankfort", "country": "USA", "country_id": "US", "postal_code": "46041", "state": "Indiana", "state_id": "IN", "street1": "47 N Jackson St." }, "company": "ACCS", "email": "internet@accs.net", "first_name": "Marcus", "last_name": "Hodges" }, "language": "English", "language_iso": "en", "reg_name": "ACCS", "shopper_id": "65783365", "subscribe_newsletter": false, "user_id": "internet@accs.net" }, "is_test": false, "payment_complete_date": "2023-01-14T06:17:59Z", "payment_status": "complete", "payment_status_id": "PCA", "purchase_date": "2023-01-14T06:17:58Z", "purchase_id": 841935113, "purchase_item": [ { "accounting": { "currency": "USD", "product_net": 72.0, "discount": 0.0, "product_vat": 5.04, "shipping": 0.0, "shipping_vat": 0.0, "eu_vat": -5.04, "margin_net": -6.03, "your_revenue": 65.97 }, "additional_information": [ { "additional_id": "AGREENOREFUNDS", "additional_value": "YES" }, { "additional_id": "AGREEPAYMETHODVALIDCANCEL", "additional_value": "YES" }, { "additional_id": "AGREEEXPIRESIFNOTPAID", "additional_value": "YES" }, { "additional_id": "DATABASEID", "additional_value": "7ktPA+Eaq+LM4vNMkQdbwBkUMDzzFYXmH+m21n3w5Rk=" } ], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 7944, "product_id": 301028317, "product_name": "AyaNova perpetual single user license includes one year maintenance plan ", "product_single_price": 24.0, "purchase_item_key": [], "quantity": 3, "running_no": 1, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "subscription": { "expiration_date": "2024-01-14T06:17:59Z", "id": "841935113-1", "interval": "Yearly without end", "renewal_discount_count": "", "renewal_discount_start": "", "renewal_type": "auto", "retention_discount_count": "", "retention_discount_percent": "", "start_date": "2023-01-14T00:00:00", "status": "ToProcess", "status_id": "TOP" }, "vat_pct": 7.0, "your_product_id": "perpetual" } ], "purchase_origin": "online", "sequential_invoice_no": "e5-US-2023-00000064245" } } } ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// RAVEN MONTHLY SUBSCRIPTION { "creation_date": "2023-01-07T01:54:11Z", "id": 356798113, "order_notification": { "purchase": { "customer_data": { "billing_contact": { "address": { "city": "UNIONVILLE", "country": "Canada", "country_id": "CA", "postal_code": "L3R 9W6", "state": "Ontario", "state_id": "ON", "street1": "4261 HWY 7 E, Unit A14 #399" }, "company": "sportseffect", "email": "service@sportseffect.com", "first_name": "Mark", "last_name": "Hopkins" }, "customer_payment_data": { "currency": "CAD", "payment_method": "Visa" }, "delivery_contact": { "address": { "city": "UNIONVILLE", "country": "Canada", "country_id": "CA", "postal_code": "L3R 9W6", "state": "Ontario", "state_id": "ON", "street1": "4261 HWY 7 E, Unit A14 #399" }, "company": "sportseffect", "email": "service@sportseffect.com", "first_name": "Mark", "last_name": "Hopkins" }, "language": "English", "language_iso": "en", "reg_name": "sportseffect", "shopper_id": "65387076", "subscribe_newsletter": false, "user_id": "service@sportseffect.com" }, "is_test": false, "payment_complete_date": "2023-01-07T01:54:11Z", "payment_status": "complete", "payment_status_id": "PCA", "purchase_date": "2023-01-07T01:54:09Z", "purchase_id": 840549123, "purchase_item": [ { "accounting": { "currency": "USD", "product_net": 23.8, "discount": 0.0, "product_vat": 3.09, "shipping": 0.0, "shipping_vat": 0.0, "eu_vat": -3.09, "margin_net": -4.03, "your_revenue": 19.77 }, "additional_information": [ { "additional_id": "AGREENOREFUNDS", "additional_value": "YES" }, { "additional_id": "AGREEPAYMETHODVALIDCANCEL", "additional_value": "YES" }, { "additional_id": "AGREEEXPIRESIFNOTPAID", "additional_value": "YES" }, { "additional_id": "DATABASEID", "additional_value": "New subscription" } ], "currency": "USD", "delivery_type": "Electronically", "discount": 0.0, "extended_download_price": 0.0, "manual_order_price": 0.0, "notification_no": 7937, "product_id": 301028467, "product_name": "AyaNova subscription one user monthly", "product_single_price": 11.9, "purchase_item_key": [], "quantity": 2, "running_no": 1, "shipping_price": 0.0, "shipping_vat_pct": 0.0, "subscription": { "expiration_date": "2023-02-07T00:15:13Z", "id": "833674953-1", "interval": "Monthly without end", "original_notification_no": "7925", "original_purchase_id": "833674953", "original_running_no": "1", "renewal_discount_count": "", "renewal_discount_start": "", "renewal_type": "auto", "retention_discount_count": "", "retention_discount_percent": "", "start_date": "2022-12-07T00:00:00", "status": "ToProcess", "status_id": "TOP" }, "vat_pct": 13.0, "your_product_id": "subscriptionmonthly" } ], "purchase_origin": "Subscription", "sequential_invoice_no": "e5-DE-2023-00000055949" } } } */ #endregion sample vendor notifications //order notification json schema on this page down //actual schema: https://api.shareit.com/xml/2.4/ordernotification.xsd //https://account.mycommerce.com/home/wiki/7479997 //json format //https://account.mycommerce.com/home/wiki/7479805 //overall info ///////////////////////////////////////////////////////////////////// }//eoc }//eons