diff --git a/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeRequest.cs b/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeRequest.cs
new file mode 100644
index 0000000..f84b639
--- /dev/null
+++ b/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeRequest.cs
@@ -0,0 +1,12 @@
+namespace DocumentOperator.Application.Common.DTOs;
+
+///
+/// Request to extract Swiss QR Code from a PDF document.
+/// The QR code must be located on the last page of the document.
+///
+/// Array of reference strings to pass through in the response
+/// PDF document encoded as Base64 string
+public record ExtractSwissQrCodeRequest(
+ IReadOnlyList References,
+ string Base64Pdf
+);
diff --git a/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeResponse.cs b/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeResponse.cs
new file mode 100644
index 0000000..2ea3ad4
--- /dev/null
+++ b/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeResponse.cs
@@ -0,0 +1,46 @@
+namespace DocumentOperator.Application.Common.DTOs;
+
+///
+/// Response containing extracted Swiss QR Code data and passed-through references.
+///
+/// Reference strings passed through from the request
+/// Parsed Swiss QR Code data from the last page of the PDF
+public record ExtractSwissQrCodeResponse(
+ IReadOnlyList References,
+ SwissQrCodeDataDto QrCodeData
+);
+
+///
+/// Swiss QR Code data according to Swiss QR Bill Standard 2.0
+///
+public record SwissQrCodeDataDto(
+ string QrType,
+ string Version,
+ string CodingType,
+ string Iban,
+ AddressDataDto Creditor,
+ AddressDataDto? UltimateCreditor,
+ decimal? Amount,
+ string Currency,
+ AddressDataDto? UltimateDebtor,
+ string ReferenceType,
+ string? Reference,
+ string? UnstructuredMessage,
+ string? BillInformation,
+ IReadOnlyList? AlternativeProcedureParameters
+);
+
+///
+/// Address data (creditor or debtor) in Swiss QR Code
+///
+public record AddressDataDto(
+ string AddressType,
+ string Name,
+ string? Street,
+ string? BuildingNumber,
+ string? AddressLine1,
+ string? AddressLine2,
+ string PostalCode,
+ string City,
+ string Country
+);
diff --git a/DocumentOperator.Application/Features/Documents/ExtractSwissQrCode/ExtractSwissQrCodeHandler.cs b/DocumentOperator.Application/Features/Documents/ExtractSwissQrCode/ExtractSwissQrCodeHandler.cs
new file mode 100644
index 0000000..54e6d10
--- /dev/null
+++ b/DocumentOperator.Application/Features/Documents/ExtractSwissQrCode/ExtractSwissQrCodeHandler.cs
@@ -0,0 +1,33 @@
+using DocumentOperator.Application.Common.Interfaces;
+using MediatR;
+
+namespace DocumentOperator.Application.Features.Documents.ExtractSwissQrCode;
+
+///
+/// Handles extraction of Swiss QR Code from PDF documents.
+/// Uses ISwissQrCodeProcessor to extract QR code from the last page and parse it.
+///
+public sealed class ExtractSwissQrCodeHandler : IRequestHandler
+{
+ private readonly ISwissQrCodeProcessor _qrCodeProcessor;
+
+ public ExtractSwissQrCodeHandler(ISwissQrCodeProcessor qrCodeProcessor)
+ {
+ _qrCodeProcessor = qrCodeProcessor;
+ }
+
+ public async Task Handle(ExtractSwissQrCodeQuery request, CancellationToken cancellationToken)
+ {
+ // Convert Base64 string to byte array
+ byte[] pdfBytes = request.PdfContent.ToByteArray();
+
+ // Extract and parse Swiss QR Code from last page
+ var qrCodeData = await _qrCodeProcessor.ExtractSwissQrCodeAsync(pdfBytes, cancellationToken);
+
+ // Return references (passed through) + QR code data
+ return new ExtractSwissQrCodeResult(
+ References: request.References,
+ QrCodeData: qrCodeData
+ );
+ }
+}
diff --git a/DocumentOperator.Application/Features/Documents/ExtractSwissQrCode/ExtractSwissQrCodeQuery.cs b/DocumentOperator.Application/Features/Documents/ExtractSwissQrCode/ExtractSwissQrCodeQuery.cs
new file mode 100644
index 0000000..7259588
--- /dev/null
+++ b/DocumentOperator.Application/Features/Documents/ExtractSwissQrCode/ExtractSwissQrCodeQuery.cs
@@ -0,0 +1,26 @@
+using DocumentOperator.Domain.Models.ValueObjects;
+using DocumentOperator.Domain.ValueObjects;
+using MediatR;
+
+namespace DocumentOperator.Application.Features.Documents.ExtractSwissQrCode;
+
+///
+/// Query to extract Swiss QR Code data from a PDF document.
+/// Returns references (passed through) and parsed QR code data.
+///
+/// Array of reference strings to pass through in the response
+/// PDF document content as Base64 string
+public record ExtractSwissQrCodeQuery(
+ IReadOnlyList References,
+ Base64String PdfContent
+) : IRequest;
+
+///
+/// Result containing passed-through references and extracted Swiss QR Code data
+///
+/// Reference strings passed through from request
+/// Parsed Swiss QR Code data from the last page of the PDF
+public record ExtractSwissQrCodeResult(
+ IReadOnlyList References,
+ SwissQrCodeData QrCodeData
+);
diff --git a/DocumentOperator.Application/Features/Documents/ExtractSwissQrCode/ExtractSwissQrCodeValidator.cs b/DocumentOperator.Application/Features/Documents/ExtractSwissQrCode/ExtractSwissQrCodeValidator.cs
new file mode 100644
index 0000000..d7c48f2
--- /dev/null
+++ b/DocumentOperator.Application/Features/Documents/ExtractSwissQrCode/ExtractSwissQrCodeValidator.cs
@@ -0,0 +1,21 @@
+using FluentValidation;
+
+namespace DocumentOperator.Application.Features.Documents.ExtractSwissQrCode;
+
+///
+/// Validates ExtractSwissQrCodeQuery before handler execution.
+/// Ensures references array and PDF content are provided.
+///
+public sealed class ExtractSwissQrCodeValidator : AbstractValidator
+{
+ public ExtractSwissQrCodeValidator()
+ {
+ RuleFor(x => x.References)
+ .NotNull()
+ .WithMessage("References array is required.");
+
+ RuleFor(x => x.PdfContent)
+ .NotNull()
+ .WithMessage("PDF content is required.");
+ }
+}
diff --git a/DocumentOperator.Tests/Unit/Application/Features/ExtractSwissQrCode/ExtractSwissQrCodeHandlerTests.cs b/DocumentOperator.Tests/Unit/Application/Features/ExtractSwissQrCode/ExtractSwissQrCodeHandlerTests.cs
new file mode 100644
index 0000000..d413167
--- /dev/null
+++ b/DocumentOperator.Tests/Unit/Application/Features/ExtractSwissQrCode/ExtractSwissQrCodeHandlerTests.cs
@@ -0,0 +1,95 @@
+using DocumentOperator.Application.Common.Interfaces;
+using DocumentOperator.Application.Features.Documents.ExtractSwissQrCode;
+using DocumentOperator.Domain.Models.ValueObjects;
+using DocumentOperator.Domain.ValueObjects;
+using FluentAssertions;
+using Moq;
+using Xunit;
+
+namespace DocumentOperator.Tests.Unit.Application.Features.ExtractSwissQrCode;
+
+public sealed class ExtractSwissQrCodeHandlerTests
+{
+ private readonly Mock _mockQrCodeProcessor;
+ private readonly ExtractSwissQrCodeHandler _handler;
+
+ public ExtractSwissQrCodeHandlerTests()
+ {
+ _mockQrCodeProcessor = new Mock();
+ _handler = new ExtractSwissQrCodeHandler(_mockQrCodeProcessor.Object);
+ }
+
+ [Fact]
+ public async Task Handle_ValidRequest_ReturnsQrCodeDataAndReferences()
+ {
+ // Arrange
+ var references = new List { "REF-001", "REF-002" };
+ var pdfBase64 = "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCjIgMCBvYmoKPDwKL1R5cGUgL1BhZ2VzCi9LaWRzIFszIDAgUl0KL0NvdW50IDEKPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCAyIDAgUgovTWVkaWFCb3ggWzAgMCA2MTIgNzkyXQovQ29udGVudHMgNCAwIFIKPj4KZW5kb2JqCjQgMCBvYmoKPDwKL0xlbmd0aCAzMgo+PgpzdHJlYW0KQlQKL0YxIDEyIFRmCjEwMCA3MDAgVGQKKEhlbGxvKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA1CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNSAwMDAwMCBuIAowMDAwMDAwMDY0IDAwMDAwIG4gCjAwMDAwMDAxMjEgMDAwMDAgbiAKMDAwMDAwMDIxMyAwMDAwMCBuIAp0cmFpbGVyCjw8Ci9TaXplIDUKL1Jvb3QgMSAwIFIKPj4Kc3RhcnR4cmVmCjI5NAolJUVPRgo=";
+ var base64String = Base64String.Create(pdfBase64);
+ var query = new ExtractSwissQrCodeQuery(references, base64String);
+
+ var expectedQrCodeData = new SwissQrCodeData
+ {
+ QrType = "SPC",
+ Version = "0200",
+ CodingType = "1",
+ Iban = "CH4431999123000889012",
+ Creditor = new AddressData
+ {
+ AddressType = "S",
+ Name = "Robert Schneider AG",
+ Street = "Rue du Lac",
+ BuildingNumber = "1268",
+ PostalCode = "2501",
+ City = "Biel",
+ Country = "CH"
+ },
+ UltimateCreditor = null,
+ Amount = 1949.75m,
+ Currency = "CHF",
+ UltimateDebtor = null,
+ ReferenceType = "QRR",
+ Reference = "210000000003139471430009017",
+ UnstructuredMessage = "Order from 15.01.2025",
+ BillInformation = null,
+ AlternativeProcedureParameters = null
+ };
+
+ _mockQrCodeProcessor
+ .Setup(x => x.ExtractSwissQrCodeAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(expectedQrCodeData);
+
+ // Act
+ var result = await _handler.Handle(query, CancellationToken.None);
+
+ // Assert
+ result.Should().NotBeNull();
+ result.References.Should().BeEquivalentTo(references);
+ result.QrCodeData.Should().BeEquivalentTo(expectedQrCodeData);
+
+ _mockQrCodeProcessor.Verify(
+ x => x.ExtractSwissQrCodeAsync(It.IsAny(), It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task Handle_QrCodeProcessorThrowsException_PropagatesException()
+ {
+ // Arrange
+ var references = new List { "REF-001" };
+ var pdfBase64 = "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCjIgMCBvYmoKPDwKL1R5cGUgL1BhZ2VzCi9LaWRzIFszIDAgUl0KL0NvdW50IDEKPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCAyIDAgUgovTWVkaWFCb3ggWzAgMCA2MTIgNzkyXQovQ29udGVudHMgNCAwIFIKPj4KZW5kb2JqCjQgMCBvYmoKPDwKL0xlbmd0aCAzMgo+PgpzdHJlYW0KQlQKL0YxIDEyIFRmCjEwMCA3MDAgVGQKKEhlbGxvKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA1CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNSAwMDAwMCBuIAowMDAwMDAwMDY0IDAwMDAwIG4gCjAwMDAwMDAxMjEgMDAwMDAgbiAKMDAwMDAwMDIxMyAwMDAwMCBuIAp0cmFpbGVyCjw8Ci9TaXplIDUKL1Jvb3QgMSAwIFIKPj4Kc3RhcnR4cmVmCjI5NAolJUVPRgo=";
+ var base64String = Base64String.Create(pdfBase64);
+ var query = new ExtractSwissQrCodeQuery(references, base64String);
+
+ _mockQrCodeProcessor
+ .Setup(x => x.ExtractSwissQrCodeAsync(It.IsAny(), It.IsAny()))
+ .ThrowsAsync(new InvalidOperationException("QR Code processing failed"));
+
+ // Act
+ Func act = async () => await _handler.Handle(query, CancellationToken.None);
+
+ // Assert
+ await act.Should().ThrowAsync()
+ .WithMessage("QR Code processing failed");
+ }
+}