public void Acknowledge(Uri resourceUri, global::Nop.Core.Domain.Orders.Order order) { try { var connector = Connector.Create(_klarnaSettings.SharedSecret); var klarnaOrder = new Klarna.Checkout.Order(connector, resourceUri) { ContentType = ContentType }; klarnaOrder.Fetch(); var fetchedData = klarnaOrder.Marshal(); var typedData = KlarnaOrder.FromDictionary(fetchedData); if (typedData.Status == KlarnaOrder.StatusCheckoutComplete) { var updateData = new KlarnaOrder { Status = KlarnaOrder.StatusCreated, MerchantReference = new MerchantReference { OrderId1 = order.Id.ToString(CultureInfo.InvariantCulture), OrderId2 = order.OrderGuid.ToString() } }; var dictData = updateData.ToDictionary(); klarnaOrder.Update(dictData); } } catch (Exception ex) { throw new NopException("Error Acknowledging Klarna Order", ex); } }
public Uri Create() { var cart = _klarnaCheckoutUtils.GetCart(); var merchant = _klarnaCheckoutUtils.GetMerchant(); var supportedLocale = _klarnaCheckoutUtils.GetSupportedLocale(); var gui = _klarnaCheckoutUtils.GetGui(); var options = _klarnaCheckoutUtils.GetOptions(); var klarnaOrder = new KlarnaOrder { Cart = cart, Merchant = merchant, Gui = gui, Options = options, Locale = supportedLocale.Locale, PurchaseCountry = supportedLocale.PurchaseCountry, PurchaseCurrency = supportedLocale.PurchaseCurrency }; var dictData = klarnaOrder.ToDictionary(); var connector = Connector.Create(_klarnaSettings.SharedSecret); var order = new Klarna.Checkout.Order(connector) { BaseUri = new Uri(BaseUri), ContentType = ContentType }; order.Create(dictData); var location = order.Location; var kcoOrderRequest = GetKcoOrderRequest(_workContext.CurrentCustomer, location); _klarnaRepository.Insert(kcoOrderRequest); return location; }
public void SyncBillingAndShippingAddress(global::Nop.Core.Domain.Customers.Customer customer, KlarnaOrder klarnaOrder) { try { var billingAddress = klarnaOrder.BillingAddress; var shippingAddress = klarnaOrder.ShippingAddress; var nopBillingAddress = customer.Addresses.FirstOrDefault(billingAddress.RepresentsAddress); if (nopBillingAddress == null) { nopBillingAddress = new global::Nop.Core.Domain.Common.Address { CreatedOnUtc = DateTime.UtcNow }; customer.Addresses.Add(nopBillingAddress); } customer.BillingAddress = nopBillingAddress; billingAddress.CopyTo(nopBillingAddress); var nopShippingAddress = customer.Addresses.FirstOrDefault(shippingAddress.RepresentsAddress); if (nopShippingAddress == null) { nopShippingAddress = new global::Nop.Core.Domain.Common.Address { CreatedOnUtc = DateTime.UtcNow }; customer.Addresses.Add(nopShippingAddress); } customer.ShippingAddress = nopShippingAddress; shippingAddress.CopyTo(nopShippingAddress); _customerService.UpdateCustomer(customer); } catch (Exception ex) { var billing = JsonConvert.SerializeObject(klarnaOrder.BillingAddress); var shipping = JsonConvert.SerializeObject(klarnaOrder.ShippingAddress); throw new KlarnaCheckoutException(string.Format(CultureInfo.CurrentCulture, "Error syncing addresses. Billing: {0}, Shipping: {1}", billing, shipping), ex); } }
public bool Update(Uri resourceUri) { try { var cart = _klarnaCheckoutUtils.GetCart(); var options = _klarnaCheckoutUtils.GetOptions(); var connector = Connector.Create(_klarnaSettings.SharedSecret); var supportedLocale = _klarnaCheckoutUtils.GetSupportedLocale(); var klarnaOrder = new KlarnaOrder { Cart = cart, Options = options, Locale = supportedLocale.Locale, PurchaseCountry = supportedLocale.PurchaseCountry, PurchaseCurrency = supportedLocale.PurchaseCurrency }; var order = new Order(connector, resourceUri) { ContentType = ContentType }; var dictData = klarnaOrder.ToDictionary(); order.Update(dictData); return true; } catch (Exception ex) { var exceptionJson = JsonConvert.SerializeObject(ex.Data); _logger.Warning(string.Format(CultureInfo.CurrentCulture, "KlarnaCheckout: Error updating Klarna order. Will try to create a new one. ResourceURI: {0}, Data: {1}", resourceUri, exceptionJson), exception: ex); } return false; }
private Order CreateOrderAndSyncWithKlarna(KlarnaCheckoutEntity klarnaRequest, Customer customer, KlarnaOrder klarnaOrder, Uri resourceUri) { var processPaymentRequest = new ProcessPaymentRequest { OrderGuid = klarnaRequest.OrderGuid, CustomerId = customer.Id, StoreId = _storeContext.CurrentStore.Id, PaymentMethodSystemName = "Motillo.KlarnaCheckout" }; var placeOrderResult = _orderProcessingService.PlaceOrder(processPaymentRequest); // If you tamper with the cart after the klarna widget is rendered nop fails to create the order. if (!placeOrderResult.Success) { var errors = string.Join("; ", placeOrderResult.Errors); _logger.Error(string.Format(CultureInfo.CurrentCulture, "KlarnaCheckout: Klarna has been processed but order could not be created in Nop! Klarna ID={0}, ResourceURI={1}. Errors: {2}", klarnaOrder.Id, klarnaRequest.KlarnaResourceUri, errors), customer: customer); if (!_klarnaCheckoutPaymentService.CancelPayment(klarnaOrder.Reservation, customer)) { _logger.Error("KlarnaCheckout: Error canceling reservation: " + klarnaOrder.Reservation, customer: customer); } throw new KlarnaCheckoutException("Error creating order: " + errors); } // Order was successfully created. var orderId = placeOrderResult.PlacedOrder.Id; var nopOrder = _orderService.GetOrderById(orderId); _klarnaCheckoutPaymentService.Acknowledge(resourceUri, nopOrder); nopOrder.AuthorizationTransactionId = klarnaOrder.Reservation; _orderService.UpdateOrder(nopOrder); var orderTotalInCurrentCurrency = _currencyService.ConvertFromPrimaryStoreCurrency(nopOrder.OrderTotal, _workContext.WorkingCurrency); // We need to ensure the shopping cart wasn't tampered with before the user confirmed the Klarna order. // If so an order may have been created which doesn't represent the paid items. // Due to rounding when using prices contains more than 2 decimals (e.g. currency conversion), we allow // a slight diff in paid price and nop's reported price. // For example nop rounds the prices after _all_ cart item prices have been summed but when sending // items to Klarna, each price needs to be rounded separately (Klarna uses 2 decimals). // Assume a cart with two items. // 1.114 + 2.114 = 3.228 which nop rounds to 3.23. // 1.11 + 2.11 is sent to Klarna, which totals 3.22. var allowedPriceDiff = orderTotalInCurrentCurrency*0.01m; var diff = Math.Abs(orderTotalInCurrentCurrency - (klarnaOrder.Cart.TotalPriceIncludingTax.Value/100m)); if (diff < allowedPriceDiff) { nopOrder.OrderNotes.Add(new OrderNote { Note = "KlarnaCheckout: Order acknowledged. Uri: " + resourceUri, DisplayToCustomer = false, CreatedOnUtc = DateTime.UtcNow }); _orderService.UpdateOrder(nopOrder); if (_orderProcessingService.CanMarkOrderAsPaid(nopOrder)) { _orderProcessingService.MarkOrderAsPaid(nopOrder); // Sometimes shipping isn't required, e.g. if only ordering virtual gift cards. // In those cases, make sure the Klarna order is activated. if (nopOrder.OrderStatus == OrderStatus.Complete) { var activationResult = _klarnaCheckoutPaymentService.Activate(klarnaOrder.Reservation, customer); if (activationResult != null) { nopOrder.OrderNotes.Add(new OrderNote { Note = string.Format(CultureInfo.CurrentCulture, "KlarnaCheckout: Order complete after payment, activating Klarna order. Reservation: {0}, RiskStatus: {1}, InvoiceNumber: {2}", klarnaOrder.Reservation, activationResult.RiskStatus, activationResult.InvoiceNumber), CreatedOnUtc = DateTime.UtcNow, DisplayToCustomer = false }); _orderService.UpdateOrder(nopOrder); klarnaRequest.Status = KlarnaCheckoutStatus.Activated; _repository.Update(klarnaRequest); _logger.Information(string.Format(CultureInfo.CurrentCulture, "KlarnaCheckout: Order complete after payment, activating Klarna order. OrderId: {0}, OrderGuid: {1}", nopOrder.Id, nopOrder.OrderGuid)); } } } } else { var orderTotalInCents = _klarnaCheckoutHelper.ConvertToCents(orderTotalInCurrentCurrency); nopOrder.OrderNotes.Add(new OrderNote { Note = string.Format(CultureInfo.CurrentCulture, "KlarnaCheckout: Order total differs from Klarna order. OrderTotal: {0}, OrderTotalInCents: {1}, KlarnaTotal: {2}, AllowedDiff: {3}, Diff: {4}, Uri: {5}", orderTotalInCurrentCurrency, orderTotalInCents, klarnaOrder.Cart.TotalPriceIncludingTax, allowedPriceDiff, diff, resourceUri), DisplayToCustomer = false, CreatedOnUtc = DateTime.UtcNow }); nopOrder.OrderNotes.Add(new OrderNote { Note = "KlarnaCheckout: Order total differs from paid amount to Klarna. Order has not been marked as paid. Please contact an administrator.", DisplayToCustomer = true, CreatedOnUtc = DateTime.UtcNow }); _orderService.UpdateOrder(nopOrder); _logger.Warning(string.Format(CultureInfo.CurrentCulture, "KlarnaCheckout: Order total differs. Please verify the sum and mark the order as paid in the administration. See order notes for more details. OrderId: {0}, OrderGuid: {1}", nopOrder.Id, nopOrder.OrderGuid), customer: customer); } return nopOrder; }
private void SyncKlarnaAndNopOrder(KlarnaCheckoutEntity klarnaRequest, Customer customer, out Order nopOrder, out KlarnaOrder klarnaOrder) { nopOrder = _orderService.GetOrderByGuid(klarnaRequest.OrderGuid); var resourceUri = new Uri(klarnaRequest.KlarnaResourceUri); var order = _klarnaCheckoutPaymentService.Fetch(resourceUri); var data = order.Marshal(); var jsonData = JsonConvert.SerializeObject(data); klarnaOrder = JsonConvert.DeserializeObject<Models.KlarnaOrder>(jsonData); // Create the order if it doesn't exist in nop. According to the Klarna Checkout // developer guidelines, one should only create the order if the status is // 'checkout_complete'. // https://developers.klarna.com/en/klarna-checkout/acknowledge-an-order if (nopOrder == null && klarnaOrder.Status == KlarnaOrder.StatusCheckoutComplete) { if (klarnaOrder.Status == KlarnaOrder.StatusCheckoutComplete) { klarnaRequest.Status = KlarnaCheckoutStatus.Complete; _repository.Update(klarnaRequest); } _klarnaCheckoutPaymentService.SyncBillingAndShippingAddress(customer, klarnaOrder); nopOrder = CreateOrderAndSyncWithKlarna(klarnaRequest, customer, klarnaOrder, resourceUri); } }