Skip to content

Commit

Permalink
Complete create new transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
illunix committed Dec 16, 2023
1 parent 06216aa commit a422fe6
Show file tree
Hide file tree
Showing 16 changed files with 187 additions and 83 deletions.
15 changes: 8 additions & 7 deletions samples/Axepta.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,33 @@
builder.Services
.AddEndpointsApiExplorer()
.AddSwaggerGen()
.AddAxeptaPaywall();
.AddAxeptaPaywall(builder.Configuration);

var app = builder.Build();

var payment = app.MapGroup("payments");
var paymentEndpoints = app.MapGroup("payments");

payment.MapPost(
paymentEndpoints.MapPost(
"/",
async (
IAxepta axepta,
CancellationToken ct
) =>
{
await axepta.CreatePaymentAsync(
var payment = await axepta.CreatePaymentAsync(
new()
{
Type = PaymentType.Sale,
ServiceId = "62f574ed-d4ad-4a7e-9981-89ed7284aaba",
ServiceId = "eff3207f-d2a0-4560-99ce-bba83267c90b",
Amount = 100,
Currency = "PLN",
OrderId = "123456789",
PaymentMethod = PaymentMethod.Pbl,
PaymentMethodChannel = "pbl",
PaymentMethodChannel = PaymentMethodChannel.Ipko,
SuccessReturnUrl = "https://example.com/success",
FailureReturnUrl = "https://example.com/failure",
ReturnUrl = "https://example.com",
ClientIp = "192.168.10.2",
Customer = new()
{
Id = "123",
Expand All @@ -44,7 +45,7 @@ await axepta.CreatePaymentAsync(
ct
);
return Results.Ok();
return Results.Ok(payment);
}
);

Expand Down
9 changes: 4 additions & 5 deletions samples/Axepta.Sample/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
"axepta-paywall": {
"merchantId": "ir49nkdgnuex458f6wnq",
"authToken": "ttfc9ve4zeseca4egs0pguk15c3yckkwf7d1n1ts8e55y5hs68886ujt76z5glbl",
"sandbox": true
}
}
12 changes: 5 additions & 7 deletions samples/Axepta.Sample/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"axepta-paywall": {
"merchantId": "ir49nkdgnuex458f6wnq",
"authToken": "ttfc9ve4zeseca4egs0pguk15c3yckkwf7d1n1ts8e55y5hs68886ujt76z5glbl",
"sandbox": false
}
}
6 changes: 3 additions & 3 deletions src/Axepta.SDK/Attributes/RequiredIfAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Axepta.SDK.Attributes;

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
internal class RequiredIfAttribute : ValidationAttribute
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
internal sealed class RequiredIfAttribute : ValidationAttribute
{
private readonly string _propertyName;
private readonly object _desiredValue;
Expand All @@ -16,7 +16,7 @@ object desiredValue
}

protected override ValidationResult? IsValid(
object value,
object? value,
ValidationContext validationContext
)
{
Expand Down
16 changes: 14 additions & 2 deletions src/Axepta.SDK/Entities/Request/Payment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
/// </summary>
public sealed record Payment
{
private int _amount;

/// <summary>
/// Gets or initializes the type of payment.
/// </summary>
Expand All @@ -21,7 +23,11 @@ public sealed record Payment
/// Gets or initializes the amount of the payment.
/// </summary>
[JsonPropertyName("amount")]
public required int Amount { get; init; }
public required int Amount
{
get => _amount;
set => _amount = value * 100;
}

/// <summary>
/// Gets or initializes the currency code for the payment amount.
Expand All @@ -48,7 +54,7 @@ public sealed record Payment
/// Gets or initializes the payment method channel for the transaction.
/// </summary>
[JsonPropertyName("paymentMethodChannel")]
public required string PaymentMethodChannel { get; init; }
public required PaymentMethodChannel PaymentMethodChannel { get; init; }

/// <summary>
/// Gets or initializes the URL to redirect to after a successful payment.
Expand Down Expand Up @@ -80,6 +86,12 @@ public sealed record Payment
[JsonPropertyName("customer")]
public required Customer Customer { get; init; }

/// <summary>
/// Gets or initializes client ip address associated with the user.
/// </summary>
[JsonPropertyName("clientIp")]
public required string ClientIp { get; init; }

/// <summary>
/// Gets or initializes the title associated with the payment. Can be null.
/// </summary>
Expand Down
7 changes: 5 additions & 2 deletions src/Axepta.SDK/Entities/Response/Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
public sealed record Data
{
[JsonPropertyName("transaction")]
public required Transaction Transaction { get; init; }
public Transaction? Transaction { get; init; }

[JsonPropertyName("action")]
public required Action Action { get; init; }
public Action? Action { get; init; }

[JsonPropertyName("validatorErrors")]
public IReadOnlyList<ValidationError>? ValidationErrors { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Axepta.SDK.Entities.Response;

public sealed class CreatePaymentResponse
public sealed class ResponseRoot
{
[JsonPropertyName("status")]
public required string Status { get; set; }
Expand Down
21 changes: 21 additions & 0 deletions src/Axepta.SDK/Entities/Response/ValidationError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Axepta.SDK.Entities.Response;

public sealed record ValidationError
{
private readonly string? _property;

[JsonPropertyName("property")]
public required string Property
{
get => _property!;
init => _property = value
.Replace(
"instance.",
string.Empty
)
.FirstCharToUpper();
}

[JsonPropertyName("message")]
public required string Message { get; init; }
}
34 changes: 34 additions & 0 deletions src/Axepta.SDK/Enums/PaymentMethodChannel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace Axepta.SDK.Enums;

public enum PaymentMethodChannel
{
#region pbs
Bnpparibas,
Mtransfer,
Bzwbk,
Pekao24,
Inteligo,
Ing,
Ipko,
Getin,
CreditAgricole,
Alior,
Pbs,
Millennium,
Citi,
Bos,
Pocztowy,
Plusbank,
Bs,
Bspb,
Nest,
#endregion
#region card
Ecom3ds,
Oneclick,
Recurring,
#endregion
#region Blik
Blik
#endregion
}
16 changes: 16 additions & 0 deletions src/Axepta.SDK/Exceptions/AxeptaException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,20 @@
internal sealed class AxeptaException : Exception
{
public AxeptaException(string msg) : base(msg) { }

public AxeptaException(IReadOnlyList<ValidationError>? validationErrors) : base(CreateValidationExceptionMessage(validationErrors)) { }

private static string CreateValidationExceptionMessage(IReadOnlyList<ValidationError>? validationErrors)
{
if (validationErrors is null)
return string.Empty;

var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Validation errors occurred:");

foreach (var error in validationErrors)
stringBuilder.AppendLine($"- {error.Property}: {error.Message}");

return stringBuilder.ToString();
}
}
101 changes: 55 additions & 46 deletions src/Axepta.SDK/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,50 @@

public static class Extensions
{
private static JsonSerializerOptions _sourceGenOptions = new JsonSerializerOptions
private static readonly JsonSerializerOptions _sourceGenOptions = new()
{
TypeInfoResolver = JsonContext.Default,
Converters =
{
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
}
},
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

public static IServiceCollection AddAxeptaPaywall(this IServiceCollection services)
public static IServiceCollection AddAxeptaPaywall(
this IServiceCollection services,
IConfiguration cfg
)
{
var optionsSelection = cfg.GetSection(AxeptaPaywallOptions.SelectionName);

services
.AddOptions<AxeptaPaywallOptions>()
.Bind(optionsSelection)
.ValidateOnStart();

var axeptaPaywallOptions = optionsSelection.Get<AxeptaPaywallOptions>();

var axeptaUrl = axeptaPaywallOptions!.Sandbox ?
"api.sandbox.axepta.pl" :
"api.axepta.pl";

services.AddHttpClient<IAxepta, Services.Axepta>(q =>
{
q.BaseAddress = new("https://api.axepta.pl/v1/merchant/ir49nkdgnuex458f6wnq");
q.BaseAddress = new($"https://{axeptaUrl}/v1/merchant/{axeptaPaywallOptions.MerchantId}/");
q.DefaultRequestHeaders.Accept.Add(new("application/json"));
q.DefaultRequestHeaders.TryAddWithoutValidation(
"Content-Type",
"application/json; charset=utf-8"
);
q.DefaultRequestHeaders.Authorization = new(
"Bearer",
"ttfc9ve4zeseca4egs0pguk15c3yckkwf7d1n1ts8e55y5hs68886ujt76z5glbl"
axeptaPaywallOptions.AuthToken
);
})
.AddPolicyHandler(HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(q => q.StatusCode == System.Net.HttpStatusCode.NotFound)
.OrResult(q => q.StatusCode == HttpStatusCode.NotFound)
.WaitAndRetryAsync(
2,
q => TimeSpan.FromSeconds(Math.Pow(
Expand All @@ -40,40 +57,8 @@ public static IServiceCollection AddAxeptaPaywall(this IServiceCollection servic

return services;
}

internal static async Task PostAsync<T>(
this HttpClient http,
string url,
T body,
CancellationToken ct
)
{
HttpResponseMessage? httpRes = null;

try
{
httpRes = await http.PostAsync(
url,
new StringContent(
JsonSerializer.Serialize(
body,
_sourceGenOptions
),
Encoding.UTF8,
"application/json"
),
ct
);

httpRes.EnsureSuccessStatusCode();
}
catch (HttpRequestException)
{
throw new AxeptaException(await httpRes!.Content.ReadAsStringAsync(ct));
}
}

public static async Task<K> PostAsync<T, K>(
internal static async Task<K> PostAsync<T, K>(
this HttpClient http,
string url,
T body,
Expand All @@ -83,13 +68,15 @@ public static async Task<K> PostAsync<T, K>(
{
HttpResponseMessage? httpRes = null;

var elo = JsonSerializer.Serialize(
body,
_sourceGenOptions
);

Console.WriteLine(elo);

try
{
var elo = JsonSerializer.Serialize(
body,
_sourceGenOptions
);

httpRes = await http.PostAsync(
url,
new StringContent(
Expand All @@ -112,9 +99,31 @@ await httpRes.Content.ReadAsStringAsync(ct),
}
catch (HttpRequestException)
{
var elo = httpRes!.Content.ReadAsStringAsync();
switch (httpRes?.StatusCode)
{
case HttpStatusCode.Unauthorized:
throw new AxeptaException("Authorization failed: The provided token is invalid, preventing authorized access to the requested resource.");
case HttpStatusCode.UnprocessableEntity:
{
var resBody = JsonSerializer.Deserialize(
await httpRes.Content.ReadAsStringAsync(ct),
typeof(ResponseRoot),
_sourceGenOptions
)! as ResponseRoot;

throw new AxeptaException(await httpRes!.Content.ReadAsStringAsync(ct));
throw new AxeptaException(resBody?.Data.ValidationErrors);
}
default:
throw new AxeptaException(await httpRes!.Content.ReadAsStringAsync(ct));
}
}
}

internal static string FirstCharToUpper(this string input) =>
input switch
{
null => throw new ArgumentNullException(nameof(input)),
"" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
_ => string.Concat(input[0].ToString().ToUpper(), input.AsSpan(1))
};
}
Loading

0 comments on commit a422fe6

Please sign in to comment.