mirror of
https://github.com/NecroticBamboo/QRBee.git
synced 2025-12-21 12:11:53 +00:00
Work on Transaction started. Pre-checks complete.
This commit is contained in:
parent
4722585e17
commit
82b8d1820e
@ -207,18 +207,18 @@ 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)
|
||||
public virtual System.Threading.Tasks.Task<PaymentResponse> PayAsync(PaymentRequest body)
|
||||
{
|
||||
return InsertTransactionAsync(body, System.Threading.CancellationToken.None);
|
||||
return PayAsync(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)
|
||||
public virtual async System.Threading.Tasks.Task<PaymentResponse> PayAsync(PaymentRequest body, System.Threading.CancellationToken cancellationToken)
|
||||
{
|
||||
var urlBuilder_ = new System.Text.StringBuilder();
|
||||
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/QRBee/InsertTransaction");
|
||||
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/QRBee/Pay");
|
||||
|
||||
var client_ = _httpClient;
|
||||
var disposeClient_ = false;
|
||||
@ -230,6 +230,7 @@ namespace QRBee.Core.Client
|
||||
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
|
||||
request_.Content = content_;
|
||||
request_.Method = new System.Net.Http.HttpMethod("POST");
|
||||
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain"));
|
||||
|
||||
PrepareRequest(client_, request_, urlBuilder_);
|
||||
|
||||
@ -254,7 +255,12 @@ namespace QRBee.Core.Client
|
||||
var status_ = (int)response_.StatusCode;
|
||||
if (status_ == 200)
|
||||
{
|
||||
return;
|
||||
var objectResponse_ = await ReadObjectResponseAsync<PaymentResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
return objectResponse_.Object;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -15,7 +15,36 @@
|
||||
/// WARNING: this should always be encrypted and never transmitted in clear text form.
|
||||
/// </summary>
|
||||
/// <returns>Converted string</returns>
|
||||
public string AsString() => $"{TransactionId}|{CardNumber}|{ExpirationDateMMYY}|{ValidFrom}|{CardHolderName}|{CVC}|{IssueNo}";
|
||||
public string AsString() => $"{TransactionId}|{CardNumber}|{ExpirationDateMMYY}|{ValidFrom}|{CardHolderName}|{CVC}|{IssueNo ?? 0}";
|
||||
|
||||
public static ClientCardData FromString(string input)
|
||||
{
|
||||
var s = input.Split('|');
|
||||
if (s.Length < 7)
|
||||
{
|
||||
throw new ApplicationException("Expected 7 or more elements");
|
||||
}
|
||||
|
||||
var res = new ClientCardData()
|
||||
{
|
||||
TransactionId = s[0],
|
||||
CardNumber = s[1],
|
||||
ExpirationDateMMYY = s[2],
|
||||
ValidFrom = s[3],
|
||||
CardHolderName = s[4],
|
||||
CVC = s[5]
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(s[6]))
|
||||
res.IssueNo = Convert.ToInt32(s[6]);
|
||||
|
||||
if (res.IssueNo <= 0)
|
||||
{
|
||||
res.IssueNo = null;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,16 +30,10 @@ namespace QRBee.Droid
|
||||
LoadApplication(new App(AddServices));
|
||||
ZXing.Mobile.MobileBarcodeScanner.Initialize(Application);
|
||||
|
||||
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) == (int) Permission.Granted)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) != (int) Permission.Granted)
|
||||
{
|
||||
ActivityCompat.RequestPermissions(this, new String[] {Manifest.Permission.Camera}, 0);
|
||||
// ActivityCompat.RequestPermissions(this, new String[] { Manifest.Permission.UseFingerprint }, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
|
||||
|
||||
@ -48,8 +48,9 @@ namespace QRBee.ViewModels
|
||||
//QrCode = null;
|
||||
IsVisible = false;
|
||||
|
||||
await service.InsertTransactionAsync(paymentRequest);
|
||||
var response = await service.PayAsync(paymentRequest);
|
||||
|
||||
//TODO handle response
|
||||
await Application.Current.MainPage.DisplayAlert("Success", "The transaction completed successfully ", "Ok");
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@ -35,11 +35,11 @@ namespace QRBee.Api.Controllers
|
||||
return _service.Update(clientId,value);
|
||||
}
|
||||
|
||||
[HttpPost("InsertTransaction")]
|
||||
public Task InsertTransaction([FromBody] PaymentRequest value)
|
||||
[HttpPost("Pay")]
|
||||
public Task<PaymentResponse> Pay([FromBody] PaymentRequest value)
|
||||
{
|
||||
_logger.LogInformation($"Trying to insert new transaction {value.ClientResponse.MerchantRequest.MerchantTransactionId}");
|
||||
return _service.InsertTransaction(value);
|
||||
return _service.Pay(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,13 @@
|
||||
/// <param name="info">Information to be inserted</param>
|
||||
Task PutTransactionInfo(TransactionInfo info);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve transaction information from database
|
||||
/// </summary>
|
||||
/// <param name="id">Identifier by which transaction information will be retrieved</param>
|
||||
/// <returns>Transaction information</returns>
|
||||
Task<TransactionInfo> GetTransactionInfoByTransactionId(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts CertificateInfo into database
|
||||
/// </summary>
|
||||
@ -43,6 +50,13 @@
|
||||
/// </summary>
|
||||
/// <param name="id">Identifier by which certificate information will be retrieved</param>
|
||||
/// <returns>Certificate information</returns>
|
||||
Task<CertificateInfo> GetCertificateInfo(string id);
|
||||
Task<CertificateInfo> GetCertificateInfoByCertificateId(string id);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve certificate information from database
|
||||
/// </summary>
|
||||
/// <param name="clientId">Identifier by which certificate information will be retrieved</param>
|
||||
/// <returns>Certificate information</returns>
|
||||
Task<CertificateInfo> GetCertificateInfoByUserId(string clientId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,6 +102,12 @@ namespace QRBee.Api.Services.Database
|
||||
return cursor.Current.FirstOrDefault();
|
||||
}
|
||||
|
||||
public async Task<TransactionInfo> GetTransactionInfoByTransactionId(string id)
|
||||
{
|
||||
var transaction = await TryGetTransactionInfo(id);
|
||||
return transaction ?? throw new ApplicationException($"Transaction with Id: {id} not found.");
|
||||
}
|
||||
|
||||
public async Task InsertCertificate(CertificateInfo info)
|
||||
{
|
||||
var collection = _database.GetCollection<CertificateInfo>("Certificates");
|
||||
@ -131,7 +137,7 @@ namespace QRBee.Api.Services.Database
|
||||
/// <returns>null if certificate doesn't exist or CertificateInfo</returns>
|
||||
private async Task<CertificateInfo?> TryGetCertificateInfo(string id)
|
||||
{
|
||||
var collection = _database.GetCollection<CertificateInfo>("Transactions");
|
||||
var collection = _database.GetCollection<CertificateInfo>("Certificates");
|
||||
using var cursor = await collection.FindAsync($"{{ Id: \"{id}\" }}");
|
||||
if (!await cursor.MoveNextAsync())
|
||||
{
|
||||
@ -141,10 +147,23 @@ namespace QRBee.Api.Services.Database
|
||||
return cursor.Current.FirstOrDefault();
|
||||
}
|
||||
|
||||
public async Task<CertificateInfo> GetCertificateInfo(string id)
|
||||
public async Task<CertificateInfo> GetCertificateInfoByCertificateId(string id)
|
||||
{
|
||||
var certificate = await TryGetCertificateInfo(id);
|
||||
return certificate ?? throw new ApplicationException($"Certificate with Id: {id} not found.");
|
||||
}
|
||||
|
||||
public async Task<CertificateInfo> GetCertificateInfoByUserId(string clientId)
|
||||
{
|
||||
var collection = _database.GetCollection<CertificateInfo>("Certificates");
|
||||
using var cursor = await collection.FindAsync($"{{ ClientId: \"{clientId}\" }}");
|
||||
if (!await cursor.MoveNextAsync())
|
||||
{
|
||||
throw new ApplicationException($"Certificate with ClientId: {clientId} not found.");
|
||||
}
|
||||
|
||||
return cursor.Current.FirstOrDefault() ?? throw new ApplicationException($"Certificate with ClientId: {clientId} not found.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,5 +34,12 @@ namespace QRBee.Api.Services.Database
|
||||
|
||||
public PaymentRequest Request { get; set; }
|
||||
|
||||
public enum TransactionStatus
|
||||
{
|
||||
Pending = 0,
|
||||
Rejected = 1,
|
||||
Succeeded = 2,
|
||||
}
|
||||
public TransactionStatus Status { get; set; } = TransactionStatus.Pending;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ namespace QRBee.Api.Services
|
||||
/// Handles InsertTransaction request
|
||||
/// </summary>
|
||||
/// <param name="value">Payment request</param>
|
||||
Task InsertTransaction(PaymentRequest value);
|
||||
Task<PaymentResponse> Pay(PaymentRequest value);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ namespace QRBee.Api.Services
|
||||
public async Task<RegistrationResponse> Register(RegistrationRequest request)
|
||||
{
|
||||
|
||||
Validate(request);
|
||||
ValidateRegistration(request);
|
||||
|
||||
var info = Convert(request);
|
||||
|
||||
@ -69,18 +69,11 @@ namespace QRBee.Api.Services
|
||||
|
||||
public Task Update(string clientId, RegistrationRequest request)
|
||||
{
|
||||
Validate(request);
|
||||
ValidateRegistration(request);
|
||||
var info = Convert(request);
|
||||
return _storage.UpdateUser(info);
|
||||
}
|
||||
|
||||
public Task InsertTransaction(PaymentRequest value)
|
||||
{
|
||||
var info = Convert(value);
|
||||
return _storage.PutTransactionInfo(info);
|
||||
}
|
||||
|
||||
private void Validate(RegistrationRequest request)
|
||||
private void ValidateRegistration(RegistrationRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
@ -130,6 +123,105 @@ namespace QRBee.Api.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PaymentResponse> Pay(PaymentRequest value)
|
||||
{
|
||||
//1. Check payment request parameters for validity
|
||||
ValidateTransaction(value);
|
||||
|
||||
//2. Check client signature
|
||||
var t2 = CheckSignature(
|
||||
value.ClientResponse.AsDataForSignature(),
|
||||
value.ClientResponse.ClientSignature,
|
||||
value.ClientResponse.ClientId);
|
||||
|
||||
//3. Check merchant signature
|
||||
var t3 = CheckSignature(
|
||||
value.ClientResponse.MerchantRequest.AsDataForSignature(),
|
||||
value.ClientResponse.MerchantRequest.MerchantSignature,
|
||||
value.ClientResponse.MerchantRequest.MerchantId);
|
||||
|
||||
//4. Check if transaction was already processed
|
||||
var t4 = CheckTransaction(value.ClientResponse.MerchantRequest.MerchantTransactionId);
|
||||
|
||||
//Parallel task execution
|
||||
await Task.WhenAll(t2, t3, t4);
|
||||
|
||||
//5. Decrypt client card data
|
||||
var clientCardData = DecryptClientData(value.ClientResponse.EncryptedClientCardData);
|
||||
|
||||
//6. Check client card data for validity
|
||||
//7. Register preliminary transaction record with expiry of one minute
|
||||
//8. Send client card data to a payment gateway
|
||||
//9. Record transaction with result
|
||||
//10. Make response for merchant
|
||||
var info = Convert(value);
|
||||
await _storage.PutTransactionInfo(info);
|
||||
return new PaymentResponse();
|
||||
}
|
||||
|
||||
private void ValidateTransaction(PaymentRequest request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new NullReferenceException();
|
||||
}
|
||||
|
||||
var clientId = request.ClientResponse.ClientId;
|
||||
var merchantId = request.ClientResponse.MerchantRequest.MerchantId;
|
||||
var transactionId = request.ClientResponse.MerchantRequest.MerchantTransactionId;
|
||||
var amount = request.ClientResponse.MerchantRequest.Amount;
|
||||
|
||||
if (clientId == null || merchantId == null || transactionId == null)
|
||||
{
|
||||
throw new ApplicationException("Id isn't valid");
|
||||
}
|
||||
|
||||
if (amount is <= 0 or >= 10000)
|
||||
{
|
||||
throw new ApplicationException($"Amount \"{amount}\" isn't valid");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckSignature(string data,string signature, string id)
|
||||
{
|
||||
var info = await _storage.GetCertificateInfoByUserId(id);
|
||||
var certificate = _securityService.Deserialize(info.Certificate);
|
||||
|
||||
var check = _securityService.Verify(
|
||||
Encoding.UTF8.GetBytes(data),
|
||||
System.Convert.FromBase64String(signature),
|
||||
certificate);
|
||||
|
||||
if (!check)
|
||||
{
|
||||
throw new ApplicationException($"Signature is incorrect for Id: {id}.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckTransaction(string transactionId)
|
||||
{
|
||||
var info = await _storage.GetTransactionInfoByTransactionId(transactionId);
|
||||
switch (info.Status)
|
||||
{
|
||||
case TransactionInfo.TransactionStatus.Succeeded:
|
||||
throw new ApplicationException($"Transaction with Id: {transactionId} was already made.");
|
||||
case TransactionInfo.TransactionStatus.Rejected:
|
||||
throw new ApplicationException($"Transaction with Id: {transactionId} is not valid.");
|
||||
case TransactionInfo.TransactionStatus.Pending:
|
||||
throw new ApplicationException($"Transaction with Id: {transactionId} is already in progress.");
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private ClientCardData DecryptClientData(string encryptedClientCardData)
|
||||
{
|
||||
var info = System.Convert.FromBase64String(encryptedClientCardData);
|
||||
var bytes = _securityService.Decrypt(info);
|
||||
var s = Encoding.UTF8.GetString(bytes);
|
||||
return ClientCardData.FromString(s);
|
||||
}
|
||||
|
||||
private static RSA LoadRsaPublicKey(StringRSAParameters stringParameters)
|
||||
{
|
||||
var rsaParameters = new RSAParameters
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user