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:
parent
c59d0caba2
commit
3644730b5f
|
@ -147,7 +147,10 @@ static async void HandleTap(IsoReader reader, HttpClient httpClient, TerminalSta
|
||||||
case TerminalState.Debit:
|
case TerminalState.Debit:
|
||||||
case TerminalState.Credit:
|
case TerminalState.Credit:
|
||||||
var finalAmount = Math.Abs(amount) * (state == TerminalState.Debit ? 1 : -1);
|
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;
|
break;
|
||||||
case TerminalState.Link:
|
case TerminalState.Link:
|
||||||
await CallGet(httpClient, String.Format("/api/card/link?card={0}", Convert.ToHexString(uid)));
|
await CallGet(httpClient, String.Format("/api/card/link?card={0}", Convert.ToHexString(uid)));
|
||||||
|
|
|
@ -6,7 +6,8 @@ namespace AfRApay.Web.Backend.Tables;
|
||||||
|
|
||||||
[Table(Name = "Users")]
|
[Table(Name = "Users")]
|
||||||
public class User {
|
public class User {
|
||||||
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
|
[Column(Name = "ID"), PrimaryKey, Identity, NotNull] public int Id { get; set; }
|
||||||
[Column(Name = "Nickname"), NotNull] public string Nickname { get; set; }
|
[Column(Name = "Nickname"), NotNull] public string Nickname { get; set; }
|
||||||
[Column(Name = "Balance")] public decimal Balance { get; set; }
|
[Column(Name = "Balance")] public decimal Balance { get; set; }
|
||||||
|
[Column(Name = "LastIdempotencyKey")] public string LastIdempotencyKey { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,18 +7,23 @@ namespace AfRApay.Web.Controllers;
|
||||||
[ApiController, Route("/api/card/transaction")]
|
[ApiController, Route("/api/card/transaction")]
|
||||||
public class CardTransaction : Controller {
|
public class CardTransaction : Controller {
|
||||||
[HttpGet]
|
[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();
|
var db = new Database.DbConn();
|
||||||
if (db.Cards.Any(p => p.CardId == card)) {
|
if (db.Cards.Any(p => p.CardId == card)) {
|
||||||
var userId = db.Cards.First(p => p.CardId == card).UserId;
|
var userId = db.Cards.First(p => p.CardId == card).UserId;
|
||||||
var user = db.Users.First(p => p.Id == userId);
|
var user = db.Users.First(p => p.Id == userId);
|
||||||
|
|
||||||
if (user.Balance - amount < -50) {
|
// If an idempotency key is given, disregard duplicate requests.
|
||||||
return "E:Balance too low!";
|
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}";
|
return $"S:{user.Nickname}: {user.Balance:N2}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue