diff --git a/integration-libs/opf/base/root/model/opf-metadata-store.model.ts b/integration-libs/opf/base/root/model/opf-metadata-store.model.ts index 146fc9020b1..f5d140f8096 100644 --- a/integration-libs/opf/base/root/model/opf-metadata-store.model.ts +++ b/integration-libs/opf/base/root/model/opf-metadata-store.model.ts @@ -9,6 +9,6 @@ export interface OpfMetadataModel { selectedPaymentOptionId: number | undefined; defaultSelectedPaymentOptionId?: number; isPaymentInProgress: boolean; - paymentSessionId: string | undefined; + opfPaymentSessionId: string | undefined; isTermsAndConditionsAlertClosed: boolean; } diff --git a/integration-libs/opf/base/root/services/opf-metadata-state-persistence.service.spec.ts b/integration-libs/opf/base/root/services/opf-metadata-state-persistence.service.spec.ts index b262ae6cc41..50b777c8ae1 100644 --- a/integration-libs/opf/base/root/services/opf-metadata-state-persistence.service.spec.ts +++ b/integration-libs/opf/base/root/services/opf-metadata-state-persistence.service.spec.ts @@ -17,7 +17,7 @@ const mockOpfMetadata: OpfMetadataModel = { isPaymentInProgress: true, selectedPaymentOptionId: 111, termsAndConditionsChecked: true, - paymentSessionId: '111111', + opfPaymentSessionId: '111111', isTermsAndConditionsAlertClosed: false, }; diff --git a/integration-libs/opf/base/root/services/opf-metadata-store.service.spec.ts b/integration-libs/opf/base/root/services/opf-metadata-store.service.spec.ts index 318b2af527b..a60b0e8d8c4 100644 --- a/integration-libs/opf/base/root/services/opf-metadata-store.service.spec.ts +++ b/integration-libs/opf/base/root/services/opf-metadata-store.service.spec.ts @@ -11,7 +11,7 @@ const initialState: OpfMetadataModel = { termsAndConditionsChecked: false, selectedPaymentOptionId: undefined, isPaymentInProgress: false, - paymentSessionId: undefined, + opfPaymentSessionId: undefined, isTermsAndConditionsAlertClosed: false, }; @@ -19,7 +19,7 @@ const state: OpfMetadataModel = { isPaymentInProgress: true, selectedPaymentOptionId: 111, termsAndConditionsChecked: true, - paymentSessionId: '111111', + opfPaymentSessionId: '111111', isTermsAndConditionsAlertClosed: false, }; @@ -77,7 +77,7 @@ describe('OpfMetadataStoreService', () => { isPaymentInProgress: true, termsAndConditionsChecked: true, selectedPaymentOptionId: 111, - paymentSessionId: '111111', + opfPaymentSessionId: '111111', isTermsAndConditionsAlertClosed: false, }; diff --git a/integration-libs/opf/base/root/services/opf-metadata-store.service.ts b/integration-libs/opf/base/root/services/opf-metadata-store.service.ts index 35097e26ba7..f1987268079 100644 --- a/integration-libs/opf/base/root/services/opf-metadata-store.service.ts +++ b/integration-libs/opf/base/root/services/opf-metadata-store.service.ts @@ -14,7 +14,7 @@ export class OpfMetadataStoreService { termsAndConditionsChecked: false, selectedPaymentOptionId: undefined, isPaymentInProgress: false, - paymentSessionId: undefined, + opfPaymentSessionId: undefined, isTermsAndConditionsAlertClosed: false, }); diff --git a/integration-libs/opf/base/root/services/opf-resource-loader.service.spec.ts b/integration-libs/opf/base/root/services/opf-resource-loader.service.spec.ts index 3ee1c5ae9cd..76b77c4b9d4 100644 --- a/integration-libs/opf/base/root/services/opf-resource-loader.service.spec.ts +++ b/integration-libs/opf/base/root/services/opf-resource-loader.service.spec.ts @@ -71,6 +71,7 @@ describe('OpfResourceLoaderService', () => { it('should load provider resources successfully for scripts', fakeAsync(() => { const mockScriptResource = { + attributes: [{ key: 'crossorigin', value: 'use-credentials' }], url: 'script-url', sri: 'fake-hash-code', type: OpfDynamicScriptResourceType.SCRIPT, @@ -176,7 +177,10 @@ describe('OpfResourceLoaderService', () => { const mockStylesResources = { url: 'style-url', sri: 'fake-hash-code', - attributes: [{ key: 'mock-key', value: 'mock-value' }], + attributes: [ + { key: 'mock-key', value: 'mock-value' }, + { key: 'crossorigin', value: 'use-credentials' }, + ], type: OpfDynamicScriptResourceType.STYLES, }; diff --git a/integration-libs/opf/base/root/services/opf-resource-loader.service.ts b/integration-libs/opf/base/root/services/opf-resource-loader.service.ts index efaf936bfcf..461c70f9af8 100644 --- a/integration-libs/opf/base/root/services/opf-resource-loader.service.ts +++ b/integration-libs/opf/base/root/services/opf-resource-loader.service.ts @@ -23,6 +23,7 @@ export class OpfResourceLoaderService { protected platformId = inject(PLATFORM_ID); protected readonly OPF_RESOURCE_ATTRIBUTE_KEY = 'data-opf-resource'; + protected readonly CORS_DEFAULT_VALUE = 'anonymous'; protected embedStyles(embedOptions: { attributes?: OpfKeyValueMap[]; @@ -40,6 +41,10 @@ export class OpfResourceLoaderService { link.setAttribute(this.OPF_RESOURCE_ATTRIBUTE_KEY, 'true'); if (sri) { link.integrity = sri; + const corsKeyvalue = attributes?.find( + (attr) => attr.key === 'crossorigin' && !!attr.value?.length + ); + link.crossOrigin = corsKeyvalue?.value ?? this.CORS_DEFAULT_VALUE; } if (attributes?.length) { attributes.forEach((attribute) => { @@ -84,6 +89,12 @@ export class OpfResourceLoaderService { if (resource?.sri) { attributes['integrity'] = resource.sri; + const corsKeyvalue: OpfKeyValueMap | undefined = + resource?.attributes?.find( + (attr) => attr.key === 'crossorigin' && !!attr.value?.length + ); + attributes['crossOrigin'] = + corsKeyvalue?.value ?? this.CORS_DEFAULT_VALUE; } if (resource.attributes) { diff --git a/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.component.html b/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.component.html index 8476c9865f6..20f63ffc727 100644 --- a/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.component.html +++ b/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.component.html @@ -67,8 +67,8 @@ - -
+ +
diff --git a/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.service.spec.ts b/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.service.spec.ts index bd68b0f2eaa..53fb1195bd6 100644 --- a/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.service.spec.ts +++ b/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.service.spec.ts @@ -300,7 +300,6 @@ describe('OpfCheckoutPaymentWrapperService', () => { isLoading: false, isError: false, renderType: OpfPaymentRenderPattern.FULL_PAGE, - data: mockUrl, destination: { url: mockUrl, form: [] }, }); }); @@ -313,13 +312,13 @@ describe('OpfCheckoutPaymentWrapperService', () => { }; (service as any).storePaymentSessionId(mockPaymentSessionData); expect(opfMetadataStoreServiceMock.updateOpfMetadata).toHaveBeenCalledWith({ - paymentSessionId: mockPaymentSessionId, + opfPaymentSessionId: mockPaymentSessionId, }); mockPaymentSessionData.pattern = OpfPaymentRenderPattern.HOSTED_FIELDS; (service as any).storePaymentSessionId(mockPaymentSessionData); expect(opfMetadataStoreServiceMock.updateOpfMetadata).toHaveBeenCalledWith({ - paymentSessionId: undefined, + opfPaymentSessionId: undefined, }); }); @@ -349,7 +348,6 @@ describe('OpfCheckoutPaymentWrapperService', () => { isLoading: false, isError: false, renderType: OpfPaymentRenderPattern.IFRAME, - data: mockUrl, destination: { url: mockUrl, form: mockFormData }, }); }); @@ -400,7 +398,7 @@ describe('OpfCheckoutPaymentWrapperService', () => { isLoading: false, isError: false, renderType: OpfPaymentRenderPattern.HOSTED_FIELDS, - data: '', + html: '', }); done(); }); diff --git a/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.service.ts b/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.service.ts index 18c26f1f8ae..7a0937ba357 100644 --- a/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.service.ts +++ b/integration-libs/opf/checkout/components/opf-checkout-payment-wrapper/opf-checkout-payment-wrapper.service.ts @@ -140,7 +140,9 @@ export class OpfCheckoutPaymentWrapperService { paymentOptionConfig.paymentSessionId ? paymentOptionConfig.paymentSessionId : undefined; - this.opfMetadataStoreService.updateOpfMetadata({ paymentSessionId }); + this.opfMetadataStoreService.updateOpfMetadata({ + opfPaymentSessionId: paymentSessionId, + }); } reloadPaymentMode(): void { @@ -149,18 +151,16 @@ export class OpfCheckoutPaymentWrapperService { } } + /** + * Render payment option covering the three patterns: IFRAME, FULL_PAGE, HOSTED_FIELDS. + * Context to explain this method logic: + * All three patterns can contains `dynamicScript` value. + * IFRAME and FULL_PAGE patterns can also have `destination` value. + * if `dynamicScript` and `destination` are present in same config, dynamicScript takes precendence. + * @param config + * @returns : none, OpfPaymentRenderMethodEvent gets emitted + */ renderPaymentGateway(config: OpfPaymentSessionData) { - if (config?.destination) { - this.renderPaymentMethodEvent$.next({ - isLoading: false, - isError: false, - renderType: config?.pattern, - data: config?.destination.url, - destination: config?.destination, - }); - return; - } - if (config?.dynamicScript) { const html = config?.dynamicScript?.html; @@ -174,7 +174,7 @@ export class OpfCheckoutPaymentWrapperService { isLoading: false, isError: false, renderType: config?.pattern, - data: html, + html, }); if (html) { @@ -186,11 +186,16 @@ export class OpfCheckoutPaymentWrapperService { }); return; } - this.handlePaymentInitiationError({ - message: 'Payment Configuration problem', - }) - .pipe(take(1)) - .subscribe(); + if (config?.destination) { + this.renderPaymentMethodEvent$.next({ + isLoading: false, + isError: false, + renderType: config?.pattern, + destination: config?.destination, + }); + return; + } + this.handleGeneralPaymentError().pipe(take(1)).subscribe(); } protected handlePaymentInitiationError( diff --git a/integration-libs/opf/checkout/components/opf-checkout-payments/opf-checkout-payments.component.spec.ts b/integration-libs/opf/checkout/components/opf-checkout-payments/opf-checkout-payments.component.spec.ts index 046328f31cd..5c8babc04d8 100644 --- a/integration-libs/opf/checkout/components/opf-checkout-payments/opf-checkout-payments.component.spec.ts +++ b/integration-libs/opf/checkout/components/opf-checkout-payments/opf-checkout-payments.component.spec.ts @@ -87,7 +87,7 @@ const mockOpfMetadata: OpfMetadataModel = { selectedPaymentOptionId: 111, termsAndConditionsChecked: true, defaultSelectedPaymentOptionId: 1, - paymentSessionId: '111111', + opfPaymentSessionId: '111111', isTermsAndConditionsAlertClosed: false, }; @@ -193,7 +193,7 @@ describe('OpfCheckoutPaymentsComponent', () => { selectedPaymentOptionId: undefined, termsAndConditionsChecked: true, defaultSelectedPaymentOptionId, - paymentSessionId: '111111', + opfPaymentSessionId: '111111', isTermsAndConditionsAlertClosed: false, }) ); diff --git a/integration-libs/opf/payment/root/components/opf-payment-verification/opf-payment-verification.service.spec.ts b/integration-libs/opf/payment/root/components/opf-payment-verification/opf-payment-verification.service.spec.ts index 45c8ece2570..16c30be82f7 100644 --- a/integration-libs/opf/payment/root/components/opf-payment-verification/opf-payment-verification.service.spec.ts +++ b/integration-libs/opf/payment/root/components/opf-payment-verification/opf-payment-verification.service.spec.ts @@ -112,14 +112,20 @@ describe('OpfPaymentVerificationService', () => { cxRoute: 'paymentVerificationResult', }, }, - queryParams: of({ paymentSessionId: mockPaymentSessionId }), + queryParams: of({ + opfPaymentSessionId: mockPaymentSessionId, + keyMock: 'valueMock', + }), } as unknown as ActivatedRoute; - it('should verify the result URL and return the response map if the route cxRoute is "paymentVerificationResult"', (done) => { + it('should verify the result URL and return the response map without opfPaymentSessionId if the route cxRoute is "paymentVerificationResult"', (done) => { service.verifyResultUrl(mockRouteSnapshot).subscribe((result) => { expect(result.paymentSessionId).toEqual(mockPaymentSessionId); expect(result.paramsMap).toEqual([ - { key: 'paymentSessionId', value: mockPaymentSessionId }, + { + key: 'keyMock', + value: 'valueMock', + }, ]); done(); }); @@ -133,14 +139,14 @@ describe('OpfPaymentVerificationService', () => { cxRoute: 'paymentVerificationResult', }, }, - queryParams: of({ afterRedirectScriptFlag: 'true' }), + queryParams: of({ opfAfterRedirectFlag: 'true' }), } as unknown as ActivatedRoute; const mockOpfMetadata: OpfMetadataModel = { isPaymentInProgress: true, selectedPaymentOptionId: 111, termsAndConditionsChecked: true, - paymentSessionId: mockPaymentSessionId, + opfPaymentSessionId: mockPaymentSessionId, isTermsAndConditionsAlertClosed: false, }; @@ -151,7 +157,7 @@ describe('OpfPaymentVerificationService', () => { service.verifyResultUrl(mockRouteSnapshot).subscribe((result) => { expect(result.paymentSessionId).toEqual(mockPaymentSessionId); expect(result.paramsMap).toEqual([ - { key: 'afterRedirectScriptFlag', value: 'true' }, + { key: 'opfAfterRedirectFlag', value: 'true' }, ]); expect(result.afterRedirectScriptFlag).toEqual('true'); done(); @@ -180,7 +186,7 @@ describe('OpfPaymentVerificationService', () => { isPaymentInProgress: true, selectedPaymentOptionId: 111, termsAndConditionsChecked: true, - paymentSessionId: undefined, + opfPaymentSessionId: undefined, isTermsAndConditionsAlertClosed: false, }; @@ -212,7 +218,7 @@ describe('OpfPaymentVerificationService', () => { isPaymentInProgress: true, selectedPaymentOptionId: 111, termsAndConditionsChecked: true, - paymentSessionId: undefined, + opfPaymentSessionId: undefined, isTermsAndConditionsAlertClosed: false, }; @@ -500,7 +506,7 @@ describe('OpfPaymentVerificationService', () => { isPaymentInProgress: true, selectedPaymentOptionId: 111, termsAndConditionsChecked: true, - paymentSessionId: '111111', + opfPaymentSessionId: '111111', isTermsAndConditionsAlertClosed: false, }; @@ -522,7 +528,7 @@ describe('OpfPaymentVerificationService', () => { isPaymentInProgress: false, selectedPaymentOptionId: 111, termsAndConditionsChecked: true, - paymentSessionId: '111111', + opfPaymentSessionId: '111111', isTermsAndConditionsAlertClosed: false, }; diff --git a/integration-libs/opf/payment/root/components/opf-payment-verification/opf-payment-verification.service.ts b/integration-libs/opf/payment/root/components/opf-payment-verification/opf-payment-verification.service.ts index 0fdc1da2d7e..edf9b78b830 100644 --- a/integration-libs/opf/payment/root/components/opf-payment-verification/opf-payment-verification.service.ts +++ b/integration-libs/opf/payment/root/components/opf-payment-verification/opf-payment-verification.service.ts @@ -85,22 +85,26 @@ export class OpfPaymentVerificationService { }), concatMap((paymentSessionId: string | undefined) => { if (!paymentSessionId) { - return throwError(this.opfDefaultPaymentError); + return throwError(() => this.opfDefaultPaymentError); } return of({ paymentSessionId, - paramsMap, + paramsMap: paramsMap.filter( + (param) => + param.key !== + OpfPaymentVerificationUrlInput.OPF_PAYMENT_SESSION_ID + ), afterRedirectScriptFlag: this.findInParamsMap( - 'afterRedirectScriptFlag', + OpfPaymentVerificationUrlInput.OPF_AFTER_REDIRECT_FLAG, paramsMap ), }); }) ) - : throwError({ + : throwError(() => ({ ...this.opfDefaultPaymentError, message: 'opfPayment.errors.cancelPayment', - }); + })); } protected getPaymentSessionId( @@ -108,7 +112,7 @@ export class OpfPaymentVerificationService { ): Observable { if (paramMap?.length) { const paymentSessionId = this.findInParamsMap( - OpfPaymentVerificationUrlInput.PAYMENT_SESSION_ID, + OpfPaymentVerificationUrlInput.OPF_PAYMENT_SESSION_ID, paramMap ); return paymentSessionId @@ -121,7 +125,7 @@ export class OpfPaymentVerificationService { protected getPaymentSessionIdFromStorage(): Observable { return this.opfMetadataStoreService.getOpfMetadataState().pipe( take(1), - map((opfMetaData) => opfMetaData?.paymentSessionId) + map((opfMetaData) => opfMetaData?.opfPaymentSessionId) ); } @@ -153,12 +157,12 @@ export class OpfPaymentVerificationService { ) { return of(true); } else if (response.result === OpfPaymentVerificationResult.CANCELLED) { - return throwError({ + return throwError(() => ({ ...this.opfDefaultPaymentError, message: 'opfPayment.errors.cancelPayment', - }); + })); } else { - return throwError(this.opfDefaultPaymentError); + return throwError(() => this.opfDefaultPaymentError); } } diff --git a/integration-libs/opf/payment/root/model/opf-payment-verification.model.ts b/integration-libs/opf/payment/root/model/opf-payment-verification.model.ts index 0e446a8a846..b64284f8092 100644 --- a/integration-libs/opf/payment/root/model/opf-payment-verification.model.ts +++ b/integration-libs/opf/payment/root/model/opf-payment-verification.model.ts @@ -22,6 +22,6 @@ export enum OpfPaymentVerificationResult { } export enum OpfPaymentVerificationUrlInput { - PAYMENT_SESSION_ID = 'paymentSessionId', - ORDER_ID = 'orderId', + OPF_AFTER_REDIRECT_FLAG = 'opfAfterRedirectFlag', + OPF_PAYMENT_SESSION_ID = 'opfPaymentSessionId', } diff --git a/integration-libs/opf/payment/root/model/opf-payment.model.ts b/integration-libs/opf/payment/root/model/opf-payment.model.ts index 467db3f0a71..9405a30b6e9 100644 --- a/integration-libs/opf/payment/root/model/opf-payment.model.ts +++ b/integration-libs/opf/payment/root/model/opf-payment.model.ts @@ -172,7 +172,7 @@ export interface OpfPaymentRenderMethodEvent { isLoading: boolean; isError: boolean; renderType?: OpfPaymentRenderPattern; - data?: string | null; + html?: string | null; destination?: OpfPaymentDestination; } diff --git a/integration-libs/opf/quick-buy/components/opf-quick-buy-buttons/apple-pay/apple-pay.service.ts b/integration-libs/opf/quick-buy/components/opf-quick-buy-buttons/apple-pay/apple-pay.service.ts index 0c90c8a7262..e5f84e33d1d 100644 --- a/integration-libs/opf/quick-buy/components/opf-quick-buy-buttons/apple-pay/apple-pay.service.ts +++ b/integration-libs/opf/quick-buy/components/opf-quick-buy-buttons/apple-pay/apple-pay.service.ts @@ -49,6 +49,13 @@ export class ApplePayService { ); protected opfQuickBuyFacade = inject(OpfQuickBuyFacade); protected paymentInProgress = false; + protected readonly defaultApplePayCardParameters: any = { + shippingMethods: [], + merchantCapabilities: ['supports3DS'], + supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'], + requiredShippingContactFields: ['email', 'name', 'postalAddress'], + requiredBillingContactFields: ['email', 'name', 'postalAddress'], + }; protected initialTransactionDetails: QuickBuyTransactionDetails = { context: OpfQuickBuyLocation.PRODUCT, @@ -168,16 +175,12 @@ export class ApplePayService { this.transactionDetails = this.initTransactionDetails(transactionInput); const countryCode = transactionInput?.countryCode || ''; const initialRequest: ApplePayJS.ApplePayPaymentRequest = { + ...this.defaultApplePayCardParameters, currencyCode: this.transactionDetails.total.currency, total: { amount: this.transactionDetails.total.amount, label: this.transactionDetails.total.label, }, - shippingMethods: [], - merchantCapabilities: ['supports3DS'], - supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'], - requiredShippingContactFields: ['email', 'name', 'postalAddress'], - requiredBillingContactFields: ['email', 'name', 'postalAddress'], countryCode, }; diff --git a/integration-libs/opf/quick-buy/components/opf-quick-buy-buttons/google-pay/google-pay.service.ts b/integration-libs/opf/quick-buy/components/opf-quick-buy-buttons/google-pay/google-pay.service.ts index 37e7ed64855..bf628c41417 100644 --- a/integration-libs/opf/quick-buy/components/opf-quick-buy-buttons/google-pay/google-pay.service.ts +++ b/integration-libs/opf/quick-buy/components/opf-quick-buy-buttons/google-pay/google-pay.service.ts @@ -76,6 +76,22 @@ export class OpfGooglePayService { }, }; + protected readonly defaultGooglePayCardParameters: any = { + allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], + allowedCardNetworks: [ + 'AMEX', + 'DISCOVER', + 'INTERAC', + 'JCB', + 'MASTERCARD', + 'VISA', + ], + billingAddressRequired: true, + billingAddressParameters: { + format: 'FULL', + }, + }; + private initialTransactionInfo: google.payments.api.TransactionInfo = { totalPrice: '0.00', totalPriceStatus: 'ESTIMATED', @@ -480,19 +496,7 @@ export class OpfGooglePayService { this.googlePaymentRequest.allowedPaymentMethods = [ { parameters: { - allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], - allowedCardNetworks: [ - 'AMEX', - 'DISCOVER', - 'INTERAC', - 'JCB', - 'MASTERCARD', - 'VISA', - ], - billingAddressRequired: true, - billingAddressParameters: { - format: 'FULL', - }, + ...this.defaultGooglePayCardParameters, }, tokenizationSpecification: { parameters: {