Skip to content

Commit

Permalink
feat: add hostedFields pattern#2 (#17744)
Browse files Browse the repository at this point in the history
CXSPA-3879
  • Loading branch information
FollowTheFlo authored Sep 21, 2023
1 parent a265e76 commit 6ef7b96
Show file tree
Hide file tree
Showing 26 changed files with 899 additions and 244 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import {
AfterRedirectScriptResponse,
OpfPaymentVerificationPayload,
OpfPaymentVerificationResponse,
SubmitCompleteRequest,
Expand Down Expand Up @@ -43,4 +44,8 @@ export abstract class OpfPaymentAdapter {
otpKey: string,
paymentSessionId: string
): Observable<SubmitCompleteResponse>;

abstract afterRedirectScripts(
paymentSessionId: string
): Observable<AfterRedirectScriptResponse>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class MockOpfPaymentAdapter implements OpfPaymentAdapter {
verifyPayment = createSpy().and.returnValue(of({}));
submitPayment = createSpy().and.returnValue(of({}));
submitCompletePayment = createSpy().and.returnValue(of({}));
afterRedirectScripts = createSpy().and.returnValue(of({}));
}

describe('OpfPaymentConnector', () => {
Expand Down Expand Up @@ -53,4 +54,9 @@ describe('OpfPaymentConnector', () => {
service.submitCompletePayment({}, '1', '2').pipe(take(1)).subscribe();
expect(adapter.submitCompletePayment).toHaveBeenCalledWith({}, '1', '2');
});

it('should call afterRedirectScripts method from adapter', () => {
service.afterRedirectScripts('1').pipe(take(1)).subscribe();
expect(adapter.afterRedirectScripts).toHaveBeenCalledWith('1');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { Injectable } from '@angular/core';
import {
AfterRedirectScriptResponse,
OpfPaymentVerificationPayload,
OpfPaymentVerificationResponse,
SubmitCompleteRequest,
Expand Down Expand Up @@ -47,4 +48,10 @@ export class OpfPaymentConnector {
paymentSessionId
);
}

public afterRedirectScripts(
paymentSessionId: string
): Observable<AfterRedirectScriptResponse> {
return this.adapter.afterRedirectScripts(paymentSessionId);
}
}
Empty file.
136 changes: 104 additions & 32 deletions integration-libs/opf/base/core/facade/opf-global-functions.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import {
} from '@angular/core';
import { WindowRef } from '@spartacus/core';
import {
GlobalFunctionsInput,
GlobalOpfPaymentMethods,
KeyValuePair,
MerchantCallback,
OpfGlobalFunctionsFacade,
OpfPaymentFacade,
PaymentMethod,
TargetPage,
} from '@spartacus/opf/base/root';
import { LAUNCH_CALLER, LaunchDialogService } from '@spartacus/storefront';
import { Observable } from 'rxjs';
Expand All @@ -34,12 +36,25 @@ export class OpfGlobalFunctionsService implements OpfGlobalFunctionsFacade {
protected launchDialogService: LaunchDialogService
) {}

registerGlobalFunctions(
paymentSessionId: string,
vcr?: ViewContainerRef
): void {
this.registerSubmit(paymentSessionId, vcr);
this.registerSubmitComplete(paymentSessionId, vcr);
registerGlobalFunctions({
targetPage,
paymentSessionId,
vcr,
paramsMap,
}: GlobalFunctionsInput): void {
switch (targetPage) {
case TargetPage.CHECKOUT_REVIEW:
this.registerSubmit(paymentSessionId, vcr);
this.registerSubmitComplete(paymentSessionId, vcr);
break;
case TargetPage.RESULT:
this.registerSubmitCompleteRedirect(paymentSessionId, vcr);
this.registerGetRedirectParams(paramsMap ?? []);
break;
default:
break;
}

this._isGlobalServiceInit = true;
}

Expand Down Expand Up @@ -88,6 +103,15 @@ export class OpfGlobalFunctionsService implements OpfGlobalFunctionsFacade {
.unsubscribe();
}

protected registerGetRedirectParams(
paramsMap: Array<KeyValuePair> = []
): void {
this.getGlobalFunctionContainer().getRedirectParams = () =>
paramsMap.map((p) => {
return { key: p.key, value: p.value };
});
}

protected registerSubmit(
paymentSessionId: string,
vcr?: ViewContainerRef
Expand Down Expand Up @@ -145,6 +169,39 @@ export class OpfGlobalFunctionsService implements OpfGlobalFunctionsFacade {
};
}

protected runSubmitComplete(
cartId: string,
additionalData: Array<KeyValuePair>,
callbackArray: [MerchantCallback, MerchantCallback, MerchantCallback],
paymentSessionId: string,
returnPath?: string | undefined,
vcr?: ViewContainerRef
) {
return this.ngZone.run(() => {
let overlayedSpinner: void | Observable<ComponentRef<any> | undefined>;
if (vcr) {
overlayedSpinner = this.startLoaderSpinner(vcr);
}

return this.opfPaymentFacade
.submitCompletePayment({
additionalData,
paymentSessionId,
cartId,
callbackArray,
returnPath,
})
.pipe(
finalize(() => {
if (overlayedSpinner) {
this.stopLoaderSpinner(overlayedSpinner);
}
})
)
.toPromise();
});
}

protected registerSubmitComplete(
paymentSessionId: string,
vcr?: ViewContainerRef
Expand All @@ -168,33 +225,48 @@ export class OpfGlobalFunctionsService implements OpfGlobalFunctionsFacade {
submitPending: MerchantCallback;
submitFailure: MerchantCallback;
}): Promise<boolean> => {
return this.ngZone.run(() => {
let overlayedSpinner: void | Observable<ComponentRef<any> | undefined>;
if (vcr) {
overlayedSpinner = this.startLoaderSpinner(vcr);
}
const callbackArray: [
MerchantCallback,
MerchantCallback,
MerchantCallback
] = [submitSuccess, submitPending, submitFailure];
return this.runSubmitComplete(
cartId,
additionalData,
[submitSuccess, submitPending, submitFailure],
paymentSessionId,
undefined,
vcr
);
};
}

return this.opfPaymentFacade
.submitCompletePayment({
additionalData,
paymentSessionId,
cartId,
callbackArray,
})
.pipe(
finalize(() => {
if (overlayedSpinner) {
this.stopLoaderSpinner(overlayedSpinner);
}
})
)
.toPromise();
});
protected registerSubmitCompleteRedirect(
paymentSessionId: string,
vcr?: ViewContainerRef
): void {
this.getGlobalFunctionContainer().submitCompleteRedirect = ({
cartId,
additionalData,
submitSuccess = (): void => {
// this is intentional
},
submitPending = (): void => {
// this is intentional
},
submitFailure = (): void => {
// this is intentional
},
}: {
cartId: string;
additionalData: Array<KeyValuePair>;
submitSuccess: MerchantCallback;
submitPending: MerchantCallback;
submitFailure: MerchantCallback;
}): Promise<boolean> => {
return this.runSubmitComplete(
cartId,
additionalData,
[submitSuccess, submitPending, submitFailure],
paymentSessionId,
'checkoutReviewOrder',
vcr
);
};
}
}
20 changes: 20 additions & 0 deletions integration-libs/opf/base/core/facade/opf-payment.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TestBed } from '@angular/core/testing';
import { CommandService } from '@spartacus/core';
import { Observable, of } from 'rxjs';
import {
AfterRedirectScriptResponse,
OpfPaymentVerificationPayload,
OpfPaymentVerificationResponse,
SubmitCompleteInput,
Expand All @@ -27,6 +28,12 @@ class MockPaymentConnector {
result: 'result',
}) as Observable<OpfPaymentVerificationResponse>;
}
afterRedirectScripts(
paymentSessionId: string
): Observable<AfterRedirectScriptResponse> {
console.log(paymentSessionId);
return of({ afterRedirectScript: {} });
}
}

class MockOpfPaymentHostedFieldsService {
Expand Down Expand Up @@ -211,4 +218,17 @@ describe('OpfPaymentService', () => {
expect(response).toBe(true);
});
});

it('should call afterRedirectScripts from connector with the correct payload', () => {
const paymentSessionId = 'exampleSessionId';

const connectorVerifySpy = spyOn(
paymentConnector,
'afterRedirectScripts'
).and.callThrough();

service.afterRedirectScripts(paymentSessionId);

expect(connectorVerifySpy).toHaveBeenCalledWith(paymentSessionId);
});
});
16 changes: 16 additions & 0 deletions integration-libs/opf/base/core/facade/opf-payment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { Injectable } from '@angular/core';
import { Command, CommandService } from '@spartacus/core';
import {
AfterRedirectScriptResponse,
OpfPaymentFacade,
OpfPaymentVerificationPayload,
OpfPaymentVerificationResponse,
Expand Down Expand Up @@ -54,6 +55,17 @@ export class OpfPaymentService implements OpfPaymentFacade {
);
});

protected afterRedirectScriptsCommand: Command<
{
paymentSessionId: string;
},
AfterRedirectScriptResponse
> = this.commandService.create((payload) => {
return this.opfPaymentConnector.afterRedirectScripts(
payload.paymentSessionId
);
});

constructor(
protected commandService: CommandService,
protected opfPaymentConnector: OpfPaymentConnector,
Expand Down Expand Up @@ -81,4 +93,8 @@ export class OpfPaymentService implements OpfPaymentFacade {
): Observable<boolean> {
return this.submitCompletePaymentCommand.execute({ submitCompleteInput });
}

afterRedirectScripts(paymentSessionId: string) {
return this.afterRedirectScriptsCommand.execute({ paymentSessionId });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,12 @@ describe('OpfPaymentErrorHandlerService', () => {
type: 'type',
message: 'Test error message',
};
const returnPath = ['checkout', 'payment'];
const returnPath = 'checkout';
service.handlePaymentError(error, returnPath);
expect(mockGlobalMessageService.add).toHaveBeenCalled();
expect(mockRoutingService.go).toHaveBeenCalledWith(returnPath);
expect(mockRoutingService.go).toHaveBeenCalledWith({
cxRoute: returnPath,
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class OpfPaymentErrorHandlerService {

handlePaymentError(
error: OpfPaymentError | undefined,
returnPath: Array<string> = []
returnPath?: string
): void {
let message = defaultError.message;
if (error?.status === HttpResponseStatus.BAD_REQUEST) {
Expand All @@ -67,8 +67,8 @@ export class OpfPaymentErrorHandlerService {
}
}
this.displayError(error ? { ...error, message } : undefined);
if (returnPath.length) {
this.routingService.go([...returnPath]);
if (returnPath?.length) {
this.routingService.go({ cxRoute: returnPath });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ describe('OpfPaymentHostedFieldsService', () => {
cartId: 'mockCartId',
additionalData: [{ key: 'key', value: 'value' }],
paymentSessionId: 'sessionId',
returnPath: ['checkout', 'payment'],
returnPath: 'checkout',
callbackArray: [() => {}, () => {}, () => {}],
};

const mockSubmitCompleteInput: SubmitCompleteInput = {
cartId: 'mockCartId',
additionalData: [{ key: 'key', value: 'value' }],
paymentSessionId: 'sessionId',
returnPath: ['checkout', 'payment'],
returnPath: 'checkout',
callbackArray: [() => {}, () => {}, () => {}],
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ export class OpfPaymentHostedFieldsService {
this.userIdService.getUserId(),
this.activeCartFacade.getActiveCartId(),
]).pipe(
filter(
([userId, activeCartId]: [string, string]) => !!activeCartId && !!userId
),
switchMap(([userId, activeCartId]: [string, string]) => {
submitRequest.cartId = activeCartId;
return this.opfOtpFacade.generateOtpKey(userId, activeCartId);
Expand Down Expand Up @@ -129,6 +132,9 @@ export class OpfPaymentHostedFieldsService {
this.userIdService.getUserId(),
this.activeCartFacade.getActiveCartId(),
]).pipe(
filter(
([userId, activeCartId]: [string, string]) => !!activeCartId && !!userId
),
switchMap(([userId, activeCartId]: [string, string]) => {
submitCompleteRequest.cartId = activeCartId;
return this.opfOtpFacade.generateOtpKey(userId, activeCartId);
Expand Down
5 changes: 5 additions & 0 deletions integration-libs/opf/base/core/tokens/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { InjectionToken } from '@angular/core';
import { Converter } from '@spartacus/core';
import {
AfterRedirectScriptResponse,
OpfPaymentVerificationResponse,
SubmitCompleteResponse,
SubmitResponse,
Expand All @@ -23,3 +24,7 @@ export const OPF_PAYMENT_SUBMIT_NORMALIZER = new InjectionToken<
export const OPF_PAYMENT_SUBMIT_COMPLETE_NORMALIZER = new InjectionToken<
Converter<any, SubmitCompleteResponse>
>('OpfPaymentSubmitCompleteNormalizer');

export const OPF_AFTER_REDIRECT_SCRIPTS_NORMALIZER = new InjectionToken<
Converter<any, AfterRedirectScriptResponse>
>('OpfAfterRedirectScriptsNormalizer');
Loading

0 comments on commit 6ef7b96

Please sign in to comment.