Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class CreateCustomerTokenRequest : KlarnaApiRequest
{
// Properties based on Klarna API for generating a customer token
[JsonProperty("purchase_country")]
public string PurchaseCountry { get; set; }

[JsonProperty("purchase_currency")]
public string PurchaseCurrency { get; set; }

[JsonProperty("locale")]
public string Locale { get; set; }

[JsonProperty("description")]
public string Description { get; set; }

[JsonProperty("intended_use")] // e.g. SUBSCRIPTION
public string IntendedUse { get; set; }

[JsonProperty("merchant_urls")]
public KlarnaMerchantUrls MerchantUrls { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class CreateOrderRequest : KlarnaApiRequest
{
// Properties based on Klarna API for creating an order
// Often similar to CreateSessionRequest but might include authorization_token
[JsonProperty("purchase_country")]
public string PurchaseCountry { get; set; }

[JsonProperty("purchase_currency")]
public string PurchaseCurrency { get; set; }

[JsonProperty("locale")]
public string Locale { get; set; }

[JsonProperty("order_amount")]
public long OrderAmount { get; set; }

[JsonProperty("order_tax_amount")]
public long OrderTaxAmount { get; set; }

[JsonProperty("order_lines")]
public KlarnaOrderLine[] OrderLines { get; set; }

// Billing and shipping addresses, customer details etc.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class CreateOrderResponse : KlarnaApiResponse
{
// Properties based on Klarna API response for creating an order
// e.g., OrderId, RedirectUrl, FraudStatus etc.
[JsonProperty("order_id")]
public string OrderId { get; set; }

[JsonProperty("redirect_url")]
public string RedirectUrl { get; set; }

[JsonProperty("fraud_status")]
public string FraudStatus { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class CreateSessionRequest : KlarnaApiRequest
{
// Properties based on Klarna API for creating a session
// e.g., OrderAmount, OrderLines, PurchaseCountry, Currency, Locale, MerchantUrls etc.
[JsonProperty("purchase_country")]
public string PurchaseCountry { get; set; }

[JsonProperty("purchase_currency")]
public string PurchaseCurrency { get; set; }

[JsonProperty("locale")]
public string Locale { get; set; }

[JsonProperty("order_amount")]
public long OrderAmount { get; set; }

[JsonProperty("order_tax_amount")]
public long OrderTaxAmount { get; set; }

[JsonProperty("order_lines")]
public KlarnaOrderLine[] OrderLines { get; set; }

[JsonProperty("merchant_urls")]
public KlarnaMerchantUrls MerchantUrls { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class CreateSessionResponse : KlarnaApiResponse
{
// Properties based on Klarna API response for creating a session
// e.g., SessionId, ClientToken, PaymentMethodCategories etc.
[JsonProperty("session_id")]
public string SessionId { get; set; }

[JsonProperty("client_token")]
public string ClientToken { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class CustomerTokenResponse : KlarnaApiResponse
{
[JsonProperty("token_id")]
public string TokenId { get; set; }

[JsonProperty("redirect_url")]
public string RedirectUrl { get; set; }
}
}
10 changes: 10 additions & 0 deletions src/Smartstore.Modules/Smartstore.Klarna/Client/KlarnaApiConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Smartstore.Klarna.Client
{
// Placeholder for Klarna API Configuration
public class KlarnaApiConfig
{
public string ApiUrl { get; set; } // e.g., "https://api.klarna.com/" or "https://api.playground.klarna.com/"
public string ApiKey { get; set; } // Username or Merchant ID
public string ApiSecret { get; set; } // Password or Shared Secret
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace Smartstore.Klarna.Client
{
public class KlarnaApiException : Exception
{
public KlarnaApiException(string message) : base(message)
{
}

public KlarnaApiException(string message, Exception innerException) : base(message, innerException)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Smartstore.Klarna.Client
{
public class KlarnaApiRequest
{
// TODO: Implement request properties
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Smartstore.Klarna.Client
{
public class KlarnaApiResponse
{
// TODO: Implement response properties
}
}
111 changes: 111 additions & 0 deletions src/Smartstore.Modules/Smartstore.Klarna/Client/KlarnaHttpClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Smartstore.Klarna.Client
{
public class KlarnaHttpClient
{
private readonly HttpClient _httpClient;
private readonly KlarnaApiConfig _apiConfig; // Assuming a config class for API key, secret, and base URL

public KlarnaHttpClient(HttpClient httpClient, KlarnaApiConfig apiConfig)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_apiConfig = apiConfig ?? throw new ArgumentNullException(nameof(apiConfig));

_httpClient.BaseAddress = new Uri(_apiConfig.ApiUrl); // e.g., "https://api.klarna.com/"
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Basic Authentication: username:password -> base64
var authToken = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{_apiConfig.ApiKey}:{_apiConfig.ApiSecret}"));
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authToken);
}

// Example: Create Credit Session
public async Task<CreateSessionResponse> CreateCreditSessionAsync(CreateSessionRequest request)
{
var requestUri = "payments/v1/sessions"; // Example endpoint
return await SendRequestAsync<CreateSessionResponse>(HttpMethod.Post, requestUri, request);
}

// Example: Update Credit Session
public async Task UpdateCreditSessionAsync(string sessionId, UpdateSessionRequest request)
{
var requestUri = $"payments/v1/sessions/{sessionId}"; // Example endpoint
await SendRequestAsync<object>(HttpMethod.Post, requestUri, request); // No specific response body for update, or define one if needed
}

// Example: Get Session Details
public async Task<SessionDetailsResponse> GetCreditSessionAsync(string sessionId)
{
var requestUri = $"payments/v1/sessions/{sessionId}"; // Example endpoint
return await SendRequestAsync<SessionDetailsResponse>(HttpMethod.Get, requestUri);
}

// Example: Create Order
public async Task<CreateOrderResponse> CreateOrderAsync(string authorizationToken, CreateOrderRequest request)
{
var requestUri = $"ordermanagement/v1/orders"; // Example endpoint for Klarna Order Management API
// For order creation, Klarna might require the authorization_token from the payment session
// This might need a different setup or HttpClient instance if the base URL or auth changes for OMS API
return await SendRequestAsync<CreateOrderResponse>(HttpMethod.Post, requestUri, request);
}

// Example: Generate Customer Token
public async Task<CustomerTokenResponse> GenerateCustomerTokenAsync(CreateCustomerTokenRequest request)
{
var requestUri = "customer-token/v1/tokens"; // Example endpoint
return await SendRequestAsync<CustomerTokenResponse>(HttpMethod.Post, requestUri, request);
}

private async Task<TResponse> SendRequestAsync<TResponse>(HttpMethod method, string requestUri, object requestData = null)
{
var requestMessage = new HttpRequestMessage(method, requestUri);

if (requestData != null)
{
var jsonRequest = JsonConvert.SerializeObject(requestData);
requestMessage.Content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
}

HttpResponseMessage responseMessage = await _httpClient.SendAsync(requestMessage);

if (!responseMessage.IsSuccessStatusCode)
{
var errorContent = await responseMessage.Content.ReadAsStringAsync();
// TODO: Deserialize errorContent into a KlarnaError object if Klarna provides a standard error format
throw new KlarnaApiException($"Klarna API request failed with status code {responseMessage.StatusCode}: {errorContent}");
}

var jsonResponse = await responseMessage.Content.ReadAsStringAsync();
if (string.IsNullOrWhiteSpace(jsonResponse) && typeof(TResponse) != typeof(object))
{
// Handle cases where Klarna might return an empty body for success (e.g., 204 No Content)
// and TResponse is not expecting it (e.g. not 'object' or a specific 'EmptyResponse' type)
if (responseMessage.StatusCode == System.Net.HttpStatusCode.NoContent)
{
return default; // Or throw, or return a specific object indicating success with no content
}
throw new KlarnaApiException("Received an empty response from Klarna API when content was expected.");
}

// If TResponse is object, it means we don't expect a specific response body or it's handled by status code.
if (typeof(TResponse) == typeof(object))
{
return default; // Or a new object(), depending on how you want to signal this.
}

try
{
return JsonConvert.DeserializeObject<TResponse>(jsonResponse);
}
catch (JsonException ex)
{
throw new KlarnaApiException($"Failed to deserialize Klarna API response: {ex.Message}", ex);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class KlarnaMerchantUrls
{
[JsonProperty("confirmation")]
public string Confirmation { get; set; }

[JsonProperty("notification")]
public string Notification { get; set; }

[JsonProperty("push")]
public string Push { get; set; }
}
}
28 changes: 28 additions & 0 deletions src/Smartstore.Modules/Smartstore.Klarna/Client/KlarnaOrderLine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class KlarnaOrderLine
{
[JsonProperty("type")]
public string Type { get; set; } // e.g. "physical", "digital", "shipping_fee"

[JsonProperty("name")]
public string Name { get; set; }

[JsonProperty("quantity")]
public int Quantity { get; set; }

[JsonProperty("unit_price")]
public long UnitPrice { get; set; } // In cents

[JsonProperty("total_amount")]
public long TotalAmount { get; set; } // In cents

[JsonProperty("tax_rate")]
public long TaxRate { get; set; } // In percent * 100, e.g., 2500 for 25%

[JsonProperty("total_tax_amount")]
public long TotalTaxAmount { get; set; } // In cents
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class SessionDetailsResponse : KlarnaApiResponse
{
// Properties for session details
// e.g., Status, OrderAmount, OrderLines, ClientToken, ExpiresAt etc.
[JsonProperty("session_id")]
public string SessionId { get; set; }

[JsonProperty("client_token")]
public string ClientToken { get; set; }

[JsonProperty("status")]
public string Status { get; set; }

[JsonProperty("order_amount")]
public long OrderAmount { get; set; }

[JsonProperty("order_lines")]
public KlarnaOrderLine[] OrderLines { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Newtonsoft.Json;

namespace Smartstore.Klarna.Client
{
public class UpdateSessionRequest : KlarnaApiRequest
{
// Properties for updating a session
// e.g., OrderAmount, OrderLines etc. (similar to CreateSessionRequest)
[JsonProperty("order_amount")]
public long OrderAmount { get; set; }

[JsonProperty("order_lines")]
public KlarnaOrderLine[] OrderLines { get; set; }
}
}
Loading