AfRApay.Web: Make transactions idempotent (#12)

Co-authored-by: embr <git@liclac.eu>
Reviewed-on: #12
Co-authored-by: embr <embr@noreply.zotan.network>
Co-committed-by: embr <embr@noreply.zotan.network>
This commit is contained in:
embr 2023-02-09 01:36:34 +01:00 committed by Laura Hausmann
parent c59d0caba2
commit 3644730b5f
Signed by: zotan's git
GPG key ID: C40BFEA2B3BA06A8
3 changed files with 20 additions and 11 deletions

View file

@ -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, Uri.EscapeDataString(Convert.ToBase64String(idempotencyKey))));
break;
case TerminalState.Link:
await CallGet(httpClient, String.Format("/api/card/link?card={0}", Convert.ToHexString(uid)));

View file

@ -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; }
}

View file

@ -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}";
}