-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds a new type of process task for adding payment to apps. For now, using Nets Easy as payment processor.
- Loading branch information
Showing
59 changed files
with
4,792 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
using Altinn.App.Api.Infrastructure.Filters; | ||
using Altinn.App.Core.Features.Payment; | ||
using Altinn.App.Core.Features.Payment.Exceptions; | ||
using Altinn.App.Core.Features.Payment.Models; | ||
using Altinn.App.Core.Features.Payment.Services; | ||
using Altinn.App.Core.Internal.Instances; | ||
using Altinn.App.Core.Internal.Process; | ||
using Altinn.App.Core.Internal.Process.Elements.AltinnExtensionProperties; | ||
using Altinn.Platform.Storage.Interface.Models; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace Altinn.App.Api.Controllers; | ||
|
||
/// <summary> | ||
/// Controller for handling payment operations. | ||
/// </summary> | ||
[AutoValidateAntiforgeryTokenIfAuthCookie] | ||
[ApiController] | ||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] | ||
[Route("{org}/{app}/instances/{instanceOwnerPartyId:int}/{instanceGuid:guid}/payment")] | ||
public class PaymentController : ControllerBase | ||
{ | ||
private readonly IInstanceClient _instanceClient; | ||
private readonly IProcessReader _processReader; | ||
private readonly IPaymentService _paymentService; | ||
private readonly IOrderDetailsCalculator? _orderDetailsCalculator; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PaymentController"/> class. | ||
/// </summary> | ||
public PaymentController( | ||
IInstanceClient instanceClient, | ||
IProcessReader processReader, | ||
IPaymentService paymentService, | ||
IOrderDetailsCalculator? orderDetailsCalculator = null | ||
) | ||
{ | ||
_instanceClient = instanceClient; | ||
_processReader = processReader; | ||
_paymentService = paymentService; | ||
_orderDetailsCalculator = orderDetailsCalculator; | ||
} | ||
|
||
/// <summary> | ||
/// Get updated payment information for the instance. Will contact the payment processor to check the status of the payment. Current task must be a payment task. See payment related documentation. | ||
/// </summary> | ||
/// <param name="org">unique identifier of the organisation responsible for the app</param> | ||
/// <param name="app">application identifier which is unique within an organisation</param> | ||
/// <param name="instanceOwnerPartyId">unique id of the party that this the owner of the instance</param> | ||
/// <param name="instanceGuid">unique id to identify the instance</param> | ||
/// <param name="language">The currently used language by the user (or null if not available)</param> | ||
/// <returns>An object containing updated payment information</returns> | ||
[HttpGet] | ||
[ProducesResponseType(typeof(PaymentInformation), StatusCodes.Status200OK)] | ||
[ProducesResponseType(StatusCodes.Status404NotFound)] | ||
public async Task<IActionResult> GetPaymentInformation( | ||
[FromRoute] string org, | ||
[FromRoute] string app, | ||
[FromRoute] int instanceOwnerPartyId, | ||
[FromRoute] Guid instanceGuid, | ||
[FromQuery] string? language = null | ||
) | ||
{ | ||
Instance instance = await _instanceClient.GetInstance(app, org, instanceOwnerPartyId, instanceGuid); | ||
AltinnPaymentConfiguration? paymentConfiguration = _processReader | ||
.GetAltinnTaskExtension(instance.Process.CurrentTask.ElementId) | ||
?.PaymentConfiguration; | ||
|
||
if (paymentConfiguration == null) | ||
{ | ||
throw new PaymentException("Payment configuration not found in AltinnTaskExtension"); | ||
} | ||
|
||
PaymentInformation paymentInformation = await _paymentService.CheckAndStorePaymentStatus( | ||
instance, | ||
paymentConfiguration, | ||
language | ||
); | ||
|
||
return Ok(paymentInformation); | ||
} | ||
|
||
/// <summary> | ||
/// Run order details calculations and return the result. Does not require the current task to be a payment task. | ||
/// </summary> | ||
/// <param name="org">unique identifier of the organisation responsible for the app</param> | ||
/// <param name="app">application identifier which is unique within an organisation</param> | ||
/// <param name="instanceOwnerPartyId">unique id of the party that this the owner of the instance</param> | ||
/// <param name="instanceGuid">unique id to identify the instance</param> | ||
/// <param name="language">The currently used language by the user (or null if not available)</param> | ||
/// <returns>An object containing updated payment information</returns> | ||
[HttpGet("order-details")] | ||
[ProducesResponseType(typeof(OrderDetails), StatusCodes.Status200OK)] | ||
[ProducesResponseType(StatusCodes.Status404NotFound)] | ||
public async Task<IActionResult> GetOrderDetails( | ||
[FromRoute] string org, | ||
[FromRoute] string app, | ||
[FromRoute] int instanceOwnerPartyId, | ||
[FromRoute] Guid instanceGuid, | ||
[FromQuery] string? language = null | ||
) | ||
{ | ||
if (_orderDetailsCalculator == null) | ||
{ | ||
throw new PaymentException( | ||
"You must add an implementation of the IOrderDetailsCalculator interface to the DI container. See payment related documentation." | ||
); | ||
} | ||
|
||
Instance instance = await _instanceClient.GetInstance(app, org, instanceOwnerPartyId, instanceGuid); | ||
OrderDetails orderDetails = await _orderDetailsCalculator.CalculateOrderDetails(instance, language); | ||
|
||
return Ok(orderDetails); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using Altinn.App.Core.Features.Payment.Models; | ||
using Altinn.App.Core.Features.Payment.Services; | ||
using Altinn.App.Core.Internal.App; | ||
using Altinn.App.Core.Internal.Process; | ||
using Altinn.App.Core.Internal.Process.Elements; | ||
using Altinn.App.Core.Internal.Process.Elements.AltinnExtensionProperties; | ||
using Altinn.App.Core.Models.Process; | ||
using Altinn.App.Core.Models.UserAction; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Altinn.App.Core.Features.Action | ||
{ | ||
/// <summary> | ||
/// User action for payment | ||
/// </summary> | ||
internal class PaymentUserAction : IUserAction | ||
{ | ||
private readonly IProcessReader _processReader; | ||
private readonly ILogger<PaymentUserAction> _logger; | ||
private readonly IPaymentService _paymentService; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PaymentUserAction"/> class | ||
/// </summary> | ||
public PaymentUserAction( | ||
IProcessReader processReader, | ||
IPaymentService paymentService, | ||
ILogger<PaymentUserAction> logger | ||
) | ||
{ | ||
_processReader = processReader; | ||
_paymentService = paymentService; | ||
_logger = logger; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public string Id => "pay"; | ||
|
||
/// <inheritdoc /> | ||
public async Task<UserActionResult> HandleAction(UserActionContext context) | ||
{ | ||
if ( | ||
_processReader.GetFlowElement(context.Instance.Process.CurrentTask.ElementId) | ||
is not ProcessTask currentTask | ||
) | ||
{ | ||
return UserActionResult.FailureResult( | ||
new ActionError() { Code = "NoProcessTask", Message = "Current task is not a process task." } | ||
); | ||
} | ||
|
||
_logger.LogInformation( | ||
"Payment action handler invoked for instance {Id}. In task: {CurrentTaskId}", | ||
context.Instance.Id, | ||
currentTask.Id | ||
); | ||
|
||
AltinnPaymentConfiguration? paymentConfiguration = currentTask | ||
.ExtensionElements | ||
?.TaskExtension | ||
?.PaymentConfiguration; | ||
if (paymentConfiguration == null) | ||
{ | ||
throw new ApplicationConfigException( | ||
"PaymentConfig is missing in the payment process task configuration." | ||
); | ||
} | ||
|
||
(PaymentInformation paymentInformation, bool alreadyPaid) = await _paymentService.StartPayment( | ||
context.Instance, | ||
paymentConfiguration, | ||
context.Language | ||
); | ||
|
||
if (alreadyPaid) | ||
{ | ||
return UserActionResult.FailureResult( | ||
error: new ActionError { Code = "PaymentAlreadyCompleted", Message = "Payment already completed." }, | ||
errorType: ProcessErrorType.Conflict | ||
); | ||
} | ||
|
||
string? paymentDetailsRedirectUrl = paymentInformation.PaymentDetails?.RedirectUrl; | ||
|
||
return paymentDetailsRedirectUrl == null | ||
? UserActionResult.SuccessResult() | ||
: UserActionResult.RedirectResult(new Uri(paymentDetailsRedirectUrl)); | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/Altinn.App.Core/Features/Payment/Exceptions/PaymentException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
namespace Altinn.App.Core.Features.Payment.Exceptions | ||
{ | ||
/// <summary> | ||
/// Represents an exception that is thrown when an error occurs during payment processing. | ||
/// </summary> | ||
public class PaymentException : Exception | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PaymentException"/> class. | ||
/// </summary> | ||
/// <param name="message"></param> | ||
public PaymentException(string message) | ||
: base(message) { } | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/Altinn.App.Core/Features/Payment/IOrderDetailsCalculator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
namespace Altinn.App.Core.Features.Payment; | ||
|
||
using Altinn.Platform.Storage.Interface.Models; | ||
using Models; | ||
|
||
/// <summary> | ||
/// Interface that app developers need to implement in order to use the payment feature | ||
/// </summary> | ||
public interface IOrderDetailsCalculator | ||
{ | ||
/// <summary> | ||
/// Method that calculates an order based on an instance. | ||
/// </summary> | ||
/// <remarks> | ||
/// The instance (and its data) needs to be fetched based on the <see cref="Instance"/> if the calculation | ||
/// depends on instance or data properties. | ||
/// This method can be called multiple times for the same instance, in order to preview the price before payment starts. | ||
/// </remarks> | ||
/// <returns>The Payment order that contains information about the requested payment</returns> | ||
Task<OrderDetails> CalculateOrderDetails(Instance instance, string? language); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
namespace Altinn.App.Core.Features.Payment.Models; | ||
|
||
/// <summary> | ||
/// Represents an address. | ||
/// </summary> | ||
public class Address | ||
{ | ||
/// <summary> | ||
/// The name associated with the address. | ||
/// </summary> | ||
public string? Name { get; set; } | ||
|
||
/// <summary> | ||
/// The first line of the address. | ||
/// </summary> | ||
public string? AddressLine1 { get; set; } | ||
|
||
/// <summary> | ||
/// The second line of the address. | ||
/// </summary> | ||
public string? AddressLine2 { get; set; } | ||
|
||
/// <summary> | ||
/// The postal code of the address. | ||
/// </summary> | ||
public string? PostalCode { get; set; } | ||
|
||
/// <summary> | ||
/// The city of the address. | ||
/// </summary> | ||
public string? City { get; set; } | ||
|
||
/// <summary> | ||
/// The country of the address. | ||
/// </summary> | ||
public string? Country { get; set; } | ||
} |
17 changes: 17 additions & 0 deletions
17
src/Altinn.App.Core/Features/Payment/Models/CardDetails.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
namespace Altinn.App.Core.Features.Payment.Models; | ||
|
||
/// <summary> | ||
/// The details of a payment card. | ||
/// </summary> | ||
public class CardDetails | ||
{ | ||
/// <summary> | ||
/// The masked PAN of the card. | ||
/// </summary> | ||
public string? MaskedPan { get; set; } | ||
|
||
/// <summary> | ||
/// The expiry date of the card. | ||
/// </summary> | ||
public string? ExpiryDate { get; set; } | ||
} |
12 changes: 12 additions & 0 deletions
12
src/Altinn.App.Core/Features/Payment/Models/InvoiceDetails.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
namespace Altinn.App.Core.Features.Payment.Models; | ||
|
||
/// <summary> | ||
/// The details of an invoice. | ||
/// </summary> | ||
public class InvoiceDetails | ||
{ | ||
/// <summary> | ||
/// The invoice number, if available. | ||
/// </summary> | ||
public string? InvoiceNumber { get; set; } | ||
} |
Oops, something went wrong.