[accelerator] prepaid acceleration
This commit is contained in:
		
							parent
							
								
									78f03bd6d5
								
							
						
					
					
						commit
						8fee195577
					
				| @ -219,7 +219,7 @@ | ||||
|               </ng-container> | ||||
| 
 | ||||
|               <!-- LOGIN CTA --> | ||||
|               <ng-container *ngIf="stateService.isMempoolSpaceBuild && !isLoggedIn()"> | ||||
|               <ng-container *ngIf="stateService.isMempoolSpaceBuild && !isLoggedIn() && paymentType === 'bitcoin'"> | ||||
|                 <tr class="group-first group-last" style="border-top: 1px dashed grey"> | ||||
|                   <td class="item"></td> | ||||
|                   <td class="amt"></td> | ||||
| @ -242,7 +242,7 @@ | ||||
|         </div> | ||||
|       </div> | ||||
|    | ||||
|       <div class="row mb-3" *ngIf="isLoggedIn()"> | ||||
|       <div class="row mb-3" *ngIf="isLoggedIn() && paymentType === 'bitcoin'"> | ||||
|         <div class="col"> | ||||
|           <div class="d-flex justify-content-end" *ngIf="user && estimate.hasAccess"> | ||||
|             <button class="btn btn-sm btn-primary btn-success" style="width: 150px" (click)="accelerate()">Accelerate</button> | ||||
| @ -250,6 +250,14 @@ | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="row d-flex justify-content-end mr-1" style="height: 48px" *ngIf="!hideCashApp"> | ||||
|         <div id="cash-app-pay" style="max-width: 320px" [style]="showSpinner ? 'opacity: 0' : 'opacity: 1'"></div> | ||||
|         <div *ngIf="showSpinner" class="d-flex align-items-center"> | ||||
|           <span class="mr-2">Loading</span> | ||||
|           <div class="spinner-border text-light" style="width: 25px; height: 25px"></div> | ||||
|         </div> | ||||
|       </div> | ||||
|    | ||||
|     </div> | ||||
|   </ng-container> | ||||
| </div> | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges, HostListener, ChangeDetectorRef } from '@angular/core'; | ||||
| import { ApiService } from '../../services/api.service'; | ||||
| import { Subscription, catchError, of, tap } from 'rxjs'; | ||||
| import { StorageService } from '../../services/storage.service'; | ||||
| import { Transaction } from '../../interfaces/electrs.interface'; | ||||
| @ -40,7 +39,7 @@ export const MAX_BID_RATIO = 4; | ||||
|   templateUrl: 'accelerate-preview.component.html', | ||||
|   styleUrls: ['accelerate-preview.component.scss'] | ||||
| }) | ||||
| export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges { | ||||
| export class AcceleratePreviewComponent implements OnDestroy, OnChanges { | ||||
|   @Input() tx: Transaction | undefined; | ||||
|   @Input() scrollEvent: boolean; | ||||
| 
 | ||||
| @ -63,18 +62,37 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges | ||||
| 
 | ||||
|   maxRateOptions: RateOption[] = []; | ||||
| 
 | ||||
|   // Cashapp payment
 | ||||
|   paymentType: 'bitcoin' | 'cashapp' = 'bitcoin'; | ||||
|   cashAppSubscription: Subscription; | ||||
|   conversionsSubscription: Subscription; | ||||
|   payments: any; | ||||
|   showSpinner = false; | ||||
|   square: any; | ||||
|   cashAppPay: any; | ||||
|   hideCashApp = false; | ||||
| 
 | ||||
|   constructor( | ||||
|     public stateService: StateService, | ||||
|     private servicesApiService: ServicesApiServices, | ||||
|     private storageService: StorageService, | ||||
|     private audioService: AudioService, | ||||
|     private cd: ChangeDetectorRef | ||||
|   ) { } | ||||
|   ) { | ||||
|     if (window.document.referrer === 'cash.app') { | ||||
|       this.paymentType = 'cashapp'; | ||||
|     } else { | ||||
|       this.paymentType = 'bitcoin'; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ngOnDestroy(): void { | ||||
|     if (this.estimateSubscription) { | ||||
|       this.estimateSubscription.unsubscribe(); | ||||
|     } | ||||
|     if (this.cashAppPay) { | ||||
|       this.cashAppPay.destroy(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ngOnChanges(changes: SimpleChanges): void { | ||||
| @ -83,9 +101,16 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit() { | ||||
|   ngAfterViewInit() { | ||||
|     this.showSpinner = true; | ||||
| 
 | ||||
|     this.user = this.storageService.getAuth()?.user ?? null; | ||||
| 
 | ||||
|     this.servicesApiService.setupSquare$().subscribe(ids => { | ||||
|       this.square = { | ||||
|         appId: ids.squareAppId, | ||||
|         locationId: ids.squareLocationId | ||||
|       }; | ||||
|       this.estimateSubscription = this.servicesApiService.estimate$(this.tx.txid).pipe( | ||||
|         tap((response) => { | ||||
|           if (response.status === 204) { | ||||
| @ -101,6 +126,11 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges | ||||
|               this.estimateSubscription.unsubscribe(); | ||||
|             } | ||||
| 
 | ||||
|             if (this.paymentType === 'cashapp') { | ||||
|               this.estimate.userBalance = 999999999; | ||||
|               this.estimate.enoughBalance = true; | ||||
|             } | ||||
| 
 | ||||
|             if (this.estimate.hasAccess === true && this.estimate.userBalance <= 0) { | ||||
|               if (this.isLoggedIn()) { | ||||
|                 this.error = `not_enough_balance`; | ||||
| @ -135,6 +165,9 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges | ||||
| 
 | ||||
|             if (!this.error) { | ||||
|               this.scrollToPreview('acceleratePreviewAnchor', 'start'); | ||||
|               if (this.paymentType === 'cashapp') { | ||||
|                 this.setupSquare(); | ||||
|               }  | ||||
|             } | ||||
|           } | ||||
|         }), | ||||
| @ -146,6 +179,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges | ||||
|           return of(null); | ||||
|         }) | ||||
|       ).subscribe(); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
| @ -216,4 +250,99 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges | ||||
|   onResize(): void { | ||||
|     this.isMobile = window.innerWidth <= 767.98; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * CashApp payment | ||||
|    */ | ||||
|   setupSquare() { | ||||
|     const init = () => { | ||||
|       this.initSquare(); | ||||
|     }; | ||||
| 
 | ||||
|     //@ts-ignore
 | ||||
|     if (!window.Square) { | ||||
|       console.warn('Square.js failed to load properly. Retrying in 1 second.'); | ||||
|       setTimeout(init, 1000); | ||||
|     } else { | ||||
|       init(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async initSquare(): Promise<void> { | ||||
|     try { | ||||
|       //@ts-ignore
 | ||||
|       this.payments = window.Square.payments(this.square.appId, this.square.locationId) | ||||
|       await this.requestCashAppPayment(); | ||||
|     } catch (e) { | ||||
|       console.error(e); | ||||
|       this.error = 'Error loading Square Payments'; | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async requestCashAppPayment() { | ||||
|     if (this.cashAppSubscription) { | ||||
|       this.cashAppSubscription.unsubscribe(); | ||||
|     } | ||||
|     if (this.conversionsSubscription) { | ||||
|       this.conversionsSubscription.unsubscribe(); | ||||
|     } | ||||
|     this.hideCashApp = false; | ||||
| 
 | ||||
|      | ||||
|     this.conversionsSubscription = this.stateService.conversions$.subscribe( | ||||
|       async (conversions) => { | ||||
|         const maxCostUsd = this.maxCost / 100_000_000 * conversions.USD; | ||||
|         const paymentRequest = this.payments.paymentRequest({ | ||||
|           countryCode: 'US', | ||||
|           currencyCode: 'USD', | ||||
|           total: { | ||||
|             amount: maxCostUsd.toString(), | ||||
|             label: 'Total', | ||||
|             pending: true, | ||||
|             productUrl: `https://mempool.space/tx/${this.tx.txid}`, | ||||
|           } | ||||
|         }); | ||||
|         this.cashAppPay = await this.payments.cashAppPay(paymentRequest, { | ||||
|           redirectURL: 'https://my.website/checkout', | ||||
|           referenceId: `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`, | ||||
|         }); | ||||
|         await this.cashAppPay.attach('#cash-app-pay'); | ||||
|         this.showSpinner = false; | ||||
|          | ||||
|         const that = this; | ||||
|         this.cashAppPay.addEventListener('ontokenization', function (event) { | ||||
|           const { tokenResult, error } = event.detail; | ||||
|           if (error) { | ||||
|             this.error = error; | ||||
|           } else if (tokenResult.status === 'OK') { | ||||
|             that.hideCashApp = true; | ||||
| 
 | ||||
|             that.accelerationSubscription = that.servicesApiService.accelerateWithCashApp$( | ||||
|               that.tx.txid, | ||||
|               that.userBid, | ||||
|               tokenResult.token, | ||||
|               tokenResult.details.cashAppPay.cashtag, | ||||
|               tokenResult.details.cashAppPay.referenceId | ||||
|             ).subscribe({ | ||||
|               next: () => { | ||||
|                 that.audioService.playSound('ascend-chime-cartoon'); | ||||
|                 that.showSuccess = true; | ||||
|                 that.scrollToPreviewWithTimeout('successAlert', 'center'); | ||||
|                 that.estimateSubscription.unsubscribe(); | ||||
|               }, | ||||
|               error: (response) => { | ||||
|                 if (response.status === 403 && response.error === 'not_available') { | ||||
|                   that.error = 'waitlisted'; | ||||
|                 } else { | ||||
|                   that.error = response.error; | ||||
|                 } | ||||
|                 that.scrollToPreviewWithTimeout('mempoolError', 'center'); | ||||
|               } | ||||
|             }); | ||||
|           } | ||||
|         }); | ||||
|       } | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -7,7 +7,6 @@ import { EnterpriseService } from '../../services/enterprise.service'; | ||||
| import { NavigationService } from '../../services/navigation.service'; | ||||
| import { MenuComponent } from '../menu/menu.component'; | ||||
| import { StorageService } from '../../services/storage.service'; | ||||
| import { ApiService } from '../../services/api.service'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-master-page', | ||||
| @ -45,7 +44,6 @@ export class MasterPageComponent implements OnInit, OnDestroy { | ||||
|     private enterpriseService: EnterpriseService, | ||||
|     private navigationService: NavigationService, | ||||
|     private storageService: StorageService, | ||||
|     private apiService: ApiService, | ||||
|     private router: Router, | ||||
|   ) { } | ||||
| 
 | ||||
|  | ||||
| @ -141,6 +141,10 @@ export class ServicesApiServices { | ||||
|     return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate`, { txInput: txInput, userBid: userBid }); | ||||
|   } | ||||
| 
 | ||||
|   accelerateWithCashApp$(txInput: string, userBid: number, token: string, cashtag: string, referenceId: string) { | ||||
|     return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate/cashapp`, { txInput: txInput, userBid: userBid, token: token, cashtag: cashtag, referenceId: referenceId }); | ||||
|   } | ||||
| 
 | ||||
|   getAccelerations$(): Observable<Acceleration[]> { | ||||
|     return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations`); | ||||
|   } | ||||
| @ -160,4 +164,8 @@ export class ServicesApiServices { | ||||
|   getAccelerationStats$(): Observable<AccelerationStats> { | ||||
|     return this.httpClient.get<AccelerationStats>(`${SERVICES_API_PREFIX}/accelerator/accelerations/stats`); | ||||
|   } | ||||
| 
 | ||||
|   setupSquare$(): Observable<{squareAppId: string, squareLocationId: string}> { | ||||
|     return this.httpClient.get<{squareAppId: string, squareLocationId: string}>(`${SERVICES_API_PREFIX}/square/setup`); | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user