Files
step3/api/QuestionsApi/Services/DonationStore.cs
T
2026-07-19 22:26:33 -05:00

75 lines
3.6 KiB
C#

using MongoDB.Driver;
using QuestionsApi.Models;
namespace QuestionsApi.Services;
public sealed class DonationStore
{
private readonly IMongoCollection<Donation> _donations;
public DonationStore(MongoDatabaseProvider mongo)
{
_donations = mongo.Database.GetCollection<Donation>("Donations");
_donations.Indexes.CreateMany([
new CreateIndexModel<Donation>(Builders<Donation>.IndexKeys.Ascending(value => value.UserId).Descending(value => value.CreatedUtc)),
new CreateIndexModel<Donation>(Builders<Donation>.IndexKeys.Ascending(value => value.StripeCheckoutSessionId),
new CreateIndexOptions<Donation>
{
Unique = true,
PartialFilterExpression = Builders<Donation>.Filter.Type(value => value.StripeCheckoutSessionId, MongoDB.Bson.BsonType.String)
}),
new CreateIndexModel<Donation>(Builders<Donation>.IndexKeys.Ascending(value => value.Status).Descending(value => value.CreatedUtc))
]);
}
public async Task<Donation> CreateAsync(string userId, long amountCents)
{
var donation = new Donation { UserId = userId, AmountCents = amountCents };
await _donations.InsertOneAsync(donation);
return donation;
}
public async Task<Donation?> GetOwnedAsync(string id, string userId) =>
await _donations.Find(value => value.Id == id && value.UserId == userId).FirstOrDefaultAsync();
public async Task AttachCheckoutSessionAsync(string id, string sessionId) =>
await _donations.UpdateOneAsync(
value => value.Id == id && value.Status == DonationStatus.Pending,
Builders<Donation>.Update.Set(value => value.StripeCheckoutSessionId, sessionId).Set(value => value.UpdatedUtc, DateTime.UtcNow));
public async Task MarkCheckoutCreationFailedAsync(string id, string failureCode) =>
await _donations.UpdateOneAsync(
value => value.Id == id && value.Status == DonationStatus.Pending,
Builders<Donation>.Update.Set(value => value.Status, DonationStatus.Failed)
.Set(value => value.FailureCode, failureCode).Set(value => value.UpdatedUtc, DateTime.UtcNow));
public async Task<bool> ApplyStripeStatusAsync(
string id,
string sessionId,
long amountCents,
string currency,
string status,
string paymentIntentId,
string eventId)
{
if (string.IsNullOrWhiteSpace(id) || string.IsNullOrWhiteSpace(sessionId)) return false;
var filter = Builders<Donation>.Filter.Eq(value => value.Id, id)
& Builders<Donation>.Filter.Eq(value => value.AmountCents, amountCents)
& Builders<Donation>.Filter.Eq(value => value.Currency, currency.ToLowerInvariant())
& (Builders<Donation>.Filter.Eq(value => value.StripeCheckoutSessionId, sessionId)
| Builders<Donation>.Filter.Eq(value => value.StripeCheckoutSessionId, null));
if (status != DonationStatus.Paid)
filter &= Builders<Donation>.Filter.Eq(value => value.Status, DonationStatus.Pending);
var now = DateTime.UtcNow;
var update = Builders<Donation>.Update.Set(value => value.Status, status)
.Set(value => value.StripeCheckoutSessionId, sessionId)
.Set(value => value.StripePaymentIntentId, paymentIntentId)
.Set(value => value.LastStripeEventId, eventId)
.Set(value => value.UpdatedUtc, now);
if (status == DonationStatus.Paid) update = update.Set(value => value.PaidUtc, now);
return (await _donations.UpdateOneAsync(filter, update)).ModifiedCount > 0;
}
}