public async Task <JObject> GetOrderAsync(string ID, DecodedToken decodedToken) { var supplierID = ID.Split("-")[1]; var me = await _oc.Me.GetAsync(accessToken : decodedToken.AccessToken); Require.That(decodedToken.CommerceRole == CommerceRole.Seller || supplierID == me.Supplier.ID, new ErrorCode("Unauthorized", 401, $"You are not authorized view this order")); try { var type = Assembly.GetExecutingAssembly().GetTypeByAttribute <SupplierSyncAttribute>(attribute => attribute.SupplierID == supplierID) ?? Assembly.GetExecutingAssembly().GetTypeByAttribute <SupplierSyncAttribute>(attribute => attribute.SupplierID == "Generic"); if (type == null) { throw new MissingMethodException($"Command for {supplierID} is unavailable"); } var command = (ISupplierSyncCommand)Activator.CreateInstance(type, _settings); var method = command.GetType().GetMethod($"GetOrderAsync", BindingFlags.Public | BindingFlags.Instance); if (method == null) { throw new MissingMethodException($"Get Order Method for {supplierID} is unavailable"); } return(await(Task <JObject>) method.Invoke(command, new object[] { ID, decodedToken })); } catch (MissingMethodException mex) { throw new Exception(JsonConvert.SerializeObject(new ApiError() { Data = new { decodedToken, OrderID = ID }, ErrorCode = mex.Message, Message = $"Missing Method for: {supplierID ?? "Invalid Supplier"}" })); } }
public async Task <Payment> AuthorizePayment( OrderCloudIntegrationsCreditCardPayment payment, string userToken, string merchantID ) { Require.That((payment.CreditCardID != null) || (payment.CreditCardDetails != null), new ErrorCode("CreditCard.CreditCardAuth", 400, "Request must include either CreditCardDetails or CreditCardID")); var cc = await GetMeCardDetails(payment, userToken); Require.That(payment.IsValidCvv(cc), new ErrorCode("CreditCardAuth.InvalidCvv", 400, "CVV is required for Credit Card Payment")); Require.That(cc.Token != null, new ErrorCode("CreditCardAuth.InvalidToken", 400, "Credit card must have valid authorization token")); Require.That(cc.xp.CCBillingAddress != null, new ErrorCode("Invalid Bill Address", 400, "Credit card must have a billing address")); var orderWorksheet = await _oc.IntegrationEvents.GetWorksheetAsync <HSOrderWorksheet>(OrderDirection.Incoming, payment.OrderID); var order = orderWorksheet.Order; Require.That(!order.IsSubmitted, new ErrorCode("CreditCardAuth.AlreadySubmitted", 400, "Order has already been submitted")); var ccAmount = orderWorksheet.Order.Total; var ocPaymentsList = (await _oc.Payments.ListAsync <HSPayment>(OrderDirection.Incoming, payment.OrderID, filters: "Type=CreditCard")); var ocPayments = ocPaymentsList.Items; var ocPayment = ocPayments.Any() ? ocPayments[0] : null; if (ocPayment == null) { throw new CatalystBaseException("Payment.MissingCreditCardPayment", "Order is missing credit card payment"); } try { if (ocPayment?.Accepted == true) { if (ocPayment.Amount == ccAmount) { return(ocPayment); } else { await VoidTransactionAsync(ocPayment, order, userToken); } } var call = await _cardConnect.AuthWithoutCapture(CardConnectMapper.Map(cc, order, payment, merchantID, ccAmount)); ocPayment = await _oc.Payments.PatchAsync <HSPayment>(OrderDirection.Incoming, order.ID, ocPayment.ID, new PartialPayment { Accepted = true, Amount = ccAmount }); return(await _oc.Payments.CreateTransactionAsync(OrderDirection.Incoming, order.ID, ocPayment.ID, CardConnectMapper.Map(ocPayment, call))); } catch (CreditCardAuthorizationException ex) { ocPayment = await _oc.Payments.PatchAsync <HSPayment>(OrderDirection.Incoming, order.ID, ocPayment.ID, new PartialPayment { Accepted = false, Amount = ccAmount }); await _oc.Payments.CreateTransactionAsync(OrderDirection.Incoming, order.ID, ocPayment.ID, CardConnectMapper.Map(ocPayment, ex.Response)); throw new CatalystBaseException($"CreditCardAuth.{ex.ApiError.ErrorCode}", ex.ApiError.Message, ex.Response); } }
public async Task <HSSupplier> GetMySupplier(string supplierID, DecodedToken decodedToken) { var me = await _oc.Me.GetAsync(accessToken : decodedToken.AccessToken); Require.That(supplierID == me.Supplier.ID, new ErrorCode("Unauthorized", 401, $"You are only authorized to view {me.Supplier.ID}.")); return(await _oc.Suppliers.GetAsync <HSSupplier>(supplierID)); }
private async Task <CurrencySymbol> GetCurrencyForUser(string userToken) { var buyerUserGroups = await _oc.Me.ListUserGroupsAsync <HSLocationUserGroup>(opts => opts.AddFilter(u => u.xp.Type == "BuyerLocation"), userToken); var currency = buyerUserGroups.Items.FirstOrDefault(u => u.xp.Currency != null)?.xp?.Currency; Require.That(currency != null, new ErrorCode("Exchange Rate Error", 400, "Exchange Rate Not Defined For User")); return((CurrencySymbol)currency); }
public async Task <TaxCertificate> UpdateAsync(string locationID, TaxCertificate cert, DecodedToken decodedToken) { await EnsureUserCanManageLocationResaleCert(locationID, decodedToken); var buyerID = locationID.Split('-')[0]; var address = await _oc.Addresses.GetAsync <HSAddressBuyer>(buyerID, locationID); Require.That(address.xp.AvalaraCertificateID == cert.ID, new ErrorCode("Insufficient Access", 403, $"User cannot modofiy this cert")); var updatedCert = await _avalara.UpdateCertificateAsync(cert.ID, cert, address); return(updatedCert); }
public async Task <HSSupplier> UpdateSupplier(string supplierID, PartialSupplier supplier, DecodedToken decodedToken) { var me = await _oc.Me.GetAsync(accessToken : decodedToken.AccessToken); Require.That(decodedToken.CommerceRole == CommerceRole.Seller || supplierID == me.Supplier.ID, new ErrorCode("Unauthorized", 401, $"You are not authorized to update supplier {supplierID}")); var currentSupplier = await _oc.Suppliers.GetAsync <HSSupplier>(supplierID); var updatedSupplier = await _oc.Suppliers.PatchAsync <HSSupplier>(supplierID, supplier); // Update supplier products only on a name change if (currentSupplier.Name != supplier.Name || currentSupplier.xp.Currency.ToString() != supplier.xp.Currency.Value) { var productsToUpdate = await _oc.Products.ListAllAsync <HSProduct>( supplierID : supplierID, accessToken : decodedToken.AccessToken ); ApiClient supplierClient = await _apiClientHelper.GetSupplierApiClient(supplierID, decodedToken.AccessToken); if (supplierClient == null) { throw new Exception($"Default supplier client not found. SupplierID: {supplierID}"); } var configToUse = new OrderCloudClientConfig { ApiUrl = decodedToken.ApiUrl, AuthUrl = decodedToken.AuthUrl, ClientId = supplierClient.ID, ClientSecret = supplierClient.ClientSecret, GrantType = GrantType.ClientCredentials, Roles = new[] { ApiRole.SupplierAdmin, ApiRole.ProductAdmin }, }; var ocClient = new OrderCloudClient(configToUse); await ocClient.AuthenticateAsync(); var token = ocClient.TokenResponse.AccessToken; foreach (var product in productsToUpdate) { product.xp.Facets["supplier"] = new List <string>() { supplier.Name }; product.xp.Currency = supplier.xp.Currency; } await Throttler.RunAsync(productsToUpdate, 100, 5, product => ocClient.Products.SaveAsync(product.ID, product, accessToken: token)); } return(updatedSupplier); }
public virtual async Task SendSingleTemplateEmailMultipleRcpts(string from, List <EmailAddress> tos, string templateID, object templateData) { Require.That(templateID != null, new ErrorCode("SendgridError", 501, "Required Sengrid template ID not configured in app settings")); { var fromEmail = new EmailAddress(from); var msg = MailHelper.CreateSingleTemplateEmailToMultipleRecipients(fromEmail, tos, templateID, templateData); var response = await _client.SendEmailAsync(msg); if (!response.IsSuccessStatusCode) { throw new Exception("Error sending sendgrid email"); } } }
public async Task SendSingleTemplateEmailMultipleRcptsAttachment(string from, List <EmailAddress> tos, string templateID, object templateData, CloudAppendBlob fileReference, string fileName) { Require.That(templateID != null, new ErrorCode("SendgridError", 501, "Required Sengrid template ID not configured in app settings")); { var fromEmail = new EmailAddress(from); var msg = MailHelper.CreateSingleTemplateEmailToMultipleRecipients(fromEmail, tos, templateID, templateData); using (var stream = await fileReference.OpenReadAsync()) { await msg.AddAttachmentAsync(fileName, stream); } var response = await _client.SendEmailAsync(msg); if (!response.IsSuccessStatusCode) { throw new Exception("Error sending sendgrid email"); } } }
private async Task ValidateOrderAsync(HSOrderWorksheet worksheet, OrderCloudIntegrationsCreditCardPayment payment, string userToken) { Require.That( !worksheet.Order.IsSubmitted, new ErrorCode("OrderSubmit.AlreadySubmitted", 400, "Order has already been submitted") ); var shipMethodsWithoutSelections = worksheet?.ShipEstimateResponse?.ShipEstimates?.Where(estimate => estimate.SelectedShipMethodID == null); Require.That( worksheet?.ShipEstimateResponse != null && shipMethodsWithoutSelections.Count() == 0, new ErrorCode("OrderSubmit.MissingShippingSelections", 400, "All shipments on an order must have a selection"), shipMethodsWithoutSelections ); Require.That( !worksheet.LineItems.Any() || payment != null, new ErrorCode("OrderSubmit.MissingPayment", 400, "Order contains standard line items and must include credit card payment details"), worksheet.LineItems ); var lineItemsInactive = await GetInactiveLineItems(worksheet, userToken); Require.That( !lineItemsInactive.Any(), new ErrorCode("OrderSubmit.InvalidProducts", 400, "Order contains line items for products that are inactive"), lineItemsInactive ); try { // ordercloud validates the same stuff that would be checked on order submit await _oc.Orders.ValidateAsync(OrderDirection.Incoming, worksheet.Order.ID); } catch (OrderCloudException ex) { // credit card payments aren't accepted yet, so ignore this error for now // we'll accept the payment once the credit card auth goes through (before order submit) var errors = ex.Errors.Where(ex => ex.ErrorCode != "Order.CannotSubmitWithUnaccceptedPayments"); if (errors.Any()) { throw new CatalystBaseException("OrderSubmit.OrderCloudValidationError", "Failed ordercloud validation, see Data for details", errors); } } }
private static string FindSortableProp(Type type, string path) { if (path.StartsWith("xp.")) { return(path); } var queue = new Queue <string>(path.Split('.')); var prop = type.GetProperty(queue.Dequeue(), BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); Require.That(prop != null, ErrorCodes.List.InvalidProperty, new InvalidPropertyError(type, path)); //TODO: evaluate this requirement for reference sake //Require.That(prop.HasAttribute<SortableAttribute>(), ErrorCodes.List.InvalidSortProperty, new InvalidPropertyError(type, path)); var result = prop?.Name; if (queue.Any()) { result += "." + FindSortableProp(prop.PropertyType, queue.JoinString(".")); } return(result); }
private void ValidateLineItemStatusChange(List <HSLineItem> previousLineItemStates, LineItemStatusChanges lineItemStatusChanges, VerifiedUserType userType) { /* need to validate 2 things on a lineitem status change * * 1) user making the request has the ability to make that line item change based on usertype * 2) there are sufficient amount of the previous quantities for each lineitem */ // 1) var allowedLineItemStatuses = LineItemStatusConstants.ValidLineItemStatusSetByUserType[userType]; Require.That(allowedLineItemStatuses.Contains(lineItemStatusChanges.Status), new ErrorCode("Not authorized to set this status on a lineItem", 400, $"Not authorized to set line items to {lineItemStatusChanges.Status}")); // 2) var areCurrentQuantitiesToSupportChange = lineItemStatusChanges.Changes.All(lineItemChange => { return(ValidateCurrentQuantities(previousLineItemStates, lineItemChange, lineItemStatusChanges.Status)); }); Require.That(areCurrentQuantitiesToSupportChange, new ErrorCode("Invalid lineItem status change", 400, $"Current lineitem quantity statuses on the order are not sufficient to support the requested change")); }
public async Task SendSingleTemplateEmailSingleRcptAttachment(string from, string to, string templateID, object templateData, IFormFile fileReference) { Require.That(templateID != null, new ErrorCode("SendgridError", 501, "Required Sengrid template ID not configured in app settings")); { var fromEmail = new EmailAddress(from); var toEmail = new EmailAddress(to); var msg = MailHelper.CreateSingleTemplateEmail(fromEmail, toEmail, templateID, templateData); if (fileReference != null) { using (var stream = fileReference.OpenReadStream()) { await msg.AddAttachmentAsync(fileReference.FileName, stream); } } var response = await _client.SendEmailAsync(msg); if (!response.IsSuccessStatusCode) { throw new Exception("Error sending sendgrid email"); } } }
public async Task <HSLineItem> UpsertLineItem(string orderID, HSLineItem liReq, DecodedToken decodedToken) { // get me product with markedup prices correct currency and the existing line items in parellel var productRequest = _meProductCommand.Get(liReq.ProductID, decodedToken); var existingLineItemsRequest = _oc.LineItems.ListAllAsync <HSLineItem>(OrderDirection.Outgoing, orderID, filters: $"Product.ID={liReq.ProductID}", accessToken: decodedToken.AccessToken); var orderRequest = _oc.Orders.GetAsync(OrderDirection.Incoming, orderID); await Task.WhenAll(productRequest, existingLineItemsRequest, orderRequest); var existingLineItems = await existingLineItemsRequest; var product = await productRequest; var order = await orderRequest; var li = new HSLineItem(); var markedUpPrice = ValidateLineItemUnitCost(orderID, product, existingLineItems, liReq); liReq.UnitPrice = await markedUpPrice; Require.That(!order.IsSubmitted, new ErrorCode("Invalid Order Status", 400, "Order has already been submitted")); liReq.xp.StatusByQuantity = LineItemStatusConstants.EmptyStatuses; liReq.xp.StatusByQuantity[LineItemStatus.Open] = liReq.Quantity; var preExistingLi = ((List <HSLineItem>)existingLineItems).Find(eli => LineItemsMatch(eli, liReq)); if (preExistingLi != null) { liReq.ID = preExistingLi.ID; //ensure we do not change the line item id when updating li = await _oc.LineItems.SaveAsync <HSLineItem>(OrderDirection.Incoming, orderID, preExistingLi.ID, liReq); } else { li = await _oc.LineItems.CreateAsync <HSLineItem>(OrderDirection.Incoming, orderID, liReq); } await _promotionCommand.AutoApplyPromotions(orderID); return(li); }
private async Task EnsureUserCanAccessOrder(HSOrder order, DecodedToken decodedToken) { /* ensures user has access to order through at least 1 of 3 methods * 1) user submitted the order * 2) user has access to all orders from the location of the billingAddressID * 3) the order is awaiting approval and the user is in the approving group */ var isOrderSubmitter = order.FromUser.Username == decodedToken.Username; if (isOrderSubmitter) { return; } var isUserInLocationOrderAccessGroup = await _locationPermissionCommand.IsUserInAccessGroup(order.BillingAddressID, UserGroupSuffix.ViewAllOrders.ToString(), decodedToken); if (isUserInLocationOrderAccessGroup) { return; } if (order.Status == OrderStatus.AwaitingApproval) { // logic assumes there is only one approving group per location var isUserInApprovalGroup = await _locationPermissionCommand.IsUserInAccessGroup(order.BillingAddressID, UserGroupSuffix.OrderApprover.ToString(), decodedToken); if (isUserInApprovalGroup) { return; } } // if function has not been exited yet we throw an insufficient access error Require.That(false, new ErrorCode("Insufficient Access", 403, $"User cannot access order {order.ID}")); }
public async Task EnsureUserIsLocationAdmin(string locationID, DecodedToken decodedToken) { var hasAccess = await IsUserInAccessGroup(locationID, UserGroupSuffix.PermissionAdmin.ToString(), decodedToken); Require.That(hasAccess, new ErrorCode("Insufficient Access", 403, $"User cannot manage permissions for: {locationID}")); }
private async Task EnsureUserCanManageLocationResaleCert(string locationID, DecodedToken decodedToken) { var hasAccess = await _locationPermissionCommand.IsUserInAccessGroup(locationID, UserGroupSuffix.ResaleCertAdmin.ToString(), decodedToken); Require.That(hasAccess, new ErrorCode("Insufficient Access", 403, $"User cannot manage resale certs for: {locationID}")); }
private async Task EnsureUserCanAccessLocationOrders(string locationID, DecodedToken decodedToken, string overrideErrorMessage = "") { var hasAccess = await _locationPermissionCommand.IsUserInAccessGroup(locationID, UserGroupSuffix.ViewAllOrders.ToString(), decodedToken); Require.That(hasAccess, new ErrorCode("Insufficient Access", 403, $"User cannot access orders from this location: {locationID}")); }