diff --git a/AfRApay.FTM/Program.cs b/AfRApay.FTM/Program.cs index 9bbd8bb..5b63610 100644 --- a/AfRApay.FTM/Program.cs +++ b/AfRApay.FTM/Program.cs @@ -147,7 +147,10 @@ static async void HandleTap(IsoReader reader, HttpClient httpClient, TerminalSta case TerminalState.Debit: case TerminalState.Credit: var finalAmount = Math.Abs(amount) * (state == TerminalState.Debit ? 1 : -1); - await CallGet(httpClient, String.Format("/api/card/transaction?card={0}&amount={1}", Convert.ToHexString(uid), finalAmount)); + var idempotencyKey = new byte[18]; + Random.Shared.NextBytes(idempotencyKey); + await CallGet(httpClient, String.Format("/api/card/transaction?card={0}&amount={1}&ik={2}", + Convert.ToHexString(uid), finalAmount, Convert.ToBase64String(idempotencyKey))); break; case TerminalState.Link: await CallGet(httpClient, String.Format("/api/card/link?card={0}", Convert.ToHexString(uid))); diff --git a/AfRApay.Web/Backend/Tables/User.cs b/AfRApay.Web/Backend/Tables/User.cs index 1526b59..e2818c1 100644 --- a/AfRApay.Web/Backend/Tables/User.cs +++ b/AfRApay.Web/Backend/Tables/User.cs @@ -6,7 +6,8 @@ namespace AfRApay.Web.Backend.Tables; [Table(Name = "Users")] public class User { - [Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; } - [Column(Name = "Nickname"), NotNull] public string Nickname { get; set; } - [Column(Name = "Balance")] public decimal Balance { get; set; } + [Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; } + [Column(Name = "Nickname"), NotNull] public string Nickname { get; set; } + [Column(Name = "Balance")] public decimal Balance { get; set; } + [Column(Name = "LastIdempotencyKey")] public string LastIdempotencyKey { get; set; } } diff --git a/AfRApay.Web/Controllers/CardTransaction.cs b/AfRApay.Web/Controllers/CardTransaction.cs index 0f73b57..15f6a05 100644 --- a/AfRApay.Web/Controllers/CardTransaction.cs +++ b/AfRApay.Web/Controllers/CardTransaction.cs @@ -7,18 +7,23 @@ namespace AfRApay.Web.Controllers; [ApiController, Route("/api/card/transaction")] public class CardTransaction : Controller { [HttpGet] - public string Get([FromQuery] string card, [FromQuery] decimal amount) { + public string Get([FromQuery] string card, [FromQuery] decimal amount, [FromQuery] string ik) { var db = new Database.DbConn(); if (db.Cards.Any(p => p.CardId == card)) { var userId = db.Cards.First(p => p.CardId == card).UserId; var user = db.Users.First(p => p.Id == userId); - - if (user.Balance - amount < -50) { - return "E:Balance too low!"; + + // If an idempotency key is given, disregard duplicate requests. + if (ik == "" || ik != user.LastIdempotencyKey) { + if (user.Balance - amount < -50) { + return "E:Balance too low!"; + } + + user.Balance -= amount; + user.LastIdempotencyKey = ik; + db.Update(user); } - - user.Balance -= amount; - db.Update(user); + return $"S:{user.Nickname}: {user.Balance:N2}"; }