Beispiel #1
0
        private async Task <OrderDBE> InsertNewExplodedOrderAsync(int merchantId, NewOrderMBE newExplodedOrder, int?refOrderId = null)
        {
            //Step 1: Store the new merchant Order
            var newDBOrder = new OrderDBE()
            {
                Status           = Enums.ORDER_STATUS.New,
                MerchantId       = merchantId,
                CustomerName     = newExplodedOrder.CustomerName,
                PhoneNumber      = newExplodedOrder.CustomerPhoneNo,
                OrderDateTimeUTC = DateTime.UtcNow,
                RefOrderId       = refOrderId
            };

            await _dbContext.InsertOrderAsync(newDBOrder);

            //Step 2: create the first event
            var newDBOrderEvent = new OrderEventDBE()
            {
                OrderId          = newDBOrder.OrderId,
                EventDateTimeUTC = DateTime.UtcNow,
                OrderStatus      = Enums.ORDER_STATUS.New,
                EventDescription = refOrderId.HasValue ? $"Order created (from {refOrderId.Value})." : "Order created"
            };

            await _dbContext.InsertOrderEventAsync(newDBOrderEvent);

            //newDBOrder.OrderEvents.Add(newDBOrderEvent);        // build exploded order

            //Step 3: iterate through orderLineItems collection to save it in the db
            foreach (var orderLineItem in newExplodedOrder.OrderLineItems)
            {
                var catalogItem = await _dbContext.GetCatalogItemAsync(orderLineItem.ItemGuid);

                var newDBOrderLineItem = new OrderLineItemDBE()
                {
                    ItemName        = catalogItem.ItemName,
                    ItemUnitPrice   = catalogItem.ItemUnitPrice,
                    OrderId         = newDBOrder.OrderId,
                    CatalogItemGuid = orderLineItem.ItemGuid
                };

                await _dbContext.InsertOrderLineItemAsync(newDBOrderLineItem);

                //newDBOrder.OrderLineItems.Add(newDBOrderLineItem);        // build exploded order
            }

            return(newDBOrder);
        }
Beispiel #2
0
        private async Task SendSMSMessageAsync(OrderDBE dbOrderExploded, WebUrlConfigurationBE webUrlConfig)
        {
            // Step 1: calc the order total
            decimal orderTotal = (dbOrderExploded.OrderLineItems != null)
                                            ? dbOrderExploded.OrderLineItems.Sum(oli => oli.ItemUnitPrice)
                                            : 0.0M;

            // Step 2: Build the SMS Msg
            string payAwayURL = $"{webUrlConfig.HPPBaseUrl}/customerorder/{dbOrderExploded.OrderGuid}";

            StringBuilder messageBody = new StringBuilder();

            messageBody.AppendLine($"Hello {dbOrderExploded.CustomerName}");

            // we do not know what culture the server is set for so we are explicit, we want to make it formats currency with a US $
            var specificCulture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
            FormattableString formattableString = $"{dbOrderExploded.Merchant.MerchantName} is sending you this link to a secure payment page to enter your payment info for your Order Number: {dbOrderExploded.OrderId:0000} for: {orderTotal:C}";

            messageBody.AppendLine(formattableString.ToString(specificCulture));
            messageBody.AppendLine($"{payAwayURL}");

            // Step 3: Send the SMS msg
            // convert the phone no to the "normalized format"  +15131234567 that the SMS api accepts
            (bool isValidPhoneNo, string formattedPhoneNo, string normalizedPhoneNo) = Utilities.PhoneNoHelpers.NormalizePhoneNo(dbOrderExploded.PhoneNumber);
            var msgSid = SMSController.SendSMSMessage(String.Empty, formattedPhoneNo, messageBody.ToString());

            // Step 4 Create & Save the SMS event
            var dbOrderEvent = new OrderEventDBE()
            {
                OrderId          = dbOrderExploded.OrderId,
                EventDateTimeUTC = DateTime.UtcNow,
                OrderStatus      = Enums.ORDER_STATUS.SMS_Sent,
                EventDescription = $"SMS sent to [{normalizedPhoneNo}]."
            };

            await _dbContext.InsertOrderEventAsync(dbOrderEvent);

            // Step 5: Update the order status
            dbOrderExploded.Status = Enums.ORDER_STATUS.SMS_Sent;
            await _dbContext.UpdateOrderAsync(dbOrderExploded);
        }
Beispiel #3
0
        /// <summary>
        /// Inserts new order event
        /// </summary>
        /// <param name="newOrderEvent">The new order event.</param>
        /// <returns>OrderEventDBE</returns>
        /// <remarks>
        /// only used by the ResetDB method so we can keep the same guids across reloads.
        /// </remarks>
        internal async Task InsertOrderEventAsync(OrderEventDBE newOrderEvent)
        {
            this.OrderEvents.Add(newOrderEvent);

            try
            {
                await this.SaveChangesAsync();
            }
            catch (DbUpdateException ex)
            {
                // exception was raised by the db (ex: UK violation)
                var sqlException = ex.InnerException;

                // we do this to disconnect the exception that bubbles up from the dbcontext which will be disposed when it leaves this method
                throw new ApplicationException(sqlException.Message);
            }
            catch (Exception)
            {
                // rethrow exception
                throw;
            }
        }
Beispiel #4
0
        public async Task <ActionResult> UpdateOrder([FromRoute] Guid orderGuid, [FromBody] NewOrderMBE updatedOrder)
        {
            // get the existing order
            var dbOrder = await _dbContext.GetOrderAsync(orderGuid);

            // if we did not find a matching order
            if (dbOrder == null)
            {
                return(BadRequest(new ArgumentException($"OrderGuid: [{orderGuid}] not found", nameof(orderGuid))));
            }

            // Biz Logic: Cannot change the order if it has already been paid for.
            if (dbOrder.Status == Enums.ORDER_STATUS.SMS_Sent || dbOrder.Status == Enums.ORDER_STATUS.Paid)
            {
                return(BadRequest(new ArgumentException($"Changes are not allowed after the SMS has been sent or the order has been paid", nameof(orderGuid))));
            }

            #region === Validation =================================================
            // validate the input params
            if (string.IsNullOrEmpty(updatedOrder.CustomerName))
            {
                return(BadRequest(new ArgumentException(@"The order name cannot be blank.", nameof(updatedOrder.CustomerName))));
            }
            else if (string.IsNullOrEmpty(updatedOrder.CustomerPhoneNo))
            {
                return(BadRequest(new ArgumentException(@"The order phone number cannot be blank.", nameof(updatedOrder.CustomerPhoneNo))));
            }
            (bool isValidPhoneNo, string formatedPhoneNo, _) = PhoneNoHelpers.NormalizePhoneNo(updatedOrder.CustomerPhoneNo);
            if (!isValidPhoneNo)
            {
                return(BadRequest(new ArgumentNullException(nameof(updatedOrder.CustomerPhoneNo), $"[{updatedOrder.CustomerPhoneNo}] is NOT a supported Phone No format.")));
            }
            else
            {
                updatedOrder.CustomerPhoneNo = formatedPhoneNo;
            }

            // validate the catalog guids
            foreach (var orderLineItem in updatedOrder.OrderLineItems)
            {
                // try to find the catalog item
                var catalogItem = await _dbContext.GetCatalogItemAsync(orderLineItem.ItemGuid);

                // if it did not exist (ie: a invalid guid)
                if (catalogItem == null)
                {
                    return(BadRequest(new ArgumentNullException(nameof(orderLineItem.ItemGuid), $"Error : [{orderLineItem.ItemGuid}] Is not a valid catalog item guid.")));
                }
            }

            #endregion

            try
            {
                // Step 1: update the dbOrder with the values we just got
                dbOrder.CustomerName = updatedOrder.CustomerName;
                dbOrder.PhoneNumber  = updatedOrder.CustomerPhoneNo;
                dbOrder.Status       = Enums.ORDER_STATUS.Updated;

                await _dbContext.UpdateOrderAsync(dbOrder);

                // Step 2: in this demo code we are just going to delete and re-add the order line items
                await _dbContext.DeleteOrderLineItemsAsync(dbOrder.OrderId);

                //iterate through orderLineItems collection to save it in the db
                foreach (var orderLineItem in updatedOrder.OrderLineItems)
                {
                    var catalogItem = await _dbContext.GetCatalogItemAsync(orderLineItem.ItemGuid);

                    var dbOrderLineItem = new OrderLineItemDBE()
                    {
                        ItemName        = catalogItem.ItemName,
                        ItemUnitPrice   = catalogItem.ItemUnitPrice,
                        OrderId         = dbOrder.OrderId,
                        CatalogItemGuid = orderLineItem.ItemGuid
                    };

                    await _dbContext.InsertOrderLineItemAsync(dbOrderLineItem);
                }

                // Step 3: create an event
                var dbOrderEvent = new OrderEventDBE()
                {
                    OrderId          = dbOrder.OrderId,
                    EventDateTimeUTC = DateTime.UtcNow,
                    OrderStatus      = Enums.ORDER_STATUS.Updated,
                    EventDescription = "Order updated."
                };

                //save order event
                await _dbContext.InsertOrderEventAsync(dbOrderEvent);

                return(NoContent());
            }
            catch (Exception ex)
            {
                return(BadRequest(new ApplicationException($"Error: [{ex.Message}] Failed to update merchant order.")));
            }
        }
Beispiel #5
0
        public async Task <ActionResult> SubmitOrderPayment([FromRoute] Guid orderGuid, [FromBody] PaymentInfoMBE paymentInfo)
        {
            //query the db
            var dbOrderExploded = await _dbContext.GetOrderExplodedAsync(orderGuid);

            #region === Validation =====================
            //Biz Logic: check to see if the order guid is correct
            if (dbOrderExploded == null)
            {
                return(NotFound($"Customer order with ID: {orderGuid} not found"));
            }

            // == Expiration Date =====================================
            // Step 2a: Is it even a valid date (this takes care of wacky month values)
            if (!DateTime.TryParse($"{paymentInfo.ExpMonth}/1/{ paymentInfo.ExpYear}", out DateTime parsedDate))
            {
                return(BadRequest($"{paymentInfo.ExpMonth}/{paymentInfo.ExpYear} is not a valid expiration date"));
            }

            // Step 2b: The expiration date cannot be to far into the future (this takes care of yrs too far into the future)
            if (parsedDate > DateTime.Today.AddYears(5))
            {
                return(BadRequest($"{paymentInfo.ExpMonth}/{ paymentInfo.ExpYear} is not a valid expiration date"));
            }

            // Step 2c: Is the card still valid today (cards are valid thru the last day of the month  (this check prevents dates in the past)
            DateTime calcExpireDate = parsedDate.AddMonths(1).AddDays(-1);
            if (DateTime.Today > calcExpireDate)
            {
                return(BadRequest($"Payment Instrument is no longer valid, expired on {calcExpireDate:MM/dd/yyyy}"));
            }

            // == Tip Amount ====================================
            // Step 3a: Check to see if the order has a tip even when the merchant doesn't support tips.
            if (dbOrderExploded.Merchant.IsSupportsTips == false &&
                paymentInfo.TipAmount.HasValue &&
                paymentInfo.TipAmount.Value != 0.0M)
            {
                return(BadRequest($"This merchant does NOT support tips."));
            }

            // Step 3b: Biz Logic: check to see if tip is less than zero
            if (paymentInfo.TipAmount.HasValue &&
                paymentInfo.TipAmount.Value < 0.0M)
            {
                return(BadRequest($"The tip amount: {paymentInfo.TipAmount} cannot less than $0.00."));
            }

            // == CC Number ===================================
            string paymentPan = paymentInfo.PAN.Trim().CleanUp();
            // // Step 4a: Biz Logi  cc: check to see if credit card number is fine.
            if (paymentPan.Length != 16)
            {
                return(BadRequest("Your Credit card number must be 16 digits."));
            }

            if (!paymentPan.CheckLuhn())
            {
                return(BadRequest("Your Credit card number must pass a LUN check."));
            }

            // == Auth Code ===================================
            // check to see if auth code is present
            if (!String.IsNullOrEmpty(dbOrderExploded.AuthCode))
            {
                return(BadRequest("Order has already been marked paid."));
            }
            #endregion

            try
            {
                //update the dbOrder with the values we just got
                dbOrderExploded.CreditCardNumber = paymentPan;
                dbOrderExploded.ExpMonth         = paymentInfo.ExpMonth;
                dbOrderExploded.ExpYear          = paymentInfo.ExpYear;
                dbOrderExploded.AuthCode         = CardNetworkHelper.GenerateAuthCode();
                dbOrderExploded.TipAmount        = paymentInfo.TipAmount ?? 0.9M;

                //update order
                dbOrderExploded.Status = Enums.ORDER_STATUS.Paid;
                await _dbContext.UpdateOrderAsync(dbOrderExploded);

                //Write the order payment event
                var dbOrderEvent = new OrderEventDBE()
                {
                    OrderId          = dbOrderExploded.OrderId,
                    EventDateTimeUTC = DateTime.UtcNow,
                    OrderStatus      = Enums.ORDER_STATUS.Paid,
                    EventDescription = $"Order has been paid."
                };

                //save order event
                await _dbContext.InsertOrderEventAsync(dbOrderEvent);

                // send notification to all connected clients
                await _messageHub.Clients.All.SendAsync("ReceiveMessage", "Server", $"Order: [{orderGuid}] updated");

                return(NoContent());
            }
            catch (Exception ex)
            {
                return(BadRequest(new ApplicationException($"Error: [{ex.Message}] Failed to send order payment.")));
            }
        }