diff --git a/QRBee.Core/Client/Client.cs b/QRBee.Core/Client/Client.cs
index 9a1b912..8e70d31 100644
--- a/QRBee.Core/Client/Client.cs
+++ b/QRBee.Core/Client/Client.cs
@@ -205,6 +205,77 @@ namespace QRBee.Core.Client
}
}
+ /// Success
+ /// A server side error occurred.
+ public virtual System.Threading.Tasks.Task InsertTransactionAsync(PaymentRequest body)
+ {
+ return InsertTransactionAsync(body, System.Threading.CancellationToken.None);
+ }
+
+ /// A cancellation token that can be used by other objects or threads to receive notice of cancellation.
+ /// Success
+ /// A server side error occurred.
+ 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
{
public ObjectResponseResult(T responseObject, string responseText)
diff --git a/QRBee.Core/Data/ClientToMerchantResponse.cs b/QRBee.Core/Data/ClientToMerchantResponse.cs
index 07f2339..a54b65a 100644
--- a/QRBee.Core/Data/ClientToMerchantResponse.cs
+++ b/QRBee.Core/Data/ClientToMerchantResponse.cs
@@ -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)
///
/// Converted string
- public string AsString() => $"{ClientId}|{TimeStampUTC:O}|{Request.AsString()}";
+ public string AsQRCodeString() => $"{ClientId}|{TimeStampUTC:O}|{MerchantRequest.AsQRCodeString()}";
///
/// 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)
};
diff --git a/QRBee.Core/Data/MerchantToClientRequest.cs b/QRBee.Core/Data/MerchantToClientRequest.cs
index ea09ed9..42e7a8b 100644
--- a/QRBee.Core/Data/MerchantToClientRequest.cs
+++ b/QRBee.Core/Data/MerchantToClientRequest.cs
@@ -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)
///
/// String conversion
- 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}";
///
/// 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)
};
diff --git a/QRBee.Core/Data/PaymentRequest.cs b/QRBee.Core/Data/PaymentRequest.cs
index a3fb9b4..1b5adda 100644
--- a/QRBee.Core/Data/PaymentRequest.cs
+++ b/QRBee.Core/Data/PaymentRequest.cs
@@ -3,7 +3,7 @@
public record PaymentRequest
{
- public ClientToMerchantResponse Request
+ public ClientToMerchantResponse ClientResponse
{
get;
set;
@@ -13,7 +13,18 @@
/// Convert PaymentRequest to string
///
/// Converted string
- 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};
+ }
}
}
diff --git a/QRBee/QRBee/ViewModels/ClientPageViewModel.cs b/QRBee/QRBee/ViewModels/ClientPageViewModel.cs
index fe8bc09..fc1d2b8 100644
--- a/QRBee/QRBee/ViewModels/ClientPageViewModel.cs
+++ b/QRBee/QRBee/ViewModels/ClientPageViewModel.cs
@@ -37,12 +37,12 @@ namespace QRBee.ViewModels
{
var scanner = DependencyService.Get();
var result = await scanner.ScanQR();
- if (result != null)
- {
- _merchantToClientRequest = MerchantToClientRequest.FromString(result);
- Amount = $"{_merchantToClientRequest.Amount:N2}";
- IsVisible = true;
- }
+ if (result == null)
+ return;
+
+ _merchantToClientRequest = MerchantToClientRequest.FromString(result);
+ Amount = $"{_merchantToClientRequest.Amount:N2}";
+ IsVisible = true;
}
catch (Exception)
{
@@ -99,21 +99,22 @@ 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 response = new ClientToMerchantResponse
- {
- ClientId = Guid.NewGuid().ToString("D"),
- TimeStampUTC = DateTime.UtcNow,
- Request = _merchantToClientRequest
-
- };
- // TODO Create merchant signature.
- QrCode = response.AsString();
- }
+ 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,
+ MerchantRequest = _merchantToClientRequest
+
+ };
+ // TODO Create merchant signature.
+ QrCode = response.AsQRCodeString();
+
-
}
}
diff --git a/QRBee/QRBee/ViewModels/MerchantPageViewModel.cs b/QRBee/QRBee/ViewModels/MerchantPageViewModel.cs
index 47942b5..2e1d864 100644
--- a/QRBee/QRBee/ViewModels/MerchantPageViewModel.cs
+++ b/QRBee/QRBee/ViewModels/MerchantPageViewModel.cs
@@ -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();
var result = await scanner.ScanQR();
- if (result != null)
- {
+ if (result == null)
+ return;
- }
+ var client = new HttpClient(GetInsecureHandler());
+ var localSettings = DependencyService.Resolve();
+
+ 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;
}
diff --git a/QRBee/QRBee/ViewModels/RegisterViewModel.cs b/QRBee/QRBee/ViewModels/RegisterViewModel.cs
index f48ac53..5058cec 100644
--- a/QRBee/QRBee/ViewModels/RegisterViewModel.cs
+++ b/QRBee/QRBee/ViewModels/RegisterViewModel.cs
@@ -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();
var service = new Core.Client.Client(localSettings.QRBeeApiUrl,client);
diff --git a/QRBee/QRBee/Views/MerchantPage.xaml b/QRBee/QRBee/Views/MerchantPage.xaml
index 3083f91..299adf2 100644
--- a/QRBee/QRBee/Views/MerchantPage.xaml
+++ b/QRBee/QRBee/Views/MerchantPage.xaml
@@ -27,7 +27,8 @@
BarcodeFormat="QR_CODE"
BarcodeValue="{Binding QrCode}"
HorizontalOptions="FillAndExpand"
- VerticalOptions="FillAndExpand">
+ VerticalOptions="FillAndExpand"
+ IsVisible="{Binding IsVisible}">
diff --git a/QRBeeApi/Controllers/QRBeeController.cs b/QRBeeApi/Controllers/QRBeeController.cs
index b93fd37..1fd8bc6 100644
--- a/QRBeeApi/Controllers/QRBeeController.cs
+++ b/QRBeeApi/Controllers/QRBeeController.cs
@@ -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);
+ }
}
}
diff --git a/QRBeeApi/Services/Database/IStorage.cs b/QRBeeApi/Services/Database/IStorage.cs
index b362113..a839d15 100644
--- a/QRBeeApi/Services/Database/IStorage.cs
+++ b/QRBeeApi/Services/Database/IStorage.cs
@@ -9,7 +9,6 @@
/// Insert userInfo into database
///
/// Information to be inserted
- ///
Task PutUserInfo(UserInfo info);
///
@@ -19,7 +18,17 @@
/// User information
Task GetUserInfo(string email);
+ ///
+ /// Update user record
+ ///
+ /// New user record
+ ///
Task UpdateUser(UserInfo info);
+ ///
+ /// Insert transactionInfo into database
+ ///
+ /// Information to be inserted
+ Task PutTransactionInfo(TransactionInfo info);
}
}
diff --git a/QRBeeApi/Services/Database/Storage.cs b/QRBeeApi/Services/Database/Storage.cs
index df59218..505814d 100644
--- a/QRBeeApi/Services/Database/Storage.cs
+++ b/QRBeeApi/Services/Database/Storage.cs
@@ -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("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}");
+ }
+
+ ///
+ /// Try to find if the Transaction already exists in the database
+ ///
+ /// parameter by which to find TransactionInfo
+ /// null if transaction doesn't exist or TransactionInfo
+ private async Task TryGetTransactionInfo(string id)
+ {
+ var collection = _database.GetCollection("Transactions");
+ using var cursor = await collection.FindAsync($"{{ Id: \"{id}\" }}");
+ if (!await cursor.MoveNextAsync())
+ {
+ return null;
+ }
+
+ return cursor.Current.FirstOrDefault();
+ }
}
}
diff --git a/QRBeeApi/Services/Database/TransactionInfo.cs b/QRBeeApi/Services/Database/TransactionInfo.cs
new file mode 100644
index 0000000..e892bd9
--- /dev/null
+++ b/QRBeeApi/Services/Database/TransactionInfo.cs
@@ -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}";
+ }
+
+ ///
+ /// Never use directly. Use instead.
+ ///
+ [BsonId] public string Id { get; set; }
+
+ [BsonIgnore] public string? TransactionId => Id;
+ public DateTime ServerTimeStamp { get; set; }
+
+ public PaymentRequest Request { get; set; }
+
+ }
+}
diff --git a/QRBeeApi/Services/IQRBeeAPI.cs b/QRBeeApi/Services/IQRBeeAPI.cs
index cf64ebf..8442b5b 100644
--- a/QRBeeApi/Services/IQRBeeAPI.cs
+++ b/QRBeeApi/Services/IQRBeeAPI.cs
@@ -23,5 +23,11 @@ namespace QRBee.Api.Services
/// Update request
Task Update(string clientId, RegistrationRequest value);
+ ///
+ /// Handles InsertTransaction request
+ ///
+ /// Payment request
+ Task InsertTransaction(PaymentRequest value);
+
}
}
diff --git a/QRBeeApi/Services/QRBeeAPI.cs b/QRBeeApi/Services/QRBeeAPI.cs
index f39bf31..064a254 100644
--- a/QRBeeApi/Services/QRBeeAPI.cs
+++ b/QRBeeApi/Services/QRBeeAPI.cs
@@ -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);
+ }
+
}
}