diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..6af9f25
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,24 @@
+name: 'CI'
+
+on:
+ push:
+ branches:
+ - main
+ - dev
+ pull_request: {}
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - uses: actions/setup-dotnet@v4
+ with:
+ global-json-file: './global.json'
+ - run: dotnet --version
+ - name: Build
+ run: dotnet build
+ - name: Test
+ run: dotnet test --no-build
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
deleted file mode 100644
index 8b4fbc3..0000000
--- a/.github/workflows/dotnet.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: CI
-
-on:
- push:
- branches: [ "main" ]
- pull_request:
- branches: [ "main" ]
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
- - name: Setup .NET
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 6.0.x
- - name: Restore dependencies
- run: dotnet restore
- - name: Build
- run: dotnet build --no-restore
- - name: Test
- run: dotnet test --no-build --verbosity normal
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..c0e3ec5
--- /dev/null
+++ b/global.json
@@ -0,0 +1,7 @@
+{
+ "sdk": {
+ "version": "8.0.400",
+ "rollForward": "latestFeature"
+ }
+ }
+
\ No newline at end of file
diff --git a/samples/Axepta.Sample/Program.cs b/samples/Axepta.Sample/Program.cs
index 14b267d..9b4c6d3 100644
--- a/samples/Axepta.Sample/Program.cs
+++ b/samples/Axepta.Sample/Program.cs
@@ -46,6 +46,37 @@ CancellationToken ct
}
);
+paymentEndpoints.MapPost(
+ "/payment-url",
+ async (
+ IAxepta axepta,
+ CancellationToken ct
+ ) =>
+ {
+ var payment = await axepta.CreatePaymentUrlAsync(
+ new()
+ {
+ Amount = 100,
+ Currency = Currency.PLN,
+ OrderId = "123456789",
+ ReturnUrl = "https://example.com",
+ SuccessReturnUrl = "https://example.com/success",
+ FailureReturnUrl = "https://example.com/failure",
+ Customer = new()
+ {
+ Id = "123",
+ FirstName = "Jan",
+ LastName = "Kowalski",
+ Email = "jan.kowalski@example.com"
+ }
+ },
+ ct
+ );
+
+ return Results.Ok(payment);
+ }
+);
+
paymentEndpoints.MapPost(
"/webhook",
async (
diff --git a/samples/Axepta.Sample/appsettings.Development.json b/samples/Axepta.Sample/appsettings.Development.json
index e3af801..f7094b9 100644
--- a/samples/Axepta.Sample/appsettings.Development.json
+++ b/samples/Axepta.Sample/appsettings.Development.json
@@ -1,11 +1,11 @@
{
"axepta-paywall": {
- "merchantId": "5n3bxvzoxife15djkmpa",
+ "merchantId": "ir49nkdgnuex458f6wnq",
"service": {
- "id": "bf1efa70-a6d8-4c08-a512-52e8aa025952",
- "key": "XrvZlCclg45zOOBtANK5reKo4onNlMrKAwgM"
+ "id": "eff3207f-d2a0-4560-99ce-bba83267c90b",
+ "key": "67ieYPyhrqMIpx7UZCLfSimWT0vUm0I_lH0Z"
},
- "authToken": "rc9wg0rosqzfx4dcdh1epzloemn5fhuis62gy37faxhd7iojhhop6eb341jbqfpw",
+ "authToken": "ttfc9ve4zeseca4egs0pguk15c3yckkwf7d1n1ts8e55y5hs68886ujt76z5glbl",
"sandbox": true
}
}
diff --git a/src/Axepta.SDK/Axepta.SDK.csproj b/src/Axepta.SDK/Axepta.SDK.csproj
index 231db80..e55150f 100644
--- a/src/Axepta.SDK/Axepta.SDK.csproj
+++ b/src/Axepta.SDK/Axepta.SDK.csproj
@@ -5,8 +5,7 @@
enable
enable
Axepta.SDK
- 1.0.4
- alpha
+ 1.0.5
Maksymilian Szokalski
Axepta.SDK is a class library for BNP Paribas Axepta Paywall
BNP Paribas;Axepta;Paywall;Payments;Transactions
diff --git a/src/Axepta.SDK/Entities/Request/GeneratePaymentLink.cs b/src/Axepta.SDK/Entities/Request/GeneratePaymentLink.cs
new file mode 100644
index 0000000..97ac1a9
--- /dev/null
+++ b/src/Axepta.SDK/Entities/Request/GeneratePaymentLink.cs
@@ -0,0 +1,90 @@
+namespace Axepta.SDK;
+
+///
+/// Represents a payment transaction, encapsulating details such as service ID, amount, currency,
+/// order information, customer details, and redirection URLs for successful, failed, or general outcomes.
+/// This record focuses on capturing essential information required to generate a payment link.
+///
+public sealed record GeneratePaymentLink
+{
+ private int _amount;
+
+ ///
+ /// Gets or sets the service ID associated with the payment. This is a unique identifier for the service involved in the payment.
+ ///
+ [JsonPropertyName("serviceId")]
+ public string? ServiceId { get; private set; }
+
+ ///
+ /// Gets or sets the payment amount. The setter converts the input amount from a major currency unit (e.g., dollars) to a minor unit (e.g., cents).
+ ///
+ [JsonPropertyName("amount")]
+ public required int Amount
+ {
+ get => _amount;
+ set => _amount = value * 100;
+ }
+
+ ///
+ /// Gets or sets the currency code for the payment, following the ISO 4217 standard.
+ ///
+ [StringLength(3)]
+ [JsonPropertyName("currency")]
+ public required Currency Currency { get; init; }
+
+ ///
+ /// Gets or sets the unique identifier for the payment order, which can be used for tracking and referencing the transaction.
+ ///
+ [StringLength(100)]
+ [RegularExpression(AllowedCharactersPatterns.ADDITIONAL_ALLOWED_CHARACTERS_PATTERN)]
+ [JsonPropertyName("orderId")]
+ public required string OrderId { get; init; }
+
+ ///
+ /// Gets or sets the URL to which the user is redirected after a successful payment. This URL is used for post-transaction navigation.
+ ///
+ [StringLength(300)]
+ [Url]
+ [JsonPropertyName("successReturnUrl")]
+ public required string SuccessReturnUrl { get; init; }
+
+ ///
+ /// Gets or sets the URL to which the user is redirected after a failed payment. This provides a means to handle unsuccessful transactions.
+ ///
+ [StringLength(300)]
+ [Url]
+ [JsonPropertyName("failureReturnUrl")]
+ public required string FailureReturnUrl { get; init; }
+
+ ///
+ /// Gets or sets a general return URL for the payment, used for redirecting the user after the transaction is processed, regardless of the outcome.
+ ///
+ [StringLength(300)]
+ [Url]
+ [JsonPropertyName("returnUrl")]
+ public required string ReturnUrl { get; init; }
+
+ ///
+ /// Gets or sets the customer's information, essential for processing the payment and for record-keeping purposes.
+ ///
+ [JsonPropertyName("customer")]
+ public required Customer Customer { get; init; }
+
+ ///
+ /// Gets or sets the title associated with the payment. This can be a description or label for the payment, and can be null.
+ ///
+ [StringLength(255)]
+ [RegularExpression(AllowedCharactersPatterns.ADDITIONAL_ALLOWED_CHARACTERS_PATTERN)]
+ [JsonPropertyName("title")]
+ public string? Title { get; init; }
+
+ ///
+ /// Gets or sets the expiration date and time of the transaction. If not specified, the transaction remains valid indefinitely.
+ /// Transactions not completed by this timestamp will be automatically cancelled.
+ ///
+ [JsonPropertyName("activeTo")]
+ public DateTime? ActiveTo { get; init; }
+
+ internal void SetServiceId(string serviceId)
+ => ServiceId = serviceId;
+}
diff --git a/src/Axepta.SDK/Entities/Response/ResponseRoot.cs b/src/Axepta.SDK/Entities/Response/AxeptaResponseRoot.cs
similarity index 82%
rename from src/Axepta.SDK/Entities/Response/ResponseRoot.cs
rename to src/Axepta.SDK/Entities/Response/AxeptaResponseRoot.cs
index 195202f..05b5637 100644
--- a/src/Axepta.SDK/Entities/Response/ResponseRoot.cs
+++ b/src/Axepta.SDK/Entities/Response/AxeptaResponseRoot.cs
@@ -1,6 +1,6 @@
namespace Axepta.SDK;
-public sealed class ResponseRoot
+public sealed class AxeptaResponseRoot
{
[JsonPropertyName("status")]
public required string Status { get; set; }
diff --git a/src/Axepta.SDK/Entities/Response/Data.cs b/src/Axepta.SDK/Entities/Response/Data.cs
index f901396..dd97ea4 100644
--- a/src/Axepta.SDK/Entities/Response/Data.cs
+++ b/src/Axepta.SDK/Entities/Response/Data.cs
@@ -8,6 +8,9 @@ public sealed record Data
[JsonPropertyName("payment")]
public PaymentResponse? Payment { get; init; }
+ [JsonPropertyName("paymentLink")]
+ public PaymentLink? PaymentLink { get; init; }
+
[JsonPropertyName("action")]
public Action? Action { get; init; }
diff --git a/src/Axepta.SDK/Entities/Response/PaymentLink.cs b/src/Axepta.SDK/Entities/Response/PaymentLink.cs
new file mode 100644
index 0000000..15aaea6
--- /dev/null
+++ b/src/Axepta.SDK/Entities/Response/PaymentLink.cs
@@ -0,0 +1,10 @@
+namespace Axepta.SDK;
+
+public sealed record PaymentLink
+{
+ [JsonPropertyName("paymentId")]
+ public required string PaymentId { get; init; }
+
+ [JsonPropertyName("url")]
+ public required string Url { get; init; }
+}
\ No newline at end of file
diff --git a/src/Axepta.SDK/Extensions.cs b/src/Axepta.SDK/Extensions.cs
index 629bc67..09c2de1 100644
--- a/src/Axepta.SDK/Extensions.cs
+++ b/src/Axepta.SDK/Extensions.cs
@@ -143,9 +143,9 @@ await httpResContentAsJson().ConfigureAwait(false),
{
var resBody = JsonSerializer.Deserialize(
await httpResContentAsJson().ConfigureAwait(false),
- typeof(ResponseRoot),
+ typeof(AxeptaResponseRoot),
JsonSerializerOptions
- )! as ResponseRoot;
+ )! as AxeptaResponseRoot;
throw new AxeptaException(resBody?.Data.ValidationErrors);
}
diff --git a/src/Axepta.SDK/JSON/JsonContext.cs b/src/Axepta.SDK/JSON/JsonContext.cs
index 0b68de5..1db6cd8 100644
--- a/src/Axepta.SDK/JSON/JsonContext.cs
+++ b/src/Axepta.SDK/JSON/JsonContext.cs
@@ -1,6 +1,7 @@
namespace Axepta.SDK.JSON;
[JsonSerializable(typeof(Payment))]
-[JsonSerializable(typeof(ResponseRoot))]
+[JsonSerializable(typeof(GeneratePaymentLink))]
+[JsonSerializable(typeof(AxeptaResponseRoot))]
[JsonSerializable(typeof(Notification))]
internal sealed partial class JsonContext : JsonSerializerContext { }
\ No newline at end of file
diff --git a/src/Axepta.SDK/Services/Abstractions/IAxepta.cs b/src/Axepta.SDK/Services/Abstractions/IAxepta.cs
index 2988e58..e9b581b 100644
--- a/src/Axepta.SDK/Services/Abstractions/IAxepta.cs
+++ b/src/Axepta.SDK/Services/Abstractions/IAxepta.cs
@@ -11,20 +11,25 @@ public interface IAxepta
///
/// The payment details required to process the payment.
/// An optional cancellation token to cancel the request.
- /// A task that represents the asynchronous operation. The task result contains the object with the payment response.
- Task CreatePaymentAsync(
+ /// A task that represents the asynchronous operation. The task result contains the object with the payment response.
+ Task CreatePaymentAsync(
Payment payment,
CancellationToken ct = default
);
+ Task CreatePaymentUrlAsync(
+ GeneratePaymentLink paymentLink,
+ CancellationToken ct = default
+ );
+
///
/// Creates a refund asynchronously.
///
/// The unique identifier of the payment to be refunded.
/// The refund details required to process the refund.
/// An optional cancellation token to cancel the request.
- /// A task that represents the asynchronous operation. The task result contains the object with the refund response.
- Task CreateRefundAsync(
+ /// A task that represents the asynchronous operation. The task result contains the object with the refund response.
+ Task CreateRefundAsync(
Guid paymentId,
Refund refund,
CancellationToken ct = default
diff --git a/src/Axepta.SDK/Services/Axepta.cs b/src/Axepta.SDK/Services/Axepta.cs
index 60c5fe2..64f39a9 100644
--- a/src/Axepta.SDK/Services/Axepta.cs
+++ b/src/Axepta.SDK/Services/Axepta.cs
@@ -5,27 +5,40 @@ internal sealed class Axepta(
IOptions options
) : IAxepta
{
- public Task CreatePaymentAsync(
+ public Task CreatePaymentAsync(
Payment payment,
CancellationToken ct = default
)
{
payment.SetServiceId(options.Value.Service.Id);
- return http.PostAsync(
+ return http.PostAsync(
"transaction",
payment,
ct
);
}
- public Task CreateRefundAsync(
+ public Task CreatePaymentUrlAsync(
+ GeneratePaymentLink paymentLink,
+ CancellationToken ct = default
+ )
+ {
+ paymentLink.SetServiceId(options.Value.Service.Id);
+ return http.PostAsync(
+ "payment-link",
+ paymentLink,
+ ct
+ );
+ }
+
+ public Task CreateRefundAsync(
Guid paymentId,
Refund refund,
CancellationToken ct = default
)
{
refund.SetServiceId(options.Value.Service.Id);
- return http.PostAsync(
+ return http.PostAsync(
$"payment/{paymentId}/refund",
refund,
ct
@@ -36,7 +49,7 @@ public async Task GetTransactionAsync(
Guid transactionId,
CancellationToken ct = default
)
- => (await http.GetAsync(
+ => (await http.GetAsync(
$"transaction/{transactionId}",
ct
)).Data.Transaction!;
@@ -45,7 +58,7 @@ public async Task GetPaymentAsync(
Guid paymentId,
CancellationToken ct = default
)
- => (await http.GetAsync(
+ => (await http.GetAsync(
$"payment/{paymentId}",
ct
)).Data.Payment!;