Add payment methods screen
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Socket } from 'ngx-socket-io';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
/*
|
||||
* The following interfaces are copied from the backend.
|
||||
@@ -26,17 +27,16 @@ export interface ICart {
|
||||
|
||||
export interface IPaymentMethod {
|
||||
method: any;
|
||||
amount: number
|
||||
amount: number;
|
||||
}
|
||||
|
||||
export enum PaymentStatus {
|
||||
CANCELLED = -2,
|
||||
REQUESTED = -1,
|
||||
PENDING = 0,
|
||||
PARTIALLY = 1,
|
||||
UNCONFIRMED = 2,
|
||||
DONE = 3,
|
||||
CANCELLED = 4
|
||||
UNCONFIRMED = 1,
|
||||
DONE = 2,
|
||||
}
|
||||
|
||||
export interface IInvoice {
|
||||
selector: string;
|
||||
paymentMethods: IPaymentMethod[];
|
||||
@@ -62,33 +62,132 @@ export class BackendService {
|
||||
|
||||
SERVER_URL = 'http://localhost:2009';
|
||||
|
||||
invoice: IInvoice | null = null;
|
||||
invoiceUpdate: BehaviorSubject<IInvoice | null>;
|
||||
|
||||
constructor(
|
||||
private socket: Socket,
|
||||
private http: HttpClient
|
||||
) {
|
||||
this.invoiceUpdate = new BehaviorSubject<IInvoice | null>(null);
|
||||
this.socket.on('status', (data: any) => {
|
||||
console.log('Status has been updated to: ', data);
|
||||
});
|
||||
this.socket.on('subscribe', (success: boolean) => {
|
||||
if (success) { console.log('We\'re getting the progress of this invoice!'); }
|
||||
else { console.log('Subscription failed'); }
|
||||
else { console.log('Subscribtion failed'); }
|
||||
});
|
||||
}
|
||||
|
||||
subscribeTo(selector: string) {
|
||||
getSocket(): Socket {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
subscribeTo(selector: string): void {
|
||||
this.socket.on('subscribe', (status: boolean) => {
|
||||
if (status) {
|
||||
this.updateInvoice();
|
||||
console.log('Successfully subscribed to this invoice');
|
||||
}
|
||||
else { console.log('Failed to subscribe'); }
|
||||
});
|
||||
|
||||
this.socket.emit('subscribe', { selector });
|
||||
}
|
||||
|
||||
getInvoice(selector: string): Promise<IInvoice> {
|
||||
updateInvoice(): void {
|
||||
if (this.invoice !== undefined || this.invoice !== null) {
|
||||
this.setInvoice(this.invoice?.selector!);
|
||||
}
|
||||
}
|
||||
|
||||
setInvoice(selector: string): Promise<IInvoice> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.http.get(this.SERVER_URL + '/invoice/' + selector, {
|
||||
observe: 'body',
|
||||
responseType: 'json'
|
||||
}).toPromise().then((invoice) => {
|
||||
resolve(invoice as IInvoice);
|
||||
this.invoice = invoice as IInvoice;
|
||||
this.invoiceUpdate.next(this.invoice);
|
||||
resolve(this.invoice);
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setPaymentMethod(method: CryptoUnits): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
this.http.post(`${this.SERVER_URL}/invoice/${this.invoice?.selector}/setmethod`, { method }, {
|
||||
responseType: 'json'
|
||||
}).toPromise().then(() => {
|
||||
this.setInvoice(this.invoice!!.selector);
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Path to icon
|
||||
*/
|
||||
getIcon(unit: CryptoUnits): string {
|
||||
switch (unit) {
|
||||
case CryptoUnits.BITCOIN:
|
||||
return 'assets/Bitcoin.svg';
|
||||
case CryptoUnits.BITCOINCASH:
|
||||
return 'assets/BitcoinCash.svg';
|
||||
case CryptoUnits.DOGECOIN:
|
||||
return 'assets/Dogecoin.png';
|
||||
case CryptoUnits.ETHEREUM:
|
||||
return 'assets/Ethereum.svg';
|
||||
case CryptoUnits.LITECOIN:
|
||||
return 'assets/Litecoin.svg';
|
||||
case CryptoUnits.MONERO:
|
||||
return 'assets/Monero.svg';
|
||||
}
|
||||
}
|
||||
|
||||
findCryptoBySymbol(symbol: string): string | null {
|
||||
for (const coin in CryptoUnits) {
|
||||
// @ts-ignore: This actually works but I thing it's too hacky for TS. Allow me this one, please?
|
||||
if (CryptoUnits[coin] === symbol.toUpperCase()) {
|
||||
return coin.charAt(0).toUpperCase() + coin.toLowerCase().slice(1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getAmount(): string | undefined {
|
||||
return this.invoice?.paymentMethods.find(item => {
|
||||
return item.method === CryptoUnits.BITCOIN;
|
||||
})?.amount.toFixed(8);
|
||||
}
|
||||
|
||||
getStatus(): string {
|
||||
switch (this.invoice?.status) {
|
||||
case PaymentStatus.PENDING:
|
||||
return 'Pending';
|
||||
case PaymentStatus.UNCONFIRMED:
|
||||
return 'Unconfirmed';
|
||||
case PaymentStatus.DONE:
|
||||
return 'Paid';
|
||||
case PaymentStatus.CANCELLED:
|
||||
return 'Cancelled';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
isInvoiceDone(): boolean {
|
||||
return this.invoice?.status === PaymentStatus.DONE;
|
||||
}
|
||||
|
||||
isInvoicePending(): boolean {
|
||||
return this.invoice?.status === PaymentStatus.PENDING;
|
||||
}
|
||||
|
||||
isInvoiceRequested(): boolean {
|
||||
return this.invoice?.status === PaymentStatus.REQUESTED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="header">
|
||||
<h2>LibrePay</h2>
|
||||
<a>Cancel payment</a>
|
||||
<a *ngIf="this.backend.isInvoicePending()">Cancel payment</a>
|
||||
</div>
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { BackendService } from '../backend.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
@@ -7,7 +8,7 @@ import { Component, OnInit } from '@angular/core';
|
||||
})
|
||||
export class HeaderComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
constructor(public backend: BackendService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
@@ -19,6 +19,17 @@
|
||||
/* box-shadow: 1px 1px 112px 23px rgba(0,0,0,0.75); */
|
||||
}
|
||||
|
||||
.smaller {
|
||||
min-width: 200px;
|
||||
width: 40vw !important;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.content {
|
||||
width: 100vw !important;
|
||||
}
|
||||
}
|
||||
|
||||
.content * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="cube"></div>
|
||||
<div class="cube"></div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="content" [ngClass]="{smaller: this.backend.isInvoiceRequested()}">
|
||||
<app-header></app-header>
|
||||
<app-payment></app-payment>
|
||||
</div>
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { BackendService } from '../backend.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pay',
|
||||
@@ -7,7 +8,7 @@ import { Component, OnInit } from '@angular/core';
|
||||
})
|
||||
export class PayComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
constructor(public backend: BackendService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
.payment {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
height: 500px;
|
||||
background-color: hsl(0, 0%, 11%);
|
||||
border-radius: 8px;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
|
||||
.request {
|
||||
transform: translateY(-40px);
|
||||
}
|
||||
|
||||
.main {
|
||||
display: grid;
|
||||
height: 400px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.qr {
|
||||
position: relative;
|
||||
transform: translateY(25%);
|
||||
@@ -42,12 +50,12 @@
|
||||
}
|
||||
|
||||
/* Data */
|
||||
.data {
|
||||
.main .data {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 50px;
|
||||
grid-template-rows: 1fr 4rem 4rem 4rem 1fr;
|
||||
}
|
||||
#target {
|
||||
.main #target {
|
||||
grid-row: 2;
|
||||
}
|
||||
#amount {
|
||||
@@ -57,6 +65,65 @@
|
||||
grid-row: 4;
|
||||
}
|
||||
|
||||
h3 {
|
||||
.main h3 {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
#title {
|
||||
padding-top: 2rem;
|
||||
line-height: 1;
|
||||
font-size: 20pt;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#price {
|
||||
text-align: center;
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
#list {
|
||||
overflow-y: scroll;
|
||||
scroll-behavior: smooth;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#list::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#list li {
|
||||
padding-top: 10px;
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
border-radius: 8px;
|
||||
display: grid;
|
||||
cursor: pointer;
|
||||
grid-template-columns: 75px 1fr;
|
||||
grid-row: 1fr;
|
||||
list-style: none;
|
||||
padding-bottom: 1rem;
|
||||
transition: .3s ease;
|
||||
line-height: 0;
|
||||
}
|
||||
#list li:hover {
|
||||
box-shadow: 0px 0px 61px 3px rgba(0,0,0,0.3) inset;
|
||||
}
|
||||
#list li img {
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
margin: 0 auto;
|
||||
padding-top: 12px;
|
||||
grid-row-start: 1;
|
||||
grid-row-end: 2;
|
||||
}
|
||||
#list li p {
|
||||
grid-row: 1;
|
||||
grid-column: 2;
|
||||
}
|
||||
#list li h4 {
|
||||
grid-row: 2;
|
||||
grid-column: 2;
|
||||
}
|
||||
@@ -1,9 +1,23 @@
|
||||
<div class="payment">
|
||||
<div class="payment request" *ngIf="this.backend.isInvoiceRequested()">
|
||||
<h3 id="title">Choose your<br>payment method</h3>
|
||||
<p id="price">{{ this.backend.invoice!!.totalPrice!!.toFixed(2) }} €</p>
|
||||
|
||||
<ul id="list">
|
||||
<li *ngFor="let coin of this.backend.invoice!!.paymentMethods" (click)="chooseMethod(coin.method)">
|
||||
<img [src]="this.backend.getIcon(coin.method)">
|
||||
<div>
|
||||
<h4>{{ this.backend.findCryptoBySymbol(coin.method) }}</h4>
|
||||
<p>{{ coin.amount }} {{ coin.method }}</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="payment main" *ngIf="!this.backend.isInvoiceRequested() && ready">
|
||||
<div class="qrWrapper">
|
||||
<div class="qr">
|
||||
<img src="assets/Bitcoin.svg">
|
||||
<qrcode
|
||||
[qrdata]="'bitcoin:' + invoice!!.receiveAddress"
|
||||
[qrdata]="'bitcoin:' + this.backend.invoice!!.receiveAddress"
|
||||
[width]="256"
|
||||
[errorCorrectionLevel]="'M'"
|
||||
[elementType]="'svg'"
|
||||
@@ -12,16 +26,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="data" *ngIf="ready">
|
||||
<div class="data">
|
||||
<!-- Payment data -->
|
||||
<span id="target">Send to
|
||||
<h3>{{ invoice!!.receiveAddress }}</h3>
|
||||
<h3>{{ this.backend.invoice!.receiveAddress }}</h3>
|
||||
</span>
|
||||
<span id="amount">Amount
|
||||
<h3>{{ getAmount() }} BTC</h3>
|
||||
<h3>{{ this.backend.getAmount() }} BTC</h3>
|
||||
</span>
|
||||
<span id="status">Status
|
||||
<h3>{{ getStatus() }}</h3>
|
||||
<h3>{{ this.backend.getStatus() }}</h3>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { BackendService, IInvoice, CryptoUnits, PaymentStatus } from '../backend.service';
|
||||
import { BackendService, IInvoice, CryptoUnits, PaymentStatus, IPaymentMethod } from '../backend.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-payment',
|
||||
@@ -12,10 +12,9 @@ export class PaymentComponent implements OnInit {
|
||||
paymentSelector = '';
|
||||
choosenPaymentMethod = CryptoUnits.BITCOIN;
|
||||
ready = false;
|
||||
invoice: IInvoice | null = null;
|
||||
|
||||
constructor(
|
||||
private backend: BackendService,
|
||||
public backend: BackendService,
|
||||
private route: ActivatedRoute
|
||||
) { }
|
||||
|
||||
@@ -27,32 +26,13 @@ export class PaymentComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
async get() {
|
||||
this.invoice = await this.backend.getInvoice(this.paymentSelector);
|
||||
chooseMethod(coin: CryptoUnits) {
|
||||
this.backend.setPaymentMethod(coin);
|
||||
}
|
||||
|
||||
async get(): Promise<void> {
|
||||
await this.backend.setInvoice(this.paymentSelector);
|
||||
this.ready = true;
|
||||
}
|
||||
|
||||
getAmount() {
|
||||
return this.invoice?.paymentMethods.find(item => {
|
||||
return item.method === CryptoUnits.BITCOIN;
|
||||
})?.amount.toFixed(8);
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
switch (this.invoice?.status) {
|
||||
case PaymentStatus.PENDING:
|
||||
return 'Pending';
|
||||
case PaymentStatus.PARTIALLY:
|
||||
return 'Partly';
|
||||
case PaymentStatus.UNCONFIRMED:
|
||||
return 'Unconfirmed';
|
||||
case PaymentStatus.DONE:
|
||||
return 'Paid';
|
||||
case PaymentStatus.CANCELLED:
|
||||
return 'Cancelled';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user