feature-libs/checkout/components/components/payment-method/payment-method.component.ts
| changeDetection | ChangeDetectionStrategy.OnPush |
| selector | cx-payment-method |
| templateUrl | ./payment-method.component.html |
Properties |
Methods |
|
constructor(userPaymentService: UserPaymentService, checkoutService: CheckoutFacade, checkoutDeliveryService: CheckoutDeliveryFacade, checkoutPaymentService: CheckoutPaymentFacade, globalMessageService: GlobalMessageService, activatedRoute: ActivatedRoute, translation: TranslationService, activeCartService: ActiveCartService, checkoutStepService: CheckoutStepService)
|
||||||||||||||||||||||||||||||
|
Parameters :
|
| back |
back()
|
|
Returns :
void
|
| Protected createCard | ||||||||||||
createCard(paymentDetails: PaymentDetails, cardLabels: literal type, selected: PaymentDetails)
|
||||||||||||
|
Parameters :
Returns :
Card
|
| Protected getCardIcon | ||||||
getCardIcon(code: string)
|
||||||
|
Parameters :
Returns :
string
|
| hideNewPaymentForm |
hideNewPaymentForm()
|
|
Returns :
void
|
| next |
next()
|
|
Returns :
void
|
| ngOnDestroy |
ngOnDestroy()
|
|
Returns :
void
|
| ngOnInit |
ngOnInit()
|
|
Returns :
void
|
| selectPaymentMethod | ||||||
selectPaymentMethod(paymentDetails: PaymentDetails)
|
||||||
|
Parameters :
Returns :
void
|
| Protected sendPaymentMethodFailGlobalMessage | ||||||
sendPaymentMethodFailGlobalMessage(field: string)
|
||||||
|
Parameters :
Returns :
void
|
| setPaymentDetails | |||||
setPaymentDetails(undefined: literal type)
|
|||||
|
Parameters :
Returns :
void
|
| showNewPaymentForm |
showNewPaymentForm()
|
|
Returns :
void
|
| 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>