Merge pull request #5368 from mempool/nymkappa/google-pay
[accelerator] add support for Google Pay payment
This commit is contained in:
		
						commit
						b5d89b83fa
					
				| @ -389,21 +389,29 @@ | ||||
|                   </div> | ||||
|                 } | ||||
|               </div> | ||||
|               @if (canPayWithCashapp || canPayWithApplePay) { | ||||
|               @if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay) { | ||||
|                 <div class="col-sm text-center flex-grow-0  d-flex flex-column justify-content-center align-items-center"> | ||||
|                   <p class="text-nowrap">—<span i18n="or">OR</span>—</p> | ||||
|                 </div> | ||||
|               } | ||||
|             } | ||||
|             @if (canPayWithCashapp || canPayWithApplePay) { | ||||
|             @if (canPayWithCashapp || canPayWithApplePay || canPayWithGooglePay) { | ||||
|               <div class="col-sm text-center d-flex flex-column justify-content-center align-items-center"> | ||||
|                 <p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <app-fiat [value]="cost"></app-fiat> with</p> | ||||
|                 @if (canPayWithCashapp) { | ||||
|                   <img class="paymentMethod mx-2" style="width: 200px" src="/resources/cash-app.svg" height=55 (click)="moveToStep('cashapp')"> | ||||
|                 } | ||||
|                 @if (canPayWithApplePay) { | ||||
|                   @if (canPayWithCashapp) { <hr class="w-25 mt-2 mb-2"> } | ||||
|                   <img style="cursor: pointer;" src="/resources/apple-pay.svg" height=55 (click)="moveToStep('applepay')"> | ||||
|                   @if (canPayWithCashapp) { <span class="mt-1 mb-1"></span> } | ||||
|                   <div class="paymentMethod mx-2" style="width: 200px; height: 55px"> | ||||
|                   <img src="/resources/apple-pay.png" height=37 (click)="moveToStep('applepay')"> | ||||
|                   </div> | ||||
|                 } | ||||
|                 @if (canPayWithGooglePay) { | ||||
|                   @if (canPayWithCashapp || canPayWithApplePay) { <span class="mt-1 mb-1"></span> } | ||||
|                   <div class="paymentMethod mx-2" style="width: 200px; height: 55px"> | ||||
|                     <img src="/resources/google-pay.png" height=37 (click)="moveToStep('googlepay')"> | ||||
|                   </div> | ||||
|                 } | ||||
|               </div> | ||||
|             } | ||||
| @ -427,7 +435,7 @@ | ||||
|         <button type="button" class="mt-1 btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="moveToStep('summary')" i18n="go-back">Go back</button> | ||||
|       </div> | ||||
|     </div> | ||||
|   } @else if (step === 'cashapp' || step === 'applepay') { | ||||
|   } @else if (step === 'cashapp' || step === 'applepay' || step === 'googlepay') { | ||||
|     <!-- Show checkout page --> | ||||
|     <div class="row mb-md-1 text-center" id="confirm-title"> | ||||
|       <div class="col-sm" id="confirm-payment-title"> | ||||
| @ -443,7 +451,7 @@ | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     @if (step === 'cashapp' && !loadingCashapp || step === 'applepay' && !loadingApplePay) { | ||||
|     @if (step === 'cashapp' && !loadingCashapp || step === 'applepay' && !loadingApplePay || step === 'googlepay' && !loadingGooglePay) { | ||||
|       <div class="row text-center mt-1"> | ||||
|         <div class="col-sm"> | ||||
|           <div class="form-group w-100"> | ||||
| @ -463,11 +471,13 @@ | ||||
|       <div class="col-sm"> | ||||
|         <div class="form-group w-100"> | ||||
|           @if (step === 'applepay') { | ||||
|             <div id="apple-pay-button" class="apple-pay-button apple-pay-button-white" [style]="loadingApplePay ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div> | ||||
|             <div id="apple-pay-button" class="apple-pay-button apple-pay-button-black" style="height: 50px"  [style]="loadingApplePay ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div> | ||||
|           } @else if (step === 'cashapp') {           | ||||
|             <div id="cash-app-pay" class="d-inline-block" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div> | ||||
|             <div id="cash-app-pay" class="d-inline-block" style="height: 50px" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div> | ||||
|           } @else if (step === 'googlepay') { | ||||
|             <div id="google-pay-button" class="d-inline-block" style="height: 50px" [style]="loadingGooglePay ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div> | ||||
|           } | ||||
|           @if (loadingCashapp || loadingApplePay) { | ||||
|           @if (loadingCashapp || loadingApplePay || loadingGooglePay) { | ||||
|           <div display="d-flex flex-row justify-content-center"> | ||||
|             <span i18n="accelerator.loading-payment-method">Loading payment method...</span> | ||||
|             <div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div> | ||||
|  | ||||
| @ -1,7 +1,8 @@ | ||||
| /* eslint-disable no-console */ | ||||
| import { Component, OnInit, OnDestroy, Output, EventEmitter, Input, ChangeDetectorRef, SimpleChanges, HostListener } from '@angular/core'; | ||||
| import { Subscription, tap, of, catchError, Observable, switchMap } from 'rxjs'; | ||||
| import { ServicesApiServices } from '../../services/services-api.service'; | ||||
| import { md5, nextRoundNumber, insecureRandomUUID } from '../../shared/common.utils'; | ||||
| import { md5, insecureRandomUUID } from '../../shared/common.utils'; | ||||
| import { StateService } from '../../services/state.service'; | ||||
| import { AudioService } from '../../services/audio.service'; | ||||
| import { ETA, EtaService } from '../../services/eta.service'; | ||||
| @ -11,7 +12,7 @@ import { IAuth, AuthServiceMempool } from '../../services/auth.service'; | ||||
| import { EnterpriseService } from '../../services/enterprise.service'; | ||||
| import { ApiService } from '../../services/api.service'; | ||||
| 
 | ||||
| export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp'; | ||||
| export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp' | 'applePay' | 'googlePay'; | ||||
| 
 | ||||
| export type AccelerationEstimate = { | ||||
|   hasAccess: boolean; | ||||
| @ -24,7 +25,7 @@ export type AccelerationEstimate = { | ||||
|   mempoolBaseFee: number; | ||||
|   vsizeFee: number; | ||||
|   pools: number[]; | ||||
|   availablePaymentMethods: {[method: string]: {min: number, max: number}}; | ||||
|   availablePaymentMethods: Record<PaymentMethod, {min: number, max: number}>; | ||||
|   unavailable?: boolean; | ||||
|   options: { // recommended bid options
 | ||||
|     fee: number; // recommended userBid in sats
 | ||||
| @ -47,7 +48,7 @@ export const MIN_BID_RATIO = 1; | ||||
| export const DEFAULT_BID_RATIO = 2; | ||||
| export const MAX_BID_RATIO = 4; | ||||
| 
 | ||||
| type CheckoutStep = 'quote' | 'summary' | 'checkout' | 'cashapp' | 'applepay' | 'processing' | 'paid' | 'success'; | ||||
| type CheckoutStep = 'quote' | 'summary' | 'checkout' | 'cashapp' | 'applepay' | 'googlepay' | 'processing' | 'paid' | 'success'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-accelerate-checkout', | ||||
| @ -62,6 +63,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|   @Input() scrollEvent: boolean; | ||||
|   @Input() cashappEnabled: boolean = true; | ||||
|   @Input() applePayEnabled: boolean = false; | ||||
|   @Input() googlePayEnabled: boolean = true; | ||||
|   @Input() advancedEnabled: boolean = false; | ||||
|   @Input() forceMobile: boolean = false; | ||||
|   @Input() showDetails: boolean = false; | ||||
| @ -83,14 +85,12 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
| 
 | ||||
|   private _step: CheckoutStep = 'summary'; | ||||
|   simpleMode: boolean = true; | ||||
|   paymentMethod: 'cashapp' | 'btcpay'; | ||||
|   timeoutTimer: any; | ||||
| 
 | ||||
|   authSubscription$: Subscription; | ||||
|   auth: IAuth | null = null; | ||||
| 
 | ||||
|   // accelerator stuff
 | ||||
|   square: { appId: string, locationId: string}; | ||||
|   accelerationUUID: string; | ||||
|   accelerationSubscription: Subscription; | ||||
|   difficultySubscription: Subscription; | ||||
| @ -112,13 +112,13 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|   // square
 | ||||
|   loadingCashapp = false; | ||||
|   loadingApplePay = false; | ||||
|   cashappError = false; | ||||
|   cashappSubmit: any; | ||||
|   loadingGooglePay = false; | ||||
|   payments: any; | ||||
|   cashAppPay: any; | ||||
|   applePay: any; | ||||
|   googlePay: any; | ||||
|   conversionsSubscription: Subscription; | ||||
|   conversions: any; | ||||
|   conversions: Record<string, number>; | ||||
| 
 | ||||
|   // btcpay
 | ||||
|   loadingBtcpayInvoice = false; | ||||
| @ -137,13 +137,13 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|     this.accelerationUUID = insecureRandomUUID(); | ||||
| 
 | ||||
|     // Check if Apple Pay available
 | ||||
|     // @ts-ignore https://developer.apple.com/documentation/apple_pay_on_the_web/apple_pay_js_api/checking_for_apple_pay_availability#overview
 | ||||
|     if (window.ApplePaySession) { | ||||
|     // https://developer.apple.com/documentation/apple_pay_on_the_web/apple_pay_js_api/checking_for_apple_pay_availability#overview
 | ||||
|     if (window['ApplePaySession']) { | ||||
|       this.applePayEnabled = true; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit() { | ||||
|   ngOnInit(): void { | ||||
|     this.authSubscription$ = this.authService.getAuth$().subscribe((auth) => { | ||||
|       if (this.auth?.user?.userId !== auth?.user?.userId) { | ||||
|         this.auth = auth; | ||||
| @ -168,13 +168,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|       this.moveToStep('summary'); | ||||
|     } | ||||
| 
 | ||||
|     this.servicesApiService.setupSquare$().subscribe(ids => { | ||||
|       this.square = { | ||||
|         appId: ids.squareAppId, | ||||
|         locationId: ids.squareLocationId | ||||
|       }; | ||||
|     }); | ||||
| 
 | ||||
|     this.conversionsSubscription = this.stateService.conversions$.subscribe( | ||||
|       async (conversions) => { | ||||
|         this.conversions = conversions; | ||||
| @ -182,7 +175,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   ngOnDestroy() { | ||||
|   ngOnDestroy(): void { | ||||
|     if (this.estimateSubscription) { | ||||
|       this.estimateSubscription.unsubscribe(); | ||||
|     } | ||||
| @ -202,7 +195,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   moveToStep(step: CheckoutStep) { | ||||
|   moveToStep(step: CheckoutStep): void { | ||||
|     this._step = step; | ||||
|     if (this.timeoutTimer) { | ||||
|       clearTimeout(this.timeoutTimer); | ||||
| @ -228,13 +221,18 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|       this.insertSquare(); | ||||
|       this.setupSquare(); | ||||
|       this.scrollToElementWithTimeout('confirm-title', 'center', 100); | ||||
|     } else if (this._step === 'googlepay' && this.googlePayEnabled) { | ||||
|       this.loadingGooglePay = true; | ||||
|       this.insertSquare(); | ||||
|       this.setupSquare(); | ||||
|       this.scrollToElementWithTimeout('confirm-title', 'center', 100); | ||||
|     } else if (this._step === 'paid') { | ||||
|       this.timePaid = Date.now(); | ||||
|       this.timeoutTimer = setTimeout(() => { | ||||
|         if (this.step === 'paid') { | ||||
|           this.accelerateError = 'internal_server_error'; | ||||
|         } | ||||
|       }, 120000) | ||||
|       }, 120000); | ||||
|     } | ||||
|     this.hasDetails.emit(this._step === 'quote'); | ||||
|   } | ||||
| @ -252,7 +250,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|       this.scrollToElement(id, position); | ||||
|     }, timeout); | ||||
|   } | ||||
|   scrollToElement(id: string, position: ScrollLogicalPosition) { | ||||
|   scrollToElement(id: string, position: ScrollLogicalPosition): void { | ||||
|     const acceleratePreviewAnchor = document.getElementById(id); | ||||
|     if (acceleratePreviewAnchor) { | ||||
|       this.cd.markForCheck(); | ||||
| @ -267,7 +265,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|   /** | ||||
|    * Accelerator | ||||
|    */ | ||||
|   fetchEstimate() { | ||||
|   fetchEstimate(): void { | ||||
|     if (this.estimateSubscription) { | ||||
|       this.estimateSubscription.unsubscribe(); | ||||
|     } | ||||
| @ -331,7 +329,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|         } | ||||
|       }), | ||||
| 
 | ||||
|       catchError((response) => { | ||||
|       catchError(() => { | ||||
|         this.estimate = undefined; | ||||
|         this.quoteError = `cannot_accelerate_tx`; | ||||
|         this.estimateSubscription.unsubscribe(); | ||||
| @ -402,8 +400,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|    * Square | ||||
|    */ | ||||
|   insertSquare(): void { | ||||
|     //@ts-ignore
 | ||||
|     if (window.Square) { | ||||
|     if (window['Square']) { | ||||
|       return; | ||||
|     } | ||||
|     let statsUrl = 'https://sandbox.web.squarecdn.com/v1/square.js'; | ||||
| @ -415,19 +412,17 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|       statsUrl = 'https://web.squarecdn.com/v1/square.js'; | ||||
|     } | ||||
| 
 | ||||
|     (function() { | ||||
|     (function(): void { | ||||
|       const d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; | ||||
|       // @ts-ignore
 | ||||
|       g.type='text/javascript'; g.src=statsUrl; s.parentNode.insertBefore(g, s); | ||||
|     })(); | ||||
|   } | ||||
|   setupSquare() { | ||||
|     const init = () => { | ||||
|   setupSquare(): void { | ||||
|     const init = (): void => { | ||||
|       this.initSquare(); | ||||
|     }; | ||||
| 
 | ||||
|     //@ts-ignore
 | ||||
|     if (!window.Square) { | ||||
|     if (!window['Square']) { | ||||
|       console.debug('Square.js failed to load properly. Retrying in 1 second.'); | ||||
|       setTimeout(init, 1000); | ||||
|     } else { | ||||
| @ -436,25 +431,33 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|   } | ||||
|   async initSquare(): Promise<void> { | ||||
|     try { | ||||
|       //@ts-ignore
 | ||||
|       this.payments = window.Square.payments(this.square.appId, this.square.locationId) | ||||
|       const urlParams = new URLSearchParams(window.location.search); | ||||
|       if (this._step === 'cashapp' || urlParams.get('cash_request_id')) { | ||||
|         await this.requestCashAppPayment(); | ||||
|       } else if (this._step === 'applepay') { | ||||
|         await this.requestApplePayPayment(); | ||||
|       } | ||||
|       this.servicesApiService.setupSquare$().subscribe({ | ||||
|         next: async (ids) => { | ||||
|           this.payments = window['Square'].payments(ids.squareAppId, ids.squareLocationId); | ||||
|           const urlParams = new URLSearchParams(window.location.search); | ||||
|           if (this._step === 'cashapp' || urlParams.get('cash_request_id')) { | ||||
|             await this.requestCashAppPayment(); | ||||
|           } else if (this._step === 'applepay') { | ||||
|             await this.requestApplePayPayment(); | ||||
|           } else if (this._step === 'googlepay') { | ||||
|             await this.requestGooglePayPayment(); | ||||
|           } | ||||
|         }, | ||||
|         error: () => { | ||||
|           console.debug('Error loading Square Payments'); | ||||
|           this.accelerateError = 'cannot_setup_square'; | ||||
|         } | ||||
|       }); | ||||
|     } catch (e) { | ||||
|       console.debug('Error loading Square Payments', e); | ||||
|       this.cashappError = true; | ||||
|       return; | ||||
|       this.accelerateError = 'cannot_setup_square'; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * APPLE PAY | ||||
|    */ | ||||
|   async requestApplePayPayment() { | ||||
|   async requestApplePayPayment(): Promise<void> { | ||||
|     if (this.conversionsSubscription) { | ||||
|       this.conversionsSubscription.unsubscribe(); | ||||
|     } | ||||
| @ -505,6 +508,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|                 this.accelerationUUID | ||||
|               ).subscribe({ | ||||
|                 next: () => { | ||||
|                   this.apiService.logAccelerationRequest$(this.tx.txid).subscribe(); | ||||
|                   this.audioService.playSound('ascend-chime-cartoon'); | ||||
|                   if (this.applePay) { | ||||
|                     this.applePay.destroy(); | ||||
| @ -541,10 +545,97 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * GOOGLE PAY | ||||
|    */ | ||||
|   async requestGooglePayPayment(): Promise<void> { | ||||
|     if (this.conversionsSubscription) { | ||||
|       this.conversionsSubscription.unsubscribe(); | ||||
|     } | ||||
| 
 | ||||
|     this.conversionsSubscription = this.stateService.conversions$.subscribe( | ||||
|       async (conversions) => { | ||||
|         this.conversions = conversions; | ||||
|         if (this.googlePay) { | ||||
|           this.googlePay.destroy(); | ||||
|         } | ||||
| 
 | ||||
|         const costUSD = this.cost / 100_000_000 * conversions.USD; | ||||
|         const paymentRequest = this.payments.paymentRequest({ | ||||
|           countryCode: 'US', | ||||
|           currencyCode: 'USD', | ||||
|           total: { | ||||
|             amount: costUSD.toFixed(2), | ||||
|             label: 'Total' | ||||
|           } | ||||
|         }); | ||||
|         this.googlePay = await this.payments.googlePay(paymentRequest , { | ||||
|           referenceId: `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`, | ||||
|         }); | ||||
| 
 | ||||
|         await this.googlePay.attach(`#google-pay-button`, { | ||||
|           buttonType: 'pay', | ||||
|           buttonSizeMode: 'fill', | ||||
|         }); | ||||
|         this.loadingGooglePay = false; | ||||
| 
 | ||||
|         document.getElementById('google-pay-button').addEventListener('click', async event => { | ||||
|           event.preventDefault(); | ||||
|           const tokenResult = await this.googlePay.tokenize(); | ||||
|           if (tokenResult?.status === 'OK') { | ||||
|             const card = tokenResult.details?.card; | ||||
|             if (!card || !card.brand || !card.expMonth || !card.expYear || !card.last4) { | ||||
|               console.error(`Cannot retreive payment card details`); | ||||
|               this.accelerateError = 'apple_pay_no_card_details'; | ||||
|               return; | ||||
|             } | ||||
|             const cardTag = md5(`${card.brand}${card.expMonth}${card.expYear}${card.last4}`.toLowerCase()); | ||||
|             this.servicesApiService.accelerateWithGooglePay$( | ||||
|               this.tx.txid, | ||||
|               tokenResult.token, | ||||
|               cardTag, | ||||
|               `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`, | ||||
|               this.accelerationUUID | ||||
|             ).subscribe({ | ||||
|               next: () => { | ||||
|                 this.apiService.logAccelerationRequest$(this.tx.txid).subscribe(); | ||||
|                 this.audioService.playSound('ascend-chime-cartoon'); | ||||
|                 if (this.googlePay) { | ||||
|                   this.googlePay.destroy(); | ||||
|                 } | ||||
|                 setTimeout(() => { | ||||
|                   this.moveToStep('paid'); | ||||
|                 }, 1000); | ||||
|               }, | ||||
|               error: (response) => { | ||||
|                 this.accelerateError = response.error; | ||||
|                 if (!(response.status === 403 && response.error === 'not_available')) { | ||||
|                   setTimeout(() => { | ||||
|                     // Reset everything by reloading the page :D, can be improved
 | ||||
|                     const urlParams = new URLSearchParams(window.location.search); | ||||
|                     window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``)); | ||||
|                   }, 3000); | ||||
|                 } | ||||
|               } | ||||
|             }); | ||||
|           } else { | ||||
|             let errorMessage = `Tokenization failed with status: ${tokenResult.status}`; | ||||
|             if (tokenResult.errors) { | ||||
|               errorMessage += ` and errors: ${JSON.stringify( | ||||
|                 tokenResult.errors, | ||||
|               )}`;
 | ||||
|             } | ||||
|             throw new Error(errorMessage); | ||||
|           } | ||||
|         }); | ||||
|       } | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * CASHAPP | ||||
|    */ | ||||
|   async requestCashAppPayment() { | ||||
|   async requestCashAppPayment(): Promise<void> { | ||||
|     if (this.conversionsSubscription) { | ||||
|       this.conversionsSubscription.unsubscribe(); | ||||
|     } | ||||
| @ -566,18 +657,14 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|             label: 'Total', | ||||
|             pending: true, | ||||
|             productUrl: `${redirectHostname}/tracker/${this.tx.txid}`, | ||||
|           }, | ||||
|           button: { shape: 'semiround', size: 'small', theme: 'light'} | ||||
|           } | ||||
|         }); | ||||
|         this.cashAppPay = await this.payments.cashAppPay(paymentRequest, { | ||||
|           redirectURL: `${redirectHostname}/tracker/${this.tx.txid}`, | ||||
|           referenceId: `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`, | ||||
|           button: { shape: 'semiround', size: 'small', theme: 'light'} | ||||
|           referenceId: `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}` | ||||
|         }); | ||||
| 
 | ||||
|         if (this.step === 'cashapp') { | ||||
|           await this.cashAppPay.attach(`#cash-app-pay`, { theme: 'light', size: 'small', shape: 'semiround' }) | ||||
|         } | ||||
|         await this.cashAppPay.attach(`#cash-app-pay`, { theme: 'dark' }); | ||||
|         this.loadingCashapp = false; | ||||
| 
 | ||||
|         this.cashAppPay.addEventListener('ontokenization', event => { | ||||
| @ -626,7 +713,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|   /** | ||||
|    * BTCPay | ||||
|    */ | ||||
|   async requestBTCPayInvoice() { | ||||
|   async requestBTCPayInvoice(): Promise<void> { | ||||
|     this.servicesApiService.generateBTCPayAcceleratorInvoice$(this.tx.txid, this.userBid).pipe( | ||||
|       switchMap(response => { | ||||
|         return this.servicesApiService.retreiveInvoice$(response.btcpayInvoiceId); | ||||
| @ -656,53 +743,60 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|   /** | ||||
|    * UI events | ||||
|    */ | ||||
|   selectedOptionChanged(event) { | ||||
|   selectedOptionChanged(event): void { | ||||
|     this.selectedOption = event.target.id; | ||||
|   } | ||||
| 
 | ||||
|   get step() { | ||||
|   get step(): CheckoutStep { | ||||
|     return this._step; | ||||
|   } | ||||
| 
 | ||||
|   get paymentMethods() { | ||||
|     return Object.keys(this.estimate?.availablePaymentMethods || {}); | ||||
|   get paymentMethods(): PaymentMethod[] { | ||||
|     return Object.keys(this.estimate?.availablePaymentMethods || {}) as PaymentMethod[]; | ||||
|   } | ||||
| 
 | ||||
|   get couldPayWithBitcoin() { | ||||
|   get couldPayWithBitcoin(): boolean { | ||||
|     return !!this.estimate?.availablePaymentMethods?.bitcoin; | ||||
|   } | ||||
| 
 | ||||
|   get couldPayWithCashapp() { | ||||
|   get couldPayWithCashapp(): boolean { | ||||
|     if (!this.cashappEnabled) { | ||||
|       return false; | ||||
|     } | ||||
|     return !!this.estimate?.availablePaymentMethods?.cashapp; | ||||
|   } | ||||
| 
 | ||||
|   get couldPayWithApplePay() { | ||||
|   get couldPayWithApplePay(): boolean { | ||||
|     if (!this.applePayEnabled) { | ||||
|       return false; | ||||
|     } | ||||
|     return !!this.estimate?.availablePaymentMethods?.applePay; | ||||
|   } | ||||
| 
 | ||||
|   get couldPayWithBalance() { | ||||
|   get couldPayWithGooglePay(): boolean { | ||||
|     if (!this.googlePayEnabled) { | ||||
|       return false; | ||||
|     } | ||||
|     return !!this.estimate?.availablePaymentMethods?.googlePay; | ||||
|   } | ||||
| 
 | ||||
|   get couldPayWithBalance(): boolean { | ||||
|     if (!this.hasAccessToBalanceMode) { | ||||
|       return false; | ||||
|     } | ||||
|     return !!this.estimate?.availablePaymentMethods?.balance; | ||||
|   } | ||||
| 
 | ||||
|   get couldPay() { | ||||
|     return this.couldPayWithBalance || this.couldPayWithBitcoin || this.couldPayWithCashapp || this.couldPayWithApplePay; | ||||
|   get couldPay(): boolean { | ||||
|     return this.couldPayWithBalance || this.couldPayWithBitcoin || this.couldPayWithCashapp || this.couldPayWithApplePay || this.couldPayWithGooglePay; | ||||
|   } | ||||
| 
 | ||||
|   get canPayWithBitcoin() { | ||||
|   get canPayWithBitcoin(): boolean { | ||||
|     const paymentMethod = this.estimate?.availablePaymentMethods?.bitcoin; | ||||
|     return paymentMethod && this.cost >= paymentMethod.min && this.cost <= paymentMethod.max; | ||||
|   } | ||||
| 
 | ||||
|   get canPayWithCashapp() { | ||||
|   get canPayWithCashapp(): boolean { | ||||
|     if (!this.cashappEnabled || !this.conversions) { | ||||
|       return false; | ||||
|     } | ||||
| @ -718,7 +812,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   get canPayWithApplePay() { | ||||
|   get canPayWithApplePay(): boolean { | ||||
|     if (!this.applePayEnabled || !this.conversions) { | ||||
|       return false; | ||||
|     } | ||||
| @ -734,7 +828,23 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   get canPayWithBalance() { | ||||
|   get canPayWithGooglePay(): boolean { | ||||
|     if (!this.googlePayEnabled || !this.conversions) { | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|     const paymentMethod = this.estimate?.availablePaymentMethods?.googlePay; | ||||
|     if (paymentMethod) { | ||||
|       const costUSD = (this.cost / 100_000_000 * this.conversions.USD); | ||||
|       if (costUSD >= paymentMethod.min && costUSD <= paymentMethod.max) { | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   get canPayWithBalance(): boolean { | ||||
|     if (!this.hasAccessToBalanceMode) { | ||||
|       return false; | ||||
|     } | ||||
| @ -742,11 +852,11 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | ||||
|     return paymentMethod && this.cost >= paymentMethod.min && this.cost <= paymentMethod.max && this.cost <= this.estimate?.userBalance; | ||||
|   } | ||||
| 
 | ||||
|   get canPay() { | ||||
|     return this.canPayWithBalance || this.canPayWithBitcoin || this.canPayWithCashapp || this.canPayWithApplePay; | ||||
|   get canPay(): boolean { | ||||
|     return this.canPayWithBalance || this.canPayWithBitcoin || this.canPayWithCashapp || this.canPayWithApplePay || this.canPayWithGooglePay; | ||||
|   } | ||||
| 
 | ||||
|   get hasAccessToBalanceMode() { | ||||
|   get hasAccessToBalanceMode(): boolean { | ||||
|     return this.isLoggedIn() && this.estimate?.hasAccess; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -7,7 +7,7 @@ export interface WebsocketResponse { | ||||
|   backend?: 'esplora' | 'electrum' | 'none'; | ||||
|   block?: BlockExtended; | ||||
|   blocks?: BlockExtended[]; | ||||
|   conversions?: any; | ||||
|   conversions?: Record<string, number>; | ||||
|   txConfirmed?: string; | ||||
|   historicalDate?: string; | ||||
|   mempoolInfo?: MempoolInfo; | ||||
|  | ||||
| @ -144,6 +144,10 @@ export class ServicesApiServices { | ||||
|     return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/applePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, accelerationUUID: accelerationUUID }); | ||||
|   } | ||||
| 
 | ||||
|   accelerateWithGooglePay$(txInput: string, token: string, cardTag: string, referenceId: string, accelerationUUID: string) { | ||||
|     return this.httpClient.post<any>(`${this.stateService.env.SERVICES_API}/accelerator/accelerate/googlePay`, { txInput: txInput, cardTag: cardTag, token: token, referenceId: referenceId, accelerationUUID: accelerationUUID }); | ||||
|   } | ||||
| 
 | ||||
|   getAccelerations$(): Observable<Acceleration[]> { | ||||
|     return this.httpClient.get<Acceleration[]>(`${this.stateService.env.SERVICES_API}/accelerator/accelerations`); | ||||
|   } | ||||
|  | ||||
| @ -138,7 +138,7 @@ export class StateService { | ||||
|   blocksSubject$ = new BehaviorSubject<BlockExtended[]>([]); | ||||
|   blocks$: Observable<BlockExtended[]>; | ||||
|   transactions$ = new BehaviorSubject<TransactionStripped[]>(null); | ||||
|   conversions$ = new ReplaySubject<any>(1); | ||||
|   conversions$ = new ReplaySubject<Record<string, number>>(1); | ||||
|   bsqPrice$ = new ReplaySubject<number>(1); | ||||
|   mempoolInfo$ = new ReplaySubject<MempoolInfo>(1); | ||||
|   mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1); | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								frontend/src/resources/apple-pay.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								frontend/src/resources/apple-pay.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 46 KiB | 
| @ -1,84 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||
| <svg version="1.1" id="Artwork" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||
| 	 width="165.52107px" height="105.9651px" viewBox="0 0 165.52107 105.9651" enable-background="new 0 0 165.52107 105.9651" | ||||
| 	 xml:space="preserve"> | ||||
| <g> | ||||
| 	<path id="XMLID_4_" d="M150.69807,0H14.82318c-0.5659,0-1.1328,0-1.69769,0.0033c-0.47751,0.0034-0.95391,0.0087-1.43031,0.0217 | ||||
| 		c-1.039,0.0281-2.0869,0.0894-3.1129,0.2738c-1.0424,0.1876-2.0124,0.4936-2.9587,0.9754 | ||||
| 		c-0.9303,0.4731-1.782,1.0919-2.52009,1.8303c-0.73841,0.7384-1.35721,1.5887-1.83021,2.52 | ||||
| 		c-0.4819,0.9463-0.7881,1.9166-0.9744,2.9598c-0.18539,1.0263-0.2471,2.074-0.2751,3.1119 | ||||
| 		c-0.0128,0.4764-0.01829,0.9528-0.0214,1.4291c-0.0033,0.5661-0.0022,1.1318-0.0022,1.6989V91.142 | ||||
| 		c0,0.5671-0.0011,1.13181,0.0022,1.69901c0.00311,0.4763,0.0086,0.9527,0.0214,1.4291 | ||||
| 		c0.028,1.03699,0.08971,2.08469,0.2751,3.11069c0.1863,1.0436,0.4925,2.0135,0.9744,2.9599 | ||||
| 		c0.473,0.9313,1.0918,1.7827,1.83021,2.52c0.73809,0.7396,1.58979,1.3583,2.52009,1.8302 | ||||
| 		c0.9463,0.4831,1.9163,0.7892,2.9587,0.9767c1.026,0.1832,2.0739,0.2456,3.1129,0.2737c0.4764,0.0108,0.9528,0.0172,1.43031,0.0194 | ||||
| 		c0.56489,0.0044,1.13179,0.0044,1.69769,0.0044h135.87489c0.5649,0,1.13181,0,1.69659-0.0044 | ||||
| 		c0.47641-0.0022,0.95282-0.0086,1.4314-0.0194c1.0368-0.0281,2.0845-0.0905,3.11301-0.2737 | ||||
| 		c1.041-0.1875,2.0112-0.4936,2.9576-0.9767c0.9313-0.4719,1.7805-1.0906,2.52011-1.8302c0.7372-0.7373,1.35599-1.5887,1.8302-2.52 | ||||
| 		c0.48299-0.9464,0.78889-1.9163,0.97429-2.9599c0.1855-1.026,0.2457-2.0737,0.2738-3.11069 | ||||
| 		c0.013-0.4764,0.01941-0.9528,0.02161-1.4291c0.00439-0.5672,0.00439-1.1319,0.00439-1.69901V14.8242 | ||||
| 		c0-0.5671,0-1.1328-0.00439-1.6989c-0.0022-0.4763-0.00861-0.9527-0.02161-1.4291c-0.02811-1.0379-0.0883-2.0856-0.2738-3.1119 | ||||
| 		c-0.18539-1.0432-0.4913-2.0135-0.97429-2.9598c-0.47421-0.9313-1.093-1.7816-1.8302-2.52 | ||||
| 		c-0.73961-0.7384-1.58881-1.3572-2.52011-1.8303c-0.9464-0.4818-1.9166-0.7878-2.9576-0.9754 | ||||
| 		c-1.0285-0.1844-2.0762-0.2457-3.11301-0.2738c-0.47858-0.013-0.95499-0.0183-1.4314-0.0217C151.82988,0,151.26297,0,150.69807,0 | ||||
| 		L150.69807,0z"/> | ||||
| 	<path id="XMLID_3_" fill="#FFFFFF" d="M150.69807,3.532l1.67149,0.0032c0.4528,0.0032,0.90561,0.0081,1.36092,0.0205 | ||||
| 		c0.79201,0.0214,1.71849,0.0643,2.58209,0.2191c0.7507,0.1352,1.38029,0.3408,1.9845,0.6484 | ||||
| 		c0.5965,0.3031,1.14301,0.7003,1.62019,1.1768c0.479,0.4797,0.87671,1.0271,1.18381,1.6302 | ||||
| 		c0.30589,0.5995,0.51019,1.2261,0.64459,1.9823c0.1544,0.8542,0.1971,1.7832,0.21881,2.5801 | ||||
| 		c0.01219,0.4498,0.01819,0.8996,0.0204,1.3601c0.00429,0.5569,0.0042,1.1135,0.0042,1.6715V91.142 | ||||
| 		c0,0.558,0.00009,1.1136-0.0043,1.6824c-0.00211,0.4497-0.0081,0.8995-0.0204,1.3501c-0.02161,0.7957-0.0643,1.7242-0.2206,2.5885 | ||||
| 		c-0.13251,0.7458-0.3367,1.3725-0.64429,1.975c-0.30621,0.6016-0.70331,1.1484-1.18022,1.6251 | ||||
| 		c-0.47989,0.48-1.0246,0.876-1.62819,1.1819c-0.5997,0.3061-1.22821,0.51151-1.97151,0.6453 | ||||
| 		c-0.88109,0.157-1.84639,0.2002-2.57339,0.2199c-0.4574,0.0103-0.9126,0.01649-1.37889,0.0187 | ||||
| 		c-0.55571,0.0043-1.1134,0.0042-1.6692,0.0042H14.82318c-0.0074,0-0.0146,0-0.0221,0c-0.5494,0-1.0999,0-1.6593-0.0043 | ||||
| 		c-0.4561-0.00211-0.9112-0.0082-1.3512-0.0182c-0.7436-0.0201-1.7095-0.0632-2.5834-0.2193 | ||||
| 		c-0.74969-0.1348-1.3782-0.3402-1.9858-0.6503c-0.59789-0.3032-1.1422-0.6988-1.6223-1.1797 | ||||
| 		c-0.4764-0.4756-0.8723-1.0207-1.1784-1.6232c-0.3064-0.6019-0.5114-1.2305-0.64619-1.9852 | ||||
| 		c-0.15581-0.8626-0.19861-1.7874-0.22-2.5777c-0.01221-0.4525-0.01731-0.9049-0.02021-1.3547l-0.0022-1.3279l0.0001-0.3506V14.8242 | ||||
| 		l-0.0001-0.3506l0.0021-1.3251c0.003-0.4525,0.0081-0.9049,0.02031-1.357c0.02139-0.7911,0.06419-1.7163,0.22129-2.5861 | ||||
| 		c0.1336-0.7479,0.3385-1.3765,0.6465-1.9814c0.3037-0.5979,0.7003-1.1437,1.17921-1.6225 | ||||
| 		c0.477-0.4772,1.02309-0.8739,1.62479-1.1799c0.6011-0.3061,1.2308-0.5116,1.9805-0.6465c0.8638-0.1552,1.7909-0.198,2.5849-0.2195 | ||||
| 		c0.4526-0.0123,0.9052-0.0172,1.3544-0.0203l1.6771-0.0033H150.69807"/> | ||||
| 	<g> | ||||
| 		<g> | ||||
| 			<path d="M45.1862,35.64053c1.41724-1.77266,2.37897-4.15282,2.12532-6.58506c-2.07464,0.10316-4.60634,1.36871-6.07207,3.14276 | ||||
| 				c-1.31607,1.5192-2.4809,3.99902-2.17723,6.3293C41.39111,38.72954,43.71785,37.36345,45.1862,35.64053"/> | ||||
| 			<path d="M47.28506,38.98252c-3.38211-0.20146-6.25773,1.91951-7.87286,1.91951c-1.61602,0-4.08931-1.81799-6.76438-1.76899 | ||||
| 				c-3.48177,0.05114-6.71245,2.01976-8.4793,5.15079c-3.63411,6.2636-0.95904,15.55471,2.57494,20.65606 | ||||
| 				c1.71618,2.5238,3.78447,5.30269,6.50976,5.20287c2.57494-0.10104,3.58421-1.66732,6.71416-1.66732 | ||||
| 				c3.12765,0,4.03679,1.66732,6.76252,1.61681c2.82665-0.05054,4.59381-2.52506,6.30997-5.05132 | ||||
| 				c1.96878-2.877,2.77473-5.65498,2.82542-5.80748c-0.0507-0.05051-5.45058-2.12204-5.50065-8.33358 | ||||
| 				c-0.05098-5.20101,4.23951-7.6749,4.44144-7.82832C52.3832,39.4881,48.5975,39.08404,47.28506,38.98252"/> | ||||
| 		</g> | ||||
| 		<g> | ||||
| 			<path d="M76.73385,31.94381c7.35096,0,12.4697,5.06708,12.4697,12.44437c0,7.40363-5.22407,12.49704-12.65403,12.49704h-8.13892 | ||||
| 				v12.94318h-5.88037v-37.8846H76.73385z M68.41059,51.9493h6.74732c5.11975,0,8.0336-2.75636,8.0336-7.53479 | ||||
| 				c0-4.77792-2.91385-7.50845-8.00727-7.50845h-6.77365V51.9493z"/> | ||||
| 			<path d="M90.73997,61.97864c0-4.8311,3.70182-7.79761,10.26583-8.16526l7.56061-0.44614v-2.12639 | ||||
| 				c0-3.07185-2.07423-4.90959-5.53905-4.90959c-3.28251,0-5.33041,1.57492-5.82871,4.04313h-5.35574 | ||||
| 				c0.31499-4.98859,4.56777-8.66407,11.3941-8.66407c6.69466,0,10.97377,3.54432,10.97377,9.08388v19.03421h-5.43472v-4.54194 | ||||
| 				h-0.13065c-1.60125,3.07185-5.09341,5.01441-8.71623,5.01441C94.52078,70.30088,90.73997,66.94038,90.73997,61.97864z | ||||
| 				 M108.56641,59.4846v-2.17905l-6.8,0.41981c-3.38683,0.23649-5.30306,1.73291-5.30306,4.09579 | ||||
| 				c0,2.41504,1.99523,3.99046,5.04075,3.99046C105.46823,65.81161,108.56641,63.08108,108.56641,59.4846z"/> | ||||
| 			<path d="M119.34167,79.9889v-4.5946c0.4193,0.10483,1.36425,0.10483,1.83723,0.10483c2.6252,0,4.04313-1.10245,4.90908-3.9378 | ||||
| 				c0-0.05267,0.49931-1.68025,0.49931-1.70658l-9.97616-27.64562h6.14268l6.98432,22.47371h0.10432l6.98433-22.47371h5.9857 | ||||
| 				l-10.34483,29.06304c-2.36186,6.69517-5.0924,8.84789-10.81577,8.84789C121.17891,80.12006,119.76098,80.06739,119.34167,79.9889 | ||||
| 				z"/> | ||||
| 		</g> | ||||
| 	</g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| <g> | ||||
| </g> | ||||
| </svg> | ||||
| Before Width: | Height: | Size: 6.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								frontend/src/resources/google-pay.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								frontend/src/resources/google-pay.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 30 KiB | 
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user