From ced3251f8bd519778e2921801b27919be1dd928f Mon Sep 17 00:00:00 2001
From: PJaneta
Date: Thu, 26 Sep 2024 14:47:03 +0200
Subject: [PATCH] AD-315 I can't pay with any method if the first payment
attempt is canceled
---
.../src/lib/adyen-payments.module.ts | 10 ++---
.../adyen-redirect-error.component.ts | 25 +++++++----
...checkout-adyen-payment-method.component.ts | 44 ++++++++++++-------
.../additional-details.connector.ts | 1 -
...or.ts => adyen-order-connector.service.ts} | 10 +++--
.../occ/adapters/occ-adyen-order.adapter.ts | 43 ++++++++++++++++++
.../occ/adapters/occ-placeorder.adapter.ts | 27 ------------
...r-confirmation-payment-status.component.ts | 6 +--
.../src/lib/service/adyen-order.service.ts | 31 +++++++++++--
9 files changed, 131 insertions(+), 66 deletions(-)
rename projects/adyen-payments/src/lib/core/connectors/{placeorder.connector.ts => adyen-order-connector.service.ts} (51%)
create mode 100644 projects/adyen-payments/src/lib/core/occ/adapters/occ-adyen-order.adapter.ts
delete mode 100644 projects/adyen-payments/src/lib/core/occ/adapters/occ-placeorder.adapter.ts
diff --git a/projects/adyen-payments/src/lib/adyen-payments.module.ts b/projects/adyen-payments/src/lib/adyen-payments.module.ts
index fadfb55..7fbbd0d 100644
--- a/projects/adyen-payments/src/lib/adyen-payments.module.ts
+++ b/projects/adyen-payments/src/lib/adyen-payments.module.ts
@@ -7,8 +7,6 @@ import {CheckoutAdyenConfigurationService} from "./service/checkout-adyen-config
import {CheckoutConfigurationConnector} from "./core/connectors/checkout-configuration.connector";
import {OccCheckoutConfigAdapter} from "./core/occ/adapters/occ-checkout-config.adapter";
import {CheckoutAdyenEventListener} from "./events/checkout-adyen-event.listener";
-import {PlaceOrderConnector} from "./core/connectors/placeorder.connector";
-import {OccPlaceOrderAdapter} from "./core/occ/adapters/occ-placeorder.adapter";
import {AdyenOrderService} from "./service/adyen-order.service";
import {OrderAdapter, OrderConnector, OrderHistoryConnector, OrderHistoryAdapter} from "@spartacus/order/core"
import {OccOrderAdapter, OccOrderHistoryAdapter} from "@spartacus/order/occ"
@@ -18,6 +16,8 @@ import {OccAdditionalDetailsAdapter} from "./core/occ/adapters/occ-additionaldet
import {AdyenRedirectModule} from "./adyen-redirect/adyen-redirect.module";
import {I18nConfig, provideConfig, provideDefaultConfig} from '@spartacus/core';
import {adyenCheckoutTranslationChunksConfig, adyenCheckoutTranslations} from "./assets/translations/translations";
+import {AdyenOrderConnector} from "./core/connectors/adyen-order-connector.service";
+import {OccAdyenOrderAdapter} from "./core/occ/adapters/occ-adyen-order.adapter";
@@ -32,9 +32,9 @@ import {adyenCheckoutTranslationChunksConfig, adyenCheckoutTranslations} from ".
providers: [CheckoutAdyenConfigurationService,
AdyenOrderService,
AdyenAddressService,
- PlaceOrderConnector,
- AdditionalDetailsConnector,
OrderConnector,
+ AdditionalDetailsConnector,
+ AdyenOrderConnector,
{
provide: OrderAdapter,
useClass: OccOrderAdapter,
@@ -52,7 +52,7 @@ import {adyenCheckoutTranslationChunksConfig, adyenCheckoutTranslations} from ".
provide: OrderHistoryAdapter,
useClass: OccOrderHistoryAdapter
},
- OccPlaceOrderAdapter,
+ OccAdyenOrderAdapter,
OccAdditionalDetailsAdapter,
OccCheckoutConfigAdapter,
CheckoutAdyenEventListener,
diff --git a/projects/adyen-payments/src/lib/adyen-redirect/adyen-redirect-error.component.ts b/projects/adyen-payments/src/lib/adyen-redirect/adyen-redirect-error.component.ts
index 0b22a48..c707fb1 100644
--- a/projects/adyen-payments/src/lib/adyen-redirect/adyen-redirect-error.component.ts
+++ b/projects/adyen-payments/src/lib/adyen-redirect/adyen-redirect-error.component.ts
@@ -1,4 +1,4 @@
-import {Component, OnInit} from '@angular/core';
+import {Component, OnDestroy, OnInit} from '@angular/core';
import {CartType, MultiCartFacade} from '@spartacus/cart/base/root';
import {
GlobalMessageService,
@@ -9,14 +9,16 @@ import {
UserIdService
} from '@spartacus/core';
import {errorCodePrefix} from "../assets/translations/translations";
+import {Subscription} from "rxjs";
@Component({
selector: 'adyen-redirect-error',
templateUrl: './adyen-redirect.component.html',
})
-export class AdyenRedirectErrorComponent implements OnInit {
+export class AdyenRedirectErrorComponent implements OnInit, OnDestroy {
private messageTimeout: number = 20000;
private placeOrderErrorCodePrefix: string = errorCodePrefix + '.';
+ private subscriptions = new Subscription();
constructor(protected routingService: RoutingService,
protected globalMessageService: GlobalMessageService,
@@ -27,7 +29,7 @@ export class AdyenRedirectErrorComponent implements OnInit {
}
private addErrorMessage() {
- this.routingService.getParams().subscribe(params => {
+ let subscribeRouting = this.routingService.getParams().subscribe(params => {
let errorCode = params['errorCode'];
if (errorCode) {
@@ -39,15 +41,19 @@ export class AdyenRedirectErrorComponent implements OnInit {
this.multiCartFacade.reloadCart(OCC_CART_ID_CURRENT)
- this.userIdService.takeUserId().subscribe((userId) => {
+ let subscribeUser = this.userIdService.takeUserId().subscribe((userId) => {
this.multiCartFacade.loadCart({cartId: OCC_CART_ID_CURRENT, userId})
- this.multiCartFacade.getCartIdByType(CartType.ACTIVE).subscribe((cartId) => {
+
+ let subscribeCart = this.multiCartFacade.getCartIdByType(CartType.ACTIVE).subscribe((cartId) => {
this.routingService.go({cxRoute: "checkoutAdyenPaymentDetails"})
- })
- })
+ });
+ this.subscriptions.add(subscribeCart);
+ });
+ this.subscriptions.add(subscribeUser);
}
- })
+ });
+ this.subscriptions.add(subscribeRouting);
}
@@ -55,4 +61,7 @@ export class AdyenRedirectErrorComponent implements OnInit {
this.addErrorMessage();
}
+ ngOnDestroy(): void {
+ this.subscriptions.unsubscribe();
+ }
}
diff --git a/projects/adyen-payments/src/lib/checkout-adyen-payment-method/checkout-adyen-payment-method.component.ts b/projects/adyen-payments/src/lib/checkout-adyen-payment-method/checkout-adyen-payment-method.component.ts
index faced84..d367475 100644
--- a/projects/adyen-payments/src/lib/checkout-adyen-payment-method/checkout-adyen-payment-method.component.ts
+++ b/projects/adyen-payments/src/lib/checkout-adyen-payment-method/checkout-adyen-payment-method.component.ts
@@ -1,18 +1,19 @@
import {ChangeDetectionStrategy, Component, ElementRef, OnDestroy, OnInit, ViewChild,} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
-import {ActiveCartFacade} from '@spartacus/cart/base/root';
-import {CheckoutDeliveryAddressFacade, CheckoutPaymentFacade,} from '@spartacus/checkout/base/root';
+import {ActiveCartFacade, CartType, MultiCartFacade} from '@spartacus/cart/base/root';
+import {CheckoutDeliveryAddressFacade,} from '@spartacus/checkout/base/root';
import {
Address,
+ EventService,
getLastValueSync,
- GlobalMessageService,
+ OCC_CART_ID_CURRENT,
PaymentDetails,
RoutingService,
- TranslationService,
+ UserIdService,
UserPaymentService,
} from '@spartacus/core';
import {BehaviorSubject, Subscription,} from 'rxjs';
-import {filter, map, take,switchMap,} from 'rxjs/operators';
+import {filter, map, switchMap, take,} from 'rxjs/operators';
import {CheckoutStepService} from "@spartacus/checkout/base/components";
import AdyenCheckout from '@adyen/adyen-web';
import {CheckoutAdyenConfigurationService} from "../service/checkout-adyen-configuration.service";
@@ -25,8 +26,6 @@ import AdyenCheckoutError from "@adyen/adyen-web/dist/types/core/Errors/AdyenChe
import {PlaceOrderResponse} from "../core/models/occ.order.models";
import {AdyenOrderService} from "../service/adyen-order.service";
import {CheckoutAdyenConfigurationReloadEvent} from "../events/checkout-adyen.events";
-import { UserIdService } from '@spartacus/core';
-import { EventService } from '@spartacus/core';
@Component({
selector: 'cx-payment-method',
@@ -58,17 +57,15 @@ export class CheckoutAdyenPaymentMethodComponent implements OnInit, OnDestroy {
constructor(
protected userPaymentService: UserPaymentService,
protected checkoutDeliveryAddressFacade: CheckoutDeliveryAddressFacade,
- protected checkoutPaymentFacade: CheckoutPaymentFacade,
protected activatedRoute: ActivatedRoute,
- protected translationService: TranslationService,
protected routingService: RoutingService,
protected activeCartFacade: ActiveCartFacade,
protected checkoutStepService: CheckoutStepService,
- protected globalMessageService: GlobalMessageService,
protected checkoutAdyenConfigurationService: CheckoutAdyenConfigurationService,
protected adyenOrderService: AdyenOrderService,
protected eventService: EventService,
- private userIdService: UserIdService
+ private userIdService: UserIdService,
+ protected multiCartFacade: MultiCartFacade,
) {
}
@@ -141,7 +138,7 @@ export class CheckoutAdyenPaymentMethodComponent implements OnInit, OnDestroy {
holderNameRequired: adyenConfig.cardHolderNameRequired,
enableStoreDetails: adyenConfig.showRememberTheseDetails
},
- paypal: {
+ paypal: {
intent: "authorize"
}
},
@@ -166,9 +163,7 @@ export class CheckoutAdyenPaymentMethodComponent implements OnInit, OnDestroy {
onPaymentCompleted(data: OnPaymentCompletedData, element?: UIElement) {
console.info(data, element);
},
- onError(error: AdyenCheckoutError, element?: UIElement) {
- console.error(error.name, error.message, error.stack, element);
- },
+ onError: (error: AdyenCheckoutError) => this.handleError(error),
onSubmit: (state: any, element: UIElement) => this.handlePayment(state.data),
onAdditionalDetails: (state: any, element?: UIElement) => this.handleAdditionalDetails(state.data),
onActionHandled(data: ActionHandledReturnObject) {
@@ -233,6 +228,25 @@ export class CheckoutAdyenPaymentMethodComponent implements OnInit, OnDestroy {
}
}
+ private handleError(error: AdyenCheckoutError) {
+ let subscribeCancel = this.adyenOrderService.sendPaymentCancelled().subscribe(() => {
+ this.multiCartFacade.reloadCart(OCC_CART_ID_CURRENT)
+
+ let subscribeUser = this.userIdService.takeUserId().subscribe((userId) => {
+ this.multiCartFacade.loadCart({cartId: OCC_CART_ID_CURRENT, userId})
+
+ let subscribeCart = this.multiCartFacade.getCartIdByType(CartType.ACTIVE).subscribe((cartId) => {
+ this.eventService.dispatch(
+ new CheckoutAdyenConfigurationReloadEvent()
+ );
+ });
+ subscribeCart.unsubscribe();
+ });
+ subscribeUser.unsubscribe();
+ subscribeCancel.unsubscribe();
+ });
+ }
+
private resetDropInComponent() {
this.dropIn.unmount();
this.dropIn.mount(this.hook.nativeElement)
diff --git a/projects/adyen-payments/src/lib/core/connectors/additional-details.connector.ts b/projects/adyen-payments/src/lib/core/connectors/additional-details.connector.ts
index 48fa915..f3a3fc3 100644
--- a/projects/adyen-payments/src/lib/core/connectors/additional-details.connector.ts
+++ b/projects/adyen-payments/src/lib/core/connectors/additional-details.connector.ts
@@ -1,6 +1,5 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
-import { OccPlaceOrderAdapter } from '../occ/adapters/occ-placeorder.adapter';
import {PlaceOrderRequest, PlaceOrderResponse} from "../models/occ.order.models";
import {OccAdditionalDetailsAdapter} from "../occ/adapters/occ-additionaldetails.adapter";
diff --git a/projects/adyen-payments/src/lib/core/connectors/placeorder.connector.ts b/projects/adyen-payments/src/lib/core/connectors/adyen-order-connector.service.ts
similarity index 51%
rename from projects/adyen-payments/src/lib/core/connectors/placeorder.connector.ts
rename to projects/adyen-payments/src/lib/core/connectors/adyen-order-connector.service.ts
index 556d41b..d921893 100644
--- a/projects/adyen-payments/src/lib/core/connectors/placeorder.connector.ts
+++ b/projects/adyen-payments/src/lib/core/connectors/adyen-order-connector.service.ts
@@ -1,13 +1,17 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
-import { OccPlaceOrderAdapter } from '../occ/adapters/occ-placeorder.adapter';
import {PlaceOrderRequest, PlaceOrderResponse} from "../models/occ.order.models";
+import {OccAdyenOrderAdapter} from "../occ/adapters/occ-adyen-order.adapter";
@Injectable()
-export class PlaceOrderConnector {
- constructor(protected adapter: OccPlaceOrderAdapter) {}
+export class AdyenOrderConnector {
+ constructor(protected adapter: OccAdyenOrderAdapter) {}
placeOrder(userId: string, cartId: string, orderData: PlaceOrderRequest): Observable {
return this.adapter.placeOrder(userId, cartId, orderData);
}
+
+ paymentCanceled(userId: string, cartId: string, orderCode: string): Observable {
+ return this.adapter.cancelPayment(userId, cartId, orderCode);
+ }
}
diff --git a/projects/adyen-payments/src/lib/core/occ/adapters/occ-adyen-order.adapter.ts b/projects/adyen-payments/src/lib/core/occ/adapters/occ-adyen-order.adapter.ts
new file mode 100644
index 0000000..5e557db
--- /dev/null
+++ b/projects/adyen-payments/src/lib/core/occ/adapters/occ-adyen-order.adapter.ts
@@ -0,0 +1,43 @@
+import {Injectable} from '@angular/core';
+import {HttpClient} from '@angular/common/http';
+import {OccEndpointsService} from '@spartacus/core';
+import {Observable} from 'rxjs';
+import {PlaceOrderRequest, PlaceOrderResponse} from "../../models/occ.order.models";
+
+@Injectable()
+export class OccAdyenOrderAdapter {
+
+ constructor(
+ protected http: HttpClient,
+ protected occEndpoints: OccEndpointsService
+ ) {
+ }
+
+ public placeOrder(userId: string, cartId: string, orderData: PlaceOrderRequest): Observable {
+ return this.http.post(this.getPlaceOrderEndpoint(userId, cartId), orderData);
+ }
+
+ protected getPlaceOrderEndpoint(userId: string, cartId: string): string {
+ return this.occEndpoints.buildUrl('users/${userId}/carts/${cartId}/adyen/place-order', {
+ urlParams: {
+ userId,
+ cartId,
+ }
+ });
+ }
+
+ public cancelPayment(userId: string, cartId: string, orderCode: string): Observable {
+ return this.http.post(this.getPaymentCanceledEndpoint(userId, cartId, orderCode), {})
+ }
+
+ protected getPaymentCanceledEndpoint(userId: string, cartId: string, orderCode: string): string {
+ return this.occEndpoints.buildUrl('users/${userId}/adyen/payment-canceled/${orderCode}', {
+ urlParams: {
+ userId,
+ cartId,
+ orderCode
+ }
+ });
+ }
+
+}
diff --git a/projects/adyen-payments/src/lib/core/occ/adapters/occ-placeorder.adapter.ts b/projects/adyen-payments/src/lib/core/occ/adapters/occ-placeorder.adapter.ts
deleted file mode 100644
index 0f5fa7d..0000000
--- a/projects/adyen-payments/src/lib/core/occ/adapters/occ-placeorder.adapter.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { OccEndpointsService } from '@spartacus/core';
-import { Observable } from 'rxjs';
-import {PlaceOrderRequest, PlaceOrderResponse} from "../../models/occ.order.models";
-
-@Injectable()
-export class OccPlaceOrderAdapter {
-
- constructor(
- protected http: HttpClient,
- protected occEndpoints: OccEndpointsService
- ) {}
-
- public placeOrder(userId: string, cartId: string, orderData: PlaceOrderRequest): Observable {
- return this.http.post(this.getPlaceOrderEndpoint(userId, cartId), orderData);
- }
-
- protected getPlaceOrderEndpoint(userId: string, cartId: string): string {
- return this.occEndpoints.buildUrl('users/${userId}/carts/${cartId}/adyen/place-order', {
- urlParams: {
- userId,
- cartId,
- }
- });
- }
-}
diff --git a/projects/adyen-payments/src/lib/order/components/order-confirmation/order-confirmation-payment-status/order-confirmation-payment-status.component.ts b/projects/adyen-payments/src/lib/order/components/order-confirmation/order-confirmation-payment-status/order-confirmation-payment-status.component.ts
index 826efbb..5341a27 100644
--- a/projects/adyen-payments/src/lib/order/components/order-confirmation/order-confirmation-payment-status/order-confirmation-payment-status.component.ts
+++ b/projects/adyen-payments/src/lib/order/components/order-confirmation/order-confirmation-payment-status/order-confirmation-payment-status.component.ts
@@ -14,7 +14,7 @@ export class OrderConfirmationPaymentStatusComponent implements OnInit, OnDestro
constructor(protected orderPaymentStatusService: OrderPaymentStatusService,
protected adyenOrderService: AdyenOrderService) {
adyenOrderService.getOrderDetails().subscribe(orderData => {
- this.orderCode = orderData!.code as string;
+ this.orderCode = orderData? orderData.code : undefined;
})
}
@@ -22,7 +22,7 @@ export class OrderConfirmationPaymentStatusComponent implements OnInit, OnDestro
private numberOfRetries = 30;
private currentRetry = 1;
- private orderCode: string;
+ private orderCode?: string;
paymentStatus$: BehaviorSubject;
@@ -30,7 +30,7 @@ export class OrderConfirmationPaymentStatusComponent implements OnInit, OnDestro
private timerCallback() {
- if (this.currentRetry <= this.numberOfRetries) {
+ if (this.currentRetry <= this.numberOfRetries && this.orderCode) {
this.orderPaymentStatusService.getOrderStatus(this.orderCode).subscribe((status) => {
this.paymentStatus$.next(status);
if (status !== 'waiting') {
diff --git a/projects/adyen-payments/src/lib/service/adyen-order.service.ts b/projects/adyen-payments/src/lib/service/adyen-order.service.ts
index 19b86a5..0fc05a6 100644
--- a/projects/adyen-payments/src/lib/service/adyen-order.service.ts
+++ b/projects/adyen-payments/src/lib/service/adyen-order.service.ts
@@ -11,9 +11,9 @@ import {
UserIdService
} from "@spartacus/core";
import {OrderConnector, OrderHistoryConnector, OrderService} from '@spartacus/order/core';
-import {catchError, map, Observable, of, switchMap, tap} from "rxjs";
+import {BehaviorSubject, catchError, map, Observable, of, switchMap, tap} from "rxjs";
import {OrderPlacedEvent} from '@spartacus/order/root';
-import {PlaceOrderConnector} from "../core/connectors/placeorder.connector";
+import {AdyenOrderConnector} from "../core/connectors/adyen-order-connector.service";
import {ActiveCartFacade} from '@spartacus/cart/base/root';
import {AddressData, PlaceOrderRequest, PlaceOrderResponse} from "../core/models/occ.order.models";
import {HttpErrorResponse} from "@angular/common/http";
@@ -24,9 +24,10 @@ import {errorCodePrefix} from "../assets/translations/translations";
@Injectable()
export class AdyenOrderService extends OrderService {
private messageTimeout: number = 20000;
+ private placedOrderNumber$ = new BehaviorSubject(undefined);
private placeOrderErrorCodePrefix: string = errorCodePrefix + '.';
- constructor(protected placeOrderConnector: PlaceOrderConnector,
+ constructor(protected placeOrderConnector: AdyenOrderConnector,
protected additionalDetailsConnector: AdditionalDetailsConnector,
protected override activeCartFacade: ActiveCartFacade,
protected override userIdService: UserIdService,
@@ -49,6 +50,7 @@ export class AdyenOrderService extends OrderService {
this.placeOrderConnector.placeOrder(userId, cartId, AdyenOrderService.preparePlaceOrderRequest(paymentData, billingAddress)).pipe(
tap((placeOrderResponse) => {
this.placedOrder$.next(placeOrderResponse.orderData);
+ this.placedOrderNumber$.next(placeOrderResponse.orderNumber)
this.eventService.dispatch(
{
userId,
@@ -96,6 +98,7 @@ export class AdyenOrderService extends OrderService {
this.additionalDetailsConnector.sendAdditionalDetails(userId, cartId, details).pipe(
tap((placeOrderResponse) => {
this.placedOrder$.next(placeOrderResponse.orderData);
+ this.placedOrderNumber$.next(placeOrderResponse.orderNumber);
this.eventService.dispatch(
{
userId,
@@ -130,11 +133,31 @@ export class AdyenOrderService extends OrderService {
}
);
-
sendAdditionalDetails(details: any): Observable {
return this.sendAdditionalDetailsCommand.execute(details);
}
+ protected sendCancelledPaymentCommand: Command =
+ this.commandService.create(
+ () =>
+ this.checkoutPreconditions().pipe(
+ switchMap(([userId, cartId]) => {
+ return this.placedOrderNumber$.pipe(
+ map((orderNumber) => {
+ this.placeOrderConnector.paymentCanceled(userId, cartId, orderNumber!).subscribe()
+ }))
+ }
+ )
+ ),
+ {
+ strategy: CommandStrategy.CancelPrevious,
+ }
+ );
+
+ sendPaymentCancelled(): Observable {
+ return this.sendCancelledPaymentCommand.execute({});
+ }
+
loadOrderDetails(orderCode: string): void {
this.userIdService.takeUserId().subscribe(
(userId) => {