Merchant to client to merchant round trip implemented (no security).

This commit is contained in:
Andrey Shabarshov 2021-12-26 12:28:53 +00:00
parent de95d78a23
commit 575d0ccf0b
7 changed files with 227 additions and 41 deletions

View File

@ -32,7 +32,25 @@
set;
}
public string AsString() => $"{Request.AsString()}|{ClientId}|{TimeStampUTC:O}";
public string AsString() => $"{ClientId}|{TimeStampUTC:O}|{Request.AsString()}";
public static ClientToMerchantResponse FromString(string input)
{
var s = input.Split('|');
if (s.Length != 3)
{
throw new ApplicationException("Expected 3 elements");
}
var res = new ClientToMerchantResponse()
{
Request = MerchantToClientRequest.FromString(string.Join("|", s.Skip(2))),
ClientId = s[0],
TimeStampUTC = DateTime.ParseExact(s[1], "O", null)
};
return res;
}
}
}

View File

@ -1,4 +1,6 @@
namespace QRBee.Core.Data
using System.Globalization;
namespace QRBee.Core.Data
{
public record MerchantToClientRequest
{
@ -32,7 +34,27 @@
set;
}
public string AsString() => $"{TransactionId}|{Name}|{Amount:0.00}|{TimeStampUTC:O}";
public string AsString() => $"{TransactionId}|{Name}|{Amount.ToString("0.00", CultureInfo.InvariantCulture)}|{TimeStampUTC:O}";
public static MerchantToClientRequest FromString(string input)
{
var s = input.Split('|');
if (s.Length != 4)
{
throw new ApplicationException("Expected 4 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)
};
return res;
}
}
}

View File

@ -1,12 +1,116 @@
using System;
using System.Collections.Generic;
using System.Text;
using QRBee.Core.Data;
using QRBee.Services;
using QRBee.Views;
using Xamarin.Forms;
namespace QRBee.ViewModels
{
internal class ClientPageViewModel
internal class ClientPageViewModel: BaseViewModel
{
public bool _isVisible;
public string _amount;
private string _qrCode;
private MerchantToClientRequest _merchantToClientRequest;
private readonly ClientPage _clientPage;
public ClientPageViewModel(Views.ClientPage clientPage)
{
ScanCommand = new Command(OnScanButtonClicked);
GenerateQrCommand = new Command(OnGenerateQrClicked);
_clientPage = clientPage;
}
public Command ScanCommand
{
get;
}
public Command GenerateQrCommand
{
get;
}
private async void OnScanButtonClicked(object sender)
{
try
{
var scanner = DependencyService.Get<IQRScanner>();
var result = await scanner.ScanQR();
if (result != null)
{
_merchantToClientRequest = MerchantToClientRequest.FromString(result);
Amount = $"{_merchantToClientRequest.Amount:N2}";
IsVisible = true;
}
}
catch (Exception ex)
{
throw;
}
}
public string Amount
{
get => _amount;
set
{
if (value == _amount)
{
return;
}
_amount = value;
OnPropertyChanged(nameof(Amount));
}
}
public bool IsVisible
{
get => _isVisible;
set
{
if (value == _isVisible)
{
return;
}
_isVisible = value;
OnPropertyChanged(nameof(IsVisible));
}
}
public string QrCode
{
get => _qrCode;
set
{
// _qrCode = $"{Amount}/{Name}";
if (_qrCode == value)
return;
_qrCode = value;
OnPropertyChanged(nameof(QrCode));
}
}
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();
}
}
}
}

View File

@ -1,21 +1,43 @@
using System;
using QRBee.Core.Data;
using QRBee.Services;
using Xamarin.Forms;
namespace QRBee.ViewModels
{
internal class MerchantPageViewModel : BaseViewModel
{
private bool _isVisible;
private string _name;
private decimal _amount;
private string _qrCode;
public Command GenerateQrCommand { get; }
public Command ScanCommand{ get; }
public MerchantPageViewModel()
{
ScanCommand = new Command(OnScanButtonClicked);
GenerateQrCommand = new Command(OnGenerateQrClicked);
}
private async void OnScanButtonClicked(object sender)
{
try
{
var scanner = DependencyService.Get<IQRScanner>();
var result = await scanner.ScanQR();
if (result != null)
{
}
}
catch (Exception ex)
{
throw;
}
}
public string Name
{
get => _name;
@ -42,6 +64,20 @@ namespace QRBee.ViewModels
}
}
public bool IsVisible
{
get => _isVisible;
set
{
if (value == _isVisible)
{
return;
}
_isVisible = value;
OnPropertyChanged(nameof(IsVisible));
}
}
public string QrCode
{
get => _qrCode;
@ -58,9 +94,16 @@ namespace QRBee.ViewModels
public async void OnGenerateQrClicked(object obj)
{
QrCode = $"{Name}.{Amount:0.00}.{DateTime.UtcNow:O}";
// Prefixing with `//` switches to a different navigation stack instead of pushing to the active one
// await Shell.Current.GoToAsync($"//{nameof(AboutPage)}");
var trans = new MerchantToClientRequest
{
TransactionId = Guid.NewGuid().ToString("D"),
Name = Name,
Amount = Amount,
TimeStampUTC = DateTime.UtcNow
};
// TODO Create merchant signature.
QrCode = trans.AsString();
IsVisible = true;
}
}

View File

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:QRBee.ViewModels"
xmlns:forms="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
xmlns:common="clr-namespace:ZXing.Common;assembly=zxing.portable"
x:DataType="viewmodels:ClientPageViewModel"
x:Class="QRBee.Views.ClientPage">
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
@ -9,13 +13,28 @@
<!-- HorizontalOptions="CenterAndExpand" /> -->
<StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand">
<StackLayout Orientation="Vertical">
<Label x:Name="Name" VerticalOptions="FillAndExpand" Text="Merchant name: "/>
<Label x:Name="Amount" VerticalOptions="FillAndExpand" Text="Amount: "/>
<Label VerticalOptions="FillAndExpand" Text="Amount:"/>
<Label VerticalOptions="FillAndExpand" Text="{Binding Amount}"/>
</StackLayout>
<forms:ZXingBarcodeImageView
BarcodeFormat="QR_CODE"
BarcodeValue="{Binding QrCode}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<forms:ZXingBarcodeImageView.BarcodeOptions>
<common:EncodingOptions Width="300" Height="300" />
</forms:ZXingBarcodeImageView.BarcodeOptions>
</forms:ZXingBarcodeImageView>
</StackLayout>
<StackLayout Orientation="Horizontal" VerticalOptions="End" Margin="0,0,0,10">
<Button Text="Scan me" HorizontalOptions="CenterAndExpand" BackgroundColor="Aqua" TextColor="Red" Clicked="OnScanButtonClicked"/>
<StackLayout Orientation="Vertical" VerticalOptions="End" Margin="0,0,0,10">
<StackLayout Orientation="Horizontal">
<Button Text="Accept" HorizontalOptions="FillAndExpand" BackgroundColor="DarkGreen" IsVisible="{Binding IsVisible}" Command="{Binding GenerateQrCommand}"/>
<Button Text="Deny" HorizontalOptions="FillAndExpand" BackgroundColor="DarkRed" IsVisible="{Binding IsVisible}"/>
</StackLayout>
<Button Text="Scan me" HorizontalOptions="FillAndExpand" BackgroundColor="Aqua" TextColor="Red" Command="{Binding ScanCommand}"/>
</StackLayout>
</StackLayout>

View File

@ -1,5 +1,6 @@
using System;
using QRBee.Services;
using QRBee.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@ -11,25 +12,7 @@ namespace QRBee.Views
public ClientPage()
{
InitializeComponent();
}
private async void OnScanButtonClicked(object sender, EventArgs args)
{
try
{
var scanner = DependencyService.Get<IQRScanner>();
var result = await scanner.ScanQR();
if (result != null)
{
Name.Text = result;
Amount.Text = result;
}
}
catch (Exception ex)
{
throw;
}
this.BindingContext = new ClientPageViewModel(this);
}
}
}

View File

@ -23,9 +23,6 @@
<!-- -->
<!-- </StackLayout> -->
<!-- -->
<!-- <StackLayout Orientation="Horizontal"> -->
<!-- -->
<!-- </StackLayout> -->
<forms:ZXingBarcodeImageView
BarcodeFormat="QR_CODE"
@ -40,9 +37,9 @@
</StackLayout>
<StackLayout Orientation="Horizontal" VerticalOptions="End" Margin="0,0,0,10">
<Button Text="Generate QR code" HorizontalOptions="CenterAndExpand" BackgroundColor="Red" TextColor="Aqua" Command="{Binding GenerateQrCommand}"/>
<StackLayout Orientation="Vertical" VerticalOptions="End" Margin="0,0,0,10">
<Button Text="Scan response" HorizontalOptions="FillAndExpand" BackgroundColor="Aqua" TextColor="Red" IsVisible="{Binding IsVisible}" Command="{Binding ScanCommand}"/>
<Button Text="Generate QR code" HorizontalOptions="FillAndExpand" BackgroundColor="Red" TextColor="Aqua" Command="{Binding GenerateQrCommand}"/>
</StackLayout>