File

feature-libs/checkout/components/components/payment-method/payment-method.component.ts

Implements

OnInit OnDestroy

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector cx-payment-method
templateUrl ./payment-method.component.html

Index

Properties
Methods

Constructor

constructor(userPaymentService: UserPaymentService, checkoutService: CheckoutFacade, checkoutDeliveryService: CheckoutDeliveryFacade, checkoutPaymentService: CheckoutPaymentFacade, globalMessageService: GlobalMessageService, activatedRoute: ActivatedRoute, translation: TranslationService, activeCartService: ActiveCartService, checkoutStepService: CheckoutStepService)
Parameters :
Name Type Optional
userPaymentService UserPaymentService No
checkoutService CheckoutFacade No
checkoutDeliveryService CheckoutDeliveryFacade No
checkoutPaymentService CheckoutPaymentFacade No
globalMessageService GlobalMessageService No
activatedRoute ActivatedRoute No
translation TranslationService No
activeCartService ActiveCartService No
checkoutStepService CheckoutStepService No

Methods

back
back()
Returns : void
Protected createCard
createCard(paymentDetails: PaymentDetails, cardLabels: literal type, selected: PaymentDetails)
Parameters :
Name Type Optional
paymentDetails PaymentDetails No
cardLabels literal type No
selected PaymentDetails No
Returns : Card
Protected getCardIcon
getCardIcon(code: string)
Parameters :
Name Type Optional
code string No
Returns : string
hideNewPaymentForm
hideNewPaymentForm()
Returns : void
next
next()
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
selectPaymentMethod
selectPaymentMethod(paymentDetails: PaymentDetails)
Parameters :
Name Type Optional
paymentDetails PaymentDetails No
Returns : void
Protected sendPaymentMethodFailGlobalMessage
sendPaymentMethodFailGlobalMessage(field: string)
Parameters :
Name Type Optional
field string No
Returns : void
setPaymentDetails
setPaymentDetails(undefined: literal type)
Parameters :
Name Type Optional
literal type No
Returns : void
showNewPaymentForm
showNewPaymentForm()
Returns : void

Properties

backBtnText
Default value : this.checkoutStepService.getBackBntText(this.activatedRoute)
cards$
Type : Observable<literal type[]>
Protected deliveryAddress
Type : Address
existingPaymentMethods$
Type : Observable<PaymentDetails[]>
iconTypes
Default value : ICON_TYPE
isGuestCheckout
Default value : false
isLoading$
Type : Observable<boolean>
newPaymentFormManuallyOpened
Default value : false
selectedMethod$
Type : Observable<PaymentDetails>
Protected shouldRedirect
Type : boolean
import {
  ChangeDetectionStrategy,
  Component,
  OnDestroy,
  OnInit,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import {
  CheckoutDeliveryFacade,
  CheckoutFacade,
  CheckoutPaymentFacade,
} from '@spartacus/checkout/root';
import {
  ActiveCartService,
  Address,
  GlobalMessageService,
  GlobalMessageType,
  PaymentDetails,
  TranslationService,
  UserPaymentService,
} from '@spartacus/core';
import { Card, ICON_TYPE } from '@spartacus/storefront';
import { combineLatest, Observable, of } from 'rxjs';
import { map, switchMap, take, tap } from 'rxjs/operators';
import { CheckoutStepService } from '../../services/checkout-step.service';

@Component({
  selector: 'cx-payment-method',
  templateUrl: './payment-method.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PaymentMethodComponent implements OnInit, OnDestroy {
  iconTypes = ICON_TYPE;
  existingPaymentMethods$: Observable<PaymentDetails[]>;
  isLoading$: Observable<boolean>;
  cards$: Observable<{ content: Card; paymentMethod: PaymentDetails }[]>;
  selectedMethod$: Observable<PaymentDetails>;
  isGuestCheckout = false;
  newPaymentFormManuallyOpened = false;

  backBtnText = this.checkoutStepService.getBackBntText(this.activatedRoute);

  protected shouldRedirect: boolean;
  protected deliveryAddress: Address;

  constructor(
    protected userPaymentService: UserPaymentService,
    protected checkoutService: CheckoutFacade,
    protected checkoutDeliveryService: CheckoutDeliveryFacade,
    protected checkoutPaymentService: CheckoutPaymentFacade,
    protected globalMessageService: GlobalMessageService,
    protected activatedRoute: ActivatedRoute,
    protected translation: TranslationService,
    protected activeCartService: ActiveCartService,
    protected checkoutStepService: CheckoutStepService
  ) {}

  ngOnInit() {
    this.shouldRedirect = false;
    this.isLoading$ = this.userPaymentService.getPaymentMethodsLoading();

    if (!this.activeCartService.isGuestCart()) {
      this.userPaymentService.loadPaymentMethods();
    } else {
      this.isGuestCheckout = true;
    }

    this.checkoutDeliveryService
      .getDeliveryAddress()
      .pipe(take(1))
      .subscribe((address: Address) => {
        this.deliveryAddress = address;
      });

    this.existingPaymentMethods$ = this.userPaymentService.getPaymentMethods();

    this.selectedMethod$ = this.checkoutPaymentService.getPaymentDetails().pipe(
      tap((paymentInfo: any) => {
        if (paymentInfo && !!Object.keys(paymentInfo).length) {
          if (paymentInfo['hasError']) {
            Object.keys(paymentInfo).forEach((key) => {
              if (key.startsWith('InvalidField')) {
                this.sendPaymentMethodFailGlobalMessage(paymentInfo[key]);
              }
            });
            this.checkoutService.clearCheckoutStep(3);
          } else if (this.shouldRedirect) {
            this.next();
          }
        }
      })
    );

    this.cards$ = combineLatest([
      this.existingPaymentMethods$.pipe(
        switchMap((methods) => {
          return !methods?.length
            ? of([])
            : combineLatest(
                methods.map((method) =>
                  combineLatest([
                    of(method),
                    this.translation.translate('paymentCard.expires', {
                      month: method.expiryMonth,
                      year: method.expiryYear,
                    }),
                  ]).pipe(
                    map(([payment, translation]) => ({
                      payment,
                      expiryTranslation: translation,
                    }))
                  )
                )
              );
        })
      ),
      this.selectedMethod$,
      this.translation.translate('paymentForm.useThisPayment'),
      this.translation.translate('paymentCard.defaultPaymentMethod'),
      this.translation.translate('paymentCard.selected'),
    ]).pipe(
      map(
        ([
          paymentMethods,
          selectedMethod,
          textUseThisPayment,
          textDefaultPaymentMethod,
          textSelected,
        ]) => {
          if (
            paymentMethods.length &&
            (!selectedMethod || Object.keys(selectedMethod).length === 0)
          ) {
            const defaultPaymentMethod = paymentMethods.find(
              (paymentMethod) => paymentMethod.payment.defaultPayment
            );
            if (defaultPaymentMethod) {
              selectedMethod = defaultPaymentMethod.payment;
              this.checkoutPaymentService.setPaymentDetails(selectedMethod);
            }
          }
          return paymentMethods.map((payment) => ({
            content: this.createCard(
              payment.payment,
              {
                textExpires: payment.expiryTranslation,
                textUseThisPayment,
                textDefaultPaymentMethod,
                textSelected,
              },
              selectedMethod
            ),
            paymentMethod: payment.payment,
          }));
        }
      )
    );
  }

  selectPaymentMethod(paymentDetails: PaymentDetails): void {
    this.checkoutPaymentService.setPaymentDetails(paymentDetails);
  }

  showNewPaymentForm(): void {
    this.newPaymentFormManuallyOpened = true;
  }

  hideNewPaymentForm(): void {
    this.newPaymentFormManuallyOpened = false;
  }

  setPaymentDetails({
    paymentDetails,
    billingAddress,
  }: {
    paymentDetails: PaymentDetails;
    billingAddress?: Address;
  }): void {
    const details: PaymentDetails = { ...paymentDetails };
    details.billingAddress = billingAddress || this.deliveryAddress;
    this.checkoutPaymentService.createPaymentDetails(details);
    this.shouldRedirect = true;
  }

  ngOnDestroy(): void {
    this.checkoutPaymentService.paymentProcessSuccess();
  }

  protected getCardIcon(code: string): string {
    let ccIcon: string;
    if (code === 'visa') {
      ccIcon = this.iconTypes.VISA;
    } else if (code === 'master' || code === 'mastercard_eurocard') {
      ccIcon = this.iconTypes.MASTER_CARD;
    } else if (code === 'diners') {
      ccIcon = this.iconTypes.DINERS_CLUB;
    } else if (code === 'amex') {
      ccIcon = this.iconTypes.AMEX;
    } else {
      ccIcon = this.iconTypes.CREDIT_CARD;
    }

    return ccIcon;
  }

  protected sendPaymentMethodFailGlobalMessage(field: string) {
    this.globalMessageService.add(
      {
        key: 'paymentMethods.invalidField',
        params: { field },
      },
      GlobalMessageType.MSG_TYPE_ERROR
    );
  }

  protected createCard(
    paymentDetails: PaymentDetails,
    cardLabels: {
      textDefaultPaymentMethod: string;
      textExpires: string;
      textUseThisPayment: string;
      textSelected: string;
    },
    selected: PaymentDetails
  ): Card {
    return {
      title: paymentDetails.defaultPayment
        ? cardLabels.textDefaultPaymentMethod
        : '',
      textBold: paymentDetails.accountHolderName,
      text: [paymentDetails.cardNumber ?? '', cardLabels.textExpires],
      img: this.getCardIcon(paymentDetails.cardType?.code as string),
      actions: [{ name: cardLabels.textUseThisPayment, event: 'send' }],
      header:
        selected?.id === paymentDetails.id
          ? cardLabels.textSelected
          : undefined,
    };
  }

  next(): void {
    this.checkoutStepService.next(this.activatedRoute);
  }

  back(): void {
    this.checkoutStepService.back(this.activatedRoute);
  }
}
<ng-container *ngIf="cards$ | async as cards">
  <h2 class="cx-checkout-title d-none d-lg-block d-xl-block">
    {{ 'paymentForm.payment' | cxTranslate }}
  </h2>
  <ng-container *ngIf="!(isLoading$ | async); else loading">
    <ng-container
      *ngIf="
        cards?.length && !newPaymentFormManuallyOpened;
        else newPaymentForm
      "
    >
      <p class="cx-checkout-text">
        {{ 'paymentForm.choosePaymentMethod' | cxTranslate }}
      </p>
      <div class="cx-checkout-btns row">
        <div class="col-md-12 col-lg-6">
          <button
            class="btn btn-block btn-action"
            (click)="showNewPaymentForm()"
          >
            {{ 'paymentForm.addNewPayment' | cxTranslate }}
          </button>
        </div>
      </div>

      <div class="cx-checkout-body row">
        <div
          class="cx-payment-card col-md-12 col-lg-6"
          *ngFor="let card of cards; let i = index"
        >
          <div class="cx-payment-card-inner">
            <cx-card
              [border]="true"
              [fitToContainer]="true"
              [content]="card.content"
              (sendCard)="selectPaymentMethod(card.paymentMethod)"
            ></cx-card>
          </div>
        </div>
      </div>

      <div class="row cx-checkout-btns">
        <div class="col-md-12 col-lg-6">
          <button class="btn btn-block btn-action" (click)="back()">
            {{ backBtnText | cxTranslate }}
          </button>
        </div>
        <div class="col-md-12 col-lg-6">
          <button
            class="btn btn-block btn-primary"
            [disabled]="!(selectedMethod$ | async)?.id"
            (click)="next()"
          >
            {{ 'common.continue' | cxTranslate }}
          </button>
        </div>
      </div>
    </ng-container>

    <ng-template #newPaymentForm>
      <cx-payment-form
        (setPaymentDetails)="setPaymentDetails($event)"
        (closeForm)="hideNewPaymentForm()"
        (goBack)="back()"
        [paymentMethodsCount]="cards?.length || 0"
        [setAsDefaultField]="!isGuestCheckout"
      ></cx-payment-form>
    </ng-template>
  </ng-container>

  <ng-template #loading>
    <div class="cx-spinner"><cx-spinner></cx-spinner></div>
  </ng-template>
</ng-container>
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""