mirror of
https://github.com/NecroticBamboo/QRBee.git
synced 2025-12-21 12:11:53 +00:00
Payment transaction fixed.
This commit is contained in:
parent
01cafaba14
commit
400a41760b
@ -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]))
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -12,8 +12,8 @@
|
|||||||
|
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Trace"
|
"Default": "Trace",
|
||||||
//"Microsoft.AspNetCore": "Debug"
|
"Microsoft.AspNetCore": "Information"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user