390 lines
16 KiB
C#
390 lines
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Headers;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using gehGassi.Core.Interfaces;
|
|
using gehGassi.Domain.Common;
|
|
using gehGassi.Klarna.Checkout;
|
|
using gehGassi.Klarna.OrderManagement;
|
|
using Microsoft.Extensions.Options;
|
|
using Newtonsoft.Json;
|
|
using Address = gehGassi.Klarna.Checkout.Address;
|
|
using Order = gehGassi.Klarna.Checkout.Order;
|
|
using OrderLine = gehGassi.Klarna.Checkout.OrderLine;
|
|
using ShippingOption = gehGassi.Klarna.Checkout.ShippingOption;
|
|
|
|
namespace gehGassi.External.Services
|
|
{
|
|
/// <summary>
|
|
/// Service der alle benötigten Klarna-Funktionen zur Verfügung stellt
|
|
/// </summary>
|
|
public class KlarnaService : IKlarnaService
|
|
{
|
|
private HttpClient _httpClient;
|
|
private readonly IOrderService _orderService;
|
|
private readonly ICustomerService _customerService;
|
|
private readonly IShopSettingsService _shopSettingsService;
|
|
private readonly IOptions<ShopOptions> _shopOptions;
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
/// <param name="orderService">Instanz eines IOrderService</param>
|
|
/// <param name="customerService">Instanz eines ICustomerService</param>
|
|
/// <param name="shopSettingsService">Instanz eines IShopSettingsService</param>
|
|
/// <param name="shopOptions">Instanz von ShopOptions</param>
|
|
public KlarnaService(IOrderService orderService, ICustomerService customerService, IShopSettingsService shopSettingsService, IOptions<ShopOptions> shopOptions)
|
|
{
|
|
_orderService = orderService;
|
|
_customerService = customerService;
|
|
_shopSettingsService = shopSettingsService;
|
|
_shopOptions = shopOptions;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt einen HTTP-Client für die Kommunikation mit Klarna zurück
|
|
/// </summary>
|
|
/// <returns>HttpClient</returns>
|
|
internal HttpClient GetHttpClient(string username, string password)
|
|
{
|
|
if (_httpClient == null)
|
|
{
|
|
var baseUri = _shopOptions.Value.KlarnaBaseUrl;
|
|
if (_shopOptions.Value.KlarnaUsePlayground)
|
|
baseUri = _shopOptions.Value.KlarnaPlaygroundUrl;
|
|
|
|
_httpClient = new HttpClient()
|
|
{
|
|
BaseAddress = new Uri(baseUri)
|
|
};
|
|
var authString = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
|
|
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authString);
|
|
}
|
|
return _httpClient;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Prüft ob die Klarna Credentials korrekt sind und eine Verbindung zur Api aufgenommen werden kann
|
|
/// </summary>
|
|
/// <param name="username">Klarna Benutzername</param>
|
|
/// <param name="password">Klarna Passwort</param>
|
|
/// <returns>true wenn erfolgreich, false sonst</returns>
|
|
public async Task<bool> TestCredentialsAsync(string username, string password)
|
|
{
|
|
var client = GetHttpClient(username, password);
|
|
|
|
var result = await client.PostAsync("checkout/v3/orders", new StringContent(""));
|
|
return result.StatusCode != HttpStatusCode.Unauthorized;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erstellen einer Klarna-Order
|
|
/// </summary>
|
|
/// <param name="orderId">Id der simple print Bestellung</param>
|
|
/// <param name="username">Klarna Benutzername</param>
|
|
/// <param name="password">Klarna Passwort</param>
|
|
/// <param name="termsUrl">Redirect-Url für AGB</param>
|
|
/// <param name="checkoutUrl">Url für Checkout</param>
|
|
/// <param name="confirmationUrl">Bestätigungs-URL</param>
|
|
/// <param name="pushUrl">URL für Push-Notifications</param>
|
|
/// <returns>Klarna Order oder null, wenn ein Fehler aufgetreten ist</returns>
|
|
public async Task<Order> CreateOrderAsync(long orderId, string username, string password, string termsUrl, string checkoutUrl, string confirmationUrl, string pushUrl)
|
|
{
|
|
var client = GetHttpClient(username, password);
|
|
|
|
var order = await _orderService.GetAsync(orderId);
|
|
if (order == null)
|
|
return null;
|
|
|
|
var orderItems = await _orderService.GetItemsAsync(orderId);
|
|
if (!orderItems.Any())
|
|
return null;
|
|
|
|
var customer = await _customerService.GetAsync(order.CustomerId.Value);
|
|
if (customer == null)
|
|
return null;
|
|
|
|
var shopSettings = await _shopSettingsService.GetAsync();
|
|
|
|
//TODO: Locale noch behandeln
|
|
var klarnaOrder = new Order
|
|
{
|
|
PurchaseCountry = _shopOptions.Value.SourceCountry.ToUpperInvariant(),
|
|
PurchaseCurrency = "EUR",
|
|
Locale = "de-AT",
|
|
OrderAmount = (int)((order.TotalGross - order.ShipmentGross) * 100),
|
|
OrderTaxAmount = (int)(((order.TotalGross - order.ShipmentGross) - (order.Total - order.Shipment)) * 100),
|
|
OrderLines = new List<OrderLine>()
|
|
};
|
|
|
|
var shippingTaxRate = (int)(((Math.Round(order.ShipmentGross / (order.Shipment > 0 ? order.Shipment : 1), 2, MidpointRounding.AwayFromZero) - 1) * 100) * 100);
|
|
if (shippingTaxRate < 0)
|
|
shippingTaxRate = 0;
|
|
var shippingOption = new ShippingOption()
|
|
{
|
|
Id = "postal",
|
|
Name = "POST",
|
|
Price = (int)((order.ShipmentGross) * 100),
|
|
Preselected = true,
|
|
TaxAmount = (int)(((order.ShipmentGross - order.Shipment)) * 100),
|
|
TaxRate = shippingTaxRate
|
|
};
|
|
|
|
klarnaOrder.ShippingOptions = new List<ShippingOption>();
|
|
klarnaOrder.ShippingOptions.Add(shippingOption);
|
|
klarnaOrder.SelectedShippingOption = shippingOption;
|
|
|
|
klarnaOrder.BillingAddress = new Address()
|
|
{
|
|
GivenName = order.BillingAddress.FirstName,
|
|
FamilyName = order.BillingAddress.LastName,
|
|
OrganizationName = order.BillingAddress.Company,
|
|
StreetAddress = order.BillingAddress.AddressLine1,
|
|
StreetAddress2 = order.BillingAddress.AddressLine2,
|
|
PostalCode = order.BillingAddress.Zip,
|
|
City = order.BillingAddress.City,
|
|
Region = order.BillingAddress.State,
|
|
Country = order.BillingAddress.CountryCode
|
|
};
|
|
|
|
klarnaOrder.ShippingAddress = new Address()
|
|
{
|
|
GivenName = order.DeliveryAddress.FirstName,
|
|
FamilyName = order.DeliveryAddress.LastName,
|
|
OrganizationName = order.DeliveryAddress.Company,
|
|
StreetAddress = order.DeliveryAddress.AddressLine1,
|
|
StreetAddress2 = order.DeliveryAddress.AddressLine2,
|
|
PostalCode = order.DeliveryAddress.Zip,
|
|
City = order.DeliveryAddress.City,
|
|
Region = order.DeliveryAddress.State,
|
|
Country = order.DeliveryAddress.CountryCode
|
|
};
|
|
|
|
foreach (var orderItem in orderItems)
|
|
{
|
|
var taxRate = (int)(((Math.Round(orderItem.PriceGross / (orderItem.Price > 0 ? orderItem.Price : 1), 2, MidpointRounding.AwayFromZero) - 1) * 100) * 100);
|
|
if (taxRate < 0)
|
|
taxRate = 0;
|
|
var klarnaOrderLine = new OrderLine
|
|
{
|
|
Type = "physical",
|
|
Reference = orderItem.Sku,
|
|
Name = orderItem.Name,
|
|
Quantity = orderItem.Quantity,
|
|
UnitPrice = (int)(orderItem.PriceGross * 100),
|
|
TaxRate = taxRate,
|
|
TotalAmount = (int)(orderItem.TotalGross * 100),
|
|
TotalTaxAmount = (int)((orderItem.TotalGross - orderItem.Total) * 100)
|
|
};
|
|
klarnaOrderLine.Type = orderItem.ProductType switch
|
|
{
|
|
ProductType.Physical => "physical",
|
|
ProductType.Download => "digital",
|
|
ProductType.Listing => "digital",
|
|
ProductType.Advertisement => "digital",
|
|
ProductType.Pin => "digital",
|
|
ProductType.Banner => "digital",
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
};
|
|
klarnaOrder.OrderLines.Add(klarnaOrderLine);
|
|
}
|
|
|
|
klarnaOrder.MerchantUrls = new MerchantUrls()
|
|
{
|
|
Terms = termsUrl,
|
|
Checkout = checkoutUrl,
|
|
Confirmation = confirmationUrl,
|
|
Push = pushUrl
|
|
};
|
|
|
|
var jsonOrder = JsonConvert.SerializeObject(klarnaOrder);
|
|
|
|
var result = await client.PostAsync("checkout/v3/orders", new StringContent(jsonOrder, Encoding.UTF8, "application/json"));
|
|
if (result.IsSuccessStatusCode)
|
|
{
|
|
var responseJson = await result.Content.ReadAsStringAsync();
|
|
var response = JsonConvert.DeserializeObject<Order>(responseJson);
|
|
|
|
order.KlarnaOrderId = response.OrderId;
|
|
order.PaymentStatus = PaymentStatus.Pending;
|
|
order.PaymentInfo = $"{DateTime.UtcNow}: Klarna order created. OrderId: {response.OrderId}" + System.Environment.NewLine + order.PaymentInfo;
|
|
await _orderService.CommitAsync("klarna");
|
|
|
|
return response;
|
|
}
|
|
else
|
|
{
|
|
var response = await result.Content.ReadAsStringAsync();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Abfragen einer Klarna-Order
|
|
/// </summary>
|
|
/// <param name="klarnaOrderId">Klarna Order ID</param>
|
|
/// <param name="username">Klarna Benutzername</param>
|
|
/// <param name="password">Klarna Passwort</param>
|
|
/// <returns>Klarna Order oder null, wenn nicht gefunden</returns>
|
|
public async Task<Order> GetOrderAsync(string klarnaOrderId, string username, string password)
|
|
{
|
|
var client = GetHttpClient(username, password);
|
|
|
|
var result = await client.GetAsync($"checkout/v3/orders/{klarnaOrderId}");
|
|
if (result.IsSuccessStatusCode)
|
|
{
|
|
var responseJson = await result.Content.ReadAsStringAsync();
|
|
var response = JsonConvert.DeserializeObject<Order>(responseJson);
|
|
|
|
//order.KlarnaOrderId = response.OrderId;
|
|
//order.PaymentStatus = PaymentStatus.Pending;
|
|
//await _orderService.CommitAsync("klarna");
|
|
|
|
if (response.Status == "checkout_complete")
|
|
{
|
|
return response;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var response = await result.Content.ReadAsStringAsync();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Abfragen einer Klarna-Order - OrderManagement API
|
|
/// </summary>
|
|
/// <param name="klarnaOrderId">Klarna Order ID</param>
|
|
/// <param name="username">Klarna Benutzername</param>
|
|
/// <param name="password">Klarna Passwort</param>
|
|
/// <returns>Klarna Order oder null, wenn nicht gefunden</returns>
|
|
public async Task<Klarna.OrderManagement.Order> GetManagementOrderAsync(string klarnaOrderId, string username, string password)
|
|
{
|
|
var client = GetHttpClient(username, password);
|
|
|
|
var result = await client.GetAsync($"ordermanagement/v1/orders/{klarnaOrderId}");
|
|
if (result.IsSuccessStatusCode)
|
|
{
|
|
var responseJson = await result.Content.ReadAsStringAsync();
|
|
var response = JsonConvert.DeserializeObject<Klarna.OrderManagement.Order>(responseJson);
|
|
|
|
return response;
|
|
}
|
|
else
|
|
{
|
|
var response = await result.Content.ReadAsStringAsync();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bestätigen einer Klarna-Order - OrderManagement API
|
|
/// </summary>
|
|
/// <param name="klarnaOrderId">Klarna Order ID</param>
|
|
/// <param name="username">Klarna Benutzername</param>
|
|
/// <param name="password">Klarna Passwort</param>
|
|
/// <returns>Klarna Order oder null, wenn nicht gefunden</returns>
|
|
public async Task<bool> AcknowledgeOrderAsync(string klarnaOrderId, string username, string password)
|
|
{
|
|
var client = GetHttpClient(username, password);
|
|
|
|
var result = await client.PostAsync($"ordermanagement/v1/orders/{klarnaOrderId}/acknowledge", new StringContent(""));
|
|
if (result.IsSuccessStatusCode)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
var response = await result.Content.ReadAsStringAsync();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bestätigen des Versands einer Bestellung - OrderManagement API
|
|
/// </summary>
|
|
/// <param name="orderId">Order ID</param>
|
|
/// <param name="username">Klarna Benutzername</param>
|
|
/// <param name="password">Klarna Passwort</param>
|
|
/// <returns>true wenn erfolgreich, false sonst</returns>
|
|
public async Task<CaptureOrderResult> CaptureOrderAsync(long orderId, string username, string password)
|
|
{
|
|
var result = CaptureOrderResult.Success;
|
|
var client = GetHttpClient(username, password);
|
|
|
|
var order = await _orderService.GetAsync(orderId);
|
|
if (order != null)
|
|
{
|
|
var orderItems = await _orderService.GetItemsAsync(orderId);
|
|
if (orderItems.Any())
|
|
{
|
|
try
|
|
{
|
|
var klarnaOrder = await GetManagementOrderAsync(order.KlarnaOrderId, username, password);
|
|
if (klarnaOrder != null)
|
|
{
|
|
if (klarnaOrder.RemainingAuthorizedAmount is > 0)
|
|
{
|
|
var capture = new Capture() { CapturedAmount = klarnaOrder.RemainingAuthorizedAmount };
|
|
var jsonOrder = JsonConvert.SerializeObject(capture);
|
|
|
|
try
|
|
{
|
|
var httpResult = await client.PostAsync($"ordermanagement/v1/orders/{order.KlarnaOrderId}/captures", new StringContent(jsonOrder, Encoding.UTF8, "application/json"));
|
|
if (httpResult.IsSuccessStatusCode)
|
|
{
|
|
result = CaptureOrderResult.Success;
|
|
}
|
|
else
|
|
{
|
|
if (httpResult.StatusCode == HttpStatusCode.Forbidden)
|
|
result = CaptureOrderResult.CaptureNotAllowed;
|
|
if (httpResult.StatusCode == HttpStatusCode.NotFound)
|
|
result = CaptureOrderResult.OrderNotFound;
|
|
else
|
|
result = CaptureOrderResult.AuthorizationFailed;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
result = CaptureOrderResult.Timeout;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = CaptureOrderResult.RemainingAuthorizedAmount;
|
|
}
|
|
}
|
|
else
|
|
result = CaptureOrderResult.OrderNotFound;
|
|
}
|
|
catch
|
|
{
|
|
result = CaptureOrderResult.Timeout;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = CaptureOrderResult.OrderNotFound;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = CaptureOrderResult.OrderNotFound;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|