diff --git a/backend/app.js b/backend/app.js
index dd4b096..e51a2cd 100644
--- a/backend/app.js
+++ b/backend/app.js
@@ -2,6 +2,7 @@ const express = require('express');
const app = express();
const bodyparser = require('body-parser');
const cors = require('cors');
+const crypto = require('crypto');
const postmodel = require('./models/post');
const usermodel = require('./models/user');
const ownermodel = require('./models/owner');
@@ -12,6 +13,8 @@ const txmodel = require('./models/tx');
const mongoose = require('mongoose');
const stdrpc = require('stdrpc');
const CoinGecko = require('coingecko-api');
+var URLSafeBase64 = require('urlsafe-base64');
+var Buffer = require('buffer/').Buffer;
var db = require('./config/db');
mongoose.connect('mongodb://'+db.user+':'+db.password+'@'+db.server+'/'+db.database).then(() => {
@@ -74,16 +77,24 @@ function hexToString(hexString) {
return str;
}
+function sendPin(pin, address) {
+ //var memo = URLSafeBase64.encode(Buffer.from('ZGO pin: '.concat(pin)));
+ var memo = Buffer.from('ZGO pin: '.concat(pin)).toString('hex');
+ //console.log(typeof(memo));
+ var amounts = [
+ {
+ address: address,
+ amount: 0.00000001,
+ memo: memo
+ }
+ ];
+ rpc.z_sendmany(fullnode.addr, amounts).catch((err) => {
+ console.log('Sendmany', err);
+ });
+}
+
var blockInterval = setInterval( function() {
console.log('Node periodic Zcash scan');
- //usermodel.find({}, function (err, docs) {
- //if (err) {
- //console.log(err);
- //} else {
- //console.log(session, blocktime);
- //console.log(docs);
- //}
- //});
rpc.z_listreceivedbyaddress(fullnode.addr, 1).then(txs => {
var re = /.*ZGO::(.*)\sReply-To:\s(z\w+)/;
async.each (txs, function(txData, callback) {
@@ -95,17 +106,31 @@ var blockInterval = setInterval( function() {
var address = match[2];
var session = match[1];
var blocktime = txData.blocktime;
+ var amount = txData.amount;
+ var expiration = blocktime;
//console.log(' ', session, blocktime);
if (txData.confirmations >= 10 ) {
usermodel.findOne({address: address, session: session, blocktime: blocktime}).then(function(doc){
if (doc != null) {
console.log('Found user');
} else {
- console.log('User not found', session, blocktime);
+ console.log('User not found', session, blocktime, amount);
+ if (amount >= 0.001 && amount < 0.005){
+ expiration = blocktime + 3600;
+ } else if (amount >= 0.005){
+ expiration = blocktime + 24*3600;
+ }
+ console.log('exp', expiration);
+ const n = crypto.randomInt(0, 10000000);
+ const pin = n.toString().padStart(6, '0');
+ sendPin(pin, address);
var user = new usermodel({
address: address,
session: session,
- blocktime: blocktime
+ blocktime: blocktime,
+ expiration: expiration,
+ pin: pin,
+ validated: false
});
user.save(function(error) {
if (error) {
@@ -181,6 +206,10 @@ app.use((req, res, next) => {
});
+app.get('/api/test', (req, res, next) => {
+ sendPin('12345678', 'zs1w6nkameazc5gujm69350syl5w8tgvyaphums3pw8eytzy5ym08x7dvskmykkatmwrucmgv3er8e');
+ res.status(200).send('Endpoint triggered');
+});
app.get('/api/users', (req, res, next) => {
console.log('Get: /api/users');
@@ -223,8 +252,7 @@ app.get('/api/pending', (req, res, next) => {
app.get('/api/getuser', (req, res, next) => {
console.log('Get: /api/getuser/', req.query.session);
var today = new Date().getTime() / 1000;
- var expiration = today - (24*3600);
- usermodel.find({'session': req.query.session, 'blocktime': { $gt: expiration }}).
+ usermodel.find({'session': req.query.session, 'expiration': { $gt: today }}).
then((documents) => {
if(documents.length > 0){
//console.log(documents);
@@ -253,8 +281,6 @@ app.get('/api/blockheight', (req, res, next) => {
});
});
-
-
app.get('/api/txs', (req, res, next) => {
console.log('Get: /api/txs');
rpc.z_listreceivedbyaddress(fullnode.addr, 10).then(txs => {
@@ -301,6 +327,21 @@ app.post('/api/addowner', (req, res, next) => {
});
});
+app.post('/api/validateuser', (req, res, next) => {
+ console.log('Post: /api/validateuser');
+ usermodel.findByIdAndUpdate(req.body.user._id, req.body.user,
+ function(err, docs) {
+ if (err) {
+ console.log(err);
+ } else {
+ res.status(201).json({
+ message: 'User Validated',
+ user: docs
+ });
+ }
+ });
+});
+
app.post('/api/updateowner', (req, res, next) => {
console.log('Post: /api/updateowner');
ownermodel.findByIdAndUpdate(req.body.owner._id, req.body.owner,
diff --git a/backend/models/user.js b/backend/models/user.js
index a40171e..83ae866 100644
--- a/backend/models/user.js
+++ b/backend/models/user.js
@@ -3,7 +3,10 @@ const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
address: {type: String, required:true},
session: {type: String, required:true},
- blocktime: {type: Number, required:true}
+ blocktime: {type: Number, required:true},
+ expiration: {type: Number, required:true, default:0},
+ pin: {type: String, required:true},
+ validated: {type: Boolean, required:true}
});
module.exports = mongoose.model('User', userSchema);
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index cff4598..464931c 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -11,7 +11,7 @@ const routes: Routes = [
//{ path: 'create', component: PostCreateComponent, canActivate: [AuthGuardService]},
{ path: 'shop', component: ViewerComponent, canActivate: [AuthGuardService]},
{ path: 'orders', component: ListOrdersComponent, canActivate: [AuthGuardService]},
- { path: 'login', component: LoginComponent}
+ { path: 'login', component: LoginComponent, resolve: { response: NodeResolverService}}
];
@NgModule({
diff --git a/src/app/app.component.css b/src/app/app.component.css
index 150f53d..435af29 100644
--- a/src/app/app.component.css
+++ b/src/app/app.component.css
@@ -1,5 +1,3 @@
main{
- margin-top: 16px;
- width: 80%;
margin: auto;
}
diff --git a/src/app/checkout/checkout.component.html b/src/app/checkout/checkout.component.html
index 7034aea..3458441 100644
--- a/src/app/checkout/checkout.component.html
+++ b/src/app/checkout/checkout.component.html
@@ -1,5 +1,5 @@
-
Scan to make payment
+Scan to make payment
diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html
index 6ea406e..5f2346e 100644
--- a/src/app/header/header.component.html
+++ b/src/app/header/header.component.html
@@ -1,14 +1,10 @@
-
- Z Go!
-
- Last block seen: {{heightUpdate | async}}
+
-
+ Last block:
+ {{heightUpdate | async}}
diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts
index 7d828cf..4e88167 100644
--- a/src/app/header/header.component.ts
+++ b/src/app/header/header.component.ts
@@ -3,7 +3,6 @@ import { MatDialog, MatDialogConfig} from '@angular/material/dialog';
import {FullnodeService} from '../fullnode.service';
import { UserService } from '../user.service';
import {Subscription, Observable} from 'rxjs';
-import { SettingsComponent } from '../settings/settings.component';
import {Owner} from '../owner.model';
@@ -42,28 +41,6 @@ export class HeaderComponent implements OnInit, OnDestroy {
ngOnDestroy(){
}
- shortenZaddr(address:string) {
- var addr = address;
- var end = addr.length;
- var last = end - 5;
- return addr.substring(0,5).concat('...').concat(addr.substring(last, end));
- }
- openSettings() {
-
- const dialogConfig = new MatDialogConfig();
-
- dialogConfig.disableClose = true;
- dialogConfig.autoFocus = true;
- dialogConfig.data = this.owner;
-
- const dialogRef = this.dialog.open(SettingsComponent, dialogConfig);
- dialogRef.afterClosed().subscribe((val) => {
- if (val != null) {
- console.log('Saving settings');
- this.userService.updateOwner(val);
- }
- });
- }
}
diff --git a/src/app/listorders/listorders.component.css b/src/app/listorders/listorders.component.css
index 121ab70..a9d0799 100644
--- a/src/app/listorders/listorders.component.css
+++ b/src/app/listorders/listorders.component.css
@@ -20,3 +20,6 @@ img.icon{
.total{
font-size: large;
}
+img.total{
+ margin-bottom:-2px;
+}
diff --git a/src/app/listorders/listorders.component.html b/src/app/listorders/listorders.component.html
index f6fa94a..e14b581 100644
--- a/src/app/listorders/listorders.component.html
+++ b/src/app/listorders/listorders.component.html
@@ -1,6 +1,6 @@
-
{{(ownerUpdate | async)!.name}}
+
{{(ownerUpdate | async)!.name}}
@@ -11,11 +11,11 @@
Today's Total:
- {{todayTotal}}
+ {{todayTotal | number: '1.0-6'}}
|
Overall Total:
- {{total}}
+ {{total | number: '1.0-6'}}
|
@@ -25,13 +25,14 @@
- {{order.totalZec}} (@{{order.price | currency: 'USD'}})
+ {{order.totalZec}}
- {{order.timestamp | date: 'medium'}}
+ {{order.timestamp | date: 'short'}}
Order: {{order._id}}
+ Zcash price: {{order.price | currency: 'USD'}}
{{item.qty}} x {{item.name}}
diff --git a/src/app/login/login.component.css b/src/app/login/login.component.css
index 0177792..1e0275a 100644
--- a/src/app/login/login.component.css
+++ b/src/app/login/login.component.css
@@ -4,6 +4,7 @@
mat-card.coolcard{
background-color: #FF5722;
color: #FFFFFF;
+ margin: 5px;
}
.icon{
font-family: 'Material Icons';
@@ -14,3 +15,7 @@ mat-card.coolcard{
font-size: 120%;
padding: 3px;
}
+.alert-success{
+ margin: 5px;
+ background-color: #BBFFBB
+}
diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html
index d571d2d..222274e 100644
--- a/src/app/login/login.component.html
+++ b/src/app/login/login.component.html
@@ -1,61 +1,62 @@
-
-
- __||__ _____ _
-|___ / / ____| | |
- / / | | __ ___ | |
- / / | | |_ |/ _ \| |
- / /__ | |__| | (_) |_|
-/__ _| \_____|\___/(_)
- ||
-
-
+
+
+
+
+
+
+
+
+
+
+
+
Last block seen: {{ heightUpdate | async }}
-
-
-
-
-
-
-
-
- A non-custodial point-of-sale application, powered by Zcash!
-
- - Your Zcash shielded address is your login.
- - Your customer pays directly to your wallet.
-
-
- |
-
- 0">
- Login received!
-
- It has {{tx.confirmations}} confirmations, needs 10.
-
-
-
-
-
- Session length
-
-
- {{ticket.viewValue}}
-
-
-
-
-
-
-
-
- |
-
-
+
+ A non-custodial point-of-sale application, powered by Zcash!
+ Your Zcash shielded address is your login.
+ Your customer pays directly to your wallet.
+
+
+
+ Check your wallet
+
+
+ PIN
+
+
+
+
+
+
+
0">
+ Login received!
+
+ It needs {{10 - tx.confirmations}} more confirmations.
+
+
+
+
+
+ Session length
+
+
+ {{ticket.viewValue}}
+
+
+
+
+
+
+
+
diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts
index 274d2e9..b54edf0 100644
--- a/src/app/login/login.component.ts
+++ b/src/app/login/login.component.ts
@@ -6,6 +6,7 @@ import { UserService } from '../user.service';
import { FullnodeService } from '../fullnode.service';
import { ScanComponent} from '../scan/scan.component';
import { Tx } from '../tx.model';
+import {User} from '../user.model';
import { Subscription, Observable } from 'rxjs';
import { v4 as uuidv4 } from 'uuid';
var QRCode = require('easyqrcodejs');
@@ -25,10 +26,19 @@ export class LoginComponent implements OnInit {
nodeAddress: string = '';
localToken: string | null = '';
selectedValue: number = 0.001;
+ public user:User = {
+ address: '',
+ session: '',
+ blocktime: 0,
+ expiration: 0,
+ pin: '',
+ validated: false
+ };
private FullnodeSub: Subscription = new Subscription();
private UserSub: Subscription = new Subscription();
public heightUpdate: Observable;
public uZaddrUpdate: Observable;
+ public userUpdate:Observable;
public txsUpdate: Observable;
tickets = [
{
@@ -39,8 +49,10 @@ export class LoginComponent implements OnInit {
viewValue: 'One day'
}
];
+ prompt: boolean = false;
entryForm: FormGroup;
+ pinForm: FormGroup;
constructor(
private fb: FormBuilder,
@@ -55,8 +67,15 @@ export class LoginComponent implements OnInit {
this.entryForm = fb.group({
selectedSession: [0.001, Validators.required]
});
+ this.pinForm = fb.group({
+ pinValue: [null, Validators.required]
+ });
this.heightUpdate = fullnodeService.heightUpdate;
this.uZaddrUpdate = userService.uZaddrUpdate;
+ this.userUpdate = userService.userUpdate;
+ this.userUpdate.subscribe((user) => {
+ this.user = user;
+ });
this.txsUpdate = userService.txUpdate;
this.txsUpdate.subscribe((txs) => {
this.txs = txs;
@@ -76,26 +95,7 @@ export class LoginComponent implements OnInit {
localStorage.setItem('s4z_token', token);
this.localToken = token;
}
- this.userService.findUser();
- this.userService.uZaddrUpdate.
- subscribe((userAddr: string) => {
- if (userAddr.length != 0) {
- console.log('Log in found!');
- this.router.navigate(['/shop']);
- } else {
- console.log('No login for existing token found');
- //console.log('Showing QR code for login');
- ////console.log(URLSafeBase64.encode(Buffer.from('S4ZEC::'.concat(localToken))));
- //var codeString = `zcash:${this.nodeAddress}?amount=0.005&memo=${URLSafeBase64.encode(Buffer.from('ZGO::'.concat(this.localToken!)))}`;
- //console.log(codeString);
- //var qrcode = new QRCode(document.getElementById("qrcode"), {
- //text: codeString,
- //logo: "/assets/zcash.png",
- //logoWidth: 80,
- //logoHeight: 80
- //});
- }
- });
+ this.loginCheck();
});
this.intervalHolder = setInterval(() => {
this.fullnodeService.getHeight();
@@ -106,21 +106,25 @@ export class LoginComponent implements OnInit {
}
loginCheck(){
+ var today = new Date().getTime() / 1000;
this.userService.findUser();
this.userService.findPending();
this.txsUpdate.subscribe((txs) => {
this.txs = txs;
});
- this.uZaddrUpdate.subscribe((userAddr: string) => {
- if (userAddr.length != 0) {
+ this.userUpdate.subscribe((user) => {
+ if (user.expiration > today) {
+ this.prompt = true;
console.log('Log in found in blockchain!');
- this.router.navigate(['/shop']);
+ if (user.validated) {
+ this.router.navigate(['/shop']);
+ }
}
});
}
login() {
- console.log('Dropdown:', this.entryForm.value.selectedSession);
+ //console.log('Dropdown:', this.entryForm.value.selectedSession);
const dialogConfig = new MatDialogConfig();
dialogConfig.disableClose = true;
@@ -137,6 +141,13 @@ export class LoginComponent implements OnInit {
});
}
+ confirmPin(){
+ if (this.user.pin === this.pinForm.value.pinValue) {
+ this.userService.validateUser();
+ this.router.navigate(['/shop']);
+ }
+ }
+
ngOnDestroy(){
this.FullnodeSub.unsubscribe();
this.UserSub.unsubscribe();
diff --git a/src/app/order/order.service.ts b/src/app/order/order.service.ts
index 69334b6..8d69190 100644
--- a/src/app/order/order.service.ts
+++ b/src/app/order/order.service.ts
@@ -16,7 +16,10 @@ export class OrderService {
user:{
address: '',
session: '',
- blocktime: 0
+ blocktime: 0,
+ expiration: 0,
+ pin: '',
+ validated: false
},
order: {
address: '',
diff --git a/src/app/tx.model.ts b/src/app/tx.model.ts
index 9e8d9b8..69b43f5 100644
--- a/src/app/tx.model.ts
+++ b/src/app/tx.model.ts
@@ -3,4 +3,5 @@ export interface Tx {
address: string;
session: string;
confirmations: number;
+ amount: number;
}
diff --git a/src/app/user.model.ts b/src/app/user.model.ts
index ff6ebca..bf43ae3 100644
--- a/src/app/user.model.ts
+++ b/src/app/user.model.ts
@@ -3,4 +3,7 @@ export interface User {
address: string;
session: string;
blocktime: number;
+ expiration: number;
+ pin: string;
+ validated: boolean;
}
diff --git a/src/app/user.service.ts b/src/app/user.service.ts
index f8a403f..524b5bb 100644
--- a/src/app/user.service.ts
+++ b/src/app/user.service.ts
@@ -13,7 +13,10 @@ export class UserService{
user: {
address: '',
session: '',
- blocktime: 0
+ blocktime: 0,
+ expiration: 0,
+ pin: '',
+ validated: false
},
owner: {
address: '',
@@ -99,6 +102,15 @@ export class UserService{
}
}
+ validateUser(){
+ var validatedUser: User = this.dataStore.user;
+ validatedUser.validated = true;
+ this.http.post<{message: string, user: User}>(this.beUrl+'api/validateuser', {user: validatedUser}, {headers: this.reqHeaders}).
+ subscribe((responseData) => {
+ console.log(responseData.message);
+ });
+ }
+
addOwner(address: string) {
const owner: Owner={_id: '', address: address, name: 'Zgo-'.concat(address.substring(0,5))};
let obs = this.http.post<{message: string}>(this.beUrl+'api/addowner', {address: owner.address, name: owner.name}, {headers: this.reqHeaders});
diff --git a/src/app/viewer/viewer.component.css b/src/app/viewer/viewer.component.css
index f6d0e97..f4e7eaf 100644
--- a/src/app/viewer/viewer.component.css
+++ b/src/app/viewer/viewer.component.css
@@ -1,3 +1,9 @@
* {
font-family: 'Roboto Mono', monospace;
}
+.icon{
+ font-family: 'Material Icons';
+}
+.small{
+ font-size: x-small;
+}
diff --git a/src/app/viewer/viewer.component.html b/src/app/viewer/viewer.component.html
index e4cd943..0ef72f0 100644
--- a/src/app/viewer/viewer.component.html
+++ b/src/app/viewer/viewer.component.html
@@ -1,15 +1,13 @@
-
{{(ownerUpdate | async)!.name}}
+
{{(ownerUpdate | async)!.name}}
+
{{ shortenZaddr((ownerUpdate | async)!.address) }}
+
+
+
-
+
+
diff --git a/src/app/viewer/viewer.component.ts b/src/app/viewer/viewer.component.ts
index fbbb81c..a13abe9 100644
--- a/src/app/viewer/viewer.component.ts
+++ b/src/app/viewer/viewer.component.ts
@@ -5,8 +5,10 @@ import { UserService } from '../user.service';
import { FullnodeService } from '../fullnode.service';
import { ItemService } from '../items/items.service';
import { Subscription, Observable } from 'rxjs';
+import { SettingsComponent } from '../settings/settings.component';
import {Owner} from '../owner.model';
+import {User} from '../user.model';
@Component({
@@ -16,9 +18,20 @@ import {Owner} from '../owner.model';
})
export class ViewerComponent implements OnInit {
+ intervalHolder: any;
public message: string = "Welcome to the inside!";
+ public user: User = {
+ address: '',
+ session: '',
+ blocktime: 0,
+ expiration: 0,
+ pin: '',
+ validated: false
+ };
+ private owner: Owner= {_id:'', address: 'none', name:''};
public addrUpdate: Observable;
public ownerUpdate: Observable;
+ public userUpdate: Observable;
constructor(
public fullnodeService: FullnodeService,
@@ -28,15 +41,57 @@ export class ViewerComponent implements OnInit {
){
this.addrUpdate = fullnodeService.addrUpdate;
this.ownerUpdate = userService.ownerUpdate;
+ this.ownerUpdate.subscribe((owner) => {
+ this.owner = owner;
+ });
+ this.userUpdate = userService.userUpdate;
+ this.userUpdate.subscribe((user) => {
+ this.user = user;
+ });
}
ngOnInit(){
this.ownerUpdate.subscribe((owner) => {
this.message = owner.name;
});
+ this.loginCheck();
+ this.intervalHolder = setInterval(() => {
+ this.loginCheck();
+ }, 60000);
}
ngOnDestroy(){
}
+ shortenZaddr(address:string) {
+ var addr = address;
+ var end = addr.length;
+ var last = end - 5;
+ return addr.substring(0,5).concat('...').concat(addr.substring(last, end));
+ }
+ openSettings() {
+
+ const dialogConfig = new MatDialogConfig();
+
+ dialogConfig.disableClose = true;
+ dialogConfig.autoFocus = true;
+ dialogConfig.data = this.owner;
+
+ const dialogRef = this.dialog.open(SettingsComponent, dialogConfig);
+ dialogRef.afterClosed().subscribe((val) => {
+ if (val != null) {
+ console.log('Saving settings');
+ this.userService.updateOwner(val);
+ }
+ });
+ }
+
+ loginCheck(){
+ var today = new Date().getTime() / 1000;
+ console.log('User check', this.user.validated);
+ if (this.user.expiration < today || !this.user.validated) {
+ console.log('Log in expired!');
+ this.router.navigate(['/login']);
+ }
+ }
}
diff --git a/src/assets/logo.png b/src/assets/logo.png
new file mode 100644
index 0000000..5c1d436
Binary files /dev/null and b/src/assets/logo.png differ