Payment transaction fixed.

This commit is contained in:
Andrey Shabarshov 2022-03-30 12:34:20 +01:00
parent 01cafaba14
commit 400a41760b
10 changed files with 167 additions and 127 deletions

View File

@ -4,8 +4,8 @@
{ {
public string TransactionId { get; set; } public string TransactionId { get; set; }
public string CardNumber { get; set; } public string CardNumber { get; set; }
public string ExpirationDateMMYY { get; set; } public string ExpirationDateYYYYMM { get; set; }
public string ValidFrom { get; set; } public string ValidFromYYYYMM { get; set; }
public string CardHolderName { get; set; } public string CardHolderName { get; set; }
public string CVC { get; set; } public string CVC { get; set; }
public int? IssueNo { get; set; } public int? IssueNo { get; set; }
@ -15,7 +15,7 @@
/// WARNING: this should always be encrypted and never transmitted in clear text form. /// WARNING: this should always be encrypted and never transmitted in clear text form.
/// </summary> /// </summary>
/// <returns>Converted string</returns> /// <returns>Converted string</returns>
public string AsString() => $"{TransactionId}|{CardNumber}|{ExpirationDateMMYY}|{ValidFrom}|{CardHolderName}|{CVC}|{IssueNo ?? 0}"; public string AsString() => $"{TransactionId}|{CardNumber}|{ExpirationDateYYYYMM}|{ValidFromYYYYMM}|{CardHolderName}|{CVC}|{IssueNo ?? 0}";
public static ClientCardData FromString(string input) public static ClientCardData FromString(string input)
{ {
@ -27,12 +27,12 @@
var res = new ClientCardData() var res = new ClientCardData()
{ {
TransactionId = s[0], TransactionId = s[0],
CardNumber = s[1], CardNumber = s[1],
ExpirationDateMMYY = s[2], ExpirationDateYYYYMM = s[2],
ValidFrom = s[3], ValidFromYYYYMM = s[3],
CardHolderName = s[4], CardHolderName = s[4],
CVC = s[5] CVC = s[5]
}; };
if (!string.IsNullOrWhiteSpace(s[6])) if (!string.IsNullOrWhiteSpace(s[6]))

View File

@ -3,22 +3,17 @@
public record ClientToMerchantResponse public record ClientToMerchantResponse
{ {
public MerchantToClientRequest MerchantRequest { get; set; } public MerchantToClientRequest MerchantRequest { get; set; }
public string ClientId { get; set; }
public string ClientId { get; set; } public DateTime TimeStampUTC { get; set; }
public string ClientSignature { get; set; }
public DateTime TimeStampUTC { get; set; } public string EncryptedClientCardData { get; set; }
public string ClientSignature { get; set; }
public string EncryptedClientCardData { get; set; }
/// <summary> /// <summary>
/// Convert ClientToMerchantResponse to string to be used as QR Code source (along with client signature) /// Convert ClientToMerchantResponse to string to be used as QR Code source (along with client signature)
/// </summary> /// </summary>
/// <returns> Converted string</returns> /// <returns> Converted string</returns>
public string AsQRCodeString() => $"{ClientId}|{TimeStampUTC:O}|{ClientSignature}"; public string AsQRCodeString() => $"{ClientId}|{TimeStampUTC:yyyy-MM-dd:HH.mm.ss.ffff}|{ClientSignature}|{EncryptedClientCardData}";
public string AsDataForSignature() => $"{ClientId}|{TimeStampUTC:yyyy-MM-dd:HH.mm.ss.ffff}|{MerchantRequest.AsQRCodeString()}";
public string AsDataForSignature() => $"{ClientId}|{TimeStampUTC:O}|{MerchantRequest.AsQRCodeString()}";
/// <summary> /// <summary>
/// Convert from string /// Convert from string
@ -26,19 +21,26 @@
/// <param name="input">A string representation of ClientToMerchantResponse</param> /// <param name="input">A string representation of ClientToMerchantResponse</param>
/// <returns>Converted string</returns> /// <returns>Converted string</returns>
/// <exception cref="ApplicationException">Thrown if the input string is incorrect</exception> /// <exception cref="ApplicationException">Thrown if the input string is incorrect</exception>
public static ClientToMerchantResponse FromString(string input) public static ClientToMerchantResponse FromString(string input, MerchantToClientRequest merchantRequest)
{ {
if (merchantRequest == null)
throw new ArgumentNullException(nameof(merchantRequest));
if ( string.IsNullOrWhiteSpace(merchantRequest.MerchantSignature) )
throw new ApplicationException("Request is not signed by a merchant");
var s = input.Split('|'); var s = input.Split('|');
if (s.Length < 3) if (s.Length < 4)
{ {
throw new ApplicationException($"Expected 3 or more elements but got {s.Length}"); throw new ApplicationException($"Expected 4 elements but got {s.Length}");
} }
var res = new ClientToMerchantResponse() var res = new ClientToMerchantResponse()
{ {
ClientId = s[0], ClientId = s[0],
TimeStampUTC = DateTime.ParseExact(s[1], "O", null), TimeStampUTC = DateTime.ParseExact(s[1], "yyyy-MM-dd:HH.mm.ss.ffff", null),
ClientSignature = s[2] ClientSignature = s[2],
EncryptedClientCardData = s[3],
MerchantRequest = merchantRequest
}; };
return res; return res;

View File

@ -4,25 +4,19 @@ namespace QRBee.Core.Data
{ {
public record MerchantToClientRequest public record MerchantToClientRequest
{ {
public string MerchantId { get; set; } public string MerchantId { get; set; }
public string MerchantTransactionId { get; set; } public string MerchantTransactionId { get; set; }
public string Name { get; set; }
public string Name { get; set; } public decimal Amount { get; set; }
public DateTime TimeStampUTC { get; set; }
public decimal Amount { get; set; } public string MerchantSignature { get; set; }
public DateTime TimeStampUTC { get; set; }
public string MerchantSignature { get; set; }
/// <summary> /// <summary>
/// Convert MerchantToClientRequest to string to be used as QR Code source (along with merchant signature) /// Convert MerchantToClientRequest to string to be used as QR Code source (along with merchant signature)
/// </summary> /// </summary>
/// <returns>String conversion</returns> /// <returns>String conversion</returns>
public string AsQRCodeString() => $"{AsDataForSignature()}|{MerchantSignature}"; public string AsQRCodeString() => $"{AsDataForSignature()}|{MerchantSignature}";
public string AsDataForSignature() => $"{MerchantId}|{MerchantTransactionId}|{Name}|{Amount.ToString("0.00", CultureInfo.InvariantCulture)}|{TimeStampUTC:yyyy-MM-dd:HH.mm.ss.ffff}";
public string AsDataForSignature() => $"{MerchantId}|{MerchantTransactionId}|{Name}|{Amount.ToString("0.00", CultureInfo.InvariantCulture)}|{TimeStampUTC:O}";
/// <summary> /// <summary>
/// Convert from string /// Convert from string
@ -40,12 +34,12 @@ namespace QRBee.Core.Data
var res = new MerchantToClientRequest var res = new MerchantToClientRequest
{ {
MerchantId = s[0], MerchantId = s[0],
MerchantTransactionId = s[1], MerchantTransactionId = s[1],
Name = s[2], Name = s[2],
Amount = Convert.ToDecimal(s[3], CultureInfo.InvariantCulture), Amount = Convert.ToDecimal(s[3], CultureInfo.InvariantCulture),
TimeStampUTC = DateTime.ParseExact(s[4],"O",null), TimeStampUTC = DateTime.ParseExact(s[4], "yyyy-MM-dd:HH.mm.ss.ffff", null),
MerchantSignature = s[5] MerchantSignature = s[5]
}; };

View File

@ -18,6 +18,6 @@
/// Convert PaymentResponse to string to be encrypted and transmitted back to merchant /// Convert PaymentResponse to string to be encrypted and transmitted back to merchant
/// </summary> /// </summary>
/// <returns>Converted string</returns> /// <returns>Converted string</returns>
public string AsDataForSignature() => $"{ServerTransactionId}|{PaymentRequest.AsString()}|{ServerTimeStampUTC:O}|{Success}|{RejectReason}"; public string AsDataForSignature() => $"{ServerTransactionId}|{PaymentRequest.AsString()}|{ServerTimeStampUTC:yyyy-MM-dd:HH.mm.ss.ffff}|{Success}|{RejectReason}";
} }
} }

View File

@ -61,10 +61,10 @@ namespace QRBee.ViewModels
if (result == null) if (result == null)
return; return;
_merchantToClientRequest = MerchantToClientRequest.FromString(result); _merchantToClientRequest = MerchantToClientRequest.FromString(result);
Amount = $"{_merchantToClientRequest.Amount:N2}"; Amount = $"{_merchantToClientRequest.Amount:N2}";
IsAcceptDenyButtonVisible = true; IsAcceptDenyButtonVisible = true;
IsScanButtonVisible = false; IsScanButtonVisible = false;
} }
catch (Exception) catch (Exception)
{ {
@ -148,36 +148,40 @@ namespace QRBee.ViewModels
public async void OnAcceptQrCommand(object obj) public async void OnAcceptQrCommand(object obj)
{ {
var answer = await Application.Current.MainPage.DisplayAlert("Confirmation", "Would you like to accept the offer?", "Yes", "No"); var answer = await Application.Current.MainPage.DisplayAlert("Confirmation", "Would you like to accept the offer?", "Yes", "No");
if (!answer) return; if (!answer)
return;
var settings = _localSettings.LoadSettings(); var settings = _localSettings.LoadSettings();
var response = new ClientToMerchantResponse var response = new ClientToMerchantResponse
{ {
ClientId = settings.ClientId, ClientId = settings.ClientId,
TimeStampUTC = DateTime.UtcNow, TimeStampUTC = DateTime.UtcNow,
MerchantRequest = _merchantToClientRequest, MerchantRequest = _merchantToClientRequest,
EncryptedClientCardData = EncryptCardData(settings, _merchantToClientRequest.MerchantTransactionId) EncryptedClientCardData = EncryptCardData(settings, _merchantToClientRequest.MerchantTransactionId)
}; };
var clientSignature = _securityService.Sign(Encoding.UTF8.GetBytes(response.AsDataForSignature()));
response.ClientSignature = Convert.ToBase64String(clientSignature);
QrCode = response.AsQRCodeString(); var clientSignature = _securityService.Sign(Encoding.UTF8.GetBytes(response.AsDataForSignature()));
IsQrVisible = true; response.ClientSignature = Convert.ToBase64String(clientSignature);
QrCode = response.AsQRCodeString();
IsQrVisible = true;
IsAcceptDenyButtonVisible = false; IsAcceptDenyButtonVisible = false;
IsScanButtonVisible = true; IsScanButtonVisible = true;
} }
private string EncryptCardData(Settings settings, string transactionId) private string EncryptCardData(Settings settings, string transactionId)
{ {
var clientCardData = new ClientCardData var clientCardData = new ClientCardData
{ {
TransactionId = transactionId, TransactionId = transactionId,
CardNumber = settings.CardNumber, CardNumber = settings.CardNumber,
ExpirationDateMMYY = settings.ExpirationDate, ExpirationDateYYYYMM = string.IsNullOrWhiteSpace(settings.ExpirationDate) ? null : DateTime.Parse(settings.ExpirationDate).ToString("yyyy-MM"),
ValidFrom = settings.ValidFrom, ValidFromYYYYMM = string.IsNullOrWhiteSpace(settings.ValidFrom) ? null : DateTime.Parse(settings.ValidFrom).ToString("yyyy-MM"),
CardHolderName = settings.CardHolderName, CardHolderName = settings.CardHolderName,
CVC = settings.CVC, CVC = settings.CVC,
IssueNo = settings.IssueNo IssueNo = settings.IssueNo
}; };
var bytes = _securityService.Encrypt(Encoding.UTF8.GetBytes(clientCardData.AsString()),_securityService.APIServerCertificate); var bytes = _securityService.Encrypt(Encoding.UTF8.GetBytes(clientCardData.AsString()),_securityService.APIServerCertificate);

View File

@ -24,38 +24,51 @@ namespace QRBee.ViewModels
public MerchantPageViewModel(IQRScanner scanner, ILocalSettings settings, ISecurityService securityService) public MerchantPageViewModel(IQRScanner scanner, ILocalSettings settings, ISecurityService securityService)
{ {
_scanner = scanner; _scanner = scanner;
_settings = settings; _settings = settings;
_securityService = securityService; _securityService = securityService;
ScanCommand = new Command(OnScanButtonClicked); ScanCommand = new Command(OnScanButtonClicked);
GenerateQrCommand = new Command(OnGenerateQrClicked); GenerateQrCommand = new Command(OnGenerateQrClicked);
var localSettings = DependencyService.Resolve<ILocalSettings>(); var localSettings = DependencyService.Resolve<ILocalSettings>();
Name = localSettings.LoadSettings().Name; Name = localSettings.LoadSettings().Name;
} }
private async void OnScanButtonClicked(object sender) private async void OnScanButtonClicked(object sender)
{ {
try try
{ {
var result = await _scanner.ScanQR(); var result = await _scanner.ScanQR();
if (result == null) if (string.IsNullOrWhiteSpace(result))
return; return;
var client = new HttpClient(GetInsecureHandler()); var clientResponse = ClientToMerchantResponse.FromString(result, _lastRequest);
var service = new Core.Client.Client(_settings.QRBeeApiUrl, client); if (string.IsNullOrWhiteSpace(clientResponse.ClientSignature))
var clientResponse = ClientToMerchantResponse.FromString(result); throw new ApplicationException("Request is not signed by a client");
if (string.IsNullOrWhiteSpace(clientResponse.EncryptedClientCardData))
throw new ApplicationException("Request does not contain client's card data");
clientResponse.MerchantRequest = _lastRequest; var paymentRequest = new PaymentRequest
var paymentRequest = new PaymentRequest
{ {
ClientResponse = clientResponse ClientResponse = clientResponse
}; };
//QrCode = null; //QrCode = null;
IsVisible = false; IsVisible = false;
var response = await service.PayAsync(paymentRequest); // ------------------------------------- SEND PAYMENT REQUEST ------------------------------------------
//
// ____ _ __ ____ __ _____ _ _ _____
// | _ \ / \\ \ / / \/ | ____| \ | |_ _|
// | |_) / _ \\ V /| |\/| | _| | \| | | |
// | __/ ___ \| | | | | | |___| |\ | | |
// |_| /_/ \_\_| |_| |_|_____|_| \_| |_|
//
//
var apiService = new Core.Client.Client(_settings.QRBeeApiUrl, new HttpClient(GetInsecureHandler()));
var response = await apiService.PayAsync(paymentRequest);
//
// -----------------------------------------------------------------------------------------------------
if (response.Success) if (response.Success)
{ {

View File

@ -31,6 +31,12 @@
/// <param name="info">Information to be inserted</param> /// <param name="info">Information to be inserted</param>
Task PutTransactionInfo(TransactionInfo info); Task PutTransactionInfo(TransactionInfo info);
/// <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>
Task<TransactionInfo?> TryGetTransactionInfoByTransactionId(string id);
/// <summary> /// <summary>
/// Retrieve transaction information from database /// Retrieve transaction information from database
/// </summary> /// </summary>

View File

@ -71,7 +71,7 @@ namespace QRBee.Api.Services.Database
{ {
var collection = _database.GetCollection<TransactionInfo>("Transactions"); var collection = _database.GetCollection<TransactionInfo>("Transactions");
var transaction = await TryGetTransactionInfo(info.Id); var transaction = await TryGetTransactionInfoByTransactionId(info.Id);
if (transaction == null) if (transaction == null)
{ {
@ -83,12 +83,7 @@ namespace QRBee.Api.Services.Database
_logger.LogInformation($"Found transaction with ClientId: {info.Id}"); _logger.LogInformation($"Found transaction with ClientId: {info.Id}");
} }
/// <summary> public async Task<TransactionInfo?> TryGetTransactionInfoByTransactionId(string id)
/// 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"); var collection = _database.GetCollection<TransactionInfo>("Transactions");
using var cursor = await collection.FindAsync($"{{ _id: \"{id}\" }}"); using var cursor = await collection.FindAsync($"{{ _id: \"{id}\" }}");
@ -102,7 +97,7 @@ namespace QRBee.Api.Services.Database
public async Task<TransactionInfo> GetTransactionInfoByTransactionId(string id) public async Task<TransactionInfo> GetTransactionInfoByTransactionId(string id)
{ {
var transaction = await TryGetTransactionInfo(id); var transaction = await TryGetTransactionInfoByTransactionId(id);
return transaction ?? throw new ApplicationException($"Transaction with Id: {id} not found."); return transaction ?? throw new ApplicationException($"Transaction with Id: {id} not found.");
} }

View File

@ -14,21 +14,23 @@ namespace QRBee.Api.Services
/// </summary> /// </summary>
public class QRBeeAPIService: IQRBeeAPI public class QRBeeAPIService: IQRBeeAPI
{ {
private readonly IStorage _storage; private readonly IStorage _storage;
private readonly ISecurityService _securityService; private readonly ISecurityService _securityService;
private readonly IPrivateKeyHandler _privateKeyHandler; private readonly IPrivateKeyHandler _privateKeyHandler;
private readonly IPaymentGateway _paymentGateway; private readonly IPaymentGateway _paymentGateway;
private static readonly object _lock = new (); private readonly ILogger<QRBeeAPIService> _logger;
private static readonly object _lock = new ();
private const int MaxNameLength = 512; private const int MaxNameLength = 512;
private const int MaxEmailLength = 512; private const int MaxEmailLength = 512;
public QRBeeAPIService(IStorage storage, ISecurityService securityService, IPrivateKeyHandler privateKeyHandler, IPaymentGateway paymentGateway) public QRBeeAPIService(IStorage storage, ISecurityService securityService, IPrivateKeyHandler privateKeyHandler, IPaymentGateway paymentGateway, ILogger<QRBeeAPIService> logger)
{ {
_storage = storage; _storage = storage;
_securityService = securityService; _securityService = securityService;
_privateKeyHandler = privateKeyHandler; _privateKeyHandler = privateKeyHandler;
_paymentGateway = paymentGateway; _paymentGateway = paymentGateway;
_logger = logger;
Init(_privateKeyHandler); Init(_privateKeyHandler);
} }
@ -48,14 +50,14 @@ namespace QRBee.Api.Services
ValidateRegistration(request); ValidateRegistration(request);
var info = Convert(request); var info = Convert(request);
var clientId = await _storage.PutUserInfo(info); var clientId = await _storage.PutUserInfo(info);
using var rsa = LoadRsaPublicKey(request.CertificateRequest.RsaPublicKey); using var rsa = LoadRsaPublicKey(request.CertificateRequest.RsaPublicKey);
var bytes = rsa.ExportRSAPublicKey(); var bytes = rsa.ExportRSAPublicKey();
var clientCertificate = _securityService.CreateCertificate(clientId,bytes); var clientCertificate = _securityService.CreateCertificate(clientId,bytes);
var convertedClientCertificate = Convert(clientCertificate, clientId,request.Email); var convertedClientCertificate = Convert(clientCertificate, clientId,request.Email);
await _storage.InsertCertificate(convertedClientCertificate); await _storage.InsertCertificate(convertedClientCertificate);
@ -81,9 +83,9 @@ namespace QRBee.Api.Services
throw new NullReferenceException(); throw new NullReferenceException();
} }
var name = request.Name; var name = request.Name;
var email = request.Email; var email = request.Email;
var dateOfBirth = request.DateOfBirth; var dateOfBirth = request.DateOfBirth;
var certificateRequest = request.CertificateRequest; var certificateRequest = request.CertificateRequest;
if (string.IsNullOrEmpty(name) || name.All(char.IsLetter) == false || name.Length >= MaxNameLength) if (string.IsNullOrEmpty(name) || name.All(char.IsLetter) == false || name.Length >= MaxNameLength)
@ -126,10 +128,23 @@ namespace QRBee.Api.Services
public async Task<PaymentResponse> Pay(PaymentRequest value) public async Task<PaymentResponse> Pay(PaymentRequest value)
{ {
// --------------------------------- RECEIVE PAYMENT REQUEST --------------------------------------
//
// ____ _ __ ____ __ _____ _ _ _____
// | _ \ / \\ \ / / \/ | ____| \ | |_ _|
// | |_) / _ \\ V /| |\/| | _| | \| | | |
// | __/ ___ \| | | | | | |___| |\ | | |
// |_| /_/ \_\_| |_| |_|_____|_| \_| |_|
//
//
try try
{ {
//1. Check payment request parameters for validity //1. Check payment request parameters for validity
ValidateTransaction(value); ValidateTransaction(value);
var tid = value.ClientResponse.MerchantRequest.MerchantTransactionId;
_logger.LogInformation($"Transaction=\"{tid}\" Pre-validated");
//2. Check client signature //2. Check client signature
var t2 = CheckSignature( var t2 = CheckSignature(
@ -144,22 +159,25 @@ namespace QRBee.Api.Services
value.ClientResponse.MerchantRequest.MerchantId); value.ClientResponse.MerchantRequest.MerchantId);
//4. Check if transaction was already processed //4. Check if transaction was already processed
var t4 = CheckTransaction(value.ClientResponse.MerchantRequest.MerchantTransactionId); var t4 = CheckTransaction(tid);
//Parallel task execution //Parallel task execution
await Task.WhenAll(t2, t3, t4); await Task.WhenAll(t2, t3, t4);
_logger.LogInformation($"Transaction=\"{tid}\" Fully validated");
//5. Decrypt client card data //5. Decrypt client card data
var clientCardData = DecryptClientData(value.ClientResponse.EncryptedClientCardData); var clientCardData = DecryptClientData(value.ClientResponse.EncryptedClientCardData);
//6. Check client card data for validity //6. Check client card data for validity
await CheckClientCardData(clientCardData); await CheckClientCardData(clientCardData, value.ClientResponse.MerchantRequest.MerchantTransactionId);
_logger.LogInformation($"Transaction=\"{tid}\" Client card data validated");
//7. Register preliminary transaction record with expiry of one minute //7. Register preliminary transaction record with expiry of one minute
var info = Convert(value); var info = Convert(value);
info.Status = TransactionInfo.TransactionStatus.Pending; info.Status = TransactionInfo.TransactionStatus.Pending;
await _storage.PutTransactionInfo(info); await _storage.PutTransactionInfo(info);
_logger.LogInformation($"Transaction=\"{tid}\" initialized");
//8. Send client card data to a payment gateway //8. Send client card data to a payment gateway
var res = await _paymentGateway.Payment(info, clientCardData); var res = await _paymentGateway.Payment(info, clientCardData);
@ -175,6 +193,7 @@ namespace QRBee.Api.Services
info.RejectReason = res.ErrorMessage; info.RejectReason = res.ErrorMessage;
} }
await _storage.UpdateTransaction(info); await _storage.UpdateTransaction(info);
_logger.LogInformation($"Transaction=\"{tid}\" complete Status=\"{info.Status}\"");
//10. Make response for merchant //10. Make response for merchant
var response = MakePaymentResponse(value, info.TransactionId ?? "", info.Status==TransactionInfo.TransactionStatus.Succeeded, info.RejectReason); var response = MakePaymentResponse(value, info.TransactionId ?? "", info.Status==TransactionInfo.TransactionStatus.Succeeded, info.RejectReason);
@ -194,14 +213,15 @@ namespace QRBee.Api.Services
var response = new PaymentResponse var response = new PaymentResponse
{ {
ServerTransactionId = transactionId, ServerTransactionId = transactionId,
PaymentRequest = value, PaymentRequest = value,
ServerTimeStampUTC = DateTime.UtcNow, ServerTimeStampUTC = DateTime.UtcNow,
Success = result, Success = result,
RejectReason = errorMessage, RejectReason = errorMessage,
}; };
var signature = _securityService.Sign(Encoding.UTF8.GetBytes(response.AsDataForSignature())); var signature = _securityService.Sign(Encoding.UTF8.GetBytes(response.AsDataForSignature()));
response.ServerSignature = System.Convert.ToBase64String(signature); response.ServerSignature = System.Convert.ToBase64String(signature);
return response; return response;
} }
@ -212,10 +232,10 @@ namespace QRBee.Api.Services
throw new NullReferenceException(); throw new NullReferenceException();
} }
var clientId = request.ClientResponse.ClientId; var clientId = request.ClientResponse.ClientId;
var merchantId = request.ClientResponse.MerchantRequest.MerchantId; var merchantId = request.ClientResponse.MerchantRequest.MerchantId;
var transactionId = request.ClientResponse.MerchantRequest.MerchantTransactionId; var transactionId = request.ClientResponse.MerchantRequest.MerchantTransactionId;
var amount = request.ClientResponse.MerchantRequest.Amount; var amount = request.ClientResponse.MerchantRequest.Amount;
if (clientId == null || merchantId == null || transactionId == null) if (clientId == null || merchantId == null || transactionId == null)
{ {
@ -230,7 +250,7 @@ namespace QRBee.Api.Services
private async Task CheckSignature(string data,string signature, string id) private async Task CheckSignature(string data,string signature, string id)
{ {
var info = await _storage.GetCertificateInfoByUserId(id); var info = await _storage.GetCertificateInfoByUserId(id);
var certificate = _securityService.Deserialize(info.Certificate); var certificate = _securityService.Deserialize(info.Certificate);
var check = _securityService.Verify( var check = _securityService.Verify(
@ -246,8 +266,8 @@ namespace QRBee.Api.Services
private async Task CheckTransaction(string transactionId) private async Task CheckTransaction(string transactionId)
{ {
var info = await _storage.GetTransactionInfoByTransactionId(transactionId); var info = await _storage.TryGetTransactionInfoByTransactionId(transactionId);
switch (info.Status) switch (info?.Status)
{ {
case TransactionInfo.TransactionStatus.Succeeded: case TransactionInfo.TransactionStatus.Succeeded:
throw new ApplicationException($"Transaction with Id: {transactionId} was already made."); throw new ApplicationException($"Transaction with Id: {transactionId} was already made.");
@ -262,18 +282,24 @@ namespace QRBee.Api.Services
private ClientCardData DecryptClientData(string encryptedClientCardData) private ClientCardData DecryptClientData(string encryptedClientCardData)
{ {
var info = System.Convert.FromBase64String(encryptedClientCardData); var info = System.Convert.FromBase64String(encryptedClientCardData);
var bytes = _securityService.Decrypt(info); var bytes = _securityService.Decrypt(info);
var s = Encoding.UTF8.GetString(bytes); var s = Encoding.UTF8.GetString(bytes);
return ClientCardData.FromString(s); return ClientCardData.FromString(s);
} }
private async Task CheckClientCardData(ClientCardData data) private async Task CheckClientCardData(ClientCardData data, string merchantTransactionId)
{ {
var transactionId = data.TransactionId; if ( data.TransactionId != merchantTransactionId)
var expirationDate = DateTime.Parse(data.ExpirationDateMMYY); throw new ApplicationException($"Transaction IDs don't match");
var validFrom = DateTime.Parse(data.ValidFrom);
var holderName = data.CardHolderName; //_logger.LogInformation(data.AsString());
var transactionId = data.TransactionId;
var expirationDate = string.IsNullOrWhiteSpace(data.ExpirationDateYYYYMM) ? default : DateTime.ParseExact(data.ExpirationDateYYYYMM, "yyyy-MM", null);
var validFrom = string.IsNullOrWhiteSpace(data.ValidFromYYYYMM) ? default : DateTime.ParseExact(data.ValidFromYYYYMM, "yyyy-MM", null);
var holderName = data.CardHolderName;
await CheckTransaction(transactionId); await CheckTransaction(transactionId);

View File

@ -12,8 +12,8 @@
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Trace" "Default": "Trace",
//"Microsoft.AspNetCore": "Debug" "Microsoft.AspNetCore": "Information"
} }
}, },
"AllowedHosts": "*" "AllowedHosts": "*"