import { AfterViewInit , ChangeDetectionStrategy , ChangeDetectorRef , Component , ElementRef , EventEmitter , Input , NgZone , OnChanges , OnDestroy , Output , SimpleChanges , ViewChild , } from '@angular/core' ; import { Subject } from 'rxjs' ; import { ICancelCallbackData , IClientAuthorizeCallbackData , ICreateOrderCallbackActions , IInitCallbackData , IOnApproveCallbackActions , IOnApproveCallbackData , IOnClickCallbackActions , IOnInitCallbackActions , IOnShippingChangeActions , IOnShippingChangeData , IPayPalConfig , ICreateSubscriptionCallbackActions , ICreateSubscriptionCallbackData , } from '../models/paypal-models' ; import { PayPalScriptService } from '../services/paypal-script.service' ; @ Component ( { changeDetection : ChangeDetectionStrategy . OnPush , selector : 'ngx-paypal' , template : `
    <div #payPalButtonContainer [id]="payPalButtonContainerId"></div>
    ` } ) export class NgxPaypalComponent implements OnChanges , OnDestroy , AfterViewInit { /**
     * Configuration for paypal.
     */ @ Input ( ) config ? : IPayPalConfig ; /**
     * If enabled, paypal SDK script will be loaded. Useful if you want to have multiple PayPal components on the same page
     * sharing base configuration. In such a case only a single component may register script.
     */ @ Input ( ) registerScript : boolean = true ; /**
     * Emitted when paypal script is loaded
     */ @ Output ( ) scriptLoaded = new EventEmitter < any > ( ) ; /**
     * Id of the element where PayPal button will be rendered
     */ public payPalButtonContainerId ? : string ; private readonly ngUnsubscribe : Subject < void > = new Subject < void > ( ) ; private payPalButtonContainerElem ? : ElementRef ; @ ViewChild ( 'payPalButtonContainer' , { static : false } ) set payPalButtonContainer ( content ) { this . payPalButtonContainerElem = content ; } /**
     * Flag that indicates if paypal should be initialized (required for handling script load events and availability of DOM element)
     */ private initializePayPal : boolean = true ; /**
     * Reference to PayPal global API
     */ private payPal : any ; constructor ( private paypalScriptService , private cdr , private ngZone ) { } ngOnChanges ( changes ) { if ( ! this . payPalButtonContainerId ) { this . payPalButtonContainerId = this . generateElementId ( ) ; } const config = this . config ; if ( changes . config . isFirstChange ( ) ) { if ( config && this . registerScript ) { this . initPayPalScript ( config , ( payPal ) => { this . payPal = payPal ; this . doPayPalCheck ( ) ; } ) ; } } if ( ! changes . config . isFirstChange ( ) ) { this . reinitialize ( config ) ; } } ngOnDestroy ( ) { this . paypalScriptService . destroyPayPalScript ( ) ; this . ngUnsubscribe . next ( ) ; this . ngUnsubscribe . complete ( ) ; } ngAfterViewInit ( ) { this . doPayPalCheck ( ) ; } customInit ( payPal ) { this . payPal = payPal ; this . doPayPalCheck ( ) ; } reinitialize ( config ) { this . config = config ; this . payPal = undefined ; this . paypalScriptService . destroyPayPalScript ( ) ; this . payPalButtonContainerId = this . generateElementId ( ) ; this . initializePayPal = true ; if ( this . payPalButtonContainerElem ) { while ( this . payPalButtonContainerElem . nativeElement . firstChild ) { this . payPalButtonContainerElem . nativeElement . removeChild ( this . payPalButtonContainerElem . nativeElement . firstChild ) ; } } this . cdr . detectChanges ( ) ; if ( this . config ) { if ( ! this . payPal ) { this . initPayPalScript ( this . config , ( payPal ) => { this . payPal = payPal ; this . doPayPalCheck ( ) ; } ) ; } else { this . doPayPalCheck ( ) ; } } } private doPayPalCheck ( ) { if ( this . initializePayPal && this . config && this . payPal && this . payPalButtonContainerElem ) { if ( this . payPalButtonContainerElem . nativeElement . id ) { this . initializePayPal = false ; this . initPayPal ( this . config , this . payPal ) ; } } } private initPayPalScript ( config , initPayPal ) { this . paypalScriptService . registerPayPalScript ( { clientId : config . clientId , commit : config . advanced && config . advanced . commit ? config . advanced . commit : undefined , currency : config . currency , vault : config . vault , intent : config . intent , extraParams : config . advanced && config . advanced . extraQueryParams ? config . advanced . extraQueryParams : [ ] } , ( paypal ) => { this . scriptLoaded . next ( paypal ) ; initPayPal ( paypal ) ; } ) ; } private generateElementId ( ) { return `ngx-captcha-id-${ new Date ( ) . valueOf ( ) }` ; } private initPayPal ( config , paypal ) { this . ngZone . runOutsideAngular ( ( ) => { const createOrder = ( data , actions ) => { return this . ngZone . run ( ( ) => { if ( config . createOrderOnClient && config . createOrderOnServer ) { throw Error ( `Both 'createOrderOnClient' and 'createOrderOnServer' are defined.
                    Please choose one or the other.` ) ; } if ( ! config . createOrderOnClient && ! config . createOrderOnServer ) { throw Error ( `Neither 'createOrderOnClient' or 'createOrderOnServer' are defined.
                    Please define one of these to create order.` ) ; } if ( config . createOrderOnClient ) { return actions . order . create ( config . createOrderOnClient ( data ) ) ; } if ( config . createOrderOnServer ) { return config . createOrderOnServer ( data ) ; } throw Error ( `Invalid state for 'createOrder'.` ) ; } ) ; } ; const createSubscription = ( data , actions ) => { return this . ngZone . run ( ( ) => { if ( config . createSubscriptionOnClient ) { return actions . subscription . create ( config . createSubscriptionOnClient ( data ) ) ; } } ) ; } ; const onShippingChange = ( data , actions ) => { return this . ngZone . run ( ( ) => { if ( config . onShippingChange ) { return config . onShippingChange ( data , actions ) ; } } ) ; } ; const buttonsConfig = { style : config . style , onApprove : ( data , actions ) => { return this . ngZone . run ( ( ) => { if ( config . onApprove ) { config . onApprove ( data , actions ) ; } if ( config . authorizeOnServer ) { return config . authorizeOnServer ( data , actions ) ; } const onClientAuthorization = config . onClientAuthorization ; if ( onClientAuthorization ) { actions . order . capture ( ) . then ( ( details ) => { this . ngZone . run ( ( ) => { onClientAuthorization ( details ) ; } ) ; } ) ; return ; } } ) ; } , onError : ( error ) => { this . ngZone . run ( ( ) => { if ( config . onError ) { config . onError ( error ) ; } } ) ; } , onCancel : ( data , actions ) => { this . ngZone . run ( ( ) => { if ( config . onCancel ) { config . onCancel ( data , actions ) ; } } ) ; } , onClick : ( data , actions ) => { this . ngZone . run ( ( ) => { if ( config . onClick ) { config . onClick ( data , actions ) ; } } ) ; } , onInit : ( data , actions ) => { this . ngZone . run ( ( ) => { if ( config . onInit ) { config . onInit ( data , actions ) ; } } ) ; } , ... ( ( config . createOrderOnClient || config . createOrderOnServer ) && { createOrder } ) , ... ( ( config . createSubscriptionOnClient ) && { createSubscription } ) , ... ( config . onShippingChange && { onShippingChange } ) } ; paypal . Buttons ( buttonsConfig ) . render ( `#${ this . payPalButtonContainerId }` ) ; } ) ; } }