Implement receipt QR code

This commit is contained in:
Rene Vergara 2022-04-08 18:23:13 -05:00
parent 934e28446c
commit 8e98bff2ae
13 changed files with 172 additions and 14 deletions

View file

@ -540,7 +540,7 @@ app.get('/api/receipt', (req, res, next) => {
}); });
} else { } else {
res.status(204).json({ res.status(204).json({
message: 'no session received', message: 'no valid ID received',
order: null order: null
}); });
} }

View file

@ -37,6 +37,7 @@ import { BusinessComponent } from './business/business.component';
import { SearchOptionsPipe } from './searchoptions.pipe'; import { SearchOptionsPipe } from './searchoptions.pipe';
import { TermsComponent } from './terms/terms.component'; import { TermsComponent } from './terms/terms.component';
import { ReceiptComponent } from './receipt/receipt.component'; import { ReceiptComponent } from './receipt/receipt.component';
import { ReceiptQRComponent } from './receipt-qr/receipt-qr.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -57,7 +58,8 @@ import { ReceiptComponent } from './receipt/receipt.component';
BusinessComponent, BusinessComponent,
SearchOptionsPipe, SearchOptionsPipe,
TermsComponent, TermsComponent,
ReceiptComponent ReceiptComponent,
ReceiptQRComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View file

@ -7,6 +7,7 @@ import { UserService } from '../user.service';
import { OrderService } from './order.service'; import { OrderService } from './order.service';
import { CancelComponent } from '../cancel/cancel.component'; import { CancelComponent } from '../cancel/cancel.component';
import { CheckoutComponent} from '../checkout/checkout.component'; import { CheckoutComponent} from '../checkout/checkout.component';
import { ReceiptQRComponent} from '../receipt-qr/receipt-qr.component';
@Component({ @Component({
selector: 'app-order', selector: 'app-order',
@ -97,8 +98,20 @@ export class OrderComponent implements OnInit{
const dialogRef = this.dialog.open(CheckoutComponent, dialogConfig); const dialogRef = this.dialog.open(CheckoutComponent, dialogConfig);
dialogRef.afterClosed().subscribe((val) => { dialogRef.afterClosed().subscribe((val) => {
if (val) { if (val) {
const dialogConfig2 = new MatDialogConfig();
dialogConfig2.disableClose = true;
dialogConfig2.autoFocus = true;
dialogConfig2.data = {
order: this.order._id
};
console.log('Payment confirmed!'); console.log('Payment confirmed!');
this.orderService.closeOrder(); this.orderService.closeOrder();
const dialogRef2 = this.dialog.open(ReceiptQRComponent, dialogConfig2);
dialogRef2.afterClosed().subscribe( val => {
if (val) {
console.log('Receipt closed.');
}
});
} else { } else {
console.log('Returning to order'); console.log('Returning to order');
} }

View file

@ -0,0 +1,4 @@
.text {
font-family: 'Spartan', sans-serif;
}

View file

@ -0,0 +1,13 @@
<div align="center" mat-dialog-title>
<h4 class="text">Scan for your Receipt</h4>
</div>
<mat-dialog-content>
<div class="qrcode" id="receipt-qr"> </div>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-raised-button class="text" (click)="close()">
<mat-icon class="icon">close</mat-icon>Close
</button>
</mat-dialog-actions>

View file

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReceiptQRComponent } from './receipt-qr.component';
describe('ReceiptQRComponent', () => {
let component: ReceiptQRComponent;
let fixture: ComponentFixture<ReceiptQRComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ReceiptQRComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ReceiptQRComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,38 @@
import { Inject, Component, OnInit, ViewEncapsulation} from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
var QRCode = require('easyqrcodejs');
@Component({
selector: 'app-receipt-qr',
templateUrl: './receipt-qr.component.html',
styleUrls: ['./receipt-qr.component.css']
})
export class ReceiptQRComponent implements OnInit {
receiptUrl: SafeUrl;
codeString: string = '';
constructor(
private dialogRef: MatDialogRef<ReceiptQRComponent>,
private sanitizer: DomSanitizer,
@Inject(MAT_DIALOG_DATA) public data: { order: string}
) {
this.codeString = `https://zgo.cash/receipt/${data.order}`;
this.receiptUrl = this.sanitizer.bypassSecurityTrustUrl(this.codeString);
}
ngOnInit(): void {
var qrcode = new QRCode(document.getElementById("receipt-qr"), {
text: this.codeString,
logo: "/assets/zgo-prp.png",
logoWidth: 80,
logoHeight: 80
});
}
close() {
this.dialogRef.close(true);
}
}

View file

@ -2,6 +2,8 @@ import { Injectable } from '@angular/core';
import { Subject, BehaviorSubject, Observable } from 'rxjs'; import { Subject, BehaviorSubject, Observable } from 'rxjs';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http'; import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { Order } from './order/order.model'; import { Order } from './order/order.model';
import { Owner } from './owner.model';
import { UserService } from './user.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -51,7 +53,9 @@ export class ReceiptService {
}; };
private _orderUpdated: BehaviorSubject<Order> = new BehaviorSubject(this.dataStore.order); private _orderUpdated: BehaviorSubject<Order> = new BehaviorSubject(this.dataStore.order);
public readonly orderUpdate: Observable<Order> = this._orderUpdated.asObservable(); public readonly orderUpdate: Observable<Order> = this._orderUpdated.asObservable();
public ownerUpdate: Observable<Owner>; public _nameUpdated: BehaviorSubject<string> = new BehaviorSubject(this.dataStore.owner.name);
public readonly nameUpdate: Observable<string>= this._nameUpdated.asObservable();
public readonly ownerUpdate;
private apiKey = 'Le2adeic8Thah4Aeng4daem6i'; private apiKey = 'Le2adeic8Thah4Aeng4daem6i';
private reqHeaders: HttpHeaders; private reqHeaders: HttpHeaders;
@ -61,19 +65,21 @@ export class ReceiptService {
) { ) {
this.reqHeaders = new HttpHeaders().set('Authorization', this.apiKey); this.reqHeaders = new HttpHeaders().set('Authorization', this.apiKey);
this.ownerUpdate = userService.ownerUpdate; this.ownerUpdate = userService.ownerUpdate;
this.ownerUpdate.subscribe((owner) => {
this.dataStore.owner = owner;
});
} }
getOrderById(id:string) { getOrderById(id:string) {
const params = new HttpParams().append('id', id); const params = new HttpParams().append('id', id);
let obs = this.http.get<{message: string, order: any}>(this.beUrl+'api/order', { headers:this.reqHeaders, params:params, observe: 'response'}); let obs = this.http.get<{message: string, order: any}>(this.beUrl+'api/receipt', { headers:this.reqHeaders, params:params, observe: 'response'});
obs.subscribe((OrderDataResponse) => { obs.subscribe((OrderDataResponse) => {
if (OrderDataResponse.status == 200) { if (OrderDataResponse.status == 200) {
this.dataStore.order = OrderDataResponse.body!.order; this.dataStore.order = OrderDataResponse.body!.order;
this._orderUpdated.next(Object.assign({}, this.dataStore).order); this._orderUpdated.next(Object.assign({}, this.dataStore).order);
this.userService.getOwner(this.dataStore.order.address);
this.ownerUpdate.subscribe((owner) => {
this.dataStore.owner = owner;
this._nameUpdated.next(Object.assign({}, this.dataStore).owner.name);
});
} else { } else {
console.log('No order found'); console.log('No order found');
} }
@ -81,4 +87,5 @@ export class ReceiptService {
return obs; return obs;
} }
} }

View file

@ -28,3 +28,9 @@
span.tt{ span.tt{
font-family: 'Roboto-Mono', monospace; font-family: 'Roboto-Mono', monospace;
} }
img.total{
margin-bottom:-2px;
}
.small{
font-size: 10px;
}

View file

@ -4,19 +4,40 @@
</span> </span>
<span class="spacer"></span> <span class="spacer"></span>
<span align="center"> <span align="center">
<p class="text">Name</p> <p class="text">{{name}}</p>
</span> </span>
</mat-toolbar> </mat-toolbar>
<div align="center"> <div align="center">
<mat-card> <mat-card>
<mat-card-title> <mat-card-title>
Receipt Total: <img class="total" src="/assets/spartan-zec.png" height="18px" />{{order.totalZec | number: '1.0-6'}}
</mat-card-title> </mat-card-title>
<mat-card-subtitle> <mat-card-subtitle>
Date {{order.timestamp | date}}
</mat-card-subtitle> </mat-card-subtitle>
<mat-card-content> <mat-card-content>
<p>receipt works! orderID:{{orderId}}</p> <p class="small">Order ID: {{orderId}}</p>
<p>Zcash Price: {{order.price | currency: order.currency.toUpperCase()}}</p>
<div align="center">
<table>
<tr>
<th align="left">
Item
</th>
<th align="center">
Qty.
</th>
<th align="right">
Price ({{order.currency.toUpperCase()}})
</th>
</tr>
<tr *ngFor="let item of order.lines">
<td align="left">{{item.name}}</td>
<td align="center">{{item.qty}}</td>
<td align="right">{{(item.qty * item.cost) | currency: order.currency.toUpperCase()}} </td>
<tr>
</table>
</div>
</mat-card-content> </mat-card-content>
</mat-card> </mat-card>

View file

@ -1,7 +1,8 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Order} from '../order/order.model'; import { Order} from '../order/order.model';
import { OrderService } from '../order/order.service'; import { ReceiptService } from '../receipt.service';
import { Observable } from 'rxjs';
@Component({ @Component({
selector: 'app-receipt', selector: 'app-receipt',
@ -10,14 +11,42 @@ import { OrderService } from '../order/order.service';
}) })
export class ReceiptComponent implements OnInit { export class ReceiptComponent implements OnInit {
orderId; orderId;
//order:Order; public orderUpdate: Observable<Order>;
public nameUpdate: Observable<string>;
name: string = '';
order:Order = {
address: '',
session: '',
timestamp: '',
closed: false,
currency: '',
price: 0,
total: 0,
totalZec: 0,
lines: [
{
qty: 1,
name: '',
cost:0
}
]
};
constructor( constructor(
private _ActiveRoute:ActivatedRoute, private _ActiveRoute:ActivatedRoute,
public orderService: OrderService public receiptService: ReceiptService
) { ) {
this.orderId = this._ActiveRoute.snapshot.paramMap.get("orderId"); this.orderId = this._ActiveRoute.snapshot.paramMap.get("orderId");
this.orderUpdate = receiptService.orderUpdate;
this.nameUpdate = receiptService.nameUpdate;
receiptService.getOrderById(this.orderId!);
this.orderUpdate.subscribe(order => {
this.order = order;
});
this.nameUpdate.subscribe(name => {
this.name = name;
});
} }
ngOnInit(): void { ngOnInit(): void {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/zgo-prp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB