Files
fiscalos/src/FiscalOS.API/Transactions/FireWebhook/Endpoint.cs
T
Stevan Freeborn a9f1ced0d5 feat: add Plaid transaction handling and async queue processing
- introduced new migration to add Cursor column to AccountMetadata.
- updated AppDbContext model snapshot to reflect changes in the database schema.
- implemented async queue processing with ChannelAsyncQueue and AsyncQueueHostedService.
- created Plaid transaction handlers for added, modified, and removed transactions.
- developed PlaidTransactionService and PlaidTransactionSyncer for syncing transactions.
- added SyncUpdatesProcessor to handle sync updates from Plaid.
- updated tests to accommodate changes in institution and account metadata handling.
2026-03-09 04:29:01 -05:00

65 lines
1.9 KiB
C#

using Going.Plaid;
using Going.Plaid.Entity;
namespace FiscalOS.API.Transactions.FireWebhook;
internal static class Endpoint
{
private const string Route = "/fire-webhook";
public static RouteHandlerBuilder MapFireWebhookEndpoint(this RouteGroupBuilder groupBuilder)
{
return groupBuilder.MapPost(Route, HandleAsync);
}
private static async Task<IResult> HandleAsync(
HttpContext httpContext,
[FromBody] Request request,
[FromServices] AppDbContext appDbContext,
[FromServices] IEncryptor encryptor,
[FromServices] PlaidClient plaidClient,
CancellationToken ct
)
{
var userId = httpContext.GetUserId();
var user = await appDbContext.Users
.Include(u => u.Accounts.Where(a => a.Id.ToString() == request.AccountId))
.ThenInclude(static a => a.Institution!)
.ThenInclude(static i => i.Metadata)
.FirstOrDefaultAsync(u => u.Id == userId, ct);
if (user is null)
{
return Results.Unauthorized();
}
if (user.Accounts.Any() is false)
{
return Results.NotFound();
}
var plaidMetadata = (PlaidInstitutionMetadata?)user.Accounts.First().Institution?.Metadata;
if (plaidMetadata is null)
{
return Results.InternalServerError("No metadata associated with account");
}
var accessToken = await encryptor.DecryptAsyncFor(user, plaidMetadata.EncryptedAccessToken, ct);
var fireEventResponse = await plaidClient.SandboxItemFireWebhookAsync(new()
{
AccessToken = accessToken,
WebhookType = SandboxItemFireWebhookRequestWebhookTypeEnum.Transactions,
WebhookCode = SandboxItemFireWebhookRequestWebhookCodeEnum.SyncUpdatesAvailable,
});
if (fireEventResponse.IsSuccessStatusCode is false)
{
return Results.InternalServerError(fireEventResponse.Error?.ErrorMessage);
}
return Results.Ok(new { fireEventResponse.RequestId, fireEventResponse.WebhookFired });
}
}