Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Claudiu Bria 34 posts 146 karma points
    Feb 21, 2017 @ 16:32
    Claudiu Bria
    0

    buy product api call

    Hi ,

    I'm trying to have apis that manipulate data inside Merchello from outside Umbraco as proof of concept.

    Currently I'm trying to have an api that buys a product. This api would be called from outside Umbraco, and it should simply create an invoice.

    Here's what i have so far:

        [HttpGet]
        public HttpResponseMessage Buy(string productKey, string customerKey)
        {
            var buyResponse = new BuyResponse();
    
            Mandate.ParameterNotNullOrEmpty(productKey, "productKey");
            Mandate.ParameterNotNullOrEmpty(customerKey, "customerKey");
    
            var merchelloServices = MerchelloContext.Current.Services;
            var customerService = merchelloServices.CustomerService;
            var customer = customerService.GetByKey(new Guid(customerKey));
            if (customer == null)
            {
                throw new Exception("Customer does not exists",null);
            }
    
            buyResponse.Customer = customer;
            buyResponse.CustomerFound = true;
    
            var umbracoServices = ApplicationContext.Current.Services;
            var memberService = umbracoServices.MemberService;
    
            var member = memberService.GetByEmail(customer.Email);
            if (member == null)
            {
                throw new Exception("Member does not exists", null);
            }
    
            buyResponse.Member = member;
            buyResponse.MemberFound = true;
    
            var productService = merchelloServices.ProductService;
            var product = productService.GetByKey(new Guid(productKey));
            if (product == null)
            {
                throw new Exception("Product does not exists", null);
            }
    
            buyResponse.Product = product;
            buyResponse.ProductFound = true;
    
            var defaultCatalogKey = MerchelloConstants.Warehouse.DefaultWarehouseCatalogKey;
    
            var extendedData = new ExtendedDataCollection();
            // this is used to determine where a shipment originates
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.WarehouseCatalogKey, defaultCatalogKey.ToString());
            // items set to shippable
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.TrackInventory, "false");
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.Shippable, "false");
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.CurrencyCode, "USD");
    
            var customerBasket = customer.Basket();
            customerBasket.Empty();
            customerBasket.AddItem(product, product.Name, 1, extendedData);
            customerBasket.Save();
    
            buyResponse.Basket = customerBasket;
            buyResponse.BasketCreated = true;
    
            var invoiceService = merchelloServices.InvoiceService;
    
            var invoice = invoiceService.CreateInvoice(MerchelloConstants.InvoiceStatus.Unpaid);
            // I'd say we need to add a parameter to the service so we don't have to do this
            // http://issues.merchello.com/youtrack/issue/M-434
            ((Invoice)invoice).CustomerKey = customer.Key;
    
            // The version key is useful in some cases to invalidate shipping quotes or taxation calculations
            invoice.VersionKey = Guid.NewGuid();
    
            invoice.Items.Add(customerBasket.Items);
    
            invoiceService.Save(invoice);
    
            buyResponse.Invoice = invoice;
            buyResponse.InvoiceCreated = true;
    
    
            var response = new HttpResponseMessage
            {
                Content = new ObjectContent<BuyResponse>(buyResponse, Configuration.Formatters.JsonFormatter),
                StatusCode = HttpStatusCode.OK
            };
            return response;
        }
    
        private class BuyResponse
        {
            public ICustomer Customer = null;
            public bool CustomerFound = false;
            public IMember Member = null;
            public bool MemberFound = false;
            public IProduct Product = null;
            public bool ProductFound = false;
            public IBasket Basket = null;
            public bool BasketCreated = false;
            public IInvoice Invoice = null;
            public bool InvoiceCreated = false;
        }
    
    }
    

    My problem is that I get the following exception:

    While debugging I found out that the exception is due to line 80 in LineItemRepositoryBase.cs:

        public virtual void SaveLineItem(LineItemCollection items, Guid containerKey)
        {          
            var existing = GetByContainerKey(containerKey);
    
            // assert there are no existing items not in the new set of items.  If there are ... delete them
            var toDelete = existing.Where(x => items.All(item => item.Key != x.Key)).ToArray();
            if (toDelete.Any())
            {
                foreach (var d in toDelete)
                {
                    Delete(d);
                }
            }
    
            foreach (var item in items)
            {
                // In the mapping between different line item types the container key is 
                // invalidated so we need to set it to the current container.
                if (!item.ContainerKey.Equals(containerKey)) item.ContainerKey = containerKey;
    
                SaveLineItem(item as T);
            }
        }
    

    Line 80 is: SaveLineItem(item as T). Here the item becomes null. Therefore, the SaveLineItem implementation results in "Object reference not set to an instance of an object." exception:

        public virtual void SaveLineItem(T item)
        {
            if (!item.HasIdentity)
            {
                PersistNewItem(item);
            }
            else
            {
                PersistUpdatedItem(item);
            }            
        }
    

    What am I doing wrong here, please ?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Feb 23, 2017 @ 17:55
    Rusty Swayne
    0

    Without being able to test - can't be sure of all the issues, but the following line is a problem.

      invoice.Items.Add(customerBasket.Items);
    

    In this case you are adding IItemCacheLineItem to IInvoiceLineItem so you need to convert them.

    In fact, you probably don't event need to use the basket in this case .... just build the invoice.

  • Claudiu Bria 34 posts 146 karma points
    Feb 27, 2017 @ 17:21
    Claudiu Bria
    0

    Here's my progress so far, I get the invoice, also the payment, but I get error on saving the shipment:

        [HttpGet]
        public HttpResponseMessage Buy(Guid productKey, Guid customerKey)
        {
            var logData = new ExtendedLoggerData();
            logData.AddCategory("Merchello");
            logData.AddCategory("AjourCMSApi");
    
            var buyResponse = new BuyResponse();
    
            Mandate.ParameterNotNullOrEmpty(productKey.ToString(), "productKey");
            Mandate.ParameterNotNullOrEmpty(customerKey.ToString(), "customerKey");
    
            var merchelloServices = MerchelloContext.Current.Services;
    
    
            var customerResponse = GetCustomer(customerKey, logData);
            buyResponse.CustomerResponse = customerResponse;
            var customer = customerResponse.Customer;
    
            if (customer.Addresses.All(x => x.AddressType != AddressType.Billing))
            {
                var invalidOp = new InvalidOperationException("Billing address not found");
                MultiLogHelper.Error<BuyResponse>("Could not find billing address", invalidOp, logData);
                throw invalidOp;
            }
    
            var productService = merchelloServices.ProductService;
            var product = productService.GetByKey(productKey);
            if (product == null)
            {
                throw new Exception("Product does not exists", null);
            }
    
            buyResponse.Product = product;
            buyResponse.ProductFound = true;
    
            var defaultCatalogKey = MerchelloConstants.Warehouse.DefaultWarehouseCatalogKey;
    
            var extendedData = new ExtendedDataCollection();
            // this is used to determine where a shipment originates
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.WarehouseCatalogKey, defaultCatalogKey.ToString());
            // items set to shippable
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.TrackInventory, "false");
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.Shippable, "true");
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.CurrencyCode, "USD");
    
            var customerBasket = customer.Basket();
            customerBasket.Empty();
            customerBasket.AddItem(product, product.Name, 1, extendedData);
            customerBasket.Save();
    
            buyResponse.Basket = customerBasket;
            buyResponse.BasketCreated = true;
    
            var checkoutMgr = customerBasket.GetCheckoutManager();
            var checkoutCustomer = (ICustomer)checkoutMgr.Customer.Context.Customer;
    
            var customerExtendedDataRefreshed = RefreshCustomerExtendedData(ref checkoutCustomer);
    
            buyResponse.CheckoutCustomer = checkoutCustomer;
            buyResponse.CheckoutCustomerFound = true;
    
            var checkoutCustomerExtendedDataShippingAddress = checkoutCustomer.ExtendedData.GetAddress(AddressType.Shipping);
            var customerCountryCode = checkoutCustomerExtendedDataShippingAddress.CountryCode;
    
            if (!checkoutMgr.Payment.IsReadyToInvoice())
            {
                var invalidOp = new InvalidOperationException("CheckoutManager is not ready to invoice");
                MultiLogHelper.Error<BuyResponse>("Not ready to invoice", invalidOp, logData);
                throw invalidOp;
            }
    
            var paymentGatewayMethod = checkoutMgr.Payment.GetPaymentGatewayMethods().FirstOrDefault();
    
            if (paymentGatewayMethod == null)
            {
                var invalidOp = new InvalidOperationException("Payment Gateway method not found");
                MultiLogHelper.Error<BuyResponse>("Could not find payment gateway method", invalidOp, logData);
                throw invalidOp;
            }
    
            if (paymentGatewayMethod.PaymentMethod == null)
            {
                var invalidOp = new InvalidOperationException("Payment method not found");
                MultiLogHelper.Error<BuyResponse>("Could not find payment method", invalidOp, logData);
                throw invalidOp;
            }
    
            buyResponse.PaymentMethod = paymentGatewayMethod.PaymentMethod;
            buyResponse.PaymentMethodSet = true;
    
            checkoutMgr.Payment.SavePaymentMethod(paymentGatewayMethod.PaymentMethod);
    
            var authorizePaymentResult = checkoutMgr.Payment.AuthorizePayment(paymentGatewayMethod.PaymentMethod.Key);
    
            var invoice = authorizePaymentResult.Invoice;
    
    
            var invoiceService = merchelloServices.InvoiceService;
    
            ((Invoice)invoice).CustomerKey = customer.Key;
    
            invoice.VersionKey = Guid.NewGuid();
    
            //Preparing shipment
            var gatewayproviderService = merchelloServices.GatewayProviderService;
            var shipmentService = merchelloServices.ShipmentService;
            var catalog = merchelloServices.WarehouseService.GetAllWarehouseCatalogs().FirstOrDefault();
            var shipCountry = gatewayproviderService.GetShipCountry(catalog.Key, customerCountryCode);
            var warehouse = merchelloServices.WarehouseService.GetDefaultWarehouse();
            var shipmentStatus = merchelloServices.ShipmentService.GetShipmentStatusByKey(MerchelloConstants.ShipmentStatus.Quoted);
            var shipment1 = merchelloServices.ShipmentService.CreateShipment(shipmentStatus, warehouse.AsAddress(), checkoutCustomerExtendedDataShippingAddress, invoice.Items);
    
            //Get rates and quotes
            var key = MerchelloConstants.ProviderKeys.Shipping.FixedRateShippingProviderKey;
    
            var rateTableProvider = (FixedRateShippingGatewayProvider)MerchelloContext.Current.Gateways.Shipping.GetProviderByKey(key);
            var shipMethods = rateTableProvider.ShipMethods.ToArray();
            var shipMethod = rateTableProvider.ShipMethods.FirstOrDefault(x => x.ShipCountryKey == shipCountry.Key);
            shipment1.ShipMethodKey = shipMethod.Key;
            buyResponse.Shipment1 = shipment1;
            var quote = rateTableProvider.QuoteShipMethodForShipment(shipment1, rateTableProvider.GetShippingGatewayMethod(shipMethod.Key, shipCountry.Key));
    
            //Add shipping quotes to invoice
            var quoteItem = quote.AsLineItemOf<InvoiceLineItem>();
            quoteItem.ExtendedData.AddShipment(shipment1);
            invoice.Items.Add(quoteItem);
    
            invoiceService.Save(invoice);
    
            buyResponse.Invoice = invoice;
            buyResponse.InvoiceCreated = true;
    
    
            var payment = gatewayproviderService.CreatePayment(PaymentMethodType.Cash, invoice.Total, paymentGatewayMethod.PaymentMethod.Key);
            buyResponse.Payment = payment;
            var capturePaymentResult = ((Invoice) invoice).CapturePayment(payment, paymentGatewayMethod, payment.Amount);
            buyResponse.PaymentResult = capturePaymentResult;
    
            if (capturePaymentResult.Payment.Success)
            {
                var approved = capturePaymentResult.ApproveOrderCreation;
                if (approved)
                {
                    var order = ((Invoice) invoice).PrepareOrder();
                    //order.OrderStatus = new OrderStatus() { Active = true, Key = MerchelloConstants.ShipmentStatus.Delivered, Alias = "delivered", Name = "Delivered", Reportable = true, SortOrder = 1 };
                    order.VersionKey = Guid.NewGuid();
                    var orderService = merchelloServices.OrderService;
                    orderService.Save(order);
                    buyResponse.Order = order;
                    buyResponse.OrderCreated = true;
    
                    var shippingLineItem = invoice.ShippingLineItems().FirstOrDefault();
                    var shipment = shippingLineItem.ExtendedData.GetShipment<InvoiceLineItem>();
                    ((Shipment)shipment).ShipmentStatus = new ShipmentStatus() { Active = true, Key = MerchelloConstants.ShipmentStatus.Delivered, Alias = "delivered", Name = "Delivered", Reportable = true, SortOrder = 1 };
                    shipmentService.Save(shipment);
    
    
                    var orderStatusKey = MerchelloConstants.OrderStatus.Fulfilled;
                    var orderStatus = orderService.GetOrderStatusByKey(orderStatusKey);
                    order.OrderStatus = orderStatus;
                    orderService.Save(order);
    
    
                }
    
    
    
            }
    
            var response = new HttpResponseMessage
            {
                Content = new ObjectContent<BuyResponse>(buyResponse, Configuration.Formatters.JsonFormatter),
                StatusCode = HttpStatusCode.OK
            };
            return response;
        }
    

    At line

    shipmentService.Save(shipment);

    i get "Unable to cast object of type 'Merchello.Core.Models.InvoiceLineItem' to type 'Merchello.Core.Models.IOrderLineItem'." error.

    I also tried the shipmentApiController mechanism, so instead of lines:

                    var shippingLineItem = invoice.ShippingLineItems().FirstOrDefault();
                    var shipment = shippingLineItem.ExtendedData.GetShipment<InvoiceLineItem>();
                    ((Shipment)shipment).ShipmentStatus = new ShipmentStatus() { Active = true, Key = MerchelloConstants.ShipmentStatus.Delivered, Alias = "delivered", Name = "Delivered", Reportable = true, SortOrder = 1 };
                    shipmentService.Save(shipment);
    

    I tried:

                    var shipmentApicontroller = new Merchello.Web.Editors.ShipmentApiController();
    
                    var shipmentModel = new Merchello.Web.Models.Shipping.ShipmentRequestDisplay();
                    var shipmentStatusFinal = shipmentService.GetShipmentStatusByKey(MerchelloConstants.ShipmentStatus.Delivered);
                    shipmentModel.ShipmentStatusKey = MerchelloConstants.ShipmentStatus.Delivered;
                    shipmentModel.ShipMethodKey = shipMethod.Key;
                    shipmentModel.TrackingNumber = "";
    
                    var shipmentOrderDisplay = new Merchello.Web.Models.ContentEditing.OrderDisplay
                    {
                        Key = order.Key,
                        InvoiceKey = invoice.Key,
                        OrderDate = order.OrderDate,
                        OrderNumber = order.OrderNumber,
                        OrderNumberPrefix = order.OrderNumberPrefix,
                        VersionKey = order.VersionKey,
                        OrderStatus = new OrderStatusDisplay() { Active = true, Key = MerchelloConstants.OrderStatus.Fulfilled, Alias = "fulfiled", Name = "Fulfilled", Reportable = true, SortOrder = 1 }
                    };
    
                    var shipmentItems = new List<Merchello.Web.Models.ContentEditing.OrderLineItemDisplay>();
    
                    foreach (var lineItem in invoice.ShippingLineItems())
                    {
                        var shipmentItem = new Merchello.Web.Models.ContentEditing.OrderLineItemDisplay
                        {
                            Key = lineItem.Key
                        };
    
                        shipmentItems.Add(shipmentItem);
                    }
    
                    shipmentOrderDisplay.Items = shipmentItems;
    
                    shipmentModel.Order = shipmentOrderDisplay;
    
                    var aaa = shipmentApicontroller.NewShipment(shipmentModel);
    

    but I still can't get the shipment saved and/or visible.

    Please, how can I save the shipment so i can ge it visible in the backoffice ?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Feb 27, 2017 @ 22:03
    Rusty Swayne
    0

    The shipment is based off a shipment rate quote that is saved in a shipment line item in the invoice.

    The error you are getting is due to the line items in the shipment need to be based off OrderLine items. An order is generated (by default) when an invoice is paid - this is based on a merchello.config setting.

    The order line items include a shipmentKey which is how Merchello is able to break an order into multiple shipments.

    As for the line item conversion, try the extension:

     AsLineItemOf<T>
    

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Core/Extensions.ILineItem.cs#L45

    Here is the usage in the order builder:

    https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Core/Chains/OrderCreation/ConvertInvoiceItemsToOrderItemsTask.cs#L38

  • Claudiu Bria 34 posts 146 karma points
    Feb 28, 2017 @ 11:12
    Claudiu Bria
    0

    Thank you Rusty for your quick response.

    Now my code, locally became:

                    var shippingLineItem = invoice.ShippingLineItems().FirstOrDefault();
                    var shipment = shippingLineItem.ExtendedData.GetShipment<InvoiceLineItem>();
                    ((Shipment)shipment).Items.Clear();
                    var invoiceItems = invoice.Items.Where(
                        x =>
                            (x.LineItemType != LineItemType.Shipping &&
                            x.LineItemType != LineItemType.Tax &&
                            x.LineItemType != LineItemType.Discount) &&
                            !(x.LineItemType == LineItemType.Custom && !x.ExtendedData.GetShippableValue()));
    
                    foreach (var item in invoiceItems)
                    {
                        ((Shipment) shipment).Items.Add(item.AsLineItemOf<OrderLineItem>());
                    }
                    ((Shipment)shipment).ShipmentStatus = new ShipmentStatus() { Active = true, Key = MerchelloConstants.ShipmentStatus.Delivered, Alias = "delivered", Name = "Delivered", Reportable = true, SortOrder = 1 };
                    shipmentService.Save(shipment);
    

    Now I get the db error: Additional information: The INSERT statement conflicted with the FOREIGN KEY constraint "FKmerchOrderItemmerchOrder". The conflict occurred in database "umbracoCms", table "dbo.merchOrder", column 'pk'. The error is again on the line

                    shipmentService.Save(shipment);
    
  • Claudiu Bria 34 posts 146 karma points
    Feb 28, 2017 @ 11:53
    Claudiu Bria
    0

    I also tried:

                    var shipment2Status = merchelloServices.ShipmentService.GetShipmentStatusByKey(MerchelloConstants.ShipmentStatus.Delivered);
                    var shipment2 = merchelloServices.ShipmentService.CreateShipment(shipment2Status, warehouse.AsAddress(), checkoutCustomerExtendedDataShippingAddress, order.Items);
                    shipment2.ShipMethodKey = shipMethod.Key;
                    shipmentService.Save(shipment2);
    

    The same error: The INSERT statement conflicted with the FOREIGN KEY constraint "FKmerchOrderItemmerchOrder". The conflict occurred in database "umbracoCms", table "dbo.merchOrder", column 'pk'. The statement has been terminated.

    What am I doing wrong please ?

  • Claudiu Bria 34 posts 146 karma points
    Mar 01, 2017 @ 11:46
    Claudiu Bria
    1

    I found out that the fix for the sql exception above is to set the ContainerKey property of each shipment item to the order key:

                     foreach (var shipment2Item in shipment2.Items)
                    {
                        shipment2Item.ContainerKey = order.Key;
                    }
                    shipment2.VersionKey = Guid.NewGuid();
                    shipmentService.Save(shipment2);
    
  • Claudiu Bria 34 posts 146 karma points
    Mar 01, 2017 @ 11:54
    Claudiu Bria
    0

    I also managed to make it work the other approach, the shipmentApi call, based on and thanks to Ryan Dansie's code here :

                    var shipmentApicontroller = new Merchello.Web.Editors.ShipmentApiController();
    
                    var shipmentModel = new Merchello.Web.Models.Shipping.ShipmentRequestDisplay();
                    var shipmentStatusFinal = shipmentService.GetShipmentStatusByKey(MerchelloConstants.ShipmentStatus.Delivered);
                    shipmentModel.ShipmentStatusKey = MerchelloConstants.ShipmentStatus.Delivered;
                    shipmentModel.ShipMethodKey = shipMethod.Key;
                    shipmentModel.TrackingNumber = "";
    
                    var shipmentOrderDisplay = new Merchello.Web.Models.ContentEditing.OrderDisplay
                    {
                        Key = order.Key,
                        InvoiceKey = invoice.Key,
                        OrderDate = order.OrderDate,
                        OrderNumber = order.OrderNumber,
                        OrderNumberPrefix = order.OrderNumberPrefix,
                        VersionKey = order.VersionKey,
                        OrderStatus = new OrderStatusDisplay() { Active = true, Key = MerchelloConstants.OrderStatus.Fulfilled, Alias = "fulfiled", Name = "Fulfilled", Reportable = true, SortOrder = 1 }
                    };
    
                    var shipmentItems = new List<Merchello.Web.Models.ContentEditing.OrderLineItemDisplay>();
    
                    foreach (var lineItem in order.Items)
                    {
    
                        var shipmentItem = new Merchello.Web.Models.ContentEditing.OrderLineItemDisplay
                        {
                            Key = lineItem.Key,
                            ContainerKey = order.Key,
                            Exported = lineItem.Exported,
                            ExtendedData = lineItem.ExtendedData,
                            LineItemType = lineItem.LineItemType,
                            LineItemTfKey = lineItem.LineItemTfKey,
                            Name = lineItem.Name,
                            Price = lineItem.Price,
                            Quantity = lineItem.Quantity,
                            ShipmentKey = shipment1.Key,
                            Sku = lineItem.Sku
                        };
    
                        shipmentItems.Add(shipmentItem);
                    }
    
                    shipmentOrderDisplay.Items = shipmentItems;
    
                    shipmentModel.Order = shipmentOrderDisplay;
    
                    var aaa = shipmentApicontroller.NewShipment(shipmentModel);
    

    Of course this looks more like a workaround, since it's much slower in execution, but it does eventually create the shipment.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Mar 01, 2017 @ 17:03
    Rusty Swayne
    0

    This is sort of an odd approach as the ApiController is essentially being used as a service. Essentially what this is doing is emulating the AJAX request from the back office Angular by passing a OrderDisplay model to the controller, which internally builds an IShipment (using all of the line items in the order passed) and then maps that back to a ShipmentDisplay for rendering in the back office UI.

    The only thing I think is accomplishing is giving access to the internal ShipmentBuilderChain class. This executes the tasks configured in the merchello.config to construct a shipment and does allow for altering the shipment object building process ... but since you're doing EVERYTHING via code anyway, I'm not sure I see the benefit. https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Core/Builders/ShipmentBuilderChain.cs#L14

  • Claudiu Bria 34 posts 146 karma points
    Mar 01, 2017 @ 16:51
    Claudiu Bria
    100

    Finally, this is my proof of concept v1:

        [HttpGet]
        public HttpResponseMessage GetCustomer(Guid guid)
        {
            var logData = new ExtendedLoggerData();
            logData.AddCategory("Merchello");
            logData.AddCategory("AjourCMSApi");
    
            var getCustomerResponse = GetCustomer(guid, logData);
    
            var response = new HttpResponseMessage
            {
                Content = new ObjectContent<GetCustomerResponse>(getCustomerResponse, Configuration.Formatters.JsonFormatter),
                StatusCode = HttpStatusCode.OK
            };
            return response;
        }
    
        private GetCustomerResponse GetCustomer(Guid guid, ExtendedLoggerData loggerData)
        {
            var getCustomerResponse = new GetCustomerResponse();
    
            var merchelloServices = MerchelloContext.Current.Services;
            var customerService = merchelloServices.CustomerService;
            var customer = customerService.GetByKey(guid);
    
            if (customer == null)
            {
                throw new Exception("Customer does not exists", null);
            }
    
            getCustomerResponse.Customer = customer;
            getCustomerResponse.CustomerFound = true;
    
            var umbracoServices = ApplicationContext.Current.Services;
            var memberService = umbracoServices.MemberService;
    
            var member = memberService.GetByEmail(customer.Email);
            if (member == null)
            {
                throw new Exception("Member does not exists", null);
            }
    
            getCustomerResponse.Member = member;
            getCustomerResponse.MemberFound = true;
    
            return getCustomerResponse;
        }
    
    
        internal class GetCustomerResponse
        {
            public ICustomer Customer = null;
            public bool CustomerFound = false;
            public IMember Member = null;
            public bool MemberFound = false;
        }
    
    
    
        /// <summary>
        /// Gets the <see cref="NewMemberModelFactory{TModel}"/>.
        /// </summary>
        private NewMemberModelFactory<NewMemberModel> NewMemberModelFactory { get; set; }
    
        [HttpGet]
        public HttpResponseMessage CreateCustomer(string loginName, string firstName, string lastName, string email, string password, string addressName = null, string addressAddress1 = null,
            string addressAddress2 = null, string addressOrganization = null, string addressLocality = null, string addressRegion = null, string addressPhone = null, string addressPostalCode = null,
            string addressCountryCode = null)
        {
            var logData = new ExtendedLoggerData();
            logData.AddCategory("Merchello");
            logData.AddCategory("AjourCMSApi");
    
            var createdCustomer = new CreateCustomerResponse();
    
            Mandate.ParameterNotNullOrEmpty(loginName, "loginName");
            Mandate.ParameterNotNullOrEmpty(firstName, "firstName");
            Mandate.ParameterNotNullOrEmpty(lastName, "lastName");
            Mandate.ParameterNotNullOrEmpty(email, "email");
            Mandate.ParameterNotNullOrEmpty(password, "password");
    
            var merchelloServices = MerchelloContext.Current.Services;
            var customerService = merchelloServices.CustomerService;
            var existingCustomer = customerService.GetByLoginName(loginName);
            if (existingCustomer != null)
            {
                throw new Exception("Customer already exists",
                    new Exception("Customer already exists: " + existingCustomer.ToStringInternal()));
            }
            var customer = customerService.CreateCustomerWithKey(loginName, firstName, lastName, email);
            var address1 = new Address
            {
                Name = addressName,
                Address1 = addressAddress1,
                Address2 = addressAddress2,
                AddressType = AddressType.Billing,
                Organization = addressOrganization,
                Locality = addressLocality,
                Region = addressRegion,
                Phone = addressPhone,
                PostalCode = addressPostalCode,
                CountryCode = addressCountryCode
            };
            var address2 = new Address
            {
                Name = addressName,
                Address1 = addressAddress1,
                Address2 = addressAddress2,
                AddressType = AddressType.Shipping,
                Organization = addressOrganization,
                Locality = addressLocality,
                Region = addressRegion,
                Phone = addressPhone,
                PostalCode = addressPostalCode,
                CountryCode = addressCountryCode
            };
            customer.CreateCustomerAddress(address1, "defaultBilling", address1.AddressType);
            customer.CreateCustomerAddress(address2, "defaultShipping", address2.AddressType);
            customerService.Save(customer);
    
            createdCustomer.Customer = customer;
            createdCustomer.CustomerCreated = true;
    
    
    
    
            var newMemberModelFactory = new NewMemberModelFactory<NewMemberModel>();
            Mandate.ParameterNotNull(newMemberModelFactory, "newMemberModelFactory");
            NewMemberModelFactory = newMemberModelFactory;
    
            var umbracoServices = ApplicationContext.Current.Services;
            var memberService = umbracoServices.MemberService;
    
            var model = new NewMemberModel
            {
                FirstName = firstName,
                LastName = lastName,
                Email = email,
                Password = password,
                MemberTypeAlias = "merchelloCustomer",
                ViewData = new StoreViewData(),
                PersistLogin = false
            };
    
            var registerModel = Members.CreateRegistrationModel(model.MemberTypeAlias);
            registerModel.Name = model.Email;
            registerModel.Email = model.Email;
            registerModel.Password = model.Password;
            registerModel.Username = model.Email;
            registerModel.UsernameIsEmail = true;
    
            var fn = new UmbracoProperty { Alias = "firstName", Name = "First Name", Value = model.FirstName };
            var ln = new UmbracoProperty { Alias = "lastName", Name = "Last Name", Value = model.LastName };
            registerModel.MemberProperties.Add(fn);
            registerModel.MemberProperties.Add(ln);
    
            MembershipCreateStatus status;
    
            //// Add a message for the attempt
            MultiLogHelper.Info<CreateCustomerResponse>("Registering a new member", logData);
    
            var member = Members.RegisterMember(registerModel, out status, model.PersistLogin);
    
    
            var registration = NewMemberModelFactory.Create(model, status);
    
            if (registration.ViewData.Success)
            {
                var membership = memberService.GetByEmail(model.Email);
    
                if (member != null)
                {
                    memberService.AssignRole(membership.Id, "Customers");
                    memberService.Save(membership);
                    createdCustomer.Member = membership;
                    createdCustomer.MemberCreated = true;
                }
    
            }
    
            var response = new HttpResponseMessage
            {
                Content = new ObjectContent<CreateCustomerResponse>(createdCustomer, Configuration.Formatters.JsonFormatter),
                StatusCode = HttpStatusCode.Created
            };
            return response;
        }
    
        private class CreateCustomerResponse
        {
            public ICustomer Customer = null;
            public bool CustomerCreated = false;
            public IMember Member = null;
            public bool MemberCreated = false;
        }
    
    
        [HttpGet]
        public HttpResponseMessage Buy(Guid productKey, Guid customerKey)
        {
            var logData = new ExtendedLoggerData();
            logData.AddCategory("Merchello");
            logData.AddCategory("AjourCMSApi");
    
            var buyResponse = new BuyResponse();
    
            Mandate.ParameterNotNullOrEmpty(productKey.ToString(), "productKey");
            Mandate.ParameterNotNullOrEmpty(customerKey.ToString(), "customerKey");
    
            var merchelloServices = MerchelloContext.Current.Services;
    
            var customerResponse = GetCustomer(customerKey, logData);
            buyResponse.CustomerResponse = customerResponse;
            var customer = customerResponse.Customer;
    
            if (customer.Addresses.All(x => x.AddressType != AddressType.Billing))
            {
                var invalidOp = new InvalidOperationException("Billing address not found");
                MultiLogHelper.Error<BuyResponse>("Could not find billing address", invalidOp, logData);
                throw invalidOp;
            }
    
            var productService = merchelloServices.ProductService;
            var product = productService.GetByKey(productKey);
            if (product == null)
            {
                throw new Exception("Product does not exists", null);
            }
    
            buyResponse.Product = product;
            buyResponse.ProductFound = true;
    
            var defaultCatalogKey = MerchelloConstants.Warehouse.DefaultWarehouseCatalogKey;
    
            var extendedData = new ExtendedDataCollection();
            // this is used to determine where a shipment originates
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.WarehouseCatalogKey, defaultCatalogKey.ToString());
            // items set to shippable
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.TrackInventory, "false");
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.Shippable, "true");
            extendedData.SetValue(MerchelloConstants.ExtendedDataKeys.CurrencyCode, "USD");
    
            var customerBasket = customer.Basket();
            customerBasket.Empty();
            customerBasket.AddItem(product, product.Name, 1, extendedData);
            customerBasket.Save();
    
            buyResponse.Basket = customerBasket;
            buyResponse.BasketCreated = true;
    
            var checkoutMgr = customerBasket.GetCheckoutManager();
            var checkoutCustomer = (ICustomer)checkoutMgr.Customer.Context.Customer;
    
            var customerExtendedDataRefreshed = RefreshCustomerExtendedData(ref checkoutCustomer);
    
            buyResponse.CheckoutCustomer = checkoutCustomer;
            buyResponse.CheckoutCustomerFound = true;
    
            var checkoutCustomerExtendedDataShippingAddress = checkoutCustomer.ExtendedData.GetAddress(AddressType.Shipping);
            var customerCountryCode = checkoutCustomerExtendedDataShippingAddress.CountryCode;
    
            if (!checkoutMgr.Payment.IsReadyToInvoice())
            {
                var invalidOp = new InvalidOperationException("CheckoutManager is not ready to invoice");
                MultiLogHelper.Error<BuyResponse>("Not ready to invoice", invalidOp, logData);
                throw invalidOp;
            }
    
            var paymentGatewayMethod = checkoutMgr.Payment.GetPaymentGatewayMethods().FirstOrDefault();
    
            if (paymentGatewayMethod == null)
            {
                var invalidOp = new InvalidOperationException("Payment Gateway method not found");
                MultiLogHelper.Error<BuyResponse>("Could not find payment gateway method", invalidOp, logData);
                throw invalidOp;
            }
    
            if (paymentGatewayMethod.PaymentMethod == null)
            {
                var invalidOp = new InvalidOperationException("Payment method not found");
                MultiLogHelper.Error<BuyResponse>("Could not find payment method", invalidOp, logData);
                throw invalidOp;
            }
    
            buyResponse.PaymentMethod = paymentGatewayMethod.PaymentMethod;
            buyResponse.PaymentMethodSet = true;
    
            checkoutMgr.Payment.SavePaymentMethod(paymentGatewayMethod.PaymentMethod);
    
            var authorizePaymentResult = checkoutMgr.Payment.AuthorizePayment(paymentGatewayMethod.PaymentMethod.Key);
    
            var invoice = authorizePaymentResult.Invoice;
    
            var invoiceService = merchelloServices.InvoiceService;
    
            ((Invoice)invoice).CustomerKey = customer.Key;
    
            invoice.VersionKey = Guid.NewGuid();
    
            //Preparing shipment
            var gatewayproviderService = merchelloServices.GatewayProviderService;
            var shipmentService = merchelloServices.ShipmentService;
            var catalog = merchelloServices.WarehouseService.GetAllWarehouseCatalogs().FirstOrDefault();
            var shipCountry = gatewayproviderService.GetShipCountry(catalog.Key, customerCountryCode);
            var warehouse = merchelloServices.WarehouseService.GetDefaultWarehouse();
            var shipmentStatus = merchelloServices.ShipmentService.GetShipmentStatusByKey(MerchelloConstants.ShipmentStatus.Quoted);
            var shipment = merchelloServices.ShipmentService.CreateShipment(shipmentStatus, warehouse.AsAddress(), checkoutCustomerExtendedDataShippingAddress, invoice.Items);
    
            //Get rates and quotes
            var key = MerchelloConstants.ProviderKeys.Shipping.FixedRateShippingProviderKey;
    
            var rateTableProvider = (FixedRateShippingGatewayProvider)MerchelloContext.Current.Gateways.Shipping.GetProviderByKey(key);
            var shipMethods = rateTableProvider.ShipMethods.ToArray();
            var shipMethod = rateTableProvider.ShipMethods.FirstOrDefault(x => x.ShipCountryKey == shipCountry.Key);
            shipment.ShipMethodKey = shipMethod.Key;
    
            buyResponse.Shipment = shipment;
            var quote = rateTableProvider.QuoteShipMethodForShipment(shipment, rateTableProvider.GetShippingGatewayMethod(shipMethod.Key, shipCountry.Key));
    
            //Add shipping quotes to invoice
            var quoteItem = quote.AsLineItemOf<InvoiceLineItem>();
            quoteItem.ExtendedData.AddShipment(shipment);
            invoice.Items.Add(quoteItem);
    
            invoiceService.Save(invoice);
    
            buyResponse.Invoice = invoice;
            buyResponse.InvoiceCreated = true;
    
    
            var payment = gatewayproviderService.CreatePayment(PaymentMethodType.Cash, invoice.Total, paymentGatewayMethod.PaymentMethod.Key);
            buyResponse.Payment = payment;
            var capturePaymentResult = ((Invoice) invoice).CapturePayment(payment, paymentGatewayMethod, payment.Amount);
            buyResponse.PaymentResult = capturePaymentResult;
    
            if (capturePaymentResult.Payment.Success)
            {
                var approved = capturePaymentResult.ApproveOrderCreation;
                if (approved)
                {
                    var order = ((Invoice) invoice).PrepareOrder();
                    order.VersionKey = Guid.NewGuid();
                    var orderService = merchelloServices.OrderService;
                    orderService.Save(order);
                    buyResponse.Order = order;
                    buyResponse.OrderCreated = true;
    
                    foreach (var shipment1Item in shipment.Items)
                    {
                        shipment1Item.ContainerKey = order.Key;
                    }
                    shipment.VersionKey = Guid.NewGuid();
                    shipmentService.Save(shipment);
                    shipment.AuditCreated();
    
                    foreach (var orderShippingItem in order.Items)
                    {
                        ((OrderLineItem)orderShippingItem).ShipmentKey = shipment.Key;
                    }
                    order.VersionKey = Guid.NewGuid();
                    orderService.Save(order);
                    order.AuditCreated();
    
                    var shipmentStatusFinal = merchelloServices.ShipmentService.GetShipmentStatusByKey(MerchelloConstants.ShipmentStatus.Delivered);
                    shipment.ShipmentStatus = shipmentStatusFinal;
                    shipment.VersionKey = Guid.NewGuid();
                    shipmentService.Save(shipment);
                    shipment.AuditStatusChanged();
                    buyResponse.ShipmentDelivered = true;
    
                    var orderStatusKey = MerchelloConstants.OrderStatus.Fulfilled;
                    var orderStatus = orderService.GetOrderStatusByKey(orderStatusKey);
                    order.OrderStatus = orderStatus;
                    order.VersionKey = Guid.NewGuid();
                    orderService.Save(order);
                    buyResponse.OrderFulfilled = true;
    
                }
            }
    
            var response = new HttpResponseMessage
            {
                Content = new ObjectContent<BuyResponse>(buyResponse, Configuration.Formatters.JsonFormatter),
                StatusCode = HttpStatusCode.OK
            };
            return response;
        }
    
        private static bool RefreshCustomerExtendedData(ref ICustomer customer)
        {
            var customerExtendedDataAddressRefreshed = false;
            var customerExtendedDataBillingAddressRefreshed = false;
            var customerExtendedDataShippingAddressRefreshed = false;
            if (customer.Addresses.Any(x => x.AddressType == AddressType.Billing) && customer.ExtendedData.GetAddress(AddressType.Billing) == null)
            {
                var customerBillingAddress = customer.Addresses.First(x => x.AddressType == AddressType.Billing);
                var address1 = new Address
                {
                    Name = customerBillingAddress.Label,
                    Address1 = customerBillingAddress.Address1,
                    Address2 = customerBillingAddress.Address2,
                    AddressType = AddressType.Billing,
                    Organization = customerBillingAddress.Company,
                    Locality = customerBillingAddress.Locality,
                    Region = customerBillingAddress.Region,
                    Phone = customerBillingAddress.Phone,
                    PostalCode = customerBillingAddress.PostalCode,
                    CountryCode = customerBillingAddress.CountryCode
                };
                customer.ExtendedData.AddAddress(address1, AddressType.Billing);
                customerExtendedDataBillingAddressRefreshed = true;
            }
            if (customer.Addresses.Any(x => x.AddressType == AddressType.Shipping) && customer.ExtendedData.GetAddress(AddressType.Shipping) == null)
            {
                var customerShippingAddress = customer.Addresses.First(x => x.AddressType == AddressType.Shipping);
                var address2 = new Address
                {
                    Name = customerShippingAddress.Label,
                    Address1 = customerShippingAddress.Address1,
                    Address2 = customerShippingAddress.Address2,
                    AddressType = AddressType.Shipping,
                    Organization = customerShippingAddress.Company,
                    Locality = customerShippingAddress.Locality,
                    Region = customerShippingAddress.Region,
                    Phone = customerShippingAddress.Phone,
                    PostalCode = customerShippingAddress.PostalCode,
                    CountryCode = customerShippingAddress.CountryCode
                };
                customer.ExtendedData.AddAddress(address2, AddressType.Shipping);
                customerExtendedDataShippingAddressRefreshed = true;
            }
            customerExtendedDataAddressRefreshed = customerExtendedDataBillingAddressRefreshed || customerExtendedDataShippingAddressRefreshed;
    
            return customerExtendedDataAddressRefreshed;
        }
    
        internal class BuyResponse
        {
            public GetCustomerResponse CustomerResponse = null;
            public ICustomer CheckoutCustomer = null;
            public bool CheckoutCustomerFound = false;
            public IProduct Product = null;
            public bool ProductFound = false;
            public IBasket Basket = null;
            public bool BasketCreated = false;
            public IPaymentMethod PaymentMethod = null;
            public bool PaymentMethodSet = false;
            public IInvoice Invoice = null;
            public bool InvoiceCreated = false;
            public IPayment Payment = null;
            public IPaymentResult PaymentResult = null;
            public IOrder Order = null;
            public bool OrderCreated = false;
            public IShipment Shipment = null;
            public bool ShipmentDelivered = false;
            public bool OrderFulfilled = false;
        }
    

    It creates a new customer and buys a product on behalf of the customer. This will be useful in the scenario where the customer wants to buy a digital product from the merchello webshop without the web interface. One aspect I intended here is that the chain of operation could be continued by the customer manually using the web interface of merchello, at any stage. Rusty, if you have some time, could you please point me to anything I'm doing wrong here ?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Mar 01, 2017 @ 17:17
    Rusty Swayne
    0

    Wow - looks like you've figured things out pretty well. It's sort of cool to see a flattened workflow like this =)

    Essentially what you've done is parted together the execution points for information collected in multiple steps in the CheckoutManager classes.

    I'd say good job =)

  • Claudiu Bria 34 posts 146 karma points
    Mar 02, 2017 @ 07:31
    Claudiu Bria
    0

    Thank you Rusty for your good words.

    Since our current goal here at my company is to provide an additional way to manipulate data of a webshop externally without using the web interface, I think it's going to be an interesting ride, so I'm very excited to progress into flattened workflows like this, so I'm gonna bother you further with questions, if that's ok with you. Of course I will post here all of this work for anyone interested.

    My next step is to develop a bit the Buy method and make it more realistic from the payment perspective, adding credit card payment flow. I'm particularly interested in both Authorize.Net and PayPal workflows. Any ideas would be appreciated. Thank you again for your support.

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    Mar 02, 2017 @ 18:15
    Rusty Swayne
    1

    With the payments you will have to have some customer information - in the case of Authorize - the CC info and PayPal (if you redirect) they have to enter their stuff into PayPal.

    The good news is you have everything you need to just allow Merchello to handle those as it would normally using the invoice extensions.

      var invoice = MerchelloContext.Current.Services.InvoiceService.GetByKey([invoiceKey]);
    
      var paymentAttempt = invoice.AuthorizeCapturePayment (... one of the overloads here);
    

    We also just added the Authorize.Net plugin to be moved into the Core in 2.6.0 - http://issues.merchello.com/youtrack/agiles/76-1/77-81

    Be great to have another set of eyes on the progress - and always happy to have some help =)

Please Sign in or register to post replies

Write your reply to:

Draft