diff --git a/tests/SIGCM2.Api.Tests/Products/ProductPricesAuditFailureTests.cs b/tests/SIGCM2.Api.Tests/Products/ProductPricesAuditFailureTests.cs
new file mode 100644
index 0000000..ccc63e7
--- /dev/null
+++ b/tests/SIGCM2.Api.Tests/Products/ProductPricesAuditFailureTests.cs
@@ -0,0 +1,157 @@
+using System.Net;
+using System.Net.Http.Headers;
+using System.Net.Http.Json;
+using System.Text.Json;
+using Dapper;
+using FluentAssertions;
+using Microsoft.Data.SqlClient;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using NSubstitute;
+using NSubstitute.ExceptionExtensions;
+using SIGCM2.Application.Abstractions.Security;
+using SIGCM2.Application.Audit;
+using SIGCM2.Domain.Entities;
+using SIGCM2.TestSupport;
+using Xunit;
+
+namespace SIGCM2.Api.Tests.Products;
+
+///
+/// PRD-003 — Batch 7 / T7.4: Audit failure e2e test.
+///
+/// Verifies that when IAuditLogger.LogAsync throws, the TransactionScope rolls back:
+/// - dbo.ProductPrices row is NOT inserted (fail-closed).
+/// - dbo.AuditEvent row is NOT created.
+/// - HTTP response is 500 (unhandled exception propagates through ExceptionFilter).
+///
+/// Uses CreateClientWithOverrides (pattern from issue #36) to inject a throwing IAuditLogger
+/// mock without touching the shared factory.
+///
+/// DB: SIGCM2_Test_Api (ApiIntegration collection).
+///
+[Collection("ApiIntegration")]
+public sealed class ProductPricesAuditFailureTests : IAsyncLifetime
+{
+ private const string ConnectionString = TestConnectionStrings.ApiTestDb;
+
+ private readonly TestWebAppFactory _factory;
+ private int _medioId;
+ private int _productTypeId;
+
+ public ProductPricesAuditFailureTests(TestWebAppFactory factory)
+ {
+ _factory = factory;
+ }
+
+ public async Task InitializeAsync()
+ {
+ await using var conn = new SqlConnection(ConnectionString);
+ await conn.OpenAsync();
+
+ _medioId = await conn.QuerySingleAsync(
+ "SELECT TOP 1 Id FROM dbo.Medio WHERE Activo = 1 ORDER BY Id");
+
+ var ptId = await conn.QuerySingleOrDefaultAsync(
+ "SELECT TOP 1 Id FROM dbo.ProductType WHERE IsActive = 1 ORDER BY Id");
+ if (ptId is null)
+ {
+ ptId = await conn.QuerySingleAsync("""
+ INSERT INTO dbo.ProductType (Nombre, HasDuration, RequiresText, RequiresCategory, IsBundle, AllowImages, IsActive, FechaCreacion)
+ VALUES ('PT_AuditFail_Test', 0, 0, 0, 0, 0, 1, SYSUTCDATETIME());
+ SELECT CAST(SCOPE_IDENTITY() AS INT);
+ """);
+ }
+ _productTypeId = ptId.Value;
+ }
+
+ public Task DisposeAsync() => Task.CompletedTask;
+
+ // ── T7.4 — Audit failure rolls back the ProductPrice insert ──────────────
+
+ [Fact]
+ public async Task PostPrice_WhenAuditLoggerThrows_Returns500AndRollsBackInsert()
+ {
+ // Arrange: seed a unique product
+ var productId = await SeedProductAsync();
+
+ // Count rows before the failing POST
+ await using var conn = new SqlConnection(ConnectionString);
+ await conn.OpenAsync();
+
+ var priceCountBefore = await conn.ExecuteScalarAsync(
+ "SELECT COUNT(1) FROM dbo.ProductPrices WHERE ProductId = @ProductId",
+ new { ProductId = productId });
+
+ var auditCountBefore = await conn.ExecuteScalarAsync(
+ "SELECT COUNT(1) FROM dbo.AuditEvent WHERE Action = 'product_price.created' AND TargetType = 'ProductPrice'");
+
+ // Build a mock IAuditLogger that throws on LogAsync
+ var throwingAuditLogger = Substitute.For();
+ throwingAuditLogger
+ .LogAsync(
+ Arg.Any(), Arg.Any(), Arg.Any(),
+ Arg.Any