[accelerator] accelerate with lightning
This commit is contained in:
parent
f1572f0038
commit
66a88b8422
@ -58,12 +58,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
}
|
|
||||||
|
} @else if (step === 'paymentMethod') {
|
||||||
@else if (step === 'checkout') {
|
|
||||||
<!-- Show checkout page -->
|
|
||||||
<div class="row mb-md-1 text-center">
|
<div class="row mb-md-1 text-center">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
|
<h1 style="font-size: larger;">Select your payment method</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pt-2 d-flex justify-content-around">
|
||||||
|
@if (cashappEnabled) {
|
||||||
|
<img class="paymentMethod" src="/resources/cash-app.svg" height=55 (click)="selectPaymentMethod('cashapp')">
|
||||||
|
}
|
||||||
|
<img class="paymentMethod" src="/resources/btcpay.svg" height=55 (click)="selectPaymentMethod('btcpay')">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
} @else if (step === 'checkout') {
|
||||||
|
<!-- Show checkout page -->
|
||||||
|
<div class="row mb-md-1 text-center">
|
||||||
|
<div class="col-sm" id="confirm-payment-title">
|
||||||
<h1 style="font-size: larger;">Confirm your payment</h1>
|
<h1 style="font-size: larger;">Confirm your payment</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -76,36 +88,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (!loadingCashapp) {
|
@if (paymentMethod === 'cashapp') {
|
||||||
|
@if (!loadingCashapp) {
|
||||||
|
<div class="row text-center mt-1">
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group w-100">
|
||||||
|
<span><u><strong>Total additional cost</strong></u><br>
|
||||||
|
<span style="font-size: 16px" class="d-block mt-2">
|
||||||
|
Pay
|
||||||
|
<strong><app-fiat [value]="cost"></app-fiat></strong>
|
||||||
|
with
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="row text-center mt-1">
|
<div class="row text-center mt-1">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<div class="form-group w-100">
|
<div class="form-group w-100">
|
||||||
<span><u><strong>Total additional cost</strong></u><br>
|
<div id="cash-app-pay" class="d-inline-block" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
|
||||||
<span style="font-size: 16px" class="d-block mt-2">
|
@if (loadingCashapp) {
|
||||||
Pay
|
<div display="d-flex flex-row justify-content-center">
|
||||||
<strong><app-fiat [value]="cost"></app-fiat></strong>
|
<span>Loading payment method...</span>
|
||||||
with
|
<div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
|
||||||
</span>
|
</div>
|
||||||
</span>
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
} @else if (paymentMethod === 'btcpay' && invoice?.btcpayInvoiceId) {
|
||||||
|
<app-bitcoin-invoice [invoiceId]="invoice.btcpayInvoiceId" (completed)="closeModal(2000)"></app-bitcoin-invoice>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="row text-center mt-1">
|
|
||||||
<div class="col-sm">
|
|
||||||
<div class="form-group w-100">
|
|
||||||
<div id="cash-app-pay" class="d-inline-block" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
|
|
||||||
@if (loadingCashapp) {
|
|
||||||
<div display="d-flex flex-row justify-content-center">
|
|
||||||
<span>Loading payment method...</span>
|
|
||||||
<div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row mt-2 mb-2 text-center">
|
<div class="row mt-2 mb-2 text-center">
|
||||||
<div class="col-sm d-flex flex-column">
|
<div class="col-sm d-flex flex-column">
|
||||||
@ -118,7 +134,7 @@
|
|||||||
@else if (step === 'processing') {
|
@else if (step === 'processing') {
|
||||||
<div class="row mb-1 text-center">
|
<div class="row mb-1 text-center">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<h1 style="font-size: larger;">Confirm your payment</h1>
|
<h1 style="font-size: larger;">Confirming your payment</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -7,3 +7,11 @@
|
|||||||
.estimating {
|
.estimating {
|
||||||
color: var(--green)
|
color: var(--green)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.paymentMethod {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: var(--secondary);
|
||||||
|
border-radius: 15px;
|
||||||
|
border: 2px solid var(--bg);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
@ -16,12 +16,16 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
@Input() eta: number | null = null;
|
@Input() eta: number | null = null;
|
||||||
@Input() txid: string = '70c18d76cdb285a1b5bd87fdaae165880afa189809c30b4083ff7c0e69ee09ad';
|
@Input() txid: string = '70c18d76cdb285a1b5bd87fdaae165880afa189809c30b4083ff7c0e69ee09ad';
|
||||||
@Input() scrollEvent: boolean;
|
@Input() scrollEvent: boolean;
|
||||||
|
@Input() cashappEnabled: boolean;
|
||||||
@Output() close = new EventEmitter<null>();
|
@Output() close = new EventEmitter<null>();
|
||||||
|
|
||||||
calculating = true;
|
calculating = true;
|
||||||
choosenOption: 'wait' | 'accelerate' = 'wait';
|
choosenOption: 'wait' | 'accelerate' = 'wait';
|
||||||
error = '';
|
error = '';
|
||||||
|
|
||||||
|
step: 'paymentMethod' | 'cta' | 'checkout' | 'processing' = 'cta';
|
||||||
|
paymentMethod: 'cashapp' | 'btcpay';
|
||||||
|
|
||||||
// accelerator stuff
|
// accelerator stuff
|
||||||
square: { appId: string, locationId: string};
|
square: { appId: string, locationId: string};
|
||||||
accelerationUUID: string;
|
accelerationUUID: string;
|
||||||
@ -38,7 +42,10 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
cashAppPay: any;
|
cashAppPay: any;
|
||||||
cashAppSubscription: Subscription;
|
cashAppSubscription: Subscription;
|
||||||
conversionsSubscription: Subscription;
|
conversionsSubscription: Subscription;
|
||||||
step: 'cta' | 'checkout' | 'processing' = 'cta';
|
|
||||||
|
// btcpay
|
||||||
|
loadingBtcpayInvoice = false;
|
||||||
|
invoice = undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private servicesApiService: ServicesApiServices,
|
private servicesApiService: ServicesApiServices,
|
||||||
@ -77,19 +84,19 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (changes.scrollEvent) {
|
if (changes.scrollEvent) {
|
||||||
this.scrollToPreview('acceleratePreviewAnchor', 'start');
|
this.scrollToElement('acceleratePreviewAnchor', 'start');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll to element id with or without setTimeout
|
* Scroll to element id with or without setTimeout
|
||||||
*/
|
*/
|
||||||
scrollToPreviewWithTimeout(id: string, position: ScrollLogicalPosition) {
|
scrollToElementWithTimeout(id: string, position: ScrollLogicalPosition, timeout: number = 1000) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.scrollToPreview(id, position);
|
this.scrollToElement(id, position);
|
||||||
}, 1000);
|
}, timeout);
|
||||||
}
|
}
|
||||||
scrollToPreview(id: string, position: ScrollLogicalPosition) {
|
scrollToElement(id: string, position: ScrollLogicalPosition) {
|
||||||
const acceleratePreviewAnchor = document.getElementById(id);
|
const acceleratePreviewAnchor = document.getElementById(id);
|
||||||
if (acceleratePreviewAnchor) {
|
if (acceleratePreviewAnchor) {
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
@ -111,7 +118,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
this.calculating = true;
|
this.calculating = true;
|
||||||
this.estimateSubscription = this.servicesApiService.estimate$(this.txid).pipe(
|
this.estimateSubscription = this.servicesApiService.estimate$(this.txid).pipe(
|
||||||
tap((response) => {
|
tap((response) => {
|
||||||
this.calculating = false;
|
|
||||||
if (response.status === 204) {
|
if (response.status === 204) {
|
||||||
this.error = `cannot_accelerate_tx`;
|
this.error = `cannot_accelerate_tx`;
|
||||||
} else {
|
} else {
|
||||||
@ -126,6 +132,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
this.maxBidBoost = minExtraBoost * DEFAULT_BID_RATIO;
|
this.maxBidBoost = minExtraBoost * DEFAULT_BID_RATIO;
|
||||||
this.cost = this.maxBidBoost + this.estimate.mempoolBaseFee + this.estimate.vsizeFee;
|
this.cost = this.maxBidBoost + this.estimate.mempoolBaseFee + this.estimate.vsizeFee;
|
||||||
this.etaInfo$ = this.etaService.getProjectedEtaObservable(this.estimate);
|
this.etaInfo$ = this.etaService.getProjectedEtaObservable(this.estimate);
|
||||||
|
this.calculating = false;
|
||||||
|
this.cd.markForCheck();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -265,19 +273,48 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BTCPay
|
||||||
|
*/
|
||||||
|
async requestBTCPayInvoice() {
|
||||||
|
this.servicesApiService.generateBTCPayAcceleratorInvoice$(this.txid).subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
this.invoice = response;
|
||||||
|
this.cd.markForCheck();
|
||||||
|
this.scrollToElementWithTimeout('acceleratePreviewAnchor', 'start', 500);
|
||||||
|
},
|
||||||
|
error: (response) => {
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UI events
|
* UI events
|
||||||
*/
|
*/
|
||||||
enableCheckoutPage() {
|
enableCheckoutPage() {
|
||||||
|
this.step = 'paymentMethod';
|
||||||
|
}
|
||||||
|
selectPaymentMethod(paymentMethod: 'cashapp' | 'btcpay') {
|
||||||
this.step = 'checkout';
|
this.step = 'checkout';
|
||||||
this.loadingCashapp = true;
|
this.paymentMethod = paymentMethod;
|
||||||
this.insertSquare();
|
if (paymentMethod === 'cashapp') {
|
||||||
this.setupSquare();
|
this.loadingCashapp = true;
|
||||||
|
this.insertSquare();
|
||||||
|
this.setupSquare();
|
||||||
|
} else if (paymentMethod === 'btcpay') {
|
||||||
|
this.loadingBtcpayInvoice = true;
|
||||||
|
this.requestBTCPayInvoice();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
selectedOptionChanged(event) {
|
selectedOptionChanged(event) {
|
||||||
this.choosenOption = event.target.id;
|
this.choosenOption = event.target.id;
|
||||||
}
|
}
|
||||||
closeModal(): void {
|
closeModal(timeout: number = 0): void {
|
||||||
this.close.emit();
|
setTimeout(() => {
|
||||||
|
this.step = 'processing';
|
||||||
|
this.cd.markForCheck();
|
||||||
|
this.close.emit();
|
||||||
|
}, timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
<div class="wrapper">
|
||||||
|
|
||||||
|
<span *ngIf="paymentStatus === 3" class="valid-feedback d-block mt-5">
|
||||||
|
Payment successful. You can close this page.
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span *ngIf="paymentStatus === 4" class="valid-feedback d-block mt-5">
|
||||||
|
A transaction <a [href]="'/tx/' + invoice.cryptoInfo[0].payments[0].id.split('-')[0]">has been detected in the mempool</a> fully paying for this invoice. Waiting for on-chain confirmation.
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div *ngIf="paymentStatus === 2">
|
||||||
|
|
||||||
|
<form [formGroup]="paymentForm">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||||
|
<!-- <label *ngIf="invoice.addresses.BTC" class="btn btn-primary" [ngClass]="{'active': paymentForm.get('method')?.value === 'chain'}">
|
||||||
|
<input type="radio" value="chain" formControlName="method"> <fa-icon [icon]="['fas', 'link']" [fixedWidth]="true" title="Onchain"></fa-icon>
|
||||||
|
</label> -->
|
||||||
|
<label *ngIf="invoice.addresses.BTC_LightningLike" class="btn btn-primary" [ngClass]="{'active': paymentForm.get('method')?.value === 'lightning'}">
|
||||||
|
<input type="radio" value="lightning" formControlName="method"> <fa-icon [icon]="['fas', 'bolt']" [fixedWidth]="true" title="Lightning"></fa-icon>
|
||||||
|
</label>
|
||||||
|
<!-- <label *ngIf="invoice.addresses.LBTC" class="btn btn-primary" [ngClass]="{'active': paymentForm.get('method')?.value === 'lbtc'}">
|
||||||
|
<input type="radio" value="lbtc" formControlName="method"> <fa-icon [icon]="['fas', 'tint']" [fixedWidth]="true" title="Liquid Bitcoin"></fa-icon>
|
||||||
|
</label> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="paymentForm.get('method')?.value === 'chain' && invoice">
|
||||||
|
|
||||||
|
<div class="qr-wrapper">
|
||||||
|
<a [href]="bypassSecurityTrustUrl('bitcoin:' + invoice.addresses.BTC + '?amount=' + invoice.amount)" target="_blank">
|
||||||
|
<app-qrcode imageUrl="/resources/bitcoin-logo.png" [size]="200" [data]="'bitcoin:' + invoice.addresses.BTC + '?amount=' + invoice.amount"></app-qrcode>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group input-group-sm info-group">
|
||||||
|
<input type="text" class="form-control input-dark" readonly [value]="invoice.addresses.BTC">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-outline-secondary" type="button" ><app-clipboard [text]="invoice.addresses.BTC"></app-clipboard></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>{{ invoice.amount }} <span class="symbol">BTC</span></p>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="paymentForm.get('method')?.value === 'lightning' && invoice">
|
||||||
|
|
||||||
|
<div class="qr-wrapper">
|
||||||
|
<a [href]="bypassSecurityTrustUrl('lightning:' + invoice.addresses.BTC_LightningLike)" target="_blank">
|
||||||
|
<app-qrcode imageUrl="/resources/bitcoin-logo.png" [size]="200" [data]="invoice.addresses.BTC_LightningLike.toUpperCase()"></app-qrcode>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group input-group-sm info-group">
|
||||||
|
<input type="text" class="form-control input-dark" readonly [value]="invoice.addresses.BTC_LightningLike">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-outline-secondary" type="button"><app-clipboard [text]="invoice.addresses.BTC_LightningLike"></app-clipboard></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>{{ invoice.amount * 100_000_000 }} <span class="symbol">sats</span></p>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="invoice && (paymentForm.get('method')?.value === 'lbtc' || paymentForm.get('method')?.value === 'tlbtc')">
|
||||||
|
|
||||||
|
<div class="qr-wrapper">
|
||||||
|
<a [href]="bypassSecurityTrustUrl('liquidnetwork:' + invoice.addresses.LBTC + '?amount=' + invoice.amount + '&assetid=6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d')" target="_blank">
|
||||||
|
<app-qrcode imageUrl="/resources/liquid-bitcoin.png" [size]="200" [data]="'liquidnetwork:' + invoice.addresses.LBTC + '?amount=' + invoice.amount + '&assetid=6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d'"></app-qrcode>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="input-group input-group-sm info-group">
|
||||||
|
<input type="text" class="form-control input-dark" readonly [value]="invoice.addresses.LBTC" />
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-outline-secondary" type="button" ><app-clipboard [text]="invoice.addresses.LBTC"></app-clipboard></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>{{ invoice.amount }} <span class="symbol">BTC</span></p>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<p>Waiting for transaction... </p>
|
||||||
|
<div class="spinner-border text-light"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,149 @@
|
|||||||
|
.form-panel {
|
||||||
|
background-color: #292b45;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.sponsor-page {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-wrapper {
|
||||||
|
background-color: #FFF;
|
||||||
|
padding: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
margin: 20px auto 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-group {
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 240px;
|
||||||
|
height: 220px;
|
||||||
|
background-color: var(--bg);
|
||||||
|
border: 2px solid var(--bg);
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
transition: 100ms all;
|
||||||
|
margin: 30px 30px 20px 30px;
|
||||||
|
@media(min-width: 476px) {
|
||||||
|
margin: 30px 100px 20px 100px;
|
||||||
|
}
|
||||||
|
@media(min-width: 851px) {
|
||||||
|
margin: 60px 20px 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-weight: bold;
|
||||||
|
span {
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bigger {
|
||||||
|
height: 220px;
|
||||||
|
width: 240px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #5058926b;
|
||||||
|
border: 2px solid #505892;
|
||||||
|
transform: scale(1.1) translateY(-10px);
|
||||||
|
margin-top: 70px;
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background-color: #505892;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.donation-form {
|
||||||
|
max-width: 280px;
|
||||||
|
margin: auto;
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background-color: #171929;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.middle-card {
|
||||||
|
width: 280px;
|
||||||
|
height: 260px;
|
||||||
|
margin-top: 40px;
|
||||||
|
&:hover {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shiny-border {
|
||||||
|
background-color: #5058926b;
|
||||||
|
border: 2px solid #505892;
|
||||||
|
transform: scale(1.1) translateY(-10px);
|
||||||
|
margin-top: 70px;
|
||||||
|
box-shadow: 0px 0px 100px #9858ff52;
|
||||||
|
.card-header {
|
||||||
|
background-color: #505892;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.middle-card {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.donation-confirmed {
|
||||||
|
h2 {
|
||||||
|
margin-top: 50px;
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
&:last-child {
|
||||||
|
color: #9858ff;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details {
|
||||||
|
margin-top: 50px;
|
||||||
|
span {
|
||||||
|
color: #d81b60;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-dark {
|
||||||
|
background-color: var(--bg);
|
||||||
|
border-color: var(--active-bg);
|
||||||
|
color: white;
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
|
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { Subscription, timer } from 'rxjs';
|
||||||
|
import { retry, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { ServicesApiServices } from '../../services/services-api.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-bitcoin-invoice',
|
||||||
|
templateUrl: './bitcoin-invoice.component.html',
|
||||||
|
styleUrls: ['./bitcoin-invoice.component.scss']
|
||||||
|
})
|
||||||
|
export class BitcoinInvoiceComponent implements OnInit, OnDestroy {
|
||||||
|
@Input() invoiceId: string;
|
||||||
|
@Input() redirect = true;
|
||||||
|
@Output() completed = new EventEmitter();
|
||||||
|
|
||||||
|
paymentForm: FormGroup;
|
||||||
|
requestSubscription: Subscription | undefined;
|
||||||
|
paymentStatusSubscription: Subscription | undefined;
|
||||||
|
invoice: any;
|
||||||
|
paymentStatus = 1; // 1 - Waiting for invoice | 2 - Pending payment | 3 - Payment completed
|
||||||
|
paramMapSubscription: Subscription | undefined;
|
||||||
|
invoiceSubscription: Subscription | undefined;
|
||||||
|
invoiceTimeout; // Wait for angular to load all the things before making a request
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private apiService: ServicesApiServices,
|
||||||
|
private sanitizer: DomSanitizer,
|
||||||
|
private activatedRoute: ActivatedRoute
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.requestSubscription) {
|
||||||
|
this.requestSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (this.paramMapSubscription) {
|
||||||
|
this.paramMapSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (this.invoiceSubscription) {
|
||||||
|
this.invoiceSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (this.paymentStatusSubscription) {
|
||||||
|
this.paymentStatusSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.paymentForm = this.formBuilder.group({
|
||||||
|
'method': 'lightning'
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the invoice is passed in the url, fetch it and display btcpay payment
|
||||||
|
* Otherwise get a new invoice
|
||||||
|
*/
|
||||||
|
this.paramMapSubscription = this.activatedRoute.paramMap
|
||||||
|
.pipe(
|
||||||
|
tap((paramMap) => {
|
||||||
|
const invoiceId = paramMap.get('invoiceId') ?? this.invoiceId;
|
||||||
|
if (invoiceId) {
|
||||||
|
this.paymentStatusSubscription = this.apiService.retreiveInvoice$(invoiceId).pipe(
|
||||||
|
tap((invoice: any) => {
|
||||||
|
this.invoice = invoice;
|
||||||
|
this.invoice.amount = invoice.btcDue ?? (invoice.cryptoInfo.length ? parseFloat(invoice.cryptoInfo[0].totalDue) : 0) ?? 0;
|
||||||
|
|
||||||
|
if (this.invoice.amount > 0) {
|
||||||
|
this.paymentStatus = 2;
|
||||||
|
} else {
|
||||||
|
this.paymentStatus = 4;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
switchMap(() => this.apiService.getPaymentStatus$(this.invoice.id)
|
||||||
|
.pipe(
|
||||||
|
retry({ delay: () => timer(2000)})
|
||||||
|
)
|
||||||
|
),
|
||||||
|
).subscribe({
|
||||||
|
next: ((result) => {
|
||||||
|
this.paymentStatus = 3;
|
||||||
|
this.completed.emit();
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
).subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
bypassSecurityTrustUrl(text: string): SafeUrl {
|
||||||
|
return this.sanitizer.bypassSecurityTrustUrl(text);
|
||||||
|
}
|
||||||
|
}
|
@ -75,7 +75,7 @@
|
|||||||
} @else {
|
} @else {
|
||||||
<app-time kind="until" [time]="eta.time" [fastRender]="false" [fixedRender]="true"></app-time>
|
<app-time kind="until" [time]="eta.time" [fastRender]="false" [fixedRender]="true"></app-time>
|
||||||
}
|
}
|
||||||
@if (!showAccelerationSummary && isMobile && paymentType === 'cashapp' && accelerationEligible && !tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) {
|
@if (!showAccelerationSummary && isMobile && !tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) {
|
||||||
<a class="btn btn-sm accelerate btn-small-height" i18n="transaction.accelerate|Accelerate button label" (click)="onAccelerateClicked()">Accelerate</a>
|
<a class="btn btn-sm accelerate btn-small-height" i18n="transaction.accelerate|Accelerate button label" (click)="onAccelerateClicked()">Accelerate</a>
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
@ -116,7 +116,7 @@
|
|||||||
|
|
||||||
<div class="bottom-panel">
|
<div class="bottom-panel">
|
||||||
@if (showAccelerationSummary && !accelerationFlowCompleted) {
|
@if (showAccelerationSummary && !accelerationFlowCompleted) {
|
||||||
<app-accelerate-checkout *ngIf="(da$ | async) as da;" [txid]="tx.txid" [eta]="mempoolPosition?.block >= 7 ? null : da.adjustedTimeAvg * (mempoolPosition.block + 1) + now + da.timeOffset" (close)="accelerationFlowCompleted = true" [scrollEvent]="scrollIntoAccelPreview" class="h-100 w-100"></app-accelerate-checkout>
|
<app-accelerate-checkout *ngIf="(da$ | async) as da;" [cashappEnabled]="accelerationEligible" [txid]="tx.txid" [eta]="mempoolPosition?.block >= 7 ? null : da.adjustedTimeAvg * (mempoolPosition.block + 1) + now + da.timeOffset" (close)="accelerationFlowCompleted = true" [scrollEvent]="scrollIntoAccelPreview" class="h-100 w-100"></app-accelerate-checkout>
|
||||||
} @else {
|
} @else {
|
||||||
@if (tx?.acceleration && !tx.status?.confirmed) {
|
@if (tx?.acceleration && !tx.status?.confirmed) {
|
||||||
<div class="progress-icon">
|
<div class="progress-icon">
|
||||||
|
@ -107,7 +107,6 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
|||||||
now = Date.now();
|
now = Date.now();
|
||||||
da$: Observable<DifficultyAdjustment>;
|
da$: Observable<DifficultyAdjustment>;
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
paymentType: 'bitcoin' | 'cashapp' = 'bitcoin';
|
|
||||||
|
|
||||||
trackerStage: TrackerStage = 'waiting';
|
trackerStage: TrackerStage = 'waiting';
|
||||||
|
|
||||||
@ -158,9 +157,6 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.acceleratorAvailable = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === '';
|
this.acceleratorAvailable = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === '';
|
||||||
|
|
||||||
if (this.acceleratorAvailable && this.stateService.referrer === 'https://cash.app/') {
|
|
||||||
this.paymentType = 'cashapp';
|
|
||||||
}
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
if (urlParams.get('cash_request_id')) {
|
if (urlParams.get('cash_request_id')) {
|
||||||
this.showAccelerationSummary = true;
|
this.showAccelerationSummary = true;
|
||||||
@ -390,11 +386,9 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
|||||||
this.trackerStage = 'replaced';
|
this.trackerStage = 'replaced';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.showAccelerationSummary = true;
|
||||||
if (txPosition.position?.block > 0 && this.tx.weight < 4000) {
|
if (txPosition.position?.block > 0 && this.tx.weight < 4000) {
|
||||||
this.accelerationEligible = true;
|
this.accelerationEligible = true;
|
||||||
if (this.acceleratorAvailable && this.paymentType === 'cashapp') {
|
|
||||||
this.showAccelerationSummary = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -167,4 +167,19 @@ export class ServicesApiServices {
|
|||||||
requestTestnet4Coins$(address: string, sats: number) {
|
requestTestnet4Coins$(address: string, sats: number) {
|
||||||
return this.httpClient.get<{txid: string}>(`${SERVICES_API_PREFIX}/testnet4/faucet/request?address=${address}&sats=${sats}`, { responseType: 'json' });
|
return this.httpClient.get<{txid: string}>(`${SERVICES_API_PREFIX}/testnet4/faucet/request?address=${address}&sats=${sats}`, { responseType: 'json' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateBTCPayAcceleratorInvoice$(txid: string): Observable<any> {
|
||||||
|
const params = {
|
||||||
|
product: txid
|
||||||
|
};
|
||||||
|
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/payments/bitcoin`, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
retreiveInvoice$(invoiceId: string): Observable<any[]> {
|
||||||
|
return this.httpClient.get<any[]>(`${SERVICES_API_PREFIX}/payments/bitcoin/invoice?id=${invoiceId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPaymentStatus$(orderId: string): Observable<any[]> {
|
||||||
|
return this.httpClient.get<any[]>(`${SERVICES_API_PREFIX}/payments/bitcoin/check?order_id=${orderId}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,7 @@ import { HttpErrorComponent } from '../shared/components/http-error/http-error.c
|
|||||||
import { TwitterWidgetComponent } from '../components/twitter-widget/twitter-widget.component';
|
import { TwitterWidgetComponent } from '../components/twitter-widget/twitter-widget.component';
|
||||||
import { FaucetComponent } from '../components/faucet/faucet.component';
|
import { FaucetComponent } from '../components/faucet/faucet.component';
|
||||||
import { TwitterLogin } from '../components/twitter-login/twitter-login.component';
|
import { TwitterLogin } from '../components/twitter-login/twitter-login.component';
|
||||||
|
import { BitcoinInvoiceComponent } from '../components/bitcoin-invoice/bitcoin-invoice.component';
|
||||||
|
|
||||||
import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-directives/weight-directives';
|
import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-directives/weight-directives';
|
||||||
|
|
||||||
@ -230,6 +231,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
|||||||
TwitterWidgetComponent,
|
TwitterWidgetComponent,
|
||||||
FaucetComponent,
|
FaucetComponent,
|
||||||
TwitterLogin,
|
TwitterLogin,
|
||||||
|
BitcoinInvoiceComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@ -359,6 +361,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
|||||||
HttpErrorComponent,
|
HttpErrorComponent,
|
||||||
TwitterWidgetComponent,
|
TwitterWidgetComponent,
|
||||||
TwitterLogin,
|
TwitterLogin,
|
||||||
|
BitcoinInvoiceComponent,
|
||||||
|
|
||||||
MempoolBlockOverviewComponent,
|
MempoolBlockOverviewComponent,
|
||||||
ClockchainComponent,
|
ClockchainComponent,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user