All files / src/app/core/utils/paypal/adapters paypal-adapters.builder.ts

100% Statements 38/38
100% Branches 14/14
100% Functions 8/8
100% Lines 36/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 954x 4x   4x 4x     4x     4x 4x 4x 4x 4x                                                       4x   19x 19x 19x 19x 19x     19x 19x 19x         18x 18x   1x   8x   1x   3x   4x   1x             8x 8x 3x   2x   5x 3x 2x 2x       8x      
import { Injectable, NgZone, inject } from '@angular/core';
import { Observable, filter, from, map, of, switchMap } from 'rxjs';
 
import { CheckoutFacade } from 'ish-core/facades/checkout.facade';
import { ShoppingFacade } from 'ish-core/facades/shopping.facade';
import { PaymentInstrument } from 'ish-core/models/payment-instrument/payment-instrument.model';
import { PaymentMethod } from 'ish-core/models/payment-method/payment-method.model';
import { whenTruthy } from 'ish-core/utils/operators';
import { PaypalAdapterTypes, PaypalPageType } from 'ish-core/utils/paypal/paypal-config/paypal-config.service';
 
import { PaypalApplePayAdapter } from './paypal-apple-pay/paypal-apple-pay.adapter';
import { PaypalButtonsAdapter } from './paypal-buttons/paypal-buttons.adapter';
import { PaypalCardFieldsAdapter } from './paypal-card-fields/paypal-card-fields.adapter';
import { PaypalGooglePayAdapter } from './paypal-google-pay/paypal-google-pay.adapter';
import { PaypalMessagesAdapter } from './paypal-messages/paypal-messages.adapter';
 
/**
 * Configuration object for creating PayPal components.
 * Provides all necessary context and callbacks for component initialization.
 *
 */
export interface PaypalComponentsConfig {
  /** The page context where the component is being used (e.g., 'cart', 'checkout') */
  pageType: PaypalPageType;
  /** Namespace used to access the PayPal SDK instance on the window object */
  scriptNamespace: string;
  /** Type of PayPal component to create ('buttons' or 'messages' or 'card fields') */
  adapterType: PaypalAdapterTypes;
  /** PayPal payment method configuration (required for buttons) */
  paypalPaymentMethod?: PaymentMethod;
  /** Amount to be used in the component (required for messages) */
  amount$?: Observable<number>;
  /** container id of rendering div */
  containerId?: string;
  merchantId?: string;
  paymentInstrument?(paymentInstrument: PaymentInstrument): void;
}
 
/**
 * Builder service for creating and configuring PayPal SDK components.
 */
@Injectable()
export class PaypalAdaptersBuilder {
  // payPal sdk objects
  private paypalCardFields = inject(PaypalCardFieldsAdapter);
  private paypalButtons = inject(PaypalButtonsAdapter);
  private paypalMessages = inject(PaypalMessagesAdapter);
  private paypalGooglePay = inject(PaypalGooglePayAdapter);
  private paypalApplePay = inject(PaypalApplePayAdapter);
 
  constructor(
    private checkoutFacade: CheckoutFacade,
    private shoppingFacade: ShoppingFacade,
    private ngZone: NgZone
  ) {}
 
  // Creates a PayPal component based on the provided configuration.
  build(config: PaypalComponentsConfig) {
    return this.ngZone.runOutsideAngular(() => {
      switch (config.adapterType) {
        case 'Buttons':
          return this.paypalButtons.renderButtons(config);
        case 'Messages':
          return this.paypalMessages.renderMessages({ ...config, amount$: this.getAmount(config) });
        case 'CardFields':
          return from(this.paypalCardFields.renderCardFields(config.paypalPaymentMethod));
        case 'GooglePay':
          return from(this.paypalGooglePay.renderGooglePayButton(config));
        case 'ApplePay':
          return from(this.paypalApplePay.renderApplePayButton(config));
        default:
          return from(Promise.reject(new Error(`Unsupported PayPal component type: ${config.adapterType}`)));
      }
    });
  }
 
  // Calculates the appropriate amount to display in PayPal components based on page context.
  private getAmount(config: PaypalComponentsConfig): Observable<number> {
    let amount$: Observable<number> = of(0);
    if (config.pageType === 'product-details') {
      amount$ = this.shoppingFacade.selectedProductId$.pipe(
        whenTruthy(),
        switchMap(id => this.shoppingFacade.productPrices$(id).pipe(map(prices => prices.salePrice?.value || 0)))
      );
    } else if (config.pageType === 'cart' || config.pageType === 'checkout') {
      amount$ = this.checkoutFacade.basket$.pipe(
        filter(basket => !!basket),
        map(basket => basket.totals?.total.gross)
      );
    }
 
    return amount$;
  }
}