Skip to content

Conversation

@ivarne
Copy link
Member

@ivarne ivarne commented Dec 12, 2025

Currently our nets implementation depends on the user visiting the instance (eg. via the return url from nets) before the instance is marked as completed. Some users (especially on mobile where they might be directed to the BankId app, does not open the browser tab again before the login has timed out).

Fixes

Changes

  • To be able to update instance data from a hook, we need maskinporten token and thus update IInstanceClient and IDataService to have a StorageAutenicationMetod parameter on all methods (and update corresponding tests)
  • Add new (hidden) endpoint in payment controller {org}/{app}/instances/{instanceOwnerPartyId:int}/{instanceGuid:guid}/payment/nets-webhook-listener for nets to call
  • New method PaymentService.HandlePaymentCompletedWebhook, that queries nets for payment info and calls process/next if relevant.
  • Update payment netsPaymentProcessor to add the config for the web hook on payment.checkout.completed

Testing

  • Update packages
  • Configure maskinporten auth for service owner and ensure that Policy.xml allows the service owner the write action on the payment task.
  • See that

Unsolved issue

  • Race condition when frontend and backend calls /process/next

Documentation

  • User documentation is updated with a separate linked PR in altinn-studio-docs. (if applicable)

Summary by CodeRabbit

  • New Features

    • Nets webhook endpoint and payload support for completed payments.
  • Improvements

    • Per-operation cancellation support for more responsive requests.
    • Optional per-operation storage authentication for flexible access.
    • Configurable webhook key and webhook-driven payment status updates.
    • Centralized task-type constants and enhanced telemetry/logging.
  • Refactor

    • Public APIs and clients updated to accept authentication and cancellation parameters.
  • Tests

    • Updated tests, fixtures and HTTP test helpers to align with auth, cancellation and webhook flows.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 12, 2025

📝 Walkthrough

Walkthrough

Adds Nets webhook payload and listener, integrates webhook callback registration into Nets payment flow, extends payment service and telemetry, centralizes Altinn task-type constants, makes InstanceClient internal and replaces token provider with IAuthenticationTokenResolver, and propagates per-call StorageAuthenticationMethod? and CancellationToken across instance/data APIs; updates tests and test fixtures.

Changes

Cohort / File(s) Summary
Webhook & payment
src/Altinn.App.Api/Controllers/PaymentController.cs, src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsCompleteWebhookPayload.cs, src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs, src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs, src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs, src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
Adds NetsCompleteWebhook payload model and PaymentWebhookListener endpoint; validates and uses WebhookCallbackKey; wires webhook config into create-payment payload; adds HandlePaymentCompletedWebhook to IPaymentService/PaymentService; DI and telemetry additions and related logging/flows.
Instance & data clients (auth + cancellation)
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs, src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs, src/Altinn.App.Core/Internal/Data/DataService.cs, src/Altinn.App.Core/Internal/Data/IDataService.cs
Makes InstanceClient internal and implement IInstanceClient; replaces IUserTokenProvider with IAuthenticationTokenResolver; introduces per-call StorageAuthenticationMethod? authenticationMethod = null and CancellationToken ct = default to many instance/data methods and propagates these through HTTP and (de)serialization calls.
Controller callsites
src/Altinn.App.Api/Controllers/InstancesController.cs
Updates call sites to pass ct: cancellationToken into GetInstance and UpdateReadStatus invocations.
Task-type constants
src/Altinn.App.Core/Constants/AltinnTaskTypes.cs, src/Altinn.App.Core/Internal/Process/ProcessTasks/*, src/Altinn.App.Clients.Fiks/*, related
Introduces centralized AltinnTaskTypes constants and replaces hard-coded task-type string literals with these constants across process tasks and related callers.
Process engine & authorizer
src/Altinn.App.Core/Internal/Process/ProcessEngine.cs, src/Altinn.App.Core/Internal/Process/ProcessEngineAuthorizer.cs, related
Passes CancellationToken into instance fetches and migrates task-type logic to use AltinnTaskTypes; no behavior changes beyond parameter/constant usage.
Telemetry helpers
src/Altinn.App.Core/Features/Telemetry/Telemetry.Payment.cs
Adds internal telemetry helpers for payment activities and a Payment prefix constant.
Public API surface / PublicApi changes
src/Altinn.App.Core/* (manifest highlights)
Public API updates: new Nets payload models and WebhookCallbackKey; InstanceClient made internal (public removed); interfaces (IInstanceClient, IDataService, related) extended with optional StorageAuthenticationMethod? and CancellationToken parameters.
Tests — signatures, mocks & expectations
multiple test/... files (Controllers, Features, Internal/Process, Implementation, Mocks, etc.)
Updates many tests and mocks to match extended IInstanceClient/IDataService signatures (added StorageAuthenticationMethod? and CancellationToken), replaces task-type literals with AltinnTaskTypes, adjusts DI/fixture usage, and relaxes/updates mock expectations for nullable auth parameters.
Test utilities & fixtures
test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs, test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs, test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs, related
Significant test infra refactor: dynamic Mock() registry, exposed FakeHttpMessageHandler with endpoint registration/verification, StorageClientInterceptor now takes ApplicationMetadata and offers GetInstanceAndData, and DI/verification helpers updated.
Minor test/DI adjustments
various test/... files
Removes/adjusts common service registration calls, updates constructors and fixture wiring (e.g., ITestOutputHelper additions), and small test initialization changes.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.63% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add web hook support for nets' clearly and concisely summarizes the main objective of the changeset: implementing webhook support for the Nets payment provider.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/nets-callback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ivarne ivarne added feature Label Pull requests with new features. Used when generation releasenotes backport-ignore This PR is a new feature and should not be cherry-picked onto release branches labels Dec 12, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Altinn.App.Core/Internal/Data/DataService.cs (1)

118-139: Stream should be disposed after deserialization.

The dataStream returned from GetBinaryData is not disposed, which could lead to resource leaks.

     private async Task<T> GetDataForDataElement<T>(
         InstanceIdentifier instanceIdentifier,
         DataElement dataElement,
         StorageAuthenticationMethod? authenticationMethod = null
     )
     {
-        Stream dataStream = await _dataClient.GetBinaryData(
+        await using Stream dataStream = await _dataClient.GetBinaryData(
             instanceIdentifier.InstanceOwnerPartyId,
             instanceIdentifier.InstanceGuid,
             new Guid(dataElement.Id),
             authenticationMethod
         );
         if (dataStream == null)
         {
             throw new ArgumentNullException(
                 $"Failed to retrieve binary dataStream from dataClient using dataElement.Id {dataElement.Id}."
             );
         }

         return await JsonSerializer.DeserializeAsync<T>(dataStream, _jsonSerializerOptions)
             ?? throw new InvalidOperationException($"Unable to deserialize data from dataStream to type {nameof(T)}.");
     }
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (1)

132-149: QueryInstances should propagate CancellationToken.

The GetInstances method receives a CancellationToken but doesn't propagate it to the private QueryInstances helper, meaning HTTP calls during pagination cannot be cancelled.

-    private async Task<QueryResponse<Instance>> QueryInstances(string token, string url)
+    private async Task<QueryResponse<Instance>> QueryInstances(string token, string url, CancellationToken ct = default)
     {
         using var activity = _telemetry?.StartQueryInstancesActivity();
-        HttpResponseMessage response = await _client.GetAsync(token, url);
+        HttpResponseMessage response = await _client.GetAsync(token, url, cancellationToken: ct);

         if (response.StatusCode == HttpStatusCode.OK)
         {
             QueryResponse<Instance> queryResponse = await JsonSerializerPermissive.DeserializeAsync<
                 QueryResponse<Instance>
-            >(response.Content);
+            >(response.Content, ct);
             return queryResponse;
         }

Then update the calls in GetInstances:

-        QueryResponse<Instance> queryResponse = await QueryInstances(token, apiUrl);
+        QueryResponse<Instance> queryResponse = await QueryInstances(token, apiUrl, ct);
...
-            queryResponse = await QueryInstances(token, queryResponse.Next);
+            queryResponse = await QueryInstances(token, queryResponse.Next, ct);
🧹 Nitpick comments (12)
src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs (1)

65-70: Consider documenting new params consistently on all affected methods.
Some methods have <param> docs for authenticationMethod/ct, others don’t—minor, but improves API clarity.

Also applies to: 82-91, 99-107, 116-124, 133-144, 153-164, 173-183, 201-212, 229-237

test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs (1)

37-43: Test mock should forward ct (and optionally authenticationMethod) to keep semantics consistent.
Right now the new parameters are accepted but largely ignored, including not passing ct into ReadJsonFile/WriteJsonFile, and not forwarding in GetInstance(Instance ...).

 public async Task<Instance> GetInstance(
     Instance instance,
     StorageAuthenticationMethod? authenticationMethod = null,
     CancellationToken ct = default
 )
 {
     string app = instance.AppId.Split("/")[1];
     string org = instance.Org;
     int instanceOwnerId = int.Parse(instance.InstanceOwner.PartyId);
     Guid instanceGuid = Guid.Parse(instance.Id.Split("/")[1]);

-    return await GetInstance(app, org, instanceOwnerId, instanceGuid);
+    return await GetInstance(app, org, instanceOwnerId, instanceGuid, authenticationMethod, ct);
 }

(And similarly: thread ct into ReadJsonFile(...), WriteJsonFile(...), WaitAsync(...) call sites where feasible.)

Also applies to: 69-82, 84-104, 106-110, 142-150, 226-233, 235-240, 264-270, 296-302, 333-339, 376-382, 419-425, 457-461

src/Altinn.App.Api/Controllers/InstancesController.cs (1)

163-169: (Optional) Consider using HttpContext.RequestAborted consistently across controller calls.
This method now accepts cancellationToken, but other internal calls in this controller still rely on RequestAborted or no token—worth standardizing when you touch those paths next.

src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (1)

30-35: Add XML docs (and consider CancellationToken) for new interface method
This is a new internal contract surface; it should be self-describing like the others, and webhook handling is typically cancellation-aware.

@@
-    Task HandlePaymentCompletedWebhook(
+    /// <summary>
+    /// Handle a payment provider "completed" webhook and update internal payment state for the instance.
+    /// </summary>
+    Task HandlePaymentCompletedWebhook(
         Instance instance,
         ValidAltinnPaymentConfiguration paymentConfiguration,
         StorageAuthenticationMethod storageAuthenticationMethod
     );

(If the implementation does I/O, consider adding an optional CancellationToken cancellationToken = default as well, but I can’t confirm usage from the provided files.)

test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs (1)

5-5: Consider a local helper to DRY repeated GetInstance setups
The signature expansion forced repeated boilerplate; a tiny helper in this test class would cut noise and reduce future churn.

@@
 public class SigningControllerTests
 {
@@
+    private ISetup<IInstanceClient, Task<Instance>> SetupGetInstanceReturns(Instance instance) =>
+        _instanceClientMock.Setup(x =>
+            x.GetInstance(
+                It.IsAny<string>(),
+                It.IsAny<string>(),
+                It.IsAny<int>(),
+                It.IsAny<Guid>(),
+                It.IsAny<StorageAuthenticationMethod?>(),
+                It.IsAny<CancellationToken>()
+            )
+        ).ReturnsAsync(instance);
@@
-        _instanceClientMock
-            .Setup(x =>
-                x.GetInstance(
-                    It.IsAny<string>(),
-                    It.IsAny<string>(),
-                    It.IsAny<int>(),
-                    It.IsAny<Guid>(),
-                    It.IsAny<StorageAuthenticationMethod?>(),
-                    It.IsAny<CancellationToken>()
-                )
-            )
-            .ReturnsAsync(
-                new Instance
-                {
-                    ...
-                }
-            );
+        SetupGetInstanceReturns(
+            new Instance
+            {
+                ...
+            }
+        );

Also applies to: 75-85, 608-618, 732-742, 783-793, 865-875

src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (1)

200-271: New webhook handler looks structurally sound.

The implementation correctly mirrors the existing CheckAndStorePaymentStatus logic while using the provided storageAuthenticationMethod for data access operations.

A few observations:

  1. Log message (Line 206): The log message "Checking payment status for instance..." is identical to CheckAndStorePaymentStatus. Consider making it more specific, e.g., "Handling payment completed webhook for instance...".

  2. Silent returns: When paymentInformation is null (Lines 215-223) or status is Skipped (Lines 228-237), the method returns silently. The caller has no indication of what happened. This may be intentional for webhook idempotency, but consider if a return type (e.g., Task<bool> or a result enum) would be useful for observability/logging at the caller level.

-    public async Task HandlePaymentCompletedWebhook(
+    public async Task HandlePaymentCompletedWebhook(
         Instance instance,
         ValidAltinnPaymentConfiguration paymentConfiguration,
         StorageAuthenticationMethod storageAuthenticationMethod
     )
     {
-        _logger.LogInformation("Checking payment status for instance {InstanceId}.", instance.Id);
+        _logger.LogInformation("Handling payment completed webhook for instance {InstanceId}.", instance.Id);
test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (1)

1-758: Missing test coverage for HandlePaymentCompletedWebhook.

The new HandlePaymentCompletedWebhook method in PaymentService (see src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs, Lines 200-271) is not covered by any tests in this file.

Consider adding tests for:

  1. Successfully updating payment status via webhook
  2. Early return when paymentInformation is null
  3. Early return when status is PaymentStatus.Skipped
  4. Exception when PaymentProcessor is not found
  5. Exception when PaymentDetails is null

Would you like me to generate test cases for the HandlePaymentCompletedWebhook method?

src/Altinn.App.Api/Controllers/PaymentController.cs (3)

36-44: Consider using explicit constructor injection instead of IServiceProvider.

Using IServiceProvider directly (service locator pattern) obscures dependencies and makes testing harder. The existing endpoints in this controller already had explicit DI before this change.

-    public PaymentController(IServiceProvider serviceProvider)
+    public PaymentController(
+        IInstanceClient instanceClient,
+        IProcessReader processReader,
+        IPaymentService paymentService,
+        AppImplementationFactory appImplementationFactory,
+        ILogger<PaymentController> logger,
+        IOptions<NetsPaymentSettings> netsPaymentSettings)
     {
-        _instanceClient = serviceProvider.GetRequiredService<IInstanceClient>();
-        _processReader = serviceProvider.GetRequiredService<IProcessReader>();
-        _logger = serviceProvider.GetRequiredService<ILogger<PaymentController>>();
-        _netsPaymentSettings = serviceProvider.GetRequiredService<IOptions<NetsPaymentSettings>>();
-        _paymentService = serviceProvider.GetRequiredService<IPaymentService>();
-        _appImplementationFactory = serviceProvider.GetRequiredService<AppImplementationFactory>();
+        _instanceClient = instanceClient;
+        _processReader = processReader;
+        _paymentService = paymentService;
+        _appImplementationFactory = appImplementationFactory;
+        _logger = logger;
+        _netsPaymentSettings = netsPaymentSettings;
     }

133-140: Consider adding CancellationToken parameter.

The endpoint makes async calls but doesn't propagate a CancellationToken. This allows request cancellation to be honored.

     public async Task<IActionResult> PaymentWebhookListener(
         [FromRoute] string org,
         [FromRoute] string app,
         [FromRoute] int instanceOwnerPartyId,
         [FromRoute] Guid instanceGuid,
         [FromBody] NetsCompleteWebhookPayload webhookPayload,
-        [FromHeader(Name = "Authorization")] string authorizationHeader
+        [FromHeader(Name = "Authorization")] string authorizationHeader,
+        CancellationToken ct = default
     )

Then propagate ct to GetInstance and HandlePaymentCompletedWebhook calls.


160-164: Consider validating the webhook event name.

The EventName from the webhook payload is not validated. Per Nets webhook configuration, this endpoint is intended for payment.checkout.completed events. Validating this provides defense in depth.

         _logger.LogInformation(
             "Received valid Nets webhook callback for instance {InstanceGuid} for {Payment}",
             instanceGuid,
             webhookPayload.Data.PaymentId
         );
+
+        if (webhookPayload.EventName != "payment.checkout.completed")
+        {
+            _logger.LogWarning(
+                "Received unexpected Nets webhook event {EventName} for instance {InstanceGuid}. Ignoring.",
+                webhookPayload.EventName,
+                instanceGuid
+            );
+            return Ok(); // Acknowledge but don't process
+        }
src/Altinn.App.Core/Internal/Data/DataService.cs (1)

92-99: Use named parameter for consistency.

InsertJsonObject uses authenticationMethod: authenticationMethod (line 76) but UpdateBinaryData uses positional. Consider using named parameter for clarity and consistency.

         return await _dataClient.UpdateBinaryData(
             instanceIdentifier,
             "application/json",
             dataTypeId + ".json",
             dataElementId,
             referenceStream,
-            authenticationMethod
+            authenticationMethod: authenticationMethod
         );
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (1)

300-306: Inconsistent error handling: consider throwing instead of returning null.

This method returns null on failure while other methods throw PlatformHttpException. The #nullable disable pragma masks a design issue. This is existing behavior, but consider aligning with the pattern used elsewhere.

         if (response.StatusCode == HttpStatusCode.OK)
         {
             Instance instance = await JsonSerializerPermissive.DeserializeAsync<Instance>(response.Content, ct);
             return instance;
         }

         _logger.LogError(
             $"Could not update read status for instance {instanceOwnerPartyId}/{instanceGuid}. Request failed with status code {response.StatusCode}"
         );
-#nullable disable
-        return null;
-#nullable restore
+        throw await PlatformHttpException.CreateAsync(response);
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 862e60e and ab3b4eb.

📒 Files selected for processing (27)
  • src/Altinn.App.Api/Controllers/InstancesController.cs (1 hunks)
  • src/Altinn.App.Api/Controllers/PaymentController.cs (3 hunks)
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsCompleteWebhookPayload.cs (1 hunks)
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (1 hunks)
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs (2 hunks)
  • src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (1 hunks)
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (1 hunks)
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (10 hunks)
  • src/Altinn.App.Core/Internal/Data/DataService.cs (6 hunks)
  • src/Altinn.App.Core/Internal/Data/IDataService.cs (2 hunks)
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs (8 hunks)
  • src/Altinn.App.Core/Internal/Process/ProcessEngine.cs (1 hunks)
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs (2 hunks)
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs (13 hunks)
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs (9 hunks)
  • test/Altinn.App.Api.Tests/Controllers/PdfControllerTests.cs (1 hunks)
  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs (6 hunks)
  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerTests.cs (5 hunks)
  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerValidateDataTests.cs (1 hunks)
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs (13 hunks)
  • test/Altinn.App.Core.Tests/Features/Action/SigningUserActionTests.cs (1 hunks)
  • test/Altinn.App.Core.Tests/Features/Action/UniqueSignatureAuthorizerTests.cs (7 hunks)
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (18 hunks)
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (14 hunks)
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs (14 hunks)
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs (3 hunks)
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs
  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerValidateDataTests.cs
  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerTests.cs
  • test/Altinn.App.Core.Tests/Features/Action/SigningUserActionTests.cs
  • test/Altinn.App.Core.Tests/Features/Action/UniqueSignatureAuthorizerTests.cs
  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs
  • src/Altinn.App.Core/Internal/Process/ProcessEngine.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsCompleteWebhookPayload.cs
  • src/Altinn.App.Api/Controllers/InstancesController.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Controllers/PdfControllerTests.cs
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerValidateDataTests.cs
  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerTests.cs
  • test/Altinn.App.Core.Tests/Features/Action/SigningUserActionTests.cs
  • test/Altinn.App.Core.Tests/Features/Action/UniqueSignatureAuthorizerTests.cs
  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Controllers/PdfControllerTests.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
🧠 Learnings (17)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs
  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerTests.cs
  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs
  • src/Altinn.App.Core/Internal/Process/ProcessEngine.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsCompleteWebhookPayload.cs
  • src/Altinn.App.Api/Controllers/InstancesController.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerValidateDataTests.cs
  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerTests.cs
  • test/Altinn.App.Core.Tests/Features/Action/SigningUserActionTests.cs
  • test/Altinn.App.Core.Tests/Features/Action/UniqueSignatureAuthorizerTests.cs
  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Controllers/PdfControllerTests.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerValidateDataTests.cs
  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
📚 Learning: 2025-08-22T13:35:09.565Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Authentication_auth=OldUser.verified.txt:16-38
Timestamp: 2025-08-22T13:35:09.565Z
Learning: Test data in Altinn App Integration Tests snapshots (like SSNs, addresses, phone numbers in BasicAppTests.Authentication snapshots) are synthetic/fake values created for testing purposes, not real PII that needs to be scrubbed from version control.

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerValidateDataTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/test/**/*.cs : Mock external dependencies with Moq in tests

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerValidateDataTests.cs
  • test/Altinn.App.Core.Tests/Features/Action/UniqueSignatureAuthorizerTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/ValidateControllerValidateDataTests.cs
  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-10-30T14:57:20.739Z
Learnt from: danielskovli
Repo: Altinn/app-lib-dotnet PR: 1550
File: src/Altinn.App.Core/Internal/Process/ProcessEngine.cs:295-297
Timestamp: 2025-10-30T14:57:20.739Z
Learning: In ProcessEngine.ProcessNext() for service tasks, after handling a service task, processNextAction is intentionally set to `(serviceTaskResult as ServiceTaskFailedResult)?.ErrorHandling.Action`, which will be null for successful service tasks or the ErrorHandling.Action for failed tasks. This override of the original request.Action is the desired behavior, as service tasks control what action (if any) should be passed to HandleMoveToNext.

Applied to files:

  • src/Altinn.App.Core/Internal/Process/ProcessEngine.cs
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
📚 Learning: 2025-09-21T09:04:43.977Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: src/Altinn.App.Core/Features/IInstanceDataAccessor.cs:36-41
Timestamp: 2025-09-21T09:04:43.977Z
Learning: The IInstanceDataAccessor interface in Altinn.App.Core is designed for consumption only - users are not expected to implement this interface themselves, only use framework-provided implementations.

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-10-22T08:35:24.567Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1532
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:2451-2451
Timestamp: 2025-10-22T08:35:24.567Z
Learning: In Altinn/app-lib-dotnet, IDataClient is generally not implemented by customers; it’s considered internal for DI. As of 2025-10-22, GetBinaryDataStream had not been released yet, so its parameter reordering (adding optional TimeSpan? timeout before CancellationToken) is acceptable without a compatibility shim; recommend documenting the new parameter and using named arguments in samples.

Applied to files:

  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-09-07T20:03:48.030Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1463
File: test/Altinn.App.SourceGenerator.Tests/DiagnosticTests.RunJsonError.verified.txt:1-21
Timestamp: 2025-09-07T20:03:48.030Z
Learning: In Altinn.App.SourceGenerator.Tests, the path C:\temp\config\applicationmetadata.json is a hardcoded test fixture path required by Roslyn and is used consistently across all operating systems, not an actual filesystem path that varies by OS.

Applied to files:

  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
  • src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs
📚 Learning: 2025-08-29T05:32:47.222Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/AppFixture.cs:958-958
Timestamp: 2025-08-29T05:32:47.222Z
Learning: In Altinn.App.Integration.Tests, the maintainer prefers fail-fast behavior (Environment.FailFast) when critical test infrastructure components like log reading fail, rather than graceful error handling, because diagnostics are essential for debugging test failures.

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs
  • test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs
🧬 Code graph analysis (10)
src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (4)
test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (2)
  • Instance (717-728)
  • ValidAltinnPaymentConfiguration (750-757)
test/Altinn.App.Core.Tests/Features/Action/PaymentUserActionTests.cs (1)
  • Instance (149-158)
test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs (1)
  • Instance (266-277)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnPaymentConfiguration.cs (1)
  • ValidAltinnPaymentConfiguration (24-38)
test/Altinn.App.Api.Tests/Controllers/ValidateControllerTests.cs (1)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • StorageAuthenticationMethod (623-628)
  • StorageAuthenticationMethod (630-631)
test/Altinn.App.Core.Tests/Features/Action/UniqueSignatureAuthorizerTests.cs (3)
src/Altinn.App.Core/Features/Signing/Services/SigningDelegationService.cs (1)
  • Guid (146-156)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • StorageAuthenticationMethod (623-628)
  • StorageAuthenticationMethod (630-631)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (5)
  • StorageAuthenticationMethod (85-85)
  • StorageAuthenticationMethod (88-88)
  • StorageAuthenticationMethod (91-92)
  • StorageAuthenticationMethod (95-96)
  • StorageAuthenticationMethod (100-103)
test/Altinn.App.Api.Tests/Controllers/SigningControllerTests.cs (2)
src/Altinn.App.Core/Features/Maskinporten/Constants/JwtClaimTypes.cs (1)
  • Altinn (17-30)
src/Altinn.App.Core/Features/Signing/Services/SigningDelegationService.cs (1)
  • Guid (146-156)
test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (1)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (1)
  • AuthenticationMethod (44-44)
test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs (1)
test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs (1)
  • Mock (49-85)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (1)
src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsNotifications.cs (2)
  • NetsNotifications (6-12)
  • NetsWebHook (14-36)
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (5)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (3)
  • Task (45-144)
  • Task (147-156)
  • Task (159-202)
src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (5)
  • Task (15-19)
  • Task (24-28)
  • Task (30-34)
  • Task (39-39)
  • Task (44-44)
src/Altinn.App.Core/Features/Payment/Models/PaymentInformation.cs (1)
  • PaymentInformation (6-27)
src/Altinn.App.Core/Features/Payment/Exceptions/PaymentException.cs (2)
  • PaymentException (8-16)
  • PaymentException (14-15)
src/Altinn.App.Core/Features/Payment/Models/PaymentDetails.cs (1)
  • PaymentDetails (6-52)
test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs (1)
test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs (1)
  • Mock (49-85)
test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs (1)
src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs (1)
  • Task (16-23)
🪛 GitHub Actions: Verify dotnet format
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs

[error] 99-99: CSharpier formatting issue detected. Code around line 99 is not formatted according to CSharpier. Run 'dotnet csharpier --write' to fix formatting.

🪛 Gitleaks (8.30.0)
test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs

[high] 506-506: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Run dotnet build and test (windows-latest)
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Static code analysis
🔇 Additional comments (21)
test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs (1)

123-133: Good update to extended UpdateProcessAndEvents signature (auth + ct).
Mocks now match the new interface surface cleanly.

Also applies to: 142-153, 188-198, 207-218, 259-269, 278-289, 330-340, 349-360, 401-411, 420-431, 492-502, 512-523

src/Altinn.App.Core/Internal/Process/ProcessEngine.cs (1)

182-186: Good: propagate ct when re-fetching instance in the loop.

test/Altinn.App.Core.Tests/Features/Action/UniqueSignatureAuthorizerTests.cs (1)

176-185: Good test updates for IInstanceClient.GetInstance(..., auth, ct) usage.

Also applies to: 238-247, 344-353, 409-418, 474-483, 539-548, 571-590

test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (1)

57-63: Good alignment with new token resolution flow (IAuthenticationTokenResolver).

Also applies to: 88-94, 128-134, 170-176, 292-298, 324-330, 469-475

test/Altinn.App.Core.Tests/Features/Action/SigningUserActionTests.cs (1)

110-121: Good: mock setup updated to the new GetInstance(instance, auth, ct) signature.

src/Altinn.App.Api/Controllers/InstancesController.cs (1)

140-146: Good: propagate request CancellationToken into storage calls.

Also applies to: 163-169, 176-181

src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs (1)

16-77: XML documentation is complete and consistent across all modified methods; no additional release notes required.

All modified methods in IInstanceClient include proper <param> documentation for the new optional parameters (authenticationMethod and ct). Since IInstanceClient is internal (used for DI only), per established patterns in this codebase, the XML documentation is the appropriate level of documentation. The interface changes follow the same approach validated in prior PRs (#1474, #1532) where optional parameters on public/internal members are acceptable when properly documented.

test/Altinn.App.Api.Tests/Controllers/ValidateControllerTests.cs (1)

64-75: Mock signature updates look correct; optional DRY as follow-up
All updated setups match the expanded GetInstance signature. If this pattern repeats across many tests, a local helper (or fixture helper) would reduce churn.

Also applies to: 102-113, 156-167, 197-208, 266-276

test/Altinn.App.Core.Tests/Internal/Process/ServiceTasks/Legacy/EformidlingServiceTaskLegacyTests.cs (1)

3-3: Test updates align with expanded IInstanceClient.GetInstance overload
Setup/Verify now match the new signature and remain strict.

Also applies to: 34-38, 47-50

test/Altinn.App.Api.Tests/Controllers/PdfControllerTests.cs (1)

52-62: GetInstance mock updated correctly for new signature
[such changes are mechanical; this one looks consistent with the new API surface.]

test/Altinn.App.Api.Tests/Controllers/FileScanControllerTests.cs (1)

72-82: LGTM - Mock setup correctly updated for new interface signature.

The mock setup properly accommodates the expanded GetInstance signature with StorageAuthenticationMethod? and CancellationToken parameters using It.IsAny<> matchers.

test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs (1)

158-168: LGTM - Mock setup correctly updated for expanded GetInstance signature.

The mock setup properly includes StorageAuthenticationMethod? and CancellationToken parameters with It.IsAny<> matchers, maintaining test coverage while accommodating the interface changes.

test/Altinn.App.Api.Tests/Controllers/ValidateControllerValidateDataTests.cs (1)

242-253: LGTM - Mock setup correctly updated for new interface signature.

The mock setup appropriately accommodates the expanded GetInstance signature while preserving the specific parameter matching for app, org, instanceOwnerId, and testScenario.InstanceId.

test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (1)

68-86: LGTM - Mock setups correctly updated for new IDataService signatures.

The mock configurations for GetByType<PaymentInformation> and InsertJsonObject properly include the StorageAuthenticationMethod parameter, maintaining alignment with the updated interface.

src/Altinn.App.Core/Internal/Data/IDataService.cs (1)

19-25: Interface expansion looks good.

The addition of optional StorageAuthenticationMethod? parameters maintains backward compatibility while enabling per-call authentication context. This aligns with the broader pattern across storage operations in this PR.

Based on learnings, optional parameters are acceptable in this repo since consumers upgrade via NuGet and rebuild.

src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsCompleteWebhookPayload.cs (2)

8-39: Model structure looks good.

The webhook payload model is well-designed with sealed classes, required properties, and proper JSON serialization attributes.


44-52: Payload data class is appropriately minimal.

The TODO comment for adding properties in the future is reasonable. The current implementation captures the essential PaymentId needed for webhook processing.

src/Altinn.App.Core/Internal/Data/DataService.cs (1)

24-42: Good defensive null handling in GetByType.

The early return with (Guid.Empty, default) when no data element is found is a good improvement over potentially throwing exceptions.

src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (2)

25-32: Class visibility and authentication refactoring looks good.

Making InstanceClient internal and using IAuthenticationTokenResolver with a default authentication method is a clean approach that enables per-call authentication overrides.


61-87: Good implementation of per-call authentication and cancellation support.

The pattern of accepting optional authenticationMethod and CancellationToken parameters, with proper propagation to HTTP operations and deserialization, is well implemented.

test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt (1)

3184-3197: Skip: snapshot file update (public API verification output).
This *.verified.txt is a generated verification snapshot; I’m not providing review suggestions on it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Altinn.App.Core/Internal/Data/DataService.cs (1)

123-139: Potential resource leak: dataStream is not disposed.

The Stream returned by GetBinaryData should be disposed after deserialization to avoid resource leaks. Consider wrapping in a using statement or calling await using.

🔎 Proposed fix
     private async Task<T> GetDataForDataElement<T>(
         InstanceIdentifier instanceIdentifier,
         DataElement dataElement,
         StorageAuthenticationMethod? authenticationMethod = null
     )
     {
-        Stream dataStream = await _dataClient.GetBinaryData(
+        await using Stream dataStream = await _dataClient.GetBinaryData(
             instanceIdentifier.InstanceOwnerPartyId,
             instanceIdentifier.InstanceGuid,
             new Guid(dataElement.Id),
             authenticationMethod
         );
         if (dataStream == null)
         {
             throw new ArgumentNullException(
                 $"Failed to retrieve binary dataStream from dataClient using dataElement.Id {dataElement.Id}."
             );
         }

         return await JsonSerializer.DeserializeAsync<T>(dataStream, _jsonSerializerOptions)
             ?? throw new InvalidOperationException($"Unable to deserialize data from dataStream to type {nameof(T)}.");
     }
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (1)

132-149: CancellationToken not propagated to QueryInstances.

The private QueryInstances method is called from GetInstances which accepts a CancellationToken, but ct is not passed through. This means pagination queries cannot be cancelled.

🔎 Proposed fix
-    private async Task<QueryResponse<Instance>> QueryInstances(string token, string url)
+    private async Task<QueryResponse<Instance>> QueryInstances(string token, string url, CancellationToken ct = default)
     {
         using var activity = _telemetry?.StartQueryInstancesActivity();
-        HttpResponseMessage response = await _client.GetAsync(token, url);
+        HttpResponseMessage response = await _client.GetAsync(token, url, cancellationToken: ct);

         if (response.StatusCode == HttpStatusCode.OK)
         {
             QueryResponse<Instance> queryResponse = await JsonSerializerPermissive.DeserializeAsync<
                 QueryResponse<Instance>
-            >(response.Content);
+            >(response.Content, ct);
             return queryResponse;
         }
         else
         {
             _logger.LogError("Unable to query instances from Platform Storage");
             throw await PlatformHttpException.CreateAsync(response);
         }
     }

And update the call sites in GetInstances:

-        QueryResponse<Instance> queryResponse = await QueryInstances(token, apiUrl);
+        QueryResponse<Instance> queryResponse = await QueryInstances(token, apiUrl, ct);
         ...
-            queryResponse = await QueryInstances(token, queryResponse.Next);
+            queryResponse = await QueryInstances(token, queryResponse.Next, ct);
♻️ Duplicate comments (5)
test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs (1)

427-433: Fix mock signatures to use nullable StorageAuthenticationMethod? parameter.

Multiple mock setups for IInstanceClient.GetInstance(Instance, ...) use non-nullable It.IsAny<StorageAuthenticationMethod>(), but the interface defines the parameter as nullable StorageAuthenticationMethod?. This signature mismatch could prevent the mock from matching actual calls. Update all four occurrences (lines 430, 804, 1028, 1259) to use It.IsAny<StorageAuthenticationMethod?>(), matching the correct pattern shown at line 579.

🔎 Proposed fix

Apply this change at lines 430, 804, 1028, and 1259:

-                    It.IsAny<StorageAuthenticationMethod>(),
+                    It.IsAny<StorageAuthenticationMethod?>(),

Also applies to: 801-807, 1025-1031, 1256-1262

test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (1)

502-508: Hardcoded JWT literal still triggers secret scanning.

As noted in previous review comments, the hardcoded JWT-like string triggers gitleaks failures. Consider replacing with a helper function or non-JWT placeholder as previously suggested.

test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs (1)

550-560: Fix Moq argument matching for list instances.

As noted in previous review comments, using new() in Setup/Verify for List<InstanceEvent> relies on reference equality and will not match a different empty list instance created by the SUT. Replace with It.Is<List<InstanceEvent>>(e => e.Count == 0) or It.IsAny<List<InstanceEvent>>() as previously suggested.

Also applies to: 569-579

src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (1)

95-107: Critical: Guard null webhook key, use baseUrl variable, and fix CSharpier formatting.

This is a repeat of the previous review. Three issues must be fixed:

  1. Security: Setting Authorization to a null/empty WebhookCallbackKey creates an unauthenticated webhook that can be spoofed by attackers.
  2. Correctness: Line 103 uses _generalSettings.FormattedExternalAppBaseUrl(_app) instead of the baseUrl variable computed at line 51, potentially breaking webhook URLs.
  3. Formatting: Remove parentheses from new NetsNotifications() and new NetsWebHook() per CSharpier standards.
🔎 Required fix
-            Notifications = new NetsNotifications()
-            {
-                WebHooks =
-                [
-                    new NetsWebHook()
+            Notifications =
+                string.IsNullOrWhiteSpace(_settings.WebhookCallbackKey)
+                    ? null
+                    : new NetsNotifications
                     {
-                        Authorization = _settings.WebhookCallbackKey,
-                        Url =
-                            $"{_generalSettings.FormattedExternalAppBaseUrl(_app)}instances/{instance.Id}/payment/nets-webhook-listener",
-                        EventName = "payment.checkout.completed",
-                    },
-                ],
-            },
+                        WebHooks =
+                        [
+                            new NetsWebHook
+                            {
+                                Authorization = _settings.WebhookCallbackKey,
+                                Url = $"{baseUrl}instances/{instance.Id}/payment/nets-webhook-listener",
+                                EventName = "payment.checkout.completed",
+                            },
+                        ],
+                    },
test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs (1)

120-122: Use nullable StorageAuthenticationMethod? consistently in verifications.

Lines 120, 186, 251, 318, and 384 use It.IsAny<StorageAuthenticationMethod>() (non-nullable) while other verifications in this file use It.IsAny<StorageAuthenticationMethod?>() (nullable). Since the method signature likely accepts the nullable form, all verifications should match for consistency and correctness.

🔎 Recommended fix
-                    It.IsAny<StorageAuthenticationMethod>(),
+                    It.IsAny<StorageAuthenticationMethod?>(),
                     It.IsAny<CancellationToken>()

Also applies to lines 186, 251, 318, 384.

🧹 Nitpick comments (10)
test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs (3)

40-42: Dispose the ActivitySource to prevent resource leaks.

The ActivitySource is instantiated but never disposed. Consider implementing IDisposable or IAsyncDisposable on FakeHttpMessageHandler to properly dispose of the ActivitySource.

🔎 Proposed fix
-public class FakeHttpMessageHandler : HttpMessageHandler
+public class FakeHttpMessageHandler : HttpMessageHandler, IDisposable
 {
     public ActivitySource ActivitySource { get; } = new("Altinn.App.Tests.Common.Fixtures.FakeHttpMessageHandler");
+
+    protected override void Dispose(bool disposing)
+    {
+        if (disposing)
+        {
+            ActivitySource?.Dispose();
+        }
+        base.Dispose(disposing);
+    }

199-231: Pass the CancellationToken to the endpoint handler.

The cancellationToken parameter received in SendAsync is not propagated to the endpoint's Handle method. While the current Handle signature doesn't accept a CancellationToken, consider either:

  1. Adding CancellationToken to the Handle signature in the Endpoint abstract class, or
  2. Using the token to enable cancellation of endpoint matching/handling

54-64: Add input validation for constructor parameters.

Consider validating that minimumCalls is non-negative and that urlPattern is not null or empty to catch configuration errors early in tests.

🔎 Proposed validation
 public UrlPatternEndpoint(
     HttpMethod method,
     string urlPattern,
     Func<HttpRequestMessage, Task<HttpResponseMessage>> handler,
     int minimumCalls
 )
 {
+    ArgumentNullException.ThrowIfNull(method);
+    ArgumentException.ThrowIfNullOrWhiteSpace(urlPattern);
+    ArgumentNullException.ThrowIfNull(handler);
+    ArgumentOutOfRangeException.ThrowIfNegative(minimumCalls);
+
     _method = method;
     _urlPattern = urlPattern;
     _handler = handler;
     _minimumCalls = minimumCalls;
 }
test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs (3)

151-170: Consider thread safety for the mock registry.

The _mocks dictionary is accessed without synchronization. If Mock<T>() is called concurrently from multiple threads during test setup, race conditions could occur.

🔎 Proposed fix using ConcurrentDictionary
-private readonly Dictionary<Type, Mock> _mocks = [];
+private readonly ConcurrentDictionary<Type, Mock> _mocks = new();

 public Mock<T> Mock<T>()
     where T : class
 {
     var type = typeof(T);
-    if (_mocks.TryGetValue(type, out var existingMock))
-    {
-        return (Mock<T>)existingMock;
-    }
-    var mock = new Mock<T>(MockBehavior.Strict);
-    _mocks[type] = mock;
-    Services.TryAddSingleton(mock.Object);
-    return mock;
+    return (Mock<T>)_mocks.GetOrAdd(type, _ =>
+    {
+        var mock = new Mock<T>(MockBehavior.Strict);
+        Services.TryAddSingleton(mock.Object);
+        return mock;
+    });
 }

293-317: Consider potential constructor exceptions.

The WrappedServiceProvider constructor performs significant initialization (building the service provider, configuring OpenTelemetry) that could throw exceptions. While this is acceptable for test infrastructure, ensure that calling code handles potential failures gracefully.


433-444: Ensure FakeHttpMessageHandler disposal.

The FakeHttpMessageHandler contains an ActivitySource that should be disposed. Consider either:

  1. Disposing FakeHttpMessageHandler explicitly in Dispose/DisposeAsync, or
  2. Ensuring FakeHttpMessageHandler implements IDisposable and is registered with a disposal scope
🔎 Proposed disposal
 public void Dispose()
 {
     _serviceProvider.Dispose();
     if (!_dumpedTracesAndMetrics)
     {
         _dumpedTracesAndMetrics = true;
         DumpTracesAndMetrics();
     }
+    _serviceCollection.FakeHttpMessageHandler.Dispose();
     _loggerProvider.Dispose();
     _tracerProvider.Dispose();
     _meterProvider.Dispose();
 }
src/Altinn.App.Core/Internal/Data/IDataService.cs (1)

1-69: Extending IDataService with StorageAuthenticationMethod looks consistent

Adding optional StorageAuthenticationMethod? parameters to GetByType, GetById, InsertJsonObject, UpdateJsonObject, and DeleteById aligns this interface with the rest of the storage APIs and keeps call sites source‑compatible by appending the parameters with defaults. You might optionally add <param> XML docs for the new authenticationMethod arguments on InsertJsonObject/UpdateJsonObject for consistency with the other methods, but the interface itself looks good.

test/Altinn.App.Api.Tests/Mocks/InstanceClientMockSi.cs (1)

4-43: InstanceClientMockSi correctly mirrors extended IInstanceClient signatures

Updating the mock methods to include optional StorageAuthenticationMethod? and CancellationToken parameters keeps it aligned with IInstanceClient while preserving existing behavior (the new parameters are safely ignored). If you ever need to test cancellation behavior, you could thread the ct into ReadJsonFile/WriteJsonFile, but that’s not required for current usage.

Also applies to: 70-75, 84-91, 106-110, 142-147, 226-230, 235-240, 264-270, 296-302, 333-339, 376-382, 419-425, 457-461

src/Altinn.App.Core/Constants/AltinnTaskTypes.cs (1)

1-47: LGTM! Clean centralization of task type constants.

The internal static class appropriately centralizes task type identifiers. Documentation is clear and the internal visibility is correct for framework-internal use.

Optional: Minor documentation improvement

Line 24 reads "push update the instance" – consider "push updates to the instance" or "push an update to the instance" for clarity.

src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (1)

303-309: Consider adding error handling for process advancement failures.

Lines 303-309 call RunProcessNext without try-catch. If the HTTP call fails, the webhook handler will throw an unhandled exception. Consider wrapping in try-catch and logging the failure while still returning success for the webhook (since payment status was updated successfully).

🔎 Optional improvement
 if (
     paymentStatus == PaymentStatus.Paid
     && instance.Process.CurrentTask.AltinnTaskType == AltinnTaskTypes.Payment
 )
 {
-    await RunProcessNext(instance, storageAuthenticationMethod);
+    try
+    {
+        await RunProcessNext(instance, storageAuthenticationMethod);
+    }
+    catch (Exception ex)
+    {
+        _logger.LogError(ex, "Failed to advance process for instance {InstanceId} after webhook payment completion.", instance.Id);
+        // Don't rethrow - payment status update succeeded, process can be advanced manually
+    }
 }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/Altinn.App.Api/Controllers/PaymentController.cs (1)

185-187: Add null guards for Process and CurrentTask in webhook listener.

Lines 185-187 access instance.Process.CurrentTask.ElementId without checking if Process or CurrentTask is null. This is inconsistent with the defensive checks added to GetPaymentInformation (lines 69-72) and could throw NullReferenceException if the webhook arrives when the instance is not in an active task state.

🔎 Proposed fix
+        if (instance.Process?.CurrentTask?.ElementId == null)
+        {
+            _logger.LogWarning(
+                "Instance {InstanceGuid} has no current task. Cannot process Nets webhook callback.",
+                instanceGuid
+            );
+            return BadRequest("Instance has no current task");
+        }
+
         AltinnPaymentConfiguration? paymentConfiguration = _processReader
             .GetAltinnTaskExtension(instance.Process.CurrentTask.ElementId)
             ?.PaymentConfiguration;
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (1)

321-343: Add timeout to HttpClient in RunProcessNext.

The HttpClient created on line 324 uses the default timeout (100 seconds), which may be excessive for webhook-driven process advancement. A hung external app could block webhook processing indefinitely. Error handling has been improved with detailed logging and PaymentException, but an explicit timeout (e.g., 30 seconds) would further improve resilience.

🔎 Recommended timeout
         JwtToken token = await _authenticationTokenResolver.GetAccessToken(storageAuthenticationMethod);
         using var client = _httpClientFactory.CreateClient();
+        client.Timeout = TimeSpan.FromSeconds(30);
         client.BaseAddress = new Uri(_generalSettings.FormattedExternalAppBaseUrl(_app));

Based on past review comments.

🧹 Nitpick comments (2)
test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs (2)

162-162: Consider thread safety for concurrent setup scenarios.

_endpoints is not thread-safe. While typical test fixtures register endpoints before execution (avoiding concurrency), if RegisterEndpoint is called concurrently with SendAsync, race conditions could occur. This is likely acceptable for test fixtures but consider using ConcurrentBag<Endpoint> or documenting the thread-safety expectations if concurrent registration becomes a use case.


177-228: Use the imported MediaTypeHeaderValue for consistency.

Lines 195 and 221 use the fully qualified System.Net.Http.Headers.MediaTypeHeaderValue when MediaTypeHeaderValue is already available via the import on Line 4.

🔎 Proposed simplification
                         Content = new StringContent(content)
                         {
-                            Headers = { ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType) },
+                            Headers = { ContentType = new MediaTypeHeaderValue(contentType) },
                         },

And similarly on line 221:

                         Content = new StringContent(System.Text.Json.JsonSerializer.Serialize(responseObject))
                         {
                             Headers =
                             {
-                                ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"),
+                                ContentType = new MediaTypeHeaderValue("application/json"),
                             },
                         },
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 999d9d2 and cce4669.

📒 Files selected for processing (14)
  • src/Altinn.App.Api/Controllers/PaymentController.cs (4 hunks)
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs (2 hunks)
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (4 hunks)
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs (2 hunks)
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (4 hunks)
  • src/Altinn.App.Core/Features/Telemetry/Telemetry.Payment.cs (1 hunks)
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs (13 hunks)
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs (41 hunks)
  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs (2 hunks)
  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs (9 hunks)
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (15 hunks)
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs (16 hunks)
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs (1 hunks)
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Altinn.App.Core/Features/Telemetry/Telemetry.Payment.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
🧠 Learnings (16)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-09-07T20:03:48.030Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1463
File: test/Altinn.App.SourceGenerator.Tests/DiagnosticTests.RunJsonError.verified.txt:1-21
Timestamp: 2025-09-07T20:03:48.030Z
Learning: In Altinn.App.SourceGenerator.Tests, the path C:\temp\config\applicationmetadata.json is a hardcoded test fixture path required by Roslyn and is used consistently across all operating systems, not an actual filesystem path that varies by OS.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
📚 Learning: 2025-08-29T05:32:47.222Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/AppFixture.cs:958-958
Timestamp: 2025-08-29T05:32:47.222Z
Learning: In Altinn.App.Integration.Tests, the maintainer prefers fail-fast behavior (Environment.FailFast) when critical test infrastructure components like log reading fail, rather than graceful error handling, because diagnostics are essential for debugging test failures.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
📚 Learning: 2025-08-22T13:35:09.565Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Authentication_auth=OldUser.verified.txt:16-38
Timestamp: 2025-08-22T13:35:09.565Z
Learning: Test data in Altinn App Integration Tests snapshots (like SSNs, addresses, phone numbers in BasicAppTests.Authentication snapshots) are synthetic/fake values created for testing purposes, not real PII that needs to be scrubbed from version control.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs
📚 Learning: 2025-10-22T08:35:24.567Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1532
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:2451-2451
Timestamp: 2025-10-22T08:35:24.567Z
Learning: In Altinn/app-lib-dotnet, IDataClient is generally not implemented by customers; it’s considered internal for DI. As of 2025-10-22, GetBinaryDataStream had not been released yet, so its parameter reordering (adding optional TimeSpan? timeout before CancellationToken) is acceptable without a compatibility shim; recommend documenting the new parameter and using named arguments in samples.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
📚 Learning: 2025-09-21T09:04:43.977Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: src/Altinn.App.Core/Features/IInstanceDataAccessor.cs:36-41
Timestamp: 2025-09-21T09:04:43.977Z
Learning: The IInstanceDataAccessor interface in Altinn.App.Core is designed for consumption only - users are not expected to implement this interface themselves, only use framework-provided implementations.

Applied to files:

  • test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/test/**/*.cs : Mock external dependencies with Moq in tests

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs
  • test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
🧬 Code graph analysis (6)
test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs (1)
src/Altinn.App.Core/Constants/AltinnTaskTypes.cs (1)
  • AltinnTaskTypes (6-47)
src/Altinn.App.Api/Controllers/PaymentController.cs (5)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs (1)
  • NetsPaymentSettings (8-88)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (3)
  • Task (48-153)
  • Task (156-165)
  • Task (168-211)
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (7)
  • Task (55-151)
  • Task (154-238)
  • Task (240-319)
  • Task (321-343)
  • Task (346-363)
  • Task (366-381)
  • Task (383-414)
src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (5)
  • Task (15-19)
  • Task (24-28)
  • Task (30-34)
  • Task (39-39)
  • Task (44-44)
src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsCompleteWebhookPayload.cs (1)
  • NetsCompleteWebhookPayload (8-39)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (1)
src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsNotifications.cs (2)
  • NetsNotifications (6-12)
  • NetsWebHook (14-36)
src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs (1)
src/Altinn.App.Core/Constants/AltinnTaskTypes.cs (1)
  • AltinnTaskTypes (6-47)
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (4)
src/Altinn.App.Core/Configuration/GeneralSettings.cs (2)
  • GeneralSettings (9-86)
  • FormattedExternalAppBaseUrl (61-68)
src/Altinn.App.Core/Features/Payment/Exceptions/PaymentException.cs (2)
  • PaymentException (8-16)
  • PaymentException (14-15)
src/Altinn.App.Core/Features/Payment/Models/PaymentInformation.cs (1)
  • PaymentInformation (6-27)
src/Altinn.App.Core/Features/Telemetry/TelemetryActivityExtensions.cs (1)
  • Errored (421-428)
test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs (2)
test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs (1)
  • ApplicationMetadata (1385-1417)
test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs (1)
  • ApplicationMetadata (209-215)
🪛 GitHub Actions: Build and Test on windows, macos and ubuntu
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs

[error] 19-19: IDE0052: Private member '_app' can be removed as the value assigned to it is never read.

🪛 GitHub Check: Run dotnet build and test (macos-latest)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs

[failure] 19-19:
Private member 'NetsPaymentProcessor._app' can be removed as the value assigned to it is never read (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0052)


[failure] 19-19:
Private member 'NetsPaymentProcessor._app' can be removed as the value assigned to it is never read (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0052)

🪛 GitHub Check: Run dotnet build and test (ubuntu-latest)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs

[failure] 19-19:
Private member 'NetsPaymentProcessor._app' can be removed as the value assigned to it is never read (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0052)


[failure] 19-19:
Private member 'NetsPaymentProcessor._app' can be removed as the value assigned to it is never read (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0052)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Static code analysis
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (21)
test/Altinn.App.Clients.Fiks.Tests/TestFixture.cs (1)

10-10: LGTM! Good refactoring to centralized constants.

The migration to use AltinnTaskTypes.FiksArkiv improves maintainability by consolidating task type identifiers in a single location.

Also applies to: 90-91

src/Altinn.App.Clients.Fiks/FiksArkiv/FiksArkivHost.cs (1)

8-8: LGTM! Consistent use of centralized constants.

The change to use AltinnTaskTypes.FiksArkiv aligns with the test fixture update and centralizes task type identifiers, improving code maintainability.

Also applies to: 269-274

test/Altinn.App.Api.Tests/Controllers/InstancesController_ActiveInstancesTests.cs (1)

30-38: LGTM! Mock setups and verifications correctly updated.

All GetInstances mock setups and verifications have been consistently updated to include the new StorageAuthenticationMethod? and CancellationToken parameters. The nullable form It.IsAny<StorageAuthenticationMethod?>() correctly matches the actual parameter type, addressing the concern from the previous review.

Also applies to: 50-58, 94-103, 115-123, 159-167, 180-188, 222-230, 246-254, 289-297, 313-321, 355-363, 379-387

test/Altinn.App.Api.Tests/Controllers/InstancesController_CopyInstanceTests.cs (1)

158-168: LGTM! Test mocks correctly updated for extended interface signatures.

The test file has been properly updated to reflect the addition of optional StorageAuthenticationMethod? and CancellationToken parameters across IInstanceClient and IDataClient methods. All mock setups and verifications consistently use It.IsAny<StorageAuthenticationMethod?>() with the nullable matcher, correctly matching the interface signatures. The changes maintain test coverage across all CopyInstance scenarios (authorization checks, binary data handling, excluded types, etc.).

Also applies to: 215-224, 272-281, 328-337, 402-434, 457-477, 551-582, 603-625, 664-718, 776-807, 828-879, 1000-1032, 1052-1078, 1231-1263, 1284-1309

test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs (2)

31-40: Constructor refactored to accept ApplicationMetadata directly.

The constructor signature change from optional string parameters to requiring ApplicationMetadata is a breaking change for test code, but aligns with the PR's goal to support webhook authentication. The defensive initialization of Title and DataTypes ensures test fixtures have valid state.


48-48: Good defensive practice.

Assigning instance.AppId from AppMetadata.Id when null ensures consistency in test fixtures.

test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs (2)

96-141: Well-implemented pattern matching logic.

The MatchUrlPattern method correctly handles both wildcard (*) and parameter ({param}) matching. The implementation properly:

  • Advances through wildcards until / or end
  • Skips parameter names in patterns while consuming path segments in URLs
  • Ensures full match by verifying j == url.Length at the end

The use of ReadOnlySpan<char> is efficient for avoiding unnecessary allocations during pattern matching.


230-273: Solid implementation of request handling and diagnostics.

The SendAsync implementation provides excellent test support:

  • Content buffering (line 241) enables multiple reads
  • Activity tracking provides observability for debugging test failures
  • Clear error messages distinguish between no matches and ambiguous matches
  • Thread-safe RequestResponses collection captures all interactions
test/Altinn.App.Core.Tests/Internal/Process/ProcessEventHandlingTests.cs (2)

2-2: LGTM: Enum-based task types improve type safety.

The migration from string literals to AltinnTaskTypes constants enhances maintainability and reduces the risk of typos in task type references.

Also applies to: 247-247, 318-318, 462-462


118-128: LGTM: Mock updates correctly reflect expanded IInstanceClient signature.

The addition of optional StorageAuthenticationMethod? and CancellationToken parameters to all UpdateProcessAndEvents mock setups and verifications is consistent and correct, using It.IsAny<T>() to match any value passed by the SUT.

Also applies to: 137-148, 183-193, 202-213, 254-264, 273-284, 325-335, 344-355, 396-406, 415-426, 487-497, 507-518, 544-554, 563-574

src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (2)

60-65: LGTM: Webhook key validation prevents unauthenticated callbacks.

The early validation ensures that webhook registration only occurs when a secure key is configured, improving security posture.


101-112: LGTM: Webhook registration structure is correct.

The NetsNotifications payload correctly configures the webhook with authorization header, callback URL, and event subscription for payment completion.

test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (2)

493-502: LGTM: Dummy JWT helper addresses secret-scanning concerns.

The CreateDummyJwt() helper programmatically generates a JWT-like token for testing, avoiding hardcoded JWT literals that trigger secret scanners.

Also applies to: 513-515


23-23: LGTM: Migration to IAuthenticationTokenResolver is consistent.

All test methods correctly use the new IAuthenticationTokenResolver mock instead of the deprecated IUserTokenProvider, aligning with the authentication refactoring across the codebase.

Also applies to: 57-63, 88-94, 128-134, 207-213, 243-249, 292-298, 366-372, 415-421, 469-475

src/Altinn.App.Api/Controllers/PaymentController.cs (3)

38-46: LGTM: Service locator pattern simplifies DI in controller.

Resolving dependencies from IServiceProvider within the constructor is acceptable here, especially given the growing number of dependencies for webhook support.


69-72: LGTM: Defensive null checks prevent NPE in GetPaymentInformation.

The early guard for instance.Process?.CurrentTask?.ElementId prevents NullReferenceException if the instance is not in an active task state.


158-169: LGTM: Constant-time comparison mitigates timing attacks.

Using CryptographicOperations.FixedTimeEquals prevents attackers from extracting the webhook key through timing analysis.

test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs (2)

19-42: LGTM: DI-driven testing with HTTP stubs improves realism.

The refactoring from direct INetsClient mocking to MockedServiceCollection with HttpMessageHandler-based endpoint stubs more closely simulates production behavior and enables verification of actual HTTP request payloads. This aligns with the established testing patterns in the codebase.

As per coding guidelines and learnings.

Also applies to: 57-88, 124-151


29-29: LGTM: Test data now includes webhook key and uses task type constants.

Including WebhookCallbackKey in test settings and using AltinnTaskTypes.Payment instead of string literals improves test coverage and type safety.

Also applies to: 302-302

src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (2)

61-150: LGTM: Payment start flow enhanced with validation, telemetry, and zero-sum handling.

The expanded StartPayment method correctly validates IOrderDetailsCalculator presence, wraps order calculation in telemetry, selects the appropriate processor, and skips processor invocation for zero-sum orders. Error handling via activity erroring is consistent with established patterns.


240-319: LGTM: Webhook handler integrates payment status check and process advancement.

The HandlePaymentCompletedWebhook method correctly retrieves payment information using ServiceOwner authentication, updates status via the processor, persists changes, and advances the process when payment is complete and task type matches AltinnTaskTypes.Payment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (1)

279-310: Inconsistent error handling returns null instead of throwing.

Unlike other methods in this class that throw PlatformHttpException on non-OK responses, UpdateReadStatus returns null when the update fails (line 308). This inconsistency requires callers to handle both exceptions and null returns, and the #nullable disable/#nullable restore pragmas suppress compiler warnings that would otherwise catch this.

Consider aligning with the pattern used in other methods:

🔎 Proposed fix
     if (response.StatusCode == HttpStatusCode.OK)
     {
         Instance instance = await JsonSerializerPermissive.DeserializeAsync<Instance>(response.Content, ct);
         return instance;
     }

-    _logger.LogError(
-        $"Could not update read status for instance {instanceOwnerPartyId}/{instanceGuid}. Request failed with status code {response.StatusCode}"
-    );
-#nullable disable
-    return null;
-#nullable restore
+    _logger.LogError(
+        $"Could not update read status for instance {instanceOwnerPartyId}/{instanceGuid}. Request failed with status code {response.StatusCode}"
+    );
+    throw await PlatformHttpException.CreateAsync(response);
♻️ Duplicate comments (3)
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (1)

328-328: Security scanner warning appears to be a false positive.

The GitHub Advanced Security scanner flagged this line for potential XSS. However, this code serializes a Substatus object to JSON for an HTTP request body sent to the Platform Storage API, not for rendering in HTML or JavaScript. The serialized content does not flow to a web page or script context where XSS would be a concern.

src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (1)

322-344: Add timeout to prevent indefinite hangs.

Error handling has been properly added (lines 332-343), but the HTTP client still lacks a timeout. If the process advancement endpoint is slow or unresponsive, the webhook handler will hang indefinitely.

🔎 Recommended fix
 private async Task RunProcessNext(Instance instance, StorageAuthenticationMethod storageAuthenticationMethod)
 {
     JwtToken token = await _authenticationTokenResolver.GetAccessToken(storageAuthenticationMethod);
     using var client = _httpClientFactory.CreateClient();
+    client.Timeout = TimeSpan.FromSeconds(30);
     client.BaseAddress = new Uri(_generalSettings.FormattedExternalAppBaseUrl(_app));
     client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(
         "Bearer",
         token.Value
     );
     var response = await client.PutAsync($"instances/{instance.Id}/process/next", null);
     if (!response.IsSuccessStatusCode)
     {
         _logger.LogError(
             "Failed to advance process for instance {InstanceId} after payment completed webhook. Status code: {StatusCode}\n\n{content}",
             instance.Id,
             response.StatusCode,
             await response.Content.ReadAsStringAsync()
         );
         throw new PaymentException(
             $"Failed to advance process for instance {instance.Id} after payment completed webhook."
         );
     }
 }
test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs (1)

42-46: Dispose the ActivitySource to prevent resource leaks.

ActivitySource implements IDisposable but is never disposed. While it's declared as a static field, it should still be properly disposed to prevent resource leaks, especially in test scenarios where the fixture may be created multiple times.

Consider implementing IDisposable on FakeHttpMessageHandler and disposing the ActivitySource during cleanup, or use a static disposal pattern with proper lifecycle management.

Based on coding guidelines, "Remember to dispose IDisposable/IAsyncDisposable instances".

🧹 Nitpick comments (5)
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (2)

25-25: Consider sealing the class.

Since this class is internal and implements an interface, consider marking it sealed to prevent unintended inheritance.

🔎 Proposed change
-internal class InstanceClient : IInstanceClient
+internal sealed class InstanceClient : IInstanceClient

As per coding guidelines: "Use sealed for classes unless inheritance is considered a valid use-case."


244-244: Consider using the provided cancellation token in error logging.

While reading the error response with CancellationToken.None ensures error details are logged even when the operation is cancelled, it's inconsistent with cancellation token usage elsewhere in the file. Consider using ct for consistency, or document why CancellationToken.None is intentional here.

src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (1)

30-38: Good security design: verify payment status with provider.

The documentation correctly emphasizes that the implementation should call the provider for status verification rather than trusting the webhook payload alone. This is a critical security practice.

Consider adding a CancellationToken parameter for consistency with the other async methods in this interface. This would allow callers to cancel long-running webhook processing.

src/Altinn.App.Api/Controllers/PaymentController.cs (2)

38-46: Consider using standard constructor injection instead of IServiceProvider.

Using IServiceProvider for resolving dependencies is an anti-pattern known as "service locator." Standard constructor injection is preferred for testability and explicit dependency declaration.

However, if this pattern is intentionally used to defer resolution or handle optional services, please document that reasoning.

🔎 Suggested refactor using constructor injection
-    public PaymentController(IServiceProvider serviceProvider)
+    public PaymentController(
+        IInstanceClient instanceClient,
+        IProcessReader processReader,
+        ILogger<PaymentController> logger,
+        IOptions<NetsPaymentSettings> netsPaymentSettings,
+        IPaymentService paymentService,
+        AppImplementationFactory appImplementationFactory)
     {
-        _instanceClient = serviceProvider.GetRequiredService<IInstanceClient>();
-        _processReader = serviceProvider.GetRequiredService<IProcessReader>();
-        _logger = serviceProvider.GetRequiredService<ILogger<PaymentController>>();
-        _netsPaymentSettings = serviceProvider.GetRequiredService<IOptions<NetsPaymentSettings>>();
-        _paymentService = serviceProvider.GetRequiredService<IPaymentService>();
-        _appImplementationFactory = serviceProvider.GetRequiredService<AppImplementationFactory>();
+        _instanceClient = instanceClient;
+        _processReader = processReader;
+        _logger = logger;
+        _netsPaymentSettings = netsPaymentSettings;
+        _paymentService = paymentService;
+        _appImplementationFactory = appImplementationFactory;
     }

148-156: Consider returning 503 instead of 404 for missing configuration.

Returning 404 "Not Found" when WebhookCallbackKey is not configured could inadvertently signal to an attacker that the endpoint exists but is misconfigured. A 503 "Service Unavailable" might be more appropriate for a configuration issue, as it doesn't reveal as much about the system state.

Alternatively, you could return 200 OK (silent accept) to avoid giving any signal to potential attackers, since the webhook caller (Nets) won't act differently based on the response.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cce4669 and 3b89187.

📒 Files selected for processing (21)
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs
  • src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Api.Tests/Helpers/Patch/PatchServiceTests.cs
  • test/Altinn.App.Core.Tests/Eformidling/Implementation/DefaultEFormidlingServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Action/UniqueSignatureAuthorizerTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ExpressionsExclusiveGatewayTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/Common/ProcessTaskDataLockerTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/PaymentProcessTaskTests.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
  • test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/Altinn.App.Core.Tests/Features/Action/UniqueSignatureAuthorizerTests.cs
  • src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/PaymentProcessTaskTests.cs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • test/Altinn.App.Core.Tests/Eformidling/Implementation/DefaultEFormidlingServiceTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ExpressionsExclusiveGatewayTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/Common/ProcessTaskDataLockerTests.cs
  • test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs
  • test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs
  • test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Api.Tests/Helpers/Patch/PatchServiceTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Core.Tests/Eformidling/Implementation/DefaultEFormidlingServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ExpressionsExclusiveGatewayTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/Common/ProcessTaskDataLockerTests.cs
  • test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs
  • test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs
  • test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Api.Tests/Helpers/Patch/PatchServiceTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
🧠 Learnings (21)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.
📚 Learning: 2025-10-22T08:35:24.567Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1532
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:2451-2451
Timestamp: 2025-10-22T08:35:24.567Z
Learning: In Altinn/app-lib-dotnet, IDataClient is generally not implemented by customers; it’s considered internal for DI. As of 2025-10-22, GetBinaryDataStream had not been released yet, so its parameter reordering (adding optional TimeSpan? timeout before CancellationToken) is acceptable without a compatibility shim; recommend documenting the new parameter and using named arguments in samples.

Applied to files:

  • test/Altinn.App.Core.Tests/Eformidling/Implementation/DefaultEFormidlingServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Core.Tests/Eformidling/Implementation/DefaultEFormidlingServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ExpressionsExclusiveGatewayTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/Common/ProcessTaskDataLockerTests.cs
  • test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs
  • test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Api.Tests/Helpers/Patch/PatchServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • test/Altinn.App.Core.Tests/Eformidling/Implementation/DefaultEFormidlingServiceTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs
  • test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Api.Tests/Helpers/Patch/PatchServiceTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/Common/ProcessTaskDataLockerTests.cs
  • test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Core.Tests/Internal/Process/ExpressionsExclusiveGatewayTests.cs
  • test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs
  • test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
📚 Learning: 2025-09-07T20:03:48.030Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1463
File: test/Altinn.App.SourceGenerator.Tests/DiagnosticTests.RunJsonError.verified.txt:1-21
Timestamp: 2025-09-07T20:03:48.030Z
Learning: In Altinn.App.SourceGenerator.Tests, the path C:\temp\config\applicationmetadata.json is a hardcoded test fixture path required by Roslyn and is used consistently across all operating systems, not an actual filesystem path that varies by OS.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
📚 Learning: 2025-09-21T09:04:43.977Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: src/Altinn.App.Core/Features/IInstanceDataAccessor.cs:36-41
Timestamp: 2025-09-21T09:04:43.977Z
Learning: The IInstanceDataAccessor interface in Altinn.App.Core is designed for consumption only - users are not expected to implement this interface themselves, only use framework-provided implementations.

Applied to files:

  • src/Altinn.App.Core/Internal/Data/IDataService.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • src/Altinn.App.Core/Internal/Data/DataService.cs
📚 Learning: 2025-08-22T13:35:09.565Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Authentication_auth=OldUser.verified.txt:16-38
Timestamp: 2025-08-22T13:35:09.565Z
Learning: Test data in Altinn App Integration Tests snapshots (like SSNs, addresses, phone numbers in BasicAppTests.Authentication snapshots) are synthetic/fake values created for testing purposes, not real PII that needs to be scrubbed from version control.

Applied to files:

  • src/Altinn.App.Core/Internal/Data/IDataService.cs
  • test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs
  • test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/test/**/*.cs : Mock external dependencies with Moq in tests

Applied to files:

  • test/Altinn.App.Core.Tests/Internal/Process/ExpressionsExclusiveGatewayTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs
  • test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
📚 Learning: 2025-08-27T06:58:28.126Z
Learnt from: cammiida
Repo: Altinn/app-lib-dotnet PR: 1451
File: src/Altinn.App.Api/Controllers/DataTagsController.cs:0-0
Timestamp: 2025-08-27T06:58:28.126Z
Learning: In the Altinn app-lib-dotnet codebase, validation endpoints follow a pattern where validation only runs when the ignoredValidators query parameter is provided (not null). This is a conscious design choice for consistency across validation endpoints.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Register services in DI container properly following existing patterns

Applied to files:

  • test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Include integration tests for platform service interactions

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Follow the AppFixture pattern for integration tests: Central orchestrator managing test lifecycle with feature-specific operations

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-08-29T05:32:47.222Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/AppFixture.cs:958-958
Timestamp: 2025-08-29T05:32:47.222Z
Learning: In Altinn.App.Integration.Tests, the maintainer prefers fail-fast behavior (Environment.FailFast) when critical test infrastructure components like log reading fail, rather than graceful error handling, because diagnostics are essential for debugging test failures.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Remember to dispose IDisposable/IAsyncDisposable instances

Applied to files:

  • test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs
🧬 Code graph analysis (14)
test/Altinn.App.Core.Tests/Eformidling/Implementation/DefaultEFormidlingServiceTests.cs (2)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • StorageAuthenticationMethod (623-628)
  • StorageAuthenticationMethod (630-631)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (5)
  • StorageAuthenticationMethod (85-85)
  • StorageAuthenticationMethod (88-88)
  • StorageAuthenticationMethod (91-92)
  • StorageAuthenticationMethod (95-96)
  • StorageAuthenticationMethod (100-103)
src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (5)
test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/Common/ProcessTaskDataLockerTests.cs (1)
  • Instance (184-195)
test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/PaymentProcessTaskTests.cs (1)
  • Instance (261-272)
test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs (1)
  • Instance (294-305)
test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (1)
  • ValidAltinnPaymentConfiguration (784-791)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnPaymentConfiguration.cs (1)
  • ValidAltinnPaymentConfiguration (24-38)
src/Altinn.App.Api/Controllers/PaymentController.cs (5)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs (1)
  • NetsPaymentSettings (8-88)
src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (5)
  • Task (15-19)
  • Task (24-28)
  • Task (34-38)
  • Task (43-43)
  • Task (48-48)
src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsCompleteWebhookPayload.cs (1)
  • NetsCompleteWebhookPayload (8-39)
src/Altinn.App.Core/Internal/Process/ProcessTasks/PaymentProcessTask.cs (1)
  • AltinnPaymentConfiguration (102-116)
src/Altinn.App.Core/Features/Payment/Exceptions/PaymentException.cs (2)
  • PaymentException (8-16)
  • PaymentException (14-15)
test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs (2)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • StorageAuthenticationMethod (623-628)
  • StorageAuthenticationMethod (630-631)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (5)
  • StorageAuthenticationMethod (85-85)
  • StorageAuthenticationMethod (88-88)
  • StorageAuthenticationMethod (91-92)
  • StorageAuthenticationMethod (95-96)
  • StorageAuthenticationMethod (100-103)
test/Altinn.App.Core.Tests/Internal/Process/ExpressionsExclusiveGatewayTests.cs (1)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (5)
  • StorageAuthenticationMethod (85-85)
  • StorageAuthenticationMethod (88-88)
  • StorageAuthenticationMethod (91-92)
  • StorageAuthenticationMethod (95-96)
  • StorageAuthenticationMethod (100-103)
test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/Common/ProcessTaskDataLockerTests.cs (1)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (5)
  • StorageAuthenticationMethod (85-85)
  • StorageAuthenticationMethod (88-88)
  • StorageAuthenticationMethod (91-92)
  • StorageAuthenticationMethod (95-96)
  • StorageAuthenticationMethod (100-103)
test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs (2)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • StorageAuthenticationMethod (623-628)
  • StorageAuthenticationMethod (630-631)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (5)
  • StorageAuthenticationMethod (85-85)
  • StorageAuthenticationMethod (88-88)
  • StorageAuthenticationMethod (91-92)
  • StorageAuthenticationMethod (95-96)
  • StorageAuthenticationMethod (100-103)
test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs (2)
test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs (4)
  • FakeHttpMessageHandler (42-283)
  • Verify (34-34)
  • Verify (152-160)
  • Verify (276-282)
test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs (2)
  • StorageClientInterceptor (12-332)
  • StorageClientInterceptor (31-40)
test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs (2)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • StorageAuthenticationMethod (623-628)
  • StorageAuthenticationMethod (630-631)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (5)
  • StorageAuthenticationMethod (85-85)
  • StorageAuthenticationMethod (88-88)
  • StorageAuthenticationMethod (91-92)
  • StorageAuthenticationMethod (95-96)
  • StorageAuthenticationMethod (100-103)
test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs (1)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • StorageAuthenticationMethod (623-628)
  • StorageAuthenticationMethod (630-631)
test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs (2)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • StorageAuthenticationMethod (623-628)
  • StorageAuthenticationMethod (630-631)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (5)
  • StorageAuthenticationMethod (85-85)
  • StorageAuthenticationMethod (88-88)
  • StorageAuthenticationMethod (91-92)
  • StorageAuthenticationMethod (95-96)
  • StorageAuthenticationMethod (100-103)
test/Altinn.App.Api.Tests/Helpers/Patch/PatchServiceTests.cs (2)
src/Altinn.App.Core/Internal/Data/InstanceDataUnitOfWork.cs (2)
  • StorageAuthenticationMethod (623-628)
  • StorageAuthenticationMethod (630-631)
src/Altinn.App.Core/Features/AuthenticationMethod.cs (5)
  • StorageAuthenticationMethod (85-85)
  • StorageAuthenticationMethod (88-88)
  • StorageAuthenticationMethod (91-92)
  • StorageAuthenticationMethod (95-96)
  • StorageAuthenticationMethod (100-103)
src/Altinn.App.Core/Internal/Data/DataService.cs (1)
src/Altinn.App.Core/Internal/Data/IDataService.cs (3)
  • Task (22-27)
  • Task (38-43)
  • Task (48-54)
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (2)
src/Altinn.App.Core/Internal/Instances/IInstanceClient.cs (14)
  • Task (16-23)
  • Task (28-32)
  • Task (37-41)
  • Task (46-50)
  • Task (55-60)
  • Task (71-77)
  • Task (92-97)
  • Task (108-114)
  • Task (125-131)
  • Task (145-151)
  • Task (165-171)
  • Task (184-199)
  • Task (213-227)
  • Task (238-244)
src/Altinn.App.Core/Helpers/JsonSerializerPermissive.cs (1)
  • JsonSerializerPermissive (10-48)
🪛 GitHub Check: Run dotnet build and test (ubuntu-latest)
test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs

[failure] 237-237:
An expression tree may not contain a call or invocation that uses optional arguments


[failure] 577-577:
An expression tree may not contain a call or invocation that uses optional arguments


[failure] 493-493:
An expression tree may not contain a call or invocation that uses optional arguments


[failure] 461-461:
An expression tree may not contain a call or invocation that uses optional arguments


[failure] 450-450:
An expression tree may not contain a call or invocation that uses optional arguments


[failure] 409-409:
An expression tree may not contain a call or invocation that uses optional arguments


[failure] 397-397:
An expression tree may not contain a call or invocation that uses optional arguments


[failure] 385-385:
An expression tree may not contain a call or invocation that uses optional arguments


[failure] 247-247:
An expression tree may not contain a call or invocation that uses optional arguments


[failure] 237-237:
An expression tree may not contain a call or invocation that uses optional arguments

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Static code analysis
  • GitHub Check: Run dotnet build and test (windows-latest)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Run dotnet build and test (macos-latest)
🔇 Additional comments (32)
test/Altinn.App.Core.Tests/Features/Validators/LegacyValidationServiceTests/ValidationServiceTests.cs (1)

297-311: LGTM! Test mock correctly updated for nullable authentication parameter.

The mock setup correctly reflects the API change that introduces optional StorageAuthenticationMethod? parameters. Using It.IsAny<StorageAuthenticationMethod?>() properly matches the new nullable signature while maintaining test functionality.

test/Altinn.App.Core.Tests/LayoutExpressions/FullTests/DataAccessorFixture.cs (1)

228-238: LGTM! Mock updated correctly for new authentication parameter.

The mock setup has been correctly updated to accept the new nullable StorageAuthenticationMethod? parameter, matching the updated GetDataBytes signature. Using It.IsAny<StorageAuthenticationMethod?>() is appropriate for a general test fixture, allowing tests to pass any authentication method (or null).

test/Altinn.App.Core.Tests/Internal/Process/ProcessTasks/Common/ProcessTaskDataLockerTests.cs (4)

52-61: LGTM! Mock verification correctly updated for nullable parameter.

The change from It.IsAny<StorageAuthenticationMethod>() to It.IsAny<StorageAuthenticationMethod?>() correctly reflects the API signature update to make the authentication method optional.


92-101: LGTM! Mock verification correctly updated for nullable parameter.

The change from It.IsAny<StorageAuthenticationMethod>() to It.IsAny<StorageAuthenticationMethod?>() correctly reflects the API signature update to make the authentication method optional.


132-141: LGTM! Mock verification correctly updated for nullable parameter.

The change from It.IsAny<StorageAuthenticationMethod>() to It.IsAny<StorageAuthenticationMethod?>() correctly reflects the API signature update to make the authentication method optional.


172-181: LGTM! Mock verification correctly updated for nullable parameter.

The change from It.IsAny<StorageAuthenticationMethod>() to It.IsAny<StorageAuthenticationMethod?>() correctly reflects the API signature update to make the authentication method optional.

test/Altinn.App.Core.Tests/Features/Signing/SigningReceiptServiceTests.cs (2)

130-140: LGTM! Correctly adapted to nullable StorageAuthenticationMethod.

The mock setup correctly uses It.IsAny<StorageAuthenticationMethod?>() to match the new optional nullable parameter, maintaining test validity while supporting the webhook authentication changes.


408-418: LGTM! Consistent application of nullable parameter.

The second mock setup is correctly updated with It.IsAny<StorageAuthenticationMethod?>(), maintaining consistency across all GetDataBytes mocks in this test file.

test/Altinn.App.Core.Tests/Internal/Process/ExpressionsExclusiveGatewayTests.cs (1)

278-288: LGTM – Mock correctly updated for nullable authentication parameter.

The change from It.IsAny<StorageAuthenticationMethod>() to It.IsAny<StorageAuthenticationMethod?>() properly reflects the updated API signature where authentication methods are now optional. This maintains test compatibility with the broader webhook support changes.

src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (1)

25-58: Well-structured refactoring to support per-call authentication.

The switch from IUserTokenProvider to IAuthenticationTokenResolver with a default authentication method (StorageAuthenticationMethod.CurrentUser()) is a clean architectural improvement that enables webhook scenarios while maintaining backward compatibility.

test/Altinn.App.Core.Tests/Eformidling/Implementation/DefaultEFormidlingServiceTests.cs (1)

184-194: LGTM!

The mock setup correctly updated to use StorageAuthenticationMethod? to match the updated IDataClient.GetBinaryData signature.

test/Altinn.App.Tests.Common/Fixtures/MockedServiceCollection.cs (3)

151-173: Well-designed mock factory pattern.

The Mock<T>() factory with lazy creation, thread-safe access, and automatic singleton registration in DI is a clean approach for centralized mock management. Using MockBehavior.Strict ensures all interactions are explicitly set up, which helps catch unintended calls.


268-272: LGTM!

Calling TryAddCommonServices() in BuildServiceProvider() ensures the common infrastructure is always wired up before building the provider.


299-322: Good telemetry integration for test diagnostics.

The WrappedServiceProvider properly integrates OpenTelemetry for traces, metrics, and logs, which aids debugging test failures. The activity source registration for FakeHttpMessageHandler is a nice touch for tracing HTTP interactions.

test/Altinn.App.Core.Tests/Internal/Data/DataServiceTests.cs (1)

42-52: LGTM!

Mock setups correctly updated to use nullable StorageAuthenticationMethod? matching the updated IDataClient interface signatures.

test/Altinn.App.Api.Tests/Helpers/Patch/PatchServiceTests.cs (2)

88-101: LGTM!

Mock setup correctly updated for UpdateBinaryData with nullable StorageAuthenticationMethod?.


327-339: LGTM!

Mock setup correctly updated for GetDataBytes with nullable StorageAuthenticationMethod?.

test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs (1)

182-195: LGTM!

Mock verification correctly updated for InsertBinaryData with nullable StorageAuthenticationMethod? parameter.

src/Altinn.App.Api/Controllers/PaymentController.cs (2)

158-169: Good security implementation with constant-time comparison.

The use of CryptographicOperations.FixedTimeEquals prevents timing attacks when validating the authorization header. This is a proper security practice for webhook authentication.


185-192: Good defensive null check added.

This addresses the potential NullReferenceException when instance.Process or CurrentTask is null.

test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (3)

53-78: Good test infrastructure refactoring.

The fixture-based approach with MockedServiceCollection provides a clean, reusable test setup. The shared _instance and PaymentDataTypeId constants reduce duplication across tests.


591-637: Good coverage for webhook handling scenarios.

The test HandlePaymentCompletedWebhook_Calls_ProcessNextStep_WhenNetsSaysPaymentIsCompleted properly verifies the happy path with HTTP endpoint registration for the process/next call.


639-691: Verify the commented-out endpoint registration is intentional.

The commented-out FakeHttpMessageHandler.RegisterEndpoint blocks suggest these tests verify that process/next is NOT called. The test logic relies on the endpoint not being registered to fail if it's unexpectedly called. While this works, consider adding explicit verification or a comment explaining this pattern.

src/Altinn.App.Core/Internal/Data/IDataService.cs (1)

1-81: LGTM!

The interface changes are well-structured and maintain backward compatibility. All methods consistently add optional authenticationMethod and ct parameters with appropriate defaults, and the XML documentation has been updated accordingly.

src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (3)

33-52: LGTM!

Constructor properly follows DI patterns and correctly unwraps IOptions<GeneralSettings> to store the value.


55-151: LGTM!

StartPayment has been enhanced with telemetry, proper error handling, and zero-sum payment handling. The implementation validates required dependencies and handles edge cases appropriately.


241-320: Approve webhook handler.

The webhook handler correctly retrieves and updates payment status using the provided authentication method. The logic appropriately mirrors CheckAndStorePaymentStatus while passing authentication context through the data layer. The AltinnTaskTypes.Payment constant is properly defined and correctly used.

src/Altinn.App.Core/Internal/Data/DataService.cs (1)

1-154: LGTM!

The implementation correctly threads the new authenticationMethod and ct parameters through all method calls. Cancellation tokens are properly passed to async operations including serialization, deserialization, and data client calls.

test/Altinn.App.Tests.Common/Fixtures/FakeHttpMessageHandler.cs (4)

51-161: LGTM with note on pattern matching.

The UrlPatternEndpoint implementation is well-structured with thread-safe call counting. The pattern matching logic correctly handles wildcards (*) and path parameters ({param}) with efficient ReadOnlySpan usage.

Note: The pattern matching is intentionally simple per the documentation (lines 93-96) and handles the stated use cases correctly.


163-229: LGTM!

The registration methods provide a flexible API for setting up endpoints with various response types. The use of ConcurrentBag ensures thread-safe registration, and the JSON endpoint helper is a convenient addition.


231-274: LGTM!

The SendAsync implementation provides good diagnostics with activity tagging, clear error messages when endpoints don't match, and proper content buffering for multiple reads. The endpoint matching logic correctly ensures exactly one endpoint matches per request.


276-314: LGTM!

The Verify method enables test assertions on endpoint invocation counts, and the RequestResponse class provides comprehensive capture of request/response details for test validation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (1)

279-310: Reconsider the null return pattern in UpdateReadStatus.

Lines 307-309 return null by disabling nullable reference types when the response status is not OK. This violates the non-nullable return type Task<Instance> and creates ambiguity for callers.

Consider throwing an exception (like other methods in this class) or changing the return type to Task<Instance?> to make the null return explicit.

🔎 Proposed fix: throw exception for consistency
         if (response.StatusCode == HttpStatusCode.OK)
         {
             Instance instance = await JsonSerializerPermissive.DeserializeAsync<Instance>(response.Content, ct);
             return instance;
         }

         _logger.LogError(
             $"Could not update read status for instance {instanceOwnerPartyId}/{instanceGuid}. Request failed with status code {response.StatusCode}"
         );
-#nullable disable
-        return null;
-#nullable restore
+        throw await PlatformHttpException.CreateAsync(response);
🧹 Nitpick comments (1)
test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (1)

26-51: Remove unused internal Fixture class.

The internal Fixture class is no longer used by any tests in this file. All tests have been refactored to use MockedServiceCollection (initialized at line 53), making this class dead code.

🔎 Proposed fix
-    private sealed record Fixture(IServiceProvider ServiceProvider) : IDisposable
-    {
-        public PaymentService Service => (PaymentService)ServiceProvider.GetRequiredService<IPaymentService>();
-
-        public Mock<T> Mock<T>()
-            where T : class => Moq.Mock.Get(ServiceProvider.GetRequiredService<T>());
-
-        public static Fixture Create(bool addProcessor = true, bool addOrderDetailsCalculator = true)
-        {
-            var services = new ServiceCollection();
-            services.AddLogging(logging => logging.AddProvider(NullLoggerProvider.Instance));
-            services.AddAppImplementationFactory();
-            services.AddSingleton(new Mock<IDataService>(MockBehavior.Strict).Object);
-
-            if (addOrderDetailsCalculator)
-                services.AddSingleton(new Mock<IOrderDetailsCalculator>(MockBehavior.Strict).Object);
-            if (addProcessor)
-                services.AddSingleton(new Mock<IPaymentProcessor>(MockBehavior.Strict).Object);
-
-            services.AddSingleton<IPaymentService, PaymentService>();
-
-            return new Fixture(services.BuildStrictServiceProvider());
-        }
-
-        public void Dispose() => (ServiceProvider as IDisposable)?.Dispose();
-    }
-
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b89187 and 5e2103a.

📒 Files selected for processing (3)
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
🧠 Learnings (16)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/test/**/*.cs : Mock external dependencies with Moq in tests

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Include integration tests for platform service interactions

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-10-22T08:35:24.567Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1532
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:2451-2451
Timestamp: 2025-10-22T08:35:24.567Z
Learning: In Altinn/app-lib-dotnet, IDataClient is generally not implemented by customers; it’s considered internal for DI. As of 2025-10-22, GetBinaryDataStream had not been released yet, so its parameter reordering (adding optional TimeSpan? timeout before CancellationToken) is acceptable without a compatibility shim; recommend documenting the new parameter and using named arguments in samples.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs
  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Follow the AppFixture pattern for integration tests: Central orchestrator managing test lifecycle with feature-specific operations

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-08-29T05:32:47.222Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/AppFixture.cs:958-958
Timestamp: 2025-08-29T05:32:47.222Z
Learning: In Altinn.App.Integration.Tests, the maintainer prefers fail-fast behavior (Environment.FailFast) when critical test infrastructure components like log reading fail, rather than graceful error handling, because diagnostics are essential for debugging test failures.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
📚 Learning: 2025-09-21T09:04:43.977Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: src/Altinn.App.Core/Features/IInstanceDataAccessor.cs:36-41
Timestamp: 2025-09-21T09:04:43.977Z
Learning: The IInstanceDataAccessor interface in Altinn.App.Core is designed for consumption only - users are not expected to implement this interface themselves, only use framework-provided implementations.

Applied to files:

  • test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Run dotnet build and test (windows-latest)
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Static code analysis
🔇 Additional comments (6)
test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt (1)

1914-1936: Snapshot updates correctly mirror the intended public API changes

The added NETS webhook payload models, the validated WebhookCallbackKey on NetsPaymentSettings, and the extra optional StorageAuthenticationMethod? + CancellationToken parameters on IDataService and IInstanceClient all look consistent with the PR objectives. The snapshot appears to be correctly regenerated and in sync with the underlying code; no further action needed here.

Based on learnings, *.verified.txt is treated as a generated snapshot and not a place to drive further API design suggestions.

Also applies to: 1949-1952, 3056-3063, 3211-3224

src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (4)

25-32: LGTM! Class accessibility and authentication refactoring align with guidelines.

The changes correctly:

  • Make the class internal sealed per coding guidelines
  • Replace IUserTokenProvider with IAuthenticationTokenResolver to enable webhook authentication
  • Initialize _defaultAuthenticationMethod to preserve backward compatibility

Consumers should depend on the IInstanceClient interface.


39-58: LGTM! Constructor correctly updated for new authentication pattern.

The constructor properly accepts IAuthenticationTokenResolver and initializes the token resolver field.


328-328: Verify the XSS security alert.

GitHub Advanced Security flagged line 328 for potential cross-site scripting. While this appears to be server-to-server API communication (serializing a Substatus object as JSON), please verify that the substatus parameter cannot contain untrusted user input that might be reflected in error responses or logs without proper sanitization.

Based on past review comment from github-advanced-security bot.


61-421: Excellent refactoring for per-call authentication support.

The implementation consistently applies the following pattern across all methods:

  • Optional StorageAuthenticationMethod? authenticationMethod = null parameter
  • Optional CancellationToken ct = default parameter
  • Token resolution via _tokenResolver.GetAccessToken(authenticationMethod ?? _defaultAuthenticationMethod, ct)
  • Cancellation token propagation through HTTP calls and deserialization

This enables webhook authentication while preserving backward compatibility through the default authentication method.

As per learnings, adding optional parameters maintains source compatibility for consumers who rebuild via NuGet.

test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (1)

605-758: Excellent webhook test coverage.

The three new webhook tests provide comprehensive coverage of the payment completion webhook functionality:

  1. Verifies process advancement when payment is completed
  2. Ensures idempotency when process has already advanced
  3. Prevents advancement when payment status is not complete

The tests follow established patterns, use proper DI resolution, and verify expected behavior through mocks and HTTP endpoint registration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs (1)

17-42: Consider making the class sealed and field readonly.

Per coding guidelines, classes should be sealed unless inheritance is intended. Additionally, _netsPaymentSettings is never reassigned and should be readonly (also flagged by static analysis).

🔎 Proposed fix
-public class NetsPaymentProcessorTests
+public sealed class NetsPaymentProcessorTests
 {
     private static readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions(
         JsonSerializerDefaults.Web
     );
     private readonly MockedServiceCollection _fixture = new();

-    private readonly NetsPaymentSettings _netsPaymentSettings = new NetsPaymentSettings
+    private static readonly NetsPaymentSettings _netsPaymentSettings = new NetsPaymentSettings
test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (2)

653-660: Remove commented code.

This commented-out code block should be removed. If the intent is to document that the endpoint is intentionally not registered, a brief comment would be clearer.

🔎 Proposed fix
-        // Disable the process/next endpoint to ensure it is not called
-        // _fixture.FakeHttpMessageHandler.RegisterEndpoint(
-        //     HttpMethod.Put,
-        //     $"/{_instance.AppId}/instances/{_instance.Id}/process/next",
-        //     HttpStatusCode.OK,
-        //     "application/json",
-        //     "{}"
-        // );
-
+        // Note: process/next endpoint is intentionally not registered to verify it's not called
+

707-714: Remove commented code.

This is the same pattern of commented-out code that should be cleaned up.

🔎 Proposed fix
-        // Disable the process/next endpoint to ensure it is not called
-        // _fixture.FakeHttpMessageHandler.RegisterEndpoint(
-        //     HttpMethod.Put,
-        //     $"/{_instance.AppId}/instances/{_instance.Id}/process/next",
-        //     HttpStatusCode.OK,
-        //     "application/json",
-        //     "{}"
-        // );
-
+        // Note: process/next endpoint is intentionally not registered to verify it's not called
+
🧹 Nitpick comments (3)
test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (2)

430-489: Refactor test to use separate HttpResponseMessage instances.

The test reuses a single HttpResponseMessage and modifies its Content property between calls. This pattern is fragile because:

  • HttpContent can typically only be read once
  • Shared response state across calls can lead to unexpected behavior
  • The verification doesn't check endpoints or content, only request count

Additionally, line 480 declares an unused url variable with a typo (extra quote).

🔎 Recommended approach

Set up three separate response messages or use SetupSequence:

-        HttpResponseMessage httpResponseMessage = new HttpResponseMessage
-        {
-            StatusCode = HttpStatusCode.OK,
-            Content = new StringContent(JsonConvert.SerializeObject(instance), Encoding.UTF8, "application/json"),
-        };
-
-        InitializeMocks([httpResponseMessage], ["datavalues"]);
+        HttpResponseMessage[] responses = Enumerable.Range(0, 3)
+            .Select(_ => new HttpResponseMessage
+            {
+                StatusCode = HttpStatusCode.OK,
+                Content = new StringContent(JsonConvert.SerializeObject(instance), Encoding.UTF8, "application/json"),
+            })
+            .ToArray();
+
+        _handlerMock
+            .Protected()
+            .SetupSequence<Task<HttpResponseMessage>>(
+                "SendAsync",
+                ItExpr.Is<HttpRequestMessage>(p => p.RequestUri!.ToString().Contains("datavalues")),
+                ItExpr.IsAny<CancellationToken>()
+            )
+            .ReturnsAsync(responses[0])
+            .ReturnsAsync(responses[1])
+            .ReturnsAsync(responses[2]);

And remove the unused url variable and the manual Content reassignments (lines 466-470, 472-476).


452-452: Optional: Use concrete type for consistency.

This test declares target as IInstanceClient, while all other tests in the file use the concrete InstanceClient type. Consider using the concrete type for consistency, or update all tests to use the interface.

test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (1)

178-192: Consider using a more descriptive exception message.

The exception message "ddasdfg" at line 181 appears to be placeholder text. While it works for the test, a descriptive message would make test failures easier to understand.

🔎 Proposed fix
         _fixture
             .Mock<IPaymentProcessor>()
             .Setup(pp => pp.StartPayment(_instance, orderDetails, Language))
-            .ThrowsAsync(new Exception("ddasdfg"))
+            .ThrowsAsync(new Exception("Payment processor failed to start payment"))
             .Verifiable(Times.Once);
 
         // Act & Assert
         await using var sp = _fixture.BuildServiceProvider();
         var paymentService = sp.GetRequiredService<IPaymentService>();
         var exception = await Assert.ThrowsAsync<Exception>(() =>
             paymentService.StartPayment(_instance, paymentConfiguration, Language)
         );
-        Assert.Equal("ddasdfg", exception.Message);
+        Assert.Equal("Payment processor failed to start payment", exception.Message);
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e2103a and d7fb105.

📒 Files selected for processing (3)
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
🧠 Learnings (12)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs
  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/test/**/*.cs : Mock external dependencies with Moq in tests

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-10-22T08:35:24.567Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1532
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:2451-2451
Timestamp: 2025-10-22T08:35:24.567Z
Learning: In Altinn/app-lib-dotnet, IDataClient is generally not implemented by customers; it’s considered internal for DI. As of 2025-10-22, GetBinaryDataStream had not been released yet, so its parameter reordering (adding optional TimeSpan? timeout before CancellationToken) is acceptable without a compatibility shim; recommend documenting the new parameter and using named arguments in samples.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-08-22T13:35:09.565Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Authentication_auth=OldUser.verified.txt:16-38
Timestamp: 2025-08-22T13:35:09.565Z
Learning: Test data in Altinn App Integration Tests snapshots (like SSNs, addresses, phone numbers in BasicAppTests.Authentication snapshots) are synthetic/fake values created for testing purposes, not real PII that needs to be scrubbed from version control.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
🧬 Code graph analysis (1)
test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (7)
src/Altinn.App.Core/Constants/AltinnTaskTypes.cs (1)
  • AltinnTaskTypes (6-47)
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (9)
  • PaymentService (19-416)
  • PaymentService (33-52)
  • Task (55-151)
  • Task (154-238)
  • Task (241-320)
  • Task (322-344)
  • Task (347-364)
  • Task (367-382)
  • Task (384-415)
src/Altinn.App.Core/Features/Payment/Models/OrderDetails.cs (1)
  • OrderDetails (6-57)
src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (5)
  • Task (15-19)
  • Task (24-28)
  • Task (34-38)
  • Task (43-43)
  • Task (48-48)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnPaymentConfiguration.cs (2)
  • ValidAltinnPaymentConfiguration (24-38)
  • AltinnPaymentConfiguration (10-47)
src/Altinn.App.Core/Features/Payment/Models/PaymentInformation.cs (1)
  • PaymentInformation (6-27)
src/Altinn.App.Core/Features/Payment/Models/PaymentDetails.cs (1)
  • PaymentDetails (6-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Run dotnet build and test (windows-latest)
  • GitHub Check: Static code analysis
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (12)
test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (3)

60-60: LGTM: Consistent refactor to IAuthenticationTokenResolver.

All InstanceClient constructor calls have been properly updated to use _authenticationTokenResolver.Object instead of the old _userTokenProvider.Object.

Also applies to: 91-91, 132-132, 174-174, 212-212, 247-247, 296-296, 328-328, 370-370, 418-418, 533-533


554-563: LGTM: Proper fix for secret-scanning concern.

The CreateDummyJwt() helper successfully addresses the previous review comment by programmatically generating a JWT-like token instead of using hardcoded literals that trigger gitleaks.


574-576: LGTM: Mock setup properly refactored.

The InitializeMocks method correctly updates the token resolution mock to use GetAccessToken with the programmatically generated JWT.

test/Altinn.App.Core.Tests/Features/Payment/Providers/Nets/NetsPaymentProcessorTests.cs (4)

44-88: LGTM!

The test correctly validates the payment flow by registering an HTTP endpoint stub and verifying both the response content and the request payload. The use of await using for proper service provider disposal is appropriate.


90-151: LGTM!

This test properly validates order sum calculation by checking the serialized Order.Amount in the request matches expectedSum. The DI-based fixture approach is well-implemented.


180-226: LGTM!

The cancellation/termination test correctly verifies the HTTP PUT request to the terminate endpoint with proper URL and method assertions.


294-305: Good use of the AltinnTaskTypes.Payment constant.

Using the constant instead of a string literal improves maintainability and type safety.

test/Altinn.App.Core.Tests/Features/Payment/PaymentServiceTests.cs (5)

24-51: Good class structure and DI setup.

The class is properly marked sealed, uses shared constants for test data, and the constructor correctly wires up the DI container with required services. The fixture-based approach centralizes test setup effectively.


577-623: LGTM!

The webhook handler test properly sets up the mock for GetPaymentStatus, registers the HTTP endpoint for process advancement, and verifies the complete webhook flow. Good coverage of the happy path.


625-677: Good edge case coverage.

This test verifies that the process/next step is not called when the process has already advanced to a different task. The test correctly modifies _instance.Process.CurrentTask to simulate this scenario.


679-731: Good edge case coverage.

This test verifies that process advancement is skipped when the payment status is not Paid (returning Created instead). This ensures the webhook handler correctly gates on payment completion.


733-777: LGTM!

Helper methods are well-structured and provide consistent test data. The use of PaymentDataTypeId constant in CreatePaymentConfiguration ensures alignment with the fixture setup.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7fb105 and 6205413.

📒 Files selected for processing (1)
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
🧠 Learnings (9)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Remember to dispose IDisposable/IAsyncDisposable instances

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
🧬 Code graph analysis (1)
test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (1)
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (15)
  • Task (61-87)
  • Task (90-103)
  • Task (106-129)
  • Task (131-153)
  • Task (156-183)
  • Task (186-218)
  • Task (221-247)
  • Task (250-276)
  • Task (279-310)
  • Task (313-339)
  • Task (342-368)
  • Task (371-397)
  • Task (400-421)
  • InstanceClient (25-422)
  • InstanceClient (42-58)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Static code analysis
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Run dotnet build and test (windows-latest)
🔇 Additional comments (5)
test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (5)

4-4: LGTM: Required imports for authentication refactor.

The new using directives support the transition from IUserTokenProvider to IAuthenticationTokenResolver.

Also applies to: 8-8


23-24: LGTM: Mock setup aligns with authentication refactor.

The replacement of IUserTokenProvider with IAuthenticationTokenResolver is consistent with the broader refactoring effort.


57-63: LGTM: InstanceClient constructor calls updated consistently.

All test instantiations now correctly pass _authenticationTokenResolver.Object instead of the deprecated token provider.

Also applies to: 88-94, 128-134, 170-176, 208-214, 243-249, 292-298, 324-330, 366-372, 415-421, 452-458, 532-538


556-565: LGTM: Clean JWT generation helper.

The CreateDummyJwt() helper successfully addresses the secret-scanning concerns by generating a non-secret JWT programmatically instead of using hardcoded literals.


576-578: LGTM: Mock setup updated for new authentication interface.

The GetAccessToken setup correctly replaces the deprecated GetUserToken and uses the new CreateDummyJwt() helper.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs (2)

47-48: Consider making default values constants.

The default party ID and instance GUID are allocated on every call. Moving them to class-level constants would avoid repeated allocations.

🔎 Proposed refactor
+    private const int DefaultPartyId = 123456;
+    private static readonly Guid DefaultInstanceGuid = Guid.Parse("00000000-DEAD-FACE-BABE-000000000001");
+
     public void AddInstance(Instance instance)
     {
-        int defaultPartyId = 123456;
-        Guid defaultInstanceGuid = Guid.Parse("00000000-DEAD-FACE-BABE-000000000001");
         instance.InstanceOwner ??= new InstanceOwner() { };
         if (instance.InstanceOwner.PartyId is null && instance.Id is not null)
         {
             var idParts = instance.Id.Split('/');
             Assert.Equal(2, idParts.Length);
             instance.InstanceOwner.PartyId = idParts[0];
         }
-        instance.InstanceOwner.PartyId ??= defaultPartyId.ToString();
-        instance.Id ??= $"{instance.InstanceOwner.PartyId}/{defaultInstanceGuid}";
+        instance.InstanceOwner.PartyId ??= DefaultPartyId.ToString();
+        instance.Id ??= $"{instance.InstanceOwner.PartyId}/{DefaultInstanceGuid}";
         instance.Data ??= [];
         instance.AppId ??= AppMetadata.Id;
         _instances[instance.Id.ToLowerInvariant()] = instance;
     }

63-71: Add defensive error handling for consistency.

Unlike GetInstanceResponse (line 198), this method directly accesses dictionaries without validation. If the instance or data elements don't exist, it will throw KeyNotFoundException, which may obscure test setup issues.

🔎 Proposed refactor
 public (Instance instance, Dictionary<string, byte[]> data) GetInstanceAndData(
     int instanceOwnerPartyId,
     Guid instanceGuid
 )
 {
-    var instance = _instances[$"{instanceOwnerPartyId}/{instanceGuid}"];
+    if (!_instances.TryGetValue($"{instanceOwnerPartyId}/{instanceGuid}", out var instance))
+    {
+        throw new InvalidOperationException($"Instance {instanceOwnerPartyId}/{instanceGuid} not found in test fixture");
+    }
+    
     var data = instance.Data.ToDictionary(d => d.Id, d => _data[Guid.Parse(d.Id)]);
     return (instance, data);
 }

Alternatively, for more robust handling:

 public (Instance instance, Dictionary<string, byte[]> data) GetInstanceAndData(
     int instanceOwnerPartyId,
     Guid instanceGuid
 )
 {
-    var instance = _instances[$"{instanceOwnerPartyId}/{instanceGuid}"];
-    var data = instance.Data.ToDictionary(d => d.Id, d => _data[Guid.Parse(d.Id)]);
+    if (!_instances.TryGetValue($"{instanceOwnerPartyId}/{instanceGuid}", out var instance))
+    {
+        throw new InvalidOperationException($"Instance {instanceOwnerPartyId}/{instanceGuid} not found in test fixture");
+    }
+    
+    var data = new Dictionary<string, byte[]>();
+    foreach (var dataElement in instance.Data)
+    {
+        if (!Guid.TryParse(dataElement.Id, out var dataId) || !_data.TryGetValue(dataId, out var dataBytes))
+        {
+            throw new InvalidOperationException($"Data element {dataElement.Id} not found in test fixture for instance {instanceOwnerPartyId}/{instanceGuid}");
+        }
+        data[dataElement.Id] = dataBytes;
+    }
     return (instance, data);
 }
test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs (1)

369-369: Improve assertion precision for better test clarity.

The assertion checks for "12345" but the actual PartyId is 123456. While the test passes (substring match), it would be more precise to assert against the correct party ID or full instance ID.

🔎 Suggested improvement
-        Assert.Contains("Payment status is Paid for instance 12345", response.Value?.ToString());
+        Assert.Contains($"Payment status is Paid for instance {_instance.Id}", response.Value?.ToString());
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (2)

240-320: Consider adding telemetry for webhook handling.

The StartPayment method uses telemetry activity wrapping (line 61), but HandlePaymentCompletedWebhook does not. For consistency and better observability of webhook-driven flows, consider wrapping this method with a telemetry activity similar to StartPayment.

💡 Example pattern
public async Task<string> HandlePaymentCompletedWebhook(...)
{
    using var activity = _telemtry?.StartHandleWebhookActivity();
    try
    {
        // existing logic
    }
    catch (Exception ex)
    {
        activity?.Errored(ex);
        throw;
    }
}

28-28: Minor typo in field name.

The field is named _telemtry instead of _telemetry. While consistent throughout the file, correcting the spelling would improve code quality.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6205413 and 7b637b2.

📒 Files selected for processing (5)
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
🧠 Learnings (9)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs
  • src/Altinn.App.Api/Controllers/PaymentController.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs
  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-09-21T09:04:43.977Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: src/Altinn.App.Core/Features/IInstanceDataAccessor.cs:36-41
Timestamp: 2025-09-21T09:04:43.977Z
Learning: The IInstanceDataAccessor interface in Altinn.App.Core is designed for consumption only - users are not expected to implement this interface themselves, only use framework-provided implementations.

Applied to files:

  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-10-22T08:35:24.567Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1532
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:2451-2451
Timestamp: 2025-10-22T08:35:24.567Z
Learning: In Altinn/app-lib-dotnet, IDataClient is generally not implemented by customers; it’s considered internal for DI. As of 2025-10-22, GetBinaryDataStream had not been released yet, so its parameter reordering (adding optional TimeSpan? timeout before CancellationToken) is acceptable without a compatibility shim; recommend documenting the new parameter and using named arguments in samples.

Applied to files:

  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
📚 Learning: 2025-08-22T13:35:09.565Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Authentication_auth=OldUser.verified.txt:16-38
Timestamp: 2025-08-22T13:35:09.565Z
Learning: Test data in Altinn App Integration Tests snapshots (like SSNs, addresses, phone numbers in BasicAppTests.Authentication snapshots) are synthetic/fake values created for testing purposes, not real PII that needs to be scrubbed from version control.

Applied to files:

  • test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs
🧬 Code graph analysis (4)
src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (1)
src/Altinn.App.Core/Internal/Process/Elements/AltinnExtensionProperties/AltinnPaymentConfiguration.cs (1)
  • ValidAltinnPaymentConfiguration (24-38)
src/Altinn.App.Api/Controllers/PaymentController.cs (5)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs (1)
  • NetsPaymentSettings (8-88)
src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (5)
  • Task (15-19)
  • Task (24-28)
  • Task (36-40)
  • Task (45-45)
  • Task (50-50)
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (7)
  • Task (55-151)
  • Task (154-238)
  • Task (241-320)
  • Task (322-344)
  • Task (347-364)
  • Task (367-382)
  • Task (384-415)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (3)
  • Task (45-150)
  • Task (153-162)
  • Task (165-208)
src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsCompleteWebhookPayload.cs (1)
  • NetsCompleteWebhookPayload (8-39)
test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs (4)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs (1)
  • NetsPaymentSettings (8-88)
src/Altinn.App.Api/Controllers/PaymentController.cs (1)
  • PaymentController (38-46)
src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (5)
  • Task (15-19)
  • Task (24-28)
  • Task (36-40)
  • Task (45-45)
  • Task (50-50)
src/Altinn.App.Core/Features/Payment/Models/PaymentDetails.cs (1)
  • PaymentDetails (6-52)
test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs (2)
src/Altinn.App.Core/Internal/Language/LanguageConst.cs (1)
  • LanguageConst (3-8)
src/Altinn.App.Core/Helpers/InstantiationHelper.cs (1)
  • InstanceOwner (196-219)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run dotnet build and test (macos-latest)
  • GitHub Check: Analyze (csharp)
  • GitHub Check: Static code analysis
  • GitHub Check: Run dotnet build and test (ubuntu-latest)
  • GitHub Check: Run dotnet build and test (windows-latest)
🔇 Additional comments (8)
test/Altinn.App.Tests.Common/Fixtures/StorageClientInterceptor.cs (3)

31-40: LGTM!

The constructor refactoring correctly initializes AppMetadata with sensible defaults for titles and data types, using LanguageConst for language keys as per established patterns.


104-105: LGTM!

The routing correctly calls the renamed GetInstanceResponse method.


196-210: LGTM!

The renamed method maintains correct logic with defensive dictionary access and appropriate error responses.

src/Altinn.App.Core/Features/Payment/Services/IPaymentService.cs (1)

30-40: LGTM! Well-documented webhook handler method.

The new HandlePaymentCompletedWebhook method is properly documented and the signature aligns well with the PR's objective of adding webhook support with per-call authentication.

test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs (1)

88-434: Excellent test coverage for webhook scenarios.

The test suite comprehensively covers the webhook flow including authentication validation, configuration checks, and various task states. The use of helper methods and mock verification follows good testing practices.

src/Altinn.App.Api/Controllers/PaymentController.cs (2)

129-227: Well-implemented webhook listener with proper security controls.

The webhook endpoint correctly implements:

  • Constant-time authorization comparison to prevent timing attacks
  • ServiceOwner authentication for system-level access
  • Proper null checking for process state
  • Graceful handling of edge cases (missing config returns 200 OK to prevent unnecessary retries)

The approach of using the webhook as a trigger to fetch authoritative payment status (rather than trusting the payload) is a sound security practice.


69-72: Good defensive check for process state.

Adding validation that CurrentTask exists before accessing its properties prevents potential NullReferenceException and provides a clear error message.

src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (1)

54-151: Well-structured payment initialization with comprehensive error handling.

The StartPayment method properly:

  • Validates required dependencies early
  • Handles existing payments (already paid vs. already started)
  • Wraps calculations with telemetry
  • Gracefully handles zero-amount orders by skipping processor invocation
  • Includes proper error reporting via telemetry

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Fix all issues with AI Agents 🤖
In @test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs:
- Line 434: The test method
UpdateDataValues_WithFullInstance_SuccessfullyCallsStorage has a typo in its
parameter name: rename the parameter from methodVerion to methodVersion in the
method signature and update any usages within the method body (and related test
attributes or invocations) to match the corrected name so the code compiles and
reads correctly.
♻️ Duplicate comments (2)
test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (1)

445-491: Resource leak: HttpResponseMessage not disposed.

The HttpResponseMessage created at line 445 is never disposed, and the test reuses the same response instance across multiple method invocations via InitializeMocks. This pattern was flagged in previous review comments.

As per coding guidelines, remember to dispose IDisposable instances. Based on learnings, similar resource management concerns were raised in prior reviews.

src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (1)

276-276: Fix missing string interpolation prefix.

This is the same issue flagged in the previous review. The return statement is missing the $ prefix, so {instance.Id} will be returned as literal text instead of the actual instance ID.

🔎 Proposed fix
-            return "Payment status is 'Skipped' for instance {instance.Id}. Won't ask payment processor for status.";
+            return $"Payment status is 'Skipped' for instance {instance.Id}. Won't ask payment processor for status.";
🧹 Nitpick comments (4)
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (4)

247-247: Make the log message more specific to webhook handling.

The log message says "Checking payment status" but this method is specifically handling a webhook callback. Consider a more descriptive message like "Handling payment completed webhook for instance {InstanceId}."

🔎 Proposed fix
-        _logger.LogInformation("Checking payment status for instance {InstanceId}.", instance.Id);
+        _logger.LogInformation("Handling payment completed webhook for instance {InstanceId}.", instance.Id);

288-293: Document why language is hardcoded to null in webhook handler.

The language parameter is hardcoded to null when calling GetPaymentStatus. Since webhooks are server-to-server callbacks without user context, this makes sense, but consider adding a comment to clarify this design decision.

🔎 Suggested comment
+        // Language is null because webhooks are server-to-server calls without user context
         (PaymentStatus paymentStatus, PaymentDetails updatedPaymentDetails) = await paymentProcessor.GetPaymentStatus(
             instance,
             paymentDetails.PaymentId,
             totalPriceIncVat,
             language: null
         );

322-346: Good addition of HTTP timeout; consider adding CancellationToken support.

The explicit 5-minute timeout (line 327) properly addresses the previous review comment and prevents indefinite hangs. The timeout value is appropriately generous for process engine calls.

As an optional improvement, consider adding a CancellationToken parameter to this method for better cancellation support, especially since it's an async operation that makes an external HTTP call.

🔎 Optional refactor to add CancellationToken
-    private async Task RunProcessNext(Instance instance, StorageAuthenticationMethod storageAuthenticationMethod)
+    private async Task RunProcessNext(Instance instance, StorageAuthenticationMethod storageAuthenticationMethod, CancellationToken cancellationToken = default)
     {
         JwtToken token = await _authenticationTokenResolver.GetAccessToken(storageAuthenticationMethod);
         using var client = _httpClientFactory.CreateClient();
         // Be reasonably generous with timeout since this involves process engine calls
         client.Timeout = TimeSpan.FromMinutes(5);
         client.BaseAddress = new Uri(_generalSettings.FormattedExternalAppBaseUrl(_app));
         client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(
             "Bearer",
             token.Value
         );
-        var response = await client.PutAsync($"instances/{instance.Id}/process/next", null);
+        var response = await client.PutAsync($"instances/{instance.Id}/process/next", null, cancellationToken);
         if (!response.IsSuccessStatusCode)
         {
             _logger.LogError(
                 "Failed to advance process for instance {InstanceId} after payment completed webhook. Status code: {StatusCode}\n\n{content}",
                 instance.Id,
                 response.StatusCode,
                 await response.Content.ReadAsStringAsync()
             );
             throw new PaymentException(
                 $"Failed to advance process for instance {instance.Id} after payment completed webhook."
             );
         }
     }

Then update the call site on line 317:

-            await RunProcessNext(instance, storageAuthenticationMethod);
+            await RunProcessNext(instance, storageAuthenticationMethod, cancellationToken);

Note: This would require adding a CancellationToken parameter to HandlePaymentCompletedWebhook as well.


19-19: Consider sealing the class per coding guidelines.

The coding guidelines specify: "Use sealed for classes unless inheritance is considered a valid use-case." Since PaymentService is an internal service implementation, consider marking it as sealed unless inheritance is intentionally supported.

🔎 Proposed change
-internal class PaymentService : IPaymentService
+internal sealed class PaymentService : IPaymentService

Based on coding guidelines, services should be sealed by default.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b637b2 and 6e0c636.

📒 Files selected for processing (3)
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
  • test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs
  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/Altinn.App.Api.Tests/Controllers/PaymentControllerTests.cs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
**/test/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/test/**/*.cs: Prefer xUnit asserts over FluentAssertions in tests
Mock external dependencies with Moq in tests

Files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
🧠 Learnings (10)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn.App.Integration.Tests project, xUnit attributes like [Fact] are available without explicit "using Xunit;" directives, likely through global usings or implicit usings configuration. The code compiles and works as-is.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: Applies to **/*.cs : Remember to dispose IDisposable/IAsyncDisposable instances

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-08-29T10:45:57.158Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1456
File: test/Altinn.App.Integration.Tests/_fixture/Tests.cs:3-4
Timestamp: 2025-08-29T10:45:57.158Z
Learning: In Altinn app-lib-dotnet projects, ImplicitUsings is enabled in Directory.Build.props, which automatically includes common using statements including Xunit namespace for test projects. This eliminates the need for explicit "using Xunit;" statements in test files.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which supports default interface methods.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-09-29T08:34:53.864Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: src/Altinn.App.Core/EFormidling/Interface/IEFormidlingService.cs:18-30
Timestamp: 2025-09-29T08:34:53.864Z
Learning: Altinn.App.Core project targets net8.0, not netstandard2.0, which fully supports default interface methods.

Applied to files:

  • test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs
🧬 Code graph analysis (2)
test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (1)
src/Altinn.App.Core/Infrastructure/Clients/Storage/InstanceClient.cs (15)
  • Task (61-87)
  • Task (90-103)
  • Task (106-129)
  • Task (131-153)
  • Task (156-183)
  • Task (186-218)
  • Task (221-247)
  • Task (250-276)
  • Task (279-310)
  • Task (313-339)
  • Task (342-368)
  • Task (371-397)
  • Task (400-421)
  • InstanceClient (25-422)
  • InstanceClient (42-58)
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (6)
src/Altinn.App.Core/Configuration/GeneralSettings.cs (2)
  • GeneralSettings (9-86)
  • FormattedExternalAppBaseUrl (61-68)
src/Altinn.App.Core/Features/Payment/Exceptions/PaymentException.cs (2)
  • PaymentException (8-16)
  • PaymentException (14-15)
src/Altinn.App.Core/Features/Payment/Models/PaymentInformation.cs (1)
  • PaymentInformation (6-27)
src/Altinn.App.Core/Features/Payment/Models/PaymentDetails.cs (1)
  • PaymentDetails (6-52)
src/Altinn.App.Core/Features/Payment/Models/OrderDetails.cs (1)
  • OrderDetails (6-57)
src/Altinn.App.Core/Constants/AltinnTaskTypes.cs (1)
  • AltinnTaskTypes (6-47)
🔇 Additional comments (5)
test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs (2)

556-565: LGTM: Dummy JWT generator addresses secret-scanning concern.

The CreateDummyJwt helper successfully resolves the previous issue with hardcoded JWT literals that triggered secret scanners. The implementation correctly generates a non-secret JWT-like token for testing purposes.


576-578: LGTM: Token resolver mock correctly configured.

The InitializeMocks method properly sets up the IAuthenticationTokenResolver mock using the CreateDummyJwt helper, aligning with the migration from IUserTokenProvider to IAuthenticationTokenResolver.

src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (3)

33-52: LGTM: Constructor properly wired with new dependencies.

The constructor correctly injects and assigns the new dependencies needed for webhook support and process advancement. The optional telemetry parameter follows the established pattern in the codebase.


55-151: LGTM: Well-structured payment initialization flow.

The refactored StartPayment method now includes proper validation, telemetry instrumentation, and enhanced error handling. The zero-sum optimization (lines 121-125) and distinct logging for already-paid vs. already-started payments (lines 81-99) are particularly good additions.


312-318: Verify process advancement is complete for webhook flow.

The PR description states "The implementation is incomplete: it currently does not perform process transitions or post events, because those previously happened in the frontend." However, this code does advance the process via RunProcessNext. Please clarify:

  1. Does the /instances/{id}/process/next endpoint handle all necessary transitions and events?
  2. Are there additional actions (e.g., posting platform events) that still need to be implemented for full webhook support?

[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public async Task UpdateDataValues_WithFullInstance_SuccessfullyCallsStorage(int methodVerion)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix parameter name typo.

The parameter name methodVerion should be methodVersion.

🔎 Proposed fix
-    public async Task UpdateDataValues_WithFullInstance_SuccessfullyCallsStorage(int methodVerion)
+    public async Task UpdateDataValues_WithFullInstance_SuccessfullyCallsStorage(int methodVersion)
     {
         Guid instanceGuid = Guid.NewGuid();
         int instanceOwnerId = 1337;
@@ -461,7 +461,7 @@
         );
 
         // Act
-        switch (methodVerion)
+        switch (methodVersion)
         {
             case 1:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public async Task UpdateDataValues_WithFullInstance_SuccessfullyCallsStorage(int methodVerion)
public async Task UpdateDataValues_WithFullInstance_SuccessfullyCallsStorage(int methodVersion)
🤖 Prompt for AI Agents
In @test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs around line
434, The test method UpdateDataValues_WithFullInstance_SuccessfullyCallsStorage
has a typo in its parameter name: rename the parameter from methodVerion to
methodVersion in the method signature and update any usages within the method
body (and related test attributes or invocations) to match the corrected name so
the code compiles and reads correctly.

@ivarne
Copy link
Member Author

ivarne commented Jan 5, 2026

/publish

@github-actions
Copy link

github-actions bot commented Jan 5, 2026

PR release:

⚙️ Building...
✅ Done!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/Altinn.App.Api/Controllers/PaymentController.cs (3)

39-47: Consider using direct constructor injection instead of Service Locator pattern.

The controller now uses IServiceProvider to resolve all dependencies at runtime, which obscures the explicit dependencies and complicates testing. Direct constructor injection makes dependencies clearer and improves testability.

🔎 Proposed refactor to use direct constructor injection
-public PaymentController(IServiceProvider serviceProvider)
+public PaymentController(
+    IInstanceClient instanceClient,
+    IProcessReader processReader,
+    ILogger<PaymentController> logger,
+    IOptions<NetsPaymentSettings> netsPaymentSettings,
+    IPaymentService paymentService,
+    AppImplementationFactory appImplementationFactory)
 {
-    _instanceClient = serviceProvider.GetRequiredService<IInstanceClient>();
-    _processReader = serviceProvider.GetRequiredService<IProcessReader>();
-    _logger = serviceProvider.GetRequiredService<ILogger<PaymentController>>();
-    _netsPaymentSettings = serviceProvider.GetRequiredService<IOptions<NetsPaymentSettings>>();
-    _paymentService = serviceProvider.GetRequiredService<IPaymentService>();
-    _appImplementationFactory = serviceProvider.GetRequiredService<AppImplementationFactory>();
+    _instanceClient = instanceClient;
+    _processReader = processReader;
+    _logger = logger;
+    _netsPaymentSettings = netsPaymentSettings;
+    _paymentService = paymentService;
+    _appImplementationFactory = appImplementationFactory;
 }

198-217: Minor: Redundant null-conditional operator.

Line 206 uses instance.Process.CurrentTask?.ElementId with a null-conditional operator, but CurrentTask was already validated to be non-null at line 189. This is harmless but slightly redundant.

🔎 Proposed simplification
             _logger.LogInformation(
                 "Payment configuration not found in AltinnTaskExtension for task {CurrentTask}. Cannot process Nets webhook callback for instance {InstanceId}. Likely the callback is for an old payment.",
-                instance.Process.CurrentTask?.ElementId,
+                instance.Process.CurrentTask.ElementId,
                 instance.Id
             );
             // If the current task is not a payment task, just log and return OK with a ProblemDetails that nets ignore, but might be logged.
             return Ok(
                 new ProblemDetails()
                 {
                     Detail =
-                        $"Payment configuration not found in AltinnTaskExtension for task {instance.Process.CurrentTask?.ElementId}",
+                        $"Payment configuration not found in AltinnTaskExtension for task {instance.Process.CurrentTask.ElementId}",
                 }
             );

143-229: Consider adding CancellationToken parameter for proper async cancellation.

The webhook endpoint performs async operations but doesn't accept a CancellationToken parameter, preventing proper cancellation propagation if the request is aborted.

🔎 Proposed enhancement
 public async Task<IActionResult> PaymentWebhookListener(
     [FromRoute] string org,
     [FromRoute] string app,
     [FromRoute] int instanceOwnerPartyId,
     [FromRoute] Guid instanceGuid,
     [FromBody] NetsCompleteWebhookPayload webhookPayload,
-    [FromHeader(Name = "Authorization")] string authorizationHeader
+    [FromHeader(Name = "Authorization")] string authorizationHeader,
+    CancellationToken cancellationToken = default
 )

Then pass cancellationToken to async method calls like GetInstance and HandlePaymentCompletedWebhook if they support it.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b160dc3 and 39d05c0.

📒 Files selected for processing (3)
  • src/Altinn.App.Api/Controllers/PaymentController.cs
  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt
  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Use CSharpier for code formatting (required before commits). Formatting happens automatically when building due to CSharpier.MSBuild
Use internal accessibility on types by default
Use sealed for classes unless inheritance is considered a valid use-case
Use Nullable Reference Types in C#
Remember to dispose IDisposable/IAsyncDisposable instances
For HTTP APIs, define ...Request and ...Response DTOs (e.g., LookupPersonRequest.cs and corresponding response)
Types meant to be implemented by apps should be marked with the ImplementableByApps attribute
Don't use .GetAwaiter().GetResult(), .Result(), .Wait() or other blocking APIs on Task
Don't use Async suffix for async methods
Write efficient code: Don't allocate unnecessarily (e.g., avoid calling ToString twice in a row, store in a variable; sometimes a for loop is better than LINQ)
Don't invoke the same async operation multiple times in the same codepath unless necessary
Don't await async operations in a loop (prefer batching, but maintain an upper bound on parallelism that makes sense)
Use strongly-typed configuration classes instead of untyped configuration
Register services in DI container properly following existing patterns

Files:

  • src/Altinn.App.Api/Controllers/PaymentController.cs
🧠 Learnings (9)
📓 Common learnings
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.
📚 Learning: 2025-08-22T13:46:43.017Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Full_auth=ServiceOwner_testCase=MultipartXmlPrefill_7_Logs.verified.txt:41-42
Timestamp: 2025-08-22T13:46:43.017Z
Learning: In Altinn App Integration Tests, OldUser and OldServiceOwner test snapshots intentionally preserve the legacy "AuthMethod: localtest" authentication method as part of a migration strategy, while new User and ServiceOwner tests use updated authentication methods like BankID and maskinporten. The "Old*" variants should not be updated to remove localtest references.

Applied to files:

  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt
  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
📚 Learning: 2025-08-22T13:35:09.565Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1446
File: test/Altinn.App.Integration.Tests/Basic/_snapshots/BasicAppTests.Authentication_auth=OldUser.verified.txt:16-38
Timestamp: 2025-08-22T13:35:09.565Z
Learning: Test data in Altinn App Integration Tests snapshots (like SSNs, addresses, phone numbers in BasicAppTests.Authentication snapshots) are synthetic/fake values created for testing purposes, not real PII that needs to be scrubbed from version control.

Applied to files:

  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt
  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
📚 Learning: 2025-09-18T10:23:05.995Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1357
File: test/Altinn.App.Api.Tests/OpenApi/OpenApiSpecChangeDetection.SaveJsonSwagger.verified.json:7263-0
Timestamp: 2025-09-18T10:23:05.995Z
Learning: In PR #1357, ApplicationMetadata.apiScopes (now ApiScopesConfiguration) is currently unused at runtime; the schema change is acceptable without a transition mechanism for now.

Applied to files:

  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt
  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
📚 Learning: 2025-09-18T10:12:21.841Z
Learnt from: martinothamar
Repo: Altinn/app-lib-dotnet PR: 1357
File: src/Altinn.App.Api/Infrastructure/Middleware/ScopeAuthorizationMiddleware.cs:289-293
Timestamp: 2025-09-18T10:12:21.841Z
Learning: In Altinn App security middleware like ScopeAuthorizationMiddleware, prefer explicit failures (throwing exceptions) over silently skipping unexpected endpoint types during discovery to prevent potential authorization gaps and ensure all endpoints are properly handled according to security configuration.

Applied to files:

  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt
  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
📚 Learning: 2025-09-21T08:19:43.290Z
Learnt from: ivarne
Repo: Altinn/app-lib-dotnet PR: 1473
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:1414-1414
Timestamp: 2025-09-21T08:19:43.290Z
Learning: In Altinn/app-lib-dotnet, *.verified.txt files (e.g., test/**/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt) are verification snapshots for guarding public API. Do not generate review suggestions based on these files; ignore them when assessing code changes.

Applied to files:

  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Custom_0_Metadata.verified.txt
📚 Learning: 2025-11-28T08:07:52.106Z
Learnt from: CR
Repo: Altinn/app-lib-dotnet PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T08:07:52.106Z
Learning: New features should follow the established pattern in /src/Altinn.App.Core/Features/ with feature-specific folders, DI registration, telemetry, and test coverage

Applied to files:

  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
  • src/Altinn.App.Api/Controllers/PaymentController.cs
📚 Learning: 2025-10-17T07:45:15.474Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 1474
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:233-237
Timestamp: 2025-10-17T07:45:15.474Z
Learning: In Altinn/app-lib-dotnet, maintainers accept source compatibility without guaranteeing binary compatibility across versions because consumers upgrade via NuGet and rebuild. Adding optional parameters to public extension methods (e.g., CancellationToken) is acceptable provided release notes document the change.

Applied to files:

  • test/Altinn.App.Integration.Tests/CustomScopes/_snapshots/CustomScopesTests.Metadata_Standard_0_Metadata.verified.txt
  • src/Altinn.App.Api/Controllers/PaymentController.cs
📚 Learning: 2025-09-25T08:15:25.624Z
Learnt from: bjorntore
Repo: Altinn/app-lib-dotnet PR: 745
File: test/Altinn.App.Core.Tests/PublicApiTests.PublicApi_ShouldNotChange_Unintentionally.verified.txt:3656-3671
Timestamp: 2025-09-25T08:15:25.624Z
Learning: In PR Altinn/app-lib-dotnet#745, the team decided to defer adding idempotency documentation, enriched ServiceTaskFailedResult (code/title/message), and additional OTel spans for service tasks. Do not re-suggest these changes within this PR; propose a follow-up issue instead if needed.

Applied to files:

  • src/Altinn.App.Api/Controllers/PaymentController.cs
🧬 Code graph analysis (1)
src/Altinn.App.Api/Controllers/PaymentController.cs (4)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentSettings.cs (1)
  • NetsPaymentSettings (8-88)
src/Altinn.App.Core/Features/Payment/Services/PaymentService.cs (7)
  • Task (55-151)
  • Task (154-238)
  • Task (241-320)
  • Task (322-346)
  • Task (349-366)
  • Task (369-384)
  • Task (386-417)
src/Altinn.App.Core/Features/Payment/Processors/Nets/NetsPaymentProcessor.cs (3)
  • Task (45-150)
  • Task (153-162)
  • Task (165-208)
src/Altinn.App.Core/Features/Payment/Processors/Nets/Models/NetsCompleteWebhookPayload.cs (1)
  • NetsCompleteWebhookPayload (8-39)
🔇 Additional comments (3)
src/Altinn.App.Api/Controllers/PaymentController.cs (3)

1-2: LGTM!

The new imports and fields are appropriate for the webhook implementation. The cryptography imports enable constant-time comparison for secure authorization header validation, and the Nets-related imports support webhook payload handling.

Also applies to: 8-9, 15-17, 33-34


70-83: LGTM!

The validation additions prevent potential NullReferenceException and provide clear error messages when the instance has no current task or payment configuration. Good defensive programming.


130-173: LGTM!

The webhook listener endpoint properly implements security best practices:

  • Uses [AllowAnonymous] with explicit comment explaining authorization is handled via shared secret
  • Employs constant-time comparison to prevent timing attacks
  • Validates configuration and authorization with appropriate logging and HTTP status codes
  • Hides the internal endpoint from public API documentation

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 5, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
61.54% Condition Coverage on New Code (required ≥ 65%)

See analysis details on SonarQube Cloud

Copy link
Contributor

@bjorntore bjorntore left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Frontend deals OK with the new behaviour?

@ivarne
Copy link
Member Author

ivarne commented Jan 12, 2026

Frontend deals OK with the new behaviour?

I don't know

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-ignore This PR is a new feature and should not be cherry-picked onto release branches feature Label Pull requests with new features. Used when generation releasenotes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants