mirror of
https://github.com/NecroticBamboo/QRBee.git
synced 2025-12-21 12:11:53 +00:00
Transaction mechanism implemented. UI improved.
This commit is contained in:
parent
ad183ea33f
commit
21f16b862c
@ -205,6 +205,77 @@ namespace QRBee.Core.Client
|
||||
}
|
||||
}
|
||||
|
||||
/// <returns>Success</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public virtual System.Threading.Tasks.Task InsertTransactionAsync(PaymentRequest body)
|
||||
{
|
||||
return InsertTransactionAsync(body, System.Threading.CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <returns>Success</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public virtual async System.Threading.Tasks.Task InsertTransactionAsync(PaymentRequest body, System.Threading.CancellationToken cancellationToken)
|
||||
{
|
||||
var urlBuilder_ = new System.Text.StringBuilder();
|
||||
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/QRBee/InsertTransaction");
|
||||
|
||||
var client_ = _httpClient;
|
||||
var disposeClient_ = false;
|
||||
try
|
||||
{
|
||||
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||
{
|
||||
var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value));
|
||||
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
|
||||
request_.Content = content_;
|
||||
request_.Method = new System.Net.Http.HttpMethod("POST");
|
||||
|
||||
PrepareRequest(client_, request_, urlBuilder_);
|
||||
|
||||
var url_ = urlBuilder_.ToString();
|
||||
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||
|
||||
PrepareRequest(client_, request_, url_);
|
||||
|
||||
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
var disposeResponse_ = true;
|
||||
try
|
||||
{
|
||||
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
|
||||
if (response_.Content != null && response_.Content.Headers != null)
|
||||
{
|
||||
foreach (var item_ in response_.Content.Headers)
|
||||
headers_[item_.Key] = item_.Value;
|
||||
}
|
||||
|
||||
ProcessResponse(client_, response_);
|
||||
|
||||
var status_ = (int)response_.StatusCode;
|
||||
if (status_ == 200)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeResponse_)
|
||||
response_.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeClient_)
|
||||
client_.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected struct ObjectResponseResult<T>
|
||||
{
|
||||
public ObjectResponseResult(T responseObject, string responseText)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
{
|
||||
public record ClientToMerchantResponse
|
||||
{
|
||||
public MerchantToClientRequest Request
|
||||
public MerchantToClientRequest MerchantRequest
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@ -36,7 +36,7 @@
|
||||
/// Convert ClientToMerchantResponse to string to be used as QR Code source (along with client signature)
|
||||
/// </summary>
|
||||
/// <returns> Converted string</returns>
|
||||
public string AsString() => $"{ClientId}|{TimeStampUTC:O}|{Request.AsString()}";
|
||||
public string AsQRCodeString() => $"{ClientId}|{TimeStampUTC:O}|{MerchantRequest.AsQRCodeString()}";
|
||||
|
||||
/// <summary>
|
||||
/// Convert from string
|
||||
@ -47,14 +47,14 @@
|
||||
public static ClientToMerchantResponse FromString(string input)
|
||||
{
|
||||
var s = input.Split('|');
|
||||
if (s.Length != 3)
|
||||
if (s.Length < 3)
|
||||
{
|
||||
throw new ApplicationException("Expected 3 elements");
|
||||
throw new ApplicationException("Expected 3 or more elements");
|
||||
}
|
||||
|
||||
var res = new ClientToMerchantResponse()
|
||||
{
|
||||
Request = MerchantToClientRequest.FromString(string.Join("|", s.Skip(2))),
|
||||
MerchantRequest = MerchantToClientRequest.FromString(string.Join("|", s.Skip(2))),
|
||||
ClientId = s[0],
|
||||
TimeStampUTC = DateTime.ParseExact(s[1], "O", null)
|
||||
};
|
||||
|
||||
@ -4,7 +4,13 @@ namespace QRBee.Core.Data
|
||||
{
|
||||
public record MerchantToClientRequest
|
||||
{
|
||||
public string TransactionId
|
||||
public string MerchantId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string MerchantTransactionId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@ -38,7 +44,7 @@ namespace QRBee.Core.Data
|
||||
/// Convert MerchantToClientRequest to string to be used as QR Code source (along with merchant signature)
|
||||
/// </summary>
|
||||
/// <returns>String conversion</returns>
|
||||
public string AsString() => $"{TransactionId}|{Name}|{Amount.ToString("0.00", CultureInfo.InvariantCulture)}|{TimeStampUTC:O}";
|
||||
public string AsQRCodeString() => $"{MerchantTransactionId}|{MerchantId}|{Name}|{Amount.ToString("0.00", CultureInfo.InvariantCulture)}|{TimeStampUTC:O}";
|
||||
|
||||
/// <summary>
|
||||
/// Convert from string
|
||||
@ -49,17 +55,18 @@ namespace QRBee.Core.Data
|
||||
public static MerchantToClientRequest FromString(string input)
|
||||
{
|
||||
var s = input.Split('|');
|
||||
if (s.Length != 4)
|
||||
if (s.Length != 5)
|
||||
{
|
||||
throw new ApplicationException("Expected 4 elements");
|
||||
throw new ApplicationException("Expected 5 elements");
|
||||
}
|
||||
|
||||
var res = new MerchantToClientRequest
|
||||
{
|
||||
TransactionId = s[0],
|
||||
Name = s[1],
|
||||
Amount = Convert.ToDecimal(s[2], CultureInfo.InvariantCulture),
|
||||
TimeStampUTC = DateTime.ParseExact(s[3],"O",null)
|
||||
MerchantId = s[0],
|
||||
MerchantTransactionId = s[1],
|
||||
Name = s[2],
|
||||
Amount = Convert.ToDecimal(s[3], CultureInfo.InvariantCulture),
|
||||
TimeStampUTC = DateTime.ParseExact(s[4],"O",null)
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
public record PaymentRequest
|
||||
{
|
||||
|
||||
public ClientToMerchantResponse Request
|
||||
public ClientToMerchantResponse ClientResponse
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@ -13,7 +13,18 @@
|
||||
/// Convert PaymentRequest to string
|
||||
/// </summary>
|
||||
/// <returns>Converted string</returns>
|
||||
public string AsString() => Request.AsString();
|
||||
public string AsString() => ClientResponse.AsQRCodeString();
|
||||
|
||||
public static PaymentRequest FromString(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
throw new ApplicationException("The input is wrong!");
|
||||
}
|
||||
|
||||
//doesn't work
|
||||
var response = ClientToMerchantResponse.FromString(input);
|
||||
return new PaymentRequest(){ClientResponse = response};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,13 +37,13 @@ namespace QRBee.ViewModels
|
||||
{
|
||||
var scanner = DependencyService.Get<IQRScanner>();
|
||||
var result = await scanner.ScanQR();
|
||||
if (result != null)
|
||||
{
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
_merchantToClientRequest = MerchantToClientRequest.FromString(result);
|
||||
Amount = $"{_merchantToClientRequest.Amount:N2}";
|
||||
IsVisible = true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await Application.Current.MainPage.DisplayAlert("Error","Wrong QR code scanned","Ok");
|
||||
@ -99,19 +99,20 @@ namespace QRBee.ViewModels
|
||||
public async void OnGenerateQrClicked(object obj)
|
||||
{
|
||||
|
||||
bool answer = await _clientPage.DisplayAlert("Confirmation", "Would you like to accept the offer?", "Yes", "No");
|
||||
if (answer)
|
||||
{
|
||||
var answer = await _clientPage.DisplayAlert("Confirmation", "Would you like to accept the offer?", "Yes", "No");
|
||||
if (!answer)
|
||||
return;
|
||||
|
||||
var response = new ClientToMerchantResponse
|
||||
{
|
||||
//TODO get client id from database
|
||||
ClientId = Guid.NewGuid().ToString("D"),
|
||||
TimeStampUTC = DateTime.UtcNow,
|
||||
Request = _merchantToClientRequest
|
||||
MerchantRequest = _merchantToClientRequest
|
||||
|
||||
};
|
||||
// TODO Create merchant signature.
|
||||
QrCode = response.AsString();
|
||||
}
|
||||
QrCode = response.AsQRCodeString();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using QRBee.Core.Data;
|
||||
using QRBee.Services;
|
||||
using Xamarin.Forms;
|
||||
@ -29,17 +30,43 @@ namespace QRBee.ViewModels
|
||||
{
|
||||
var scanner = DependencyService.Get<IQRScanner>();
|
||||
var result = await scanner.ScanQR();
|
||||
if (result != null)
|
||||
{
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
var client = new HttpClient(GetInsecureHandler());
|
||||
var localSettings = DependencyService.Resolve<ILocalSettings>();
|
||||
|
||||
var service = new Core.Client.Client(localSettings.QRBeeApiUrl, client);
|
||||
var paymentRequest = PaymentRequest.FromString(result);
|
||||
|
||||
//QrCode = null;
|
||||
IsVisible = false;
|
||||
|
||||
await service.InsertTransactionAsync(paymentRequest);
|
||||
|
||||
await Application.Current.MainPage.DisplayAlert("Success", "The transaction completed successfully ", "Ok");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception e)
|
||||
{
|
||||
throw;
|
||||
//TODO: delete exception message in error message
|
||||
await Application.Current.MainPage.DisplayAlert("Error", $"The Backend isn't working: {e.Message}", "Ok");
|
||||
}
|
||||
}
|
||||
|
||||
// This method must be in a class in a platform project, even if
|
||||
// the HttpClient object is constructed in a shared project.
|
||||
public HttpClientHandler GetInsecureHandler()
|
||||
{
|
||||
var handler = new HttpClientHandler();
|
||||
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
|
||||
{
|
||||
if (cert.Issuer.Equals("CN=localhost"))
|
||||
return true;
|
||||
return errors == System.Net.Security.SslPolicyErrors.None;
|
||||
};
|
||||
return handler;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public decimal Amount
|
||||
@ -98,13 +125,15 @@ namespace QRBee.ViewModels
|
||||
{
|
||||
var trans = new MerchantToClientRequest
|
||||
{
|
||||
TransactionId = Guid.NewGuid().ToString("D"),
|
||||
//TODO get merchant id from database
|
||||
MerchantId = Guid.NewGuid().ToString("D"),
|
||||
MerchantTransactionId = Guid.NewGuid().ToString("D"),
|
||||
Name = Name,
|
||||
Amount = Amount,
|
||||
TimeStampUTC = DateTime.UtcNow
|
||||
};
|
||||
// TODO Create merchant signature.
|
||||
QrCode = trans.AsString();
|
||||
QrCode = trans.AsQRCodeString();
|
||||
IsVisible = true;
|
||||
}
|
||||
|
||||
|
||||
@ -94,7 +94,8 @@ namespace QRBee.ViewModels
|
||||
|
||||
private async void OnRegisterClicked(object obj)
|
||||
{
|
||||
using var client = new HttpClient(GetInsecureHandler());
|
||||
//TODO when to dispose the client?
|
||||
var client = new HttpClient(GetInsecureHandler());
|
||||
var localSettings = DependencyService.Resolve<ILocalSettings>();
|
||||
|
||||
var service = new Core.Client.Client(localSettings.QRBeeApiUrl,client);
|
||||
|
||||
@ -27,7 +27,8 @@
|
||||
BarcodeFormat="QR_CODE"
|
||||
BarcodeValue="{Binding QrCode}"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand">
|
||||
VerticalOptions="FillAndExpand"
|
||||
IsVisible="{Binding IsVisible}">
|
||||
<forms:ZXingBarcodeImageView.BarcodeOptions>
|
||||
<common:EncodingOptions Width="300" Height="300" />
|
||||
</forms:ZXingBarcodeImageView.BarcodeOptions>
|
||||
|
||||
@ -34,5 +34,12 @@ namespace QRBee.Api.Controllers
|
||||
_logger.LogInformation($"Trying to update user {value.Name}");
|
||||
return _service.Update(clientId,value);
|
||||
}
|
||||
|
||||
[HttpPost("InsertTransaction")]
|
||||
public Task InsertTransaction([FromBody] PaymentRequest value)
|
||||
{
|
||||
_logger.LogInformation($"Trying to insert new transaction {value.ClientResponse.MerchantRequest.MerchantTransactionId}");
|
||||
return _service.InsertTransaction(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
/// Insert userInfo into database
|
||||
/// </summary>
|
||||
/// <param name="info"> Information to be inserted</param>
|
||||
/// <returns></returns>
|
||||
Task<string> PutUserInfo(UserInfo info);
|
||||
|
||||
/// <summary>
|
||||
@ -19,7 +18,17 @@
|
||||
/// <returns>User information</returns>
|
||||
Task<UserInfo> GetUserInfo(string email);
|
||||
|
||||
/// <summary>
|
||||
/// Update user record
|
||||
/// </summary>
|
||||
/// <param name="info">New user record</param>
|
||||
/// <returns></returns>
|
||||
Task UpdateUser(UserInfo info);
|
||||
|
||||
/// <summary>
|
||||
/// Insert transactionInfo into database
|
||||
/// </summary>
|
||||
/// <param name="info">Information to be inserted</param>
|
||||
Task PutTransactionInfo(TransactionInfo info);
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,5 +68,37 @@ namespace QRBee.Api.Services.Database
|
||||
await collection.ReplaceOneAsync($"{{ _id: \"{info.Id}\" }}",info, new ReplaceOptions(){IsUpsert = false});
|
||||
}
|
||||
|
||||
public async Task PutTransactionInfo(TransactionInfo info)
|
||||
{
|
||||
var collection = _database.GetCollection<TransactionInfo>("Transactions");
|
||||
|
||||
var transaction = await TryGetTransactionInfo(info.Id);
|
||||
|
||||
if (transaction == null)
|
||||
{
|
||||
await collection.InsertOneAsync(info);
|
||||
_logger.LogInformation($"Inserted new transaction with ID: {info.Id}");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Found transaction with ClientId: {info.Id}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to find if the Transaction already exists in the database
|
||||
/// </summary>
|
||||
/// <param name="id">parameter by which to find TransactionInfo</param>
|
||||
/// <returns>null if transaction doesn't exist or TransactionInfo</returns>
|
||||
private async Task<TransactionInfo?> TryGetTransactionInfo(string id)
|
||||
{
|
||||
var collection = _database.GetCollection<TransactionInfo>("Transactions");
|
||||
using var cursor = await collection.FindAsync($"{{ Id: \"{id}\" }}");
|
||||
if (!await cursor.MoveNextAsync())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return cursor.Current.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
38
QRBeeApi/Services/Database/TransactionInfo.cs
Normal file
38
QRBeeApi/Services/Database/TransactionInfo.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using QRBee.Core.Data;
|
||||
|
||||
namespace QRBee.Api.Services.Database
|
||||
{
|
||||
public record TransactionInfo
|
||||
{
|
||||
|
||||
#pragma warning disable CS8618
|
||||
public TransactionInfo()
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TransactionInfo(PaymentRequest request, DateTime serverTimeStamp)
|
||||
{
|
||||
//TODO This is a side effect. The original request will be modified.
|
||||
request.ClientResponse.EncryptedClientCardData = null;
|
||||
|
||||
ServerTimeStamp = serverTimeStamp;
|
||||
Request = request;
|
||||
Id = $"{request.ClientResponse.MerchantRequest.MerchantId}-{request.ClientResponse.MerchantRequest.MerchantTransactionId}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Never use directly. Use <see cref="TransactionId"/> instead.
|
||||
/// </summary>
|
||||
[BsonId] public string Id { get; set; }
|
||||
|
||||
[BsonIgnore] public string? TransactionId => Id;
|
||||
public DateTime ServerTimeStamp { get; set; }
|
||||
|
||||
public PaymentRequest Request { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@ -23,5 +23,11 @@ namespace QRBee.Api.Services
|
||||
/// <param name="value">Update request</param>
|
||||
Task Update(string clientId, RegistrationRequest value);
|
||||
|
||||
/// <summary>
|
||||
/// Handles InsertTransaction request
|
||||
/// </summary>
|
||||
/// <param name="value">Payment request</param>
|
||||
Task InsertTransaction(PaymentRequest value);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,6 +39,12 @@ namespace QRBee.Api.Services
|
||||
return _storage.UpdateUser(info);
|
||||
}
|
||||
|
||||
public Task InsertTransaction(PaymentRequest value)
|
||||
{
|
||||
var info = Convert(value);
|
||||
return _storage.PutTransactionInfo(info);
|
||||
}
|
||||
|
||||
private static void Validate(RegistrationRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
@ -77,5 +83,10 @@ namespace QRBee.Api.Services
|
||||
return new UserInfo(request.Name, request.Email, request.DateOfBirth);
|
||||
}
|
||||
|
||||
private static TransactionInfo Convert(PaymentRequest request)
|
||||
{
|
||||
return new TransactionInfo(request, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user