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);
            }
        }